// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VulkanShaders.cpp: Vulkan shader RHI implementation. =============================================================================*/ #include "VulkanRHIPrivate.h" #include "VulkanPendingState.h" #include "VulkanContext.h" #include "GlobalShader.h" #include "Serialization/MemoryReader.h" #include "VulkanLLM.h" #include "VulkanDescriptorSets.h" #include "RHICoreShader.h" TAutoConsoleVariable GDynamicGlobalUBs( TEXT("r.Vulkan.DynamicGlobalUBs"), 2, TEXT("2 to treat ALL uniform buffers as dynamic [default]\n")\ TEXT("1 to treat global/packed uniform buffers as dynamic\n")\ TEXT("0 to treat them as regular"), ECVF_ReadOnly | ECVF_RenderThreadSafe ); static int32 GVulkanCompressSPIRV = 0; static FAutoConsoleVariableRef GVulkanCompressSPIRVCVar( TEXT("r.Vulkan.CompressSPIRV"), GVulkanCompressSPIRV, TEXT("0 SPIRV source is stored in RAM as-is. (default)\n") TEXT("1 SPIRV source is compressed on load and decompressed as when needed, this saves RAM but can introduce hitching when creating shaders."), ECVF_ReadOnly | ECVF_RenderThreadSafe ); FCriticalSection FVulkanShader::VulkanShaderModulesMapCS; FVulkanShaderFactory::~FVulkanShaderFactory() { for (auto& Map : ShaderMap) { Map.Empty(); } } template static void ReadShaderOptionalData(FShaderCodeReader& ShaderCode, ShaderType* RHIShader) { const FShaderCodePackedResourceCounts* PackedResourceCounts = ShaderCode.FindOptionalData(); if (PackedResourceCounts) { if (RHIShader->GetFrequency() == SF_Compute) { RHIShader->SetNoDerivativeOps(EnumHasAnyFlags(PackedResourceCounts->UsageFlags, EShaderResourceUsageFlags::NoDerivativeOps)); } RHIShader->SetShaderBundleUsage(EnumHasAnyFlags(PackedResourceCounts->UsageFlags, EShaderResourceUsageFlags::ShaderBundle)); RHIShader->SetUsesBindless(EnumHasAnyFlags(PackedResourceCounts->UsageFlags, EShaderResourceUsageFlags::BindlessSamplers | EShaderResourceUsageFlags::BindlessResources)); } #if RHI_INCLUDE_SHADER_DEBUG_DATA RHIShader->Debug.ShaderName = ShaderCode.FindOptionalData(FShaderCodeName::Key); UE::RHICore::SetupShaderCodeValidationData(RHIShader, ShaderCode); #endif } template ShaderType* FVulkanShaderFactory::CreateShader(TArrayView Code, FVulkanDevice* Device) { static_assert(ShaderType::StaticFrequency != SF_RayCallable && ShaderType::StaticFrequency != SF_RayGen && ShaderType::StaticFrequency != SF_RayHitGroup && ShaderType::StaticFrequency != SF_RayMiss); const uint32 ShaderCodeLen = Code.Num(); const uint32 ShaderCodeCRC = FCrc::MemCrc32(Code.GetData(), Code.Num()); const uint64 ShaderKey = ((uint64)ShaderCodeLen | ((uint64)ShaderCodeCRC << 32)); ShaderType* RetShader = LookupShader(ShaderKey); if (RetShader == nullptr) { // Do serialize outside of lock FMemoryReaderView Ar(Code, true); FVulkanShaderHeader CodeHeader; Ar << CodeHeader; FShaderResourceTable SerializedSRT; Ar << SerializedSRT; FVulkanShader::FSpirvContainer SpirvContainer; Ar << SpirvContainer; { FRWScopeLock ScopedLock(RWLock[ShaderType::StaticFrequency], SLT_Write); FVulkanShader* const* FoundShaderPtr = ShaderMap[ShaderType::StaticFrequency].Find(ShaderKey); if (FoundShaderPtr) { RetShader = static_cast(*FoundShaderPtr); } else { RetShader = new ShaderType(Device, MoveTemp(SerializedSRT), MoveTemp(CodeHeader), MoveTemp(SpirvContainer), ShaderKey); ShaderMap[ShaderType::StaticFrequency].Add(ShaderKey, RetShader); FShaderCodeReader ShaderCode(Code); ReadShaderOptionalData(ShaderCode, RetShader); } } } return RetShader; } template FVulkanRayTracingShader* FVulkanShaderFactory::CreateRayTracingShader(TArrayView Code, FVulkanDevice* Device) { static_assert(ShaderFrequency == SF_RayCallable || ShaderFrequency == SF_RayGen || ShaderFrequency == SF_RayHitGroup || ShaderFrequency == SF_RayMiss); auto LookupRayTracingShader = [this](uint64 ShaderKey) { FVulkanRayTracingShader* RTShader = nullptr; if (ShaderKey) { FRWScopeLock ScopedLock(RWLock[ShaderFrequency], SLT_ReadOnly); FVulkanShader* const* FoundShaderPtr = ShaderMap[ShaderFrequency].Find(ShaderKey); if (FoundShaderPtr) { RTShader = static_cast(*FoundShaderPtr); } } return RTShader; }; const uint32 ShaderCodeLen = Code.Num(); const uint32 ShaderCodeCRC = FCrc::MemCrc32(Code.GetData(), Code.Num()); const uint64 ShaderKey = ((uint64)ShaderCodeLen | ((uint64)ShaderCodeCRC << 32)); FVulkanRayTracingShader* RetShader = LookupRayTracingShader(ShaderKey); if (RetShader == nullptr) { // Do serialize outside of lock FMemoryReaderView Ar(Code, true); FVulkanShaderHeader CodeHeader; Ar << CodeHeader; FShaderResourceTable SerializedSRT; Ar << SerializedSRT; FVulkanShader::FSpirvContainer SpirvContainer; Ar << SpirvContainer; const bool bIsHitGroup = (ShaderFrequency == SF_RayHitGroup); FVulkanShader::FSpirvContainer AnyHitSpirvContainer; FVulkanShader::FSpirvContainer IntersectionSpirvContainer; if (bIsHitGroup) { if (CodeHeader.RayGroupAnyHit == FVulkanShaderHeader::ERayHitGroupEntrypoint::SeparateBlob) { Ar << AnyHitSpirvContainer; } if (CodeHeader.RayGroupIntersection == FVulkanShaderHeader::ERayHitGroupEntrypoint::SeparateBlob) { Ar << IntersectionSpirvContainer; } } { FRWScopeLock ScopedLock(RWLock[ShaderFrequency], SLT_Write); FVulkanShader* const* FoundShaderPtr = ShaderMap[ShaderFrequency].Find(ShaderKey); if (FoundShaderPtr) { RetShader = static_cast(*FoundShaderPtr); } else { RetShader = new FVulkanRayTracingShader(Device, ShaderFrequency, MoveTemp(SerializedSRT), MoveTemp(CodeHeader), MoveTemp(SpirvContainer), ShaderKey); if (bIsHitGroup) { RetShader->AnyHitSpirvContainer = MoveTemp(AnyHitSpirvContainer); RetShader->IntersectionSpirvContainer = MoveTemp(IntersectionSpirvContainer); } RetShader->RayTracingPayloadType = RetShader->CodeHeader.RayTracingPayloadType; RetShader->RayTracingPayloadSize = RetShader->CodeHeader.RayTracingPayloadSize; ShaderMap[ShaderFrequency].Add(ShaderKey, RetShader); FShaderCodeReader ShaderCode(Code); ReadShaderOptionalData(ShaderCode, RetShader); } } } return RetShader; } void FVulkanShaderFactory::LookupGfxShaders(const uint64 InShaderKeys[ShaderStage::NumGraphicsStages], FVulkanShader* OutShaders[ShaderStage::NumGraphicsStages]) const { for (int32 Idx = 0; Idx < ShaderStage::NumGraphicsStages; ++Idx) { uint64 ShaderKey = InShaderKeys[Idx]; if (ShaderKey) { EShaderFrequency ShaderFrequency = ShaderStage::GetFrequencyForGfxStage((ShaderStage::EStage)Idx); FRWScopeLock ScopedLock(RWLock[ShaderFrequency], SLT_ReadOnly); FVulkanShader* const * FoundShaderPtr = ShaderMap[ShaderFrequency].Find(ShaderKey); if (FoundShaderPtr) { OutShaders[Idx] = *FoundShaderPtr; } } } } void FVulkanShaderFactory::OnDeleteShader(const FVulkanShader& Shader) { const uint64 ShaderKey = Shader.GetShaderKey(); FRWScopeLock ScopedLock(RWLock[Shader.Frequency], SLT_Write); ShaderMap[Shader.Frequency].Remove(ShaderKey); } FArchive& operator<<(FArchive& Ar, FVulkanShader::FSpirvContainer& SpirvContainer) { uint32 SpirvCodeSizeInBytes; Ar << SpirvCodeSizeInBytes; check(SpirvCodeSizeInBytes); check(Ar.IsLoading()); TArray& SpirvCode = SpirvContainer.SpirvCode; if (!GVulkanCompressSPIRV) { SpirvCode.Reserve(SpirvCodeSizeInBytes); SpirvCode.SetNumUninitialized(SpirvCodeSizeInBytes); Ar.Serialize(SpirvCode.GetData(), SpirvCodeSizeInBytes); } else { const int32 CompressedUpperBound = FCompression::CompressMemoryBound(NAME_Oodle, SpirvCodeSizeInBytes); SpirvCode.Reserve(CompressedUpperBound); SpirvCode.SetNumUninitialized(CompressedUpperBound); TArray UncompressedSpirv; UncompressedSpirv.SetNumUninitialized(SpirvCodeSizeInBytes); Ar.Serialize(UncompressedSpirv.GetData(), SpirvCodeSizeInBytes); int32 CompressedSizeBytes = CompressedUpperBound; if (FCompression::CompressMemory(NAME_Oodle, SpirvCode.GetData(), CompressedSizeBytes, UncompressedSpirv.GetData(), UncompressedSpirv.GetTypeSize() * UncompressedSpirv.Num(), ECompressionFlags::COMPRESS_BiasSpeed)) { SpirvContainer.UncompressedSizeBytes = SpirvCodeSizeInBytes; SpirvCode.SetNumUninitialized(CompressedSizeBytes); } else { SpirvCode = MoveTemp(UncompressedSpirv); } } return Ar; } FVulkanDevice* FVulkanShaderModule::Device = nullptr; FVulkanShaderModule::~FVulkanShaderModule() { Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::ShaderModule, ActualShaderModule); } FVulkanShader::FSpirvCode FVulkanShader::GetSpirvCode(const FSpirvContainer& Container) { if (Container.IsCompressed()) { TArray UncompressedSpirv; const size_t ElementSize = UncompressedSpirv.GetTypeSize(); UncompressedSpirv.Reserve(Container.GetSizeBytes() / ElementSize); UncompressedSpirv.SetNumUninitialized(Container.GetSizeBytes() / ElementSize); FCompression::UncompressMemory(NAME_Oodle, UncompressedSpirv.GetData(), Container.GetSizeBytes(), Container.SpirvCode.GetData(), Container.SpirvCode.Num()); return FSpirvCode(MoveTemp(UncompressedSpirv)); } else { return FSpirvCode(TArrayView((uint32*)Container.SpirvCode.GetData(), Container.SpirvCode.Num() / sizeof(uint32))); } } FVulkanShader::FVulkanShader(FVulkanDevice* InDevice, EShaderFrequency InFrequency, FVulkanShaderHeader&& InCodeHeader, FSpirvContainer&& InSpirvContainer, uint64 InShaderKey, TArray& InStaticSlots) : StaticSlots(InStaticSlots) , ShaderKey(InShaderKey) , CodeHeader(MoveTemp(InCodeHeader)) , Frequency(InFrequency) , SpirvContainer(MoveTemp(InSpirvContainer)) , Device(InDevice) { LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanShaders); check(Device); checkf(SpirvContainer.GetSizeBytes() != 0, TEXT("Empty SPIR-V! %s"), *CodeHeader.DebugName); const int32 NumGlobalPackedBuffer = (CodeHeader.PackedGlobalsSize > 0) ? 1 : 0; if (CodeHeader.UniformBufferInfos.Num() > NumGlobalPackedBuffer) { StaticSlots.Reserve(CodeHeader.UniformBufferInfos.Num()); for (const FVulkanShaderHeader::FUniformBufferInfo& UBInfo : CodeHeader.UniformBufferInfos) { if (const FShaderParametersMetadata* Metadata = FindUniformBufferStructByLayoutHash(UBInfo.LayoutHash)) { StaticSlots.Add(Metadata->GetLayout().StaticSlot); } else { StaticSlots.Add(MAX_UNIFORM_BUFFER_STATIC_SLOTS); } } } #if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT // main_00000000_00000000 ANSICHAR EntryPoint[24]; GetEntryPoint(EntryPoint, 24); DebugEntryPoint = EntryPoint; #endif } static TRefCountPtr CreateShaderModule(FVulkanDevice* Device, FVulkanShader::FSpirvCode& SpirvCode) { const TArrayView Spirv = SpirvCode.GetCodeView(); VkShaderModule ShaderModule; VkShaderModuleCreateInfo ModuleCreateInfo; ZeroVulkanStruct(ModuleCreateInfo, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO); //ModuleCreateInfo.flags = 0; ModuleCreateInfo.codeSize = Spirv.Num() * sizeof(uint32); ModuleCreateInfo.pCode = Spirv.GetData(); #if VULKAN_SUPPORTS_VALIDATION_CACHE VkShaderModuleValidationCacheCreateInfoEXT ValidationInfo; if (Device->GetOptionalExtensions().HasEXTValidationCache) { ZeroVulkanStruct(ValidationInfo, VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT); ValidationInfo.validationCache = Device->GetValidationCache(); ModuleCreateInfo.pNext = &ValidationInfo; } #endif VERIFYVULKANRESULT(VulkanRHI::vkCreateShaderModule(Device->GetInstanceHandle(), &ModuleCreateInfo, VULKAN_CPU_ALLOCATOR, &ShaderModule)); TRefCountPtr ReturnPtr = TRefCountPtr(new FVulkanShaderModule(Device, ShaderModule)); return ReturnPtr; } /* * Replace all subpassInput declarations with subpassInputMS * Replace all subpassLoad(Input) with subpassLoad(Input, 0) */ FVulkanShader::FSpirvCode FVulkanShader::PatchSpirvInputAttachments(FVulkanShader::FSpirvCode& SpirvCode) { TArrayView InSpirv = SpirvCode.GetCodeView(); const uint32 kHeaderLength = 5; const uint32 kOpTypeImage = 25; const uint32 kDimSubpassData = 6; const uint32 kOpImageRead = 98; const uint32 kOpLoad = 61; const uint32 kOpConstant = 43; const uint32 kOpTypeInt = 21; const uint32 Len = InSpirv.Num(); // Make sure we at least have a header if (Len < kHeaderLength) { return SpirvCode; } TArray OutSpirv; OutSpirv.Reserve(Len + 2); // Copy header OutSpirv.Append(&InSpirv[0], kHeaderLength); uint32 IntegerType = 0; uint32 Constant0 = 0; TArray> SubpassDataImages; for (uint32 Pos = kHeaderLength; Pos < Len;) { uint32* SpirvData = &InSpirv[Pos]; const uint32 InstLen = SpirvData[0] >> 16; const uint32 Opcode = SpirvData[0] & 0x0000ffffu; bool bSkip = false; if (Opcode == kOpTypeInt && SpirvData[3] == 1) { // signed int IntegerType = SpirvData[1]; } else if (Opcode == kOpConstant && SpirvData[1] == IntegerType && SpirvData[3] == 0) { // const signed int == 0 Constant0 = SpirvData[2]; } else if (Opcode == kOpTypeImage && SpirvData[3] == kDimSubpassData) { SpirvData[6] = 1; // mark as multisampled SubpassDataImages.Add(SpirvData[1]); } else if (Opcode == kOpLoad && SubpassDataImages.Contains(SpirvData[1])) { // pointers to our image SubpassDataImages.Add(SpirvData[2]); } else if (Opcode == kOpImageRead && SubpassDataImages.Contains(SpirvData[3])) { // const int 0, must be present as it's used for coord operand in image sampling check(Constant0 != 0); OutSpirv.Add((7u << 16) | kOpImageRead); // new instruction with 7 operands OutSpirv.Append(&SpirvData[1], 4); // copy existing operands OutSpirv.Add(0x40); // Sample operand OutSpirv.Add(Constant0); // Sample number bSkip = true; } if (!bSkip) { OutSpirv.Append(&SpirvData[0], InstLen); } Pos += InstLen; } return FVulkanShader::FSpirvCode(MoveTemp(OutSpirv)); } bool FVulkanShader::NeedsSpirvInputAttachmentPatching(const FGfxPipelineDesc& Desc) const { return (Desc.RasterizationSamples > 1 && CodeHeader.InputAttachmentsMask != 0); } TRefCountPtr FVulkanShader::CreateHandle(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout, uint32 LayoutHash) { FScopeLock Lock(&VulkanShaderModulesMapCS); FSpirvCode Spirv = GetPatchedSpirvCode(Desc, Layout); TRefCountPtr Module = CreateShaderModule(Device, Spirv); ShaderModules.Add(LayoutHash, Module); return Module; } FVulkanShader::FSpirvCode FVulkanShader::GetPatchedSpirvCode(const FGfxPipelineDesc& Desc, const FVulkanLayout* Layout) { FSpirvCode Spirv = GetSpirvCode(); if (NeedsSpirvInputAttachmentPatching(Desc)) { Spirv = PatchSpirvInputAttachments(Spirv); } return Spirv; } // Bindless variant of function that does not require layout for patching TRefCountPtr FVulkanShader::GetOrCreateHandle() { check(Device->SupportsBindless()); FScopeLock Lock(&VulkanShaderModulesMapCS); const uint32 MainModuleIndex = 0; TRefCountPtr* Found = ShaderModules.Find(MainModuleIndex); if (Found) { return *Found; } FSpirvCode Spirv = GetSpirvCode(); TRefCountPtr Module = CreateShaderModule(Device, Spirv); ShaderModules.Add(MainModuleIndex, Module); if (!CodeHeader.DebugName.IsEmpty()) { VULKAN_SET_DEBUG_NAME((*Device), VK_OBJECT_TYPE_SHADER_MODULE, Module->GetVkShaderModule(), TEXT("%s : (FVulkanShader*)0x%p"), *CodeHeader.DebugName, this); } return Module; } TRefCountPtr FVulkanRayTracingShader::GetOrCreateHandle(uint32 ModuleIdentifier) { check(Device->SupportsBindless()); const bool bIsAnyHitModuleIdentifier = (ModuleIdentifier == AnyHitModuleIdentifier); const bool bIsIntersectionModuleIdentifier = (ModuleIdentifier == IntersectionModuleIdentifier); // If we're using a single blob with multiple entry points, forward everything to the main module if ((bIsAnyHitModuleIdentifier && (GetCodeHeader().RayGroupAnyHit == FVulkanShaderHeader::ERayHitGroupEntrypoint::CommonBlob)) || (bIsIntersectionModuleIdentifier && (GetCodeHeader().RayGroupIntersection == FVulkanShaderHeader::ERayHitGroupEntrypoint::CommonBlob))) { return GetOrCreateHandle(MainModuleIdentifier); } FScopeLock Lock(&VulkanShaderModulesMapCS); TRefCountPtr* Found = ShaderModules.Find(ModuleIdentifier); if (Found) { return *Found; } auto CreateHitGroupHandle = [&](const FSpirvContainer& Container) { FSpirvCode Spirv = GetSpirvCode(Container); TRefCountPtr Module = CreateShaderModule(Device, Spirv); ShaderModules.Add(ModuleIdentifier, Module); return Module; }; TRefCountPtr Module; if (bIsAnyHitModuleIdentifier) { check(GetFrequency() == SF_RayHitGroup); if (GetCodeHeader().RayGroupAnyHit == FVulkanShaderHeader::ERayHitGroupEntrypoint::SeparateBlob) { Module = CreateHitGroupHandle(AnyHitSpirvContainer); } else { return TRefCountPtr(); } } else if (bIsIntersectionModuleIdentifier) { check(GetFrequency() == SF_RayHitGroup); if (GetCodeHeader().RayGroupIntersection == FVulkanShaderHeader::ERayHitGroupEntrypoint::SeparateBlob) { Module = CreateHitGroupHandle(IntersectionSpirvContainer); } else { return TRefCountPtr(); } } else { Module = CreateHitGroupHandle(SpirvContainer); } if (!CodeHeader.DebugName.IsEmpty()) { VULKAN_SET_DEBUG_NAME((*Device), VK_OBJECT_TYPE_SHADER_MODULE, Module->GetVkShaderModule(), TEXT("%s : (FVulkanShader*)0x%p"), *CodeHeader.DebugName, this); } return Module; } TRefCountPtr FVulkanShader::CreateHandle(const FVulkanLayout* Layout, uint32 LayoutHash) { FScopeLock Lock(&VulkanShaderModulesMapCS); FSpirvCode Spirv = GetSpirvCode(); TRefCountPtr Module = CreateShaderModule(Device, Spirv); ShaderModules.Add(LayoutHash, Module); if (!CodeHeader.DebugName.IsEmpty()) { VULKAN_SET_DEBUG_NAME((*Device), VK_OBJECT_TYPE_SHADER_MODULE, Module->GetVkShaderModule(), TEXT("%s : (FVulkanShader*)0x%p"), *CodeHeader.DebugName, this); } return Module; } FVulkanShader::~FVulkanShader() { PurgeShaderModules(); Device->GetShaderFactory().OnDeleteShader(*this); } void FVulkanShader::PurgeShaderModules() { FScopeLock Lock(&VulkanShaderModulesMapCS); ShaderModules.Empty(0); } FVertexShaderRHIRef FVulkanDynamicRHI::RHICreateVertexShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FPixelShaderRHIRef FVulkanDynamicRHI::RHICreatePixelShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FMeshShaderRHIRef FVulkanDynamicRHI::RHICreateMeshShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FAmplificationShaderRHIRef FVulkanDynamicRHI::RHICreateAmplificationShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FGeometryShaderRHIRef FVulkanDynamicRHI::RHICreateGeometryShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FComputeShaderRHIRef FVulkanDynamicRHI::RHICreateComputeShader(TArrayView Code, const FSHAHash& Hash) { return Device->GetShaderFactory().CreateShader(Code, Device); } FRayTracingShaderRHIRef FVulkanDynamicRHI::RHICreateRayTracingShader(TArrayView Code, const FSHAHash& Hash, EShaderFrequency ShaderFrequency) { switch (ShaderFrequency) { case EShaderFrequency::SF_RayGen: return Device->GetShaderFactory().CreateRayTracingShader(Code, Device); case EShaderFrequency::SF_RayMiss: return Device->GetShaderFactory().CreateRayTracingShader(Code, Device); case EShaderFrequency::SF_RayCallable: return Device->GetShaderFactory().CreateRayTracingShader(Code, Device); case EShaderFrequency::SF_RayHitGroup: return Device->GetShaderFactory().CreateRayTracingShader(Code, Device); default: check(false); return nullptr; } } FVulkanLayout::FVulkanLayout(FVulkanDevice* InDevice, bool InGfxLayout, bool InUsesBindless) : VulkanRHI::FDeviceChild(InDevice) , bIsGfxLayout(InGfxLayout) , bUsesBindless(InUsesBindless) , DescriptorSetLayout(Device) , PipelineLayout(VK_NULL_HANDLE) { } FVulkanLayout::~FVulkanLayout() { if (PipelineLayout != VK_NULL_HANDLE) { Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::PipelineLayout, PipelineLayout); PipelineLayout = VK_NULL_HANDLE; } } void FVulkanLayout::Compile(FVulkanDescriptorSetLayoutMap& DSetLayoutMap) { check(PipelineLayout == VK_NULL_HANDLE); DescriptorSetLayout.Compile(DSetLayoutMap); if (!bUsesBindless) { VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo; ZeroVulkanStruct(PipelineLayoutCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO); const TArray& LayoutHandles = DescriptorSetLayout.GetHandles(); PipelineLayoutCreateInfo.setLayoutCount = LayoutHandles.Num(); PipelineLayoutCreateInfo.pSetLayouts = LayoutHandles.GetData(); //PipelineLayoutCreateInfo.pushConstantRangeCount = 0; VERIFYVULKANRESULT(VulkanRHI::vkCreatePipelineLayout(Device->GetInstanceHandle(), &PipelineLayoutCreateInfo, VULKAN_CPU_ALLOCATOR, &PipelineLayout)); } } uint32 FVulkanDescriptorSetWriter::SetupDescriptorWrites( const TArray& Types, FVulkanHashableDescriptorInfo* InHashableDescriptorInfos, VkWriteDescriptorSet* InWriteDescriptors, VkDescriptorImageInfo* InImageInfo, VkDescriptorBufferInfo* InBufferInfo, uint8* InBindingToDynamicOffsetMap, VkWriteDescriptorSetAccelerationStructureKHR* InAccelerationStructuresWriteDescriptors, VkAccelerationStructureKHR* InAccelerationStructures, const FVulkanSamplerState& DefaultSampler, const FVulkanView::FTextureView& DefaultImageView) { HashableDescriptorInfos = InHashableDescriptorInfos; WriteDescriptors = InWriteDescriptors; NumWrites = Types.Num(); BindingToDynamicOffsetMap = InBindingToDynamicOffsetMap; InitWrittenMasks(NumWrites); uint32 DynamicOffsetIndex = 0; for (int32 Index = 0; Index < Types.Num(); ++Index) { InWriteDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; InWriteDescriptors->dstBinding = Index; InWriteDescriptors->descriptorCount = 1; InWriteDescriptors->descriptorType = Types[Index]; switch (Types[Index]) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: BindingToDynamicOffsetMap[Index] = DynamicOffsetIndex; ++DynamicOffsetIndex; InWriteDescriptors->pBufferInfo = InBufferInfo++; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: InWriteDescriptors->pBufferInfo = InBufferInfo++; break; case VK_DESCRIPTOR_TYPE_SAMPLER: SetWrittenBase(Index); //samplers have a default setting, don't assert on those yet. case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: // Texture.Load() still requires a default sampler... if (InHashableDescriptorInfos) // UseVulkanDescriptorCache() { InHashableDescriptorInfos[Index].Image.SamplerId = DefaultSampler.SamplerId; InHashableDescriptorInfos[Index].Image.ImageViewId = DefaultImageView.ViewId; InHashableDescriptorInfos[Index].Image.ImageLayout = static_cast(VK_IMAGE_LAYOUT_GENERAL); } InImageInfo->sampler = DefaultSampler.Sampler; InImageInfo->imageView = DefaultImageView.View; InImageInfo->imageLayout = VK_IMAGE_LAYOUT_GENERAL; InWriteDescriptors->pImageInfo = InImageInfo++; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: InAccelerationStructuresWriteDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; InAccelerationStructuresWriteDescriptors->pNext = nullptr; InAccelerationStructuresWriteDescriptors->accelerationStructureCount = 1; InAccelerationStructuresWriteDescriptors->pAccelerationStructures = InAccelerationStructures++; InWriteDescriptors->pNext = InAccelerationStructuresWriteDescriptors++; break; default: checkf(0, TEXT("Unsupported descriptor type %d"), (int32)Types[Index]); break; } ++InWriteDescriptors; } return DynamicOffsetIndex; } void FVulkanDescriptorSetsLayoutInfo::ProcessBindingsForStage(VkShaderStageFlagBits StageFlags, ShaderStage::EStage DescSetStage, const FVulkanShaderHeader& CodeHeader, FUniformBufferGatherInfo& OutUBGatherInfo) const { OutUBGatherInfo.CodeHeaders[DescSetStage] = &CodeHeader; } template void FVulkanDescriptorSetsLayoutInfo::FinalizeBindings(const FVulkanDevice& Device, const FUniformBufferGatherInfo& UBGatherInfo, const TArrayView& ImmutableSamplers, bool bUsesBindless) { // We'll be reusing this struct VkDescriptorSetLayoutBinding Binding; FMemory::Memzero(Binding); Binding.descriptorCount = 1; const bool bConvertAllUBsToDynamic = !bUsesBindless && (GDynamicGlobalUBs.GetValueOnAnyThread() > 1); const bool bConvertPackedUBsToDynamic = !bUsesBindless && (bConvertAllUBsToDynamic || (GDynamicGlobalUBs.GetValueOnAnyThread() == 1)); const uint32 MaxDescriptorSetUniformBuffersDynamic = Device.GetLimits().maxDescriptorSetUniformBuffersDynamic; int32 CurrentImmutableSampler = 0; for (int32 Stage = 0; Stage < (bIsCompute ? ShaderStage::NumComputeStages : ShaderStage::NumGraphicsStages); ++Stage) { checkSlow(StageInfos[Stage].IsEmpty()); if (const FVulkanShaderHeader* ShaderHeader = UBGatherInfo.CodeHeaders[Stage]) { FStageInfo& StageInfo = StageInfos[Stage]; Binding.stageFlags = UEFrequencyToVKStageBit(bIsCompute ? SF_Compute : ShaderStage::GetFrequencyForGfxStage((ShaderStage::EStage)Stage)); StageInfo.PackedGlobalsSize = ShaderHeader->PackedGlobalsSize; StageInfo.NumBoundUniformBuffers = ShaderHeader->NumBoundUniformBuffers; for (int32 BindingIndex = 0; BindingIndex < ShaderHeader->Bindings.Num(); ++BindingIndex) { const VkDescriptorType DescriptorType = (VkDescriptorType)ShaderHeader->Bindings[BindingIndex].DescriptorType; const bool bIsUniformBuffer = (DescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); const bool bIsGlobalPackedConstants = bIsUniformBuffer && ShaderHeader->PackedGlobalsSize && (BindingIndex == 0); if (bIsGlobalPackedConstants) { const VkDescriptorType UBType = bConvertPackedUBsToDynamic ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; const uint32 NewBindingIndex = StageInfo.Types.Add(UBType); checkf(NewBindingIndex == 0, TEXT("Packed globals should always be the first binding!")); Binding.binding = NewBindingIndex; Binding.descriptorType = UBType; AddDescriptor(Stage, Binding); } else if (bIsUniformBuffer) { VkDescriptorType UBType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; if (bConvertAllUBsToDynamic && LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < MaxDescriptorSetUniformBuffersDynamic) { UBType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; } // Here we might mess up with the stageFlags, so reset them every loop Binding.descriptorType = UBType; const FVulkanShaderHeader::FUniformBufferInfo& UBInfo = ShaderHeader->UniformBufferInfos[BindingIndex]; const bool bUBHasConstantData = (BindingIndex < (int32)ShaderHeader->NumBoundUniformBuffers); if (bUBHasConstantData) { const uint32 NewBindingIndex = StageInfo.Types.Add(UBType); check(NewBindingIndex == BindingIndex); Binding.binding = NewBindingIndex; AddDescriptor(Stage, Binding); } } else { const uint32 NewTypeIndex = StageInfo.Types.Add(DescriptorType); check(NewTypeIndex == BindingIndex); Binding.binding = BindingIndex; Binding.descriptorType = DescriptorType; AddDescriptor(Stage, Binding); } } } } CompileTypesUsageID(); GenerateHash(ImmutableSamplers, bIsCompute ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS); } FVulkanBoundShaderState::FVulkanBoundShaderState(FRHIVertexDeclaration* InVertexDeclarationRHI, FRHIVertexShader* InVertexShaderRHI, FRHIPixelShader* InPixelShaderRHI, FRHIGeometryShader* InGeometryShaderRHI) : CacheLink(InVertexDeclarationRHI, InVertexShaderRHI, InPixelShaderRHI, InGeometryShaderRHI, this) { CacheLink.AddToCache(); } FVulkanBoundShaderState::~FVulkanBoundShaderState() { CacheLink.RemoveFromCache(); } FBoundShaderStateRHIRef FVulkanDynamicRHI::RHICreateBoundShaderState( FRHIVertexDeclaration* VertexDeclarationRHI, FRHIVertexShader* VertexShaderRHI, FRHIPixelShader* PixelShaderRHI, FRHIGeometryShader* GeometryShaderRHI ) { LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanShaders); FBoundShaderStateRHIRef CachedBoundShaderState = GetCachedBoundShaderState_Threadsafe( VertexDeclarationRHI, VertexShaderRHI, PixelShaderRHI, GeometryShaderRHI ); if (CachedBoundShaderState.GetReference()) { // If we've already created a bound shader state with these parameters, reuse it. return CachedBoundShaderState; } return new FVulkanBoundShaderState(VertexDeclarationRHI, VertexShaderRHI, PixelShaderRHI, GeometryShaderRHI); } template void FVulkanDescriptorSetsLayoutInfo::FinalizeBindings(const FVulkanDevice& Device, const FUniformBufferGatherInfo& UBGatherInfo, const TArrayView& ImmutableSamplers, bool bUsesBindless); template void FVulkanDescriptorSetsLayoutInfo::FinalizeBindings(const FVulkanDevice& Device, const FUniformBufferGatherInfo& UBGatherInfo, const TArrayView& ImmutableSamplers, bool bUsesBindless);