You never initialize deviceContext, which means that it will have a garbage value. If you call GetLastError after trying ChoosePixelFormat, I imagine that you'll find that it's telling you that you have an invalid handle.
OpenGL context creation never chooses pixel format
Question
I'm trying to create an OpenGL context inside a Win32 window for a library I'm writing. I've boiled it down to a small example which will compile to help debug it. For some reason, it never chooses a pixel format properly. Here's the code:
#include <iostream>
#include <windows.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/wglew.h>
HWND handle;
HDC deviceContext;
HGLRC context;
int resolutionX = 1280;
int resolutionY = 720;
std::string title = "Test Window";
LRESULT CALLBACK eventCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int main()
{
// Register the window class
WNDCLASSA windowClass;
windowClass.style = 0;
windowClass.lpfnWndProc = &eventCallback;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = GetModuleHandle(nullptr);
windowClass.hIcon = nullptr;
windowClass.hCursor = nullptr;
windowClass.hbrBackground = nullptr;
windowClass.lpszMenuName = nullptr;
windowClass.lpszClassName = "Test_Window";
RegisterClassA(&windowClass);
// Compute position and size
HDC screenDC = GetDC(nullptr);
int left = (GetDeviceCaps(screenDC, HORZRES) - static_cast<int>(resolutionX)) / 2;
int top = (GetDeviceCaps(screenDC, VERTRES) - static_cast<int>(resolutionY)) / 2;
int width = resolutionX;
int height = resolutionY;
ReleaseDC(nullptr, screenDC);
// Set window style
DWORD win32Style = WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_SYSMENU;
// In windowed mode, adjust width and height so that window will have the requested client area
RECT rectangle = {0, 0, width, height};
AdjustWindowRect(&rectangle, win32Style, false);
width = rectangle.right - rectangle.left;
height = rectangle.bottom - rectangle.top;
// Create the window
handle = CreateWindowA("Test_Window", title.c_str(), win32Style, left, top, width, height, NULL, NULL, GetModuleHandle(nullptr), nullptr);
if(handle == NULL)
{
std::cout << "Failed to create window" << std::endl;
return 1;
}
// Setup a pixel format descriptor from the rendering settings
PIXELFORMATDESCRIPTOR descriptor;
ZeroMemory(&descriptor, sizeof(descriptor));
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.iLayerType = PFD_MAIN_PLANE;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = static_cast<BYTE>(32);
descriptor.cDepthBits = static_cast<BYTE>(24);
descriptor.cStencilBits = static_cast<BYTE>(8);
descriptor.cAlphaBits = 8;
// Get the pixel format that best matches our requirements
int bestFormat = ChoosePixelFormat(deviceContext, &descriptor);
if(bestFormat == 0)
{
std::cout << "Failed to find a suitable pixel format for device context -- cannot create OpenGL context" << std::endl;
return 1;
}
// Extract the depth and stencil bits from the chosen format
PIXELFORMATDESCRIPTOR actualFormat;
actualFormat.nSize = sizeof(actualFormat);
actualFormat.nVersion = 1;
DescribePixelFormat(deviceContext, bestFormat, sizeof(actualFormat), &actualFormat);
// Set the chosen pixel format
if(!SetPixelFormat(deviceContext, bestFormat, &actualFormat))
{
std::cout << "Failed to set pixel format for device context -- cannot create OpenGL context" << std::endl;
return 1;
}
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = reinterpret_cast<PFNWGLCREATECONTEXTATTRIBSARBPROC>(wglGetProcAddress("wglCreateContextAttribsARB"));
if(wglCreateContextAttribsARB)
{
int attributes[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(3),
WGL_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(0),
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0, 0
};
context = wglCreateContextAttribsARB(deviceContext, nullptr, attributes);
}
if(!context)
{
context = wglCreateContext(deviceContext);
if(!context)
{
std::cout << "Failed to create the OpenGL context" << std::endl;
return 1;
}
}
// Destroy the OpenGL context
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(context);
// Destroy the device context
ReleaseDC(handle, deviceContext);
// Destroy the window handle
DestroyWindow(handle);
// Unregister the window
UnregisterClassA("Test_Window", GetModuleHandle(nullptr));
std::cout << "Window created" << std::endl;
return 0;
}
LRESULT CALLBACK eventCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Associate handle and Window instance when the creation message is received
if(message == WM_CREATE)
{
LONG_PTR window = (LONG_PTR)reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams;
// Set as the "user data" parameter of the window
SetWindowLongPtr(hwnd, GWLP_USERDATA, window);
}
return DefWindowProcA(hwnd, message, wParam, lParam);
}
Solution
OTHER TIPS
wglGetProcAddress
needs a valid OpenGL context being created and bound to yield a sensible result. Which means that in order to use wglCreateContextAttribsARB
you first have to create an intermediary helper OpenGL context that gives you access to that function.
Update
As it happens I recently wrote some small helper to deal with this. Available under the terms of the MIT license at https://github.com/datenwolf/wglarb
Try to add this on the initialization of pixel format descriptor:
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;