Vulkan-Sample-Note-Specialization-Constants - CYF Blog

Vulkan-Sample-Note-Specialization-Constants

Vulkan Sample Note: Specialization constants

The sample is from Sascha Williems

TL;DR

  • Create specialization constants for control variables in shader code
  • Specialization constants could be seen as per shader or per pipeline constants

Notes

preparePipelines

Specialization info is assigned is part of the shader stage (modul) and must be set after creating the module and before creating the pipeline

void preparePipelines()
{
    ...
    // Prepare specialization data

    // Host data to take specialization constants from
    struct SpecializationData {
    // Sets the lighting model used in the fragment "uber" shader
    	uint32_t lightingModel;
    	// Parameter for the toon shading part of the fragment shader
    	float toonDesaturationFactor = 0.5f;
    } specializationData;
    
    // Each shader constant of a shader stage corresponds to one map entry
    std::array<VkSpecializationMapEntry, 2> specializationMapEntries;
    // Shader bindings based on specialization constants are marked by the new "constant_id" layout qualifier:
    //	layout (constant_id = 0) const int LIGHTING_MODEL = 0;
    //	layout (constant_id = 1) const float PARAM_TOON_DESATURATION = 0.0f;
    
    // Map entry for the lighting model to be used by the fragment shader
    specializationMapEntries[0].constantID = 0;
    specializationMapEntries[0].size = sizeof(specializationData.lightingModel);
    specializationMapEntries[0].offset = 0;
    
    // Map entry for the toon shader parameter
    specializationMapEntries[1].constantID = 1;
    specializationMapEntries[1].size = sizeof(specializationData.toonDesaturationFactor);
    specializationMapEntries[1].offset = offsetof(SpecializationData, toonDesaturationFactor);
    
    // Prepare specialization info block for the shader stage
    VkSpecializationInfo specializationInfo{};
    specializationInfo.dataSize = sizeof(specializationData);
    specializationInfo.mapEntryCount = static_cast<uint32_t>(specializationMapEntries.size());
    specializationInfo.pMapEntries = specializationMapEntries.data();
    specializationInfo.pData = &specializationData; 

    // Create pipelines
    // All pipelines will use the same "uber" shader and specialization constants to change branching and parameters of that shader
    shaderStages[0] = loadShader(getShadersPath() + "specializationconstants/uber.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
    shaderStages[1] = loadShader(getShadersPath() + "specializationconstants/uber.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
    // Specialization info is assigned is part of the shader stage (modul) and must be set after creating the module and before creating the pipeline
    shaderStages[1].pSpecializationInfo = &specializationInfo;

    // Solid phong shading
    specializationData.lightingModel = 0;
    VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.phong));

    // Phong and textured
    specializationData.lightingModel = 1;
    VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.toon));
    
    // Textured discard
    specializationData.lightingModel = 2;
    VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.textured));
}

fragment shader

#version 450
...
// We use this constant to control the flow of the shader depending on the 
// lighting model selected at pipeline creation time
layout (constant_id = 0) const int LIGHTING_MODEL = 0;
// Parameter for the toon shading part of the shader
layout (constant_id = 1) const float PARAM_TOON_DESATURATION = 0.0f; 

void main()
{
    switch (LIGHTING_MODEL) {
        case 0: // Phong		
            {
                ...
            }
        case 1: // Toon		
            {
                ...
            }
        case 2: // Textured
            {
               ... 
            }
    }
}