diff --git a/.gitmodules b/.gitmodules index e387a2e..9b84d64 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,15 @@ [submodule "ShaderPermute/glslang"] path = ShaderPermute/glslang url = https://github.com/KhronosGroup/glslang +[submodule "submodules/SPIRV-Headers"] + path = submodules/SPIRV-Headers + url = https://github.com/KhronosGroup/SPIRV-Headers + branch = vulkan-sdk-1.4.321 +[submodule "submodules/SPIRV-Tools"] + path = submodules/SPIRV-Tools + url = https://github.com/KhronosGroup/SPIRV-Tools + branch = vulkan-sdk-1.4.321 +[submodule "submodules/glslang"] + path = submodules/glslang + url = https://github.com/KhronosGroup/glslang + branch = vulkan-sdk-1.4.321 diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e2828..bc42be8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,27 +1,15 @@ # CMakeList.txt : Top-level CMake project file, do global configuration # and include sub-projects here. # -cmake_minimum_required (VERSION 3.8) +cmake_minimum_required (VERSION 3.20) -SET(CMAKE_CXX_STANDARD 23) +SET(CMAKE_CXX_STANDARD 20) project ("ShaderPermute") include(CTest) -include(FetchContent) -set(ENABLE_CTEST OFF CACHE BOOL "" FORCE) -set(ENABLE_HLSL OFF CACHE BOOL "" FORCE) +add_link_options(/PROFILE) -FetchContent_Declare( - glslang - URL https://github.com/KhronosGroup/glslang/archive/c9706bdda0ac22b9856f1aa8261e5b9e15cd20c5.zip -) - -FetchContent_Declare( - materialx - URL https://github.com/materialx/MaterialX/releases/download/v1.38.3/MaterialX_Windows_VS2022_x64_Python39.zip -) - -# Include sub-projects. -add_subdirectory ("ShaderPermute/ShaderPermuteTest") +add_subdirectory("submodules") +add_subdirectory("ShaderPermute/ShaderPermuteTest") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..b659de1 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,88 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "linux-debug", + "displayName": "Linux Debug", + "description": "Target the Windows Subsystem for Linux (WSL) or a remote system.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" } } + }, + { + "name": "macos-debug", + "displayName": "macOS Debug", + "description": "Target a remote macOS system.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" } } + }, + { + "name": "windows-base", + "description": "Target Windows with the Visual Studio development environment.", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-debug", + "displayName": "x64 Debug", + "description": "Target Windows (64-bit) with the Visual Studio development environment. (Debug)", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "x64-release", + "displayName": "x64 Release", + "description": "Target Windows (64-bit) with the Visual Studio development environment. (RelWithDebInfo)", + "inherits": "x64-debug", + "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } + }, + { + "name": "x86-debug", + "displayName": "x86 Debug", + "description": "Target Windows (32-bit) with the Visual Studio development environment. (Debug)", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "x86-release", + "displayName": "x86 Release", + "description": "Target Windows (32-bit) with the Visual Studio development environment. (RelWithDebInfo)", + "inherits": "x86-debug", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } + } + ] +} \ No newline at end of file diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index 616e0cf..0000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "configurations": [ - { - "name": "x64-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "variables": [ - { - "name": "BUILD_EXTERNAL", - "value": "False", - "type": "BOOL" - } - ] - } - ] -} \ No newline at end of file diff --git a/CppProperties.json b/CppProperties.json deleted file mode 100644 index f598ba5..0000000 --- a/CppProperties.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "configurations": [ - { - "inheritEnvironments": [ - "msvc_x64" - ], - "name": "x64-Debug", - "includePath": [ - "${env.INCLUDE}", - "${workspaceRoot}\\**" - ], - "defines": [ - "WIN32", - "_DEBUG", - "UNICODE", - "_UNICODE" - ], - "intelliSenseMode": "windows-msvc-x64" - } - ] -} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermute.hpp b/ShaderPermute/ShaderPermute.hpp index a1ea6a5..da4e675 100644 --- a/ShaderPermute/ShaderPermute.hpp +++ b/ShaderPermute/ShaderPermute.hpp @@ -6,7 +6,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -16,9 +16,9 @@ */ #pragma once -#define SPR_VERSION_MAJOR 1 +#define SPR_VERSION_MAJOR 2 #define SPR_VERSION_MINOR 0 -#define SPR_VERSION_PATCH 4 +#define SPR_VERSION_PATCH 0 #ifdef SPR_USE_FORMAT_LIB #include @@ -64,564 +64,424 @@ #if !defined(SPR_NO_GLSL) && !defined(SPR_NO_GLSL_INCLUDE) #include #include -#endif - -#if defined(SPR_MATERIALX) && !defined(SPR_NO_MATERIALX_INCLUDE) -#include +#include #endif #ifndef SPR_NO_STATIC #define SPR_STATIC static #endif -#ifndef SPR_NO_GLSL NLOHMANN_JSON_SERIALIZE_ENUM(EShLanguage, - {{EShLangVertex, "vertex"}, - {EShLangTessControl, "tesslation-control"}, - {EShLangTessEvaluation, "tesslation-evaluation"}, - {EShLangGeometry, "geometry"}, - {EShLangFragment, "fragment"}, - {EShLangCompute, "compute"}, - {EShLangRayGen, "raygen"}, - {EShLangIntersect, "intersect"}, - {EShLangAnyHit, "anyhit"}, - {EShLangClosestHit, "closehit"}, - {EShLangMiss, "miss"}, - {EShLangCallable, "callable"}, - {EShLangTaskNV, "tasknv"}, - {EShLangMeshNV, "meshnv"} - - }); + { {EShLangVertex, "vertex"}, + {EShLangTessControl, "tesslation-control"}, + {EShLangTessEvaluation, "tesslation-evaluation"}, + {EShLangGeometry, "geometry"}, + {EShLangFragment, "fragment"}, + {EShLangCompute, "compute"}, + {EShLangRayGen, "raygen"}, + {EShLangIntersect, "intersect"}, + {EShLangAnyHit, "anyhit"}, + {EShLangClosestHit, "closehit"}, + {EShLangMiss, "miss"}, + {EShLangCallable, "callable"}, + {EShLangTaskNV, "tasknv"}, + {EShLangMeshNV, "meshnv"} + + }); namespace glslang { -NLOHMANN_JSON_SERIALIZE_ENUM(EShClient, {{EShClientNone, "none"}, - {EShClientVulkan, "vulkan"}, - {EShClientOpenGL, "opengl"}}); -NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetClientVersion, - {{EShTargetVulkan_1_0, "vulkan_1_0"}, - {EShTargetVulkan_1_1, "vulkan_1_1"}, - {EShTargetVulkan_1_2, "vulkan_1_2"}, - {EShTargetOpenGL_450, "opengl_450"}}); -NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetLanguage, - {{EShTargetNone, "none"}, {EShTargetSpv, "spv"}}); -NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetLanguageVersion, - { - {EShTargetSpv_1_0, "spv_1_0"}, - {EShTargetSpv_1_1, "spv_1_1"}, - {EShTargetSpv_1_2, "spv_1_2"}, - {EShTargetSpv_1_3, "spv_1_3"}, - {EShTargetSpv_1_4, "spv_1_4"}, - {EShTargetSpv_1_5, "spv_1_5"}, - }); + NLOHMANN_JSON_SERIALIZE_ENUM(EShClient, { {EShClientNone, "none"}, + {EShClientVulkan, "vulkan"}, + {EShClientOpenGL, "opengl"} }); + NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetClientVersion, + { {EShTargetVulkan_1_0, "vulkan_1_0"}, + {EShTargetVulkan_1_1, "vulkan_1_1"}, + {EShTargetVulkan_1_2, "vulkan_1_2"}, + {EShTargetOpenGL_450, "opengl_450"} }); + NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetLanguage, + { {EShTargetNone, "none"}, {EShTargetSpv, "spv"} }); + NLOHMANN_JSON_SERIALIZE_ENUM(EShTargetLanguageVersion, + { + {EShTargetSpv_1_0, "spv_1_0"}, + {EShTargetSpv_1_1, "spv_1_1"}, + {EShTargetSpv_1_2, "spv_1_2"}, + {EShTargetSpv_1_3, "spv_1_3"}, + {EShTargetSpv_1_4, "spv_1_4"}, + {EShTargetSpv_1_5, "spv_1_5"}, + }); } // namespace glslang -#endif namespace permute { - -using lookup = - std::map>; - -enum class ShaderCodeFlags { NONE = 0, REQUIRED = 1 }; - -NLOHMANN_JSON_SERIALIZE_ENUM(ShaderCodeFlags, - {{ShaderCodeFlags::NONE, "none"}, - {ShaderCodeFlags::REQUIRED, "required"}}) - -enum class OutputType { ERROR, TEXT, BINARY }; - -SPR_NODISCARD inline bool isRequired(const ShaderCodeFlags flag) { - return (int)flag & (int)ShaderCodeFlags::REQUIRED; -} - -template -SPR_NODISCARD inline bool isInDependency(T &dependency, T &dependsOn) { - const auto endItr = end(dependency); - for (auto target : dependsOn) { - auto itr = begin(dependency); - if (std::find(itr, endItr, target) == endItr) - return false; - } - return true; -} - -struct ShaderCodes { - std::vector code; - ShaderCodeFlags flags = ShaderCodeFlags::NONE; - std::vector dependsOn; - - friend void to_json(nlohmann::json &nlohmann_json_j, - const ShaderCodes &nlohmann_json_t) { - NLOHMANN_JSON_TO(code); - SPR_OPTIONAL_TO(flags); - SPR_OPTIONAL_TO_L(dependsOn); - } - - friend void from_json(const nlohmann::json &nlohmann_json_j, - ShaderCodes &nlohmann_json_t) { - NLOHMANN_JSON_FROM(code); - SPR_OPTIONAL_FROM(flags); - SPR_OPTIONAL_FROM(dependsOn); - } -}; - -struct GenerateInput { - const std::vector &codes; - const std::vector &dependencies; - const nlohmann::json &settings; -}; - -struct GenerateOutput { - std::vector output; - OutputType type = OutputType::ERROR; - std::vector data; - void *costumData = nullptr; -}; - -class PermuteText { -public: - SPR_NODISCARD inline static GenerateOutput - generate(const GenerateInput input) { - std::vector buffer; - buffer.reserve(input.codes.size()); - for (const auto &code : input.codes) { - if (isRequired(code.flags) || code.dependsOn.empty() || - isInDependency(input.dependencies, code.dependsOn)) { - for (const auto &codePart : code.code) - buffer.push_back(codePart); - } - } - return {buffer, OutputType::TEXT}; - } -}; - -inline std::string postProcess(std::string &codePart, const lookup &callback) { - if (callback.empty()) - return codePart; - auto eItr = end(codePart); - auto startWordItr = eItr; - auto paramStartItr = eItr; - lookup::value_type::second_type func(nullptr); - for (auto itr = begin(codePart); itr != eItr; itr++) { - if (*itr == '$') { - startWordItr = itr + 1; - continue; - } - if (startWordItr != eItr && *itr == '_') { - const auto word = std::string(startWordItr, itr); - const auto fncItr = callback.find(word); - if (fncItr != end(callback)) { - func = fncItr->second; - paramStartItr = itr + 1; - } else { - startWordItr = eItr; - } - continue; - } - if (startWordItr != eItr && *itr == ' ') { - const std::string param(paramStartItr, itr); - const auto replace = func(param); - const auto distance = std::distance(startWordItr, itr); - codePart = codePart.replace(startWordItr - 1, itr, replace); - eItr = end(codePart); - itr = begin(codePart) + distance; - startWordItr = eItr; - } - } - return codePart; -} - -inline void postProcess(std::vector &codePart, - const lookup &callback) { - for (size_t i = 0; i < codePart.size(); i++) { - codePart[i] = postProcess(codePart[i], callback) + "\n"; - } -} - -#ifndef SPR_NO_GLSL -struct GlslSettings { - EShLanguage shaderType; - glslang::EShClient targetClient = glslang::EShClient::EShClientVulkan; - glslang::EShTargetClientVersion targetVersion = - glslang::EShTargetClientVersion::EShTargetVulkan_1_0; - glslang::EShTargetLanguage targetLanguage = - glslang::EShTargetLanguage::EShTargetSpv; - glslang::EShTargetLanguageVersion targetLanguageVersion = - glslang::EShTargetLanguageVersion::EShTargetSpv_1_0; - - friend void to_json(nlohmann::json &nlohmann_json_j, - const GlslSettings &nlohmann_json_t) { - NLOHMANN_JSON_TO(shaderType); - SPR_OPTIONAL_TO(targetClient); - SPR_OPTIONAL_TO(targetVersion); - SPR_OPTIONAL_TO(targetLanguage); - SPR_OPTIONAL_TO(targetLanguageVersion); - } - - friend void from_json(const nlohmann::json &nlohmann_json_j, - GlslSettings &nlohmann_json_t) { - NLOHMANN_JSON_FROM(shaderType); - SPR_OPTIONAL_FROM(targetClient); - SPR_OPTIONAL_FROM(targetVersion); - SPR_OPTIONAL_FROM(targetLanguage); - SPR_OPTIONAL_FROM(targetLanguageVersion); - } -}; - -SPR_STATIC std::map lookupCounter; - -SPR_STATIC std::string next(const std::string &input) { - const auto id = lookupCounter[input]; - lookupCounter[input]++; -#ifdef SPR_USE_FORMAT_LIB - if (input == "ublock") - return std::format("layout(binding={}) uniform BLOCK{}", id, id); - return std::format("layout(location={}) {}", id, input); -#else - std::stringstream strStream; - if (input == "ublock") { - strStream << "layout(binding=" << id << ") uniform BLOCK" << id; - } else { - strStream << "layout(location=" << id << ") " << input; - } - return strStream.str(); -#endif -} - -SPR_STATIC lookup glslLookup -#ifndef SPR_NO_STATIC - = {{"next", next}} -#endif // SPR_STATIC -; - -class ShaderTraverser; -static std::vector traverser; - -class ShaderTraverser { -public: - ShaderTraverser() { permute::traverser.push_back(this); } - - ~ShaderTraverser() { - permute::traverser.erase( - std::remove(begin(permute::traverser), end(permute::traverser), this)); - } - - virtual void visitSymbol(glslang::TIntermSymbol *) {} - virtual void visitConstantUnion(glslang::TIntermConstantUnion *) {} - virtual bool visitBinary(glslang::TVisit, glslang::TIntermBinary *) { - return true; - } - virtual bool visitUnary(glslang::TVisit, glslang::TIntermUnary *) { - return true; - } - virtual bool visitSelection(glslang::TVisit, glslang::TIntermSelection *) { - return true; - } - virtual bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate *) { - return true; - } - virtual bool visitLoop(glslang::TVisit, glslang::TIntermLoop *) { - return true; - } - virtual bool visitBranch(glslang::TVisit, glslang::TIntermBranch *) { - return true; - } - virtual bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch *) { - return true; - } - virtual void postProcess() {} - virtual bool isValid(const GlslSettings &settings) = 0; -}; - -namespace impl { - -class ShaderTraverser : public glslang::TIntermTraverser { -public: - permute::ShaderTraverser *traverser; - - ShaderTraverser(permute::ShaderTraverser *traverser) : traverser(traverser) {} - - virtual void visitSymbol(glslang::TIntermSymbol *s) { - traverser->visitSymbol(s); - } - - virtual void visitConstantUnion(glslang::TIntermConstantUnion *s) { - traverser->visitConstantUnion(s); - } - - virtual bool visitBinary(glslang::TVisit v, glslang::TIntermBinary *s) { - return traverser->visitBinary(v, s); - } - virtual bool visitUnary(glslang::TVisit v, glslang::TIntermUnary *s) { - return traverser->visitUnary(v, s); - } - virtual bool visitSelection(glslang::TVisit v, glslang::TIntermSelection *s) { - return traverser->visitSelection(v, s); - } - virtual bool visitAggregate(glslang::TVisit v, glslang::TIntermAggregate *s) { - return traverser->visitAggregate(v, s); - } - virtual bool visitLoop(glslang::TVisit v, glslang::TIntermLoop *s) { - return traverser->visitLoop(v, s); - } - virtual bool visitBranch(glslang::TVisit v, glslang::TIntermBranch *s) { - return traverser->visitBranch(v, s); - } - virtual bool visitSwitch(glslang::TVisit v, glslang::TIntermSwitch *s) { - return traverser->visitSwitch(v, s); - } -}; - -} // namespace impl - -#ifdef SPR_MATERIALX -class PermuteMaterialX { -public: - SPR_NODISCARD inline static GenerateOutput - generate(const GenerateInput input) { - auto generator = MaterialX::GlslShaderGenerator::create(); - auto doc = MaterialX::Document::createDocument(); - generator->registerShaderMetadata() auto shader = generator->generate("", ); - } -}; -#endif // SPR_NO_MATERIALX - -inline TBuiltInResource InitResources() -{ - TBuiltInResource Resources{}; - Resources.maxLights = 32; - Resources.maxClipPlanes = 6; - Resources.maxTextureUnits = 32; - Resources.maxTextureCoords = 32; - Resources.maxVertexAttribs = 64; - Resources.maxVertexUniformComponents = 4096; - Resources.maxVaryingFloats = 64; - Resources.maxVertexTextureImageUnits = 32; - Resources.maxCombinedTextureImageUnits = 80; - Resources.maxTextureImageUnits = 32; - Resources.maxFragmentUniformComponents = 4096; - Resources.maxDrawBuffers = 32; - Resources.maxVertexUniformVectors = 128; - Resources.maxVaryingVectors = 8; - Resources.maxFragmentUniformVectors = 16; - Resources.maxVertexOutputVectors = 16; - Resources.maxFragmentInputVectors = 15; - Resources.minProgramTexelOffset = -8; - Resources.maxProgramTexelOffset = 7; - Resources.maxClipDistances = 8; - Resources.maxComputeWorkGroupCountX = 65535; - Resources.maxComputeWorkGroupCountY = 65535; - Resources.maxComputeWorkGroupCountZ = 65535; - Resources.maxComputeWorkGroupSizeX = 1024; - Resources.maxComputeWorkGroupSizeY = 1024; - Resources.maxComputeWorkGroupSizeZ = 64; - Resources.maxComputeUniformComponents = 1024; - Resources.maxComputeTextureImageUnits = 16; - Resources.maxComputeImageUniforms = 8; - Resources.maxComputeAtomicCounters = 8; - Resources.maxComputeAtomicCounterBuffers = 1; - Resources.maxVaryingComponents = 60; - Resources.maxVertexOutputComponents = 64; - Resources.maxGeometryInputComponents = 64; - Resources.maxGeometryOutputComponents = 128; - Resources.maxFragmentInputComponents = 128; - Resources.maxImageUnits = 8; - Resources.maxCombinedImageUnitsAndFragmentOutputs = 8; - Resources.maxCombinedShaderOutputResources = 8; - Resources.maxImageSamples = 0; - Resources.maxVertexImageUniforms = 0; - Resources.maxTessControlImageUniforms = 0; - Resources.maxTessEvaluationImageUniforms = 0; - Resources.maxGeometryImageUniforms = 0; - Resources.maxFragmentImageUniforms = 8; - Resources.maxCombinedImageUniforms = 8; - Resources.maxGeometryTextureImageUnits = 16; - Resources.maxGeometryOutputVertices = 256; - Resources.maxGeometryTotalOutputComponents = 1024; - Resources.maxGeometryUniformComponents = 1024; - Resources.maxGeometryVaryingComponents = 64; - Resources.maxTessControlInputComponents = 128; - Resources.maxTessControlOutputComponents = 128; - Resources.maxTessControlTextureImageUnits = 16; - Resources.maxTessControlUniformComponents = 1024; - Resources.maxTessControlTotalOutputComponents = 4096; - Resources.maxTessEvaluationInputComponents = 128; - Resources.maxTessEvaluationOutputComponents = 128; - Resources.maxTessEvaluationTextureImageUnits = 16; - Resources.maxTessEvaluationUniformComponents = 1024; - Resources.maxTessPatchComponents = 120; - Resources.maxPatchVertices = 32; - Resources.maxTessGenLevel = 64; - Resources.maxViewports = 16; - Resources.maxVertexAtomicCounters = 0; - Resources.maxTessControlAtomicCounters = 0; - Resources.maxTessEvaluationAtomicCounters = 0; - Resources.maxGeometryAtomicCounters = 0; - Resources.maxFragmentAtomicCounters = 8; - Resources.maxCombinedAtomicCounters = 8; - Resources.maxAtomicCounterBindings = 1; - Resources.maxVertexAtomicCounterBuffers = 0; - Resources.maxTessControlAtomicCounterBuffers = 0; - Resources.maxTessEvaluationAtomicCounterBuffers = 0; - Resources.maxGeometryAtomicCounterBuffers = 0; - Resources.maxFragmentAtomicCounterBuffers = 1; - Resources.maxCombinedAtomicCounterBuffers = 1; - Resources.maxAtomicCounterBufferSize = 16384; - Resources.maxTransformFeedbackBuffers = 4; - Resources.maxTransformFeedbackInterleavedComponents = 64; - Resources.maxCullDistances = 8; - Resources.maxCombinedClipAndCullDistances = 8; - Resources.maxSamples = 4; - Resources.maxMeshOutputVerticesNV = 256; - Resources.maxMeshOutputPrimitivesNV = 512; - Resources.maxMeshWorkGroupSizeX_NV = 32; - Resources.maxMeshWorkGroupSizeY_NV = 1; - Resources.maxMeshWorkGroupSizeZ_NV = 1; - Resources.maxTaskWorkGroupSizeX_NV = 32; - Resources.maxTaskWorkGroupSizeY_NV = 1; - Resources.maxTaskWorkGroupSizeZ_NV = 1; - Resources.maxMeshViewCountNV = 4; - - Resources.limits.nonInductiveForLoops = 1; - Resources.limits.whileLoops = 1; - Resources.limits.doWhileLoops = 1; - Resources.limits.generalUniformIndexing = 1; - Resources.limits.generalAttributeMatrixVectorIndexing = 1; - Resources.limits.generalVaryingIndexing = 1; - Resources.limits.generalSamplerIndexing = 1; - Resources.limits.generalVariableIndexing = 1; - Resources.limits.generalConstantMatrixVectorIndexing = 1; - - return Resources; -} -const TBuiltInResource DefaultTBuiltInResource = InitResources(); - - -class PermuteGLSL { -public: - SPR_NODISCARD inline static GenerateOutput - generate(const GenerateInput input) { - lookupCounter.clear(); - auto output = PermuteText::generate(input); - if (output.type == OutputType::ERROR) - return output; - try { - postProcess(output.output, glslLookup); -#if defined(DEBUG) && !defined(SPR_NO_DEBUG_OUTPUT) - for (auto &str : output.output) { - printf("%s\n", str.c_str()); - } -#endif // DEBUG - auto stringPtr = output.output; - std::vector cstrings; - cstrings.resize(stringPtr.size()); - for (size_t i = 0; i < stringPtr.size(); i++) { - cstrings[i] = stringPtr[i].c_str(); - } - const GlslSettings settings = input.settings.get(); - auto shader = new glslang::TShader(settings.shaderType); - shader->setStrings(cstrings.data(), cstrings.size()); - shader->setEnvInput(glslang::EShSourceGlsl, settings.shaderType, - settings.targetClient, 100); - shader->setEnvClient(settings.targetClient, settings.targetVersion); - shader->setEnvTarget(settings.targetLanguage, - settings.targetLanguageVersion); - if (!shader->parse(&DefaultTBuiltInResource, 450, EProfile::ENoProfile, false, false, - EShMessages::EShMsgVulkanRules)) { - return {{shader->getInfoLog()}, OutputType::ERROR}; - } - const auto interm = shader->getIntermediate(); - const auto node = interm->getTreeRoot(); - for (const auto travPtr : traverser) { - if (!travPtr->isValid(settings)) - continue; - impl::ShaderTraverser trav(travPtr); - node->traverse(&trav); - travPtr->postProcess(); - } - std::vector outputData; - glslang::GlslangToSpv(*interm, outputData); - return {std::move(output.output), OutputType::BINARY, - std::move(outputData), shader}; - } catch (const std::exception &e) { - return {{"Could not parse glsl settings!", std::string(e.what())}, - OutputType::ERROR}; - } - return {{"Undefined error!"}, - OutputType::ERROR}; - } -}; - -#endif - -template class Permute { - -private: - std::vector codes; - nlohmann::json settings; - GenerateOutput output; - -public: - SPR_NODISCARD inline bool - generate(const std::vector &dependencies = {}) { - const GenerateInput input = {codes, dependencies, settings}; - output = T::generate(input); - return success(); - } - - SPR_NODISCARD inline bool success() const { - return output.type != OutputType::ERROR; - } - - SPR_NODISCARD inline std::vector getContent() const { - return output.output; - } - - SPR_NODISCARD inline std::vector getBinary() const { - if (output.type != OutputType::BINARY) - return {}; - return output.data; - } - - SPR_NODISCARD inline nlohmann::json getSettings() const { return settings; } - - SPR_NODISCARD inline void *getCostumData() const { return output.costumData; } - - template SPR_NODISCARD inline Settings getSettings() const { - return settings.get(); - } - - inline void toBinaryFile(const std::string &path) const { - std::ofstream output(path, std::ios_base::binary); - const auto &data = this->output.data; - output.write((char *)data.data(), data.size() * sizeof(unsigned int)); - } - - friend void to_json(nlohmann::json &nlohmann_json_j, - const Permute &nlohmann_json_t) { - NLOHMANN_JSON_TO(codes); - SPR_OPTIONAL_TO_L(settings); - } - - friend void from_json(const nlohmann::json &nlohmann_json_j, - Permute &nlohmann_json_t) { - NLOHMANN_JSON_FROM(codes); - SPR_OPTIONAL_FROM(settings); - } -}; - -template inline Permute fromJson(const nlohmann::json &json) { - return json.get>(); -} + struct GlslSettings { + EShLanguage shaderType; + glslang::EShClient targetClient = glslang::EShClient::EShClientVulkan; + glslang::EShTargetClientVersion targetVersion = + glslang::EShTargetClientVersion::EShTargetVulkan_1_0; + glslang::EShTargetLanguage targetLanguage = + glslang::EShTargetLanguage::EShTargetSpv; + glslang::EShTargetLanguageVersion targetLanguageVersion = + glslang::EShTargetLanguageVersion::EShTargetSpv_1_0; + + friend void to_json(nlohmann::json& nlohmann_json_j, + const GlslSettings& nlohmann_json_t) { + NLOHMANN_JSON_TO(shaderType); + SPR_OPTIONAL_TO(targetClient); + SPR_OPTIONAL_TO(targetVersion); + SPR_OPTIONAL_TO(targetLanguage); + SPR_OPTIONAL_TO(targetLanguageVersion); + } + + friend void from_json(const nlohmann::json& nlohmann_json_j, + GlslSettings& nlohmann_json_t) { + NLOHMANN_JSON_FROM(shaderType); + SPR_OPTIONAL_FROM(targetClient); + SPR_OPTIONAL_FROM(targetVersion); + SPR_OPTIONAL_FROM(targetLanguage); + SPR_OPTIONAL_FROM(targetLanguageVersion); + } + }; + + class ShaderTraverser : public glslang::TIntermTraverser { + public: + virtual void postProcess() {} + virtual bool isValid(const GlslSettings& settings) { return true; }; + }; + + inline TBuiltInResource InitResources() + { + TBuiltInResource Resources{}; + Resources.maxLights = 32; + Resources.maxClipPlanes = 6; + Resources.maxTextureUnits = 32; + Resources.maxTextureCoords = 32; + Resources.maxVertexAttribs = 64; + Resources.maxVertexUniformComponents = 4096; + Resources.maxVaryingFloats = 64; + Resources.maxVertexTextureImageUnits = 32; + Resources.maxCombinedTextureImageUnits = 80; + Resources.maxTextureImageUnits = 32; + Resources.maxFragmentUniformComponents = 4096; + Resources.maxDrawBuffers = 32; + Resources.maxVertexUniformVectors = 128; + Resources.maxVaryingVectors = 8; + Resources.maxFragmentUniformVectors = 16; + Resources.maxVertexOutputVectors = 16; + Resources.maxFragmentInputVectors = 15; + Resources.minProgramTexelOffset = -8; + Resources.maxProgramTexelOffset = 7; + Resources.maxClipDistances = 8; + Resources.maxComputeWorkGroupCountX = 65535; + Resources.maxComputeWorkGroupCountY = 65535; + Resources.maxComputeWorkGroupCountZ = 65535; + Resources.maxComputeWorkGroupSizeX = 1024; + Resources.maxComputeWorkGroupSizeY = 1024; + Resources.maxComputeWorkGroupSizeZ = 64; + Resources.maxComputeUniformComponents = 1024; + Resources.maxComputeTextureImageUnits = 16; + Resources.maxComputeImageUniforms = 8; + Resources.maxComputeAtomicCounters = 8; + Resources.maxComputeAtomicCounterBuffers = 1; + Resources.maxVaryingComponents = 60; + Resources.maxVertexOutputComponents = 64; + Resources.maxGeometryInputComponents = 64; + Resources.maxGeometryOutputComponents = 128; + Resources.maxFragmentInputComponents = 128; + Resources.maxImageUnits = 8; + Resources.maxCombinedImageUnitsAndFragmentOutputs = 8; + Resources.maxCombinedShaderOutputResources = 8; + Resources.maxImageSamples = 0; + Resources.maxVertexImageUniforms = 0; + Resources.maxTessControlImageUniforms = 0; + Resources.maxTessEvaluationImageUniforms = 0; + Resources.maxGeometryImageUniforms = 0; + Resources.maxFragmentImageUniforms = 8; + Resources.maxCombinedImageUniforms = 8; + Resources.maxGeometryTextureImageUnits = 16; + Resources.maxGeometryOutputVertices = 256; + Resources.maxGeometryTotalOutputComponents = 1024; + Resources.maxGeometryUniformComponents = 1024; + Resources.maxGeometryVaryingComponents = 64; + Resources.maxTessControlInputComponents = 128; + Resources.maxTessControlOutputComponents = 128; + Resources.maxTessControlTextureImageUnits = 16; + Resources.maxTessControlUniformComponents = 1024; + Resources.maxTessControlTotalOutputComponents = 4096; + Resources.maxTessEvaluationInputComponents = 128; + Resources.maxTessEvaluationOutputComponents = 128; + Resources.maxTessEvaluationTextureImageUnits = 16; + Resources.maxTessEvaluationUniformComponents = 1024; + Resources.maxTessPatchComponents = 120; + Resources.maxPatchVertices = 32; + Resources.maxTessGenLevel = 64; + Resources.maxViewports = 16; + Resources.maxVertexAtomicCounters = 0; + Resources.maxTessControlAtomicCounters = 0; + Resources.maxTessEvaluationAtomicCounters = 0; + Resources.maxGeometryAtomicCounters = 0; + Resources.maxFragmentAtomicCounters = 8; + Resources.maxCombinedAtomicCounters = 8; + Resources.maxAtomicCounterBindings = 1; + Resources.maxVertexAtomicCounterBuffers = 0; + Resources.maxTessControlAtomicCounterBuffers = 0; + Resources.maxTessEvaluationAtomicCounterBuffers = 0; + Resources.maxGeometryAtomicCounterBuffers = 0; + Resources.maxFragmentAtomicCounterBuffers = 1; + Resources.maxCombinedAtomicCounterBuffers = 1; + Resources.maxAtomicCounterBufferSize = 16384; + Resources.maxTransformFeedbackBuffers = 4; + Resources.maxTransformFeedbackInterleavedComponents = 64; + Resources.maxCullDistances = 8; + Resources.maxCombinedClipAndCullDistances = 8; + Resources.maxSamples = 4; + Resources.maxMeshOutputVerticesNV = 256; + Resources.maxMeshOutputPrimitivesNV = 512; + Resources.maxMeshWorkGroupSizeX_NV = 32; + Resources.maxMeshWorkGroupSizeY_NV = 1; + Resources.maxMeshWorkGroupSizeZ_NV = 1; + Resources.maxTaskWorkGroupSizeX_NV = 32; + Resources.maxTaskWorkGroupSizeY_NV = 1; + Resources.maxTaskWorkGroupSizeZ_NV = 1; + Resources.maxMeshViewCountNV = 4; + + Resources.limits.nonInductiveForLoops = 1; + Resources.limits.whileLoops = 1; + Resources.limits.doWhileLoops = 1; + Resources.limits.generalUniformIndexing = 1; + Resources.limits.generalAttributeMatrixVectorIndexing = 1; + Resources.limits.generalVaryingIndexing = 1; + Resources.limits.generalSamplerIndexing = 1; + Resources.limits.generalVariableIndexing = 1; + Resources.limits.generalConstantMatrixVectorIndexing = 1; + + return Resources; + } + static const TBuiltInResource DefaultTBuiltInResource = InitResources(); + + struct Dependency { + std::string name; + std::string value = "1"; + }; + + enum ResultType + { + SUCCESS, + COMPILE_ERROR + }; + + struct ResultOrError { + ResultType type; + std::vector output; + std::string error; + + ResultOrError(std::vector&& output, ResultType type = ResultType::SUCCESS) + : type(type), output(std::move(output)) { + } + + ResultOrError(const std::string& error, ResultType type = ResultType::COMPILE_ERROR) + : error(error), type(type) { + } + + operator bool() const { + return type == ResultType::SUCCESS; + } + + bool operator!() const { + return type != ResultType::SUCCESS; + } + }; + + + struct NoChache { + SPR_NODISCARD inline std::optional get(const std::string& dependencies) const { + return std::optional(); + } + + SPR_NODISCARD inline void add(const std::string& dependencies, const ResultOrError& result) {} + }; + + struct AllChache { + + std::unordered_map cache; + + SPR_NODISCARD inline std::optional get(const std::string& dependencies) const { + auto iterator = cache.find(dependencies); + if(iterator != cache.end()) { + return std::optional(iterator->second); + } + return std::optional(); + } + + SPR_NODISCARD inline void add(const std::string& dependencies, const ResultOrError& result) { + cache.emplace(dependencies, result); + } + }; + + template + concept CacheConcept = requires(const T& cache, T & cacheNoConst, const std::string& dependencies, const ResultOrError& result) { + { cache.get(dependencies) } -> std::same_as>; + { cacheNoConst.add(dependencies, result) }; + }; + static_assert(CacheConcept, "NoChache must implement CacheConcept"); + static_assert(CacheConcept, "AllChache must implement CacheConcept"); + + template + class PermuteGLSL { + GlslSettings settings; + std::vector input; + mutable T cache{}; + + public: + using CacheType = T; + std::vector traverser; + + PermuteGLSL(GlslSettings settings, const std::vector& input) + : settings(settings), input(input) { + } + + PermuteGLSL(GlslSettings settings, std::vector&& input) + : settings(settings), input(std::move(input)) { + } + + SPR_NODISCARD inline ResultOrError + generate(const std::vector& dependcies = {}) const { + std::stringstream inputDataDefsStream; + for (const auto& dep : dependcies) + { + inputDataDefsStream << "#define " << dep.name << " " << dep.value << "\n"; + } + auto preamble = inputDataDefsStream.str(); + const auto cachedValue = cache.get(preamble); + if(cachedValue) { + return *cachedValue; + } + auto shader = new glslang::TShader(settings.shaderType); + shader->setPreamble(preamble.c_str()); + shader->setStrings(input.data(), static_cast(input.size())); + shader->setEnvInput(glslang::EShSourceGlsl, settings.shaderType, + settings.targetClient, 100); + shader->setEnvClient(settings.targetClient, settings.targetVersion); + shader->setEnvTarget(settings.targetLanguage, + settings.targetLanguageVersion); + if (!shader->parse(&DefaultTBuiltInResource, 460, EProfile::ECoreProfile, false, true, + EShMessages::EShMsgVulkanRules)) { + return ResultOrError{shader->getInfoLog()}; + } + const auto interm = shader->getIntermediate(); + const auto node = interm->getTreeRoot(); + for (const auto travPtr : traverser) { + if (!travPtr->isValid(settings)) + continue; + node->traverse(travPtr); + travPtr->postProcess(); + } + std::vector outputData; + glslang::GlslangToSpv(*interm, outputData); + ResultOrError result{ std::move(outputData) }; + cache.add(preamble, result); + return result; + } + }; + + inline EShLanguage getLanguageFromExtension(const std::string& ext) { + if (ext == ".vert" || ext == ".vsh" || ext == ".vs") + return EShLangVertex; + else if (ext == ".tesc" || ext == ".tcs") + return EShLangTessControl; + else if (ext == ".tese" || ext == ".tes") + return EShLangTessEvaluation; + else if (ext == ".geom" || ext == ".gsh") + return EShLangGeometry; + else if (ext == ".frag" || ext == ".fsh" || ext == ".fs") + return EShLangFragment; + else if (ext == ".comp" || ext == ".csh") + return EShLangCompute; + else if (ext == ".rgen") + return EShLangRayGen; + else if (ext == ".rint") + return EShLangIntersect; + else if (ext == ".rahit") + return EShLangAnyHit; + else if (ext == ".rchit") + return EShLangClosestHit; + else if (ext == ".rmiss") + return EShLangMiss; + else if (ext == ".rcall") + return EShLangCallable; + else if (ext == ".tasknv") + return EShLangTaskNV; + else if (ext == ".meshnv") + return EShLangMeshNV; + throw std::runtime_error( + "Unknown shader extension: " + ext + + ". Please use one of the following: .vert, .tesc, .tese, .geom, .frag, .comp, .rgen, .rint, .rahit, .rchit, .rmiss, .rcall, .tasknv or .meshnv"); + } + + struct Permute { + GlslSettings settings = {}; + std::unordered_map> inputMap; + std::vector traverser; + + Permute() { + glslang::InitializeProcess(); + } + + ~Permute() + { + glslang::FinalizeProcess(); + } + + template + SPR_NODISCARD inline PermuteGLSL getGLSLPermute(const std::string& name) const { + auto iterator = inputMap.find(name); +#ifndef NDEBUG + if(iterator == inputMap.end()) { + std::stringstream ss; + ss << "ShaderPermute: No shader found with name '" << name << "'"; + throw std::runtime_error(ss.str()); + } +#endif // !NDEBUG + const auto& stringValues = iterator->second; + std::vector inputs(stringValues.size()); + std::transform(stringValues.begin(), stringValues.end(), + inputs.begin(), [](const std::string& str) { return str.c_str(); }); + GlslSettings settings = this->settings; + settings.shaderType = getLanguageFromExtension( + name.substr(name.find_last_of('.'))); + PermuteGLSL glsl(settings, std::move(inputs)); + glsl.traverser = traverser; + return glsl; + } + + template + SPR_NODISCARD inline PermuteGLSL fromStrings(const std::string& name, std::vector&& input) { +#ifndef NDEBUG + const auto value = inputMap.find(name); + if(value != inputMap.end()) { + std::stringstream ss; + ss << "ShaderPermute: Shader with name '" << name + << "' already exists. Please use a different name."; + throw std::runtime_error(ss.str()); + } +#endif // !NDEBUG + inputMap[name] = std::move(input); + return getGLSLPermute(name); + } #ifndef SPR_NO_FSTREAM -template inline Permute fromFile(const std::string &path) { - std::ifstream inputfile(path); - if (!inputfile.good()) - throw std::runtime_error("File not found!"); - nlohmann::json json; - inputfile >> json; - return fromJson(json); -} + template + SPR_NODISCARD inline PermuteGLSL fromFile(const std::string& path) { + std::ifstream inputfile(path); + if (!inputfile) + throw std::runtime_error("File not found!"); + std::vector inputs; + inputs.reserve(128); + for (std::string input; std::getline(inputfile, input);) { + input.append("\n"); + inputs.push_back(input); + } + return fromStrings(path, std::move(inputs)); + } #endif + }; } // namespace permute diff --git a/ShaderPermute/ShaderPermuteTest/CMakeLists.txt b/ShaderPermute/ShaderPermuteTest/CMakeLists.txt index 3b0a2e5..7b860fe 100644 --- a/ShaderPermute/ShaderPermuteTest/CMakeLists.txt +++ b/ShaderPermute/ShaderPermuteTest/CMakeLists.txt @@ -1,7 +1,9 @@ -cmake_minimum_required (VERSION 3.8) +cmake_minimum_required (VERSION 3.20) add_executable (ShaderPermuteTest "ShaderPermuteTest.cpp") -file(COPY basicTest.json lightPassFrag.json lightPassVert.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY basicTest.vert lightPassFrag.frag lightPassVert.vert DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +include(FetchContent) FetchContent_Declare( googletest @@ -10,15 +12,13 @@ FetchContent_Declare( set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -FetchContent_MakeAvailable(glslang) -FetchContent_MakeAvailable(materialx) include(GoogleTest) target_link_libraries(ShaderPermuteTest PUBLIC gtest_main) # The commands to add to your own project for glsl compiler support -target_link_libraries(ShaderPermuteTest PUBLIC glslang SPIRV) -target_include_directories(ShaderPermuteTest PUBLIC ${glslang_SOURCE_DIR} ${materialx_SOURCE_DIR}/include) +target_link_libraries(ShaderPermuteTest PUBLIC glslang SPIRV SPIRV-Tools) +target_include_directories(ShaderPermuteTest PUBLIC submodules/glslang) gtest_add_tests(TARGET ShaderPermuteTest) diff --git a/ShaderPermute/ShaderPermuteTest/ShaderPermuteTest.cpp b/ShaderPermute/ShaderPermuteTest/ShaderPermuteTest.cpp index e628b3c..58da813 100644 --- a/ShaderPermute/ShaderPermuteTest/ShaderPermuteTest.cpp +++ b/ShaderPermute/ShaderPermuteTest/ShaderPermuteTest.cpp @@ -27,62 +27,64 @@ int main() { return RUN_ALL_TESTS(); } +TEST(glsl, compile) { + permute::Permute permute; + + auto glslPermutation = permute.fromFile("basicTest.vert"); + const auto firstResult = glslPermutation.generate(); + ASSERT_TRUE(firstResult) << firstResult.error; + + auto glslPermutation2 = permute.fromFile("lightPassVert.vert"); + const auto result2 = glslPermutation2.generate(); + ASSERT_TRUE(result2) << result2.error; + + auto glslPermutation3 = permute.fromFile("lightPassFrag.frag"); + const auto result3 = glslPermutation3.generate(); + ASSERT_TRUE(result3) << result3.error; + +} + class TestTraverser : public permute::ShaderTraverser { public: - bool visited = false; + std::unordered_set symbols{}; - TestTraverser() : permute::ShaderTraverser() {} + void visitSymbol(glslang::TIntermSymbol* node) override { + symbols.insert(node->getName().c_str()); + } +}; - void visitSymbol(glslang::TIntermSymbol *) override { visited = true; } +TEST(glsl, compileWithDeps) { + permute::Permute permute; - bool isValid(const permute::GlslSettings &settings) override { return true; } -}; + TestTraverser traverser; -TEST(glsl, compile) { - auto perm = permute::fromFile("basicTest.json"); - ASSERT_TRUE(perm.generate()); - ASSERT_TRUE(perm.generate({"NORMAL"})); - ASSERT_TRUE(perm.generate({"TEXCOORD_0"})); - ASSERT_TRUE(perm.generate({"TEXCOORD_0", "NORMAL", "COLOR"})); - ASSERT_TRUE(perm.generate({"COLOR"})); - ASSERT_TRUE(perm.generate()); -} + auto glslPermutation = permute.fromFile("basicTest.vert"); + glslPermutation.traverser.push_back(&traverser); + const auto firstResult = glslPermutation.generate(); + ASSERT_TRUE(firstResult) << firstResult.error; + ASSERT_FALSE(traverser.symbols.contains("COLOR")); + traverser.symbols.clear(); -TEST(glsl, traverser) { - auto perm = permute::fromFile("basicTest.json"); - TestTraverser tt; - ASSERT_TRUE(perm.generate()); - ASSERT_TRUE(tt.visited); + const auto secondResult = glslPermutation.generate({ {"REQ_COLOR"} }); + ASSERT_TRUE(secondResult) << secondResult.error; + ASSERT_GT(secondResult.output.size(), firstResult.output.size()); + ASSERT_TRUE(traverser.symbols.contains("COLOR")); } -TEST(glsl, writeBin) { - auto perm = permute::fromFile("basicTest.json"); - ASSERT_TRUE(perm.generate()); - ASSERT_NO_THROW(perm.toBinaryFile("testOutput.spv")); -} +TEST(glsl, compileWithDepsAndAllCache) { + permute::Permute permute; -TEST(glsl, testComplex) { - auto perm = permute::fromFile("lightPassVert.json"); - ASSERT_TRUE(perm.generate()); + auto glslPermutation = permute.fromFile("basicTest.vert"); + const auto firstResult = glslPermutation.generate(); + ASSERT_TRUE(firstResult) << firstResult.error; - perm = permute::fromFile("lightPassFrag.json"); - ASSERT_TRUE(perm.generate()); -} + const auto secondResult = glslPermutation.generate({ {"REQ_COLOR"} }); + ASSERT_TRUE(secondResult) << secondResult.error; + ASSERT_GT(secondResult.output.size(), firstResult.output.size()); -TEST(text, equality) { - nlohmann::json js = {{"codes", // - { - // - { - {"code", {"test"}}, {"flags", "required"} // - }, // - { - {"code", {"not"}}, {"dependsOn", {"not"}} // - } // - }}}; - auto perm = permute::fromJson(js); - ASSERT_TRUE(perm.generate()); - ASSERT_EQ(perm.getContent(), "test\n"); - ASSERT_TRUE(perm.generate({"not"})); - ASSERT_EQ(perm.getContent(), "test\nnot\n"); + for (size_t i = 0; i < 100; i++) + { + const auto thirdResult = glslPermutation.generate({ {"REQ_COLOR"} }); + ASSERT_TRUE(thirdResult) << thirdResult.error; + } } diff --git a/ShaderPermute/ShaderPermuteTest/basicTest.json b/ShaderPermute/ShaderPermuteTest/basicTest.json deleted file mode 100644 index b81a742..0000000 --- a/ShaderPermute/ShaderPermuteTest/basicTest.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "settings": { - "shaderType": "vertex", - "targetVersion": "vulkan_1_2" - }, - "codes": [ - { - "code": [ - "$next_in vec3 POSITION;", - "$next_out vec3 POSITIONOUT;" - ], - "flags": "required" - }, - { - "code": [ - "$next_in vec4 COLOR;", - "$next_out vec4 COLOROUT;" - ], - "dependsOn": ["COLOR"] - }, - { - "code": [ - "$next_in vec3 TEXCOORD_0;", - "$next_out vec3 UV;" - ], - "dependsOn": [ "TEXCOORD_0" ] - }, - { - "code": [ - "$next_in vec3 NORMAL;", - "$next_out vec3 NORMALOUT;" - ], - "dependsOn": [ "NORMAL" ] - }, - { - "code": [ - "$next_ublock { mat4 model; } ublock_0;", - "$next_ublock { mat4 vp;} ublock_1;", - "out gl_PerVertex { vec4 gl_Position; };", - "void main() {", - "gl_Position = ublock_1.vp * ublock_0.model * vec4(POSITION, 1);", - "POSITIONOUT = gl_Position.xyz;" - ] - }, - { - "code": [ - "UV = TEXCOORD_0;" - ], - "dependsOn": [ "TEXCOORD_0" ] - }, - { - "code": [ - "COLOROUT = COLOR;" - ], - "dependsOn": ["COLOR"] - }, - { - "code": [ - "vec4 _tempNorm = vec4(NORMAL, 1) * ublock_0.model;", - "NORMALOUT = _tempNorm.xyz;" - ], - "dependsOn": [ "NORMAL" ] - }, - { - "code": [ - "}" - ], - "flags": "required" - } - ] -} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermuteTest/basicTest.vert b/ShaderPermute/ShaderPermuteTest/basicTest.vert new file mode 100644 index 0000000..d2596e6 --- /dev/null +++ b/ShaderPermute/ShaderPermuteTest/basicTest.vert @@ -0,0 +1,42 @@ +#version 450 + +layout(location=0) in vec3 POSITION; +layout(location=0) out vec3 POSITIONOUT; + +#ifdef REQ_COLOR +layout(location=1) in vec4 COLOR; +layout(location=1) out vec4 COLOROUT; +#endif + +#ifdef REQ_TEXCOORD_0 +layout(location=2) in vec3 TEXCOORD_0; +layout(location=2) out vec3 UV; +#endif + +#ifdef REQ_NORMAL +layout(location=3) in vec3 NORMAL; +layout(location=3) out vec3 NORMALOUT; +#endif + +layout(binding=0) uniform UBLOCK1 { mat4 model; } ublock_0; +layout(binding=1) uniform UBLOCK2 { mat4 vp;} ublock_1; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() { + gl_Position = ublock_1.vp * ublock_0.model * vec4(POSITION, 1); + POSITIONOUT = gl_Position.xyz; + +#ifdef REQ_TEXCOORD_0 + UV = TEXCOORD_0; +#endif + +#ifdef REQ_COLOR + COLOROUT = COLOR; +#endif + +#ifdef REQ_NORMAL + vec4 _tempNorm = vec4(NORMAL, 1) * ublock_0.model; + NORMALOUT = _tempNorm.xyz; +#endif +} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermuteTest/lightPassFrag.frag b/ShaderPermute/ShaderPermuteTest/lightPassFrag.frag new file mode 100644 index 0000000..01ee639 --- /dev/null +++ b/ShaderPermute/ShaderPermuteTest/lightPassFrag.frag @@ -0,0 +1,35 @@ +#version 450 + +layout(input_attachment_index=0, set=0, binding = 0) uniform subpassInput ALBEDO; +layout(input_attachment_index=1, set=0, binding = 1) uniform subpassInput NORMAL; +layout(input_attachment_index=2, set=0, binding = 2) uniform subpassInput ROUGHNESS_METALLIC; +layout(input_attachment_index=3, set=0, binding = 3) uniform subpassInput POSITION; + +struct Light { + vec3 pos; + vec3 color; + float intensity; +}; + +layout(set=0, binding = 4) uniform _TMP { + Light light[50]; + int lightCount; +} lights; + +layout(location=0) out vec4 colorout; +void main() { + vec3 color = subpassLoad(ALBEDO).rgb; + vec3 normal = normalize(subpassLoad(NORMAL).rgb); + vec3 pos = subpassLoad(POSITION).rgb; + vec2 roughnessMetallic = subpassLoad(ROUGHNESS_METALLIC).rg; + vec3 multiplier = vec3(0.05f, 0.05f, 0.05f); + + for(int x = 0; x < lights.lightCount; x++) { + Light lightInfo = lights.light[x]; + vec3 diff = lightInfo.pos - pos; + vec3 l = normalize(lightInfo.pos); + float refl = dot(normal, l); + multiplier += lightInfo.color * refl * lightInfo.intensity; + } + colorout = vec4(color * multiplier, 1); +} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermuteTest/lightPassFrag.json b/ShaderPermute/ShaderPermuteTest/lightPassFrag.json deleted file mode 100644 index 2bee2a2..0000000 --- a/ShaderPermute/ShaderPermuteTest/lightPassFrag.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "settings": { - "shaderType": "fragment" - }, - "codes": [ - { - "code": [ - "#version 450", - "#extension GL_KHR_vulkan_glsl: enable", - "layout(input_attachment_index=0, set=0, binding = 0) uniform subpassInput ALBEDO; ", - "layout(input_attachment_index=1, set=0, binding = 1) uniform subpassInput NORMAL; ", - "layout(input_attachment_index=2, set=0, binding = 2) uniform subpassInput ROUGHNESS_METALLIC; ", - "layout(input_attachment_index=3, set=0, binding = 3) uniform subpassInput POSITION; ", - "struct Light {", - "vec3 pos; ", - "vec3 color; ", - "float intensity; ", - "};", - "layout(set=0, binding = 4) uniform _TMP { ", - "Light light[50]; ", - "int lightCount;", - "} lights; ", - "layout(location=0) out vec4 colorout;", - "void main() { ", - "vec3 color = subpassLoad(ALBEDO).rgb; ", - "vec3 normal = normalize(subpassLoad(NORMAL).rgb); ", - "vec3 pos = subpassLoad(POSITION).rgb; ", - "vec2 roughnessMetallic = subpassLoad(ROUGHNESS_METALLIC).rg;", - "vec3 multiplier = vec3(0.05f, 0.05f, 0.05f);", - "for(int x = 0; x < lights.lightCount; x++) {", - "Light lightInfo = lights.light[x];", - "vec3 diff = lightInfo.pos - pos;", - "vec3 l = normalize(lightInfo.pos);", - "float refl = dot(normal, l); ", - "multiplier += lightInfo.color * refl * lightInfo.intensity;", - "}", - "colorout = vec4(color * multiplier, 1);", - "}" - ] - } - ] -} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermuteTest/lightPassVert.json b/ShaderPermute/ShaderPermuteTest/lightPassVert.json deleted file mode 100644 index d6e595f..0000000 --- a/ShaderPermute/ShaderPermuteTest/lightPassVert.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "settings": { - "shaderType": "vertex", - "targetVersion": "vulkan_1_2", - "targetLanguageVersion": "spv_1_5" - }, - "codes": [ - { - "code": [ - "out gl_PerVertex {", - "vec4 gl_Position;", - "};", - "void main() {", - "gl_Position = vec4(vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2) * 2.0f - 1.0f, 0.0f, 1.0f);", - "}" - ] - } - ] -} \ No newline at end of file diff --git a/ShaderPermute/ShaderPermuteTest/lightPassVert.vert b/ShaderPermute/ShaderPermuteTest/lightPassVert.vert new file mode 100644 index 0000000..0c8fa60 --- /dev/null +++ b/ShaderPermute/ShaderPermuteTest/lightPassVert.vert @@ -0,0 +1,9 @@ +#version 460 + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = vec4(vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2) * 2.0f - 1.0f, 0.0f, 1.0f); +} \ No newline at end of file diff --git a/submodules/CMakeLists.txt b/submodules/CMakeLists.txt new file mode 100644 index 0000000..8522a9c --- /dev/null +++ b/submodules/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required (VERSION 3.20) + +set(BUILD_EXTERNAL OFF) +set(ALLOW_EXTERNAL_SPIRV_TOOLS ON) +set(ENABLE_OPT ON) + +add_subdirectory("SPIRV-Headers") +add_subdirectory("SPIRV-Tools") +add_subdirectory("glslang") diff --git a/submodules/SPIRV-Headers b/submodules/SPIRV-Headers new file mode 160000 index 0000000..2a611a9 --- /dev/null +++ b/submodules/SPIRV-Headers @@ -0,0 +1 @@ +Subproject commit 2a611a970fdbc41ac2e3e328802aed9985352dca diff --git a/submodules/SPIRV-Tools b/submodules/SPIRV-Tools new file mode 160000 index 0000000..33e0256 --- /dev/null +++ b/submodules/SPIRV-Tools @@ -0,0 +1 @@ +Subproject commit 33e02568181e3312f49a3cf33df470bf96ef293a diff --git a/submodules/glslang b/submodules/glslang new file mode 160000 index 0000000..8a85691 --- /dev/null +++ b/submodules/glslang @@ -0,0 +1 @@ +Subproject commit 8a85691a0740d390761a1008b4696f57facd02c4