TIGSource Forums

Developer => Technical => Topic started by: Ethan_Hall on June 14, 2013, 12:52:31 PM



Title: OpenGL function calls crash program
Post by: Ethan_Hall on June 14, 2013, 12:52:31 PM
Hey all,

I'm working on a small library for making games using OpenGL. I created a static library into a .lib file in MSVC, and I'm using that library for a sample game to test that the library works.

The problem I am running into has to do with calling the function pointers that point to OpenGL extension functions. I am manually loading in the OpenGL extensions using wglGetProcAddress (I want to do things manually so I understand more of what is going on). I think it has to do with the way I'm setting up my code:


ogl_framework.h
Code:
#ifndef _OGL_DRAWING_H_
#define _OGL_DRAWING_H_

#ifdef _WIN32
#include <windows.h>
#include <gl\gl.h>
#pragma comment(lib, "opengl32.lib")

#include "glext.h"
#include "wglext.h"

#endif // _WIN32

#include <math.h>

namespace eagle_alpha
{
namespace ogl_framework
{
//Function pointers for extensions, should be platform agnostic
static PFNGLATTACHSHADERPROC glAttachShader;
static PFNGLBINDBUFFERPROC glBindBuffer;
static PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
static PFNGLBUFFERDATAPROC glBufferData;
static PFNGLCOMPILESHADERPROC glCompileShader;
static PFNGLCREATEPROGRAMPROC glCreateProgram;
static PFNGLCREATESHADERPROC glCreateShader;
static PFNGLDELETEBUFFERSPROC glDeleteBuffers;
static PFNGLDELETEPROGRAMPROC glDeleteProgram;
static PFNGLDELETESHADERPROC glDeleteShader;
static PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
static PFNGLDETACHSHADERPROC glDetachShader;
static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
static PFNGLGENBUFFERSPROC glGenBuffers;
static PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
static PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
static PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
static PFNGLGETPROGRAMIVPROC glGetProgramiv;
static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
static PFNGLGETSHADERIVPROC glGetShaderiv;
static PFNGLLINKPROGRAMPROC glLinkProgram;
static PFNGLSHADERSOURCEPROC glShaderSource;
static PFNGLUSEPROGRAMPROC glUseProgram;
static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
static PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
static PFNGLACTIVETEXTUREPROC glActiveTexture;
static PFNGLUNIFORM1IPROC glUniform1i;
static PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
static PFNGLUNIFORM3FVPROC glUniform3fv;
static PFNGLUNIFORM4FVPROC glUniform4fv;
static PFNGLUNIFORM1FVPROC glUniform1fv;

//actual functions
void Enable2D();
void Disable2D();

#ifdef _WIN32
bool InitializeExtensions(HWND);
bool InitializeOpenGL(HWND, int, int, float, float, bool);
void ShutdownOpenGL(HWND);
bool LoadExtensionList();
GLenum OGL_Error_Checking();
char* Get_OGL_Version();

//Function pointers for windows WGL extensions
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
#endif // _WIN32
}
}

#endif // _OGL_DRAWING_H_


And here is the code that decides to crash:


TextureQuadObject.cpp

Code:
#include "TextureQuadObject.h"

