StretchDIBits isn't working when it has an opengl context and is in fullscreen mode

I have been writing a platform layer and initially I started with software rendering using StretchDIBits. I also added the toggle fullscreen function that was used in this series.

Software Blit:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void PfBlitToScreen(PfWindow *window)
{
        // Using CS_OWNDC, hence cached the deviceContext handle.
        HDC deviceContextHandle = window->deviceContext;
        int32 scanLines = StretchDIBits(deviceContextHandle,
                                        0, 0,window->offscreenBuffer.width, window->offscreenBuffer.height,
                                        0,0,window->offscreenBuffer.width, window->offscreenBuffer.height,
                                        window->offscreenBuffer.data, &(window->offscreenBuffer.info),DIB_RGB_COLORS,SRCCOPY);
        
        ASSERT(scanLines);
}



ToggleFullscreen:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void PfToggleFullscreen(PfWindow *window)
{
    DWORD windowStyle = GetWindowLong(window->windowHandle, GWL_STYLE);
    if(windowStyle & WS_OVERLAPPEDWINDOW)
    {
        MONITORINFO monitorInfo = {sizeof(monitorInfo)};
        if(GetWindowPlacement(window->windowHandle, &(window->prevWindowPlacement)) &&
           GetMonitorInfo(MonitorFromWindow(window->windowHandle, MONITOR_DEFAULTTOPRIMARY), &monitorInfo))
        {
            SetWindowLong(window->windowHandle, GWL_STYLE, windowStyle & ~WS_OVERLAPPEDWINDOW);
            SetWindowPos(window->windowHandle, HWND_TOP,
                         monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top,
                         monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
                         monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top,
                         SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
            window->fullscreen = true;
        }
    }
    else
    {
        SetWindowLong(window->windowHandle, GWL_STYLE, windowStyle | WS_OVERLAPPEDWINDOW);
        SetWindowPlacement(window->windowHandle, &(window->prevWindowPlacement));
        SetWindowPos(window->windowHandle, 0, 0, 0, 0, 0,
                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
                     SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
        window->fullscreen = false;
    }
}



This worked as expected.
Later I added OpenGL context and still kept the option to use the PfBlitToScreen()
However when the window toggled to fullscreen mode, it would just clear the screen black.
I tried to find out the cause by stepping through the debugger but it turns out that when I stepped through the function it would output the image properly.

I later changed the PfBlitToScreen() functions such that when the window is in fullscreen mode and when the window has an OpenGL context, I would use "hardware blit" rather than "software blit" i.e. I would send the offscreen buffer as a texture to OpenGL. Having made this change, the image was properly outputted in fullscreen mode.

Modified PfBlitToScreen():
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void PfBlitToScreen(PfWindow *window)
{
    /* HACK(KARAN): stretchdibits doesn't work in fullscreen mode
    when the window has an opengl context. So when in fullscreen mode
    and if we have a glContext, use opengl texture rendering path.
    */

    if(window->fullscreen && (window->glContext != 0))
    {
        PfglMakeCurrent(window);
        PfglRenderWindow(window);
        PfglSwapBuffers(window);
    }
    else
    {
        HDC deviceContextHandle = window->deviceContext;//GetDC(window->windowHandle);
        int32 scanLines = StretchDIBits(deviceContextHandle,
                                        0, 0,window->offscreenBuffer.width, window->offscreenBuffer.height,
                                        0,0,window->offscreenBuffer.width, window->offscreenBuffer.height,
                                        window->offscreenBuffer.data, &(window->offscreenBuffer.info),DIB_RGB_COLORS,SRCCOPY);
        
        ASSERT(scanLines);
    }
}


So my question is why does this happen and is there a way to fix this so that I can use StretchDIBits even in fullscreen OpenGL context mode.

Edited by Karan Joisher on Reason: Initial post
Modern windows does a lot of optimizations when window is fully covering monitor. It bypasses compositor to have less lag when flipping buffers. Probably this also bypasses GDI rendered output.

I would avoid calling GDI functions on DC that has GL context created (HGLRC).