4 using System.Collections.Generic;
7 using SiliconStudio.Paradox.Shaders.Parser.Ast;
8 using SiliconStudio.Shaders.Ast;
9 using SiliconStudio.Shaders.Ast.Hlsl;
10 using SiliconStudio.Shaders.Utility;
11 using SiliconStudio.Shaders.Visitor;
12 using SiliconStudio.Shaders.Writer;
14 namespace SiliconStudio.
Paradox.Shaders.Parser.Mixins
21 private const string DefaultNameSpace =
"SiliconStudio.Paradox.Effects.Modules";
23 private const string BlockContextTag =
"BlockContextTag";
25 private readonly
Shader shader;
27 private int localVariableCount;
39 throw new ArgumentNullException(
"shader");
42 throw new ArgumentNullException(
"logging");
45 this.logging = logging;
46 EnablePreprocessorLine =
true;
59 var shader = ParadoxShaderParser.PreProcessAndParse(pdxfxShaderCode, filePath);
65 if (shaderKeyGenerator.Run())
67 return shaderKeyGenerator.Text;
69 throw new InvalidOperationException(loggerResult.ToString());
75 public override bool Run()
79 if (logging.HasErrors)
86 FixShaderClassTypeWithNoNameSpace();
88 var blockVisitor =
new ShaderBlockVisitor(
this, logging);
89 blockVisitor.Run(shader);
92 if (logging.HasErrors)
98 WriteLine(
"// <auto-generated>");
99 WriteLine(
"// Do not edit this file yourself!");
101 WriteLine(
"// This code was generated by Paradox Shader Mixin Code Generator.");
102 WriteLine(
"// To generate it yourself, please install SiliconStudio.Paradox.VisualStudio.Package .vsix");
103 WriteLine(
"// and re-save the associated .pdxfx.");
104 WriteLine(
"// </auto-generated>");
108 if (!blockVisitor.HasMixin && !blockVisitor.HasShaderClassType)
110 WriteLine(
"// Nothing to generate");
117 WriteLine(
"using System;");
118 WriteLine(
"using SiliconStudio.Core;");
119 WriteLine(
"using SiliconStudio.Paradox.Effects;");
120 WriteLine(
"using SiliconStudio.Paradox.Graphics;");
121 WriteLine(
"using SiliconStudio.Paradox.Shaders;");
122 WriteLine(
"using SiliconStudio.Core.Mathematics;");
123 WriteLine(
"using Buffer = SiliconStudio.Paradox.Graphics.Buffer;");
127 VisitDynamic(shader);
130 if (logging.HasErrors)
143 if (TryParameters(assignmentExpression.
Target, out typeTarget, out typeMember))
145 Write(
"context.SetParam(").Write(typeTarget).Write(
".").Write(typeMember).Write(
", ");
146 VisitDynamic(assignmentExpression.
Value);
151 base.Visit(assignmentExpression);
159 if (TryParameters(memberReferenceExpression, out typeTarget, out typeMember))
161 Write(
"context.GetParam(").Write(typeTarget).Write(
".").Write(typeMember).Write(
")");
165 base.Visit(memberReferenceExpression);
176 Write(
"[DataContract]");
177 WriteLinkLine(enumType);
178 Write(
"public enum");
180 Write(enumType.
Name);
186 WriteLinkLine(fieldDeclaration);
187 VisitDynamic(fieldDeclaration);
190 CloseBrace(
false).Write(
";").WriteLine();
201 Write(
"[DataContract]");
202 WriteLinkLine(paramsBlock);
203 Write(
"public partial class");
205 Write(paramsBlock.
Name);
207 Write(
": ShaderMixinParameters");
213 var variable = parameter.Content as
Variable;
214 if (variable == null)
217 WriteLinkLine(parameter);
218 VisitDynamic(variable);
221 CloseBrace(
false).Write(
";").WriteLine();
233 if (keywordExpression.
Name.
Text ==
"discard")
239 base.Visit(keywordExpression);
246 Write(
"public static partial class ");
262 if (IsStringInList(type.
Name,
"StructuredBuffer",
"RWStructuredBuffer",
"ConsumeStructuredBuffer",
"AppendStructuredBuffer"))
266 ProcessInitialValueStatus =
false;
276 WriteLinkLine(forEachStatement);
278 if (forEachStatement.
Variable == null)
280 localVariableCount++;
284 if (!TryParameters(forEachStatement.
Collection, out parameterType, out parameterMember))
286 Write(
@"#error ""Unexpected parameter for 'foreach params' [");
288 WriteLine(
@"]. Expecting single property access""");
292 string variable =
"____" + localVariableCount;
293 Write(
"foreach(").Write(
"var ").Write(variable).Write(
" in ");
298 if (statement == null)
300 statement =
new BlockStatement {Span = forEachStatement.Body.Span};
301 statement.Statements.Add(forEachStatement.Body);
305 VisitDynamic(statement);
307 localVariableCount--;
312 VisitDynamic(forEachStatement.
Variable);
317 VisitDynamic(forEachStatement.
Body);
328 WriteLinkLine(shaderBlock);
329 currentBlock = shaderBlock;
331 VariableAsParameterKey =
false;
334 Write(
"internal static partial class ShaderMixins");
337 Write(
"internal partial class");
339 Write(shaderBlock.
Name);
341 Write(
" : IShaderMixinBuilder");
345 Write(
"public void Generate(ShaderMixinSourceTree mixin, ShaderMixinContext context)");
351 VisitDynamic(statement);
357 WriteLine(
"[ModuleInitializer]");
358 WriteLine(
"internal static void __Initialize__()");
361 Write(
"ShaderMixinManager.Register(\"").Write(shaderBlock.Name).Write(
"\", new ").Write(shaderBlock.Name).WriteLine(
"());");
369 VariableAsParameterKey =
true;
382 var genericParameters =
new List<Expression>();
384 switch (mixinStatement.
Type)
386 case MixinStatementType.Default:
387 ExtractGenericParameters(mixinStatement.
Value, out mixinName, genericParameters);
389 WriteLinkLine(mixinStatement);
390 Write(
"context.Mixin(mixin, ");
391 WriteMixinName(mixinName);
392 WriteGenericParameters(genericParameters);
396 case MixinStatementType.Child:
401 var targetExpression = mixinStatement.Value;
403 if (assignExpression != null)
405 targetExpression = assignExpression.Value;
408 ExtractGenericParameters(targetExpression, out mixinName, genericParameters);
409 var childName = assignExpression != null ? assignExpression.Target : mixinName;
412 WriteLinkLine(mixinStatement);
413 Write(
"var __subMixin = new ShaderMixinSourceTree() { Name = ");
414 WriteMixinName(childName);
415 WriteLine(
", Parent = mixin };");
416 WriteLine(
"mixin.Children.Add(__subMixin);");
418 WriteLinkLine(mixinStatement);
419 WriteLine(
"context.BeginChild(__subMixin);");
421 WriteLinkLine(mixinStatement);
422 Write(
"context.Mixin(__subMixin, ");
423 WriteMixinName(mixinName);
424 WriteGenericParameters(genericParameters);
427 WriteLinkLine(mixinStatement);
428 WriteLine(
"context.EndChild();");
434 case MixinStatementType.Remove:
435 ExtractGenericParameters(mixinStatement.
Value, out mixinName, genericParameters);
437 WriteLinkLine(mixinStatement);
438 Write(
"context.RemoveMixin(mixin, ");
439 WriteMixinName(mixinName);
440 if (genericParameters.Count > 0)
442 logging.Error(
"Removing with generic parameters is not supported", mixinStatement.Span);
447 case MixinStatementType.Clone:
448 WriteLinkLine(mixinStatement);
449 WriteLine(
"context.CloneProperties();");
451 WriteLinkLine(mixinStatement);
452 WriteLine(
"mixin.Mixin.CloneFrom(mixin.Parent.Mixin);");
455 case MixinStatementType.Macro:
456 WriteLinkLine(mixinStatement);
457 var context = (ShaderBlockContext)currentBlock.GetTag(BlockContextTag);
458 assignExpression = mixinStatement.Value as AssignmentExpression;
462 if (assignExpression != null)
464 macroName = assignExpression.Target;
469 macroValue = assignExpression.Value;
474 if (variableReference == null || !(variableReference.Target is
VariableReferenceExpression) || !context.DeclaredParameters.Contains((((VariableReferenceExpression)variableReference.Target).Name.Text)))
476 logging.Error(
"Invalid syntax. Expecting: mixin macro Parameters.NameOfProperty or mixin macro nameOfProperty = value", mixinStatement.Span);
478 macroValue = mixinStatement.Value;
483 macroValue = mixinStatement.Value;
487 Write(
"mixin.Mixin.AddMacro(");
488 VisitDynamic(macroName);
490 VisitDynamic(macroValue);
494 case MixinStatementType.Compose:
495 assignExpression = mixinStatement.Value as AssignmentExpression;
496 if (assignExpression == null)
498 logging.Error(
"Expecting assign expression for composition", mixinStatement.Value.Span);
502 var addCompositionFunction =
"AddComposition";
507 addCompositionFunction =
"AddCompositionToArray";
510 ExtractGenericParameters(assignExpression.
Value, out mixinName, genericParameters);
514 WriteLinkLine(mixinStatement);
515 WriteLine(
"var __subMixin = new ShaderMixinSourceTree() { Parent = mixin };");
517 WriteLinkLine(mixinStatement);
518 Write(
"context.Mixin(__subMixin, ");
519 WriteMixinName(mixinName);
520 WriteGenericParameters(genericParameters);
523 Write(
"mixin.Mixin.");
524 Write(addCompositionFunction);
526 WriteMixinName(assignExpression.
Target);
527 Write(
", __subMixin.Mixin");
542 WriteLinkLine(usingStatement);
543 Write(
"using ").Write(usingStatement.Name).WriteLine(
";");
553 if (usingParametersStatement.
Body == null)
558 if (!TryParameters(usingParametersStatement.
Name, out parameterType, out parameterMember))
560 Write(
@"#error ""Unexpected parameter for 'using params' [");
561 VisitDynamic(usingParametersStatement.
Name);
562 WriteLine(
@"]. Expecting single property access""");
566 AddPushPopParameters(usingParametersStatement.
Body, parameterType, parameterMember, usingParametersStatement.
Name, usingParametersStatement.
Span);
568 Visit(usingParametersStatement.
Body);
575 blockStatement.Statements.Insert(0, pushStatement);
577 blockStatement.Statements.Add(popStatement);
585 if (memberReferenceExpression == null)
590 bool foundDeclaredParameters =
false;
591 if (currentBlock != null)
593 var context = (ShaderBlockContext)currentBlock.
GetTag(BlockContextTag);
594 HashSet<string> usings = context.DeclaredParameters;
596 if (name != null && usings.Contains(name.Name))
599 member = memberReferenceExpression.Member;
600 foundDeclaredParameters =
true;
604 return foundDeclaredParameters;
607 private void ExtractGenericParameters(
Expression expression, out
Expression mixinName, List<Expression> genericParametersOut)
609 if (genericParametersOut == null)
611 throw new ArgumentNullException(
"genericParametersOut");
614 mixinName = expression;
615 genericParametersOut.Clear();
617 var varExp = expression as VariableReferenceExpression;
622 if (identifierGeneric != null)
624 mixinName =
new VariableReferenceExpression(identifierGeneric.Text);
626 foreach (
Identifier subIdentifier
in identifierGeneric.Identifiers)
629 if (identifierDot != null)
631 if (identifierDot.Identifiers.Count == 2)
633 genericParametersOut.Add(
new MemberReferenceExpression(
new VariableReferenceExpression(identifierDot.Identifiers[0]), identifierDot.Identifiers[1]));
637 logging.Error(
"Unsupported identifier in generic used for mixin", identifierDot.Span);
642 var literalIdentifier = (LiteralIdentifier)subIdentifier;
646 else if (subIdentifier.GetType() == typeof(
Identifier))
648 genericParametersOut.Add(
new VariableReferenceExpression(subIdentifier));
652 logging.Error(
"Unsupported identifier in generic used for mixin", subIdentifier.Span);
661 foreach (
Expression genericParameter
in genericParameters)
664 VisitDynamic(genericParameter);
668 private void WriteMixinName(
Expression mixinName)
671 if (mixinName is VariableReferenceExpression)
675 VisitDynamic(mixinName);
676 if (mixinName is VariableReferenceExpression)
682 private void LogErrors()
684 foreach (var reportMessage
in logging.Messages)
688 Write(
"#error ").WriteLine(reportMessage.ToString());
693 private class ShaderBlockContext
695 public readonly HashSet<string> DeclaredParameters =
new HashSet<string>();
698 private void FixShaderClassTypeWithNoNameSpace()
700 for (
int i = 0; i < shader.Declarations.Count; i++)
702 var node = shader.Declarations[i];
706 nameSpaceBlock.Body.Add(node);
707 shader.Declarations[i] = nameSpaceBlock;
718 private ShaderBlockContext currentContext;
720 private readonly ShaderKeyGeneratorBase parent;
722 private bool isVisitingShaderClassType;
724 public ShaderBlockVisitor(ShaderKeyGeneratorBase parent,
LoggerResult logging)
727 this.parent = parent;
728 this.logging = logging;
731 public bool HasMixin {
get;
private set; }
733 public bool HasShaderClassType {
get;
private set; }
735 public void Run(
Shader shader)
737 VisitDynamic(shader);
749 isVisitingShaderClassType =
true;
752 if (isVisitingShaderClassType && !HasShaderClassType)
754 if (parent.IsParameterKey(variable))
756 HasShaderClassType =
true;
760 isVisitingShaderClassType =
false;
769 currentContext =
new ShaderBlockContext();
770 shaderBlock.SetTag(BlockContextTag, currentContext);
774 VisitDynamic(statement);
776 currentContext = null;
782 if (currentContext == null)
784 logging.Error(
"Unexpected 'using params' outside of shader block declaration", usingParametersStatement.Span);
788 HashSet<string> usings = currentContext.DeclaredParameters;
791 if (usingParametersStatement.
Body == null)
793 var simpleName = usingParametersStatement.Name as VariableReferenceExpression;
794 if (simpleName != null)
796 string typeName = simpleName.Name.Text;
798 if (usings.Contains(typeName))
800 logging.Error(
"Unexpected declaration of using params. This variable is already declared in this scope", usingParametersStatement.Span);
804 usings.Add(typeName);
810 VisitDynamic(usingParametersStatement.
Body);
virtual void Visit(EnumType enumType)
Visits the specified enum type.
Expression Name
Gets or sets the name.
virtual void Visit(ForEachStatement forEachStatement)
Visits the specified for each statement.
ShaderMixinCodeGen(Shader shader, LoggerResult logging)
Initializes a new instance of the ShaderMixinCodeGen class.
virtual void Visit(ShaderBlock shaderBlock)
Visits the specified shader block.
BlockStatement Body
Gets or sets the body.
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult
List< Expression > Values
Gets or sets the fields.
SourceSpan Span
Gets or sets the source span.
Keyword expression statement like continue; break; discard;
string Text
Gets or sets the name.
BlockStatement Body
Gets or sets the body.
Expression Collection
Gets or sets the condition.
Identifier Name
Gets or sets the name of this declaration
A class to collect parsing/expression messages.
Expression Target
Gets or sets the target receving the assigment.
AssignmentOperator
Assignment operator used in assignment expression (a = b) or statements (a = b;)
override void Visit(MemberReferenceExpression memberReferenceExpression)
virtual void Visit(UsingStatement usingStatement)
Visits the specified using statement.
Expression Value
Gets or sets the value of the assigment..
override bool Run()
Runs the code generation. Results is accessible from ShaderWriter.Text property.
A declaration inside a statement.
override string ToString()
Base root class for all statements.
virtual void Visit(MixinStatement mixinStatement)
Visits the specified mixin statement.
Variable Variable
Gets or sets the initializer.
Expression Value
Gets or sets the target of this mixin.
A member reference in the form {this}.{Name}
Statement Body
Gets or sets the condition.
StatementList Statements
Gets or sets the statements.
ReportMessageLevel
Level of a ReportMessage.
Identifier Name
Gets or sets the name.
virtual void Visit(UsingParametersStatement usingParametersStatement)
Visits the specified using parameters statement.
virtual void Visit(ParametersBlock paramsBlock)
Visits the specified params block.
virtual void Visit(ShaderClassType shader)
Base class for all generic types.
static string GenerateCsharp(string pdxfxShaderCode, string filePath)
Generates the csharp code from a pdxfx file.
List< Node > Members
Gets or sets the members.
A using params statement.
Toplevel container of a shader parsing result.
A generic identifier in the form Typename
A reference to a variable.
MixinStatementType Type
Gets or sets the type.
AssignmentOperator Operator
Gets or sets the operator.
Identifier Name
Gets or sets the type name.
This class is responsible to generate associated C# code from an effect file (extension: pdxfx)...
virtual void Visit(GenericType< ObjectType > type)
override void Visit(AssignmentExpression assignmentExpression)
object GetTag(object tagKey)
Gets a tag value associated to this node..
virtual void Visit(KeywordExpression keywordExpression)
Visits the specified keyword expression.