namespace eagle_alpha
{

TextureQuad::TextureQuad()
{
vertexArrayID = 0;
vertexBufferID = 0;
indexBufferID = 0;
}

TextureQuad::~TextureQuad()
{

}

bool TextureQuad::InitQuad()
{

// Allocate an OpenGL vertex array object.
eagle_alpha::ogl_framework::glGenVertexArrays(1, &vertexArrayID);

// Bind the vertex array object to store the vertex buffers and vertex attributes we create here.
eagle_alpha::ogl_framework::glBindVertexArray(vertexArrayID);

// Generate an ID for the vertex buffer.
eagle_alpha::ogl_framework::glGenBuffers(1, &vertexBufferID);


I know some of it looks ugly, I'm working on fixing it...

So I believe it has to do with the function pointers of ogl_framework.h being static. If I use these functions within ogl_framework.cpp (not shown) they work like a charm. But I want to use these functions from outside this translation unit, as per the example above.

I need these function pointers in the header file so other translation units can access them using the eagle_alpha::ogl_framework namespace. They need the static keyword to resolve scope issues.

Not sure exactly how to proceed, but I'm going to keep fooling around with it to see if I can fix it entirely. Thanks for any/all help or criticism!


Title: Re: OpenGL function calls crash program
Post by: Polly on June 14, 2013, 01:03:41 PM
OpenGL is a C-style API .. ditch the namespaces.


Title: Re: OpenGL function calls crash program
Post by: Ethan_Hall on June 14, 2013, 01:43:56 PM
I have since removed the namespaces surrounding the function pointers in the header file... but I still get the same error when encountering these functions... :'(


Title: Re: OpenGL function calls crash program
Post by: ThemsAllTook on June 14, 2013, 03:03:03 PM
A crash usually comes with some information about what went wrong (and if it doesn't, better diagnostic tools are needed). What does your stack trace look like?


Title: Re: OpenGL function calls crash program
Post by: Average Software on June 14, 2013, 03:17:51 PM
Your problem is that static doesn't mean what you think it means in this case.  Look up the use of the keyword extern.


Title: Re: OpenGL function calls crash program
Post by: Ethan_Hall on June 15, 2013, 08:51:09 AM
@ThemsAllTook my stack trace isn't helping me out much. The function I am calling is a member function of an instance class. This member function calls an OpenGL function defined in another translation unit. The last function that is seen in the stack trace is the member function, with this:

Frames below this may be incorrect and/or missing.

And the message is that I had an Access Violation when executing location 0x000000000 (or something akin to that).

I have tried using the extern keyword... I have used it before successfully, but I guess I am kind of confused right now how I should use it. My function pointers are declared like this:
Code:
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLBINDBUFFERPROC glBindBuffer;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
PFNGLBUFFERDATAPROC glBufferData;

So, I tried using extern as follows:

Code:
extern PFNGLATTACHSHADERPROC glAttachShader = NULL;
extern PFNGLBINDBUFFERPROC glBindBuffer = NULL;
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray = NULL;
extern PFNGLBUFFERDATAPROC glBufferData = NULL;

But that blew up even more.

I guess I am kind of confused why the static keyword doesn't work. Using it allows my program to compile and allows other translation units to see these function pointers. But this is obviously wrong since using these functions in different translation units always leads to a crash.

Also, thanks for the help so far everyone!


Title: Re: OpenGL function calls crash program
Post by: Polly on June 15, 2013, 09:03:53 AM
Use ..

Code:
extern "C"
{
extern PFNGLATTACHSHADERPROC glAttachShader;
// Etc.
}

.. in your header, and ..

Code:
extern "C"
{
PFNGLATTACHSHADERPROC glAttachShader;
// Etc.
}

.. in your source file.


Title: Re: OpenGL function calls crash program
Post by: Ethan_Hall on June 15, 2013, 09:21:57 AM
Thanks @Polly, that seems to have done the trick! I will update if there are any further troubles.

So is this the issue I am running into (via wikipedia):


    C compilers do not name mangle symbols in the way that C++ compilers do.
    Depending on the compiler and architecture, it also may be the case that calling conventions differ between the two languages.

For these reasons, for C++ code to call a C function foo(), the C++ code must prototype foo() with extern "C"




Title: Re: OpenGL function calls crash program
Post by: Polly on June 15, 2013, 09:37:50 AM
So is this the issue I am running into

You did a couple of things wrong .. not preventing C++ function name mangling was one of them yes.


Title: Re: OpenGL function calls crash program
Post by: Average Software on June 15, 2013, 10:03:41 AM
extern "C" is not required, all you need is this in your header:

Code:
namespace eagle_alpha
{
 namespace ogl_framework
 {
  //Function pointers for extensions, should be platform agnostic
  extern PFNGLATTACHSHADERPROC glAttachShader;
  extern PFNGLBINDBUFFERPROC glBindBuffer;
  // etc...
  }
}

And this in your source file:

Code:
PFNGLATTACHSHADERPROC eagle_alpha::ogl_framework::glAttachShader;
PFNGLBINDBUFFERPROC eagle_alpha::ogl_framework::glBindBuffer;
//etc...

What static does in this case is create a variable private to a translation unit.  Since you placed it in a header, every file that includes that header gets its own, unique copy of those variables..

That is, every file has its own glAttachShader.  That's why the other files are crashing with null pointers, their private copies of those variables are not being set.

Using static on a namespace scope symbol in a header file is almost always wrong.

Quote
C compilers do not name mangle symbols in the way that C++ compilers do.
    Depending on the compiler and architecture, it also may be the case that calling conventions differ between the two languages.

For these reasons, for C++ code to call a C function foo(), the C++ code must prototype foo() with extern "C"

This was not actually your problem.  Polly's solution works, but for different reasons.

Your problem was a misuse of the keyword static.  Name mangling isn't part of the issue.


Title: Re: OpenGL function calls crash program
Post by: Xienen on June 17, 2013, 04:34:57 AM
This was not actually your problem.  Polly's solution works, but for different reasons.

Your problem was a misuse of the keyword static.  Name mangling isn't part of the issue.

I concur, as name mangling only applies to functions you declare, but you are declaring variables in this case.  The reason that Polly's solution works is that it used the proper keyword "extern" in the header file which allows each translation unit to know that there is a variable out there, somewhere, with that name.  During the final step of compilation, the linker attempts to resolve these "externs" and if it is unable to find the global variable with the same name in a compiled file(typically a c or cpp file) it will throw a linker error.


Title: Re: OpenGL function calls crash program
Post by: Average Software on June 17, 2013, 05:17:18 AM
This was not actually your problem.  Polly's solution works, but for different reasons.

Your problem was a misuse of the keyword static.  Name mangling isn't part of the issue.

I concur, as name mangling only applies to functions you declare, but you are declaring variables in this case.  The reason that Polly's solution works is that it used the proper keyword "extern" in the header file which allows each translation unit to know that there is a variable out there, somewhere, with that name.  During the final step of compilation, the linker attempts to resolve these "externs" and if it is unable to find the global variable with the same name in a compiled file(typically a c or cpp file) it will throw a linker error.

Furthermore, OpenGL and function pointers are red herrings.  This is purely a language issue, the same sorts of problems would have showed up if they were ints instead of function pointers.


Title: Re: OpenGL function calls crash program
Post by: Polly on June 17, 2013, 05:17:34 AM
You guys aren't thinking ahead far enough ;)


Title: Re: OpenGL function calls crash program
Post by: Xienen on June 17, 2013, 05:44:16 AM
You guys aren't thinking ahead far enough ;)

Haha, fair enough ;)


Title: Re: OpenGL function calls crash program
Post by: Daid on June 17, 2013, 07:02:35 AM
I've never got this to work properly for me until I used GLEW:
http://glew.sourceforge.net/


Title: Re: OpenGL function calls crash program
Post by: Xienen on June 17, 2013, 12:32:02 PM
I've never got this to work properly for me until I used GLEW:
http://glew.sourceforge.net/

Yeah, OpenGL extensions are a real bitch to deal with, but it can certainly be done without using GLEW.  The real question, though, would be "Is it worth it to do it on your own without GLEW?" and for most people, I don't think it's worth the hassle.