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 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 60 bool isNativeSpirVBinaryEndianness (void) 61 { 62 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS) 63 return true; 64 #else 65 return false; 66 #endif 67 } 68 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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