Files
UnrealEngine/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.cpp
2025-05-18 13:04:45 +08:00

1806 lines
74 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VulkanDevice.cpp: Vulkan device RHI implementation.
=============================================================================*/
#include "VulkanRHIPrivate.h"
#include "VulkanDevice.h"
#include "VulkanPendingState.h"
#include "VulkanContext.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "VulkanPlatform.h"
#include "VulkanLLM.h"
#include "VulkanTransientResourceAllocator.h"
#include "VulkanExtensions.h"
#include "VulkanRenderpass.h"
#include "VulkanRayTracing.h"
#include "VulkanDescriptorSets.h"
#include "VulkanChunkedPipelineCache.h"
static TAutoConsoleVariable<int32> GRHIAllowAsyncComputeCvar(
TEXT("r.Vulkan.AllowAsyncCompute"),
0,
TEXT("0 to disable async compute queue (if available)\n")
TEXT("1 to allow async compute queue"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> GRHIAllowTransferQueueCvar(
TEXT("r.Vulkan.AllowTransferQueue"),
0,
TEXT("0 to disable transfer queue (if available)\n")
TEXT("1 to allow transfer queue"),
ECVF_ReadOnly
);
int32 GVulkanAllowConcurrentBuffer = 1;
static TAutoConsoleVariable<int32> GCVarAllowConcurrentBuffer(
TEXT("r.Vulkan.AllowConcurrentBuffer"),
GVulkanAllowConcurrentBuffer,
TEXT("When async compute is supported: \n")
TEXT(" 0 to use queue family ownership transfers with buffers\n")
TEXT(" 1 to use sharing mode concurrent with buffers"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GAllowPresentOnComputeQueue(
TEXT("r.Vulkan.AllowPresentOnComputeQueue"),
0,
TEXT("0 to present on the graphics queue")
TEXT("1 to allow presenting on the compute queue if available")
);
TAutoConsoleVariable<int32> GCVarRobustBufferAccess(
TEXT("r.Vulkan.RobustBufferAccess"),
1,
TEXT("0 to disable robust buffer access")
TEXT("1 to enable (default)"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> CVarVulkanUseD24(
TEXT("r.Vulkan.Depth24Bit"),
0,
TEXT("0: Use 32-bit float depth buffer (default)\n1: Use 24-bit fixed point depth buffer\n"),
ECVF_ReadOnly
);
static TAutoConsoleVariable<int32> GCVarVulkanTempBlockSizeKB(
TEXT("r.Vulkan.TempBlockSizeKB"),
4096,
TEXT("Size in KB of the temporary blocks allocate by contexts, used for single use ub allocs and copies (default: 4096KB)."),
ECVF_ReadOnly
);
// Mirror GPixelFormats with format information for buffers
VkFormat GVulkanBufferFormat[PF_MAX];
// Mirror GPixelFormats with format information for buffers
VkFormat GVulkanSRGBFormat[PF_MAX];
EDelayAcquireImageType GVulkanDelayAcquireImage = EDelayAcquireImageType::DelayAcquire;
TAutoConsoleVariable<int32> CVarDelayAcquireBackBuffer(
TEXT("r.Vulkan.DelayAcquireBackBuffer"),
1,
TEXT("Whether to delay acquiring the back buffer \n")
TEXT(" 0: acquire next image on frame start \n")
TEXT(" 1: acquire next image just before presenting, rendering is done to intermediate image which is then copied to a real backbuffer (default) \n"),
ECVF_ReadOnly
);
static EDelayAcquireImageType DelayAcquireBackBuffer()
{
const int32 DelayType = CVarDelayAcquireBackBuffer.GetValueOnAnyThread();
return (DelayType == 0) ? EDelayAcquireImageType::None : EDelayAcquireImageType::DelayAcquire;
}
#if VULKAN_SUPPORTS_VALIDATION_CACHE
static void LoadValidationCache(VkDevice Device, VkValidationCacheEXT& OutValidationCache)
{
VkValidationCacheCreateInfoEXT ValidationCreateInfo;
ZeroVulkanStruct(ValidationCreateInfo, VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT);
TArray<uint8> InData;
const FString& CacheFilename = VulkanRHI::GetValidationCacheFilename();
UE_LOG(LogVulkanRHI, Display, TEXT("Trying validation cache file %s"), *CacheFilename);
if (FFileHelper::LoadFileToArray(InData, *CacheFilename, FILEREAD_Silent) && InData.Num() > 0)
{
// The code below supports SDK 1.0.65 Vulkan spec, which contains the following table:
//
// Offset Size Meaning
// ------ ------------ ------------------------------------------------------------------
// 0 4 length in bytes of the entire validation cache header written as a
// stream of bytes, with the least significant byte first
// 4 4 a VkValidationCacheHeaderVersionEXT value written as a stream of
// bytes, with the least significant byte first
// 8 VK_UUID_SIZE a layer commit ID expressed as a UUID, which uniquely identifies
// the version of the validation layers used to generate these
// validation results
int32* DataPtr = (int32*)InData.GetData();
if (*DataPtr > 0)
{
++DataPtr;
int32 Version = *DataPtr++;
if (Version == VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
{
DataPtr += VK_UUID_SIZE / sizeof(int32);
}
else
{
UE_LOG(LogVulkanRHI, Warning, TEXT("Bad validation cache file %s, version=%d, expected %d"), *CacheFilename, Version, VK_PIPELINE_CACHE_HEADER_VERSION_ONE);
InData.Reset(0);
}
}
else
{
UE_LOG(LogVulkanRHI, Warning, TEXT("Bad validation cache file %s, header size=%d"), *CacheFilename, *DataPtr);
InData.Reset(0);
}
}
ValidationCreateInfo.initialDataSize = InData.Num();
ValidationCreateInfo.pInitialData = InData.Num() > 0 ? InData.GetData() : nullptr;
//ValidationCreateInfo.flags = 0;
PFN_vkCreateValidationCacheEXT vkCreateValidationCache = (PFN_vkCreateValidationCacheEXT)(void*)VulkanRHI::vkGetDeviceProcAddr(Device, "vkCreateValidationCacheEXT");
if (vkCreateValidationCache)
{
VkResult Result = vkCreateValidationCache(Device, &ValidationCreateInfo, VULKAN_CPU_ALLOCATOR, &OutValidationCache);
if (Result != VK_SUCCESS)
{
UE_LOG(LogVulkanRHI, Warning, TEXT("Failed to create Vulkan validation cache, VkResult=%d"), Result);
}
}
}
#endif
static VkExtent2D GetBestMatchedShadingRateExtents(uint32 ShadingRate, const TArray<VkPhysicalDeviceFragmentShadingRateKHR>& FragmentShadingRates)
{
// Given that for Vulkan we need to query available device shading rates, we're not guaranteed to have everything that's in our enum;
// This function walks the list of supported fragment rates returned by the device, and returns the closest match to the rate requested.
const VkExtent2D DirectMappedExtent = {
1u << (ShadingRate >> 2),
1u << (ShadingRate & 0x03)
};
VkExtent2D BestMatchedExtent = { 1, 1 };
if (BestMatchedExtent.width != DirectMappedExtent.width ||
BestMatchedExtent.height != DirectMappedExtent.height)
{
for (auto const& Rate : FragmentShadingRates)
{
if (Rate.fragmentSize.width == DirectMappedExtent.width &&
Rate.fragmentSize.height == DirectMappedExtent.height)
{
BestMatchedExtent = DirectMappedExtent;
break;
}
if ((Rate.fragmentSize.width >= BestMatchedExtent.width && Rate.fragmentSize.width <= DirectMappedExtent.width && Rate.fragmentSize.height <= DirectMappedExtent.height && Rate.fragmentSize.height >= BestMatchedExtent.height) ||
(Rate.fragmentSize.height >= BestMatchedExtent.height && Rate.fragmentSize.height <= DirectMappedExtent.height && Rate.fragmentSize.width <= DirectMappedExtent.width && Rate.fragmentSize.width >= BestMatchedExtent.width))
{
BestMatchedExtent = Rate.fragmentSize;
}
}
}
return BestMatchedExtent;
}
void FVulkanPhysicalDeviceFeatures::Query(VkPhysicalDevice PhysicalDevice, uint32 APIVersion)
{
VkPhysicalDeviceFeatures2 PhysicalDeviceFeatures2;
ZeroVulkanStruct(PhysicalDeviceFeatures2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2);
PhysicalDeviceFeatures2.pNext = &Core_1_1;
Core_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
if (APIVersion >= VK_API_VERSION_1_2)
{
Core_1_1.pNext = &Core_1_2;
Core_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
}
if (APIVersion >= VK_API_VERSION_1_3)
{
Core_1_2.pNext = &Core_1_3;
Core_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
}
VulkanRHI::vkGetPhysicalDeviceFeatures2(PhysicalDevice, &PhysicalDeviceFeatures2);
// Copy features into old struct for convenience
Core_1_0 = PhysicalDeviceFeatures2.features;
// Apply config modifications
Core_1_0.robustBufferAccess = GCVarRobustBufferAccess.GetValueOnAnyThread() > 0 ? VK_TRUE : VK_FALSE;
// Apply platform restrictions
FVulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(this);
}
FVulkanDevice::FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu)
: Device(VK_NULL_HANDLE)
, MemoryManager(this)
, DeferredDeletionQueue(this)
, Gpu(InGpu)
, ImmediateContext(nullptr)
, PipelineStateCache(nullptr)
{
RHI = InRHI;
FMemory::Memzero(Queues);
FMemory::Memzero(GpuProps);
FMemory::Memzero(FormatProperties);
FMemory::Memzero(PixelFormatComponentMapping);
ZeroVulkanStruct(GpuIdProps, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR);
ZeroVulkanStruct(GpuSubgroupProps, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES);
{
VkPhysicalDeviceProperties2KHR PhysicalDeviceProperties2;
ZeroVulkanStruct(PhysicalDeviceProperties2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR);
PhysicalDeviceProperties2.pNext = &GpuIdProps;
GpuIdProps.pNext = &GpuSubgroupProps;
VulkanRHI::vkGetPhysicalDeviceProperties2(Gpu, &PhysicalDeviceProperties2);
GpuProps = PhysicalDeviceProperties2.properties;
}
// First get the VendorId. We'll have to get properties again after finding out which extensions we want to use
VendorId = RHIConvertToGpuVendorId(GpuProps.vendorID);
UE_LOG(LogVulkanRHI, Display, TEXT("- DeviceName: %s"), ANSI_TO_TCHAR(GpuProps.deviceName));
UE_LOG(LogVulkanRHI, Display, TEXT("- API=%d.%d.%d (0x%x) Driver=0x%x VendorId=0x%x"), VK_VERSION_MAJOR(GpuProps.apiVersion), VK_VERSION_MINOR(GpuProps.apiVersion), VK_VERSION_PATCH(GpuProps.apiVersion), GpuProps.apiVersion, GpuProps.driverVersion, GpuProps.vendorID);
UE_LOG(LogVulkanRHI, Display, TEXT("- DeviceID=0x%x Type=%s"), GpuProps.deviceID, VK_TYPE_TO_STRING(VkPhysicalDeviceType, GpuProps.deviceType));
UE_LOG(LogVulkanRHI, Display, TEXT("- Max Descriptor Sets Bound %d"), GpuProps.limits.maxBoundDescriptorSets);
UE_LOG(LogVulkanRHI, Display, TEXT("- Timestamps: ComputeAndGraphics=%d Domain=%s Period=%f"),
GpuProps.limits.timestampComputeAndGraphics, VK_TYPE_TO_STRING(VkTimeDomainKHR, FVulkanPlatform::GetTimeDomain()), GpuProps.limits.timestampPeriod);
ensureMsgf(VendorId != EGpuVendorId::Unknown, TEXT("Unknown vendor ID 0x%x"), GpuProps.vendorID);
}
FVulkanDevice::~FVulkanDevice()
{
if (Device != VK_NULL_HANDLE)
{
Destroy();
Device = VK_NULL_HANDLE;
}
}
static inline FString GetQueueInfoString(const VkQueueFamilyProperties& Props)
{
FString Info;
if ((Props.queueFlags & VK_QUEUE_GRAPHICS_BIT) == VK_QUEUE_GRAPHICS_BIT)
{
Info += TEXT(" Gfx");
}
if ((Props.queueFlags & VK_QUEUE_COMPUTE_BIT) == VK_QUEUE_COMPUTE_BIT)
{
Info += TEXT(" Compute");
}
if ((Props.queueFlags & VK_QUEUE_TRANSFER_BIT) == VK_QUEUE_TRANSFER_BIT)
{
Info += TEXT(" Xfer");
}
if ((Props.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) == VK_QUEUE_SPARSE_BINDING_BIT)
{
Info += TEXT(" Sparse");
}
return Info;
};
void FVulkanDevice::CreateDevice(TArray<const ANSICHAR*>& DeviceLayers, FVulkanDeviceExtensionArray& UEExtensions)
{
LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanMisc);
check(Device == VK_NULL_HANDLE);
// Setup extension and layer info
VkDeviceCreateInfo DeviceInfo;
ZeroVulkanStruct(DeviceInfo, VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
DeviceInfo.pEnabledFeatures = &PhysicalDeviceFeatures.Core_1_0;
for (TUniquePtr<FVulkanDeviceExtension>& UEExtension : UEExtensions)
{
if (UEExtension->InUse())
{
DeviceExtensions.Add(UEExtension->GetExtensionName());
UEExtension->PreCreateDevice(DeviceInfo);
}
}
DeviceInfo.enabledExtensionCount = DeviceExtensions.Num();
DeviceInfo.ppEnabledExtensionNames = DeviceExtensions.GetData();
DeviceInfo.enabledLayerCount = DeviceLayers.Num();
DeviceInfo.ppEnabledLayerNames = (DeviceInfo.enabledLayerCount > 0) ? DeviceLayers.GetData() : nullptr;
// Setup Queue info
TArray<VkDeviceQueueCreateInfo> QueueFamilyInfos;
int32 GfxQueueFamilyIndex = -1;
int32 ComputeQueueFamilyIndex = -1;
int32 TransferQueueFamilyIndex = -1;
UE_LOG(LogVulkanRHI, Display, TEXT("Found %d Queue Families"), QueueFamilyProps.Num());
uint32 NumPriorities = 0;
for (int32 FamilyIndex = 0; FamilyIndex < QueueFamilyProps.Num(); ++FamilyIndex)
{
const VkQueueFamilyProperties& CurrProps = QueueFamilyProps[FamilyIndex];
bool bIsValidQueue = false;
if (VKHasAllFlags(CurrProps.queueFlags, VK_QUEUE_GRAPHICS_BIT))
{
if (GfxQueueFamilyIndex == -1)
{
GfxQueueFamilyIndex = FamilyIndex;
bIsValidQueue = true;
}
else
{
//#todo-rco: Support for multi-queue/choose the best queue!
}
}
if (VKHasAllFlags(CurrProps.queueFlags, VK_QUEUE_COMPUTE_BIT))
{
// Allocate a queue for async compute if:
// - async compute queue hasn't been found already
// - cvars allow for a dedicated async compute queue
// - a new family index is available
// - Sync2 is available
if ((ComputeQueueFamilyIndex == -1) &&
(GRHIAllowAsyncComputeCvar.GetValueOnAnyThread() != 0 || GAllowPresentOnComputeQueue.GetValueOnAnyThread() != 0) &&
(GfxQueueFamilyIndex != FamilyIndex) &&
SupportsParallelRendering())
{
ComputeQueueFamilyIndex = FamilyIndex;
bIsValidQueue = true;
}
}
if (VKHasAllFlags(CurrProps.queueFlags, VK_QUEUE_TRANSFER_BIT))
{
// Prefer a non-gfx transfer queue
if ((TransferQueueFamilyIndex == -1) &&
!VKHasAnyFlags(CurrProps.queueFlags, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT) &&
(GRHIAllowTransferQueueCvar.GetValueOnAnyThread() != 0) &&
SupportsParallelRendering())
{
TransferQueueFamilyIndex = FamilyIndex;
bIsValidQueue = true;
}
}
if (!bIsValidQueue)
{
UE_LOG(LogVulkanRHI, Display, TEXT("Skipping unnecessary Queue Family %d: %d queues%s"), FamilyIndex, CurrProps.queueCount, *GetQueueInfoString(CurrProps));
continue;
}
VkDeviceQueueCreateInfo& CurrQueue = QueueFamilyInfos.AddZeroed_GetRef();
CurrQueue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
CurrQueue.queueFamilyIndex = FamilyIndex;
CurrQueue.queueCount = CurrProps.queueCount;
NumPriorities += CurrProps.queueCount;
UE_LOG(LogVulkanRHI, Display, TEXT("Initializing Queue Family %d: %d queues%s"), FamilyIndex, CurrProps.queueCount, *GetQueueInfoString(CurrProps));
}
TArray<float> QueuePriorities;
QueuePriorities.AddUninitialized(NumPriorities);
float* CurrentPriority = QueuePriorities.GetData();
for (int32 Index = 0; Index < QueueFamilyInfos.Num(); ++Index)
{
VkDeviceQueueCreateInfo& CurrQueue = QueueFamilyInfos[Index];
CurrQueue.pQueuePriorities = CurrentPriority;
const VkQueueFamilyProperties& CurrProps = QueueFamilyProps[CurrQueue.queueFamilyIndex];
for (int32 QueueIndex = 0; QueueIndex < (int32)CurrProps.queueCount; ++QueueIndex)
{
*CurrentPriority++ = 1.0f;
}
}
DeviceInfo.queueCreateInfoCount = QueueFamilyInfos.Num();
DeviceInfo.pQueueCreateInfos = QueueFamilyInfos.GetData();
// Create the device
VkResult Result = VulkanRHI::vkCreateDevice(Gpu, &DeviceInfo, VULKAN_CPU_ALLOCATOR, &Device);
if (Result == VK_ERROR_INITIALIZATION_FAILED)
{
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, TEXT("Cannot create a Vulkan device. Try updating your video driver to a more recent version.\n"), TEXT("Vulkan device creation failed"));
FPlatformMisc::RequestExitWithStatus(true, 1);
}
VERIFYVULKANRESULT_EXPANDED(Result);
FVulkanPlatform::NotifyFoundDeviceLayersAndExtensions(Gpu, DeviceLayers, DeviceExtensions);
ActiveQueueFamilies.Reserve((int32)EVulkanQueueType::Count);
const uint32 NumBits = QueueFamilyProps[GfxQueueFamilyIndex].timestampValidBits;
UE_LOG(LogVulkanRHI, Display, TEXT("TimestampValidBits=%u"), NumBits);
// Create Graphics Queue, here we submit command buffers for execution
Queues[(int32)EVulkanQueueType::Graphics] = new FVulkanQueue(*this, GfxQueueFamilyIndex, EVulkanQueueType::Graphics);
ActiveQueueFamilies.Add(GfxQueueFamilyIndex);
if (ComputeQueueFamilyIndex == -1)
{
// If we didn't find a dedicated Queue, leave it null
Queues[(int32)EVulkanQueueType::AsyncCompute] = nullptr;
}
else
{
ensure(NumBits == QueueFamilyProps[ComputeQueueFamilyIndex].timestampValidBits);
Queues[(int32)EVulkanQueueType::AsyncCompute] = new FVulkanQueue(*this, ComputeQueueFamilyIndex, EVulkanQueueType::AsyncCompute);
ActiveQueueFamilies.Add(ComputeQueueFamilyIndex);
}
if (TransferQueueFamilyIndex == -1)
{
// If we didn't find a dedicated Queue, leave it null
Queues[(int32)EVulkanQueueType::Transfer] = nullptr;
}
else
{
Queues[(int32)EVulkanQueueType::Transfer] = new FVulkanQueue(*this, TransferQueueFamilyIndex, EVulkanQueueType::Transfer);
ActiveQueueFamilies.Add(TransferQueueFamilyIndex);
}
// Enumerate the available shading rates
if (OptionalDeviceExtensions.HasKHRFragmentShadingRate)
{
uint32 FragmentShadingRateCount = 0;
VulkanRHI::vkGetPhysicalDeviceFragmentShadingRatesKHR(Gpu, &FragmentShadingRateCount, nullptr);
if (FragmentShadingRateCount != 0)
{
FragmentShadingRates.SetNum(FragmentShadingRateCount);
for (uint32 i = 0; i < FragmentShadingRateCount; ++i)
{
ZeroVulkanStruct(FragmentShadingRates[i], VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR);
}
VulkanRHI::vkGetPhysicalDeviceFragmentShadingRatesKHR(Gpu, &FragmentShadingRateCount, FragmentShadingRates.GetData());
// Build a map from EVRSShadingRate to fragment size
for (uint32 ShadingRate = 0u; ShadingRate < (uint32)FragmentSizeMap.Num(); ++ShadingRate)
{
FragmentSizeMap[ShadingRate] = GetBestMatchedShadingRateExtents(ShadingRate, FragmentShadingRates);
}
}
}
UE_LOG(LogVulkanRHI, Display, TEXT("Using %d device layers%s"), DeviceLayers.Num(), DeviceLayers.Num() ? TEXT(":") : TEXT("."));
for (const ANSICHAR* Layer : DeviceLayers)
{
UE_LOG(LogVulkanRHI, Display, TEXT("* %s"), ANSI_TO_TCHAR(Layer));
}
UE_LOG(LogVulkanRHI, Display, TEXT("Using %d device extensions:"), DeviceExtensions.Num());
for (const ANSICHAR* Extension : DeviceExtensions)
{
UE_LOG(LogVulkanRHI, Display, TEXT("* %s"), ANSI_TO_TCHAR(Extension));
}
GVulkanDelayAcquireImage = DelayAcquireBackBuffer();
SetupDrawMarkers();
}
#if VULKAN_ENABLE_DRAW_MARKERS
TAutoConsoleVariable<int32> CVarVulkanDebugMarkers(
TEXT("r.Vulkan.DebugMarkers"),
4,
TEXT("0 to disable all debug markers\n")
TEXT("1 to enable debug names for resources\n")
TEXT("2 to enable debug labels for commands\n")
TEXT("3 to enable debug resource names command labels\n")
TEXT("4 to automatically enable markers depending on tool detection (default)\n"),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
#endif // VULKAN_ENABLE_DRAW_MARKERS
void FVulkanDevice::SetupDrawMarkers()
{
#if VULKAN_ENABLE_DRAW_MARKERS
const bool bGPUCrashDebugging = UE::RHI::UseGPUCrashDebugging();
bool bTraceToolFound = bGPUCrashDebugging;
#if VULKAN_HAS_DEBUGGING_ENABLED
bTraceToolFound |= GRenderDocFound;
#endif // VULKAN_HAS_DEBUGGING_ENABLED
if (RHI->SupportsDebugUtilsExt() || bUseLegacyDebugMarkerExt)
{
bool bUseLabel = bGPUCrashDebugging;
bool bUseName = false;
if (OptionalDeviceExtensions.HasEXTToolingInfo)
{
uint32_t ToolCount = 0;
VulkanRHI::vkGetPhysicalDeviceToolPropertiesEXT(Gpu, &ToolCount, nullptr);
TArray<VkPhysicalDeviceToolPropertiesEXT> ToolProperties;
ToolProperties.SetNumUninitialized(ToolCount);
for (VkPhysicalDeviceToolPropertiesEXT& Tool : ToolProperties)
{
ZeroVulkanStruct(Tool, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES);
}
VulkanRHI::vkGetPhysicalDeviceToolPropertiesEXT(Gpu, &ToolCount, ToolProperties.GetData());
for (VkPhysicalDeviceToolPropertiesEXT const& Tool : ToolProperties)
{
if (Tool.purposes & VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT)
{
bUseName = true;
if (Tool.purposes & VK_TOOL_PURPOSE_TRACING_BIT_EXT)
{
bUseLabel = true;
}
}
if (Tool.purposes & VK_TOOL_PURPOSE_TRACING_BIT_EXT)
{
bTraceToolFound = true;
}
UE_LOG(LogVulkanRHI, Display, TEXT("Tool \"%s\" version %s PurposeFlags=0x%x"), ANSI_TO_TCHAR(Tool.name), ANSI_TO_TCHAR(Tool.version), Tool.purposes);
}
}
const int VulkanDebugMarkers = FParse::Param(FCommandLine::Get(), TEXT("forcevulkandrawmarkers")) ? 3 : CVarVulkanDebugMarkers.GetValueOnRenderThread();
if (VulkanDebugMarkers != 4)
{
bUseLabel = bUseLabel || (VulkanDebugMarkers & 2);
bUseName = bUseName || (VulkanDebugMarkers & 1);
}
if (bUseLegacyDebugMarkerExt)
{
if (bUseLabel)
{
static PFN_vkCmdDebugMarkerBeginEXT LegacyCmdBeginDebugLabel;
static PFN_vkCmdDebugMarkerEndEXT LegacyCmdEndDebugLabel;
LegacyCmdBeginDebugLabel = (PFN_vkCmdDebugMarkerBeginEXT)(void*)VulkanRHI::vkGetDeviceProcAddr(Device, "vkCmdDebugMarkerBeginEXT");
LegacyCmdEndDebugLabel = (PFN_vkCmdDebugMarkerEndEXT)(void*)VulkanRHI::vkGetDeviceProcAddr(Device, "vkCmdDebugMarkerEndEXT");
if (LegacyCmdBeginDebugLabel && LegacyCmdEndDebugLabel)
{
struct Wrap
{
static VKAPI_ATTR void VKAPI_CALL CmdBeginDebugLabel(VkCommandBuffer CommandBuffer, const VkDebugUtilsLabelEXT* LabelInfo)
{
VkDebugMarkerMarkerInfoEXT MarkerInfo{ VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT };
MarkerInfo.pMarkerName = LabelInfo->pLabelName;
memcpy(MarkerInfo.color, LabelInfo->color, sizeof(LabelInfo->color));
LegacyCmdBeginDebugLabel(CommandBuffer, &MarkerInfo);
}
static VKAPI_ATTR void VKAPI_CALL CmdEndDebugLabel(VkCommandBuffer CommandBuffer)
{
LegacyCmdEndDebugLabel(CommandBuffer);
}
};
DebugMarkers.CmdBeginDebugLabel = &Wrap::CmdBeginDebugLabel;
DebugMarkers.CmdEndDebugLabel = &Wrap::CmdEndDebugLabel;
}
}
if (bUseName)
{
static PFN_vkDebugMarkerSetObjectNameEXT LegacySetObjectName;
LegacySetObjectName = (PFN_vkDebugMarkerSetObjectNameEXT)(void*)VulkanRHI::vkGetDeviceProcAddr(Device, "vkDebugMarkerSetObjectNameEXT");
if (LegacySetObjectName)
{
struct Wrap
{
static VKAPI_ATTR VkResult VKAPI_CALL SetObjectName(VkDevice Device, const VkDebugUtilsObjectNameInfoEXT* NameInfo)
{
if (NameInfo->objectType <= 25)
{
VkDebugMarkerObjectNameInfoEXT MarkerInfo{ VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT };
MarkerInfo.pObjectName = NameInfo->pObjectName;
MarkerInfo.object = NameInfo->objectHandle;
MarkerInfo.objectType = static_cast<VkDebugReportObjectTypeEXT>(NameInfo->objectType);
LegacySetObjectName(Device, &MarkerInfo);
}
return VK_SUCCESS;
}
};
DebugMarkers.SetDebugName = &Wrap::SetObjectName;
}
}
}
else
{
if (bUseLabel)
{
DebugMarkers.CmdBeginDebugLabel = (PFN_vkCmdBeginDebugUtilsLabelEXT)(void*)VulkanRHI::vkGetInstanceProcAddr(RHI->GetInstance(), "vkCmdBeginDebugUtilsLabelEXT");
DebugMarkers.CmdEndDebugLabel = (PFN_vkCmdEndDebugUtilsLabelEXT)(void*)VulkanRHI::vkGetInstanceProcAddr(RHI->GetInstance(), "vkCmdEndDebugUtilsLabelEXT");
}
if (bUseName)
{
DebugMarkers.SetDebugName = (PFN_vkSetDebugUtilsObjectNameEXT)(void*)VulkanRHI::vkGetInstanceProcAddr(RHI->GetInstance(), "vkSetDebugUtilsObjectNameEXT");
}
}
UE_LOG(LogVulkanRHI, Display, TEXT("Vulkan debug markers support: resource names %s, begin/end labels %s."), bUseName ? TEXT("enabled") : TEXT("disabled"), bUseLabel ? TEXT("enabled") : TEXT("disabled"));
}
#if VULKAN_HAS_DEBUGGING_ENABLED
if (DebugMarkers.CmdBeginDebugLabel && DebugMarkers.CmdEndDebugLabel && bTraceToolFound)
{
// We're running under RenderDoc or other trace tool, so enable capturing mode
FDynamicRHI::EnableIdealGPUCaptureOptions(true);
}
else if(bTraceToolFound)
{
UE_LOG(LogVulkanRHI, Warning, TEXT("Vulkan API trace tool detected but not running in ideal GPU capture mode."));
}
#endif // VULKAN_HAS_DEBUGGING_ENABLED
#endif // VULKAN_ENABLE_DRAW_MARKERS
#if VULKAN_ENABLE_DUMP_LAYER
FDynamicRHI::EnableIdealGPUCaptureOptions(true);
#endif
}
void FVulkanDevice::SetupFormats()
{
for (uint32 Index = 0; Index < VK_FORMAT_RANGE_SIZE; ++Index)
{
const VkFormat Format = (VkFormat)Index;
FMemory::Memzero(FormatProperties[Index]);
VulkanRHI::vkGetPhysicalDeviceFormatProperties(Gpu, Format, &FormatProperties[Index]);
}
static_assert(sizeof(VkFormat) <= sizeof(GPixelFormats[0].PlatformFormat), "PlatformFormat must be increased!");
// Create shortcuts for the possible component mappings
const VkComponentMapping ComponentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
const VkComponentMapping ComponentMappingRGB1 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE };
const VkComponentMapping ComponentMappingRG01 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };
const VkComponentMapping ComponentMappingR001 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };
const VkComponentMapping ComponentMappingRIII = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
const VkComponentMapping ComponentMapping000R = { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R };
const VkComponentMapping ComponentMappingR000 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO };
const VkComponentMapping ComponentMappingRR01 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };
// Initialize the platform pixel format map.
for (int32 Index = 0; Index < PF_MAX; ++Index)
{
GPixelFormats[Index].PlatformFormat = VK_FORMAT_UNDEFINED;
GPixelFormats[Index].Supported = false;
GVulkanBufferFormat[Index] = VK_FORMAT_UNDEFINED;
// Set default component mapping
PixelFormatComponentMapping[Index] = ComponentMappingRGBA;
}
const EPixelFormatCapabilities ColorRenderTargetRequiredCapabilities = (EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::RenderTarget);
// Default formats
MapFormatSupport(PF_B8G8R8A8, { VK_FORMAT_B8G8R8A8_UNORM }, ComponentMappingRGBA);
MapFormatSupport(PF_G8, { VK_FORMAT_R8_UNORM }, ComponentMappingR001);
MapFormatSupport(PF_FloatRGB, { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGB1, ColorRenderTargetRequiredCapabilities);
MapFormatSupport(PF_FloatRGBA, { VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA, 8);
MapFormatSupport(PF_ShadowDepth, { VK_FORMAT_D16_UNORM }, ComponentMappingRIII);
MapFormatSupport(PF_G32R32F, { VK_FORMAT_R32G32_SFLOAT }, ComponentMappingRG01, 8); // Requirement for GPU particles
MapFormatSupport(PF_A32B32G32R32F, { VK_FORMAT_R32G32B32A32_SFLOAT }, ComponentMappingRGBA, 16);
MapFormatSupport(PF_G16R16, { VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
MapFormatSupport(PF_G16R16F, { VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
MapFormatSupport(PF_G16R16F_FILTER, { VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
MapFormatSupport(PF_R16_UINT, { VK_FORMAT_R16_UINT }, ComponentMappingR001);
MapFormatSupport(PF_R16_SINT, { VK_FORMAT_R16_SINT }, ComponentMappingR001);
MapFormatSupport(PF_R32_UINT, { VK_FORMAT_R32_UINT }, ComponentMappingR001);
MapFormatSupport(PF_R32_SINT, { VK_FORMAT_R32_SINT }, ComponentMappingR001);
MapFormatSupport(PF_R8_UINT, { VK_FORMAT_R8_UINT }, ComponentMappingR001);
MapFormatSupport(PF_D24, { VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT }, ComponentMappingR000);
MapFormatSupport(PF_R16F, { VK_FORMAT_R16_SFLOAT }, ComponentMappingR001);
MapFormatSupport(PF_R16F_FILTER, { VK_FORMAT_R16_SFLOAT }, ComponentMappingR001);
MapFormatSupport(PF_FloatR11G11B10, { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGB1, ColorRenderTargetRequiredCapabilities);
MapFormatSupport(PF_A2B10G10R10, { VK_FORMAT_A2B10G10R10_UNORM_PACK32 }, ComponentMappingRGBA, 4);
MapFormatSupport(PF_A16B16G16R16, { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA, 8);
MapFormatSupport(PF_A8, { VK_FORMAT_R8_UNORM }, ComponentMapping000R);
MapFormatSupport(PF_R5G6B5_UNORM, { VK_FORMAT_R5G6B5_UNORM_PACK16 }, ComponentMappingRGBA);
MapFormatSupport(PF_B5G5R5A1_UNORM, { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_R5G5B5A1_UNORM_PACK16, VK_FORMAT_B8G8R8A8_UNORM }, ComponentMappingRGBA);
MapFormatSupport(PF_R8G8B8A8, { VK_FORMAT_R8G8B8A8_UNORM }, ComponentMappingRGBA);
MapFormatSupport(PF_R8G8B8A8_UINT, { VK_FORMAT_R8G8B8A8_UINT }, ComponentMappingRGBA);
MapFormatSupport(PF_R8G8B8A8_SNORM, { VK_FORMAT_R8G8B8A8_SNORM }, ComponentMappingRGBA);
MapFormatSupport(PF_R16G16_UINT, { VK_FORMAT_R16G16_UINT }, ComponentMappingRG01);
MapFormatSupport(PF_R16G16_SINT, { VK_FORMAT_R16G16_SINT }, ComponentMappingRG01);
MapFormatSupport(PF_R16G16B16A16_UINT, { VK_FORMAT_R16G16B16A16_UINT }, ComponentMappingRGBA);
MapFormatSupport(PF_R16G16B16A16_SINT, { VK_FORMAT_R16G16B16A16_SINT }, ComponentMappingRGBA);
MapFormatSupport(PF_R32G32_UINT, { VK_FORMAT_R32G32_UINT }, ComponentMappingRG01);
MapFormatSupport(PF_R32G32B32A32_UINT, { VK_FORMAT_R32G32B32A32_UINT }, ComponentMappingRGBA);
MapFormatSupport(PF_R16G16B16A16_SNORM, { VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA);
MapFormatSupport(PF_R16G16B16A16_UNORM, { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA);
MapFormatSupport(PF_R8G8, { VK_FORMAT_R8G8_UNORM }, ComponentMappingRG01);
MapFormatSupport(PF_V8U8, { VK_FORMAT_R8G8_UNORM }, ComponentMappingRG01);
MapFormatSupport(PF_R32_FLOAT, { VK_FORMAT_R32_SFLOAT }, ComponentMappingR001);
MapFormatSupport(PF_R8, { VK_FORMAT_R8_UNORM }, ComponentMappingR001);
MapFormatSupport(PF_G16R16_SNORM, { VK_FORMAT_R16G16_SNORM }, ComponentMappingRG01);
MapFormatSupport(PF_R8G8_UINT, { VK_FORMAT_R8G8_UINT }, ComponentMappingRG01);
MapFormatSupport(PF_R32G32B32_UINT, { VK_FORMAT_R32G32B32_UINT }, ComponentMappingRGB1);
MapFormatSupport(PF_R32G32B32_SINT, { VK_FORMAT_R32G32B32_SINT }, ComponentMappingRGB1);
MapFormatSupport(PF_R32G32B32F, { VK_FORMAT_R32G32B32_SFLOAT }, ComponentMappingRGB1);
MapFormatSupport(PF_R8_SINT, { VK_FORMAT_R8_SINT }, ComponentMappingR001);
MapFormatSupport(PF_R8G8B8, { VK_FORMAT_R8G8B8_UNORM }, ComponentMappingRGB1, ColorRenderTargetRequiredCapabilities);
// This will be the format used for 64bit image atomics
// This format is SM5 only, skip it for mobile to not confuse QA with a logged error about missing pixel format
if (GMaxRHIFeatureLevel >= ERHIFeatureLevel::SM5)
{
#if VULKAN_HAS_DEBUGGING_ENABLED
const EPixelFormatCapabilities RequiredCaps64U = GRenderDocFound ? EPixelFormatCapabilities::UAV : (EPixelFormatCapabilities::UAV | EPixelFormatCapabilities::TextureAtomics);
#else
const EPixelFormatCapabilities RequiredCaps64U = (EPixelFormatCapabilities::UAV | EPixelFormatCapabilities::TextureAtomics);
#endif
MapFormatSupport(PF_R64_UINT, { VK_FORMAT_R64_UINT, VK_FORMAT_R32G32_UINT }, ComponentMappingR001, RequiredCaps64U);
// Shaders were patched to use UAV, make sure we don't expose texture sampling
GPixelFormats[PF_R64_UINT].Capabilities &= ~(EPixelFormatCapabilities::AnyTexture | EPixelFormatCapabilities::TextureSample);
if (GRHISupportsAtomicUInt64 && !EnumHasAnyFlags(GPixelFormats[PF_R64_UINT].Capabilities, EPixelFormatCapabilities::UAV))
{
UE_LOG(LogVulkanRHI, Warning, TEXT("64bit image atomics were enabled, but the R64 format does not have UAV capabilities. Disabling support."));
GRHISupportsAtomicUInt64 = false;
}
}
if (CVarVulkanUseD24.GetValueOnAnyThread() != 0)
{
// prefer VK_FORMAT_D24_UNORM_S8_UINT
MapFormatSupport(PF_DepthStencil, { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRIII);
MapFormatSupport(PF_X24_G8, { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRR01);
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = true;
}
else
{
// prefer VK_FORMAT_D32_SFLOAT_S8_UINT
MapFormatSupport(PF_DepthStencil, { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRIII);
MapFormatSupport(PF_X24_G8, { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRR01);
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = false;
}
if (FVulkanPlatform::SupportsBCTextureFormats())
{
MapFormatSupport(PF_DXT1, { VK_FORMAT_BC1_RGB_UNORM_BLOCK }, ComponentMappingRGB1); // Also what OpenGL expects (RGBA instead RGB, but not SRGB)
MapFormatSupport(PF_DXT3, { VK_FORMAT_BC2_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_DXT5, { VK_FORMAT_BC3_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_BC4, { VK_FORMAT_BC4_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_BC5, { VK_FORMAT_BC5_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_BC6H, { VK_FORMAT_BC6H_UFLOAT_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_BC7, { VK_FORMAT_BC7_UNORM_BLOCK }, ComponentMappingRGBA);
}
if (FVulkanPlatform::SupportsASTCTextureFormats())
{
MapFormatSupport(PF_ASTC_4x4, { VK_FORMAT_ASTC_4x4_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_6x6, { VK_FORMAT_ASTC_6x6_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_8x8, { VK_FORMAT_ASTC_8x8_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_10x10, { VK_FORMAT_ASTC_10x10_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_12x12, { VK_FORMAT_ASTC_12x12_UNORM_BLOCK }, ComponentMappingRGBA);
}
if (FVulkanPlatform::SupportsETC2TextureFormats())
{
MapFormatSupport(PF_ETC2_RGB, { VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK }, ComponentMappingRGB1);
MapFormatSupport(PF_ETC2_RGBA, { VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK }, ComponentMappingRGBA);
MapFormatSupport(PF_ETC2_R11_EAC, { VK_FORMAT_EAC_R11_UNORM_BLOCK }, ComponentMappingR001);
MapFormatSupport(PF_ETC2_RG11_EAC, { VK_FORMAT_EAC_R11G11_UNORM_BLOCK }, ComponentMappingRG01);
}
if (FVulkanPlatform::SupportsR16UnormTextureFormat())
{
MapFormatSupport(PF_G16, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_SFLOAT }, ComponentMappingR001);
}
else
{
MapFormatSupport(PF_G16, { VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_UNORM }, ComponentMappingR001);
}
if (GetOptionalExtensions().HasEXTTextureCompressionASTCHDR)
{
MapFormatSupport(PF_ASTC_4x4_HDR, { VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_6x6_HDR, { VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_8x8_HDR, { VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_10x10_HDR, { VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT }, ComponentMappingRGBA);
MapFormatSupport(PF_ASTC_12x12_HDR, { VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT }, ComponentMappingRGBA);
}
// When this extension is available, PF_Unknown texture can have an external buffer attached which has an
// internal format, that can be sampled. If it really can be sampled depends on the VK_IMAGE_USAGE_SAMPLED_BIT
if (GetOptionalExtensions().HasANDROIDExternalMemoryHardwareBuffer)
{
GPixelFormats[PF_Unknown].Capabilities |= EPixelFormatCapabilities::TextureSample;
}
// Verify available Vertex Formats
{
static_assert(VET_None == 0, "Change loop below to skip VET_None");
for (int32 VETIndex = (int32)VET_None + 1; VETIndex < VET_MAX; ++VETIndex)
{
const EVertexElementType UEType = (EVertexElementType)VETIndex;
const VkFormat VulkanFormat = UEToVkBufferFormat(UEType);
const VkFormatProperties& VertexFormatProperties = GetFormatProperties(VulkanFormat);
if (VertexFormatProperties.bufferFeatures == 0)
{
UE_LOG(LogVulkanRHI, Warning, TEXT("EVertexElementType(%d) is not supported with VkFormat %d"), (int32)UEType, (int32)VulkanFormat);
}
}
}
// Verify the potential SRGB formats and fill GVulkanSRGBFormat
{
auto GetSRGBMapping = [this](const VkFormat InFormat)
{
VkFormat SRGBFormat = InFormat;
switch (InFormat)
{
case VK_FORMAT_B8G8R8A8_UNORM: SRGBFormat = VK_FORMAT_B8G8R8A8_SRGB; break;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: SRGBFormat = VK_FORMAT_A8B8G8R8_SRGB_PACK32; break;
case VK_FORMAT_R8_UNORM: SRGBFormat = ((GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1) ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_SRGB); break;
case VK_FORMAT_R8G8_UNORM: SRGBFormat = VK_FORMAT_R8G8_SRGB; break;
case VK_FORMAT_R8G8B8_UNORM: SRGBFormat = VK_FORMAT_R8G8B8_SRGB; break;
case VK_FORMAT_R8G8B8A8_UNORM: SRGBFormat = VK_FORMAT_R8G8B8A8_SRGB; break;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK: SRGBFormat = VK_FORMAT_BC1_RGB_SRGB_BLOCK; break;
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: SRGBFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; break;
case VK_FORMAT_BC2_UNORM_BLOCK: SRGBFormat = VK_FORMAT_BC2_SRGB_BLOCK; break;
case VK_FORMAT_BC3_UNORM_BLOCK: SRGBFormat = VK_FORMAT_BC3_SRGB_BLOCK; break;
case VK_FORMAT_BC7_UNORM_BLOCK: SRGBFormat = VK_FORMAT_BC7_SRGB_BLOCK; break;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; break;
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; break;
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_4x4_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_5x4_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_5x5_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_6x5_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_6x6_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_8x5_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_8x6_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_8x8_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_10x5_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_10x6_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_10x8_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_10x10_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_12x10_SRGB_BLOCK; break;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: SRGBFormat = VK_FORMAT_ASTC_12x12_SRGB_BLOCK; break;
// case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: Format = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; break;
// case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: Format = VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; break;
// case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: Format = VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG; break;
// case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: Format = VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG; break;
default: break;
}
// If we're introducing a new format, make sure it's supported
if (InFormat != SRGBFormat)
{
const VkFormatProperties& SRGBFormatProperties = GetFormatProperties(SRGBFormat);
if (!VKHasAnyFlags(SRGBFormatProperties.optimalTilingFeatures, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
{
// If we can't even sample from it, then reject the suggested SRGB format
SRGBFormat = InFormat;
}
}
return SRGBFormat;
};
for (int32 PixelFormatIndex = 0; PixelFormatIndex < PF_MAX; ++PixelFormatIndex)
{
const FPixelFormatInfo& PixelFormatInfo = GPixelFormats[PixelFormatIndex];
if (PixelFormatInfo.Supported)
{
const VkFormat OriginalFormat = (VkFormat)PixelFormatInfo.PlatformFormat;
GVulkanSRGBFormat[PixelFormatIndex] = GetSRGBMapping(OriginalFormat);
}
else
{
GVulkanSRGBFormat[PixelFormatIndex] = VK_FORMAT_UNDEFINED;
}
}
}
#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
// Print the resulting pixel format support
if (FParse::Param(FCommandLine::Get(), TEXT("PrintVulkanPixelFormatMappings")))
{
auto GetFormatCapabilities = [](EPixelFormatCapabilities FormatCapabilities)
{
#define VULKAN_CHECK_FORMAT_CAPABILITY(PF_Name) if (EnumHasAllFlags(FormatCapabilities, EPixelFormatCapabilities::PF_Name)) { CapabilitiesString += TEXT(#PF_Name) TEXT(", ");}
FString CapabilitiesString;
VULKAN_CHECK_FORMAT_CAPABILITY(TextureSample);
VULKAN_CHECK_FORMAT_CAPABILITY(TextureCube);
VULKAN_CHECK_FORMAT_CAPABILITY(RenderTarget);
VULKAN_CHECK_FORMAT_CAPABILITY(DepthStencil);
VULKAN_CHECK_FORMAT_CAPABILITY(TextureBlendable);
VULKAN_CHECK_FORMAT_CAPABILITY(TextureAtomics);
VULKAN_CHECK_FORMAT_CAPABILITY(Buffer);
VULKAN_CHECK_FORMAT_CAPABILITY(VertexBuffer);
VULKAN_CHECK_FORMAT_CAPABILITY(IndexBuffer);
VULKAN_CHECK_FORMAT_CAPABILITY(BufferAtomics);
VULKAN_CHECK_FORMAT_CAPABILITY(UAV);
return CapabilitiesString;
#undef VULKAN_CHECK_FORMAT_CAPABILITY
};
UE_LOG(LogVulkanRHI, Warning, TEXT("Pixel Format Mappings for Vulkan:"));
UE_LOG(LogVulkanRHI, Warning, TEXT("%24s | %24s | BlockBytes | Components | ComponentMapping | BufferFormat | Capabilities | SRGBFormat"),
TEXT("PixelFormatName"), TEXT("VulkanFormat"));
for (int32 PixelFormatIndex = 0; PixelFormatIndex < PF_MAX; ++PixelFormatIndex)
{
if (GPixelFormats[PixelFormatIndex].Supported)
{
const VkComponentMapping& ComponentMapping = PixelFormatComponentMapping[PixelFormatIndex];
const VkFormat VulkanFormat = (VkFormat)GPixelFormats[PixelFormatIndex].PlatformFormat;
FString VulkanFormatStr(VK_TYPE_TO_STRING(VkFormat, VulkanFormat));
VulkanFormatStr.RightChopInline(10); // Chop the VK_FORMAT_
FString SRGBFormat;
if (VulkanFormat != GVulkanSRGBFormat[PixelFormatIndex])
{
SRGBFormat = VK_TYPE_TO_STRING(VkFormat, GVulkanSRGBFormat[PixelFormatIndex]);
SRGBFormat.RightChopInline(10); // Chop the VK_FORMAT_
}
UE_LOG(LogVulkanRHI, Warning, TEXT("%24s | %24s | %10d | %10d | %10d,%d,%d,%d | %12d | 0x%08X | %s"),
GPixelFormats[PixelFormatIndex].Name,
*VulkanFormatStr,
GPixelFormats[PixelFormatIndex].BlockBytes,
GPixelFormats[PixelFormatIndex].NumComponents,
ComponentMapping.r, ComponentMapping.g, ComponentMapping.b, ComponentMapping.a,
(int32)GVulkanBufferFormat[PixelFormatIndex],
(uint32)GPixelFormats[PixelFormatIndex].Capabilities,
*SRGBFormat
);
}
}
UE_LOG(LogVulkanRHI, Warning, TEXT("Pixel Format Capabilities for Vulkan:"));
for (int32 PixelFormatIndex = 0; PixelFormatIndex < PF_MAX; ++PixelFormatIndex)
{
if (GPixelFormats[PixelFormatIndex].Supported)
{
const FString CapabilitiesString = GetFormatCapabilities(GPixelFormats[PixelFormatIndex].Capabilities);
UE_LOG(LogVulkanRHI, Warning, TEXT("%24s : %s"), GPixelFormats[PixelFormatIndex].Name, *CapabilitiesString);
}
}
}
#endif // UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
}
const VkFormatProperties& FVulkanDevice::GetFormatProperties(VkFormat InFormat) const
{
if (InFormat >= 0 && InFormat < VK_FORMAT_RANGE_SIZE)
{
return FormatProperties[InFormat];
}
// Check for extension formats
const VkFormatProperties* FoundProperties = ExtensionFormatProperties.Find(InFormat);
if (FoundProperties)
{
return *FoundProperties;
}
// Add it for faster caching next time
VkFormatProperties& NewProperties = ExtensionFormatProperties.Add(InFormat);
FMemory::Memzero(NewProperties);
VulkanRHI::vkGetPhysicalDeviceFormatProperties(Gpu, InFormat, &NewProperties);
return NewProperties;
}
void FVulkanDevice::MapBufferFormatSupport(FPixelFormatInfo& PixelFormatInfo, EPixelFormat UEFormat, VkFormat VulkanFormat)
{
check(GVulkanBufferFormat[UEFormat] == VK_FORMAT_UNDEFINED);
const VkFormatProperties& LocalFormatProperties = GetFormatProperties(VulkanFormat);
EPixelFormatCapabilities Capabilities = EPixelFormatCapabilities::None;
auto ConvertBufferCap = [&Capabilities, &LocalFormatProperties](EPixelFormatCapabilities UnrealCap, VkFormatFeatureFlags InFlag)
{
const bool HasBufferFeature = VKHasAllFlags(LocalFormatProperties.bufferFeatures, InFlag);
if (HasBufferFeature)
{
EnumAddFlags(Capabilities, UnrealCap);
}
// Make sure we aren't looking in the wrong place for a bit
check(!VKHasAnyFlags(LocalFormatProperties.linearTilingFeatures, InFlag));
check(!VKHasAnyFlags(LocalFormatProperties.optimalTilingFeatures, InFlag));
};
// Check for buffer caps, use the first one with any caps
if (LocalFormatProperties.bufferFeatures != 0)
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::Buffer);
ConvertBufferCap(EPixelFormatCapabilities::VertexBuffer, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
ConvertBufferCap(EPixelFormatCapabilities::BufferLoad, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT);
ConvertBufferCap(EPixelFormatCapabilities::BufferStore, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT);
ConvertBufferCap(EPixelFormatCapabilities::BufferAtomics, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT);
// Vulkan index buffers aren't tied to formats, so any 16 or 32 bit UINT format with a single component will do...
// But because we can't check for uint vs float, hardcode supported formats for now
if (EnumHasAllFlags(Capabilities, (EPixelFormatCapabilities::BufferLoad | EPixelFormatCapabilities::BufferStore)) &&
((VulkanFormat == VK_FORMAT_R16_UINT) || (VulkanFormat == VK_FORMAT_R32_UINT)))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::IndexBuffer);
}
GVulkanBufferFormat[UEFormat] = VulkanFormat;
PixelFormatInfo.Capabilities |= Capabilities;
}
}
void FVulkanDevice::MapImageFormatSupport(FPixelFormatInfo& PixelFormatInfo, const TArrayView<const VkFormat>& PrioritizedFormats, EPixelFormatCapabilities RequiredCapabilities)
{
// Query for MipMap support with typical parameters
auto SupportsMipMap = [this](VkFormat InFormat)
{
VkImageFormatProperties ImageFormatProperties;
VkResult RetVal = VulkanRHI::vkGetPhysicalDeviceImageFormatProperties(Gpu, InFormat, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, 0, &ImageFormatProperties);
return (RetVal == VK_SUCCESS) && (ImageFormatProperties.maxMipLevels > 1);
};
EPixelFormatCapabilities Capabilities = EPixelFormatCapabilities::None;
auto ConvertImageCap = [&Capabilities](const VkFormatProperties& InFormatProperties, EPixelFormatCapabilities UnrealCap, VkFormatFeatureFlags InFlag, bool bOnlyOptimalTiling)
{
// Do not distinguish between Linear and Optimal for now.
bool HasImageFeature = VKHasAllFlags(InFormatProperties.optimalTilingFeatures, InFlag);
if (!bOnlyOptimalTiling)
{
HasImageFeature |= VKHasAllFlags(InFormatProperties.linearTilingFeatures, InFlag);
}
if (HasImageFeature)
{
EnumAddFlags(Capabilities, UnrealCap);
}
// Make sure we aren't looking in the wrong place for a bit
check(!VKHasAnyFlags(InFormatProperties.bufferFeatures, InFlag));
};
// Go through the PrioritizedFormats and use the first one that meets RequiredCapabilities
for (int32 FormatIndex = 0; FormatIndex < PrioritizedFormats.Num(); ++FormatIndex)
{
Capabilities = EPixelFormatCapabilities::None;
const VkFormat VulkanFormat = PrioritizedFormats[FormatIndex];
const VkFormatProperties& LocalFormatProperties = GetFormatProperties(VulkanFormat);
// Check for individual texture caps
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::AnyTexture | EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::TextureLoad, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, false);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::DepthStencil, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, true);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::RenderTarget, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, true);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureBlendable, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT, true);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::AllUAVFlags | EPixelFormatCapabilities::TextureStore, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, false);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureAtomics, VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT, false);
ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureFilterable, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, false);
if (EnumHasAllFlags(Capabilities, EPixelFormatCapabilities::AnyTexture))
{
// We support gather, but some of our shaders assume offsets so check against features
if (GetPhysicalDeviceFeatures().Core_1_0.shaderImageGatherExtended)
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureGather);
}
if (SupportsMipMap(VulkanFormat))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureMipmaps);
}
if (OptionalDeviceExtensions.HasEXTImageCompressionControl)
{
VkImageCompressionPropertiesEXT ImageCompressionProperties{ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT };
VkImageFormatProperties2 ImageFormatProperties{ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2 };
ImageFormatProperties.pNext = &ImageCompressionProperties;
VkImageCompressionControlEXT CompressionControl{ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT };
CompressionControl.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
VkPhysicalDeviceImageFormatInfo2 ImageFormatInfo{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 };
ImageFormatInfo.pNext = &CompressionControl;
ImageFormatInfo.format = VulkanFormat;
ImageFormatInfo.type = VK_IMAGE_TYPE_2D;
ImageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
ImageFormatInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (VulkanRHI::vkGetPhysicalDeviceImageFormatProperties2(Gpu, &ImageFormatInfo, &ImageFormatProperties) == VK_SUCCESS &&
(ImageCompressionProperties.imageCompressionFlags & VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT) != 0)
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::LossyCompressible);
};
}
}
if (EnumHasAllFlags(Capabilities, RequiredCapabilities))
{
PixelFormatInfo.PlatformFormat = VulkanFormat;
PixelFormatInfo.Capabilities |= Capabilities;
if (FormatIndex > 0)
{
UE_LOG(LogVulkanRHI, Display, TEXT("MapImageFormatSupport: %s is not supported with VkFormat %d, falling back to VkFormat %d"), PixelFormatInfo.Name, (int32)PrioritizedFormats[0], (int32)PrioritizedFormats[FormatIndex]);
}
break;
}
}
}
// Minimum capabilities required for a Vulkan format to be considered as supported
static constexpr EPixelFormatCapabilities kDefaultTextureCapabilities = EPixelFormatCapabilities::TextureSample;
// Passthrough to specify we want to keep the initial BlockBytes value set in the PixelFormat
static constexpr int32 kDefaultBlockBytes = -1;
void FVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> InPrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities, int32 BlockBytes)
{
TArrayView<const VkFormat> PrioritizedFormats = MakeArrayView(InPrioritizedFormats);
FPixelFormatInfo& PixelFormatInfo = GPixelFormats[UEFormat];
check(PrioritizedFormats.Num() > 0);
check(!PixelFormatInfo.Supported);
check(PixelFormatInfo.Capabilities == EPixelFormatCapabilities::None);
MapBufferFormatSupport(PixelFormatInfo, UEFormat, PrioritizedFormats[0]);
MapImageFormatSupport(PixelFormatInfo, PrioritizedFormats, RequiredCapabilities);
// Flag the pixel format as supported if we can do anything with it
PixelFormatInfo.Supported = EnumHasAllFlags(PixelFormatInfo.Capabilities, RequiredCapabilities) || EnumHasAnyFlags(PixelFormatInfo.Capabilities, EPixelFormatCapabilities::Buffer);
if (PixelFormatInfo.Supported)
{
PixelFormatComponentMapping[UEFormat] = ComponentMapping;
if (BlockBytes > 0)
{
PixelFormatInfo.BlockBytes = BlockBytes;
}
}
else
{
UE_LOG(LogVulkanRHI, Error, TEXT("MapFormatSupport: %s is not supported with VkFormat %d"), PixelFormatInfo.Name, (int32)PrioritizedFormats[0]);
}
}
void FVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping)
{
MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, kDefaultTextureCapabilities, kDefaultBlockBytes);
}
void FVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, int32 BlockBytes)
{
MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, kDefaultTextureCapabilities, BlockBytes);
}
void FVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities)
{
MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, RequiredCapabilities, kDefaultBlockBytes);
}
bool FVulkanDevice::SupportsBindless() const
{
checkSlow(BindlessDescriptorManager != nullptr);
return BindlessDescriptorManager->IsSupported();
}
void FVulkanDevice::ChooseVariableRateShadingMethod()
{
auto IsFragmentShadingRateAvailable = [](VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures)
{
return FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE;
};
auto IsFragmentDensityMapAvailable = [](FOptionalVulkanDeviceExtensions& ExtensionFlags)
{
return ExtensionFlags.HasEXTFragmentDensityMap;
};
auto TurnOffFragmentShadingRate = [](VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures)
{
FragmentShadingRateFeatures.primitiveFragmentShadingRate = VK_FALSE;
FragmentShadingRateFeatures.attachmentFragmentShadingRate = VK_FALSE;
FragmentShadingRateFeatures.pipelineFragmentShadingRate = VK_FALSE;
GRHISupportsPipelineVariableRateShading = false;
GRHISupportsLargerVariableRateShadingSizes = false;
};
auto TurnOffFragmentDensityMap = [](FOptionalVulkanDeviceExtensions& ExtensionFlags, VkPhysicalDeviceFragmentDensityMapFeaturesEXT& FragmentDensityMapFeatures, VkPhysicalDeviceFragmentDensityMap2FeaturesEXT& FragmentDensityMap2Features)
{
ExtensionFlags.HasEXTFragmentDensityMap = 0;
FragmentDensityMapFeatures.fragmentDensityMap = VK_FALSE;
FragmentDensityMapFeatures.fragmentDensityMapDynamic = VK_FALSE;
FragmentDensityMapFeatures.fragmentDensityMapNonSubsampledImages = VK_FALSE;
ExtensionFlags.HasEXTFragmentDensityMap2 = 0;
FragmentDensityMap2Features.fragmentDensityMapDeferred = VK_FALSE;
};
int32 VRSFormatPreference = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Vulkan.VRSFormat"))->GetValueOnAnyThread();
UE_LOG(LogVulkanRHI, Display, TEXT("Vulkan Variable Rate Shading choice: %d."), VRSFormatPreference);
// If both FSR and FDM are available we turn off the one that we're not using to prevent Vulkan validation layers warnings.
if (IsFragmentDensityMapAvailable(OptionalDeviceExtensions) && IsFragmentShadingRateAvailable(OptionalDeviceExtensionProperties.FragmentShadingRateFeatures))
{
if (VRSFormatPreference <= (uint8)EVulkanVariableRateShadingPreference::RequireFSR)
{
TurnOffFragmentDensityMap(OptionalDeviceExtensions, OptionalDeviceExtensionProperties.FragmentDensityMapFeatures, OptionalDeviceExtensionProperties.FragmentDensityMap2Features);
}
else
{
TurnOffFragmentShadingRate(OptionalDeviceExtensionProperties.FragmentShadingRateFeatures);
}
return;
}
// When only FSR is available.
if (IsFragmentShadingRateAvailable(OptionalDeviceExtensionProperties.FragmentShadingRateFeatures))
{
if (VRSFormatPreference == (uint8)EVulkanVariableRateShadingPreference::UseFDMOnlyIfAvailable)
{
UE_LOG(LogVulkanRHI, Display, TEXT("Fragment Density Map was requested but is not available."));
}
else if (VRSFormatPreference == (uint8)EVulkanVariableRateShadingPreference::RequireFDM)
{
UE_LOG(LogVulkanRHI, Error, TEXT("Fragment Density Map was required but is not available."));
}
TurnOffFragmentDensityMap(OptionalDeviceExtensions, OptionalDeviceExtensionProperties.FragmentDensityMapFeatures, OptionalDeviceExtensionProperties.FragmentDensityMap2Features);
}
// When only FDM is available.
if (IsFragmentDensityMapAvailable(OptionalDeviceExtensions))
{
if (VRSFormatPreference == (uint8)EVulkanVariableRateShadingPreference::UseFSROnlyIfAvailable)
{
UE_LOG(LogVulkanRHI, Display, TEXT("Fragment Shading Rate was requested but is not available."));
}
else if (VRSFormatPreference == (uint8)EVulkanVariableRateShadingPreference::RequireFSR)
{
UE_LOG(LogVulkanRHI, Error, TEXT("Fragment Shading Rate was required but is not available."));
}
TurnOffFragmentShadingRate(OptionalDeviceExtensionProperties.FragmentShadingRateFeatures);
}
}
void FVulkanDevice::InitGPU()
{
LLM_SCOPE_VULKAN(ELLMTagVulkan::VulkanMisc);
uint32 QueueCount = 0;
VulkanRHI::vkGetPhysicalDeviceQueueFamilyProperties(Gpu, &QueueCount, nullptr);
check(QueueCount >= 1);
QueueFamilyProps.AddUninitialized(QueueCount);
VulkanRHI::vkGetPhysicalDeviceQueueFamilyProperties(Gpu, &QueueCount, QueueFamilyProps.GetData());
// Query base features
PhysicalDeviceFeatures.Query(Gpu, RHI->GetApiVersion());
// Setup layers and extensions
FVulkanDeviceExtensionArray UEExtensions = FVulkanDeviceExtension::GetUESupportedDeviceExtensions(this, RHI->GetApiVersion());
TArray<const ANSICHAR*> DeviceLayers = SetupDeviceLayers(UEExtensions);
// Query advanced features
{
VkPhysicalDeviceFeatures2 PhysicalDeviceFeatures2;
ZeroVulkanStruct(PhysicalDeviceFeatures2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2);
for (TUniquePtr<FVulkanDeviceExtension>& UEExtension : UEExtensions)
{
if (UEExtension->InUse())
{
UEExtension->PrePhysicalDeviceFeatures(PhysicalDeviceFeatures2);
}
}
VulkanRHI::vkGetPhysicalDeviceFeatures2(Gpu, &PhysicalDeviceFeatures2);
for (TUniquePtr<FVulkanDeviceExtension>& UEExtension : UEExtensions)
{
if (UEExtension->InUse())
{
UEExtension->PostPhysicalDeviceFeatures(OptionalDeviceExtensions);
}
}
}
// Query advances properties
{
VkPhysicalDeviceProperties2 PhysicalDeviceProperties2;
ZeroVulkanStruct(PhysicalDeviceProperties2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2);
PhysicalDeviceProperties2.pNext = &GpuIdProps;
ZeroVulkanStruct(GpuIdProps, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES);
for (TUniquePtr<FVulkanDeviceExtension>& UEExtension : UEExtensions)
{
if (UEExtension->InUse())
{
UEExtension->PrePhysicalDeviceProperties(PhysicalDeviceProperties2);
}
}
VulkanRHI::vkGetPhysicalDeviceProperties2(Gpu, &PhysicalDeviceProperties2);
for (TUniquePtr<FVulkanDeviceExtension>& UEExtension : UEExtensions)
{
if (UEExtension->InUse())
{
UEExtension->PostPhysicalDeviceProperties();
}
}
}
ChooseVariableRateShadingMethod();
UE_LOG(LogVulkanRHI, Display, TEXT("Device properties: Geometry %d BufferAtomic64 %d ImageAtomic64 %d"),
PhysicalDeviceFeatures.Core_1_0.geometryShader, OptionalDeviceExtensions.HasKHRShaderAtomicInt64, OptionalDeviceExtensions.HasImageAtomicInt64);
CreateDevice(DeviceLayers, UEExtensions);
FVulkanPlatform::InitDevice(this);
SetupFormats();
DeviceMemoryManager.Init(this);
MemoryManager.Init();
FenceManager.Init(this);
StagingManager.Init(this);
// TempBlockAllocator is currently used for UB uploads and copies
{
uint32 BlockAlignment = FMath::Max<uint32>(GetLimits().minUniformBufferOffsetAlignment, 16u);
VkBufferUsageFlags BufferUsageFlags =
(GetOptionalExtensions().HasBufferDeviceAddress ? VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT : 0) |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
if (GetOptionalExtensions().HasRayTracingPipeline)
{
BufferUsageFlags |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR;
BlockAlignment = FMath::Max<uint32>(GetOptionalExtensionProperties().RayTracingPipelineProps.shaderGroupBaseAlignment, BlockAlignment);
}
const int32 VulkanTempBlockSize = GCVarVulkanTempBlockSizeKB.GetValueOnAnyThread() * 1024;
TempBlockAllocator = new VulkanRHI::FTempBlockAllocator(this, VulkanTempBlockSize, BlockAlignment, BufferUsageFlags);
}
#if VULKAN_SUPPORTS_GPU_CRASH_DUMPS
if (UE::RHI::UseGPUCrashDebugging())
{
VkBufferCreateInfo CreateInfo;
ZeroVulkanStruct(CreateInfo, VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO);
CreateInfo.size = GMaxCrashBufferEntries * sizeof(uint32_t);
CreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VERIFYVULKANRESULT(VulkanRHI::vkCreateBuffer(Device, &CreateInfo, VULKAN_CPU_ALLOCATOR, &CrashMarker.Buffer));
VkMemoryRequirements MemReq;
FMemory::Memzero(MemReq);
VulkanRHI::vkGetBufferMemoryRequirements(Device, CrashMarker.Buffer, &MemReq);
CrashMarker.Allocation = DeviceMemoryManager.Alloc(false, CreateInfo.size, MemReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, nullptr, VULKAN_MEMORY_MEDIUM_PRIORITY, false, __FILE__, __LINE__);
uint32* Entry = (uint32*)CrashMarker.Allocation->Map(VK_WHOLE_SIZE, 0);
check(Entry);
// Start with 0 entries
*Entry = 0;
VERIFYVULKANRESULT(VulkanRHI::vkBindBufferMemory(Device, CrashMarker.Buffer, CrashMarker.Allocation->GetHandle(), 0));
}
#endif
RenderPassManager = new FVulkanRenderPassManager(*this);
if (UseVulkanDescriptorCache())
{
DescriptorSetCache = new FVulkanDescriptorSetCache(this);
}
DescriptorPoolsManager = new FVulkanDescriptorPoolsManager();
DescriptorPoolsManager->Init(this);
BindlessDescriptorManager = new FVulkanBindlessDescriptorManager(*this);
BindlessDescriptorManager->Init();
InitGlobalSamplers();
PipelineStateCache = new FVulkanPipelineStateCacheManager(this);
TArray<FString> CacheFilenames = FVulkanPlatform::GetPSOCacheFilenames();
// always look in the saved directory (for the cache from previous run that wasn't moved over to stage directory)
CacheFilenames.Add(VulkanRHI::GetPipelineCacheFilename());
ImmediateContext = new FVulkanCommandListContextImmediate(*this);
#if VULKAN_SUPPORTS_VALIDATION_CACHE
if (OptionalDeviceExtensions.HasEXTValidationCache)
{
LoadValidationCache(Device, ValidationCache);
}
#endif
FVulkanChunkedPipelineCacheManager::Init();
PipelineStateCache->InitAndLoad(CacheFilenames);
if (RHISupportsRayTracing(GMaxRHIShaderPlatform) && GetOptionalExtensions().HasRaytracingExtensions())
{
check(RayTracingCompactionRequestHandler == nullptr);
RayTracingCompactionRequestHandler = new FVulkanRayTracingCompactionRequestHandler(this);
}
FVulkanPlatform::PostInitGPU(*this);
}
void FVulkanDevice::PrepareForDestroy()
{
WaitUntilIdle();
}
void FVulkanDevice::Destroy()
{
#if VULKAN_SUPPORTS_VALIDATION_CACHE
if (ValidationCache != VK_NULL_HANDLE)
{
PFN_vkDestroyValidationCacheEXT vkDestroyValidationCache = (PFN_vkDestroyValidationCacheEXT)(void*)VulkanRHI::vkGetDeviceProcAddr(Device, "vkDestroyValidationCacheEXT");
if (vkDestroyValidationCache)
{
vkDestroyValidationCache(Device, ValidationCache, VULKAN_CPU_ALLOCATOR);
ValidationCache = VK_NULL_HANDLE;
}
}
#endif
// Release pending state that might hold references to RHI resources before we do final FlushPendingDeletes
ImmediateContext->ReleasePendingState();
if (TransientHeapCache)
{
delete TransientHeapCache;
TransientHeapCache = nullptr;
}
// Flush all pending deletes before destroying the device and any Vulkan context objects.
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
delete DescriptorSetCache;
DescriptorSetCache = nullptr;
delete DescriptorPoolsManager;
DescriptorPoolsManager = nullptr;
delete ImmediateContext;
ImmediateContext = nullptr;
delete RenderPassManager;
RenderPassManager = nullptr;
for (TArray<FVulkanQueryPool*>& PoolArray : FreeQueryPools)
{
for (FVulkanQueryPool* Pool : PoolArray)
{
delete Pool;
}
PoolArray.Empty();
}
delete PipelineStateCache;
PipelineStateCache = nullptr;
StagingManager.Deinit();
#if VULKAN_SUPPORTS_GPU_CRASH_DUMPS
if (UE::RHI::UseGPUCrashDebugging())
{
if (CrashMarker.Buffer != VK_NULL_HANDLE)
{
VulkanRHI::vkDestroyBuffer(Device, CrashMarker.Buffer, VULKAN_CPU_ALLOCATOR);
CrashMarker.Buffer = VK_NULL_HANDLE;
}
if (CrashMarker.Allocation)
{
CrashMarker.Allocation->Unmap();
DeviceMemoryManager.Free(CrashMarker.Allocation);
}
}
#endif // VULKAN_SUPPORTS_GPU_CRASH_DUMPS
DeferredDeletionQueue.Clear();
BindlessDescriptorManager->Deinit();
delete BindlessDescriptorManager;
BindlessDescriptorManager = nullptr;
FVulkanUploadContext::DestroyPool();
for (VkEvent Event : BarrierEvents)
{
VulkanRHI::vkDestroyEvent(Device, Event, VULKAN_CPU_ALLOCATOR);
}
BarrierEvents.Empty();
delete TempBlockAllocator;
TempBlockAllocator = nullptr;
MemoryManager.Deinit();
for (int32 QueueIndex = 0; QueueIndex < (int32)EVulkanQueueType::Count; ++QueueIndex)
{
delete Queues[QueueIndex];
Queues[QueueIndex] = nullptr;
}
FenceManager.Deinit();
DeviceMemoryManager.Deinit();
FVulkanChunkedPipelineCacheManager::Shutdown();
VulkanRHI::vkDestroyDevice(Device, VULKAN_CPU_ALLOCATOR);
Device = VK_NULL_HANDLE;
}
void FVulkanDevice::WaitUntilIdle()
{
RHI->RHIBlockUntilGPUIdle();
VERIFYVULKANRESULT(VulkanRHI::vkDeviceWaitIdle(Device));
}
const VkComponentMapping& FVulkanDevice::GetFormatComponentMapping(EPixelFormat UEFormat) const
{
check(GPixelFormats[UEFormat].Supported);
return PixelFormatComponentMapping[UEFormat];
}
void FVulkanDevice::NotifyDeletedImage(VkImage Image, bool bRenderTarget)
{
if (bRenderTarget)
{
// Contexts first, as it may clear the current framebuffer
GetImmediateContext().NotifyDeletedRenderTarget(Image);
// Delete framebuffers using this image
GetRenderPassManager().NotifyDeletedRenderTarget(Image);
}
}
void FVulkanDevice::NotifyDeletedGfxPipeline(class FVulkanRHIGraphicsPipelineState* Pipeline)
{
//#todo-rco: Loop through all contexts!
if (ImmediateContext && ImmediateContext->PendingGfxState)
{
ImmediateContext->PendingGfxState->NotifyDeletedPipeline(Pipeline);
}
}
void FVulkanDevice::NotifyDeletedComputePipeline(class FVulkanComputePipeline* Pipeline)
{
//#todo-rco: Loop through all contexts!
if (ImmediateContext && ImmediateContext->PendingComputeState)
{
ImmediateContext->PendingComputeState->NotifyDeletedPipeline(Pipeline);
}
if (PipelineStateCache)
{
PipelineStateCache->NotifyDeletedComputePipeline(Pipeline);
}
}
void FVulkanDevice::VulkanSetObjectName(VkObjectType Type, uint64_t Handle, const TCHAR* Name)
{
#if VULKAN_ENABLE_DRAW_MARKERS
if(DebugMarkers.SetDebugName)
{
FTCHARToUTF8 Converter(Name);
VkDebugUtilsObjectNameInfoEXT Info;
ZeroVulkanStruct(Info, VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT);
Info.objectType = Type;
Info.objectHandle = Handle;
Info.pObjectName = Converter.Get();
DebugMarkers.SetDebugName(Device, &Info);
}
#endif // VULKAN_ENABLE_DRAW_MARKERS
}
FVulkanTransientHeapCache& FVulkanDevice::GetOrCreateTransientHeapCache()
{
if (!TransientHeapCache)
{
TransientHeapCache = FVulkanTransientHeapCache::Create(this);
}
return *TransientHeapCache;
}
#if RHI_NEW_GPU_PROFILER
void FVulkanDevice::GetCalibrationTimestamp(FVulkanTiming& InOutTiming)
{
// TimestampPeriod is the number of nanoseconds required for a timestamp query to be incremented by 1
InOutTiming.GPUFrequency = (uint64)((1000.0 * 1000.0 * 1000.0) / (double)GetDeviceProperties().limits.timestampPeriod);
InOutTiming.CPUFrequency = uint64(1.0 / FPlatformTime::GetSecondsPerCycle64());
if (GetOptionalExtensions().HasEXTCalibratedTimestamps)
{
uint64_t Timestamps[2] = { 0, 0 };
uint64_t MaxDeviations[2] = { 0, 0 };
VkCalibratedTimestampInfoKHR CalibratedTimestampInfo[2];
ZeroVulkanStruct(CalibratedTimestampInfo[0], VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_KHR);
ZeroVulkanStruct(CalibratedTimestampInfo[1], VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_KHR);
CalibratedTimestampInfo[0].timeDomain = VK_TIME_DOMAIN_DEVICE_KHR;
CalibratedTimestampInfo[1].timeDomain = FVulkanPlatform::GetTimeDomain();
const bool bHasHostTimeDomain = (CalibratedTimestampInfo[1].timeDomain != VK_TIME_DOMAIN_DEVICE_KHR);
const uint32 TimestampCount = bHasHostTimeDomain ? 2 : 1;
VERIFYVULKANRESULT(VulkanDynamicAPI::vkGetCalibratedTimestampsEXT(GetInstanceHandle(), TimestampCount, CalibratedTimestampInfo, Timestamps, MaxDeviations));
InOutTiming.GPUTimestamp = Timestamps[0];
switch (CalibratedTimestampInfo[1].timeDomain)
{
case VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR:
// Divide to match values returned by Cycles64() (to be moved to FVulkanPlatform)
#if PLATFORM_ANDROID
InOutTiming.CPUTimestamp = Timestamps[1] / 1000ULL; // Android Cycle64 divides by 1000
#else
InOutTiming.CPUTimestamp = Timestamps[1] / 100ULL; // Linux Cycle64 divides by 100
#endif
break;
case VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_KHR:
InOutTiming.CPUTimestamp = Timestamps[1];
break;
default:
InOutTiming.CPUTimestamp = FPlatformTime::Cycles64();
break;
}
checkf(InOutTiming.CPUTimestamp <= FPlatformTime::Cycles64(), TEXT("New calibration timestamp (%llu) ahead of current time (%llu)."), InOutTiming.CPUTimestamp, FPlatformTime::Cycles64());
}
else
{
InOutTiming.GPUTimestamp = 0;
InOutTiming.CPUTimestamp = 0;
}
}
#else
FGPUTimingCalibrationTimestamp FVulkanDevice::GetCalibrationTimestamp()
{
auto ToMicroseconds = [](uint64_t Timestamp)
{
const double Frequency = double(FVulkanGPUTiming::GetTimingFrequency());
uint64 Microseconds = (uint64)((double(Timestamp) / Frequency) * 1000.0 * 1000.0);
return Microseconds;
};
FGPUTimingCalibrationTimestamp CalibrationTimestamp;
if (OptionalDeviceExtensions.HasEXTCalibratedTimestamps)
{
VkCalibratedTimestampInfoEXT TimestampInfo;
ZeroVulkanStruct(TimestampInfo, VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT);
TimestampInfo.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
uint64_t GPUTimestamp = 0;
uint64_t MaxDeviation = 0;
VERIFYVULKANRESULT(VulkanRHI::vkGetCalibratedTimestampsEXT(Device, 1, &TimestampInfo, &GPUTimestamp, &MaxDeviation));
CalibrationTimestamp.GPUMicroseconds = ToMicroseconds(GPUTimestamp);
const uint64 CPUTimestamp = FPlatformTime::Cycles64();
CalibrationTimestamp.CPUMicroseconds = uint64(FPlatformTime::ToSeconds64(CPUTimestamp) * 1e6);
}
return CalibrationTimestamp;
}
#endif // (RHI_NEW_GPU_PROFILER == 0)
void FVulkanDevice::ForEachQueue(TFunctionRef<void(FVulkanQueue&)> Callback)
{
static_assert((int32)EVulkanQueueType::Count == 3u, "New entries in EVulkanQueueType must be added in FVulkanDevice::ForEachQueue.");
Callback(*GetGraphicsQueue());
if (HasAsyncComputeQueue())
{
Callback(*GetComputeQueue());
}
if (HasTransferQueue())
{
Callback(*GetTransferQueue());
}
}
void FVulkanDevice::InitGlobalSamplers()
{
checkf(SamplerMap.Num() == 0, TEXT("Global Samplers should be the first samplers created."));
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::PointClampedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Point, AM_Clamp, AM_Clamp, AM_Clamp)).GetReference());
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::PointWrappedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap)).GetReference());
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::BilinearClampedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp)).GetReference());
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::BilinearWrappedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap)).GetReference());
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::TrilinearClampedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp)).GetReference());
GlobalSamplers[(uint32)FVulkanShaderHeader::EGlobalSamplerType::TrilinearWrappedSampler] =
ResourceCast(RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Trilinear, AM_Wrap, AM_Wrap, AM_Wrap)).GetReference());
static_assert((uint32)FVulkanShaderHeader::EGlobalSamplerType::Count == 6, "Need to add new global sampler decl!");
// Make sure if bindless handles are being generated that these samplers are the very first
check(!GlobalSamplers[0]->GetBindlessHandle().IsValid() || GlobalSamplers[0]->GetBindlessHandle().GetIndex() == 1);
}
VkBuffer FVulkanDevice::CreateBuffer(VkDeviceSize BufferSize, VkBufferUsageFlags BufferUsageFlags, VkBufferCreateFlags BufferCreateFlags) const
{
VkBufferCreateInfo BufferCreateInfo;
BufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
BufferCreateInfo.pNext = nullptr;
BufferCreateInfo.size = BufferSize;
BufferCreateInfo.usage = BufferUsageFlags;
BufferCreateInfo.flags = BufferCreateFlags;
// For descriptors buffers
if (GetOptionalExtensions().HasBufferDeviceAddress)
{
BufferCreateInfo.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
if (HasMultipleQueues() && GVulkanAllowConcurrentBuffer)
{
BufferCreateInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
BufferCreateInfo.queueFamilyIndexCount = ActiveQueueFamilies.Num();
BufferCreateInfo.pQueueFamilyIndices = (uint32_t*)ActiveQueueFamilies.GetData();
}
else
{
BufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
BufferCreateInfo.queueFamilyIndexCount = 0;
BufferCreateInfo.pQueueFamilyIndices = nullptr;
}
VkBuffer BufferHandle = VK_NULL_HANDLE;
VERIFYVULKANRESULT(VulkanRHI::vkCreateBuffer(Device, &BufferCreateInfo, VULKAN_CPU_ALLOCATOR, &BufferHandle));
return BufferHandle;
}
VkEvent FVulkanDevice::GetBarrierEvent()
{
check(SupportsParallelRendering());
VkEvent Handle = VK_NULL_HANDLE;
// Check if we already have one
{
FScopeLock Lock(&BarrierEventLock);
if (BarrierEvents.Num())
{
Handle = BarrierEvents.Pop(EAllowShrinking::No);
}
}
// Create a new handle if none were available
if (!Handle)
{
VkEventCreateInfo Info;
ZeroVulkanStruct(Info, VK_STRUCTURE_TYPE_EVENT_CREATE_INFO);
Info.flags = VK_EVENT_CREATE_DEVICE_ONLY_BIT;
VERIFYVULKANRESULT(VulkanRHI::vkCreateEvent(Device, &Info, VULKAN_CPU_ALLOCATOR, &Handle));
}
return Handle;
}
void FVulkanDevice::ReleaseBarrierEvent(VkEvent Handle)
{
FScopeLock Lock(&BarrierEventLock);
BarrierEvents.Add(Handle);
}