0
\$\begingroup\$

What I'm trying to do is create a specific shader states(depthstencil, blend, and rasterizer) for each mesh in my program, so jumping straigth to the code:

pDeviceContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &strides, &offset);
pDeviceContext->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
{ // set the states bellow for each mesh drawn:
    pDeviceContext->OMSetDepthStencilState(m_pDepthStencilStateON, 0);
    pDeviceContext->OMSetBlendState(m_pBlendState, BlendFactor, SampleMask);
    pDeviceContext->RSSetState(m_pRasterizerState);
}
pDeviceContext->IASetPrimitiveTopology(Topology);

The code above depicts a function which is called for each mesh in a model, there are around 200~300 models currently being drawn in each frame. So to resume this is what is not clear to me:

1- Is this a good practice? Or is there a better way of using different shader states during runtime?

2- Is it a good practice to use one DepthStencilView to each mesh, like the ones above, and doing their cleanup with pDeviceContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0); at the end of each frame?

The main reason I want to do this is because I want to draw different kind of things in runtime, in some cases I may or may not want to use the blend effect in a certain mesh, or could want the z buffer to be read-only on DepthStencilView during the drawing of a certain mesh.

I'm aware of the possibility to create a new state from the previous one, but I'm not sure if this would be a hit on performance aswell:

D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
pDepthStencilView->GetDesc(&dsvd);
dsvd.Flags = D3D11_DSV_READ_ONLY_DEPTH; // sets to Read-Only.
HRESULT hr = pDevice->CreateDepthStencilView(pDepthStencil, &dsvd, &pDepthStencilView);

If someone could explain to me what is the best approach to this kind of case it would help me pretty much.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Most questions of the form "is it a good practice to..." can be more usefully turned around: profile your current solution, and ask yourself "do I detect a measurable problem when I do it this way, that will be harmful for the kind of game I'm making?" If you don't observe a measurable problem yet — like a drop in frame rate below your target on your test hardware — then it might not be an issue at all. Keep working on other stuff until it becomes a problem. Once you observe a specific problem, ask here how to solve that problem. \$\endgroup\$
    – DMGregory
    Aug 22 '21 at 22:46
  • \$\begingroup\$ Ok, I will try to implement this to see if it works well for my case, but I'm still not very sure if this is a ordinary practice, or people do it another way... Also don't know if creating a bunch of states or using the previous one to create a new state is the better option, so I was expecting to get any clarification as these kind of things are hard to determine under a lot of side code happening behind them... \$\endgroup\$ Aug 22 '21 at 22:58
1
\$\begingroup\$

As noted in the comments, profile your scene and look at where your bottle necks. It's going to vary greatly depending on exactly how those 200-300 models are actually built.

In general, you have to draw your transparent objects after drawing your opaque ones, and some kind of sorting by material setting can help avoid thrashing GPU texture caches. The transparent objects generally have to be drawn back-to-front (a.k.a. the Painter's algorithm).

In DirectX 10/11, the state objects helped a lot of the 'unknown stalls' problems in Direct3D 9 when you called Draw. Generally creating state objects and shader objects is expensive, but setting them is not.

In DirectX 12, you have to munge everything into a Pipeline State Object so creation is heavy, but setting is super cheap.

"back in the day" of Windows XP and Direct3D 9, most games that had a good framerate had to keep total Draw calls per frame to less than ~1000. Later-generation Direct3D 9 stuff got up to 1000-3000 individual Draw routinely.

For pixel-shader heavy materials, a 'depth-prepass' (i.e. drawing the large objects with a trivial shader that just generates the depth values) and/or drawing all the opaque objects in rough front-to-back order can help.

In the end, profiling is typically needed to determine if your program is: (a) CPU-bound, (b) GPU-bound on the vertex processing, or (c) GPU-bound on pixel-fill.

\$\endgroup\$
4
  • \$\begingroup\$ Since this question was made some time ago, I've already tried something to solve the problem. What I did was create multiple state objects, each one for each mesh I had to draw, but I had a very bad performance over this, so I managed to create a class called "DeviceState" that would handle state changes and keep in cache the last state used, so it determines if the state should change or not. This incredibly almost doubled my framerate. I've been thinking about the rendering order you said aswell, as I'm having problems with some objects with z buffer disabled being occluded on the scene. \$\endgroup\$ Sep 24 '21 at 15:58
  • \$\begingroup\$ Please, let me know if you think if this is a good approach and thank you for taking your time to answer my question. \$\endgroup\$ Sep 24 '21 at 15:59
  • \$\begingroup\$ Do the models share any "materials" settings? If so, that's usually a good way to group the state. \$\endgroup\$ Sep 24 '21 at 17:40
  • \$\begingroup\$ Yes, some of them have similar material settings, but my class is only able to detect if the new data I'm trying to send is already there, so I feel it could be better. When I try to send a new material to GPU it will check if it is already there through a cache copy. \$\endgroup\$ Sep 24 '21 at 18:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .