help: OpenGL/GLSL v2.10/v1.10 -- to -- v3.20/v1.50

Started by
25 comments, last by maxgpgpu 14 years, 3 months ago
I need to update a 3D simulation/graphics/game engine from OpenGL v2.10 plus GLSL v1.10 to OpenGL v3.20 plus GLSL v1.50 - and it seems a bit overwhelming. I have the latest OpenGL/GLSL specifications from www.opengl.org/registry, and installed the latest drivers for my GT285 GPU card. Has anyone written a guide or checklist for performing such conversion? I see there is some kind of "compatibility profile" system that seems like it should help, but I don't see how that works in practice (in the code). Does that let me update one feature at a time, and be able to compile/execute/test the code containing a mix of "old and new"? If not, what does this do. How do I enable this? Everything in the current version is pretty-much state-of-the-art v2.10 techniques - nothing "old". For example, all index and vertex data is contained in IBOs and VBOs - no begin-end, no display-lists, etc. Every vertex attribute is a generic attribute (no fixed built-in attributes). Anyone gone through this and have tips, or know where tips are posted? Thanks. [Edited by - maxgpgpu on January 3, 2010 11:52:25 PM]
Advertisement
The Quick Reference Card might be of help to you. It makes it pretty clear which functions and constants are now deprecated (and thus unusable in 3.2). That said, as long as you're already doing everything in shaders (no fixed function) and using generic attributes/uniforms as you say, you shouldn't have too much work ahead of you.
It'll be easy for you, then:
1) load core ext-funcs (i.e glGenFramebuffers instead of glGenFramebuffersEXT)
2) in shaders, replace keywords varying/attrib with in/out; a replacement for gl_FragColor has to be defined manually; all built-in uniforms are gone; gl_Position stays (in almost all cases)
3) LATC-> RGtc textures

With the compatibility profile does what you expect it to do, so the transition can be done smoothly.
Wide-lines are not supported in forward-compatible profile.
Lots of simplifications in the API, less strict FBOs, lots of extra GLSL features.
shrinkage:
Thanks, I printed the quick reference sheets, and they are helpful.

idinev:
I see where GLSL supports "#version 150 compatibility", but I haven't yet noticed what I need to do in my OpenGL code to request "compatibility" with both new and old features.

-----

I am confused to read that gl_FragDepth, gl_FragColor, gl_FragData[4] are depracated. Unless --- perhaps all this means is, the "automatic inherent declaration" of these fragment output variables will be removed, so we'll be forced to declare those we need explicitly in our fragment shaders. But if they intend to remove those output variables entirely... then how does a fragment shader write a depth value, or 1~4+ color values to the default framebuffer (or 1~4+ attached framebuffer objects)? Surely they are not saying we can simply invent our own names for gl_FragDepth, gl_FragColor and gl_FragData[4+]... or are they? That seems silly, since the GPU hardware requires those values to perform the rest of its hardware implemented operations (like store the color(s) into 1~4+ framebuffers if the new depth is less-than the existing depth).

-----

I've been meaning to put all my matrix, light and material information into uniform variables for quite some time. Now it seems like "uniform blocks" are just perfect for that. So that'll be a fair chunk of alteration, but hopefully not too complex.

-----

I worry most about setting up one or more VAOs to reduce rendering overhead. No matter how many times I read discussions of VAOs, I never fully understand (though I always "almost" understand). Since all my vertices have identical attributes, perhaps all I need is one VAO, into which I jam the appropriate IBO before I call glDrawElements() or glDrawInstancedElements(). Oh, I guess I also need to enable the appropriate VBO before calling these functions. OTOH, sometimes I think I need a separate VAO for every VBO... but I just dunno.

-----

I'm planning to send 4 colors (RGBA), 2 tcoords (u,v), 1 texture ID, and a bunch of flag bits into the shader as a single uvec4 vertex attribute. Currently they are all converted from u16 integers to floats automatically, but I figure sending them all in one uvec4 will be more efficient on CPU code. This means I need to add code to my vertex shader to unpack the u16 elements from the u32 elements of the uvec4 variable, then convert them to floats with something like "vf32 = vu16 / 65535". I figure this will be faster overall by taking load off the CPU. On second thought, this work can't be done by the CPU, since this process happens when the GPU fetches the elements from my vertices, which are inside the VBOs, which are in GPU memory. Hmmm? Strange. Does anyone know how much overhead exists for performing all these integer to float conversions when vertex elements are loaded into the vertex shader? If that's efficient, I should leave the setup as is, except for two variables that I need in their native integer form (texture-# and flag bits).

