Sign in to follow this  
AutoBot

OpenGL A question about GLEW Windows setup

Recommended Posts

Hey everybody, I've been programming an application with wgl and glew, and there's one thing that's been bothering me with the code. It's required that a rendering context needs to be initialized before glew itself can be initialized. But what's getting to me is that, in my case, the [i]rendering context[/i] requires [i]glew[/i] to be initialized as well (since I call wglChoosePixelFormatARB for the context's pixel format, which is part of glew). This over all requires me to create a "dummy" rendering context just to initialize glew, and thus an extra device context and window (all of which would be deleted and reallocated later on). Is there a better way to implement this? I'd rather my code be a bit more cohesive so that I can reuse my variables more effectively, and managing the creation of these "dummy" variables bulks up my code to an extra 50 lines of code at least.

Here's the code in question, in my case:

[code]bool SetupWindow(int nWidth, int nHeight, bool bUseFS)
{
bool bRetVal = true;

int nWindowX = 0;
int nWindowY = 0;
int nPixelFormat = NULL;
PIXELFORMATDESCRIPTOR pfd;

DWORD dwExtStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwWindStyle = WS_OVERLAPPEDWINDOW;

HINSTANCE g_hInstance = GetModuleHandle(NULL);

if(bUseFS)
{
// Prepare for a mode set to the requested resolution
DEVMODE dm;
memset(&dm,0,sizeof(dm));
dm.dmSize=sizeof(dm);
dm.dmPelsWidth = nWidth;
dm.dmPelsHeight = nHeight;
dm.dmBitsPerPel = 32;
dm.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

long error = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);

if (error != DISP_CHANGE_SUCCESSFUL)
{
// Oops, something went wrong, let the user know.
if (MessageBox(NULL, TEXT("Could not set fullscreen mode.\n\
Your video card may not support the requested mode.\n\
Use windowed mode instead?"),
g_szAppName, MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
g_InFullScreen = false;
dwExtStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwWindStyle = WS_OVERLAPPEDWINDOW;
}
else
{
MessageBox(NULL, TEXT("Program will exit."), TEXT("ERROR"), MB_OK|MB_ICONSTOP);
return false;
}
}
else
{
// Mode set passed, setup the styles for fullscreen
g_InFullScreen = true;
dwExtStyle = WS_EX_APPWINDOW;
dwWindStyle = WS_POPUP;
ShowCursor(FALSE);
}
}

// setup window class
g_windClass.lpszClassName = g_szClassName; // Set the name of the Class
g_windClass.lpfnWndProc = (WNDPROC)WndProc;
g_windClass.hInstance = g_hInstance; // Use this module for the module handle
g_windClass.hCursor = LoadCursor(NULL, IDC_ARROW);// Pick the default mouse cursor
g_windClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);// Pick the default windows icons
g_windClass.hbrBackground = NULL; // No Background
g_windClass.lpszMenuName = NULL; // No menu for this window
g_windClass.style = CS_HREDRAW | CS_OWNDC | // set styles for this class, specifically to catch
CS_VREDRAW; // window redraws, unique DC, and resize
g_windClass.cbClsExtra = 0; // Extra class memory
g_windClass.cbWndExtra = 0; // Extra window memory

// Register the newly defined class
if(!RegisterClass( &g_windClass ))
bRetVal = false;

g_windowRect.left = nWindowX;
g_windowRect.right = nWindowX + nWidth;
g_windowRect.top = nWindowY;
g_windowRect.bottom = nWindowY + nHeight;

// Setup window width and height
AdjustWindowRectEx(&g_windowRect, dwWindStyle, FALSE, dwExtStyle);

//Adjust for adornments
int nWindowWidth = g_windowRect.right - g_windowRect.left;
int nWindowHeight = g_windowRect.bottom - g_windowRect.top;

// Create window
g_hWnd = CreateWindowEx(dwExtStyle, // Extended style
g_szClassName, // class name
g_szAppName, // window name
dwWindStyle |
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN,// window stlye
nWindowX, // window position, x
nWindowY, // window position, y
nWindowWidth, // height
nWindowHeight, // width
NULL, // Parent window
NULL, // menu
g_hInstance, // instance
NULL); // pass this to WM_CREATE

// now that we have a window, setup the pixel format descriptor
g_hDC = GetDC(g_hWnd);

// Set a dummy pixel format so that we can get access to wgl functions
SetPixelFormat( g_hDC, 1, &pfd);
// Create OGL context and make it current
g_hRC = wglCreateContext( g_hDC );
wglMakeCurrent( g_hDC, g_hRC );

if (g_hDC == 0 ||
g_hRC == 0)
{
bRetVal = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// Setup GLEW which loads OGL function pointers
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
bRetVal = false;
printf("Error: %s\n", glewGetErrorString(err));
}
const GLubyte *oglVersion = glGetString(GL_VERSION);
printf("This system supports OpenGL Version %s.\n", oglVersion);

// Now that extensions are setup, delete window and start over picking a real format.
wglMakeCurrent(NULL, NULL);
wglDeleteContext(g_hRC);
ReleaseDC(g_hWnd, g_hDC);
DestroyWindow(g_hWnd);

// Create the window again
g_hWnd = CreateWindowEx(dwExtStyle, // Extended style
g_szClassName, // class name
g_szAppName, // window name
dwWindStyle |
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN,// window stlye
nWindowX, // window position, x
nWindowY, // window position, y
nWindowWidth, // height
nWindowHeight, // width
NULL, // Parent window
NULL, // menu
g_hInstance, // instance
NULL); // pass this to WM_CREATE

g_hDC = GetDC(g_hWnd);

int nPixCount = 0;

// Specify the important attributes we care about
int pixAttribs[] = { WGL_SUPPORT_OPENGL_ARB, 1, // Must support OGL rendering
WGL_DRAW_TO_WINDOW_ARB, 1, // pf that can run a window
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, // must be HW accelerated
WGL_COLOR_BITS_ARB, 24, // 8 bits of each R, G and B
WGL_DEPTH_BITS_ARB, 16, // 16 bits of depth precision for window
WGL_DOUBLE_BUFFER_ARB, GL_TRUE, // Double buffered context
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, // MSAA on
WGL_SAMPLES_ARB, 8, // 8x MSAA
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, // pf should be RGBA type
0}; // NULL termination

// Ask OpenGL to find the most relevant format matching our attribs
// Only get one format back.
wglChoosePixelFormatARB(g_hDC, pixAttribs, NULL, 1, &nPixelFormat, (UINT*)&nPixCount);

if(nPixelFormat == NULL)
{
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a MSAA pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

// Try again without MSAA
pixAttribs[15] = 1;
wglChoosePixelFormatARB(g_hDC, pixAttribs, NULL, 1, &nPixelFormat, (UINT*)&nPixCount);

if(nPixelFormat == NULL)
{
// Couldn't find a format, perhaps no 3D HW or drivers are installed
g_hDC = 0;
g_hRC = 0;
bRetVal = false;
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}

if(nPixelFormat != NULL)
{
// Check for MSAA
int attrib[] = { WGL_SAMPLES_ARB };
int nResults = 0;
wglGetPixelFormatAttribivARB(g_hDC, nPixelFormat, 0, 1, attrib, &nResults);
printf("Chosen pixel format is MSAA with %d samples.\n", nResults);

// Got a format, now set it as the current one
SetPixelFormat( g_hDC, nPixelFormat, &pfd );

GLint attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
0 };

g_hRC = wglCreateContextAttribsARB(g_hDC, 0, attribs);
if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.3 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 2;
g_hRC = wglCreateContextAttribsARB(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.2 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 1;
g_hRC = wglCreateContextAttribsARB(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.1 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 0;
g_hRC = wglCreateContextAttribsARB(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.0 context.\n!!! OpenGL 3.0 and higher are not supported on this system.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}
}
}

wglMakeCurrent( g_hDC, g_hRC );
}

