PDA

View Full Version : How does OpenGL store it's states?



WhatEver
04-16-2001, 02:53 PM
Do they use 32 bits? I would assume not because there are more than 32 states.

Am I correct when I say that variables created in opengl for states are initialized using this method:

GLenum state_polygon{GL_POLYGON_SMOOTH, GL_POLYGON_STIPPLE};

Or does it look like this:
GLenum states{all state variables};

IsaackRasmussen
04-16-2001, 03:03 PM
It's probably your last suggestion, it's the one that makes most sense if you take a quick look at gl.h.

But why the heck do you want to know that? OpenGL doesn't specify how to do it, so each driver could do it differently internally.

WhatEver
04-16-2001, 03:08 PM
My model library is using bits for states. I want to store more than 32 states.

I could do this:
bool StateArray[256];

enum state{Blah1, Blah2...};

StateArray[Blah1]=true;

I'd rather know if there's a better way...is there?

MarcusL
04-16-2001, 04:12 PM
That seems like an easy straightforward way to do it... what are your problems with doing it like that?

I'm just trying to say that it really doesn't matter, as long as things are going fast enough and are easy to use in the rest of your code. (Trying to do it 'modular' is good too, but in this case you could easily write a class that overloads the [] operator, if you're into c++.)

If you have loads of bits (256 is not) then you could perhaps use std::vector<bool> which stores bools as bits (instead of taking up a byte, which is the common case) ... but you'd lose a lot more performance trying to bit-fiddle like that than you'd gain by having a smaller memory footprint.

WhatEver
04-16-2001, 05:10 PM
I started programming about 5+ years ago. During that time computers weren't all that fast, and every book I read and every tutorial I saw have brain washed me into thinking "don't use any more memory than you have to!!!". In one way it's helped me to keep things small, but in the other way it's holding me from doing more things.

I'll just have to get over it. You can't push forward unless you push the limits.

Here's an example of saving to much space. Way back when, when I had been programming for about 3 years, I had a cool idea for 3D models...it was to index vertices so you could share vertices that way. I said to myself "wow, all the indices would take up alota room in memory if I did that...the pros must have a better way". Gah!!! If I had only pushed my limits. I've learned never to hold back sence I found out that that's how the pros DO do it http://www.opengl.org/discussion_boards/ubb/biggrin.gif.

MarcusL
04-16-2001, 06:52 PM
I believe the trade-off was the same then as it is now (I've coded in C/C++ for some 8 years now, and was hacking assembly on amiga before that).

Don't optimize until you know it matters!

What if you optimize the heck out of this to save... say.. 4 kb... only to allocate 200kb of stuff in another place... (and I'm being conservative with numbers here..)

[This message has been edited by macke (edited 04-16-2001).]

MarcusL
04-16-2001, 06:57 PM
Idea: Why don't you go look in the MESA source?

ftp://ftp.mesa3d.org/pub/sourceforge/mesa3d/

It's all C, but it's an opengl implementation nevertheless.

kaber0111
04-16-2001, 08:01 PM
little magic trolls do it for you http://www.opengl.org/discussion_boards/ubb/wink.gif
no need to worry about them.

Hull
04-16-2001, 08:07 PM
Man Kaber...

.. would my graphics card work faster if I threaten it with a spanking aswell?

Maybe its a little Demon that sits on the other side of the monitor, painting his ass off with a small brush.

Maybe he is the reason why we mistype so often...

http://www.opengl.org/discussion_boards/ubb/tongue.gif

kaber0111
04-16-2001, 10:39 PM
Originally posted by Hull:
Man Kaber...

.. would my graphics card work faster if I threaten it with a spanking aswell?

Maybe its a little Demon that sits on the other side of the monitor, painting his ass off with a small brush.

Maybe he is the reason why we mistype so often...

http://www.opengl.org/discussion_boards/ubb/tongue.gif


man, now that's just silly http://www.opengl.org/discussion_boards/ubb/wink.gif
of course it would
http://www.opengl.org/discussion_boards/ubb/smile.gif


laterz.

ffish
04-16-2001, 11:35 PM
WhatEver,

My <GL/gl.h> uses a lot of #defines and each of the GLenum variables is a 32-bit number (mine are typedef'd unsigned ints). Remember that because the numbers are 32-bits doesn't mean there are 32 possible values that can be stored in them. Instead there are 2^32 possible values, or 4294967296. Bit patterns are a really good way to save space and store lots of information. For example, if you had a 3-bit variable, it could have 8 (not 3) different values: 000, 001, 010, 011, 100, 101, 110, 111

Hope that helps,
Toby

Michael Steinberg
04-16-2001, 11:48 PM
But you couldn't easily see wether a feature is enabled or disabled.

But, hey, we're inside states here. And I don't think you'd have to check the states for every polygon as opengl drivers might have to do (btw: why do they have big switch statements, when they could alter their code?)... sO I'd think it is best to make it possible that the user can actually change the states easily.

ffish
04-17-2001, 02:19 AM
Michael, you could easily test whether a feature is enabled or disabled with an inline function:




// Some hardcoded feature value.
#define SOME_FEATURE 0xSOME_BIT_PATTERN

// Given some global state variable that
// holds the system state.
inline bool isFooEnabled()
{
if ((SOME_FEATURE & global_state_variable) == SOME_FEATURE) return true;
return false;
}


or even better:




inline GLenum isEnabled(GLenum state)
{
return SOME_FEATURE & global_state_variable;
}
// Which you could use like this:
...
if (GL_DEPTH_TEST == isEnabled(GL_DEPTH_TEST)) PerformAction();
...
// which would reduce the number of "if"
// statements required plus it would work
// for all the states in the system.


Hmmm, might implement something like this myself. Currently I've got a singleton system state manager with wrappers around a whole lot of OpenGL calls. I did this in response to a post of mcraigheads which said to manage system state yourself to avoid the overhead associated with redundant state changes.

Thanks for the new train of thought WhatEver! http://www.opengl.org/discussion_boards/ubb/smile.gif

Michael Steinberg
04-17-2001, 02:26 AM
ffish
But the use won't be able to edit the values while the engine is running. That's why I have runtime-variables.
If you do wrapping of opengl states, that what you propose os really ok!

ffish
04-17-2001, 02:40 AM
Hey yeah,

You're right Michael. I guess I'll stick with my state manager, since I do update my system state variables.

Thanks http://www.opengl.org/discussion_boards/ubb/smile.gif

Michael Steinberg
04-17-2001, 02:43 AM
Well, I was relating to variables that have nothing to do with opengl states http://www.opengl.org/discussion_boards/ubb/smile.gif. Just with engine states.
I'll most probably use your thing to squeeze some performance out of it for opengl states http://www.opengl.org/discussion_boards/ubb/smile.gif
So... Thanx to YOU! http://www.opengl.org/discussion_boards/ubb/smile.gif

LordKronos
04-17-2001, 02:49 AM
How about this for a solution.
Give each state a unique value. Create an array of 32-bit integers (how big of an array will depend on how many unique states you have. Then access bits like this:

[CODE]

unsigned int bits[100]; //holds 3200 bits

unsigend int stateArrayIndex(unsigend int state)
{
return (state >> 5);
};

unsigend int stateBitIndex(unsigend int state)
{
return (state & 0x0000001F);
};

void setStateBit(unsigned int state)
{
unsigned int index = stateArrayIndex(state);
unsigned int shift = stateBitIndex(state);
bits[index] |= (1 << shift);
}

void clearStateBit(unsigned int state)
{
unsigned int index = stateArrayIndex(state);
unsigned int shift = stateBitIndex(state);
bits[index] &= ~(1 << shift);
}

bool getStateBit(unsigned int state)
{
unsigned int index = stateArrayIndex(state);
unsigned int shift = stateBitIndex(state);
unsigned int bit = bits[index] & (1 << shift);
return (bit != 0);
}

This is how I would probably do it

Michael Steinberg
04-17-2001, 02:54 AM
Hey, I think you're exagurating... After all it's about little states! you can simply use a char for a state. We aren'T living in the time where we have to use only two digits for a date!
You can have 1024 states, and they will still be only 1kb...
And yours in slower then.

LordKronos
04-17-2001, 02:57 AM
Of course, I should mention this only works for boolean states (glEnable/glDisable/glIsEnabled). Any state that has a value (like color has r,g,b,and a values) would have to be stored in its own variable. And to do something like glGetIntegerv, you would essentially have to do a giant switch statement, which is a killer for performance.

Actually, thinking about the switch, I wonder how good VC++ optimizes switch statements. If you have a switch with 1000 cases, I wonder if it does a binary search to find the case you need. I certainly hope it doesnt just plow right through them.

ffish
04-17-2001, 03:14 AM
I'll stick to my method but I like it LordKronos! Anything with obfuscating binary arithmetic must be good!

Seriously though, I guess the problem boils down to your second line: each state needs a unique value. The problem with the GL states is that there are so many of them that you can't have a bit pattern that doesn't overlap others. That's why my inline functions above can query but not set.

Also, as you mentioned, your solution works for bools but not multivalued states http://www.opengl.org/discussion_boards/ubb/frown.gif My method (not posted) borrows (or copies) from the excellent Game Programming Gems book. I have a singleton class which wraps some OpenGL calls like glEnable/Disable etc, manages OpenGL states and copes with the multivalued states. As I said, the main reason I implemented this is in response to a post by mcraighead. It's probably a bit (lot) slower than yours but more readable. I'll keep yours in mind though.

LordKronos
04-17-2001, 03:22 AM
Originally posted by Michael Steinberg:
Hey, I think you're exagurating... After all it's about little states! you can simply use a char for a state. We aren'T living in the time where we have to use only two digits for a date!
You can have 1024 states, and they will still be only 1kb...
And yours in slower then.

Were you responding to me? Its not always such a clear cut case as that. Even if we are dealing with 1024 states, yours would need a whole 1KB to remain in L1 cache, while mine would only need 32 bytes. A Pentium 3 only has 16K of L1 Data cache. Guess which version is more likely to stay put. A couple of shifts and bitwise operations could easily be faster than a cache miss. Of course, the extra instructions would also make the code more susceptible to an instruction cache miss, so it depends on the situation. If you have enough bits and/or you use them often enough, I could easily see my way being faster.

You gotta love this type of stuff. You cant prove my way is slower, I cant prove its faster. It depends on the situation.

LordKronos
04-17-2001, 03:34 AM
Originally posted by ffish:
Seriously though, I guess the problem boils down to your second line: each state needs a unique value. The problem with the GL states is that there are so many of them that you can't have a bit pattern that doesn't overlap others.

Well, for boolean states, you can get 4 billion unique states. That should be plenty.


Also, as you mentioned, your solution works for bools but not multivalued states http://www.opengl.org/discussion_boards/ubb/frown.gif

And in openGL, you have different methods for getting boolean states (ie: glIsEnabled) and multi-valued states (ie: glGetIntegerv).
You could uses this method as a wrapper for the boolean functions, and another system to wrap the multi-valued functions.

To tell you the truth, I dont always use this method...it depends what Im doing. If I have structures that I need to copy around a lot, or if I have an object with 50 states, I use this technique. For things that only have 5 or less states, I often use plain boolean values. It really depends on the situation.

Michael Steinberg
04-17-2001, 03:36 AM
Oh yeah.. these caches again... I'm not too much into hardware yet... so I say a sorry.

Still I think as long as you won't inline these functions they will be slower. Function call overhead... Stacks etc...

Hey, ffish! also have Game Programming Gems. Can you explain me the trick about this singleton pattern? What's it all about?

DaViper
04-17-2001, 03:44 AM
Originally posted by LordKronos:
And in openGL, you have different methods for getting boolean states (ie: glIsEnabled) and multi-valued states (ie: glGetIntegerv).
You could uses this method as a wrapper for the boolean functions, and another system to wrap the multi-valued functions.

glGet and glIsEnabled will surely kill your performance, you souhld build your own wrapping methods to keep track of state chances.

Chris

Michael Steinberg
04-17-2001, 03:45 AM
Oh and... is there any program which simulates some cpu? So that one could watch the caches content like debugging.

ffish
04-17-2001, 03:58 AM
Michael,

I don't know if you have a German version, but in my book on pp36-40 there is a chapter on singletons. I use the method titled "An Even Better Way" and just use that to create subclasses. For example, I have a class called glStateMgr defined like this:




#ifndef _GL_STATE_MGR_H
#define _GL_STATE_MGR_H

#ifdef _WIN32
#include <windows.h>
#endif

// Surround OpenGL C stuff with an extern declaration.
#ifdef _cplusplus
extern "C" {
#endif
#include <GL/gl.h>
#ifdef _cplusplus
};
#endif

// Include the singleton base class header.
#include "singleton.h"

// OpenGL state manager class. It is much faster to manually keep track of
// state variables than to use glGet* functions (very slow) or to redundantly
// update state variables with their current value (slow).
class glStateMgr : Singleton<glStateMgr> {
public:
// Constructor/destructor.
glStateMgr();
~glStateMgr();

// Turn on/off a capability.
void glDisable(GLenum cap);
void glEnable(GLenum cap);

// Set the matrix to be manipulated.
void glMatrixMode(GLenum mode);

// Controls how color values in the fragment being processed (the source)
// are combined with those already stored in the framebuffer (the destination).
void glBlendFunc(GLenum sfactor, GLenum dfactor);

// Sets the comparison function for the depth test.
void glDepthFunc(GLenum func);

// Sets the shading model.
void glShadeModel(GLenum mode);
private:
// Forward nested class declaration means that
// I don't have to #include <map>
// until the .cpp file.
class capsMap;

// Capabilities on/off state for the glEnable/glDisable calls.
capsMap&amp; _capsMap;

// Current depth comparison function.
GLenum _gl_depth_func;

// Current modifiable matrix.
GLenum _gl_matrix_mode;

// Current shade model.
GLenum _gl_shade_model;

// Current source blending factor.
GLenum _gl_src_blend_func;
// Current destination blending factor.
GLenum _gl_dst_blend_func;
};

#endif // _GL_STATE_MGR_H not defined.


This is an early prototype. It's not nearly as optimised for space as LK's but it's easy to understand.

I use it to keep track of the states you can see, as well as a bunch of glEnable() states too. In this project I only need these states so far, but I will extend the class to include as much as possible eventually.

Hope that helps http://www.opengl.org/discussion_boards/ubb/smile.gif,
Toby

PS I think the only way to know about optimising for the CPU is through experience. Some excellent books that I have on low level optimisation are Inner Loops by Rick Booth and Michael Abrash's Graphics Programming Black Book. Both are a little old but good reading. The best optimisation comes from choosing good algorithms first though.

Michael Steinberg
04-17-2001, 04:11 AM
Yeah, I read about it (there is no german version http://www.opengl.org/discussion_boards/ubb/frown.gif ). But what's the purpose? That's what I didn't understand.

ffish
04-17-2001, 04:25 AM
Oh, it just ensures that there is only ever one object initialised of that type. I am just a fan of Design Patterns by GOF and "correct software engineering" crap. It will mean that you can't have for example:




// This won't compile.
glStateMgr *state1 = new glStateMgr();
glStateMgr *state2 = new glStateMgr();

state1->glEnable(GL_DEPTH_TEST);
// Second call is redundant.
state2->glEnable(GL_DEPTH_TEST);


I guess what I use it for is to remove any chance of for example calling glMatrixMode(GL_MODELVIEW); every time through the render function even though it is never changed to another matrix mode. For my current project it is possible that I may make stupid mistakes (hopefully not that stupid!) since I will have 10's of files and 10,000's lines of code and many different rendering functions depending on user defined runtime variables.

I guess I just implemented it for the hell of it. I was reading that chapter and like the idea of singleton classes

Michael Steinberg
04-17-2001, 04:29 AM
So I don't have to use that thing if I swear to myselft that I'll be only using one instance of a certain class?

ffish
04-17-2001, 04:36 AM
LOL! http://www.opengl.org/discussion_boards/ubb/biggrin.gif

No you don't. It's just me being pedantic.

DaViper
04-17-2001, 04:46 AM
Originally posted by Michael Steinberg:
So I don't have to use that thing if I swear to myselft that I'll be only using one instance of a certain class?

You definetly don't need it if you can also swear that you never unnecessarily enable or disable any states or set any Colors or Functions twice in a row to the same values. http://www.opengl.org/discussion_boards/ubb/smile.gif

Chris

[This message has been edited by DaViper (edited 04-17-2001).]

Michael Steinberg
04-17-2001, 04:50 AM
I swear! http://www.opengl.org/discussion_boards/ubb/wink.gif

DaViper
04-17-2001, 04:54 AM
I can't http://www.opengl.org/discussion_boards/ubb/frown.gif

Michael Steinberg
04-17-2001, 05:00 AM
Why?

DaViper
04-17-2001, 05:04 AM
I'am having different rendering functions, so I have to reset all my states everytime I enter one of those functions.

LordKronos
04-17-2001, 06:06 AM
Originally posted by Michael Steinberg:
Oh and... is there any program which simulates some cpu? So that one could watch the caches content like debugging.

Well, I dont know of anything that can do this (it would be incredibly complex, and even more incredilbly slow). However, with the Intel VTune profiler, you can measure things like cache misses, so this might tell you what you are looking for.

LordKronos
04-17-2001, 06:11 AM
Another thought about my method. If the OpenGL constants are too spread out, any technique that uses the constant as an array index will have problems. Example: if one constant is "1" and another is "123456789", your going to need an array of 123456789 elements.

Instead, you would need some system (like the switch statement I talked about earlier) to remap a constant into an array index.


P.S.
Or an even better option performance wise is to make a higher level manager (this is what I do). In other words, have functions that are only concerned with texture state, other functions that are only concerned with lighting state,...matrix, arrays, etc.

The downside to this is that it isnt quite as transparent as "myStateManager->glEnable(...)", but if you start with this idea in mind, it works quite well.

[This message has been edited by LordKronos (edited 04-17-2001).]

CViper
04-17-2001, 07:00 AM
(originally posted by ffish (i think); somehow messed up this)



// Some hardcoded feature value.
#define SOME_FEATURE 0xSOME_BIT_PATTERN

// Given some global state variable that
// holds the system state.
inline bool isFooEnabled()
{
if ((SOME_FEATURE & global_state_variable) == SOME_FEATURE) return true;
return false;
}


or even better:




inline GLenum isEnabled(GLenum state)
{
return SOME_FEATURE & global_state_variable;
}

(end quote)

Well i'd write it that way:



#define IS_ENABLED( var, feat ) (feat & var ? 1 : 0)
or
#define IS_ENABLED( var, feat ) ((feat & var == feat) ? 1 : 0)

and then just



if( IS_ENABLED( your_var, YOUR_FANCY_FEATURE ) )
{...}


It's easier than alot of inline functions i think (but thats my personal view)

BTW: the same works for key-checking in window$ too:



#define KEYDOWN(key) ((GetAsyncKeyState(key) & 0x8000) ? 1: 0 )
#define KEYUP(key) ((GetAsyncKeyState(key) & 0x8000) ? 0: 1 )


[This message has been edited by CViper (edited 04-17-2001).]

Nocturnal
04-17-2001, 09:02 AM
You could use std::bitset<N> if you are using C++ for bit sets of arbitrary length.

NeoTuri
04-17-2001, 10:21 AM
Either that or std::set<int>, the search time is logarithmic and you only use as much memory as the number of nodes you store.

WhatEver
04-17-2001, 01:29 PM
Remind me not to have the forum email me when someone responds to my posts http://www.opengl.org/discussion_boards/ubb/biggrin.gif. I had 40 emails...LOL.

I love to read all this brainstorming. Reading your responces has also given me a new train of thought...especially with state management.

ffish
04-17-2001, 05:36 PM
CViper,

I used to agree with you when I moved from C to C++ but I like Scott Meyers' Effective C++/More Effective C++ books. He says that you should use inline functions and global const variables instead of #defines for a number of reasons, but I'll let you read the book to find out why http://www.opengl.org/discussion_boards/ubb/smile.gif You may not agree with me (and him) but he has a lot of convincing arguments. I've read both his books 3 times and I could read them both another 3 times. He's definitely a master.

Anyway, I'm not saying you're wrong and I'm right, but that's my preference http://www.opengl.org/discussion_boards/ubb/smile.gif

IainR
04-18-2001, 12:22 AM
Hi,

What about using bitfields? Good on memory usage, although maybe not quite so efficient in terms of speed - but that's probably not a problem.

eg.

typedef struct _states_t
{
int boState1 : 1;
int boState2 : 1;
etc...

} states_t;

- hope I got the syntax right...

Iain