sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English

Show/Hide search bar
black cat logo variable logo
[19 Jan 2013]

Compilation of HLSL shaders in DirectX 11

Shaders are programs for GPUs that allow to reprogram different parts of graphical pipeline. Before introduction of shaders (programable pipeline), configuration of graphical pileline was done by changing states of rendering pipeline (fixed pipeline). HLSL shader substitutes part of graphical pipeline. There are following shader stages in DirectX: vertex shader, geometry shader, tesselation shaders and fragment shaders. Vertex and fragment shaders are obligatory for valid rendering pipeline. Other shader stages are optional.
Smart COM pointers for DirectX objects
DirectX uses COM objects. To delete COM object you have to manually call Release() method of the COM object. You may forget to delete such object properly and object will stay in memory (memory leak). To automatically delete simple C++ objects you can use smart pointers, for example, from standard std library. But standard smart pointers don't call Release() in destructor. This can be fixed by using smart COM pointers. They provide same functionality as standard smart pointers. To retrieve raw pointer call ptr.GetInterfacePtr().
In Visual Studio add comsuppw.lib to project dependencies and include "comdef.h". Also declare types of pointers, which you want to use. Example for smart pointers:
// Includes
#include "d3d11.h"
#include "D3D11SDKLayers.h"
#include "d3dcompiler.h"
#include "comdef.h"

// Declare types of smart pointers for DirectX Com Objects
_COM_SMARTPTR_TYPEDEF(ID3D11Device, __uuidof(ID3D11Device));
_COM_SMARTPTR_TYPEDEF(ID3D11DeviceContext, __uuidof(ID3D11DeviceContext));
_COM_SMARTPTR_TYPEDEF(ID3D11VertexShader, __uuidof(ID3D11VertexShader));
_COM_SMARTPTR_TYPEDEF(ID3D11PixelShader, __uuidof(ID3D11PixelShader));
_COM_SMARTPTR_TYPEDEF(ID3D11InputLayout, __uuidof(ID3D11InputLayout));
_COM_SMARTPTR_TYPEDEF(ID3DBlob, __uuidof(ID3DBlob));
_COM_SMARTPTR_TYPEDEF(ID3D11ShaderReflection, IID_ID3D11ShaderReflection);
Compilation of HLSL shaders in Visual Studio
For all shader files set .hlsl extensjion. Add shaders to Visual Studio project. For each shader you can specify compilation parameters by right click on shader -> Properties. For example, you can change shader model, add debug info to shaders, choose type of shader, etc. Then build the project, Visual Studio will compile shaders and will save precompiled shaders into one of the project's folder. Precompiled shader files by default have .cso extension. After that you can can load precompiled shaders into your application, create DirectX shaders from them and use shaders for rendering.
You can compile shaders with fxc shader compiler instead of Visual Studio. fxc lighting.vertex /E VertexShaderEntry /Fo lighting_vertex.cso
fxc lighting.pixel /E PixelShaderEntry /Fo lighting_pixel.cso
Don't forget to add d3d11.lib, d3dcompiler.lib, dxguid.lib to project dependencies.
PS. Add more checks for errors in following examples if you want to use this code
Load vertex and pixel shaders:
// declare variables to store shaders and related data
ID3D11VertexShaderPtr vertexShader(nullptr);
ID3D11PixelShaderPtr pixelShader(nullptr);
ID3D11InputLayoutPtr vertexLayout(nullptr);
std::vector<D3D11_INPUT_ELEMENT_DESC> inputLayoutArray;