-----

I guess texture coordinates can be passed from vertex to fragment shader as normal/generic output variables (formerly varying AKA interpolating).

-----

As long as I can change and test one item at a time, I should be okay. To change everything at once would surely create havoc. So again, how does my OpenGL code tell OpenGL to allow a mix of new and old features?

-----

Thanks for the tips.
For Gl3.2, you need to request a GL3.2 context.
	wglMakeCurrent(hDC,hRC);		PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");	if(!wglCreateContextAttribsARB){		MessageBoxA(0,"OpenGL3.2 not supported or enabled in NVemulate",0,0);	}else{		int attribs[] = {			WGL_CONTEXT_MAJOR_VERSION_ARB, 3,			WGL_CONTEXT_MINOR_VERSION_ARB, 2,			WGL_CONTEXT_FLAGS_ARB, 0,//WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,  <--------- ADD COMPATIBLE-FLAG HERE			0,0		};				HGLRC gl3Ctx = wglCreateContextAttribsARB(hDC, 0, attribs);		if(gl3Ctx){			wglDeleteContext(hRC);			hRC =gl3Ctx;			wglMakeCurrent(hDC,hRC);			//prints("opengl3.2 success");			//prints((char*)glGetString(GL_VERSION));		}else{			MessageBoxA(0,"OpenGL3.2 context did not init, staying in 2.1",0,0);		}	}



gl_FragDepth stays. You just explicitly define the frag-outputs:
"out vec4 glFragColor;" or "out vec4 glFragData[3];" - notice the dropped '_' .




Perhaps this will make the usage of VAOs clear:

static void DrawVBO_Indexed(ILI_VBO* me){	if(me->isDwordIndex){		glDrawElements(me->PrimitiveType,me->iboSize/4,GL_UNSIGNED_INT,   NULL);	}else{		glDrawElements(me->PrimitiveType,me->iboSize/2,GL_UNSIGNED_SHORT, NULL);	}}static void DrawVBO_NonIndexed(ILI_VBO* me){	glDrawArrays(me->PrimitiveType,0,me->numVerts);}#define USE_VAOvoid ilDrawVBO(ILVBO vbo){				if(vbo->glVAO){			glBindVertexArray(vbo->glVAO);			if(vbo->glIBO){ // we have an index				DrawVBO_Indexed(vbo);			}else{				DrawVBO_NonIndexed(vbo);			}			glBindVertexArray(0);			return; // done drawing it		}		if(vbo->numVerts==0)return;		if(vbo->vtxSize==0)return;		ClearCurClientArrays(); // clears VAs		BindVBO(vbo); // binds VAs (with their VBO) and IBO; flags them as to-be-enabled		EnableCurClientArrays(); //enables the flagged VAs		if(vbo->glIBO){ // we have an index			DrawVBO_Indexed(vbo);		}else{			DrawVBO_NonIndexed(vbo);		}	}
Quote:Original post by maxgpgpu
I am confused to read that gl_FragDepth, gl_FragColor, gl_FragData[4] are deprecated...


Read Section 7.2 of the GLSL 1.5 spec.

Quote:Original post by maxgpgpu
I worry most about setting up one or more VAOs to reduce rendering overhead. No matter how many times I read discussions of VAOs, I never fully understand (though I always "almost" understand).


VAOs just allow you to "collect" all the state associated with VBOs into a single object that can be reused. When you have a VAO bound, it will store all the state changes from any VertexAttribPointer and EnableVertexAttribArray, and apply that state whenever it is bound. Further, all rendering calls will draw their state from the bound VAO. A newly created VAO provide a default state as defined in Section 6.2 of the spec.

Quote:Original post by maxgpgpu
This means I need to add code to my vertex shader to unpack the u16 elements from the u32 elements of the uvec4 variable, then convert them to floats with something like "vf32 = vu16 / 65535".


