799 lines
30 KiB
C++
799 lines
30 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
VulkanStatePipeline.cpp: Vulkan pipeline state implementation.
|
|
=============================================================================*/
|
|
|
|
#include "VulkanRHIPrivate.h"
|
|
#include "VulkanPipelineState.h"
|
|
#include "VulkanResources.h"
|
|
#include "VulkanPipeline.h"
|
|
#include "VulkanContext.h"
|
|
#include "VulkanPendingState.h"
|
|
#include "VulkanPipeline.h"
|
|
#include "VulkanLLM.h"
|
|
#include "RHICoreShader.h"
|
|
#include "GlobalRenderResources.h" // For GBlackTexture
|
|
|
|
enum
|
|
{
|
|
NumAllocationsPerPool = 8,
|
|
};
|
|
|
|
#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
|
|
static TAutoConsoleVariable<int32> GAlwaysWriteDS(
|
|
TEXT("r.Vulkan.AlwaysWriteDS"),
|
|
0,
|
|
TEXT(""),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
#endif
|
|
|
|
static bool ShouldAlwaysWriteDescriptors()
|
|
{
|
|
#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
|
|
return (GAlwaysWriteDS.GetValueOnAnyThread() != 0);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
FVulkanComputePipelineDescriptorState::FVulkanComputePipelineDescriptorState(FVulkanDevice& InDevice, FVulkanComputePipeline* InComputePipeline)
|
|
: FVulkanCommonPipelineDescriptorState(InDevice, ShaderStage::NumComputeStages, InComputePipeline->UsesBindless())
|
|
, PackedUniformBuffersMask(0)
|
|
, PackedUniformBuffersDirty(0)
|
|
, ComputePipeline(InComputePipeline)
|
|
{
|
|
LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanShaders);
|
|
check(InComputePipeline);
|
|
const FVulkanShaderHeader& CodeHeader = InComputePipeline->GetShaderCodeHeader();
|
|
PackedUniformBuffers.Init(CodeHeader, PackedUniformBuffersMask);
|
|
|
|
DescriptorSetsLayout = &InComputePipeline->GetLayout().GetDescriptorSetsLayout();
|
|
|
|
UsedSetsMask = (CodeHeader.Bindings.Num() > 0) ? 1 : 0;
|
|
|
|
CreateDescriptorWriteInfos();
|
|
InComputePipeline->AddRef();
|
|
|
|
for (const FVulkanShaderHeader::FGlobalSamplerInfo& GlobalSamplerInfo : CodeHeader.GlobalSamplerInfos)
|
|
{
|
|
checkSlow(!bUseBindless);
|
|
SetSamplerState(ShaderStage::Compute, GlobalSamplerInfo.BindingIndex, &Device.GetGlobalSamplers(GlobalSamplerInfo.Type));
|
|
}
|
|
|
|
ensure(DSWriter.Num() == 0 || DSWriter.Num() == 1);
|
|
}
|
|
|
|
void FVulkanCommonPipelineDescriptorState::CreateDescriptorWriteInfos()
|
|
{
|
|
check(DSWriteContainer.DescriptorWrites.Num() == 0);
|
|
check(UsedSetsMask <= (uint32)(((uint32)1 << MaxNumSets) - 1));
|
|
|
|
for (uint32 Set = 0; Set < MaxNumSets; ++Set)
|
|
{
|
|
const FVulkanDescriptorSetsLayoutInfo::FStageInfo& StageInfo = DescriptorSetsLayout->StageInfos[Set];
|
|
if (!StageInfo.Types.Num())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (UseVulkanDescriptorCache())
|
|
{
|
|
DSWriteContainer.HashableDescriptorInfo.AddZeroed(StageInfo.Types.Num() + 1); // Add 1 for the Layout
|
|
}
|
|
DSWriteContainer.DescriptorWrites.AddZeroed(StageInfo.Types.Num());
|
|
DSWriteContainer.DescriptorImageInfo.AddZeroed(StageInfo.NumImageInfos);
|
|
DSWriteContainer.DescriptorBufferInfo.AddZeroed(StageInfo.NumBufferInfos);
|
|
DSWriteContainer.AccelerationStructureWrites.AddZeroed(StageInfo.NumAccelerationStructures);
|
|
DSWriteContainer.AccelerationStructures.AddZeroed(StageInfo.NumAccelerationStructures);
|
|
|
|
checkf(StageInfo.Types.Num() < 255, TEXT("Need more bits for BindingToDynamicOffsetMap (currently 8)! Requires %d descriptor bindings in a set!"), StageInfo.Types.Num());
|
|
DSWriteContainer.BindingToDynamicOffsetMap.AddUninitialized(StageInfo.Types.Num());
|
|
}
|
|
|
|
FMemory::Memset(DSWriteContainer.BindingToDynamicOffsetMap.GetData(), 255, DSWriteContainer.BindingToDynamicOffsetMap.Num());
|
|
|
|
check(DSWriter.Num() == 0);
|
|
DSWriter.AddDefaulted(MaxNumSets);
|
|
|
|
const FVulkanSamplerState& DefaultSampler = Device.GetDefaultSampler();
|
|
const FVulkanView::FTextureView& DefaultImageView = ResourceCast(GBlackTexture->TextureRHI)->DefaultView->GetTextureView();
|
|
|
|
FVulkanHashableDescriptorInfo* CurrentHashableDescriptorInfo = nullptr;
|
|
if (UseVulkanDescriptorCache())
|
|
{
|
|
CurrentHashableDescriptorInfo = DSWriteContainer.HashableDescriptorInfo.GetData();
|
|
}
|
|
VkWriteDescriptorSet* CurrentDescriptorWrite = DSWriteContainer.DescriptorWrites.GetData();
|
|
VkDescriptorImageInfo* CurrentImageInfo = DSWriteContainer.DescriptorImageInfo.GetData();
|
|
VkDescriptorBufferInfo* CurrentBufferInfo = DSWriteContainer.DescriptorBufferInfo.GetData();
|
|
VkWriteDescriptorSetAccelerationStructureKHR* CurrentAccelerationStructuresWriteDescriptors = DSWriteContainer.AccelerationStructureWrites.GetData();
|
|
VkAccelerationStructureKHR* CurrentAccelerationStructures = DSWriteContainer.AccelerationStructures.GetData();
|
|
|
|
uint8* CurrentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffsetMap.GetData();
|
|
TArray<uint32> DynamicOffsetsStart;
|
|
DynamicOffsetsStart.AddZeroed(MaxNumSets);
|
|
uint32 TotalNumDynamicOffsets = 0;
|
|
|
|
for (uint32 Set = 0; Set < MaxNumSets; ++Set)
|
|
{
|
|
const FVulkanDescriptorSetsLayoutInfo::FStageInfo& StageInfo = DescriptorSetsLayout->StageInfos[Set];
|
|
if (!StageInfo.Types.Num())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DynamicOffsetsStart[Set] = TotalNumDynamicOffsets;
|
|
|
|
const uint32 NumDynamicOffsets = DSWriter[Set].SetupDescriptorWrites(
|
|
StageInfo.Types, CurrentHashableDescriptorInfo,
|
|
CurrentDescriptorWrite, CurrentImageInfo, CurrentBufferInfo, CurrentBindingToDynamicOffsetMap,
|
|
CurrentAccelerationStructuresWriteDescriptors,
|
|
CurrentAccelerationStructures,
|
|
DefaultSampler, DefaultImageView);
|
|
|
|
TotalNumDynamicOffsets += NumDynamicOffsets;
|
|
|
|
if (CurrentHashableDescriptorInfo) // UseVulkanDescriptorCache()
|
|
{
|
|
CurrentHashableDescriptorInfo += StageInfo.Types.Num();
|
|
CurrentHashableDescriptorInfo->Layout.Max0 = UINT32_MAX;
|
|
CurrentHashableDescriptorInfo->Layout.Max1 = UINT32_MAX;
|
|
CurrentHashableDescriptorInfo->Layout.LayoutId = DescriptorSetsLayout->GetHandleIds()[Set];
|
|
++CurrentHashableDescriptorInfo;
|
|
}
|
|
|
|
CurrentDescriptorWrite += StageInfo.Types.Num();
|
|
CurrentImageInfo += StageInfo.NumImageInfos;
|
|
CurrentBufferInfo += StageInfo.NumBufferInfos;
|
|
CurrentAccelerationStructuresWriteDescriptors += StageInfo.NumAccelerationStructures;
|
|
CurrentAccelerationStructures += StageInfo.NumAccelerationStructures;
|
|
|
|
CurrentBindingToDynamicOffsetMap += StageInfo.Types.Num();
|
|
}
|
|
|
|
DynamicOffsets.AddZeroed(TotalNumDynamicOffsets);
|
|
for (uint32 Set = 0; Set < MaxNumSets; ++Set)
|
|
{
|
|
DSWriter[Set].DynamicOffsets = DynamicOffsetsStart[Set] + DynamicOffsets.GetData();
|
|
}
|
|
|
|
DescriptorSetHandles.AddZeroed(MaxNumSets);
|
|
}
|
|
|
|
static inline VulkanRHI::FVulkanAllocation UpdatePackedUniformBuffers(const FPackedUniformBuffers& PackedUniformBuffers, FVulkanCommandListContext& InContext)
|
|
{
|
|
const FPackedUniformBuffers::FPackedBuffer& StagedUniformBuffer = PackedUniformBuffers.GetBuffer();
|
|
|
|
const uint32 UBSize = (uint32)StagedUniformBuffer.Num();
|
|
const uint32 UBAlign = (uint32)InContext.Device.GetLimits().minUniformBufferOffsetAlignment;
|
|
|
|
VulkanRHI::FVulkanAllocation TempAllocation;
|
|
uint8* MappedPointer = InContext.Device.GetTempBlockAllocator().Alloc(UBSize, UBAlign, InContext, TempAllocation);
|
|
|
|
FMemory::Memcpy(MappedPointer, StagedUniformBuffer.GetData(), UBSize);
|
|
|
|
return TempAllocation;
|
|
}
|
|
|
|
|
|
template<bool bUseDynamicGlobalUBs>
|
|
bool FVulkanComputePipelineDescriptorState::InternalUpdateDescriptorSets(FVulkanCommandListContext& Context)
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanUpdateDescriptorSets);
|
|
#endif
|
|
|
|
// Early exit
|
|
if (!UsedSetsMask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PackedUniformBuffersDirty != 0)
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanApplyPackedUniformBuffers);
|
|
#endif
|
|
SubmitPackedUniformBuffers<bUseDynamicGlobalUBs>(DSWriter[ShaderStage::Compute], UpdatePackedUniformBuffers(PackedUniformBuffers, Context));
|
|
PackedUniformBuffersDirty = 0;
|
|
}
|
|
|
|
// We are not using UseVulkanDescriptorCache() for compute pipelines
|
|
// Compute tend to use volatile resources that polute descriptor cache
|
|
|
|
if (!Context.AcquirePoolSetAndDescriptorsIfNeeded(*DescriptorSetsLayout, true, DescriptorSetHandles.GetData()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const VkDescriptorSet DescriptorSet = DescriptorSetHandles[0];
|
|
DSWriter[0].SetDescriptorSet(DescriptorSet);
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
for(FVulkanDescriptorSetWriter& Writer : DSWriter)
|
|
{
|
|
Writer.CheckAllWritten();
|
|
}
|
|
#endif
|
|
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
INC_DWORD_STAT_BY(STAT_VulkanNumUpdateDescriptors, DSWriteContainer.DescriptorWrites.Num());
|
|
INC_DWORD_STAT(STAT_VulkanNumDescSets);
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanVkUpdateDS);
|
|
#endif
|
|
VulkanRHI::vkUpdateDescriptorSets(Device.GetInstanceHandle(), DSWriteContainer.DescriptorWrites.Num(), DSWriteContainer.DescriptorWrites.GetData(), 0, nullptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FVulkanComputePipelineDescriptorState::UpdateBindlessDescriptors(FVulkanCommandListContext& Context)
|
|
{
|
|
check(bUseBindless);
|
|
|
|
// We should only have uniform buffers at this point
|
|
check(DSWriteContainer.DescriptorBufferInfo.Num() == DSWriteContainer.DescriptorWrites.Num());
|
|
check(DSWriteContainer.DescriptorImageInfo.Num() == 0);
|
|
|
|
FVulkanBindlessDescriptorManager::FUniformBufferDescriptorArrays StageUBs;
|
|
|
|
const FVulkanShaderHeader& Header = ComputePipeline->GetShaderCodeHeader();
|
|
|
|
TArray<VkDescriptorAddressInfoEXT>& DescriptorAddressInfos = StageUBs[ShaderStage::EStage::Compute];
|
|
DescriptorAddressInfos.SetNumZeroed(Header.NumBoundUniformBuffers);
|
|
uint32 UBIndex = 0;
|
|
|
|
// UBs are currently set from a fresh batch of descriptors for every call, so ignore PackedUniformBuffersDirty
|
|
check(PackedUniformBuffersMask <= 1);
|
|
if (PackedUniformBuffersMask != 0)
|
|
{
|
|
const FPackedUniformBuffers::FPackedBuffer& StagedUniformBuffer = PackedUniformBuffers.GetBuffer();
|
|
const int32 UBSize = StagedUniformBuffer.Num();
|
|
const int32 BindingIndex = 0;
|
|
const VkDeviceSize UBOffsetAlignment = Device.GetLimits().minUniformBufferOffsetAlignment;
|
|
|
|
VulkanRHI::FVulkanAllocation TempAllocation;
|
|
uint8* MappedPointer = Device.GetTempBlockAllocator().Alloc(UBSize, UBOffsetAlignment, Context, TempAllocation, &DescriptorAddressInfos[BindingIndex]);
|
|
FMemory::Memcpy(MappedPointer, StagedUniformBuffer.GetData(), UBSize);
|
|
|
|
PackedUniformBuffersDirty = 0;
|
|
++UBIndex;
|
|
}
|
|
|
|
for (;UBIndex < Header.NumBoundUniformBuffers; ++UBIndex)
|
|
{
|
|
VkDescriptorAddressInfoEXT& DescriptorAddressInfo = DescriptorAddressInfos[UBIndex];
|
|
check(DescriptorAddressInfo.sType == 0);
|
|
|
|
VkWriteDescriptorSet& WriteDescriptorSet = DSWriter[ShaderStage::EStage::Compute].WriteDescriptors[UBIndex];
|
|
check(WriteDescriptorSet.dstBinding == UBIndex);
|
|
check(WriteDescriptorSet.dstArrayElement == 0);
|
|
check(WriteDescriptorSet.descriptorCount == 1);
|
|
check(WriteDescriptorSet.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
|
checkf(WriteDescriptorSet.pBufferInfo && WriteDescriptorSet.pBufferInfo->buffer,
|
|
TEXT("Missing uniform buffer binding for [%s] at index [%d] of shader [%s]."),
|
|
*ComputePipeline->GetComputeShader()->GetUniformBufferName(UBIndex), UBIndex,
|
|
ComputePipeline->GetComputeShader()->GetShaderName());
|
|
|
|
VkBufferDeviceAddressInfo BufferInfo;
|
|
ZeroVulkanStruct(BufferInfo, VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO);
|
|
BufferInfo.buffer = WriteDescriptorSet.pBufferInfo->buffer;
|
|
VkDeviceAddress BufferAddress = VulkanRHI::vkGetBufferDeviceAddressKHR(Device.GetInstanceHandle(), &BufferInfo);
|
|
|
|
DescriptorAddressInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT;
|
|
DescriptorAddressInfo.address = BufferAddress + WriteDescriptorSet.pBufferInfo->offset;
|
|
DescriptorAddressInfo.range = WriteDescriptorSet.pBufferInfo->range;
|
|
}
|
|
|
|
// Send to descriptor manager
|
|
Device.GetBindlessDescriptorManager()->RegisterUniformBuffers(Context, VK_PIPELINE_BIND_POINT_COMPUTE, StageUBs);
|
|
}
|
|
|
|
FVulkanGraphicsPipelineDescriptorState::FVulkanGraphicsPipelineDescriptorState(FVulkanDevice& InDevice, FVulkanRHIGraphicsPipelineState* InGfxPipeline)
|
|
: FVulkanCommonPipelineDescriptorState(InDevice, ShaderStage::NumGraphicsStages, InGfxPipeline->UsesBindless())
|
|
, GfxPipeline(InGfxPipeline)
|
|
{
|
|
LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanShaders);
|
|
FMemory::Memzero(PackedUniformBuffersMask);
|
|
FMemory::Memzero(PackedUniformBuffersDirty);
|
|
|
|
check(InGfxPipeline && InGfxPipeline->Layout && InGfxPipeline->Layout->IsGfxLayout());
|
|
DescriptorSetsLayout = &InGfxPipeline->Layout->GetDescriptorSetsLayout();
|
|
|
|
UsedSetsMask = 0;
|
|
|
|
const FVulkanShaderFactory& ShaderFactory = Device.GetShaderFactory();
|
|
|
|
TStaticArray<const FVulkanShaderHeader*, ShaderStage::NumGraphicsStages> StageHeaders(InPlace, nullptr);
|
|
|
|
uint64 VertexShaderKey = InGfxPipeline->GetShaderKey(SF_Vertex);
|
|
if (VertexShaderKey)
|
|
{
|
|
const FVulkanVertexShader* VertexShader = ShaderFactory.LookupShader<FVulkanVertexShader>(InGfxPipeline->GetShaderKey(SF_Vertex));
|
|
check(VertexShader);
|
|
|
|
PackedUniformBuffers[ShaderStage::Vertex].Init(VertexShader->GetCodeHeader(), PackedUniformBuffersMask[ShaderStage::Vertex]);
|
|
UsedSetsMask |= VertexShader->GetCodeHeader().Bindings.Num() ? (1u << ShaderStage::Vertex) : 0u;
|
|
StageHeaders[ShaderStage::Vertex] = &VertexShader->GetCodeHeader();
|
|
}
|
|
|
|
uint64 PixelShaderKey = InGfxPipeline->GetShaderKey(SF_Pixel);
|
|
if (PixelShaderKey)
|
|
{
|
|
const FVulkanPixelShader* PixelShader = ShaderFactory.LookupShader<FVulkanPixelShader>(PixelShaderKey);
|
|
check(PixelShader);
|
|
|
|
PackedUniformBuffers[ShaderStage::Pixel].Init(PixelShader->GetCodeHeader(), PackedUniformBuffersMask[ShaderStage::Pixel]);
|
|
UsedSetsMask |= PixelShader->GetCodeHeader().Bindings.Num() ? (1u << ShaderStage::Pixel) : 0u;
|
|
StageHeaders[ShaderStage::Pixel] = &PixelShader->GetCodeHeader();
|
|
}
|
|
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
uint64 MeshShaderKey = InGfxPipeline->GetShaderKey(SF_Mesh);
|
|
if (MeshShaderKey)
|
|
{
|
|
const FVulkanMeshShader* MeshShader = ShaderFactory.LookupShader<FVulkanMeshShader>(MeshShaderKey);
|
|
check(MeshShader);
|
|
|
|
PackedUniformBuffers[ShaderStage::Mesh].Init(MeshShader->GetCodeHeader(), PackedUniformBuffersMask[ShaderStage::Mesh]);
|
|
UsedSetsMask |= MeshShader->GetCodeHeader().Bindings.Num() ? (1u << ShaderStage::Mesh) : 0u;
|
|
StageHeaders[ShaderStage::Mesh] = &MeshShader->GetCodeHeader();
|
|
}
|
|
|
|
uint64 TaskShaderKey = InGfxPipeline->GetShaderKey(SF_Amplification);
|
|
if (TaskShaderKey)
|
|
{
|
|
const FVulkanTaskShader* TaskShader = ShaderFactory.LookupShader<FVulkanTaskShader>(TaskShaderKey);
|
|
check(TaskShader);
|
|
|
|
PackedUniformBuffers[ShaderStage::Task].Init(TaskShader->GetCodeHeader(), PackedUniformBuffersMask[ShaderStage::Task]);
|
|
UsedSetsMask |= TaskShader->GetCodeHeader().Bindings.Num() ? (1u << ShaderStage::Task) : 0u;
|
|
StageHeaders[ShaderStage::Task] = &TaskShader->GetCodeHeader();
|
|
}
|
|
#endif
|
|
|
|
#if VULKAN_SUPPORTS_GEOMETRY_SHADERS
|
|
uint64 GeometryShaderKey = InGfxPipeline->GetShaderKey(SF_Geometry);
|
|
if (GeometryShaderKey)
|
|
{
|
|
const FVulkanGeometryShader* GeometryShader = ShaderFactory.LookupShader<FVulkanGeometryShader>(GeometryShaderKey);
|
|
check(GeometryShader);
|
|
|
|
PackedUniformBuffers[ShaderStage::Geometry].Init(GeometryShader->GetCodeHeader(), PackedUniformBuffersMask[ShaderStage::Geometry]);
|
|
UsedSetsMask |= GeometryShader->GetCodeHeader().Bindings.Num() ? (1u << ShaderStage::Geometry) : 0u;
|
|
StageHeaders[ShaderStage::Geometry] = &GeometryShader->GetCodeHeader();
|
|
}
|
|
#endif
|
|
|
|
CreateDescriptorWriteInfos();
|
|
//UE_LOG(LogVulkanRHI, Warning, TEXT("GfxPSOState %p For PSO %p Writes:%d"), this, InGfxPipeline, DSWriteContainer.DescriptorWrites.Num());
|
|
|
|
InGfxPipeline->AddRef();
|
|
|
|
for (int32 StageIndex = 0; StageIndex < StageHeaders.Num(); ++StageIndex)
|
|
{
|
|
const FVulkanShaderHeader* CodeHeader = StageHeaders[StageIndex];
|
|
if (CodeHeader)
|
|
{
|
|
for (const FVulkanShaderHeader::FGlobalSamplerInfo& GlobalSamplerInfo : CodeHeader->GlobalSamplerInfos)
|
|
{
|
|
checkSlow(!bUseBindless);
|
|
SetSamplerState(StageIndex, GlobalSamplerInfo.BindingIndex, &Device.GetGlobalSamplers(GlobalSamplerInfo.Type));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<bool bUseDynamicGlobalUBs>
|
|
bool FVulkanGraphicsPipelineDescriptorState::InternalUpdateDescriptorSets(FVulkanCommandListContext& Context)
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanUpdateDescriptorSets);
|
|
#endif
|
|
|
|
// Early exit
|
|
if (!UsedSetsMask)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Process updates
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanApplyPackedUniformBuffers);
|
|
#endif
|
|
for (int32 Stage = 0; Stage < ShaderStage::NumGraphicsStages; ++Stage)
|
|
{
|
|
if (PackedUniformBuffersDirty[Stage] != 0)
|
|
{
|
|
MarkDirty(SubmitPackedUniformBuffers<bUseDynamicGlobalUBs>(DSWriter[Stage], UpdatePackedUniformBuffers(PackedUniformBuffers[Stage], Context)));
|
|
PackedUniformBuffersDirty[Stage] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UseVulkanDescriptorCache() && !HasVolatileResources())
|
|
{
|
|
if (bIsResourcesDirty)
|
|
{
|
|
Device.GetDescriptorSetCache().GetDescriptorSets(GetDSetsKey(), *DescriptorSetsLayout, DSWriter, DescriptorSetHandles.GetData());
|
|
bIsResourcesDirty = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const bool bNeedsWrite = (bIsResourcesDirty || ShouldAlwaysWriteDescriptors());
|
|
|
|
// Allocate sets based on what changed
|
|
if (Context.AcquirePoolSetAndDescriptorsIfNeeded(*DescriptorSetsLayout, bNeedsWrite, DescriptorSetHandles.GetData()))
|
|
{
|
|
uint32 RemainingSetsMask = UsedSetsMask;
|
|
uint32 Set = 0;
|
|
uint32 NumSets = 0;
|
|
while (RemainingSetsMask)
|
|
{
|
|
if (RemainingSetsMask & 1)
|
|
{
|
|
const VkDescriptorSet DescriptorSet = DescriptorSetHandles[Set];
|
|
DSWriter[Set].SetDescriptorSet(DescriptorSet);
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
DSWriter[Set].CheckAllWritten();
|
|
#endif
|
|
++NumSets;
|
|
}
|
|
|
|
++Set;
|
|
RemainingSetsMask >>= 1;
|
|
}
|
|
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
INC_DWORD_STAT_BY(STAT_VulkanNumUpdateDescriptors, DSWriteContainer.DescriptorWrites.Num());
|
|
INC_DWORD_STAT_BY(STAT_VulkanNumDescSets, NumSets);
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanVkUpdateDS);
|
|
#endif
|
|
VulkanRHI::vkUpdateDescriptorSets(Device.GetInstanceHandle(), DSWriteContainer.DescriptorWrites.Num(), DSWriteContainer.DescriptorWrites.GetData(), 0, nullptr);
|
|
|
|
bIsResourcesDirty = false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FVulkanGraphicsPipelineDescriptorState::UpdateBindlessDescriptors(FVulkanCommandListContext& Context)
|
|
{
|
|
check(bUseBindless);
|
|
|
|
// We should only have uniform buffers at this point
|
|
check(DSWriteContainer.DescriptorBufferInfo.Num() == DSWriteContainer.DescriptorWrites.Num());
|
|
check(DSWriteContainer.DescriptorImageInfo.Num() == 0);
|
|
|
|
const VkDeviceSize UBOffsetAlignment = Device.GetLimits().minUniformBufferOffsetAlignment;
|
|
|
|
FVulkanBindlessDescriptorManager::FUniformBufferDescriptorArrays StageUBs;
|
|
|
|
// Process updates
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanApplyPackedUniformBuffers);
|
|
#endif
|
|
for (int32 Stage = 0; Stage < ShaderStage::NumGraphicsStages; ++Stage)
|
|
{
|
|
const FVulkanShader* VulkanShader = GfxPipeline->GetVulkanShader(GetFrequencyForGfxStage((ShaderStage::EStage)Stage));
|
|
if (!VulkanShader)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FVulkanShaderHeader& Header = VulkanShader->GetCodeHeader();
|
|
|
|
TArray<VkDescriptorAddressInfoEXT>& DescriptorAddressInfos = StageUBs[Stage];
|
|
DescriptorAddressInfos.SetNumZeroed(Header.NumBoundUniformBuffers);
|
|
uint32 UBIndex = 0;
|
|
|
|
// UBs are currently set from a fresh batch of descriptors for every call, so ignore PackedUniformBuffersDirty
|
|
check(PackedUniformBuffersMask[Stage] <= 1);
|
|
if (PackedUniformBuffersMask[Stage] != 0)
|
|
{
|
|
const FPackedUniformBuffers::FPackedBuffer& StagedUniformBuffer = PackedUniformBuffers[Stage].GetBuffer();
|
|
const int32 UBSize = StagedUniformBuffer.Num();
|
|
const int32 BindingIndex = 0;
|
|
|
|
VulkanRHI::FVulkanAllocation TempAllocation;
|
|
uint8* MappedPointer = Device.GetTempBlockAllocator().Alloc(UBSize, UBOffsetAlignment, Context, TempAllocation, &DescriptorAddressInfos[BindingIndex]);
|
|
FMemory::Memcpy(MappedPointer, StagedUniformBuffer.GetData(), UBSize);
|
|
|
|
PackedUniformBuffersDirty[Stage] = 0;
|
|
++UBIndex;
|
|
}
|
|
|
|
for (; UBIndex < Header.NumBoundUniformBuffers; ++UBIndex)
|
|
{
|
|
VkDescriptorAddressInfoEXT& DescriptorAddressInfo = DescriptorAddressInfos[UBIndex];
|
|
check(DescriptorAddressInfo.sType == 0);
|
|
|
|
VkWriteDescriptorSet& WriteDescriptorSet = DSWriter[Stage].WriteDescriptors[UBIndex];
|
|
check(WriteDescriptorSet.dstBinding == UBIndex);
|
|
check(WriteDescriptorSet.dstArrayElement == 0);
|
|
check(WriteDescriptorSet.descriptorCount == 1);
|
|
check(WriteDescriptorSet.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
|
checkSlow(WriteDescriptorSet.pBufferInfo);
|
|
|
|
VkBufferDeviceAddressInfo BufferInfo;
|
|
ZeroVulkanStruct(BufferInfo, VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO);
|
|
BufferInfo.buffer = WriteDescriptorSet.pBufferInfo->buffer;
|
|
VkDeviceAddress BufferAddress = VulkanRHI::vkGetBufferDeviceAddressKHR(Device.GetInstanceHandle(), &BufferInfo);
|
|
|
|
DescriptorAddressInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT;
|
|
DescriptorAddressInfo.address = BufferAddress + WriteDescriptorSet.pBufferInfo->offset;
|
|
DescriptorAddressInfo.range = WriteDescriptorSet.pBufferInfo->range;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send to descriptor manager
|
|
Device.GetBindlessDescriptorManager()->RegisterUniformBuffers(Context, VK_PIPELINE_BIND_POINT_GRAPHICS, StageUBs);
|
|
}
|
|
|
|
|
|
template <typename TRHIShader>
|
|
void FVulkanCommandListContext::ApplyStaticUniformBuffers(TRHIShader* Shader)
|
|
{
|
|
if (Shader)
|
|
{
|
|
const auto& StaticSlots = Shader->GetStaticSlots();
|
|
const auto& UBInfos = Shader->GetCodeHeader().UniformBufferInfos;
|
|
|
|
for (int32 BufferIndex = 0; BufferIndex < StaticSlots.Num(); ++BufferIndex)
|
|
{
|
|
const FUniformBufferStaticSlot Slot = StaticSlots[BufferIndex];
|
|
|
|
if (IsUniformBufferStaticSlotValid(Slot))
|
|
{
|
|
FRHIUniformBuffer* Buffer = GlobalUniformBuffers[Slot];
|
|
UE::RHICore::ValidateStaticUniformBuffer(Buffer, Slot, UBInfos[BufferIndex].LayoutHash);
|
|
|
|
if (Buffer)
|
|
{
|
|
RHISetShaderUniformBuffer(Shader, BufferIndex, Buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FVulkanCommandListContext::RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, uint32 StencilRef, bool bApplyAdditionalState)
|
|
{
|
|
FVulkanRHIGraphicsPipelineState* Pipeline = ResourceCast(GraphicsState);
|
|
|
|
FVulkanPipelineStateCacheManager* PipelineStateCache = Device.GetPipelineStateCache();
|
|
PipelineStateCache->LRUTouch(Pipeline);
|
|
|
|
Pipeline->FrameCounter.Set(GFrameNumberRenderThread);
|
|
|
|
FVulkanCommandBuffer& CommandBuffer = GetCommandBuffer();
|
|
bool bForceResetPipeline = !CommandBuffer.bHasPipeline;
|
|
|
|
if (PendingGfxState->SetGfxPipeline(Pipeline, bForceResetPipeline))
|
|
{
|
|
#if VULKAN_ENABLE_AGGRESSIVE_STATS
|
|
SCOPE_CYCLE_COUNTER(STAT_VulkanPipelineBind);
|
|
#endif
|
|
PendingGfxState->Bind(CommandBuffer.GetHandle());
|
|
CommandBuffer.bHasPipeline = true;
|
|
PendingGfxState->MarkNeedsDynamicStates();
|
|
}
|
|
|
|
PendingGfxState->SetStencilRef(StencilRef);
|
|
|
|
if (bApplyAdditionalState)
|
|
{
|
|
ApplyStaticUniformBuffers(static_cast<FVulkanVertexShader*>(Pipeline->VulkanShaders[ShaderStage::Vertex]));
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
ApplyStaticUniformBuffers(static_cast<FVulkanMeshShader*>(Pipeline->VulkanShaders[ShaderStage::Mesh]));
|
|
ApplyStaticUniformBuffers(static_cast<FVulkanTaskShader*>(Pipeline->VulkanShaders[ShaderStage::Task]));
|
|
#endif
|
|
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
|
|
ApplyStaticUniformBuffers(static_cast<FVulkanGeometryShader*>(Pipeline->VulkanShaders[ShaderStage::Geometry]));
|
|
#endif
|
|
ApplyStaticUniformBuffers(static_cast<FVulkanPixelShader*>(Pipeline->VulkanShaders[ShaderStage::Pixel]));
|
|
}
|
|
}
|
|
|
|
void FVulkanCommandListContext::RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState)
|
|
{
|
|
AcquirePoolSetContainer();
|
|
|
|
//#todo-rco: Set PendingGfx to null
|
|
FVulkanComputePipeline* ComputePipeline = ResourceCast(ComputePipelineState);
|
|
PendingComputeState->SetComputePipeline(ComputePipeline);
|
|
|
|
ComputePipeline->FrameCounter.Set(GFrameNumberRenderThread);
|
|
|
|
ApplyStaticUniformBuffers(ResourceCast(ComputePipeline->GetComputeShader()));
|
|
}
|
|
|
|
|
|
template bool FVulkanGraphicsPipelineDescriptorState::InternalUpdateDescriptorSets<true>(FVulkanCommandListContext& Context);
|
|
template bool FVulkanGraphicsPipelineDescriptorState::InternalUpdateDescriptorSets<false>(FVulkanCommandListContext& Context);
|
|
template bool FVulkanComputePipelineDescriptorState::InternalUpdateDescriptorSets<true>(FVulkanCommandListContext& Context);
|
|
template bool FVulkanComputePipelineDescriptorState::InternalUpdateDescriptorSets<false>(FVulkanCommandListContext& Context);
|
|
|
|
|
|
void FVulkanDescriptorSetWriter::CheckAllWritten()
|
|
{
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
auto GetVkDescriptorTypeString = [](VkDescriptorType Type)
|
|
{
|
|
switch (Type)
|
|
{
|
|
// + 19 to skip "VK_DESCRIPTOR_TYPE_"
|
|
#define VKSWITCHCASE(x) case x: return FString(&TEXT(#x)[19]);
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_SAMPLER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
|
|
VKSWITCHCASE(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
|
|
#undef VKSWITCHCASE
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FString::Printf(TEXT("Unknown VkDescriptorType %d"), (int32)Type);
|
|
};
|
|
|
|
const uint32 Writes = NumWrites;
|
|
if (Writes == 0)
|
|
return;
|
|
|
|
bool bFail = false;
|
|
if(Writes <= 32) //early out for the most common case.
|
|
{
|
|
bFail = WrittenMask[0] != ((1llu << Writes)-1);
|
|
}
|
|
else
|
|
{
|
|
const int32 Last = int32(WrittenMask.Num()-1);
|
|
for(int32 i = 0; !bFail && i < Last; ++i)
|
|
{
|
|
uint64 Mask = WrittenMask[i];
|
|
bFail = bFail || Mask != 0xffffffff;
|
|
}
|
|
|
|
const uint32 TailCount = Writes - (Last * 32);
|
|
check(TailCount != 0);
|
|
const uint32 TailMask = (1llu << TailCount)-1;
|
|
bFail = bFail || TailMask != WrittenMask[Last];
|
|
}
|
|
|
|
if(bFail)
|
|
{
|
|
FString Descriptors;
|
|
for (uint32 i = 0; i < Writes; ++i)
|
|
{
|
|
uint32 Index = i / 32;
|
|
uint32 Mask = i % 32;
|
|
if(0 == (WrittenMask[Index] & (1llu << Mask)))
|
|
{
|
|
FString TypeString = GetVkDescriptorTypeString(WriteDescriptors[i].descriptorType);
|
|
Descriptors += FString::Printf(TEXT("\t\tDescriptorWrite %d/%d Was not written(Type %s)\n"), i, NumWrites, *TypeString);
|
|
}
|
|
}
|
|
UE_LOG(LogVulkanRHI, Warning, TEXT("Not All descriptors where filled out. this can/will cause a driver crash\n%s\n"), *Descriptors);
|
|
ensureMsgf(false, TEXT("Not All descriptors where filled out. this can/will cause a driver crash\n%s\n"), *Descriptors);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FVulkanDescriptorSetWriter::Reset()
|
|
{
|
|
bHasVolatileResources = false;
|
|
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
WrittenMask = BaseWrittenMask;
|
|
#endif
|
|
}
|
|
void FVulkanDescriptorSetWriter::SetWritten(uint32 DescriptorIndex)
|
|
{
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
uint32 Index = DescriptorIndex / 32;
|
|
uint32 Mask = DescriptorIndex % 32;
|
|
WrittenMask[Index] |= (1<<Mask);
|
|
#endif
|
|
}
|
|
void FVulkanDescriptorSetWriter::SetWrittenBase(uint32 DescriptorIndex)
|
|
{
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
uint32 Index = DescriptorIndex / 32;
|
|
uint32 Mask = DescriptorIndex % 32;
|
|
BaseWrittenMask[Index] |= (1<<Mask);
|
|
#endif
|
|
}
|
|
|
|
void FVulkanDescriptorSetWriter::InitWrittenMasks(uint32 NumDescriptorWrites)
|
|
{
|
|
#if VULKAN_VALIDATE_DESCRIPTORS_WRITTEN
|
|
uint32 Size = (NumDescriptorWrites + 31) / 32;
|
|
WrittenMask.Empty(Size);
|
|
WrittenMask.SetNumZeroed(Size);
|
|
BaseWrittenMask.Empty(Size);
|
|
BaseWrittenMask.SetNumZeroed(Size);
|
|
#endif
|
|
}
|
|
|
|
void FVulkanCommonPipelineDescriptorState::SetSRV(bool bCompute, uint8 DescriptorSet, uint32 BindingIndex, FVulkanShaderResourceView* SRV)
|
|
{
|
|
check(!bUseBindless);
|
|
|
|
switch (SRV->GetViewType())
|
|
{
|
|
case FVulkanView::EType::Null:
|
|
checkf(false, TEXT("Attempt to bind a null SRV."));
|
|
break;
|
|
|
|
case FVulkanView::EType::TypedBuffer:
|
|
MarkDirty(DSWriter[DescriptorSet].WriteUniformTexelBuffer(BindingIndex, SRV->GetTypedBufferView()));
|
|
break;
|
|
|
|
case FVulkanView::EType::Texture:
|
|
{
|
|
const ERHIAccess Access = bCompute ? ERHIAccess::SRVCompute : ERHIAccess::SRVGraphics;
|
|
const FVulkanTexture* VulkanTexture = ResourceCast(SRV->GetTexture());
|
|
const VkImageLayout Layout = FVulkanPipelineBarrier::GetDefaultLayout(*VulkanTexture, Access);
|
|
MarkDirty(DSWriter[DescriptorSet].WriteImage(BindingIndex, SRV->GetTextureView(), Layout));
|
|
}
|
|
break;
|
|
|
|
case FVulkanView::EType::StructuredBuffer:
|
|
check((ResourceCast(SRV->GetBuffer())->GetBufferUsageFlags() & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) == VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
|
MarkDirty(DSWriter[DescriptorSet].WriteStorageBuffer(BindingIndex, SRV->GetStructuredBufferView()));
|
|
break;
|
|
|
|
case FVulkanView::EType::AccelerationStructure:
|
|
MarkDirty(DSWriter[DescriptorSet].WriteAccelerationStructure(BindingIndex, SRV->GetAccelerationStructureView().Handle));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FVulkanCommonPipelineDescriptorState::SetUAV(bool bCompute, uint8 DescriptorSet, uint32 BindingIndex, FVulkanUnorderedAccessView* UAV)
|
|
{
|
|
check(!bUseBindless);
|
|
|
|
switch (UAV->GetViewType())
|
|
{
|
|
case FVulkanView::EType::Null:
|
|
checkf(false, TEXT("Attempt to bind a null UAV."));
|
|
break;
|
|
|
|
case FVulkanView::EType::TypedBuffer:
|
|
MarkDirty(DSWriter[DescriptorSet].WriteStorageTexelBuffer(BindingIndex, UAV->GetTypedBufferView()));
|
|
break;
|
|
|
|
case FVulkanView::EType::Texture:
|
|
{
|
|
const ERHIAccess Access = bCompute ? ERHIAccess::UAVCompute : ERHIAccess::UAVGraphics;
|
|
const FVulkanTexture* VulkanTexture = ResourceCast(UAV->GetTexture());
|
|
const VkImageLayout ExpectedLayout = FVulkanPipelineBarrier::GetDefaultLayout(*VulkanTexture, Access);
|
|
MarkDirty(DSWriter[DescriptorSet].WriteStorageImage(BindingIndex, UAV->GetTextureView(), ExpectedLayout));
|
|
}
|
|
break;
|
|
|
|
case FVulkanView::EType::StructuredBuffer:
|
|
check((ResourceCast(UAV->GetBuffer())->GetBufferUsageFlags() & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) == VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
|
MarkDirty(DSWriter[DescriptorSet].WriteStorageBuffer(BindingIndex, UAV->GetStructuredBufferView()));
|
|
break;
|
|
|
|
case FVulkanView::EType::AccelerationStructure:
|
|
MarkDirty(DSWriter[DescriptorSet].WriteAccelerationStructure(BindingIndex, UAV->GetAccelerationStructureView().Handle));
|
|
break;
|
|
}
|
|
}
|