Pushing / popping the current program

Hello Forum.
With the spec for OpenGL4.1 , there are tons of deprecations.
One of them is the fact that glPushAttrib will no longer be there as of 4.1.

Here is the challenge : Suppose one of my vendoes creates a 3D engine based on OpenGL and lets me intervene in a post-Draw stage, after the whole scene was drawn (For my own reasons). In the standard fixed-function architecture I pushAttrib to all attrib bits, change the states of the machine , and then do my stuff. When I’m done , I popAttrib and all is well. As of 4.1 it won’t be this way. Almost everything will be in the shader program , which is fine with me, yet I could not find a way to “Push” the current shader program and then “pop” it , there is no getprogram which returns the current active program so that I may keep its ID , insert my program , and when done , re- use the old ID in order to re-activate the old program. Did I miss anything about that in the spec , or is it something there is a solution for (Or is it something the ARB needs to think about)? something like getProgram with (0) returns the current running program ID…
Thank you .

Sounds like the right hand does not know what left hand is doing. You must make your state machine, which remembers the current shader program.


GLint currProgram;
glGetIntegerv(GL_CURRENT_PROGRAM, &currProgram);

Unless you allocate a compatibility profile.

gDebugger said

Using “Get” or “Is” functions slows down render performance. These commands force the graphic system to execute all queued OpenGL calls before it can answer the “Get” or “Is” query.

You could hook glUseProgram and track the last used program yourself…

Using “Get” or “Is” functions slows down render performance. These commands force the graphic system to execute all queued OpenGL calls before it can answer the “Get” or “Is” query.

That’s a lie. It might cause that, depending on the get in question. But simply asking what object is bound to what binding point would not have to cause that.

glUseProgram(1); // Async call, command stored to queue
glUseProgram(2); // Async call, command stored to queue
glGetIntegerv(GL_CURRENT_PROGRAM, &currProgram); // Wait for all queue executing, read state from GPU -> because OpenGL do not have internal state machine and just brute-force switching state (send command to GPU) to prevent mismatch for sake of stability

glUseProgram(1); // Async call, command stored to queue

Here’s what this call actually looks like (in pseudo-code):


void glUseProgram(GLuint programId)
{
  if(g_context->IsTransformFeedbackActive())
  {
    OpenGLError(GL_INVALID_OPERATION);
    return;
  }

  if(!ProgramIdWasGenerated(programId))
  {
    OpenGLError(GL_INVALID_VALUE);
    return;
  }
  if(!IsProgramId(programId))
  {
    OpenGLError(GL_INVALID_OPERATION);
    return;
  }

  ProgramObject *pProgram = GetProgramFromId(programId);

  if(!pProgram->IsSuccessfullyLinked())
  {
    OpenGLError(GL_INVALID_OPERATION);
    return;
  }

  g_context->pCurrProgram = pProgram;
  g_context->currProgramId = programId;
}

This is all context state. Nothing is stored in a queue, because none of this command executes asynchronously.

The call to IsSuccessfullyLinked (checking if the program was linked) must happen synchronously. The OpenGL spec requires it.

Binds are not asynchronous. Especially program object binding. Remember: until you render with the program, or change the program’s state, the GPU neither knows nor cares what you bind to the context.

The kind of state where you might have to wait are things that aren’t error checked (outside of making sure that the inputs are within tolerances). Like what the current blend function is. Or the current polygon winding mode. And so forth. Things that translate directly to the GPU doing something.

Binding a program does not require the GPU to do something. Implementations may initiate a DMA when binding a program, since it suggests intent to render. But considering the need to bind to modify uniforms, even that is somewhat unlikely.

Thanks everybody for the kind responses. I like it where my questions arise a debate.

As for YarUnderoaker: A sentence like :
“Sounds like the right hand does not know what left hand is doing” is not very unuseful in forums like this, consider trying to deeper understand the question, reading the 4.1 spec rather than teaching everyone your deductive logic.

