Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
StripVisitor.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using SiliconStudio.Shaders.Ast;
7 using SiliconStudio.Shaders.Ast.Hlsl;
8 
9 namespace SiliconStudio.Shaders.Visitor
10 {
11  /// <summary>
12  /// The strip visitor collects all function and declaration used by a set of entrypoints
13  /// and remove any unreferenced/unused declaration.
14  /// </summary>
15  public class StripVisitor : ShaderVisitor
16  {
17  private Dictionary<Node, HashSet<Node>> indirectReferences;
18  private readonly string[] entryPoints;
19 
20  /// <summary>
21  /// Initializes a new instance of the <see cref="StripVisitor"/> class.
22  /// </summary>
23  /// <param name="entryPoints">The entry points to filter.</param>
24  public StripVisitor(params string[] entryPoints) : base(true, true)
25  {
26  this.entryPoints = entryPoints;
27  this.StripUniforms = true;
28  this.KeepConstantBuffers = true;
29  }
30 
31  public bool StripUniforms { get; set; }
32 
33  public bool KeepConstantBuffers { get; set; }
34 
35  [Visit]
36  public void Visit(MethodInvocationExpression methodInvocationExpression)
37  {
38  Visit((Node)methodInvocationExpression);
39  AddReference(GetDeclarationContainer(), (Node)methodInvocationExpression.TypeInference.Declaration);
40  }
41 
42  [Visit]
43  public void Visit(VariableReferenceExpression variableReferenceExpression)
44  {
45  Visit((Node)variableReferenceExpression);
46  AddReference(GetDeclarationContainer(), (Node)variableReferenceExpression.TypeInference.Declaration);
47  }
48 
49  private ConstantBuffer currentConstantBuffer = null;
50 
51  [Visit]
52  public void Visit(ConstantBuffer constantBuffer)
53  {
54  currentConstantBuffer = constantBuffer;
55  Visit((Node)constantBuffer);
56  currentConstantBuffer = null;
57  }
58 
59  protected override bool PreVisitNode(Node node)
60  {
61  // Sometimes it is desirable that constant buffer are not modified so that
62  // they can be shared between different stages, even if some variables are unused.
63  // In this case, use symetric reference so that using a constant buffer will include all its variables.
64  if (KeepConstantBuffers && currentConstantBuffer != null && node is IDeclaration)
65  {
66  AddReference(node, currentConstantBuffer);
67  AddReference(currentConstantBuffer, node);
68  }
69 
70  return base.PreVisitNode(node);
71 
72  }
73 
74  [Visit]
75  public void Visit(Parameter parameter)
76  {
77  Visit((Node)parameter);
78  var containers = GetDeclarationContainers();
79  var container = containers[containers.Count - 2];
80  AddReference((Node)container, parameter);
81  }
82 
83  [Visit]
84  public void Visit(TypeBase typeReference)
85  {
86  Visit((Node)typeReference);
87  AddReference(GetDeclarationContainer(), (Node)typeReference.TypeInference.Declaration);
88  }
89 
90  [Visit]
91  public void Visit(MethodDefinition methodDefinition)
92  {
93  Visit((Node)methodDefinition);
94 
95  // If a method definition has a method declaration, we must link them together
96  if (!ReferenceEquals(methodDefinition.Declaration, methodDefinition))
97  {
98  AddReference(methodDefinition.Declaration, methodDefinition);
99  }
100  }
101 
102  [Visit]
103  public void Visit(Variable variable)
104  {
105  Visit((Node)variable);
106  var containers = GetDeclarationContainers();
107  if (containers.Count > 1)
108  {
109  var container = containers[containers.Count - 2];
110  AddReference((Node)container, variable);
111  }
112  }
113 
114  [Visit]
115  public void Visit(Shader shader)
116  {
117  indirectReferences = new Dictionary<Node, HashSet<Node>>();
118 
119  // Visit AST.
120  Visit((Node) shader);
121 
122  // Get list of function referenced (directly or indirectly) by entry point.
123  // Using hashset and recursion to avoid cycle.
124  var collectedReferences = new List<Node>();
125  foreach (var entryPointName in entryPoints)
126  {
127  // Find entry point
128  var entryPoint = shader.Declarations.OfType<MethodDefinition>().FirstOrDefault(x => x.Name == entryPointName);
129 
130  if (entryPoint == null)
131  throw new ArgumentException(string.Format("Could not find entry point named {0}", entryPointName));
132 
133  CollectReferences(collectedReferences, entryPoint);
134  }
135 
136  StripDeclarations(shader.Declarations, collectedReferences, StripUniforms);
137  }
138 
139  /// <summary>
140  /// Strips the declarations.
141  /// </summary>
142  /// <param name="nodes">The nodes.</param>
143  /// <param name="collectedReferences">The collected references.</param>
144  private static void StripDeclarations(IList<Node> nodes, ICollection<Node> collectedReferences, bool stripUniforms)
145  {
146  // Remove all the unreferenced function amd types declaration from the shader.
147  for (int i = 0; i < nodes.Count; i++)
148  {
149  var declaration = nodes[i];
150  if (declaration is Variable)
151  {
152  var variableDeclaration = (Variable)declaration;
153  if ((!stripUniforms && variableDeclaration.Qualifiers.Contains(Ast.StorageQualifier.Uniform)) || variableDeclaration.Name.Text == "ParadoxFlipRendertarget")
154  continue;
155 
156  if (variableDeclaration.IsGroup)
157  {
158  variableDeclaration.SubVariables.RemoveAll(x => !collectedReferences.Contains(x));
159  if (variableDeclaration.SubVariables.Count == 0)
160  {
161  nodes.RemoveAt(i);
162  i--;
163  }
164  }
165  else if (!collectedReferences.Contains(declaration))
166  {
167  nodes.RemoveAt(i);
168  i--;
169  }
170  }
171  else if (declaration is IDeclaration && !collectedReferences.Contains(declaration))
172  {
173  nodes.RemoveAt(i);
174  i--;
175  }
176  else if (declaration is ConstantBuffer)
177  {
178  // Do not stript constant buffer anymore, they should be kept as is
179  if (stripUniforms)
180  {
181  var constantBuffer = (ConstantBuffer)declaration;
182  StripDeclarations(constantBuffer.Members, collectedReferences, stripUniforms);
183  }
184  }
185  }
186  }
187 
188  /// <summary>
189  /// Helper to collects the referenced declarations recursively.
190  /// </summary>
191  /// <param name="collectedReferences">The collected declarations.</param>
192  /// <param name="reference">The reference to collect.</param>
193  private void CollectReferences(List<Node> collectedReferences, Node reference)
194  {
195  if (!collectedReferences.Contains(reference))
196  {
197  // Collect reference (if not already added)
198  collectedReferences.Add(reference);
199 
200  // Collect recursively
201  HashSet<Node> referencedFunctions;
202  if (indirectReferences.TryGetValue((Node)reference, out referencedFunctions))
203  {
204  foreach (var referencedFunction in referencedFunctions)
205  CollectReferences(collectedReferences, referencedFunction);
206  }
207  }
208  }
209 
210  private void AddReference(Node parent, Node declaration)
211  {
212  if (parent != null && declaration != null)
213  {
214  HashSet<Node> childReferences;
215  if (!indirectReferences.TryGetValue(parent, out childReferences))
216  {
217  childReferences = new HashSet<Node>();
218  indirectReferences[parent] = childReferences;
219  }
220  if (!childReferences.Contains(declaration))
221  childReferences.Add(declaration);
222  }
223  }
224 
225 
226  private Node GetDeclarationContainer()
227  {
228  // By default use the method definition as the main declarator container
229  var methodDefinition = (Node)NodeStack.OfType<MethodDefinition>().LastOrDefault();
230  if (methodDefinition != null)
231  return methodDefinition;
232 
233  // Else use the IDeclaration
234  return (Node)NodeStack.OfType<IDeclaration>().LastOrDefault();
235  }
236 
237  private List<IDeclaration> GetDeclarationContainers()
238  {
239  return NodeStack.OfType<IDeclaration>().ToList();
240  }
241  }
242 }
243 
SiliconStudio.Shaders.Ast.Hlsl.ConstantBuffer ConstantBuffer
StripVisitor(params string[] entryPoints)
Initializes a new instance of the StripVisitor class.
Definition: StripVisitor.cs:24
void Visit(MethodDefinition methodDefinition)
Definition: StripVisitor.cs:91
void Visit(MethodInvocationExpression methodInvocationExpression)
Definition: StripVisitor.cs:36
A method definition with a body of statements.
Abstract node.
Definition: Node.cs:15
A variable declaration.
Definition: Variable.cs:11
A single parameter declaration.
Definition: Parameter.cs:10
override bool PreVisitNode(Node node)
Called before visiting the node.
Definition: StripVisitor.cs:59
The strip visitor collects all function and declaration used by a set of entrypoints and remove any u...
Definition: StripVisitor.cs:15
Base type for all types.
Definition: TypeBase.cs:11
Declaration of a constant buffer.
Toplevel container of a shader parsing result.
Definition: Shader.cs:12
void Visit(VariableReferenceExpression variableReferenceExpression)
Definition: StripVisitor.cs:43
void Visit(TypeBase typeReference)
Definition: StripVisitor.cs:84
Toplevel interface for a declaration.
Definition: IDeclaration.cs:8
IReferencable.AddReference() event.
void Visit(ConstantBuffer constantBuffer)
Definition: StripVisitor.cs:52