Generate and load binary shader objects

This commit is contained in:
Sascha Willems 2023-04-23 20:22:35 +02:00
parent eb904cf1be
commit 217e7cf6d2
6 changed files with 150 additions and 27 deletions

View file

@ -453,7 +453,7 @@ Basic sample showing how to use descriptor buffers to replace descriptor sets.
#### [Shader objects (VK_EXT_shader_object)](./examples/shaderobjects/)<br/> #### [Shader objects (VK_EXT_shader_object)](./examples/shaderobjects/)<br/>
Basic sample showing how to use shader objects that can be used to replace pipeline state objects. Instead of baking all state in a PSO, shaders are explicitly loaded and bound as separate objects and state is set using dynamic state extensions. Basic sample showing how to use shader objects that can be used to replace pipeline state objects. Instead of baking all state in a PSO, shaders are explicitly loaded and bound as separate objects and state is set using dynamic state extensions. The sample also stores binary shader objets and loads them on consecutive runs.
### Misc ### Misc

View file

@ -0,0 +1,26 @@
#version 450
layout (binding = 1) uniform sampler2D samplerColorMap;
layout (location = 0) in vec3 inNormal;
layout (location = 1) in vec3 inColor;
layout (location = 2) in vec3 inViewVec;
layout (location = 3) in vec3 inLightVec;
layout (location = 0) out vec4 outFragColor;
void main()
{
// Desaturate color
vec3 color = vec3(mix(inColor, vec3(dot(vec3(0.2126,0.7152,0.0722), inColor)), 0.65));
// High ambient colors because mesh materials are pretty dark
vec3 ambient = color * vec3(1.0);
vec3 N = normalize(inNormal);
vec3 L = normalize(inLightVec);
vec3 V = normalize(inViewVec);
vec3 R = reflect(-L, N);
vec3 diffuse = max(dot(N, L), 0.0) * color;
vec3 specular = pow(max(dot(R, V), 0.0), 32.0) * vec3(0.35);
outFragColor = vec4(ambient + diffuse * 1.75 + specular, 1.0);
}

Binary file not shown.

View file

@ -0,0 +1,30 @@
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec3 inColor;
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
vec4 lightPos;
} ubo;
layout (location = 0) out vec3 outNormal;
layout (location = 1) out vec3 outColor;
layout (location = 2) out vec3 outViewVec;
layout (location = 3) out vec3 outLightVec;
void main()
{
outNormal = inNormal;
outColor = inColor;
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
vec4 pos = ubo.model * vec4(inPos, 1.0);
outNormal = mat3(ubo.model) * inNormal;
vec3 lPos = mat3(ubo.model) * ubo.lightPos.xyz;
outLightVec = lPos - pos.xyz;
outViewVec = -pos.xyz;
}

Binary file not shown.

View file

