Skip to content

Instantly share code, notes, and snippets.

@Qartar
Created July 30, 2020 00:00
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Qartar/920597e672d9ab8f3c4a709d770012ee to your computer and use it in GitHub Desktop.
Minimal repro for Intel Graphics OpenGL driver issue
#include <Windows.h>
#include <gl/GL.h>
#include <cmath>
#pragma comment(lib, "opengl32")
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_DEPTH_ATTACHMENT 0x8D00
#define GL_DRAW_FRAMEBUFFER 0x8CA9
static int width = 512;
static int height = 512;
extern "C" {
// Uncomment the following line to test with Nvidia on Optimus-enabled hardware.
//__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
// Uncomment the following line to test with AMD on PowerXpress-enabled hardware.
//__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
}
LRESULT __stdcall WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_SIZE:
width = LOWORD(lParam);
height = HIWORD(lParam);
break;
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
bool MessagePump()
{
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (!GetMessageW(&msg, NULL, 0, 0)) {
return false;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return true;
}
int __stdcall WinMain(HINSTANCE hinstance, HINSTANCE, LPSTR, int)
{
//
// Window initialization
//
constexpr wchar_t classname[] = L"GlWindowClassName";
WNDCLASSEXW wcex{
sizeof(wcex),
0,
WndProc,
0,
0,
hinstance,
NULL,
LoadIconW(hinstance, IDC_ARROW),
(HBRUSH)0,
NULL,
classname,
NULL
};
RegisterClassExW(&wcex);
HWND hwnd = CreateWindowExW(
0,
classname,
L"glwindow",
WS_OVERLAPPED | WS_THICKFRAME | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
width,
height,
NULL,
NULL,
hinstance,
NULL);
//
// OpenGL initialization
//
PIXELFORMATDESCRIPTOR pfd = {
sizeof(pfd),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_TYPE_RGBA,
24,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24, 8, 0,
PFD_MAIN_PLANE,
0, 0, 0, 0,
};
HDC hdc = GetDC(hwnd);
int pf = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pf, &pfd);
HGLRC hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
// Explicitly set swap interval (workaround for separate Intel bug)
// cf. https://software.intel.com/en-us/forums/graphics-driver-bug-reporting/topic/856923
auto wglSwapIntervalEXT = (BOOL (APIENTRY*)(GLint interval))wglGetProcAddress("wglSwapIntervalEXT");
wglSwapIntervalEXT(1);
// Create off-screen render target
auto glCreateFramebuffers = (void (APIENTRY*)(GLsizei n, GLuint* framebuffers))wglGetProcAddress("glCreateFramebuffers");
auto glDeleteFramebuffers = (void (APIENTRY*)(GLsizei n, GLuint* framebuffers))wglGetProcAddress("glDeleteFramebuffers");
auto glBindFramebuffer = (void (APIENTRY*)(GLenum target, GLuint framebuffer))wglGetProcAddress("glBindFramebuffer");
auto glBlitNamedFramebuffer = (void (APIENTRY*)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter))wglGetProcAddress("glBlitNamedFramebuffer");
auto glNamedFramebufferTexture = (void (APIENTRY*)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level))wglGetProcAddress("glNamedFramebufferTexture");
GLuint fb; glCreateFramebuffers(1, &fb);
GLuint tex[2]; glGenTextures(2, tex);
glBindTexture(GL_TEXTURE_2D, tex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, tex[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
glNamedFramebufferTexture(fb, GL_COLOR_ATTACHMENT0, tex[0], 0);
glNamedFramebufferTexture(fb, GL_DEPTH_ATTACHMENT, tex[1], 0);
//
// Main loop
//
int prev_width = width;
int prev_height = height;
// Pump messages until the window is closed. Update `width` and `height`
// with the client area dimensions when a WM_SIZE message is received.
for (int n = 0; MessagePump(); ++n) {
#if 0 // Enable this check to workaround the issue
if (width != prev_width || height != prev_height) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
// glClear will invalidate the default framebuffer dimensions
glClear(0);
prev_width = width;
prev_height = height;
}
#endif
// Clear the off-screen render target for visual contrast
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glClearColor(
std::sin(n / 61.f) * .5f + .5f,
std::sin(n / 79.f) * .5f + .5f,
std::sin(n / 113.f) * .5f + .5f,
0.f);
glClear(GL_COLOR_BUFFER_BIT);
// When the window is resized the GL will not recognize that the
// dimensions of the default framebuffer have changed until it is
// cleared. This causes the blit operation to fail to copy the source
// framebuffer contents into the full extents of the default framebuffer.
// This issue does not reproduce with Nvidia graphics.
glBlitNamedFramebuffer(fb, 0, 0, 0, 1, 1, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SwapBuffers(hdc);
}
//
// OpenGL shutdown
//
glDeleteFramebuffers(1, &fb);
glDeleteTextures(2, tex);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hrc);
ReleaseDC(hwnd, hdc);
//
// Window shutdown
//
CloseWindow(hwnd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment