Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 9 of 9

Thread: Diamond GL - pure C++ for OpenGL API (in development)

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Junior Member Newbie
    Join Date
    Aug 2017
    Posts
    6

    Post Diamond GL - pure C++ for OpenGL API (in development)

    I development C++ (planned fully C++14, C++17 compatible, with Visual Studio support) wrapper for OpenGL API.
    https://github.com/AwokenGraphics/diamond-gl

    Advantages:
    - Support of GLM
    - Support of STL (vector, string)
    - Utils for simpler access
    - Same paradigm as in OpenGL (nearest as can)
    - Object oriented for C++
    - Combined most OpenGL functions
    - Support of DSA

    Disadvantages:
    - Not all API functions released at now (in TODO - FBO, query, state functions)
    - No CUDA support
    - In early stage of development

  2. #2
    Senior Member OpenGL Lord
    Join Date
    May 2009
    Posts
    5,907
    If you're looking for some advice, I find many of your design choices to be... dubious.

    Your API is needlessly objectified. As an example, take `state.hpp`.

    You have this `_blend` type, and you create a global, non-constant `blend` object. Why? What advantages does this offer over simply having those be namespace-scoped functions? None of those functions modify the members of the `_blend` object. In fact, there are no members of the `_blend` object. So what's the point of them being in an object? If you want to group those functions together, then use a namespace, or static members.

    The user gets zero benefit from calling `blend.func` vs. `blend::func`.

    Also, there are a number of places where you don't take advantage of obvious avenues for C++-ification of the OpenGL API. For example, you don't use `enum class` where appropriate. Admittedly, `gl.xml` doesn't make it easy to extract out the appropriate enumerators for every function (though it's much better now than it used to be). But any good C++ wrapper of OpenGL ought to at least make an attempt to support this.

    And then there's just some very unfortunate design choices.

    First, `uniform::set` takes `vector<T>`. That's wonderful... for people who store their data in `vector`s. For everyone else, it's a terrible API. They now have to copy that data to a `vector` before they can pass it to you. Which means every time you send data to a uniform, you have to allocate memory that will be almost immediately deleted.

    A pointer and a size would be an interface that anyone could use, regardless of their container of choice. Or even better, the GSL type `span`.

    Oh, and FYI: if you want to test if a type is the same as another type, you use `std::is_same`; you don't use `typeid` unless you need to make a runtime determination.

    Then, there's your `base` type. This is a type whose sole purpose is to allocate storage for OpenGL objects. Ignoring the bug you have in it (not deallocating memory), you derive from this class a lot. Publicly.

    But you never use it as a public base class. You aren't using inheritance for polymorphism, since `base` doesn't have any virtual functions. You treat it like a member variable. So... why isn't it a member variable? Why are you inheriting from it?

    You also use it in a lot of places where it is quite frankly unnecessary and needlessly performance-unfriendly. Consider `shader` and `program`. `base` has the ability to allocate an array of objects; you tell it how many to allocate. And yet, `shader` and `program` both... only ever allocate one such object. In fact, OpenGL doesn't even allow you to allocate an array of shader/program objects. So neither `shader` nor `program` needs dynamic allocation at all; it just needs to store a `GLuint`.

    This is even further abused with the `_mode` class. There, you're not even using the dynamic allocation to store an OpenGL object; you're just storing an integer. So why are you using `base` for this?

    I haven't done a detailed look through this, but thus far, I cannot say that I find many of the choices made in the design of this system to be particularly good.

  3. #3
    Junior Member Newbie
    Join Date
    Aug 2017
    Posts
    6
    About state functions (blend, etc.). I reserved for future multi-context purposes.
    About shaders, programs, I tried to reduce this gap. Mainly allocation doing for a single (base) variable. But when I just put the address of the program, it removing by GC.
    I replaced typeid to is_same.

    About "why I use pointers"? I reserved for multiple object creation. It doing for tuples.

  4. #4
    Senior Member OpenGL Lord
    Join Date
    May 2009
    Posts
    5,907
    Quote Originally Posted by capitalknew View Post
    About "why I use pointers"? I reserved for multiple object creation. It doing for tuples.
    My point is that `glCreateShader/Program` cannot do multiple object creation. Those functions create a single object at a time. So it makes no sense for your shader and program types to support something that OpenGL does not.

  5. #5
    Junior Member Newbie
    Join Date
    Aug 2017
    Posts
    6
    I know. I tried to make combined method for allocations. I used smart pointers for GLuint object.

  6. #6
    Senior Member OpenGL Lord
    Join Date
    May 2009
    Posts
    5,907
    Quote Originally Posted by capitalknew View Post
    I know. I tried to make combined method for allocations. I used smart pointers for GLuint object.
    Yeah, I saw that. You've fixed the memory leak, but that doesn't fix the defects in your overall design.

    Your design with this `base` class is just... pointless. Dynamically allocating a `GLuint` serves no purpose. There's no point in having a class whose only purpose is to dynamically allocate a `GLuint`. At least before, when it was possible that you could allocate an array of `GLuint`s, `base` made sense. But once you took that away, `base` stopped having a purpose.

    It would be far better to just give every derived class a `GLuint` member. And then not have them derive from `base` at all.

    Also, every one of those classes should be non-copyable. And they should have a move constructor whose job it is to manage ownership of their object (blanking out the original as it copies to the new).

    This is a far superior mechanism for doing what you're attempting:

    Code :
    template<class ObjectBuilder>
    class opengl_object
    {
    private:
    	GLuint obj_ = 0;
     
    public:
    	opengl_object() = default;
    	opengl_object()
    	{
    		reset();
    	}
     
    	//non-copyable
    	opengl_object(const opengl_object&) = delete;
    	opengl_object& operator=(const opengl_object&) = delete;
     
    	//moveable
    	opengl_object(opengl_object &&other)
    		: obj_(other.release())
    	{
    	}
     
    	opengl_object& operator=(opengl_object &&other)
    	{
    		reset(other.release());
    	}
     
    	template<typename ...Args>
    	void construct(Args ...&&args)
    	{
    		reset(ObjectBuilder::create_object(std::forward<Args>(args)...));
    	}
     
    	GLuint get() const {return obj_;}
     
    	GLuint release()
    	{
    		auto ret = obj_;
    		obj_ = 0;
    		return ret;
    	}
     
    	void reset(GLuint new_obj = 0)
    	{
    		if(obj_)
    			ObjectBuilder::delete_object(obj_);
    		obj_ = new_obj;
    	}
    };

    This class does no dynamic allocation. It has absolutely zero overhead compared to a `GLuint` object. The class exists solely to manage the object. Its destructor ensures that, if it currently manages a real OpenGL object, the object will be destroyed.

    The 'ObjectBuilder` template parameter is a type that provides static methods to create and destroy OpenGL objects of a particular kind. So you would have a `texture_builder`, `buffer_builder`, `shader_builder`, etc. Note that `opengl_object::construct` is a variadic function; this allows `create_object` to have parameters, which is vital for calling `glCreateTextures`. Here are some examples:

    Code :
    struct texture_builder
    {
    	static GLuint create_object(GLenum target)
    	{
    		GLuint ret;
    		glCreateTextures(target, &ret, 1);
    		return ret;
    	}
     
    	static void delete_object(GLuint obj)
    	{
    		glDeleteBuffers(&obj, 1);
    	}
    };
     
    struct buffer_builder
    {
    	static GLuint create_object()
    	{
    		GLuint ret;
    		glCreateBuffers(&ret, 1);
    		return ret;
    	}
     
    	static void delete_object(GLuint obj)
    	{
    		glDeleteBuffers(&obj, 1);
    	}
    };

    So, a texture type would have a member of type `opengl_object<texture_builder>`. Note that it should not derive from this class; it should just have it as a member. You can fetch the OpenGL object with the `get` method.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •