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