diff --git a/common/Vulkan/Builders.cpp b/common/Vulkan/Builders.cpp index 64a3aa8eaa..04ff9a2d11 100644 --- a/common/Vulkan/Builders.cpp +++ b/common/Vulkan/Builders.cpp @@ -149,6 +149,9 @@ namespace Vulkan m_multisample_state = {}; m_multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + m_provoking_vertex = {}; + m_provoking_vertex.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT; + // set defaults SetNoCullRasterizationState(); SetNoDepthTestState(); @@ -420,6 +423,13 @@ namespace Vulkan m_ci.subpass = subpass; } + void GraphicsPipelineBuilder::SetProvokingVertex(VkProvokingVertexModeEXT mode) + { + Util::AddPointerToChain(&m_rasterization_state, &m_provoking_vertex); + + m_provoking_vertex.provokingVertexMode = mode; + } + SamplerBuilder::SamplerBuilder() { Clear(); } void SamplerBuilder::Clear() diff --git a/common/Vulkan/Builders.h b/common/Vulkan/Builders.h index f3ffbb68a0..88a841ea80 100644 --- a/common/Vulkan/Builders.h +++ b/common/Vulkan/Builders.h @@ -128,6 +128,8 @@ namespace Vulkan void SetPipelineLayout(VkPipelineLayout layout); void SetRenderPass(VkRenderPass render_pass, u32 subpass); + void SetProvokingVertex(VkProvokingVertexModeEXT mode); + private: VkGraphicsPipelineCreateInfo m_ci; std::array m_shader_stages; @@ -152,6 +154,8 @@ namespace Vulkan std::array m_dynamic_state_values; VkPipelineMultisampleStateCreateInfo m_multisample_state; + + VkPipelineRasterizationProvokingVertexStateCreateInfoEXT m_provoking_vertex; }; class SamplerBuilder diff --git a/common/Vulkan/Context.cpp b/common/Vulkan/Context.cpp index 4878bbea20..d937d98c6f 100644 --- a/common/Vulkan/Context.cpp +++ b/common/Vulkan/Context.cpp @@ -458,6 +458,9 @@ namespace Vulkan if (enable_surface && !SupportsExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true)) return false; + m_optional_extensions.vk_ext_provoking_vertex = + SupportsExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false); + return true; } @@ -605,6 +608,15 @@ namespace Vulkan device_info.ppEnabledLayerNames = layer_names; } + // provoking vertex + VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_feature = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT}; + if (m_optional_extensions.vk_ext_provoking_vertex) + { + provoking_vertex_feature.provokingVertexLast = VK_TRUE; + Util::AddPointerToChain(&device_info, &provoking_vertex_feature); + } + VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device); if (res != VK_SUCCESS) { @@ -622,9 +634,39 @@ namespace Vulkan { vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue); } + + ProcessDeviceExtensions(); return true; } + void Context::ProcessDeviceExtensions() + { + // advanced feature checks + if (vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceFeatures2 features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; + VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT}; + void** pNext = &features2.pNext; + + // add in optional feature structs + if (m_optional_extensions.vk_ext_provoking_vertex) + { + *pNext = &provoking_vertex_features; + pNext = &provoking_vertex_features.pNext; + } + + // query + vkGetPhysicalDeviceFeatures2(m_physical_device, &features2); + + // confirm we actually support it + m_optional_extensions.vk_ext_provoking_vertex &= (provoking_vertex_features.provokingVertexLast == VK_TRUE); + } + + Console.WriteLn("VK_EXT_provoking_vertex is %s", + m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported"); + } + bool Context::CreateAllocator() { VmaAllocatorCreateInfo ci = {}; @@ -1124,7 +1166,8 @@ namespace Vulkan void Context::DeferBufferDestruction(VkBuffer object, VmaAllocation allocation) { FrameResources& resources = m_frame_resources[m_current_frame]; - resources.cleanup_resources.push_back([this, object, allocation]() { vmaDestroyBuffer(m_allocator, object, allocation); }); + resources.cleanup_resources.push_back( + [this, object, allocation]() { vmaDestroyBuffer(m_allocator, object, allocation); }); } void Context::DeferBufferViewDestruction(VkBufferView object) diff --git a/common/Vulkan/Context.h b/common/Vulkan/Context.h index d6ac0e2910..2cb35cc0f5 100644 --- a/common/Vulkan/Context.h +++ b/common/Vulkan/Context.h @@ -46,6 +46,11 @@ namespace Vulkan TEXTURE_BUFFER_SIZE = 64 * 1024 * 1024, }; + struct OptionalExtensions + { + bool vk_ext_provoking_vertex : 1; + }; + ~Context(); // Determines if the Vulkan validation layer is available on the system. @@ -89,6 +94,7 @@ namespace Vulkan __fi const VkPhysicalDeviceProperties& GetDeviceProperties() const { return m_device_properties; } __fi const VkPhysicalDeviceFeatures& GetDeviceFeatures() const { return m_device_features; } __fi const VkPhysicalDeviceLimits& GetDeviceLimits() const { return m_device_properties.limits; } + __fi const OptionalExtensions& GetOptionalExtensions() const { return m_optional_extensions; } // Helpers for getting constants __fi VkDeviceSize GetUniformBufferAlignment() const @@ -239,6 +245,7 @@ namespace Vulkan bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, const char** required_device_extensions, u32 num_required_device_extensions, const char** required_device_layers, u32 num_required_device_layers, const VkPhysicalDeviceFeatures* required_features); + void ProcessDeviceExtensions(); bool CreateAllocator(); void DestroyAllocator(); @@ -323,6 +330,7 @@ namespace Vulkan VkPhysicalDeviceFeatures m_device_features = {}; VkPhysicalDeviceProperties m_device_properties = {}; VkPhysicalDeviceMemoryProperties m_device_memory_properties = {}; + OptionalExtensions m_optional_extensions = {}; }; } // namespace Vulkan diff --git a/common/Vulkan/Util.cpp b/common/Vulkan/Util.cpp index a48ce213b5..cd90ffa982 100644 --- a/common/Vulkan/Util.cpp +++ b/common/Vulkan/Util.cpp @@ -257,6 +257,20 @@ namespace Vulkan command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 1, &buffer_info, 0, nullptr); } + void AddPointerToChain(void* head, const void* ptr) + { + VkBaseInStructure* last_st = static_cast(head); + while (last_st->pNext) + { + if (last_st->pNext == ptr) + return; + + last_st = const_cast(last_st->pNext); + } + + last_st->pNext = static_cast(ptr); + } + const char* VkResultToString(VkResult res) { switch (res) diff --git a/common/Vulkan/Util.h b/common/Vulkan/Util.h index e1863d2064..32c1935040 100644 --- a/common/Vulkan/Util.h +++ b/common/Vulkan/Util.h @@ -49,6 +49,9 @@ namespace Vulkan VkAccessFlags dst_access_mask, VkDeviceSize offset, VkDeviceSize size, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask); + // Adds a structure to a chain. + void AddPointerToChain(void* head, const void* ptr); + const char* VkResultToString(VkResult res); void LogVulkanResult(const char* func_name, VkResult res, const char* msg, ...) /*printflike(4, 5)*/; diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 7ac40d2e9e..b85aa45e3d 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -244,6 +244,7 @@ bool GSDeviceVK::CheckFeatures() m_features.image_load_store = features.fragmentStoresAndAtomics; m_features.texture_barrier = true; m_features.prefer_new_textures = true; + m_features.provoking_vertex_last = g_vulkan_context->GetOptionalExtensions().vk_ext_provoking_vertex; if (!features.dualSrcBlend) { @@ -1271,6 +1272,10 @@ bool GSDeviceVK::CompileConvertPipelines() gpb.SetNoBlendingState(); gpb.SetVertexShader(vs); + // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass + if (m_features.provoking_vertex_last) + gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); + for (ShaderConvert i = ShaderConvert::COPY; static_cast(i) < static_cast(ShaderConvert::Count); i = static_cast(static_cast(i) + 1)) { @@ -1494,6 +1499,10 @@ bool GSDeviceVK::CompileInterlacePipelines() gpb.SetRenderPass(rp, 0); gpb.SetVertexShader(vs); + // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass + if (m_features.provoking_vertex_last) + gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); + for (int i = 0; i < static_cast(m_interlace.size()); i++) { VkShaderModule ps = GetUtilityFragmentShader(*shader, StringUtil::StdStringFromFormat("ps_main%d", i).c_str()); @@ -1543,6 +1552,10 @@ bool GSDeviceVK::CompileMergePipelines() gpb.SetRenderPass(rp, 0); gpb.SetVertexShader(vs); + // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass + if (m_features.provoking_vertex_last) + gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); + for (int i = 0; i < static_cast(m_merge.size()); i++) { VkShaderModule ps = GetUtilityFragmentShader(*shader, StringUtil::StdStringFromFormat("ps_main%d", i).c_str()); @@ -1872,6 +1885,9 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p) VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, p.cms.wrgba); } + if (m_features.provoking_vertex_last) + gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); + VkPipeline pipeline = gpb.Create(g_vulkan_context->GetDevice(), g_vulkan_shader_cache->GetPipelineCache(true)); if (pipeline) {