1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2019 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 "spirv-tools/optimizer.hpp"
25
26 #include "qpInfo.h"
27
28 #include "vkPrograms.hpp"
29 #include "vkShaderToSpirV.hpp"
30 #include "vkSpirVAsm.hpp"
31 #include "vkRefUtil.hpp"
32
33 #include "deMutex.hpp"
34 #include "deFilePath.hpp"
35 #include "deArrayUtil.hpp"
36 #include "deMemory.h"
37 #include "deInt32.h"
38
39 #include "tcuCommandLine.hpp"
40
41 #include <map>
42
43 namespace vk
44 {
45
46 using std::string;
47 using std::vector;
48 using std::map;
49
50 #if defined(DE_DEBUG)
51 # define VALIDATE_BINARIES true
52 #else
53 # define VALIDATE_BINARIES false
54 #endif
55
56 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
57
58 // ProgramBinary
59
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)60 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
61 : m_format (format)
62 , m_binary (binary, binary+binarySize)
63 , m_used (false)
64 {
65 }
66
67 // Utils
68
69 namespace
70 {
71
isNativeSpirVBinaryEndianness(void)72 bool isNativeSpirVBinaryEndianness (void)
73 {
74 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
75 return true;
76 #else
77 return false;
78 #endif
79 }
80
isSaneSpirVBinary(const ProgramBinary & binary)81 bool isSaneSpirVBinary (const ProgramBinary& binary)
82 {
83 const deUint32 spirvMagicWord = 0x07230203;
84 const deUint32 spirvMagicBytes = isNativeSpirVBinaryEndianness()
85 ? spirvMagicWord
86 : deReverseBytes32(spirvMagicWord);
87
88 DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
89
90 if (binary.getSize() % sizeof(deUint32) != 0)
91 return false;
92
93 if (binary.getSize() < sizeof(deUint32))
94 return false;
95
96 if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
97 return false;
98
99 return true;
100 }
101
optimizeCompiledBinary(vector<deUint32> & binary,int optimizationRecipe,const SpirvVersion spirvVersion)102 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
103 {
104 spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
105
106 // Map SpirvVersion with spv_target_env:
107 switch (spirvVersion)
108 {
109 case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0; break;
110 case SPIRV_VERSION_1_1:
111 case SPIRV_VERSION_1_2:
112 case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1; break;
113 case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4; break;
114 case SPIRV_VERSION_1_5: targetEnv = SPV_ENV_VULKAN_1_2; break;
115 default:
116 TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
117 }
118
119 spvtools::Optimizer optimizer(targetEnv);
120
121 switch (optimizationRecipe)
122 {
123 case 1:
124 optimizer.RegisterPerformancePasses();
125 break;
126 case 2:
127 optimizer.RegisterSizePasses();
128 break;
129 default:
130 TCU_THROW(InternalError, "Unknown optimization recipe requested");
131 }
132
133 spvtools::OptimizerOptions optimizer_options;
134 optimizer_options.set_run_validator(false);
135 const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
136
137 if (!ok)
138 TCU_THROW(InternalError, "Optimizer call failed");
139 }
140
createProgramBinaryFromSpirV(const vector<deUint32> & binary)141 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
142 {
143 DE_ASSERT(!binary.empty());
144
145 if (isNativeSpirVBinaryEndianness())
146 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
147 else
148 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
149 }
150
151 } // anonymous
152
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvValidatorOptions & options)153 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
154 {
155 std::ostringstream validationLog;
156
157 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
158 {
159 buildInfo->program.linkOk = false;
160 buildInfo->program.infoLog += "\n" + validationLog.str();
161
162 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
163 }
164 }
165
validateCompiledBinary(const vector<deUint32> & binary,SpirVProgramInfo * buildInfo,const SpirvValidatorOptions & options)166 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
167 {
168 std::ostringstream validationLog;
169
170 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
171 {
172 buildInfo->compileOk = false;
173 buildInfo->infoLog += "\n" + validationLog.str();
174
175 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
176 }
177 }
178
179 de::Mutex cacheFileMutex;
180 map<deUint32, vector<deUint32> > cacheFileIndex;
181 bool cacheFileFirstRun = true;
182
shaderCacheFirstRunCheck(const char * shaderCacheFile,bool truncate)183 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
184 {
185 cacheFileMutex.lock();
186 if (cacheFileFirstRun)
187 {
188 cacheFileFirstRun = false;
189 if (truncate)
190 {
191 // Open file with "w" access to truncate it
192 FILE* f = fopen(shaderCacheFile, "wb");
193 if (f)
194 fclose(f);
195 }
196 else
197 {
198 // Parse chunked shader cache file for hashes and offsets
199 FILE* file = fopen(shaderCacheFile, "rb");
200 int count = 0;
201 if (file)
202 {
203 deUint32 chunksize = 0;
204 deUint32 hash = 0;
205 deUint32 offset = 0;
206 bool ok = true;
207 while (ok)
208 {
209 offset = (deUint32)ftell(file);
210 if (ok) ok = fread(&chunksize, 1, 4, file) == 4;
211 if (ok) ok = fread(&hash, 1, 4, file) == 4;
212 if (ok) cacheFileIndex[hash].push_back(offset);
213 if (ok) ok = fseek(file, offset + chunksize, SEEK_SET) == 0;
214 count++;
215 }
216 fclose(file);
217 }
218 }
219 }
220 cacheFileMutex.unlock();
221 }
222
intToString(deUint32 integer)223 std::string intToString (deUint32 integer)
224 {
225 std::stringstream temp_sstream;
226
227 temp_sstream << integer;
228
229 return temp_sstream.str();
230 }
231
232 // 32-bit FNV-1 hash
shadercacheHash(const char * str)233 deUint32 shadercacheHash (const char* str)
234 {
235 deUint32 hash = 0x811c9dc5;
236 deUint32 c;
237 while ((c = (deUint32)*str++) != 0)
238 {
239 hash *= 16777619;
240 hash ^= c;
241 }
242 return hash;
243 }
244
shadercacheLoad(const std::string & shaderstring,const char * shaderCacheFilename)245 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
246 {
247 deUint32 hash = shadercacheHash(shaderstring.c_str());
248 deInt32 format;
249 deInt32 length;
250 deInt32 sourcelength;
251 deUint32 i;
252 deUint32 temp;
253 deUint8* bin = 0;
254 char* source = 0;
255 deBool ok = true;
256 deBool diff = true;
257 cacheFileMutex.lock();
258
259 if (cacheFileIndex.count(hash) == 0)
260 {
261 cacheFileMutex.unlock();
262 return 0;
263 }
264 FILE* file = fopen(shaderCacheFilename, "rb");
265 ok = file != 0;
266
267 for (i = 0; i < cacheFileIndex[hash].size(); i++)
268 {
269 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET) == 0;
270 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Chunk size (skip)
271 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Stored hash
272 if (ok) ok = temp == hash; // Double check
273 if (ok) ok = fread(&format, 1, 4, file) == 4;
274 if (ok) ok = fread(&length, 1, 4, file) == 4;
275 if (ok) ok = length > 0; // sanity check
276 if (ok) bin = new deUint8[length];
277 if (ok) ok = fread(bin, 1, length, file) == (size_t)length;
278 if (ok) ok = fread(&sourcelength, 1, 4, file) == 4;
279 if (ok && sourcelength > 0)
280 {
281 source = new char[sourcelength + 1];
282 ok = fread(source, 1, sourcelength, file) == (size_t)sourcelength;
283 source[sourcelength] = 0;
284 diff = shaderstring != std::string(source);
285 }
286 if (!ok || diff)
287 {
288 // Mismatch, but may still exist in cache if there were hash collisions
289 delete[] source;
290 delete[] bin;
291 }
292 else
293 {
294 delete[] source;
295 if (file) fclose(file);
296 cacheFileMutex.unlock();
297 vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
298 delete[] bin;
299 return res;
300 }
301 }
302 if (file) fclose(file);
303 cacheFileMutex.unlock();
304 return 0;
305 }
306
shadercacheSave(const vk::ProgramBinary * binary,const std::string & shaderstring,const char * shaderCacheFilename)307 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
308 {
309 if (binary == 0)
310 return;
311 deUint32 hash = shadercacheHash(shaderstring.c_str());
312 deInt32 format = binary->getFormat();
313 deUint32 length = (deUint32)binary->getSize();
314 deUint32 chunksize;
315 deUint32 offset;
316 const deUint8* bin = binary->getBinary();
317 const de::FilePath filePath (shaderCacheFilename);
318
319 cacheFileMutex.lock();
320
321 if (cacheFileIndex[hash].size())
322 {
323 FILE* file = fopen(shaderCacheFilename, "rb");
324 deBool ok = (file != 0);
325 deBool diff = DE_TRUE;
326 deInt32 sourcelength;
327 deUint32 i;
328 deUint32 temp;
329
330 for (i = 0; i < cacheFileIndex[hash].size(); i++)
331 {
332 deUint32 cachedLength = 0;
333
334 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET) == 0;
335 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Chunk size (skip)
336 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Stored hash
337 if (ok) ok = temp == hash; // Double check
338 if (ok) ok = fread(&temp, 1, 4, file) == 4;
339 if (ok) ok = fread(&cachedLength, 1, 4, file) == 4;
340 if (ok) ok = cachedLength > 0; // sanity check
341 if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
342 if (ok) ok = fread(&sourcelength, 1, 4, file) == 4;
343
344 if (ok && sourcelength > 0)
345 {
346 char* source;
347 source = new char[sourcelength + 1];
348 ok = fread(source, 1, sourcelength, file) == (size_t)sourcelength;
349 source[sourcelength] = 0;
350 diff = shaderstring != std::string(source);
351 delete[] source;
352 }
353
354 if (ok && !diff)
355 {
356 // Already in cache (written by another thread, probably)
357 fclose(file);
358 cacheFileMutex.unlock();
359 return;
360 }
361 }
362 fclose(file);
363 }
364
365 if (!de::FilePath(filePath.getDirName()).exists())
366 de::createDirectoryAndParents(filePath.getDirName().c_str());
367
368 FILE* file = fopen(shaderCacheFilename, "ab");
369 if (!file)
370 {
371 cacheFileMutex.unlock();
372 return;
373 }
374 // Append mode starts writing from the end of the file,
375 // but unless we do a seek, ftell returns 0.
376 fseek(file, 0, SEEK_END);
377 offset = (deUint32)ftell(file);
378 chunksize = 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
379 fwrite(&chunksize, 1, 4, file);
380 fwrite(&hash, 1, 4, file);
381 fwrite(&format, 1, 4, file);
382 fwrite(&length, 1, 4, file);
383 fwrite(bin, 1, length, file);
384 length = (deUint32)shaderstring.length();
385 fwrite(&length, 1, 4, file);
386 fwrite(shaderstring.c_str(), 1, length, file);
387 fclose(file);
388 cacheFileIndex[hash].push_back(offset);
389
390 cacheFileMutex.unlock();
391 }
392
393 // Insert any information that may affect compilation into the shader string.
getCompileEnvironment(std::string & shaderstring)394 void getCompileEnvironment (std::string& shaderstring)
395 {
396 shaderstring += "GLSL:";
397 shaderstring += qpGetReleaseGlslName();
398 shaderstring += "\nSpir-v Tools:";
399 shaderstring += qpGetReleaseSpirvToolsName();
400 shaderstring += "\nSpir-v Headers:";
401 shaderstring += qpGetReleaseSpirvHeadersName();
402 shaderstring += "\n";
403 }
404
405 // Insert compilation options into the shader string.
getBuildOptions(std::string & shaderstring,const ShaderBuildOptions & buildOptions,int optimizationRecipe)406 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
407 {
408 shaderstring += "Target Spir-V ";
409 shaderstring += getSpirvVersionName(buildOptions.targetVersion);
410 shaderstring += "\n";
411 if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
412 shaderstring += "Flag:Allow relaxed offsets\n";
413 if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
414 shaderstring += "Flag:Use storage buffer storage class\n";
415 if (optimizationRecipe != 0)
416 {
417 shaderstring += "Optimization recipe ";
418 shaderstring += de::toString(optimizationRecipe);
419 shaderstring += "\n";
420 }
421 }
422
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)423 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
424 {
425 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
426 const bool validateBinary = VALIDATE_BINARIES;
427 vector<deUint32> binary;
428 std::string cachekey;
429 std::string shaderstring;
430 vk::ProgramBinary* res = 0;
431 const int optimizationRecipe = commandLine.getOptimizationRecipe();
432
433 if (commandLine.isShadercacheEnabled())
434 {
435 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
436 getCompileEnvironment(cachekey);
437 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
438
439 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
440 {
441 if (!program.sources[i].empty())
442 {
443 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
444
445 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
446 shaderstring += *it;
447 }
448 }
449
450 cachekey = cachekey + shaderstring;
451
452 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
453
454 if (res)
455 {
456 buildInfo->program.infoLog = "Loaded from cache";
457 buildInfo->program.linkOk = true;
458 buildInfo->program.linkTimeUs = 0;
459
460 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
461 {
462 if (!program.sources[shaderType].empty())
463 {
464 glu::ShaderInfo shaderBuildInfo;
465
466 shaderBuildInfo.type = (glu::ShaderType)shaderType;
467 shaderBuildInfo.source = shaderstring;
468 shaderBuildInfo.compileTimeUs = 0;
469 shaderBuildInfo.compileOk = true;
470
471 buildInfo->shaders.push_back(shaderBuildInfo);
472 }
473 }
474 }
475 }
476
477 if (!res)
478 {
479 {
480 vector<deUint32> nonStrippedBinary;
481
482 if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
483 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
484
485 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
486 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
487 TCU_CHECK_INTERNAL(!binary.empty());
488 }
489
490 if (optimizationRecipe != 0)
491 {
492 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
493 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
494 }
495
496 if (validateBinary)
497 {
498 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
499 }
500
501 res = createProgramBinaryFromSpirV(binary);
502 if (commandLine.isShadercacheEnabled())
503 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
504 }
505 return res;
506 }
507
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)508 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
509 {
510 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
511 const bool validateBinary = VALIDATE_BINARIES;
512 vector<deUint32> binary;
513 std::string cachekey;
514 std::string shaderstring;
515 vk::ProgramBinary* res = 0;
516 const int optimizationRecipe = commandLine.getOptimizationRecipe();
517
518 if (commandLine.isShadercacheEnabled())
519 {
520 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
521 getCompileEnvironment(cachekey);
522 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
523
524 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
525 {
526 if (!program.sources[i].empty())
527 {
528 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
529
530 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
531 shaderstring += *it;
532 }
533 }
534
535 cachekey = cachekey + shaderstring;
536
537 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
538
539 if (res)
540 {
541 buildInfo->program.infoLog = "Loaded from cache";
542 buildInfo->program.linkOk = true;
543 buildInfo->program.linkTimeUs = 0;
544
545 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
546 {
547 if (!program.sources[shaderType].empty())
548 {
549 glu::ShaderInfo shaderBuildInfo;
550
551 shaderBuildInfo.type = (glu::ShaderType)shaderType;
552 shaderBuildInfo.source = shaderstring;
553 shaderBuildInfo.compileTimeUs = 0;
554 shaderBuildInfo.compileOk = true;
555
556 buildInfo->shaders.push_back(shaderBuildInfo);
557 }
558 }
559 }
560 }
561
562 if (!res)
563 {
564 {
565 vector<deUint32> nonStrippedBinary;
566
567 if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
568 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
569
570 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
571 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
572 TCU_CHECK_INTERNAL(!binary.empty());
573 }
574
575 if (optimizationRecipe != 0)
576 {
577 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
578 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
579 }
580
581 if (validateBinary)
582 {
583 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
584 }
585
586 res = createProgramBinaryFromSpirV(binary);
587 if (commandLine.isShadercacheEnabled())
588 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
589 }
590 return res;
591 }
592
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo,const tcu::CommandLine & commandLine)593 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
594 {
595 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
596 const bool validateBinary = VALIDATE_BINARIES;
597 vector<deUint32> binary;
598 vk::ProgramBinary* res = 0;
599 std::string cachekey;
600 const int optimizationRecipe = commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
601
602 if (commandLine.isShadercacheEnabled())
603 {
604 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
605 getCompileEnvironment(cachekey);
606 cachekey += "Target Spir-V ";
607 cachekey += getSpirvVersionName(spirvVersion);
608 cachekey += "\n";
609 if (optimizationRecipe != 0)
610 {
611 cachekey += "Optimization recipe ";
612 cachekey += de::toString(optimizationRecipe);
613 cachekey += "\n";
614 }
615
616 cachekey += program.source;
617
618 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
619
620 if (res)
621 {
622 buildInfo->source = program.source;
623 buildInfo->compileOk = true;
624 buildInfo->compileTimeUs = 0;
625 buildInfo->infoLog = "Loaded from cache";
626 }
627 }
628
629 if (!res)
630 {
631
632 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
633 TCU_THROW(InternalError, "Failed to assemble SPIR-V");
634
635 if (optimizationRecipe != 0)
636 {
637 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
638 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
639 }
640
641 if (validateBinary)
642 {
643 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
644 }
645
646 res = createProgramBinaryFromSpirV(binary);
647 if (commandLine.isShadercacheEnabled())
648 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
649 }
650 return res;
651 }
652
disassembleProgram(const ProgramBinary & program,std::ostream * dst)653 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
654 {
655 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
656 {
657 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
658
659 if (isNativeSpirVBinaryEndianness())
660 disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
661 extractSpirvVersion(program));
662 else
663 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
664 }
665 else
666 TCU_THROW(NotSupportedError, "Unsupported program format");
667 }
668
validateProgram(const ProgramBinary & program,std::ostream * dst,const SpirvValidatorOptions & options)669 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
670 {
671 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
672 {
673 if (!isSaneSpirVBinary(program))
674 {
675 *dst << "Binary doesn't look like SPIR-V at all";
676 return false;
677 }
678
679 if (isNativeSpirVBinaryEndianness())
680 return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
681 else
682 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
683 }
684 else
685 TCU_THROW(NotSupportedError, "Unsupported program format");
686 }
687
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)688 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
689 {
690 if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
691 {
692 const struct VkShaderModuleCreateInfo shaderModuleInfo =
693 {
694 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
695 DE_NULL,
696 flags,
697 (deUintptr)binary.getSize(),
698 (const deUint32*)binary.getBinary(),
699 };
700
701 binary.setUsed();
702
703 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
704 }
705 else
706 TCU_THROW(NotSupportedError, "Unsupported program format");
707 }
708
getGluShaderType(VkShaderStageFlagBits shaderStage)709 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
710 {
711 switch (shaderStage)
712 {
713 case VK_SHADER_STAGE_VERTEX_BIT: return glu::SHADERTYPE_VERTEX;
714 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return glu::SHADERTYPE_TESSELLATION_CONTROL;
715 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return glu::SHADERTYPE_TESSELLATION_EVALUATION;
716 case VK_SHADER_STAGE_GEOMETRY_BIT: return glu::SHADERTYPE_GEOMETRY;
717 case VK_SHADER_STAGE_FRAGMENT_BIT: return glu::SHADERTYPE_FRAGMENT;
718 case VK_SHADER_STAGE_COMPUTE_BIT: return glu::SHADERTYPE_COMPUTE;
719 default:
720 DE_FATAL("Unknown shader stage");
721 return glu::SHADERTYPE_LAST;
722 }
723 }
724
getVkShaderStage(glu::ShaderType shaderType)725 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
726 {
727 static const VkShaderStageFlagBits s_shaderStages[] =
728 {
729 VK_SHADER_STAGE_VERTEX_BIT,
730 VK_SHADER_STAGE_FRAGMENT_BIT,
731 VK_SHADER_STAGE_GEOMETRY_BIT,
732 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
733 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
734 VK_SHADER_STAGE_COMPUTE_BIT,
735 VK_SHADER_STAGE_RAYGEN_BIT_NV,
736 VK_SHADER_STAGE_ANY_HIT_BIT_NV,
737 VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
738 VK_SHADER_STAGE_MISS_BIT_NV,
739 VK_SHADER_STAGE_INTERSECTION_BIT_NV,
740 VK_SHADER_STAGE_CALLABLE_BIT_NV,
741 };
742
743 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
744 }
745
746 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)747 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
748 {
749 return vk::SPIRV_VERSION_1_0;
750 }
751
752 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
getMaxSpirvVersionForVulkan(const deUint32 vulkanVersion)753 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
754 {
755 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
756
757 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_API_VERSION_MAJOR(vulkanVersion), VK_API_VERSION_MINOR(vulkanVersion), 0);
758 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
759 result = vk::SPIRV_VERSION_1_0;
760 else if (vulkanVersionMajorMinor == VK_API_VERSION_1_1)
761 result = vk::SPIRV_VERSION_1_3;
762 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_2)
763 result = vk::SPIRV_VERSION_1_5;
764
765 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
766
767 return result;
768 }
769
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)770 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
771 {
772 return getMaxSpirvVersionForVulkan(vulkanVersion);
773 }
774
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)775 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
776 {
777 return getMaxSpirvVersionForVulkan(vulkanVersion);
778 }
779
extractSpirvVersion(const ProgramBinary & binary)780 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
781 {
782 DE_STATIC_ASSERT(SPIRV_VERSION_1_5 + 1 == SPIRV_VERSION_LAST);
783
784 if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
785 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
786
787 if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
788 TCU_THROW(InternalError, "Invalid SPIR-V header format");
789
790 const deUint32 spirvBinaryVersion10 = 0x00010000;
791 const deUint32 spirvBinaryVersion11 = 0x00010100;
792 const deUint32 spirvBinaryVersion12 = 0x00010200;
793 const deUint32 spirvBinaryVersion13 = 0x00010300;
794 const deUint32 spirvBinaryVersion14 = 0x00010400;
795 const deUint32 spirvBinaryVersion15 = 0x00010500;
796 const SpirvBinaryHeader* header = reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
797 const deUint32 spirvVersion = isNativeSpirVBinaryEndianness()
798 ? header->version
799 : deReverseBytes32(header->version);
800 SpirvVersion result = SPIRV_VERSION_LAST;
801
802 switch (spirvVersion)
803 {
804 case spirvBinaryVersion10: result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
805 case spirvBinaryVersion11: result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
806 case spirvBinaryVersion12: result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
807 case spirvBinaryVersion13: result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
808 case spirvBinaryVersion14: result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
809 case spirvBinaryVersion15: result = SPIRV_VERSION_1_5; break; //!< SPIR-V 1.5
810 default: TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
811 }
812
813 return result;
814 }
815
getSpirvVersionName(const SpirvVersion spirvVersion)816 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
817 {
818 DE_STATIC_ASSERT(SPIRV_VERSION_1_5 + 1 == SPIRV_VERSION_LAST);
819 DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
820
821 std::string result;
822
823 switch (spirvVersion)
824 {
825 case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
826 case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
827 case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
828 case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
829 case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
830 case SPIRV_VERSION_1_5: result = "1.5"; break; //!< SPIR-V 1.5
831 default: result = "Unknown";
832 }
833
834 return result;
835 }
836
operator ++(SpirvVersion & spirvVersion)837 SpirvVersion& operator++(SpirvVersion& spirvVersion)
838 {
839 if (spirvVersion == SPIRV_VERSION_LAST)
840 spirvVersion = SPIRV_VERSION_1_0;
841 else
842 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
843
844 return spirvVersion;
845 }
846
847 } // vk
848