Flickering when borderless window and desktop dimensions are the same
Asked Answered
L

0

9

I'm trying to create a borderless window that fills the screen with an OpenGL viewport. The problem is, when I set the window and viewport to be the same size as the desktop, the window flashes black on losing and gaining focus, on exit, and on creation. This may have to do with Windows setting the window to some type of true "fullscreen" mode. This doesn't seem to happen in other applications (DirectX?) that use this type of borderless fullscreen window.

I believe SFML has/had a variant of this problem.

There is a workaround: don't set the window to the exact size of the desktop. For instance, make the width of the window one pixel more than the width of the desktop.

Below is an SSCCE without error checking that shows what I'm talking about. This will create a borderless fullscreen window with a desktop-sized OpenGL viewport displaying an ugly green color (exit with AltF4). You can change the line #define FIX_BUG 0 to #define FIX_BUG 1 which just adds one extra pixel to the window's width to see what the behavior I want looks like.

#define WIN32_LEAN_AND_MEAN
#include "windows.h"

#pragma comment(lib, "OpenGL32.lib")

#define FIX_BUG 0

void MyRegisterClass(HINSTANCE hInstance);
HWND MyCreateWindow(HINSTANCE hInstance);
void MySetupOpenGLContext(HWND hWnd);
void MySetPixelFormat(HDC hDC);
RECT MyGetDesktopRect();
void MyLoadOpenGLFunctions();

#define APIENTRYP APIENTRY *
#define GLAPI extern
#define GL_COLOR 0x1800

typedef int GLint;
typedef int GLsizei;
typedef unsigned int GLenum;
typedef float GLfloat;

typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
PFNGLVIEWPORTPROC glViewport;

typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value);
PFNGLCLEARBUFFERFVPROC glClearBufferfv;

static const char *windowClass = "CLASS";
static const char *windowTitle = "TITLE";

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    MyRegisterClass(hInstance);
    const HWND hWnd = MyCreateWindow(hInstance);
    MySetupOpenGLContext(hWnd);
    ShowWindow(hWnd, nCmdShow);
    HDC hDC = GetDC(hWnd);

    MSG msg;
    while (IsWindow(hWnd)) {
        while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) DispatchMessage(&msg);

        const GLfloat color[] = {0.5f, 0.5f, 0.0f, 1.0f};
        glClearBufferfv(GL_COLOR, 0, color);

        SwapBuffers(hDC);
    }

    return 0;
}

void MyRegisterClass(HINSTANCE hInstance) {
    WNDCLASSEX wcex = {0};

    wcex.cbSize = sizeof wcex;
    wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wcex.lpfnWndProc = &DefWindowProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOWFRAME + 1);
    wcex.lpszClassName = windowClass;

    RegisterClassEx(&wcex);
}

HWND MyCreateWindow(HINSTANCE hInstance) {
    RECT dRect = MyGetDesktopRect();
    return CreateWindow(windowClass, windowTitle, WS_POPUP,
                        0, 0, dRect.right + FIX_BUG, dRect.bottom,
                        NULL, NULL, hInstance, NULL);
}

void MySetupOpenGLContext(HWND hWnd) {
    HDC hDC = GetDC(hWnd);
    MySetPixelFormat(hDC);
    HGLRC hGLRC = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hGLRC);
    MyLoadOpenGLFunctions();
    RECT dRect = MyGetDesktopRect();
    glViewport(0, 0, dRect.right, dRect.bottom);
}

void MySetPixelFormat(HDC hDC) {
    PIXELFORMATDESCRIPTOR pfd = {0};

    pfd.nSize = sizeof pfd;
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cStencilBits = 8;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int format = ChoosePixelFormat(hDC, &pfd);
    SetPixelFormat(hDC, format, &pfd);
}

RECT MyGetDesktopRect() {
    RECT desktopRect;
    const HWND hDesktop = GetDesktopWindow();
    GetWindowRect(hDesktop, &desktopRect);
    return desktopRect;
}

void MyLoadOpenGLFunctions() {
    glViewport = (PFNGLVIEWPORTPROC) GetProcAddress(GetModuleHandleA("OpenGL32.dll"), "glViewport");
    glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) wglGetProcAddress("glClearBufferfv");
}

Github Gist | Raw Text

Why does this happen? Is there a way to get the behavior I want with the window set to the exact size of the desktop?

My platform: Visual Studio 2013 / 64-bit Windows 8 / AMD Radeon HD6850.

Loincloth answered 17/4, 2014 at 23:36 Comment(3)
Windows Vista+ does tend to disable the DWM when you create an OpenGL window with the same dimensions as the desktop. WGL never really created a proper fullscreen/exclusive mode like D3D has. For all intents and purposes, a borderless desktop-sized window in GL behaves a lot like a proper exclusive D3D window. I'd ask you if disabling the DWM altogether has any affect, but this is Windows 8 and that is no longer an option :-\Banquer
You might also consider tying a different swap method. I think the default is swap_exchange. swap_copy might reduce the issue. Have a look here for PFD_SWAP_EXCHAGNE and PFD_SWAP_COPY.Banquer
@AndonM.Coleman Thanks for the insightful comments. I tried the different swap flags but they don't seem to change anything as far as I can tell. Also just to note, it seems that with #define FIX_BUG 0 the window gets around 1.5-2x the FPS since the DWM is disabled (obviously the gap would narrow if actual work was done in the loop).Loincloth

© 2022 - 2024 — McMap. All rights reserved.