Whatever format you're going to need them to be when you use them should really be the format you send them to the GPU as, unless there's a really good reason not to. Why would you need to convert flag bits to floats? Not really clear on what's going on here.

Quote:Original post by maxgpgpu
I guess texture coordinates can be passed from vertex to fragment shader as normal/generic output variables (formerly varying AKA interpolating).


Yes, the varying and attribute storage qualifiers are deprecated. You can specify the type of interpolation using smooth, flat, and noperspective qualifiers.

Quote:Original post by maxgpgpu
As long as I can change and test one item at a time, I should be okay. To change everything at once would surely create havoc. So again, how does my OpenGL code tell OpenGL to allow a mix of new and old features?


See: WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
Quote:Original post by Shinkage
VAOs just allow you to "collect" all the state associated with VBOs into a single object that can be reused. When you have a VAO bound, it will store all the state changes from any VertexAttribPointer and EnableVertexAttribArray, and apply that state whenever it is bound. Further, all rendering calls will draw their state from the bound VAO. A newly created VAO provide a default state as defined in Section 6.2 of the spec.
Since every vertex in my application has the same layout, I take this to mean I can simply create and bind one VAO, and just leave it permanently bound. Then, to draw the primitives in each VBO, I simply bind that VBO, then bind the IBO that contains indices into it - then call glDrawElements(). That would be very nice... no redefining attribute offsets and reenabling attributes before every glDrawElements().

