Followers 0

# OpenGL Managing OpenGL Resources

## 13 posts in this topic

Good day ladies and gentlemen of the realm.

Towards the end of last year I decided graphics programming is something I really wanted to get into, so as part of the learning process I've been putting together a very basic OpenGL engine. One of the things I've been putting a fair bit of thought into is how to abstract OpenGL functionality, specifically OpenGL resources, in an object-oriented manner (using modern C++).

For me one of the main design considerations when abstracting resources is whether of not these abstractions should exhibit value or reference semantics, i.e. should they represent resource handles or the resources themselves.

It made sense to me, and suited my needs at the time, to follow reference semantics, so every copy of a resource object refers to the same underlying OpenGL resource. Implementation wise went I with smart pointers and custom deleters, with the approach being used for Shader, VAO, FBO and Texture objects.

Shader::Shader()
:
m_programID( new GLuint( glCreateProgram() ), [=]( GLuint* program ){ glDeleteProgram( *program ); } )
{
}


So why am I mentioning all this? Well I want to ask if there valid cases for choosing to go with value semantics, and how things might change if you had classes dedicated to managing resource pools. Also any tips anyone might have for managing OpenGL resources and creating meaningful abstractions.

0

##### Share on other sites

But can you point me to some books or articles about that 'value/reference semantics' stuff ?

Or maybe I do not understand you correctly. What is a recource handler in your situation and what it does ?

P.S.

Your constructor seems like  a useless lambda monstrocity. It makes eyes bleed.  You can't tell what it does at the first glance. Please, don't do like that :) C++ 11 is cool, but it's should be used not for the sake of the C++ 11 itself.

Also, if you're going to manage your RAM usage, I'd recommend to you not to use some automatics and smart/shared pointers at all, but to implement your own memory management mechanics, it will give you more flexibility and control over what's going on.

Do you really need to destroy your Shader object when it goes out of scope? Or it's more handy to load needed shaders, use them system-wise, and free memory manually after you're completely sure, that you'll not gonna use them at all ?

edit:

For resource pooling, I think, it's an effective approach to use your wrapper classes as the resources themselves + use standart containers with custom memory allocators and some memory counting structure for debugging.  Depends on what you've meant by 'classes dedicated to managing resource pools'.

edit2:

Hope this would be more constructive.

Edited by syskrank
-2

##### Share on other sites

Your constructor seems like  a useless lambda monstrocity. It makes eyes bleed.  You can't tell what it does at the first glance. Please, don't do like that C++ 11 is cool, but it's should be used not for the sake of the C++ 11 itself.

Thanks for the reply but I think you'll find that is not quite as useless as you say. Using a custom deleter for a smart pointer is a fairly standard idiom for automatic lifetime management of non-memory resources.

There are two main cases where you can’t use make_shared (or allocate_shared) to create an object that you know will be owned by shared_ptrs: (a) if you need a custom deleter, such as because of using shared_ptrs to manage a non-memory resource or an object allocated in a nonstandard memory area, you can’t use make_shared because it doesn’t support specifying a deleter; and (b) if you are adopting a raw pointer to an object being handed to you from other (usually legacy) code, you would construct a shared_ptr from that raw pointer directly.

Also, if you're going to manage your RAM usage, I'd recommend to you not to use some automatics and smart/shared pointers at all, but to implement your own memory management mechanics, it will give you more flexibility and control over what's going on.

Do you really need to destroy your Shader object when it goes out of scope? Or it's more handy to load needed shaders, use them system-wise, and free memory manually after you're completely sure, that you'll not gonna use them at all ?

I totally agree with that, if my ambitions were more grand and involved releasing an actual product then tighter, more manual control of resource lifetimes is definitely required for performance optimisations. That is sometimes I'll keep in the back of my mind and if my little project starts to get big enough I'll definitely look into it. As for now, resources being tied to scope seems to fit my relatively small bill.

But can you point me to some books or articles about that 'value/reference semantics' stuff ?

Or maybe I do not understand you correctly. What is a recource handler in your situation and what it does ?

The idea of values and references (to values) is a fairly fundamental topic in computer science and essentially what you are talking about is indirection. It's possible it is just the terminology that you aren't familiar with, in C++ references are implemented via pointer types and reference types, and in C# or Java the language explicitly categories types as being either "Reference Types" or "Value/Primitive Types".

http://en.wikipedia.org/wiki/Value_type

http://en.wikipedia.org/wiki/Reference_type

A succient summary from the wikipedia article "In computer science, the term value type is commonly used to refer to one of two kinds of data types: Types of values or Types of objects with deep copy semantics."

