From 217e7cf6d264b0967d1dca5720b6e1dff9dae3f2 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 23 Apr 2023 20:22:35 +0200 Subject: [PATCH] Generate and load binary shader objects --- README.md | 2 +- data/shaders/glsl/shaderobjects/phong.frag | 26 ++++ .../shaders/glsl/shaderobjects/phong.frag.spv | Bin 0 -> 2144 bytes data/shaders/glsl/shaderobjects/phong.vert | 30 +++++ .../shaders/glsl/shaderobjects/phong.vert.spv | Bin 0 -> 2772 bytes examples/shaderobjects/shaderobjects.cpp | 119 ++++++++++++++---- 6 files changed, 150 insertions(+), 27 deletions(-) create mode 100644 data/shaders/glsl/shaderobjects/phong.frag create mode 100644 data/shaders/glsl/shaderobjects/phong.frag.spv create mode 100644 data/shaders/glsl/shaderobjects/phong.vert create mode 100644 data/shaders/glsl/shaderobjects/phong.vert.spv 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 0000000000000000000000000000000000000000..d9fbe6c48a524758a438cd219f9dd628b031a702 GIT binary patch literal 2144 zcmZ9N%TiN85QaxW5Fv6A1raX^Afg~%xGIteD4+%eQL0!*NbpqULWNK+)rDI(y6`=C z1E0Vbu*%19W0n6mIW4C+sjlh&@7qjI55yVHAjY{?*W}mb>}GcC zptd_UF?kn{HW%}b__Vtg*QgaK<$^MXR!K^7U2;?MRMMdsd{j(YOck8_p#*WU(OB>q z*>)<$N>wYEd_j59GvGeqTgyPeC~w z?7gdP<#+1#VcE7qJNpgFTS5N4H~aWeaj(3+(cRO!8x#tM2YFxbf$+h8e&?{5W4q#- z{CO*fwWVrqufFM!e~oXhv|r3uJ-?dUcgx}VuF9r%c+45VeAwwDn687_BbXk7>2K7Z zI{gwp>|k<|ze`cXrTi__b975@5|@+l04!rZa}yg}?v$IyWkB@^nau!4I4HotIuH4E>M0BJAA9jKy(oI1}4XN*wz! zOb?@&9!B>&Da>be({;FD__6T#_)*&G&I`kt(6>cl&dxm3rzcwJAw2s4b60SdcSX8$ zzcam>`VDEsv#;o9xKEMHA%5iF44vS}59Y44iyp<^Ct$^oLpEV(2ZVY#Z_?v%4&YcrxF5uV0Iq8xPZ({rfam@+R zBHd!J5ySn$Tg+|gB$Hlmrfm< zg9+*Qz>}LdG9@85d&JKBwS3dk>G_wi=QGmbh@n1n31|KfrL!l)v(m|H^&d&c2j1$> zNw@mgsc*JSXsgcwHIMhQAbnnf4}0NFz*+4j>CA*-ddGh4EYGCFtw=tF=X)-lIBXX8 zLOQw4Co3I4c$?`}>F|8Utx1RDY{tC|xdyH4>i?o&og*H9?EL-ZJk063a#1^QOFJcuP$d@mG|Inu$G7V+IRk*UY zd_C)Dc^7nZ;qSc)JnQxsvzMuUVY6>+Go5VhR<_<>?yjT*n^V!Y(di6qP44RbCFR4M z_iru`GgiprYrXuBbfstkv{~Txu$n(hy?^*xyOTZty-&x8MZP1P8^7hqjbg@Gw=`Po zD{O1HYWo(sGl**zyI^+nrdvu^@M#vi0b9z_7edJ_aqeOqmek#Yy8WrUgSzg%)$MhT zoX%e|P72I;G0(t^5p#}#DU10=X0e#B%-tBwuL-8Vvy~>x<9`^BJlPG;kx4)6*5lNT zn}#Eo*t@tgzqR#y4cm{o#GHk@I0{RlPn2q*`x~gw(%*x+chvturJHXrSv@DPYFFNm zG{)J258!Wa>e^fATPj)na;3Y!ux~|~ zhy`wq57C{W`?7|*`x4XsQ6(D_HCo8lj9Iw9=)rq^1}Dku>&BlzvS0g9pW=+SV~>|A zT_5eYDqWwbw}2e|v@6+s)_1qQ7vn?sy{Mn#&c=CvN$ejG-?zT@;qWOru1U+3sP^ml$A!{XsHfvisp4xgjQ;(wFJm`T_; z@u>SLvNOMnkMsT<7Lx+|0$FTR4Leq0&ZN!loZ*+S@%n3bcYl#r+i}?X`q;C(JPG>- zy_fe^*DvmL3VHZWUxSH9|KA|j`#+5?CPn{ekj3i#pRKTZ|L4Gr*I#?R|MTe9*2iAm zwOI6e0eQIBi(umR;S4UrV$uSy%{Orc7MCS^w5N!tM#1Ct{|N z#bqgC+HBq5%?;RI%smaqd~YKAX5FLx&cOPL$2%^u@~wUcK0Jq6F!8w49I}`c{oO_u zm!;@0V!j76Mm%DEKo*y!;h1?aW9oDG5nWuCA|~c=2h3i~t*`rSbKZr$*8=Qp+~-fQ zc=WSaVd7Hcj@jJ^qD6zaWdrQsjSA?dDf7bLeM&zhz>P g?;&zM-y?J}S&DqGs_)ffFn!GBcToLbye`210gyqXf&c&j literal 0 HcmV?d00001 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"));