@ -34,6 +34,7 @@ public:
PFN_vkCreateShadersEXT vkCreateShadersEXT; PFN_vkCreateShadersEXT vkCreateShadersEXT;
PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT; PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT;
PFN_vkGetShaderBinaryDataEXT vkGetShaderBinaryDataEXT;
// With VK_EXT_shader_object pipeline state must be set at command buffer creation using these functions // With VK_EXT_shader_object pipeline state must be set at command buffer creation using these functions
// VK_EXT_dynamic_state // VK_EXT_dynamic_state
@ -109,6 +110,7 @@ public:
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
} }
// Loads a binary shader file
void _loadShader(std::string filename, char* &code, size_t &size) { void _loadShader(std::string filename, char* &code, size_t &size) {
// @todo: Android // @todo: Android
std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate); std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate);
@ -134,33 +136,97 @@ public:
VkShaderCreateInfoEXT shaderCreateInfos[2]{}; VkShaderCreateInfoEXT shaderCreateInfos[2]{};
// VS // With VK_EXT_shader_object we can generate an implementation dependent binary file that's faster to load
_loadShader(getShadersPath() + "pipelines/phong.vert.spv", shaderCodes[0], shaderCodeSizes[0]); // So we check if the binray files exist and if we can load it instead of the SPIR-V
shaderCreateInfos[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; bool binaryShadersLoaded = false;
shaderCreateInfos[0].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT;
shaderCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderCreateInfos[0].nextStage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderCreateInfos[0].codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT;
shaderCreateInfos[0].pCode = shaderCodes[0];
shaderCreateInfos[0].codeSize = shaderCodeSizes[0];
shaderCreateInfos[0].pName = "main";
shaderCreateInfos[0].setLayoutCount = 1;
shaderCreateInfos[0].pSetLayouts = &descriptorSetLayout;
// FS if (vks::tools::fileExists(getShadersPath() + "shaderobjects/phong.vert.bin") && vks::tools::fileExists(getShadersPath() + "shaderobjects/phong.frag.bin")) {
_loadShader(getShadersPath() + "pipelines/phong.frag.spv", shaderCodes[1], shaderCodeSizes[1]); // VS
shaderCreateInfos[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; _loadShader(getShadersPath() + "shaderobjects/phong.vert.bin", shaderCodes[0], shaderCodeSizes[0]);
shaderCreateInfos[1].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT; shaderCreateInfos[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
shaderCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shaderCreateInfos[0].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT;
shaderCreateInfos[1].nextStage = 0; shaderCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderCreateInfos[1].codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT; shaderCreateInfos[0].nextStage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderCreateInfos[1].pCode = shaderCodes[1]; shaderCreateInfos[0].codeType = VK_SHADER_CODE_TYPE_BINARY_EXT;
shaderCreateInfos[1].codeSize = shaderCodeSizes[1]; shaderCreateInfos[0].pCode = shaderCodes[0];
shaderCreateInfos[1].pName = "main"; shaderCreateInfos[0].codeSize = shaderCodeSizes[0];
shaderCreateInfos[1].setLayoutCount = 1; shaderCreateInfos[0].pName = "main";
shaderCreateInfos[1].pSetLayouts = &descriptorSetLayout; shaderCreateInfos[0].setLayoutCount = 1;
shaderCreateInfos[0].pSetLayouts = &descriptorSetLayout;
vkCreateShadersEXT(device, 2, shaderCreateInfos, nullptr, shaders); // FS
_loadShader(getShadersPath() + "shaderobjects/phong.frag.bin", shaderCodes[1], shaderCodeSizes[1]);
shaderCreateInfos[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
shaderCreateInfos[1].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT;
shaderCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderCreateInfos[1].nextStage = 0;
shaderCreateInfos[1].codeType = VK_SHADER_CODE_TYPE_BINARY_EXT;
shaderCreateInfos[1].pCode = shaderCodes[1];
shaderCreateInfos[1].codeSize = shaderCodeSizes[1];
shaderCreateInfos[1].pName = "main";
shaderCreateInfos[1].setLayoutCount = 1;
shaderCreateInfos[1].pSetLayouts = &descriptorSetLayout;
VkResult result = vkCreateShadersEXT(device, 2, shaderCreateInfos, nullptr, shaders);
// If the function returns e.g. VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT, the binary file is no longer (or not at all) compatible with the current implementation
if (result == VK_SUCCESS) {
binaryShadersLoaded = true;
} else {
std::cout << "Could not load binary shader files (" << vks::tools::errorString(result) << ", loading SPIR - V instead\n";
}
}
// If the binary files weren't present, or we could not load them, we load from SPIR-V
if (!binaryShadersLoaded) {
// VS
_loadShader(getShadersPath() + "shaderobjects/phong.vert.spv", shaderCodes[0], shaderCodeSizes[0]);
shaderCreateInfos[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
shaderCreateInfos[0].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT;
shaderCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderCreateInfos[0].nextStage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderCreateInfos[0].codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT;
shaderCreateInfos[0].pCode = shaderCodes[0];
shaderCreateInfos[0].codeSize = shaderCodeSizes[0];
shaderCreateInfos[0].pName = "main";
shaderCreateInfos[0].setLayoutCount = 1;
shaderCreateInfos[0].pSetLayouts = &descriptorSetLayout;
// FS
_loadShader(getShadersPath() + "shaderobjects/phong.frag.spv", shaderCodes[1], shaderCodeSizes[1]);
shaderCreateInfos[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT;
shaderCreateInfos[1].flags = VK_SHADER_CREATE_LINK_STAGE_BIT_EXT;
shaderCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderCreateInfos[1].nextStage = 0;
shaderCreateInfos[1].codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT;
shaderCreateInfos[1].pCode = shaderCodes[1];
shaderCreateInfos[1].codeSize = shaderCodeSizes[1];
shaderCreateInfos[1].pName = "main";
shaderCreateInfos[1].setLayoutCount = 1;
shaderCreateInfos[1].pSetLayouts = &descriptorSetLayout;
VK_CHECK_RESULT(vkCreateShadersEXT(device, 2, shaderCreateInfos, nullptr, shaders));
// Store the binary shader files so we can try to load them at the next start
size_t dataSize{ 0 };
char* data{ nullptr };
std::fstream is;
vkGetShaderBinaryDataEXT(device, shaders[0], &dataSize, nullptr);
data = new char[dataSize];
vkGetShaderBinaryDataEXT(device, shaders[0], &dataSize, data);
is.open(getShadersPath() + "shaderobjects/phong.vert.bin", std::ios::binary | std::ios::out);
is.write(data, dataSize);
is.close();
delete[] data;
vkGetShaderBinaryDataEXT(device, shaders[1], &dataSize, nullptr);
data = new char[dataSize];
vkGetShaderBinaryDataEXT(device, shaders[1], &dataSize, data);
is.open(getShadersPath() + "shaderobjects/phong.frag.bin", std::ios::binary | std::ios::out);
is.write(data, dataSize);
is.close();
delete[] data;
}
} }
void buildCommandBuffers() void buildCommandBuffers()
@ -260,6 +326,7 @@ public:
vkCreateShadersEXT = reinterpret_cast<PFN_vkCreateShadersEXT>(vkGetDeviceProcAddr(device, "vkCreateShadersEXT")); vkCreateShadersEXT = reinterpret_cast<PFN_vkCreateShadersEXT>(vkGetDeviceProcAddr(device, "vkCreateShadersEXT"));
vkCmdBindShadersEXT = reinterpret_cast<PFN_vkCmdBindShadersEXT>(vkGetDeviceProcAddr(device, "vkCmdBindShadersEXT")); vkCmdBindShadersEXT = reinterpret_cast<PFN_vkCmdBindShadersEXT>(vkGetDeviceProcAddr(device, "vkCmdBindShadersEXT"));
vkGetShaderBinaryDataEXT = reinterpret_cast<PFN_vkGetShaderBinaryDataEXT>(vkGetDeviceProcAddr(device, "vkGetShaderBinaryDataEXT"));
vkCmdSetViewportWithCountEXT = reinterpret_cast<PFN_vkCmdSetViewportWithCountEXT>(vkGetDeviceProcAddr(device, "vkCmdSetViewportWithCountEXT"));; vkCmdSetViewportWithCountEXT = reinterpret_cast<PFN_vkCmdSetViewportWithCountEXT>(vkGetDeviceProcAddr(device, "vkCmdSetViewportWithCountEXT"));;
vkCmdSetScissorWithCountEXT = reinterpret_cast<PFN_vkCmdSetScissorWithCountEXT>(vkGetDeviceProcAddr(device, "vkCmdSetScissorWithCountEXT")); vkCmdSetScissorWithCountEXT = reinterpret_cast<PFN_vkCmdSetScissorWithCountEXT>(vkGetDeviceProcAddr(device, "vkCmdSetScissorWithCountEXT"));