0

##### Share on other sites

Thanks for the reply but I think you'll find that is not quite as useless as you say.

I didn't say that it's useless, I said that its freaking hard to understand what it does, look : lambda list initializer + not-straightforward name 'm_programID' - clearly a mess at first sight. OR post more code so that everything would be clear :)

The idea of values and references (to values) is a fairly fundamental topic in computer science and essentially what you are talking about is indirection.

Your talks are obscure, as in the first post, which has confused me :)

0

##### Share on other sites

If they're value types, it means they can be copied, and copied fairly easily by the user (as simply as a = b;). Copying a GPU resource can be quite an expensive operation, so I'd see value-references as quite a dangerous API.

This can't be highlighted enough; I've taken the liberty of underlining part of Hodgman's post above, because it needs to be heavily emphasised.

It's also the case that you need to destroy the copied resource when your function returns, by the way, which can also be an expensive operation.  So now you're creating new GPU resources, copying to them, then destroying them, and all for what may be a fairly trivial operation.  Expensive stuff.

0

##### Share on other sites

If they're value types, it means they can be copied, and copied fairly easily by the user (as simply as a = b;). Copying a GPU resource can be quite an expensive operation, so I'd see value-references as quite a dangerous API. Also, there's no point in cloning some GPU resources -- e.g. in D3D a shader program is just code (no other state), so there's no need in ever having more than one instance of a particular program hanging around.

This was my intuition of the situation, where for the most part you want shallow copies and reference semantics. If deep copies are required then that should be offered explicitly and cannot just happen 'by mistake'.

Your shader implementation sounds quite interesting, it's pretty cool what can be done when you're familiar with the domain, especially when it comes to performance optimisations. Considering how important resource management is, I definitely need to spend some more time researching the topic.

0

##### Share on other sites

Thanks for the reply but I think you'll find that is not quite as useless as you say.

I didn't say that it's useless, I said that its freaking hard to understand what it does, look : lambda list initializer + not-straightforward name 'm_programID' - clearly a mess at first sight. OR post more code so that everything would be clear

Here is an equivalent example of using a shared_ptr with a custom deleter to manage a D311Device and the same thing for automatically releasing an SDL_Surface. I'm not sure why programID would be a confusing name, it represents the unique ID assigned by OpenGL to that particular shader program. Sure I admit I'm not very good when it comes to naming things but that made sense in my head.

Edit:

Oh and another example for managing files.

Edited by Lantre
0

##### Share on other sites
I personally chose the reference way, specifically using shared_ptrs. A major reason being that OpenGL specification itself uses language that either directly talks about a reference count or otherwise mentions that deleted resources only actually die when nothing refers to them anymore. So when an index buffer is attached to an vertex array, my vertex array object is given a (wrapped) shared_ptr that points to the index buffer.

Edit: I think some drivers were/are buggy specifically with the relation of index buffers and vertex arrays, that binding a vertex array doesn't cause the related index buffer being bound automatically. When the vertex array object in my code has a reference to the index buffer, it could work around that bug by explicitly binding the buffer. Edited by Ubik
0

##### Share on other sites

If they're value types, it means they can be copied, and copied fairly easily by the user (as simply as a = b;). Copying a GPU resource can be quite an expensive operation, so I'd see value-references as quite a dangerous API. Also, there's no point in cloning some GPU resources -- e.g. in D3D a shader program is just code (no other state), so there's no need in ever having more than one instance of a particular program hanging around.

This was my intuition of the situation, where for the most part you want shallow copies and reference semantics. If deep copies are required then that should be offered explicitly and cannot just happen 'by mistake'.

One option that hasn't been discussed is to use non-copyable values. This avoids the issue of expensive copies by out-right preventing them.

You might still use shared_ptrs to share access to these objects - but there is a big difference between "an object that moves with reference semantics" and "an object passed by reference". By still being a value it allows you to also pass them by the cheaper plain reference or raw pointer. It also allows them to be stored more efficiently in contiguous arrays.

The precise way in which access to the object is passed around is a choice for the client code, not enforced by the handle type itself.

This makes a lot of sense especially if you only have one point of control, e.g. a resource manager/resource cache, which owns the 'real' resource. Client code can then request weak/non-owning references to these resources.

This is something I'll probably be playing around with pretty soon for meshes and materials. At the moment I have each object naively loading (and owning) its own textures and meshes, which while convenient is not particularly clever.

0

##### Share on other sites

One option that hasn't been discussed is to use non-copyable values. This avoids the issue of expensive copies by out-right preventing them.

