From 9993ad2538fcf665018d37b83766a3744ee76799 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 16:24:26 -0700 Subject: [PATCH 1/7] vk: Add `VK_EXT_provoking_vertex` optimization The dreamcast uses the last vertex as the provoking vertex, while vulkan uses the first vertex. This requires an additional call to `setFirstProvokingVertex` to reorder the vertices for all incoming geometry. With `VK_EXT_provoking_vertex`, the pipeline can designate that the provoking vertex is to be the last vertex, which removes the need to re-order incoming geometry on the CPU. --- core/rend/vulkan/drawer.cpp | 10 ++- core/rend/vulkan/oit/oit_pipeline.cpp | 11 ++++ core/rend/vulkan/pipeline.cpp | 11 ++++ core/rend/vulkan/vulkan_context.cpp | 88 ++++++++++++++++++++++----- core/rend/vulkan/vulkan_context.h | 2 + 5 files changed, 105 insertions(+), 17 deletions(-) diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp index ce6080851..e67f32c1c 100644 --- a/core/rend/vulkan/drawer.cpp +++ b/core/rend/vulkan/drawer.cpp @@ -404,7 +404,15 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture) static const float scopeColor[4] = { 0.75f, 0.75f, 0.75f, 1.0f }; CommandBufferDebugScope _(cmdBuffer, "Draw", scopeColor); - setFirstProvokingVertex(pvrrc); + if (VulkanContext::Instance()->hasProvokingVertex()) + { + // Pipelines are using VK_EXT_provoking_vertex, no need to + // re-order vertices + } + else + { + setFirstProvokingVertex(pvrrc); + } // Upload vertex and index buffers VertexShaderUniforms vtxUniforms; diff --git a/core/rend/vulkan/oit/oit_pipeline.cpp b/core/rend/vulkan/oit/oit_pipeline.cpp index cb0ecd2cc..fb302db45 100644 --- a/core/rend/vulkan/oit/oit_pipeline.cpp +++ b/core/rend/vulkan/oit/oit_pipeline.cpp @@ -49,6 +49,17 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp index 37bf1f198..edfeab3f2 100644 --- a/core/rend/vulkan/pipeline.cpp +++ b/core/rend/vulkan/pipeline.cpp @@ -295,6 +295,17 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index 8439a2d37..606231b45 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -290,11 +290,6 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co optimalTilingSupported4444 = true; else NOTICE_LOG(RENDERER, "eR4G4B4A4UnormPack16 not supported for optimal tiling"); - const auto features = physicalDevice.getFeatures(); - fragmentStoresAndAtomics = !!features.fragmentStoresAndAtomics; - samplerAnisotropy = !!features.samplerAnisotropy; - if (!fragmentStoresAndAtomics) - NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting"); ShaderCompiler::Init(); @@ -358,6 +353,8 @@ bool VulkanContext::InitDevice() return false; try { + const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties(); + std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); #ifdef VK_DEBUG std::for_each(queueFamilyProperties.begin(), queueFamilyProperties.end(), @@ -434,11 +431,6 @@ bool VulkanContext::InitDevice() // Required swapchain extension tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - // Enable VK_KHR_dedicated_allocation if available - const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); - dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); - dedicatedAllocationSupported &= getMemReq2Supported; - #ifdef VK_ENABLE_BETA_EXTENSIONS tryAddDeviceExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); #endif @@ -449,16 +441,80 @@ bool VulkanContext::InitDevice() tryAddDeviceExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); #endif + // Enable VK_KHR_dedicated_allocation if available + if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1) + { + // Core in Vulkan 1.1 + dedicatedAllocationSupported = true; + } + else + { + const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + if (getMemReq2Supported) + { + dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); + } + } + + // Check for VK_KHR_get_physical_device_properties2 + // Core as of Vulkan 1.1 + const bool getPhysicalDeviceProperties2Supported = + (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1) + ? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + + if (getPhysicalDeviceProperties2Supported) + { + // Enable VK_EXT_provoking_vertex if available + provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + } + + // Get device features + + vk::PhysicalDeviceFeatures2 featuresChain{}; + vk::PhysicalDeviceFeatures& features = featuresChain.features; + + vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{}; + if (provokingVertexSupported) + { + featuresChain.pNext = &provokingVertexFeatures; + } + + // Get the physical device's features + if (getPhysicalDeviceProperties2Supported && featuresChain.pNext) + { + physicalDevice.getFeatures2(&featuresChain); + } + else + { + physicalDevice.getFeatures(&features); + } + + if (provokingVertexSupported) + { + provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast; + } + + samplerAnisotropy = features.samplerAnisotropy; + fragmentStoresAndAtomics = features.fragmentStoresAndAtomics; + if (!fragmentStoresAndAtomics) + NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting"); + // create a UniqueDevice float queuePriority = 1.0f; vk::DeviceQueueCreateInfo deviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), graphicsQueueIndex, 1, &queuePriority); - vk::PhysicalDeviceFeatures features; - if (fragmentStoresAndAtomics) - features.fragmentStoresAndAtomics = true; - if (samplerAnisotropy) - features.samplerAnisotropy = true; - device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, + + if (getPhysicalDeviceProperties2Supported) + { + vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, + nullptr, enabledExtensions); + deviceCreateInfo.pNext = &featuresChain; + device = physicalDevice.createDeviceUnique(deviceCreateInfo); + } + else + { + device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, nullptr, enabledExtensions, &features)); + } #if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 VULKAN_HPP_DEFAULT_DISPATCHER.init(*device); diff --git a/core/rend/vulkan/vulkan_context.h b/core/rend/vulkan/vulkan_context.h index 3baf5f2e7..8001905c2 100644 --- a/core/rend/vulkan/vulkan_context.h +++ b/core/rend/vulkan/vulkan_context.h @@ -167,6 +167,7 @@ class VulkanContext : public GraphicsContext, public FlightManager vk::SubmitInfo(nullptr, nullptr, buffers), fence); } bool hasPerPixel() override { return fragmentStoresAndAtomics; } + bool hasProvokingVertex() { return provokingVertexSupported; } bool recreateSwapChainIfNeeded(); void addToFlight(Deletable *object) override { inFlightObjects[GetCurrentImageIndex()].emplace_back(object); @@ -229,6 +230,7 @@ class VulkanContext : public GraphicsContext, public FlightManager bool samplerAnisotropy = false; float maxSamplerAnisotropy = 0.f; bool dedicatedAllocationSupported = false; + bool provokingVertexSupported = false; u32 vendorID = 0; int swapInterval = 1; vk::UniqueDevice device; From b010c04d632caba0dbe9fd5e099256df1ca14ff6 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 16:26:53 -0700 Subject: [PATCH 2/7] vk: Propagate physical device API version to VMA Allows VMA to make assumptions such as using the `*KHR` or non-`KHR` versions of certain function names. --- core/rend/vulkan/vmallocator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/rend/vulkan/vmallocator.cpp b/core/rend/vulkan/vmallocator.cpp index b284a4c6a..c29643e64 100644 --- a/core/rend/vulkan/vmallocator.cpp +++ b/core/rend/vulkan/vmallocator.cpp @@ -55,6 +55,7 @@ void VMAllocator::Init(vk::PhysicalDevice physicalDevice, vk::Device device, vk: allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; allocatorInfo.physicalDevice = (VkPhysicalDevice)physicalDevice; + allocatorInfo.vulkanApiVersion = physicalDevice.getProperties().apiVersion; allocatorInfo.device = (VkDevice)device; allocatorInfo.instance = (VkInstance)instance; #if !defined(NDEBUG) || defined(DEBUGFAST) From b17736beff8e4b49b084da1f339e2c53b19d0bd8 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 16:49:36 -0700 Subject: [PATCH 3/7] vk: Refactor libretro device initialization for `VK_EXT_provoking_vertex` --- core/rend/vulkan/vk_context_lr.cpp | 130 ++++++++++++++++++++++------- core/rend/vulkan/vk_context_lr.h | 4 +- 2 files changed, 102 insertions(+), 32 deletions(-) diff --git a/core/rend/vulkan/vk_context_lr.cpp b/core/rend/vulkan/vk_context_lr.cpp index 5f0db5c72..a49eeeebb 100644 --- a/core/rend/vulkan/vk_context_lr.cpp +++ b/core/rend/vulkan/vk_context_lr.cpp @@ -71,6 +71,9 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic physicalDevice = vkinstance.enumeratePhysicalDevices().front(); } context->gpu = (VkPhysicalDevice)physicalDevice; + + const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties(); + std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); // get the first index into queueFamilyProperties which supports graphics and compute @@ -124,54 +127,119 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic DEBUG_LOG(RENDERER, "Using distinct Graphics and Present queue families"); } - vk::PhysicalDeviceFeatures supportedFeatures; - physicalDevice.getFeatures(&supportedFeatures); - VulkanContext::Instance()->fragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics; - VulkanContext::Instance()->samplerAnisotropy = supportedFeatures.samplerAnisotropy; - // Enable VK_KHR_dedicated_allocation if available - bool getMemReq2Supported = false; - VulkanContext::Instance()->dedicatedAllocationSupported = false; - std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - for (unsigned i = 0; i < num_required_device_extensions; i++) - deviceExtensions.push_back(required_device_extensions[i]); - for (const auto& property : physicalDevice.enumerateDeviceExtensionProperties()) + std::set supportedExtensions; + + const auto deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties(); + for (const auto& property : deviceExtensionProperties) { - if (!strcmp(property.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) + supportedExtensions.insert(property.extensionName); + } + + std::vector enabledExtensions; + + const auto tryAddDeviceExtension = [&supportedExtensions = std::as_const(supportedExtensions), &enabledExtensions] + (std::string_view extensionName) -> bool { - deviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); - getMemReq2Supported = true; - } - else if (!strcmp(property.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) + if (supportedExtensions.count(extensionName.data())) + { + enabledExtensions.push_back(extensionName.data()); + NOTICE_LOG(RENDERER, "Device extension enabled: %s", extensionName.data()); + return true; + } + NOTICE_LOG(RENDERER, "Device extension unavailable: %s", extensionName.data()); + return false; + }; + + // Required swapchain extension + tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + // Enable VK_KHR_dedicated_allocation if available + if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1) + { + // Core in Vulkan 1.1 + VulkanContext::Instance()->dedicatedAllocationSupported = true; + } + else + { + const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + if (getMemReq2Supported) { - deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); - VulkanContext::Instance()->dedicatedAllocationSupported = true; + VulkanContext::Instance()->dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); } } - VulkanContext::Instance()->dedicatedAllocationSupported &= getMemReq2Supported; + + // Check for VK_KHR_get_physical_device_properties2 + // Core as of Vulkan 1.1 + const bool getPhysicalDeviceProperties2Supported = + (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1) + ? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + + if (getPhysicalDeviceProperties2Supported) + { + // Enable VK_EXT_provoking_vertex if available + VulkanContext::Instance()->provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + } + + // Get device features + + vk::PhysicalDeviceFeatures2 featuresChain{}; + vk::PhysicalDeviceFeatures& features = featuresChain.features; + + vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{}; + if (VulkanContext::Instance()->provokingVertexSupported) + { + featuresChain.pNext = &provokingVertexFeatures; + } + + // Get the physical device's features + if (getPhysicalDeviceProperties2Supported && featuresChain.pNext) + { + physicalDevice.getFeatures2(&featuresChain); + } + else + { + physicalDevice.getFeatures(&features); + } + + if (VulkanContext::Instance()->provokingVertexSupported) + { + VulkanContext::Instance()->provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast; + } + + VulkanContext::Instance()->samplerAnisotropy = features.samplerAnisotropy; + VulkanContext::Instance()->fragmentStoresAndAtomics = features.fragmentStoresAndAtomics; // create a Device float queuePriority = 1.0f; - vk::DeviceQueueCreateInfo deviceQueueCreateInfos[] = { + vk::DeviceQueueCreateInfo deviceQueueCreateInfo[] = { vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->queue_family_index, 1, &queuePriority), vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->presentation_queue_family_index, 1, &queuePriority), }; - vk::PhysicalDeviceFeatures features(*required_features); - if (VulkanContext::Instance()->fragmentStoresAndAtomics) - features.fragmentStoresAndAtomics = true; - if (VulkanContext::Instance()->samplerAnisotropy) - features.samplerAnisotropy = true; - vk::Device device = physicalDevice.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), - context->queue_family_index == context->presentation_queue_family_index ? 1 : 2, deviceQueueCreateInfos, - num_required_device_layers, required_device_layers, deviceExtensions.size(), &deviceExtensions[0], &features)); - context->device = (VkDevice)device; + + + vk::Device newDevice{}; + if (getPhysicalDeviceProperties2Supported) + { + vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, + nullptr, enabledExtensions); + deviceCreateInfo.pNext = &featuresChain; + newDevice = physicalDevice.createDevice(deviceCreateInfo); + } + else + { + newDevice = physicalDevice.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, + nullptr, enabledExtensions, &features)); + } + + context->device = (VkDevice)newDevice; #if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 VULKAN_HPP_DEFAULT_DISPATCHER.init(context->device); #endif // Queues - context->queue = (VkQueue)device.getQueue(context->queue_family_index, 0); - context->presentation_queue = (VkQueue)device.getQueue(context->presentation_queue_family_index, 0); + context->queue = (VkQueue)newDevice.getQueue(context->queue_family_index, 0); + context->presentation_queue = (VkQueue)newDevice.getQueue(context->presentation_queue_family_index, 0); return true; } diff --git a/core/rend/vulkan/vk_context_lr.h b/core/rend/vulkan/vk_context_lr.h index 4d49c345a..c97f489bc 100644 --- a/core/rend/vulkan/vk_context_lr.h +++ b/core/rend/vulkan/vk_context_lr.h @@ -92,6 +92,7 @@ class VulkanContext : public GraphicsContext, public FlightManager bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; } bool SupportsDedicatedAllocation() const { return dedicatedAllocationSupported; } bool hasPerPixel() override { return fragmentStoresAndAtomics; } + bool hasProvokingVertex() { return provokingVertexSupported; } const VMAllocator& GetAllocator() const { return allocator; } vk::DeviceSize GetMaxMemoryAllocationSize() const { return maxMemoryAllocationSize; } f32 GetMaxSamplerAnisotropy() const { return samplerAnisotropy ? maxSamplerAnisotropy : 1.f; } @@ -129,10 +130,11 @@ class VulkanContext : public GraphicsContext, public FlightManager bool optimalTilingSupported1555 = false; bool optimalTilingSupported4444 = false; public: + bool fragmentStoresAndAtomics = false; bool samplerAnisotropy = false; f32 maxSamplerAnisotropy = 0.f; bool dedicatedAllocationSupported = false; - bool fragmentStoresAndAtomics = false; + bool provokingVertexSupported = false; private: u32 vendorID = 0; From 893993dd40ea888fafa1662ad14bfa2ba9dddbf6 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 16:59:22 -0700 Subject: [PATCH 4/7] vk: Top out at vulkan API version to VMA to 1.1 Despite the physical device possibly being 1.2 or 1.3, we only want up to 1.1. Otherwise we will be responsible for other API functions being resolved and loaded when passing to VMA. --- core/rend/vulkan/vmallocator.cpp | 3 ++- core/rend/vulkan/vulkan_context.cpp | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/rend/vulkan/vmallocator.cpp b/core/rend/vulkan/vmallocator.cpp index c29643e64..abe592a5f 100644 --- a/core/rend/vulkan/vmallocator.cpp +++ b/core/rend/vulkan/vmallocator.cpp @@ -55,7 +55,8 @@ void VMAllocator::Init(vk::PhysicalDevice physicalDevice, vk::Device device, vk: allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; allocatorInfo.physicalDevice = (VkPhysicalDevice)physicalDevice; - allocatorInfo.vulkanApiVersion = physicalDevice.getProperties().apiVersion; + // Top-out at vulkan 1.1 + allocatorInfo.vulkanApiVersion = (physicalDevice.getProperties().apiVersion >= VK_API_VERSION_1_1) ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0; allocatorInfo.device = (VkDevice)device; allocatorInfo.instance = (VkInstance)instance; #if !defined(NDEBUG) || defined(DEBUGFAST) diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index 606231b45..304bd0c0b 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -158,10 +158,9 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co bool vulkan11 = false; if (VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion != nullptr) { - u32 apiVersion = vk::enumerateInstanceVersion(); + const u32 apiVersion = vk::enumerateInstanceVersion(); - vulkan11 = VK_API_VERSION_MAJOR(apiVersion) > 1 - || (VK_API_VERSION_MAJOR(apiVersion) == 1 && VK_API_VERSION_MINOR(apiVersion) >= 1); + vulkan11 = (apiVersion >= VK_API_VERSION_1_1); } vk::ApplicationInfo applicationInfo("Flycast", 1, "Flycast", 1, vulkan11 ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0); From 697db4f542cfb074338f4e00489ffade346eee82 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 17:13:30 -0700 Subject: [PATCH 5/7] vk: Enable `VK_EXT_provoking_vertex` usage for ModVol and Final(OIT) pipeline --- core/rend/vulkan/oit/oit_pipeline.cpp | 11 +++++++++++ core/rend/vulkan/pipeline.cpp | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/core/rend/vulkan/oit/oit_pipeline.cpp b/core/rend/vulkan/oit/oit_pipeline.cpp index fb302db45..ea640dc89 100644 --- a/core/rend/vulkan/oit/oit_pipeline.cpp +++ b/core/rend/vulkan/oit/oit_pipeline.cpp @@ -222,6 +222,17 @@ void OITPipelineManager::CreateFinalPipeline(bool dithering) 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp index edfeab3f2..c0de95156 100644 --- a/core/rend/vulkan/pipeline.cpp +++ b/core/rend/vulkan/pipeline.cpp @@ -67,6 +67,17 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool n 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil From 6b2bc0554694d3b4a23bf6732f7c1d8cfefe2a63 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 17:16:11 -0700 Subject: [PATCH 6/7] vk: Enable `VK_EXT_provoking_vertex` for ModVol(OIT) pipeline Pretty much anything handling dreamcast-geometry should use this extension when available --- core/rend/vulkan/oit/oit_pipeline.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/rend/vulkan/oit/oit_pipeline.cpp b/core/rend/vulkan/oit/oit_pipeline.cpp index ea640dc89..890ac0313 100644 --- a/core/rend/vulkan/oit/oit_pipeline.cpp +++ b/core/rend/vulkan/oit/oit_pipeline.cpp @@ -399,6 +399,17 @@ void OITPipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, boo 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil From 0c236856f4126b0d4dc09c07d87c61dfd7205d02 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Wed, 9 Oct 2024 17:26:58 -0700 Subject: [PATCH 7/7] vk: Additional `VK_EXT_provoking_vertex` pipeline fixes --- core/rend/vulkan/oit/oit_drawer.cpp | 10 +++++++++- core/rend/vulkan/oit/oit_pipeline.cpp | 22 ++++++++++++++++++++++ core/rend/vulkan/pipeline.cpp | 11 +++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/core/rend/vulkan/oit/oit_drawer.cpp b/core/rend/vulkan/oit/oit_drawer.cpp index f4ed5b3c8..2695ad3eb 100644 --- a/core/rend/vulkan/oit/oit_drawer.cpp +++ b/core/rend/vulkan/oit/oit_drawer.cpp @@ -341,7 +341,15 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture) bool firstFrameAfterInit = oitBuffers->isFirstFrameAfterInit(); oitBuffers->OnNewFrame(cmdBuffer); - setFirstProvokingVertex(pvrrc); + if (VulkanContext::Instance()->hasProvokingVertex()) + { + // Pipelines are using VK_EXT_provoking_vertex, no need to + // re-order vertices + } + else + { + setFirstProvokingVertex(pvrrc); + } // Upload vertex and index buffers UploadMainBuffer(vtxUniforms, fragUniforms); diff --git a/core/rend/vulkan/oit/oit_pipeline.cpp b/core/rend/vulkan/oit/oit_pipeline.cpp index 890ac0313..b6d3d31db 100644 --- a/core/rend/vulkan/oit/oit_pipeline.cpp +++ b/core/rend/vulkan/oit/oit_pipeline.cpp @@ -317,6 +317,17 @@ void OITPipelineManager::CreateClearPipeline() 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil @@ -522,6 +533,17 @@ void OITPipelineManager::CreateTrModVolPipeline(ModVolMode mode, int cullMode, b 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp index c0de95156..fa2e45a8b 100644 --- a/core/rend/vulkan/pipeline.cpp +++ b/core/rend/vulkan/pipeline.cpp @@ -200,6 +200,17 @@ void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2) 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); + + // Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first. + // Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the + // last vertex + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{}; + if (GetContext()->hasProvokingVertex()) + { + provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex; + pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo; + } + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil