diff --git a/README.md b/README.md index db1d7b61..fbd0449e 100644 --- a/README.md +++ b/README.md @@ -453,7 +453,7 @@ Basic sample showing how to use descriptor buffers to replace descriptor sets. #### [Shader objects (VK_EXT_shader_object)](./examples/shaderobjects/)
-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 diff --git a/data/shaders/glsl/shaderobjects/phong.frag b/data/shaders/glsl/shaderobjects/phong.frag new file mode 100644 index 00000000..b207e798 --- /dev/null +++ b/data/shaders/glsl/shaderobjects/phong.frag @@ -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); +} \ No newline at end of file diff --git a/data/shaders/glsl/shaderobjects/phong.frag.spv b/data/shaders/glsl/shaderobjects/phong.frag.spv new file mode 100644 index 00000000..d9fbe6c4 Binary files /dev/null and b/data/shaders/glsl/shaderobjects/phong.frag.spv differ diff --git a/data/shaders/glsl/shaderobjects/phong.vert b/data/shaders/glsl/shaderobjects/phong.vert new file mode 100644 index 00000000..c0069072 --- /dev/null +++ b/data/shaders/glsl/shaderobjects/phong.vert @@ -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; +} \ No newline at end of file diff --git a/data/shaders/glsl/shaderobjects/phong.vert.spv b/data/shaders/glsl/shaderobjects/phong.vert.spv new file mode 100644 index 00000000..b22327fb Binary files /dev/null and b/data/shaders/glsl/shaderobjects/phong.vert.spv differ diff --git a/examples/shaderobjects/shaderobjects.cpp b/examples/shaderobjects/shaderobjects.cpp index 0294a57d..9ec9dbe6 100644 --- a/examples/shaderobjects/shaderobjects.cpp +++ b/examples/shaderobjects/shaderobjects.cpp @@ -33,7 +33,8 @@ public: VkPhysicalDeviceShaderObjectFeaturesEXT enabledDeviceShaderObjectFeaturesEXT{}; 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 // VK_EXT_dynamic_state @@ -109,6 +110,7 @@ public: vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } + // Loads a binary shader file void _loadShader(std::string filename, char* &code, size_t &size) { // @todo: Android std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate); @@ -134,33 +136,97 @@ public: VkShaderCreateInfoEXT shaderCreateInfos[2]{}; - // VS - _loadShader(getShadersPath() + "pipelines/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; + // With VK_EXT_shader_object we can generate an implementation dependent binary file that's faster to load + // So we check if the binray files exist and if we can load it instead of the SPIR-V + bool binaryShadersLoaded = false; - // FS - _loadShader(getShadersPath() + "pipelines/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; + if (vks::tools::fileExists(getShadersPath() + "shaderobjects/phong.vert.bin") && vks::tools::fileExists(getShadersPath() + "shaderobjects/phong.frag.bin")) { + // VS + _loadShader(getShadersPath() + "shaderobjects/phong.vert.bin", 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_BINARY_EXT; + shaderCreateInfos[0].pCode = shaderCodes[0]; + shaderCreateInfos[0].codeSize = shaderCodeSizes[0]; + shaderCreateInfos[0].pName = "main"; + 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() @@ -260,6 +326,7 @@ public: vkCreateShadersEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateShadersEXT")); vkCmdBindShadersEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBindShadersEXT")); + vkGetShaderBinaryDataEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetShaderBinaryDataEXT")); vkCmdSetViewportWithCountEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetViewportWithCountEXT"));; vkCmdSetScissorWithCountEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdSetScissorWithCountEXT"));