I learn dx11 recently, and find the dx11 render state is strange, why the render state is designed to an object?
Because of performance. Setting one at a time takes a lot more cycles as opposed to setting all of them at once.
Furthermore many parameters are actually inter-linked with each other, even if they appear completely unrelated:
- For example modern GPUs require shaders to know the vertex layout (Input Assembly in D3D11 terms).
- Depth write settings interact with alpha testing (to know whether to disable early Z optimization).
- Cull mode interacts with shaders if the shader uses SV_IsFrontFace.
- On certain mobile GPUs, blending modes are patched into the shaders.
- Cull mode interacts with stencil (i.e. two sided stencil)
Because of these dependencies at the implementation level with seemingly unrelated features, changing the setting one by one would have to trigger a lot of flushing, whereas changing everything in one go allows for resolving all the dependencies because all of the data is supplied together (and validated beforehand by making the object immutable!).
TL;DR: Performance.
setMaterial(material)
{
if(material.context.depthenable == true); context->setRS(depthenable, true);
else context->setRS(depthenable, false);
}
I don't know how to easy to handle the dx11 render state object. Who can show me how you use these ridiculous things?
You're approaching it the wrong way, the D3D9 way. In order to approach the D3D11 way, you need to create your object beforehand:
struct Material
{
bool mDepthEnabled;
bool mDepthWriteEnabled;
// ... many other settings
ID3D11RasterizerState *mRasterizerState;
ID3D11BlendState *mBlendState;
ID3D11DepthStencilState *mDepthStencilState;
//Always call after you're done modifying the material.
void flush()
{
SAFE_RELEASE( mRasterizerState );
SAFE_RELEASE( mBlendState );
SAFE_RELEASE( mDepthStencilState );
D3D11_RASTERIZER_DESC rasterizerDesc;
D3D11_RENDER_TARGET_BLEND_DESC blendDesc;
D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
depthStencilDesc.DepthEnable = mDepthEnabled;
depthStencilDesc.DepthWriteMask = mDepthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ZERO : D3D11_DEPTH_WRITE_MASK_ALL;
//...Fill ALL the other settings...
device->CreateRasterizerState( rasterizerDesc, &mRasterizerState );
device->CreateBlendState( blendDesc, &mBlendState );
device->CreateDepthStencilState( depthStencilDesc, &mDepthStencilState );
}
};
void setMaterial( material )
{
//ASSUME material already has been flushed.
assert( material->mRasterizerState && "You forgot to call flush!" );
device->RSSetState( material->mRasterizerState ); //You could check if this is redundant. If it is, avoid calling again
device->OMSetBlendState( material->mBlendState );//You could check if this is redundant. If it is, avoid calling again
device->OMSetDepthStencilState( material->mDepthStencilState );//You could check if this is redundant. If it is, avoid calling again
}
The idea is simple: Create the material. Once it's set, initialize the D3D11 structures. This is all done at loading time, not every frame. Once that's done, don't modify the material frequently (at least the parts that need flushing). If you need to change parameters very often, it's better to use multiple materials instead.