Tuesday, June 8, 2010

Multiple API Render

hi
recently i didn't post a lot because the engine was down, not all things worked because replace the current render system with new one.
my engine use dx9 api for the rendering and when dx11 was out i wanted to integrate it, to do that i had two options:
1. replace all dx9 related code with dx11
2. create generic render system to support any render api (dx9, dx10, dx11, opengl, whatever)
after doing some thinking i got to the conclusion that option 2 will be the best.
option 1: good if i want dx11 only (win vista+) and supporting only dx11 features, if in the future ms decide to change few api and add few shader stages, this will be a mess to change and add those.
option 2: take some time to do, but will support anything you need and will need in the future (its all about how you design your api)

now i will describe few things i think is important to know when implementing option 2 with dx9, dx11 support (in case you will need to do it for some project).

the biggest challenge was to replace my shader system, i mainly use the dx effect framework because it seems like the best choice and very easy to use.
one of the features i use in techniques to apply different shaders, for example: when you generate shadow maps for a fench you want to reject pixels using alpha value sampled from a texture, and when you render opaque surfaces you don't, so you need 2 techniques, one for each.
features that the new shader system must to support are:
1. support any shader stages we have now and new ones that will be added in the future (if any), also it must support the technique feature so i won't need to change a lot of the code that already assume techniques.
2. keep track of constants, sampler states, textures etc, so it won't set the same constant again and again when it already set, or set texture when it is already binded.
3. ability to compile shaders for different api (for example: dx9 shader - shader model 3, and dx11 shaders - shader model 5, or opengl glsl shaders)
4. easy to use - setting shader data must be easy and understandable from the name of the functions.

supporting 1: done using file of each stage (.vs, .ps, .gs etc)
supporting 2: done using few variables that store the last state (dirty value vars)
supporting 3: done using a script that defines every technique and the stage shaders, this script supports the define macro so i could select which stage shaders and other defines to load and set depending on api macro, for example:
#if DX11
code block
#endif
the code block will be parsed only if the current renderer is dx11.
note that this script also solved the techniques issue because it contain the techniques that defines which shaders to bind when the technique is set.
when i create a shader from this script the shader is actually a multi shader as it contain few shaders and not one (each technique contain shader per stage), so this script is my multi shader script definition.
supporting 4: well, this is the easiest i think...

the next challenge was state management.
from dx10 all render states replaced with state objects: rasterizer, depth-stencil, blend and sampler state object. so you can't call SetRenderState anymore.
to solve this issue, we can do few things:
1. emulate dx10+ state objects using dx9 style render states (SetRenderState functions)
2. emulate dx9 render states using state objects.
1. in some cases you want option 1, in fact i implemented this at work where all the developers used to work with SetRenderState style.
to do this, you can use hash for the state object and create it on the fly when it's not in the hash.
2. this is what i did in my engine, 99% of the states is known in advance so no need to create state object on the fly, for the other 1%, when it happens, there must be a new object/mesh that was loaded so his material was loaded too, and all states objects was created (on the fly).
each state is tracked and managed so there the state will be state only once (when needed)

another thing that took a lot of time is to convert the dx9 shaders into both dx9/11 shaders using files each stage.
instead of duplicating the files and rewrite them using dx11 functions, i added render system define (DX9 for directx9, DX11 for directx11 etc) which is added automatically for each shader i compile and use it to know which function to use, for example:
#if DX9 == 1
col = tex2D(diffuseSampler, UV);
#else
col = diffuseTexture.Sample(diffuseSampler, UV);
#endif
when i need to recreate specific shader stage for DX11 only (for example: using tessellation), i write the shaders (add domain, hull shader files) and add their filenames the multi shader script (the same can be done for optimizing shaders to use the new dx11 functions, for example: blur and pcf can be optimized using gather function)

for now, the render system supports only dx9,dx11 render paths, opengl can be added by implementing the renderer functions.
note that i kept dx9 renderer path so i know every thing was implemented right in dx11 renderer, this is mainly to validate every function i wrote in dx11 renderer.
everything renderer with dx11 renderer must be the same (using only dx9 features) using dx9 renderer.

thats it for now, i hope will find this info useful, if you have any question on topics i didn't or forgot to cover, feels free to ask...
cya

4 comments:

MarmAmic said...

This is pretty cool...

So basically in a matter of days you can integrate opengl and make the engine work on xbox\PS3 and PC. Right?

orenk2k said...

hi
well not exactly, game engine is not just a renderer there is a lot to consider when porting into consoles (file system, threads management, memory limit, input system, sound system etc).
lets say we are not taken into account those topics, the answer is yes, in a matter of days i can write an OpenGL renderer that will work on ps3, but for those who familiar with ps3 - nothing comes to live in few days, maybe in few broken bones ;p
cya

Ido Lavy said...

Thank you for sharing, your posts are always a good read.

orenk2k said...

my pleasure ;)