SDL渲染过慢

4
我是一名C++和SDL的初学者,正在按照一些教程编写小游戏以学习一些概念。
然而,我遇到了一个问题:我的渲染速度非常慢。我使用PerformanceCounters来计算我的循环次数,有和没有渲染函数的情况下都能得到0~2毫秒每帧的结果;但当我添加渲染函数后,每帧的时间却增加到了65毫秒左右。
请问有谁能告诉我我的渲染函数出了什么问题吗?
SDL_Texture *texture;

...

// gets called by the main loop
void render(int x_offset, int y_offset)
{
  if (texture)
  {
    SDL_DestroyTexture(texture);
  }

  texture = SDL_CreateTexture(renderer,
      SDL_PIXELFORMAT_ARGB8888,
      SDL_TEXTUREACCESS_STREAMING,
      texture_w,
      texture_h);

  if (SDL_LockTexture(texture, NULL, &pixel_memory, &pitch) < 0) {
    printf("Oops! %s\n", SDL_GetError());
  }

  Uint32 *pixel;
  Uint8 *row = (Uint8 *) pixel_memory;

  for (int j = 0; j < texture_h; ++j) {
    pixel = (Uint32 *)((Uint8 *) pixel_memory + j * pitch);
    for (int i = 0; i < texture_w; ++i) {

      Uint8 alpha = 255;
      Uint8 red = 172;
      Uint8 green = 0;
      Uint8 blue = 255;

     *pixel++ = ((alpha << 24) | (red << 16) | (green << 8) | (blue));

    }
  }
  SDL_UnlockTexture(texture);
} 

也许你的编译标志被设置为 -O0 以进行调试? - name
2
为什么每次都要创建和销毁纹理?重复使用不是更好吗? - Moby Disk
我正在使用 c++ ../linux_handmade.cpp -o handmadehero -g sdl2-config --cflags --libs 进行编译,所以是的,-g 参数在那里。我尝试删除 -g 参数并且移除了 SDL_DestroyTexture,但是没有得到更好的结果。 - Thyago B. Rodrigues
3个回答

6
SDL2基于硬件渲染。即使使用流媒体标志访问纹理,也不会很快,因为你需要与GPU来回传输数据。
如果不是每一帧都创建和销毁纹理,你应该考虑在重新绘制之前清除它。
另一个选择是使用表面。你可以使用表面进行操作,然后将其绘制为纹理。我不确定收益会有多大,但我认为这比每一帧都销毁、创建、锁定和解锁纹理要好。
看着你的代码,我知道这只是一个测试,但你可以尝试使用SDL原语渲染到纹理上。
最后,在测试期间请记住,你的驱动程序可能会强制执行垂直同步,这可能会导致虚假的性能下降。

5

你的程序很可能变慢是因为你在每一帧都在销毁和创建纹理,锁定纹理/上传像素数据并不是非常快,但我怀疑这里并不是瓶颈所在。我强烈建议在进入主循环之前只分配一次纹理并在渲染期间重复使用它,然后在程序退出之前销毁它。


这是我之前在做的事情。但是我遇到了一个奇怪的 bug:只有当我将 SDL_INIT_GAMECONTROLLER 添加到 Init 函数时,增加窗口的高度会导致写入纹理像素的段错误 - 就好像它没有正确地扩展一样,我不确定。当我开始重新分配纹理时,情况就改变了,就好像它正在正确地重新调整大小。我不确定出了什么问题,但是这个提交在这里,以防您或任何人想要查看它 =] https://github.com/thyagobr/handmadehero/commit/395969ebab77edd7f4409219e4d9f9ba0b59af2f - Thyago B. Rodrigues
1
噢,我明白了。如果您正在调整窗口大小,并且该纹理应覆盖整个屏幕,则需要重新创建以匹配新的大小。但是,您只应在窗口大小更改时重新创建它,而不是每帧都重新创建。为此,您可以监听SDL_WindowEvent事件,告诉您窗口已调整大小,然后获取新大小并仅根据需要重新创建纹理。看起来您已经处理了此事件,因此只需在那里重新分配纹理即可。 - Twinklebear

2

可能没有什么需要做的。锁定纹理以进行直接像素访问是很慢的。很有可能,在渲染函数中做很多额外的事情,并不会看到进一步的速度降低。

如果你想要更快的渲染,你需要更高级别的函数。


我认为就是这样。在这种情况下,看起来你正在循环遍历一个新的纹理,并将其逐像素设置为紫色;如果你对现有的紫色纹理进行缩放模糊处理并将其贴到新的纹理上,你肯定会获得巨大的加速效果。(我不记得SDL中具体如何实现了。)至少,在渲染循环的每次迭代中都不要这样做...在启动时执行一次并保存纹理。 - Ask About Monica
我认为内部循环只是一个示例,而不是渲染循环中的实际代码。 - Moby Disk
我倾向于接受这个答案并继续前进,但有一件事情真的让我很困惑:这是一个在windows系统下完成的代码“翻译”(由Casey Muratori完成的Handmade Hero系列)。他在Windows上逐像素绘制似乎并没有对循环产生太大的影响。(如果有人感兴趣,这是整个“类”的内容:https://www.youtube.com/watch?v=tAcUIEoy2Yk) - Thyago B. Rodrigues

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接