Diligent Engine API Reference
HLSL2GLSLConverterImpl.h
1 /* Copyright 2015-2018 Egor Yusov
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12  *
13  * In no event and under no legal theory, whether in tort (including negligence),
14  * contract, or otherwise, unless required by applicable law (such as deliberate
15  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16  * liable for any damages, including any direct, indirect, special, incidental,
17  * or consequential damages of any character arising as a result of this License or
18  * out of the use or inability to use the software (including but not limited to damages
19  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20  * all other commercial damages or losses), even if such Contributor has been advised
21  * of the possibility of such damages.
22  */
23 
24 #pragma once
25 
26 #include <list>
27 #include <unordered_set>
28 #include <unordered_map>
29 
30 #include "HLSL2GLSLConverter.h"
31 #include "ObjectBase.h"
32 #include "HLSLKeywords.h"
33 #include "Shader.h"
34 #include "HashUtils.h"
35 #include "HLSLKeywords.h"
36 
37 namespace Diligent
38 {
39  struct FunctionStubHashKey
40  {
41  FunctionStubHashKey(const String& _Obj, const String& _Func, Uint32 _NumArgs) :
42  Object(_Obj),
43  Function(_Func),
44  NumArguments(_NumArgs)
45  {
46  }
47 
48  FunctionStubHashKey(const Char* _Obj, const Char* _Func, Uint32 _NumArgs) :
49  Object(_Obj),
50  Function(_Func),
51  NumArguments(_NumArgs)
52  {
53  }
54 
55  FunctionStubHashKey( FunctionStubHashKey && Key ) :
56  Object(std::move(Key.Object)),
57  Function(std::move(Key.Function)),
58  NumArguments(Key.NumArguments)
59  {
60  }
61 
62  bool operator==(const FunctionStubHashKey& rhs)const
63  {
64  return Object == rhs.Object &&
65  Function == rhs.Function &&
66  NumArguments == rhs.NumArguments;
67  }
68 
69  HashMapStringKey Object;
70  HashMapStringKey Function;
71  Uint32 NumArguments;
72  };
73 }
74 
75 namespace std
76 {
77  template<>struct hash < Diligent::FunctionStubHashKey >
78  {
79  size_t operator()( const Diligent::FunctionStubHashKey &Key ) const
80  {
81  return ComputeHash(Key.Object, Key.Function, Key.NumArguments);
82  }
83  };
84 }
85 
86 namespace Diligent
87 {
88  class HLSL2GLSLConverterImpl
89  {
90  public:
91  static const HLSL2GLSLConverterImpl& GetInstance();
92  struct ConversionAttribs
93  {
94  IShaderSourceInputStreamFactory *pSourceStreamFactory = nullptr;
95  IHLSL2GLSLConversionStream **ppConversionStream = nullptr;
96  const Char* HLSLSource = nullptr;
97  size_t NumSymbols = 0;
98  const Char* EntryPoint = nullptr;
99  SHADER_TYPE ShaderType = SHADER_TYPE_UNKNOWN;
100  bool IncludeDefinitions = false;
101  const Char* InputFileName = nullptr;
102  };
103 
104  String Convert(ConversionAttribs &Attribs)const;
105  void CreateStream(const Char* InputFileName,
106  IShaderSourceInputStreamFactory *pSourceStreamFactory,
107  const Char* HLSLSource,
108  size_t NumSymbols,
109  IHLSL2GLSLConversionStream **ppStream)const;
110 
111  private:
112  HLSL2GLSLConverterImpl();
113 
114  struct HLSLObjectInfo
115  {
116  String GLSLType; // sampler2D, sampler2DShadow, image2D, etc.
117  Uint32 NumComponents; // 0,1,2,3 or 4
118  // Texture2D<float4> -> 4
119  // Texture2D<uint> -> 1
120  // Texture2D -> 0
121  HLSLObjectInfo( const String& Type, Uint32 NComp ) :
122  GLSLType( Type ),
123  NumComponents( NComp )
124  {}
125  };
126  struct ObjectsTypeHashType
127  {
128  // This is only required to make the code compile on paranoid MSVC 2017 compiler (19.10.25017):
129  // https://stackoverflow.com/questions/47604029/move-constructors-of-stl-containers-in-msvc-2017-are-not-marked-as-noexcept
130  ObjectsTypeHashType()noexcept {}
131  ObjectsTypeHashType(ObjectsTypeHashType&& rhs)noexcept :
132  m(std::move(rhs.m))
133  {}
134  ObjectsTypeHashType& operator = (ObjectsTypeHashType&&) = delete;
135  ObjectsTypeHashType(ObjectsTypeHashType&) = delete;
136  ObjectsTypeHashType& operator = (ObjectsTypeHashType&) = delete;
137 
138  std::unordered_map<HashMapStringKey, HLSLObjectInfo> m;
139  };
140 
141  struct GLSLStubInfo
142  {
143  String Name;
144  String Swizzle;
145  GLSLStubInfo( const String& _Name, const char* _Swizzle ) :
146  Name( _Name ),
147  Swizzle( _Swizzle )
148  {}
149  };
150  // Hash map that maps GLSL object, method and number of arguments
151  // passed to the original function, to the GLSL stub function
152  // Example: {"sampler2D", "Sample", 2} -> {"Sample_2", "_SWIZZLE"}
153  std::unordered_map<FunctionStubHashKey, GLSLStubInfo> m_GLSLStubs;
154 
155  enum class TokenType
156  {
157  Undefined,
158 #define ADD_KEYWORD(keyword)kw_##keyword,
159  ITERATE_KEYWORDS(ADD_KEYWORD)
160 #undef ADD_KEYWORD
161  PreprocessorDirective,
162  Operator,
163  OpenBrace,
164  ClosingBrace,
165  OpenBracket,
166  ClosingBracket,
167  OpenStaple,
168  ClosingStaple,
169  OpenAngleBracket,
170  ClosingAngleBracket,
171  Identifier,
172  NumericConstant,
173  SrtingConstant,
174  Semicolon,
175  Comma,
176  TextBlock,
177  Assignment,
178  ComparisonOp,
179  BooleanOp,
180  BitwiseOp,
181  IncDecOp,
182  MathOp
183  };
184 
185  struct TokenInfo
186  {
187  TokenType Type;
188  String Literal;
189  String Delimiter;
190  bool IsBuiltInType()const
191  {
192  static_assert( static_cast<int>(TokenType::kw_bool) == 1 && static_cast<int>(TokenType::kw_void) == 191,
193  "If you updated built-in types, double check that all types are defined between bool and void");
194  return Type >= TokenType::kw_bool && Type <= TokenType::kw_void;
195  }
196  bool IsFlowControl()const
197  {
198  static_assert( static_cast<int>(TokenType::kw_break) == 192 && static_cast<int>(TokenType::kw_while) == 202,
199  "If you updated control flow keywords, double check that all keywords are defined between break and while");
200  return Type >= TokenType::kw_break && Type <= TokenType::kw_while;
201  }
202  TokenInfo( TokenType _Type = TokenType :: Undefined,
203  const Char* _Literal = "",
204  const Char* _Delimiter = "" ) :
205  Type( _Type ),
206  Literal( _Literal ),
207  Delimiter(_Delimiter)
208  {}
209  };
210  typedef std::list<TokenInfo> TokenListType;
211 
212 
213  class ConversionStream : public ObjectBase<IHLSL2GLSLConversionStream>
214  {
215  public:
216  typedef ObjectBase<IHLSL2GLSLConversionStream> TBase;
217  ConversionStream(IReferenceCounters *pRefCounters,
218  const HLSL2GLSLConverterImpl &Converter,
219  const char* InputFileName,
220  IShaderSourceInputStreamFactory* pInputStreamFactory,
221  const Char* HLSLSource,
222  size_t NumSymbols,
223  bool bPreserveTokens);
224 
225  String Convert(const Char* EntryPoint, SHADER_TYPE ShaderType, bool IncludeDefintions);
226  virtual void Convert(const Char* EntryPoint, SHADER_TYPE ShaderType, bool IncludeDefintions, IDataBlob **ppGLSLSource)override;
227 
228  IMPLEMENT_QUERY_INTERFACE_IN_PLACE( IID_HLSL2GLSLConversionStream, TBase )
229 
230  const String& GetInputFileName()const{ return m_InputFileName; }
231  private:
232  void InsertIncludes(String &GLSLSource, IShaderSourceInputStreamFactory* pSourceStreamFactory);
233  void Tokenize(const String &Source);
234 
235  typedef std::unordered_map<String, bool> SamplerHashType;
236 
237  const HLSLObjectInfo *FindHLSLObject(const String &Name );
238 
239  void ProcessShaderDeclaration(TokenListType::iterator EntryPointToken, SHADER_TYPE ShaderType);
240 
241  void ProcessObjectMethods(const TokenListType::iterator &ScopeStart, const TokenListType::iterator &ScopeEnd);
242 
243  void ProcessRWTextures(const TokenListType::iterator &ScopeStart, const TokenListType::iterator &ScopeEnd);
244 
245  void ProcessAtomics(const TokenListType::iterator &ScopeStart,
246  const TokenListType::iterator &ScopeEnd);
247 
248  void RegisterStruct(TokenListType::iterator &Token);
249 
250  void ProcessConstantBuffer(TokenListType::iterator &Token);
251  void ProcessStructuredBuffer(TokenListType::iterator &Token);
252  void ParseSamplers(TokenListType::iterator &ScopeStart, SamplerHashType &SamplersHash);
253  void ProcessTextureDeclaration(TokenListType::iterator &Token, const std::vector<SamplerHashType> &SamplersHash, ObjectsTypeHashType &Objects);
254  bool ProcessObjectMethod(TokenListType::iterator &Token, const TokenListType::iterator &ScopeStart, const TokenListType::iterator &ScopeEnd);
255  Uint32 CountFunctionArguments(TokenListType::iterator &Token, const TokenListType::iterator &ScopeEnd);
256  bool ProcessRWTextureStore(TokenListType::iterator &Token, const TokenListType::iterator &ScopeEnd);
257  void RemoveFlowControlAttribute(TokenListType::iterator &Token);
258  void RemoveSemantics();
259  void RemoveSpecialShaderAttributes();
260  void RemoveSemanticsFromBlock(TokenListType::iterator &Token, TokenType OpenBracketType, TokenType ClosingBracketType);
261 
262  // IteratorType may be String::iterator or String::const_iterator.
263  // While iterator is convertible to const_iterator,
264  // iterator& cannot be converted to const_iterator& (Microsoft compiler allows
265  // such conversion, while gcc does not)
266  template<typename IteratorType>
267  String PrintTokenContext(IteratorType &TargetToken, Int32 NumAdjacentLines);
268 
269  struct ShaderParameterInfo
270  {
271  enum class StorageQualifier : Int8
272  {
273  Unknown = 0,
274  In = 1,
275  Out = 2,
276  InOut = 3,
277  Ret = 4
278  }storageQualifier;
279 
280  struct GSAttributes
281  {
282  enum class PrimitiveType : Int8
283  {
284  Undefined = 0,
285  Point = 1,
286  Line = 2,
287  Triangle = 3,
288  LineAdj = 4,
289  TriangleAdj = 5
290  };
291  enum class StreamType : Int8
292  {
293  Undefined = 0,
294  Point = 1,
295  Line = 2,
296  Triangle = 3
297  };
298  PrimitiveType PrimType;
299  StreamType Stream;
300  GSAttributes() :
301  PrimType(PrimitiveType::Undefined),
302  Stream(StreamType::Undefined)
303  {}
304  }GSAttribs;
305 
306  struct HSAttributes
307  {
308  enum class InOutPatchType : Int8
309  {
310  Undefined = 0,
311  InputPatch = 1,
312  OutputPatch = 2
313  }PatchType;
314  HSAttributes() :
315  PatchType(InOutPatchType::Undefined)
316  {}
317  }HSAttribs;
318 
319  String ArraySize;
320  String Type;
321  String Name;
322  String Semantic;
323  std::vector<ShaderParameterInfo> members;
324 
325  ShaderParameterInfo() :
326  storageQualifier(StorageQualifier::Unknown)
327  {}
328  };
329  void ParseShaderParameter(TokenListType::iterator &Token, ShaderParameterInfo &ParamInfo);
330  void ProcessFunctionParameters( TokenListType::iterator &Token, std::vector<ShaderParameterInfo>& Params, bool &bIsVoid );
331  String AddFlatQualifier(const String& Type);
332  void ProcessFragmentShaderArguments( std::vector<ShaderParameterInfo>& Params,
333  String &GlobalVariables,
334  std::stringstream &ReturnHandlerSS,
335  String &Prologue );
336 
337  String BuildParameterName(const std::vector<const ShaderParameterInfo*>& MemberStack, Char Separator, const Char* Prefix = "", const Char *SubstituteInstName = "", const Char *Index = "");
338 
339  template<typename THandler>
340  void ProcessScope(TokenListType::iterator &Token, TokenListType::iterator ScopeEnd, TokenType OpenParenType, TokenType ClosingParenType, THandler Handler);
341 
342  template<typename TArgHandler>
343  void ProcessShaderArgument( const ShaderParameterInfo &Param,
344  int ShaderInd,
345  int IsOutVar,
346  std::stringstream &PrologueSS,
347  TArgHandler ArgHandler);
348 
349  void ProcessVertexShaderArguments( std::vector<ShaderParameterInfo>& Params,
350  String &Globals,
351  std::stringstream &ReturnHandlerSS,
352  String &Prologue );
353 
354  void ProcessGeometryShaderArguments( TokenListType::iterator &TypeToken,
355  std::vector<ShaderParameterInfo>& Params,
356  String &Globals,
357  String &Prologue );
358 
359  void ProcessHullShaderConstantFunction( const Char *FuncName, bool &bTakesInputPatch );
360 
361  void ProcessShaderAttributes( TokenListType::iterator &TypeToken,
362  std::unordered_map<HashMapStringKey, String>& Attributes);
363 
364  void ProcessHullShaderArguments( TokenListType::iterator &TypeToken,
365  std::vector<ShaderParameterInfo>& Params,
366  String &Globals,
367  std::stringstream &ReturnHandlerSS,
368  String &Prologue );
369  void ProcessDomainShaderArguments( TokenListType::iterator &TypeToken,
370  std::vector<ShaderParameterInfo>& Params,
371  String &Globals,
372  std::stringstream &ReturnHandlerSS,
373  String &Prologue );
374  void ProcessComputeShaderArguments( TokenListType::iterator &TypeToken,
375  std::vector<ShaderParameterInfo>& Params,
376  String &Globals,
377  String &Prologue );
378 
379  void FindClosingBracket( TokenListType::iterator &Token, const TokenListType::iterator &ScopeEnd, TokenType OpenBracketType, TokenType ClosingBracketType );
380 
381  void ProcessReturnStatements( TokenListType::iterator &Token, bool IsVoid, const Char *EntryPoint, const Char *MacroName );
382 
383  void ProcessGSOutStreamOperations( TokenListType::iterator &Token, const String &OutStreamName, const char *EntryPoint );
384 
385  String BuildGLSLSource();
386 
387  // Tokenized source code
388  TokenListType m_Tokens;
389 
390  // List of tokens defining structs
391  std::unordered_map<HashMapStringKey, TokenListType::iterator> m_StructDefinitions;
392 
393  // Stack of parsed objects, for every scope level.
394  // There are currently only two levels:
395  // level 0 - global scope, contains all global objects
396  // (textures, buffers)
397  // level 1 - function body, contains all objects
398  // defined as function arguments
399  std::vector< ObjectsTypeHashType > m_Objects;
400 
401  const bool m_bPreserveTokens;
402  const HLSL2GLSLConverterImpl &m_Converter;
403 
404  // This member is only used to compare input name
405  // when subsequent shaders are converted from already tokenized source
406  const String m_InputFileName;
407  };
408 
409  // HLSL keyword->token info hash map
410  // Example: "Texture2D" -> TokenInfo(TokenType::Texture2D, "Texture2D")
411  std::unordered_map<HashMapStringKey, TokenInfo> m_HLSLKeywords;
412 
413  // Set of all GLSL image types (image1D, uimage1D, iimage1D, image2D, ... )
414  std::unordered_set<HashMapStringKey> m_ImageTypes;
415 
416  // Set of all HLSL atomic operations (InterlockedAdd, InterlockedOr, ...)
417  std::unordered_set<HashMapStringKey> m_AtomicOperations;
418 
419  // HLSL semantic -> glsl variable, for every shader stage and input/output type (in == 0, out == 1)
420  // Example: [vertex, output] SV_Position -> gl_Position
421  // [fragment, input] SV_Position -> gl_FragCoord
422  static constexpr int InVar = 0;
423  static constexpr int OutVar = 1;
424  std::unordered_map<HashMapStringKey, String> m_HLSLSemanticToGLSLVar[6][2];
425  };
426 }
427 
428 // Intro
429 // DirectX and OpenGL use different shading languages. While mostly being very similar,
430 // the language syntax differs substantially sometimes. Having two versions of each
431 // shader is clearly not an option for real projects. Maintaining intermediate representation
432 // that translates to both languages is one solution, but it might complicate shader development
433 // and debugging.
434 //
435 // HLSL converter allows HLSL shader files to be converted into GLSL source.
436 // The entire shader development can thus be performed using HLSL tools. Since no intermediate
437 // representation is used, shader files can be directly compiled by the HLSL compiler.
438 // All tools available for HLSL shader devlopment, analysis and optimization can be
439 // used. The source can then be transaprently converted to GLSL.
440 //
441 //
442 // Using HLSL Converter
443 // * The following rules are used to convert HLSL texture declaration into GLSL sampler:
444 // - HLSL texture dimension defines GLSL sampler dimension:
445 // - Texture2D -> sampler2D
446 // - TextureCube -> samplerCube
447 // - HLSL texture component type defines GLSL sampler type. If no type is specified, float4 is assumed:
448 // - Texture2D<float> -> sampler2D
449 // - Texture3D<uint4> -> usampler3D
450 // - Texture2DArray<int2> -> isampler2DArray
451 // - Texture2D -> sampler2D
452 // - To distinguish if sampler should be shadow or not, the converter tries to find <Texture Name>_sampler
453 // among samplers (global variables and function arguments). If the sampler type is comparison,
454 // the texture is converted to shadow sampler. If sampler state is either not comparison or not found,
455 // regular sampler is used.
456 // Examples:
457 // - Texture2D g_ShadowMap; -> sampler2DShadow g_ShadowMap;
458 // SamplerComparisonState g_ShadowMap_sampler;
459 // - Texture2D g_Tex2D; -> sampler2D g_Tex2D;
460 // SamplerState g_Tex2D_sampler;
461 // Texture3D g_Tex3D; -> sampler3D g_Tex3D;
462 //
463 // * GLSL requires format to be specified for all images allowing writes. HLSL converter allows GLSL image
464 // format specification inside the special comment block:
465 // Example:
466 // RWTexture2D<float /* format=r32f */ > Tex2D;
467 // * In OpenGL tessellation, domain, partitioning, and topology are properties of tessellation evaluation
468 // shader rather than tessellation control shader. The following specially formatted comment should be placed
469 // on top of domain shader declararion to specify the attributes
470 // /* partitioning = {integer|fractional_even|fractional_odd}, outputtopology = {triangle_cw|triangle_ccw} */
471 // Example:
472 // /* partitioning = fractional_even, outputtopology = triangle_cw */
473 
474 
475 // Requirements:
476 // * GLSL allows samplers to be declared as global variables or function arguments only.
477 // It does not allow local variables of sampler type.
478 //
479 // Important notes/known issues:
480 //
481 // * GLSL compiler does not handle float3 structure members correctly. It is
482 // strongly suggested not to use this type in structure definitions
483 //
484 // * At least NVidia GLSL compiler does not apply layout(row_major) to
485 // structure members. By default, all matrices in both HLSL and GLSL
486 // are column major
487 //
488 // * GLSL compiler does not properly handle structs passed as function arguments!!!!
489 // struct MyStruct
490 // {
491 // matrix Matr;
492 // }
493 // void Func(in MyStruct S)
494 // {
495 // ...
496 // mul(f4PosWS, S.Matr); <--- This will not work!!!
497 // }
498 // DO NOT pass structs to functions, use only built-in types!!!
499 //
500 // * GLSL does not support most of the implicit type conversions. The following are some
501 // examples of required modifications to HLSL code:
502 // ** float4 vec = 0; -> float4 vec = float4(0.0, 0.0, 0.0, 0.0);
503 // ** float x = 0; -> float x = 0.0;
504 // ** uint x = 0; -> uint x = 0u;
505 // ** GLES is immensely strict about type conversions. For instance,
506 // this code will produce compiler error: float4(0, 0, 0, 0)
507 // It must be written as float4(0.0, 0.0, 0.0, 0.0)
508 // * GLSL does not support relational and boolean operations on vector types:
509 // ** float2 p = float2(1.0,2.0), q = float2(3.0,4.0);
510 // bool2 b = x < y; -> Error
511 // all(p<q) -> Error
512 // ** To facilitate relational and boolean operations on vector types, the following
513 // functions are predefined:
514 // - Less
515 // - LessEqual
516 // - Greater
517 // - GreaterEqual
518 // - Equal
519 // - NotEqual
520 // - Not
521 // - And
522 // - Or
523 // - BoolToFloat
524 // ** Examples:
525 // bool2 b = x < y; -> bool2 b = Less(x, y);
526 // all(p>=q) -> all( GreaterEqual(p,q) )
527 //
528 // * When accessing elements of an HLSL matrix, the first index is always row:
529 // mat[row][column]
530 // In GLSL, the first index is always column:
531 // mat[column][row]
532 // MATRIX_ELEMENT(mat, row, col) macros is provided to facilitate matrix element retrieval
533 
534 // * The following functions do not have counterparts in GLSL and should be avoided:
535 // ** Texture2DArray.SampleCmpLevelZero()
536 // ** TextureCube.SampleCmpLevelZero()
537 // ** TextureCubeArray.SampleCmpLevelZero()
538 
539 
540 // * Input variables to a shader stage must be listed in exact same order as outputs from
541 // the previous stage. Function return value counts as the first output argument.
SHADER_TYPE
Describes the shader type.
Definition: Shader.h:46
Namespace for the OpenGL implementation of the graphics engine.
Definition: BufferD3D11Impl.h:34
Definition: AdvancedMath.h:316
Unknown shader type.
Definition: Shader.h:48