That's actually what I do for resources, but the value is only used inside the render-device implementation  They're owned by an RAII value, and then references are given out to the user, e.g.

//hidden internally, not seen by the user
struct NativeBuffer : NonCopyable { /*GL, D3D, etc buffer handle member variable*/
NativeBuffer( ... ) { /*acquire handle*/ }
~NativeBuffer() { /* release handle*/ }
};

//what the user gets:
typedef Pool<NativeBuffer>::Handle BufferHandle;

class Device
{
public:
BufferHandle CreateBuffer(...);
void         ReleaseBuffer( BufferHandle );
ResourceLock MapBuffer( BufferHandle );
void         UnmapBuffer( ResourceLock );
private:
Pool<NativeBuffer> m_Buffers;
}

A system to allow shared ownership is implemented on top of this -- e.g. you might have a model-asset that owns some buffers, and then many model-instances that all share the one model-asset.

Edited by Hodgman
1

##### Share on other sites

One option that hasn't been discussed is to use non-copyable values. This avoids the issue of expensive copies by out-right preventing them.

That's actually what I do for resources, but the value is only used inside the render-device implementation  They're owned by an RAII value, and then references are given out to the user, e.g.

//hidden internally, not seen by the user
struct NativeBuffer : NonCopyable { /*GL, D3D, etc buffer handle member variable*/
NativeBuffer( ... ) { /*acquire handle*/ }
~NativeBuffer() { /* release handle*/ }
};

//what the user gets:
typedef Pool<NativeBuffer>::Handle BufferHandle;

class Device
{
public:
BufferHandle CreateBuffer(...);
void         ReleaseBuffer( BufferHandle );
ResourceLock MapBuffer( BufferHandle );
void         UnmapBuffer( ResourceLock );
private:
Pool<NativeBuffer> m_Buffers;
}

A system to allow shared ownership is implemented on top of this -- e.g. you might have a model-asset that owns some buffers, and then many model-instances that all share the one model-asset.

Once you give out references to their respective users what mechanism do you use to track usage in order to determine what should or shouldn't be cached? If that is even a relevant question for your framework.

0

##### Share on other sites

Once you give out references to their respective users what mechanism do you use to track usage in order to determine what should or shouldn't be cached? If that is even a relevant question for your framework.

Creating/destroying a GL buffer is equivalent to calling malloc/free, except that you're dealing with GPU-addressable RAM. So, at this level I don't do any advanced management or caching.

Instead, the next level of the architecture can do those things. You have objects that are composed of GL resources, such as buffers -- e.g. a model asset loaded from disk. The model asset can have one-to-one "value" ownership over the buffers/resources. When the asset is created/destroyed, the resources are created/destroyed.

A model instance can then have shared ownership (e.g. reference counting / shared_ptr / etc) of a model asset. A file system can deal with reading bytes from disk, and a model factory can deal with converting those streams of bytes into model assets. An asset cache can perform the caching, e.g. by having a map/dictionary member that associates asset names with model asset objects. When the reference count on a model asset is zero, it can be deleted from the asset cache (which will free up the GL resources). When creating a model instance, it can use the asset cache, file system and model factory to either fetch an existing model asset or create a new one.

2

## Create an account

Register a new account

Followers 0

• ### Similar Content

• So it's been a while since I took a break from my whole creating a planet in DX11. Last time around I got stuck on fixing a nice LOD.
A week back or so I got help to find this:
https://github.com/sp4cerat/Planet-LOD
In general this is what I'm trying to recreate in DX11, he that made that planet LOD uses OpenGL but that is a minor issue and something I can solve. But I have a question regarding the code
He gets the position using this row
vec4d pos = b.var.vec4d["position"]; Which is then used further down when he sends the variable "center" into the drawing function:
if (pos.len() < 1) pos.norm(); world::draw(vec3d(pos.x, pos.y, pos.z));
Inside the draw function this happens:
draw_recursive(p3[0], p3[1], p3[2], center); Basically the 3 vertices of the triangle and the center of details that he sent as a parameter earlier: vec3d(pos.x, pos.y, pos.z)
Now onto my real question, he does vec3d edge_center[3] = { (p1 + p2) / 2, (p2 + p3) / 2, (p3 + p1) / 2 }; to get the edge center of each edge, nothing weird there.
But this is used later on with:
vec3d d = center + edge_center[i]; edge_test[i] = d.len() > ratio_size; edge_test is then used to evaluate if there should be a triangle drawn or if it should be split up into 3 new triangles instead. Why is it working for him? shouldn't it be like center - edge_center or something like that? Why adding them togheter? I asume here that the center is the center of details for the LOD. the position of the camera if stood on the ground of the planet and not up int he air like it is now.

