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