Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to design an OpenGL DirectX "Abstraction Layer"

I was reading about creating graphics "abstraction layers" to make it convenient to switch between graphics platforms. Unfortunately, I have not been able to find much detail about the subject. Is this abstraction achievable at the function level with something like this?

void pushMatrix(){
  if (directx){
     // do directx function

  }else if (opengl){
     // do opengl function
  }

}

Is this how it works? Is there a better way? Can anyone point me to some examples of things that do this or more example code?

like image 628
Justin Meiners Avatar asked Sep 12 '10 23:09

Justin Meiners


4 Answers

What is usually done is to have an interface to a "generic" renderer :

class RendererInterface{
    virtual DrawMesh() = 0;
    virtual SwapBuffers() = 0;
    /// etc
}

with one implementation for each lib :

class OpenGLRenderer : public RendererInterface{
    virtual DrawMesh(){... }
    ....
}

But the concept is the same as Alexander's answer.

like image 93
Calvin1602 Avatar answered Oct 11 '22 18:10

Calvin1602


Yes, that is how it works. By abstracting the graphics API you use, you are removing it from what the programmer needs to worry about. This is similar to the way GDI abstracts the device being written to, such as that the programmer needn't worry about it.

In this sense, your library of functions similar to the one above would be like the HDC. It is the interface of this abstraction.

It would be more difficult then a simple check for API, then calling the appropriate function(s). The two API's (DirectX and OpenGL) are very different, and abstracting them both into a single interface would not be easy, especially if you want to cover most of the functionality.

You would need to more abstraction then simply making universal functions. You would need to create more complex structures.

Say hypothetically that OpenGL required you to call function A, then function B to make something happen, but DX required that you call func B, then func A. To accomodate this, you would need to do something similar to this:

void functionA(...) {
  if (OpenGL) {
    glFuncA();
  } else {
    //store parameters
  }
}

void functionB(...) {
  if (OpenGL) {
    glFuncB();
  } else {
    dxFuncB();
    dxFuncA( saved params );
  }
}

It is not a very good example, but it demonstrates the principal. Creating an abstraction for to very big and different API's would require much thought, and far more effort then wrapping each function.

like image 37
Alexander Rafferty Avatar answered Oct 11 '22 20:10

Alexander Rafferty


That would end up messy and inefficient. I would abstract via a scene graph, i.e. have a generalized high-level representation of the "scene". This scene would then be rendered by a "renderer" instance, which could be implemented using either OpenGL or Direct3D.

I suggest you take a look at a cross-platform graphics engine such as Ogre3d.

like image 4
Mikayla Hutchinson Avatar answered Oct 11 '22 18:10

Mikayla Hutchinson


I agree with mhutch 100%. By restructuring your renderer to focus on WHAT it's doing, rather than on HOW to do it, you will make a much cleaner design.

Designing around an API too closely, or worse, littering your code with if (this_platform) { do_this(); } else { do_that(); } will quickly make a large mess of the program.

My advice is to go through your app and figure out a list of WHAT it does, try not to pay a lot of attention to HOW. It might be late in your project to be doing this, I don't know your specific scenario, but (IMHO) refactoring it from an API call littered nightmare to something that abstracts it just enough will improve your final result.

Although at first thought, you might think that implementing a scene graphing system would add a bunch of overhead. In reality, I got major speedups with my rendering engine by using the scene graph to manage state changes - by avoiding state changes (changing textures, changing shaders, etc) and drawing the scene in a far more optimal order. Things like instancing are elegantly integrated into a scene graphing system and can give you major speedups as well as cleaning all the API-centric mess out of your code and separating the "what it does" from the "how it does it" in your main program logic.

like image 3
Doug65536 Avatar answered Oct 11 '22 18:10

Doug65536