// specify pathes to .cso files
void buildShaderProgram(ID3D11Device * d3dDevice,
                  const std::string & vertexShaderPath,
                  const std::string & pixelShaderPath)
{
   HRESULT hr = S_OK;

   // load precompiled vertex shader from file
   ID3DBlobPtr vertexBlob;
   hr = D3DReadFileToBlob(vertexShaderPath.c_str(), &vertexBlob);
   if(FAILED(hr))
   {
      // error during loading of precompiled shader
      return false;
   }

   // create vertex shader from precompiled shader
   hr = d3dDevice->CreateVertexShader(
         vertexBlob->GetBufferPointer(),
         vertexBlob->GetBufferSize(),
         nullptr, &m_impl->vertexShader);
   if(FAILED(hr))
   {
      // error during cretion of vertex shader
      return false;
   }

   // load precompiled pixel shader from file
   ID3DBlobPtr pixelBlob;
   hr = D3DReadFileToBlob(pixelShaderPath.c_str(), &pixelBlob);
   if(FAILED(hr))
   {
      // error during loading of precompiled shader
      return false;
   }

   // create pixel shader from precompiled shader
   hr = d3dDevice->CreatePixelShader(
         pixelBlob->GetBufferPointer(),
         pixelBlob->GetBufferSize(),
         nullptr, &m_impl->pixelShader);
   if(FAILED(hr))
   {
      // error during creation of pixel shader
      return false;
   }

   // get description of input layout (attributes in OpenGL)
   hr = enumInputLayout(d3dDevice, vertexBlob.GetInterfacePtr());
   if(FAILED(hr))
   {
      // impossible to create input layout
      return false;
   }

   // get description of constant buffers, textures and samplers (uniforms in OpenGL)
   enumConstantBuffers(d3dDevice, vertexBlob.GetInterfacePtr());
   enumConstantBuffers(d3dDevice, pixelBlob.GetInterfacePtr());

   return true;
}
Get description for input parameters (attributes) for vertex shader and create Input Layout:
HRESULT enumInputLayout(ID3D11Device * d3dDevice, ID3DBlob * VSBlob)
{
   HRESULT hr = S_OK;

   // Get description from precompiled shader
   ID3D11ShaderReflectionPtr vertReflect;
   D3DReflect(
         VSBlob->GetBufferPointer(),
         VSBlob->GetBufferSize(),
         IID_ID3D11ShaderReflection,
         (void**) &vertReflect
      );

   D3D11_SHADER_DESC descVertex;
   vertReflect->GetDesc(&descVertex);

   // save description of input parameters (attributes of vertex shader)
   inputLayoutArray.clear();
   uint32 byteOffset = 0;
   D3D11_SIGNATURE_PARAMETER_DESC input_desc;
   for (uint i = 0; i    {
      // get description of input parameter
      vertReflect->GetInputParameterDesc(i, &input_desc);
      
      // fill element description to create input layout later
      D3D11_INPUT_ELEMENT_DESC ie;
      ie.SemanticName = input_desc.SemanticName;
      ie.SemanticIndex = input_desc.SemanticIndex;
      ie.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
      ie.InputSlot = i;
      ie.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
      ie.InstanceDataStepRate = 0;
      ie.AlignedByteOffset = byteOffset;
      
      // determine correct format of input parameter and offset
      if (input_desc.Mask == 1)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32_FLOAT;
         }
         byteOffset += 4;
      }
      else if (input_desc.Mask <= 3)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_FLOAT;
         }
         byteOffset += 8;
      }
      else if (input_desc.Mask <= 7)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_FLOAT;
         }
         byteOffset += 12;
      }
      else if (input_desc.Mask <= 15)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
         }
         byteOffset += 16;
      }
      
      inputLayoutArray.push_back(ie);

      // you can save input_desc here (if needed)
   }
   
   // create input layout from previosly created description
   uint numElements = inputLayoutArray.size();
   hr = d3dDevice->CreateInputLayout(
         inputLayoutArray.data(),
         numElements,
         VSBlob->GetBufferPointer(),
         VSBlob->GetBufferSize(),
         &vertexLayout
      );

   if(FAILED(hr))
   {
      // impossible to create input layout
      return hr;
   }
   
   return S_OK;
}
Get description of constant buffers (uniforms OpenGL)
HRESULT initializeConstantBuffers(ID3D11Device * d3dDevice, ID3DBlob * blob, bool isVS)
{
   // get description of precompiled shader
   ID3D11ShaderReflectionPtr pReflector = NULL;
   D3DReflect(
         blob->GetBufferPointer(),
         blob->GetBufferSize(),
         IID_ID3D11ShaderReflection,
         (void**) &pReflector
      );

   D3D11_SHADER_DESC desc;
   pReflector->GetDesc( &desc );

   // get description of each constant buffer in the shader
   for (uint i = 0; i < desc.ConstantBuffers; i++)
   {
      // get description of constant buffer
      D3D11_SHADER_BUFFER_DESC shaderBuffer;
      ID3D11ShaderReflectionConstantBuffer * pConstBuffer =
                  pReflector->GetConstantBufferByIndex(i);
      pConstBuffer->GetDesc(&shaderBuffer);
      
      // you can save shaderBuffer here (if needed)

      // get description of each variable in constant buffer
      for (uint j = 0; j < shaderBuffer.Variables; j++)
      {
         // description of variable
         ID3D11ShaderReflectionVariable * pVariable =
                  pConstBuffer->GetVariableByIndex(j);
         D3D11_SHADER_VARIABLE_DESC varDesc;
         pVariable->GetDesc(&varDesc);

         // type of variable
         D3D11_SHADER_TYPE_DESC varType;
         ID3D11ShaderReflectionType * pType = pVariable->GetType();
         pType->GetDesc(&varType);

         // you can save varType and varDesc here (if needed)
      }

      // get binding description for constant buffer
      D3D11_SHADER_INPUT_BIND_DESC bindingDesc;
      hr = pReflector->GetResourceBindingDescByName(shaderBuffer.Name, &bindingDesc);

      // PS. You can save descriptions of constant buffers in following struct
      //struct ConstantBufferDescription
      //{
      // std::pair variables;
      // D3D11_SHADER_INPUT_BIND_DESC bindingDescription;
      //}

   }

   ///////////////////////////////////////////////////

   // Save description of textures and samplers
   for(uint i=0; i    {
      D3D11_SHADER_INPUT_BIND_DESC inputBindDesc;
      pReflector->GetResourceBindingDesc(i, &inputBindDesc);
   
      // save description of textures and samplers here
   }

   return S_OK;
}
Set active shader:
// Set vertex shader
d3dContext->VSSetShader(vertexShader.GetInterfacePtr(), nullptr, 0);
// Set fragment shader
d3dContext->PSSetShader(pixelShader.GetInterfacePtr(), nullptr, 0);



Sun and Black Cat- Igor Dykhta (igor dykhta email) 2007-2014