if (g_hDC == 0 ||
g_hRC == 0)
{
bRetVal = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window."),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// If everything went as planned, display the window
if( bRetVal )
{
ShowWindow( g_hWnd, SW_SHOW );
SetForegroundWindow( g_hWnd );
SetFocus( g_hWnd );
g_ContinueRendering = true;
}

return bRetVal;
}[/code]

Help is greatly appreciated!

Side note: I know that there is also WGLs ChoosePixelFormat function, but as far as I know that is older than the glew version and not necessarily recommended by the ARB.

Share this post


Link to post
Share on other sites
[quote name='AutoBot' timestamp='1311304790' post='4838759']
Is there a better way to implement this?
[/quote]

No.

[quote]
I'd rather my code be a bit more cohesive so that I can reuse my variables more effectively, and managing the creation of these "dummy" variables bulks up my code to an extra 50 lines of code at least.
[/quote]

Which are (1) incorrect and (2) can be reduced to about 10. Incorrect because I don't see where you'd set up PIXELFORMATDESCRIPTOR to create dummy context. You may end up with no GL context or 1.1 Microsoft context, and then wonder why glew doesn't work like you'd expect.
Can be reduced because your dummy window doesn't need a style or particular size (1x1 window is enough).
And once you've written that code, you'll hardly be ever touching it again, so in my opinion no big deal here.

[quote]
Side note: I know that there is also WGLs ChoosePixelFormat function, but as far as I know that is older than the glew version and not necessarily recommended by the ARB.
[/quote]


What makes you think so? ChoosePixelFormat just doesn't allow you to set some additional attributes, that's all. And you still need it to properly set up pfd when creating dummy window.

Share this post


Link to post
Share on other sites
So basically your code is
PIXELFORMATDESCRIPTOR pfd;
SetPixelFormat( g_hDC, 1, &pfd);

Any reason why you are not using ChoosePixelFormat? And why do you pass a non-initialized variable to SetPixelFormat? There are examples all over the net on how to setup a pixel format.

Share this post


Link to post
Share on other sites
You need the dummy context to get the function pointers. There's really no way around that. But what I suggest you do instead of initializing GLEW in the middle of your window manager, just load the handful of function pointers you need to proceed, then go ahead to create the real one using your new functions and destroy the dummy context. Once the dummy context is created, it's just one additional line of code per function you want to use.
[source]
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
[/source]
The reason you need the dummy rendering context is that function pointers is rendering context dependent. This is important to be aware of because if the dummy context is not from your hardware driver, you likely won't get the correct function pointer to set the pixel format, if you get anything at all. It also implies that you shouldn't load function pointers [i]at all[/i] until the real rendering context is created. So don't initialize GLEW until after the real rendering context is created, so load the handful of functions you need manually.

edit: Well, what the above said about the pixel format applies of course. But at some point you may need these intermediate functions to manage your rendering context; for example if you do need an exclusive pixel format, or simply a post-3.0 context, then you need to consider this.

Share this post


Link to post
Share on other sites
I have a bit more understanding about how this is supposed to work, so thanks for the insight so far everybody. Hopefully someone can resolve my last few questions.

[quote]It also implies that you shouldn't load function pointers [i]at all[/i] until the real rendering context is created. [/quote]
Is wglChoosePixelFormatARB an exception to this? It is part of the creation of the rendering context itself, after all.
And that actually raises another question in my head: how is wglChoosePixelFormatARB itself dependent on the rendering context being initialized?

[quote]Any reason why you are not using ChoosePixelFormat? [/quote]
I suppose it's just a tendency on my part to have the most updated material possible to work with. Just personal preference, I suppose. What Brother Bob said at the end of his post.

[quote]And why do you pass a non-initialized variable to SetPixelFormat? [/quote]
Are you referring to the pfd? Isn't SetPixelFormat supposed to initialize that to the data held in the pixel format?

[quote]Which are (1) incorrect and (2) can be reduced to about 10. Incorrect because I don't see where you'd set up PIXELFORMATDESCRIPTOR to create dummy context. You may end up with no GL context or 1.1 Microsoft context, and then wonder why glew doesn't work like you'd expect.
Can be reduced because your dummy window doesn't need a style or particular size (1x1 window is enough).[/quote]
Do you mean that I don't even need a pfd for the dummy contexts? Or a window?

EDIT: I've tried to trim down the code a bit:

[code]bool SetupWindow(int nWidth, int nHeight)
{
bool bRetVal = true;

int nWindowX = 0;
int nWindowY = 0;
int nPixelFormat = NULL;
PIXELFORMATDESCRIPTOR pfd;

DWORD dwExtStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwWindStyle = WS_OVERLAPPEDWINDOW;

HINSTANCE g_hInstance = GetModuleHandle(NULL);

// setup window class
g_windClass.lpszClassName = g_szClassName; // Set the name of the Class
g_windClass.lpfnWndProc = (WNDPROC)WndProc;
g_windClass.hInstance = g_hInstance; // Use this module for the module handle
g_windClass.hCursor = LoadCursor(NULL, IDC_ARROW);// Pick the default mouse cursor
g_windClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);// Pick the default windows icons
g_windClass.hbrBackground = NULL; // No Background
g_windClass.lpszMenuName = NULL; // No menu for this window
g_windClass.style = CS_HREDRAW | CS_OWNDC | // set styles for this class, specifically to catch
CS_VREDRAW; // window redraws, unique DC, and resize
g_windClass.cbClsExtra = 0; // Extra class memory
g_windClass.cbWndExtra = 0; // Extra window memory

// Register the newly defined class
if(!RegisterClass( &g_windClass ))
bRetVal = false;

g_windowRect.left = nWindowX;
g_windowRect.right = nWindowX + nWidth;
g_windowRect.top = nWindowY;
g_windowRect.bottom = nWindowY + nHeight;

// Setup window width and height
AdjustWindowRectEx(&g_windowRect, dwWindStyle, FALSE, dwExtStyle);

//Adjust for adornments
nWidth = g_windowRect.right - g_windowRect.left;
nHeight = g_windowRect.bottom - g_windowRect.top;

// Create the window
g_hWnd = CreateWindowEx(dwExtStyle, // Extended style
g_szClassName, // class name
g_szAppName, // window name
dwWindStyle |
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN,// window stlye
nWindowX, // window position, x
nWindowY, // window position, y
nWidth, // height
nHeight, // width
NULL, // Parent window
NULL, // menu
g_hInstance, // instance
NULL); // pass this to WM_CREATE

g_hDC = GetDC(g_hWnd);
SetPixelFormat( g_hDC, 1, &pfd); // Set a dummy pixel format so that we can get access to wgl functions
g_hRC = wglCreateContext( g_hDC ); // Create OGL context
wglMakeCurrent( g_hDC, g_hRC ); // and make it current

PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");

int nPixCount = 0;

// Specify the important attributes we care about
int pixAttribs[] = { WGL_SUPPORT_OPENGL_ARB, 1, // Must support OGL rendering
WGL_DRAW_TO_WINDOW_ARB, 1, // pf that can run a window
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, // must be HW accelerated
WGL_COLOR_BITS_ARB, 24, // 8 bits of each R, G and B
WGL_DEPTH_BITS_ARB, 16, // 16 bits of depth precision for window
WGL_DOUBLE_BUFFER_ARB, GL_TRUE, // Double buffered context
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, // MSAA on
WGL_SAMPLES_ARB, 8, // 8x MSAA
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, // pf should be RGBA type
0}; // NULL termination

// Ask OpenGL to find the most relevant format matching our attribs
// Only get one format back.
wglChoosePixelFormat(g_hDC, pixAttribs, NULL, 1, &nPixelFormat, (UINT*)&nPixCount);

