PDA

View Full Version : Does opengl 1.4 support retained mode?



6krktr
04-06-2017, 12:52 PM
And will openGL 1.4 programs on either mode run on machines that only support, say, 4.5?

mhagain
04-06-2017, 01:04 PM
You need to be clear what you mean by "retained mode"; no version of OpenGL has a mode that is formally known as "retained mode".

At a guess I would think that you mean "not immediate mode", i.e drawing using vertex arrays and vertex buffers.

Vertex arrays have existed since the GL_EXT_vertex_array (http://oss.sgi.com/projects/ogl-sample/registry/EXT/vertex_array.txt) extension and were made core in OpenGL 1.1; so long as your GL_VERSION is 1.1 or higher you can just use vertex arrays without needing to check for extensions. It is not recommended to use GL_EXT_vertex_array; just use the GL 1.1 entry points and enums without the -EXT suffix.

VBOs have existed since the GL_ARB_vertex_buffer_object (https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_vertex_buffer_object.txt) extension and were made core in OpenGL 1.5; in this case the core version is the same as the extension so you can safely use the extension as a fallback for hardware lower than OpenGL 1.5 without otherwise changing the code (on the other hand you definitely do not want to code fallbacks for anything older than 1.1).

In the (extremely) unlikely event that you find hardware that still works and is actually in use but that doesn't support either GL_ARB_vertex_buffer_object or GL 1.5, the extension can be almost trivially software-emulated in your own code.

Generic vertex attributes have existed since the GL_ARB_vertex_program (https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_vertex_program.txt) extension, but this extension was never promoted to core OpenGL. Support for it is absolutely ubiquitous among the 3 main desktop GL vendors however; even crusty old parts like the Intel 910 support it, and you can quite reliably assume that it's present. Generic attributes were also brought into OpenGL 2.0, so if you want to use them you should check for a GL_VERSION of 2.0 or higher, or use the extension as a fallback.

Each OpenGL version, excluding core profiles, is a superset of all preceding versions so yes, newer GL versions are quite capable of running programs written for older.

Beware however that OpenGL specifies functionality, not performance. While an older program will work, the code paths it uses will probably be sub-optimal on modern hardware and drivers.

6krktr
04-06-2017, 01:20 PM
You need to be clear what you mean by "retained mode"; no version of OpenGL has a mode that is formally known as "retained mode".
I'm referring to immediate and retained mode as discussed here: (I would post a link to it instead, but it doesn't let me as a new user)

What does “immediate mode” mean in OpenGL? - Stack Overflow

One example of "immediate mode" is using glBegin and glEnd with glVertex in between them. Another example of "immediate mode" is to use glDrawArrays with a client vertex array (i.e. not a vertex buffer object).

....

Edit:
By common request, the same thing in retained mode would look somewhat like this:

float verts = {...};
float colors = {...};
static_assert(sizeof(verts) == sizeof(colors), "");

// not really needed for this example, but mandatory in core profile after GL 3.2
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

GLuint buf[2];
glGenBuffers(2, buf);

// assuming a layout(location = 0) for position and
// layout(location = 1) for color in the vertex shader

// vertex positions
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

// copy/paste for color... same code as above. A real, non-trivial program would
// normally use a single buffer for both -- usually with stride (5th param) to
// glVertexAttribPointer -- that presumes interleaving the verts and colors arrays.
// It's somewhat uglier but has better cache performance (ugly does however not
// matter for a real program, since data is loaded from a modelling-tool generated
// binary file anyway).
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

glDrawArrays(GL_TRIANGLES, 0, 3);

Dark Photon
04-06-2017, 06:56 PM
I'm referring to immediate and retained mode as discussed here: (I would post a link to it instead, but it doesn't let me as a new user)

This is old Microsoft Direct3D terminology:

* Retained Mode Versus Immediate Mode (https://msdn.microsoft.com/en-us/library/windows/desktop/ff684178(v=vs.85).aspx)

I don't recall it being used much with OpenGL -- mostly just occasional mentions from those coming from Direct3D.

In OpenGL, you'd commonly talk about using: immediate mode, client arrays, or VBOs (which are basically server-side buffers). For OpenGL, use of VBOs is analogous to "retained mode".

Here's a link to that stackoverflow page you were referring to:

* https://stackoverflow.com/questions/6733934/what-does-immediate-mode-mean-in-opengl

One you get a few more posts under your belt, you'll be able to post links (it's a spam-prevention feature in the forum software).

6krktr
04-07-2017, 05:44 AM
Basically I read about retained mode from this SO thread:


What does “immediate mode” mean in OpenGL?

...

