【问题标题】:SDL2 strange behaviour of hardware renderingSDL2 硬件渲染的奇怪行为
【发布时间】:2018-03-20 16:02:30
【问题描述】:

我想创建我的第一个游戏。
没什么特别的,只是按下 WSAD 键时蓝色矩形移动。

问题是当我运行我的游戏时,矩形存在错误(见下图)。错误仅在水平移动时出现,而不是垂直移动。


有趣的是,当我换行时:

renderer = SDL_CreateRenderer(display, -1, SDL_RENDERER_ACCELERATED)

到:

renderer = SDL_CreateRenderer(display, -1, SDL_RENDERER_SOFTWARE)

一切正常

我正在使用 Windows 10、MinGw 和 CMake(C++14) 和 SDL 2.0.8、英特尔酷睿 i5 第 7 代、Radeon M7 R465

我的代码 OnRender 函数负责渲染,也许我做错了什么?(我的代码中的函数发布在问题的末尾)

我也使用SDL_WINDOW_OPENGL 标志来创建我的窗口,但将其更改为SDL_WINDOW_SHOWN 不会改变任何东西。

#include <SDL2/SDL.h>

class Game
{
private:
    SDL_Surface *display_surf = nullptr;
    SDL_Renderer *renderer = nullptr;
    SDL_Window *display = nullptr;

private:
    bool running, prW = false, prS = false, prD = false, prA = false;
    int x, y;
    int spd_y, spd_x;
    int scr_w, scr_h;

public:
    Game();
    int OnExecute();

public:
    bool OnInit();
    void OnEvent( SDL_Event *event );
    void OnLoop();
    void OnRender();
    void OnCleanup();
};

Game::Game()
{
    running = false;
}

int Game::OnExecute()
{
    if( !OnInit() )
    {
        return -1;
    }

    running = true;
    SDL_Event event;
    while( running )
    {
        while( SDL_PollEvent( &event ) )
        {
            OnEvent( &event );
        }

        OnLoop();
        OnRender();
        SDL_Delay( 1 );
    }

    OnCleanup();
    return 0;
}

bool Game::OnInit()
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
    {
        return false;
    }

    SDL_DisplayMode dspm;
    if( SDL_GetDesktopDisplayMode( 0, &dspm ) < 0 )
    {
        return false;
    }

    scr_h = dspm.h;
    scr_w = dspm.w;

    if( ( display = SDL_CreateWindow( "Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080,
        SDL_WINDOW_OPENGL ) ) == nullptr )
    {
        return false;
    }

    display_surf = SDL_GetWindowSurface( display );


    if( ( renderer = SDL_CreateRenderer( display, -1, SDL_RENDERER_ACCELERATED ) ) == nullptr )
    {
        return false;
    }

    x = 0;
    y = 0;

    spd_x = 0;
    spd_y = 0;

    SDL_SetWindowFullscreen( display, SDL_WINDOW_FULLSCREEN );

    return true;

}

void Game::OnEvent( SDL_Event *event )
{
    if( event->type == SDL_QUIT )
    {
        running = false;
        return;
    }

    switch( event->type )
    {
    case SDL_KEYDOWN:
        switch( event->key.keysym.sym )
        {
        case SDLK_w:
            if( prS )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = -5;
            }
            prW = true;
            break;
        case SDLK_s:
            if( prW )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = 5;
            }
            prS = true;
            break;
        case SDLK_d:
            if( prA )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = 5;
            }
            prD = true;
            break;
        case SDLK_a:
            if( prD )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = -5;
            }
            prA = true;
            break;
        default:
            return;
        }

        break;
    case SDL_KEYUP:
        switch( event->key.keysym.sym )
        {
        case SDLK_w:
            if( !prS )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = 5;
            }
            prW = false;
            break;
        case SDLK_s:
            if( !prW )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = -5;
            }
            prS = false;
            break;
        case SDLK_a:
            if( !prD )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = 5;
            }
            prA = false;
            break;
        case SDLK_d:
            if( !prA )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = -5;
            }
            prD = false;
            break;
        default:
            return;
        }
    default:
        return;
    }
}

void Game::OnLoop()
{
    x += spd_x;
    y += spd_y;

    if( x < 0 )
    {
        x = 0;
    }
    else if( x > scr_w - 100 )
    {
        x = scr_w - 100;
    }

    if( y < 0 )
    {
        y = 0;
    }
    else if( y > scr_h - 100 )
    {
        y = scr_h - 100;
    }
}

void Game::OnRender()
{
    SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0x00 );

    SDL_RenderClear( renderer );

    SDL_Rect charc;
    charc.x = x;
    charc.y = y;
    charc.w = 100;
    charc.h = 100;

    SDL_SetRenderDrawColor( renderer, 0, 0, 0xff, 0 );

    SDL_RenderFillRect( renderer, &charc );

    SDL_RenderPresent( renderer );
}

void Game::OnCleanup()
{
    SDL_DestroyWindow( display );
    SDL_Quit();
}

int main( int argc, char** argv )
{
    Game game;
    return game.OnExecute();
}

【问题讨论】:

  • 你的主循环是什么样的?您也可以尝试将 SDL_RENDER_PRESENTVSYNC 添加到您的渲染器初始化函数中。
  • @Aram 刚刚发布了我的代码
  • @genpfault 刚刚发布了我的代码。我知道它必须是“最小的”,但我不确定在这种情况下“最小”是什么,所以我刚刚发布了所有代码。时间不长。
  • 尝试在 onRender 之后添加几毫秒的睡眠,std::this_thread::sleep_for(std::chrono::milliseconds(5));包括chrono和thread。看看会发生什么。
  • 如果你点击了给你的链接,你会知道在这种情况下“最小”是什么。 MCVE 不是建议。这是一个要求。您应该已经创建了一个作为您自己调试的一部分!

