1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2015 Google Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Program utilities.
22 *//*--------------------------------------------------------------------*/
23
24 #include "vkPrograms.hpp"
25 #include "vkGlslToSpirV.hpp"
26 #include "vkSpirVAsm.hpp"
27 #include "vkRefUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30
31 #include "deArrayUtil.hpp"
32 #include "deMemory.h"
33 #include "deInt32.h"
34
35 namespace vk
36 {
37
38 using std::string;
39 using std::vector;
40 using tcu::TestLog;
41
42 #if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
43 # define VALIDATE_BINARIES true
44 #else
45 # define VALIDATE_BINARIES false
46 #endif
47
48 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
49
50 // ProgramBinary
51
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)52 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
53 : m_format (format)
54 , m_binary (binary, binary+binarySize)
55 {
56 }
57
58 // Utils
59
60 namespace
61 {
62
isNativeSpirVBinaryEndianness(void)63 bool isNativeSpirVBinaryEndianness (void)
64 {
65 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
66 return true;
67 #else
68 return false;
69 #endif
70 }
71
isSaneSpirVBinary(const ProgramBinary & binary)72 bool isSaneSpirVBinary (const ProgramBinary& binary)
73 {
74 const deUint32 spirvMagicWord = 0x07230203;
75 const deUint32 spirvMagicBytes = isNativeSpirVBinaryEndianness()
76 ? spirvMagicWord
77 : deReverseBytes32(spirvMagicWord);
78
79 DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
80
81 if (binary.getSize() % sizeof(deUint32) != 0)
82 return false;
83
84 if (binary.getSize() < sizeof(deUint32))
85 return false;
86
87 if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
88 return false;
89
90 return true;
91 }
92
createProgramBinaryFromSpirV(const vector<deUint32> & binary)93 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
94 {
95 DE_ASSERT(!binary.empty());
96
97 if (isNativeSpirVBinaryEndianness())
98 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
99 else
100 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
101 }
102
103 } // anonymous
104
buildProgram(const glu::ProgramSources & program,ProgramFormat binaryFormat,glu::ShaderProgramInfo * buildInfo)105 ProgramBinary* buildProgram (const glu::ProgramSources& program, ProgramFormat binaryFormat, glu::ShaderProgramInfo* buildInfo)
106 {
107 const bool validateBinary = VALIDATE_BINARIES;
108
109 if (binaryFormat == PROGRAM_FORMAT_SPIRV)
110 {
111 vector<deUint32> binary;
112
113 if (!compileGlslToSpirV(program, &binary, buildInfo))
114 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
115
116 if (validateBinary)
117 {
118 std::ostringstream validationLog;
119
120 if (!validateSpirV(binary.size(), &binary[0], &validationLog))
121 {
122 buildInfo->program.linkOk = false;
123 buildInfo->program.infoLog += "\n" + validationLog.str();
124
125 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
126 }
127 }
128
129 return createProgramBinaryFromSpirV(binary);
130 }
131 else
132 TCU_THROW(NotSupportedError, "Unsupported program format");
133 }
134
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo)135 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo)
136 {
137 const bool validateBinary = VALIDATE_BINARIES;
138 vector<deUint32> binary;
139
140 if (!assembleSpirV(&program, &binary, buildInfo))
141 TCU_THROW(InternalError, "Failed to assemble SPIR-V");
142
143 if (validateBinary)
144 {
145 std::ostringstream validationLog;
146
147 if (!validateSpirV(binary.size(), &binary[0], &validationLog))
148 {
149 buildInfo->compileOk = false;
150 buildInfo->infoLog += "\n" + validationLog.str();
151
152 TCU_THROW(InternalError, "Validation failed for assembled SPIR-V binary");
153 }
154 }
155
156 return createProgramBinaryFromSpirV(binary);
157 }
158
disassembleProgram(const ProgramBinary & program,std::ostream * dst)159 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
160 {
161 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
162 {
163 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
164
165 if (isNativeSpirVBinaryEndianness())
166 disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst);
167 else
168 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
169 }
170 else
171 TCU_THROW(NotSupportedError, "Unsupported program format");
172 }
173
validateProgram(const ProgramBinary & program,std::ostream * dst)174 bool validateProgram (const ProgramBinary& program, std::ostream* dst)
175 {
176 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
177 {
178 if (!isSaneSpirVBinary(program))
179 {
180 *dst << "Binary doesn't look like SPIR-V at all";
181 return false;
182 }
183
184 if (isNativeSpirVBinaryEndianness())
185 return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst);
186 else
187 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
188 }
189 else
190 TCU_THROW(NotSupportedError, "Unsupported program format");
191 }
192
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)193 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
194 {
195 if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
196 {
197 const struct VkShaderModuleCreateInfo shaderModuleInfo =
198 {
199 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
200 DE_NULL,
201 flags,
202 (deUintptr)binary.getSize(),
203 (const deUint32*)binary.getBinary(),
204 };
205
206 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
207 }
208 else
209 TCU_THROW(NotSupportedError, "Unsupported program format");
210 }
211
getGluShaderType(VkShaderStageFlagBits shaderStage)212 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
213 {
214 switch (shaderStage)
215 {
216 case VK_SHADER_STAGE_VERTEX_BIT: return glu::SHADERTYPE_VERTEX;
217 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return glu::SHADERTYPE_TESSELLATION_CONTROL;
218 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return glu::SHADERTYPE_TESSELLATION_EVALUATION;
219 case VK_SHADER_STAGE_GEOMETRY_BIT: return glu::SHADERTYPE_GEOMETRY;
220 case VK_SHADER_STAGE_FRAGMENT_BIT: return glu::SHADERTYPE_FRAGMENT;
221 case VK_SHADER_STAGE_COMPUTE_BIT: return glu::SHADERTYPE_COMPUTE;
222 default:
223 DE_FATAL("Unknown shader stage");
224 return glu::SHADERTYPE_LAST;
225 }
226 }
227
getVkShaderStage(glu::ShaderType shaderType)228 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
229 {
230 static const VkShaderStageFlagBits s_shaderStages[] =
231 {
232 VK_SHADER_STAGE_VERTEX_BIT,
233 VK_SHADER_STAGE_FRAGMENT_BIT,
234 VK_SHADER_STAGE_GEOMETRY_BIT,
235 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
236 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
237 VK_SHADER_STAGE_COMPUTE_BIT
238 };
239
240 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
241 }
242
243 } // vk
244