26#ifdef RHI_VULKAN_HAS_X11
27#define VK_USE_PLATFORM_XLIB_KHR
28#define VK_USE_PLATFORM_XCB_KHR
33#ifdef RHI_VULKAN_HAS_WAYLAND
34#define VK_USE_PLATFORM_WAYLAND_KHR
35#include <wayland-client.h>
39#define VK_USE_PLATFORM_WIN32_KHR
42#include <vulkan/vulkan.h>
54#define VMA_STATIC_VULKAN_FUNCTIONS 0
55#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
56#include <vk_mem_alloc.h>
60#include <magic_enum.hpp>
62#include <present_blit_vert.h>
63#include <present_blit_frag.h>
73#include <unordered_map>
81 case VK_SUCCESS:
return "VK_SUCCESS";
82 case VK_NOT_READY:
return "VK_NOT_READY";
83 case VK_TIMEOUT:
return "VK_TIMEOUT";
84 case VK_EVENT_SET:
return "VK_EVENT_SET";
85 case VK_EVENT_RESET:
return "VK_EVENT_RESET";
86 case VK_INCOMPLETE:
return "VK_INCOMPLETE";
87 case VK_ERROR_OUT_OF_HOST_MEMORY:
return "VK_ERROR_OUT_OF_HOST_MEMORY";
88 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
89 case VK_ERROR_INITIALIZATION_FAILED:
return "VK_ERROR_INITIALIZATION_FAILED";
90 case VK_ERROR_DEVICE_LOST:
return "VK_ERROR_DEVICE_LOST";
91 case VK_ERROR_MEMORY_MAP_FAILED:
return "VK_ERROR_MEMORY_MAP_FAILED";
92 case VK_ERROR_LAYER_NOT_PRESENT:
return "VK_ERROR_LAYER_NOT_PRESENT";
93 case VK_ERROR_EXTENSION_NOT_PRESENT:
return "VK_ERROR_EXTENSION_NOT_PRESENT";
94 case VK_ERROR_FEATURE_NOT_PRESENT:
return "VK_ERROR_FEATURE_NOT_PRESENT";
95 case VK_ERROR_INCOMPATIBLE_DRIVER:
return "VK_ERROR_INCOMPATIBLE_DRIVER";
96 case VK_ERROR_SURFACE_LOST_KHR:
return "VK_ERROR_SURFACE_LOST_KHR";
97 case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
98 case VK_SUBOPTIMAL_KHR:
return "VK_SUBOPTIMAL_KHR";
99 case VK_ERROR_OUT_OF_DATE_KHR:
return "VK_ERROR_OUT_OF_DATE_KHR";
100 default:
return "VK_RESULT_UNKNOWN";
108 return static_cast<long long>( aTimer.
msecs() );
111#ifdef VK_USE_PLATFORM_XLIB_KHR
113struct X11_CONNECTION_OWNER
115 xcb_connection_t* xcb =
nullptr;
116 Display* xlib =
nullptr;
120 bool HasCompositor()
const
122 const std::string selection =
"_NET_WM_CM_S" + std::to_string( screen );
126 xcb_intern_atom_reply_t* atomReply = xcb_intern_atom_reply(
127 xcb, xcb_intern_atom( xcb, 1, selection.size(), selection.c_str() ),
nullptr );
132 const xcb_atom_t atom = atomReply->atom;
135 if( atom == XCB_ATOM_NONE )
138 xcb_get_selection_owner_reply_t* ownerReply =
139 xcb_get_selection_owner_reply( xcb, xcb_get_selection_owner( xcb, atom ),
nullptr );
144 const bool owned = ownerReply->owner != XCB_WINDOW_NONE;
151 const Atom atom = XInternAtom( xlib, selection.c_str(), True );
152 return atom !=
None && XGetSelectionOwner( xlib, atom ) !=
None;
161 xcb_disconnect( xcb );
164 XCloseDisplay( xlib );
173 VkDebugUtilsMessageSeverityFlagBitsEXT aSeverity,
174 VkDebugUtilsMessageTypeFlagsEXT aType,
175 const VkDebugUtilsMessengerCallbackDataEXT* aCallbackData,
271#ifdef VK_USE_PLATFORM_XLIB_KHR
272 PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR =
nullptr;
275#ifdef VK_USE_PLATFORM_XCB_KHR
276 PFN_vkCreateXcbSurfaceKHR CreateXcbSurfaceKHR =
nullptr;
279#ifdef VK_USE_PLATFORM_WAYLAND_KHR
280 PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR =
nullptr;
283#ifdef VK_USE_PLATFORM_WIN32_KHR
284 PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR =
nullptr;
309 const char*
GetName()
const override;
325 size_t sizeBytes,
size_t offset = 0 )
override;
331 size_t sizeBytes )
override;
352 uint32_t attachmentIndex = 0 )
const override;
364 void Draw( uint32_t vertexCount, uint32_t firstVertex = 0 )
override;
365 void DrawInstanced( uint32_t indexCount, uint32_t instanceCount,
366 uint32_t firstIndex = 0, int32_t vertexOffset = 0,
367 uint32_t firstInstance = 0 )
override;
368 void DrawIndexedInstanced( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex = 0,
369 int32_t vertexOffset = 0, uint32_t firstInstance = 0 )
override;
375 uint32_t height,
float dpiScale )
override;
378 int aAllowed )
override;
383 const RHI_SCISSOR_RECT* aSourceRect = nullptr )
override;
386 uint32_t attachmentIndex,
387 void* buffer,
size_t bufferSize )
override;
389 uint32_t aSourceX = 0, uint32_t aSourceY = 0 )
override;
408 VkRenderPass
makeRenderPass(
const std::vector<VK_ATTACHMENT>& aColor,
409 const std::optional<VK_ATTACHMENT>& aDepth );
426 bool endUploadCommands( VkCommandBuffer aCmd,
const std::unique_lock<std::recursive_mutex>& aLock );
514 int stageData(
const void* aData,
size_t aSizeBytes, VkBuffer& aBuffer );
517 void endStagedUpload( VkCommandBuffer aCmd, std::unique_lock<std::recursive_mutex>& aLock,
int aStaging );
521 std::unordered_map<uint32_t, BufferAllocation>
m_buffers;
532 std::unordered_map<uint32_t, ShaderAllocation>
m_shaders;
576 VkImageLayout newLayout,
577 VkPipelineStageFlags srcStage,
578 VkPipelineStageFlags dstStage );
628 bool createImage2D( VkFormat aFormat, uint32_t aWidth, uint32_t aHeight, VkImageUsageFlags aUsage,
629 bool aDedicated, VkImage& aImage, VmaAllocation& aAllocation );
632 VkImageView
createView( VkImage aImage, VkFormat aFormat, VkImageAspectFlags aAspect );
647 VkResult
buildPipeline( VkShaderModule aVertex, VkShaderModule aFragment,
648 const VkPipelineVertexInputStateCreateInfo& aVertexInput,
649 VkPrimitiveTopology aTopology,
650 const std::vector<VkPipelineColorBlendAttachmentState>& aBlend,
651 const VkPipelineDepthStencilStateCreateInfo& aDepthStencil,
652 const std::vector<VkDynamicState>& aDynamicStates, VkPipelineLayout aLayout,
653 VkRenderPass aRenderPass, VkPipeline& aPipeline );
660#ifdef VK_USE_PLATFORM_XLIB_KHR
661 X11_CONNECTION_OWNER x11Owner;
672 VkFormat
format = VK_FORMAT_R8G8B8A8_UNORM;
673 VkColorSpaceKHR
colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
718 VkResult
result = VK_ERROR_INITIALIZATION_FAILED;
727 VkSurfaceFormatKHR aFormat, VkExtent2D aExtent, VkSwapchainKHR aOld,
792 const int flags = wxDL_VERBATIM | wxDL_LAZY | wxDL_QUIET;
797 if( !
m_vulkanLib.Load( wxS(
"libvulkan.so.1" ), flags ) )
801 m_vk.GetInstanceProcAddr =
802 reinterpret_cast<PFN_vkGetInstanceProcAddr
>(
LoadVulkanFunction(
"vkGetInstanceProcAddr" ) );
804 if( !
m_vk.GetInstanceProcAddr )
807 auto loadGlobal = [
this](
const char* aName )
812 m_vk.CreateInstance =
reinterpret_cast<PFN_vkCreateInstance
>( loadGlobal(
"vkCreateInstance" ) );
813 m_vk.EnumerateInstanceLayerProperties =
reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties
>(
814 loadGlobal(
"vkEnumerateInstanceLayerProperties" ) );
815 m_vk.EnumerateInstanceExtensionProperties =
reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties
>(
816 loadGlobal(
"vkEnumerateInstanceExtensionProperties" ) );
818 return m_vk.CreateInstance !=
nullptr;
824 VkApplicationInfo appInfo{};
825 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
826 appInfo.pApplicationName =
"KiCad";
827 appInfo.applicationVersion = VK_MAKE_VERSION( 1, 0, 0 );
828 appInfo.pEngineName =
"KiCad RHI";
829 appInfo.engineVersion = VK_MAKE_VERSION( 1, 0, 0 );
830 appInfo.apiVersion = VK_API_VERSION_1_1;
832 std::vector<const char*> layers;
834 if( enableValidation &&
m_vk.EnumerateInstanceLayerProperties )
836 uint32_t layerCount = 0;
837 m_vk.EnumerateInstanceLayerProperties( &layerCount,
nullptr );
838 std::vector<VkLayerProperties> available( layerCount );
839 m_vk.EnumerateInstanceLayerProperties( &layerCount, available.data() );
841 for(
const auto& layer : available )
843 if( std::strcmp( layer.layerName,
"VK_LAYER_KHRONOS_validation" ) == 0 )
845 layers.push_back(
"VK_LAYER_KHRONOS_validation" );
851 if( enableValidation && layers.empty() )
853 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: validation requested but "
854 "VK_LAYER_KHRONOS_validation not available" ) );
858 extensions.push_back( VK_KHR_SURFACE_EXTENSION_NAME );
860#ifdef VK_USE_PLATFORM_XLIB_KHR
861 extensions.push_back( VK_KHR_XLIB_SURFACE_EXTENSION_NAME );
864#ifdef VK_USE_PLATFORM_XCB_KHR
865 extensions.push_back( VK_KHR_XCB_SURFACE_EXTENSION_NAME );
868#ifdef VK_USE_PLATFORM_WAYLAND_KHR
869 extensions.push_back( VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME );
872#ifdef VK_USE_PLATFORM_WIN32_KHR
873 extensions.push_back( VK_KHR_WIN32_SURFACE_EXTENSION_NAME );
876 bool debugUtilsAvailable =
false;
878 if( enableValidation &&
m_vk.EnumerateInstanceExtensionProperties )
880 uint32_t extCount = 0;
881 m_vk.EnumerateInstanceExtensionProperties(
nullptr, &extCount,
nullptr );
882 std::vector<VkExtensionProperties> availExts( extCount );
883 m_vk.EnumerateInstanceExtensionProperties(
nullptr, &extCount, availExts.data() );
885 for(
const auto& ext : availExts )
887 if( std::strcmp( ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME ) == 0 )
889 extensions.push_back( VK_EXT_DEBUG_UTILS_EXTENSION_NAME );
890 debugUtilsAvailable =
true;
896 if( enableValidation && !debugUtilsAvailable )
898 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: validation requested but "
899 "VK_EXT_debug_utils not available" ) );
903 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: requesting instance extension %s" ), ext );
905 for(
const auto& layer : layers )
906 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: requesting layer %s" ), layer );
908 VkInstanceCreateInfo createInfo{};
909 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
910 createInfo.pApplicationInfo = &appInfo;
911 createInfo.enabledLayerCount =
static_cast<uint32_t
>( layers.size() );
912 createInfo.ppEnabledLayerNames = layers.empty() ? nullptr : layers.data();
913 createInfo.enabledExtensionCount =
static_cast<uint32_t
>(
extensions.size() );
914 createInfo.ppEnabledExtensionNames =
extensions.data();
918 if(
result != VK_SUCCESS )
920 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: vkCreateInstance failed (%d)" ),
921 static_cast<int>(
result ) );
925#define LOAD_INST_FN( fn ) \
926 m_vk.fn = reinterpret_cast<PFN_vk##fn>( LoadInstanceFunction( "vk" #fn ) )
939 LOAD_INST_FN( GetPhysicalDeviceSurfaceCapabilitiesKHR );
941 LOAD_INST_FN( GetPhysicalDeviceSurfacePresentModesKHR );
943#ifdef VK_USE_PLATFORM_XLIB_KHR
947#ifdef VK_USE_PLATFORM_XCB_KHR
951#ifdef VK_USE_PLATFORM_WAYLAND_KHR
955#ifdef VK_USE_PLATFORM_WIN32_KHR
959 if( debugUtilsAvailable )
964 if(
m_vk.CreateDebugUtilsMessengerEXT )
966 VkDebugUtilsMessengerCreateInfoEXT dbgInfo{};
967 dbgInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
968 dbgInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
969 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
970 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
971 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
972 dbgInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
973 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
974 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
977 VkResult dbgResult =
m_vk.CreateDebugUtilsMessengerEXT(
980 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: debug messenger %s" ),
981 dbgResult == VK_SUCCESS ?
"created" :
"FAILED" );
993 if( !
m_vk.EnumeratePhysicalDevices )
996 uint32_t deviceCount = 0;
997 m_vk.EnumeratePhysicalDevices(
m_instance, &deviceCount,
nullptr );
999 if( deviceCount == 0 )
1002 std::vector<VkPhysicalDevice> devices( deviceCount );
1003 m_vk.EnumeratePhysicalDevices(
m_instance, &deviceCount, devices.data() );
1009 for( VkPhysicalDevice dev : devices )
1011 VkPhysicalDeviceProperties props;
1012 m_vk.GetPhysicalDeviceProperties( dev, &props );
1014 uint32_t queueFamilyCount = 0;
1015 m_vk.GetPhysicalDeviceQueueFamilyProperties( dev, &queueFamilyCount,
nullptr );
1016 std::vector<VkQueueFamilyProperties> queueFamilies( queueFamilyCount );
1017 m_vk.GetPhysicalDeviceQueueFamilyProperties( dev, &queueFamilyCount,
1018 queueFamilies.data() );
1020 bool hasGraphics =
false;
1022 for(
const auto& qf : queueFamilies )
1024 if( qf.queueFlags & VK_QUEUE_GRAPHICS_BIT )
1034 if( props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && !discrete )
1036 else if( props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU && !integrated )
1042 else if( integrated )
1049 VkPhysicalDeviceProperties props;
1051 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: selected GPU '%s' (type=%d) from %u device(s)" ),
1052 props.deviceName,
static_cast<int>( props.deviceType ), deviceCount );
1064 uint32_t queueFamilyCount = 0;
1066 std::vector<VkQueueFamilyProperties> queueFamilies( queueFamilyCount );
1068 queueFamilies.data() );
1072 for( uint32_t i = 0; i < queueFamilyCount; i++ )
1074 if( queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT )
1086 float queuePriority = 1.0f;
1087 VkDeviceQueueCreateInfo queueInfo{};
1088 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1090 queueInfo.queueCount = 1;
1091 queueInfo.pQueuePriorities = &queuePriority;
1093 VkPhysicalDeviceFeatures features{};
1095 std::vector<const char*> deviceExtensions;
1099 bool swapchainSupported =
true;
1101 std::vector<VkExtensionProperties> avail;
1103 auto has = [&](
const char* aName )
1105 return std::any_of( avail.begin(), avail.end(),
1106 [&](
const VkExtensionProperties& e )
1108 return std::strcmp( e.extensionName, aName ) == 0;
1112 if(
m_vk.EnumerateDeviceExtensionProperties )
1114 uint32_t extCount = 0;
1117 avail.resize( extCount );
1120 swapchainSupported = has( VK_KHR_SWAPCHAIN_EXTENSION_NAME );
1123 if( swapchainSupported )
1124 deviceExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME );
1126 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: swapchain extension unsupported; offscreen only" ) );
1129 VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures{};
1130 VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures{};
1132 presentIdFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR;
1133 presentWaitFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR;
1134 presentIdFeatures.pNext = &presentWaitFeatures;
1136 if( swapchainSupported &&
m_vk.GetPhysicalDeviceFeatures2 && has( VK_KHR_PRESENT_ID_EXTENSION_NAME )
1137 && has( VK_KHR_PRESENT_WAIT_EXTENSION_NAME ) )
1139 VkPhysicalDeviceFeatures2 query{};
1140 query.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1141 query.pNext = &presentIdFeatures;
1144 m_presentWait = presentIdFeatures.presentId && presentWaitFeatures.presentWait;
1149 deviceExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME );
1150 deviceExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME );
1153 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: present wait %s" ),
m_presentWait ?
"on" :
"off (fence)" );
1155 VkDeviceCreateInfo deviceInfo{};
1156 deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1157 deviceInfo.pNext =
m_presentWait ? &presentIdFeatures :
nullptr;
1158 deviceInfo.queueCreateInfoCount = 1;
1159 deviceInfo.pQueueCreateInfos = &queueInfo;
1160 deviceInfo.pEnabledFeatures = &features;
1161 deviceInfo.enabledExtensionCount =
static_cast<uint32_t
>( deviceExtensions.size() );
1162 deviceInfo.ppEnabledExtensionNames = deviceExtensions.data();
1166 if(
result != VK_SUCCESS )
1171 if(
m_vk.GetDeviceQueue )
1180#define LOAD_DEV_FN( fn ) \
1181 m_vk.fn = reinterpret_cast<PFN_vk##fn>( LoadDeviceFunction( "vk" #fn ) )
1260 VkPhysicalDeviceProperties props;
1263 m_deviceName = std::string(
"Vulkan (" ) + props.deviceName +
")";
1267 if(
m_vk.GetPhysicalDeviceProperties2 )
1269 VkPhysicalDeviceSubgroupProperties subgroup{};
1270 subgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
1271 VkPhysicalDeviceProperties2 properties{};
1272 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1273 properties.pNext = &subgroup;
1276 if( subgroup.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT )
1284 VmaVulkanFunctions vmaFuncs{};
1285 vmaFuncs.vkGetInstanceProcAddr =
m_vk.GetInstanceProcAddr;
1286 vmaFuncs.vkGetDeviceProcAddr =
m_vk.GetDeviceProcAddr;
1288 VmaAllocatorCreateInfo allocatorInfo{};
1289 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_1;
1293 allocatorInfo.pVulkanFunctions = &vmaFuncs;
1295 return vmaCreateAllocator( &allocatorInfo, &
m_allocator ) == VK_SUCCESS;
1304 | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1306 | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1308 return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1310 | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1313 return VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1324 return VK_FORMAT_R8G8B8A8_UNORM;
1330 return VK_FORMAT_D32_SFLOAT;
1341 return VK_FILTER_LINEAR;
1355 return VK_BLEND_FACTOR_ONE;
1366 return VK_BLEND_OP_ADD;
1378 return VK_COMPARE_OP_LESS;
1394 return VK_FORMAT_R32G32B32A32_SFLOAT;
1405 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1417 return VK_DYNAMIC_STATE_VIEWPORT;
1423 if( !
m_vk.CreateCommandPool )
1426 VkCommandPoolCreateInfo poolInfo{};
1427 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1428 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1437 VkCommandBufferAllocateInfo allocInfo{};
1438 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1440 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1441 allocInfo.commandBufferCount = 1;
1443 return m_vk.AllocateCommandBuffers(
m_device, &allocInfo, &aCmd ) == VK_SUCCESS;
1449 VkCommandBufferBeginInfo beginInfo{};
1450 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1451 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1453 return m_vk.BeginCommandBuffer( aCmd, &beginInfo );
1459 VkSubmitInfo submitInfo{};
1460 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1461 submitInfo.commandBufferCount = 1;
1462 submitInfo.pCommandBuffers = &aCmd;
1470 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1474 if(
m_vk.EndCommandBuffer( cmd ) == VK_SUCCESS &&
submitCommands( cmd ) == VK_SUCCESS )
1483 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: one-shot command submit failed" ) );
1506 if(
result != VK_SUCCESS )
1528 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1535 if( idleResult != VK_SUCCESS )
1536 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: SyncSubmittedPasses idle=%d" ), idleResult );
1545 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1582 const std::unique_lock<std::recursive_mutex>& aLock )
1584 if( !aLock.owns_lock() )
1594 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1603 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1622 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1630 if( candidate.
inUse || candidate.
size < aSizeBytes )
1634 best =
static_cast<int>( i );
1646 while( size < aSizeBytes )
1649 VkBufferCreateInfo stagingInfo{};
1650 stagingInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1651 stagingInfo.size = size;
1652 stagingInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1654 VmaAllocationCreateInfo stagingAllocInfo{};
1655 stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
1656 stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
1659 VmaAllocationInfo mapInfo{};
1661 if( vmaCreateBuffer(
m_allocator, &stagingInfo, &stagingAllocInfo, &staging.
buffer,
1662 &staging.
allocation, &mapInfo ) != VK_SUCCESS )
1667 staging.
mapped = mapInfo.pMappedData;
1668 staging.
size = size;
1669 staging.
inUse =
true;
1677 return static_cast<int>( i );
1694 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1697 aBuffer = staging.
buffer;
1698 std::memcpy( staging.
mapped, aData, aSizeBytes );
1716 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
1718 if( aIndex < 0 || aIndex >=
static_cast<int>(
m_stagingPool.size() ) )
1726 total += staging.size;
1740 if( largest < 0 || candidate.size >
m_stagingPool[largest].size )
1741 largest =
static_cast<int>( i );
1749 total -= victim.
size;
1760 vmaDestroyBuffer(
m_allocator, staging.buffer, staging.allocation );
1771 == std::this_thread::get_id();
1776 VkDebugUtilsMessageSeverityFlagBitsEXT aSeverity,
1777 VkDebugUtilsMessageTypeFlagsEXT aType,
1778 const VkDebugUtilsMessengerCallbackDataEXT* aCallbackData,
1781 const char* severityStr =
"UNKNOWN";
1783 if( aSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT )
1784 severityStr =
"ERROR";
1785 else if( aSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT )
1786 severityStr =
"WARNING";
1787 else if( aSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT )
1788 severityStr =
"INFO";
1789 else if( aSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT )
1790 severityStr =
"VERBOSE";
1792 const char* typeStr =
"GENERAL";
1794 if( aType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT )
1795 typeStr =
"VALIDATION";
1796 else if( aType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT )
1797 typeStr =
"PERFORMANCE";
1799 wxLogTrace(
traceRhi, wxS(
"Vulkan %s [%s]: %s" ),
1800 severityStr, typeStr, aCallbackData->pMessage );
1808 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: init (validation=%s)" ),
1809 enableValidation ?
"ON" :
"OFF" );
1813 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: InitLoader FAILED" ) );
1819 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateVkInstance FAILED" ) );
1825 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: SelectPhysicalDevice FAILED" ) );
1831 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateLogicalDevice FAILED" ) );
1837 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: InitVMA FAILED" ) );
1843 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: InitCommandPool FAILED" ) );
1847 if(
m_vk.CreatePipelineCache )
1849 VkPipelineCacheCreateInfo cacheInfo{};
1850 cacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
1890#ifdef VK_USE_PLATFORM_XLIB_KHR
1892 sa.x11Owner.Close();
1901 m_vk.DestroyRenderPass(
m_device, rp.renderPass,
nullptr );
1904 m_vk.DestroyDescriptorPool(
m_device, poolEntry.pool,
nullptr );
1907 m_vk.DestroyDescriptorSetLayout(
m_device, bgl.layout,
nullptr );
1916 m_vk.DestroyShaderModule(
m_device, shader.module,
nullptr );
1924 vmaDestroyBuffer(
m_allocator, buf.buffer, buf.allocation );
1940 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: destroyed" ) );
1945 VkImageUsageFlags aUsage,
bool aDedicated, VkImage& aImage,
1946 VmaAllocation& aAllocation )
1948 VkImageCreateInfo imageInfo{};
1949 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1950 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1951 imageInfo.format = aFormat;
1952 imageInfo.extent = { aWidth, aHeight, 1 };
1953 imageInfo.mipLevels = 1;
1954 imageInfo.arrayLayers = 1;
1955 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1956 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1957 imageInfo.usage = aUsage;
1958 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1959 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1961 VmaAllocationCreateInfo allocInfo{};
1962 allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
1965 allocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1967 return vmaCreateImage(
m_allocator, &imageInfo, &allocInfo, &aImage, &aAllocation,
nullptr ) == VK_SUCCESS;
1973 VkImageViewCreateInfo viewInfo{};
1974 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1975 viewInfo.image = aImage;
1976 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1977 viewInfo.format = aFormat;
1978 viewInfo.subresourceRange.aspectMask = aAspect;
1979 viewInfo.subresourceRange.levelCount = 1;
1980 viewInfo.subresourceRange.layerCount = 1;
1984 if(
m_vk.CreateImageView(
m_device, &viewInfo,
nullptr, &view ) != VK_SUCCESS )
1993 VkSamplerCreateInfo samplerInfo{};
1994 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1995 samplerInfo.magFilter = aFilter;
1996 samplerInfo.minFilter = aFilter;
1997 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
1998 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1999 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
2000 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
2004 if(
m_vk.CreateSampler(
m_device, &samplerInfo,
nullptr, &sampler ) != VK_SUCCESS )
2031 for( VkDescriptorSetLayout dsLayout : aPipeline.
dsLayouts )
2032 m_vk.DestroyDescriptorSetLayout(
m_device, dsLayout,
nullptr );
2051 for(
size_t i = 0; i < aFb.
colorImages.size(); i++ )
2077 switch( aEntry.
type )
2085 vmaDestroyBuffer(
m_allocator, it->second.buffer, it->second.allocation );
2113 m_vk.DestroyShaderModule(
m_device, it->second.module,
nullptr );
2141 m_vk.DestroyDescriptorSetLayout(
m_device, it->second.layout,
nullptr );
2169 m_vk.DestroyRenderPass(
m_device, it->second.renderPass,
nullptr );
2251 VkBufferCreateInfo bufInfo{};
2252 bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
2255 bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2257 VmaAllocationCreateInfo allocInfo{};
2258 allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
2262 allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT
2263 | VMA_ALLOCATION_CREATE_MAPPED_BIT;
2268 VmaAllocationInfo allocationInfo{};
2271 &buffer, &allocation, &allocationInfo );
2273 if(
result != VK_SUCCESS )
2303 size_t sizeBytes,
size_t offset )
2305 if( !
m_bufferPool.IsValid( handle ) || !data || sizeBytes == 0 )
2317 if( it->second.mappable && it->second.mappedPtr )
2324 std::memcpy(
static_cast<uint8_t*
>( it->second.mappedPtr ) + offset, data, sizeBytes );
2325 vmaFlushAllocation(
m_allocator, it->second.allocation, offset, sizeBytes );
2330 const int stagingIndex =
stageData( data, sizeBytes, stagingBuffer );
2332 if( stagingIndex < 0 )
2335 std::unique_lock<std::recursive_mutex> oneShotLock(
m_deviceMutex, std::defer_lock );
2344 VkBufferCopy copyRegion{};
2345 copyRegion.srcOffset = 0;
2346 copyRegion.dstOffset = offset;
2347 copyRegion.size = sizeBytes;
2349 m_vk.CmdCopyBuffer( cmd, stagingBuffer, it->second.buffer, 1, ©Region );
2376 if(
createImage2D( vkFormat, desc.
width, desc.
height, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
2430 if( stagingIndex < 0 )
2433 std::unique_lock<std::recursive_mutex> oneShotLock(
m_deviceMutex, std::defer_lock );
2443 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT );
2445 VkBufferImageCopy region{};
2446 region.bufferOffset = 0;
2447 region.bufferRowLength = 0;
2448 region.bufferImageHeight = 0;
2449 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2450 region.imageSubresource.mipLevel = 0;
2451 region.imageSubresource.baseArrayLayer = 0;
2452 region.imageSubresource.layerCount = 1;
2453 region.imageOffset = { 0, 0, 0 };
2454 region.imageExtent = { tex.
width, tex.
height, 1 };
2456 m_vk.CmdCopyBufferToImage( cmd, stagingBuffer, tex.
image,
2457 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion );
2460 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT );
2477 VkShaderModuleCreateInfo moduleInfo{};
2478 moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
2480 moduleInfo.pCode =
reinterpret_cast<const uint32_t*
>( desc.
bytecode );
2485 if(
result != VK_SUCCESS )
2487 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateShaderModule FAILED %s(%d) bytes=%zu" ),
2519 case VK_IMAGE_LAYOUT_UNDEFINED:
return 0;
2520 case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
2521 case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
2522 case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
2523 case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2524 case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
2525 case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
2532static VkImageMemoryBarrier
imageBarrier( VkImage aImage, VkImageAspectFlags aAspect, VkImageLayout aOld,
2533 VkImageLayout aNew, VkAccessFlags aSrcAccess, VkAccessFlags aDstAccess )
2535 VkImageMemoryBarrier barrier{};
2536 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2537 barrier.srcAccessMask = aSrcAccess;
2538 barrier.dstAccessMask = aDstAccess;
2539 barrier.oldLayout = aOld;
2540 barrier.newLayout = aNew;
2541 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2542 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2543 barrier.image = aImage;
2544 barrier.subresourceRange = { aAspect, 0, 1, 0, 1 };
2559 return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
2565 VkShaderStageFlags flags = 0;
2568 flags |= VK_SHADER_STAGE_VERTEX_BIT;
2571 flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
2574 flags |= VK_SHADER_STAGE_COMPUTE_BIT;
2583 return { aFormat, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED,
2584 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR };
2589 const std::optional<VK_ATTACHMENT>& aDepth )
2591 std::vector<VkAttachmentDescription> attachments;
2592 std::vector<VkAttachmentReference>
colorRefs;
2596 VkAttachmentDescription att{};
2597 att.format = aAttachment.format;
2598 att.samples = VK_SAMPLE_COUNT_1_BIT;
2599 att.loadOp = aAttachment.loadOp;
2600 att.storeOp = aAttachment.storeOp;
2601 att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2602 att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2603 att.initialLayout = aAttachment.initialLayout;
2604 att.finalLayout = aAttachment.finalLayout;
2610 const uint32_t
index =
static_cast<uint32_t
>( attachments.size() );
2612 colorRefs.push_back( {
index, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } );
2613 attachments.push_back( describe( color ) );
2616 const VkAttachmentReference depthRef{
static_cast<uint32_t
>( attachments.size() ),
2617 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
2620 attachments.push_back( describe( *aDepth ) );
2622 VkSubpassDescription subpass{};
2623 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
2624 subpass.colorAttachmentCount =
static_cast<uint32_t
>(
colorRefs.size() );
2625 subpass.pColorAttachments =
colorRefs.data();
2628 subpass.pDepthStencilAttachment = &depthRef;
2630 VkRenderPassCreateInfo rpInfo{};
2631 rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
2632 rpInfo.attachmentCount =
static_cast<uint32_t
>( attachments.size() );
2633 rpInfo.pAttachments = attachments.data();
2634 rpInfo.subpassCount = 1;
2635 rpInfo.pSubpasses = &subpass;
2639 if(
m_vk.CreateRenderPass(
m_device, &rpInfo,
nullptr, &renderPass ) != VK_SUCCESS )
2650 if( !
m_vk.CreateGraphicsPipelines || !
m_vk.CreatePipelineLayout || !
m_vk.CreateRenderPass )
2667 std::optional<VK_ATTACHMENT> depth;
2669 if( useDepthStencil )
2672 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
2675 const VkRenderPass renderPass =
2677 VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED,
2678 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } },
2684 std::vector<VkDescriptorSetLayout> dsLayouts;
2687 auto destroyDsLayouts = [&]()
2689 for( VkDescriptorSetLayout dsLayout : dsLayouts )
2690 m_vk.DestroyDescriptorSetLayout(
m_device, dsLayout,
nullptr );
2700 m_vk.DestroyRenderPass(
m_device, renderPass,
nullptr );
2704 dsLayouts.push_back( dsLayout );
2707 VkPushConstantRange pushRange{};
2708 pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
2709 pushRange.offset = 0;
2712 VkPipelineLayoutCreateInfo layoutInfo{};
2713 layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
2714 layoutInfo.setLayoutCount =
static_cast<uint32_t
>( dsLayouts.size() );
2715 layoutInfo.pSetLayouts = dsLayouts.empty() ? nullptr : dsLayouts.data();
2716 layoutInfo.pushConstantRangeCount = 1;
2717 layoutInfo.pPushConstantRanges = &pushRange;
2721 if(
m_vk.CreatePipelineLayout(
m_device, &layoutInfo,
nullptr, &pipelineLayout ) != VK_SUCCESS )
2724 m_vk.DestroyRenderPass(
m_device, renderPass,
nullptr );
2728 std::vector<VkVertexInputBindingDescription> bindings;
2729 std::vector<VkVertexInputAttributeDescription> attributes;
2735 VkVertexInputBindingDescription binding{};
2736 binding.binding = b;
2737 binding.stride = vb.stride;
2739 ? VK_VERTEX_INPUT_RATE_INSTANCE
2740 : VK_VERTEX_INPUT_RATE_VERTEX;
2741 bindings.push_back( binding );
2743 for(
const auto& attr : vb.attributes )
2745 VkVertexInputAttributeDescription a{};
2746 a.location = attr.location;
2749 a.offset = attr.offset;
2750 attributes.push_back( a );
2754 VkPipelineVertexInputStateCreateInfo vertexInput{};
2755 vertexInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
2756 vertexInput.vertexBindingDescriptionCount =
static_cast<uint32_t
>( bindings.size() );
2757 vertexInput.pVertexBindingDescriptions = bindings.data();
2758 vertexInput.vertexAttributeDescriptionCount =
static_cast<uint32_t
>( attributes.size() );
2759 vertexInput.pVertexAttributeDescriptions = attributes.data();
2761 std::vector<VkPipelineColorBlendAttachmentState> blendAttachments;
2765 VkPipelineColorBlendAttachmentState defaultBlend{};
2766 defaultBlend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
2767 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
2768 blendAttachments.push_back( defaultBlend );
2774 VkPipelineColorBlendAttachmentState vkBlend{};
2775 vkBlend.blendEnable = bs.enabled ? VK_TRUE : VK_FALSE;
2782 vkBlend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
2783 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
2784 blendAttachments.push_back( vkBlend );
2788 VkPipelineDepthStencilStateCreateInfo depthStencil{};
2789 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
2793 depthStencil.stencilTestEnable = VK_FALSE;
2796 std::vector<VkDynamicState> dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT,
2797 VK_DYNAMIC_STATE_SCISSOR };
2803 if( vkDs != VK_DYNAMIC_STATE_VIEWPORT && vkDs != VK_DYNAMIC_STATE_SCISSOR )
2804 dynamicStates.push_back( vkDs );
2808 const VkResult
result =
buildPipeline( vertIt->second.module, fragIt->second.module, vertexInput,
2810 dynamicStates, pipelineLayout, renderPass, pipeline );
2812 if(
result != VK_SUCCESS )
2814 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateGraphicsPipelines FAILED %s(%d)" ),
2816 m_vk.DestroyPipelineLayout(
m_device, pipelineLayout,
nullptr );
2817 m_vk.DestroyRenderPass(
m_device, renderPass,
nullptr );
2827 m_vk.DestroyPipelineLayout(
m_device, pipelineLayout,
nullptr );
2828 m_vk.DestroyRenderPass(
m_device, renderPass,
nullptr );
2837 pa.
layout = pipelineLayout;
2841 const double ms = createTimer.
msecs();
2846 wxLogTrace(
traceRhi, wxS(
"%s: CreatePipeline took %.1fms (total %.1fms)" ),
2861 const VkPipelineVertexInputStateCreateInfo& aVertexInput,
2862 VkPrimitiveTopology aTopology,
2863 const std::vector<VkPipelineColorBlendAttachmentState>& aBlend,
2864 const VkPipelineDepthStencilStateCreateInfo& aDepthStencil,
2865 const std::vector<VkDynamicState>& aDynamicStates,
2866 VkPipelineLayout aLayout, VkRenderPass aRenderPass,
2867 VkPipeline& aPipeline )
2869 VkPipelineShaderStageCreateInfo stages[2] = {};
2870 stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2871 stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
2872 stages[0].module = aVertex;
2873 stages[0].pName =
"main";
2874 stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2875 stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
2876 stages[1].module = aFragment;
2877 stages[1].pName =
"main";
2879 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
2880 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
2881 inputAssembly.topology = aTopology;
2884 VkPipelineViewportStateCreateInfo viewportState{};
2885 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
2886 viewportState.viewportCount = 1;
2887 viewportState.scissorCount = 1;
2889 VkPipelineRasterizationStateCreateInfo rasterizer{};
2890 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
2891 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
2892 rasterizer.cullMode = VK_CULL_MODE_NONE;
2893 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
2894 rasterizer.lineWidth = 1.0f;
2896 VkPipelineMultisampleStateCreateInfo multisample{};
2897 multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
2898 multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
2900 VkPipelineColorBlendStateCreateInfo blendState{};
2901 blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
2902 blendState.attachmentCount =
static_cast<uint32_t
>( aBlend.size() );
2903 blendState.pAttachments = aBlend.data();
2905 VkPipelineDynamicStateCreateInfo dynamicStateInfo{};
2906 dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
2907 dynamicStateInfo.dynamicStateCount =
static_cast<uint32_t
>( aDynamicStates.size() );
2908 dynamicStateInfo.pDynamicStates = aDynamicStates.data();
2910 VkGraphicsPipelineCreateInfo pipelineInfo{};
2911 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
2912 pipelineInfo.stageCount = 2;
2913 pipelineInfo.pStages = stages;
2914 pipelineInfo.pVertexInputState = &aVertexInput;
2915 pipelineInfo.pInputAssemblyState = &inputAssembly;
2916 pipelineInfo.pViewportState = &viewportState;
2917 pipelineInfo.pRasterizationState = &rasterizer;
2918 pipelineInfo.pMultisampleState = &multisample;
2919 pipelineInfo.pColorBlendState = &blendState;
2920 pipelineInfo.pDepthStencilState = &aDepthStencil;
2921 pipelineInfo.pDynamicState = &dynamicStateInfo;
2922 pipelineInfo.layout = aLayout;
2923 pipelineInfo.renderPass = aRenderPass;
2931 std::vector<VkDescriptorSetLayoutBinding> bindings;
2939 VkDescriptorSetLayoutCreateInfo layoutInfo{};
2940 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
2941 layoutInfo.bindingCount =
static_cast<uint32_t
>( bindings.size() );
2942 layoutInfo.pBindings = bindings.data();
2946 if(
m_vk.CreateDescriptorSetLayout(
m_device, &layoutInfo,
nullptr, &layout ) != VK_SUCCESS )
2966 for(
size_t i = 0; i < 3; ++i )
2982 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
2984 size_t bindings = 0;
2987 bindings += layout.
entries.size();
3007 allocation.
desc = aDesc;
3027 VkPushConstantRange pushRange{};
3028 pushRange.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
3030 VkPipelineLayoutCreateInfo layoutInfo{};
3031 layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
3032 layoutInfo.setLayoutCount =
static_cast<uint32_t
>( allocation.
native.
dsLayouts.size() );
3035 layoutInfo.pPushConstantRanges = aDesc.
pushConstantBytes ? &pushRange :
nullptr;
3043 sizeof( uint32_t ) } };
3045 for(
const auto& constant : aDesc.
constants )
3048 { constant.id,
static_cast<uint32_t
>( values.size() *
sizeof( uint32_t ) ),
sizeof( uint32_t ) } );
3049 values.push_back( constant.value );
3052 VkSpecializationInfo specialization{};
3053 specialization.mapEntryCount =
static_cast<uint32_t
>( entries.size() );
3054 specialization.pMapEntries = entries.data();
3055 specialization.dataSize = values.size() *
sizeof( uint32_t );
3056 specialization.pData = values.data();
3057 VkComputePipelineCreateInfo pipelineInfo{};
3058 pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
3059 pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
3060 pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
3062 pipelineInfo.stage.pName =
"main";
3063 pipelineInfo.stage.pSpecializationInfo = &specialization;
3085 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3119 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3154 void* mapped =
nullptr;
3158 std::vector<int> staging;
3159 std::vector<READBACK> readbacks;
3162 auto release = [&]()
3164 for(
int index : staging )
3167 for(
const READBACK& readback : readbacks )
3168 vmaDestroyBuffer(
m_allocator, readback.buffer, readback.allocation );
3171 auto fail = [&]( VkResult aResult )
3180 return fail( VK_ERROR_OUT_OF_HOST_MEMORY );
3184 if(
result != VK_SUCCESS )
3190 auto barrier = [&]( VkPipelineStageFlags aSrcStage, VkAccessFlags aSrcAccess, VkPipelineStageFlags aDstStage,
3191 VkAccessFlags aDstAccess )
3193 VkMemoryBarrier memory{};
3194 memory.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
3195 memory.srcAccessMask = aSrcAccess;
3196 memory.dstAccessMask = aDstAccess;
3197 m_vk.CmdPipelineBarrier( cmd, aSrcStage, aDstStage, 0, 1, &memory, 0,
nullptr, 0,
nullptr );
3200 const VkPipelineStageFlags work = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
3201 const VkAccessFlags workAccess = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
3202 | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
3205 barrier( VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_MEMORY_WRITE_BIT, work, workAccess );
3208 std::array<RHI_BindGroupHandle, RHI_MAX_BIND_GROUPS> bound{};
3212 switch( command.op )
3217 if( !command.bytes )
3221 VkBufferCopy
copy{};
3222 copy.size = command.bytes;
3224 if( command.op == OP::UPLOAD )
3226 const int index =
stageData( command.data.data(), command.bytes, host );
3231 return fail( VK_ERROR_OUT_OF_DEVICE_MEMORY );
3234 staging.push_back(
index );
3235 copy.dstOffset = command.offset;
3239 VkBufferCreateInfo
info{};
3240 info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3241 info.size = command.bytes;
3242 info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3243 VmaAllocationCreateInfo allocInfo{};
3244 allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
3245 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
3246 VmaAllocationInfo mapped{};
3251 if(
result != VK_SUCCESS )
3257 readback.mapped = mapped.pMappedData;
3258 readbacks.push_back( readback );
3259 host = readback.buffer;
3260 copy.srcOffset = command.offset;
3265 barrier( work, workAccess, VK_PIPELINE_STAGE_TRANSFER_BIT,
3266 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT );
3267 m_vk.CmdCopyBuffer( cmd, command.op == OP::UPLOAD ? host : device, command.op == OP::UPLOAD ? device : host,
3269 barrier( VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, work | VK_PIPELINE_STAGE_HOST_BIT,
3270 workAccess | VK_ACCESS_HOST_READ_BIT );
3276 m_vk.CmdBindPipeline( cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->
native.
pipeline );
3279 case OP::BIND_GROUP: bound[command.index] = command.group;
break;
3281 case OP::PUSH_CONSTANTS:
3282 m_vk.CmdPushConstants( cmd, pipeline->
native.
layout, VK_SHADER_STAGE_COMPUTE_BIT, 0,
3283 static_cast<uint32_t
>( command.bytes ), command.data.data() );
3293 m_vk.CmdBindDescriptorSets( cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->
native.
layout,
3294 static_cast<uint32_t
>( i ), 1, &set, 0,
nullptr );
3297 m_vk.CmdDispatch( cmd, command.groupsX, command.groupsY, 1 );
3301 barrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT,
3302 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT );
3309 if(
result == VK_SUCCESS )
3312 if(
result == VK_SUCCESS )
3317 if(
result != VK_SUCCESS )
3322 for(
const READBACK& readback : readbacks )
3324 vmaInvalidateAllocation(
m_allocator, readback.allocation, 0, readback.command->bytes );
3325 std::memcpy( readback.command->destination, readback.mapped, readback.command->bytes );
3339 if( !
m_vk.CreateDescriptorSetLayout )
3351 m_vk.DestroyDescriptorSetLayout(
m_device, vkLayout,
nullptr );
3378 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3382 if( entry.allocatedSets < entry.maxSets )
3384 entry.allocatedSets++;
3390 const uint32_t setsPerPool = 128;
3392 VkDescriptorPoolSize poolSizes[] = {
3393 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, setsPerPool * 2 },
3394 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, setsPerPool },
3395 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, setsPerPool * 16 },
3398 VkDescriptorPoolCreateInfo poolInfo{};
3399 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
3400 poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
3401 poolInfo.maxSets = setsPerPool;
3403 poolInfo.poolSizeCount =
static_cast<uint32_t
>( std::size( poolSizes ) ) - (
m_storageDescriptors ? 0 : 1 );
3404 poolInfo.pPoolSizes = poolSizes;
3408 if(
m_vk.CreateDescriptorPool(
m_device, &poolInfo,
nullptr, &pool ) != VK_SUCCESS )
3410 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireDescriptorPool FAILED" ) );
3421 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3425 if( entry.pool == pool )
3430 if( entry.allocatedSets > 0 )
3431 entry.allocatedSets--;
3440 VkImageLayout newLayout,
3441 VkPipelineStageFlags srcStage,
3442 VkPipelineStageFlags dstStage )
3451 m_vk.CmdPipelineBarrier( cmd, srcStage, dstStage, 0, 0,
nullptr, 0,
nullptr, 1, &barrier );
3460 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup invalid layout=%u" ),
3465 if( !
m_vk.CreateDescriptorPool || !
m_vk.AllocateDescriptorSets || !
m_vk.UpdateDescriptorSets )
3467 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup missing descriptor funcs" ) );
3471 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3478 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup layout not found=%u" ),
3483 const auto& layoutDesc = layoutIt->second.desc;
3490 VkDescriptorSetAllocateInfo allocInfo{};
3491 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
3492 allocInfo.descriptorPool = pool;
3493 allocInfo.descriptorSetCount = 1;
3494 allocInfo.pSetLayouts = &layoutIt->second.layout;
3498 if(
m_vk.AllocateDescriptorSets(
m_device, &allocInfo, &dset ) != VK_SUCCESS )
3500 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup descriptor set alloc FAILED "
3507 std::vector<VkWriteDescriptorSet> writes;
3508 std::vector<VkDescriptorBufferInfo> bufferInfos;
3509 std::vector<VkDescriptorImageInfo> imageInfos;
3510 bufferInfos.reserve( desc.
entries.size() );
3511 imageInfos.reserve( desc.
entries.size() );
3518 VkWriteDescriptorSet write{};
3519 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3520 write.dstSet = dset;
3521 write.dstBinding =
res.binding;
3522 write.dstArrayElement = 0;
3523 write.descriptorCount = 1;
3530 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup missing buffer "
3531 "layout=%u binding=%u handle=%u" ),
3533 static_cast<unsigned>(
res.bufferHandle.value ) );
3541 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup buffer lookup FAILED "
3542 "layout=%u binding=%u handle=%u" ),
3544 static_cast<unsigned>(
res.bufferHandle.value ) );
3548 VkDescriptorBufferInfo bi{};
3549 bi.buffer = bufIt->second.buffer;
3550 bi.offset =
res.offset;
3551 bi.range = (
res.size > 0 ) ?
res.size : bufIt->second.size;
3552 bufferInfos.push_back( bi );
3554 write.pBufferInfo = &bufferInfos.back();
3560 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup missing texture "
3561 "layout=%u binding=%u handle=%u" ),
3563 static_cast<unsigned>(
res.textureHandle.value ) );
3571 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup texture lookup FAILED "
3572 "layout=%u binding=%u handle=%u" ),
3574 static_cast<unsigned>(
res.textureHandle.value ) );
3578 VkDescriptorImageInfo ii{};
3579 ii.sampler = texIt->second.sampler;
3580 ii.imageView = texIt->second.imageView;
3581 ii.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3582 imageInfos.push_back( ii );
3584 write.pImageInfo = &imageInfos.back();
3587 writes.push_back( write );
3591 if( !desc.
entries.empty() && writes.empty() )
3593 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup produced no descriptor writes "
3594 "layout=%u entries=%zu" ),
3600 if( !writes.empty() )
3602 m_vk.UpdateDescriptorSets(
m_device,
static_cast<uint32_t
>( writes.size() ),
3603 writes.data(), 0,
nullptr );
3610 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateBindGroup handle alloc FAILED" ) );
3623 for(
const auto& entry : desc.
entries )
3625 if( entry.bufferHandle )
3640 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3644 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup skipped renderPassActive=%d "
3645 "cmd=%p group=%u handle=%u" ),
3647 static_cast<unsigned>(
group.value ) );
3653 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup invalid group index=%u" ),
3660 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup invalid handle=%u" ),
3661 static_cast<unsigned>(
group.value ) );
3669 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup lookup FAILED handle=%u" ),
3670 static_cast<unsigned>(
group.value ) );
3676 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup missing active pipeline "
3677 "group=%u handle=%u active=%u" ),
3678 groupIndex,
static_cast<unsigned>(
group.value ),
3687 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: BindGroup pipeline lookup FAILED active=%u" ),
3692 for( uint32_t bufferIndex : bgIt->second.bufferIndices )
3694 auto bufIt =
m_buffers.find( bufferIndex );
3701 pipeIt->second.layout, groupIndex, 1,
3702 &bgIt->second.descriptorSet, 0,
nullptr );
3715 return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
3727 return VK_ATTACHMENT_STORE_OP_DONT_CARE;
3733 if( !
m_vk.CreateRenderPass )
3741 std::vector<VK_ATTACHMENT> colors;
3748 : VK_IMAGE_LAYOUT_UNDEFINED,
3749 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } );
3752 std::optional<VK_ATTACHMENT> depth;
3759 ds.depthLoadOp ==
RHI_LOAD_OP::LOAD ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
3760 : VK_IMAGE_LAYOUT_UNDEFINED,
3761 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
3773 m_vk.DestroyRenderPass(
m_device, renderPass,
nullptr );
3820 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
3821 | VK_IMAGE_USAGE_SAMPLED_BIT,
3822 true,
image, allocation ) )
3838 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
3839 | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
3852 std::vector<VK_ATTACHMENT> colors;
3857 VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED,
3858 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } );
3861 std::optional<VK_ATTACHMENT> depth;
3866 VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
3867 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
3875 std::vector<VkImageView> framebufferAttachments = fb.
colorViews;
3878 framebufferAttachments.push_back( fb.
depthView );
3880 VkFramebufferCreateInfo fbInfo{};
3881 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
3883 fbInfo.attachmentCount =
static_cast<uint32_t
>( framebufferAttachments.size() );
3884 fbInfo.pAttachments = framebufferAttachments.data();
3885 fbInfo.width = desc.
width;
3886 fbInfo.height = desc.
height;
3898 fba = std::move( fb );
3900 for( uint32_t i = 0; i < fba.
colorViews.size(); i++ )
3907 const VkSampler sampler =
createSampler( VK_FILTER_LINEAR );
3912 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: FBO color sampler creation failed" ) );
3935 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
3954 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3955 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
3956 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT );
3960 m_vk.EndCommandBuffer( initCmd );
3971 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: FBO init-transition submit failed" ) );
3998 if( attachmentIndex >= it->second.colorTextureHandles.size() )
4001 return it->second.colorTextureHandles[attachmentIndex];
4026 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4039 std::vector<VkClearValue> clearValues;
4040 clearValues.reserve( rpa.
colorDescs.size() + 1 );
4055 cv.color.float32[0] = ca.clearColor[0];
4056 cv.color.float32[1] = ca.clearColor[1];
4057 cv.color.float32[2] = ca.clearColor[2];
4058 cv.color.float32[3] = ca.clearColor[3];
4061 clearValues.push_back( cv );
4068 VkClearValue depthStencilClear{};
4071 clearValues.push_back( depthStencilClear );
4074 VkRenderPassBeginInfo rpBeginInfo{};
4075 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
4078 rpBeginInfo.renderArea = { { 0, 0 }, { fba.
width, fba.
height } };
4079 rpBeginInfo.clearValueCount =
static_cast<uint32_t
>( clearValues.size() );
4080 rpBeginInfo.pClearValues = clearValues.data();
4082 m_vk.CmdBeginRenderPass(
m_currentCmd, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE );
4095 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4105 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: EndRenderPass submit=%d" ),
result );
4118 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4126 vp.width = viewport.
width;
4127 vp.height = viewport.
height;
4137 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4143 rect.offset = { scissor.
x, scissor.
y };
4152 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4168 m_vk.CmdBindPipeline(
m_currentCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, it->second.pipeline );
4175 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4192 VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
4193 0,
static_cast<uint32_t
>( sizeBytes ), data );
4199 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4214 VkBuffer vkBuf = it->second.buffer;
4215 VkDeviceSize vkOffset = 0;
4223 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4238 m_vk.CmdBindIndexBuffer(
m_currentCmd, it->second.buffer, 0, VK_INDEX_TYPE_UINT16 );
4244 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4254 uint32_t firstVertex, int32_t ,
4255 uint32_t firstInstance )
4257 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4262 m_vk.CmdDraw(
m_currentCmd, vertexCount, instanceCount, firstVertex, firstInstance );
4267 int32_t vertexOffset, uint32_t firstInstance )
4269 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
4274 m_vk.CmdDrawIndexed(
m_currentCmd, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance );
4280 if( !
m_vk.GetPhysicalDeviceSurfaceSupportKHR )
4284 VkBool32 presentSupport = VK_FALSE;
4287 &presentSupport ) != VK_SUCCESS
4288 || !presentSupport )
4305 VkDescriptorSetLayoutBinding dsBinding{};
4306 dsBinding.binding = 0;
4307 dsBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4308 dsBinding.descriptorCount = 1;
4309 dsBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
4311 VkDescriptorSetLayoutCreateInfo dsLayoutInfo{};
4312 dsLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
4313 dsLayoutInfo.bindingCount = 1;
4314 dsLayoutInfo.pBindings = &dsBinding;
4326 VkPipelineLayoutCreateInfo layoutInfo{};
4327 layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
4328 layoutInfo.setLayoutCount = 1;
4338 VkShaderModuleCreateInfo vertModuleInfo{};
4339 vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
4340 vertModuleInfo.codeSize = k_present_blit_vert.spirvSize;
4341 vertModuleInfo.pCode =
reinterpret_cast<const uint32_t*
>( k_present_blit_vert.spirv );
4345 if(
m_vk.CreateShaderModule(
m_device, &vertModuleInfo,
nullptr, &vertModule ) != VK_SUCCESS )
4348 VkShaderModuleCreateInfo fragModuleInfo{};
4349 fragModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
4350 fragModuleInfo.codeSize = k_present_blit_frag.spirvSize;
4351 fragModuleInfo.pCode =
reinterpret_cast<const uint32_t*
>( k_present_blit_frag.spirv );
4355 if(
m_vk.CreateShaderModule(
m_device, &fragModuleInfo,
nullptr, &fragModule ) != VK_SUCCESS )
4357 m_vk.DestroyShaderModule(
m_device, vertModule,
nullptr );
4364 if( !pipelineRenderPass )
4366 m_vk.DestroyShaderModule(
m_device, vertModule,
nullptr );
4367 m_vk.DestroyShaderModule(
m_device, fragModule,
nullptr );
4372 VkPipelineVertexInputStateCreateInfo vertexInput{};
4373 vertexInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
4375 VkPipelineColorBlendAttachmentState blendAtt{};
4376 blendAtt.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
4377 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
4379 VkPipelineDepthStencilStateCreateInfo depthStencil{};
4380 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
4383 const VkResult
result =
buildPipeline( vertModule, fragModule, vertexInput, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
4384 { blendAtt }, depthStencil,
4385 { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR },
4388 m_vk.DestroyShaderModule(
m_device, vertModule,
nullptr );
4389 m_vk.DestroyShaderModule(
m_device, fragModule,
nullptr );
4390 m_vk.DestroyRenderPass(
m_device, pipelineRenderPass,
nullptr );
4392 if(
result != VK_SUCCESS )
4397 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: present blit pipeline created for format=%d" ),
4398 static_cast<int>( aSwapchainFormat ) );
4420 std::vector<RHI_PRESENT_MODE> offered;
4421 bool composited = aSurface.
wayland;
4423#ifdef VK_USE_PLATFORM_XLIB_KHR
4425 composited = composited || aSurface.x11Owner.HasCompositor();
4428 if(
m_vk.GetPhysicalDeviceSurfacePresentModesKHR )
4430 uint32_t modeCount = 0;
4432 std::vector<VkPresentModeKHR> modes( modeCount );
4435 for( VkPresentModeKHR mode : modes )
4437 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: present mode available=%d" ),
static_cast<int>( mode ) );
4439 if( mode == VK_PRESENT_MODE_MAILBOX_KHR )
4441 else if( mode == VK_PRESENT_MODE_IMMEDIATE_KHR )
4446#if defined( __linux__ )
4447 const bool immediateUnderCompositor =
true;
4449 const bool immediateUnderCompositor =
false;
4454 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: present mode %s (compositor %s)" ),
4455 std::string( magic_enum::enum_name( chosen ) ), composited ?
"owns the present" :
"absent" );
4464 return VK_PRESENT_MODE_FIFO_KHR;
4470 if( !
m_vk.GetSwapchainImagesKHR || !
m_vk.CreateImageView )
4473 uint32_t imageCount = 0;
4476 if( imageCount == 0 )
4479 swa.
images.resize( imageCount );
4484 for( uint32_t i = 0; i < imageCount; i++ )
4493 VkSemaphoreCreateInfo semInfo{};
4494 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
4500 if( !
m_vk.CreateSemaphore ||
m_vk.CreateSemaphore(
m_device, &semInfo,
nullptr, &semaphore ) != VK_SUCCESS )
4517 for( uint32_t i = 0; i < imageCount; i++ )
4519 VkFramebufferCreateInfo fbInfo{};
4520 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
4522 fbInfo.attachmentCount = 1;
4524 fbInfo.width = swa.
extent.width;
4525 fbInfo.height = swa.
extent.height;
4528 if(
m_vk.CreateFramebuffer(
m_device, &fbInfo,
nullptr,
4538 VkDescriptorPoolSize presentPoolSize{};
4539 presentPoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4540 presentPoolSize.descriptorCount = 1;
4542 VkDescriptorPoolCreateInfo presentPoolInfo{};
4543 presentPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
4544 presentPoolInfo.maxSets = 1;
4545 presentPoolInfo.poolSizeCount = 1;
4546 presentPoolInfo.pPoolSizes = &presentPoolSize;
4548 if(
m_vk.CreateDescriptorPool(
m_device, &presentPoolInfo,
nullptr,
4554 VkDescriptorSetAllocateInfo dsAlloc{};
4555 dsAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
4557 dsAlloc.descriptorSetCount = 1;
4600 if( !aKeepSwapchain && swa.
swapchain &&
m_vk.DestroySwapchainKHR )
4614 bool waylandSurface =
false;
4615#ifdef VK_USE_PLATFORM_XLIB_KHR
4616 X11_CONNECTION_OWNER ownedX11;
4621 && (
m_vk.CreateXcbSurfaceKHR ||
m_vk.CreateXlibSurfaceKHR ) )
4627 ? XDisplayString( uiDisplay )
4631#ifdef VK_USE_PLATFORM_XCB_KHR
4634 if(
m_vk.CreateXcbSurfaceKHR )
4636 xcb_connection_t* connection = xcb_connect(
displayName, &ownedX11.screen );
4638 if( connection && !xcb_connection_has_error( connection ) )
4640 VkXcbSurfaceCreateInfoKHR xcbInfo{};
4641 xcbInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
4642 xcbInfo.connection = connection;
4643 xcbInfo.window =
static_cast<xcb_window_t
>( window );
4645 VkResult xcbResult =
4646 m_vk.CreateXcbSurfaceKHR(
m_instance, &xcbInfo,
nullptr, &vkSurface );
4648 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateXcbSurface conn=%p "
4649 "window=0x%x result=%d surface=%p" ),
4650 static_cast<void*
>( xcbInfo.connection ),
4651 static_cast<unsigned>( xcbInfo.window ),
4652 static_cast<int>( xcbResult ),
4653 static_cast<void*
>( vkSurface ) );
4657 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateSurface xcb_connect failed" ) );
4661 ownedX11.xcb = connection;
4662 else if( connection )
4663 xcb_disconnect( connection );
4670 Display* ownedDisplay = XOpenDisplay(
displayName );
4671 Display* surfaceDisplay = ownedDisplay ? ownedDisplay : uiDisplay;
4673 VkXlibSurfaceCreateInfoKHR xlibInfo{};
4674 xlibInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
4675 xlibInfo.dpy = surfaceDisplay;
4676 xlibInfo.window = window;
4678 VkResult xlibResult =
4679 m_vk.CreateXlibSurfaceKHR(
m_instance, &xlibInfo,
nullptr, &vkSurface );
4680 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateXlibSurface dpy=%p window=0x%lx "
4681 "result=%d surface=%p" ),
4682 static_cast<void*
>( xlibInfo.dpy ),
4683 static_cast<unsigned long>( xlibInfo.window ),
4684 static_cast<int>( xlibResult ),
4685 static_cast<void*
>( vkSurface ) );
4689 ownedX11.xlib = ownedDisplay;
4690 ownedX11.screen = DefaultScreen( ownedDisplay );
4692 else if( ownedDisplay )
4693 XCloseDisplay( ownedDisplay );
4698#ifdef VK_USE_PLATFORM_WAYLAND_KHR
4702 &&
m_vk.CreateWaylandSurfaceKHR )
4704 VkWaylandSurfaceCreateInfoKHR waylandInfo{};
4705 waylandInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
4709 VkResult waylandResult =
4710 m_vk.CreateWaylandSurfaceKHR(
m_instance, &waylandInfo,
nullptr, &vkSurface );
4712 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateWaylandSurface display=%p surface=%p "
4713 "result=%d vkSurface=%p" ),
4715 static_cast<int>( waylandResult ),
4716 static_cast<void*
>( vkSurface ) );
4720#ifdef VK_USE_PLATFORM_WIN32_KHR
4724 &&
m_vk.CreateWin32SurfaceKHR )
4726 VkWin32SurfaceCreateInfoKHR win32Info{};
4727 win32Info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
4728 win32Info.hinstance = GetModuleHandle(
nullptr );
4731 VkResult win32Result =
4732 m_vk.CreateWin32SurfaceKHR(
m_instance, &win32Info,
nullptr, &vkSurface );
4733 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateWin32Surface hwnd=%p "
4734 "result=%d vkSurface=%p" ),
4736 static_cast<int>( win32Result ),
4737 static_cast<void*
>( vkSurface ) );
4744 uint32_t presentFamily = UINT32_MAX;
4747 auto closeOwnedDisplay = [&]()
4749#ifdef VK_USE_PLATFORM_XLIB_KHR
4756 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateSurface FAILED - no present queue family "
4757 "supports this surface" ) );
4759 if(
m_vk.DestroySurfaceKHR )
4762 closeOwnedDisplay();
4766 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateSurface presentFamily=%u graphicsFamily=%u" ),
4773 if(
m_vk.DestroySurfaceKHR )
4776 closeOwnedDisplay();
4783#ifdef VK_USE_PLATFORM_XLIB_KHR
4784 sa.x11Owner = ownedX11;
4787 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateSurface OK (handle=%u)" ),
4804 if( it->second.surface &&
m_vk.DestroySurfaceKHR )
4805 m_vk.DestroySurfaceKHR(
m_instance, it->second.surface,
nullptr );
4807#ifdef VK_USE_PLATFORM_XLIB_KHR
4808 it->second.x11Owner.Close();
4816static VkExtent2D
swapchainExtent(
const VkSurfaceCapabilitiesKHR& aCaps, uint32_t aWidth, uint32_t aHeight )
4818 if( aCaps.currentExtent.width != UINT32_MAX )
4819 return aCaps.currentExtent;
4821 return { std::max( aCaps.minImageExtent.width, std::min( aCaps.maxImageExtent.width, aWidth ) ),
4822 std::max( aCaps.minImageExtent.height, std::min( aCaps.maxImageExtent.height, aHeight ) ) };
4827 const VkSurfaceCapabilitiesKHR& aCaps,
4828 VkSurfaceFormatKHR aFormat, VkExtent2D aExtent,
4829 VkSwapchainKHR aOld,
4834 uint32_t imageCount = aCaps.minImageCount + 1;
4836 if( aCaps.maxImageCount > 0 && imageCount > aCaps.maxImageCount )
4837 imageCount = aCaps.maxImageCount;
4841 VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
4842 const VkCompositeAlphaFlagBitsKHR alphaPreference[] = {
4843 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
4844 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
4845 VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
4846 VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR
4849 for(
auto alpha : alphaPreference )
4851 if( aCaps.supportedCompositeAlpha & alpha )
4853 compositeAlpha = alpha;
4860 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: compositeAlpha=0x%x" ),
4861 static_cast<unsigned>( compositeAlpha ) );
4864 VkSurfaceTransformFlagBitsKHR preTransform =
4865 ( aCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR )
4866 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
4867 : aCaps.currentTransform;
4869 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: %spreTransform current=0x%x chosen=0x%x "
4871 aResize ?
"ResizeSwapchain " :
"",
4872 static_cast<unsigned>( aCaps.currentTransform ),
4873 static_cast<unsigned>( preTransform ),
4874 static_cast<unsigned>( aCaps.supportedTransforms ) );
4876 VkSwapchainCreateInfoKHR swapInfo{};
4877 swapInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
4878 swapInfo.surface = aSurface.
surface;
4879 swapInfo.minImageCount = imageCount;
4880 swapInfo.imageFormat = aFormat.format;
4881 swapInfo.imageColorSpace = aFormat.colorSpace;
4882 swapInfo.imageExtent = aExtent;
4883 swapInfo.imageArrayLayers = 1;
4884 swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
4885 | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
4886 swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
4887 swapInfo.preTransform = preTransform;
4888 swapInfo.compositeAlpha = compositeAlpha;
4889 swapInfo.presentMode = presentMode;
4890 swapInfo.clipped = VK_TRUE;
4891 swapInfo.oldSwapchain = aOld;
4895 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: swapInfo surface=%p minImages=%u format=%d "
4896 "colorSpace=%d extent=%ux%u layers=%u usage=0x%x "
4897 "sharingMode=%d preTransform=0x%x compositeAlpha=0x%x "
4898 "presentMode=%d clipped=%d oldSwapchain=%p" ),
4899 static_cast<void*
>( swapInfo.surface ),
4900 swapInfo.minImageCount,
4901 static_cast<int>( swapInfo.imageFormat ),
4902 static_cast<int>( swapInfo.imageColorSpace ),
4903 swapInfo.imageExtent.width, swapInfo.imageExtent.height,
4904 swapInfo.imageArrayLayers,
4905 swapInfo.imageUsage,
4906 static_cast<int>( swapInfo.imageSharingMode ),
4907 static_cast<unsigned>( swapInfo.preTransform ),
4908 static_cast<unsigned>( swapInfo.compositeAlpha ),
4909 static_cast<int>( swapInfo.presentMode ),
4910 static_cast<int>( swapInfo.clipped ),
4911 static_cast<void*
>( swapInfo.oldSwapchain ) );
4914 const char* who = aResize ?
"ResizeSwapchain" :
"CreateSwapchain";
4916 auto tryCreate = [&]( VkPresentModeKHR aMode, uint32_t aImageCount,
const char* aLabel ) ->
bool
4918 swapInfo.presentMode = aMode;
4919 swapInfo.minImageCount = aImageCount;
4921 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: %s attempt=%s "
4922 "presentMode=%d minImages=%u" ),
4923 who, aLabel,
static_cast<int>( aMode ), aImageCount );
4927 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: %s result=%s(%d) "
4931 return made.
result == VK_SUCCESS;
4937 if( tryCreate( presentMode, imageCount,
"preferred" )
4938 || ( presentMode != VK_PRESENT_MODE_FIFO_KHR
4939 && tryCreate( VK_PRESENT_MODE_FIFO_KHR, imageCount,
"fifo" ) )
4940 || ( imageCount != aCaps.minImageCount
4941 && tryCreate( presentMode, aCaps.minImageCount,
"min-images" ) )
4942 || ( presentMode != VK_PRESENT_MODE_FIFO_KHR
4943 && imageCount != aCaps.minImageCount
4944 && tryCreate( VK_PRESENT_MODE_FIFO_KHR, aCaps.minImageCount,
"fifo-min-images" ) ) )
4966 VkSurfaceKHR vkSurface = surfIt->second.surface;
4968 if( !
m_vk.GetPhysicalDeviceSurfaceCapabilitiesKHR
4969 || !
m_vk.GetPhysicalDeviceSurfaceFormatsKHR
4970 || !
m_vk.CreateSwapchainKHR )
4975 VkSurfaceCapabilitiesKHR surfCaps{};
4978 &surfCaps ) != VK_SUCCESS )
4980 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: GetSurfaceCapabilities failed" ) );
4984 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: surfCaps extent=%ux%u (min=%ux%u max=%ux%u) "
4985 "requested=%ux%u supportedComposite=0x%x "
4986 "supportedUsage=0x%x minImages=%u maxImages=%u" ),
4987 surfCaps.currentExtent.width, surfCaps.currentExtent.height,
4988 surfCaps.minImageExtent.width, surfCaps.minImageExtent.height,
4989 surfCaps.maxImageExtent.width, surfCaps.maxImageExtent.height,
4991 surfCaps.supportedCompositeAlpha,
4992 surfCaps.supportedUsageFlags,
4993 surfCaps.minImageCount, surfCaps.maxImageCount );
5002 std::vector<VkSurfaceFormatKHR> surfFormats(
formatCount );
5006 VkSurfaceFormatKHR chosenFormat = surfFormats[0];
5008 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: %u surface formats available:" ),
formatCount );
5012 wxLogTrace(
traceRhi, wxS(
" format[%u]: format=%d colorSpace=%d" ),
5013 i,
static_cast<int>( surfFormats[i].format ),
5014 static_cast<int>( surfFormats[i].colorSpace ) );
5018 static const VkFormat formatPrefs[] = {
5019 VK_FORMAT_R8G8B8A8_UNORM,
5020 VK_FORMAT_B8G8R8A8_UNORM,
5023 for( VkFormat preferred : formatPrefs )
5027 for(
const auto& fmt : surfFormats )
5029 if( fmt.format == preferred
5030 && fmt.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR )
5042 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: chosen format=%d colorSpace=%d" ),
5043 static_cast<int>( chosenFormat.format ),
5044 static_cast<int>( chosenFormat.colorSpace ) );
5049 if( extent.width == 0 || extent.height == 0 )
5051 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: CreateSwapchain SKIPPED (zero extent)" ) );
5058 if( made.
result != VK_SUCCESS )
5060 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: vkCreateSwapchainKHR failed (%d)" ),
5061 static_cast<int>( made.
result ) );
5065 VkSwapchainKHR vkSwapchain = made.
swapchain;
5067 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: swapchain created %ux%u format=%d images=%u "
5069 extent.width, extent.height,
static_cast<int>( chosenFormat.format ),
5076 m_vk.DestroySwapchainKHR(
m_device, vkSwapchain,
nullptr );
5083 swa.
format = chosenFormat.format;
5088 && ( extent.width != desc.
width || extent.height != desc.
height );
5102 VkSemaphoreCreateInfo semInfo{};
5103 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
5105 VkFenceCreateInfo fenceInfo{};
5106 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
5107 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
5109 if( !
m_vk.CreateSemaphore || !
m_vk.CreateFence
5126 uint32_t height,
float dpiScale )
5128 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain %ux%u dpi=%.1f" ),
5129 width, height, dpiScale );
5150 VkSurfaceKHR vkSurface = surfIt->second.surface;
5152 VkSurfaceCapabilitiesKHR surfCaps{};
5153 VkResult capsResult =
m_vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(
5156 if( capsResult != VK_SUCCESS )
5158 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain GetSurfaceCaps FAILED (%d)" ),
5159 static_cast<int>( capsResult ) );
5165 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain surfExtent=%ux%u "
5166 "requested=%ux%u minExtent=%ux%u maxExtent=%ux%u" ),
5167 extent.width, extent.height, width, height,
5168 surfCaps.minImageExtent.width, surfCaps.minImageExtent.height,
5169 surfCaps.maxImageExtent.width, surfCaps.maxImageExtent.height );
5171 if( extent.width == 0 || extent.height == 0 )
5173 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain SKIPPED (zero extent)" ) );
5180 VkSwapchainKHR oldSwapchain = swa.
swapchain;
5183 { swa.format, swa.colorSpace }, extent,
5184 oldSwapchain,
true );
5186 if( made.
result != VK_SUCCESS )
5188 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain CreateSwapchain FAILED (%d) "
5190 static_cast<int>( made.
result ), extent.width, extent.height );
5194 m_vk.DestroySwapchainKHR(
m_device, oldSwapchain,
nullptr );
5204 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain %s extent=%ux%u images=%zu" ),
5205 resourcesOk ?
"OK" :
"RESOURCES_FAILED",
5206 extent.width, extent.height, swa.
images.size() );
5215 && ( extent.width != width || extent.height != height );
5232 if( it ==
m_swapchains.end() || !it->second.waitingForSurfaceExtent )
5236 auto now = std::chrono::steady_clock::now();
5243 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
5249 VkSurfaceCapabilitiesKHR caps{};
5250 VkResult capsResult =
m_vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(
5253 if( capsResult != VK_SUCCESS )
5256 if( caps.currentExtent.width != UINT32_MAX
5263 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: surface reached requested extent %ux%u; "
5264 "resizing swapchain" ),
5302 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
5329 const uint64_t timeoutNs =
5330 static_cast<uint64_t
>( std::chrono::duration_cast<std::chrono::nanoseconds>( aTimeout ).count() );
5334 const uint64_t allowed =
static_cast<uint64_t
>( std::max( aAllowed, 1 ) );
5362 return it ==
m_swapchains.end() || it->second.presentMode == VK_PRESENT_MODE_FIFO_KHR
5363 || it->second.presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR;
5371 return it !=
m_swapchains.end() && it->second.presentMode == VK_PRESENT_MODE_MAILBOX_KHR;
5389 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: Present begin attempt=%llu handle=%u "
5390 "extent=%ux%u srcFBO=%s needsResize=%s" ),
5391 static_cast<unsigned long long>( attempt ),
5394 aSrcFBO ?
"valid" :
"null",
5403 if( fbIt !=
m_framebuffers.end() && !fbIt->second.colorImages.empty() )
5404 srcImage = fbIt->second.colorImages[0];
5407 if( !
m_vk.AcquireNextImageKHR || !
m_vk.QueuePresentKHR
5408 || !
m_vk.WaitForFences || !
m_vk.ResetFences )
5419 VkResult waitResult = VK_TIMEOUT;
5421 while( waitResult == VK_TIMEOUT )
5426 if( waitResult == VK_TIMEOUT &&
VkElapsedMs( waitLogTimer ) >= 1000 )
5428 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: WaitForFences still waiting "
5429 "attempt=%llu elapsed=%lldms" ),
5430 static_cast<unsigned long long>( attempt ),
5432 waitLogTimer.
Start();
5438 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: WaitForFences attempt=%llu result=%s(%d) "
5440 static_cast<unsigned long long>( attempt ),
5444 if( waitResult != VK_SUCCESS )
5448 std::unique_lock<std::recursive_mutex> presentLock(
m_deviceMutex );
5464 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AllocateCommandBuffers FAILED "
5466 static_cast<unsigned long long>( attempt ) );
5472 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: Present deferred resize %ux%u" ),
5480 if( !resizeOk || swa.
images.empty() )
5487 uint32_t imageIndex = 0;
5492 acquireTimer.
Stop();
5494 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage attempt=%llu result=%s(%d) "
5495 "image=%u elapsed=%lldms" ),
5496 static_cast<unsigned long long>( attempt ),
5500 if( acquireResult == VK_ERROR_OUT_OF_DATE_KHR )
5502 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage out-of-date, "
5503 "resizing swapchain" ) );
5508 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: ResizeSwapchain after OUT_OF_DATE FAILED" ) );
5513 acquireTimer.
Start();
5517 acquireTimer.
Stop();
5519 if( acquireResult != VK_SUCCESS )
5521 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage retry FAILED "
5522 "attempt=%llu result=%s(%d) image=%u elapsed=%lldms" ),
5523 static_cast<unsigned long long>( attempt ),
5530 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage retry OK "
5531 "attempt=%llu image=%u elapsed=%lldms" ),
5532 static_cast<unsigned long long>( attempt ),
5535 else if( acquireResult == VK_SUBOPTIMAL_KHR )
5537 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage suboptimal "
5538 "attempt=%llu (continuing)" ),
5539 static_cast<unsigned long long>( attempt ) );
5541 else if( acquireResult != VK_SUCCESS )
5543 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: AcquireNextImage FAILED attempt=%llu "
5545 static_cast<unsigned long long>( attempt ),
5549 if( acquireResult == VK_ERROR_DEVICE_LOST )
5552 if( acquireResult == VK_ERROR_SURFACE_LOST_KHR )
5569 bool presentBlitOk =
false;
5577 if( fbIt !=
m_framebuffers.end() && !fbIt->second.colorTextureHandles.empty() )
5578 srcTexHandle = fbIt->second.colorTextureHandles[0];
5588 srcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
5589 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
5590 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
5591 VK_ACCESS_SHADER_READ_BIT );
5593 m_vk.CmdPipelineBarrier( cmd,
5594 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
5595 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
5596 0, 0,
nullptr, 0,
nullptr, 1, &fboBarrier );
5599 VkDescriptorImageInfo imgInfo{};
5600 imgInfo.sampler = texIt->second.sampler;
5601 imgInfo.imageView = texIt->second.imageView;
5602 imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5604 VkWriteDescriptorSet write{};
5605 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5607 write.dstBinding = 0;
5608 write.descriptorCount = 1;
5609 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5610 write.pImageInfo = &imgInfo;
5612 m_vk.UpdateDescriptorSets(
m_device, 1, &write, 0,
nullptr );
5614 VkRenderPassBeginInfo rpBegin{};
5615 rpBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
5618 rpBegin.renderArea.extent = swa.
extent;
5620 m_vk.CmdBeginRenderPass( cmd, &rpBegin, VK_SUBPASS_CONTENTS_INLINE );
5623 vp.width =
static_cast<float>( swa.
extent.width );
5624 vp.height =
static_cast<float>( swa.
extent.height );
5629 if( aSourceRect && aSourceRect->
width > 0 && aSourceRect->
height > 0 )
5631 const float scaleX = vp.width /
static_cast<float>( aSourceRect->
width );
5632 const float scaleY = vp.height /
static_cast<float>( aSourceRect->
height );
5634 vp.x = -
static_cast<float>( aSourceRect->
x ) * scaleX;
5635 vp.y = -
static_cast<float>( aSourceRect->
y ) * scaleY;
5636 vp.width =
static_cast<float>( fbIt->second.width ) * scaleX;
5637 vp.height =
static_cast<float>( fbIt->second.height ) * scaleY;
5640 m_vk.CmdSetViewport( cmd, 0, 1, &vp );
5643 scissor.extent = swa.
extent;
5644 m_vk.CmdSetScissor( cmd, 0, 1, &scissor );
5646 m_vk.CmdBindPipeline( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, blitPipeline );
5648 m_vk.CmdBindDescriptorSets( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
5652 m_vk.CmdDraw( cmd, 3, 1, 0, 0 );
5654 m_vk.CmdEndRenderPass( cmd );
5656 presentBlitOk =
true;
5661 if( !presentBlitOk )
5663 const VkImageMemoryBarrier toDst =
imageBarrier( swa.
images[imageIndex], VK_IMAGE_ASPECT_COLOR_BIT,
5664 VK_IMAGE_LAYOUT_UNDEFINED,
5665 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0,
5666 VK_ACCESS_TRANSFER_WRITE_BIT );
5668 m_vk.CmdPipelineBarrier( cmd,
5669 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
5670 VK_PIPELINE_STAGE_TRANSFER_BIT,
5671 0, 0,
nullptr, 0,
nullptr, 1, &toDst );
5673 VkClearColorValue magenta = { { 1.0f, 0.0f, 1.0f, 1.0f } };
5674 m_vk.CmdClearColorImage( cmd, swa.
images[imageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &magenta, 1,
5675 &toDst.subresourceRange );
5677 const VkImageMemoryBarrier toPresent =
imageBarrier( swa.
images[imageIndex], VK_IMAGE_ASPECT_COLOR_BIT,
5678 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
5679 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
5680 VK_ACCESS_TRANSFER_WRITE_BIT, 0 );
5682 m_vk.CmdPipelineBarrier( cmd,
5683 VK_PIPELINE_STAGE_TRANSFER_BIT,
5684 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
5685 0, 0,
nullptr, 0,
nullptr, 1, &toPresent );
5688 m_vk.EndCommandBuffer( cmd );
5691 VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
5693 VkSubmitInfo submitInfo{};
5694 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
5695 submitInfo.waitSemaphoreCount = 1;
5697 submitInfo.pWaitDstStageMask = &waitStage;
5698 submitInfo.commandBufferCount = 1;
5699 submitInfo.pCommandBuffers = &cmd;
5700 submitInfo.signalSemaphoreCount = 1;
5701 submitInfo.pSignalSemaphores = &renderFinished;
5708 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: QueueSubmit attempt=%llu result=%s(%d) "
5709 "image=%u elapsed=%lldms" ),
5710 static_cast<unsigned long long>( attempt ),
5714 if( submitResult != VK_SUCCESS )
5719 VkFenceCreateInfo fenceInfo{};
5720 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
5721 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
5724 if(
m_vk.CreateFence(
m_device, &fenceInfo,
nullptr, &newFence ) == VK_SUCCESS )
5730 if( submitResult == VK_ERROR_DEVICE_LOST )
5739 VkPresentInfoKHR presentInfo{};
5740 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
5741 presentInfo.waitSemaphoreCount = 1;
5742 presentInfo.pWaitSemaphores = &renderFinished;
5743 presentInfo.swapchainCount = 1;
5744 presentInfo.pSwapchains = &swa.
swapchain;
5745 presentInfo.pImageIndices = &imageIndex;
5747 VkPresentIdKHR presentId{};
5752 presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR;
5753 presentId.swapchainCount = 1;
5754 presentId.pPresentIds = &thisPresent;
5755 presentInfo.pNext = &presentId;
5760 presentTimer.
Stop();
5762 if( presentResult == VK_SUCCESS || presentResult == VK_SUBOPTIMAL_KHR )
5770 presentLock.unlock();
5772 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: QueuePresent attempt=%llu result=%s(%d) "
5773 "image=%u elapsed=%lldms" ),
5774 static_cast<unsigned long long>( attempt ),
5778 if( presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR )
5780 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: QueuePresent %s (will resize next frame)" ),
5781 presentResult == VK_ERROR_OUT_OF_DATE_KHR ?
"out-of-date" :
"suboptimal" );
5786 if( presentResult == VK_ERROR_DEVICE_LOST )
5789 if( presentResult == VK_ERROR_SURFACE_LOST_KHR )
5792 if( presentResult != VK_SUCCESS )
5798 wxLogTrace(
traceRhi, wxS(
"VulkanDevice: Present OK extent=%ux%u srcFBO=%s" ),
5800 aSrcFBO ?
"valid" :
"null" );
5809 uint32_t attachmentIndex,
5810 void* buffer,
size_t bufferSize )
5826 size_t required =
static_cast<size_t>( fba.
width ) * fba.
height * bpp;
5828 if( bufferSize < required )
5831 VkImage srcImage = fba.
colorImages[attachmentIndex];
5833 VkBufferCreateInfo stagingBufInfo{};
5834 stagingBufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5835 stagingBufInfo.size = required;
5836 stagingBufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5838 VmaAllocationCreateInfo stagingAllocInfo{};
5839 stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
5840 stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
5841 | VMA_ALLOCATION_CREATE_MAPPED_BIT;
5845 VmaAllocationInfo stagingInfo{};
5847 if( vmaCreateBuffer(
m_allocator, &stagingBufInfo, &stagingAllocInfo,
5848 &stagingBuffer, &stagingAllocation, &stagingInfo ) != VK_SUCCESS )
5854 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
5860 vmaDestroyBuffer(
m_allocator, stagingBuffer, stagingAllocation );
5868 srcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
5869 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
5870 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
5871 VK_ACCESS_TRANSFER_READ_BIT );
5873 m_vk.CmdPipelineBarrier( cmd,
5874 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
5875 VK_PIPELINE_STAGE_TRANSFER_BIT,
5876 0, 0,
nullptr, 0,
nullptr, 1, &barrier );
5878 VkBufferImageCopy region{};
5879 region.bufferOffset = 0;
5880 region.bufferRowLength = 0;
5881 region.bufferImageHeight = 0;
5882 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
5883 region.imageSubresource.mipLevel = 0;
5884 region.imageSubresource.baseArrayLayer = 0;
5885 region.imageSubresource.layerCount = 1;
5886 region.imageOffset = { 0, 0, 0 };
5887 region.imageExtent = { fba.
width, fba.
height, 1 };
5889 m_vk.CmdCopyImageToBuffer( cmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
5890 stagingBuffer, 1, ®ion );
5892 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
5893 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5894 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
5895 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
5897 m_vk.CmdPipelineBarrier( cmd,
5898 VK_PIPELINE_STAGE_TRANSFER_BIT,
5899 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
5900 0, 0,
nullptr, 0,
nullptr, 1, &barrier );
5904 vmaDestroyBuffer(
m_allocator, stagingBuffer, stagingAllocation );
5908 vmaInvalidateAllocation(
m_allocator, stagingAllocation, 0, VK_WHOLE_SIZE );
5909 std::memcpy( buffer, stagingInfo.pMappedData, required );
5911 vmaDestroyBuffer(
m_allocator, stagingBuffer, stagingAllocation );
5917 uint32_t aSourceX, uint32_t aSourceY )
5919 std::lock_guard<std::recursive_mutex> lock(
m_deviceMutex );
5951 const VkPipelineStageFlags depthStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
5952 | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
5953 const VkAccessFlags depthAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
5954 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
5956 auto barrier = [&]( VkImage aImage, VkImageLayout aOld, VkImageLayout aNew, VkAccessFlags aSrcAccess,
5957 VkAccessFlags aDstAccess )
5959 return imageBarrier( aImage, VK_IMAGE_ASPECT_DEPTH_BIT, aOld, aNew, aSrcAccess, aDstAccess );
5963 VkImageMemoryBarrier before[2] = {
5964 barrier( src.
depthImage, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
5965 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, depthAccess, VK_ACCESS_TRANSFER_READ_BIT ),
5966 barrier( dst.
depthImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, depthAccess,
5967 VK_ACCESS_TRANSFER_WRITE_BIT )
5970 m_vk.CmdPipelineBarrier( cmd, depthStages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0,
nullptr, 2,
5973 VkImageCopy region{};
5974 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
5975 region.srcSubresource.layerCount = 1;
5976 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
5977 region.dstSubresource.layerCount = 1;
5978 region.srcOffset = {
static_cast<int32_t
>( aSourceX ),
static_cast<int32_t
>( aSourceY ), 0 };
5982 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion );
5984 VkImageMemoryBarrier after[2] = {
5985 barrier( src.
depthImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
5986 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, depthAccess ),
5987 barrier( dst.
depthImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
5988 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, depthAccess )
5991 m_vk.CmdPipelineBarrier( cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, depthStages, 0, 0,
nullptr, 0,
nullptr, 2,
5994 m_vk.EndCommandBuffer( cmd );
6008 auto device = std::make_unique<RHI_VULKAN_DEVICE>( enableValidation );
6010 if( !device->IsInitialized() )
static wxString formatCount(int aCount)
A small class to help profiling.
void Stop()
Save the time when this function was called, and set the counter stane to stop.
void Start()
Start or restart the counter.
double msecs(bool aSinceLast=false)
Plain owning command data, recordable on any thread without device access.
const std::vector< COMMAND > & Commands() const
Creates, uses and destroys GPU resources through one graphics API backend.
RHI_DEFERRED_RELEASE_QUEUE m_deferredReleaseQueue
void deferDestroy(RHI_HANDLE_POOL< Tag > &aPool, RHI_DEFERRED_RESOURCE_TYPE aType, RHI_HANDLE< Tag > aHandle)
Only the destroy that wins the pending-destroy claim enqueues, so racing destroys of one handle canno...
Thread-safe pool that allocates and recycles generation-tracked handles for device implementations.
RHI_HANDLE_POOL< RHI_TAG_BIND_GROUP > m_bindGroupPool
std::unordered_map< uint32_t, SurfaceAllocation > m_surfaces
static constexpr size_t STAGING_POOL_BYTES
bool acquirePassCmd(VkCommandBuffer &aCmd)
std::unordered_map< uint32_t, ComputePipelineAllocation > m_computePipelines
std::atomic< std::thread::id > m_uploadBatchOwner
std::unordered_map< uint32_t, ShaderAllocation > m_shaders
void ExecuteDeferredRelease(const RHI_DEFERRED_RELEASE_ENTRY &aEntry)
double m_totalPipelineCreateMs
~RHI_VULKAN_DEVICE() override
uint32_t m_maxTextureSize
VkResult buildPipeline(VkShaderModule aVertex, VkShaderModule aFragment, const VkPipelineVertexInputStateCreateInfo &aVertexInput, VkPrimitiveTopology aTopology, const std::vector< VkPipelineColorBlendAttachmentState > &aBlend, const VkPipelineDepthStencilStateCreateInfo &aDepthStencil, const std::vector< VkDynamicState > &aDynamicStates, VkPipelineLayout aLayout, VkRenderPass aRenderPass, VkPipeline &aPipeline)
void SetScissor(const RHI_SCISSOR_RECT &scissor) override
Only valid inside a render pass.
static VkFilter TranslateFilter(RHI_FILTER filter)
RHI_FramebufferHandle CreateFramebuffer(const RHI_FRAMEBUFFER_DESC &desc) override
Returns a null handle on failure.
VkDebugUtilsMessengerEXT m_debugMessenger
void UploadTexture(RHI_TextureHandle handle, const void *data, size_t sizeBytes) override
Data is tightly packed rows in the texture's format, exactly width * height * bpp bytes.
void * LoadInstanceFunction(const char *name)
static VkFormat TranslateTextureFormat(RHI_TEXTURE_FORMAT format)
RHI_HANDLE_POOL< RHI_TAG_PIPELINE > m_pipelinePool
void DestroyBindGroupLayout(RHI_BindGroupLayoutHandle handle) override
VkPipelineCache m_pipelineCache
bool CreateLogicalDevice()
VkDescriptorSetLayout createSetLayout(const RHI_BIND_GROUP_LAYOUT_DESC &aDesc)
RHI_SURFACE_EXTENT_POLL_RESULT PollSurfaceExtent(RHI_SwapchainHandle aSwapchain) override
Retry a fixed surface extent that lagged behind the requested window size.
void TransitionTexture(VkCommandBuffer cmd, TextureAllocation &tex, VkImageLayout newLayout, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage)
void ReadDeviceProperties()
RHI_COMPUTE_RESULT ExecuteCompute(const RHI_COMPUTE_JOB &aJob) override
Submit, wait for completion, and copy all readbacks before returning.
bool IsInitialized() const override
bool PresentWaitsForDisplay(RHI_SwapchainHandle aSwapchain) const override
False when presents show at once instead of at the next vertical blank, so frames drawn faster than t...
void BindPipeline(RHI_PipelineHandle pipeline) override
Only valid inside a render pass.
void DestroyShader(RHI_ShaderHandle handle) override
void DestroyBuffer(RHI_BufferHandle handle) override
The handle's generation is retired, so later use of the stale handle is detected.
void SetPushConstants(const void *data, size_t sizeBytes) override
Size must not exceed RHI_MAX_PUSH_CONSTANT_BYTES.
RHI_HANDLE_POOL< RHI_TAG_SWAPCHAIN > m_swapchainPool
void DestroyRenderPass(RHI_RenderPassHandle handle) override
VkPipelineLayout m_presentBlitLayout
RHI_HANDLE_POOL< RHI_TAG_BUFFER > m_bufferPool
void DrawInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex=0, int32_t vertexOffset=0, uint32_t firstInstance=0) override
Non-indexed, so vertexOffset is ignored.
int AcquireStaging(size_t aSizeBytes)
RHI_BindGroupHandle CreateBindGroup(const RHI_BIND_GROUP_DESC &desc) override
Returns a null handle on failure.
RHI_PipelineHandle CreatePipeline(const RHI_PIPELINE_DESC &desc) override
Returns a null handle on failure.
VkResult submitCommands(VkCommandBuffer aCmd)
void SetViewport(const RHI_VIEWPORT &viewport) override
Only valid inside a render pass.
void DrawIndexedInstanced(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex=0, int32_t vertexOffset=0, uint32_t firstInstance=0) override
Reads 16-bit indices from the bound index buffer.
bool EndRenderPass() override
Returns false if no render pass is active.
RHI_SurfaceHandle CreateSurface(const RHI_SURFACE_DESC &desc) override
Returns a null handle if the native window handle is invalid.
VkCommandBuffer EnsureUploadCmdBuf()
RHI_HANDLE_POOL< RHI_TAG_COMPUTE_PIPELINE > m_computePipelinePool
VkPipeline EnsurePresentBlit(VkFormat aSwapchainFormat)
RHI_PipelineHandle m_activePipeline
bool ReadbackFramebufferPixels(RHI_FramebufferHandle handle, uint32_t attachmentIndex, void *buffer, size_t bufferSize) override
buffer must hold at least width * height * bpp bytes.
void endStagedUpload(VkCommandBuffer aCmd, std::unique_lock< std::recursive_mutex > &aLock, int aStaging)
RHI_VULKAN_DEVICE(bool enableValidation)
std::unordered_map< uint32_t, PipelineAllocation > m_pipelines
RHI_BACKEND GetBackend() const override
bool allocateCommandBuffer(VkCommandBuffer &aCmd)
VkRenderPass makeRenderPass(const std::vector< VK_ATTACHMENT > &aColor, const std::optional< VK_ATTACHMENT > &aDepth)
RHI_HANDLE_POOL< RHI_TAG_SHADER > m_shaderPool
std::vector< size_t > m_batchPooledStaging
void BeginUploadBatch() override
UpdateBuffer() and UploadTexture() calls between these share one submission instead of syncing with t...
void ReleaseDescriptorSet(VkDescriptorPool pool, VkDescriptorSet dset=VK_NULL_HANDLE)
VkCommandBuffer m_currentCmd
RHI_HANDLE_POOL< RHI_TAG_TEXTURE > m_texturePool
void ExecuteDeferredReleases() override
void MarkBufferInUse(BufferAllocation &aBuffer)
void DestroySurface(RHI_SurfaceHandle handle) override
Destroy the surface's swapchain first.
RHI_ShaderHandle CreateShader(const RHI_SHADER_DESC &desc) override
Returns a null handle on failure.
RHI_TextureHandle GetFramebufferColorTexture(RHI_FramebufferHandle handle, uint32_t attachmentIndex=0) const override
The texture lets a later pass sample the framebuffer.
void DestroyPresentBlit()
bool SubmitOneShot(VkCommandBuffer cmd)
bool endUploadCommands(VkCommandBuffer aCmd, const std::unique_lock< std::recursive_mutex > &aLock)
uint32_t m_graphicsQueueFamily
RHI_FRAME_OP_RESULT Present(RHI_SwapchainHandle handle, RHI_FramebufferHandle aSrcFBO={}, const RHI_SCISSOR_RECT *aSourceRect=nullptr) override
When aSrcFBO is valid its first color attachment is copied to the swapchain first.
void BindGroup(uint32_t groupIndex, RHI_BindGroupHandle group) override
Only valid inside a render pass with a bound pipeline.
VkCommandPool m_commandPool
bool RealizeBuffer(RHI_BufferHandle handle, const RHI_BUFFER_DESC &desc) override
Create the GPU buffer for a reserved handle.
RHI_TextureHandle ReserveTextureHandle() override
Thread-safe handle reservation, realized later by RealizeTexture().
void destroyTexture(TextureAllocation &aTexture)
VkCommandBuffer beginUploadCommands(std::unique_lock< std::recursive_mutex > &aLock)
uint32_t m_computeSubgroupSize
int stageData(const void *aData, size_t aSizeBytes, VkBuffer &aBuffer)
std::vector< VkCommandBuffer > m_freeCmds
RHI_RenderPassHandle CreateRenderPass(const RHI_RENDER_PASS_DESC &desc) override
Returns a null handle on failure.
VkSampler createSampler(VkFilter aFilter)
void DestroyComputePipeline(RHI_ComputePipelineHandle aHandle) override
void DestroyStagingPool()
RHI_SwapchainHandle CreateSwapchain(const RHI_SWAPCHAIN_DESC &desc) override
Returns a null handle on failure.
bool CreateSwapchainResources(SwapchainAllocation &swa)
std::vector< DescriptorPoolEntry > m_descriptorPools
uint32_t MaxTextureSize() const override
Largest texture width or height in texels.
bool FindPresentQueueFamily(VkSurfaceKHR vkSurface, uint32_t &outFamily)
bool PresentReplacesQueued(RHI_SwapchainHandle aSwapchain) const override
True when a present replaces one still queued for the next vertical blank, as in Vulkan MAILBOX mode,...
bool CopyFramebufferDepth(RHI_FramebufferHandle aSource, RHI_FramebufferHandle aDestination, uint32_t aSourceX=0, uint32_t aSourceY=0) override
Fill all of aDestination with source depth from (aSourceX, aSourceY) on, ordered after passes already...
void SyncSubmittedPasses()
void destroySwapchainObjects(SwapchainAllocation &aSwa)
std::unordered_map< uint32_t, BindGroupAllocation > m_bindGroups
std::unordered_map< uint32_t, SwapchainAllocation > m_swapchains
std::unordered_map< uint32_t, TextureAllocation > m_textures
RHI_HANDLE_POOL< RHI_TAG_RENDER_PASS > m_renderPassPool
std::unordered_map< uint32_t, FramebufferAllocation > m_framebuffers
void DestroyPipeline(RHI_PipelineHandle handle) override
bool RealizeTexture(RHI_TextureHandle handle, const RHI_TEXTURE_DESC &desc) override
Create the GPU texture for a reserved handle.
std::unordered_map< uint32_t, BindGroupLayoutAllocation > m_bindGroupLayouts
std::unordered_map< VkFormat, VkPipeline > m_presentBlitPipelines
bool CreateVkInstance(bool enableValidation)
void DestroyFramebuffer(RHI_FramebufferHandle handle) override
bool BeginRenderPass(const RHI_RENDER_PASS_BEGIN_DESC &desc) override
Draws and dynamic state changes must come between this and EndRenderPass().
void DestroyBindGroup(RHI_BindGroupHandle handle) override
RHI_COMPUTE_DEVICE_LIMITS ComputeDeviceLimits() const override
VkPhysicalDeviceLimits m_deviceLimits
bool SelectPhysicalDevice()
VkCommandBuffer m_uploadCmdBuf
void Draw(uint32_t vertexCount, uint32_t firstVertex=0) override
Draws are only valid inside a render pass with a bound pipeline.
RHI_COMPUTE_PIPELINE_LIMITS ComputePipelineLimits(RHI_ComputePipelineHandle aHandle) const override
VkResult beginOneTimeCommands(VkCommandBuffer aCmd)
void DestroyTexture(RHI_TextureHandle handle) override
Render thread only.
void * LoadVulkanFunction(const char *name)
wxDynamicLibrary m_vulkanLib
void EndUploadBatch() override
RHI_HANDLE_POOL< RHI_TAG_FRAMEBUFFER > m_framebufferPool
bool SupportsCompute() const override
Compute defaults keep backends without compute support usable for graphics.
RHI_ComputePipelineHandle CreateComputePipeline(const RHI_COMPUTE_PIPELINE_DESC &aDesc) override
VkResult submitPassCmd(VkCommandBuffer aCmd)
bool WaitForPresentSlot(RHI_SwapchainHandle aSwapchain, std::chrono::milliseconds aTimeout, int aAllowed) override
Wait until fewer than aAllowed presents are queued for the display or the timeout passes,...
std::unordered_map< uint32_t, RenderPassAllocation > m_renderPasses
VkDescriptorSetLayout m_presentBlitDSLayout
void LoadDeviceFunctions()
RHI_HANDLE_POOL< RHI_TAG_SURFACE > m_surfacePool
void UpdateBuffer(RHI_BufferHandle handle, const void *data, size_t sizeBytes, size_t offset=0) override
The buffer need not be mappable.
RHI_BufferHandle ReserveBufferHandle() override
Reserve a handle without creating the GPU buffer, so the UI thread can hand out handles while Realize...
std::unordered_map< uint32_t, BufferAllocation > m_buffers
void DestroySwapchain(RHI_SwapchainHandle handle) override
VkDescriptorPool AcquireDescriptorPool()
bool createImage2D(VkFormat aFormat, uint32_t aWidth, uint32_t aHeight, VkImageUsageFlags aUsage, bool aDedicated, VkImage &aImage, VmaAllocation &aAllocation)
VkPresentModeKHR ChoosePresentMode(const SurfaceAllocation &aSurface)
void BindVertexBuffer(uint32_t slot, RHI_BufferHandle buffer) override
Only valid inside a render pass.
VkPhysicalDevice m_physicalDevice
RHI_BindGroupLayoutHandle CreateBindGroupLayout(const RHI_BIND_GROUP_LAYOUT_DESC &desc) override
Returns a null handle on failure.
std::recursive_mutex m_deviceMutex
bool ResizeSwapchain(RHI_SwapchainHandle handle, uint32_t width, uint32_t height, float dpiScale) override
Call when the window size or DPI scale changes.
void BindIndexBuffer(RHI_BufferHandle buffer) override
Indices are 16 bits on every backend.
void ReleaseStaging(int aIndex)
VkImageView createView(VkImage aImage, VkFormat aFormat, VkImageAspectFlags aAspect)
void DestroySwapchainResources(SwapchainAllocation &swa, bool aKeepSwapchain=false)
std::vector< VkCommandBuffer > m_submittedCmds
void destroyPipeline(PipelineAllocation &aPipeline)
void destroyFramebufferObjects(FramebufferAllocation &aFb)
std::vector< StagingBuffer > m_stagingPool
VkSwapchainTry createVkSwapchain(const SurfaceAllocation &aSurface, const VkSurfaceCapabilitiesKHR &aCaps, VkSurfaceFormatKHR aFormat, VkExtent2D aExtent, VkSwapchainKHR aOld, bool aResize)
static constexpr size_t STAGING_MIN_BYTES
RHI_HANDLE_POOL< RHI_TAG_BIND_GROUP_LAYOUT > m_bindGroupLayoutPool
const char * GetName() const override
Backend and GPU name for display, such as "Vulkan (GeForce RTX 3080)".
void * LoadDeviceFunction(const char *name)
static VkBufferUsageFlags TranslateBufferUsage(RHI_BUFFER_TYPE type)
bool m_storageDescriptors
bool CurrentThreadOwnsUploadBatch() const
const StructColors * colorRefs()
Global list of legacy color names, still used all over the place for constructing COLOR4D's.
static wxString displayName(const DATABASE_LIB_TABLE &aTable)
const wxChar *const traceRhi
Flag to enable debug output of the RHI rendering backend.
static const std::vector< std::string > extensions
RHI_SHADER_STAGE
Bit flags for the shader stages that see a binding.
@ STORAGE_BUFFER_READONLY
@ COMBINED_TEXTURE_SAMPLER
constexpr int RHI_MAX_BIND_GROUPS
Most bind groups one pipeline can use.
constexpr bool RhiRangeWithinBuffer(size_t aOffset, size_t aSize, size_t aTotal)
Return true when [aOffset, aOffset + aSize) lies within aTotal bytes.
constexpr uint32_t RHI_MAX_PUSH_CONSTANT_BYTES
Largest push constant size SetPushConstants() accepts, in bytes.
bool RhiValidateComputeJob(const RHI_COMPUTE_JOB &aJob, const RHI_COMPUTE_DEVICE_LIMITS &aLimits, const RHI_COMPUTE_JOB_LOOKUP &aLookup)
Check a whole job against live resources before any of it executes, so a bad job has no partial effec...
bool RhiValidateComputePipelineDesc(const RHI_COMPUTE_PIPELINE_DESC &aDesc)
Validate the portable contract without accessing a GPU or inspecting shader bytecode.
constexpr uint32_t RHI_SPEC_ID_WORKGROUP_Y
constexpr uint32_t RHI_SPEC_ID_WORKGROUP_X
Specialization IDs reserved for the workgroup dimensions; Z is always one.
RHI_SURFACE_EXTENT_POLL_RESULT
RHI_FRAME_OP_RESULT
Windows headers define ERROR as a macro.
RHI_HANDLE< RHI_TAG_FRAMEBUFFER > RHI_FramebufferHandle
RHI_HANDLE< RHI_TAG_TEXTURE > RHI_TextureHandle
RHI_HANDLE< RHI_TAG_BUFFER > RHI_BufferHandle
RHI_HANDLE< RHI_TAG_PIPELINE > RHI_PipelineHandle
RHI_HANDLE< RHI_TAG_BIND_GROUP > RHI_BindGroupHandle
RHI_HANDLE< RHI_TAG_COMPUTE_PIPELINE > RHI_ComputePipelineHandle
RHI_HANDLE< RHI_TAG_BIND_GROUP_LAYOUT > RHI_BindGroupLayoutHandle
uint32_t RhiHandleIndex(RHI_HANDLE< Tag > h)
RHI_HANDLE< RHI_TAG_SURFACE > RHI_SurfaceHandle
RHI_HANDLE< RHI_TAG_SHADER > RHI_ShaderHandle
RHI_HANDLE< RHI_TAG_RENDER_PASS > RHI_RenderPassHandle
RHI_HANDLE< RHI_TAG_SWAPCHAIN > RHI_SwapchainHandle
constexpr uint32_t RHI_HANDLE_INDEX_MASK
bool RhiPipelineUsesDepthStencil(const RHI_PIPELINE_DESC &aDesc)
Return true when the pipeline targets framebuffers with a depth/stencil attachment.
RHI_PRESENT_MODE
How a swapchain hands finished images to the display.
@ MAILBOX
Replaces the queued image at each blank, never tears, no wait.
@ FIFO
Waits for a vertical blank, never tears, adds up to a refresh of latency.
@ IMMEDIATE
Shows the image at once, tears unless a compositor owns the final present.
RHI_PRESENT_MODE RhiChoosePresentMode(const std::vector< RHI_PRESENT_MODE > &aOffered, bool aComposited, bool aImmediateUnderCompositor=false)
Choose the present mode for a swapchain from the modes the surface offers.
uint32_t RhiTextureBytesPerPixel(RHI_TEXTURE_FORMAT format)
RHI_BACKEND
Identifies which graphics backend a device implements.
static VkBlendOp TranslateBlendOp(RHI_BLEND_OP op)
static VkAttachmentLoadOp TranslateLoadOp(RHI_LOAD_OP op)
static VkAttachmentStoreOp TranslateStoreOp(RHI_STORE_OP op)
static VkFormat RhiDefaultDepthFormat()
static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT aSeverity, VkDebugUtilsMessageTypeFlagsEXT aType, const VkDebugUtilsMessengerCallbackDataEXT *aCallbackData, void *aUserData)
static VkDynamicState TranslateDynamicState(RHI_DYNAMIC_STATE state)
static VkFormat TranslateVertexFormat(RHI_VERTEX_FORMAT format)
static VkShaderStageFlags TranslateShaderStage(RHI_SHADER_STAGE stages)
static long long VkElapsedMs(PROF_TIMER &aTimer)
static VkDescriptorType TranslateBindingType(RHI_BINDING_TYPE type)
static VkCompareOp TranslateCompareOp(RHI_COMPARE_OP op)
static VkExtent2D swapchainExtent(const VkSurfaceCapabilitiesKHR &aCaps, uint32_t aWidth, uint32_t aHeight)
static VkPrimitiveTopology TranslateTopology(RHI_PRIMITIVE_TOPOLOGY topology)
std::unique_ptr< RHI_DEVICE > RhiCreateVulkanDevice(bool enableValidation)
Create the Vulkan device, loading libvulkan at run time.
static VkAccessFlags LayoutToAccessMask(VkImageLayout layout)
static VK_ATTACHMENT presentAttachment(VkFormat aFormat)
static const char * VkResultToString(VkResult aResult)
static VkImageMemoryBarrier imageBarrier(VkImage aImage, VkImageAspectFlags aAspect, VkImageLayout aOld, VkImageLayout aNew, VkAccessFlags aSrcAccess, VkAccessFlags aDstAccess)
static VkBlendFactor TranslateBlendFactor(RHI_BLEND_FACTOR factor)
std::vector< RHI_BIND_GROUP_RESOURCE > entries
RHI_BindGroupLayoutHandle layout
std::vector< RHI_BINDING_ENTRY > entries
uint64_t minStorageBufferOffsetAlignment
uint64_t maxSharedMemoryBytes
uint32_t maxWorkgroupInvocations
uint64_t maxStorageBufferRange
std::array< uint32_t, 3 > maxWorkgroupSize
std::array< uint32_t, 3 > maxDispatchGroups
Backend command representation; payload bytes are owned by the job.
Backend lookups for RhiValidateComputeJob.
std::function< const RHI_BIND_GROUP_LAYOUT_DESC *(RHI_BindGroupHandle)> groupLayout
std::function< bool(RHI_BufferHandle, size_t aOffset, size_t aBytes)> storageRange
std::function< const RHI_COMPUTE_PIPELINE_DESC *(RHI_ComputePipelineHandle)> pipeline
std::vector< RHI_SPECIALIZATION_CONSTANT > constants
uint32_t pushConstantBytes
std::vector< RHI_BIND_GROUP_LAYOUT_DESC > bindGroupLayouts
RHI_DEFERRED_RESOURCE_TYPE type
uint32_t handleValue
generation and index
RHI_LOAD_OP stencilLoadOp
RHI_COMPARE_OP depthCompareOp
std::vector< RHI_TEXTURE_FORMAT > colorFormats
RHI_DISPLAY_TYPE displayType
Everything here is fixed at creation except the states listed in dynamicStates.
std::vector< RHI_BIND_GROUP_LAYOUT_DESC > bindGroupLayouts
Element n is bind group n, as passed to RHI_DEVICE::BindGroup().
std::vector< RHI_DYNAMIC_STATE > dynamicStates
RHI_TEXTURE_FORMAT colorFormat
RHI_PRIMITIVE_TOPOLOGY topology
std::vector< RHI_VERTEX_BUFFER_LAYOUT_DESC > vertexBuffers
RHI_ShaderHandle vertexShader
RHI_ShaderHandle fragmentShader
std::vector< RHI_BLEND_STATE > blendStates
One per color attachment.
RHI_DEPTH_STENCIL_STATE depthStencil
RHI_FramebufferHandle framebuffer
bool hasClearColorOverride
Replaces the render pass clear color of every CLEAR color attachment, so the background can change pe...
RHI_RenderPassHandle renderPass
float clearColorOverride[4]
std::vector< RHI_COLOR_ATTACHMENT_DESC > colorAttachments
RHI_DEPTH_STENCIL_ATTACHMENT_DESC depthStencilAttachment
Same pixel space as RHI_VIEWPORT.
The bytecode is SPIR-V on Vulkan and MSL source text on Metal.
Descriptor for creating a surface from a native window.
RHI_NATIVE_WINDOW_HANDLE window
Descriptor for creating a swapchain.
RHI_SurfaceHandle surface
RHI_TEXTURE_FORMAT format
Top-left origin, in device pixels.
std::vector< uint32_t > bufferIndices
RHI_BindGroupLayoutHandle layout
VkDescriptorSet descriptorSet
RHI_BIND_GROUP_LAYOUT_DESC desc
VkDescriptorSetLayout layout
PipelineAllocation native
RHI_COMPUTE_PIPELINE_DESC desc
std::vector< VkImage > colorImages
VkRenderPass internalRenderPass
std::vector< VkImageView > colorViews
std::vector< RHI_TEXTURE_FORMAT > colorFormats
VkFramebuffer framebuffer
std::vector< RHI_TextureHandle > colorTextureHandles
VmaAllocation depthAllocation
std::vector< VmaAllocation > colorAllocations
std::vector< VkDescriptorSetLayout > dsLayouts
std::vector< RHI_COLOR_ATTACHMENT_DESC > colorDescs
RHI_DEPTH_STENCIL_ATTACHMENT_DESC depthStencilDesc
uint32_t currentImageIndex
VkExtent2D requestedExtent
VkDescriptorSet presentDescSet
VkSemaphore imageAvailable
RHI_SurfaceHandle surface
VkExtent2D lastLoggedExtent
VkCommandBuffer lastPresentCmd
bool waitingForSurfaceExtent
std::vector< VkImageView > imageViews
std::vector< VkSemaphore > renderFinished
std::vector< VkImage > images
VkPresentModeKHR presentMode
VkColorSpaceKHR colorSpace
std::vector< VkFramebuffer > presentFramebuffers
VkRenderPass presentRenderPass
VkDescriptorPool presentDescPool
std::chrono::steady_clock::time_point nextSurfaceExtentCheck
uint64_t presentAttemptCount
VkImageLayout currentLayout
RHI_TEXTURE_FORMAT format
VkPresentModeKHR preferredMode
VkPresentModeKHR presentMode
VkImageLayout initialLayout
VkAttachmentLoadOp loadOp
VkAttachmentStoreOp storeOp
VkImageLayout finalLayout
PFN_vkCreateFence CreateFence
PFN_vkCmdClearColorImage CmdClearColorImage
PFN_vkDestroyDevice DestroyDevice
PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties
PFN_vkDestroySemaphore DestroySemaphore
PFN_vkCmdCopyImage CmdCopyImage
PFN_vkCreateDescriptorPool CreateDescriptorPool
PFN_vkQueuePresentKHR QueuePresentKHR
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR
PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout
PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties
PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR
PFN_vkCmdCopyBufferToImage CmdCopyBufferToImage
PFN_vkCmdPipelineBarrier CmdPipelineBarrier
PFN_vkQueueWaitIdle QueueWaitIdle
PFN_vkCmdSetScissor CmdSetScissor
PFN_vkCreateCommandPool CreateCommandPool
PFN_vkAcquireNextImageKHR AcquireNextImageKHR
PFN_vkGetDeviceProcAddr GetDeviceProcAddr
PFN_vkEnumerateInstanceLayerProperties EnumerateInstanceLayerProperties
PFN_vkResetFences ResetFences
PFN_vkCmdBindPipeline CmdBindPipeline
PFN_vkDestroySwapchainKHR DestroySwapchainKHR
PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout
PFN_vkCreateSemaphore CreateSemaphore
PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices
PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties
PFN_vkCreateSwapchainKHR CreateSwapchainKHR
PFN_vkCmdDispatch CmdDispatch
PFN_vkCmdEndRenderPass CmdEndRenderPass
PFN_vkBeginCommandBuffer BeginCommandBuffer
PFN_vkQueueSubmit QueueSubmit
PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers
PFN_vkCmdBindIndexBuffer CmdBindIndexBuffer
PFN_vkEndCommandBuffer EndCommandBuffer
PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2
PFN_vkGetInstanceProcAddr GetInstanceProcAddr
PFN_vkCreatePipelineCache CreatePipelineCache
PFN_vkCmdSetViewport CmdSetViewport
PFN_vkCreateInstance CreateInstance
PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2
PFN_vkDestroyCommandPool DestroyCommandPool
PFN_vkCreateDevice CreateDevice
PFN_vkCreateImageView CreateImageView
PFN_vkCreateShaderModule CreateShaderModule
PFN_vkAllocateCommandBuffers AllocateCommandBuffers
PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT
PFN_vkDestroySurfaceKHR DestroySurfaceKHR
PFN_vkFreeDescriptorSets FreeDescriptorSets
PFN_vkCmdCopyImageToBuffer CmdCopyImageToBuffer
PFN_vkDestroyRenderPass DestroyRenderPass
PFN_vkDestroyFence DestroyFence
PFN_vkCreateFramebuffer CreateFramebuffer
PFN_vkCreateRenderPass CreateRenderPass
PFN_vkGetDeviceQueue GetDeviceQueue
PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties
PFN_vkCmdPushConstants CmdPushConstants
PFN_vkWaitForPresentKHR WaitForPresentKHR
PFN_vkCmdDrawIndexed CmdDrawIndexed
PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines
PFN_vkDestroyImageView DestroyImageView
PFN_vkUpdateDescriptorSets UpdateDescriptorSets
PFN_vkDestroyShaderModule DestroyShaderModule
PFN_vkDestroyPipelineLayout DestroyPipelineLayout
PFN_vkDestroyPipeline DestroyPipeline
PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets
PFN_vkCreateComputePipelines CreateComputePipelines
PFN_vkCreatePipelineLayout CreatePipelineLayout
PFN_vkCreateSampler CreateSampler
PFN_vkAllocateDescriptorSets AllocateDescriptorSets
PFN_vkFreeCommandBuffers FreeCommandBuffers
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR
PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT
PFN_vkWaitForFences WaitForFences
PFN_vkDestroyFramebuffer DestroyFramebuffer
PFN_vkDeviceWaitIdle DeviceWaitIdle
PFN_vkDestroySampler DestroySampler
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR
PFN_vkDestroyInstance DestroyInstance
PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR
PFN_vkDestroyPipelineCache DestroyPipelineCache
PFN_vkCmdCopyBuffer CmdCopyBuffer
PFN_vkCmdBeginRenderPass CmdBeginRenderPass
PFN_vkDestroyDescriptorPool DestroyDescriptorPool
VECTOR3I expected(15, 30, 45)
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.