Search code examples
javadoublebufferedbufferstrategywindowed

BufferStrategy in windowed mode causes constant intense white screen flicker


I put this code together based on a lot of examples I found around here on stackoverflow. When I run the program the entire screen flickers intensely. I'm sure there is something simple I'm overlooking, but so far have been unable to track down a solution. I've been debugging this for a couple hours mostly with the help of online forum reading, so I figured it was time to ask the audience.

public class Screen extends JComponent {

    @Override
    public Dimension getPreferredSize(){
        Dimension tempDimension = Toolkit.getDefaultToolkit().getScreenSize();
        return tempDimension;
    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D)bufferStrategy.getDrawGraphics();
        g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
        if(game==null){
            drawSplash(g2D);
        }else{
            drawBoard(g2D);
        }
        g2D.dispose();
        bufferStrategy.show();
    }
}

If any additional code is required, I can provide it. Thank you for your help, stackoverflow!


Solution

  • To achieve the results you are getting, you either have another class which extends from Canvas or are using the BufferStrategy from the top level container. In either case, both must be visible on the screen.

    Basically, they are fighting each other, as they are two different painting algorithms. Swing, which is a passive painting algorithm, paints updates as they are needed and the BufferStrategy, which uses an active algorithm, requiring your to schedule the updates to the buffer as required.

    Both use a double buffering algorithm.

    So, you should pick one or the other...

    public class Screen extends JComponent {
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
    
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g.create();
            g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
            if (game == null) {
                drawSplash(g2D);
            } else {
                drawBoard(g2D);
            }
            g2D.dispose();
        }
    }
    

    or something like...

    public void gameEngine(BufferStrategy strategy) {
    
        // Main loop
        while (!done) {
            // Prepare for rendering the next frame
            // ...
    
            // Render single frame
            do {
                // The following loop ensures that the contents of the drawing buffer
                // are consistent in case the underlying surface was recreated
                do {
                    // Get a new graphics context every time through the loop
                    // to make sure the strategy is validated
                    Graphics2D g2D = (Graphics2D) strategy.getDrawGraphics();
    
                    // Render to graphics
                    g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //sprites overlap instead of overwrite
                    if (game == null) {
                        drawSplash(g2D);
                    } else {
                        drawBoard(g2D);
                    }
                    // Dispose the graphics
                    g2D.dispose();
    
                    // Repeat the rendering if the drawing buffer contents
                    // were restored
                } while (strategy.contentsRestored());
    
                // Display the buffer
                strategy.show();
    
                // Repeat the rendering if the drawing buffer was lost
            } while (strategy.contentsLost());
        }
    }
    

    Which was pretty much ripped from the JavaDocs for BufferStrategy

    BTW, this...

    @Override
    public Dimension getPreferredSize(){
        Dimension tempDimension = Toolkit.getDefaultToolkit().getScreenSize();
        return tempDimension;
    }
    

    is a really bad design, you are making assumptions about the state of the component which may not meet reality. You should allow the window to decide how large it ultimately wants to be, which can be achieved by using setExtendedState and passing it JFrame.MAXIMIZED_BOTH, which will take into consideration other OS elements, like the task bar or dock