Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions VL.Stride.Runtime/src/Rendering/Effects/EffectPins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,26 @@ class ParameterPinDescription : EffectPinDescription
public readonly ParameterKey Key;
public readonly int Count;
public readonly bool IsPermutationKey;

// This value gets passed to the live pin - for example SetVar<Vector4> (and not just the plain vector)
public readonly object RuntimeDefaultValue;
private readonly string? nodeInstanceId;
private readonly bool isStage;
private readonly ParameterKey internalKey;

public ParameterPinDescription(HashSet<string> usedNames, ParameterKey key, int count = 1, object compilationDefaultValue = null, bool isPermutationKey = false, string name = null, Type typeInPatch = null, object runtimeDefaultValue = null)
// Accept isStage as a parameter, determined by the caller from shader metadata/variable attributes
public ParameterPinDescription(HashSet<string> usedNames, ParameterKey key, int count = 1, object compilationDefaultValue = null, bool isPermutationKey = false, string name = null, Type typeInPatch = null, object runtimeDefaultValue = null, string? nodeInstanceId = null, bool isStage = false)
{
Key = key;
IsPermutationKey = isPermutationKey;
Count = count;
this.nodeInstanceId = nodeInstanceId;
this.isStage = isStage;
// User-facing name should not include nodeInstanceId
Name = name ?? key.GetPinName(usedNames);
// Internal shader key: unique per node unless 'stage'
if (!isStage && nodeInstanceId != null)
internalKey = key.WithUniqueInstance(nodeInstanceId);
else
internalKey = key;
var elementType = typeInPatch ?? key.PropertyType;
compilationDefaultValue = compilationDefaultValue ?? key.DefaultValueMetadata?.GetDefaultValue();
// TODO: This should be fixed in Stride
Expand All @@ -110,12 +120,11 @@ public ParameterPinDescription(HashSet<string> usedNames, ParameterKey key, int

public override string Name { get; }
public override Type Type { get; }

// The plain value read by the compiler, for example in case of IComputeValue<Vector4> this would be just the vector itself
public override object DefaultValueBoxed { get; }
public override IVLPin CreatePin(GraphicsDevice graphicsDevice, ParameterCollection parameters)
{
return EffectPins.CreatePin(graphicsDevice, parameters, Key, Count, IsPermutationKey, RuntimeDefaultValue, Type);
// Use the internalKey for shader parameter assignment
return EffectPins.CreatePin(graphicsDevice, parameters, internalKey, Count, IsPermutationKey, RuntimeDefaultValue, Type);
}

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ static IVLNodeDescription NewShaderFXNode(this IVLNodeDescriptionFactory factory
invalidated: changes,
init: buildContext =>
{
// Generate a unique node instance identifier
var nodeInstanceId = Guid.NewGuid().ToString("N");
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<IVLPinDescription>();
var _outputs = new List<IVLPinDescription>() { buildContext.Pin("Output", outputType) };
Expand All @@ -44,29 +46,30 @@ static IVLNodeDescription NewShaderFXNode(this IVLNodeDescriptionFactory factory
var key = parameter.Key;
var name = key.Name;

// Expose World only - all other world dependent parameters we can compute on our own
if (WellKnownParameters.PerDrawMap.ContainsKey(name))
{
// Expose World only - all other world dependent parameters we can compute on our own
needsWorld = true;
continue;
}

_inputs.Add(CreatePinDescription(in parameter, usedNames, shaderMetadata));
// Pass nodeInstanceId to pin description for unique naming
_inputs.Add(CreatePinDescription(in parameter, usedNames, shaderMetadata, nodeInstanceId: nodeInstanceId));
}

// local input values
foreach (var key in shaderMetadata.ParsedShader?.GetUniformInputs() ?? Enumerable.Empty<ParameterKey>())
{
var name = key.Name;

// Expose World only - all other world dependent parameters we can compute on our own
if (WellKnownParameters.PerDrawMap.ContainsKey(name))
{
// Expose World only - all other world dependent parameters we can compute on our own
needsWorld = true;
continue;
}

_inputs.Add(CreatePinDescription(key, 1, usedNames, shaderMetadata));
_inputs.Add(CreatePinDescription(key, 1, usedNames, shaderMetadata, nodeInstanceId: nodeInstanceId));
}

if (needsWorld)
Expand Down
17 changes: 13 additions & 4 deletions VL.Stride.Runtime/src/Rendering/Effects/EffectShaderNodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,19 @@ static IEnumerable<IVLNodeDescription> GetNodeDescriptions(ServiceRegistry servi
}
}

private static ParameterPinDescription CreatePinDescription(in ParameterKeyInfo keyInfo, HashSet<string> usedNames, ShaderMetadata shaderMetadata, string? name = null, bool? isOptionalOverride = default)
private static ParameterPinDescription CreatePinDescription(in ParameterKeyInfo keyInfo, HashSet<string> usedNames, ShaderMetadata shaderMetadata, string? name = null, bool? isOptionalOverride = default, string? nodeInstanceId = null)
{
return CreatePinDescription(keyInfo.Key, keyInfo.Count, usedNames, shaderMetadata, name, isOptionalOverride);
// Determine if this variable is a 'stage' variable using the Stride shader AST variable's Qualifiers
bool isStage = false;
if (shaderMetadata.ParsedShader != null && shaderMetadata.ParsedShader.VariablesByName.TryGetValue(keyInfo.Key.GetVariableName(), out var variable))
{
// Check for 'stage' in the Qualifiers collection (Stride AST)
isStage = variable.Qualifiers != null && variable.Qualifiers.Any(q => q.Name.Text.Equals("stage", StringComparison.OrdinalIgnoreCase));
}
return CreatePinDescription(keyInfo.Key, keyInfo.Count, usedNames, shaderMetadata, name, isOptionalOverride, nodeInstanceId, isStage);
}

private static ParameterPinDescription CreatePinDescription(ParameterKey key, int count, HashSet<string> usedNames, ShaderMetadata shaderMetadata, string? name = null, bool? isOptionalOverride = default)
private static ParameterPinDescription CreatePinDescription(ParameterKey key, int count, HashSet<string> usedNames, ShaderMetadata shaderMetadata, string? name = null, bool? isOptionalOverride = default, string? nodeInstanceId = null, bool isStage = false)
{
var typeInPatch = shaderMetadata.GetPinType(key, out var runtimeDefaultValue, out var compilationDefaultValue);
shaderMetadata.GetPinDocuAndVisibility(key, out var summary, out var remarks, out var isOptional);
Expand All @@ -240,7 +247,9 @@ private static ParameterPinDescription CreatePinDescription(ParameterKey key, in
compilationDefaultValue: compilationDefaultValue,
name: name,
typeInPatch: typeInPatch,
runtimeDefaultValue: runtimeDefaultValue)
runtimeDefaultValue: runtimeDefaultValue,
nodeInstanceId: nodeInstanceId,
isStage: isStage)
{
IsVisible = !isOptional,
Summary = summary,
Expand Down
27 changes: 26 additions & 1 deletion VL.Stride.Runtime/src/Rendering/Effects/EffectUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,13 @@ internal static void SelectPin<TPin>(this IVLPin[] pins, IVLPinDescription descr
pin = pins.OfType<TPin>().FirstOrDefault(p => p.Name == description.Name);
}

public static string GetPinName(this ParameterKey key, HashSet<string> usedNames)
public static string GetPinName(this ParameterKey key, HashSet<string> usedNames, string? nodeInstanceId = null)
{
var variableName = key.GetVariableName();
var shaderName = key.GetShaderName();
var camelCasedName = FCamelCasePattern.Replace(variableName, match => $"{match.Value[0]} {match.Value[1]}");
var result = char.ToUpper(camelCasedName[0]) + camelCasedName.Substring(1);
// Do NOT append nodeInstanceId to the user-facing name
if (usedNames.Add(result))
return result;
return $"{shaderName} {result}";
Expand Down Expand Up @@ -795,4 +796,28 @@ enum TexturingParameters
Texture8TexelSize,
Texture9TexelSize
}

static class ParameterKeyExtensions
{
public static ParameterKey WithUniqueInstance(this ParameterKey key, string nodeInstanceId)
{
// Only change the key name for uniqueness, keep all other properties
var uniqueName = $"{key.Name}__{nodeInstanceId}";
var keyType = key.GetType();
// Try to create a new key of the same type with the unique name
if (keyType.IsGenericType)
{
var genericDef = keyType.GetGenericTypeDefinition();
var arg = keyType.GetGenericArguments()[0];
if (genericDef == typeof(ValueParameterKey<>))
return (ParameterKey)Activator.CreateInstance(typeof(ValueParameterKey<>).MakeGenericType(arg), uniqueName, key.DefaultValueMetadata);
if (genericDef == typeof(PermutationParameterKey<>))
return (ParameterKey)Activator.CreateInstance(typeof(PermutationParameterKey<>).MakeGenericType(arg), uniqueName, key.DefaultValueMetadata);
if (genericDef == typeof(ObjectParameterKey<>))
return (ParameterKey)Activator.CreateInstance(typeof(ObjectParameterKey<>).MakeGenericType(arg), uniqueName, key.DefaultValueMetadata);
}
// fallback: just return the original key
return key;
}
}
}