Hlavní navigace

Pohled pod kapotu JVM – double buffering v praxi

31. 12. 2013
Doba čtení: 24 minut

Sdílet

V dnešní části seriálu o programovacím jazyce Java i o virtuálním stroji Javy si popíšeme a na několika demonstračních příkladech ukážeme, jakým způsobem je možné při vykreslování využívat double buffering, což může vést ke značnému urychlení celé graficky náročné aplikace či k zamezení nežádoucích vizuálních chyb.

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

8. Druhý demonstrační příklad: zapnutí a vypnutí double bufferingu při práci s komponentami 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ů

11. Odkazy na Internetu

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.get­BufferCapabilities() 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.getFron­tBufferCapabilities()BufferCapabilities.getBac­kBufferCapabilities() 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.setDoubleBuffe­red(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.createBuf­ferStrategy(int) popř. (což je asi méně časté) zavoláním metody java.awt.Window.createBuf­ferStrategy(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:

root_podpora

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ů:

11. Odkazy na Internetu

  1. Class BufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/i­mage/BufferStrategy.html
  2. Class Graphics
    http://docs.oracle.com/ja­vase/1.5.0/docs/api/java/aw­t/Graphics.html
  3. Double Buffering and Page Flipping
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/doublebuf.html
  4. BufferStrategy and BufferCapabilities
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/bufferstrategy.html
  5. Java:Tutorials:Double Buffering
    http://content.gpwiki.org/in­dex.php/Java:Tutorials:Dou­ble_Buffering
  6. Double buffer in standard Java AWT
    http://www.codeproject.com/Ar­ticles/2136/Double-buffer-in-standard-Java-AWT
  7. Java 2D: Hardware Accelerating – Part 1 – Volatile Images
    http://www.javalobby.org/fo­rums/thread.jspa?threadID=16840&tstar­t=0
  8. Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
    http://www.javalobby.org/ja­va/forums/t16867.html
  9. How does paintComponent work?
    http://stackoverflow.com/qu­estions/15544549/how-does-paintcomponent-work
  10. A Swing Architecture Overview
    http://www.oracle.com/technet­work/java/architecture-142923.html
  11. Class javax.swing.JComponent
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html
  12. Class java.awt.Component
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html
  13. Class java.awt.Component.BltBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.BltBufferStrategy.html
  14. Class java.awt.Component.FlipBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.FlipBufferStrategy­.html
  15. Metoda java.awt.Component.isDoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html#isDoubleBuffe­red()
  16. Metoda javax.swing.JComponent.is­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#isDouble­Buffered()
  17. Metoda javax.swing.JComponent.set­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#setDouble­Buffered(boolean)
  18. Javadoc – třída GraphicsDevice
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsDevice.html
  19. Javadoc – třída GraphicsEnvironment
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsEnvironment.html
  20. Javadoc – třída GraphicsConfiguration
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsConfiguration.html
  21. Javadoc – třída DisplayMode
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Dis­playMode.html
  22. Lesson: Full-Screen Exclusive Mode API
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/
  23. Full-Screen Exclusive Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/exclusivemode.html
  24. Display Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/displaymode.html
  25. Using the Full-Screen Exclusive Mode API in Java
    http://www.developer.com/ja­va/other/article.php/3609776/U­sing-the-Full-Screen-Exclusive-Mode-API-in-Java.htm
  26. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  27. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  28. MultiMedia eXtensions
    http://softpixel.com/~cwrig­ht/programming/simd/mmx.phpi
  29. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  30. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  31. Intel corporation: Extending the World's Most Popular Processor Architecture
    http://download.intel.com/techno­logy/architecture/new-instructions-paper.pdf
  32. SIMD architectures:
    http://arstechnica.com/ol­d/content/2000/03/simd.ar­s/
  33. GC safe-point (or safepoint) and safe-region
    http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html
  34. Safepoints in HotSpot JVM
    http://blog.ragozin.info/2012/10/sa­fepoints-in-hotspot-jvm.html
  35. Java theory and practice: Synchronization optimizations in Mustang
    http://www.ibm.com/develo­perworks/java/library/j-jtp10185/
  36. How to build hsdis
    http://hg.openjdk.java.net/jdk7/hot­spot/hotspot/file/tip/src/sha­re/tools/hsdis/README
  37. Java SE 6 Performance White Paper
    http://www.oracle.com/technet­work/java/6-performance-137236.html
  38. Lukas Stadler's Blog
    http://classparser.blogspot­.cz/2010/03/hsdis-i386dll.html
  39. How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
    http://dropzone.nfshost.com/hsdis.htm
  40. PrintAssembly
    https://wikis.oracle.com/dis­play/HotSpotInternals/Prin­tAssembly
  41. The Java Virtual Machine Specification: 3.14. Synchronization
    http://docs.oracle.com/ja­vase/specs/jvms/se7/html/jvms-3.html#jvms-3.14
  42. The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4
  43. The Java Virtual Machine Specification: 17.4. Memory Model
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.4
  44. The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.7
  45. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  46. ASM Home page
    http://asm.ow2.org/
  47. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  48. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  49. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  50. BCEL Home page
    http://commons.apache.org/bcel/
  51. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  52. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  53. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  54. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  55. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  56. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  57. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  58. Javassist
    http://www.jboss.org/javassist/
  59. Byteman
    http://www.jboss.org/byteman
  60. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  61. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  62. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  63. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  64. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  65. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  66. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  67. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  68. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  69. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  70. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  71. Cobertura
    http://cobertura.sourceforge.net/
  72. jclasslib bytecode viewer
    http://www.ej-technologies.com/products/jclas­slib/overview.html

Byl pro vás článek přínosný?