2022-10-22 01:51:26 +00:00
/*
* Copyright ( c ) 2014 - 2021 , NVIDIA CORPORATION . All rights reserved .
2023-03-06 19:06:15 +00:00
* Copyright ( C ) 2022 Stephen Pridham ( id Tech 4 x integration )
* Copyright ( C ) 2023 Stephen Saunders ( id Tech 4 x integration )
* Copyright ( C ) 2023 Robert Beckebans ( id Tech 4 x integration )
2022-10-22 01:51:26 +00:00
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE .
*/
2024-02-23 13:46:04 +00:00
// SRS - Can now enable PCH here due to updated nvrhi CMakeLists.txt that makes Vulkan-Headers private
# include <precompiled.h>
# pragma hdrstop
2022-10-22 01:51:26 +00:00
# include <string>
# include <queue>
# include <unordered_set>
2022-12-24 19:10:55 +00:00
# include "renderer/RenderCommon.h"
2023-11-30 17:26:38 +00:00
# include "framework/Common_local.h"
2022-10-22 01:51:26 +00:00
# include <sys/DeviceManager.h>
# include <nvrhi/vulkan.h>
2024-02-04 06:41:33 +00:00
# define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
# include <vulkan/vulkan.hpp>
2023-05-13 14:22:52 +00:00
// SRS - optionally needed for MoltenVK runtime config visibility
2024-01-30 01:58:28 +00:00
# if defined(__APPLE__)
2024-02-04 15:40:18 +00:00
# if defined( USE_MoltenVK )
#if 0
# include <MoltenVK/mvk_vulkan.h>
# include <MoltenVK/mvk_config.h> // SRS - will eventually move to these mvk include files for MoltenVK >= 1.2.7 / SDK >= 1.3.275.0
# else
# include <MoltenVK/vk_mvk_moltenvk.h> // SRS - now deprecated, but provides backwards compatibility for MoltenVK < 1.2.7 / SDK < 1.3.275.0
# endif
# endif
# if defined( VK_EXT_layer_settings ) || defined( USE_MoltenVK )
idCVar r_mvkSynchronousQueueSubmits ( " r_mvkSynchronousQueueSubmits " , " 0 " , CVAR_BOOL | CVAR_INIT , " Use MoltenVK's synchronous queue submit option. " ) ;
idCVar r_mvkUseMetalArgumentBuffers ( " r_mvkUseMetalArgumentBuffers " , " 2 " , CVAR_INTEGER | CVAR_INIT , " Use MoltenVK's Metal argument buffers option (0=Off, 1=Always On, 2=On when VK_EXT_descriptor_indexing enabled) " , 0, 2 ) ;
# endif
2022-12-12 23:13:55 +00:00
# endif
2022-10-22 01:51:26 +00:00
# include <nvrhi/validation.h>
2024-01-17 04:56:32 +00:00
# include <libs/optick/optick.h>
2022-10-22 01:51:26 +00:00
2023-02-14 00:06:41 +00:00
# if defined( USE_AMD_ALLOCATOR )
2023-02-22 08:48:00 +00:00
# define VMA_IMPLEMENTATION
# define VMA_STATIC_VULKAN_FUNCTIONS 0
# define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
# include "vk_mem_alloc.h"
2023-02-14 00:06:41 +00:00
2023-02-22 08:48:00 +00:00
VmaAllocator m_VmaAllocator = nullptr ;
2023-02-14 00:06:41 +00:00
2023-02-22 08:48:00 +00:00
idCVar r_vmaDeviceLocalMemoryMB ( " r_vmaDeviceLocalMemoryMB " , " 256 " , CVAR_INTEGER | CVAR_INIT , " Size of VMA allocation block for gpu memory. " ) ;
2023-02-14 00:06:41 +00:00
# endif
2023-06-01 03:09:33 +00:00
idCVar r_preferFastSync ( " r_preferFastSync " , " 1 " , CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL , " Prefer Fast Sync/no-tearing in place of VSync off/tearing (Vulkan only) " ) ;
2022-10-22 01:51:26 +00:00
// Define the Vulkan dynamic dispatcher - this needs to occur in exactly one cpp file in the program.
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
2024-03-22 02:19:12 +00:00
# if defined(__APPLE__) && defined( USE_MoltenVK )
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 9 ) && USE_OPTICK
2024-04-02 14:46:08 +00:00
static bool optickCapturing = false ;
2024-03-22 02:19:12 +00:00
2024-04-02 14:46:08 +00:00
// SRS - Optick callback function for notification of state changes
static bool optickStateChangedCallback ( Optick : : State : : Type state )
{
switch ( state )
{
case Optick : : State : : START_CAPTURE :
optickCapturing = true ;
break ;
2024-03-22 02:19:12 +00:00
2024-04-02 14:46:08 +00:00
case Optick : : State : : STOP_CAPTURE :
case Optick : : State : : CANCEL_CAPTURE :
optickCapturing = false ;
break ;
2024-03-22 02:19:12 +00:00
2024-04-02 14:46:08 +00:00
default :
break ;
2024-03-22 02:19:12 +00:00
}
2024-04-02 14:46:08 +00:00
return true ;
}
2024-03-22 02:19:12 +00:00
# endif
# endif
2022-10-22 01:51:26 +00:00
class DeviceManager_VK : public DeviceManager
{
public :
[[nodiscard]] nvrhi : : IDevice * GetDevice ( ) const override
{
if ( m_ValidationLayer )
{
return m_ValidationLayer ;
}
return m_NvrhiDevice ;
}
[[nodiscard]] nvrhi : : GraphicsAPI GetGraphicsAPI ( ) const override
{
return nvrhi : : GraphicsAPI : : VULKAN ;
}
protected :
bool CreateDeviceAndSwapChain ( ) override ;
void DestroyDeviceAndSwapChain ( ) override ;
void ResizeSwapChain ( ) override
{
if ( m_VulkanDevice )
{
destroySwapChain ( ) ;
createSwapChain ( ) ;
}
}
nvrhi : : ITexture * GetCurrentBackBuffer ( ) override
{
return m_SwapChainImages [ m_SwapChainIndex ] . rhiHandle ;
}
nvrhi : : ITexture * GetBackBuffer ( uint32_t index ) override
{
if ( index < m_SwapChainImages . size ( ) )
{
return m_SwapChainImages [ index ] . rhiHandle ;
}
return nullptr ;
}
uint32_t GetCurrentBackBufferIndex ( ) override
{
return m_SwapChainIndex ;
}
uint32_t GetBackBufferCount ( ) override
{
return uint32_t ( m_SwapChainImages . size ( ) ) ;
}
void BeginFrame ( ) override ;
void EndFrame ( ) override ;
void Present ( ) override ;
const char * GetRendererString ( ) const override
{
return m_RendererString . c_str ( ) ;
}
bool IsVulkanInstanceExtensionEnabled ( const char * extensionName ) const override
{
return enabledExtensions . instance . find ( extensionName ) ! = enabledExtensions . instance . end ( ) ;
}
bool IsVulkanDeviceExtensionEnabled ( const char * extensionName ) const override
{
return enabledExtensions . device . find ( extensionName ) ! = enabledExtensions . device . end ( ) ;
}
bool IsVulkanLayerEnabled ( const char * layerName ) const override
{
return enabledExtensions . layers . find ( layerName ) ! = enabledExtensions . layers . end ( ) ;
}
void GetEnabledVulkanInstanceExtensions ( std : : vector < std : : string > & extensions ) const override
{
for ( const auto & ext : enabledExtensions . instance )
{
extensions . push_back ( ext ) ;
}
}
void GetEnabledVulkanDeviceExtensions ( std : : vector < std : : string > & extensions ) const override
{
for ( const auto & ext : enabledExtensions . device )
{
extensions . push_back ( ext ) ;
}
}
void GetEnabledVulkanLayers ( std : : vector < std : : string > & layers ) const override
{
for ( const auto & ext : enabledExtensions . layers )
{
layers . push_back ( ext ) ;
}
}
private :
bool createInstance ( ) ;
bool createWindowSurface ( ) ;
void installDebugCallback ( ) ;
bool pickPhysicalDevice ( ) ;
bool findQueueFamilies ( vk : : PhysicalDevice physicalDevice , vk : : SurfaceKHR surface ) ;
bool createDevice ( ) ;
bool createSwapChain ( ) ;
void destroySwapChain ( ) ;
struct VulkanExtensionSet
{
std : : unordered_set < std : : string > instance ;
std : : unordered_set < std : : string > layers ;
std : : unordered_set < std : : string > device ;
} ;
// minimal set of required extensions
VulkanExtensionSet enabledExtensions =
{
// instance
{
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
} ,
// layers
2022-11-08 20:21:50 +00:00
{ } ,
2022-10-22 01:51:26 +00:00
// device
{
VK_KHR_SWAPCHAIN_EXTENSION_NAME ,
VK_KHR_MAINTENANCE1_EXTENSION_NAME ,
2023-05-13 14:22:52 +00:00
# if defined(__APPLE__) && defined( VK_KHR_portability_subset )
// SRS - This is required for using the MoltenVK portability subset implementation on macOS
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
2022-10-22 01:51:26 +00:00
# endif
} ,
} ;
// optional extensions
VulkanExtensionSet optionalExtensions =
{
// instance
{
2023-12-28 19:28:20 +00:00
# if defined(__APPLE__)
# if defined( VK_KHR_portability_enumeration )
2022-10-24 03:31:12 +00:00
// SRS - This is optional since it only became manadatory with Vulkan SDK 1.3.216.0 or later
2022-10-22 01:51:26 +00:00
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME ,
2023-12-28 19:28:20 +00:00
# endif
# if defined( VK_EXT_layer_settings )
2024-01-17 04:56:32 +00:00
// SRS - This is optional since implemented only for MoltenVK 1.2.7 / SDK 1.3.275.0 or later
2023-12-28 19:28:20 +00:00
VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ,
# endif
2022-10-22 01:51:26 +00:00
# endif
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME ,
VK_EXT_DEBUG_UTILS_EXTENSION_NAME
} ,
// layers
2023-10-27 17:26:13 +00:00
{ } ,
2022-10-22 01:51:26 +00:00
// device
{
VK_EXT_DEBUG_MARKER_EXTENSION_NAME ,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME ,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME ,
VK_NV_MESH_SHADER_EXTENSION_NAME ,
2022-11-08 20:21:50 +00:00
VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME ,
2023-05-05 02:36:46 +00:00
# if USE_OPTICK
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME ,
# endif
2023-10-19 16:21:37 +00:00
# if defined( VK_KHR_format_feature_flags2 )
2023-10-17 18:07:53 +00:00
VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME ,
2023-11-30 17:26:38 +00:00
# endif
2023-12-28 19:28:20 +00:00
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME ,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME
2022-10-22 01:51:26 +00:00
} ,
} ;
2022-10-24 03:31:12 +00:00
2022-10-22 01:51:26 +00:00
std : : unordered_set < std : : string > m_RayTracingExtensions =
{
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME ,
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME ,
VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME ,
VK_KHR_RAY_QUERY_EXTENSION_NAME ,
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME
} ;
std : : string m_RendererString ;
vk : : Instance m_VulkanInstance ;
vk : : DebugReportCallbackEXT m_DebugReportCallback ;
vk : : PhysicalDevice m_VulkanPhysicalDevice ;
int m_GraphicsQueueFamily = - 1 ;
int m_ComputeQueueFamily = - 1 ;
int m_TransferQueueFamily = - 1 ;
int m_PresentQueueFamily = - 1 ;
vk : : Device m_VulkanDevice ;
vk : : Queue m_GraphicsQueue ;
vk : : Queue m_ComputeQueue ;
vk : : Queue m_TransferQueue ;
vk : : Queue m_PresentQueue ;
vk : : SurfaceKHR m_WindowSurface ;
vk : : SurfaceFormatKHR m_SwapChainFormat ;
vk : : SwapchainKHR m_SwapChain ;
struct SwapChainImage
{
vk : : Image image ;
nvrhi : : TextureHandle rhiHandle ;
} ;
std : : vector < SwapChainImage > m_SwapChainImages ;
uint32_t m_SwapChainIndex = uint32_t ( - 1 ) ;
nvrhi : : vulkan : : DeviceHandle m_NvrhiDevice ;
nvrhi : : DeviceHandle m_ValidationLayer ;
2023-10-04 16:24:49 +00:00
//nvrhi::CommandListHandle m_BarrierCommandList; // SRS - no longer needed
2023-03-27 19:51:43 +00:00
std : : queue < vk : : Semaphore > m_PresentSemaphoreQueue ;
2022-10-22 01:51:26 +00:00
vk : : Semaphore m_PresentSemaphore ;
2023-02-28 23:02:45 +00:00
nvrhi : : EventQueryHandle m_FrameWaitQuery ;
2022-10-22 01:51:26 +00:00
2023-06-01 03:09:33 +00:00
// SRS - flags indicating support for various Vulkan surface presentation modes
bool enablePModeMailbox = false ; // r_swapInterval = 0 (defaults to eImmediate if not available)
bool enablePModeImmediate = false ; // r_swapInterval = 0 (defaults to eFifo if not available)
bool enablePModeFifoRelaxed = false ; // r_swapInterval = 1 (defaults to eFifo if not available)
2022-11-18 04:42:06 +00:00
2023-05-13 14:22:52 +00:00
// SRS - flag indicating support for presentation timing via VK_GOOGLE_display_timing extension
bool displayTimingEnabled = false ;
2022-10-22 01:51:26 +00:00
2023-12-28 19:28:20 +00:00
// SRS - slot for Vulkan device API version at runtime (initialize to Vulkan build version)
uint32_t m_DeviceApiVersion = VK_HEADER_VERSION_COMPLETE ;
2024-01-17 04:56:32 +00:00
// SRS - function pointer for initing Vulkan DynamicLoader, VMA, Optick, and MoltenVK functions
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr ;
# if defined(__APPLE__) && defined( USE_MoltenVK )
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 )
// SRS - function pointer for retrieving MoltenVK advanced performance statistics
PFN_vkGetPerformanceStatisticsMVK vkGetPerformanceStatisticsMVK = nullptr ;
# endif
2024-03-22 02:19:12 +00:00
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 9 ) && USE_OPTICK
// SRS - Optick event storage for MoltenVK's Vulkan-to-Metal encoding thread
Optick : : EventStorage * mvkAcquireEventStorage ;
Optick : : EventStorage * mvkSubmitEventStorage ;
Optick : : EventStorage * mvkEncodeEventStorage ;
Optick : : EventDescription * mvkAcquireEventDesc ;
Optick : : EventDescription * mvkSubmitEventDesc ;
Optick : : EventDescription * mvkEncodeEventDesc ;
int64_t mvkLatestSubmitTime = 0 ;
int64_t mvkPreviousSubmitTime = 0 ;
int64_t mvkPreviousSubmitWaitTime = 0 ;
double mvkPreviousAcquireHash = 0.0 ;
# endif
2024-01-17 04:56:32 +00:00
# endif
2022-10-22 01:51:26 +00:00
private :
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback (
VkDebugReportFlagsEXT flags ,
VkDebugReportObjectTypeEXT objType ,
uint64_t obj ,
size_t location ,
int32_t code ,
const char * layerPrefix ,
const char * msg ,
void * userData )
{
const DeviceManager_VK * manager = ( const DeviceManager_VK * ) userData ;
if ( manager )
{
2023-03-06 16:05:43 +00:00
const auto & ignored = manager - > m_DeviceParams . ignoredVulkanValidationMessageLocations ;
2022-10-22 01:51:26 +00:00
const auto found = std : : find ( ignored . begin ( ) , ignored . end ( ) , location ) ;
if ( found ! = ignored . end ( ) )
{
return VK_FALSE ;
}
}
if ( flags & VK_DEBUG_REPORT_ERROR_BIT_EXT )
{
idLib : : Printf ( " [Vulkan] ERROR location=0x%zx code=%d, layerPrefix='%s'] %s \n " , location , code , layerPrefix , msg ) ;
}
else if ( flags & VK_DEBUG_REPORT_WARNING_BIT_EXT )
{
idLib : : Printf ( " [Vulkan] WARNING location=0x%zx code=%d, layerPrefix='%s'] %s \n " , location , code , layerPrefix , msg ) ;
}
else if ( flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT )
{
idLib : : Printf ( " [Vulkan] PERFORMANCE WARNING location=0x%zx code=%d, layerPrefix='%s'] %s \n " , location , code , layerPrefix , msg ) ;
}
else if ( flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT )
{
idLib : : Printf ( " [Vulkan] INFO location=0x%zx code=%d, layerPrefix='%s'] %s \n " , location , code , layerPrefix , msg ) ;
}
else if ( flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT )
{
idLib : : Printf ( " [Vulkan] DEBUG location=0x%zx code=%d, layerPrefix='%s'] %s \n " , location , code , layerPrefix , msg ) ;
}
return VK_FALSE ;
}
} ;
static std : : vector < const char * > stringSetToVector ( const std : : unordered_set < std : : string > & set )
{
std : : vector < const char * > ret ;
for ( const auto & s : set )
{
ret . push_back ( s . c_str ( ) ) ;
}
return ret ;
}
template < typename T >
static std : : vector < T > setToVector ( const std : : unordered_set < T > & set )
{
std : : vector < T > ret ;
for ( const auto & s : set )
{
ret . push_back ( s ) ;
}
return ret ;
}
bool DeviceManager_VK : : createInstance ( )
{
2022-10-24 03:31:12 +00:00
# if defined( VULKAN_USE_PLATFORM_SDL )
2022-10-22 01:51:26 +00:00
// SRS - Populate enabledExtensions with required SDL instance extensions
auto sdl_instanceExtensions = get_required_extensions ( ) ;
for ( auto instanceExtension : sdl_instanceExtensions )
{
enabledExtensions . instance . insert ( instanceExtension ) ;
}
2022-10-24 03:31:12 +00:00
# elif defined( VK_USE_PLATFORM_WIN32_KHR )
enabledExtensions . instance . insert ( VK_KHR_SURFACE_EXTENSION_NAME ) ;
enabledExtensions . instance . insert ( VK_KHR_WIN32_SURFACE_EXTENSION_NAME ) ;
# endif
2022-10-22 01:51:26 +00:00
// add instance extensions requested by the user
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . requiredVulkanInstanceExtensions )
2022-10-22 01:51:26 +00:00
{
enabledExtensions . instance . insert ( name ) ;
}
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . optionalVulkanInstanceExtensions )
2022-10-22 01:51:26 +00:00
{
optionalExtensions . instance . insert ( name ) ;
}
// add layers requested by the user
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . requiredVulkanLayers )
2022-10-22 01:51:26 +00:00
{
enabledExtensions . layers . insert ( name ) ;
}
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . optionalVulkanLayers )
2022-10-22 01:51:26 +00:00
{
optionalExtensions . layers . insert ( name ) ;
}
std : : unordered_set < std : : string > requiredExtensions = enabledExtensions . instance ;
// figure out which optional extensions are supported
for ( const auto & instanceExt : vk : : enumerateInstanceExtensionProperties ( ) )
{
const std : : string name = instanceExt . extensionName ;
if ( optionalExtensions . instance . find ( name ) ! = optionalExtensions . instance . end ( ) )
{
enabledExtensions . instance . insert ( name ) ;
}
requiredExtensions . erase ( name ) ;
}
if ( ! requiredExtensions . empty ( ) )
{
std : : stringstream ss ;
ss < < " Cannot create a Vulkan instance because the following required extension(s) are not supported: " ;
for ( const auto & ext : requiredExtensions )
{
ss < < std : : endl < < " - " < < ext ;
}
common - > FatalError ( " %s " , ss . str ( ) . c_str ( ) ) ;
return false ;
}
common - > Printf ( " Enabled Vulkan instance extensions: \n " ) ;
for ( const auto & ext : enabledExtensions . instance )
{
common - > Printf ( " %s \n " , ext . c_str ( ) ) ;
}
std : : unordered_set < std : : string > requiredLayers = enabledExtensions . layers ;
2023-12-28 19:28:20 +00:00
auto instanceVersion = vk : : enumerateInstanceVersion ( ) ;
2022-10-22 01:51:26 +00:00
for ( const auto & layer : vk : : enumerateInstanceLayerProperties ( ) )
{
const std : : string name = layer . layerName ;
if ( optionalExtensions . layers . find ( name ) ! = optionalExtensions . layers . end ( ) )
{
enabledExtensions . layers . insert ( name ) ;
}
2023-12-28 19:28:20 +00:00
# if defined(__APPLE__) && !defined( USE_MoltenVK )
2023-10-27 17:26:13 +00:00
// SRS - Vulkan SDK < 1.3.268.1 does not have native VK_KHR_synchronization2 support on macOS, add Khronos layer to emulate
2023-12-28 19:28:20 +00:00
else if ( name = = " VK_LAYER_KHRONOS_synchronization2 " & & instanceVersion < VK_MAKE_API_VERSION ( 0 , 1 , 3 , 268 ) )
2023-10-27 17:26:13 +00:00
{
enabledExtensions . layers . insert ( name ) ;
}
# endif
2022-10-22 01:51:26 +00:00
requiredLayers . erase ( name ) ;
}
2022-10-24 03:31:12 +00:00
2022-10-22 01:51:26 +00:00
if ( ! requiredLayers . empty ( ) )
{
std : : stringstream ss ;
ss < < " Cannot create a Vulkan instance because the following required layer(s) are not supported: " ;
for ( const auto & ext : requiredLayers )
{
ss < < std : : endl < < " - " < < ext ;
}
common - > FatalError ( " %s " , ss . str ( ) . c_str ( ) ) ;
return false ;
}
common - > Printf ( " Enabled Vulkan layers: \n " ) ;
for ( const auto & layer : enabledExtensions . layers )
{
common - > Printf ( " %s \n " , layer . c_str ( ) ) ;
}
auto instanceExtVec = stringSetToVector ( enabledExtensions . instance ) ;
auto layerVec = stringSetToVector ( enabledExtensions . layers ) ;
auto applicationInfo = vk : : ApplicationInfo ( )
. setApiVersion ( VK_MAKE_VERSION ( 1 , 2 , 0 ) ) ;
// create the vulkan instance
vk : : InstanceCreateInfo info = vk : : InstanceCreateInfo ( )
. setEnabledLayerCount ( uint32_t ( layerVec . size ( ) ) )
. setPpEnabledLayerNames ( layerVec . data ( ) )
. setEnabledExtensionCount ( uint32_t ( instanceExtVec . size ( ) ) )
. setPpEnabledExtensionNames ( instanceExtVec . data ( ) )
2022-10-24 03:31:12 +00:00
. setPApplicationInfo ( & applicationInfo ) ;
2023-12-28 19:28:20 +00:00
# if defined(__APPLE__)
# if defined( VK_KHR_portability_enumeration )
2022-10-24 03:31:12 +00:00
if ( enabledExtensions . instance . find ( VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME ) ! = enabledExtensions . instance . end ( ) )
{
info . setFlags ( vk : : InstanceCreateFlagBits ( VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR ) ) ;
}
2023-12-28 19:28:20 +00:00
# endif
2024-01-30 01:58:28 +00:00
# if defined( VK_EXT_layer_settings )
// SRS - set MoltenVK runtime configuration parameters on macOS via standardized VK_EXT_layer_settings extension
2023-12-28 19:28:20 +00:00
std : : vector < vk : : LayerSettingEXT > layerSettings ;
vk : : LayerSettingsCreateInfoEXT layerSettingsCreateInfo ;
const vk : : Bool32 valueTrue = vk : : True , valueFalse = vk : : False ;
2024-01-30 01:58:28 +00:00
const int32_t useMetalArgumentBuffers = r_mvkUseMetalArgumentBuffers . GetInteger ( ) ;
const float timestampPeriodLowPassAlpha = 1.0 ;
2023-12-28 19:28:20 +00:00
if ( enabledExtensions . instance . find ( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) ! = enabledExtensions . instance . end ( ) )
{
// SRS - use MoltenVK layer for configuration via VK_EXT_layer_settings extension
2024-01-30 01:58:28 +00:00
vk : : LayerSettingEXT layerSetting = { " MoltenVK " , " " , vk : : LayerSettingTypeEXT ( 0 ) , 1 , nullptr } ;
2023-12-28 19:28:20 +00:00
// SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR()
layerSetting . pSettingName = " MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS " ;
layerSetting . type = vk : : LayerSettingTypeEXT : : eBool32 ;
layerSetting . pValues = r_mvkSynchronousQueueSubmits . GetBool ( ) ? & valueTrue : & valueFalse ;
layerSettings . push_back ( layerSetting ) ;
// SRS - Enable MoltenVK's image view swizzle feature in case we don't have native image view swizzle
layerSetting . pSettingName = " MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE " ;
layerSetting . type = vk : : LayerSettingTypeEXT : : eBool32 ;
layerSetting . pValues = & valueTrue ;
layerSettings . push_back ( layerSetting ) ;
// SRS - Turn MoltenVK's Metal argument buffer feature on for descriptor indexing only
layerSetting . pSettingName = " MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS " ;
layerSetting . type = vk : : LayerSettingTypeEXT : : eInt32 ;
layerSetting . pValues = & useMetalArgumentBuffers ;
layerSettings . push_back ( layerSetting ) ;
// SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration
layerSetting . pSettingName = " MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA " ;
layerSetting . type = vk : : LayerSettingTypeEXT : : eFloat32 ;
layerSetting . pValues = & timestampPeriodLowPassAlpha ;
layerSettings . push_back ( layerSetting ) ;
2024-01-30 01:58:28 +00:00
// SRS - Only enable MoltenVK performance tracking if using API and available based on version
# if defined( USE_MoltenVK )
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 )
2023-12-28 19:28:20 +00:00
// SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS
layerSetting . pSettingName = " MVK_CONFIG_PERFORMANCE_TRACKING " ;
layerSetting . type = vk : : LayerSettingTypeEXT : : eBool32 ;
layerSetting . pValues = & valueTrue ;
layerSettings . push_back ( layerSetting ) ;
2024-01-30 01:58:28 +00:00
# endif
# endif
2023-12-28 19:28:20 +00:00
layerSettingsCreateInfo . settingCount = uint32_t ( layerSettings . size ( ) ) ;
layerSettingsCreateInfo . pSettings = layerSettings . data ( ) ;
info . setPNext ( & layerSettingsCreateInfo ) ;
}
# endif
2022-10-24 03:31:12 +00:00
# endif
2022-10-22 01:51:26 +00:00
const vk : : Result res = vk : : createInstance ( & info , nullptr , & m_VulkanInstance ) ;
if ( res ! = vk : : Result : : eSuccess )
{
2024-02-03 16:26:31 +00:00
common - > FatalError ( " Failed to create a Vulkan instance, error code = %s " , nvrhi : : vulkan : : resultToString ( ( VkResult ) res ) ) ;
2022-10-22 01:51:26 +00:00
return false ;
}
VULKAN_HPP_DEFAULT_DISPATCHER . init ( m_VulkanInstance ) ;
return true ;
}
void DeviceManager_VK : : installDebugCallback ( )
{
auto info = vk : : DebugReportCallbackCreateInfoEXT ( )
. setFlags ( vk : : DebugReportFlagBitsEXT : : eError |
vk : : DebugReportFlagBitsEXT : : eWarning |
// vk::DebugReportFlagBitsEXT::eInformation |
vk : : DebugReportFlagBitsEXT : : ePerformanceWarning )
. setPfnCallback ( vulkanDebugCallback )
. setPUserData ( this ) ;
2022-12-16 18:42:12 +00:00
const vk : : Result res = m_VulkanInstance . createDebugReportCallbackEXT ( & info , nullptr , & m_DebugReportCallback ) ;
2022-10-22 01:51:26 +00:00
assert ( res = = vk : : Result : : eSuccess ) ;
}
bool DeviceManager_VK : : pickPhysicalDevice ( )
{
2024-02-03 16:26:31 +00:00
vk : : Format requestedFormat = vk : : Format ( nvrhi : : vulkan : : convertFormat ( m_DeviceParams . swapChainFormat ) ) ;
2023-03-06 16:05:43 +00:00
vk : : Extent2D requestedExtent ( m_DeviceParams . backBufferWidth , m_DeviceParams . backBufferHeight ) ;
2022-10-22 01:51:26 +00:00
auto devices = m_VulkanInstance . enumeratePhysicalDevices ( ) ;
// Start building an error message in case we cannot find a device.
std : : stringstream errorStream ;
errorStream < < " Cannot find a Vulkan device that supports all the required extensions and properties. " ;
// build a list of GPUs
std : : vector < vk : : PhysicalDevice > discreteGPUs ;
std : : vector < vk : : PhysicalDevice > otherGPUs ;
for ( const auto & dev : devices )
{
auto prop = dev . getProperties ( ) ;
errorStream < < std : : endl < < prop . deviceName . data ( ) < < " : " ;
// check that all required device extensions are present
std : : unordered_set < std : : string > requiredExtensions = enabledExtensions . device ;
auto deviceExtensions = dev . enumerateDeviceExtensionProperties ( ) ;
for ( const auto & ext : deviceExtensions )
{
requiredExtensions . erase ( std : : string ( ext . extensionName . data ( ) ) ) ;
}
bool deviceIsGood = true ;
if ( ! requiredExtensions . empty ( ) )
{
// device is missing one or more required extensions
for ( const auto & ext : requiredExtensions )
{
errorStream < < std : : endl < < " - missing " < < ext ;
}
deviceIsGood = false ;
}
auto deviceFeatures = dev . getFeatures ( ) ;
if ( ! deviceFeatures . samplerAnisotropy )
{
// device is a toaster oven
errorStream < < std : : endl < < " - does not support samplerAnisotropy " ;
deviceIsGood = false ;
}
if ( ! deviceFeatures . textureCompressionBC )
{
errorStream < < std : : endl < < " - does not support textureCompressionBC " ;
deviceIsGood = false ;
}
// check that this device supports our intended swap chain creation parameters
auto surfaceCaps = dev . getSurfaceCapabilitiesKHR ( m_WindowSurface ) ;
auto surfaceFmts = dev . getSurfaceFormatsKHR ( m_WindowSurface ) ;
auto surfacePModes = dev . getSurfacePresentModesKHR ( m_WindowSurface ) ;
2023-06-06 15:42:22 +00:00
// SRS/Ricardo Garcia rg3 - clamp swapChainBufferCount to the min/max capabilities of the surface
m_DeviceParams . swapChainBufferCount = Max ( surfaceCaps . minImageCount , m_DeviceParams . swapChainBufferCount ) ;
m_DeviceParams . swapChainBufferCount = surfaceCaps . maxImageCount > 0 ? Min ( m_DeviceParams . swapChainBufferCount , surfaceCaps . maxImageCount ) : m_DeviceParams . swapChainBufferCount ;
2022-10-22 01:51:26 +00:00
2023-06-09 18:48:36 +00:00
/* SRS - Don't check extent here since window manager surfaceCaps may restrict extent to something smaller than requested
- Instead , check and clamp extent to window manager surfaceCaps during swap chain creation inside createSwapChain ( )
2022-10-22 01:51:26 +00:00
if ( surfaceCaps . minImageExtent . width > requestedExtent . width | |
surfaceCaps . minImageExtent . height > requestedExtent . height | |
surfaceCaps . maxImageExtent . width < requestedExtent . width | |
surfaceCaps . maxImageExtent . height < requestedExtent . height )
{
errorStream < < std : : endl < < " - cannot support the requested swap chain size: " ;
errorStream < < " requested " < < requestedExtent . width < < " x " < < requestedExtent . height < < " , " ;
errorStream < < " available " < < surfaceCaps . minImageExtent . width < < " x " < < surfaceCaps . minImageExtent . height ;
errorStream < < " - " < < surfaceCaps . maxImageExtent . width < < " x " < < surfaceCaps . maxImageExtent . height ;
deviceIsGood = false ;
}
2023-06-09 18:48:36 +00:00
*/
2022-10-22 01:51:26 +00:00
bool surfaceFormatPresent = false ;
for ( const vk : : SurfaceFormatKHR & surfaceFmt : surfaceFmts )
{
if ( surfaceFmt . format = = requestedFormat )
{
surfaceFormatPresent = true ;
break ;
}
}
if ( ! surfaceFormatPresent )
{
// can't create a swap chain using the format requested
errorStream < < std : : endl < < " - does not support the requested swap chain format " ;
deviceIsGood = false ;
}
2023-06-01 03:09:33 +00:00
if ( find ( surfacePModes . begin ( ) , surfacePModes . end ( ) , vk : : PresentModeKHR : : eFifo ) = = surfacePModes . end ( ) )
2022-11-18 04:42:06 +00:00
{
2023-06-01 03:09:33 +00:00
// this should never happen since eFifo is mandatory according to the Vulkan spec
errorStream < < std : : endl < < " - does not support the required surface present modes " ;
2022-11-18 04:42:06 +00:00
deviceIsGood = false ;
}
2022-10-22 01:51:26 +00:00
if ( ! findQueueFamilies ( dev , m_WindowSurface ) )
{
// device doesn't have all the queue families we need
errorStream < < std : : endl < < " - does not support the necessary queue types " ;
deviceIsGood = false ;
}
// check that we can present from the graphics queue
uint32_t canPresent = dev . getSurfaceSupportKHR ( m_GraphicsQueueFamily , m_WindowSurface ) ;
if ( ! canPresent )
{
errorStream < < std : : endl < < " - cannot present " ;
deviceIsGood = false ;
}
if ( ! deviceIsGood )
{
continue ;
}
if ( prop . deviceType = = vk : : PhysicalDeviceType : : eDiscreteGpu )
{
discreteGPUs . push_back ( dev ) ;
}
else
{
otherGPUs . push_back ( dev ) ;
}
}
// pick the first discrete GPU if it exists, otherwise the first integrated GPU
if ( ! discreteGPUs . empty ( ) )
{
m_VulkanPhysicalDevice = discreteGPUs [ 0 ] ;
return true ;
}
if ( ! otherGPUs . empty ( ) )
{
m_VulkanPhysicalDevice = otherGPUs [ 0 ] ;
return true ;
}
common - > FatalError ( " %s " , errorStream . str ( ) . c_str ( ) ) ;
return false ;
}
bool DeviceManager_VK : : findQueueFamilies ( vk : : PhysicalDevice physicalDevice , vk : : SurfaceKHR surface )
{
auto props = physicalDevice . getQueueFamilyProperties ( ) ;
for ( int i = 0 ; i < int ( props . size ( ) ) ; i + + )
{
const auto & queueFamily = props [ i ] ;
if ( m_GraphicsQueueFamily = = - 1 )
{
if ( queueFamily . queueCount > 0 & &
( queueFamily . queueFlags & vk : : QueueFlagBits : : eGraphics ) )
{
m_GraphicsQueueFamily = i ;
}
}
if ( m_ComputeQueueFamily = = - 1 )
{
if ( queueFamily . queueCount > 0 & &
( queueFamily . queueFlags & vk : : QueueFlagBits : : eCompute ) & &
! ( queueFamily . queueFlags & vk : : QueueFlagBits : : eGraphics ) )
{
m_ComputeQueueFamily = i ;
}
}
if ( m_TransferQueueFamily = = - 1 )
{
if ( queueFamily . queueCount > 0 & &
( queueFamily . queueFlags & vk : : QueueFlagBits : : eTransfer ) & &
! ( queueFamily . queueFlags & vk : : QueueFlagBits : : eCompute ) & &
! ( queueFamily . queueFlags & vk : : QueueFlagBits : : eGraphics ) )
{
m_TransferQueueFamily = i ;
}
}
if ( m_PresentQueueFamily = = - 1 )
{
vk : : Bool32 presentSupported ;
2022-10-24 03:31:12 +00:00
// SRS - Use portable implmentation for detecting presentation support vs. Windows-specific Vulkan call
2022-10-22 01:51:26 +00:00
if ( queueFamily . queueCount > 0 & &
2022-12-16 18:42:12 +00:00
physicalDevice . getSurfaceSupportKHR ( i , surface , & presentSupported ) = = vk : : Result : : eSuccess )
2022-10-22 01:51:26 +00:00
{
if ( presentSupported )
{
m_PresentQueueFamily = i ;
}
}
}
}
if ( m_GraphicsQueueFamily = = - 1 | |
m_PresentQueueFamily = = - 1 | |
2023-03-06 16:05:43 +00:00
( m_ComputeQueueFamily = = - 1 & & m_DeviceParams . enableComputeQueue ) | |
( m_TransferQueueFamily = = - 1 & & m_DeviceParams . enableCopyQueue ) )
2022-10-22 01:51:26 +00:00
{
return false ;
}
return true ;
}
bool DeviceManager_VK : : createDevice ( )
{
// figure out which optional extensions are supported
auto deviceExtensions = m_VulkanPhysicalDevice . enumerateDeviceExtensionProperties ( ) ;
for ( const auto & ext : deviceExtensions )
{
const std : : string name = ext . extensionName ;
if ( optionalExtensions . device . find ( name ) ! = optionalExtensions . device . end ( ) )
{
enabledExtensions . device . insert ( name ) ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableRayTracingExtensions & & m_RayTracingExtensions . find ( name ) ! = m_RayTracingExtensions . end ( ) )
2022-10-22 01:51:26 +00:00
{
enabledExtensions . device . insert ( name ) ;
}
}
bool accelStructSupported = false ;
bool bufferAddressSupported = false ;
bool rayPipelineSupported = false ;
bool rayQuerySupported = false ;
bool meshletsSupported = false ;
bool vrsSupported = false ;
2022-11-08 20:21:50 +00:00
bool sync2Supported = false ;
2022-10-22 01:51:26 +00:00
common - > Printf ( " Enabled Vulkan device extensions: \n " ) ;
for ( const auto & ext : enabledExtensions . device )
{
common - > Printf ( " %s \n " , ext . c_str ( ) ) ;
if ( ext = = VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME )
{
accelStructSupported = true ;
}
else if ( ext = = VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME )
{
// RB: only makes problems at the moment
bufferAddressSupported = true ;
}
else if ( ext = = VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME )
{
rayPipelineSupported = true ;
}
else if ( ext = = VK_KHR_RAY_QUERY_EXTENSION_NAME )
{
rayQuerySupported = true ;
}
else if ( ext = = VK_NV_MESH_SHADER_EXTENSION_NAME )
{
meshletsSupported = true ;
}
else if ( ext = = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME )
{
vrsSupported = true ;
}
2022-11-08 20:21:50 +00:00
else if ( ext = = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME )
{
sync2Supported = true ;
}
2023-05-05 02:36:46 +00:00
else if ( ext = = VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME )
{
2023-05-13 14:22:52 +00:00
displayTimingEnabled = true ;
2023-05-05 02:36:46 +00:00
}
2022-10-22 01:51:26 +00:00
}
std : : unordered_set < int > uniqueQueueFamilies =
{
m_GraphicsQueueFamily ,
m_PresentQueueFamily
} ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableComputeQueue )
2022-10-22 01:51:26 +00:00
{
uniqueQueueFamilies . insert ( m_ComputeQueueFamily ) ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableCopyQueue )
2022-10-22 01:51:26 +00:00
{
uniqueQueueFamilies . insert ( m_TransferQueueFamily ) ;
}
float priority = 1.f ;
std : : vector < vk : : DeviceQueueCreateInfo > queueDesc ;
for ( int queueFamily : uniqueQueueFamilies )
{
queueDesc . push_back ( vk : : DeviceQueueCreateInfo ( )
. setQueueFamilyIndex ( queueFamily )
. setQueueCount ( 1 )
. setPQueuePriorities ( & priority ) ) ;
}
auto accelStructFeatures = vk : : PhysicalDeviceAccelerationStructureFeaturesKHR ( )
. setAccelerationStructure ( true ) ;
auto bufferAddressFeatures = vk : : PhysicalDeviceBufferAddressFeaturesEXT ( )
. setBufferDeviceAddress ( true ) ;
auto rayPipelineFeatures = vk : : PhysicalDeviceRayTracingPipelineFeaturesKHR ( )
. setRayTracingPipeline ( true )
. setRayTraversalPrimitiveCulling ( true ) ;
auto rayQueryFeatures = vk : : PhysicalDeviceRayQueryFeaturesKHR ( )
. setRayQuery ( true ) ;
auto meshletFeatures = vk : : PhysicalDeviceMeshShaderFeaturesNV ( )
. setTaskShader ( true )
. setMeshShader ( true ) ;
2023-10-19 16:21:37 +00:00
// SRS - get/set shading rate features which are detected individually by nvrhi (not just at extension level)
vk : : PhysicalDeviceFeatures2 actualDeviceFeatures2 ;
vk : : PhysicalDeviceFragmentShadingRateFeaturesKHR fragmentShadingRateFeatures ;
actualDeviceFeatures2 . pNext = & fragmentShadingRateFeatures ;
m_VulkanPhysicalDevice . getFeatures2 ( & actualDeviceFeatures2 ) ;
2022-10-22 01:51:26 +00:00
auto vrsFeatures = vk : : PhysicalDeviceFragmentShadingRateFeaturesKHR ( )
2023-10-19 16:21:37 +00:00
. setPipelineFragmentShadingRate ( fragmentShadingRateFeatures . pipelineFragmentShadingRate )
. setPrimitiveFragmentShadingRate ( fragmentShadingRateFeatures . primitiveFragmentShadingRate )
. setAttachmentFragmentShadingRate ( fragmentShadingRateFeatures . attachmentFragmentShadingRate ) ;
2022-10-22 01:51:26 +00:00
2022-11-10 17:19:54 +00:00
auto sync2Features = vk : : PhysicalDeviceSynchronization2FeaturesKHR ( )
2022-11-08 20:21:50 +00:00
. setSynchronization2 ( true ) ;
# if defined(__APPLE__) && defined( VK_KHR_portability_subset )
auto portabilityFeatures = vk : : PhysicalDevicePortabilitySubsetFeaturesKHR ( )
2023-04-08 19:30:04 +00:00
# if USE_OPTICK
. setEvents ( true )
# endif
2022-11-08 20:21:50 +00:00
. setImageViewFormatSwizzle ( true ) ;
void * pNext = & portabilityFeatures ;
# else
2022-10-22 01:51:26 +00:00
void * pNext = nullptr ;
2022-11-08 20:21:50 +00:00
# endif
2022-10-22 01:51:26 +00:00
# define APPEND_EXTENSION(condition, desc) if (condition) { (desc).pNext = pNext; pNext = &(desc); } // NOLINT(cppcoreguidelines-macro-usage)
APPEND_EXTENSION ( accelStructSupported , accelStructFeatures )
APPEND_EXTENSION ( bufferAddressSupported , bufferAddressFeatures )
APPEND_EXTENSION ( rayPipelineSupported , rayPipelineFeatures )
APPEND_EXTENSION ( rayQuerySupported , rayQueryFeatures )
APPEND_EXTENSION ( meshletsSupported , meshletFeatures )
APPEND_EXTENSION ( vrsSupported , vrsFeatures )
2022-11-08 20:21:50 +00:00
APPEND_EXTENSION ( sync2Supported , sync2Features )
2022-10-22 01:51:26 +00:00
# undef APPEND_EXTENSION
auto deviceFeatures = vk : : PhysicalDeviceFeatures ( )
. setShaderImageGatherExtended ( true )
2023-10-19 16:21:37 +00:00
. setShaderStorageImageReadWithoutFormat ( actualDeviceFeatures2 . features . shaderStorageImageReadWithoutFormat )
2022-10-22 01:51:26 +00:00
. setSamplerAnisotropy ( true )
. setTessellationShader ( true )
. setTextureCompressionBC ( true )
2022-10-24 03:31:12 +00:00
# if !defined(__APPLE__)
2022-10-22 01:51:26 +00:00
. setGeometryShader ( true )
# endif
2022-11-08 20:21:50 +00:00
. setFillModeNonSolid ( true )
2022-10-22 01:51:26 +00:00
. setImageCubeArray ( true )
. setDualSrcBlend ( true ) ;
auto vulkan12features = vk : : PhysicalDeviceVulkan12Features ( )
. setDescriptorIndexing ( true )
. setRuntimeDescriptorArray ( true )
. setDescriptorBindingPartiallyBound ( true )
. setDescriptorBindingVariableDescriptorCount ( true )
. setTimelineSemaphore ( true )
. setShaderSampledImageArrayNonUniformIndexing ( true )
2022-12-06 22:43:56 +00:00
. setBufferDeviceAddress ( bufferAddressSupported )
2023-04-08 19:30:04 +00:00
# if USE_OPTICK
. setHostQueryReset ( true )
# endif
2022-10-22 01:51:26 +00:00
. setPNext ( pNext ) ;
auto layerVec = stringSetToVector ( enabledExtensions . layers ) ;
auto extVec = stringSetToVector ( enabledExtensions . device ) ;
auto deviceDesc = vk : : DeviceCreateInfo ( )
. setPQueueCreateInfos ( queueDesc . data ( ) )
. setQueueCreateInfoCount ( uint32_t ( queueDesc . size ( ) ) )
. setPEnabledFeatures ( & deviceFeatures )
. setEnabledExtensionCount ( uint32_t ( extVec . size ( ) ) )
. setPpEnabledExtensionNames ( extVec . data ( ) )
. setEnabledLayerCount ( uint32_t ( layerVec . size ( ) ) )
. setPpEnabledLayerNames ( layerVec . data ( ) )
. setPNext ( & vulkan12features ) ;
const vk : : Result res = m_VulkanPhysicalDevice . createDevice ( & deviceDesc , nullptr , & m_VulkanDevice ) ;
if ( res ! = vk : : Result : : eSuccess )
{
2024-02-03 16:26:31 +00:00
common - > FatalError ( " Failed to create a Vulkan physical device, error code = %s " , nvrhi : : vulkan : : resultToString ( ( VkResult ) res ) ) ;
2022-10-22 01:51:26 +00:00
return false ;
}
m_VulkanDevice . getQueue ( m_GraphicsQueueFamily , 0 , & m_GraphicsQueue ) ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableComputeQueue )
2022-10-22 01:51:26 +00:00
{
m_VulkanDevice . getQueue ( m_ComputeQueueFamily , 0 , & m_ComputeQueue ) ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableCopyQueue )
2022-10-22 01:51:26 +00:00
{
m_VulkanDevice . getQueue ( m_TransferQueueFamily , 0 , & m_TransferQueue ) ;
}
m_VulkanDevice . getQueue ( m_PresentQueueFamily , 0 , & m_PresentQueue ) ;
VULKAN_HPP_DEFAULT_DISPATCHER . init ( m_VulkanDevice ) ;
2022-11-18 15:47:29 +00:00
2022-11-10 19:50:09 +00:00
// SRS - Determine if preferred image depth/stencil format D24S8 is supported (issue with Vulkan on AMD GPUs)
vk : : ImageFormatProperties imageFormatProperties ;
2022-11-18 04:42:06 +00:00
const vk : : Result ret = m_VulkanPhysicalDevice . getImageFormatProperties ( vk : : Format : : eD24UnormS8Uint ,
2022-11-18 15:47:29 +00:00
vk : : ImageType : : e2D ,
vk : : ImageTiling : : eOptimal ,
vk : : ImageUsageFlags ( vk : : ImageUsageFlagBits : : eDepthStencilAttachment ) ,
vk : : ImageCreateFlags ( 0 ) ,
& imageFormatProperties ) ;
2023-03-06 16:05:43 +00:00
m_DeviceParams . enableImageFormatD24S8 = ( ret = = vk : : Result : : eSuccess ) ;
2022-11-18 04:42:06 +00:00
2023-06-01 03:09:33 +00:00
// SRS/rg3 - Determine which Vulkan surface present modes are supported by device and surface
2022-11-18 04:42:06 +00:00
auto surfacePModes = m_VulkanPhysicalDevice . getSurfacePresentModesKHR ( m_WindowSurface ) ;
2023-06-01 03:09:33 +00:00
enablePModeMailbox = find ( surfacePModes . begin ( ) , surfacePModes . end ( ) , vk : : PresentModeKHR : : eMailbox ) ! = surfacePModes . end ( ) ;
enablePModeImmediate = find ( surfacePModes . begin ( ) , surfacePModes . end ( ) , vk : : PresentModeKHR : : eImmediate ) ! = surfacePModes . end ( ) ;
2022-11-18 04:42:06 +00:00
enablePModeFifoRelaxed = find ( surfacePModes . begin ( ) , surfacePModes . end ( ) , vk : : PresentModeKHR : : eFifoRelaxed ) ! = surfacePModes . end ( ) ;
2022-11-18 15:47:29 +00:00
2023-12-28 19:28:20 +00:00
// stash the device renderer string and api version
2022-10-22 01:51:26 +00:00
auto prop = m_VulkanPhysicalDevice . getProperties ( ) ;
m_RendererString = std : : string ( prop . deviceName . data ( ) ) ;
2023-12-28 19:28:20 +00:00
m_DeviceApiVersion = prop . apiVersion ;
2022-10-22 01:51:26 +00:00
2023-02-14 00:06:41 +00:00
# if defined( USE_AMD_ALLOCATOR )
// SRS - initialize the vma allocator
VmaVulkanFunctions vulkanFunctions = { } ;
2024-01-17 04:56:32 +00:00
vulkanFunctions . vkGetInstanceProcAddr = vkGetInstanceProcAddr ;
2024-02-04 15:40:18 +00:00
vulkanFunctions . vkGetDeviceProcAddr = ( PFN_vkGetDeviceProcAddr ) vkGetInstanceProcAddr ( m_VulkanInstance , " vkGetDeviceProcAddr " ) ;
2023-02-14 00:06:41 +00:00
VmaAllocatorCreateInfo allocatorCreateInfo = { } ;
allocatorCreateInfo . vulkanApiVersion = VK_API_VERSION_1_2 ;
allocatorCreateInfo . physicalDevice = m_VulkanPhysicalDevice ;
allocatorCreateInfo . device = m_VulkanDevice ;
allocatorCreateInfo . instance = m_VulkanInstance ;
allocatorCreateInfo . pVulkanFunctions = & vulkanFunctions ;
allocatorCreateInfo . flags = bufferAddressSupported ? VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT : 0 ;
2023-02-17 05:22:58 +00:00
allocatorCreateInfo . preferredLargeHeapBlockSize = r_vmaDeviceLocalMemoryMB . GetInteger ( ) * 1024 * 1024 ;
2023-02-14 00:06:41 +00:00
vmaCreateAllocator ( & allocatorCreateInfo , & m_VmaAllocator ) ;
# endif
2022-10-22 01:51:26 +00:00
common - > Printf ( " Created Vulkan device: %s \n " , m_RendererString . c_str ( ) ) ;
return true ;
}
/*
* Vulkan Example base class
*
* Copyright ( C ) by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
bool DeviceManager_VK : : createWindowSurface ( )
{
// Create the platform-specific surface
2022-10-24 03:31:12 +00:00
# if defined( VULKAN_USE_PLATFORM_SDL )
2022-10-22 01:51:26 +00:00
// SRS - Support generic SDL platform for linux and macOS
2024-02-04 06:41:33 +00:00
auto res = vk : : Result ( CreateSDLWindowSurface ( ( VkInstance ) m_VulkanInstance , ( VkSurfaceKHR * ) & m_WindowSurface ) ) ;
2022-12-16 18:42:12 +00:00
2022-10-24 03:31:12 +00:00
# elif defined( VK_USE_PLATFORM_WIN32_KHR )
2022-12-16 18:42:12 +00:00
auto surfaceCreateInfo = vk : : Win32SurfaceCreateInfoKHR ( )
2022-12-19 11:40:45 +00:00
. setHinstance ( ( HINSTANCE ) windowInstance )
. setHwnd ( ( HWND ) windowHandle ) ;
2022-12-16 18:42:12 +00:00
2024-02-04 06:41:33 +00:00
auto res = m_VulkanInstance . createWin32SurfaceKHR ( & surfaceCreateInfo , nullptr , & m_WindowSurface ) ;
2022-10-22 01:51:26 +00:00
# endif
2022-12-16 18:42:12 +00:00
if ( res ! = vk : : Result : : eSuccess )
2022-10-22 01:51:26 +00:00
{
2024-02-03 16:26:31 +00:00
common - > FatalError ( " Failed to create a Vulkan window surface, error code = %s " , nvrhi : : vulkan : : resultToString ( ( VkResult ) res ) ) ;
2022-10-22 01:51:26 +00:00
return false ;
}
return true ;
}
void DeviceManager_VK : : destroySwapChain ( )
{
if ( m_VulkanDevice )
{
m_VulkanDevice . waitIdle ( ) ;
}
2022-11-08 20:21:50 +00:00
while ( ! m_SwapChainImages . empty ( ) )
{
auto sci = m_SwapChainImages . back ( ) ;
m_SwapChainImages . pop_back ( ) ;
sci . rhiHandle = nullptr ;
}
2022-10-22 01:51:26 +00:00
if ( m_SwapChain )
{
m_VulkanDevice . destroySwapchainKHR ( m_SwapChain ) ;
m_SwapChain = nullptr ;
}
}
bool DeviceManager_VK : : createSwapChain ( )
{
m_SwapChainFormat =
{
2023-03-06 16:05:43 +00:00
vk : : Format ( nvrhi : : vulkan : : convertFormat ( m_DeviceParams . swapChainFormat ) ) ,
2022-10-22 01:51:26 +00:00
vk : : ColorSpaceKHR : : eSrgbNonlinear
} ;
2023-06-09 18:48:36 +00:00
// SRS - Clamp swap chain extent within the range supported by the device / window surface
auto surfaceCaps = m_VulkanPhysicalDevice . getSurfaceCapabilitiesKHR ( m_WindowSurface ) ;
m_DeviceParams . backBufferWidth = idMath : : ClampInt ( surfaceCaps . minImageExtent . width , surfaceCaps . maxImageExtent . width , m_DeviceParams . backBufferWidth ) ;
m_DeviceParams . backBufferHeight = idMath : : ClampInt ( surfaceCaps . minImageExtent . height , surfaceCaps . maxImageExtent . height , m_DeviceParams . backBufferHeight ) ;
2023-03-06 16:05:43 +00:00
vk : : Extent2D extent = vk : : Extent2D ( m_DeviceParams . backBufferWidth , m_DeviceParams . backBufferHeight ) ;
2022-10-22 01:51:26 +00:00
std : : unordered_set < uint32_t > uniqueQueues =
{
uint32_t ( m_GraphicsQueueFamily ) ,
uint32_t ( m_PresentQueueFamily )
} ;
std : : vector < uint32_t > queues = setToVector ( uniqueQueues ) ;
const bool enableSwapChainSharing = queues . size ( ) > 1 ;
2023-06-01 03:09:33 +00:00
// SRS/rg3 - set up Vulkan present mode based on vsync setting and available surface features
vk : : PresentModeKHR presentMode ;
switch ( m_DeviceParams . vsyncEnabled )
{
case 0 :
presentMode = enablePModeMailbox & & r_preferFastSync . GetBool ( ) ? vk : : PresentModeKHR : : eMailbox :
2023-07-14 06:46:32 +00:00
( enablePModeImmediate ? vk : : PresentModeKHR : : eImmediate : vk : : PresentModeKHR : : eFifo ) ;
2023-06-01 03:09:33 +00:00
break ;
case 1 :
presentMode = enablePModeFifoRelaxed ? vk : : PresentModeKHR : : eFifoRelaxed : vk : : PresentModeKHR : : eFifo ;
break ;
case 2 :
default :
presentMode = vk : : PresentModeKHR : : eFifo ; // eFifo always supported according to Vulkan spec
}
2022-10-22 01:51:26 +00:00
auto desc = vk : : SwapchainCreateInfoKHR ( )
. setSurface ( m_WindowSurface )
2023-03-06 16:05:43 +00:00
. setMinImageCount ( m_DeviceParams . swapChainBufferCount )
2022-10-22 01:51:26 +00:00
. setImageFormat ( m_SwapChainFormat . format )
. setImageColorSpace ( m_SwapChainFormat . colorSpace )
. setImageExtent ( extent )
. setImageArrayLayers ( 1 )
. setImageUsage ( vk : : ImageUsageFlagBits : : eColorAttachment | vk : : ImageUsageFlagBits : : eTransferDst | vk : : ImageUsageFlagBits : : eSampled )
. setImageSharingMode ( enableSwapChainSharing ? vk : : SharingMode : : eConcurrent : vk : : SharingMode : : eExclusive )
. setQueueFamilyIndexCount ( enableSwapChainSharing ? uint32_t ( queues . size ( ) ) : 0 )
. setPQueueFamilyIndices ( enableSwapChainSharing ? queues . data ( ) : nullptr )
. setPreTransform ( vk : : SurfaceTransformFlagBitsKHR : : eIdentity )
. setCompositeAlpha ( vk : : CompositeAlphaFlagBitsKHR : : eOpaque )
2023-06-01 03:09:33 +00:00
. setPresentMode ( presentMode )
2022-10-22 01:51:26 +00:00
. setClipped ( true )
. setOldSwapchain ( nullptr ) ;
const vk : : Result res = m_VulkanDevice . createSwapchainKHR ( & desc , nullptr , & m_SwapChain ) ;
if ( res ! = vk : : Result : : eSuccess )
{
2024-02-03 16:26:31 +00:00
common - > FatalError ( " Failed to create a Vulkan swap chain, error code = %s " , nvrhi : : vulkan : : resultToString ( ( VkResult ) res ) ) ;
2022-10-22 01:51:26 +00:00
return false ;
}
// retrieve swap chain images
auto images = m_VulkanDevice . getSwapchainImagesKHR ( m_SwapChain ) ;
for ( auto image : images )
{
SwapChainImage sci ;
sci . image = image ;
nvrhi : : TextureDesc textureDesc ;
2023-03-06 16:05:43 +00:00
textureDesc . width = m_DeviceParams . backBufferWidth ;
textureDesc . height = m_DeviceParams . backBufferHeight ;
textureDesc . format = m_DeviceParams . swapChainFormat ;
2022-10-22 01:51:26 +00:00
textureDesc . debugName = " Swap chain image " ;
textureDesc . initialState = nvrhi : : ResourceStates : : Present ;
textureDesc . keepInitialState = true ;
textureDesc . isRenderTarget = true ;
sci . rhiHandle = m_NvrhiDevice - > createHandleForNativeTexture ( nvrhi : : ObjectTypes : : VK_Image , nvrhi : : Object ( sci . image ) , textureDesc ) ;
m_SwapChainImages . push_back ( sci ) ;
}
m_SwapChainIndex = 0 ;
return true ;
}
bool DeviceManager_VK : : CreateDeviceAndSwapChain ( )
{
// RB: control these through the cmdline
2023-03-06 16:05:43 +00:00
m_DeviceParams . enableNvrhiValidationLayer = r_useValidationLayers . GetInteger ( ) > 0 ;
m_DeviceParams . enableDebugRuntime = r_useValidationLayers . GetInteger ( ) > 1 ;
2022-10-22 01:51:26 +00:00
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableDebugRuntime )
2022-10-22 01:51:26 +00:00
{
2022-10-23 12:45:53 +00:00
enabledExtensions . instance . insert ( VK_EXT_DEBUG_REPORT_EXTENSION_NAME ) ;
2022-10-24 03:31:12 +00:00
# if defined(__APPLE__) && defined( USE_MoltenVK )
2022-12-12 23:13:55 +00:00
}
// SRS - when USE_MoltenVK defined, load libMoltenVK vs. the default libvulkan
2022-12-16 18:42:12 +00:00
static const vk : : DynamicLoader dl ( " libMoltenVK.dylib " ) ;
2022-10-23 12:45:53 +00:00
# else
2022-10-22 01:51:26 +00:00
enabledExtensions . layers . insert ( " VK_LAYER_KHRONOS_validation " ) ;
}
2022-12-16 18:42:12 +00:00
// SRS - make static so ~DynamicLoader() does not prematurely unload vulkan dynamic lib
static const vk : : DynamicLoader dl ;
2022-12-12 23:13:55 +00:00
# endif
2024-01-17 04:56:32 +00:00
vkGetInstanceProcAddr = dl . getProcAddress < PFN_vkGetInstanceProcAddr > ( " vkGetInstanceProcAddr " ) ;
2022-10-22 01:51:26 +00:00
VULKAN_HPP_DEFAULT_DISPATCHER . init ( vkGetInstanceProcAddr ) ;
# define CHECK(a) if (!(a)) { return false; }
CHECK ( createInstance ( ) ) ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableDebugRuntime )
2022-10-22 01:51:26 +00:00
{
installDebugCallback ( ) ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . swapChainFormat = = nvrhi : : Format : : SRGBA8_UNORM )
2022-10-22 01:51:26 +00:00
{
2023-03-06 16:05:43 +00:00
m_DeviceParams . swapChainFormat = nvrhi : : Format : : SBGRA8_UNORM ;
2022-10-22 01:51:26 +00:00
}
2023-03-06 16:05:43 +00:00
else if ( m_DeviceParams . swapChainFormat = = nvrhi : : Format : : RGBA8_UNORM )
2022-10-22 01:51:26 +00:00
{
2023-03-06 16:05:43 +00:00
m_DeviceParams . swapChainFormat = nvrhi : : Format : : BGRA8_UNORM ;
2022-10-22 01:51:26 +00:00
}
// add device extensions requested by the user
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . requiredVulkanDeviceExtensions )
2022-10-22 01:51:26 +00:00
{
enabledExtensions . device . insert ( name ) ;
}
2023-03-06 16:05:43 +00:00
for ( const std : : string & name : m_DeviceParams . optionalVulkanDeviceExtensions )
2022-10-22 01:51:26 +00:00
{
optionalExtensions . device . insert ( name ) ;
}
CHECK ( createWindowSurface ( ) ) ;
CHECK ( pickPhysicalDevice ( ) ) ;
CHECK ( findQueueFamilies ( m_VulkanPhysicalDevice , m_WindowSurface ) ) ;
2022-12-19 11:40:45 +00:00
2023-12-28 19:28:20 +00:00
// SRS - when USE_MoltenVK defined, set MoltenVK runtime configuration parameters on macOS (deprecated version)
2022-12-12 23:13:55 +00:00
# if defined(__APPLE__) && defined( USE_MoltenVK )
2023-12-28 19:28:20 +00:00
# if defined( VK_EXT_layer_settings )
// SRS - for backwards compatibility at runtime: execute only if we can't find the VK_EXT_layer_settings extension
if ( enabledExtensions . instance . find ( VK_EXT_LAYER_SETTINGS_EXTENSION_NAME ) = = enabledExtensions . instance . end ( ) )
# endif
{
2024-01-30 01:58:28 +00:00
// SRS - vkSetMoltenVKConfigurationMVK() now deprecated, but retained for MoltenVK < 1.2.7 / SDK < 1.3.275.0
const PFN_vkGetMoltenVKConfigurationMVK vkGetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const)
2024-02-04 15:40:18 +00:00
( PFN_vkGetMoltenVKConfigurationMVK ) vkGetInstanceProcAddr ( m_VulkanInstance , " vkGetMoltenVKConfigurationMVK " ) ;
2024-01-30 01:58:28 +00:00
const PFN_vkSetMoltenVKConfigurationMVK vkSetMoltenVKConfigurationMVK = // NOLINT(misc-misplaced-const)
2024-02-04 15:40:18 +00:00
( PFN_vkSetMoltenVKConfigurationMVK ) vkGetInstanceProcAddr ( m_VulkanInstance , " vkSetMoltenVKConfigurationMVK " ) ;
2022-12-12 23:13:55 +00:00
2023-12-28 19:28:20 +00:00
vk : : PhysicalDeviceFeatures2 deviceFeatures2 ;
vk : : PhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures ;
deviceFeatures2 . setPNext ( & portabilityFeatures ) ;
m_VulkanPhysicalDevice . getFeatures2 ( & deviceFeatures2 ) ;
2022-12-12 23:13:55 +00:00
2023-12-28 19:28:20 +00:00
MVKConfiguration mvkConfig ;
size_t mvkConfigSize = sizeof ( mvkConfig ) ;
2022-12-12 23:13:55 +00:00
2023-12-28 19:28:20 +00:00
vkGetMoltenVKConfigurationMVK ( m_VulkanInstance , & mvkConfig , & mvkConfigSize ) ;
2023-02-28 23:02:45 +00:00
2023-12-28 19:28:20 +00:00
// SRS - Set MoltenVK's synchronous queue submit option for vkQueueSubmit() & vkQueuePresentKHR()
if ( mvkConfig . synchronousQueueSubmits = = VK_TRUE & & ! r_mvkSynchronousQueueSubmits . GetBool ( ) )
{
idLib : : Printf ( " Disabled MoltenVK's synchronous queue submits... \n " ) ;
mvkConfig . synchronousQueueSubmits = VK_FALSE ;
}
2022-12-12 23:13:55 +00:00
2023-12-28 19:28:20 +00:00
// SRS - If we don't have native image view swizzle, enable MoltenVK's image view swizzle feature
if ( portabilityFeatures . imageViewFormatSwizzle = = VK_FALSE )
{
idLib : : Printf ( " Enabled MoltenVK's image view swizzle... \n " ) ;
mvkConfig . fullImageViewSwizzle = VK_TRUE ;
}
2024-01-30 01:58:28 +00:00
// SRS - Set MoltenVK's Metal argument buffer option for descriptor resource scaling
// - Also needed for Vulkan SDK 1.3.268.1 to work around SPIRV-Cross issue for Metal conversion.
// - See https://github.com/KhronosGroup/MoltenVK/issues/2016 and https://github.com/goki/vgpu/issues/9
// - Issue solved in Vulkan SDK >= 1.3.275.0, but config uses VK_EXT_layer_settings instead of this code.
if ( mvkConfig . useMetalArgumentBuffers = = MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS_NEVER & & r_mvkUseMetalArgumentBuffers . GetInteger ( ) )
2023-12-28 19:28:20 +00:00
{
2024-01-30 01:58:28 +00:00
idLib : : Printf ( " Enabled MoltenVK's Metal argument buffers... \n " ) ;
mvkConfig . useMetalArgumentBuffers = MVKUseMetalArgumentBuffers ( r_mvkUseMetalArgumentBuffers . GetInteger ( ) ) ;
2023-12-28 19:28:20 +00:00
}
2024-02-04 15:40:18 +00:00
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 )
2023-12-28 19:28:20 +00:00
if ( mvkConfig . apiVersionToAdvertise > = VK_MAKE_API_VERSION ( 0 , 1 , 2 , 268 ) )
{
// SRS - Disable MoltenVK's timestampPeriod filter for HUD / Optick profiler timing calibration
mvkConfig . timestampPeriodLowPassAlpha = 1.0 ;
// SRS - Enable MoltenVK's performance tracking for display of Metal encoding timer on macOS
mvkConfig . performanceTracking = VK_TRUE ;
}
2024-02-04 15:40:18 +00:00
# endif
2022-12-12 23:13:55 +00:00
2023-10-04 16:33:32 +00:00
vkSetMoltenVKConfigurationMVK ( m_VulkanInstance , & mvkConfig , & mvkConfigSize ) ;
2022-12-12 23:13:55 +00:00
}
2024-01-17 04:56:32 +00:00
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 )
// SRS - Get function pointer for retrieving MoltenVK advanced performance statistics in DeviceManager_VK::BeginFrame()
2024-02-04 15:40:18 +00:00
vkGetPerformanceStatisticsMVK = ( PFN_vkGetPerformanceStatisticsMVK ) vkGetInstanceProcAddr ( m_VulkanInstance , " vkGetPerformanceStatisticsMVK " ) ;
2024-01-17 04:56:32 +00:00
# endif
2024-03-22 02:19:12 +00:00
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 9 ) && USE_OPTICK
// SRS - Initialize Optick event storage and descriptions for MoltenVK events
mvkAcquireEventStorage = Optick : : RegisterStorage ( " Mvk_ImageAcquire " , uint64_t ( - 1 ) , Optick : : ThreadMask : : Main ) ;
mvkSubmitEventStorage = Optick : : RegisterStorage ( " Mvk_CmdBufSubmit " , uint64_t ( - 1 ) , Optick : : ThreadMask : : Main ) ;
mvkEncodeEventStorage = Optick : : RegisterStorage ( " Mvk_EncodeThread " , uint64_t ( - 1 ) , Optick : : ThreadMask : : GPU ) ;
mvkAcquireEventDesc = Optick : : EventDescription : : CreateShared ( " Acquire_Wait " ) ;
mvkSubmitEventDesc = Optick : : EventDescription : : CreateShared ( " Submit_Wait " ) ;
mvkEncodeEventDesc = Optick : : EventDescription : : CreateShared ( " Metal_Encode " ) ;
Optick : : SetStateChangedCallback ( ( Optick : : StateCallback ) optickStateChangedCallback ) ;
# endif
2022-12-12 23:13:55 +00:00
# endif
2022-10-22 01:51:26 +00:00
CHECK ( createDevice ( ) ) ;
auto vecInstanceExt = stringSetToVector ( enabledExtensions . instance ) ;
auto vecLayers = stringSetToVector ( enabledExtensions . layers ) ;
auto vecDeviceExt = stringSetToVector ( enabledExtensions . device ) ;
nvrhi : : vulkan : : DeviceDesc deviceDesc ;
deviceDesc . errorCB = & DefaultMessageCallback : : GetInstance ( ) ;
deviceDesc . instance = m_VulkanInstance ;
deviceDesc . physicalDevice = m_VulkanPhysicalDevice ;
deviceDesc . device = m_VulkanDevice ;
deviceDesc . graphicsQueue = m_GraphicsQueue ;
deviceDesc . graphicsQueueIndex = m_GraphicsQueueFamily ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableComputeQueue )
2022-10-22 01:51:26 +00:00
{
deviceDesc . computeQueue = m_ComputeQueue ;
deviceDesc . computeQueueIndex = m_ComputeQueueFamily ;
}
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableCopyQueue )
2022-10-22 01:51:26 +00:00
{
deviceDesc . transferQueue = m_TransferQueue ;
deviceDesc . transferQueueIndex = m_TransferQueueFamily ;
}
deviceDesc . instanceExtensions = vecInstanceExt . data ( ) ;
deviceDesc . numInstanceExtensions = vecInstanceExt . size ( ) ;
deviceDesc . deviceExtensions = vecDeviceExt . data ( ) ;
deviceDesc . numDeviceExtensions = vecDeviceExt . size ( ) ;
m_NvrhiDevice = nvrhi : : vulkan : : createDevice ( deviceDesc ) ;
2023-03-06 16:05:43 +00:00
if ( m_DeviceParams . enableNvrhiValidationLayer )
2022-10-22 01:51:26 +00:00
{
m_ValidationLayer = nvrhi : : validation : : createValidationLayer ( m_NvrhiDevice ) ;
}
CHECK ( createSwapChain ( ) ) ;
2023-10-04 16:24:49 +00:00
//m_BarrierCommandList = m_NvrhiDevice->createCommandList(); // SRS - no longer needed
2022-10-22 01:51:26 +00:00
2023-03-30 05:07:43 +00:00
// SRS - Give each swapchain image its own semaphore in case of overlap (e.g. MoltenVK async queue submit)
for ( int i = 0 ; i < m_SwapChainImages . size ( ) ; i + + )
2023-03-27 19:51:43 +00:00
{
m_PresentSemaphoreQueue . push ( m_VulkanDevice . createSemaphore ( vk : : SemaphoreCreateInfo ( ) ) ) ;
}
m_PresentSemaphore = m_PresentSemaphoreQueue . front ( ) ;
2022-10-22 01:51:26 +00:00
2023-02-28 23:02:45 +00:00
m_FrameWaitQuery = m_NvrhiDevice - > createEventQuery ( ) ;
m_NvrhiDevice - > setEventQuery ( m_FrameWaitQuery , nvrhi : : CommandQueue : : Graphics ) ;
2022-10-22 01:51:26 +00:00
# undef CHECK
2024-01-18 02:35:08 +00:00
# if USE_OPTICK
2024-02-04 15:40:18 +00:00
const Optick : : VulkanFunctions optickVulkanFunctions = { ( PFN_vkGetInstanceProcAddr_ ) vkGetInstanceProcAddr } ;
2024-01-18 02:35:08 +00:00
# endif
2024-01-17 04:56:32 +00:00
OPTICK_GPU_INIT_VULKAN ( ( VkInstance ) m_VulkanInstance , ( VkDevice * ) & m_VulkanDevice , ( VkPhysicalDevice * ) & m_VulkanPhysicalDevice , ( VkQueue * ) & m_GraphicsQueue , ( uint32_t * ) & m_GraphicsQueueFamily , 1 , & optickVulkanFunctions ) ;
2023-04-08 19:30:04 +00:00
2022-10-22 01:51:26 +00:00
return true ;
}
void DeviceManager_VK : : DestroyDeviceAndSwapChain ( )
{
2023-04-08 19:30:04 +00:00
OPTICK_SHUTDOWN ( ) ;
2023-12-07 23:16:11 +00:00
if ( m_VulkanDevice )
{
m_VulkanDevice . waitIdle ( ) ;
}
2023-06-01 04:53:02 +00:00
2023-02-28 23:02:45 +00:00
m_FrameWaitQuery = nullptr ;
2023-03-30 05:07:43 +00:00
for ( int i = 0 ; i < m_SwapChainImages . size ( ) ; i + + )
2023-03-27 19:51:43 +00:00
{
m_VulkanDevice . destroySemaphore ( m_PresentSemaphoreQueue . front ( ) ) ;
m_PresentSemaphoreQueue . pop ( ) ;
}
2022-10-22 01:51:26 +00:00
m_PresentSemaphore = vk : : Semaphore ( ) ;
2023-10-04 16:24:49 +00:00
//m_BarrierCommandList = nullptr; // SRS - no longer needed
2022-10-22 01:51:26 +00:00
2023-03-30 05:07:43 +00:00
destroySwapChain ( ) ;
2022-10-22 01:51:26 +00:00
m_NvrhiDevice = nullptr ;
m_ValidationLayer = nullptr ;
m_RendererString . clear ( ) ;
if ( m_DebugReportCallback )
{
m_VulkanInstance . destroyDebugReportCallbackEXT ( m_DebugReportCallback ) ;
}
2023-02-14 00:06:41 +00:00
# if defined( USE_AMD_ALLOCATOR )
if ( m_VmaAllocator )
{
// SRS - make sure image allocation garbage is emptied for all frames
for ( int i = 0 ; i < NUM_FRAME_DATA ; i + + )
{
idImage : : EmptyGarbage ( ) ;
}
vmaDestroyAllocator ( m_VmaAllocator ) ;
m_VmaAllocator = nullptr ;
}
# endif
2022-10-22 01:51:26 +00:00
if ( m_VulkanDevice )
{
m_VulkanDevice . destroy ( ) ;
m_VulkanDevice = nullptr ;
}
if ( m_WindowSurface )
{
assert ( m_VulkanInstance ) ;
m_VulkanInstance . destroySurfaceKHR ( m_WindowSurface ) ;
m_WindowSurface = nullptr ;
}
if ( m_VulkanInstance )
{
m_VulkanInstance . destroy ( ) ;
m_VulkanInstance = nullptr ;
}
}
void DeviceManager_VK : : BeginFrame ( )
{
2023-11-30 17:26:38 +00:00
OPTICK_CATEGORY ( " Vulkan_BeginFrame " , Optick : : Category : : Wait ) ;
2024-03-07 20:55:00 +00:00
// SRS - get Vulkan GPU memory usage for display in statistics overlay HUD
vk : : PhysicalDeviceMemoryProperties2 memoryProperties2 ;
vk : : PhysicalDeviceMemoryBudgetPropertiesEXT memoryBudget ;
memoryProperties2 . pNext = & memoryBudget ;
m_VulkanPhysicalDevice . getMemoryProperties2 ( & memoryProperties2 ) ;
VkDeviceSize gpuMemoryAllocated = 0 ;
for ( uint32_t i = 0 ; i < memoryProperties2 . memoryProperties . memoryHeapCount ; i + + )
{
gpuMemoryAllocated + = memoryBudget . heapUsage [ i ] ;
2023-12-28 19:28:20 +00:00
# if defined(__APPLE__)
2024-03-07 20:55:00 +00:00
// SRS - macOS Vulkan API <= 1.2.268 has heap reporting defect, use heapUsage[0] only
if ( m_DeviceApiVersion < = VK_MAKE_API_VERSION ( 0 , 1 , 2 , 268 ) )
{
break ;
2023-12-28 19:28:20 +00:00
}
2024-03-07 20:55:00 +00:00
# endif
2023-12-28 19:28:20 +00:00
}
2024-03-07 20:55:00 +00:00
commonLocal . SetRendererGpuMemoryMB ( gpuMemoryAllocated / 1024 / 1024 ) ;
2023-10-04 16:33:32 +00:00
2022-10-22 01:51:26 +00:00
const vk : : Result res = m_VulkanDevice . acquireNextImageKHR ( m_SwapChain ,
std : : numeric_limits < uint64_t > : : max ( ) , // timeout
m_PresentSemaphore ,
vk : : Fence ( ) ,
& m_SwapChainIndex ) ;
2022-11-10 19:56:18 +00:00
assert ( res = = vk : : Result : : eSuccess | | res = = vk : : Result : : eSuboptimalKHR ) ;
2022-10-22 01:51:26 +00:00
m_NvrhiDevice - > queueWaitForSemaphore ( nvrhi : : CommandQueue : : Graphics , m_PresentSemaphore , 0 ) ;
}
void DeviceManager_VK : : EndFrame ( )
{
2024-02-28 16:59:41 +00:00
OPTICK_CATEGORY ( " Vulkan_EndFrame " , Optick : : Category : : Wait ) ;
2022-10-22 01:51:26 +00:00
m_NvrhiDevice - > queueSignalSemaphore ( nvrhi : : CommandQueue : : Graphics , m_PresentSemaphore , 0 ) ;
2023-10-04 16:24:49 +00:00
// SRS - Don't need barrier commandlist if EndFrame() is called before executeCommandList() in idRenderBackend::GL_EndFrame()
//m_BarrierCommandList->open(); // umm...
//m_BarrierCommandList->close();
//m_NvrhiDevice->executeCommandList( m_BarrierCommandList );
2024-03-22 02:19:12 +00:00
# if defined(__APPLE__) && defined( USE_MoltenVK )
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 9 ) && USE_OPTICK
// SRS - Capture MoltenVK command buffer submit time just before executeCommandList() in idRenderBackend::GL_EndFrame()
mvkPreviousSubmitTime = mvkLatestSubmitTime ;
mvkLatestSubmitTime = Optick : : GetHighPrecisionTime ( ) ;
# endif
# endif
2022-10-22 01:51:26 +00:00
}
void DeviceManager_VK : : Present ( )
{
2023-05-23 18:45:34 +00:00
OPTICK_GPU_FLIP ( m_SwapChain ) ;
2023-04-08 19:30:04 +00:00
OPTICK_CATEGORY ( " Vulkan_Present " , Optick : : Category : : Wait ) ;
2024-03-07 20:55:00 +00:00
OPTICK_TAG ( " Frame " , idLib : : frameNumber - 1 ) ;
2023-04-08 19:30:04 +00:00
2023-05-05 02:36:46 +00:00
void * pNext = nullptr ;
# if USE_OPTICK
2023-05-13 14:22:52 +00:00
// SRS - if display timing enabled, define the presentID for labeling the Optick GPU VSync / Present queue
2023-05-05 02:36:46 +00:00
vk : : PresentTimeGOOGLE presentTime = vk : : PresentTimeGOOGLE ( )
. setPresentID ( idLib : : frameNumber - 1 ) ;
vk : : PresentTimesInfoGOOGLE presentTimesInfo = vk : : PresentTimesInfoGOOGLE ( )
2023-07-14 07:45:35 +00:00
. setSwapchainCount ( 1 )
. setPTimes ( & presentTime ) ;
2023-05-13 14:22:52 +00:00
if ( displayTimingEnabled )
2023-05-05 02:36:46 +00:00
{
pNext = & presentTimesInfo ;
}
# endif
2022-10-22 01:51:26 +00:00
vk : : PresentInfoKHR info = vk : : PresentInfoKHR ( )
. setWaitSemaphoreCount ( 1 )
. setPWaitSemaphores ( & m_PresentSemaphore )
. setSwapchainCount ( 1 )
. setPSwapchains ( & m_SwapChain )
2023-05-05 02:36:46 +00:00
. setPImageIndices ( & m_SwapChainIndex )
. setPNext ( pNext ) ;
2022-10-22 01:51:26 +00:00
const vk : : Result res = m_PresentQueue . presentKHR ( & info ) ;
2022-11-10 19:56:18 +00:00
assert ( res = = vk : : Result : : eSuccess | | res = = vk : : Result : : eErrorOutOfDateKHR | | res = = vk : : Result : : eSuboptimalKHR ) ;
2022-10-22 01:51:26 +00:00
2023-03-30 05:07:43 +00:00
// SRS - Cycle the semaphore queue and setup m_PresentSemaphore for the next swapchain image
2023-03-27 19:51:43 +00:00
m_PresentSemaphoreQueue . pop ( ) ;
m_PresentSemaphoreQueue . push ( m_PresentSemaphore ) ;
m_PresentSemaphore = m_PresentSemaphoreQueue . front ( ) ;
2023-03-24 15:29:40 +00:00
# if !defined(__APPLE__) || !defined( USE_MoltenVK )
// SRS - validation layer is present only when the vulkan loader + layers are enabled (i.e. not MoltenVK standalone)
if ( m_DeviceParams . enableDebugRuntime )
2022-10-22 01:51:26 +00:00
{
// according to vulkan-tutorial.com, "the validation layer implementation expects
// the application to explicitly synchronize with the GPU"
m_PresentQueue . waitIdle ( ) ;
}
2023-03-24 15:29:40 +00:00
else
# endif
{
if constexpr ( NUM_FRAME_DATA > 2 )
{
2023-04-08 19:30:04 +00:00
OPTICK_CATEGORY ( " Vulkan_Sync3 " , Optick : : Category : : Wait ) ;
2023-03-24 15:29:40 +00:00
// SRS - For triple buffering, sync on previous frame's command queue completion
m_NvrhiDevice - > waitEventQuery ( m_FrameWaitQuery ) ;
}
2023-03-30 17:30:33 +00:00
2023-03-24 15:29:40 +00:00
m_NvrhiDevice - > resetEventQuery ( m_FrameWaitQuery ) ;
m_NvrhiDevice - > setEventQuery ( m_FrameWaitQuery , nvrhi : : CommandQueue : : Graphics ) ;
if constexpr ( NUM_FRAME_DATA < 3 )
{
2023-04-08 19:30:04 +00:00
OPTICK_CATEGORY ( " Vulkan_Sync2 " , Optick : : Category : : Wait ) ;
2023-03-24 15:29:40 +00:00
// SRS - For double buffering, sync on current frame's command queue completion
m_NvrhiDevice - > waitEventQuery ( m_FrameWaitQuery ) ;
}
}
2024-03-22 02:19:12 +00:00
# if defined(__APPLE__) && defined( USE_MoltenVK )
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 6 )
if ( vkGetPerformanceStatisticsMVK )
{
// SRS - get MoltenVK's Metal encoding time for display in statistics overlay HUD
MVKPerformanceStatistics mvkPerfStats ;
size_t mvkPerfStatsSize = sizeof ( mvkPerfStats ) ;
if ( vkGetPerformanceStatisticsMVK ( m_VulkanDevice , & mvkPerfStats , & mvkPerfStatsSize ) = = VK_SUCCESS )
{
uint64 mvkEncodeTime = Max ( 0.0 , mvkPerfStats . queue . commandBufferEncoding . latest - mvkPerfStats . queue . retrieveCAMetalDrawable . latest ) * 1000000.0 ;
# if MVK_VERSION >= MVK_MAKE_VERSION( 1, 2, 9 ) && USE_OPTICK
if ( optickCapturing )
{
// SRS - create custom Optick event that displays MoltenVK's command buffer submit waiting time
OPTICK_STORAGE_EVENT ( mvkSubmitEventStorage , mvkSubmitEventDesc , mvkPreviousSubmitTime , mvkPreviousSubmitTime + mvkPreviousSubmitWaitTime ) ;
OPTICK_STORAGE_TAG ( mvkSubmitEventStorage , mvkPreviousSubmitTime + mvkPreviousSubmitWaitTime / 2 , " Frame " , idLib : : frameNumber - 2 ) ;
// SRS - select latest acquire time if hashes match and we didn't retrieve a new image, otherwise select previous acquire time
double mvkLatestAcquireHash = mvkPerfStats . queue . retrieveCAMetalDrawable . latest + mvkPerfStats . queue . retrieveCAMetalDrawable . previous ;
int64_t mvkAcquireWaitTime = mvkLatestAcquireHash = = mvkPreviousAcquireHash ? mvkPerfStats . queue . retrieveCAMetalDrawable . latest * 1000000.0 : mvkPerfStats . queue . retrieveCAMetalDrawable . previous * 1000000.0 ;
// SRS - select latest presented frame if we are running synchronous, otherwise select previous presented frame as reference
int64_t mvkAcquireStartTime = mvkPreviousSubmitTime + mvkPreviousSubmitWaitTime ;
int32_t frameNumberTag = idLib : : frameNumber - 2 ;
if ( r_mvkSynchronousQueueSubmits . GetBool ( ) )
{
mvkAcquireStartTime = mvkLatestSubmitTime + int64_t ( mvkPerfStats . queue . waitSubmitCommandBuffers . latest * 1000000.0 ) ;
mvkAcquireWaitTime = mvkPerfStats . queue . retrieveCAMetalDrawable . latest * 1000000.0 ;
frameNumberTag = idLib : : frameNumber - 1 ;
}
// SRS - create custom Optick event that displays MoltenVK's image acquire waiting time
OPTICK_STORAGE_EVENT ( mvkAcquireEventStorage , mvkAcquireEventDesc , mvkAcquireStartTime , mvkAcquireStartTime + mvkAcquireWaitTime ) ;
OPTICK_STORAGE_TAG ( mvkAcquireEventStorage , mvkAcquireStartTime + mvkAcquireWaitTime / 2 , " Frame " , frameNumberTag ) ;
// SRS - when Optick is active, use MoltenVK's previous encoding time to select game command buffer vs. Optick's command buffer
int64_t mvkEncodeStartTime = mvkAcquireStartTime + mvkAcquireWaitTime ;
mvkEncodeTime = Max ( int64_t ( 0 ) , int64_t ( mvkPerfStats . queue . commandBufferEncoding . previous * 1000000.0 ) - mvkAcquireWaitTime ) ;
// SRS - create custom Optick event that displays MoltenVK's Vulkan-to-Metal encoding time
OPTICK_STORAGE_EVENT ( mvkEncodeEventStorage , mvkEncodeEventDesc , mvkEncodeStartTime , mvkEncodeStartTime + mvkEncodeTime ) ;
OPTICK_STORAGE_TAG ( mvkEncodeEventStorage , mvkEncodeStartTime + mvkEncodeTime / 2 , " Frame " , frameNumberTag ) ;
mvkPreviousSubmitWaitTime = mvkPerfStats . queue . waitSubmitCommandBuffers . latest * 1000000.0 ;
mvkPreviousAcquireHash = mvkLatestAcquireHash ;
}
# endif
commonLocal . SetRendererMvkEncodeMicroseconds ( mvkEncodeTime / 1000 ) ;
}
}
# endif
# endif
2022-10-22 01:51:26 +00:00
}
DeviceManager * DeviceManager : : CreateVK ( )
{
return new DeviceManager_VK ( ) ;
}