if(nPixelFormat == NULL)
{
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a MSAA pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

// Try again without MSAA
pixAttribs[15] = 1;
wglChoosePixelFormat(g_hDC, pixAttribs, NULL, 1, &nPixelFormat, (UINT*)&nPixCount);

if(nPixelFormat == NULL)
{
// Couldn't find a format, perhaps no 3D HW or drivers are installed
g_hDC = 0;
g_hRC = 0;
bRetVal = false;
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}

if(nPixelFormat != NULL)
{
// Got a format, now set it as the current one
SetPixelFormat( g_hDC, nPixelFormat, &pfd );

GLint attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
0 };

g_hRC = wglCreateContextAttribs(g_hDC, 0, attribs);
if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.3 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 2;
g_hRC = wglCreateContextAttribs(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.2 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 1;
g_hRC = wglCreateContextAttribs(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.1 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 0;
g_hRC = wglCreateContextAttribs(g_hDC, 0, attribs);

if (g_hRC == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.0 context.\n!!! OpenGL 3.0 and higher are not supported on this system.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}
}
}

wglMakeCurrent( g_hDC, g_hRC );
}

if (g_hDC == 0 ||
g_hRC == 0)
{
bRetVal = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window."),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// Setup GLEW which loads OGL function pointers
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
bRetVal = false;
printf("Error: %s\n", glewGetErrorString(err));
}
const GLubyte *oglVersion = glGetString(GL_VERSION);
printf("This system supports OpenGL Version %s.\n", oglVersion);

// If everything went as planned, display the window
if( bRetVal )
{
ShowWindow( g_hWnd, SW_SHOW );
SetForegroundWindow( g_hWnd );
SetFocus( g_hWnd );
g_ContinueRendering = true;
}

return bRetVal;
}[/code]

This makes the code a bit cleaner, imo. I've also removed the full screen check just so it's more to the point. Unfortunately this code doesn't work, as all of my other OpenGL calls won't work now! GLEW doesn't return an error in this case, so I'm not sure what could be wrong now... Should I just leave the code as it was in the first place?

Share this post


Link to post
Share on other sites
[quote name='AutoBot' timestamp='1311354406' post='4839018']
I have a bit more understanding about how this is supposed to work, so thanks for the insight so far everybody. Hopefully someone can resolve my last few questions.

[quote]It also implies that you shouldn't load function pointers [i]at all[/i] until the real rendering context is created. [/quote]
Is wglChoosePixelFormatARB an exception to this? It is part of the creation of the rendering context itself, after all.
And that actually raises another question in my head: how is wglChoosePixelFormatARB itself dependent on the rendering context being initialized?
[/quote]
Yes, it is a part of context creation, but it is part of an [i]extension[/i], and in order to determine if it's even present, you first need to create that legacy dummy context with plain WinAPI calls.

[quote]
[quote]Any reason why you are not using ChoosePixelFormat? [/quote]
I suppose it's just a tendency on my part to have the most updated material possible to work with. Just personal preference, I suppose. What Brother Bob said at the end of his post.
[/quote]
What Brother Bob said at the end of his post was that he confirmed what I and V-man tried to tell you: that your originally posted code shouldn't work.

[quote]
[quote]And why do you pass a non-initialized variable to SetPixelFormat? [/quote]
Are you referring to the pfd? Isn't SetPixelFormat supposed to initialize that to the data held in the pixel format?
[/quote]
What data? Held where? It is [b]you[/b] who should initialize the descriptor to tell Windows that you want driver-provided OpenGL. That's not even funny. It seems you misunderstood all of us and the documentation to boot. See below.

[quote]
[quote]Which are (1) incorrect and (2) can be reduced to about 10. Incorrect because I don't see where you'd set up PIXELFORMATDESCRIPTOR to create dummy context. You may end up with no GL context or 1.1 Microsoft context, and then wonder why glew doesn't work like you'd expect.
Can be reduced because your dummy window doesn't need a style or particular size (1x1 window is enough).[/quote]
Do you mean that I don't even need a pfd for the dummy contexts? Or a window?
[/quote]
No, all's the other way around. You need both dummy context and dummy window. What I meant, and what V-man specifically pointed out, is that in your original snippet (and the current one also), you just declare pfd variable, do not initialize it in any way, do not use ChoosePixelFormat, but simply call SetPixelFormat right away. This is completely and utterly wrong. I'm wondering how it even worked for you before. Either it was a miracle, or your originally posted snippet was incomplete.

The procedure should be like this:
1) Create PIXELFORMATDESCRIPTOR variable
2) Properly initialize it (set cbSize field, buffer sizes, flags (specifically PFD_SUPPORT_OPENGL))
3) Call ChoosePixelFormat to get index of the pixel format that is a closest match to the one you describe in PIXELFORMATDESCRIPTOR variable
4) Call SetPixelFormat
5) Call wglCreateContext.

That's it, your dummy-trow-away-later-context is ready, you can now make it current and proceed with GL initialization as you want.

Both your snippets miss points (2) and (3), which means that best case scenario is that you may get GL 1.1 context as implemented by Microsoft. Which wouldn't have any fancy wglChoosePixelFormat and wglCreateContextAttribs. Worst case - you won't get any context at all. Most terrible case - in release build you may end up with access violation.

Be sure to read carefully through [url="http://www.opengl.org/wiki/Creating_an_OpenGL_Context"]this[/url] to clear things up further, specifically the section "Proper context creation".

Share this post


Link to post
Share on other sites
(Please keep in mind that I'm referring to the code in the OP, not in my latter post which I already know doesn't work)

At the beginning of my code I do only call SetPixelFormat with an uninitialized pfd and so forth because its supposed to be a dummy pixel format. It's only purpose is to help give access to the wgl functions, so that later on I can query for a more sophisticated pixel format with MSAA and other features not supported by the generic wgl implementation.

I had assumed that wglChoosePixelFormatARB used the attributes array ("int attribs[]" in my code) to supersede the pfd. Thus when I called SetPixelFormat, it would supply the pfd with essential information from the attributes and newly found pixel format to supplement for more generic wgl functions that demand a pfd argument. I did some searching though and found that this isn't completely correct, I need to use DescribePixelFormat to apply the pixel format's data to the pfd. For some reason the code seemed to work beforehand on Visual C++ 2010, maybe because the pfd info isn't as critical when using ARB extensions. Maybe because DescribePixelFormat is already called inside the SetPixelFormat function. I honestly don't know. But just to be safe I'm going to put DescribePixelFormat in just for the sake of firm code. Is that the best thing to do?

In terms of the dummy window and contexts, I'm probably just going to leave those in and initialize glew before the pixel format setup like I have, which is probably how it's supposed to work anyway. But advice is still greatly appreciated if there's a more efficient way to go about doing this! I also have a more object-oriented version of this program in the works which might make it a bit cleaner, I'll post it here once I'm done if anybody's interested.

EDIT: I've read the wiki page as suggested, definitely some great stuff in there, it also confirms that it's best to leave my code as it is for the most part. One thing that's a bit vague is where (in "Pixel Format Extensions") it says:

[quote][font="sans-serif"][size="2"]Once you have a pixel format number, you can set it just like any pixel format with SetPixelFormat.[/size][/font][/quote]
They don't say anything much about the pfd in this case, and it seems to me that the attribs parameter would basically make the pfd redundant, other than for legacy purposes. I suppose that's what DescribePixelFormat is for though.

And sorry if I've offended you at all, capricorn... :S

Share this post


Link to post
Share on other sites
[quote name='AutoBot' timestamp='1311379705' post='4839156']
(Please keep in mind that I'm referring to the code in the OP, not in my latter post which I already know doesn't work)

