Obsah
1. Pohled pod kapotu JVM – double buffering v praxi
2. Využití většího množství zadních bufferů
3. Zjištění konfigurace bufferů u všech nainstalovaných grafických subsystémů
4. První demonstrační příklad: výpis informací o předním a zadním bufferu
5. Výsledek prvního demonstračního příkladu na Windows XP s integrovanou grafickou kartou
6. Výsledek prvního demonstračního příkladu na Linuxu se samostatným grafickým akcelerátorem
7. Double buffering využívaný při vykreslování komponent knihovny Swing
9. Využití třídy BufferStrategy pro řízení double bufferingu
10. Repositář se zdrojovými kódy obou dnešních demonstračních příkladů
1. Pohled pod kapotu JVM – double buffering v praxi
V dnešní části seriálu o programovacím jazyce Java i o virtuálním stroji Javy si řekneme, jakým způsobem je možné v praxi využít takzvaný double buffering. Připomeňme si, že se tímto názvem označuje známý a v počítačové grafice již velmi dlouho využívaný postup, při němž se vykreslování grafického uživatelského rozhraní aplikace popř. prostředí hry neprovádí přímo na obrazovku, ale do pomocné bitmapy označované termínem zadní buffer (back buffer). Obrazovka, resp. přesněji řečeno bitmapa zobrazená na obrazovce a tedy viditelná uživateli, je při použití double bufferingu označována termínem přední buffer (front buffer). Vykreslování je do neviditelného zadního bufferu prováděno z toho důvodu, aby uživatel neviděl nepříjemné poblikávání obrazu při mazání/kreslení pozadí a taktéž při postupném přikreslování všech dalších grafických prvků, které mají být na obrazovce viditelné.
Po dokončení vykreslení všech grafických objektů do zadního bufferu je však nutné tento buffer učinit viditelným. To lze provést dvěma způsoby. V případě, že je zadní buffer uložen v paměti grafické karty, je většinou možné jednoduše prohodit role předního a zadního bufferu, a to velmi jednoduchou operací nevyžadující žádné přenosy dat. Tento způsob se nazývá page flipping a je samozřejmě podporován i v JVM, ovšem v mnoha případech pouze při použití exkluzivního celoobrazovkového režimu (prohození obsahu obou bufferů se typicky provádí ve chvíli takzvaného vertikálního zatemnění – vertical blank (VBLANK)).
Druhý způsob spočívá v blokovém přenosu obsahu celého zadního bufferu do bufferu předního, a to operací typu BitBlt (BLIT). Opět záleží na možnostech konkrétního grafického subsystému i na způsobu uložení zadního bufferu, zda je tato operace provedena přímo na grafickém akcelerátoru (což je samozřejmě rychlejší řešení) či zda je nutné přenášet obsah zadního bufferu do bufferu předního přes systémovou sběrnici. Poněkud předběhneme, ale už zde je zapotřebí říci, že v případě použití double bufferingu a uložení zadního bufferu v operační paměti nemusí aplikace pracovat optimálně, což typicky nastává u některých aplikací využívajících knihovnu Swing.
2. Využití většího množství zadních bufferů
V předchozí kapitole jsme si řekli, že double buffering může být realizován minimálně dvěma způsoby – operací typu BitBlt či page flippingem. Ve skutečnosti se však v některých případech nepoužívají pouze dva buffery (tj. jeden přední a jeden zadní buffer), ale dokonce i větší množství zadních bufferů. V některých hrách či dalších graficky náročných aplikacích se typicky používají tři buffery: přední (viditelný) buffer, první zadní (neviditelný) buffer s již připravenou scénou a třetí (taktéž neviditelný) buffer, do něhož se právě provádí vykreslování. Výhoda této konfigurace spočívá v tom, že aplikace může provádět vykreslování nejvyšší možnou rychlostí a nemusí čekat na synchronizaci s vertikálním zatemněním obrazu, kdy je možné a vhodné prohodit obsah předního a zadního bufferu (jen v tomto okamžiku totiž nebudou patrné rušivé elementy v obrazu – typický je horizontální „zlom“ ve chvíli, kdy se obraz přenáší bez synchronizace).
3. Zjištění konfigurace bufferů u všech nainstalovaných grafických subsystémů
Pro vývoj aplikací, které mají využívat možnosti grafického subsystému optimálním způsobem, je nutné zjistit, kolik zadních bufferů lze použít a jakým způsobem je vlastně double buffering na daném systému implementován (či zda je vůbec podporován). K tomuto účelu lze v Javě použít metodu GraphicsConfiguration.getBufferCapabilities() vracející objekt typu BufferCapabilities. Ve třídě BufferCapabilities najdeme několik důležitých metod:
# | Metoda | Význam |
---|---|---|
1 | boolean isMultiBufferAvailable() | vrací pravdivostní hodnotu true, když existuje více než jeden zadní buffer |
2 | boolean isPageFlipping() | vrací pravdivostní hodnotu true, když je podporováno prohazování předního a zadního bufferu |
3 | boolean isFullScreenRequired() | vrací pravdivostní hodnotu true, když je nutné se přepnout do exkluzivního režimu pro využití double bufferingu |
Dále je možné s využitím dvou metod BufferCapabilities.getFrontBufferCapabilities() a BufferCapabilities.getBackBufferCapabilities() zjistit přesnější informace o předním a zadním bufferu:
# | Metoda | Význam |
---|---|---|
1 | boolean isAccelerated() | vrací pravdivostní hodnotu true, když je práce s bufferem realizována na grafickém akcelerátoru |
2 | boolean isTrueVolatile() | vrací pravdivostní hodnotu true, když se může obsah bufferu ztratit například při přepnutí aplikace |
Obecně platí, že volatilní budou pouze ty buffery, které jsou současně akcelerované; opačná implikace však nemusí být splněna, což znamená, že lze mít nevolatilní a současně i akcelerované buffery, což je z hlediska programátora ideální situace.
4. První demonstrační příklad: výpis informací o předním a zadním bufferu
Metody pro zjištění schopností grafického subsystému, které byly stručně popsány v předchozí kapitole, budou použity v dnešním prvním demonstračním příkladu nazvaném GraphicsDevicesTest2, jenž je založen na starším příkladu GraphicsDevicesTest. Tento příklad po svém překladu a spuštění nejdříve zjistí, jaká grafická zařízení jsou na daném operačním systému dostupná a posléze vypíše jak základní konfiguraci každého zařízení, tak i podporované celoobrazovkové grafické režimy. Kromě toho jsou vypsány i informace o podpoře double bufferingu, především to, zda je podporováno větší množství zadních bufferů, zda je podporován rychlý page flipping (prohození funkcí předního a zadního bufferu) i to, zda je nutné pro využití double bufferingu provést přepnutí do exkluzivního celoobrazovkového grafického režimu. Dále se vypíšou i základní informace o předním i zadním bufferu – zejména zda je jejich vykreslování řešeno grafickým akcelerátorem a zde se může obsah bufferu v některém okamžiku poškodit (volatile).
Následuje výpis celého zdrojového kódu dnešního prvního demonstračního příkladu:
import java.awt.BufferCapabilities; import java.awt.DisplayMode; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.ImageCapabilities; import java.awt.GraphicsDevice.WindowTranslucency; /** * Vypis zakladnich vlastnosti grafickych zarizeni, ktere lze vyuzit primo * z Javy. Vypisou se i informace o prednim a zadnim bufferu. */ public class GraphicsDevicesTest2 { /** * Vstupni bod do tohoto testu. */ public static void main(String[] args) { GraphicsDevice[] graphicsDevices = getScreenGraphicsDevices(); printInfoAboutEachGraphicsDevice(graphicsDevices); } /** * Ziska pole se vsemi dostupnymi grafickymi zarizenimi. * * @return pole se vsemi dostupnymi grafickymi zarizenimi */ private static GraphicsDevice[] getScreenGraphicsDevices() { GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices(); return graphicsDevices; } /** * Vypise informaci o vsech grafickych zarizenich predanych v parametru * graphicsDevices. * * @param graphicsDevices * pole se vsemi dostupnymi grafickymi zarizenimi */ private static void printInfoAboutEachGraphicsDevice(GraphicsDevice[] graphicsDevices) { for (int j = 0; j < graphicsDevices.length; j++) { GraphicsDevice graphicsDevice = graphicsDevices[j]; printGraphicsDeviceInfo(graphicsDevice); } } /** * Vypise informaci o grafickem zarizeni predanem v parametru * graphicsDevice. * * @param graphicsDevice * objekt reprezentujici dostupne graficke zarizeni. */ private static void printGraphicsDeviceInfo(GraphicsDevice graphicsDevice) { System.out.println("Device ID: " + graphicsDevice.getIDstring()); System.out.println("Type: " + getDeviceType(graphicsDevice)); System.out.println("Accelerated memory size: " + graphicsDevice.getAvailableAcceleratedMemory()); System.out.println("Display change supported: " + graphicsDevice.isDisplayChangeSupported()); System.out.println("Full screen supported: " + graphicsDevice.isFullScreenSupported()); printTransparencyInfo(graphicsDevice); printBuffersConfiguration(graphicsDevice); System.out.println("Graphics modes:"); DisplayMode[] modes = graphicsDevice.getDisplayModes(); printAvailableDisplayModes(modes); System.out.println(); } /** * Vypis zda zarizeni podporuje pruhlednost a/nebo prusvitnost. * * @param graphicsDevice * objekt reprezentujici dostupne graficke zarizeni. */ private static void printTransparencyInfo(GraphicsDevice graphicsDevice) { System.out.println("Transparency/translucency support:"); System.out.println(" Per pixel transparency: " + graphicsDevice.isWindowTranslucencySupported(WindowTranslucency.PERPIXEL_TRANSPARENT)); System.out.println(" Per pixel translucency: " + graphicsDevice.isWindowTranslucencySupported(WindowTranslucency.PERPIXEL_TRANSLUCENT)); System.out.println(" Per window translucency: " + graphicsDevice.isWindowTranslucencySupported(WindowTranslucency.TRANSLUCENT)); } /** * Vypis informace o bufferech (prednim a zadnim bufferu atd.). * * @param graphicsDevice * objekt reprezentujici dostupne graficke zarizeni. */ private static void printBuffersConfiguration(GraphicsDevice graphicsDevice) { // ziskat vsechny dostupne graficke konfigurace GraphicsConfiguration[] graphicsConfigurations = graphicsDevice.getConfigurations(); for (GraphicsConfiguration graphicsConfiguration : graphicsConfigurations) { System.out.println("Graphics configuration: " + graphicsConfiguration.toString()); BufferCapabilities bufferCapabilities = graphicsConfiguration.getBufferCapabilities(); System.out.println(" Buffers configuration:"); System.out.println(" Multi buffer available: " + bufferCapabilities.isMultiBufferAvailable()); System.out.println(" Page flipping supported: " + bufferCapabilities.isPageFlipping()); System.out.println(" Full screen required: " + bufferCapabilities.isFullScreenRequired()); // moznosti predniho bufferu ImageCapabilities frontBuffer = bufferCapabilities.getFrontBufferCapabilities(); // moznosti zadniho bufferu ImageCapabilities backBuffer = bufferCapabilities.getBackBufferCapabilities(); System.out.println(" Front buffer accelerated: " + frontBuffer.isAccelerated()); System.out.println(" Front buffer volatile: " + frontBuffer.isTrueVolatile()); System.out.println(" Back buffer accelerated: " + backBuffer.isAccelerated()); System.out.println(" Back buffer volatile: " + backBuffer.isTrueVolatile()); } } /** * Vrati typ grafickeho zarizeni. * * @param graphicsDevice * objekt reprezentujici dostupne graficke zarizeni. * @return typ zarizeni (textova reprezentace) */ private static String getDeviceType(GraphicsDevice graphicsDevice) { switch (graphicsDevice.getType()) { case GraphicsDevice.TYPE_RASTER_SCREEN: return "raster screen"; case GraphicsDevice.TYPE_PRINTER: return "printer"; case GraphicsDevice.TYPE_IMAGE_BUFFER: return "image buffer"; default: // nemelo by nastat return "???"; } } /** * Vypise dostupne graficke rezimy a jejich zakladni vlastnosti. * * @param modes pole s grafickymi rezimy pro dane zarizeni. */ private static void printAvailableDisplayModes(DisplayMode[] modes) { for (DisplayMode mode : modes) { final int width = mode.getWidth(); final int height = mode. getHeight(); final int bpp = mode.getBitDepth(); final int refreshRate = mode.getRefreshRate(); System.out.println(" " + width + "x" + height + "@" + bpp + " bpp\t(" + refreshRate + " Hz)"); } } }
5. Výsledek prvního demonstračního příkladu na Windows XP s integrovanou grafickou kartou
Podívejme se nyní na výsledky získané po spuštění demonstračního příkladu GraphicsDevicesTest2 na několika počítačích vybavených různými operačními systémy a samozřejmě i rozdílnými grafickými kartami. Prvním počítačem je již několikrát zmiňovaný starobylý (více než desetiletý) notebook s nainstalovaným Oracle JDK7 a neaktualizovaným :-) systémem Microsoft Windows XP vybavený čipsetem Intel 855 GME. Zajímavé je, že na tomto systému není (alespoň v aktuální konfiguraci) podporován page flipping, což znamená, že double buffering zde bude realizován fyzickým přenosem dat. Taktéž zde není dostupné větší množství zadních bufferů, což může způsobit problémy při běhu některých aplikací vyžadujících například dva zadní buffery:
Device ID: \Display0 Type: raster screen Accelerated memory size: -1 Display change supported: false Full screen supported: true Transparency/translucency support: Per pixel transparency: true Per pixel translucency: true Per window translucency: true Graphics configuration: sun.awt.Win32GraphicsConfig@2a15cd[dev=Win32GraphicsDevice[screen=0],pixfmt=0] Buffers configuration: Multi buffer available: false Page flipping supported: false Full screen required: false Front buffer accelerated: false Front buffer volatile: false Back buffer accelerated: false Back buffer volatile: false Graphics modes: 320x200@8 bpp (70 Hz) 320x200@16 bpp (70 Hz) 320x200@32 bpp (70 Hz) 320x240@8 bpp (70 Hz) 320x240@16 bpp (70 Hz) 320x240@32 bpp (70 Hz) 400x300@8 bpp (70 Hz) 400x300@16 bpp (70 Hz) 400x300@32 bpp (70 Hz) 512x384@8 bpp (70 Hz) 512x384@16 bpp (70 Hz) 512x384@32 bpp (70 Hz) 640x480@8 bpp (60 Hz) 640x400@8 bpp (70 Hz) 800x600@8 bpp (60 Hz) 1024x768@8 bpp (60 Hz) 640x480@16 bpp (60 Hz) 800x600@16 bpp (60 Hz) 1024x768@16 bpp (60 Hz) 640x480@32 bpp (60 Hz) 800x600@32 bpp (60 Hz) 1024x768@32 bpp (60 Hz) 640x400@16 bpp (70 Hz) 640x400@32 bpp (70 Hz)
6. Výsledek prvního demonstračního příkladu na Linuxu se samostatným grafickým akcelerátorem
Při spuštění demonstračního příkladu GraphicsDevicesTest2 na počítači s OpenJDK (Linux, X Window) a samostatným grafickým akcelerátorem získáme poněkud odlišné výsledky. Grafická karta zde podporuje více grafických konfigurací a navíc – což je mnohem důležitější – podporuje i rychlý page flipping, což je ostatně patrné i z následujícího výpisu:
Device ID: :0.0 Type: raster screen Accelerated memory size: -1 Display change supported: false Full screen supported: true Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x21] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x23] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x24] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x25] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x26] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x27] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x28] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x29] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2a] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2b] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2c] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2d] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2e] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x2f] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x30] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0x31] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics configuration: X11GraphicsConfig[dev=X11GraphicsDevice[screen=0],vis=0xa7] Buffers configuration: Multi buffer available: false Page flipping supported: true Full screen required: false Front buffer accelerated: true Front buffer volatile: false Back buffer accelerated: true Back buffer volatile: false Graphics modes: 1280x1024@-1 bpp (50 Hz) 1280x1024@-1 bpp (51 Hz) 1280x960@-1 bpp (52 Hz) 1152x864@-1 bpp (53 Hz) 1152x864@-1 bpp (54 Hz) 1152x864@-1 bpp (55 Hz) 1152x864@-1 bpp (56 Hz) 1024x768@-1 bpp (57 Hz) 1024x768@-1 bpp (58 Hz) 1024x768@-1 bpp (59 Hz) 960x600@-1 bpp (60 Hz) 960x540@-1 bpp (61 Hz) 840x525@-1 bpp (62 Hz) 840x525@-1 bpp (63 Hz) 840x525@-1 bpp (64 Hz) 832x624@-1 bpp (65 Hz) 800x600@-1 bpp (66 Hz) 800x600@-1 bpp (67 Hz) 800x600@-1 bpp (68 Hz) 800x600@-1 bpp (69 Hz) 800x600@-1 bpp (70 Hz) 800x600@-1 bpp (71 Hz) 800x512@-1 bpp (72 Hz) 720x450@-1 bpp (73 Hz) 680x384@-1 bpp (74 Hz) 680x384@-1 bpp (75 Hz) 640x512@-1 bpp (76 Hz) 640x512@-1 bpp (77 Hz) 640x480@-1 bpp (78 Hz) 640x480@-1 bpp (79 Hz) 640x480@-1 bpp (80 Hz) 640x480@-1 bpp (81 Hz) 576x432@-1 bpp (82 Hz) 576x432@-1 bpp (83 Hz) 576x432@-1 bpp (84 Hz) 576x432@-1 bpp (85 Hz) 512x384@-1 bpp (86 Hz) 512x384@-1 bpp (87 Hz) 512x384@-1 bpp (88 Hz) 416x312@-1 bpp (89 Hz) 400x300@-1 bpp (90 Hz) 400x300@-1 bpp (91 Hz) 400x300@-1 bpp (92 Hz) 400x300@-1 bpp (93 Hz) 320x240@-1 bpp (94 Hz) 320x240@-1 bpp (95 Hz) 320x240@-1 bpp (96 Hz)
Poznámka: o tom, že v některých případech (typicky na Linuxu s X Window) se nevrátí kompletní informace o podporovaných grafických režimech, popř. se vrátí obecnější informace, než jakou by programátor mnohdy potřeboval znát, jsme se již zmiňovali. Ve výše zobrazeném výpisu se například nevrátila přesná informace o bitové hloubce, ale hodnota –1, která odpovídá konstantě BIT_DEPTH_MULTI.
7. Double buffering využívaný při vykreslování komponent knihovny Swing
Double buffering se používá i v knihovně Swing při vykreslování všech komponent, tj. ovládacích prvků grafického uživatelského rozhraní. Díky použití double bufferingu se vykreslování i složitějších komponent, například tabulek či stromů, obejde bez nepříjemného poblikávání, a to i v případě, že je Javovská aplikace spuštěna na pomalejších počítačích. Ovšem právě na méně výkonných strojích může double buffering využívaný knihovnou Swing způsobit jiné problémy – pomalou odezvu na příkazy zadávané uživatelem. S určitou (ale ne moc velkou) nadsázkou je dokonce možné říci, že double buffering ve Swingu nemalou měrou přispěl k tomu, že je Java považována za nevhodnou platformu pro vývoj aplikací pro desktop. Proč tomu tak je? Již v páté kapitole jsme viděli, že na některých typech počítačů a operačních systémů nemusí být podporováno hardwarově urychlené přepínání bufferů (page flipping) ani rychlý blokový přenos pixelů ze zadního bufferu do bufferu předního.
V tomto případě může být double buffering aplikovaný na všechna okna a všechny dialogy skutečně kontraproduktivní, protože sice zamezí vzniku vizuálních chyb při překreslování okna/dialogu, ovšem veškeré reakce na příkazy zadávané uživatelem (stisk tlačítka, výběr jedné položky z listboxu, rozbalení stromu atd.) budou opožděny. Navíc se zvýší spotřeba operační paměti, protože se zadní buffer bude vytvářet pro každé okno či dialog, což například pro hlavní maximalizované okno o velikosti 1280×1024 pixelů může vést k alokaci dalších pěti megabajtů (ty je navíc nutné přenášet po sběrnici). Nicméně double buffering je možné vypnout, a to i selektivně pro každou komponentu. Praktický význam však má zapnutí či vypnutí double bufferingu jen při aplikaci na rootPane okna či dialogu. Zapnutí či vypnutí double bufferingu zajistí metoda JComponent.setDoubleBuffered(boolean), na aktuální nastavení se lze kdykoli dotázat metodou JComponent.isDoubleBuffered().
8. Druhý demonstrační příklad: zapnutí a vypnutí double bufferingu při práci s komponentami knihovny Swing
To, jakým způsobem se může projevit vypnutí double bufferingu v knihovně Swing, si ukážeme na dnešním druhém demonstračním příkladu. Jedná se o dialog obsahující textový editor se zobrazenou HTML stránkou a zavírací tlačítko. HTML stránka je poměrně rozsáhlá (konkrétně se jedná o neupravený předchozí díl tohoto seriálu), což znamená, že Swing bude při překreslování celého dialogu volat velké množství svých interních metod pro zobrazení řetězce, výpočet mezer mezi znaky atd. S využitím atributu DOUBLE_BUFFERING je možné povolit či naopak zakázat double buffering celého dialogu. Změna se projeví jak při editaci HTML stránky (zde je však rozdíl viditelný jen na skutečně velmi pomalých strojích), tak zejména při zvětšování okna v případě, že je obsah okna neustále překreslován (je nutné správně nastavit správce oken, aby se kontinuální překreslování skutečně provádělo). Pokud je double buffering zapnutý, bude sice překreslování dokonalé a bez vizuálních vad, ovšem okno bude překresleno znatelně pomaleji, při vypnutém double bufferingu budou naproti tomu viditelné vizuální chyby a poblikávání částí dialogu.
Následuje výpis zdrojového kódu dnešního druhého demonstračního příkladu:
import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JScrollPane; import javax.swing.WindowConstants; /** * Jednoduchy dialog pro testovani moznosti double bufferingu. * * @author Pavel Tisnovsky */ public class TestDialog extends JDialog { /** * Generated serial version UID. */ private static final long serialVersionUID = -6415126729773286780L; /** * Povoleni ci naopak zakaz double bufferingu. */ private static final boolean DOUBLE_BUFFERING = false; /** * Konstruktor testovaciho dialogu. * * @throws IOException */ public TestDialog() throws IOException { setPreferredSize(new java.awt.Dimension(1024, 768)); /* nastaveni double bufferingu */ JComponent rootPane = this.rootPane; System.out.println("double buffering enabled by default: " + rootPane.isDoubleBuffered()); rootPane.setDoubleBuffered(DOUBLE_BUFFERING); addTextArea(); addCloseButton(); this.pack(); } /** * Pridani editoru s nactenou HTML strankou do dialogu. * * @throws FileNotFoundException * @throws IOException */ private void addTextArea() throws FileNotFoundException, IOException { StringBuilder htmlPage = new StringBuilder(); readHtmlFile(htmlPage); this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); JEditorPane textArea = new JEditorPane("text/html", htmlPage.toString()); textArea.setEditable(true); this.add(new JScrollPane(textArea), BorderLayout.CENTER); } /** * Pridani zaviraciho tlacitka do dialogu. */ private void addCloseButton() { JButton closeButton = new JButton("Close"); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dispose(); } }); this.add(closeButton, BorderLayout.SOUTH); } /** * Nacteni HTML stranky. * * @param htmlPage * @throws FileNotFoundException * @throws IOException */ private void readHtmlFile(StringBuilder htmlPage) throws FileNotFoundException, IOException { final FileInputStream fileInputStream = new FileInputStream("java109.html"); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); BufferedReader reader = new BufferedReader(inputStreamReader); String line; while ((line = reader.readLine()) != null) { htmlPage.append(line); } reader.close(); } /** * Zobrazeni testovaciho dialogu. * * @param args * @throws IOException */ public static void main(String[] args) throws IOException { new TestDialog().setVisible(true); } }
9. Využití třídy BufferStrategy pro řízení double bufferingu
Knihovna Swing sice provádí double buffering automaticky, ovšem při vykreslování prostředí hry či v dalších situacích je většinou nutné přední a zadní buffer řídit programově. K tomu slouží třída nazvaná poněkud zvláštně BufferStrategy. Instance této třídy se vytvoří zavoláním java.awt.Canvas.createBufferStrategy(int) popř. (což je asi méně časté) zavoláním metody java.awt.Window.createBufferStrategy(int). Celočíselná hodnota, která se těmto metodám předává, reprezentuje celkový počet bufferů. Přitom je zaručeno, že se daný počet bufferů skutečně vytvoří, ale v závislosti na možnostech konkrétního počítače nemusí být podporován page flipping a dokonce ani akcelerované přenášení obsahu zadního bufferu do bufferu předního. Na to je zapotřebí dát pozor, protože na mnoha systémech není možné vytvořit více než dva akcelerované buffery a žádost o vytvoření většího množství bufferů zde tedy povede spíše ke snížení celkového výkonu.
Jakmile je nastaven double buffering, změní se i vykreslovací rutina, protože ta již nebude provádět vykreslování přímo na canvas, ale do zadního bufferu:
BufferStrategy bufferStrategy = canvas.getBufferStrategy(); while (!done) { Graphics graphics; try { graphics = bufferStrategy.getDrawGraphics(); ... zde se provádí vykreslování ... ... s využitím objektu "graphics" ... } finally { graphics.dispose(); } bufferStrategy.show(); }
Prakticky si tuto možnost otestujeme příště na dvojici demonstračních příkladů.
10. Repositář se zdrojovými kódy obou dnešních demonstračních příkladů
Následují odkazy na zdrojové kódy uložené do Mercurial repositáře. V následující tabulce najdete linky na prozatím nejnovější verze obou dnes popsaných demonstračních příkladů:
# | Zdrojový soubor/skript | Umístění souboru v repositáři |
---|---|---|
1 | GraphicsDevicesTest2.java | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/086ce0554efb/jvm/gfx/GraphicsDevicesTest2.java |
2 | TestDialog.java | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/086ce0554efb/jvm/gfx/TestDialog.java |
3 | java109.html (datový soubor) | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/086ce0554efb/jvm/gfx/java109.html |
11. Odkazy na Internetu
- Class BufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html - Class Graphics
http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics.html - Double Buffering and Page Flipping
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html - BufferStrategy and BufferCapabilities
http://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html - Java:Tutorials:Double Buffering
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering - Double buffer in standard Java AWT
http://www.codeproject.com/Articles/2136/Double-buffer-in-standard-Java-AWT - Java 2D: Hardware Accelerating – Part 1 – Volatile Images
http://www.javalobby.org/forums/thread.jspa?threadID=16840&tstart=0 - Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
http://www.javalobby.org/java/forums/t16867.html - How does paintComponent work?
http://stackoverflow.com/questions/15544549/how-does-paintcomponent-work - A Swing Architecture Overview
http://www.oracle.com/technetwork/java/architecture-142923.html - Class javax.swing.JComponent
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html - Class java.awt.Component
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html - Class java.awt.Component.BltBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.BltBufferStrategy.html - Class java.awt.Component.FlipBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.FlipBufferStrategy.html - Metoda java.awt.Component.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#isDoubleBuffered() - Metoda javax.swing.JComponent.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#isDoubleBuffered() - Metoda javax.swing.JComponent.setDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setDoubleBuffered(boolean) - Javadoc – třída GraphicsDevice
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsDevice.html - Javadoc – třída GraphicsEnvironment
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsEnvironment.html - Javadoc – třída GraphicsConfiguration
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsConfiguration.html - Javadoc – třída DisplayMode
http://docs.oracle.com/javase/7/docs/api/java/awt/DisplayMode.html - Lesson: Full-Screen Exclusive Mode API
http://docs.oracle.com/javase/tutorial/extra/fullscreen/ - Full-Screen Exclusive Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html - Display Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/displaymode.html - Using the Full-Screen Exclusive Mode API in Java
http://www.developer.com/java/other/article.php/3609776/Using-the-Full-Screen-Exclusive-Mode-API-in-Java.htm - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the World's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html