1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // OutputVulkanGLSL:
7 //   Code that outputs shaders that fit GL_KHR_vulkan_glsl, to be fed to glslang to generate
8 //   SPIR-V.
9 //   See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
10 //
11 
12 #include "compiler/translator/OutputVulkanGLSL.h"
13 
14 #include "compiler/translator/BaseTypes.h"
15 #include "compiler/translator/Symbol.h"
16 #include "compiler/translator/ValidateVaryingLocations.h"
17 #include "compiler/translator/util.h"
18 
19 namespace sh
20 {
21 
TOutputVulkanGLSL(TInfoSinkBase & objSink,ShHashFunction64 hashFunction,NameMap & nameMap,TSymbolTable * symbolTable,sh::GLenum shaderType,int shaderVersion,ShShaderOutput output,bool forceHighp,bool enablePrecision,ShCompileOptions compileOptions)22 TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
23                                      ShHashFunction64 hashFunction,
24                                      NameMap &nameMap,
25                                      TSymbolTable *symbolTable,
26                                      sh::GLenum shaderType,
27                                      int shaderVersion,
28                                      ShShaderOutput output,
29                                      bool forceHighp,
30                                      bool enablePrecision,
31                                      ShCompileOptions compileOptions)
32     : TOutputGLSL(objSink,
33                   hashFunction,
34                   nameMap,
35                   symbolTable,
36                   shaderType,
37                   shaderVersion,
38                   output,
39                   compileOptions),
40       mNextUnusedBinding(0),
41       mNextUnusedInputLocation(0),
42       mNextUnusedOutputLocation(0),
43       mForceHighp(forceHighp),
44       mEnablePrecision(enablePrecision)
45 {}
46 
writeLayoutQualifier(TIntermSymbol * symbol)47 void TOutputVulkanGLSL::writeLayoutQualifier(TIntermSymbol *symbol)
48 {
49     const TType &type = symbol->getType();
50 
51     bool needsSetBinding = IsSampler(type.getBasicType()) ||
52                            (type.isInterfaceBlock() && (type.getQualifier() == EvqUniform ||
53                                                         type.getQualifier() == EvqBuffer)) ||
54                            IsImage(type.getBasicType()) || IsSubpassInputType(type.getBasicType());
55     bool needsLocation = type.getQualifier() == EvqAttribute ||
56                          type.getQualifier() == EvqVertexIn ||
57                          type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier());
58     bool needsInputAttachmentIndex = IsSubpassInputType(type.getBasicType());
59     bool needsSpecConstId          = type.getQualifier() == EvqSpecConst;
60 
61     if (!NeedsToWriteLayoutQualifier(type) && !needsSetBinding && !needsLocation &&
62         !needsInputAttachmentIndex && !needsSpecConstId)
63     {
64         return;
65     }
66 
67     TInfoSinkBase &out                      = objSink();
68     const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
69 
70     // This isn't super clean, but it gets the job done.
71     // See corresponding code in glslang_wrapper_utils.cpp.
72     const char *blockStorage  = nullptr;
73     const char *matrixPacking = nullptr;
74 
75     if (type.isInterfaceBlock())
76     {
77         const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
78         TLayoutBlockStorage storage           = interfaceBlock->blockStorage();
79 
80         // Make sure block storage format is specified.
81         if (storage != EbsStd430)
82         {
83             // Change interface block layout qualifiers to std140 for any layout that is not
84             // explicitly set to std430.  This is to comply with GL_KHR_vulkan_glsl where shared and
85             // packed are not allowed (and std140 could be used instead) and unspecified layouts can
86             // assume either std140 or std430 (and we choose std140 as std430 is not yet universally
87             // supported).
88             storage = EbsStd140;
89         }
90 
91         if (interfaceBlock->blockStorage() != EbsUnspecified)
92         {
93             blockStorage = getBlockStorageString(storage);
94         }
95     }
96 
97     // Specify matrix packing if necessary.
98     if (layoutQualifier.matrixPacking != EmpUnspecified)
99     {
100         matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking);
101     }
102     const char *kCommaSeparator = ", ";
103     const char *separator       = "";
104     out << "layout(";
105 
106     // If the resource declaration is about input attachment, need to specify input_attachment_index
107     if (needsInputAttachmentIndex)
108     {
109         out << "input_attachment_index=" << layoutQualifier.inputAttachmentIndex;
110         separator = kCommaSeparator;
111     }
112 
113     // If it's a specialization constant, add that constant_id qualifier.
114     if (needsSpecConstId)
115     {
116         out << separator << "constant_id=" << layoutQualifier.location;
117     }
118 
119     // If the resource declaration requires set & binding layout qualifiers, specify arbitrary
120     // ones.
121     if (needsSetBinding)
122     {
123         out << separator << "set=0, binding=" << nextUnusedBinding();
124         separator = kCommaSeparator;
125     }
126 
127     if (needsLocation)
128     {
129         uint32_t location = 0;
130         if (layoutQualifier.index <= 0)
131         {
132             // Note: for index == 1 (dual source blending), don't count locations as they are
133             // expected to alias the color output locations.  Only one dual-source output is
134             // supported, so location will be always 0.
135             const unsigned int locationCount =
136                 CalculateVaryingLocationCount(symbol->getType(), getShaderType());
137             location = IsShaderIn(type.getQualifier()) ? nextUnusedInputLocation(locationCount)
138                                                        : nextUnusedOutputLocation(locationCount);
139         }
140 
141         out << separator << "location=" << location;
142         separator = kCommaSeparator;
143     }
144 
145     // Output the list of qualifiers already known at this stage, i.e. everything other than
146     // `location` and `set`/`binding`.
147     std::string otherQualifiers = getCommonLayoutQualifiers(symbol);
148 
149     if (blockStorage)
150     {
151         out << separator << blockStorage;
152         separator = kCommaSeparator;
153     }
154     if (matrixPacking)
155     {
156         out << separator << matrixPacking;
157         separator = kCommaSeparator;
158     }
159     if (!otherQualifiers.empty())
160     {
161         out << separator << otherQualifiers;
162     }
163 
164     out << ") ";
165 }
166 
writeVariableType(const TType & type,const TSymbol * symbol,bool isFunctionArgument)167 void TOutputVulkanGLSL::writeVariableType(const TType &type,
168                                           const TSymbol *symbol,
169                                           bool isFunctionArgument)
170 {
171     TType overrideType(type);
172 
173     // External textures are treated as 2D textures in the vulkan back-end
174     if (type.getBasicType() == EbtSamplerExternalOES)
175     {
176         overrideType.setBasicType(EbtSampler2D);
177     }
178 
179     TOutputGLSL::writeVariableType(overrideType, symbol, isFunctionArgument);
180 }
181 
writeVariablePrecision(TPrecision precision)182 bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision)
183 {
184     if ((precision == EbpUndefined) || !mEnablePrecision)
185         return false;
186 
187     TInfoSinkBase &out = objSink();
188     if (mForceHighp)
189         out << getPrecisionString(EbpHigh);
190     else
191         out << getPrecisionString(precision);
192     return true;
193 }
194 
195 }  // namespace sh
196