Thursday, October 14, 2010

How to use BufferStrategy in Java

One of the things I didn't know when starting Java programming was the state of the hardware acceleration support. Java does provide hardware acceleration. In earlier version the double buffering was a bit buggy but since jre6 it works very well in swing. It also uses DirectX and Opengl under the hood which you can enable/disable. Hardware accelerated drawing is enabled by default on most systems where it is properly installed. To take advantage of this hardware acceleration you want to use a java.awt.Canvas component. You can let swing do the double buffering which entails simply repainting the screen and not worrying about using the BufferStrategy class. It seems to perform well, so you should test both methods out and see which works best for you. In this snippet I will not mix swing and awt so I use a Frame instead of a JFrame. I noticed from an early access release of jdk7 that if you use a Canvas component inside a JFrame nothing will draw, unlike jre6 and below where it doesn't present an issue. I do not know whether this is intentional to try and keep native resources and lightweight resource from conflicting or some other such implementation detail but it did cause me a headache for a little while.

VSync Improving Visuals or Triple Buffering
This is a minor annoyance when running in windowed mode for a game. Occasionally the display and the drawing of the window will become out of sync and cause some tearing or jittering of the constantly refreshing window. It happens due to not being able to refresh the window contents at the exact same rate the display is actually refreshing the entire screen. This isn't too noticeable with such high refresh rates on monitors, but just in case you need to be able to do this, there is a function. I didn't really know about it till recently, it's in the java.awt.Toolkit called sync(). You can also create a buffer strategy with 3 buffers, or triple buffering, which is known to almost eliminate the need for the sync so that your program isn't in the middle of drawing to the buffer while the display is drawing from the buffer.
EDIT: Use java.awt.Toolkit called sync() to update the display buffer (whatever that really is) to be the most current drawing surface in the buffer strategy. Should help ensure the user sees the latest and greatest from the application.

Other Useful Options
A number of different options are also available as System Properties for java2d. For instance the trace property which tells you how long and specifically which drawing functions are called. This will let you know whether the hardware acceleration drawing layer in Java is being called at all.
java -Dsun.java2d.trace=log
Setting System Properties in the code
A nice thing to be able to do is to use a configuration file loaded by your program to save/load these types of graphics options or other kinds of options that your program may allow the user to change but are used by the Java API before the application starts. I like to use static initialization blocks for this. So I will have something like the following in my main class.
// performance settings.

static {
    System.setProperty("sun.java2d.transaccel", "True");
    // System.setProperty("sun.java2d.trace", "timestamp,log,count");
    // System.setProperty("sun.java2d.opengl", "True");
    System.setProperty("sun.java2d.d3d", "True");
    System.setProperty("sun.java2d.ddforcevram", "True");
}

You can set it up however you like and even get complex with it, loading the settings from a config file like I mentioned above, or popup a nice little window with selectable options for d3d versus opengl. The trace line specifies the format of all output from java2d as described in the system properties link.

Using BufferStrategy
Use the createBufferStrategy(int numBuffers) in the canvas component only after the frame has been created and displayed. You can control how many buffers to use for rendering, the most I'd suggest would be 3 any more than that is overkill, imho. Creation of the strategy may cause a java.lang.IllegalStateException to be thrown if the canvas does not have a parent who is being displayed. Meaning you need to add the Canvas component to a Container and make sure that container is visible.
// create a strategy that uses two buffers, or is double buffered.
  this.createBufferStrategy(2);

  // get a reference to the strategy object, for use in our render method
  // this isn't necessary but it eliminates a call during rendering.
  strategy = this.getBufferStrategy();

To use the BufferStrategy for rendering you want to use the strategy's getDrawGraphics and show methods. The getDrawGraphics creates a Graphics2D object that will draw to one of the buffers, if you are using 2 or more buffers this will be an offscreen buffer, allowing you to draw everything completely before displaying it which eliminates flickering and allows you to clear the buffer without the user noticing, and being able to redraw everything. Clearing and redrawing is essential for animation as you want to redraw the entire screen when objects move or change.

Example drawing code from the snippet:
// the back buffer graphics object
  Graphics2D bkG = (Graphics2D) strategy.getDrawGraphics();  
  
  // clear the backbuffer, this could be substituted for a background
  // image or a tiled background.
  bkG.setPaint(backgroundGradient);
  bkG.fillRect(0, 0, getWidth(), getHeight());

  // TODO: Draw your game world, or scene or anything else here.

  // Rectangle2D is a shape subclass, and the graphics object can render
  // it, makes things a little easier.
  bkG.setColor(Color.green.darker());
  bkG.fill(basicBlock);

  // properly dispose of the backbuffer graphics object. Release resources
  // and cleanup.
  bkG.dispose();

  // flip/draw the backbuffer to the canvas component.
  strategy.show();

  // synchronize with the display refresh rate.
  Toolkit.getDefaultToolkit().sync();

