Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vk: Add VK_EXT_provoking_vertex optimization #1681

Merged
merged 7 commits into from
Oct 11, 2024
Merged
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
Loading