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 4x integration)
|
|
|
|
* Copyright (C) 2023 Stephen Saunders (id Tech 4x integration)
|
|
|
|
* Copyright (C) 2023 Robert Beckebans (id Tech 4x 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
|
|
|
|
|
2024-04-02 15:17:47 +00:00
|
|
|
idCVar r_vkPreferFastSync( "r_vkPreferFastSync", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Prefer Fast Sync/no-tearing in place of VSync off/tearing" );
|
2023-06-01 03:09:33 +00:00
|
|
|
|
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
|
2024-05-11 21:45:07 +00:00
|
|
|
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
|
2024-05-08 22:17:47 +00:00
|
|
|
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME
|
2022-10-22 01:51:26 +00:00
|
|
|
},
|
|
|
|
// layers
|
2023-10-27 17:26:13 +00:00
|
|
|
{ },
|
2022-10-22 01:51:26 +00:00
|
|
|
// device
|
|
|
|
{
|
2024-05-11 21:45:07 +00:00
|
|
|
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
|
2022-10-22 01:51:26 +00:00
|
|
|
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,
|
2024-05-11 21:47:42 +00:00
|
|
|
#endif
|
|
|
|
#if defined( VK_KHR_maintenance4 )
|
|
|
|
VK_KHR_MAINTENANCE_4_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 = ×tampPeriodLowPassAlpha;
|
|
|
|
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 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( 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:
|
2024-04-02 15:17:47 +00:00
|
|
|
presentMode = enablePModeMailbox && r_vkPreferFastSync.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-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" );
|
2024-05-10 15:54:43 +00:00
|
|
|
|
2024-05-11 22:32:01 +00:00
|
|
|
// SRS - Suppress specific [ WARNING-Shader-OutputNotConsumed ] false positive validation warnings which are by design:
|
|
|
|
// 0xc81ad50e: vkCreateGraphicsPipelines(): pCreateInfos[0].pVertexInputState Vertex attribute at location X not consumed by vertex shader.
|
|
|
|
// 0x9805298c: vkCreateGraphicsPipelines(): pCreateInfos[0] fragment shader writes to output location 0 with no matching attachment.
|
2024-05-10 15:54:43 +00:00
|
|
|
#ifdef _WIN32
|
2024-05-11 22:32:01 +00:00
|
|
|
SetEnvironmentVariable( "VK_LAYER_MESSAGE_ID_FILTER", "0xc81ad50e;0x9805298c" );
|
2024-05-10 15:54:43 +00:00
|
|
|
#else
|
2024-05-11 22:32:01 +00:00
|
|
|
setenv( "VK_LAYER_MESSAGE_ID_FILTER", "0xc81ad50e:0x9805298c", 1 );
|
2024-05-10 15:54:43 +00:00
|
|
|
#endif
|
2022-10-22 01:51:26 +00:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|