I am building a C++ application that uses DirectX 12 as graphics api.
I want to define macros(for eg #define A_MACRO)
when compiling shader programs.
I looked on the internet but I didn't found where to put it. In terms of code I have this:
Root signature creation:
void Effect::createRootSignature(const D3D12_ROOT_PARAMETER* rootParams,
UINT nbParams,
const D3D12_STATIC_SAMPLER_DESC* samplers,
UINT numSamplers)
{
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(nbParams,
rootParams,
numSamplers,
samplers,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS);
ID3DBlob* errorBuff;
ID3DBlob* signature;
HRESULT hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &errorBuff);
if (FAILED(hr))
{
TRACE((char*)errorBuff->GetBufferPointer());
assert(false);
return;
}
hr = D3D12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature));
assert(SUCCEEDED(hr));
}
Shader deserialization:
ID3DBlob* Effect::readShader(const std::string& shaderName)
{
std::wstring shaderPath(shaderName.begin(), shaderName.end());
shaderPath = SHADER_PATH(shaderPath + L".cso");
ID3DBlob* shader = nullptr;
HRESULT hr = D3DReadFileToBlob(shaderPath.c_str(), &shader);
if (FAILED(hr))
{
return nullptr;
}
return shader;
}
PSO complilation:
void Effect::compilePipeline(PipelineStatePtr& pipelineStateDst,
ID3DBlob* vertexShader,
ID3DBlob* pixelShader,
const D3D12_INPUT_ELEMENT_DESC* inputLayoutElementDesc,
const UINT inputLayoutNumElements,
const D3D12_BLEND_DESC& blendDesc,
const D3D12_DEPTH_STENCIL_DESC& depthStencilDesc,
const D3D12_RASTERIZER_DESC& rasterizerDesc,
const std::vector<DXGI_FORMAT>& renderTargetFormats,
const D3D12_PRIMITIVE_TOPOLOGY_TYPE topologyType)
{
// Fill out a shader bytecode structure, which is basically just a pointer
// to the shader bytecode and the size of the shader bytecode.
D3D12_SHADER_BYTECODE vertexShaderBytecode = {};
vertexShaderBytecode.BytecodeLength = vertexShader->GetBufferSize();
vertexShaderBytecode.pShaderBytecode = vertexShader->GetBufferPointer();
// Fill out a pixel shader bytecode structure.
D3D12_SHADER_BYTECODE pixelShaderBytecode = {};
pixelShaderBytecode.BytecodeLength = pixelShader->GetBufferSize();
pixelShaderBytecode.pShaderBytecode = pixelShader->GetBufferPointer();
// Fill out an input layout description structure
D3D12_INPUT_LAYOUT_DESC inputLayoutDesc = {};
inputLayoutDesc.NumElements = inputLayoutNumElements;
inputLayoutDesc.pInputElementDescs = inputLayoutElementDesc;
// Fill out a PSO description
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = inputLayoutDesc;
psoDesc.pRootSignature = m_rootSignature;
psoDesc.VS = vertexShaderBytecode;
psoDesc.PS = pixelShaderBytecode;
psoDesc.PrimitiveTopologyType = topologyType;
assert(renderTargetFormats.size() <= 8);
const UINT numRenderTargets = std::min((UINT)renderTargetFormats.size(), 8u);
for (nbUint32 i = 0; i < numRenderTargets; ++i)
psoDesc.RTVFormats[i] = renderTargetFormats[i];
psoDesc.SampleDesc = m_sampleDesc;
psoDesc.SampleMask = 0xffffffff;
psoDesc.RasterizerState = rasterizerDesc;
psoDesc.BlendState = blendDesc;
psoDesc.NumRenderTargets = numRenderTargets;
psoDesc.DepthStencilState = depthStencilDesc;
psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT7;
// Now create the PSO
HRESULT hr = D3D12Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineStateDst));
assert(SUCCEEDED(hr));
}
Macros can only be used during shader compilation. Once built, the shader blob is immutable and you cannot change it. To build variants of the same shader controlled by macros, you must invoke the shader compiler multiple times and create PSOs from each distinct shader blob.
For an example, see DirectX Tool Kit for DX12 and the CompileShaders.cmd
script.