As for the topic discussed:
I didn’t find the glGetIntegerv(GL_CURRENT_PROGRAM, &currProgram); on the new 4.1 spec, will it still be supported ?
As of the 4.1 there will still be support for UseProgram , yet a new , more encapsulated Pipeline emerges from 4.1 , and there is a comment about CURRENT_PROGRAM becomming ACTIVE_PROGRAM there, yet again , there is no GetIntegerv with a CURRENT_PIPE or ACTIVE_PIPE so that if I am creating an Engine , yieling control to the user at a post-draw stage, I must instruct the user to call some “Push current engine state” method and “pop current engine state method” in order for the state to be kept. Am I right ? Thise does remind me a bit of DX…

“Sounds like the right hand does not know what left hand is doing” is not very unuseful in forums like this, consider trying to deeper understand the question

But he’s right. There’s a portion of your program that’s out of your control; if there wasn’t, you’d know when a program object changed because code under your control caused it to change.

I didn’t find the glGetIntegerv(GL_CURRENT_PROGRAM, &currProgram)

What didn’t you find? glGetIntegerv is a GL 1.1 function; it’s always been there. And GL_CURRENT_PROGRAM has been around since the GL 2.0 days. If you can link a program, if you can call glUseProgram, then you can get the current program. It’s a package deal.

there is a comment about CURRENT_PROGRAM becomming ACTIVE_PROGRAM there

Which means nothing, since they both represent the same integer value. It’s just a word change. And a completely, utterly needless one at that.

Man, every time I look at ARB_separate_program_objects, I find something else stupid about it. It’s truly amazing.

there is no GetIntegerv with a CURRENT_PIPE or ACTIVE_PIPE

No, there isn’t. But there is GL_PROGRAM_PIPELINE_BINDING.

Rule number 1 about OpenGL: everything you do that isn’t a rendering call sets state. Rule number 2: every state setting has an associated state getting.

Though OpenGL’s naming isn’t always consistent…

so that if I am creating an Engine , yieling control to the user at a post-draw stage, I must instruct the user to call some “Push current engine state” method and “pop current engine state method” in order for the state to be kept.

Nobody’s forcing you to. It all depends on what you want and what you’re making.

For example, in most things I would call an “engine”, I would simply forbid the user from calling any OpenGL code from outside the render loop. Oh, they can try it, but the results won’t be pretty. I would provide appropriate hooks for user code to be executed in various locations of course. But there would be no general sharing of the rendering system.

The rendering engine owns the rendering system. Just like the sound engine owns the sound system. That’s the way Ogre3D handles it.

GL_CURRENT_PROGRAM has been renamed GL_ACTIVE_PROGRAM, but is also part of a program pipeline object now.

A program pipeline object is sort of like a vertex array object, but for grouping together programs attached to each stage of the pipeline.

a program pipeline object contains:
-active program - glUniformXXX calls affect this program
-vertex shader program - attached to vertex shader stage.
-geometry shader program - attached to geometry shader stage.
-tess control program - attached to tess control shader stage.
-tess evaluation program - attached to tess evaluation shader stage.
-fragment shader program - attached to fragment shader stage.

Binding a different program pipeline object will change all this state in one go.

Calling glUseProgram(prog) binds prog to each state of the current program pipeline object. Calling glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, prog); will attach prog to the vertex shader + fragment shader stages of the current program pipeline object.

The spec actually says that GL_ACTIVE_PROGRAM should be retrieved with glGetProgramPipelineiv(pipeline, GL_ACTIVE_PROGRAM, &active_prog); And doesn’t mention being able to retrieve it with glGetInteger at all, although it probably still works (haven’t tested it).

I’m currently trying to produce a more readable html version of the 4.1 core spec state table, because it’s hard for people to know what state belongs to what, with bind-to-edit hiding what is actually being modified.

The OpenGL spec just has the program pipeline object state + related context state dumped in with the program object state at the moment, but they should really be separated. The program object state PROGRAM_SEPARABLE that indicates whether a program is separable is missing from the table too.