Precision and high frame rates
To make a good game you really do not need more than 60 frames per second. There is a lot of discussion about the subject and many say that the human eye will always be able to see screen refreshes. I think that it has nothing to do with the frame rate as much as with the movement and update timing. As long as the animation in your game is smooth without noticeable and often glitches then it will still be professional. You can always try and aim for a higher frame rate I think I have seen suggestions about around 100fps and I don't know why it's that number, jumping from either 30 to 60 and then all the way to 100. That's 1 frame every 10 milliseconds. While you can do a lot in 10 milliseconds there is a lot you can't do, so find what works best for your game and try and keep the amount objects moved every frame to change at a steady rate relative to their speed. So if an object is moving really fast across the screen small fluctuations in the rate of change in position every frame will cause jittery and jerky movement.

For this snippet I will not show the particulars of creating smooth movement that is for later snippets, where I will go through different kinds of movement. So at long last here it is:

26 comments:

Anonymous said...

didnt know you could fix the refresh issue, my programs have been plagued with screen tearing, cheers

Anonymous said...

I just tried to use BufferStrategy and Toolkit.sync() however framerates are in the many thousands instead of my monitor's 60Hz. if sync() does what it sounds like it should do it should automatically limit my framerate to my monitor's (you artificially cap framerate with the Timer). Mine runs in windowed mode, and BufferStrategy reports false for isFullScreenRequired() and true of isPageFlipping()... seems like a broken promise to me!

Nick said...

Good catch indeed it is misleading because I too mistook the Toolkit.sync() function for a vertical sync feature. It is not and in no way does it force a wait condition on any thread that calls it though I'd have to see the underlining implementation to be sure of all that it is doing but as you have also noticed it does not impede the framerate.

What it does do is make sure that the display buffer whereever it is is update to date with whats in the currently showing buffer in the buffer strategy. Thus to help avoid tearing or delays in viewing.

Though tearing isn't much of an issue anymore and only happens when the display is in the middle of a draw to the screen and the buffer changes at the same time. Thus why vsync was used in earlier days with crt monitors. I have never noticed tearing on lcd monitors not saying it doesn't happen though.

isFullRequired being false and isPageFlipping true means in windowed mode you are able to use multi display buffers which should reduce the chances of tearing.

Some resources on vsync
http://en.wikipedia.org/wiki/Screen_tearing
http://www.gamedev.net/topic/552272-synchronization-with-refresh-rate-vertical-sync---not-for-game/
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering

Are you experiencing tearing or hoping that this sync function would eliminate a potential issue on some machines?

Anonymous said...

Excellent explanation... except, I am still getting a java.lang.IllegalStateException. I don't understand how to fix this based on your explanation :( .

Nick said...

Let me try to expand on the explanation of why you get an IllegalStateException when creating the BufferStrategy. The canvas has to have access to native resources in order to create the strategy. To do that it has to be added to a Frame and that frame has to have isDisplayable equal to true. I've found that if you try to create the strategy in the constructor it will fail because you need to create the canvas, add it to a frame, make sure the frame created it's native resources before you can create the buffers since their linked to native resources which make sense since you want GPU resources to be used.

So depending on where and when you are trying to create the strategy you could get the exception.

One final note that I know I need to mention in more detail somewhere is that in the example I have opengl turned on by default for cross-platform reasons. I know there are issues with some NVIDIA cards on Windows in Java but I don't remember it causing an IllegalStateException to be thrown. With this snippet I have seen exceptions thrown intermittently upon closing the application.

P.S. If you are still having trouble getting it to work just provide a little bit more detail whether the snippet here works, stack trace, any changes or things you are trying to do, I could probably get it to work if given a failing example.

Unknown said...

Posted a long thank you, had to sign into google. Lost post....not typing all that again...anyway thank you.

Unknown said...
This comment has been removed by the author.
Gönenç33 said...

tekirdağ
tokat
elazığ
adıyaman
çankırı
18Q6Q5

StardustPhantomXYOLG129 said...

Maraş Lojistik
Hatay Lojistik
Tokat Lojistik
Elazığ Lojistik
Aksaray Lojistik
AOM

GalacticCipheress said...

aydın evden eve nakliyat
bursa evden eve nakliyat
trabzon evden eve nakliyat
bilecik evden eve nakliyat
antep evden eve nakliyat
Y3SH

Quasar1389G said...

https://istanbulolala.biz/
UV8

2A589Nasir98B24 said...

