Skip to content

Commit

Permalink
vk: Add VK_EXT_provoking_vertex optimization (#1681)
Browse files Browse the repository at this point in the history
* 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.

* 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.

* vk: Refactor libretro device initialization for `VK_EXT_provoking_vertex`

* 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.

* vk: Enable `VK_EXT_provoking_vertex` usage for ModVol and Final(OIT) pipeline

* vk: Enable `VK_EXT_provoking_vertex` for ModVol(OIT) pipeline

Pretty much anything handling dreamcast-geometry should use this extension when available

* vk: Additional `VK_EXT_provoking_vertex` pipeline fixes
  • Loading branch information
Wunkolo authored Oct 11, 2024
1 parent 5b34356 commit 4d73cc8
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 53 deletions.
10 changes: 9 additions & 1 deletion core/rend/vulkan/drawer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion core/rend/vulkan/oit/oit_drawer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
55 changes: 55 additions & 0 deletions core/rend/vulkan/oit/oit_pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -211,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
Expand Down Expand Up @@ -295,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
Expand Down Expand Up @@ -377,6 +410,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
Expand Down Expand Up @@ -489,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
Expand Down
33 changes: 33 additions & 0 deletions core/rend/vulkan/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -189,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
Expand Down Expand Up @@ -295,6 +317,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
Expand Down
130 changes: 99 additions & 31 deletions core/rend/vulkan/vk_context_lr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();

// get the first index into queueFamilyProperties which supports graphics and compute
Expand Down Expand Up @@ -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<const char *> 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<std::string> 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<const char*> 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;
}
Expand Down
4 changes: 3 additions & 1 deletion core/rend/vulkan/vk_context_lr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions core/rend/vulkan/vmallocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +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;
// 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)
Expand Down
Loading

0 comments on commit 4d73cc8

Please sign in to comment.