SDL2窗口在调整大小时变黑

Ara*_*emi 4 opengl macos sdl-2

我已经开始使用SDL2,但对它没有经验。我在Mac系统上工作。几乎一切都很好,但是我有一个问题,当调整可调整大小的窗口的大小,拖动手柄时,窗口会变成黑色,而我只能在释放后重新绘制它。而且我已经检查过,在调整窗口大小时,不会产生任何事件,并且由于事件循环只是暂停,所以我没有办法干预或检测到此情况。有没有可能的解决方案?

这是代码(有关处理调整大小事件的教程的几乎副本):

SDL_Event event;

SDL_Rect nativeSize;
SDL_Rect newWindowSize;

float scaleRatioW;//This is to change anything that might rely on something like mouse coords
float scaleRatioH; //(such as a button on screen) over to the new coordinate system scaling would create

SDL_Window * window; //Our beautiful window
SDL_Renderer * renderer; //The renderer for our window
SDL_Texture * backBuffer; //The back buffer that we will be rendering everything to before scaling up

SDL_Texture * ballImage; //A nice picture to demonstrate the scaling;

bool resize;

void InitValues(); //Initialize all the variables needed
void InitSDL();     //Initialize the window, renderer, backBuffer, and image;
bool HandleEvents(); //Handle the window changed size event
void Render();            //Switches the render target back to the window and renders the back buffer, then switches back.
void Resize();      //The important part for stretching. Changes the viewPort, changes the scale ratios

void InitValues()
{
    nativeSize.x = 0;
    nativeSize.y = 0;
    nativeSize.w = 256;
    nativeSize.h = 224; //A GameBoy size window width and height

    scaleRatioW = 1.0f;
    scaleRatioH = 1.0f;

    newWindowSize.x = 0;
    newWindowSize.y = 0;
    newWindowSize.w = nativeSize.w;
    newWindowSize.h = nativeSize.h;

    window = NULL;
    renderer = NULL;
    backBuffer = NULL;
    ballImage = NULL;

    resize = false;
}

void InitSDL()
{
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        //cout << "Failed to initialize SDL" << endl;
        printf("%d\r\n", __LINE__);
    }

    //Set the scaling quality to nearest-pixel
    if(SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0") < 0)
    {
        //cout << "Failed to set Render Scale Quality" << endl;
         printf("%d\r\n", __LINE__);
    }

    //Window needs to be resizable
    window = SDL_CreateWindow("Rescaling Windows!",
                                                    SDL_WINDOWPOS_CENTERED,
                                                    SDL_WINDOWPOS_CENTERED,
                                                    256,
                                                    224,
                                                    SDL_WINDOW_RESIZABLE);

    //You must use the SDL_RENDERER_TARGETTEXTURE flag in order to target the backbuffer
    renderer = SDL_CreateRenderer(window,
                                                      -1,
                                                      SDL_RENDERER_ACCELERATED |
                                                      SDL_RENDERER_TARGETTEXTURE);

    //Set to blue so it's noticeable if it doesn't do right.
    SDL_SetRenderDrawColor(renderer, 0, 0, 200, 255);

    //Similarly, you must use SDL_TEXTUREACCESS_TARGET when you create the texture
    backBuffer = SDL_CreateTexture(renderer,
                                                       SDL_GetWindowPixelFormat(window),
                                                       SDL_TEXTUREACCESS_TARGET,
                                                       nativeSize.w,
                                                       nativeSize.h);

    //IMPORTANT Set the back buffer as the target
    SDL_SetRenderTarget(renderer, backBuffer);

    //Load an image yay
    SDL_Surface * image = SDL_LoadBMP("Ball.bmp");

    ballImage = SDL_CreateTextureFromSurface(renderer, image);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);

    SDL_FreeSurface(image);
}

bool HandleEvents()
{
    while(SDL_PollEvent(&event) )
    {
       printf("%d\r\n", __LINE__);
       if(event.type == SDL_QUIT)
       {
             printf("%d\r\n", __LINE__);
            return true;
       }
        else if(event.type == SDL_WINDOWEVENT)
        {
            if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
            {
                resize = true;
                printf("%d\r\n", __LINE__);
            }
        }

        return false;
    }
    return false;
}

void Render()
{
    SDL_RenderCopy(renderer, ballImage, NULL, NULL); //Render the entire ballImage to the backBuffer at (0, 0)
    printf("%d\r\n", __LINE__);

    SDL_SetRenderTarget(renderer, NULL); //Set the target back to the window

    if(resize)
    {
        Resize();
        resize = false;
    }
    printf("%d\r\n", __LINE__);

    SDL_RenderCopy(renderer, backBuffer, &nativeSize, &newWindowSize); //Render the backBuffer onto the screen at (0,0)
    SDL_RenderPresent(renderer);
    SDL_RenderClear(renderer); //Clear the window buffer

    SDL_SetRenderTarget(renderer, backBuffer); //Set the target back to the back buffer
    SDL_RenderClear(renderer); //Clear the back buffer
    printf("%d\r\n", __LINE__);

}

