SDL tilemap 渲染很慢
Posted
技术标签:
【中文标题】SDL tilemap 渲染很慢【英文标题】:SDL tilemap rendering quite slow 【发布时间】:2019-07-08 09:00:31 【问题描述】:我正在使用 SDL 编写一个显示相当大的瓷砖地图(大约 240*240 瓷砖)的模拟。由于我对 SDL 库非常陌生,因此我无法确定在渲染超过 50,000 个图块时性能相当缓慢是否实际上是正常的。每个图块始终可见,大小约为 4*4px。目前它通过 2d 数组迭代每一帧并渲染每一个图块,这给了我大约 40fps 的速度,实际上太慢了,无法将任何游戏逻辑放在系统后面。
我试图找到一些替代系统,例如只更新更新的图块,但人们总是评论这是一种不好的做法,并且渲染器应该每帧都被清理等等。
Here a picture of the map
所以我基本上想问有没有比每帧渲染每个图块更高效的系统。
编辑:以下是我使用的简单渲染方法
void World::DirtyBiomeDraw(Graphics *graphics)
if(_biomeTexture == NULL)
_biomeTexture = graphics->loadImage("assets/biome_sprites.png");
printf("Biome texture loaded.\n");
for(int i = 0; i < globals::WORLD_WIDTH; i++)
for(int l = 0; l < globals::WORLD_HEIGHT; l++)
SDL_Rect srect;
srect.h = globals::SPRITE_SIZE;
srect.w = globals::SPRITE_SIZE;
if(sites[l][i].biome > 0)
srect.y = 0;
srect.x = (globals::SPRITE_SIZE * sites[l][i].biome) - globals::SPRITE_SIZE;
else
srect.y = globals::SPRITE_SIZE;
srect.x = globals::SPRITE_SIZE * fabs(sites[l][i].biome);
SDL_Rect drect = i * globals::SPRITE_SIZE * globals::SPRITE_SCALE, l * globals::SPRITE_SIZE * globals::SPRITE_SCALE,
globals::SPRITE_SIZE * globals::SPRITE_SCALE, globals::SPRITE_SIZE * globals::SPRITE_SCALE;
graphics->blitOnRenderer(_biomeTexture, &srect, &drect);
所以在这种情况下,每个图块都称为“站点”,这是因为它们还存储了湿度、温度等信息。
每个站点在生成过程中都分配了一个生物群落,每个生物群落基本上都是一个 ID,每个陆地生物群落的 ID 都大于 0,每个水的 ID 都是 0 或更低。
这允许我将每个按 ID 排序的生物群落精灵放入“biome_sprites.png”图像中。所有的土地精灵基本上都在第一排,而所有的水瓦都在第二排。这样我就不必手动将精灵分配给生物群系,并且该方法可以通过将图块大小(基本上是宽度)与生物群系相乘来自行完成。
这是biome ID table from my SDD/GDD 和actual spritesheet。
图形类中的 blitOnRenderer 方法基本上只是运行一个 SDL_RenderCopy 将纹理传送到渲染器上。
void Graphics::blitOnRenderer(SDL_Texture *texture, SDL_Rect
*sourceRectangle, SDL_Rect *destinationRectangle)
SDL_RenderCopy(this->_renderer, texture, sourceRectangle, destinationRectangle);
在游戏循环中,每一帧都会调用 RenderClear 和 RenderPresent。
我真的希望我能理解地解释它,问任何你想要的东西,我是向你们寻求帮助的人,所以我能做的至少是合作:D
【问题讨论】:
不是每一帧的每一块瓷砖,而是将它们全部渲染到一个纹理,然后只渲染该纹理(当然,如果它们不改变)。另外,请确保您使用的是SDL_Texture
而不是SDL_Surface
。
所有 240x240 的图块是否同时可见?难以置信。即使在 4K 显示器上,每个图块水平方向最多 16 个像素(忽略垂直方向)。渲染中的另一个常见策略是“视锥体剔除”,即不将绝对不可见的内容输入 GPU。
没有代码我们无法分析它。唯一可以确定的是,其中通常存在几个类似或其他类型的幼稚性能瓶颈。
由于您的图块只是彩色方块,我建议构建单个 240x240 表面/纹理并使用单个 SDL_RenderCopy 调用对其进行渲染。
@Scheff 是的,它们在任何时候都同时可见。编辑:我稍后会发布代码
【参考方案1】:
戳 SDL2 开发人员以获得 SDL_RenderCopy()
的多项目版本(类似于现有的 SDL_RenderDrawLines()
/SDL_RenderDrawPoints()
/SDL_RenderDrawRects()
函数)和/或 batched SDL_Renderer 后端。
现在,您正在尝试将 240*240 = 57000 个绘制调用塞入 GPU 的喉咙;至少在任何给定的 16 毫秒内,您通常只能指望 1000-4000 次绘制调用。
或者切换到 OpenGL 并自己进行批处理。
【讨论】:
这是一个很好的答案:D 我到处寻找限制规范,到目前为止几乎没有发现任何东西。关于基于更新的渲染的任何提示?就像只在更新时渲染图块? @NovayaFinch: 嗯,如果一切都正确批处理(纹理图集 + 大 'ole 几何缓冲区),您可以在一次绘制调用中渲染整个 240x240 地图;如果图块发生变化,您可以通过glBufferSubData()
仅现场上传受影响的图块,或者重新上传整个几何缓冲区; 4-6 MiB 的几何数据并不多。以上是关于SDL tilemap 渲染很慢的主要内容,如果未能解决你的问题,请参考以下文章