We have an application where we use both GDI and OpenGL to draw to the same HWND, but exclusively.
Example:
initially we are in 2d mode, so we draw on it using GDI
then we switch to 3d mode and we draw on it using OpenGL
then we switch back to 2d mode and we draw on it using GDI
When switching to 3d mode, we just create an OpenGL context for that HWND and we can draw on it using OpenGL. When switching back to 2d mode, we just destroy the OpenGL context and we can draw to the HWND using GDI.
This worked very well until recently. On Windows 10, for some NVidia cards, this does not work any more if the driver is more recent than 382.05. In this case, when we delete the OpenGL context and draw on the HWND using GDI, the window still displays the last content from OpenGL.
I have checked all available pixel formats. All have the same issue.
Are we doing something wrong, or is it an NVidia bug? Do you see solutions / workarounds?
There is possibility that it is related to NVidia + Intel dual GPU setups, but there is at least one counterexample. Cards on for which we have feedback:
NOT REPRODUCED:
GTX 980M, single GPU
GTX 1060, single GPU
REPRODUCED:
GTX 1060 (Forceware 397.31) + Intel HD Graphics 630
Quadro M3000M (Forceware 387.95) + Intel HD Graphics P530
Qudrao K110M + Intel HD 4600
Quadro P3000 + Intel HD 630
Quadro M4000 (Forceware 385.90), single GPU
It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad. It is also not an option to destroy and recreate the HWND.
Below is a sample application to reproduce the issue. By default, the application shows a blue background with some text in GDI mode. In OpenGL mode a rotating triangle is shown. Space key is used to switch between modes.
#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;
}
Update: #Ripi2 and #datenwolf suggested that switching back to GDI is not allowed once we set a pixel format for the window that does not have PFD_SUPPORT_GDI flag. This is an excerpt from SetPixelFormat documentation:
Setting the pixel format of a window more than once can lead to significant complications for the Window Manager and for multithread applications, so it is not allowed.
That is a strong sign that they are correct.
Update 2: I had stated that it is not an option to recreate the HWND. But after rethinking, that seems to be the easiest solution to me.
The code:
#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;
}
What happened to you is, that up until now you relied on undefined behaviour – actually it never was supposed to work in the first place, and you were just lucky. Once you've set a doublebuffered pixelformat on a window that does not set the PFD_SUPPORT_GDI flag, you no longer can use it for GDI operations.
The OpenGL rendering context does not matter! A lot of people – it behooves me for what reason those who believe it do believe it – suffer from the misconception, that OpenGL render contexts in some way were tied to a particular HDC or HWND. Nothing could be further from the truth. As long as a drawables pixelformat is compatible to a given OpenGL context, that context can be bound to it.
And because there're no ties between your OpenGL render contexts and the windows, all that little dance of destroying and re-creating the context has no meaningful effect whatsoever. Maybe, just maybe, that little dance triggered some codepath in the driver, which made your illegal actions somehow work. But what's more likely is, that you just did the raindance in expectation for it to do something useful, while it was completely bogus in the first place, and you could just have not done it in the first place, to the same effect.
Are we doing something wrong,
Yes, yes you are. You are doing something, that never was allowed or supposed to work in the first place. You just didn't notice it, because so far OSs/drivers didn't make use of the wiggle room afforded to them by this not being allowed for you. However recent developments in GPU/OS/drivers now do make use of the wiggle room, and just steamroll over your code.
It is not an option to draw 2d content in OpenGL .
Why?
It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad
Did you actually try and profile it? 10 bucks says this would perform just fine.
A bit of search at OpenGL on Windows - Generic Implementation and Hardware Implementations reveals:
OpenGL and GDI graphics cannot be mixed in a double-buffered window.
An application can directly draw both OpenGL graphics and GDI graphics
into a single-buffered window, but not into a double-buffered window.
And also from PIXELFORMATDESCRIPTOR structure
PFD_SUPPORT_GDI: The buffer supports GDI drawing. This flag and
PFD_DOUBLEBUFFER are mutually exclusive in the current generic
implementation.
Since you are using OpenGL 1.1, just add PFD_SUPPORT_GDI flag to your PIXELFORMATDESCRIPTOR pfd
I would not destroy the GL context. Instead I would try to change the
glViewport(x0,y0,xs,ys);
to small area in some corner or even hidden like
glViewport(0,0,1,1);
That way even if bug persist the user will see only pixel artifact in corner somwhere most likely not even notice it (especially if rendered with the same color as background).
Related
When I call GLClear without swapping the buffer, it is causing memory leak.
I am not sure if this is a driver issue or if I am doing something wrong.
If anyone can give it a try or have any idea, please tell me.
TL;DR, this is the code summarized. You can find all the code after this block
static bool CloseFlag = false;
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
//Handle input....
return 0;
case WM_CLOSE:
CloseFlag = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
//Create window (with double buffer) and register it...
}
int main ()
{
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
gladLoadWGL(hDC)
gladLoadGL()
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
glClear(GL_COLOR_BUFFER_BIT);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
Here's a minimum code for reproducing it that uses Win32.
#ifndef UNICODE
#define UNICODE
#define _UNICODE
#endif
#include <windows.h> /* must include this before GL/gl.h */
#include "glad/glad.h"
#include "glad/glad_wgl.h"
#include "wglext.h"
#include <string>
static bool CloseFlag = false;
void display(HDC hDC)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(0, 1);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2i(-1, -1);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2i(1, -1);
glEnd();
glFlush();
SwapBuffers(hDC);
}
void JustClear()
{
glClear(GL_COLOR_BUFFER_BIT);
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
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 */
//PostQuitMessage(0);
CloseFlag = true;
break;
}
return 0;
case WM_CLOSE:
CloseFlag = true;
//PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
int pf;
HDC hDC;
HWND hWnd;
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
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)) {
return NULL;
}
}
std::wstring className = L"OpenGL";
hWnd = CreateWindow(className.c_str(), className.c_str(), WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
x, y, width, height, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
int main ()
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd; /* window */
MSG msg; /* message */
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
if (hWnd == NULL)
exit(1);
hDC = GetDC(hWnd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
if(!gladLoadWGL(hDC))
{
return -1;
}
if (!gladLoadGL()) //Load Glad
{
return -1;
}
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//Render
//display(hDC);
JustClear();
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
wglext.h is from here
Here's the OpenGL glad config
/*
OpenGL loader generated by glad 0.1.36 on Fri Oct 28 11:12:34 2022.
Language/Generator: C/C++
Specification: gl
APIs: gl=3.3
Profile: compatibility
Extensions:
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions=""
Online:
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3
*/
and Glad wgl 1.0 with all the extensions
and here's the CMake I used
cmake_minimum_required(VERSION 3.14)
set (CMAKE_CXX_STANDARD 11)
# For Clang to do parsing
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# set the project name
project(Example)
add_executable(EXAMPLE_EXE ${CMAKE_CURRENT_LIST_DIR}/Src/main.cpp)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/glad_v0.1.36")
find_package(OpenGL REQUIRED)
target_include_directories(EXAMPLE_EXE PUBLIC "${CMAKE_CURRENT_LIST_DIR}/External/wglExt")
target_link_libraries(EXAMPLE_EXE PUBLIC OpenGL::GL glad)
I created a child window using WinAPI, now I'm trying to draw a triangle there. It is drawn, but its scale is not correct.
I think this is due to the fact that I have not properly installed orthogonal system. It is set in the init() function, I call it when the child window is created in the WM_CREATE message, it is triggered, but the orthographic projection is still not set to the desired size. So I only see the bottom of the triangle.
#include <Windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")
//opengl values
int windowWidth = 600, windowHeight = 800, windowDepth = 600;
void init();
HWND childOpenGLWindowHWND = NULL;
HWND CreateOpenGLChildWindow(wchar_t* title, int x, int y, int width, int height,
BYTE type, DWORD flags, HWND hWndParent, HINSTANCE hInstance, WNDPROC wndProc)
{
int pf;
HDC hDC;
HWND hWnd;
PIXELFORMATDESCRIPTOR pfd;
WNDCLASS wc;
wc.style = CS_OWNDC;
wc.lpfnWndProc = wndProc;
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: "
L"Cannot register window class.", L"Error", MB_OK);
return NULL;
}
hWnd = CreateWindow(
L"OpenGL",
title,
WS_CHILD,
x,
y,
width,
height,
hWndParent,
NULL,
NULL,
NULL
);
if (!hWnd) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_TYPE_RGBA | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, L"ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.", L"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, L"SetPixelFormat() failed: "
"Cannot set format specified.", L"Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
wchar_t WinName[] = L"MainFrame";
int WINAPI WinMain(
HINSTANCE This,
HINSTANCE Prev,
LPSTR cmd,
int mode
)
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd;
MSG msg;
WNDCLASS wc;
wc.hInstance = This;
wc.lpszClassName = WinName;
wc.lpfnWndProc = WndProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
if (!RegisterClass(&wc)) return NULL;
int windowWidth = 800;
int windowHeight = 800;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
hWnd = CreateWindow(
WinName,
L"Title",
WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX,
(screenWidth - windowWidth) / 2,
(screenHeight - windowHeight) / 2,
windowWidth,
windowHeight,
HWND_DESKTOP,
NULL,
This,
NULL
);
if (!hWnd)
{
MessageBox(NULL, L"MAIN HWND ERROR!!!",
L"Error", MB_OK);
exit(1);
}
childOpenGLWindowHWND = CreateOpenGLChildWindow(
WinName,
0,
0,
600,
800,
NULL,
NULL,
hWnd,
This,
WndProc
);
if (!childOpenGLWindowHWND)
{
MessageBox(NULL, L"Child window init error", L"Error", MB_OK);
return 0;
}
hDC = GetDC(childOpenGLWindowHWND);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
SendMessage(hWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), NULL);
ShowWindow(hWnd, mode);
ShowWindow(childOpenGLWindowHWND, mode);
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(childOpenGLWindowHWND, hDC);
wglDeleteContext(hRC);
DestroyWindow(childOpenGLWindowHWND);
return msg.wParam;
//return NULL;
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 1, 0);
glVertex3f(-50, 0, 0);
glVertex3f(50, 0, 0);
glVertex3f(0, 50, 0);
glEnd();
glFlush();
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
init();
case WM_PAINT:
display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return NULL;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return NULL;
case WM_CHAR:
switch (wParam) {
case 27:
PostQuitMessage(0);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return NULL;
}
The WM_CREATE message is triggered by CreateWindow. This is before the OpenGL Context is created by wglCreateContext and made current by wglMakeCurrent.
Thus not any OpenGL instruction takes effect at this point.
I recommend to implement the WM_SHOWWINDOW message instead:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
switch (message)
{
// [...]
case WM_SHOWWINDOW:
BeginPaint(hWnd, &ps);
init();
EndPaint(hWnd, &ps);
return NULL;
case WM_PAINT:
BeginPaint(hWnd, &ps);
display();
EndPaint(hWnd, &ps);
return NULL;
// [...]
}
return NULL;
}
I'm trying to make a fullscreen transparent window on the desktop that i can render on with opengl.
I don't want to overflow the screen but so far that seems to be the only way i can. It's a pretty dirty hack imo and would hope someone knows of a professional solution.
Here's the code:
// libs needed to compile: opengl32 gdi32 dwmapi
#include <windows.h>
#include <GL/gl.h>
#include <dwmapi.h>
HDC hDC;
HGLRC hRC;
HWND hWnd;
bool running=true;
int w=GetSystemMetrics(SM_CXSCREEN),h=GetSystemMetrics(SM_CYSCREEN);
void CreateContext(){
PIXELFORMATDESCRIPTOR pfd; hDC=GetDC(hWnd);
SetPixelFormat(hDC,ChoosePixelFormat(hDC,&pfd),&pfd);
hRC=wglCreateContext(hDC); wglMakeCurrent(hDC,hRC);
}
void EnableTransparency(){DWM_BLURBEHIND b={DWM_BB_ENABLE|DWM_BB_BLURREGION,TRUE,CreateRectRgn(0,0,-1,-1)}; DwmEnableBlurBehindWindow(hWnd,&b);}
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
switch(uMsg){
case WM_CLOSE: running=false; return 0;
case WM_KEYDOWN: if(wParam==VK_ESCAPE){running=false;} return 0;
}return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
void CreateWin(){
WNDCLASS wc={};
wc.lpfnWndProc=WndProc;
wc.hInstance=GetModuleHandle(NULL);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpszClassName="OpenGL";
RegisterClass(&wc);
hWnd = CreateWindow(wc.lpszClassName,"Title",WS_POPUP,0,-1,w,h+1,0,0,wc.hInstance,0); // increasing height by 1 pixel
ShowWindow(hWnd,SW_SHOW);
EnableTransparency();
CreateContext();
}
void PumpMessages(){MSG msg; while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))DispatchMessage(&msg);}
int main(){
CreateWin();
glViewport(0,0,w,h); // the visible screen area (excluding h+1 overflow)
while(running){
PumpMessages();
glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); // fill solid black
glBegin(GL_TRIANGLES); // transparent triangular window
glColor4f(1,0,0,0.5f); glVertex3f( 0, 1,0); // red (center)
glColor4f(0,1,0,0.5f); glVertex3f(-1,-1,0); // green (left)
glColor4f(0,0,1,0.5f); glVertex3f( 1,-1,0); // blue (right)
glEnd();
SwapBuffers(hDC);
}
return 0;
}
When running your code, I don't actually get any transparency, so I can't understand why you are needing to overflow the screen. But here's how I changed your code to work for me, and without overflowing the screen.
// libs needed to compile: opengl32 gdi32 dwmapi
#include <windows.h>
#include <GL/gl.h>
#include <dwmapi.h>
HDC hDC;
HGLRC hRC;
HWND hWnd;
bool running = true;
int w = GetSystemMetrics(SM_CXSCREEN), h = GetSystemMetrics(SM_CYSCREEN);
void CreateContext() {
PIXELFORMATDESCRIPTOR pfd; hDC = GetDC(hWnd);
SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd);
hRC = wglCreateContext(hDC); wglMakeCurrent(hDC, hRC);
}
void EnableTransparency() {
SetLayeredWindowAttributes(hWnd, NULL, NULL, NULL);
const MARGINS margins = { -1 };
DwmExtendFrameIntoClientArea(hWnd, &margins);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE: running = false; return 0;
case WM_KEYDOWN: if (wParam == VK_ESCAPE) { running = false; } return 0;
}return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void CreateWin() {
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = "OpenGL";
RegisterClass(&wc);
hWnd = CreateWindowEx(WS_EX_LAYERED, wc.lpszClassName, "Title", WS_POPUP, 0, 0, w, h, 0, 0, wc.hInstance, 0);
ShowWindow(hWnd, SW_SHOW);
CreateContext();
EnableTransparency();
}
void PumpMessages() { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))DispatchMessage(&msg); }
int main() {
CreateWin();
glViewport(0, 0, w, h); // the visible screen area
while (running) {
PumpMessages();
glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); // fill solid black
glBegin(GL_TRIANGLES); // transparent triangular window
glColor4f(1, 0, 0, 0.5f); glVertex3f(0, 1, 0); // red (center)
glColor4f(0, 1, 0, 0.5f); glVertex3f(-1, -1, 0); // green (left)
glColor4f(0, 0, 1, 0.5f); glVertex3f(1, -1, 0); // blue (right)
glEnd();
SwapBuffers(hDC);
}
return 0;
}
Checking error codes has been left out. Essentially, we use a window with the layered style to get transparency. We call SetLayeredWindowAttributes with no flags, because we are not using any of these layered window features, but we need to call the function for the window to become visible.
My goal is to create a basic tab control using the Win32 API that contains a canvas for rendering OpenGL. My tab contains a static control for rendering OpenGL. However, the only way I can get the canvas to appear in the GUI is to exclude the tab control (comment out the CREATE_TAB_PANE macro in my example to do this).
My example is given below:
// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out
#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")
#define CREATE_TAB_PANE
enum { IDC_TAB = 200, IDC_CANVAS = 201 };
static HWND TabPaneId;
static HWND CanvasId;
static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;
static HDC CanvasDc;
////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam) {
switch (msg) {
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam) {
if (msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
wglMakeCurrent(CanvasDc, CanvasRc);
SwapBuffers(CanvasDc);
return 0;
}
return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}
////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {
glClearColor(0.0, 0.0, 0.0, 0.0);
}
////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3d(1, 0, 0);
glBegin(GL_LINE_STRIP);
glVertex2d(0.2, 0.2);
glVertex2d(0.8, 0.8);
glEnd();
SwapBuffers(CanvasDc);
}
////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd) {
// Create window
const char *className = "OpenGlTab";
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
exit(0);
}
HWND winId = CreateWindowEx(0,
className,
"Tab Pane w/ OpenGL",
WS_OVERLAPPEDWINDOW,
100, 100, 600, 400,
0,
0,
hInstance,
0);
if (!winId) {
exit(0);
}
ShowWindow(winId, nShowCmd);
UpdateWindow(winId);
#ifdef CREATE_TAB_PANE
// Create tab pane
TabPaneId = CreateWindow(WC_TABCONTROL,
0,
WS_CHILD | WS_VISIBLE,
10, 10, 566, 343,
winId,
HMENU(IDC_TAB),
hInstance,
0);
TCITEM tabItem = {0};
tabItem.mask = TCIF_TEXT;
tabItem.pszText = "Tab";
SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));
#endif
// Create OpenGL canvas
int w = 200;
int h = 200;
CanvasId = CreateWindow("static",
"",
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
20, 100, w, h,
winId,
HMENU(IDC_CANVAS),
hInstance,
0);
CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
GWLP_WNDPROC,
(LONG_PTR)OpenGlCanvasProc);
CanvasDc = GetDC(CanvasId);
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1, // version
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // color depth
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
16, // depth buffer
0,
0,
0,
0,
0, 0, 0
};
int pixelFormat = ChoosePixelFormat(CanvasDc, &pfd);
SetPixelFormat(CanvasDc, pixelFormat, &pfd);
CanvasRc = wglCreateContext(CanvasDc);
// Render OpenGL canvas
wglMakeCurrent(CanvasDc, CanvasRc);
CanvasResize(w, h);
CanvasInit();
CanvasDisplay();
SwapBuffers(CanvasDc);
// Execute GUI
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
My updated example with changes per Chris:
// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out
#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")
#define CREATE_TAB_PANE
enum { IDC_TAB = 200, IDC_CANVAS = 201 };
static HWND TabPaneId;
static HWND CanvasId;
static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;
////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam) {
switch (msg) {
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {
glClearColor(0.0, 0.0, 0.0, 0.0);
}
////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3d(1, 0, 0);
glBegin(GL_LINE_STRIP);
glVertex2d(0.2, 0.2);
glVertex2d(0.8, 0.8);
glEnd();
}
////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam) {
switch (msg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
wglMakeCurrent(hdc, CanvasRc);
CanvasDisplay();
SwapBuffers(hdc);
wglMakeCurrent(NULL, NULL);
ReleaseDC(hwnd, hdc);
EndPaint(hwnd, &ps);
return 0;
}
case WM_SIZE:
{
HDC hdc = GetDC(hwnd);
wglMakeCurrent(hdc, CanvasRc);
CanvasResize(LOWORD(lParam), HIWORD(lParam));
wglMakeCurrent(NULL, NULL);
ReleaseDC(hwnd, hdc);
return 0;
}
}
return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}
////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd) {
// Create window
const char *className = "OpenGlTab";
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
exit(0);
}
HWND winId = CreateWindowEx(0,
className,
"Tab Pane w/ OpenGL",
WS_OVERLAPPEDWINDOW,
100, 100, 600, 400,
0,
0,
hInstance,
0);
if (!winId) {
exit(0);
}
ShowWindow(winId, nShowCmd);
UpdateWindow(winId);
#ifdef CREATE_TAB_PANE
// Create tab pane
TabPaneId = CreateWindow(WC_TABCONTROL,
0,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
10, 10, 566, 343,
winId,
HMENU(IDC_TAB),
hInstance,
0);
TCITEM tabItem = {0};
tabItem.mask = TCIF_TEXT;
tabItem.pszText = "Tab";
SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));
#endif
// Create OpenGL canvas
int w = 200;
int h = 200;
CanvasId = CreateWindow("static",
"",
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
20, 100, w, h,
winId,
HMENU(IDC_CANVAS),
hInstance,
0);
CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
GWLP_WNDPROC,
(LONG_PTR)OpenGlCanvasProc);
HDC hdc = GetDC(CanvasId);
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1, // version
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // color depth
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
16, // depth buffer
0,
0,
0,
0,
0, 0, 0
};
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
CanvasRc = wglCreateContext(hdc);
// Render OpenGL canvas
wglMakeCurrent(hdc, CanvasRc);
CanvasResize(w, h);
CanvasInit();
CanvasDisplay();
SwapBuffers(hdc);
wglMakeCurrent(NULL, NULL);
ReleaseDC(CanvasId, hdc);
// Execute GUI
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
You have a number of things going on in this sample that are all possibly contributing:
The most immediate issue might simply be that the tab window is covering the canvas window and you are not actually painting anything in response to WM_PAINT. The tab control is going to paint over your canvas as soon as its invalidated as Windows usually lets child windows paint all over each other; so adding WS_CLIPSIBLINGS to the tab control might help.
You are grabbing an HDC to the static control and holding on to it after associating it with the current wgl context. You should not really do this unless you are using a window class with CS_OWNDC and especially not with one that probably has CS_PARENTDC (because then, as soon as the parent - or a different child - window paints, the DC is re-associated with a window that never had SetPixelFormat associated with it).
You are just making your opengl context current, and expecting it to be set later. This is fine - assuming you have a CS_OWNDC window with an HDC you can grab up front and keep around - and also assuming you never want to create a 2nd GL context for any reason.
So, when doing OpenGL in an application where you are not controlling the window class styles (or there might be more than one OpenGL context) you need to ensure that you always clear the current context and release the DC as soon as you are done with it.
For example, your CanvasWindowProc should look more like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,&ps);
wglMakeCurrent(glrc,hdc);
CanvasDisplay();
SwapBuffers();
wglMakeCurrent(NULL,NULL);
EndPaint(&ps);
}
return 0;
case WM_SIZE:
{
HDC hdc = GetDC(hwnd);
wglMakeCurrent(glrc,hdc);
CanvasResize(LOWORD(lParam),HIWORD(lParam));
wglMakeCurrent(NULL,NULL);
ReleaseDC(hwnd,hdc);
}
break;
My problem is that my program isn't displaying anything on screen.
This is the "main.cpp" code:
#include "paStdAfx.h"
#include "OpenGL.h"
HDC hDC = NULL;
HGLRC hRC = NULL;
HWND hwnd = NULL;
HINSTANCE hInstance = GetModuleHandle(NULL);
const wchar_t* szClassName = _T("*project name :3*");
static std::wstring Titles[] = {
_T("*project name :3* - 100% free!"),
_T("*project name :3* - 100% OpenGL!"),
_T("*project name :3* - Not cross platform!"),
_T("*project name :3* - Rawr"),
_T("*project name :3* - Entirely C++!"),
_T("*project name :3* - Woo, /r/gamedev!"),
_T("*project name :3* - Platypi, platypi everywhere."),
_T("*project name :3* - Nom nom nom"),
_T("*project name :3* - Thanks, StackExchange!"),
_T("*project name :3* - DRM Free!"),
_T("*project name :3* - <3"),
_T("*project name :3* - Minecraft is also fun!")
};
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CreateOpenGLWindow(const wchar_t*, int, int, int);
OpenGL ogl;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
{
srand(time(NULL));
const std::wstring wtt = Titles[rand() % 11];
const wchar_t* WindowTitle = wtt.c_str();
BOOL done = FALSE;
MSG msg;
if(!CreateOpenGLWindow(WindowTitle, 800, 600, 32)){ MessageBox(NULL, _T("Could not create window :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
done = TRUE;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
ogl.RenderGLScene();
SwapBuffers(hDC);
}
}
ogl.KillOpenGL(hwnd, hDC, hRC);
return 0;
}
BOOL CreateOpenGLWindow(const wchar_t* title, int width, int height, int bits)
{
WNDCLASSEX wcx = {0};
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_OWNDC;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PROGRAMICON));
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wcx.lpszClassName = szClassName;
wcx.lpszMenuName = NULL;
wcx.hIconSm = (HICON) LoadImage(hInstance, MAKEINTRESOURCE(IDI_PROGRAMICON), IMAGE_ICON, 16, 16, 0);
if(!RegisterClassEx(&wcx)){ MessageBox(NULL, _T("Failed to register window!"), _T("Error! :("), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
if(!(hwnd = CreateWindowEx(dwExStyle, szClassName, title, dwStyle, 200, 69, width, height, NULL, NULL, hInstance, NULL))){ MessageBox(NULL, _T("Failed to create the window!"), _T("Error! :("), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
ogl.CreateOpenGLContext(hwnd, &hDC, &hRC);
ogl.PrepareOpenGLScene();
ogl.ResizeGLScene(width, height);
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
SetFocus(hwnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
ogl.ProgramIcon(hwnd);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
and this is the "OpenGL.cpp" code:
#include "paStdAfx.h"
#include "OpenGL.h"
GLvoid OpenGL::CreateOpenGLContext(HWND hwnd, HDC* hDC, HGLRC* hRC)
{
PIXELFORMATDESCRIPTOR pfd;
HGLRC tempContext;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;
*hDC = GetDC(hwnd);
int PixelFormat = ChoosePixelFormat(*hDC, &pfd);
if(PixelFormat == 0){ MessageBox(NULL, _T("Could not choose pixel format :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
if(!SetPixelFormat(*hDC, PixelFormat, &pfd)){ MessageBox(NULL, _T("Could not set pixel format :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(3); }
tempContext = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, tempContext);
GLenum err = glewInit();
if(GLEW_OK != err){ MessageBox(NULL, _T("Failed to initialize GLEW! :("), _T("Warning!"), MB_OK | MB_ICONINFORMATION); }
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
0
};
if(!glewIsSupported("GL_VERSION_3_1")){ MessageBox(NULL, _T("OpenGL 3.1 not supported :("), _T("Warning!"), MB_OK | MB_ICONERROR); }
if(wglewIsSupported("WGL_ARB_create_context") == 1)
{
*hRC = wglCreateContextAttribsARB(*hDC, 0, attribs);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(tempContext);
wglMakeCurrent(*hDC, *hRC);
}
else{ *hRC = tempContext; }
const char* GLVersionString = (char*) glGetString(GL_VERSION);
int OpenGLVersion[2];
glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]);
glGetIntegerv(GL_MINOR_VERSION, &OpenGLVersion[1]);
}
GLvoid OpenGL::PrepareOpenGLScene(GLvoid)
{
glClearColor(0.0f, 0.6f, 1.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}
GLvoid OpenGL::RenderGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
/* Begin OpenGL Rendering */
/* End OpenGL Rendering */
}
GLvoid OpenGL::ResizeGLScene(int w, int h)
{
float ratio = 1.0 * w / h;
if(h == 0)
h = 1;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, ratio, 0.1f, 1000.0f);
glViewport(0, 0, w, h);
}
GLvoid OpenGL::ProgramIcon(HWND hwnd)
{
HICON hIcon, hIconSm;
hIcon = (HICON) LoadImage(NULL, _T("data/icon.ico"), IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(NULL, _T("Could not load the big icon! :("), _T("Error!"), MB_OK | MB_ICONERROR);
//---------------------------------------------------------------------------------------------//
hIconSm = (HICON) LoadImage(NULL, _T("data/icon.ico"), IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(NULL, _T("Could not load the small icon! :("), _T("Error!"), MB_OK | MB_ICONERROR);
}
GLvoid OpenGL::KillOpenGL(HWND hwnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hwnd, hDC);
}
All the rendering code would go inside the "RenderGLScene" function inside of OpenGL.cpp, but when I place code to render basic things like trianlges or squares on the screen nothing appears. I have tried tinkering with the "gluLookAt()" function and the "gluPerspective()" function as well because I think those might be the source of my problem. I have tried both VBO and the older method that uses glBegin()/glEnd().
You create a double buffered context, but I don't see you performing a buffer swap (wglSwapBuffers) after finishing the rendering. Without a buffer swap you won't see anything.
What does your render code look like. Remember that the camera always is placed in the origin, which means that you need to draw things at negative z coordinates.
If you created a core context all drawing must be done using a VAO. Check the glError() return codes to see what the glDraw*() reports back.