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

2105 lines
84 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VulkanExtensions.h"
#include "VulkanDevice.h"
#include "VulkanRHIPrivate.h"
#include "IHeadMountedDisplayModule.h"
#include "IHeadMountedDisplayVulkanExtensions.h"
#include "Misc/CommandLine.h"
#include "RHICore.h"
#include "RHICoreNvidiaAftermath.h"
// ADDING A NEW EXTENSION:
//
// A - If the extension simply needs to be queried for driver support and added at device creation (and set a flag):
// Add an FVulkanDeviceExtension/FVulkanInstanceExtension directly in the array returned by GetUESupportedDeviceExtensions/GetUESupportedInstanceExtensions.
//
// B - The extension requires the use of specialized Feature and/or Property structures, or other extended processing:
// * Add a [instance/device] class for your extension, try to call it FVulkan[ExtensionNameInCamelCaps]Extension so we have consistency
// * Feed it the extension name string, use the SDK's define if possible : VK_[BLA_BLA_BLA]_EXTENSION_NAME
// * If there is a VULKAN_SUPPORTS_* define that enables/disables your extension in code, provide it so that the extension knows if it should be used or not.
// We don't IFDEF the extensions code so that we still know of its existence and we can report warnings if someone tries to use a disabled extension.
// If there is no define to enable/disable the extension in code, then simply use VULKAN_EXTENSION_ENABLED.
//
// Tips:
// - Feature and Property structures specific to extensions that won't be needed beyond init should be included in the extension's class instead of the device.
// - To add engine support for a complex extension, but require some kind of external activation (eg plugin) you can use its EExtensionActivation state (see header definition).
// - If an extension is supported on multiple platforms, it may be cleaner to include it here and simply disable its VULKAN_SUPPORTS_* value in the Vulkan platform header where it's not supported.
TAutoConsoleVariable<int32> GRHIAllow64bitShaderAtomicsCvar(
TEXT("r.Vulkan.Allow64bitShaderAtomics"),
1,
TEXT("Whether to enable 64bit buffer/image atomics required by Nanite\n")
TEXT("0 to disable 64bit atomics\n")
TEXT("1 to enable (default)"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GRHIAllow16bitOps(
TEXT("r.Vulkan.Allow16bitOps"),
1,
TEXT("Whether to enable 16bit ops to speeds up TSR\n")
TEXT("0 to disable\n")
TEXT("1 to enable (default)"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GVulkanRayTracingCVar(
TEXT("r.Vulkan.RayTracing"),
1,
TEXT("0: Do not enable Vulkan ray tracing extensions\n")
TEXT("1: Enable experimental ray tracing support (default)"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GVulkanAllowHostQueryResetCVar(
TEXT("r.Vulkan.AllowHostQueryReset"),
1,
TEXT("0: Do not enable support for Host Query Reset extension\n")
TEXT("1: Enable Host Query Reset (default)"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GVulkanAllowSync2BarriersCVar(
TEXT("r.Vulkan.AllowSynchronization2"),
1,
TEXT("Enables the use of advanced barriers that combine the use of the VK_KHR_separate_depth_stencil_layouts \n")
TEXT("and VK_KHR_synchronization2 to reduce the reliance on layout tracking (except for defragging).\n")
TEXT("This is necessary in order to support parallel command buffer generation.\n")
TEXT("0: Do not enable support for sync2 barriers.\n")
TEXT("1: Enable sync2 barriers (default)"),
ECVF_ReadOnly
);
TAutoConsoleVariable<int32> GVulkanVariableRateShadingFormatCVar(
TEXT("r.Vulkan.VRSFormat"),
0,
TEXT("Allows to choose the preferred Variable Rate Shading option. \n")
TEXT("0: Prefer Fragment Shading Rate if both Fragment Shading Rate and Fragment Density Map are available.\n")
TEXT("1: Use Fragment Shading Rate if available. A message will be reported if not available. \n")
TEXT("2: Require Fragment Shading Rate. Will generate an error if the extension is not available. \n")
TEXT("3: Prefer Fragment Density Map if both Fragment Shading Rate and Fragment Density Map are available.\n")
TEXT("4: Use Fragment Density Map if available. A message will be reported if not available.\n")
TEXT("5: Require Fragment Density Map. Will generate an error if the extension is not available."),
ECVF_ReadOnly
);
#if VULKAN_HAS_DEBUGGING_ENABLED
extern TAutoConsoleVariable<int32> GGPUValidationCvar;
#endif
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> FVulkanDynamicRHI::HMDVulkanExtensions;
TArray<const ANSICHAR*> FVulkanDeviceExtension::ExternalExtensions;
TArray<const ANSICHAR*> FVulkanInstanceExtension::ExternalExtensions;
template <typename ExistingChainType, typename NewStructType>
static void AddToPNext(ExistingChainType& Existing, NewStructType& Added)
{
Added.pNext = (void*)Existing.pNext;
Existing.pNext = (void*)&Added;
}
struct FOptionalVulkanDeviceExtensionProperties& FVulkanDeviceExtension::GetDeviceExtensionProperties()
{
const FOptionalVulkanDeviceExtensionProperties& ExtensionProperties = Device->GetOptionalExtensionProperties();
return const_cast<FOptionalVulkanDeviceExtensionProperties&>(ExtensionProperties);
}
#define VERIFYVULKANRESULT_INIT(VkFunction) { const VkResult ScopedResult = VkFunction; \
if (ScopedResult == VK_ERROR_INITIALIZATION_FAILED) { \
UE_LOG(LogVulkanRHI, Error, \
TEXT("%s failed\n at %s:%u\nThis typically means Vulkan is not properly set up in your system; try running vulkaninfo from the Vulkan SDK."), \
ANSI_TO_TCHAR(#VkFunction), ANSI_TO_TCHAR(__FILE__), __LINE__); } \
else if (ScopedResult < VK_SUCCESS) { \
VulkanRHI::VerifyVulkanResult(ScopedResult, #VkFunction, __FILE__, __LINE__); }}
TArray<VkExtensionProperties> FVulkanDeviceExtension::GetDriverSupportedDeviceExtensions(VkPhysicalDevice Gpu, const ANSICHAR* LayerName)
{
TArray<VkExtensionProperties> OutDeviceExtensions;
uint32 Count = 0;
VERIFYVULKANRESULT_INIT(VulkanRHI::vkEnumerateDeviceExtensionProperties(Gpu, LayerName, &Count, nullptr));
if (Count > 0)
{
OutDeviceExtensions.AddZeroed(Count);
VERIFYVULKANRESULT_INIT(VulkanRHI::vkEnumerateDeviceExtensionProperties(Gpu, LayerName, &Count, OutDeviceExtensions.GetData()));
}
OutDeviceExtensions.Sort([](const VkExtensionProperties& A, const VkExtensionProperties& B) { return FCStringAnsi::Strcmp(A.extensionName, B.extensionName) < 0; });
return OutDeviceExtensions;
}
TArray<VkExtensionProperties> FVulkanInstanceExtension::GetDriverSupportedInstanceExtensions(const ANSICHAR* LayerName)
{
TArray<VkExtensionProperties> OutInstanceExtensions;
uint32 Count = 0;
VERIFYVULKANRESULT_INIT(VulkanRHI::vkEnumerateInstanceExtensionProperties(LayerName, &Count, nullptr));
if (Count > 0)
{
OutInstanceExtensions.AddZeroed(Count);
VERIFYVULKANRESULT_INIT(VulkanRHI::vkEnumerateInstanceExtensionProperties(LayerName, &Count, OutInstanceExtensions.GetData()));
}
OutInstanceExtensions.Sort([](const VkExtensionProperties& A, const VkExtensionProperties& B) { return FCStringAnsi::Strcmp(A.extensionName, B.extensionName) < 0; });
return OutInstanceExtensions;
}
#undef VERIFYVULKANRESULT_INIT
// *** Vulkan Device Extension support ***
// Typical flow:
// 1- For the selected rendering device, the engine will query the supported extensions (FVulkanDeviceExtension are constructed and support is queried)
// 2- Followed by a query of Physical Device Features (PrePhysicalDeviceFeatures, PostPhysicalDeviceFeatures)
// 3- Followed by a query of Physical Device Properties (PrePhysicalDeviceProperties, PostPhysicalDeviceProperties)
// 4- Finally, the device is created (PreCreateDevice)
// ***** VK_KHR_maintenance4
class FVulkanKHRMaintenance4Extension : public FVulkanDeviceExtension
{
public:
FVulkanKHRMaintenance4Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_MAINTENANCE_4_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(Maintenance4Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, Maintenance4Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (Maintenance4Features.maintenance4 == VK_TRUE);
ExtensionFlags.HasKHRMaintenance4 = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, Maintenance4Features);
}
}
private:
VkPhysicalDeviceMaintenance4FeaturesKHR Maintenance4Features;
};
// ***** VK_KHR_maintenance5
class FVulkanKHRMaintenance5Extension : public FVulkanDeviceExtension
{
public:
FVulkanKHRMaintenance5Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_MAINTENANCE_5_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(Maintenance5Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, Maintenance5Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (Maintenance5Features.maintenance5 == VK_TRUE);
if (bRequirementsPassed)
{
ExtensionFlags.HasKHRMaintenance5 = true;
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, Maintenance5Features);
}
}
private:
VkPhysicalDeviceMaintenance5FeaturesKHR Maintenance5Features;
};
// ***** VK_KHR_maintenance7
class FVulkanKHRMaintenance7Extension : public FVulkanDeviceExtension
{
public:
FVulkanKHRMaintenance7Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_MAINTENANCE_7_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(Maintenance7Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, Maintenance7Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (Maintenance7Features.maintenance7 == VK_TRUE);
if (bRequirementsPassed)
{
ExtensionFlags.HasKHRMaintenance7 = true;
}
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceMaintenance7PropertiesKHR& PhysicalDeviceMaintenance7Properties = GetDeviceExtensionProperties().PhysicalDeviceMaintenance7Properties;
ZeroVulkanStruct(PhysicalDeviceMaintenance7Properties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, PhysicalDeviceMaintenance7Properties);
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, Maintenance7Features);
}
}
private:
VkPhysicalDeviceMaintenance7FeaturesKHR Maintenance7Features;
};
// ***** VK_KHR_driver_properties
class FVulkanKHRDriverPropertiesExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRDriverPropertiesExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, VULKAN_SUPPORTS_DRIVER_PROPERTIES, VK_API_VERSION_1_2)
{}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
VkPhysicalDeviceDriverPropertiesKHR& PhysicalDeviceDriverProperties = GetDeviceExtensionProperties().PhysicalDeviceDriverProperties;
ZeroVulkanStruct(PhysicalDeviceDriverProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, PhysicalDeviceDriverProperties);
}
virtual void PostPhysicalDeviceProperties() override final
{
const VkPhysicalDeviceDriverPropertiesKHR& PhysicalDeviceDriverProperties = GetDeviceExtensionProperties().PhysicalDeviceDriverProperties;
UE_LOG(LogVulkanRHI, Display, TEXT("Vulkan Physical Device Driver Properties:"));
UE_LOG(LogVulkanRHI, Display, TEXT("- driverName: %s"), ANSI_TO_TCHAR(PhysicalDeviceDriverProperties.driverName));
UE_LOG(LogVulkanRHI, Display, TEXT("- driverInfo: %s"), ANSI_TO_TCHAR(PhysicalDeviceDriverProperties.driverInfo));
}
};
// ***** VK_KHR_shader_atomic_int64
class FVulkanShaderAtomicInt64Extension : public FVulkanDeviceExtension
{
public:
FVulkanShaderAtomicInt64Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2)
{
bEnabledInCode = bEnabledInCode && (GRHIAllow64bitShaderAtomicsCvar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(BufferAtomicFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, BufferAtomicFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (BufferAtomicFeatures.shaderBufferInt64Atomics == VK_TRUE);
ExtensionFlags.HasKHRShaderAtomicInt64 = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, BufferAtomicFeatures);
}
}
VkPhysicalDeviceShaderAtomicInt64Features BufferAtomicFeatures;
};
// ***** VK_EXT_shader_image_atomic_int64
class FVulkanShaderImageAtomicInt64Extension : public FVulkanDeviceExtension
{
public:
FVulkanShaderImageAtomicInt64Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && (GRHIAllow64bitShaderAtomicsCvar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(ImageAtomicFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, ImageAtomicFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (ImageAtomicFeatures.shaderImageInt64Atomics == VK_TRUE);
ExtensionFlags.HasImageAtomicInt64 = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
// The PreCreateDevice() call is after all extension have gone through PostPhysicalDeviceFeatures(), so ExtensionFlags will be filled for both.
const FOptionalVulkanDeviceExtensions& ExtensionFlags = Device->GetOptionalExtensions();
GRHISupportsAtomicUInt64 = ExtensionFlags.HasKHRShaderAtomicInt64 && ExtensionFlags.HasImageAtomicInt64;
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, ImageAtomicFeatures);
}
}
VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT ImageAtomicFeatures;
};
// ***** VK_EXT_shader_viewport_index_layer
class FVulkanEXTShaderViewportIndexLayerExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTShaderViewportIndexLayerExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, VULKAN_SUPPORTS_SHADER_VIEWPORT_INDEX_LAYER, VK_API_VERSION_1_2)
{}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
GRHISupportsArrayIndexFromAnyShader = true;
}
};
// ***** VK_KHR_separate_depth_stencil_layouts
class FVulkanKHRSeparateDepthStencilLayoutsExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRSeparateDepthStencilLayoutsExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2)
{
bEnabledInCode = bEnabledInCode && (GVulkanAllowSync2BarriersCVar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(SeparateDepthStencilLayoutsFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, SeparateDepthStencilLayoutsFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (SeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts == VK_TRUE);
ExtensionFlags.HasSeparateDepthStencilLayouts = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, SeparateDepthStencilLayoutsFeatures);
}
}
private:
VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR SeparateDepthStencilLayoutsFeatures;
};
// ***** VK_KHR_synchronization2
class FVulkanKHRSynchronization2 : public FVulkanDeviceExtension
{
public:
FVulkanKHRSynchronization2(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3)
{
bEnabledInCode = bEnabledInCode && (GVulkanAllowSync2BarriersCVar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(Synchronization2Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, Synchronization2Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (Synchronization2Features.synchronization2 == VK_TRUE);
ExtensionFlags.HasKHRSynchronization2 = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, Synchronization2Features);
}
}
private:
VkPhysicalDeviceSynchronization2FeaturesKHR Synchronization2Features;
};
// ***** VK_KHR_multiview
class FVulkanKHRMultiviewExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRMultiviewExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_MULTIVIEW_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_1)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(DeviceMultiviewFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, DeviceMultiviewFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (DeviceMultiviewFeatures.multiview == VK_TRUE);
ExtensionFlags.HasKHRMultiview = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, DeviceMultiviewFeatures);
}
}
private:
VkPhysicalDeviceMultiviewFeatures DeviceMultiviewFeatures;
};
// ***** VK_EXT_scalar_block_layout
class FVulkanEXTScalarBlockLayoutExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTScalarBlockLayoutExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, VULKAN_SUPPORTS_SCALAR_BLOCK_LAYOUT, VK_API_VERSION_1_2)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(ScalarBlockLayoutFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, ScalarBlockLayoutFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (ScalarBlockLayoutFeatures.scalarBlockLayout == VK_TRUE);
ExtensionFlags.HasEXTScalarBlockLayout = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, ScalarBlockLayoutFeatures);
}
}
private:
VkPhysicalDeviceScalarBlockLayoutFeaturesEXT ScalarBlockLayoutFeatures;
};
// ***** VK_EXT_descriptor_indexing
class FVulkanEXTDescriptorIndexingExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTDescriptorIndexingExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VULKAN_SUPPORTS_DESCRIPTOR_INDEXING, VK_API_VERSION_1_2)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(DescriptorIndexingFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, DescriptorIndexingFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (DescriptorIndexingFeatures.runtimeDescriptorArray == VK_TRUE) &&
(DescriptorIndexingFeatures.descriptorBindingPartiallyBound == VK_TRUE) &&
(DescriptorIndexingFeatures.descriptorBindingUpdateUnusedWhilePending == VK_TRUE) &&
(DescriptorIndexingFeatures.descriptorBindingVariableDescriptorCount == VK_TRUE);
ExtensionFlags.HasEXTDescriptorIndexing = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, DescriptorIndexingFeatures);
}
}
private:
VkPhysicalDeviceDescriptorIndexingFeaturesEXT DescriptorIndexingFeatures;
};
// ***** VK_KHR_fragment_shading_rate
class FVulkanKHRFragmentShadingRateExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRFragmentShadingRateExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode &= HardwareVariableRateShadingSupportedByPlatform(GMaxRHIShaderPlatform);
// FSR should be enabled even if FDM is preferred because it could be not available.
int32 VRSFormatPreference = GVulkanVariableRateShadingFormatCVar->GetInt();
bEnabledInCode &= (VRSFormatPreference <= (uint8) EVulkanVariableRateShadingPreference::RequireFSR || VRSFormatPreference == (uint8)EVulkanVariableRateShadingPreference::PreferFDM);
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
ZeroVulkanStruct(FragmentShadingRateProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, FragmentShadingRateProperties);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
ExtensionFlags.HasKHRFragmentShadingRate = 1;
VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures = GetDeviceExtensionProperties().FragmentShadingRateFeatures;
GRHISupportsAttachmentVariableRateShading = (FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE);
GRHISupportsPipelineVariableRateShading = (FragmentShadingRateFeatures.pipelineFragmentShadingRate == VK_TRUE);
if (FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE)
{
GRHIVariableRateShadingImageDataType = VRSImage_Palette;
GRHIVariableRateShadingImageFormat = PF_R8_UINT;
}
else
{
GRHIVariableRateShadingImageDataType = VRSImage_NotSupported;
GRHIVariableRateShadingImageFormat = PF_Unknown;
}
}
virtual void PostPhysicalDeviceProperties() override final
{
VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures = GetDeviceExtensionProperties().FragmentShadingRateFeatures;
if (FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE)
{
GRHIVariableRateShadingImageTileMinWidth = FragmentShadingRateProperties.minFragmentShadingRateAttachmentTexelSize.width;
GRHIVariableRateShadingImageTileMinHeight = FragmentShadingRateProperties.minFragmentShadingRateAttachmentTexelSize.height;
GRHIVariableRateShadingImageTileMaxWidth = FragmentShadingRateProperties.maxFragmentShadingRateAttachmentTexelSize.width;
GRHIVariableRateShadingImageTileMaxHeight = FragmentShadingRateProperties.maxFragmentShadingRateAttachmentTexelSize.height;
if (FragmentShadingRateProperties.maxFragmentSize.width >= 4 && FragmentShadingRateProperties.maxFragmentSize.height >= 4)
{
// FYI FVulkanDevice::GetBestMatchedShadingRateExtents does extent filtering
GRHISupportsLargerVariableRateShadingSizes = GRHISupportsPipelineVariableRateShading;
}
// todo: We don't currently care much about the other properties here, but at some point in the future we probably will.
UE_LOG(LogVulkanRHI, Verbose, TEXT("Image-based Variable Rate Shading supported via KHRFragmentShadingRate extension. Selected VRS tile size %u by %u pixels per VRS image texel."), GRHIVariableRateShadingImageTileMinWidth, GRHIVariableRateShadingImageTileMinHeight);
}
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures = GetDeviceExtensionProperties().FragmentShadingRateFeatures;
ZeroVulkanStruct(FragmentShadingRateFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, FragmentShadingRateFeatures);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
VkPhysicalDeviceFragmentShadingRateFeaturesKHR& FragmentShadingRateFeatures = GetDeviceExtensionProperties().FragmentShadingRateFeatures;
if (FragmentShadingRateFeatures.attachmentFragmentShadingRate == VK_TRUE || FragmentShadingRateFeatures.pipelineFragmentShadingRate == VK_TRUE)
{
AddToPNext(DeviceCreateInfo, FragmentShadingRateFeatures);
}
}
private:
VkPhysicalDeviceFragmentShadingRatePropertiesKHR FragmentShadingRateProperties;
};
// ***** VK_EXT_fragment_density_map
class FVulkanEXTFragmentDensityMapExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTFragmentDensityMapExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode &= HardwareVariableRateShadingSupportedByPlatform(GMaxRHIShaderPlatform);
// FDM should be enabled even if the preferred choice is FSR because that might not be available.
int32 VRSFormatPreference = GVulkanVariableRateShadingFormatCVar->GetInt();
bEnabledInCode &= (VRSFormatPreference >= (uint8) EVulkanVariableRateShadingPreference::PreferFDM || VRSFormatPreference == (uint8) EVulkanVariableRateShadingPreference::PreferFSR);
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
ZeroVulkanStruct(FragmentDensityMapProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT);
AddToPNext(PhysicalDeviceProperties2, FragmentDensityMapProperties);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
VkPhysicalDeviceFragmentDensityMapFeaturesEXT& FragmentDensityMapFeatures = GetDeviceExtensionProperties().FragmentDensityMapFeatures;
bRequirementsPassed = (FragmentDensityMapFeatures.fragmentDensityMap == VK_TRUE);
ExtensionFlags.HasEXTFragmentDensityMap = bRequirementsPassed;
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
VkPhysicalDeviceFragmentDensityMapFeaturesEXT& FragmentDensityMapFeatures = GetDeviceExtensionProperties().FragmentDensityMapFeatures;
ZeroVulkanStruct(FragmentDensityMapFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, FragmentDensityMapFeatures);
}
virtual void PostPhysicalDeviceProperties() override final
{
// Use the Fragment Density Map extension if the Fragment Shading Rate extension is not available or if both are available but
// Fragment Density Map is the preferred user choice.
// NOTE: FVulkanKHRFragmentShadingRateExtension must be placed before FVulkanEXTFragmentDensityMapExtension for this to work!
if ((!GRHISupportsAttachmentVariableRateShading || (GVulkanVariableRateShadingFormatCVar->GetInt() >= (uint8) EVulkanVariableRateShadingPreference::PreferFDM)) && bRequirementsPassed)
{
GRHISupportsAttachmentVariableRateShading = true;
// Go with the smallest tile size for now, and also force to square, since this seems to be standard.
// TODO: Eventually we may want to surface the range of possible tile sizes depending on end use cases, but for now this is being used for foveated rendering and smallest tile size
// is preferred.
GRHIVariableRateShadingImageTileMinWidth = FragmentDensityMapProperties.minFragmentDensityTexelSize.width;
GRHIVariableRateShadingImageTileMinHeight = FragmentDensityMapProperties.minFragmentDensityTexelSize.height;
GRHIVariableRateShadingImageTileMaxWidth = FragmentDensityMapProperties.maxFragmentDensityTexelSize.width;
GRHIVariableRateShadingImageTileMaxHeight = FragmentDensityMapProperties.maxFragmentDensityTexelSize.height;
GRHIVariableRateShadingImageDataType = VRSImage_Fractional;
GRHIVariableRateShadingImageFormat = PF_R8G8;
UE_LOG(LogVulkanRHI, Display, TEXT("Image-based Variable Rate Shading supported via EXTFragmentDensityMap extension. Selected VRS tile size %u by %u pixels per VRS image texel."), GRHIVariableRateShadingImageTileMinWidth, GRHIVariableRateShadingImageTileMinHeight);
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceFragmentDensityMapFeaturesEXT& FragmentDensityMapFeatures = GetDeviceExtensionProperties().FragmentDensityMapFeatures;
AddToPNext(DeviceCreateInfo, FragmentDensityMapFeatures);
}
}
private:
VkPhysicalDeviceFragmentDensityMapPropertiesEXT FragmentDensityMapProperties;
};
// ***** VK_EXT_fragment_density_map2
class FVulkanEXTFragmentDensityMap2Extension : public FVulkanDeviceExtension
{
public:
FVulkanEXTFragmentDensityMap2Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_FRAGMENT_DENSITY_MAP_2_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode &= HardwareVariableRateShadingSupportedByPlatform(GMaxRHIShaderPlatform);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
VkPhysicalDeviceFragmentDensityMap2FeaturesEXT& FragmentDensityMap2Features = GetDeviceExtensionProperties().FragmentDensityMap2Features;
ZeroVulkanStruct(FragmentDensityMap2Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, FragmentDensityMap2Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
VkPhysicalDeviceFragmentDensityMap2FeaturesEXT& FragmentDensityMap2Features = GetDeviceExtensionProperties().FragmentDensityMap2Features;
bRequirementsPassed = (FragmentDensityMap2Features.fragmentDensityMapDeferred == VK_TRUE);
ExtensionFlags.HasEXTFragmentDensityMap2 = bRequirementsPassed;
GRHISupportsLateVariableRateShadingUpdate = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceFragmentDensityMap2FeaturesEXT& FragmentDensityMap2Features = GetDeviceExtensionProperties().FragmentDensityMap2Features;
AddToPNext(DeviceCreateInfo, FragmentDensityMap2Features);
}
}
};
// ***** VK_KHR_fragment_shader_barycentric
class FVulkanKHRFragmentShaderBarycentricExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRFragmentShaderBarycentricExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR& FragmentShaderBarycentricProps = GetDeviceExtensionProperties().FragmentShaderBarycentricProps;
ZeroVulkanStruct(FragmentShaderBarycentricProps, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, FragmentShaderBarycentricProps);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(FragmentShaderBarycentricFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, FragmentShaderBarycentricFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (FragmentShaderBarycentricFeatures.fragmentShaderBarycentric == VK_TRUE);
// Should be runtime guaranteed through SM6 profile
ExtensionFlags.HasKHRFragmentShaderBarycentric = bRequirementsPassed;
GRHIGlobals.SupportsBarycentricsSemantic = bRequirementsPassed;
}
virtual void PostPhysicalDeviceProperties() override final
{
VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR& FragmentShaderBarycentricProps = GetDeviceExtensionProperties().FragmentShaderBarycentricProps;
//UE_LOG(LogVulkanRHI, Verbose, TEXT("triStripVertexOrderIndependentOfProvokingVertex is %s"), FragmentShaderBarycentricProps.triStripVertexOrderIndependentOfProvokingVertex ? TEXT("true") : TEXT("false"));
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
// fragmentShaderBarycentric indicates that the implementation supports the BaryCoordKHR and BaryCoordNoPerspKHR SPIR - V fragment shader built - ins
// and supports the PerVertexKHR SPIR - V decoration on fragment shader input variables.
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, FragmentShaderBarycentricFeatures);
}
}
VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR FragmentShaderBarycentricFeatures;
};
// ***** VK_KHR_compute_shader_derivatives
class FVulkanKHRComputeShaderDerivatives : public FVulkanDeviceExtension
{
public:
FVulkanKHRComputeShaderDerivatives(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
VkPhysicalDeviceComputeShaderDerivativesFeaturesKHR& ComputeShaderDerivativesFeatures = GetDeviceExtensionProperties().ComputeShaderDerivativesFeatures;
ZeroVulkanStruct(ComputeShaderDerivativesFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, ComputeShaderDerivativesFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
VkPhysicalDeviceComputeShaderDerivativesFeaturesKHR& ComputeShaderDerivativesFeatures = GetDeviceExtensionProperties().ComputeShaderDerivativesFeatures;
bRequirementsPassed = (ComputeShaderDerivativesFeatures.computeDerivativeGroupLinear == VK_TRUE);
// Should be runtime guaranteed through SM6 profile
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceComputeShaderDerivativesFeaturesKHR& ComputeShaderDerivativesFeatures = GetDeviceExtensionProperties().ComputeShaderDerivativesFeatures;
AddToPNext(DeviceCreateInfo, ComputeShaderDerivativesFeatures);
}
}
};
// ***** VK_KHR_get_memory_requirements2
class FVulkanKHRGetMemoryRequirements2Extension : public FVulkanDeviceExtension
{
public:
FVulkanKHRGetMemoryRequirements2Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_1)
{}
};
// ***** VK_KHR_buffer_device_address
class FVulkanKHRBufferDeviceAddressExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRBufferDeviceAddressExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2) {}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(BufferDeviceAddressFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, BufferDeviceAddressFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (BufferDeviceAddressFeatures.bufferDeviceAddress == VK_TRUE);
ExtensionFlags.HasBufferDeviceAddress = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, BufferDeviceAddressFeatures);
}
}
private:
VkPhysicalDeviceBufferDeviceAddressFeaturesKHR BufferDeviceAddressFeatures;
};
// ***** VK_KHR_acceleration_structure
class FVulkanKHRAccelerationStructureExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRAccelerationStructureExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && GVulkanRayTracingCVar.GetValueOnAnyThread() && !FParse::Param(FCommandLine::Get(), TEXT("noraytracing"));
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(AccelerationStructureFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, AccelerationStructureFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (AccelerationStructureFeatures.accelerationStructure == VK_TRUE) &&
(AccelerationStructureFeatures.descriptorBindingAccelerationStructureUpdateAfterBind == VK_TRUE);
ExtensionFlags.HasAccelerationStructure = bRequirementsPassed;
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
VkPhysicalDeviceAccelerationStructurePropertiesKHR& AccelerationStructure = GetDeviceExtensionProperties().AccelerationStructureProps;
ZeroVulkanStruct(AccelerationStructure, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, AccelerationStructure);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, AccelerationStructureFeatures);
}
}
private:
VkPhysicalDeviceAccelerationStructureFeaturesKHR AccelerationStructureFeatures;
};
// ***** VK_KHR_ray_tracing_pipeline
class FVulkanKHRRayTracingPipelineExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRRayTracingPipelineExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && GVulkanRayTracingCVar.GetValueOnAnyThread() && !FParse::Param(FCommandLine::Get(), TEXT("noraytracing"));
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(RayTracingPipelineFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, RayTracingPipelineFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (RayTracingPipelineFeatures.rayTracingPipeline == VK_TRUE) &&
(RayTracingPipelineFeatures.rayTraversalPrimitiveCulling == VK_TRUE);
ExtensionFlags.HasRayTracingPipeline = bRequirementsPassed;
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
VkPhysicalDeviceRayTracingPipelinePropertiesKHR& RayTracingPipeline = GetDeviceExtensionProperties().RayTracingPipelineProps;
ZeroVulkanStruct(RayTracingPipeline, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR);
AddToPNext(PhysicalDeviceProperties2, RayTracingPipeline);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, RayTracingPipelineFeatures);
GRHISupportsRayTracingDispatchIndirect = (RayTracingPipelineFeatures.rayTracingPipelineTraceRaysIndirect == VK_TRUE);
}
}
private:
VkPhysicalDeviceRayTracingPipelineFeaturesKHR RayTracingPipelineFeatures;
};
// ***** VK_KHR_ray_query
class FVulkanKHRRayQueryExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRRayQueryExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_RAY_QUERY_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && GVulkanRayTracingCVar.GetValueOnAnyThread() && !FParse::Param(FCommandLine::Get(), TEXT("noraytracing"));
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(RayQueryFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, RayQueryFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (RayQueryFeatures.rayQuery == VK_TRUE);
ExtensionFlags.HasRayQuery = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, RayQueryFeatures);
}
}
private:
VkPhysicalDeviceRayQueryFeaturesKHR RayQueryFeatures;
};
// ***** VK_KHR_ray_tracing_position_fetch
class FVulkanKHRRayTracingPositionFetchExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRRayTracingPositionFetchExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_RAY_TRACING_POSITION_FETCH_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && GVulkanRayTracingCVar.GetValueOnAnyThread() && !FParse::Param(FCommandLine::Get(), TEXT("noraytracing"));
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(RayTracingPositionFetchFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, RayTracingPositionFetchFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (RayTracingPositionFetchFeatures.rayTracingPositionFetch == VK_TRUE);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, RayTracingPositionFetchFeatures);
}
}
private:
VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR RayTracingPositionFetchFeatures;
};
// ***** VK_KHR_timeline_semaphore
class FVulkanKHRTimelineSemaphoreExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRTimelineSemaphoreExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(TimelineSemaphoreFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, TimelineSemaphoreFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (TimelineSemaphoreFeatures.timelineSemaphore == VK_TRUE);
ExtensionFlags.HasKHRTimelineSemaphore = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, TimelineSemaphoreFeatures);
}
}
private:
VkPhysicalDeviceTimelineSemaphoreFeatures TimelineSemaphoreFeatures;
};
// ***** VK_EXT_mesh_shader
class FVulkanEXTMeshShaderExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTMeshShaderExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_MESH_SHADER_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(MeshShaderFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, MeshShaderFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (MeshShaderFeatures.meshShader == VK_TRUE) && (MeshShaderFeatures.multiviewMeshShader == VK_TRUE);
ExtensionFlags.HasEXTMeshShader = bRequirementsPassed;
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
VkPhysicalDeviceMeshShaderPropertiesEXT& MeshShaderProperties = GetDeviceExtensionProperties().MeshShaderProperties;
ZeroVulkanStruct(MeshShaderProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT);
AddToPNext(PhysicalDeviceProperties2, MeshShaderProperties);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
if (!GetDeviceExtensionProperties().FragmentShadingRateFeatures.primitiveFragmentShadingRate)
{
MeshShaderFeatures.primitiveFragmentShadingRateMeshShader = VK_FALSE;
}
AddToPNext(DeviceCreateInfo, MeshShaderFeatures);
}
}
private:
VkPhysicalDeviceMeshShaderFeaturesEXT MeshShaderFeatures;
};
// ***** VK_AMD_buffer_marker (vendor)
class FVulkanAMDBufferMarkerExtension : public FVulkanDeviceExtension
{
public:
FVulkanAMDBufferMarkerExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_AMD_BUFFER_MARKER_EXTENSION_NAME, VULKAN_SUPPORTS_AMD_BUFFER_MARKER)
{
bEnabledInCode = bEnabledInCode && UE::RHI::UseGPUCrashDebugging() && UE::RHICore::AllowVendorDevice();
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
ExtensionFlags.HasAMDBufferMarker = 1;
}
};
// ***** VK_NV_device_diagnostic_checkpoints (vendor)
class FVulkanNVDeviceDiagnosticCheckpointsExtension : public FVulkanDeviceExtension
{
public:
FVulkanNVDeviceDiagnosticCheckpointsExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, VULKAN_SUPPORTS_NV_DIAGNOSTICS)
{
bEnabledInCode = bEnabledInCode && UE::RHI::UseGPUCrashDebugging() && UE::RHICore::AllowVendorDevice();
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
ExtensionFlags.HasNVDiagnosticCheckpoints = 1;
}
};
// ***** VK_NV_device_diagnostics_config (vendor)
class FVulkanNVDeviceDiagnosticConfigExtension : public FVulkanDeviceExtension
{
public:
FVulkanNVDeviceDiagnosticConfigExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, VULKAN_SUPPORTS_NV_DIAGNOSTICS)
{
bEnabledInCode = bEnabledInCode && UE::RHI::UseGPUCrashDebugging() && UE::RHICore::AllowVendorDevice();
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(DeviceDiagnosticsConfigFeaturesNV, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV);
AddToPNext(PhysicalDeviceFeatures2, DeviceDiagnosticsConfigFeaturesNV);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (DeviceDiagnosticsConfigFeaturesNV.diagnosticsConfig == VK_TRUE);
ExtensionFlags.HasNVDeviceDiagnosticConfig = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, DeviceDiagnosticsConfigFeaturesNV);
ZeroVulkanStruct(DeviceDiagnosticsConfigCreateInfoNV, VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV);
DeviceDiagnosticsConfigCreateInfoNV.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
AddToPNext(DeviceCreateInfo, DeviceDiagnosticsConfigCreateInfoNV);
#if NV_AFTERMATH && (RHI_NEW_GPU_PROFILER == 0)
// Vulkan's breadcrumb / markers implementation differs from the one provided by RHICore.
// @todo unify the implementation so this isn't necessary
extern void AftermathResolveMarkerCallback(const void* Marker, void** ResolvedMarkerData, uint32_t * MarkerSize);
UE::RHICore::Nvidia::Aftermath::InitializeBeforeDeviceCreation([](const void* MarkerData, const uint32_t MarkerDataSize, void* UserData, void** ResolvedMarkerData, uint32_t* ResolvedMarkerDataSize) -> void
{
AftermathResolveMarkerCallback(MarkerData, ResolvedMarkerData, ResolvedMarkerDataSize);
});
extern void AftermathLateAssociate(float TimeLimitSeconds, uint32 FrameLimit);
UE::RHICore::Nvidia::Aftermath::SetLateShaderAssociateCallback(AftermathLateAssociate);
#endif
}
}
private:
VkPhysicalDeviceDiagnosticsConfigFeaturesNV DeviceDiagnosticsConfigFeaturesNV;
VkDeviceDiagnosticsConfigCreateInfoNV DeviceDiagnosticsConfigCreateInfoNV;
};
// ***** VK_NV_ray_tracing_validation (vendor)
class FVulkanNVRayTracingValidationExtension : public FVulkanDeviceExtension
{
public:
FVulkanNVRayTracingValidationExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_NV_RAY_TRACING_VALIDATION_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
#if VULKAN_HAS_DEBUGGING_ENABLED
bEnabledInCode = bEnabledInCode && (GValidationCvar.GetValueOnAnyThread() > 0) && UE::RHICore::AllowVendorDevice();
#else
bEnabledInCode = false;
#endif
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(RayTracingValidationFeaturesNV, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_VALIDATION_FEATURES_NV);
AddToPNext(PhysicalDeviceFeatures2, RayTracingValidationFeaturesNV);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (RayTracingValidationFeaturesNV.rayTracingValidation == VK_TRUE);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, RayTracingValidationFeaturesNV);
}
}
private:
VkPhysicalDeviceRayTracingValidationFeaturesNV RayTracingValidationFeaturesNV;
};
// ***** VK_EXT_device_fault
class FVulkanEXTDeviceFaultExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTDeviceFaultExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_DEVICE_FAULT_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(DeviceFaultFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, DeviceFaultFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (DeviceFaultFeatures.deviceFault == VK_TRUE);
ExtensionFlags.HasEXTDeviceFault = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, DeviceFaultFeatures);
}
}
private:
VkPhysicalDeviceFaultFeaturesEXT DeviceFaultFeatures;
};
// ***** VK_EXT_host_query_reset
class FVulkanEXTHostQueryResetExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTHostQueryResetExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2)
{
bEnabledInCode = bEnabledInCode && (GVulkanAllowHostQueryResetCVar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(HostQueryResetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, HostQueryResetFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (HostQueryResetFeatures.hostQueryReset == VK_TRUE);
ExtensionFlags.HasEXTHostQueryReset = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, HostQueryResetFeatures);
}
}
private:
VkPhysicalDeviceHostQueryResetFeaturesEXT HostQueryResetFeatures;
};
// ***** VK_EXT_subgroup_size_control
class FVulkanEXTSubgroupSizeControlExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTSubgroupSizeControlExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(SubgroupSizeControlFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, SubgroupSizeControlFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (SubgroupSizeControlFeatures.subgroupSizeControl == VK_TRUE);
ExtensionFlags.HasEXTSubgroupSizeControl = bRequirementsPassed;
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& SubgroupSizeControlProperties = GetDeviceExtensionProperties().SubgroupSizeControlProperties;
ZeroVulkanStruct(SubgroupSizeControlProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES);
AddToPNext(PhysicalDeviceProperties2, SubgroupSizeControlProperties);
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& SubgroupSizeControlProperties = GetDeviceExtensionProperties().SubgroupSizeControlProperties;
GRHIMinimumWaveSize = SubgroupSizeControlProperties.minSubgroupSize;
GRHIMaximumWaveSize = SubgroupSizeControlProperties.maxSubgroupSize;
AddToPNext(DeviceCreateInfo, SubgroupSizeControlFeatures);
}
}
private:
VkPhysicalDeviceSubgroupSizeControlFeaturesEXT SubgroupSizeControlFeatures;
};
// ***** VK_EXT_shader_demote_to_helper_invocation
class FVulkanEXTShaderDemoteToHelperInvocationExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTShaderDemoteToHelperInvocationExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(ShaderDemoteToHelperInvocationFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, ShaderDemoteToHelperInvocationFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (ShaderDemoteToHelperInvocationFeatures.shaderDemoteToHelperInvocation == VK_TRUE);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, ShaderDemoteToHelperInvocationFeatures);
}
}
private:
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT ShaderDemoteToHelperInvocationFeatures;
};
// ***** VK_EXT_calibrated_timestamps
class FVulkanEXTCalibratedTimestampsExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTCalibratedTimestampsExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED)
{
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
uint32 TimeDomainCount = 0;
VulkanRHI::vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(Device->GetPhysicalHandle(), &TimeDomainCount, nullptr);
TArray<VkTimeDomainEXT, TInlineAllocator<4>> TimeDomains;
TimeDomains.SetNumZeroed(TimeDomainCount);
VulkanRHI::vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(Device->GetPhysicalHandle(), &TimeDomainCount, &TimeDomains[0]);
for (VkTimeDomainEXT TimeDomain : TimeDomains)
{
if (TimeDomain == VK_TIME_DOMAIN_DEVICE_EXT)
{
ExtensionFlags.HasEXTCalibratedTimestamps = 1;
break;
}
}
}
};
// ***** VK_EXT_descriptor_buffer
class FVulkanEXTDescriptorBuffer : public FVulkanDeviceExtension
{
public:
FVulkanEXTDescriptorBuffer(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED)
{
// Sync2 is a prereq
bEnabledInCode = bEnabledInCode && (GVulkanAllowSync2BarriersCVar.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(DescriptorBufferFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, DescriptorBufferFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
// only enable descriptor buffers if we also support mutable descriptor types (value filled prior)
bRequirementsPassed = (DescriptorBufferFeatures.descriptorBuffer == VK_TRUE);
ExtensionFlags.HasEXTDescriptorBuffer = bRequirementsPassed;
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceDescriptorBufferPropertiesEXT& DescriptorBufferProperties = GetDeviceExtensionProperties().DescriptorBufferProps;
ZeroVulkanStruct(DescriptorBufferProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT);
AddToPNext(PhysicalDeviceProperties2, DescriptorBufferProperties);
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, DescriptorBufferFeatures);
const VkPhysicalDeviceDescriptorBufferPropertiesEXT& DescriptorBufferProperties = GetDeviceExtensionProperties().DescriptorBufferProps;
UE_LOG(LogVulkanRHI, Display, TEXT("Enabling Vulkan Descriptor Buffers with: ")
TEXT("allowSamplerImageViewPostSubmitCreation=%u, maxDescriptorBufferBindings=%u, ")
TEXT("maxSamplerDescriptorBufferBindings=%u, maxResourceDescriptorBufferBindings=%u, ")
TEXT("samplerDescriptorBufferAddressSpaceSize=%llu, resourceDescriptorBufferAddressSpaceSize=%llu, ")
TEXT("maxSamplerDescriptorBufferRange=%llu, maxResourceDescriptorBufferRange=%llu, ")
TEXT("descriptorBufferAddressSpaceSize=%llu, descriptorBufferOffsetAlignment=%llu, ")
TEXT("samplerDescriptorSize=%llu"),
DescriptorBufferProperties.allowSamplerImageViewPostSubmitCreation, DescriptorBufferProperties.maxDescriptorBufferBindings,
DescriptorBufferProperties.maxSamplerDescriptorBufferBindings, DescriptorBufferProperties.maxResourceDescriptorBufferBindings,
DescriptorBufferProperties.samplerDescriptorBufferAddressSpaceSize, DescriptorBufferProperties.resourceDescriptorBufferAddressSpaceSize,
DescriptorBufferProperties.maxSamplerDescriptorBufferRange, DescriptorBufferProperties.maxResourceDescriptorBufferRange,
DescriptorBufferProperties.descriptorBufferAddressSpaceSize, DescriptorBufferProperties.descriptorBufferOffsetAlignment,
DescriptorBufferProperties.samplerDescriptorSize);
}
}
private:
VkPhysicalDeviceDescriptorBufferFeaturesEXT DescriptorBufferFeatures;
};
// ***** VK_KHR_16bit_storage
class FVulkanKHR16BitStorageExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHR16BitStorageExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_16BIT_STORAGE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && (GRHIAllow16bitOps.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(Device16BitStorageFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, Device16BitStorageFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (
Device16BitStorageFeatures.storageBuffer16BitAccess == VK_TRUE &&
Device16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE &&
Device16BitStorageFeatures.storagePushConstant16 == VK_TRUE);
ExtensionFlags.HasKHR16bitStorage = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, Device16BitStorageFeatures);
}
}
VkPhysicalDevice16BitStorageFeaturesKHR Device16BitStorageFeatures;
};
// ***** VK_KHR_shader_float16_int8
class FVulkanKHRShaderFloat16Int8Extension : public FVulkanDeviceExtension
{
public:
FVulkanKHRShaderFloat16Int8Extension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
bEnabledInCode = bEnabledInCode && (GRHIAllow16bitOps.GetValueOnAnyThread() != 0);
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(ShaderFloat16Int8Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR);
AddToPNext(PhysicalDeviceFeatures2, ShaderFloat16Int8Features);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (ShaderFloat16Int8Features.shaderFloat16 == VK_TRUE);
ExtensionFlags.HasKHRShaderFloat16 = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
// The PreCreateDevice() call is after all extension have gone through PostPhysicalDeviceFeatures(), so ExtensionFlags will be filled for both.
const FOptionalVulkanDeviceExtensions& ExtensionFlags = Device->GetOptionalExtensions();
GRHIGlobals.SupportsNative16BitOps = ExtensionFlags.HasKHR16bitStorage && ExtensionFlags.HasKHRShaderFloat16 &&
(FDataDrivenShaderPlatformInfo::GetSupportsRealTypes(GMaxRHIShaderPlatform) != ERHIFeatureSupport::Unsupported);
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, ShaderFloat16Int8Features);
}
}
VkPhysicalDeviceShaderFloat16Int8Features ShaderFloat16Int8Features;
};
// ***** VK_KHR_depth_stencil_resolve
class FVulkanKHRDepthStencilResolveExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRDepthStencilResolveExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
ExtensionFlags.HasKHRDepthStencilResolve = 1;
GRHISupportsDepthStencilResolve = ExtensionFlags.HasKHRDepthStencilResolve;
}
};
// ***** VK_EXT_pipeline_creation_cache_control
class FVulkanEXTPipelineCreationCacheControlExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTPipelineCreationCacheControlExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3)
{}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(PhysicalDevicePipelineCreationCacheControlFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, PhysicalDevicePipelineCreationCacheControlFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (PhysicalDevicePipelineCreationCacheControlFeatures.pipelineCreationCacheControl == VK_TRUE);
ExtensionFlags.HasEXTPipelineCreationCacheControl = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, PhysicalDevicePipelineCreationCacheControlFeatures);
}
}
VkPhysicalDevicePipelineCreationCacheControlFeatures PhysicalDevicePipelineCreationCacheControlFeatures;
};
// ***** VK_KHR_sampler_ycbcr_conversion
class FVulkanKHRSamplerYcbcrConversionExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRSamplerYcbcrConversionExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_1)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(PhysicalDeviceSamplerYcbcrConversionFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES);
AddToPNext(PhysicalDeviceFeatures2, PhysicalDeviceSamplerYcbcrConversionFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (PhysicalDeviceSamplerYcbcrConversionFeatures.samplerYcbcrConversion == VK_TRUE);
ExtensionFlags.HasKHRSamplerYcbcrConversion = bRequirementsPassed;
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, PhysicalDeviceSamplerYcbcrConversionFeatures);
}
}
private:
VkPhysicalDeviceSamplerYcbcrConversionFeatures PhysicalDeviceSamplerYcbcrConversionFeatures;
};
// ***** VK_EXT_image_compression_control
class FVulkanEXTImageCompressionControlExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTImageCompressionControlExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(PhysicalDeviceImageCompressionControlFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, PhysicalDeviceImageCompressionControlFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (PhysicalDeviceImageCompressionControlFeatures.imageCompressionControl == VK_TRUE);
ExtensionFlags.HasEXTImageCompressionControl = bRequirementsPassed;
if (ExtensionFlags.HasEXTImageCompressionControl && !ExtensionFlags.HasKHRMaintenance5 && !VulkanDynamicAPI::vkGetImageSubresourceLayout2KHR)
{
VulkanDynamicAPI::vkGetImageSubresourceLayout2KHR = VulkanDynamicAPI::vkGetImageSubresourceLayout2EXT;
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, PhysicalDeviceImageCompressionControlFeatures);
}
}
private:
VkPhysicalDeviceImageCompressionControlFeaturesEXT PhysicalDeviceImageCompressionControlFeatures;
};
// ***** VK_KHR_graphics_pipeline_library
class FVulkanKHRGraphicsPipelineLibraryExtension : public FVulkanDeviceExtension
{
public:
FVulkanKHRGraphicsPipelineLibraryExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(GraphicsPipelineLibraryFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, GraphicsPipelineLibraryFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (GraphicsPipelineLibraryFeatures.graphicsPipelineLibrary == VK_TRUE);
}
virtual void PrePhysicalDeviceProperties(VkPhysicalDeviceProperties2KHR& PhysicalDeviceProperties2) override final
{
if (bRequirementsPassed)
{
VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT& GraphicsPipelineLibraryProperties = GetDeviceExtensionProperties().GraphicsPipelineLibraryProperties;
ZeroVulkanStruct(GraphicsPipelineLibraryProperties, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT);
AddToPNext(PhysicalDeviceProperties2, GraphicsPipelineLibraryProperties);
}
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, GraphicsPipelineLibraryFeatures);
}
}
VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT GraphicsPipelineLibraryFeatures;
};
// ***** VK_EXT_mutable_descriptor_type
class FVulkanEXTMutableDescriptorTypeExtension : public FVulkanDeviceExtension
{
public:
FVulkanEXTMutableDescriptorTypeExtension(FVulkanDevice* InDevice)
: FVulkanDeviceExtension(InDevice, VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED)
{
}
virtual void PrePhysicalDeviceFeatures(VkPhysicalDeviceFeatures2KHR& PhysicalDeviceFeatures2) override final
{
ZeroVulkanStruct(MutableDescriptorTypeFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT);
AddToPNext(PhysicalDeviceFeatures2, MutableDescriptorTypeFeatures);
}
virtual void PostPhysicalDeviceFeatures(FOptionalVulkanDeviceExtensions& ExtensionFlags) override final
{
bRequirementsPassed = (MutableDescriptorTypeFeatures.mutableDescriptorType == VK_TRUE);
}
virtual void PreCreateDevice(VkDeviceCreateInfo& DeviceCreateInfo) override final
{
if (bRequirementsPassed)
{
AddToPNext(DeviceCreateInfo, MutableDescriptorTypeFeatures);
}
}
VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT MutableDescriptorTypeFeatures;
};
template <typename ExtensionType>
static void FlagExtensionSupport(const TArray<VkExtensionProperties>& ExtensionProperties, TArray<TUniquePtr<ExtensionType>>& UEExtensions, uint32 ApiVersion, const TCHAR* ExtensionTypeName)
{
// Flag the extension support
UE_LOG(LogVulkanRHI, Display, TEXT("Found %d available %s extensions :"), ExtensionProperties.Num(), ExtensionTypeName);
for (const VkExtensionProperties& Extension : ExtensionProperties)
{
const int32 ExtensionIndex = ExtensionType::FindExtension(UEExtensions, Extension.extensionName);
const bool bFound = (ExtensionIndex != INDEX_NONE);
bool bIsCore = false;
if (bFound)
{
UEExtensions[ExtensionIndex]->SetSupported();
// Set the core flag if the extension was promoted for our current api version
bIsCore = UEExtensions[ExtensionIndex]->SetCore(ApiVersion);
}
UE_LOG(LogVulkanRHI, Display, TEXT(" %s %s"), bIsCore ? TEXT("*") : bFound ? TEXT("+") : TEXT("-"), ANSI_TO_TCHAR(Extension.extensionName));
}
}
FVulkanDeviceExtensionArray FVulkanDeviceExtension::GetUESupportedDeviceExtensions(FVulkanDevice* InDevice, uint32 ApiVersion)
{
FVulkanDeviceExtensionArray OutUEDeviceExtensions;
#define ADD_SIMPLE_EXTENSION(EXTENSION_NAME, ENABLED_IN_CODE, PROMOTED_VER, FLAG_SETTER) \
OutUEDeviceExtensions.Add(MakeUnique<FVulkanDeviceExtension>(InDevice, EXTENSION_NAME, ENABLED_IN_CODE, PROMOTED_VER, FLAG_SETTER, FVulkanExtensionBase::AutoActivate))
#define ADD_EXTERNAL_EXTENSION(EXTENSION_NAME, ENABLED_IN_CODE, PROMOTED_VER, FLAG_SETTER) \
OutUEDeviceExtensions.Add(MakeUnique<FVulkanDeviceExtension>(InDevice, EXTENSION_NAME, ENABLED_IN_CODE, PROMOTED_VER, FLAG_SETTER, FVulkanExtensionBase::ManuallyActivate))
#define ADD_CUSTOM_EXTENSION(EXTENSION_CLASS) \
OutUEDeviceExtensions.Add(MakeUnique<EXTENSION_CLASS>(InDevice));
// Generic simple extensions :
ADD_SIMPLE_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED, nullptr);
ADD_SIMPLE_EXTENSION(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, VULKAN_SUPPORTS_MEMORY_BUDGET, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasMemoryBudget));
ADD_SIMPLE_EXTENSION(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME, VULKAN_SUPPORTS_MEMORY_PRIORITY, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasMemoryPriority));
ADD_SIMPLE_EXTENSION(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, VULKAN_SUPPORTS_RENDERPASS2, VK_API_VERSION_1_2, DEVICE_EXT_FLAG_SETTER(HasKHRRenderPass2));
ADD_SIMPLE_EXTENSION(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasDeferredHostOperations));
ADD_SIMPLE_EXTENSION(VK_KHR_SPIRV_1_4_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2, DEVICE_EXT_FLAG_SETTER(HasSPIRV_14));
ADD_SIMPLE_EXTENSION(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2, DEVICE_EXT_FLAG_SETTER(HasShaderFloatControls));
ADD_SIMPLE_EXTENSION(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_2, DEVICE_EXT_FLAG_SETTER(HasKHRImageFormatList));
ADD_SIMPLE_EXTENSION(VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, VULKAN_SUPPORTS_VALIDATION_CACHE, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasEXTValidationCache));
ADD_SIMPLE_EXTENSION(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasEXTLoadStoreOpNone));
ADD_SIMPLE_EXTENSION(VK_QCOM_RENDER_PASS_SHADER_RESOLVE_EXTENSION_NAME, VULKAN_SUPPORTS_QCOM_RENDERPASS_SHADER_RESOLVE, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasQcomRenderPassShaderResolve));
ADD_SIMPLE_EXTENSION(VK_EXT_TOOLING_INFO_EXTENSION_NAME, VULKAN_HAS_DEBUGGING_ENABLED || VULKAN_ENABLE_DRAW_MARKERS, VK_API_VERSION_1_3, DEVICE_EXT_FLAG_SETTER(HasEXTToolingInfo));
ADD_SIMPLE_EXTENSION(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VK_API_VERSION_1_3, nullptr); // Only enabled because maintenance5 depends on it
ADD_SIMPLE_EXTENSION(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED, DEVICE_EXT_FLAG_SETTER(HasKHRPipelineLibrary));
// Externally activated extensions (supported by the engine, but enabled externally by plugin or other) :
ADD_EXTERNAL_EXTENSION(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VULKAN_HAS_DEBUGGING_ENABLED || VULKAN_ENABLE_DRAW_MARKERS, VULKAN_EXTENSION_NOT_PROMOTED, nullptr);
// Extensions with custom classes :
ADD_CUSTOM_EXTENSION(FVulkanKHRDriverPropertiesExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRMaintenance4Extension);
ADD_CUSTOM_EXTENSION(FVulkanKHRMaintenance5Extension);
ADD_CUSTOM_EXTENSION(FVulkanKHRMaintenance7Extension);
ADD_CUSTOM_EXTENSION(FVulkanShaderAtomicInt64Extension);
ADD_CUSTOM_EXTENSION(FVulkanShaderImageAtomicInt64Extension);
ADD_CUSTOM_EXTENSION(FVulkanEXTScalarBlockLayoutExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTShaderViewportIndexLayerExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRSeparateDepthStencilLayoutsExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRSynchronization2);
ADD_CUSTOM_EXTENSION(FVulkanKHRFragmentShadingRateExtension); // must be kept BEFORE DensityMap!
ADD_CUSTOM_EXTENSION(FVulkanEXTFragmentDensityMapExtension); // must be kept AFTER ShadingRate!
ADD_CUSTOM_EXTENSION(FVulkanEXTFragmentDensityMap2Extension);
ADD_CUSTOM_EXTENSION(FVulkanKHRMultiviewExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRGetMemoryRequirements2Extension);
ADD_CUSTOM_EXTENSION(FVulkanEXTDescriptorIndexingExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTHostQueryResetExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTSubgroupSizeControlExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTCalibratedTimestampsExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTDescriptorBuffer);
ADD_CUSTOM_EXTENSION(FVulkanEXTDeviceFaultExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTShaderDemoteToHelperInvocationExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHR16BitStorageExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRShaderFloat16Int8Extension);
ADD_CUSTOM_EXTENSION(FVulkanKHRDepthStencilResolveExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTPipelineCreationCacheControlExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRFragmentShaderBarycentricExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRComputeShaderDerivatives);
ADD_CUSTOM_EXTENSION(FVulkanKHRSamplerYcbcrConversionExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRTimelineSemaphoreExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTMeshShaderExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTImageCompressionControlExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRGraphicsPipelineLibraryExtension);
ADD_CUSTOM_EXTENSION(FVulkanEXTMutableDescriptorTypeExtension);
// Needed for Raytracing
ADD_CUSTOM_EXTENSION(FVulkanKHRBufferDeviceAddressExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRAccelerationStructureExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRRayTracingPipelineExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRRayQueryExtension);
ADD_CUSTOM_EXTENSION(FVulkanKHRRayTracingPositionFetchExtension);
// Vendor extensions
ADD_CUSTOM_EXTENSION(FVulkanAMDBufferMarkerExtension);
ADD_CUSTOM_EXTENSION(FVulkanNVDeviceDiagnosticCheckpointsExtension);
ADD_CUSTOM_EXTENSION(FVulkanNVDeviceDiagnosticConfigExtension);
ADD_CUSTOM_EXTENSION(FVulkanNVRayTracingValidationExtension);
// Add in platform specific extensions
FVulkanPlatform::GetDeviceExtensions(InDevice, OutUEDeviceExtensions);
// Helper function to go through a list of extensions and activate them (or add them)
auto ActivateExternalExtensions = [&](TArray<const ANSICHAR*> Extensions, const TCHAR* Requester) {
for (const ANSICHAR* ExtensionName : Extensions)
{
const int32 ExtensionIndex = FindExtension(OutUEDeviceExtensions, ExtensionName);
if (ExtensionIndex == INDEX_NONE)
{
OutUEDeviceExtensions.Add(MakeUnique<FVulkanDeviceExtension>(InDevice, ExtensionName, VULKAN_EXTENSION_ENABLED));
UE_LOG(LogVulkanRHI, Warning, TEXT("%s requested device extension [%s] isn't part of the engine's core extension list. Adding it on-the-fly..."), Requester, ANSI_TO_TCHAR(ExtensionName));
}
else
{
OutUEDeviceExtensions[ExtensionIndex]->SetActivated();
}
}
};
// Add HMD requested extensions
{
if (IHeadMountedDisplayModule::IsAvailable())
{
FVulkanDynamicRHI::HMDVulkanExtensions = IHeadMountedDisplayModule::Get().GetVulkanExtensions();
}
if (FVulkanDynamicRHI::HMDVulkanExtensions.IsValid())
{
TArray<const ANSICHAR*> HMDExtensions;
FVulkanDynamicRHI::HMDVulkanExtensions->GetVulkanDeviceExtensionsRequired(InDevice->GetPhysicalHandle(), HMDExtensions);
ActivateExternalExtensions(HMDExtensions, TEXT("HMD"));
}
}
// Add extensions added outside the RHI (eg plugins)
ActivateExternalExtensions(ExternalExtensions, TEXT("Externally"));
// Now that all the extensions are listed, update their support flags
FlagExtensionSupport(GetDriverSupportedDeviceExtensions(InDevice->GetPhysicalHandle()), OutUEDeviceExtensions, ApiVersion, TEXT("device"));
#undef ADD_SIMPLE_EXTENSION
#undef ADD_EXTERNAL_EXTENSION
#undef ADD_CUSTOM_EXTENSION
return OutUEDeviceExtensions;
}
// *** Vulkan Instance Extension support ***
// Typical flow:
// 1- The engine will query the supported extensions at RHI creation (FVulkanDeviceExtension are constructed and support is queried)
// 2- The supported extensions are added at VkInstance creation (PreCreateInstance)
// ***** VK_EXT_validation_features
class FVulkanEXTValidationFeaturesExtension : public FVulkanInstanceExtension
{
public:
FVulkanEXTValidationFeaturesExtension()
: FVulkanInstanceExtension(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME, VULKAN_HAS_DEBUGGING_ENABLED & VULKAN_HAS_VALIDATION_FEATURES, VULKAN_EXTENSION_NOT_PROMOTED, nullptr, FVulkanExtensionBase::ManuallyActivate)
{}
virtual void PreCreateInstance(VkInstanceCreateInfo& InstanceCreateInfo, FOptionalVulkanInstanceExtensions& ExtensionFlags) override final
{
#if VULKAN_HAS_DEBUGGING_ENABLED
check(GValidationCvar.GetValueOnAnyThread() > 0);
auto GetValidationFeaturesEnabled = []()
{
TArray<VkValidationFeatureEnableEXT> Features;
const int32 GPUValidationValue = GGPUValidationCvar.GetValueOnAnyThread();
if (GPUValidationValue > 0)
{
Features.Add(VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT);
if (GPUValidationValue > 1)
{
Features.Add(VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT);
}
}
if (FParse::Param(FCommandLine::Get(), TEXT("vulkanbestpractices")))
{
Features.Add(VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT);
}
if (FParse::Param(FCommandLine::Get(), TEXT("vulkandebugsync")))
{
Features.Add(VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT);
}
return Features;
};
ValidationFeaturesEnabled = GetValidationFeaturesEnabled();
if (ValidationFeaturesEnabled.Num())
{
ZeroVulkanStruct(ValidationFeatures, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT);
ValidationFeatures.enabledValidationFeatureCount = (uint32)ValidationFeaturesEnabled.Num();
ValidationFeatures.pEnabledValidationFeatures = ValidationFeaturesEnabled.GetData();
AddToPNext(InstanceCreateInfo, ValidationFeatures);
}
#endif
}
private:
VkValidationFeaturesEXT ValidationFeatures;
TArray<VkValidationFeatureEnableEXT> ValidationFeaturesEnabled;
};
FVulkanInstanceExtensionArray FVulkanInstanceExtension::GetUESupportedInstanceExtensions(uint32 ApiVersion)
{
FVulkanInstanceExtensionArray OutUEInstanceExtensions;
// Generic simple extensions :
OutUEInstanceExtensions.Add(MakeUnique<FVulkanInstanceExtension>(VK_KHR_SURFACE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED));
OutUEInstanceExtensions.Add(MakeUnique<FVulkanInstanceExtension>(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, VULKAN_EXTENSION_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED));
// Debug extensions :
OutUEInstanceExtensions.Add(MakeUnique<FVulkanInstanceExtension>(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VULKAN_HAS_DEBUGGING_ENABLED, VULKAN_EXTENSION_NOT_PROMOTED, nullptr, FVulkanExtensionBase::ManuallyActivate));
// Extensions with custom classes :
OutUEInstanceExtensions.Add(MakeUnique<FVulkanEXTValidationFeaturesExtension>());
// Add in platform specific extensions
FVulkanPlatform::GetInstanceExtensions(OutUEInstanceExtensions);
// Helper function to go through a list of extensions and activate them (or add them)
auto ActivateExternalExtensions = [&] (TArray<const ANSICHAR*> Extensions, const TCHAR* Requester) {
for (const ANSICHAR* ExtensionName : Extensions)
{
const int32 ExtensionIndex = FindExtension(OutUEInstanceExtensions, ExtensionName);
if (ExtensionIndex == INDEX_NONE)
{
OutUEInstanceExtensions.Add(MakeUnique<FVulkanInstanceExtension>(ExtensionName, VULKAN_EXTENSION_ENABLED));
UE_LOG(LogVulkanRHI, Warning, TEXT("%s requested instance extension [%s] isn't part of the engine's core extension list. Adding it on-the-fly..."), Requester, ANSI_TO_TCHAR(ExtensionName));
}
else
{
OutUEInstanceExtensions[ExtensionIndex]->SetActivated();
}
}
};
// Add HMD requested extensions
{
if (IHeadMountedDisplayModule::IsAvailable())
{
FVulkanDynamicRHI::HMDVulkanExtensions = IHeadMountedDisplayModule::Get().GetVulkanExtensions();
}
if (FVulkanDynamicRHI::HMDVulkanExtensions.IsValid())
{
TArray<const ANSICHAR*> HMDExtensions;
FVulkanDynamicRHI::HMDVulkanExtensions->GetVulkanInstanceExtensionsRequired(HMDExtensions);
ActivateExternalExtensions(HMDExtensions, TEXT("HMD"));
}
}
// Add extensions added outside the RHI (eg plugins)
ActivateExternalExtensions(ExternalExtensions, TEXT("Externally"));
// Now that all the extensions are listed, update their support flags
FlagExtensionSupport(GetDriverSupportedInstanceExtensions(), OutUEInstanceExtensions, ApiVersion, TEXT("instance"));
return OutUEInstanceExtensions;
}