Sponsored By

Introduction to Diligent Engine

Diligent Engine is a light-weight cross-platform graphics layer. Its main goal is to take advantages of the next-generation graphics APIs, but at the same time support older platforms. This post is an introduction to the engine.

Egor Yusov, Blogger

March 23, 2016

3 Min Read

Diligent Engine is a light-weight cross-platform abstraction layer between the application and the platform-specific graphics API. Its main goal is to take advantages of the next-generation APIs such as Direct3D12 and Vulkan, but at the same time provide support for older platforms via Direct3D11, OpenGL and OpenGLES. Diligent Engine exposes common front-end for all supported platforms and is designed to be used as a graphics subsystem in a game engine or any other 3D application. It is distributed under Apache 2.0 license and is free to use. Full source code is available for download at GitHub. The engine contains shader source code converter that allows shaders authored in HLSL to be translated to GLSL.

The first version of the engine was D3D11-style API that had back-ends in OpenGL and OpenGLES. The new version (2.0.alpha) is designed to take advantages of the next-generation APIs. DE2.0 is a mix of D3D11 and D3D12 style APIs. It attempts to do heavy lifting of resource liftime managment, state tracking, synchronization and other low-level tasks, but has lower CPU overhead through the use of D3D12 features.

In DE2.0, all coarse-grain state objects such as Depth Stencil State, Blend State and Rasterizer State as well as shader and other states are combined into single Pipeline State object. The following example illustrates creation of a Pipeline Sate object in Diligent Engine 2.0:


PipelineStateDesc PSODesc;

PSODesc.GraphicsPipeline.NumRenderTargets = 1;
PSODesc.GraphicsPipeline.RTVFormats[0] = TEX_FORMAT_RGBA8_UNORM_SRGB;

// Initialize depth-stencil state
DepthStencilStateDesc &DepthStencilDesc = PSODesc.GraphicsPipeline.DepthStencilDesc;
DepthStencilDesc.DepthEnable = False;
DepthStencilDesc.DepthWriteEnable = False;

// Initialize blend state
BlendStateDesc &BSDesc = PSODesc.GraphicsPipeline.BlendDesc;
BSDesc.IndependentBlendEnable = False;
RenderTargetBlendDesc &RT0 = BSDesc.RenderTargets[0];
RT0.BlendEnable = True;
RT0.RenderTargetWriteMask = COLOR_MASK_ALL;
RT0.SrcBlend = BLEND_FACTOR_SRC_ALPHA;
RT0.DestBlend = BLEND_FACTOR_INV_SRC_ALPHA;
RT0.BlendOp = BLEND_OPERATION_ADD;
RT0.SrcBlendAlpha = BLEND_FACTOR_SRC_ALPHA;
RT0.DestBlendAlpha = BLEND_FACTOR_INV_SRC_ALPHA;
RT0.BlendOpAlpha = BLEND_OPERATION_ADD;

// Initialize rasterizer state
RasterizerStateDesc &RasterizerDesc = PSODesc.GraphicsPipeline.RasterizerDesc;
RasterizerDesc.FillMode = FILL_MODE_SOLID;
RasterizerDesc.CullMode = CULL_MODE_NONE;
RasterizerDesc.FrontCounterClockwise = True;
RasterizerDesc.ScissorEnable = True;
RasterizerDesc.AntialiasedLineEnable = False;


PSODesc.GraphicsPipeline.PrimitiveTopologyType = PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
PSODesc.GraphicsPipeline.pVS = pVertexShader;
PSODesc.GraphicsPipeline.pPS = pPixelShader;
LayoutElement TextLayoutElems[] = 
{
    LayoutElement( 0, 0, 3, VT_FLOAT32, False ),
    LayoutElement( 1, 0, 4, VT_UINT8, True ),
    LayoutElement( 2, 0, 2, VT_FLOAT32, False ),
};
InputLayoutDesc &Layout = PSODesc.GraphicsPipeline.InputLayout;
Layout.LayoutElements = TextLayoutElems;
Layout.NumElements = _countof( TextLayoutElems );

PSODesc.Name = "Example Pipeline State";
m_pDevice->CreatePipelineState(PSODesc, &m_pPSO);


Once PSO is created, it can be bound to the device context with SetPipelineState()  method:


m_pContext->SetPipelineState(m_pPSO);


One of the advantages of the Diligent Engine is an efficient resource binding model. In Diligent Engine 1.0, resources were bound directly to shaders, which is the old-API style. To better fit next-generation APIs, Diligent Engine 2.0 implements a new binding model (more on this in the next post). The model introduces Shader Resource Binding object and classifies shader variables into one of the three categories:

  • Static variables are constant across all shader instances. They must be set once directly through IShader::BindResources()  or through the shader variable interface queried from the shader. 

  • Mutable variables are constant across shader resource bindings instance. They must be set once through IShaderResourceBinding::BindResources()  or through the shader variable interface queried from the resource binding. Mutable variables cannot be set through IShader  interface.

  • Dynamic variables can be set multiple times for every instance of shader resource binding. They cannot be set through IShader interface.

Shader Resource Binding objects are created by the Pipeline State:


m_pPSO->CreateShaderResourceBinding(&m_pSRB);


To set resources within a shader resource binding, IShaderResourceBinding::BindResources() method can be used in the same way it was used in Diligent Engine 1.0. Alternatively, specific variable can be queried and initialized:


m_pSRB->GetVariable(SHADER_TYPE_PIXEL, "Tex")->Set(m_pTextureSRV);


In Diligent Engine 2.0, resources are bound explicitly to the graphics pipeline by the CommitShaderResources()  method of the device context. The method takes shader resource binding as an argument:


m_pContext->CommitShaderResources(m_pSRB);

Check out atmospheric light scattering sample for the example of the engine usage.

 

Find Diligent Engine on the Web, follow us on Twitter, and Facebook.

Read more about:

Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like