By common request, the same thing in retained mode would look somewhat like this:


float verts = {...};
float colors = {...};
static_assert(sizeof(verts) == sizeof(colors), "");

// not really needed for this example, but mandatory in core profile after GL 3.2
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

GLuint buf[2];
glGenBuffers(2, buf);

// assuming a layout(location = 0) for position and
// layout(location = 1) for color in the vertex shader

// vertex positions
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

// copy/paste for color... same code as above. A real, non-trivial program would
// normally use a single buffer for both -- usually with stride (5th param) to
// glVertexAttribPointer -- that presumes interleaving the verts and colors arrays.
// It's somewhat uglier but has better cache performance (ugly does however not
// matter for a real program, since data is loaded from a modelling-tool generated
// binary file anyway).
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

glDrawArrays(GL_TRIANGLES, 0, 3);

I read your edit, but am still new opengl and don't know if you're talking about the quite same thing.

Dark Photon
04-07-2017, 05:57 AM
Basically I read about retained mode from this SO thread:

I read your edit, but am still new opengl and don't know if you're talking about the quite same thing.

Yes. VBOs = Vertex buffer objects. "Buffer objects" refer to blocks of memory owned by the OpenGL graphics driver. glGenBuffers (https://www.khronos.org/opengl/wiki/GLAPI/glGenBuffers), glBindBuffer (https://www.khronos.org/opengl/wiki/GLAPI/glBindBuffer), and glBufferData (https://www.khronos.org/opengl/wiki/GLAPI/glBufferData) in your example above are 3 OpenGL API calls which operate on buffer objects (creating, binding, and allocating data storage for a buffer object, respectively). You can read more about them in the OpenGL wiki here: Buffer Object (https://www.khronos.org/opengl/wiki/Buffer_Object).

mhagain
04-07-2017, 07:49 AM
Basically I read about retained mode from this SO thread:

I read your edit, but am still new opengl and don't know if you're talking about the quite same thing.

Yes, indeed.

The SO confusion likely arises because once upon a time Direct3D (not OpenGL) had an "immediate mode" and a "retained mode". These are referred to in John Carmack's (in)famous OpenGL vs Direct3D rant of 1996, which I guess is where most people pick up the terminology from nowadays.

"Immediate mode" was where you issue graphics API commands to draw stuff.

"Retained mode" was where you describe a scene at quite a high level and the driver generates the graphics API commands for you.

At the time Carmack predicted that a significantly higher percentage of programs would use Direct3D retained mode; as it turned out, the only program I'm aware of that used it was a Quake level editor.

That's the simplified explanation, but fast-forward a few years and Direct3D no longer has "retained mode" so it's an "immediate mode" only API. However, OpenGL has another, different definition of "immediate mode" (and AFAIK it's not even a formal definition but rather something that's grown - like fungus - over the years), and that's the use of glBegin/glVertex/glEnd commands. So at some stage "retained mode" in the context of OpenGL has come to mean "not immediate mode", rather than the original (Direct3D) definition.

Long story short, that's why I asked for clarification - because depending on context, when somebody says "retained mode" it could be one of two things they actually mean.

So to summarize, yes, what the SO thread calls "retained mode" does work in GL 1.4, so long as the GL_ARB_vertex_buffer_object and GL_ARB_vertex_program extensions are available.

However, it can also work in GL versions as low as 1.1 on the following basis.

If GL_ARB_vertex_buffer_object is not available you can draw from system memory pointers rather than from buffer objects.

If GL_ARB_vertex_program is not available you can use glEnableClientState and gl*Pointer calls rather than glEnableVertexAttribArray and glVertexAttribPointer calls.

Dark Photon
04-07-2017, 06:53 PM
"Retained mode" was where you describe a scene at quite a high level and the driver generates the graphics API commands for you.

Thanks for the correction, mhagain. I remember this concept coming up back in the Fahrenheit (https://en.wikipedia.org/wiki/Fahrenheit_%28graphics_API%29) days. But never associated it with what D3D retained mode was.


At the time Carmack predicted that a significantly higher percentage of programs would use Direct3D retained mode; as it turned out, the only program I'm aware of that used it was a Quake level editor.

Apparently there were others. This page contains a short list:

* Direct3D Retained Mode removed from Windows Vista (https://support.microsoft.com/en-us/help/969150/direct3d-retained-mode-removed-from-windows-vista)

More info on ol' Direct3D retained mode:

* Retained Mode Versus Immediate Mode (https://msdn.microsoft.com/en-us/library/windows/desktop/ff684178(v=vs.85).aspx)
* A Direct3D Retained Mode Example in BCB4 (http://edn.embarcadero.com/en/article/10278)
* Direct3D Retained mode sample (http://www.mycplus.com/tutorials/microsoft-direct-x-programming/direct3d-sample/)
* Direct3D Retained Mode DLL (d3drm.dll) 5.1.2600.0 (http://community.pcgamingwiki.com/files/file/3-direct3d-retained-mode-dll-d3drmdll/)

6krktr
04-08-2017, 04:00 AM
Thank you all. So, if I've understood correctly, I only need to use the ARB version of those functions, like glBufferDataARB and glGenBuffersARB. The thing is I don't know which header I should import for them. I currently import only glut.h, glu.h and gl.h. I've tried with glx.h and internal/glcore.h, but none of them contains it.

I upgraded to OpenGL 2.1 a few months ago, but that caused graphical bugs on my OS ( Ubuntu ), I so had to downgrade. So I'm stuck with 1.4 at least for the immediate future.

GClements
04-08-2017, 06:50 AM
Thank you all. So, if I've understood correctly, I only need to use the ARB version of those functions, like glBufferDataARB and glGenBuffersARB. The thing is I don't know which header I should import for them. I currently import only glut.h, glu.h and gl.h. I've tried with glx.h and internal/glcore.h, but none of them contains it.

If you want function prototypes for functions added in later versions of OpenGL and in extensions, you need


#define GL_GLEXT_PROTOTYPES

before including gl.h (the declarations are actually in glext.h, which should be included from gl.h).

Otherwise, you only get declarations for the function pointers, e.g.


typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);


The way that you're supposed to access such functions is to declare a variable to hold the pointer and obtain the pointer with e.g. glXGetProcAddress():


PFNGLGENBUFFERSARBPROC glGenBuffersARB;
...
/* you need to ensure that a context is bound before calling this */
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) glXGetProcAddress("glGenBuffersARB");


The reason for this is that it allows you to use the extension if it's supported, and use a different approach if it isn't. Whereas if you rely upon the function being exported by libGL, your program won't even start on a system where libGL doesn't export that function. Also, just because libGL exports the function, it doesn't mean that the X server supports it, so you're supposed to query the OpenGL version and/or the list of supported extensions before actually using the function.

6krktr
04-09-2017, 08:09 AM
Thank you. After a few hours of playing I think I got the hang of it and made it work. I have two last questions.
Firstly, why do some of the additional functions end in APPLE, instead of ARB or EXT? Is APPLE just a funny name of another extension?
Secondly, judging by the last argument of glBufferDataARB (GL_STATIC_*, GL_DYNAMIC_*, GL_SERIAL_*), this second method of drawing can be used not only in real time, but statically as well. In case I want to draw statically most of the time, but updating the canvas on user demand, is the following methodology sound:

1. Init OpenGL and the ARB extensions once in the constructor.
2. When the scene is to be redrawed (maybe due to user input, maybe due to resizing), remake all of the objects and draw them anew.

The essence of that in code:

//header:
typedef struct
{
double x, y, r, g, b, a;
}
vertex;

// in the class definition:
void ogldraw(GLenum mode, std::vector<vertex> points);
void initARBs();
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
// ... the rest of them ...
PFNGLENABLECLIENTSTATEIEXTPROC glEnableClientStateiEXT;
bool run;

//cpp:
// in the constructor:
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(200,200);
initARBs();
}

void OGLplotter::initARBs()
{
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) glXGetProcAddress(
reinterpret_cast<const GLubyte*>("glGenVertexArrays"));
// ... the rest of them ...
glEnableClientStateiEXT = (PFNGLENABLECLIENTSTATEIEXTPROC) glXGetProcAddress(
reinterpret_cast<const GLubyte*>("glEnableClientStateiEXT"));
}

// paint event
{
wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
wxGLContext wxglc(this);
wxGLCanvas *canvas= this;
wxglc.SetCurrent(*canvas);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
run = (glGetString(GL_VERSION)!=NULL);
glViewport(0, 0, ClientSize.x, ClientSize.y);
glClearColor(1.0, 1.0, 1.0, 1.0);
if (run)
redraw();
}

void OGLplotter::redraw()
{
wxPaintDC(this);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0,100,0,200);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

GLint w, h;

w = (GLint)GetSize().x;
h = (GLint)GetSize().y;

glOrtho( -w/2, w/2, h/2, -h/2, 0.f, 1.f );

glColorMaterial(GL_FRONT, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_CULL_FACE);
glClearColor(1.0,1.0,1.0,0.0);

// some preliminary drawing omitted

glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

drawAll();

glFlush();
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
SwapBuffers();
}

//drawAll() does most of the drawing and the only OpenGL relevant thing is that it makes a lot of calls to this function:
void OGLplotter::ogldraw(GLenum mode, std::vector<vertex> points)
{
GLuint vao;
glGenVertexArraysAPPLE(1, &vao);
glBindVertexArrayAPPLE(vao);
GLuint vbo1;
glGenBuffersARB(1, &vbo1);
glBindBufferARB(GL_ARRAY_BUFFER, vbo1);
glBufferDataARB(GL_ARRAY_BUFFER,points.size() * sizeof(vertex),&points[0],GL_STATIC_DRAW);
glVertexPointer(2, GL_DOUBLE, sizeof(vertex), NULL);
glColorPointer(4, GL_DOUBLE, sizeof(vertex), (void*)(sizeof(double)*2));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArraysEXT(mode, 0, points.size());

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}

mhagain
04-15-2017, 08:45 AM
APPLE here indicates it's a vendor-specific extension, in this case supplied by Apple's OpenGL implementation. Sometimes vendor-specific extensions are adopted by multiple vendors and become -EXT.

In general you should prefer to always use the non-vendor, non-extension variants if they're available. So use glGenVertexArrays, NOT glGenVertexArraysAPPLE. If they're not available, you should prefer to use -ARB versions, then -EXT, and finally vendor-specific.

That way you stand the best chance of having your code work on multiple platforms, meaning both software platforms AND hardware platforms.

NEVER mix-and-match suffixes. glBufferDataARB with GL_ARRAY_BUFFER, for example; if you're using an extension version of a function call, ALWAYS use the extension version of the GLenum too. Likewise NEVER mix-and-match extension and non-extension calls; glVertexPointer and glDrawArraysEXT, for example. In some cases it might be safe and you might have no errors, but in others it won't be, and you'll be wondering what happened.

NEVER use GLUT_SINGLE. It's astonishing that this still appears in tutorials today. GLUT_SINGLE is a hangover from the early/mid 1990s when support for double-buffered contexts might not be present, but nowadays it's actually single-buffered contexts that have worse compatibility. Use GLUT_DOUBLE and make a glutSwapBuffers call at the end of each frame - you see, double-buffered contexts aren't more difficult.

Don't use double data types for gl*Pointer calls unless you're absolutely certain that you need them. At best you'll be dropped back to software emulation or (even if they're supported in hardware) you'll run slower.

Don't glGen objects in your drawing functions - creating objects is slow and you'll leak memory and object handles. glGen objects one-time-only at startup, then re-use those objects in your drawing functions.

Do read and understand the documentation. Just because OpenGL had a reputation for being easy in 1996, it doesn't mean you get to avoid reading documentation. Using VAOs is illegal with legacy fixed vertex attributes, for example.

This may all seem annoying and nitpicking, but if you put in the effort you'll avoid a lot of pain and suffering in the future if you just learn to pick up good habits and do it the right way now.

6krktr
04-15-2017, 10:52 AM
Thank you! I fixed most of the things you mentioned. I do have two questions:
-Isn't SwapBuffers() a substitute for glutSwapBuffers()? I initialise my window with GL, not GLU or GLUT, and on glutSwapBuffers() I get "Error in freeglut: glutSwapBuffers() called with no current window defined.". I ran with GLUT_DOUBLE and SwapBuffers() and everything ran fine.
-This might be a bit silly, but according to your fourth point, if I use ARB extensions calls, should I not use any non-extension calls? After the fix I both gl*ARB with _ARB suffixes and gl* without suffixes. As far as I'm aware extensions are, well, extensions and none has all the core functionalities.

mhagain
04-15-2017, 12:53 PM
glutSwapBuffers is just going to be a wrapper around the platform-native SwapBuffers implementation. If you're not using GLUT (and this part is confusing because you've posted code that uses GLUT) then you don't use glutSwapBuffers.

Extensions - think of them as "packages". So GL_ARB_multitexture is a "package", GL_ARB_vertex_buffer_object is another "package", etc. It's mostly OK to use the -ARB versions of one but the suffix-less versions of another, but it's not OK to mix within a single "package". The other thing to be careful about is that extensions frequently have dependencies and interactions, and are typically written against a specific version of the GL specification. So GL_ARB_vertex_array_object is written against the GL 2.1 specification, meaning that if you use GL_ARB_vertex_array_object then for guaranteed compatibility you should be using the suffix-less variants for anything from GL 2.1 or below.

This is an area that's full of traps so like I said, you really need to start with the OpenGL specification and registry and read documentation, or alternatively pick a version and just use core OpenGL versions of calls within that version and extensions only for anything higher.