69259
Niğde Lojistik
Düzce Lojistik
Manisa Evden Eve Nakliyat
Yalova Parça Eşya Taşıma
Antep Lojistik

8220BBenjamin0B42D said...

5A8BC
Batman Evden Eve Nakliyat
Maraş Evden Eve Nakliyat
Tekirdağ Evden Eve Nakliyat
Giresun Evden Eve Nakliyat
Referans Kimliği Nedir

F6BB1Linda6A661 said...

07B75
Balıkesir Şehir İçi Nakliyat
Ardahan Şehirler Arası Nakliyat
Erzurum Evden Eve Nakliyat
Kırıkkale Evden Eve Nakliyat
Mardin Şehirler Arası Nakliyat
Osmaniye Evden Eve Nakliyat
Kastamonu Parça Eşya Taşıma
Ardahan Evden Eve Nakliyat
Sinop Şehir İçi Nakliyat

E2341JesseB1D45 said...

7D9DF
Elazığ Parça Eşya Taşıma
Bitci Güvenilir mi
Karaman Evden Eve Nakliyat
Artvin Şehirler Arası Nakliyat
Kütahya Parça Eşya Taşıma
Batman Parça Eşya Taşıma
Osmaniye Parça Eşya Taşıma
Keep Coin Hangi Borsada
Çerkezköy Çilingir

167ACEricF505F said...

1EA8B
Ünye Çelik Kapı
Mardin Şehirler Arası Nakliyat
Adıyaman Parça Eşya Taşıma
Burdur Lojistik
Ardahan Evden Eve Nakliyat
Çerkezköy Motor Ustası
Iğdır Parça Eşya Taşıma
Şırnak Lojistik
Eskişehir Lojistik

7DCCBRaul8E87E said...

76540
Sakarya Şehir İçi Nakliyat
Ardahan Şehirler Arası Nakliyat
Ağrı Evden Eve Nakliyat
Etimesgut Parke Ustası
Maraş Parça Eşya Taşıma
Etlik Boya Ustası
Batman Evden Eve Nakliyat
Karabük Şehir İçi Nakliyat
Kilis Evden Eve Nakliyat

7E9C0MichaelDC5EB said...

ADE96
Gölbaşı Parke Ustası
Gümüşhane Lojistik
Bitlis Şehir İçi Nakliyat
Bayburt Evden Eve Nakliyat
Amasya Evden Eve Nakliyat
Coinex Güvenilir mi
Burdur Parça Eşya Taşıma
Bingöl Lojistik
Bitlis Lojistik

84503VictoriaD7FCB said...

7023A
Uşak Lojistik
Muş Şehirler Arası Nakliyat
Ünye Yol Yardım
Batman Lojistik
Kastamonu Parça Eşya Taşıma
Ünye Evden Eve Nakliyat
Adıyaman Lojistik
Urfa Evden Eve Nakliyat
Batman Şehirler Arası Nakliyat

DE1A4Cody066FB said...

7D943
Çorum Evden Eve Nakliyat
seo paketleri
buy clenbuterol
Karaman Şehirler Arası Nakliyat
Uşak Lojistik
Ankara Şehirler Arası Nakliyat
Zonguldak Şehirler Arası Nakliyat
Gate io Güvenilir mi
boldenone

F269CDana7E937 said...

814A9
Ünye Petek Temizleme
Nevşehir Evden Eve Nakliyat
Çerkezköy Çelik Kapı
Eskişehir Evden Eve Nakliyat
Sivas Evden Eve Nakliyat
Gölbaşı Fayans Ustası
Elazığ Evden Eve Nakliyat
Ankara Asansör Tamiri
Çanakkale Evden Eve Nakliyat

9B1DESolomon3D187 said...

15D45
binance referans

701E4Curtis09574 said...

509B0
düzce kızlarla rastgele sohbet
yozgat bedava sohbet siteleri
ısparta canli sohbet bedava
canli sohbet
aydın yabancı canlı sohbet
telefonda kızlarla sohbet
ordu canlı sohbet et
afyon mobil sohbet siteleri
tekirdağ bedava görüntülü sohbet sitesi

05EEERylie67553 said...

5BD10
shiba
roninchain
avax
safepal
raydium
defillama
dappradar
sushi
trust wallet

-takipci satin al---------- said...

53EB3
bybit
telegram kripto grupları
huobi
mercatox
mobil 4g proxy
canlı sohbet siteleri
bybit
canlı sohbet ucretsiz
filtre kağıdı

-Takipci-Satin-alma------ said...

D3EBE
January 2024 Calendar
canli sohbet
kizlarla canli sohbet
bitexen
ilk kripto borsası
June 2024 Calendar
August 2024 Calendar
kredi kartı ile kripto para alma
telegram kripto para grupları

Post a Comment