PDA

View Full Version : OpenGL C++ wrapper



aqnuep
08-22-2010, 04:36 AM
Hi,

As far as I know, there is no real C++ wrapper for OpenGL. If there is one, please disprove me.

Most people might think that there is no too much sense for creating a C++ wrapper for OpenGL. That is maybe true, however, with that many object types currently available in OpenGL and having DSA implemented in most drivers maybe it makes sense as it would make the management of the objects more local thus providing better design and easier implementation.

Before you would argue with me, I'm not having difficulties with using the C API as I'm pretty familiar with OpenGL and with almost all the extensions currently supported by vendors, however after seeing that Khronos has created a C++ wrapper for OpenCL I was thinking about that maybe such a header-only OpenGL C++ wrapper would come handy as well. Creating a header only wrapper with inline methods would not even sacrifice any runtime performance.

So my question is whether there is any such a wrapper for OpenGL 3+ core. If not, maybe I will sacrifice some of my time to create one if there is interest for it.

Please feel free to share your thoughts.

Groovounet
08-22-2010, 07:40 PM
Hi,

I think that the problem we a C++ OpenGL wrapper is that it's going to be much more complicated to build one where 2 programmers will agree on the design.

The difference between OpenCL and OpenGL is that OpenCL is have a high consistency but OpenGL doesn't and it becomes more and more obvious as the ARB release new specifications... :p

I like the idea but I'm not sure how it could succeed.

aqnuep
08-23-2010, 03:36 AM
Hi,

My plan initially is to create a C++ wrapper for the object handling part of OpenGL so the generic, yet not object oriented elements of the API would be left for the future.

Also, I would be interested to create some virtual "board" where the developers can argue and agree in a common form to make this C++ wrapper a quasi official wrapper instead of having hundreds of different implementations.

kRogue
08-31-2010, 02:10 PM
I can see the temptation for a very simple bare-bones C++ wrapper, one can have things like this:



class GLTextureHandle
{
public:
GLuint m_name;

void
TextureParameter(GLenum pname, const GLint *v)
{
glTextureParameterivEXT(m_name, pname, v);
}

void
TextureParameter(GLenum pname, const GLfloat *v)
{
glTextureParameterfvEXT(m_name, pname, v);
}

template<unsigned int N, typename T>
void
TextureParameter(GLenum pname, const array<T,N> &amp;v)
{
TextureParameter(pname, &amp;v[0]);
}

void
getTextureParamter(GLenum pname, GLint *v)
{
glGetTextureParameterivEXT(m_name, pname, v);
}

void
getTextureParamter(GLenum pname, GLfloat *v)
{
glGetTextureParameterivEXT(m_name, pname, v);
}

template<unsigned int N, typename T>
array<T,N>
getTextureParamter(enum pname)
{
array<T,N> return_value;
getTextureParamter(pname, &amp;return_value[0]);
return return_value;
}

//etc...

}

//or potentially do NOT make any member functions to
//GLTextureHandler and create functions mirroring
//GL names but such functions taking the type
//GLTextureHandle as the first argument. Which
//is better is the subject of flamewars.



Another nice trick to have would be what many C++ folks do anyways in GL:



template<typename T>
struct opengl_trait
{
typedef T basic_type;
};


//typetraits for basic types
template<>
struct opengl_trait<GLbyte>
{
enum { type=GL_BYTE };
enum { count=1 };
enum { stride=sizeof(signed char) };
typedef GLbyte basic_type;
};

//and so on for basic types...


template<typename T, unsigned int N>
struct opengl_trait< array<T,N> >
{
enum { type=opengl_trait<T>::type };
enum { count=N*opengl_trait<T>::count };
enum { stride=sizeof(array<T,N>) };

typedef typename opengl_trait<T>::basic_type basic_type;
};



which then fits nicely for making tags to feed into a nicer version of glVertexAttribPointer... if your arrays are not interleaved, then the stride you need sizeof(mystruct). Similar template tagging would potentially be nice for glTexImage and it's friends...

But DSA is not core... I would hazard a guess that DSA will sort of sneak into the spec (as we have DSA for uniforms on programs and for manipulating samplers)... what remains is textures, buffer objects, framebuffer objects and renderbuffers... though I think the use cases of renderbuffers for GL on desktop are non-existant now..

I'd wait for for DSA to sneak into the spec first before embarking on making those header file(s). My 2 cents on the whole thing would be to make types that are just handles to GL objects and not representing the objects themselves (i.e. dtor does not call glDelWhatevers), this way one avoids the ctor/dtor/copy ctor "fun".

