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);
}