Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SamplerMappingVisitor.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.Text;
6 using System.Linq;
7 using SiliconStudio.Shaders.Ast;
8 using SiliconStudio.Shaders.Ast.Hlsl;
9 using SiliconStudio.Shaders.Visitor;
10 
11 namespace SiliconStudio.Shaders.Convertor
12 {
13  /// <summary>
14  /// Collect the texture and sampler pair used in the HLSL shader.
15  /// </summary>
17  {
18  private Dictionary<SamplerTextureKey, Variable> samplerMapping;
19  private HashSet<Variable> textureAccesses;
20  private Shader shader;
21  private List<TextureSamplerMethodKey> textureSamplerMethods = new List<TextureSamplerMethodKey>();
22  private static readonly object ScopeValueKey = new object();
23  private CloneContext cloneContext = new CloneContext();
24 
25  public SamplerMappingVisitor(Shader shader, Dictionary<SamplerTextureKey, Variable> samplerMapping)
26  {
27  this.shader = shader;
28  this.samplerMapping = samplerMapping;
29  textureAccesses = new HashSet<Variable>();
30 
31  // Add all global declarations for clone context in order to avoid any clone on this object
32  foreach (var variable in shader.Declarations)
33  {
34  cloneContext.Add(variable, variable);
35  }
36  }
37 
38  /// <summary>
39  /// Gets or sets a flag specifying whether compatibility profile is used for texture functions.
40  /// As an example, with compatibility on, texture() might become texture2D().
41  /// </summary>
42  /// <value>
43  /// true if texture compatibility profile is enabled, false if not.
44  /// </value>
45  public bool TextureFunctionsCompatibilityProfile { get; set; }
46 
47  public override void Run(MethodDefinition methodEntry)
48  {
49  base.Run(methodEntry);
50 
51  var existingTextures = new HashSet<Variable>(samplerMapping.Select(x => x.Key.Texture));
52  foreach (var texture in textureAccesses)
53  {
54  if (!existingTextures.Contains(texture))
55  GenerateGLSampler(null, texture);
56  }
57 
58  for (int i = this.textureSamplerMethods.Count - 1; i >= 0; i--)
59  {
60  var textureSamplerMethodKey = this.textureSamplerMethods[i];
61  var entryIndex = shader.Declarations.IndexOf(textureSamplerMethodKey.Method);
62  this.shader.Declarations.Insert(entryIndex, textureSamplerMethodKey.NewMethod);
63  }
64  }
65 
66  private Variable FindGlobalVariable(Expression expression)
67  {
68  var variableRef = expression as VariableReferenceExpression;
69  if (variableRef != null)
70  {
71  var variable = variableRef.TypeInference.Declaration as Variable;
72 
73  if (variable != null)
74  {
75  // If a variable has an initial value, find the global variable
76  if (!shader.Declarations.Contains(variable) && variable.InitialValue != null)
77  {
78  return this.FindGlobalVariable(variable.InitialValue);
79  }
80 
81  variable = (Variable)variable.GetTag(ScopeValueKey) ?? variable;
82 
83  // Is this a global variable?
84  if (shader.Declarations.Contains(variable))
85  {
86  return variable;
87  }
88  }
89  }
90  return null;
91  }
92 
94  protected void Visit(VariableReferenceExpression variableRef)
95  {
96  ((ScopeDeclarationWithRef)ScopeStack.Peek()).VariableReferences.Add(variableRef);
97  }
98 
99  protected override void Visit(MethodInvocationExpression methodInvocationExpression)
100  {
101  // Visit first children
102  base.Visit(methodInvocationExpression);
103 
104  // Convert member expression
105  var variableRef = methodInvocationExpression.Target as VariableReferenceExpression;
106  var memberRef = methodInvocationExpression.Target as MemberReferenceExpression;
107  if (memberRef != null)
108  {
109  // TODO handle Texture2D<float>
110  var textureVariable = this.FindGlobalVariable(memberRef.Target);
111 
112  if (textureVariable != null)
113  {
114  var textureType = textureVariable.Type.ResolveType();
115 
116  if (textureType is TextureType || (textureType.IsBuiltIn && textureType.Name.Text.StartsWith("Texture", StringComparison.InvariantCultureIgnoreCase)))
117  {
118  switch (memberRef.Member)
119  {
120  case "Load":
121  {
122  GenerateGLSampler(null, textureVariable);
123  }
124  break;
125  case "GetDimensions":
126  {
127  textureAccesses.Add(textureVariable);
128  }
129  break;
130  case "Sample":
131  case "SampleBias":
132  case "SampleGrad":
133  case "SampleLevel":
134  {
135  var sampler = this.FindGlobalVariable(methodInvocationExpression.Arguments[0]);
136  if (sampler == null)
137  throw new InvalidOperationException(string.Format("Unable to find sampler [{0}] as a global variable",
138  methodInvocationExpression.Arguments[0]));
139 
140  GenerateGLSampler(sampler, textureVariable);
141  }
142  break;
143  }
144  }
145  }
146  }
147  else if (variableRef != null)
148  {
149  string methodName = variableRef.Name.Text;
150 
151  // Transform texture fetch
152  var texFetchInfo = ParseTexFetch(methodName);
153  if (texFetchInfo != null)
154  {
155  var fetchInstructionSecondPart = string.Empty;
156  switch (texFetchInfo.Item2)
157  {
158  case TexFetchType.Default:
159  if (methodInvocationExpression.Arguments.Count == 4)
160  fetchInstructionSecondPart = "Grad";
161  break;
162  case TexFetchType.Bias:
163  // Bias is encoded in w, so replicated argument and extract only w. Compiler/optimizer should do the rest of the job.
164  methodInvocationExpression.Arguments.Add(new MemberReferenceExpression(new ParenthesizedExpression(methodInvocationExpression.Arguments[1]), "w"));
165  break;
166  case TexFetchType.Grad:
167  fetchInstructionSecondPart = "Grad";
168  break;
169  case TexFetchType.Proj:
170  fetchInstructionSecondPart = "Proj";
171  break;
172  case TexFetchType.Lod:
173  fetchInstructionSecondPart = "Lod";
174 
175  // LOD is encoded in w, so replicated argument and extract only w. Compiler/optimizer should do the rest of the job.
176  methodInvocationExpression.Arguments.Add(new MemberReferenceExpression(new ParenthesizedExpression(methodInvocationExpression.Arguments[1]), "w"));
177  break;
178  }
179 
180  if (TextureFunctionsCompatibilityProfile)
181  {
182  var stringBuilder = new StringBuilder("texture", 32);
183  if (texFetchInfo.Item1 == 4)
184  stringBuilder.Append("Cube");
185  else
186  stringBuilder.Append(texFetchInfo.Item1).Append('D');
187  stringBuilder.Append(fetchInstructionSecondPart);
188  variableRef.Name = stringBuilder.ToString();
189  }
190  else
191  {
192  variableRef.Name = "texture" + fetchInstructionSecondPart;
193  }
194 
195  // TODO: Check how many components are required (for now it only do xy, but it might be x or xyz depending on texture dimension).
196  if (texFetchInfo.Item2 != TexFetchType.Proj)
197  {
198  var previousArgument = methodInvocationExpression.Arguments[1];
199  var sizeOfArguments = texFetchInfo.Item1 == 4 ? 3 : texFetchInfo.Item1;
200  var vectorType = previousArgument.TypeInference.TargetType as VectorType;
201 
202  // If argument type is not the size of the expected argument, use explicit swizzle
203  if (vectorType == null || vectorType.Dimension != sizeOfArguments)
204  methodInvocationExpression.Arguments[1] = new MemberReferenceExpression(new ParenthesizedExpression(previousArgument), "xyzw".Substring(0, sizeOfArguments));
205  }
206 
207  // Add the sampler
208  var samplerRefExpr = methodInvocationExpression.Arguments[0] as VariableReferenceExpression;
209  if (samplerRefExpr != null)
210  {
211  var samplerVariable = samplerRefExpr.TypeInference.Declaration as Variable;
212  var newSamplerType = texFetchInfo.Item1 < 4 ? new SamplerType("sampler" + texFetchInfo.Item1 + "D") : new SamplerType("samplerCube");
213  this.ChangeVariableType(samplerVariable, newSamplerType);
214  }
215  }
216  }
217  }
218 
219  private void ChangeVariableType(Variable samplerVariable, TypeBase newType)
220  {
221  if (samplerVariable != null)
222  {
223  samplerVariable.Type = newType;
224  if (samplerVariable is Parameter)
225  {
226  return;
227  }
228 
229  var variableInitialValue = samplerVariable.InitialValue as VariableReferenceExpression;
230  if (variableInitialValue != null)
231  {
232  this.ChangeVariableType(variableInitialValue.TypeInference.Declaration as Variable, newType);
233  }
234  }
235  }
236 
238  {
239  var textureParameters = new List<Parameter>();
240  var parameterValues = new List<Expression>();
241  var parameterGlobalValues = new List<Variable>();
242 
243  var samplerTypes = new List<int>();
244 
245  for (int i = 0; i < method.Parameters.Count; i++)
246  {
247  var parameter = method.Parameters[i];
248  if (parameter.Type is TextureType || parameter.Type is StateType)
249  {
250  textureParameters.Add(parameter);
251 
252  // Find global variable
253  var parameterValue = this.FindGlobalVariable(invoke.Arguments[i]);
254 
255  // Set the tag ScopeValue for the current parameter
256  parameter.SetTag(ScopeValueKey, parameterValue);
257 
258  // Add only new variable
259  if (!parameterGlobalValues.Contains(parameterValue))
260  parameterGlobalValues.Add(parameterValue);
261  }
262  else if ( i < invoke.Arguments.Count)
263  {
264  parameterValues.Add(invoke.Arguments[i]);
265  if (parameter.Type is SamplerType)
266  {
267  samplerTypes.Add(i);
268  }
269  }
270  }
271 
272  // We have texture/sampler parameters. We need to generate a new specialized method
273  if (textureParameters.Count > 0)
274  {
275  // Order parameter values by name
276  parameterGlobalValues.Sort((left, right) => left.Name.Text.CompareTo(right.Name.Text));
277 
278  var methodKey = new TextureSamplerMethodKey(method);
279 
280  int indexOf = textureSamplerMethods.IndexOf(methodKey);
281 
282  if (indexOf < 0)
283  {
284  methodKey.Initialize(cloneContext);
285  textureSamplerMethods.Add(methodKey);
286  }
287  else
288  {
289  // If a key is found again, add it as it was reused in order to keep usage in order
290  methodKey = textureSamplerMethods[indexOf];
291  textureSamplerMethods.RemoveAt(indexOf);
292  textureSamplerMethods.Add(methodKey);
293  }
294 
295  methodKey.Invokers.Add(invoke);
296 
297  var newTarget = new VariableReferenceExpression(methodKey.NewMethod.Name) { TypeInference = { Declaration = methodKey.NewMethod, TargetType = invoke.TypeInference.TargetType } };
298  invoke.Target = newTarget;
299  invoke.Arguments = parameterValues;
300  invoke.TypeInference.Declaration = methodKey.NewMethod;
301  invoke.TypeInference.TargetType = invoke.TypeInference.TargetType;
302 
303  this.VisitDynamic(methodKey.NewMethod);
304  }
305  else
306  {
307  // Visit the method callstack
308  this.VisitDynamic(method);
309 
310  // There is an anonymous sampler type
311  // We need to resolve its types after the method definition was processed
312  if (samplerTypes.Count > 0)
313  {
314  foreach (var samplerTypeIndex in samplerTypes)
315  {
316  var samplerRef = invoke.Arguments[samplerTypeIndex] as VariableReferenceExpression;
317  if (samplerRef != null)
318  {
319  var samplerDecl = samplerRef.TypeInference.Declaration as Variable;
320  ChangeVariableType(samplerDecl, method.Parameters[samplerTypeIndex].Type);
321  }
322  }
323  }
324  }
325 
326  // Remove temporary parameters
327  if (textureParameters.Count > 0)
328  {
329  foreach (var textureParameter in textureParameters)
330  {
331  textureParameter.RemoveTag(ScopeValueKey);
332  }
333  }
334  }
335 
336  /// <summary>
337  /// Generates a OpenGL sampler based on sampler/texture combination.
338  /// </summary>
339  /// <param name="sampler">The D3D sampler (can be null).</param>
340  /// <param name="texture">The D3D texture.</param>
341  private void GenerateGLSampler(Variable sampler, Variable texture)
342  {
343  Variable glslSampler;
344 
345  if (texture == null)
346  throw new InvalidOperationException();
347 
348  var samplerKey = new SamplerTextureKey(sampler, texture);
349  if (!samplerMapping.TryGetValue(samplerKey, out glslSampler))
350  {
351  glslSampler = new Variable(new TypeName(texture.Type.ResolveType().Name.Text.Replace("Texture", "sampler")), texture.Name + (sampler != null ? "_" + sampler.Name : "_NoSampler")) { Span = sampler == null ? texture.Span : sampler.Span };
352  samplerMapping.Add(samplerKey, glslSampler);
353  }
354  }
355 
356  /// <summary>
357  /// Parses the texture fetch.
358  /// </summary>
359  /// <param name="name">
360  /// The name.
361  /// </param>
362  /// <returns>
363  /// A tuple indicating the dimension and the <see cref="TexFetchType"/>
364  /// </returns>
365  private static Tuple<int, TexFetchType> ParseTexFetch(string name)
366  {
367  if (!name.StartsWith("tex"))
368  return null;
369 
370  name = name.Substring(3);
371 
372  int dimension;
373 
374  if (name.StartsWith("1D"))
375  dimension = 1;
376  else if (name.StartsWith("2D"))
377  dimension = 2;
378  else if (name.StartsWith("3D"))
379  dimension = 3;
380  else if (name.StartsWith("CUBE"))
381  dimension = 4;
382  else
383  return null;
384 
385  // Remove parsed size
386  name = name.Substring((dimension == 4) ? 4 : 2);
387 
388  TexFetchType fetchType;
389  switch (name)
390  {
391  case "":
392  fetchType = TexFetchType.Default;
393  break;
394  case "lod":
395  fetchType = TexFetchType.Lod;
396  break;
397  case "grad":
398  fetchType = TexFetchType.Grad;
399  break;
400  case "bias":
401  fetchType = TexFetchType.Bias;
402  break;
403  case "proj":
404  fetchType = TexFetchType.Proj;
405  break;
406  default:
407  return null;
408  }
409 
410  return new Tuple<int, TexFetchType>(dimension, fetchType);
411  }
412 
413 
414  /// <summary>
415  /// Texture fetch type.
416  /// </summary>
417  private enum TexFetchType
418  {
419  /// <summary>
420  /// Default fetch.
421  /// </summary>
422  Default,
423 
424  /// <summary>
425  /// Mipmap Lod fetch.
426  /// </summary>
427  Lod,
428 
429  /// <summary>
430  /// Gradient fetch.
431  /// </summary>
432  Grad,
433 
434  /// <summary>
435  /// Bias fetch.
436  /// </summary>
437  Bias,
438 
439  /// <summary>
440  /// Proj fetch.
441  /// </summary>
442  Proj,
443  }
444 
445  protected override ScopeDeclaration NewScope(IScopeContainer container = null)
446  {
447  return new ScopeDeclarationWithRef(container);
448  }
449 
450  private class ScopeDeclarationWithRef : ScopeDeclaration
451  {
452  public ScopeDeclarationWithRef()
453  : this(null)
454  {
455  }
456 
457  public ScopeDeclarationWithRef(IScopeContainer scopeContainer)
458  : base(scopeContainer)
459  {
460  VariableReferences = new List<VariableReferenceExpression>();
461  }
462 
463  public List<VariableReferenceExpression> VariableReferences { get; private set; }
464  }
465 
466 
467 
468  private class TextureSamplerMethodKey
469  {
470  private string methodName;
471 
472  public TextureSamplerMethodKey(MethodDefinition method)
473  {
474  Invokers = new List<MethodInvocationExpression>();
475  this.Method = method;
476 
477  Variables = new List<Variable>();
478  foreach (var parameter in Method.Parameters)
479  {
480  var variableValue = (Variable)parameter.GetTag(ScopeValueKey);
481  if (variableValue != null)
482  {
483  Variables.Add(variableValue);
484  }
485  }
486  }
487 
488  public List<MethodInvocationExpression> Invokers { get; set; }
489 
490  public void Initialize(CloneContext previousCloneContext)
491  {
492  // Clone original context
493  var cloneContext = new CloneContext();
494 
495  foreach (var keyValurPair in previousCloneContext)
496  {
497  cloneContext.Add(keyValurPair.Key, keyValurPair.Value);
498  }
499 
500  // Removes the method to clone
501  cloneContext.Remove(Method);
502 
503  // Clone the old method with the clone context
504  NewMethod = Method.DeepClone(cloneContext);
505 
506  var oldParameters = NewMethod.Parameters;
507  NewMethod.Parameters = new List<Parameter>();
508  int j = 0;
509  for (int i = 0; i < oldParameters.Count; i++)
510  {
511  var parameter = oldParameters[i];
512  var variableValue = (Variable)this.Method.Parameters[i].GetTag(ScopeValueKey);
513  if (variableValue != null)
514  {
515  this.NewMethod.Body.Insert(j, new DeclarationStatement(parameter));
516  j++;
517  parameter.InitialValue = new VariableReferenceExpression(variableValue.Name) { TypeInference = { Declaration = variableValue, TargetType = variableValue.Type } };
518  }
519  else
520  NewMethod.Parameters.Add(parameter);
521  }
522 
523  // Create new method with new method name
524  var methodNameBuild = new StringBuilder();
525  methodNameBuild.Append(Method.Name);
526  foreach (var variable in Variables)
527  {
528  methodNameBuild.Append("_");
529  methodNameBuild.Append(variable.Name);
530  }
531  methodName = methodNameBuild.ToString();
532  NewMethod.Name = methodName;
533  }
534 
535  public MethodDefinition Method { get; private set; }
536 
537  public List<Variable> Variables { get; private set; }
538 
539  public MethodDefinition NewMethod { get; private set; }
540 
541  public bool Equals(TextureSamplerMethodKey other)
542  {
543  if (ReferenceEquals(null, other))
544  return false;
545  if (ReferenceEquals(this, other))
546  return true;
547 
548  if (this.Variables.Count != other.Variables.Count)
549  return false;
550 
551  if (!ReferenceEquals(other.Method, this.Method))
552  return false;
553 
554  for (int i = 0; i < this.Variables.Count; i++)
555  {
556  if (!ReferenceEquals(this.Variables[i], other.Variables[i]))
557  return false;
558  }
559 
560  return true;
561  }
562 
563  public override bool Equals(object obj)
564  {
565  if (ReferenceEquals(null, obj))
566  return false;
567  if (ReferenceEquals(this, obj))
568  return true;
569  if (obj.GetType() != typeof(TextureSamplerMethodKey))
570  return false;
571  return Equals((TextureSamplerMethodKey)obj);
572  }
573 
574  public override int GetHashCode()
575  {
576  unchecked
577  {
578  int result = 0;
579  foreach (var variable in Variables)
580  {
581  result = (result * 397) ^ variable.GetHashCode();
582  }
583  result = (result * 397) ^ (this.Method != null ? this.Method.GetHashCode() : 0);
584  return result;
585  }
586  }
587 
588  public override string ToString()
589  {
590  return NewMethod == null ? "[" + Method.Name.Text + "]" : NewMethod.Name.Text;
591  }
592  }
593  }
594 }
Base class for all vector types
Definition: VectorType.cs:10
TypeBase Type
Gets or sets the type.
Definition: Variable.cs:61
Tag a visitable method with this attribute.
Definition: VisitorBase.cs:295
A typeless reference.
Definition: TypeName.cs:10
A tag interface to identify a container for scope declarations.
A Scope declaration provides a way to retrieve all scope declaration (variable, methods...etc.) and attached nodes.
virtual TypeBase ResolveType()
Resolves the type.
Definition: TypeBase.cs:101
override void ProcessMethodInvocation(MethodInvocationExpression invoke, MethodDefinition method)
override ScopeDeclaration NewScope(IScopeContainer container=null)
An expression surrounded by parenthesis.
A method definition with a body of statements.
Use the default mode depending on the type of the field/property.
A variable declaration.
Definition: Variable.cs:11
A single parameter declaration.
Definition: Parameter.cs:10
List< Expression > Arguments
Gets or sets the arguments.
A member reference in the form {this}.{Name}
Base type for all types.
Definition: TypeBase.cs:11
Provides a dictionary of cloned values, where the [key] is the original object and [value] the new ob...
Toplevel container of a shader parsing result.
Definition: Shader.cs:12
List< Parameter > Parameters
Gets or sets the parameters.
SamplerMappingVisitor(Shader shader, Dictionary< SamplerTextureKey, Variable > samplerMapping)
override void Visit(MethodInvocationExpression methodInvocationExpression)
override void Run(MethodDefinition methodEntry)
Collect the texture and sampler pair used in the HLSL shader.
object GetTag(object tagKey)
Gets a tag value associated to this node..
Definition: Node.cs:78
void Visit(VariableReferenceExpression variableRef)