Тайловая карта SDL2 — слишком медленная

Я использую SDL2 для написания игры, которая отображает тайловую карту в каждом кадре, но производительность слишком низкая. Я написал небольшую программу, чтобы изолировать проблему. Учтите, что «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), что не оставляет места для игровой логики. Кроме того, я запускаю его на довольно быстром компьютере.

Я почти уверен, что использую неправильную стратегию, но я не уверен, какая из них правильная. Возможно, было бы лучше создать большую текстуру и реже обновлять ее, но я не смог найти никакой документации о том, как копировать одну текстуру поверх другой.

Итак, как я могу улучшить производительность карты тайлов?


person André Wagner    schedule 03.09.2013    source источник
comment
Не могли бы вы добавить показатель производительности перед SDL_RenderPresent, чтобы знать, потеряете ли вы время там или раньше.   -  person dzada    schedule 03.09.2013
comment
Я не знаю SDL2, но, может быть, рендеринг ограничен vsync? Пробовали запускать в полноэкранном режиме?   -  person user694733    schedule 03.09.2013
comment
@dzada Большая часть времени уходит на SDL_RenderPresent. Около 2 мс до и 14 мс после.   -  person André Wagner    schedule 03.09.2013
comment
@ user694733 Я тестировал в полноэкранном режиме, но получил тот же результат. Кроме того, в SDL2 теперь есть флаг, указывающий, используется ли VSYNC или нет: wiki.libsdl.org/SDL_CreateRenderer   -  person André Wagner    schedule 03.09.2013
comment
Насколько я знаю, эти флаги могут быть переопределены видеодрайвером. Возможно, SDL_GetRendererInfo поможет вам.   -  person user694733    schedule 03.09.2013
comment
Знаете ли вы, используется ли ваша видеокарта или только ваш процессор (использует ли он opengl?)   -  person dzada    schedule 03.09.2013
comment
@ user694733 Используя SDL_GetRendererInfo, я понимаю, что использую аппаратное ускорение (SDL_RENDERER_ACCELERATED установлен, а SDL_RENDERER_SOFTWARE не установлен).   -  person André Wagner    schedule 03.09.2013
comment
@dzada Используя SDL_GetRendererInfo, я понимаю, что меня не блокирует VSYNC (SDL_RENDERER_PRESENTVSYNC не установлен).   -  person André Wagner    schedule 03.09.2013
comment
Лучший способ проверить, заблокирован ли VSYNC кодом, картой/драйверами GFX или чем-то еще, — просто нарисовать одну плитку. Для этого вы должны получить более 100+ кадров в секунду. Если он остается на уровне 60, вы можете быть уверены, что VSYNC включен.   -  person Zammalad    schedule 21.02.2015


Ответы (4)


На этой странице упоминается, что флаг SDL_RENDERER_PRESENTVSYNC означает, что вы синхронизированы с частотой обновления. попробуй это

    renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
person dzada    schedule 03.09.2013
comment
Используя SDL_GetRendererInfo, я понимаю, что использую аппаратное ускорение (SDL_RENDERER_ACCELERATED установлен, а SDL_RENDERER_SOFTWARE не установлен). Я также понимаю, что меня не блокирует VSYNC (SDL_RENDERER_PRESENTVSYNC не установлен). - person André Wagner; 03.09.2013
comment
Хорошо, извините, я думал, что это было установлено. - person dzada; 03.09.2013

В моей системе с vsync у меня есть много дополнительного времени (~ 15 мс) до SDL_RenderPresent():

#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);
    }
}
person genpfault    schedule 09.10.2013

Вы можете сначала отрендерить большую текстуру, используя 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
person otus    schedule 09.10.2013

Это вертикальная синхронизация, вызванная вашим графическим драйвером (серверная часть opengl). Он контролируется пользователем и не может быть изменен самими приложениями.

person keltar    schedule 09.10.2013