At the beginning of my code I do only call SetPixelFormat with an uninitialized pfd and so forth because its supposed to be a dummy pixel format. It's only purpose is to help give access to the wgl functions, so that later on I can query for a more sophisticated pixel format with MSAA and other features not supported by the generic wgl implementation.
[/quote]

I think you've overestimated the term 'dummy' here :) It is not supposed to be a dummy pixel format. It is supposed to be a real pixel format suitable for OpenGL context creation. Yes, the context is 'dummy' in the sense that you won't do any rendering with it and you'll throw it away after getting a couple of function pointers (wglChoosePixelFormat and wglCreateContextAttribs), but nevertheless it should be a proper OpenGL context. Trying to create one without setting proper pixel format for DC won't get you far (I already mentioned what you might get).
To set proper pixel format, you need to initialize your pfd struct and [b]feed it to WinAPI's ChoosePixelFormat() function[/b]. It will return [b]pixel format index[/b], which you should use in SetPixelFormat() call. Alternatively, you could enumerate all available pixel formats to find suitable index without using ChoosePixelFormat().
In your original snippet you just use index 1, which I suspect just by coincidence happens to be an index for OpenGL-compatible pixel format [b]on your system[/b] (the "miracle" I mentioned earlier). You need to substitute this '1' with proper index.
pfd struct won't have any behavioral effects on SetPixelFormat() call. And yes, you may use that same pfd struct later when you call SetPixelFormat() with another index that you get from wglChoosePixelFormatARB(), it won't do any harm. Again, it's [b]pixel format index[/b] that matters in the SetPixelFormat() call, not the descriptor.


[quote]
I had assumed that wglChoosePixelFormatARB used the attributes array ("int attribs[]" in my code) to supersede the pfd.
[/quote]
Precisely so.

[quote]
Thus when I called SetPixelFormat, it would supply the pfd with essential information from the attributes and newly found pixel format to supplement for more generic wgl functions that demand a pfd argument.
[/quote]
SetPixelFormat() doesn't supply anything. Take a closer look at it's signature, or even better, at it's documentation.

[quote]
I did some searching though and found that this isn't completely correct, I need to use DescribePixelFormat to apply the pixel format's data to the pfd.
[/quote]
Well, you could do it if only for consistency's sake, but it doesn't really matter. As I explained above, the affecting parameter for SetPixelFormat() is pixel format index, not pfd struct.

[quote]
In terms of the dummy window and contexts, I'm probably just going to leave those in and initialize glew before the pixel format setup like I have, which is probably how it's supposed to work anyway.
[/quote]
Yep, if you finally get that 'dummy' context creation right.

[quote]
But advice is still greatly appreciated if there's a more efficient way to go about doing this!
[/quote]
What's inefficient in this approach? It won't hurt your framerate in any way. And as I mentioned earlier, it'd be precisely that kind of code you're unlikely to ever touch again. If you think it's too much lines of code, you can always reformat 'dummy' window creation to one line.

[quote]
EDIT: I've read the wiki page as suggested, definitely some great stuff in there, it also confirms that it's best to leave my code as it is for the most part. One thing that's a bit vague is where (in "Pixel Format Extensions") it says:

[quote][font="sans-serif"][size="2"]Once you have a pixel format number, you can set it just like any pixel format with SetPixelFormat.[/size][/font][/quote]
They don't say anything much about the pfd in this case, and it seems to me that the attribs parameter would basically make the pfd redundant, other than for legacy purposes. I suppose that's what DescribePixelFormat is for though.
[/quote]
They don't say anything because it doesn't matter. Though I must say it'd be nice if it was explicitly mentioned :)

[quote]
And sorry if I've offended you at all, capricorn... :S
[/quote]
Nah, I wasn't offended, I was frustrated. [img]http://public.gamedev.net/public/style_emoticons/default/cool.gif[/img]

Share this post


Link to post
Share on other sites
[quote name='capricorn' timestamp='1311391934' post='4839202']
[quote name='AutoBot' timestamp='1311379705' post='4839156']
(Please keep in mind that I'm referring to the code in the OP, not in my latter post which I already know doesn't work)

At the beginning of my code I do only call SetPixelFormat with an uninitialized pfd and so forth because its supposed to be a dummy pixel format. It's only purpose is to help give access to the wgl functions, so that later on I can query for a more sophisticated pixel format with MSAA and other features not supported by the generic wgl implementation.
[/quote]
I think you've overestimated the term 'dummy' here :) It is not supposed to be a dummy pixel format. It is supposed to be a real pixel format suitable for OpenGL context creation. Yes, the context is 'dummy' in the sense that you won't do any rendering with it and you'll throw it away after getting a couple of function pointers (wglChoosePixelFormat and wglCreateContextAttribs), but nevertheless it should be a proper OpenGL context. Trying to create one without setting proper pixel format for DC won't get you far (I already mentioned what you might get).
To set proper pixel format, you need to initialize your pfd struct and [b]feed it to WinAPI's ChoosePixelFormat() function[/b]. It will return [b]pixel format index[/b], which you should use in SetPixelFormat() call. Alternatively, you could enumerate all available pixel formats to find suitable index without using ChoosePixelFormat().
In your original snippet you just use index 1, which I suspect just by coincidence happens to be an index for OpenGL-compatible pixel format [b]on your system[/b] (the "miracle" I mentioned earlier). You need to substitute this '1' with proper index.
pfd struct won't have any behavioral effects on SetPixelFormat() call. And yes, you may use that same pfd struct later when you call SetPixelFormat() with another index that you get from wglChoosePixelFormatARB(), it won't do any harm. Again, it's [b]pixel format index[/b] that matters in the SetPixelFormat() call, not the descriptor.
[/quote]
I would like to extend on this part and explain why it is important to do everything correct, even if it's just a dummy context, and it's from my first reply here where I say that function pointers are context dependent.

Consider what happens if you supply some random pixel format like you do. Windows tries to match your random pixel format with some suitable pixel format that is actually supported. What do you think happens to the rest of your program if that pixel format is only supported by Microsoft's own software implementation? The first thing that happens is that wglChoosePixelFormatARB cannot be loaded, becuase Microsoft's implementation doesn't support it. So if you're not setting a proper pixel format, you may not be able to proceed at all.

This is why it's important to do everything properly, even for the pixel format and the context you're not going to use for the rest of the program, but you [i]are [/i]using it for a short time, and it has to be set up properly like any other pixel format or rendering context.

Share this post


Link to post
Share on other sites
This is the definition of
[b]BOOL SetPixelFormat( HDC [/b][i] hdc[/i][b], [/b][b] int [/b][i] iPixelFormat[/i][b],[/b] [b] CONST PIXELFORMATDESCRIPTOR * [/b][i] ppfd[/i] [b]);[/b]

so SetPixelFormat won't modify ppfd. You must set it up and provide it to SetPixelFormat. Also, check MSDN if you want to see how the function works.

Share this post


Link to post
Share on other sites
[quote name='V-man' timestamp='1311418907' post='4839248']
This is the definition of
[b]BOOL SetPixelFormat( HDC [/b][i] hdc[/i][b], [/b][b] int [/b][i] iPixelFormat[/i][b],[/b] [b] CONST PIXELFORMATDESCRIPTOR * [/b][i] ppfd[/i] [b]);[/b]

so SetPixelFormat won't modify ppfd. You must set it up and provide it to SetPixelFormat. Also, check MSDN if you want to see how the function works.
[/quote]

It is not necessary to provide relevant pfd to SetPixelFormat. It'll still either set or fail to set the pixel format you specify via iPixelFormat.

Share this post