Quote:Shinkage
Whatever format you're going to need them to be when you use them should really be the format you send them to the GPU as, unless there's a really good reason not to. Why would you need to convert flag bits to floats? Not really clear on what's going on here.
Here is what my vertices look like (with each 32-bit element between :colons:.
position.x   :   position.y   :   position.z   :  east.xzenith.x     :   zenith.y     :   zenith.z     :  east.ynorth.x      :   north.y      :   north.z      :  east.zcolor.rg     :   color.ba     :   tcoord.xy    :  tcoord.z + flagbits

Note that "zenith", "north", "east" are just descriptive names I give to the surface vectors (more often called "normal", "tangent", "bitangent"). Note how I put the "east" vector into the .w component of the other vectors, so I can tell OpenGL/GLSL to load all four vec3 elements I need in only three vec4 attributes. This leaves the final row of elements. In my OpenGL code, each of the "color" and "tcoord" elements are u16 variables in the CPU version of my vertices, to get the most dynamic range and precision possible in 16-bits. But "color.rgba" and "tcoord.xy" must be converted to f32 variables IN the shader, or ON THE WAY to the shader, because that's most natural for shader code. However, what I call "tcoord.z" needs to stay an integer in the shader, because that value selects the desired texture --- from the one texture array that my program keeps all textures in (and normal-maps, and potentially lookup-tables and more). The flagbit integer also needs to stay an integer, because those bits tell the shader how to combine color.rgba with texture-color.rgba, how to handle alpha, whether to perform bump-mapping, horizon-mapping, etc.

In my OpenGL v2.10 code those tcoord.z and flagbits are floating point values, which I've managed to make work via kludgery, but obviously the code will become clean the moment they become the integers they should be.

Okay, now finally to my point and question.

I could define one attributes to make the GPU load and convert the u16 color.rgba values into a vec4 color.rgba variable, and another attribute to make the GPU load and convert the u16 tcoord.xy values into a vec2 tcoord.xy variable, and another attribute to make the GPU load the u16 tcoord.z value into a u16 variable, and another attribute to make the GPU load the u16 flagbits value into a u16 variable.

OR

I could define one attribute to make the GPU load the entire final row of values (color.rgba, tcoord.xy, tcoord.z, flagbits) into a single uvec4 variable, then convert them to the desired types with my own vertex shader code. My code would need to isolate the 8 u16 values, convert the first 6 of those values into f32 variables by multiplying them by (1/65535). The last 2 u16 values are perfectly fine as u16 values (in most paths through my shaders).

I have not been able to decide whether my application will be faster if I define those 4 separate attributes, and let the GPU convert and transfer them separately... or whether loading them all into the shader as one attribute, then extracting the elements myself will be faster. That's my question, I guess.

Without the VAO (which is where I am before converting to v3.20/v1.50), the extra code to define and enable those extra attributes before each glDrawElements() call convinced me it was better to send that last row of the vertex structure as one attribute - to reduce overhead on the CPU. Once the VAO is in place, and the CPU need not define and enable those attributes, the choice is not so clear. Any idea which is faster with VAO?
idinev:
I can't get an OpenGL v3.20 context. In fact, I can't successfully compile a program with the wglCreateContextAttribs() function in it (function not recognized), and ditto for the new constants like WGL_CONTEXT_MAJOR_VERSION_ARB and so forth.

While brings me back to a question I forgot (or never knew)... where are the WGL functions located (in what .lib file), and where are the WGL declarations (in what .h file). My program includes the glee.h and glee.c files, but they don't seem to contain those symbols... so what do I need to do to create a compilable OpenGL v3.20 program?

NOTE: When I create a normal context with wglCreateContext(), the following line returns a "3.2.0" string in the "version" variable... thus I suppose a v3.20 version is being created.

  const u08* version = glGetString (GL_VERSION);


However, some constants and functions I would expect to be defined in a v3.20 context are not defined, including:

GL_MAJOR_VERSION
GL_MINOR_VERSION
WGL_CONTEXT_MAJOR_VERSION
WGL_CONTEXT_MINOR_VERSION
WGL_CONTEXT_MAJOR_VERSION_ARB
WGL_CONTEXT_MINOR_VERSION_ARB
wglCreateContextAttribs()
wglCreateContextAttribsARB()

Also, to make the glGetAttribIPointer() function to work in my program, I had to change the name to glGetAttribIPointerEXT(). However, the EXT should not be necessary in v3.20, correct?

So it seems like I need to do something to make the later declarations available. Perhaps GLEE and GLEW have simply fallen behind the times, and this explains my problems... I'm not sure. Perhaps I should try to find the nvidia headers and switch from GLEE to the nvidia stuff (thoug I had problems doing that last year when I last attempted this, and I finally gave up and continued on with GLEE). Advice?


[Edited by - maxgpgpu on December 28, 2009 6:00:03 AM]
GLee has not yet been upgraded to support GL3.2. Afaik it supports only 3.1. For 3.2 support you could try glew source code from repository.

Or, if you dont want to use glee or glew, then WGL_... defines are in wglext.h file (google for it). And wgl... functions must be loaded dynamically as rest of non-v1.1 gl/wgl functions - through wglGetProcAddress function.
So, I guess you mean "PFNWGLCREATECONTEXTATTRIBSARBPROC" was an unknown symbol?
Anyway, instead of waiting for Glee, Glew and whatnot to update/etc, I suggest you try my way:

Visit http://www.opengl.org/registry/ and get the (latest versions of) header-files glext.h and wglext.h . They're always at:
http://www.opengl.org/registry/api/glext.h
http://www.opengl.org/registry/api/wglext.h

Download my http://dl.dropbox.com/u/1969613/openglForum/gl_extensions.h

In your main.cpp or wherever, put this code:
#define OPENGL_MACRO(proc,proctype) PFN##proctype##PROC proc#define OPTIONAL_EXT(proc,proctype) PFN##proctype##PROC proc#include "gl_extensions.h" // this defines the variables (function-pointers)static PROC uglGetProcAddress(const char* pName,bool IsOptional){	PROC res = wglGetProcAddress(pName);	if(res || IsOptional)return res;	MessageBoxA(0,pName,"Missing OpenGL extention proc!",0);	ExitProcess(0);}void InitGLExtentions(){ // this loads all function-pointers	#define OPENGL_MACRO(proc,proctype) proc = (PFN##proctype##PROC)uglGetProcAddress(#proc,false)	#define OPTIONAL_EXT(proc,proctype) proc = (PFN##proctype##PROC)uglGetProcAddress(#proc,true)	#include "gl_extensions.h"}


In the source files, where you need GL calls do:
#include <gl/gl.h>#include <gl/glext.h>#include <gl/wglext.h>#include "gl_extensions.h"


Voila, you're ready to use all GL symbols. And without waiting for glee/glew, you can add more funcs in the gl_extensions.h file when they're available (after a new extension comes-out and a driver-update supports it).

This topic is closed to new replies.

Advertisement