【问题标题】:SDL2 tilemap - too slowSDL2 tilemap - 太慢了
【发布时间】:2013-09-06 15:46:14
【问题描述】:

我正在使用 SDL2 编写一个在每一帧都显示 tilemap 的游戏,但是性能太慢了。我写了一个小程序来隔离问题。考虑“temp.bmp”是一个 16x16 的图像。

#include <stdio.h>

#include "SDL2/SDL.h"
#include "SDL2/SDL_timer.h"
#include "SDL2/SDL_image.h"

int main()
{
    SDL_Window* win;
    SDL_Renderer* ren;
    int x, y;

    SDL_Init(SDL_INIT_VIDEO);
    SDL_CreateWindowAndRenderer(800, 600, 0, &win, &ren);
    SDL_Surface* sf = IMG_Load("temp.bmp");
    SDL_Texture* tx = SDL_CreateTextureFromSurface(ren, sf);

    for(;;) {
        Uint32 t = SDL_GetTicks();
        for(x=0; x<800; x+=16) {
            for(y=0; y<600; y+=16) {
                SDL_Rect src = { 0, 0, 16, 16 };
                SDL_Rect dst = { x, y, 16, 16 };
                SDL_RenderCopy(ren, tx, &src, &dst);
            }
        }
        SDL_RenderPresent(ren);
        printf("%ld ms\n", SDL_GetTicks() - t);
    }
}

运行程序,我看到渲染一帧大约需要 16 毫秒。这正好是 60 FPS (1000/60),没有给游戏逻辑留下空间。另外,我正在一台速度非常快的计算机上运行它。

我很确定我使用了错误的策略,但我不确定什么是正确的。也许创建一个大纹理并减少更新频率是可行的方法,但我找不到任何关于如何将一个纹理复制到另一个纹理的文档。

那么,我该如何提高 tilemap 的性能呢?

【问题讨论】:

  • 您能否在SDL_RenderPresent之前添加一个性能指标,以了解您之前是否在此浪费了时间。
  • 我不知道 SDL2,但渲染可能受到 vsync 的限制?你试过在全屏模式下运行吗?
  • 据我所知,这些标志可以被视频驱动程序覆盖。也许SDL_GetRendererInfo 对你有帮助。
  • 你知道你是用显卡还是只用你的CPU(后面是用opengl吗?)
  • 测试 VSYNC 是否被代码、GFX 卡/驱动程序或其他东西锁定的最佳方法是仅绘制单个图块。为此,您应该获得超过 100+ fps 的速度。如果它保持在 60,那么您可以确定 VSYNC 已开启。

标签: c sdl sdl-2


【解决方案1】:

this 页面上,它提到 SDL_RENDERER_PRESENTVSYNC 标志表示您已同步到刷新率。 试试这个

    renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);

【讨论】:

  • 通过使用 SDL_GetRendererInfo,我知道我正在使用硬件加速(设置了 SDL_RENDERER_ACCELERATED 而未设置 SDL_RENDERER_SOFTWARE)。我还知道我没有被 VSYNC 阻止(未设置 SDL_RENDERER_PRESENTVSYNC)。
  • 好吧抱歉我以为已经设置好了。
【解决方案2】:

在我的 vsync 系统上,SDL_RenderPresent() 之前我有很多额外的时间(~15ms):

#include <stdio.h>
#include <stdlib.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_timer.h>

int main( int argc, char** argv )
{
    SDL_Window* win;
    SDL_Renderer* ren;
    int x, y;

    SDL_Init( SDL_INIT_VIDEO );

    win = SDL_CreateWindow
        ( 
        "Window", 
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
        800, 600, 
        0 
        );

    ren = SDL_CreateRenderer
        ( 
        win, 
        -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
        );

    for(;;) 
    {
        Uint32 t = SDL_GetTicks();
        for(x=0; x<800; x+=16) 
        {
            for(y=0; y<600; y+=16) 
            {
                SDL_Rect dst = { x, y, 16, 16 };
                SDL_SetRenderDrawColor( ren, rand()%255, rand()%255, rand()%255, 255 );
                SDL_RenderFillRect( ren, &dst );
            }
        }

        // adjust this up/down to figure out how much extra time you have
        // stop when you start seeing frame times exceeding ~16ms
        SDL_Delay( 15 );

        SDL_RenderPresent(ren);
        printf("%ld ms\n", SDL_GetTicks() - t);
    }
}

【讨论】:

    【解决方案3】:

    您可以先使用SDL_SetRenderTarget 渲染到大纹理。然后该纹理每帧仅绘制一次。

    即类似:

    Uint32 fmt;
    SDL_QueryTexture(tx, &fmt, NULL, NULL, NULL);
    SDL_Texture* tm = SDL_CreateTexture(ren, fmt, SDL_TEXTUREACCESS_TARGET, 800, 600);
    SDL_SetRenderTarget(ren, tm);
    // Your draw loop here
    SDL_SetRenderTarget(ren, NULL);
    // Real draw loop only needs to RenderCopy tm
    

    【讨论】:

      【解决方案4】:

      这是由您的图形驱动程序(opengl 后端)强制执行的垂直同步。它是由用户控制的,并且是应用程序本身不可更改的。

      【讨论】:

        猜你喜欢
        • 2013-03-10
        • 2014-06-07
        • 2016-05-31
        • 2011-07-07
        • 2015-08-23
        • 2012-07-05
        • 2016-01-08
        • 2014-03-12
        • 2021-03-26
        相关资源
        最近更新 更多