Link to post
Share on other sites
[quote name='capricorn' timestamp='1311457482' post='4839374']
[quote name='V-man' timestamp='1311418907' post='4839248']
This is the definition of
[b]BOOL SetPixelFormat( HDC [/b][i] hdc[/i][b], [/b][b] int [/b][i] iPixelFormat[/i][b],[/b] [b] CONST PIXELFORMATDESCRIPTOR * [/b][i] ppfd[/i] [b]);[/b]

so SetPixelFormat won't modify ppfd. You must set it up and provide it to SetPixelFormat. Also, check MSDN if you want to see how the function works.
[/quote]

It is not necessary to provide relevant pfd to SetPixelFormat. It'll still either set or fail to set the pixel format you specify via iPixelFormat.
[/quote]



The MSDN documentation says something about the metafile component using the pfd argument for recording information, probably if someone wanted to access it later, or something.

And I understand about the dummy pixel format now, I think. It seems I got lucky with the '1' format, lol. I thought it was the index to some "default" index before, but now I know that isn't correct. I am a little confused at one part though: what exactly do I [i]have[/i] to initialize in the pfd in order for the ARB functions to work? So far what I've done is made it as close as possible to my attribs array... I suppose that's good enough though. The code still works with that anyway.

I've actually made a lot of improvements to the code, including giving it an object-oriented interface which I mentioned earlier. This time I'm just going to post the whole entire program just for entirety, but keep in mind that I've been using the "GL Tools" library from the OGL Superbible as I'm still learning the low-level functions. Here it is:

[code]#ifndef GLEW_STATIC
#define GLEW_STATIC
#endif

#include <GL\glew.h>
#include <GL\wglew.h>

#include "GLTools.h"
#include "GLShaderManager.h"

class Application
{
public:
void Start(int width = 800, int height = 600);
void End();

void MainLoop();

bool running;
const TCHAR *name;
} app;

class WindowManager
{
HINSTANCE hInstance;

WNDPROC wndProc;

WNDCLASS wndClass;

DWORD wndStyle;
DWORD extStyle;

RECT dimensions;
bool fullScreen;

public:
HWND hWnd;

void CoreInit(int width, int height);
void Start(int width, int height);
void End();

void FullStart(int width, int height)
{
CoreInit(width, height),
Start(width, height);
}

void Restart()
{
// Destroy the window
if(hWnd)
{
DestroyWindow(hWnd);
hWnd = NULL;
}

// Recreate Window
Start(
dimensions.right - dimensions.left,
dimensions.bottom - dimensions.top);
}

void Show()
{
ShowWindow( hWnd, SW_SHOW ),
SetForegroundWindow( hWnd ),
SetFocus( hWnd );
}
} windowManager;

class GraphicsManager
{
HGLRC renderingContext;
HDC deviceContext;

GLBatch triangleBatch;
GLShaderManager shaderManager;

bool sceneReady;

public:
void Start(int width, int height);
void End();

void SetupScene();
void RenderScene();

void ChangeSize(int width, int height);

void CheckErrors(GLuint progName = 0);
} graphicsManager;

///////////////////////////////////////////////////////////////////////////////
// Main program function, called on startup
// First setup the window and OGL state, then enter rendering loop
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
app.Start();

graphicsManager.SetupScene();
app.MainLoop();

app.End();
return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Callback functions to handle all window functions this app cares about.
// Once complete, pass message on to next app in the hook chain.
LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
// Handle relevant messages individually
switch(uMsg)
{
case WM_SIZE:
graphicsManager.ChangeSize(LOWORD(lParam),HIWORD(lParam));
graphicsManager.RenderScene();
break;
case WM_CLOSE:
app.running = false;
PostQuitMessage(0);
return 0;
default:
// Nothing to do now
break;
}

// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

void Application::Start(int width, int height)
{
running = true;
name = TEXT("Triangle");

windowManager.FullStart(width, height);
graphicsManager.Start(width, height);
}

void Application::End()
{
graphicsManager.End();
windowManager.End();
}

void Application::MainLoop()
{
while(running)
{
MSG msg;

// Check for waiting mssgs
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
{
running = false;
}
else
{
// Deal with mssgs
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
graphicsManager.RenderScene();
}

Sleep(0);
}
}

