调整大小时SDL2窗口变黑

时间:2016-01-23 19:12:13

标签: macos opengl sdl-2

我已经开始使用SDL2并且没有经验。我正在研究Mac系统。几乎一切都很好,但是我有一个问题,当调整可调整大小的窗口时,在拖动手柄时,窗口变黑,我只能在释放后重新绘制它。我已经检查过,当窗口正在调整大小时,没有事件被生成,我无法干涉或检测到这一点,因为事件循环只是暂停。有没有可能的解决方案?

这是代码(关于处理resize事件的教程的几乎副本):

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;
}

2 个答案:

答案 0 :(得分:4)

事实证明,这不是我的代码所特有的,并且它是MacOSX中所有OpenGL库的更广泛问题的一部分。尽管GLFW中最近的补丁修复了它,并且在随XCode本身提供的GLUT版本中,它更好,你只是在调整大小时观察窗口中的闪烁。

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

问题在于OSX窗口管理器的阻塞特性,它阻止了所有事件,直到鼠标被释放。

要解决此问题,您应该操作正在使用的库并重新编译它。您应该将这些或类似的东西(取决于您的开发环境)添加到resize处理程序以绕过该块:

ofNotifyUpdate();
instance->display();

这是灾难性的,如果你是一个新手,并希望能够毫无困难地使用库更新。另一种解决方案是通过编写另一个执行该操作的事件处理程序来覆盖SDL行为。它更好,因为它不需要编辑SDL代码,但增加了一堆平台特定的代码,我个人不倾向于并会导致很多我不想要的问题花时间修理。

经过2天的搜索,因为我刚刚启动项目并且没有多少依赖SDL,我决定切换到GLFW,它具有最流畅的调整大小处理,并且我没有观察到闪烁。

答案 1 :(得分:2)

好的,在与SDL2进行了一些战斗后,我得到了macOS 10.12。

问题在于:

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

    幸运的是,您可以使用SDL_SetEventFilter挂钩到事件处理程序。每次收到活动时都会触发。因此,对于发生的所有调整大小事件。

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

    示例:

    //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
    

    重要提示:请勿在{{1​​}}回调中调用SDL_PollEvent,因为这会导致卡住事件的奇怪行为。 (例如,调整大小不会停止)