// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #if COMPILE_WITH_VK_SHADER_COMPILER #include "ShaderCompilerVulkan.h" #include "Engine/Core/Log.h" #include "Engine/Platform/Platform.h" #include "Engine/Threading/Threading.h" #include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Graphics/Config.h" #include "Engine/GraphicsDevice/Vulkan/Types.h" // Use glslang for HLSL to SPIR-V translation // Source: https://github.com/KhronosGroup/glslang // License: modified BSD #define NV_EXTENSIONS 1 #define AMD_EXTENSIONS 1 #define ENABLE_HLSL 1 #define ENABLE_OPT 1 #include #include #include #include #define PRINT_UNIFORMS 0 #define PRINT_DESCRIPTORS 0 namespace { CriticalSection CompileShaderVulkanLocker; int32 CompileShaderVulkanInstances = 0; } class Includer : public glslang::TShader::Includer { private: ShaderCompilationContext* _context; IncludeResult* include(const char* headerName, const char* includerName, int depth) const { const char* source; int32 sourceLength; if (ShaderCompiler::GetIncludedFileSource(_context, includerName, headerName, source, sourceLength)) return nullptr; return New(headerName, source, sourceLength, nullptr); } public: Includer(ShaderCompilationContext* context) { _context = context; } public: // [glslang::TShader::Include] IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override { return include(headerName, includerName, (int)inclusionDepth); } IncludeResult* includeSystem(const char* headerName, const char* includerName, size_t inclusionDepth) override { return include(headerName, includerName, (int)inclusionDepth); } void releaseInclude(IncludeResult* result) override { if (result) Delete(result); } }; ShaderCompilerVulkan::ShaderCompilerVulkan(ShaderProfile profile) : ShaderCompiler(profile) { ScopeLock lock(CompileShaderVulkanLocker); if (CompileShaderVulkanInstances == 0) { glslang::InitializeProcess(); const auto ver = glslang::GetVersion(); LOG(Info, "Using glslang {0}.{1}.{2} compiler (SPIR-V version: {3})", ver.major, ver.minor, ver.patch, String(spvSoftwareVersionString())); } CompileShaderVulkanInstances++; } ShaderCompilerVulkan::~ShaderCompilerVulkan() { ScopeLock lock(CompileShaderVulkanLocker); CompileShaderVulkanInstances--; if (CompileShaderVulkanInstances == 0) { glslang::FinalizeProcess(); } } // @formatter:off const TBuiltInResource DefaultTBuiltInResource = { /* .MaxLights = */ 32, /* .MaxClipPlanes = */ 6, /* .MaxTextureUnits = */ 32, /* .MaxTextureCoords = */ 32, /* .MaxVertexAttribs = */ 64, /* .MaxVertexUniformComponents = */ 4096, /* .MaxVaryingFloats = */ 64, /* .MaxVertexTextureImageUnits = */ 32, /* .MaxCombinedTextureImageUnits = */ 80, /* .MaxTextureImageUnits = */ 32, /* .MaxFragmentUniformComponents = */ 4096, /* .MaxDrawBuffers = */ 32, /* .MaxVertexUniformVectors = */ 128, /* .MaxVaryingVectors = */ 8, /* .MaxFragmentUniformVectors = */ 16, /* .MaxVertexOutputVectors = */ 16, /* .MaxFragmentInputVectors = */ 15, /* .MinProgramTexelOffset = */ -8, /* .MaxProgramTexelOffset = */ 7, /* .MaxClipDistances = */ 8, /* .MaxComputeWorkGroupCountX = */ 65535, /* .MaxComputeWorkGroupCountY = */ 65535, /* .MaxComputeWorkGroupCountZ = */ 65535, /* .MaxComputeWorkGroupSizeX = */ 1024, /* .MaxComputeWorkGroupSizeY = */ 1024, /* .MaxComputeWorkGroupSizeZ = */ 64, /* .MaxComputeUniformComponents = */ 1024, /* .MaxComputeTextureImageUnits = */ 16, /* .MaxComputeImageUniforms = */ 8, /* .MaxComputeAtomicCounters = */ 8, /* .MaxComputeAtomicCounterBuffers = */ 1, /* .MaxVaryingComponents = */ 60, /* .MaxVertexOutputComponents = */ 64, /* .MaxGeometryInputComponents = */ 64, /* .MaxGeometryOutputComponents = */ 128, /* .MaxFragmentInputComponents = */ 128, /* .MaxImageUnits = */ 8, /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, /* .MaxCombinedShaderOutputResources = */ 8, /* .MaxImageSamples = */ 0, /* .MaxVertexImageUniforms = */ 0, /* .MaxTessControlImageUniforms = */ 0, /* .MaxTessEvaluationImageUniforms = */ 0, /* .MaxGeometryImageUniforms = */ 0, /* .MaxFragmentImageUniforms = */ 8, /* .MaxCombinedImageUniforms = */ 8, /* .MaxGeometryTextureImageUnits = */ 16, /* .MaxGeometryOutputVertices = */ 256, /* .MaxGeometryTotalOutputComponents = */ 1024, /* .MaxGeometryUniformComponents = */ 1024, /* .MaxGeometryVaryingComponents = */ 64, /* .MaxTessControlInputComponents = */ 128, /* .MaxTessControlOutputComponents = */ 128, /* .MaxTessControlTextureImageUnits = */ 16, /* .MaxTessControlUniformComponents = */ 1024, /* .MaxTessControlTotalOutputComponents = */ 4096, /* .MaxTessEvaluationInputComponents = */ 128, /* .MaxTessEvaluationOutputComponents = */ 128, /* .MaxTessEvaluationTextureImageUnits = */ 16, /* .MaxTessEvaluationUniformComponents = */ 1024, /* .MaxTessPatchComponents = */ 120, /* .MaxPatchVertices = */ 32, /* .MaxTessGenLevel = */ 64, /* .MaxViewports = */ 16, /* .MaxVertexAtomicCounters = */ 0, /* .MaxTessControlAtomicCounters = */ 0, /* .MaxTessEvaluationAtomicCounters = */ 0, /* .MaxGeometryAtomicCounters = */ 0, /* .MaxFragmentAtomicCounters = */ 8, /* .MaxCombinedAtomicCounters = */ 8, /* .MaxAtomicCounterBindings = */ 1, /* .MaxVertexAtomicCounterBuffers = */ 0, /* .MaxTessControlAtomicCounterBuffers = */ 0, /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, /* .MaxGeometryAtomicCounterBuffers = */ 0, /* .MaxFragmentAtomicCounterBuffers = */ 1, /* .MaxCombinedAtomicCounterBuffers = */ 1, /* .MaxAtomicCounterBufferSize = */ 16384, /* .MaxTransformFeedbackBuffers = */ 4, /* .MaxTransformFeedbackInterleavedComponents = */ 64, /* .MaxCullDistances = */ 8, /* .MaxCombinedClipAndCullDistances = */ 8, /* .MaxSamples = */ 4, /* .maxMeshOutputVerticesNV = */ 256, /* .maxMeshOutputPrimitivesNV = */ 512, /* .maxMeshWorkGroupSizeX_NV = */ 32, /* .maxMeshWorkGroupSizeY_NV = */ 1, /* .maxMeshWorkGroupSizeZ_NV = */ 1, /* .maxTaskWorkGroupSizeX_NV = */ 32, /* .maxTaskWorkGroupSizeY_NV = */ 1, /* .maxTaskWorkGroupSizeZ_NV = */ 1, /* .maxMeshViewCountNV = */ 4, /* .maxDualSourceDrawBuffersEXT = */ 1, /* .limits = */ { /* .nonInductiveForLoops = */ 1, /* .whileLoops = */ 1, /* .doWhileLoops = */ 1, /* .generalUniformIndexing = */ 1, /* .generalAttributeMatrixVectorIndexing = */ 1, /* .generalVaryingIndexing = */ 1, /* .generalSamplerIndexing = */ 1, /* .generalVariableIndexing = */ 1, /* .generalConstantMatrixVectorIndexing = */ 1, } }; // @formatter:on struct Descriptor { int32 Slot; int32 Binding; int32 Size; int32 Count; SpirvShaderResourceBindingType BindingType; VkDescriptorType DescriptorType; SpirvShaderResourceType ResourceType; std::string Name; }; SpirvShaderResourceType GetTextureType(const glslang::TSampler& sampler) { switch (sampler.dim) { case glslang::Esd1D: return sampler.isArrayed() ? SpirvShaderResourceType::Texture1DArray : SpirvShaderResourceType::Texture1D; case glslang::Esd2D: return sampler.isArrayed() ? SpirvShaderResourceType::Texture2DArray : SpirvShaderResourceType::Texture2D; case glslang::Esd3D: return SpirvShaderResourceType::Texture3D; case glslang::EsdCube: return SpirvShaderResourceType::TextureCube; default: CRASH; return SpirvShaderResourceType::Unknown; } } bool IsUavType(const glslang::TType& type) { if (type.getQualifier().isReadOnly()) return false; return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || (type.getQualifier().storage == glslang::EvqBuffer); } class DescriptorsCollector { public: int32 Images = 0; int32 Buffers = 0; int32 TexelBuffers = 0; int32 DescriptorsCount = 0; Descriptor Descriptors[SpirvShaderDescriptorInfo::MaxDescriptors]; public: Descriptor* Add(glslang::TVarEntryInfo& ent) { const glslang::TType& type = ent.symbol->getType(); const char* name = ent.symbol->getName().c_str(); auto& qualifier = type.getQualifier(); if (DescriptorsCount == SpirvShaderDescriptorInfo::MaxDescriptors) { // Prevent too many descriptors LOG(Warning, "Too many descriptors in use."); return nullptr; } // Guess the descriptor type based on reflection information VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; SpirvShaderResourceType resourceType = SpirvShaderResourceType::Unknown; SpirvShaderResourceBindingType resourceBindingType = SpirvShaderResourceBindingType::INVALID; if (type.getBasicType() == glslang::EbtSampler) { if (!qualifier.hasBinding()) { // Each resource must have binding specified (from HLSL shaders that do it explicitly) LOG(Warning, "Found an uniform \'{0}\' without a binding qualifier. Each uniform must have an explicitly defined binding number.", String(name)); return nullptr; } if (type.getSampler().isCombined()) { // Texture + Sampler combined is not supported LOG(Warning, "Combined sampler \'{0}\' from glsl language is not supported.", String(name)); return nullptr; } if (type.getSampler().isPureSampler()) { // Sampler descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; resourceType = SpirvShaderResourceType::Sampler; resourceBindingType = SpirvShaderResourceBindingType::SAMPLER; } else if (type.getSampler().dim == glslang::EsdBuffer) { if (IsUavType(type)) { // Buffer UAV descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; resourceType = SpirvShaderResourceType::Buffer; resourceBindingType = SpirvShaderResourceBindingType::UAV; } else { // Buffer SRV descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; resourceType = SpirvShaderResourceType::Buffer; resourceBindingType = SpirvShaderResourceBindingType::SRV; } } else if (type.isTexture()) { // Texture SRV descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; resourceType = GetTextureType(type.getSampler()); resourceBindingType = SpirvShaderResourceBindingType::SRV; } else if (type.isImage()) { if (type.getSampler().dim == glslang::EsdBuffer) { // Buffer UAV descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; resourceType = SpirvShaderResourceType::Buffer; } else { // Texture UAV descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; resourceType = GetTextureType(type.getSampler()); } resourceBindingType = SpirvShaderResourceBindingType::UAV; } } else if (qualifier.storage == glslang::EvqUniform) { if (type.getBasicType() != glslang::EbtBlock) { // Skip uniforms that are not contained inside structures LOG(Warning, "Invalid uniform \'{1} {0}\'. Shader uniforms that are not constant buffer blocks are not supported.", String(name), String(type.getBasicTypeString().c_str())); return nullptr; } // CB descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; resourceType = SpirvShaderResourceType::ConstantBuffer; name = type.getTypeName().c_str(); resourceBindingType = SpirvShaderResourceBindingType::CB; } else if (qualifier.storage == glslang::EvqBuffer) { if (qualifier.isReadOnly()) { // Buffer SRV descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; resourceType = SpirvShaderResourceType::Buffer; resourceBindingType = SpirvShaderResourceBindingType::SRV; } else { // Buffer UAV descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; resourceType = SpirvShaderResourceType::Buffer; resourceBindingType = SpirvShaderResourceBindingType::UAV; } } const auto index = DescriptorsCount++; auto& descriptor = Descriptors[index]; descriptor.Binding = index; descriptor.Slot = qualifier.layoutBinding; descriptor.Size = -1; descriptor.BindingType = resourceBindingType; descriptor.DescriptorType = descriptorType; descriptor.ResourceType = resourceType; descriptor.Name = name; descriptor.Count = type.isSizedArray() ? type.getCumulativeArraySize() : 1; // Get the output info about shader uniforms usage switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: Images += descriptor.Count; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: Buffers += descriptor.Count; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: TexelBuffers += descriptor.Count; break; default: LOG(Warning, "Invalid descriptor type {0} for symbol {1}.", (int32)descriptorType, String(name)); return nullptr; } return &descriptor; } }; class MyIoMapResolver : public glslang::TDefaultIoResolverBase { private: int32 _set; DescriptorsCollector* _collector; public: MyIoMapResolver(int32 set, DescriptorsCollector* collector, const glslang::TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) , _set(set) , _collector(collector) { } public: // [glslang::TDefaultIoResolverBase] bool validateBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override { return true; } glslang::TResourceType getResourceType(const glslang::TType& type) override { if (isUavType(type)) return glslang::EResUav; if (isSrvType(type)) return glslang::EResTexture; if (isSamplerType(type)) return glslang::EResSampler; if (isUboType(type)) return glslang::EResUbo; return glslang::EResCount; } int resolveBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override { // Skip unused things if (!ent.live) return -1; // Add resource const auto descriptor = _collector->Add(ent); if (descriptor) { return ent.newBinding = reserveSlot(_set, descriptor->Binding, descriptor->Count); } return ent.newBinding; } int resolveSet(EShLanguage stage, glslang::TVarEntryInfo& ent) override { // Skip unused things if (!ent.live) return -1; // Use different slot per-stage return ent.newSet = _set; } }; bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite) { // TODO: test without locking ScopeLock lock(CompileShaderVulkanLocker); Includer includer(_context); // Prepare if (WriteShaderFunctionBegin(_context, meta)) return true; auto options = _context->Options; auto type = meta.GetStage(); // Prepare EShLanguage lang = EShLanguage::EShLangCount; switch (type) { case ShaderStage::Vertex: lang = EShLanguage::EShLangVertex; break; case ShaderStage::Hull: lang = EShLanguage::EShLangTessControl; break; case ShaderStage::Domain: lang = EShLanguage::EShLangTessEvaluation; break; case ShaderStage::Geometry: lang = EShLanguage::EShLangGeometry; break; case ShaderStage::Pixel: lang = EShLanguage::EShLangFragment; break; case ShaderStage::Compute: lang = EShLanguage::EShLangCompute; break; default: LOG(Error, "Unknown shader type."); return true; } EShMessages messages = (EShMessages)(EShMsgReadHlsl | EShMsgSpvRules | EShMsgVulkanRules); // Compile all shader function permutations for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++) { #if PRINT_DESCRIPTORS LOG(Warning, "VULKAN SHADER {0}: {1}[{2}]", _context->Options->TargetName, String(meta.Name), permutationIndex); #endif _macros.Clear(); // Get function permutation macros meta.GetDefinitionsForPermutation(permutationIndex, _macros); // Add additional define for compiled function name GetDefineForFunction(meta, _macros); // Add custom and global macros (global last because contain null define to indicate ending) _macros.Add(_context->Options->Macros); _macros.Add(_globalMacros); // Offset inputs for some pipeline stages to match the descriptors sets layout int32 stageSet; switch (type) { case ShaderStage::Vertex: case ShaderStage::Compute: stageSet = 0; break; case ShaderStage::Pixel: stageSet = 1; break; case ShaderStage::Geometry: stageSet = 2; break; case ShaderStage::Hull: stageSet = 3; break; case ShaderStage::Domain: stageSet = 4; break; default: LOG(Error, "Unknown shader type."); return true; } // Parse HLSL shader using glslang glslang::TShader shader(lang); glslang::TProgram program; shader.setEntryPoint(meta.Name.Get()); shader.setSourceEntryPoint(meta.Name.Get()); int lengths = options->SourceLength - 1; const char* names = _context->TargetNameAnsi; shader.setStringsWithLengthsAndNames(&options->Source, &lengths, &names, 1); const int defaultVersion = 450; std::string preamble; for (int32 i = 0; i < _macros.Count() - 1; i++) { auto& macro = _macros[i]; preamble.append("#define "); preamble.append(macro.Name); if (macro.Definition) { preamble.append(" "); preamble.append(macro.Definition); } preamble.append("\n"); } shader.setPreamble(preamble.c_str()); shader.setInvertY(true); //shader.setAutoMapLocations(true); //shader.setAutoMapBindings(true); //shader.setShiftBinding(glslang::TResourceType::EResUav, 500); shader.setHlslIoMapping(true); shader.setEnvInput(glslang::EShSourceHlsl, lang, glslang::EShClientVulkan, defaultVersion); shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0); if (!shader.parse(&DefaultTBuiltInResource, defaultVersion, false, messages, includer)) { const auto msg = shader.getInfoLog(); _context->OnError(msg); return true; } program.addShader(&shader); // Generate reflection information if (!program.link(messages)) { const auto msg = program.getInfoLog(); _context->OnError(msg); return true; } if (!program.getIntermediate(lang)) { const auto msg = program.getInfoLog(); _context->OnError(msg); return true; } DescriptorsCollector descriptorsCollector; MyIoMapResolver resolver(stageSet, &descriptorsCollector, *program.getIntermediate(lang)); if (!program.mapIO(&resolver)) { const auto msg = program.getInfoLog(); _context->OnError(msg); return true; } if (!program.buildReflection()) { const auto msg = program.getInfoLog(); _context->OnError(msg); return true; } // Process shader reflection data SpirvShaderHeader header; Platform::MemoryClear(&header, sizeof(header)); ShaderBindings bindings = { 0, 0, 0, 0 }; { // Extract constant buffers usage information for (int blockIndex = 0; blockIndex < program.getNumLiveUniformBlocks(); blockIndex++) { auto size = program.getUniformBlockSize(blockIndex); auto uniform = program.getUniformBlockTType(blockIndex); auto& qualifier = uniform->getQualifier(); auto binding = (int32)qualifier.layoutBinding; if (!qualifier.hasBinding()) { // Each uniform must have a valid binding //LOG(Warning, "Found a uniform block \'{0}\' without a binding qualifier. Each uniform block must have an explicitly defined binding number.", String(uniform->getTypeName().c_str())); continue; } // Shared storage buffer if (qualifier.storage == glslang::EvqBuffer) { // RWBuffer } else { // Uniform buffer bool found = false; for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++) { auto& descriptor = descriptorsCollector.Descriptors[i]; if (descriptor.BindingType == SpirvShaderResourceBindingType::CB && descriptor.Binding == binding) { found = true; descriptor.Size = size; break; } } if (!found) { LOG(Warning, "Failed to find descriptor for the uniform block \'{0}\' of size {1} (bytes), binding: {2}.", String(uniform->getTypeName().c_str()), size, binding); } } } #if PRINT_UNIFORMS // Debug printing all uniforms for (int32 index = 0; index < program.getNumLiveUniformVariables(); index++) { auto uniform = program.getUniformTType(index); auto qualifier = uniform->getQualifier(); if (!uniform->isArray()) LOG(Warning, "Shader {0}:{1} - uniform: {2} {3} at binding {4}", _context->TargetNameAnsi, String(meta.Name), uniform->getCompleteString().c_str(), program.getUniformName(index), qualifier.layoutBinding ); } #endif // Process all descriptors header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images; header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers; header.DescriptorInfo.TexelBufferViewsCount = descriptorsCollector.TexelBuffers; for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++) { auto& descriptor = descriptorsCollector.Descriptors[i]; // Skip cases (eg. AppendStructuredBuffer counter buffer) if (descriptor.Slot == MAX_uint16) continue; auto& d = header.DescriptorInfo.DescriptorTypes[header.DescriptorInfo.DescriptorTypesCount++]; d.Binding = descriptor.Binding; d.Set = stageSet; d.Slot = descriptor.Slot; d.BindingType = descriptor.BindingType; d.DescriptorType = descriptor.DescriptorType; d.ResourceType = descriptor.ResourceType; d.Count = descriptor.Count; switch (descriptor.BindingType) { case SpirvShaderResourceBindingType::CB: ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_CB_BINDED); bindings.UsedCBsMask |= 1 << descriptor.Slot; break; case SpirvShaderResourceBindingType::SRV: ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_SR_BINDED); bindings.UsedSRsMask |= 1 << descriptor.Slot; break; case SpirvShaderResourceBindingType::UAV: ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_UA_BINDED); bindings.UsedUAsMask |= 1 << descriptor.Slot; break; } if (descriptor.BindingType == SpirvShaderResourceBindingType::CB) { if (descriptor.Size == -1) { // Skip unused constant buffers continue; } if (descriptor.Size == 0) { LOG(Warning, "Found constant buffer \'{1}\' at slot {0} but it's not used or has no valid size.", descriptor.Slot, String(descriptor.Name.c_str())); continue; } for (int32 b = 0; b < _constantBuffers.Count(); b++) { auto& cc = _constantBuffers[b]; if (cc.Slot == descriptor.Slot) { // Mark as used and cache some data cc.IsUsed = true; cc.Size = descriptor.Size; break; } } } #if PRINT_DESCRIPTORS String type; switch (descriptor.BindingType) { case SpirvShaderResourceBindingType::INVALID: type = TEXT("INVALID"); break; case SpirvShaderResourceBindingType::CB: type = TEXT("CB"); break; case SpirvShaderResourceBindingType::SAMPLER: type = TEXT("SAMPLER"); break; case SpirvShaderResourceBindingType::SRV: type = TEXT("SRV"); break; case SpirvShaderResourceBindingType::UAV: type = TEXT("UAV"); break; default: type = TEXT("?"); } LOG(Warning, "VULKAN SHADER RESOURCE: slot: {1}, binding: {2}, name: {0}, type: {3}", String(descriptor.Name.c_str()), descriptor.Slot, descriptor.Binding, type); #endif } } // Generate SPIR-V (optimize it at the same time) std::vector spirv; spv::SpvBuildLogger logger; glslang::SpvOptions spvOptions; spvOptions.generateDebugInfo = false; spvOptions.disassemble = false; spvOptions.disableOptimizer = options->NoOptimize; spvOptions.optimizeSize = !options->NoOptimize; spvOptions.stripDebugInfo = !options->GenerateDebugData; #if BUILD_DEBUG spvOptions.validate = true; #else spvOptions.validate = false; #endif glslang::GlslangToSpv(*program.getIntermediate(lang), spirv, &logger, &spvOptions); const std::string spirvLogOutput = logger.getAllMessages(); if (!spirvLogOutput.empty()) { LOG(Warning, "SPIR-V generator log:\n{0}", String(spirvLogOutput.c_str())); } if (spirv.empty()) { LOG(Warning, "SPIR-V generator failed"); return true; } #if 0 // Dump SPIR-V as text for debugging { spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0); std::string spirvText; tools.Disassemble(spirv, &spirvText); _context->OnCollectDebugInfo(meta, permutationIndex, spirvText.c_str(), (int32)spirvText.size()); } #endif int32 spirvBytesCount = (int32)spirv.size() * sizeof(unsigned); header.Type = SpirvShaderHeader::Types::Raw; if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), &spirv[0], spirvBytesCount)) return true; if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros)) return true; } return WriteShaderFunctionEnd(_context, meta); } bool ShaderCompilerVulkan::OnCompileBegin() { if (ShaderCompiler::OnCompileBegin()) return true; //_globalMacros.Add({ "VULKAN", "1" }); // glslang compiler adds VULKAN define if EShMsgVulkanRules flag is specified // TODO: handle options->TreatWarningsAsErrors return false; } #endif