Specialzation Constants

143 views
Skip to first unread message

Robert Osfield

unread,
Mar 27, 2020, 12:53:29 PM3/27/20
to vsg-users : VulkanSceneGraph Developer Discussion Group
Today I refactored the vsg::ShaderStage (wrapper of VkShaderStage) support for Specialization Constants to make them easier to setup.   What are they?  Have a look at the LunarG docs on them:


They are essentially a means of passing constant int/float scaler values to the shader setup so that the driver can optimize the shaders.  Common usage of Specialization Constants is to use them to save arrays or pass in constant numerical value.  Alas Vulkan/GLSL/SPIR-V doesn't yet support pass vec2/vec3/vec4/mat4 or struct, if you try you'll just errors :report -|

The only examples we currently have of Specialization Constants being used are in the vsgUnity project and vsgExamples/Desckrtop/vsgcompute example.  The

   computeStage->setSpecializationConstants({
        {0, vsg::intValue::create(width)},
        {1, vsg::intValue::create(height)},
        {2, vsg::intValue::create(workgroupSize)}
    });

The maps to GLSL shader:

layout(constant_id = 0) const int WIDTH = 1024;
layout(constant_id = 1) const int HEIGHT = 1024;
layout (local_size_x_id = 2, local_size_y_id = 2, local_size_z = 1 ) in;  // pass in WORKGROUP_SIZE as specialization constant_id=2

Note the shader provides defaults in case the application doesn't provide the SpecializationConstants.  In the above example we are using ints, but you can use vsg::intValue/vsg::uintValue, vsg::floatValue and vsg::doubleValue for int, uint, float and double respectively.  You can also now mix and match the types, so pass int's for dimensions, and floats for scales/offsets etc.

The API for ShaderStage::setSpecializationConstants() takes a map<uint32_t, ref_ptr<vsg::Data>>, the example above uses an C++11 initialize list to set up this map and pass it to he ShaderStage.  The first value/key of the map is the constant_id value used i the GLSL shader, while the value_type is a vsg::Data object, so the API allows you to pass in any Data type, but until Vulkan/GLSL/SPir-V becomes more flexible you'll need to stick to the vsg::Value<T> types.

The new API replaces two set methods and associated data structures that were more Vulkan centric, but as the this particular bit of the Vulkan API is a bit cryptic you also needed to know how it hung together, the new API is simpler and does all the Vulkan specific packing when required for you rather than have you work out value sizes and offsets and packing all the data together.

The downside of this change is existing .vsgb and .vsgt files that contain vsg::ShaderStage will have the old style data structures serialized in them, rather than the newly recreated SerializationConstants map that is now serialized.  This change unfortunately means that when you update to VSG master you'll need to rebuild your .vsgb and .vsgt files.

For this pain though you get an API that is works in a way that is much more of clear mapping between the scene graph level settings and how you use it in GLSL, no longer do you have to worry about packing in intermediate data structures, you can just pass the values you want to use and associated them with appropriate constant_id.  No need to shy away for using what would be otherwise an esoteric feature.

Rainer Gericke

unread,
Aug 24, 2022, 4:29:47 AM8/24/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

I want to use specialization constants to define tiling textures. The tiling should be user defined and my idea is to pass the repeat factors in u and v direction to the shader. In my piece of software it is done by:

auto fragmentShader = vsg::read_cast<vsg::ShaderStage>("vsg/shaders/vsg3d_phong.frag", options);
fragmentShader->specializationConstants = {{0, vsg::floatValue::create(uScale)}, {1, vsg::floatValue::create(vScale)}};

In the fragment shader (modified assimp_phong.frag) I inserted the lines:

layout(constant_id = 0) const float uScale = 1.0;
layout(constant_id = 1) const float vScale = 1.0;

and later (here only one line as example)

diffuseColor *= texture(diffuseMap, vec2(texCoord0.s * uScale, texCoord0.t * vScale));

Good thing so far, it works as expected, but only for the the very first graphic object. Consecutive calls with different repeat factors for other objects keep still the first settings.

I have two questions:
  1. Is this the correct use for specialization constants, or should a uniform buffer be used instead?
  2. Tiling textures can produce weird optical effects, like Moiré patterns or blurring. It can possibly avoided by anisotropic filtering. How can I set this?
Best

Robert Osfield

unread,
Aug 24, 2022, 4:48:54 AM8/24/22
to vsg-...@googlegroups.com
Hi Rainer,

Specialization constants only enable the shaders to embed the constants at time of setting up the vk shader stage, you can't change the vales afterwards.

To pass in values that change you'll need to vertex arrays or uniforms. With vertex arrays you can pass a single value as per instance.

I think there might be a way of querying texture dimensions within the shaders but don't recall specific off the top of my head.

Cheers,
Robert

Rainer Gericke

unread,
Aug 25, 2022, 4:29:30 AM8/25/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Thanks, Robert,

the uniform buffer makes it work.

I also found out to set the sampler settings, maybe it's obvious, but I show the code for this here:
// enable texturing with anisotrpy filtering
auto sampler = vsg::Sampler::create();
sampler->addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // default yet, just an example how to set
sampler->addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler->addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler->anisotropyEnable = VK_TRUE;
sampler->maxAnisotropy = m_maxAnisotropy;
graphicsPipelineConfig->assignTexture(descriptors, "diffuseMap", textureData, sampler);

Best,
Rainer
Reply all
Reply to author
Forward
0 new messages