标签: c++ rendering sdl-2


【解决方案1】:

看起来很像tearing,由高帧率和缺乏垂直同步引起。

我可以通过在SDL_CreateRenderer() 上将SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC 传递给flags 来获得无泪图:

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

class Game
{
private:
    SDL_Renderer *renderer = nullptr;
    SDL_Window *display = nullptr;

private:
    bool running, prW = false, prS = false, prD = false, prA = false;
    int x, y;
    int spd_y, spd_x;
    int scr_w, scr_h;

public:
    Game();
    int OnExecute();

public:
    bool OnInit();
    void OnEvent( SDL_Event *event );
    void OnLoop();
    void OnRender();
    void OnCleanup();
};

Game::Game()
{
    running = false;
}

int Game::OnExecute()
{
    if( !OnInit() )
    {
        return -1;
    }

    running = true;
    SDL_Event event;

    Uint32 beg = SDL_GetTicks();
    size_t frames = 0;
    while( running )
    {
        while( SDL_PollEvent( &event ) )
        {
            OnEvent( &event );
        }

        OnLoop();
        OnRender();

        frames++;
        Uint32 end = SDL_GetTicks();
        if( end - beg > 1000 )
        {
            std::cout << "Frame time: " << ( end - beg ) / frames << " ms" << std::endl;
            beg = end;
            frames = 0;
        }
    }

    OnCleanup();
    return 0;
}

bool Game::OnInit()
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
    {
        return false;
    }

    if( ( display = SDL_CreateWindow( "Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, 0 ) ) == nullptr )
    {
        return false;
    }

    scr_w = 1280;
    scr_h = 720;

    Uint32 rflags = SDL_RENDERER_ACCELERATED;
    rflags |= SDL_RENDERER_PRESENTVSYNC;
    if( ( renderer = SDL_CreateRenderer( display, -1, rflags ) ) == nullptr )
    {
        return false;
    }

    x = 0;
    y = 0;

    spd_x = 0;
    spd_y = 0;

    return true;
}

void Game::OnEvent( SDL_Event *event )
{
    if( event->type == SDL_QUIT )
    {
        running = false;
        return;
    }

    switch( event->type )
    {
    case SDL_KEYDOWN:
        switch( event->key.keysym.sym )
        {
        case SDLK_w:
            if( prS )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = -5;
            }
            prW = true;
            break;
        case SDLK_s:
            if( prW )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = 5;
            }
            prS = true;
            break;
        case SDLK_d:
            if( prA )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = 5;
            }
            prD = true;
            break;
        case SDLK_a:
            if( prD )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = -5;
            }
            prA = true;
            break;
        default:
            return;
        }

        break;
    case SDL_KEYUP:
        switch( event->key.keysym.sym )
        {
        case SDLK_w:
            if( !prS )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = 5;
            }
            prW = false;
            break;
        case SDLK_s:
            if( !prW )
            {
                spd_y = 0;
            }
            else
            {
                spd_y = -5;
            }
            prS = false;
            break;
        case SDLK_a:
            if( !prD )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = 5;
            }
            prA = false;
            break;
        case SDLK_d:
            if( !prA )
            {
                spd_x = 0;
            }
            else
            {
                spd_x = -5;
            }
            prD = false;
            break;
        default:
            return;
        }
    default:
        return;
    }
}

void Game::OnLoop()
{
    x += spd_x;
    y += spd_y;

    if( x < 0 )
    {
        x = 0;
    }
    else if( x > scr_w - 100 )
    {
        x = scr_w - 100;
    }

    if( y < 0 )
    {
        y = 0;
    }
    else if( y > scr_h - 100 )
    {
        y = scr_h - 100;
    }
}

void Game::OnRender()
{
    SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0x00 );

    SDL_RenderClear( renderer );

    SDL_Rect charc;
    charc.x = x;
    charc.y = y;
    charc.w = 100;
    charc.h = 100;

    SDL_SetRenderDrawColor( renderer, 0, 0, 0xff, 0 );

    SDL_RenderFillRect( renderer, &charc );

    SDL_Delay( 1 );
    SDL_RenderPresent( renderer );
}

void Game::OnCleanup()
{
    SDL_DestroyWindow( display );
    SDL_Quit();
}

int main( int argc, char** argv )
{
    Game game;
    return game.OnExecute();
}

如果我只是通过SDL_RENDERER_ACCELERATED,我会感到撕裂,并且大大更高的帧速率。

确保您的操作系统未配置为默认禁用垂直同步。

【讨论】:

  • 这也适用于我,但SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC 仍然比SDL_RENDERER_SOFTWARE 快吗?
  • @JK:真的取决于你的硬件。没有什么能阻止某人发布一个病态可怕的 OpenGL/DirectX 驱动程序 :) 但对于更大的工作负载,SDL_RENDERER_ACCELERATED 很可能比SDL_RENDERER_SOFTWARE 更快。
【解决方案2】:

在 SDL 2.0.8 上启用 SDL_RENDERER_ACCELERATED 标志时,许多开发人员似乎遇到了一些不良行为。

已在 libsdl 的 bugzilla (https://bugzilla.libsdl.org/show_bug.cgi?id=4110) 中打开了一张票。这是另一个问题,但描述中提到了硬件渲染的问题。

目前,我使用软件渲染 (SDL_RENDERER_SOFTWARE) 作为后备。不是我真正想做的,但现在我得到了预期的结果。

我会尝试 PREVENTSYNC...

【讨论】:

  • 经过测试,但不幸的是 SDL_RENDERER_PRESENTVSYNC 对我不起作用
猜你喜欢
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多