diff --git a/VL.Stride.Runtime/src/Rendering/Effects/EffectShaderNodes.ShaderFX.cs b/VL.Stride.Runtime/src/Rendering/Effects/EffectShaderNodes.ShaderFX.cs index 29caaee9..b00c94cd 100644 --- a/VL.Stride.Runtime/src/Rendering/Effects/EffectShaderNodes.ShaderFX.cs +++ b/VL.Stride.Runtime/src/Rendering/Effects/EffectShaderNodes.ShaderFX.cs @@ -29,8 +29,8 @@ static IVLNodeDescription NewShaderFXNode(this IVLNodeDescriptionFactory factory init: buildContext => { var outputType = shaderMetadata.GetShaderFXOutputType(out var innerType); - var (_effect, _messages, _) = - CreateEffectInstance("ShaderFXEffect", shaderName, shaderMetadata, serviceRegistry, graphicsDevice); + var (_effect, _messages, _) + = CreateEffectInstance("ShaderFXEffect", shaderName, shaderMetadata, serviceRegistry, graphicsDevice); var _inputs = new List(); var _outputs = new List() { buildContext.Pin("Output", outputType) }; @@ -72,6 +72,81 @@ static IVLNodeDescription NewShaderFXNode(this IVLNodeDescriptionFactory factory if (needsWorld) _inputs.Add(new ParameterPinDescription(usedNames, TransformationKeys.World)); + // Build set of variable names with 'stage' qualifier + var stageVariableNames = new HashSet(); + if (shaderMetadata.ParsedShader != null) + { + foreach (var v in shaderMetadata.ParsedShader.Variables) + { + if (v.Qualifiers != null && v.Qualifiers.Contains(Stride.Core.Shaders.Ast.Stride.StrideStorageQualifier.Stage)) + stageVariableNames.Add(v.Name.Text); + } + } + + // Group pins by variable name + var pinsByVarName = new Dictionary>(); + foreach (var input in _inputs) + { + if (input is ParameterPinDescription paramDesc) + { + var varName = paramDesc.Key.GetVariableName(); + if (!pinsByVarName.TryGetValue(varName, out var list)) + pinsByVarName[varName] = list = new List(); + list.Add(paramDesc); + } + } + + // Set pin names: simple if unique, long if ambiguous + foreach (var pair in pinsByVarName) + { + var list = pair.Value; + if (list.Count == 1) + { + // Use simple name + list[0].Name = pair.Key; + } + else + { + // Use long name (already set by default) + // Optionally, could forcefully set to paramDesc.Key.Name for clarity + foreach (var pin in list) + pin.Name = pin.Key.Name; + } + } + + // Deduplicate _inputs (with stage variable logic) + var uniqueInputs = new Dictionary(); + var stagePins = new HashSet(); + foreach (var input in _inputs) + { + if (input is ParameterPinDescription paramDesc) + { + var keyName = paramDesc.Key.Name; + var varName = paramDesc.Key.GetVariableName(); + if (stageVariableNames.Contains(varName)) + { + // Only keep one pin for this stage variable + if (!stagePins.Contains(varName)) + { + uniqueInputs["stage:" + varName] = paramDesc; + stagePins.Add(varName); + } + } + else + { + // Allow multiple pins for non-stage variables (by keyName) + // Prefer pins with longer names for better clarity and disambiguation in case of conflicts + if (!uniqueInputs.TryGetValue(keyName, out var existing) || (existing.Name.Length < paramDesc.Name.Length)) + uniqueInputs[keyName] = paramDesc; + } + } + else + uniqueInputs[input.Name ?? input.GetType().FullName ?? Guid.NewGuid().ToString()] = input; + uniqueInputs[Guid.NewGuid().ToString()] = input; + } + } + _inputs = uniqueInputs.Values.ToList(); + return buildContext.Node( inputs: _inputs, outputs: _outputs,