One needs to make sure the API is that it does not "lose" information and nothing goes wrong in using GL calls directly, this is why I'd emphasize the handler thing.

rombust
09-01-2010, 01:10 AM
You may want to have a look at ClanLib ( http://clanlib.org )

The clanDisplay, that the clanGL display target uses, was written like an OpenGL C++ wrapper. (See Sources/GL)

As already stated, it is very tricky to get right, and will not satisfy all programmers. There are always "speed" / "flexibility" / "ease of use" issues that contradict each other.

Since ClanLib has a free (BSD style) license, you can extract the code for your project if you wish.

Groovounet
09-01-2010, 04:27 AM
I don't think it would have any "speed" issue at it would be an inline only library.

Groovounet
09-01-2010, 04:39 AM
I had a look at the ClanLib wrapper and I don't think it would be anything close to the way to go for a C++ OpenGL wrapper.

aqnuep
11-19-2010, 08:05 AM
I've started to work on the OpenGL C++ wrapper what I was talking about. I would be still interested in having some board where we can discuss the pros and contras for various design approaches.

Actually I want to deal only with GL3+ core features so it is much simpler than I thought (at least from the size point of view as design is still not so simple).

I wanted to go with the naming conventions of how they made the cl.hpp for OpenCL, however having drawing routines named like:

context.drawElements(...)

seemed inconvenient for me so I've rather gone with the so called C++ Coding Standard (http://www.possibility.com/Cpp/CppCodingStandard.html) ended up with a more natural method naming convention:

context.DrawElements(...)

Besides that I wanted to emphasize the nature of state modification commands so I replaced calls like:

context.BlendFunc(...)
context.ColorMask(...)

with ones that have the Set* prefix thus clearly showing their purposes:

context.SetBlendFunc(...)
context.SetColorMask(...)

As binding objects for rendering is also state change, I rather replaced the Bind* prefices also with Set* prefices:

context.SetVertexArray(...)
context.SetTexture(...)

I also try to avoid the need as often as possible to use target specifiers so such new methods were introduced:

context.SetReadFramebuffer(...)
context.SetWriteFramebuffer(...)
context.SetUniformBuffer(...)

I'm progressing with the library pretty well but very often meet critical design questions where the word of more programmers would be benefitial so I would encourage you to join me in the discussions in some way.

Also, besides GL++ I will most probably introduce other headers like WGL++ and GLX++ for context creation and stuff just for convenience.

Groovounet: I'm thinking about interacting in the header with GLM as in many cases the generalized vector and matrix variables of GLM would be convenient to use (e.g. uniform value setting). So I would definitely expect your participation in the discussions about design issues.

aqnuep
11-19-2010, 08:10 AM
Yeah, and one more thing: I introduced some lightweight reference counting mechanism as currently OpenGL lacks of reference counted objects.

Obviously ctor, dtor, copy ctor and = operator has been implemented accordingly.

Groovounet
11-19-2010, 09:50 AM
I always have an issue with custom conventions. I don't believe in truth, I believe in agreements which are challenging enough.

Custom conventions are certainly nice for those who use them but for the others... I'm not sure.

If this project belong to OpenGL, I think is should follows as much as possible the Khronos Group conventions because they have been agreed among talented OpenGL programmers and I find quite pretentious to assume we could do better. This said, the OpenGL specification is full stupid choices but compare to the great stuff it must be not so bad.

Personally, if I was writing an object oriented C++ wrapper I would follow as much as possible the OpenCL C++ wrapper.

I think it's hard to agree on C, it's much more harder to agree on C++.

Regarding GLM, the pain comes from the C API which has a different function for every uniform (eg) types... On the wrapper side it's not many choices but writing each single function but I think it would be great to design this part in a way that the library user would be able to use template all the way in its code which is pretty straight forward to provide.

aqnuep
11-19-2010, 10:25 AM
Personally, if I was writing an object oriented C++ wrapper I would follow as much as possible the OpenCL C++ wrapper.

I agree, but the OpenCL C++ wrapper is also kind of awful and even though it fits much more the generic C++ coding style (if we can say that such exists :D) but as I mentioned, camelCase style names look quite awkward and maybe more difficult for OpenGL programmers to get used to.

About the Set* prefix issue (which I wantonly selected), I was thinking about the following use case scenario:

BindTexture(...)
UseProgram(...)
BlendFunc(...)

While all these configure the current state of the context they use various convensions:
1. Bind* prefix
2. Use* prefix
3. No prefix at all

I thought it would be more convenient to do the same in the following way:
SetTexture(...)
SetProgram(...)
SetBlendFunc(...)

This also reflects the usual OO convention to prefix object state change methods with Set*.

I don't question the knowledge of the members of the Khronos Group, I strongly respect them. However, the fact that the naming conventions vary that lot in the OpenGL API is not necessarily their mistake as most of the old functions are from the very first version of the specification.

I strongly believe that if they would have the chance, they would definitely rewrite most of the names in a more meaningful way, just that would introduce even more redundancy if we want to keep backward compatibility.

Taking in consideration that, maybe the ARB would choose some similar style if they would design the API now, from scratch.

Anyway, it seems that the issue to have some form of common agreement seems to be even more overwhelming than I thought originally :D

Groovounet
11-19-2010, 10:52 AM
What's wrong with the OpenCL C++ wrapper?

It's always really easy to argue against custom conventions.

Personally, I love the "bind" token and I see several meaning to it:
- It relates to objects (a container), either that can be modified by other OpenGL calls or for draw. glBindBuffer(GL_ARRAY_ELEMENT_BUFFER, ...) + VAO [censored] me off on that regard.

UseProgram was an accident as well that the PPO fixed.

I also like that OpenGL doesn't use Set* but use Get* to enforce the "natural" way to communicate between the CPU and the GPU.

Challenging project! ;)

