5

I'm executing the following code to create a core-profile OpenGL context.

Specifically, I am:

  1. Creating a dummy window
  2. Using this dummy window to request an OpenGL context (I assume it would be hardware accelerated, but I'm not sure that would even matter)
  3. Using this OpenGL context to load OpenGL function pointers
  4. With these function pointers, I then attempt to use wglCreateContextAttribsARB to create second context specifically with a core profile in a second window.

Code:

WNDCLASSW wcDummy = {0};
wcDummy.lpfnWndProc     = +[](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){return DefWindowProcW(hWnd, message, wParam, lParam);};
wcDummy.hInstance       = GetModuleHandle(0);
wcDummy.hbrBackground   = (HBRUSH)(COLOR_BACKGROUND);
wcDummy.lpszClassName   = L"Dummy";
wcDummy.style           = CS_OWNDC;

if(!RegisterClassW(&wcDummy))
{
    get_and_print_error();
    return false;
}

HWND windowDummy = CreateWindowW(wcDummy.lpszClassName, title.c_str(), WS_DISABLED, 0, 0, 640, 480, 0, 0, wcDummy.hInstance, NULL);
if(windowDummy == NULL)
{
    get_and_print_error();
    return false;
}

PIXELFORMATDESCRIPTOR pfdDummy =
{
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    24,
    8,
    0, 0, 0, 0, 0, 0
};

HDC dummyDrawingContext = GetDC(windowDummy);

INT pixelFormatDummy = ChoosePixelFormat(dummyDrawingContext, &pfdDummy);
SetPixelFormat(dummyDrawingContext, pixelFormatDummy, &pfdDummy);

HGLRC dummyContext = wglCreateContext(dummyDrawingContext);
wglMakeCurrent(dummyDrawingContext, dummyContext);

if(wglGetCurrentContext() != NULL)
{
    load_gl_functions();
}
else
    return false;

PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;

GLint64 numExtensions;
glGetInteger64v(GL_NUM_EXTENSIONS, &numExtensions);
std::cout << "Available Extensions:\n";
for(GLint64 i = 0; i < numExtensions; ++i)
{
    const GLubyte* extensionName = glGetStringi(GL_EXTENSIONS, i);

    std::cout << "\n\t" << (const char*)extensionName;

    if(std::strcmp((const char*)extensionName, "WGL_ARB_create_context") == 0)
    {
        wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
    }
}
std::cout << std::endl;

wglDeleteContext(dummyContext);
DestroyWindow(windowDummy);

WNDCLASSW wc = {0};
wc.lpfnWndProc      = Window::WndProc;
wc.hInstance        = GetModuleHandle(0);
wc.hbrBackground    = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName    = title.c_str();
wc.style = CS_OWNDC;

if(!RegisterClassW(&wc))
{
    get_and_print_error();
    return false;
}

HWND window = CreateWindowW(wc.lpszClassName, title.c_str(), WS_OVERLAPPED|WS_VISIBLE|WS_SYSMENU ,0,0,640,480,0,0,wc.hInstance,this);
if(window == NULL)
{
    get_and_print_error();
    return false;
}

PIXELFORMATDESCRIPTOR pfd =
{
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    24,
    8,
    0, 0, 0, 0, 0, 0
};

HDC m_drawingContext = GetDC(window);

INT pixelFormat = ChoosePixelFormat(m_drawingContext, &pfd);
SetPixelFormat(m_drawingContext, pixelFormat, &pfd);

const GLint attribList[] =
{
    WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
    WGL_CONTEXT_MINOR_VERSION_ARB, 4,
    WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
    0
};

m_glRenderContext = wglCreateContextAttribsARB(m_drawingContext, 0, attribList);

wglMakeCurrent(m_drawingContext, m_glRenderContext);

if(wglGetCurrentContext() != NULL)
{
    load_gl_functions();
}
else
    return false;

const GLubyte* driver = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);

std::wcout << "Device:       " << std::wstring(convert_gl_string_to_win32_string(driver)) << std::endl;
std::wcout << "GL Version:   " << std::wstring(convert_gl_string_to_win32_string(version)) << std::endl;
std::wcout << "GLSL Version: " << std::wstring(convert_gl_string_to_win32_string(glslVersion)) << std::endl;
std::wcout << std::endl;

The problem is that the WGL_ARB_create_context extension does not exist.