void WindowManager::CoreInit(int width, int height)
{
hInstance = GetModuleHandle(NULL);
wndProc = (WNDPROC)WndProc;

extStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
wndStyle = WS_OVERLAPPEDWINDOW;

if(MessageBox(NULL, TEXT("Would you like to run in Fullscreen Mode?"), app.name, MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
// Prepare for a mode set to the requested resolution
DEVMODE dm;
memset (&dm, 0, sizeof(dm));

dm.dmSize = sizeof(dm);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmBitsPerPel = 32;
dm.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

long error = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);

if (error != DISP_CHANGE_SUCCESSFUL)
{
// Oops, something went wrong, let the user know.
if (MessageBox(NULL, TEXT("Could not set fullscreen mode.\n\
Your video card may not support the requested mode.\n\
Use windowed mode instead?"),
app.name, MB_YESNO|MB_ICONEXCLAMATION) == IDYES)
{
fullScreen = false;
extStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
wndStyle = WS_OVERLAPPEDWINDOW;
}
else
{
MessageBox(NULL, TEXT("Program will exit."), TEXT("ERROR"), MB_OK|MB_ICONSTOP);
app.running = false;
}
}
else
{
// Mode set passed, setup the styles for fullscreen
fullScreen = true;
extStyle = WS_EX_APPWINDOW;
wndStyle = WS_POPUP;
ShowCursor(FALSE);
}
}

// Setup window width and height
dimensions.left = 0;
dimensions.right = width;
dimensions.top = 0;
dimensions.bottom = height;

AdjustWindowRectEx(&dimensions, wndStyle, FALSE, extStyle);

//Adjust for adornments
width = dimensions.right - dimensions.left;
height = dimensions.bottom - dimensions.top;

// setup window class
wndClass.lpszClassName = TEXT("OGL_WINCLASS"); // Set the name of the Class
wndClass.lpfnWndProc = wndProc; // Set pointer to WndProc
wndClass.hInstance = hInstance; // Use this module for the module handle
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Pick the default mouse cursor
wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Pick the default windows icons
wndClass.hbrBackground = NULL; // No Background
wndClass.lpszMenuName = NULL; // No menu for this window
wndClass.style = CS_HREDRAW | CS_OWNDC | // set styles for this class, specifically to catch
CS_VREDRAW; // window redraws, unique DC, and resize
wndClass.cbClsExtra = 0; // Extra class memory
wndClass.cbWndExtra = 0; // Extra window memory

// Register the newly defined class
if(!RegisterClass( &wndClass ))
{
app.running = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window. (wndClass registration failure)\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}

void WindowManager::Start(int width, int height)
{
// Create window
hWnd = CreateWindowEx( extStyle, // Extended style
TEXT("OGL_WINCLASS"), // class name
app.name, // window name
wndStyle |
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN, // window stlye
0, // window position, x
0, // window position, y
width, // height
height, // width
NULL, // Parent window
NULL, // menu
hInstance, // instance
NULL); // pass this to WM_CREATE

}

void WindowManager::End()
{
// Disable fullscreen
if (fullScreen)
{
ChangeDisplaySettings(NULL,0);
ShowCursor(TRUE);
}

// Destroy the window
if(hWnd)
{
DestroyWindow(hWnd);
hWnd = NULL;
}

// Delete the window class
UnregisterClass(TEXT("OGL_WINCLASS"), hInstance);
hInstance = NULL;
ShowCursor(TRUE);
}

void GraphicsManager::Start(int width, int height)
{
sceneReady = false;

int nPixelFormat = NULL;
PIXELFORMATDESCRIPTOR pfd;

// now that we have a window, setup the pixel format descriptor
deviceContext = GetDC(windowManager.hWnd);

memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;

nPixelFormat = ChoosePixelFormat(deviceContext, &pfd);

// Set a dummy pixel format so that we can get access to wgl functions
SetPixelFormat( deviceContext, nPixelFormat, &pfd);

// Create OGL context and make it current
renderingContext = wglCreateContext( deviceContext );
wglMakeCurrent( deviceContext, renderingContext );

if (deviceContext == 0 ||
renderingContext == 0)
{
app.running = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window. (Context failure)\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// Setup GLEW which loads OGL function pointers
if (GLEW_OK != glewInit())
{
app.running = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window. (GLEW failure)\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// Now that extensions are setup, delete window and start over picking a real format.
wglMakeCurrent(NULL, NULL);
wglDeleteContext(renderingContext);
ReleaseDC(windowManager.hWnd, deviceContext);
windowManager.Restart();

deviceContext = GetDC(windowManager.hWnd);

UINT nPixCount = 0;

// Specify the important attributes we care about
int pixAttribs[] = { WGL_SUPPORT_OPENGL_ARB, 1, // Must support OGL rendering
WGL_DRAW_TO_WINDOW_ARB, 1, // pf that can run a window
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, // must be HW accelerated
WGL_COLOR_BITS_ARB, 24, // 8 bits of each R, G and B
WGL_DEPTH_BITS_ARB, 16, // 16 bits of depth precision for window
WGL_DOUBLE_BUFFER_ARB, GL_TRUE, // Double buffered context
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, // MSAA on
WGL_SAMPLES_ARB, 8, // 8x MSAA
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, // pf should be RGBA type
0}; // NULL termination

// Ask OpenGL to find the most relevant format matching our attribs
// Only get one format back.
wglChoosePixelFormatARB(deviceContext, pixAttribs, NULL, 1, &nPixelFormat, &nPixCount);

if(nPixelFormat == NULL)
{
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a MSAA pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

// Try again without MSAA
pixAttribs[15] = 1;
wglChoosePixelFormatARB(deviceContext, pixAttribs, NULL, 1, &nPixelFormat, &nPixCount);

if(nPixelFormat == NULL)
{
// Couldn't find a format, perhaps no 3D HW or drivers are installed
deviceContext = 0;
renderingContext = 0;
app.running = false;
MessageBox(NULL,
TEXT("!!! An error occurred trying to find a pixel format with the requested attribs.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}

if(nPixelFormat != NULL)
{
// Got a format, now set it as the current one
memset(&pfd, 0, sizeof(pfd));
DescribePixelFormat(deviceContext, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat( deviceContext, nPixelFormat, &pfd );

GLint attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
0 };

renderingContext = wglCreateContextAttribsARB(deviceContext, 0, attribs);
if (renderingContext == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.3 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 2;
renderingContext = wglCreateContextAttribsARB(deviceContext, 0, attribs);

if (renderingContext == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.2 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 1;
renderingContext = wglCreateContextAttribsARB(deviceContext, 0, attribs);

if (renderingContext == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.1 context.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);

attribs[3] = 0;
renderingContext = wglCreateContextAttribsARB(deviceContext, 0, attribs);

if (renderingContext == NULL)
{
MessageBox(NULL,
TEXT("!!! Could not create an OpenGL 3.0 context.\n!!! OpenGL 3.0 and higher are not supported on this system.\n"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}
}
}
}

wglMakeCurrent( deviceContext, renderingContext );
}

if (deviceContext == 0 ||
renderingContext == 0)
{
app.running = false;
MessageBox(NULL,
TEXT("!!! An error occured creating an OpenGL window. (Context failure)"),
TEXT("ERROR"),
MB_OK|MB_ICONEXCLAMATION);
}

// If everything went as planned, display the window
if( app.running )
windowManager.Show();

ChangeSize(width, height);
}

void GraphicsManager::End()
{
//Cleanup OGL RC
if(renderingContext)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(renderingContext);
renderingContext = NULL;
}

// release the DC
if(deviceContext)
{
ReleaseDC(windowManager.hWnd, deviceContext);
deviceContext = NULL;
}
}

void GraphicsManager::SetupScene()
{
if(app.running == false) return;

// Blue background
glClearColor(0.0f, 0.0f, 1.0f, 1.0f );

shaderManager.InitializeStockShaders();

// Load up a triangle
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };

triangleBatch.Begin(GL_TRIANGLES, 3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();

sceneReady = true;
}

void GraphicsManager::RenderScene()
{
if(app.running == false || sceneReady == false) return;

// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
triangleBatch.Draw();

// Do the buffer Swap
SwapBuffers(deviceContext);

CheckErrors();
}

void GraphicsManager::ChangeSize(int width, int height)
{
glViewport(0, 0, width, height);
}

void GraphicsManager::CheckErrors(GLuint progName)
{
GLenum error = glGetError();

if (error != GL_NO_ERROR)
{
fprintf(stderr, "A GL Error has occured\n");
}

GLenum fboStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);

if(fboStatus != GL_FRAMEBUFFER_COMPLETE)
{
switch (fboStatus)
{
case GL_FRAMEBUFFER_UNDEFINED:
// Oops, no window exists?
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_UNDEFINED"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
// Check the status of each attachment
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
// Attach at least one buffer to the FBO
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
// Check that all attachments enabled via
// glDrawBuffers exist in FBO
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"), app.name, MB_OK|MB_ICONEXCLAMATION);
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
// Check that the buffer specified via
// glReadBuffer exists in FBO
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
// Reconsider formats used for attached buffers
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_UNSUPPORTED"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
// Make sure the number of samples for each
// attachment is the same
MessageBox(NULL, TEXT("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"), app.name, MB_OK|MB_ICONEXCLAMATION);
break;
//case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
// Make sure the number of layers for each
// attachment is the same
//break;
}
fprintf(stderr,"The framebuffer is not complete\n");
}

if (progName != 0)
{
glValidateProgram(progName);
int iIsProgValid = 0;
glGetProgramiv(progName, GL_VALIDATE_STATUS, &iIsProgValid);
if(iIsProgValid == 0)
{
MessageBox(NULL, TEXT("The current program is not valid\n"), app.name, MB_YESNO|MB_ICONEXCLAMATION);
fprintf(stderr,"The current program is not valid\n");
}
}
}[/code]


This is hopefully the start of a game engine subsystem layout, and of course its only a start at the moment. It still makes the code a lot more readable in my opinion, not to mention easier to use. Any opinions on it? (Sorry if the indentations look all screwed up, it looks a lot better in my IDE for some reason)

Tons of thanks to capricorn, V-man, and Brother Bob for helping me out with this, I definitely know a lot more than before. I'm not sure if there's much more to worry about now, I'm satisfied with how the program's turned out for the most part. Any further suggestions are still welcome, of course. Thanks again!

Share this post


Link to post
Share on other sites
[quote name='AutoBot' timestamp='1311473147' post='4839439']
And I understand about the dummy pixel format now, I think. It seems I got lucky with the '1' format, lol. I thought it was the index to some "default" index before, but now I know that isn't correct. I am a little confused at one part though: what exactly do I [i]have[/i] to initialize in the pfd in order for the ARB functions to work? So far what I've done is made it as close as possible to my attribs array... I suppose that's good enough though. The code still works with that anyway.
[/quote]
Theoretically, you should match them exactly. Any difference in the final pixel formats may result in different contexts, and the function pointers won't work. But in practice, as close as possible is typically good enough. As long as the dummy context is created is a hardware accelerated context, you should be fine. Just be aware that having small differences may tip the scale different ways when choosing between what contexts are created (hardware vs. software implementation), but any reasonable pixel format usually tips it towards the hardware context.

Share this post


Link to post
Share on other sites
[quote name='AutoBot' timestamp='1311473147' post='4839439']
The MSDN documentation says something about the metafile component using the pfd argument for recording information, probably if someone wanted to access it later, or something.
[/quote]
Almost. It's for recording [url="http://msdn.microsoft.com/en-us/library/dd145051(v=VS.85).aspx"]metafiles[/url]. As long as you don't do that, it does not matter at all what pfd you pass to SetPixelFormat().

[quote]
I am a little confused at one part though: what exactly do I [i]have[/i] to initialize in the pfd in order for the ARB functions to work? So far what I've done is made it as close as possible to my attribs array... I suppose that's good enough though. The code still works with that anyway.
[/quote]
Yeah, I'd say that's good enough... almost :) Actually, it'd be better if you removed that PFD_GENERIC_ACCELERATED flag. It's only meaningful to have it for the software GDI pixel formats.

[quote]
This is hopefully the start of a game engine subsystem layout, and of course its only a start at the moment. It still makes the code a lot more readable in my opinion, not to mention easier to use. Any opinions on it? (Sorry if the indentations look all screwed up, it looks a lot better in my IDE for some reason)
[/quote]
Regarding GL, I'd only encourage you to heed Brother Bob's advice and load GL functions after your real context creation. But I can't say for sure if GLEW is currently capable of properly doing that, and it's quite a work to implement it by hand. So you may consider this for the future work at least.

[quote]
Tons of thanks to capricorn, V-man, and Brother Bob for helping me out with this, I definitely know a lot more than before. I'm not sure if there's much more to worry about now, I'm satisfied with how the program's turned out for the most part. Any further suggestions are still welcome, of course. Thanks again!
[/quote]
You're welcome.

Share this post


Link to post
Share on other sites
[quote name='capricorn' timestamp='1311505255' post='4839549']
Regarding GL, I'd only encourage you to heed Brother Bob's advice and load GL functions after your real context creation. But I can't say for sure if GLEW is currently capable of properly doing that, and it's quite a work to implement it by hand. So you may consider this for the future work at least.
[/quote]
Quite a work? I showed how to do it in my first post; it is one line of code for every function you need, and at the moment he only needs two function.

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1311508750' post='4839562']
[quote name='capricorn' timestamp='1311505255' post='4839549']
Regarding GL, I'd only encourage you to heed Brother Bob's advice and load GL functions after your real context creation. But I can't say for sure if GLEW is currently capable of properly doing that, and it's quite a work to implement it by hand. So you may consider this for the future work at least.
[/quote]
Quite a work? I showed how to do it in my first post; it is one line of code for every function you need, and at the moment he only needs two function.
[/quote]

Quite a work doing this by hand ("...[i]load GL functions after your real context creation"[/i]). I mentioned this because I'm not sure if GLEW is consistent with GL's deprecation/removal model (e.g. strict interface for core GL3.3 profile does not include all functions from previous GL versions, but GLEW loader might try and load them anyway, and, what's most frustrating, even succeed if the driver's not conformant). Maybe it's worth asking the authors, it's hard to tell from GLEW code at a glance.

I myself load all GL function pointers by hand (i'm currently working in D so I don't have the luxury of GLEW. Could use Derelict instead, but it doesn't work entirely well, and I don't think this is the proper place to explain why [img]http://public.gamedev.net/public/style_emoticons/default/wink.gif[/img] ), and I'm already considering splitting GL interface into different sections consistent with deprecation model.

EDIT: writing that, I remembered I've read in GL specs that "calling removed functions would result in an INVALID_OPERATION", so I've gone and rechecked that. It seems that conformant driver should still provide removed functions, but they just simply operate as the docs state. Well, maybe it is better to have that than calling a function via null pointer (e.g. if functions were totally absent). Edited by capricorn

Share this post


Link to post
Share on other sites
[quote name='capricorn' timestamp='1311505255' post='4839549']
Regarding GL, I'd only encourage you to heed Brother Bob's advice and load GL functions after your real context creation. But I can't say for sure if GLEW is currently capable of properly doing that, and it's quite a work to implement it by hand. So you may consider this for the future work at least.[/quote]

Yeah, that is a good thing to be aware of just in case GLEW doesn't do the job, though you have to admit it is pretty nice if you want immediate access to GL functions :D

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1311502691' post='4839539']
[quote name='AutoBot' timestamp='1311473147' post='4839439']
And I understand about the dummy pixel format now, I think. It seems I got lucky with the '1' format, lol. I thought it was the index to some "default" index before, but now I know that isn't correct. I am a little confused at one part though: what exactly do I [i]have[/i] to initialize in the pfd in order for the ARB functions to work? So far what I've done is made it as close as possible to my attribs array... I suppose that's good enough though. The code still works with that anyway.
[/quote]
Theoretically, you should match them exactly. Any difference in the final pixel formats may result in different contexts, and the function pointers won't work. But in practice, as close as possible is typically good enough. As long as the dummy context is created is a hardware accelerated context, you should be fine. Just be aware that having small differences may tip the scale different ways when choosing between what contexts are created (hardware vs. software implementation), but any reasonable pixel format usually tips it towards the hardware context.
[/quote]

From what I understand, you either get the GL driver, from which you can get your tons of GL function pointers or #2, you get Microsoft GDI which is GL 1.1

What is there to worry about? Your graphics driver is not going to give you some other set of pointers.
Has anyone ever seen a case where you had a different set of pointers for a hw accelerated context GL 1.2 or 1.3 or 1.3 or 1.4 or 1.5 or 2.0......?

If you read the MSDN documentation, different pixel formats can give you a different set of pointers but I think in practice, you just have 2 cases.

Share this post


Link to post
Share on other sites
What you should worry about is not getting a software context when creating the dummy context. Not sure what you're getting at though; you seems to agree with what I say, but present it in a way that you seem to disagree with me.

Share this post


Link to post
Share on other sites
[quote name='V-man' timestamp='1311795939' post='4841260']

From what I understand, you either get the GL driver, from which you can get your tons of GL function pointers or #2, you get Microsoft GDI which is GL 1.1
If you read the MSDN documentation, different pixel formats can give you a different set of pointers but I think in practice, you just have 2 cases.
[/quote]


In practice, it's implementation-specific. As I mentioned above, specifications for modern GL versions state that calling removed functions should result in GL_INVALID_OPERATION error. It means that GL implementations (i.e. drivers) [b]will[/b] provide different functionality for different GL versions. Alas, while it is determined what functionality is available for a specific GL version, it is not clearly specified [b]at what level[/b] it is controlled. Function behavior may be controlled on a context level (i.e. function call is forwarded to implementation-specific context object, thus, same function pointer but different behavior on different GL versions), or at function acquisition level (i.e. when you fetch function pointer from the driver via e.g. wglGetProcAddress, thus, different function pointers for different GL versions). The latter approach may introduce very unpleasant (and platform/driver-specific) bugs to your programs, but since it is not explicitly covered by specification, one should not act on assumption that no implementation would ever do this. So to be completely safe, it's better to tie your own function pointers to a specific GL version and load them while a context of that specific version is active. This does no harm (save some programming effort) but deals with both possible cases equally well. Perhaps at some point, the specs will become explicit in this regard, but for now this is the safest route to take.

Share this post


Link to post
Share on other sites
Brother Bob, I guess I misunderstood you.

capricorn, you say that it is implementation specific. I am speaking in terms of what I have experienced.
Sure, keep multiple sets of pointers because according to MSDN, you should.

As for GLEW, IIRC, when I setup my multisampled pixelformat on my window and called glewInit again, it farted. Since glewInit did not like to be called twice, I commented out the one for my multisampled Window.
There is the multiple context GLEW (GLEW MX) but I didn't bother with it.

Share this post


Link to post
Share on other sites
[quote name='V-man' timestamp='1311855127' post='4841542']
capricorn, you say that it is implementation specific. I am speaking in terms of what I have experienced.
Sure, keep multiple sets of pointers because according to MSDN, you should.
[/quote]

I didn't mean to offend, and I apologize if that is the case. I as well have not encountered any problems with contexts in the past, but only recently I realized that it's more luck than anything.

What I meant doesn't only concern MSDN's wglGetProcAddress documentation or Microsoft at all. Here's an excerpt from wglCreateContextAttribs spec (exactly the same holds for glXCreateContextAttribs too):

[quote]
[font="monospace"][size="2"]All OpenGL 3.2 implementations are required to implement the core profile, but implementation of the compatibility profile is optional.[/size][/font]

[font="monospace"][size="2"]...[/size][/font]

[font="monospace"][size="2"]The legacy context creation routines can only return OpenGL 3.1 [/size][/font][font="monospace"][size="2"]contexts if the GL_ARB_compatibility extension is supported, and can [/size][/font][font="monospace"][size="2"]only return OpenGL 3.2 or greater contexts implementing the [/size][/font][font="monospace"][size="2"]compatibility profile. This ensures compatibility for existing[/size][/font][font="monospace"][size="2"] applications. However, 3.0-aware applications are encouraged to use[/size][/font][font="monospace"][size="2"] wglCreateContextAttribsARB instead of the legacy routines. [/size][/font]
[/quote]
This implies that drivers do not have to even expose modern GL functionality when you create context via wglCreateContext (or glXCreateContext). Yes, it seems that currently, both major vendors create compatibility profile of maximum-supported GL versions for legacy contexts, so there seems to be nothing to worry about. Furthermore, nVidia [url="http://developer.nvidia.com/opengl-driver"]clearly states[/url] that it will continue to do so in the future. I've seen no similar claims from AMD.

Here is what happens if I simulate the situation where there is no compatibility profile, thus wglCreateContext creates 2.1 context. I manually create two contexts, 2.1 and 4.1 and try to fetch glDrawArraysInstanced function from both. Here's the code (written in D2):

[code]

auto dc = GetDC(hwnd);

int[] attribs21 =
[
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
0,
];

int[] attribs41 =
[
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0,
];

auto rc21 = wglCreateContextAttribsARB(dc, null, attribs21.ptr);
auto rc41 = wglCreateContextAttribsARB(dc, null, attribs41.ptr);

enum testFunc = "glDrawArraysInstanced";
// Try to fetch glDrawArraysInstanced from 2.1 context
wglMakeCurrent(dc, rc21);
writeln("GL", to!string(cast(char*)glGetString(GL_VERSION)), ", ", testFunc, " address == ", wglGetProcAddress(testFunc));
// Try to fetch glDrawArraysInstanced from 4.1 context
wglMakeCurrent(dc, rc41);
writeln("GL", to!string(cast(char*)glGetString(GL_VERSION)), ", ", testFunc, " address == ", wglGetProcAddress(testFunc));
[/code]

Here are the results (GeForce GTS 450 with 275.33 driver):

[quote]
GL2.1.2, glDrawArraysInstanced address == 0
GL4.1.0, glDrawArraysInstanced address == 69A31840
[/quote]

As you can see, should the driver create 2.1 context instead of compatibility profile of modern one, blind loading of all GL functions on legacy context creation stops working. I would suggest that similar behavior is possible for functions removed from modern version, though on my setup I get the same address for, say, glVertexPointer, it simply behaves as it should on 3.1+ (i.e. returns GL_INVALID_OPERATION). Why I think so is because technically, maintaining the same function pointer in this case must come with an overhead (i.e. version comparison), albeit currently negligible. The more functions will get removed, the more it will become noticeable for programs that still use them (i.e. today's software).

Again, at the current moment, I think both AMD and nVidia create a compatibility profile for legacy contexts. How it will hold in the future is entirely up to them. Personally, I think that one's better off counting on specs rather than on various company claims, because the company can't override specification due to, say, "marketing reasons" and stay compliant. If this sounds only like a matter of preference, then I prefer to not have a headache in a year or two from unexpected bugs on new hardware/drivers.

Share this post


Link to post
Share on other sites
No, you didn't offend me.
My point is that there is 1 single driver and it will always return the same pointer no matter what hw accelerated pixel format you chose.

I'm not sure why you would create a GL 2.1 context and call wglGetProcAddress("glDrawArraysInstanced"). That doesn't make any sense.It would be like calling wglGetProcAddress("EatCrap") and expecting to get a valid pointer.

Share this post


Link to post
Share on other sites
[quote name='V-man' timestamp='1311944524' post='4842123']
No, you didn't offend me.
[/quote]

I'm glad to hear it. English is not my native, and I can't say I'm good at "toning" my messages right, so I was afraid I'm ruining the discussion.

[quote]
My point is that there is 1 single driver and it will always return the same pointer no matter what hw accelerated pixel format you chose.
[/quote]
The issue I'm talking about is not related to pixel formats at all. But the driver is a whole different story. You say there is one single driver. But in fact, there is one [b]current driver version[/b]. What will the new driver version do in half a year? The driver for a next OS version? What will the driver from the same manufacturer, but made for a different platform, do? How can you be sure it'll behave exactly as it does now, considering that specification allows it to behave differently?

[quote]
I'm not sure why you would create a GL 2.1 context and call wglGetProcAddress("glDrawArraysInstanced"). That doesn't make any sense.It would be like calling wglGetProcAddress("EatCrap") and expecting to get a valid pointer.
[/quote]
Yeah, I can see I wasn't clear enough. I'll try to elaborate. The common practice to use modern GL is:

1) create a legacy context via wglCreateContext
2) load [b]all[/b] GL function pointers (including "non-legacy" ones, i.e. 3.0+ functions)
3) create modern GL context
4) use modern GL functionality

According to the specs, there is a flaw in this approach. The drivers don't have to provide modern GL functionality for legacy contexts. They [b]may[/b] provide it, and they currently do, but that is not a requirement. In other words, wglCreateContext (or glXCreateContext, or whatever it is on Mac) may either return 3.1+ compatibility profile context, or 3.0 (or even earlier) one. With my test case I tried to demonstrate what would happen if the driver didn't provide compatibility profile. Namely, point (2) from the above list wouldn't work as it should: not all GL function pointers would be loaded. I took version 2.1 just as a common pre-GL3 version. I might as well have taken 3.0 and some 3.3 function instead. Today's drivers do implement compatibility profile, which means they can provide modern contexts for legacy context creation calls, and thus allow for the above point (2) to work. Maybe they'll continue doing so in the future, maybe they won't. Well, nVidia currently claims it will. AMD - I don't know. Other vendors - I don't know. Sticking with the specs (and loading functions according to GL version) would make the developer a bit (in my case - a lot) happier if the driver vendor drops compatibility profile at some point.

Share this post


Link to post
Share on other sites
[quote name='capricorn' timestamp='1311986468' post='4842373']
The issue I'm talking about is not related to pixel formats at all. But the driver is a whole different story. You say there is one single driver. But in fact, there is one [b]current driver version[/b]. What will the new driver version do in half a year? The driver for a next OS version? What will the driver from the same manufacturer, but made for a different platform, do? How can you be sure it'll behave exactly as it does now, considering that specification allows it to behave differently?[/quote]

That's fine. Code your program to handle the situation in case you get a different set of pointers for different pixel formats. That is what everyone is suppose to be doing anyway. I think you got confused when I said that their is 1 driver and hw accelerated pixel formats, you get the same pointer. I was only speaking in terms of my observation.

[quote name='capricorn' timestamp='1311986468' post='4842373'] According to the specs, there is a flaw in this approach. The drivers don't have to provide modern GL functionality for legacy contexts[/quote]

That's not a flaw. The spec is always right.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this