void Resize()
{
    int w, h;
    printf("%d\r\n", __LINE__);

    SDL_GetWindowSize(window, &w, &h);

    scaleRatioW = w / nativeSize.w;
    scaleRatioH = h / nativeSize.h;  //The ratio from the native size to the new size

    newWindowSize.w = w;
    newWindowSize.h = h;

    //In order to do a resize, you must destroy the back buffer. Try without it, it doesn't work
    SDL_DestroyTexture(backBuffer);
    backBuffer = SDL_CreateTexture(renderer,
                                   SDL_GetWindowPixelFormat(window),
                                   SDL_TEXTUREACCESS_TARGET, //Again, must be created using this
                                   nativeSize.w,
                                   nativeSize.h);

    SDL_Rect viewPort;
    SDL_RenderGetViewport(renderer, &viewPort);

    if(viewPort.w != newWindowSize.w || viewPort.h != newWindowSize.h)
    {
        //VERY IMPORTANT - Change the viewport over to the new size. It doesn't do this for you.
        SDL_RenderSetViewport(renderer, &newWindowSize);
    }
}

int main(int argc, char * argv[])
{
    InitValues();
    InitSDL();

    bool quit = false;
    printf("%d\r\n", __LINE__);

    while(!quit)
    {
        printf("%d\r\n", __LINE__);
        quit = HandleEvents();
        Render();
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ara*_*emi 5

原来,这不是我的代码特有的,它是MacOSX中所有OpenGL库的一个更广泛问题的一部分。尽管最近的GLFW修补程序已修复了该问题,并且在XCode本身提供的GLUT版本中已对其进行了修复,但它要好得多,并且您在调整大小时仅观察到窗口中的闪烁。

https://github.com/openframeworks/openFrameworks/issues/2800 https://github.com/openframeworks/openFrameworks/issues/2456

问题是由于OSX窗口管理器的阻止特性,它会阻止所有事件,直到释放鼠标为止。

为了解决这个问题,您应该操纵正在使用的库并重新编译它。您应该将这些或类似的内容(取决于您的开发环境)添加到调整大小处理程序中,以绕过该块:

ofNotifyUpdate();
instance->display();
Run Code Online (Sandbox Code Playgroud)

如果您是新手,并且希望能够轻松使用库更新,那将是灾难性的。另一个解决方案是通过编写另一个做到这一点的事件处理程序来覆盖SDL行为。这样比较好,因为它不需要编辑SDL代码,但是会增加一堆我个人不喜欢的平台特定代码,并且会引起很多我不想花时间修复的问题。

经过两天的搜索,由于我才刚刚开始该项目,并且对SDL的依赖不多,因此我决定切换到GLFW,它具有最流畅的调整大小处理功能,并且没有闪烁。


Mar*_*idt 5

好的,在与 SDL2 进行了一番斗争之后,我让它与 macOS 10.12 一起工作。

这是问题所在:

  1. 例如,当您轮询事件时,SDL2 会捕获调整大小事件并仅重新发送最后 3 个事件SDL_PollEvent(&event)
  2. 在此期间(您按下鼠标左键单击调整大小区域并按住鼠标)SDL_PollEvent正在阻塞。

这是解决方法:

幸运的是,您可以使用SDL_SetEventFilter. 每次收到事件时都会触发。因此,对于所有发生的调整大小事件。

所以你可以做的是注册你自己的事件过滤器回调,它基本上允许每个事件(通过返回1),监听调整大小事件,并将它们发送到你的绘制循环。

例子:

//register this somewhere
int filterEvent(void *userdata, SDL_Event * event) {
    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
        //convert userdata pointer to yours and trigger your own draw function
        //this is called very often now
        //IMPORTANT: Might be called from a different thread, see SDL_SetEventFilter docs
        ((MyApplicationClass *)userdata)->myDrawFunction(); 

        //return 0 if you don't want to handle this event twice
        return 0;
    }

    //important to allow all events, or your SDL_PollEvent doesn't get any event
    return 1;
}


///after SDL_Init
SDL_SetEventFilter(filterEvent, this) //this is instance of MyApplicationClass for example
Run Code Online (Sandbox Code Playgroud)

重要提示:不要SDL_PollEventfilterEvent回调中调用,因为这会导致卡住事件的奇怪行为。(例如,有时调整大小不会停止)