However, if I forget about checking the list of extensions, i.e. forgo the loop and simply:

wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");

I end up with a function pointer and everything works correctly.

Why does my list of available extensions not provide the WGL_ARB_create_context string, even though the extension exists?

EDIT I have an AMD Radeon HD 7900 seriesY

NeomerArcana
  • 1,978
  • 3
  • 23
  • 50
  • https://gamedev.stackexchange.com/questions/86892/wglcreatecontextattribsarb-is-undefined – Hans Passant Aug 29 '17 at 11:33
  • @HansPassant: Not really related. The question is `Why is WGL_ARB_create_context not included in the extension list although it is available?". – BDL Aug 29 '17 at 11:51
  • @BDL I have a AMD 7950; so it's not driver or hardware specific. – NeomerArcana Aug 29 '17 at 11:57
  • 1
    re: the `wcDummy.lpfnWndProc` init: Neat, didn't know about [no-capture lambdas being convertible to regular function pointers](https://stackoverflow.com/a/18889029/44729). – genpfault Aug 29 '17 at 13:07
  • @genpfault nice little trick – NeomerArcana Aug 29 '17 at 13:26
  • @genpfault In my dummy GL window I just use L"STATIC" as the class name. Seems to work fine. I wonder if this is a terrible boob by me? It means I don't have to register a class at all. – Robinson Aug 29 '17 at 19:10

3 Answers3

8

Why does my list of available extensions not provide the WGL_ARB_create_context string, even though the extension exists?

Because WGL extensions are not required to be advertised via the GL extension strings. There is the WGL_ARB_extension_string extension which controls the advertisement of WGL extensions. Quoting from that spec:

Applications should call wglGetProcAddress to see whether or not wglGetExtensionsStringARB is supported. If it is supported then it can be used to determine which WGL extensions are supported by the device.

So, to not create any recursive issue, the function pointer is guaranteed to be valid if it is non-NULL.

If you wonder why some WGL extensions are still in the GL extension string: THis is a legacy thing once more. Quoting issue 1 from that extenstion spec again:

Note that extensions that were previously advertised via glGetString (e.g., the swap interval extension) should continue to be advertised there so existing applications don't break. They should also be advertised via wglGetExtensionsStringARB so new applications can make one call to find out which WGL extensions are supported.

Side note:

Your GL extension query mechanism using glGetStringi will only work beginning with GL 3.0. Both glGetInteger64v and glGetStringi might not be available, and this code will most likely just crash if run on some older GPU, or when falling back to Microsoft's GL 1.1 renderer.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
derhass
  • 43,833
  • 2
  • 57
  • 78
3

The reason why the extension WGL_ARB_create_context is not in the list of supported extensions is because it is technically not an OpenGL extension but a WGL extension. glGetStringi only returns OpenGL extensions but not WGL ones1.

To query WGL extensions, the WGL_ARB_extensions_string extension has to be used which provides the wglGetExtensionsStringARB method. The only way to check whether this extension is available is to query the address of wglGetExtensionsStringARB and see whether it returns 0.

Applications should call wglGetProcAddress to see whether or not wglGetExtensionsStringARB is supported.

Note, that if you only want to know whether WGL_ARB_create_context it is easier to query the address of wglCreateContextARB and check that result.


1 There are a few exceptions due to historical reasons. The extension descriptions states:

Note that extensions that were previously advertised via glGetString (e.g., the swap interval extension) should continue to be advertised there so existing applications don't break. They should also be advertised via wglGetExtensionsStringARB so new applications can make one call to find out which WGL extensions are supported.

BDL
  • 21,052
  • 22
  • 49
  • 55
-1

You use glGetInteger64v to retrieve the number of extensions. But this function is not provided by Windows, it must be assigned a pointer as any gl-command after OpenGL 1.1
Same problem with glGetStringi. Both are OGL >= 3.0 functions.
Thus, your loop is useless. I don't know that code can even compile. Something you don't show may assign pointers to those functions.

wglCreateContextAttribsARB does exist in Windows, so you get a pointer without checking before for its extension.

Functions for getting extensions that are available in Windows are wglGetExtensionsStringARB or wglGetExtensionsStringEXT. Get their pointers by the usual wglGetProcAddress.

Ripi2
  • 7,031
  • 1
  • 17
  • 33