Jump to content
  • Advertisement
Sign in to follow this  
Happy SDE

DX11 Cleanup immediate context from unused objects attached to it.

This topic is 915 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am learning DX11 over some tutorials where usually one mesh is rendering.

And I found two ways how context is cleaning up:

 

1. No cleanup at all: all buffers, shaders are set up in initialization.

And sometimes macro like SAVE_RELEASE() is called in shutdown.

 

2. Explicit calls like

m_spContext->VSSetShader(nullptr, nullptr, 0); 

after each Draw() call.

 

Also I found the third way:

m_spContext->ClearState();

I am a little bit concern about performance if I will use it before each setup resources for each Draw() call.

 

Because of rendering several objects with different shaders/buffers/… might bring bugs like using the wrong Stencil State, or having Geometry Shader attached when it is not in use, I am asking the question:

 

How do developers in real world applications/games are cleaning up the context?

 

Thanks in advance.

Edited by Happy SDE

Share this post


Link to post
Share on other sites
Advertisement
Usually incorrectly and banging on their engine until it looks right :P. But the dark ages are over and dx12 addresses this problem by wrapping all the state into a single object.

You should be concerned about pref, but the old api isn't going to make it obvious about how expensive different calls are. Just try to change state as little as possible.

As for cleanup, setting a state object addrefs it so releasing will never mess up an in-flight operation.

Share this post


Link to post
Share on other sites

Thank you, Dingleberry and Hodgman for ideas!

 

I decided for my engine to implement 2 things:
1. For each switch between renderers (Forward <-> Deferred<->Normals) I will completely reset context with m_spContext->ClearState();
    Because it uses only when I press a button in GUI, it should be lightweight.
2. For some objects like Geometry Shaders, Blend States, … that are rare in use, I will create RAII wrappers.
    The wrappers in the destructor will reset this particular resource in m_context to null.
 

This will solve some issues:
1. There should not be much performance penalty.
     It will be quite simple and will not reset all objects (like Vertex Shader/CB/…) to null (as Dingleberry suggested to keep it simple).

2. My code uses exceptions, so RAII should be good for it.
 

When I will switch to DX12, I will certainly redesign this approach.

Edited by Happy SDE

Share this post


Link to post
Share on other sites

2. For some objects like Geometry Shaders, Blend States, … that are rare in use, I will create RAII wrappers.
The wrappers in the destructor will reset this particular resource in m_context to null.

 

Without actually knowing the implications, setting things to null is probably fine, there's a slight possibility low end/integrated drivers will do something stupid though. For example, if you have two identical draw calls that use the same state, if things get set to null in between, is there going to be a cost associated? Ideally there shouldn't be but it's hard to know for sure since the gpu doesn't actually work like how pre-12 apis are presented anyway.

 

Pre-12 my mindset was typically to trust the driver to do the "right" thing as little as possible and I'd still get tons of things wrong. I'd usually create a whole separate and kind-of-redundant state machine on top of dx which of course is vaguely useful to a gl implementation too.

 

E: Ultimately if you just want to mess around with shaders and familiarize with the api, it's not going to matter until you're doing a ton of draw calls or are desperate to reach low end gpus.

Edited by Dingleberry

Share this post


Link to post
Share on other sites
Just have a compile-time or run-time switch that resets state after each draw call. You can use it in your debug builds, and in your automated tests (if you have any). That way you can validate whenever you want to, but you don't have to worry about performance in your release builds.

Share this post


Link to post
Share on other sites

I don’t have a clear picture how real Game developers measure performance.

Assume this simplified example: I want to draw 3 axes lines in my scene.
I want the lines be AA.
But for Deferred renderer it can’t be antialiased because render targets have DXGI_SAMPLE_DESC with quality = 0 and count = 1.
 

I see 2 choices:

 

1. Add a bool variable that will switch it off creation and setup rasterizer (for Deferred renderer)

    Cons: +bool, +some code, + 2 if-s in runtime
 

2. Create and use rasterizer for Deferred renderer like for other (like Forward) renderers with AA.

Don't use member variable m_bUseAA at all.

Driver just will not AA-rasterize the lines, they will be aliased, but set up it 2 times.
    Pros: simple code.
 

I might measure the performance by adding ETW events (start+stop) on each call and analyze time delta in xperf.
But it is like using big-gun approach. =)
 

If I would be a REAL GAME DEVELOPER, how would I measure the performance impact?
(let’s step aside that this call is using once per frame and have not much performance impact: I just want to measure it and use this approach in other situations)

OS - Windows only, DX - ver.11

 

Initialization:

void AxesRenderer::createAaLineRasterizerState()
{ 
  //Deferred renderer can’t use AntialiasedLineEnable because of it’s RTs are not multisampled 
  if (!m_bUseAA) {  return; }
 
  CD3D11_RASTERIZER_DESC desc{ D3D11_DEFAULT }; 
  desc.MultisampleEnable     = TRUE; 
  desc.AntialiasedLineEnable = TRUE;
 
  _check_hr(D3dDevice::GetInstance().GetDevice()->CreateRasterizerState(&desc, m_lineAntialisedRasterizer.GetAddressOf())); 
  SetDebugName(m_lineAntialisedRasterizer.Get(), "AxesRenderer::Line AA Rasterizer");
}

Usage:

void AxesRenderer::Render(const DirectX::XMMATRIX& viewProjMatrix)
{
   ...shaders and resources setup... 
   if (m_bUseAA)
   {  
      EtwRaiiStartStopWrapper etw{“RSSetState”}; // << Is there a better way?  
      m_context->RSSetState(m_lineAntialisedRasterizer.Get()); //Measure this call
   }
 
   m_context->Draw(6, 0);

   if (m_bUseAA) 
   {  
      EtwRaiiStartStopWrapper etw{“RSSetState”}; // << Is there a better way?
      m_context->RSSetState(nullptr);    //Measure this call
   }
}
Edited by Happy SDE

Share this post


Link to post
Share on other sites

If I would be a REAL GAME DEVELOPER, how would I measure the performance impact?

 

There's a bunch of tools that hook into your dlls and capture all your d3d/gl calls and play them back, like Intel's GPA Frame analyzer or Nvidia's Visual Profiler thing. This is useful because you can do experiments like setting 4x4 textures everywhere to see if you're texture limited or if your bottleneck is somewhere else. Also they're usually minimally intrusive to your code. However, they're all kinda buggy in my experience and nothing ever beats just looking at your frame time since it's a holistic measurement.

 

In something like a game you're usually going to have a relatively small number of different "pipelines". A simple way is to assign some sort of hashable key to each drawn thing that sets up the render state for you. Maybe an enum that's like "UI_MATERIAL" or "AXES_LINES_MATERIAL". Then you can come up with some sorting algorithm based on that enum so things get batched together nicely. 

Edited by Dingleberry

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!