diff --git a/Apps/Playground/ReferenceImages/iridescence-nme.png b/Apps/Playground/ReferenceImages/iridescence-nme.png new file mode 100644 index 000000000..688275065 Binary files /dev/null and b/Apps/Playground/ReferenceImages/iridescence-nme.png differ diff --git a/Apps/Playground/ReferenceImages/nme-multi-build.png b/Apps/Playground/ReferenceImages/nme-multi-build.png new file mode 100644 index 000000000..776109f1e Binary files /dev/null and b/Apps/Playground/ReferenceImages/nme-multi-build.png differ diff --git a/Apps/Playground/Scripts/config.json b/Apps/Playground/Scripts/config.json index c2fa039f4..de81518f4 100644 --- a/Apps/Playground/Scripts/config.json +++ b/Apps/Playground/Scripts/config.json @@ -1,6 +1,17 @@ { "root": "https://cdn.babylonjs.com", "tests": [ + { + "title": "Iridescence NME", + "playgroundId": "#2FDQT5#1507", + "referenceImage": "iridescence-nme.png" + }, + { + "title": "NME Multi Build", + "playgroundId": "#D8AK3Z#104", + "referenceImage": "nme-multi-build.png", + "renderCount": 50 + }, { "title": "EXR Loader", "playgroundId": "#4RN0VF#151", diff --git a/Plugins/NativeEngine/Source/ShaderCompilerD3D.h b/Plugins/NativeEngine/Source/ShaderCompilerD3D.h index 93bd00eff..e359a13be 100644 --- a/Plugins/NativeEngine/Source/ShaderCompilerD3D.h +++ b/Plugins/NativeEngine/Source/ShaderCompilerD3D.h @@ -111,6 +111,7 @@ namespace Babylon ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsD3D(program, ids, vertexAttributeRenaming); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::InvertYDerivativeOperands(program); + ShaderCompilerTraversers::ReassignBindingToSamplers(program); // clang-format off static const spirv_cross::HLSLVertexAttributeRemap attributes[] = { diff --git a/Plugins/NativeEngine/Source/ShaderCompilerMetal.cpp b/Plugins/NativeEngine/Source/ShaderCompilerMetal.cpp index e8a13c023..3116f8481 100644 --- a/Plugins/NativeEngine/Source/ShaderCompilerMetal.cpp +++ b/Plugins/NativeEngine/Source/ShaderCompilerMetal.cpp @@ -107,7 +107,8 @@ namespace Babylon ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsMetal(program, ids, vertexAttributeRenaming); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::InvertYDerivativeOperands(program); - + ShaderCompilerTraversers::ReassignBindingToSamplers(program); + std::string vertexGLSL(vertexSource.data(), vertexSource.size()); auto [vertexParser, vertexCompiler] = CompileShader(program, EShLangVertex, vertexGLSL); diff --git a/Plugins/NativeEngine/Source/ShaderCompilerTraversers.cpp b/Plugins/NativeEngine/Source/ShaderCompilerTraversers.cpp index f3d0633c1..dd98d187a 100644 --- a/Plugins/NativeEngine/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/NativeEngine/Source/ShaderCompilerTraversers.cpp @@ -901,6 +901,78 @@ namespace Babylon::ShaderCompilerTraversers TIntermediate* m_intermediate{}; }; + + /// + /// https://github.com/BabylonJS/BabylonNative/issues/1411 + /// When a same sampler is declared in VS and FS, GLSlang assign a different binding location for VS and FS. + /// Max binding location is 16 so if more than 8 samplers are declared then D3Dcompile will not compile the shader. + /// This is the case for NME shaders for example. + /// The following traverser list samplers from the VS (and their binding location) and set the same binding location + /// in FS if it's present. If a sampler is present in FS but not in VS, then a binding location id will be used and incremented. + /// potential solution replacements: + /// - do not expose samplers in generated nme shaders (this doesn't fix the issue if samplers are declared in VS and FS for genuine reasons) + /// Apart from potential regressions and more complex TS code, the problem resides in D3D world only. + /// - use spirv optimizer tool to remove unused samplers: it will not fix the issue, binary will be bigger and compilation time will be longer. + /// + class ReassignBindingToSamplersTraverser : public TIntermTraverser + { + public: + static void Traverse(TProgram& program) + { + ReassignBindingToSamplersTraverser reassignBindingToSamplersTraverser{ }; + program.getIntermediate(EShLangVertex)->getTreeRoot()->traverse(&reassignBindingToSamplersTraverser); + reassignBindingToSamplersTraverser.m_fragmentPass = true; + program.getIntermediate(EShLangFragment)->getTreeRoot()->traverse(&reassignBindingToSamplersTraverser); + } + + protected: + void visitSymbol(TIntermSymbol* symbol) override { + // Check if the symbol is a sampler + const TType& type = symbol->getType(); + if (type.getBasicType() == EbtSampler) { + TQualifier& qualifier = symbol->getWritableType().getQualifier(); + std::string name{symbol->getName().c_str()}; + static const std::string suffix = "Texture"; + // appends 'Texture' to the name see SamplerSplitterTraverser for differenciation + bool textureEnds = name.size() >= suffix.size() && name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0; + if (!textureEnds) + { + name += suffix; + } + if (m_fragmentPass) + { + // fragment pass + auto iter = m_samplerLayoutBinding.find(name); + if (iter == m_samplerLayoutBinding.end()) + { + qualifier.layoutBinding = ++m_nextLayoutBinding; + m_samplerLayoutBinding[name] = qualifier.layoutBinding; + } + else + { + qualifier.layoutBinding = iter->second; + } + } + else + { + if (qualifier.hasBinding()) + { + // vertex pass + m_samplerLayoutBinding[name] = qualifier.layoutBinding; + m_nextLayoutBinding = std::max(m_nextLayoutBinding, qualifier.layoutBinding); + } + } + } + } + + private: + ReassignBindingToSamplersTraverser() + { + } + std::map m_samplerLayoutBinding; + bool m_fragmentPass{}; + unsigned int m_nextLayoutBinding{0}; + }; } ScopeT MoveNonSamplerUniformsIntoStruct(TProgram& program, IdGenerator& ids) @@ -937,4 +1009,9 @@ namespace Babylon::ShaderCompilerTraversers { InvertYDerivativeOperandsTraverser::Traverse(program); } + + void ReassignBindingToSamplers(glslang::TProgram& program) + { + ReassignBindingToSamplersTraverser::Traverse(program); + } } diff --git a/Plugins/NativeEngine/Source/ShaderCompilerTraversers.h b/Plugins/NativeEngine/Source/ShaderCompilerTraversers.h index a24b42710..97e880c4b 100644 --- a/Plugins/NativeEngine/Source/ShaderCompilerTraversers.h +++ b/Plugins/NativeEngine/Source/ShaderCompilerTraversers.h @@ -82,4 +82,6 @@ namespace Babylon::ShaderCompilerTraversers /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L44-L45 /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L62-L65 void InvertYDerivativeOperands(glslang::TProgram& program); + + void ReassignBindingToSamplers(glslang::TProgram& program); }