0
\$\begingroup\$

The goal: issue least calls to d3d11 ImmediateContext and render advanced effects like toon / outline / anything that requires rendering the same vertex buffers with at least two different vertex shader and pixel shader programs efficiently by sorting opaque drawcalls front to back and transparent drawcalls back to front.

What I did: Abstract calls to d3d11 immediate context by a DrawCall struct that acts as a gpu command recorder. A call to DrawCall.SetIndexBuffer will create a c# Action that will issue a call on the ImmediateContext upon invoke call. The DrawCall internal gpu command list is sorted the following way: set shaders, set constantbuffers, set blendstate, set stencilstate, set vertexbuffers.... I am using sharpdx and c#.

My problem: I use a 64bit sortkey for my gpu commands. When rendering complex effects like outline or anything that requires state changes to outputmerger the whole idea with sorting doesn't seem right... example: I render opaque objects first and then transparent objects... I want to render an object opaque with a diffuse shader and it should have a red transparent outline. For this effect I have to modify stencil and blend states in between drawindexed calls. According to my sortkey those gpu commands are on different draw loops opaque<>transparent but the stencilstate requires both drawindexed calls to be one after another.

Should I implement a chained gpu command for multipass rendering or are there better ways to handle such effects?

\$\endgroup\$
0
\$\begingroup\$

Background: There is no short answer here, but as someone who also codes in C# and Sharpdx, I can go over some of the learnings I have.

I sacrificed some of my flexibility for specific for fixed function draw actions. That being for example, draw my terrain, draw my models, text etc. But, what I do differently is that there is a pass specific to shadows, a pass specific to above water, below water, and transparency. I tag dynamic assets to render in these passes, so that I have the flexibility to select what rendering needs to be applied.

What I end up having is a fixed graphics pipeline with components within it that allow for flexibility, it also made the job of using deferred rendering much easier.

I, like yourself started out with a fully fledged flexible pipeline, but ended with a more fixed rendering pipeline wtih some flexibility. This is where I think you will end up, you have a need to control some elements/anchor points of your pipeline which allowing for your engine to take care of some of the finer details. As from your problem, you want to exert some specific control of the drawing at times.

On a performance note, you really don't want to be changing the state too often because you flush the GPU pipeline. On more modern GPUs, this isnt as bad (usually only affects a few cycles). So you may end up grouping your drawing commands differently, that is draw all relevant objects with your diffuse shader, then modify stencils states etc then draw all the objects again etc...

Chaining GPU commands (say through DrawIndirect) also will give you a performance improvement if your GPU is being starved by the CPU and you want to feed the pipeline quickly. I tested for example deferred rendering in my game engine which allowed for the draw commands to be recorded and buffered. This had significant performance improvement in Debug but literally no frame rate improvement in release.

Gl, hope you find your solution.

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.