mya*_*lim 7 c++ opengl winapi gdi nvidia
我们有一个应用程序,我们使用GDI和OpenGL来绘制相同的HWND,但是只有.
例:
当切换到3d模式时,我们只为该HWND创建一个OpenGL上下文,我们可以使用OpenGL绘制它.当切换回2d模式时,我们只是破坏OpenGL上下文,我们可以使用GDI绘制到HWND.
直到最近,这种方法运作良好.在Windows 10上,对于某些NVidia卡,如果驱动程序比382.05更新,则此功能不再起作用.在这种情况下,当我们删除OpenGL上下文并使用GDI在HWND上绘图时,窗口仍然显示来自OpenGL的最后一个内容.
我检查了所有可用的像素格式.所有人都有同样的问题.
我们做错了什么,还是NVidia错误?你看到解决方案/解决方法吗?
它可能与NVidia + Intel双GPU设置有关,但至少有一个反例.我们有反馈的卡片:
不复制:
转载:
在OpenGL中绘制2d内容不是一种选择,反之亦然.此外,该应用程序对性能非常敏感,因此不能选择将2d内容绘制到屏幕外的GDI图像,以便将其绘制为OpenGL四元组.它也不是销毁和重新创建HWND的选项.
下面是重现该问题的示例应用程序.默认情况下,应用程序显示蓝色背景,其中一些文本处于GDI模式.在OpenGL模式下,显示了旋转三角形.空格键用于在模式之间切换.
#include <windows.h>
#include <GL/gl.h>
#include <iostream>
#pragma comment( lib, "OpenGL32.lib" )
HWND s_hwnd = 0;
HDC s_hdc = 0;
HGLRC s_hglrc = 0;
bool s_quit = false;
static HGLRC createContext(HWND hwnd, HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
int pf = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pf, &pfd);
return wglCreateContext(hdc);
}
static void display()
{
if (s_hglrc)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glIndexi(1);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 0.8f);
glIndexi(2);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(-0.8f, -0.8f);
glIndexi(3);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(0.8f, -0.8f);
glEnd();
glFlush();
SwapBuffers(s_hdc);
}
else
{
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
RECT rect;
GetClientRect(s_hwnd, &rect);
FillRect(s_hdc, &rect, brush);
DeleteObject(brush);
DrawText(s_hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
GdiFlush();
}
}
static void toggle_between_GDI_and_OpenGL()
{
if (!s_hglrc)
{
s_hglrc = createContext(s_hwnd, s_hdc);
wglMakeCurrent(s_hdc, s_hglrc);
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
}
else
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
s_hglrc = 0;
}
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_ERASEBKGND:
return 0;
case WM_PAINT:
display();
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_TIMER:
display();
return 0;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
s_quit = true;
break;
case ' ':
toggle_between_GDI_and_OpenGL();
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
}
return 0;
case WM_CLOSE:
s_quit = true;
return 0;
case WM_QUIT:
s_quit = true;
return 0;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static HWND CreateOpenGLWindow()
{
HWND hWnd;
WNDCLASS wc;
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
hWnd = CreateWindow(L"OpenGL", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0, 0, 256, 256, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
void executeApplication()
{
s_hwnd = CreateOpenGLWindow();
if (s_hwnd == NULL)
exit(1);
s_hdc = GetDC(s_hwnd);
//toggle_between_GDI_and_OpenGL(); // initialize OpenGL
ShowWindow(s_hwnd, SW_SHOW);
UpdateWindow(s_hwnd);
SetTimer(s_hwnd, 1, 50, NULL);
while (1) {
MSG msg;
while (PeekMessage(&msg, s_hwnd, 0, 0, PM_NOREMOVE)) {
if (!s_quit && GetMessage(&msg, s_hwnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
goto quit;
}
}
if (s_quit)
goto quit;
}
quit:
wglMakeCurrent(NULL, NULL);
if (s_hglrc)
toggle_between_GDI_and_OpenGL(); // uninitialize OpenGL
DestroyWindow(s_hwnd);
DeleteDC(s_hdc);
}
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
executeApplication();
return 0;
}
int main()
{
executeApplication();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
更新:@Ripi2和@datenwolf建议,一旦我们为没有PFD_SUPPORT_GDI标志的窗口设置像素格式,就不允许切换回GDI .这是SetPixelFormat文档的摘录:
多次设置窗口的像素格式会导致Window Manager和多线程应用程序出现严重的复杂情况,因此不允许这样做.
这是一个强有力的迹象,表明它们是正确的.
更新2:我曾声明不能重新创建HWND.但经过重新思考,这对我来说似乎是最简单的解决方案.
代码:
#include <windows.h>
#include <GL/gl.h>
#include <iostream>
#pragma comment( lib, "OpenGL32.lib" )
HWND s_mainWnd = 0;
HWND s_childWnd = 0;
HGLRC s_hglrc = 0;
bool s_isOpenGLMode = false;
bool s_quit = false;
static HWND CreateChildWindow(HWND hWndParent);
static HGLRC createContext(HWND hwnd, HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
int pf = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pf, &pfd);
return wglCreateContext(hdc);
}
static void display()
{
HDC hdc = GetDC(s_childWnd);
if (s_isOpenGLMode)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glIndexi(1);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 0.8f);
glIndexi(2);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(-0.8f, -0.8f);
glIndexi(3);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(0.8f, -0.8f);
glEnd();
glFlush();
SwapBuffers(hdc);
}
else
{
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
RECT rect;
GetClientRect(s_childWnd, &rect);
FillRect(hdc, &rect, brush);
DeleteObject(brush);
DrawText(hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
GdiFlush();
}
DeleteDC(hdc);
}
static void toggle_between_GDI_and_OpenGL()
{
if (!s_isOpenGLMode)
{
DestroyWindow(s_childWnd);
s_childWnd = CreateChildWindow(s_mainWnd);
ShowWindow(s_childWnd, SW_SHOW);
HDC hdc = GetDC(s_childWnd);
s_hglrc = createContext(s_childWnd, hdc);
wglMakeCurrent(hdc, s_hglrc);
DeleteDC(hdc);
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
RECT rect;
GetClientRect(s_childWnd, &rect);
glViewport(0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom));
}
else
{
if (s_hglrc)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
s_hglrc = 0;
}
DestroyWindow(s_childWnd);
s_childWnd = CreateChildWindow(s_mainWnd);
ShowWindow(s_childWnd, SW_SHOW);
}
s_isOpenGLMode = !s_isOpenGLMode;
}
LONG WINAPI WindowProc_MainWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_TIMER:
display();
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
s_quit = true;
break;
case ' ':
toggle_between_GDI_and_OpenGL();
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
}
return 0;
case WM_CLOSE:
case WM_QUIT:
s_quit = true;
return 0;
case WM_SIZE:
if (s_childWnd)
MoveWindow(s_childWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
break;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LONG WINAPI WindowProc_ChildWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_ERASEBKGND:
return 0;
case WM_PAINT:
display();
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
if (s_hglrc && s_isOpenGLMode)
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static HWND CreateMainWindow()
{
static HINSTANCE hInstance = 0;
if (!hInstance)
{
hInstance = GetModuleHandle(NULL);
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc_MainWnd;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MainWindow";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
HWND hWnd = CreateWindow(L"MainWindow", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
300, 300, 256, 256, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
static HWND CreateChildWindow(HWND hWndParent)
{
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance)
{
hInstance = GetModuleHandle(NULL);
WNDCLASS wc;
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc_ChildWnd;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"ChildWindow";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
RECT rect;
GetClientRect(hWndParent, &rect);
HWND hWnd = CreateWindow(L"ChildWindow", L"ChildWindow", WS_CHILD,
0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom), hWndParent, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
void executeApplication()
{
s_mainWnd = CreateMainWindow();
if (s_mainWnd == NULL)
exit(1);
s_childWnd = CreateChildWindow(s_mainWnd);
//toggle_between_GDI_and_OpenGL(); // initialize OpenGL
ShowWindow(s_mainWnd, SW_SHOW);
ShowWindow(s_childWnd, SW_SHOW);
UpdateWindow(s_mainWnd);
UpdateWindow(s_childWnd);
SetTimer(s_mainWnd, 1, 50, NULL);
while (1) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (!s_quit && GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
goto quit;
}
}
if (s_quit)
goto quit;
}
quit:
if (s_hglrc)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
}
DestroyWindow(s_childWnd);
DestroyWindow(s_mainWnd);
}
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
executeApplication();
return 0;
}
int main()
{
executeApplication();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
你发生了什么事,到现在为止你依赖于未定义的行为 - 实际上它本来就不应该起作用,而你很幸运.一旦在未设置PFD_SUPPORT_GDI标志的窗口上设置了双缓冲像素格式,就不再可以将其用于GDI操作.
OpenGL渲染上下文无关紧要!很多人 - 因为我相信它确实相信它的理由而让我理解 - 遭受误解,OpenGL以某种方式渲染上下文与特定的HDC或HWND相关联.没有东西会离事实很远.只要drawables pixelformat 与给定的OpenGL上下文兼容,该上下文就可以绑定到它.
而且因为你的OpenGL渲染上下文和窗口之间没有联系,所以破坏和重新创建上下文的所有小舞蹈都没有任何有意义的效果.也许,只是也许,那个小小的舞蹈触发了驱动程序中的一些代码路径,这使得你的非法行为在某种程度上起作用.但更有可能的是,你只是期望它做一些有用的事情,虽然它首先是完全虚假的,你可能只是没有做到这一点,达到同样的效果.
我们做错了吗
是的,是的,你是.你正在做的事情,从来没有被允许或应该首先工作.你只是没有注意到它,因为到目前为止操作系统/驱动程序没有利用提供给他们的摆动空间,这是不允许的.然而,GPU/OS /驱动程序的最新发展现在确实利用了摆动空间,并且只是压缩代码.
在OpenGL中绘制2d内容不是一种选择.
为什么?
在OpenGL中绘制2d内容不是一种选择,反之亦然.此外,该应用程序是非常性能敏感的,因此它不能选择将2d内容绘制到屏幕外的GDI图像,以便将其绘制为OpenGL四元组
你真的试过它吗?10块钱说这会表现得很好.
| 归档时间: |
|
| 查看次数: |
603 次 |
| 最近记录: |