渲染图像闪烁,但120 fps

时间:2014-07-30 13:01:33

标签: java performance rendering

我的游戏引擎正在JFrame内的Canvas上绘制平铺地图。我正在使用Bufferstrategy来获得更平滑的渲染,并尝试在程序内的几个点提升性能,最终得到ca.每秒120帧,用黑色填充1920 * 1080窗口,在顶部绘制一些等距的512 * 256个瓷砖。移动地图时,瓷砖之间会出现小黑线。我认为它们的出现是因为一些瓷砖已经移动到新位置,但是当放在屏幕上时,图像还没有完成。但我真的没有解决方案,我也不确定我是否正确。
我应该知道我在我的程序中使用两个线程,一个线程调用更新和渲染方法,另一个线程当前只是获取用户输入来移动相机位置。此外,在一些推广活动中,一切都很顺利,而在其他一些场合,它的表现非常糟糕。

以下是窗口创建和渲染的一些代码:

private final void createWindow(){
    frame = new JFrame(title);
    frame.setMinimumSize(size);
    frame.setMaximumSize(size);
    frame.setPreferredSize(size);

    display = new Canvas();
    display.setMinimumSize(size);
    display.setMaximumSize(size);
    display.setPreferredSize(size);

    image = ImageLoader.boostPerformance(new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_RGB));

    graphics = (Graphics2D) image.getGraphics();

    frame.add(display);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    frame.requestFocusInWindow();
}

public final void renderToScreen(){
    BufferStrategy bs = display.getBufferStrategy();
    if(bs==null){
        display.createBufferStrategy(bufferCount);
        return;
    }

    graphicsToScreen = (Graphics2D) bs.getDrawGraphics();

    graphicsToScreen.drawImage(image, 0, 0, display.getWidth(), display.getHeight(), null);
    graphicsToScreen.dispose();
    bs.show();
}

我在其他类中使用Graphics2D图形对象来绘制图块。

public void render(Graphics2D g) {
    g.setColor(Color.black);
    g.fillRect(0, 0, getWidth(), getHeight());

    tilemap.render(g);

    g.setColor(Color.red);
    g.drawString(String.valueOf(Main.actualFps), 30, 30);

}

上面是使用我提到的图形对象的另一个类中的render方法。 在这里你可以看到瓷砖地图渲染方法:

private final void renderIsometric(Graphics2D g){

    for(int x = 0; x < 4; x++){
        for(int y = 3; y >= 0; y--){
            if(x > tilesX - 1|| y > tilesY - 1)break;
            if(x < 0 || y < 0)continue;
            if(map[x][y] == null)continue;

            g.drawImage(map[x][y].getImage(), (int)orthoToIso(x, y).x - cameraPosX, (int)orthoToIso(x, y).y - cameraPosY, null);
        }
    }
}

最后这里是主循环,以确保fps计数器正常工作,120 fps的值是正确的:

while(running){
        long now = System.nanoTime();
        double ns = 1000000000.0 / preferredUps;
        delta += (now - lastTime) / ns;
        lastTime = now;
        while(delta >= 1){
            //call the update method 
            update();
            updates++;
            delta--;
        }
        //call the render method 
        render();
        frames++;

        //refresh the actualUps and actualFps once every second
        if(System.currentTimeMillis() - timer > 1000){
            timer+=1000;
            actualUps = updates;
            actualFps = frames;
            updates = 0;
            frames = 0;
        }
    }

如何摆脱这些微小的滞后和闪烁问题?另外我一直想知道通过LWJGL而不是Java2D使用OpenGL会带来更好的结果吗?


谢谢你的帮助!

2 个答案:

答案 0 :(得分:0)

你看到的很可能没有闪烁严厉的说话。它的撕裂编织工件。它是更新屏幕而不同步屏幕刷新的垂直同步的自然结果。

窗口系统一般是为了尊重v-sync(它们有利于性能,因为这些效果在普通用户界面中并不是非常明显 - 或者很重要)。据我所知,没有办法与(J)帧同步(你可能会用JNI破解某些东西,要求操作系统告诉你栅格光束何时位于帧的顶部,但那是在点旁边)

同样以120fps渲染实际上可能会使问题更糟,除非显示设备也会在120hz刷新 - 当你渲染的帧多于设备实际显示的时候强制设备显示来自多个帧的图形(例如,屏幕上半部分的帧1,下半部分的帧2)。这将始终导致具有移动物体的可移植物品。

在纯Java中有一种方法,那就是全屏模式(等待v-sync,因此你的渲染率受限于并且由刷新率控制显示设备)。

你可以试试openGL,但我不知道它是否会在窗口模式下渲染时同步。

改善目标的第一步是渲染速度比显示设备显示的速度快(例如60hz屏幕最大60 fps)。

答案 1 :(得分:0)

如果您的应用程序不是时间关键,您可以使用双缓冲策略解决闪烁问题。

基本上,您正在合成的图像被复制到辅助的屏幕外图像缓冲区,该缓冲区不会渲染此帧,而是渲染到下一帧。您可以使用一些技巧让Swing组件自动为您提供双缓冲,另请参阅:

https://gamedev.stackexchange.com/questions/30921/thread-safe-double-buffering

Double buffered image example in Jpanel

双缓冲的缺点是你引入了一帧延迟,这意味着你的输入只会导致比没有双缓冲的情况晚一帧的可见变化。在120hz,这可能不是一个大问题,但仅仅是为了完整起见..