Quote:
/../
The main drawback to this approach is that Renderer contained a large number
of virtual functions. In an application with a large number of calls to the
virtual functions, there is a performance hit due to those calls. Specically,
there are many data cache misses due to the lookup of the function pointers in
the virtual function table (the tables are global data). WM5 has a concrete
class Renderer that does not have virtual functions. The class is implemented for each graphics API. The code for these APIs is also part of WM5
LibGraphics. The selection of the API is controlled via build congurations.
Now I can understand that virtual functions are a performance hit, but skimming through the source code to Wild Magic 5 (Wm5Renderer.cpp in particular), I was stunned to find that each Enable()/Disable()/Bind()/Unbind() call did a std::map::find() operation to lookup the corresponding platform dependent concrete class (VertexShader <-> PdrVertexShader). This to me seems like an even worse performance hit than the actual virtual function call. After all, if the majority of your renderer's work is in executing virtual function calls or looking up platform specific implementations from a std::map, you surely is doing something wrong, no?
(the Pdr prefix is his notation of implementation specific class, decided at compile-time, e.g. there is a PdrVertexBuffer for both OpenGL and D3D9)
I'm very curious about the profiling results if one where to eliminate all std::map::find() lookups and instead store an opaque pointer to the implementation, e.g. VertexShader would contain a void* storing the PdrVertexShader.
I would like to think there are ways to still have inheritance and virtual functions for renderer implementations, but the virtual function call is only done once in the Draw() call from the application, and not for every state change, as would seem to be the case in Wild Magic.
Thoughts?
[Edited by - void0 on September 13, 2010 7:40:34 AM]