SDL瓷砖地图渲染很慢

4
我使用SDL编写了一个模拟程序,显示了一个非常大的图块地图(约240 * 240个图块)。由于我对SDL库还比较陌生,因此我无法确定每秒渲染超过50,000个图块的相当缓慢的性能是否正常。每个图块始终可见,大约为4 * 4px。目前它正在每帧通过2D数组迭代并渲染每个单独的图块,这给我大约40fps,速度太慢,实际上无法在系统后添加任何游戏逻辑。
我尝试找到一些替代系统,例如仅更新已更新的图块,但人们总是评论说这是一种不好的做法,渲染器应该在每个帧中进行清理等等。
这里有地图的图片。
所以我想问一下是否有比每帧渲染每个单独图块更高效的系统呢?
这是我正在使用的简单渲染方法的编辑。
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”图像中。所有陆地精灵基本上都在第一行,而所有水域瓦片都在第二行。这样,我就不必手动为生物群系分配精灵,该方法可以通过将瓷砖大小(基本上是宽度)与生物群系相乘来自行完成。
这是我的SDD/GDD中的生物群系ID表实际的精灵表
graphics类中的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 - Blaze
1
由于您的图块只是彩色正方形,我建议构建一个单独的240x240表面/纹理,并使用单个SDL_RenderCopy调用进行渲染。 - HolyBlackCat
@Scheff 是的,它们在任何时候都同时可见。编辑:我稍后会发布代码。 - Novaya Finch
请添加 blitOnRenderer 代码。没有它会很难帮助您。根据当前状态,我可以建议使用每个单元格由四边形表示的网格,并具有 drect 位置和 srect 纹理坐标。或者您可以尝试将所有相同类型的图块批处理到一个调用中。 - Michael Nastenko
@MichaelNastenko 当然没问题,我刚刚添加了它。 - Novaya Finch
显示剩余2条评论
1个回答

2
请向SDL2开发人员请求一个多项版本的SDL_RenderCopy()(类似于现有的SDL_RenderDrawLines()/SDL_RenderDrawPoints()/SDL_RenderDrawRects()函数)和/或批处理SDL_Renderer后端。目前,您正在尝试将至少240 * 240 = 57000个绘制调用传送到GPU中,您通常只能在任何给定的16毫秒内使用1000-4000个绘制调用。或者切换到OpenGL并自己进行批处理。

这是一个真正好的答案 :D 我到处寻找限制规格,但迄今几乎没有找到任何内容。关于基于更新的渲染有什么提示吗?例如仅在更新时渲染瓷砖? - Novaya Finch
@NovayaFinch:嗯,如果一切都被正确地批处理(纹理图集+大型几何缓冲区),您可以在单个绘制调用中呈现整个240x240地图;如果瓷砖发生变化,则可以通过glBufferSubData()仅上传受影响的瓷砖或重新上传整个几何缓冲区;4-6 MiB的几何数据并不算太多。 - genpfault

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