aqnuep
11-19-2010, 11:13 AM
About the OpenCL C++ wrapper it is weird for me to say:

CmdQueue.enqueueReadBuffer(...)

when in C I would say:

clEnqueueReadBuffer(...)

for me the following would be more convenient as capitals stay the same (maybe I'm wrong?):

CmdQueue.EnqueueReadBuffer(...)

Agree that the UseProgram was a mistake and I see that it is fixed.
About the BindBuffer(ARRAY_ELEMENT_BUFFER, ...) + VAO issue, here DSA made the right choice and introduced the VertexArrayIndexOffsetEXT function that also handles buffer objects. I don't know why they didn't use the IndexPointer as like they did it with VertexAttribPointer?

I don't have any problem with the Bind* prefix, just I was thinking about if I already have a Set* prefix then why should I have two different one?

You're right that at first it sounds more natural to not use the Set* prefix but I have the following arguments for it:

1. It fits better the "usual" OO naming conventions
2. This way you can simply categorize the commands as state change, state query and operations

Just to explain in more detail the second argument:
In case of the context you have these categories:
1. Set* prefix: setting textures, programs, framebuffers, vertex array, other configurations that *only* change the state of the context, nothing more
2. Get* prefix: querying context state such as bound framebuffers, vertex array, etc.
3. No prefix: actual rendering commands that really *execute* something on the GPU (this does not just means Draw* commands but also query-, transform feedback-, conditional rendering start-end, sync fence, etc.)

Now I see that I'll definitely fail to make the wrapper useful in general for the community as I can't even agree with the first person I talk to :D

Anyway, I will make it for for sure, because I need it.

P.S.: Just one more note: my plan is not to write a wrapper that is as tight as possible but I would like to introduce some abstraction layer as well to make it a bit easier to use than the plain C API of OpenGL. Maybe this is where the difference of our view points comes from.

aqnuep
11-19-2010, 02:02 PM
Just a few additional things:
I've chosen to hide some of the implementation details because there are some redundant things as well as some legacy stuff that simply does not make too much sense in modern OpenGL.

Examples:

Separate shader objects:
Actually in case of separate shader objects the role of the shaders is kind of subsumed by programs and the role of programs is subsumed by program pipeline objects (just think about the CreateShaderProgramv function).
Taking in consideration this, maybe pipeline objects should be completely hidden (however I didn't get to a final decision on this one as maybe it would be just too complicated to do so).

Index type and index offset in draw commands:
DSA introduced the VertexArrayIndexOffsetEXT command that actually specifies both the stride and the type of indices so actually having the index type in the draw commands is just redundant. That could be hided.
Also, the first index for a draw command is specified with an offset rather than the first index number which is inconvenient. Knowing the stride actually makes it possible to use first index parameter rather than index offset (this is difficult to accomplish only in case of MultiDrawElements commands as the rest is straightforward).

Texture units:
You can actually bind several textures to the same unit if you use different targets. This is even worse when you have to query the bound texture for a particular texture unit as there you don't know the target to check.

Missing DSA things:
You cannot do indexed query to get the sampler attached to a texture unit.
No DSA version for VertexAttribDivisor.

I could continue actually.

Anyway, my goal is also to hide as much as possible of the inconsistencies resulting from backward compatibility.

Groovounet
11-19-2010, 06:11 PM
Heuummm? Oo

BindBuffer(ARRAY_ELEMENT_BUFFER, ...) has no DSA equivalent.
VertexArrayIndexOffsetEXT is for glIndexPointer which is deprecated.

About the convention, all I want to say is that it's just a matter of taste and there is nothing to argue.

Personally, I think that CreateShaderProgramv has nothing to do in the OpenGL specification. Since when OpenGL contains helper functions? It should be a GLU function but in any case I would not use it and in my mind it's deprecated.

I have an "abstraction library" as well but I don't think this could be compare to an OpenGL C++ wrapper. So the question is actually what do you want to do? Your own renderer (like everyone) or an OpenGL C++ wrapper(for everyone... challenging!)?

There are other missing DSA functions. Because of VertexAttribDivisor I don't use DSA for VAO and I consider that glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...); is not a VAO state for higher consistency.
I don't use DSA for multisampled texture either.

Is it really possible to write a C++ wrapper without a full DSA API?

aqnuep
11-19-2010, 07:24 PM
I think you're not right about VertexArrayIndexOffsetEXT as it is not just 100% equivalent to glIndexPointer. It has a buffer handle parameter and as I understand it is for subsuming the BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...) stuff.

Btw, yes, multisampled texture commands are also missing.

Alfonse Reinheart
11-20-2010, 01:42 AM
Personally, I think that CreateShaderProgramv has nothing to do in the OpenGL specification. Since when OpenGL contains helper functions? It should be a GLU function but in any case I would not use it and in my mind it's deprecated.

Well, the ARB doesn't control GLU. So it's either here or nothing. Furthermore, it's a bit more than just a convenience function. While it may be functionally equivalent to compiling a shader object, linking the shader program, and deleting the shader object, it will not be implemented that way.

Right now, every time you build programs on some implementations (*cough* NVIDIA *cough*), they're effectively compiled twice. The first time is when you make shader objects. But they must be compiled again when you link your shader. The reason for this is that certain errors have to be caught in the shader stage, and others in the link stage. NVIDIA doesn't care to split their compiler and linker, so they just put all the text together and run the compiler in both stages. This may be true of ATI implementations as well.

The point is that it is not purely a convenience. It can be a substantial performance gain for your application.


I think you're not right about VertexArrayIndexOffsetEXT as it is not just 100% equivalent to glIndexPointer. It has a buffer handle parameter and as I understand it is for subsuming the BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...) stuff.

No, it's not. glIndexPointer could use buffer objects through GL_ARRAY_BUFFER, just like glVertexPointer, glColorPointer, etc. Though granted nobody used them with buffer objects, but the GL spec mandated it all the same.

aqnuep
11-20-2010, 04:16 AM
No, it's not. glIndexPointer could use buffer objects through GL_ARRAY_BUFFER, just like glVertexPointer, glColorPointer, etc. Though granted nobody used them with buffer objects, but the GL spec mandated it all the same.

Good to know that. The last time I tried IndexPointer with buffer objects was about 5 years ago and as I remember at that time I was struggling with it on both ATI and NVIDIA (don't know the exact reason though).

kyle_
11-20-2010, 05:19 AM
The last time I tried IndexPointer with buffer objects was about 5 years ago and as I remember at that time I was struggling with it on both ATI and NVIDIA (don't know the exact reason though).
Thats kinda interesting, what did you need color indices for?

aqnuep
11-20-2010, 05:32 AM
OMG! I just checked the legacy specification. In fact IndexPointer is for color indices :D

Really, several years ago, when I last tried to use them I thought that it is for element array indices :D

Now I feel shame. Sorry, I feel completely stupid.

aqnuep
11-20-2010, 06:03 AM
Btw, am I right then that GL_EXT_direct_state_access does not have any mean to specify the element array buffer binding for the VAO in a DSA way? That's just nuts.

aqnuep
11-20-2010, 06:04 AM

Groovounet
11-21-2010, 01:16 PM
Yeap, that's what I was saying.
It pissed me of when I discovered it... another problem of VAO design.