I’d avoid glGetXXX calls where possible, because depending on how they’re implemented by the driver could have a large impact on performance, and it’s hard to tell which ones will have an impact. Even NVidia had slow glGet calls that didn’t need to be slow at one time when they re-wrote part of their driver. This is mentioned at http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=235486 (I assume it’s improved now - but not sure).

Some glGet calls will always force certain operations before them to be completed before you can get the result, such as reading pixels after drawing/reading buffer data after writing, or getting query results before polling to see if the result is ready.

ps. Does anyone know what the initial value for PROGRAM_SEPARABLE is meant to be? I assume it’s FALSE, but could be TRUE just as easily.

Does anyone know what the initial value for PROGRAM_SEPARABLE is meant to be?

Wow. ARB_separate_program_objects really is the shoddiest extension specification I’ve ever seen. It really does not say. The most fundamental task for any extension is to specify what state is and what object owns it. And it does not say.

This is an absolutely atrocious extension. The ARB should be ashamed of themselves for how terrible this spec is. It’s bad enough that it makes no sense. It’s bad enough that it defines stupid behavior in various cases. But it doesn’t even do the most basic job of a specification.

Alfonse, knock it off. This is like the third thread and umteenth post I’ve seen you whining with this “destructive” criticism. You’re the most "de"motivating and unpersuasive person on these forums. I don’t know what your goal is, but you’re teaching folks (especially those on the ARB) to ignore you and just skip your posts by default (I’m to where I skip most of yours now).

If you want to effect “positive” change, consider a polite, brief, numbered list of the specific problems you see in the spec and your proposed change to the wording.

And stop bashing, dial down your ego, and use some empathy for the person you’re talking to. You make folks not want to come here for help and technical discussion. So unless that’s your goal, knock it off an be kind and polite. If it is your goal, just go away.

If you want to effect “positive” change, consider a polite, brief, numbered list of the specific problems you see in the spec and your proposed change to the wording.

Way ahead of you. Not that I expect it to matter, of course. Given the number of bugs that are “new” and still there months after having been submitted, I won’t be holding my breath for them fixing these issues anytime soon.

I don’t know what your goal is, but you’re teaching folks (especially those on the ARB) to ignore you and just skip your posts by default (I’m to where I skip most of yours now).

My goal is to express my outrage for the shoddy workmanship of this extension. Outrage is a legitimate form of communication, and a necessary one when someone has done something… well, outrageous.

Like releasing a spec that is as clearly unfinished as this one. And directly promoting it to core without any kind of trial period, which makes fixing many of its mistakes difficult if not impossible (thanks to the need to preserve backwards-compatibility).

I understand that people make mistakes. I understand that there will be less-than-perfect specifications released. I get that; I really do.

But ARB_separate_shader_objects is not the result of well-intentioned errors. The pattern of those errors is clearly indicative of this. You can see it in the very language of the spec that the various interactions of its functionality were not well considered. The extension itself seems slip-shod and poorly assembled, as if they took EXT_separate_shader_objects and tried to bolt onto it pipeline objects and linking user-defined varyings by user-defined resources. Just read the issues section; it’s a jumbled mess of things that, in some cases, aren’t even congruent with the final spec itself.

This particular error, not putting an enumerator in a state table, is minor; it happens. And in any other extension, it would be an honest mistake; just something that slipped through and nothing more. But the fact that it happened in association with so many other errors shows it to not be an honest mistake. It is very clearly the product of a rushed and hurried process. Malfeasance induced by a fixed and immovable release date.

The purpose of the ARB is to make specifications for interfacing with a rendering system. This is a very poor specification; they have failed to do their job.

It’s not like people were going to crucify the ARB if they didn’t ship this extension in time for SIGGRAPH; nobody forced them to release this spec that day (or if they did, then the incident wasn’t widely publicized). People have been waiting since the very first GL_ARB_shader_objects specification (released back in 2003) to have separate programs; a few months to get it right wouldn’t have hurt anyone. And nobody forced them to directly promote it to the 4.1 core without any time for user consideration.

These are all decisions the ARB made themselves. They were bad decisions, not honest mistakes. Given all of this, why is outrage not an appropriate response?