Full code can be seen here:
https://github.com/sp4cerat/Planet-LOD/blob/master/src.simple/Main.cpp
If anyone would like to take a look and try to help me understand this code I would love this person. I'm running out of ideas on how to solve this in my own head, most likely twisted it one time to many up in my head
Toastmastern

• I googled around but are unable to find source code or details of implementation.
What keywords should I search for this topic?
Things I would like to know:
A. How to ensure that partially covered pixels are rasterized?
Apparently by expanding each triangle by 1 pixel or so, rasterization problem is almost solved.
But it will result in an unindexable triangle list without tons of overlaps. Will it incur a large performance penalty?
How to ensure proper synchronizations in GLSL?
GLSL seems to only allow int32 atomics on image.
C. Is there some simple ways to estimate coverage on-the-fly?
In case I am to draw 2D shapes onto an exisitng target:
1. A multi-pass whatever-buffer seems overkill.
2. Multisampling could cost a lot memory though all I need is better coverage.
Besides, I have to blit twice, if draw target is not multisampled.

• By mapra99
Hello

I am working on a recent project and I have been learning how to code in C# using OpenGL libraries for some graphics. I have achieved some quite interesting things using TAO Framework writing in Console Applications, creating a GLUT Window. But my problem now is that I need to incorporate the Graphics in a Windows Form so I can relate the objects that I render with some .NET Controls.

To deal with this problem, I have seen in some forums that it's better to use OpenTK instead of TAO Framework, so I can use the glControl that OpenTK libraries offer. However, I haven't found complete articles, tutorials or source codes that help using the glControl or that may insert me into de OpenTK functions. Would somebody please share in this forum some links or files where I can find good documentation about this topic? Or may I use another library different of OpenTK?

Thanks!

• Hello, I have been working on SH Irradiance map rendering, and I have been using a GLSL pixel shader to render SH irradiance to 2D irradiance maps for my static objects. I already have it working with 9 3D textures so far for the first 9 SH functions.
In my GLSL shader, I have to send in 9 SH Coefficient 3D Texures that use RGBA8 as a pixel format. RGB being used for the coefficients for red, green, and blue, and the A for checking if the voxel is in use (for the 3D texture solidification shader to prevent bleeding).
My problem is, I want to knock this number of textures down to something like 4 or 5. Getting even lower would be a godsend. This is because I eventually plan on adding more SH Coefficient 3D Textures for other parts of the game map (such as inside rooms, as opposed to the outside), to circumvent irradiance probe bleeding between rooms separated by walls. I don't want to reach the 32 texture limit too soon. Also, I figure that it would be a LOT faster.
Is there a way I could, say, store 2 sets of SH Coefficients for 2 SH functions inside a texture with RGBA16 pixels? If so, how would I extract them from inside GLSL? Let me know if you have any suggestions ^^.
• By KarimIO
EDIT: I thought this was restricted to Attribute-Created GL contexts, but it isn't, so I rewrote the post.
Hey guys, whenever I call SwapBuffers(hDC), I get a crash, and I get a "Too many posts were made to a semaphore." from Windows as I call SwapBuffers. What could be the cause of this?
Update: No crash occurs if I don't draw, just clear and swap.
static PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor 1, // Version Number PFD_DRAW_TO_WINDOW | // Format Must Support Window PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, // Must Support Double Buffering PFD_TYPE_RGBA, // Request An RGBA Format 32, // Select Our Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored 0, // No Alpha Buffer 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored 24, // 24Bit Z-Buffer (Depth Buffer) 0, // No Stencil Buffer 0, // No Auxiliary Buffer PFD_MAIN_PLANE, // Main Drawing Layer 0, // Reserved 0, 0, 0 // Layer Masks Ignored }; if (!(hDC = GetDC(windowHandle))) return false; unsigned int PixelFormat; if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) return false; if (!SetPixelFormat(hDC, PixelFormat, &pfd)) return false; hRC = wglCreateContext(hDC); if (!hRC) { std::cout << "wglCreateContext Failed!\n"; return false; } if (wglMakeCurrent(hDC, hRC) == NULL) { std::cout << "Make Context Current Second Failed!\n"; return false; } ... // OGL Buffer Initialization glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glBindVertexArray(vao); glUseProgram(myprogram); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (void *)indexStart); SwapBuffers(GetDC(window_handle));

• 19
• 14
• 23
• 11
• 28