diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 81b09c66d4..72cd721c9e 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -691,6 +691,8 @@ VkResult VulkanContext::CreateDevice(int physical_device) { extensionsLookup_.KHR_present_wait = EnableDeviceExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME, 0); } + extensionsLookup_.EXT_provoking_vertex = EnableDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, 0); + // Optional features if (extensionsLookup_.KHR_get_physical_device_properties2 && vkGetPhysicalDeviceFeatures2) { VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR }; @@ -698,17 +700,23 @@ VkResult VulkanContext::CreateDevice(int physical_device) { VkPhysicalDeviceMultiviewFeatures multiViewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES }; VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR }; VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; + VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT }; features2.pNext = &multiViewFeatures; multiViewFeatures.pNext = &presentWaitFeatures; presentWaitFeatures.pNext = &presentIdFeatures; - presentIdFeatures.pNext = nullptr; - + if (extensionsLookup_.EXT_provoking_vertex) { + presentIdFeatures.pNext = &provokingVertexFeatures; + provokingVertexFeatures.pNext = nullptr; + } else { + presentIdFeatures.pNext = nullptr; + } vkGetPhysicalDeviceFeatures2(physical_devices_[physical_device_], &features2); deviceFeatures_.available.standard = features2.features; deviceFeatures_.available.multiview = multiViewFeatures; deviceFeatures_.available.presentWait = presentWaitFeatures; deviceFeatures_.available.presentId = presentIdFeatures; + deviceFeatures_.available.provokingVertex = provokingVertexFeatures; } else { vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard); deviceFeatures_.available.multiview = {}; @@ -744,6 +752,11 @@ VkResult VulkanContext::CreateDevice(int physical_device) { if (extensionsLookup_.KHR_present_wait) { deviceFeatures_.enabled.presentWait.presentWait = deviceFeatures_.available.presentWait.presentWait; } + deviceFeatures_.enabled.provokingVertex = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT }; + if (extensionsLookup_.EXT_provoking_vertex) { + deviceFeatures_.enabled.provokingVertex.provokingVertexLast = true; + } + // deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader; VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; @@ -762,7 +775,13 @@ VkResult VulkanContext::CreateDevice(int physical_device) { features2.pNext = &deviceFeatures_.enabled.multiview; deviceFeatures_.enabled.multiview.pNext = &deviceFeatures_.enabled.presentWait; deviceFeatures_.enabled.presentWait.pNext = &deviceFeatures_.enabled.presentId; - deviceFeatures_.enabled.presentId.pNext = nullptr; + if (extensionsLookup_.EXT_provoking_vertex) { + // TODO: Write some proper chaining thing. + deviceFeatures_.enabled.presentId.pNext = &deviceFeatures_.enabled.provokingVertex; + deviceFeatures_.enabled.provokingVertex.pNext = nullptr; + } else { + deviceFeatures_.enabled.presentId.pNext = nullptr; + } } else { device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard; } diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index bda7484dac..629fb7c5f1 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -279,6 +279,7 @@ public: VkPhysicalDeviceMultiviewFeatures multiview; VkPhysicalDevicePresentWaitFeaturesKHR presentWait; VkPhysicalDevicePresentIdFeaturesKHR presentId; + VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertex; }; const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const { diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index a7ca178c16..2dd6a3fcbe 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -268,6 +268,7 @@ struct VulkanExtensions { bool KHR_present_id; // Should probably check the feature flags instead. bool KHR_present_wait; // Same bool GOOGLE_display_timing; + bool EXT_provoking_vertex; // bool EXT_depth_range_unrestricted; // Allows depth outside [0.0, 1.0] in 32-bit float depth buffers. }; diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 4e5b65e094..da40ad300b 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -50,6 +50,7 @@ enum class PipelineFlags : u8 { USES_GEOMETRY_SHADER = (1 << 3), USES_MULTIVIEW = (1 << 4), // Inherited from the render pass it was created with. USES_DISCARD = (1 << 5), + USES_FLAT_SHADING = (1 << 6), }; ENUM_CLASS_BITOPS(PipelineFlags); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 247d978800..c1af6cc342 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -90,6 +90,7 @@ public: VkDynamicState dynamicStates[6]{}; VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + VkPipelineRasterizationProvokingVertexStateCreateInfoEXT rs_provoking{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT }; // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. Promise *vertexShader = nullptr; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index c47fb46f84..d03ab9eaff 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1221,8 +1221,6 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char gDesc.dss = depth->info; - raster->ToVulkan(&gDesc.rs); - // Copy bindings from input layout. gDesc.topology = primToVK[(int)desc.prim]; @@ -1244,6 +1242,11 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; raster->ToVulkan(&gDesc.rs); + if (renderManager_.GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) { + gDesc.rs.pNext = &gDesc.rs_provoking; + gDesc.rs_provoking.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT; + } + pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << (size_t)RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT, false, tag ? tag : "thin3d"); if (desc.uniformDesc) { @@ -1564,6 +1567,7 @@ std::vector VKContext::GetFeatureList() const { AddFeature(features, "multiviewGeometryShader", vulkan_->GetDeviceFeatures().available.multiview.multiviewGeometryShader, vulkan_->GetDeviceFeatures().enabled.multiview.multiviewGeometryShader); AddFeature(features, "presentId", vulkan_->GetDeviceFeatures().available.presentId.presentId, vulkan_->GetDeviceFeatures().enabled.presentId.presentId); AddFeature(features, "presentWait", vulkan_->GetDeviceFeatures().available.presentWait.presentWait, vulkan_->GetDeviceFeatures().enabled.presentWait.presentWait); + AddFeature(features, "provokingVertexLast", vulkan_->GetDeviceFeatures().available.provokingVertex.provokingVertexLast, vulkan_->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast); features.emplace_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat)); diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index 420f318eb3..3e244bc1c3 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -132,8 +132,8 @@ void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight) { *pHeight = height; } - *pWidth *= VR_GetConfigFloat(VR_CONFIG_VIEWPORT_SUPERSAMPLING); - *pHeight *= VR_GetConfigFloat(VR_CONFIG_VIEWPORT_SUPERSAMPLING); + *pWidth = (int)(*pWidth * VR_GetConfigFloat(VR_CONFIG_VIEWPORT_SUPERSAMPLING)); + *pHeight = (int)(*pHeight * VR_GetConfigFloat(VR_CONFIG_VIEWPORT_SUPERSAMPLING)); } void VR_Recenter(engine_t* engine) { diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 5339ecb26b..d7ba9fb96f 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -124,6 +124,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled; bool doFlatShading = id.Bit(FS_BIT_FLATSHADE) && !flatBug; + if (doFlatShading) { + *fragmentShaderFlags |= FragmentShaderFlags::USES_FLAT_SHADING; + } + ShaderDepalMode shaderDepalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2); if (texture3D) { shaderDepalMode = ShaderDepalMode::OFF; diff --git a/GPU/Common/FragmentShaderGenerator.h b/GPU/Common/FragmentShaderGenerator.h index 63f0a52044..832c634571 100644 --- a/GPU/Common/FragmentShaderGenerator.h +++ b/GPU/Common/FragmentShaderGenerator.h @@ -47,6 +47,7 @@ struct FShaderID; // Can technically be deduced from the fragment shader ID, but this is safer. enum class FragmentShaderFlags : u32 { USES_DISCARD = 2, + USES_FLAT_SHADING = 4, }; ENUM_CLASS_BITOPS(FragmentShaderFlags); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index f141d06119..c8d0250888 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -229,12 +229,17 @@ void DrawEngineVulkan::DoFlush() { GEPrimitiveType prim = prevPrim_; - // Always use software for flat shading to fix the provoking index. - bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); + // Always use software for flat shading to fix the provoking index + // if the provoking vertex extension is not available. + bool provokingVertexOk = (tess || gstate.getShadeMode() != GE_SHADE_FLAT); + if (renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) { + provokingVertexOk = true; + } + bool useHWTransform = CanUseHardwareTransform(prim) && provokingVertexOk; uint32_t ibOffset; uint32_t vbOffset; - + // The optimization to avoid indexing isn't really worth it on Vulkan since it means creating more pipelines. // This could be avoided with the new dynamic state extensions, but not available enough on mobile. const bool forceIndexed = draw_->GetDeviceCaps().verySlowShaderCompiler; @@ -399,6 +404,10 @@ void DrawEngineVulkan::DoFlush() { params.allowClear = framebufferManager_->UseBufferedRendering(); params.allowSeparateAlphaClear = false; params.provokeFlatFirst = true; + if (renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) { + // We can get the OpenGL behavior, no need for workarounds. + params.provokeFlatFirst = false; + } params.flippedY = true; params.usesHalfZ = true; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index dce7a5fb2b..074fa8abd0 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -285,6 +285,11 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, rs.polygonMode = VK_POLYGON_MODE_FILL; rs.depthClampEnable = key.depthClampEnable; + if (renderManager->GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) { + rs.pNext = &desc->rs_provoking; + desc->rs_provoking.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT; + } + desc->fragmentShaderSource = fs->GetShaderString(SHADER_STRING_SOURCE_CODE); desc->vertexShaderSource = vs->GetShaderString(SHADER_STRING_SOURCE_CODE); if (gs) { @@ -376,6 +381,9 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * if (fs->Flags() & FragmentShaderFlags::USES_DISCARD) { pipelineFlags |= PipelineFlags::USES_DISCARD; } + if (fs->Flags() & FragmentShaderFlags::USES_FLAT_SHADING) { + pipelineFlags |= PipelineFlags::USES_FLAT_SHADING; + } if (vs->Flags() & VertexShaderFlags::MULTI_VIEW) { pipelineFlags |= PipelineFlags::USES_MULTIVIEW; } diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 9a95259b33..4ee6e40a2b 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -75,6 +75,7 @@ struct VulkanPipeline { bool UsesDepthStencil() const { return (pipelineFlags & PipelineFlags::USES_DEPTH_STENCIL) != 0; } bool UsesGeometryShader() const { return (pipelineFlags & PipelineFlags::USES_GEOMETRY_SHADER) != 0; } bool UsesDiscard() const { return (pipelineFlags & PipelineFlags::USES_DISCARD) != 0; } + bool UsesFlatShading() const { return (pipelineFlags & PipelineFlags::USES_FLAT_SHADING) != 0; } u32 GetVariantsBitmask() const; }; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 240ee8c97c..694a28bb13 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -616,7 +616,7 @@ void GPUDriverTestScreen::ShaderTest(UIContext &dc) { // Draw rectangle that should be flat shaded dc.BeginPipeline(flatShadingPipeline_, samplerNearest_); // There is a "provoking vertex" difference here between GL and Vulkan when using flat shading. One gets one color, one gets the other. - // Wherever possible we should reconfigure the GL provoking vertex to match Vulkan, probably. + // However, we now use the VK_EXT_provoking_vertex extension to make it match up (and match with the PSP). dc.DrawImageVGradient(ImageID("I_ICON"), 0xFFFFFFFF, 0xFF808080, bounds); dc.Flush();