• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(TCompiler * compiler,TInfoSinkBase & objSink,bool enablePrecision,ShCompileOptions compileOptions)22 TOutputVulkanGLSL::TOutputVulkanGLSL(TCompiler *compiler,
23                                      TInfoSinkBase &objSink,
24                                      bool enablePrecision,
25                                      ShCompileOptions compileOptions)
26     : TOutputGLSL(compiler, objSink, compileOptions),
27       mNextUnusedBinding(0),
28       mNextUnusedInputLocation(0),
29       mNextUnusedOutputLocation(0),
30       mEnablePrecision(enablePrecision)
31 {}
32 
writeLayoutQualifier(TIntermSymbol * symbol)33 void TOutputVulkanGLSL::writeLayoutQualifier(TIntermSymbol *symbol)
34 {
35     const TType &type = symbol->getType();
36 
37     bool needsSetBinding = IsSampler(type.getBasicType()) ||
38                            (type.isInterfaceBlock() && (type.getQualifier() == EvqUniform ||
39                                                         type.getQualifier() == EvqBuffer)) ||
40                            IsImage(type.getBasicType()) || IsSubpassInputType(type.getBasicType());
41     bool needsLocation = type.getQualifier() == EvqAttribute ||
42                          type.getQualifier() == EvqVertexIn ||
43                          type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier());
44     bool needsInputAttachmentIndex = IsSubpassInputType(type.getBasicType());
45     bool needsSpecConstId          = type.getQualifier() == EvqSpecConst;
46 
47     if (!NeedsToWriteLayoutQualifier(type) && !needsSetBinding && !needsLocation &&
48         !needsInputAttachmentIndex && !needsSpecConstId)
49     {
50         return;
51     }
52 
53     TInfoSinkBase &out                      = objSink();
54     const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
55 
56     // This isn't super clean, but it gets the job done.
57     // See corresponding code in glslang_wrapper_utils.cpp.
58     const char *blockStorage  = nullptr;
59     const char *matrixPacking = nullptr;
60 
61     if (type.isInterfaceBlock())
62     {
63         const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
64         TLayoutBlockStorage storage           = interfaceBlock->blockStorage();
65 
66         // Make sure block storage format is specified.
67         if (storage != EbsStd430)
68         {
69             // Change interface block layout qualifiers to std140 for any layout that is not
70             // explicitly set to std430.  This is to comply with GL_KHR_vulkan_glsl where shared and
71             // packed are not allowed (and std140 could be used instead) and unspecified layouts can
72             // assume either std140 or std430 (and we choose std140 as std430 is not yet universally
73             // supported).
74             storage = EbsStd140;
75         }
76 
77         if (interfaceBlock->blockStorage() != EbsUnspecified)
78         {
79             blockStorage = getBlockStorageString(storage);
80         }
81     }
82 
83     // Specify matrix packing if necessary.
84     if (layoutQualifier.matrixPacking != EmpUnspecified)
85     {
86         matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking);
87     }
88     const char *kCommaSeparator = ", ";
89     const char *separator       = "";
90     out << "layout(";
91 
92     // If the resource declaration is about input attachment, need to specify input_attachment_index
93     if (needsInputAttachmentIndex)
94     {
95         out << "input_attachment_index=" << layoutQualifier.inputAttachmentIndex;
96         separator = kCommaSeparator;
97     }
98 
99     // If it's a specialization constant, add that constant_id qualifier.
100     if (needsSpecConstId)
101     {
102         out << separator << "constant_id=" << layoutQualifier.location;
103     }
104 
105     // If the resource declaration requires set & binding layout qualifiers, specify arbitrary
106     // ones.
107     if (needsSetBinding)
108     {
109         out << separator << "set=0, binding=" << nextUnusedBinding();
110         separator = kCommaSeparator;
111     }
112 
113     if (needsLocation)
114     {
115         uint32_t location = 0;
116         if (layoutQualifier.index <= 0)
117         {
118             // Note: for index == 1 (dual source blending), don't count locations as they are
119             // expected to alias the color output locations.  Only one dual-source output is
120             // supported, so location will be always 0.
121             const unsigned int locationCount =
122                 CalculateVaryingLocationCount(symbol->getType(), getShaderType());
123             location = IsShaderIn(type.getQualifier()) ? nextUnusedInputLocation(locationCount)
124                                                        : nextUnusedOutputLocation(locationCount);
125         }
126 
127         out << separator << "location=" << location;
128         separator = kCommaSeparator;
129     }
130 
131     // Output the list of qualifiers already known at this stage, i.e. everything other than
132     // `location` and `set`/`binding`.
133     std::string otherQualifiers = getCommonLayoutQualifiers(symbol);
134 
135     if (blockStorage)
136     {
137         out << separator << blockStorage;
138         separator = kCommaSeparator;
139     }
140     if (matrixPacking)
141     {
142         out << separator << matrixPacking;
143         separator = kCommaSeparator;
144     }
145     if (!otherQualifiers.empty())
146     {
147         out << separator << otherQualifiers;
148     }
149 
150     out << ") ";
151 }
152 
writeVariableType(const TType & type,const TSymbol * symbol,bool isFunctionArgument)153 void TOutputVulkanGLSL::writeVariableType(const TType &type,
154                                           const TSymbol *symbol,
155                                           bool isFunctionArgument)
156 {
157     TType overrideType(type);
158 
159     // External textures are treated as 2D textures in the vulkan back-end
160     if (type.getBasicType() == EbtSamplerExternalOES)
161     {
162         overrideType.setBasicType(EbtSampler2D);
163     }
164 
165     TOutputGLSL::writeVariableType(overrideType, symbol, isFunctionArgument);
166 }
167 
writeVariablePrecision(TPrecision precision)168 bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision)
169 {
170     if ((precision == EbpUndefined) || !mEnablePrecision)
171         return false;
172 
173     TInfoSinkBase &out = objSink();
174     out << getPrecisionString(precision);
175     return true;
176 }
177 
178 }  // namespace sh
179