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 "vkShaderToSpirV.hpp"
26 #include "vkSpirVAsm.hpp"
27 #include "vkRefUtil.hpp"
28
29 #include "deArrayUtil.hpp"
30 #include "deMemory.h"
31 #include "deInt32.h"
32
33 namespace vk
34 {
35
36 using std::string;
37 using std::vector;
38
39 #if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
40 # define VALIDATE_BINARIES true
41 #else
42 # define VALIDATE_BINARIES false
43 #endif
44
45 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
46
47 // ProgramBinary
48
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)49 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
50 : m_format (format)
51 , m_binary (binary, binary+binarySize)
52 {
53 }
54
55 // Utils
56
57 namespace
58 {
59
isNativeSpirVBinaryEndianness(void)60 bool isNativeSpirVBinaryEndianness (void)
61 {
62 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
63 return true;
64 #else
65 return false;
66 #endif
67 }
68
isSaneSpirVBinary(const ProgramBinary & binary)69 bool isSaneSpirVBinary (const ProgramBinary& binary)
70 {
71 const deUint32 spirvMagicWord = 0x07230203;
72 const deUint32 spirvMagicBytes = isNativeSpirVBinaryEndianness()
73 ? spirvMagicWord
74 : deReverseBytes32(spirvMagicWord);
75
76 DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
77
78 if (binary.getSize() % sizeof(deUint32) != 0)
79 return false;
80
81 if (binary.getSize() < sizeof(deUint32))
82 return false;
83
84 if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
85 return false;
86
87 return true;
88 }
89
createProgramBinaryFromSpirV(const vector<deUint32> & binary)90 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
91 {
92 DE_ASSERT(!binary.empty());
93
94 if (isNativeSpirVBinaryEndianness())
95 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
96 else
97 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
98 }
99
100 } // anonymous
101
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvVersion spirvVersion)102 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvVersion spirvVersion)
103 {
104 std::ostringstream validationLog;
105
106 if (!validateSpirV(binary.size(), &binary[0], &validationLog, spirvVersion))
107 {
108 buildInfo->program.linkOk = false;
109 buildInfo->program.infoLog += "\n" + validationLog.str();
110
111 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
112 }
113 }
114
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo)115 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo)
116 {
117 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
118 const bool validateBinary = VALIDATE_BINARIES;
119 vector<deUint32> binary;
120
121 {
122 vector<deUint32> nonStrippedBinary;
123
124 if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
125 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
126
127 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
128 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
129 TCU_CHECK_INTERNAL(!binary.empty());
130 }
131
132 if (validateBinary)
133 validateCompiledBinary(binary, buildInfo, spirvVersion);
134
135 return createProgramBinaryFromSpirV(binary);
136 }
137
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo)138 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo)
139 {
140 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
141 const bool validateBinary = VALIDATE_BINARIES;
142 vector<deUint32> binary;
143
144 {
145 vector<deUint32> nonStrippedBinary;
146
147 if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
148 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
149
150 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
151 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
152 TCU_CHECK_INTERNAL(!binary.empty());
153 }
154
155 if (validateBinary)
156 validateCompiledBinary(binary, buildInfo, spirvVersion);
157
158 return createProgramBinaryFromSpirV(binary);
159 }
160
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo)161 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo)
162 {
163 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
164 const bool validateBinary = VALIDATE_BINARIES;
165 vector<deUint32> binary;
166
167 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
168 TCU_THROW(InternalError, "Failed to assemble SPIR-V");
169
170 if (validateBinary)
171 {
172 std::ostringstream validationLog;
173
174 if (!validateSpirV(binary.size(), &binary[0], &validationLog, spirvVersion))
175 {
176 buildInfo->compileOk = false;
177 buildInfo->infoLog += "\n" + validationLog.str();
178
179 TCU_THROW(InternalError, "Validation failed for assembled SPIR-V binary");
180 }
181 }
182
183 return createProgramBinaryFromSpirV(binary);
184 }
185
disassembleProgram(const ProgramBinary & program,std::ostream * dst)186 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
187 {
188 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
189 {
190 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
191
192 if (isNativeSpirVBinaryEndianness())
193 disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
194 extractSpirvVersion(program));
195 else
196 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
197 }
198 else
199 TCU_THROW(NotSupportedError, "Unsupported program format");
200 }
201
validateProgram(const ProgramBinary & program,std::ostream * dst)202 bool validateProgram (const ProgramBinary& program, std::ostream* dst)
203 {
204 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
205 {
206 if (!isSaneSpirVBinary(program))
207 {
208 *dst << "Binary doesn't look like SPIR-V at all";
209 return false;
210 }
211
212 if (isNativeSpirVBinaryEndianness())
213 return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
214 extractSpirvVersion(program));
215 else
216 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
217 }
218 else
219 TCU_THROW(NotSupportedError, "Unsupported program format");
220 }
221
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)222 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
223 {
224 if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
225 {
226 const struct VkShaderModuleCreateInfo shaderModuleInfo =
227 {
228 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
229 DE_NULL,
230 flags,
231 (deUintptr)binary.getSize(),
232 (const deUint32*)binary.getBinary(),
233 };
234
235 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
236 }
237 else
238 TCU_THROW(NotSupportedError, "Unsupported program format");
239 }
240
getGluShaderType(VkShaderStageFlagBits shaderStage)241 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
242 {
243 switch (shaderStage)
244 {
245 case VK_SHADER_STAGE_VERTEX_BIT: return glu::SHADERTYPE_VERTEX;
246 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return glu::SHADERTYPE_TESSELLATION_CONTROL;
247 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return glu::SHADERTYPE_TESSELLATION_EVALUATION;
248 case VK_SHADER_STAGE_GEOMETRY_BIT: return glu::SHADERTYPE_GEOMETRY;
249 case VK_SHADER_STAGE_FRAGMENT_BIT: return glu::SHADERTYPE_FRAGMENT;
250 case VK_SHADER_STAGE_COMPUTE_BIT: return glu::SHADERTYPE_COMPUTE;
251 default:
252 DE_FATAL("Unknown shader stage");
253 return glu::SHADERTYPE_LAST;
254 }
255 }
256
getVkShaderStage(glu::ShaderType shaderType)257 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
258 {
259 static const VkShaderStageFlagBits s_shaderStages[] =
260 {
261 VK_SHADER_STAGE_VERTEX_BIT,
262 VK_SHADER_STAGE_FRAGMENT_BIT,
263 VK_SHADER_STAGE_GEOMETRY_BIT,
264 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
265 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
266 VK_SHADER_STAGE_COMPUTE_BIT
267 };
268
269 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
270 }
271
272 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)273 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
274 {
275 return vk::SPIRV_VERSION_1_0;
276 }
277
278 // Max supported versions for each vulkan version
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)279 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
280 {
281 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
282
283 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
284 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
285 result = vk::SPIRV_VERSION_1_0;
286 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
287 result = vk::SPIRV_VERSION_1_3;
288
289 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
290
291 return result;
292 }
293
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)294 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
295 {
296 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
297
298 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
299 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
300 result = vk::SPIRV_VERSION_1_0;
301 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
302 result = vk::SPIRV_VERSION_1_3;
303
304 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
305
306 return result;
307 }
308
extractSpirvVersion(const ProgramBinary & binary)309 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
310 {
311 DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
312
313 if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
314 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
315
316 if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
317 TCU_THROW(InternalError, "Invalid SPIR-V header format");
318
319 const deUint32 spirvBinaryVersion10 = 0x00010000;
320 const deUint32 spirvBinaryVersion11 = 0x00010100;
321 const deUint32 spirvBinaryVersion12 = 0x00010200;
322 const deUint32 spirvBinaryVersion13 = 0x00010300;
323 const SpirvBinaryHeader* header = reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
324 const deUint32 spirvVersion = isNativeSpirVBinaryEndianness()
325 ? header->version
326 : deReverseBytes32(header->version);
327 SpirvVersion result = SPIRV_VERSION_LAST;
328
329 switch (spirvVersion)
330 {
331 case spirvBinaryVersion10: result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
332 case spirvBinaryVersion11: result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
333 case spirvBinaryVersion12: result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
334 case spirvBinaryVersion13: result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
335 default: TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
336 }
337
338 return result;
339 }
340
getSpirvVersionName(const SpirvVersion spirvVersion)341 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
342 {
343 DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
344 DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
345
346 std::string result;
347
348 switch (spirvVersion)
349 {
350 case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
351 case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
352 case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
353 case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
354 default: result = "Unknown";
355 }
356
357 return result;
358 }
359
operator ++(SpirvVersion & spirvVersion)360 SpirvVersion& operator++(SpirvVersion& spirvVersion)
361 {
362 if (spirvVersion == SPIRV_VERSION_LAST)
363 spirvVersion = SPIRV_VERSION_1_0;
364 else
365 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
366
367 return spirvVersion;
368 }
369
370 } // vk
371