diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index e0c35fc33..8bb6c9595 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -516,6 +516,11 @@ struct Liverpool { BitField<24, 1, u32> dx_linear_attr_clip_enable; BitField<26, 1, u32> zclip_near_disable; BitField<27, 1, u32> zclip_far_disable; + + bool ZclipEnable() const { + ASSERT(zclip_near_disable == zclip_far_disable); + return !zclip_near_disable; + } }; enum class PolygonMode : u32 { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 91d0de4b9..6a6ebec2c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -109,18 +109,28 @@ GraphicsPipeline::GraphicsPipeline( .patchControlPoints = is_rect_list ? 3U : (is_quad_list ? 4U : key.patch_control_points), }; - const vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex_state = { - .provokingVertexMode = key.provoking_vtx_last == Liverpool::ProvokingVtxLast::First - ? vk::ProvokingVertexModeEXT::eFirstVertex - : vk::ProvokingVertexModeEXT::eLastVertex}; + vk::StructureChain raster_chain = { + vk::PipelineRasterizationStateCreateInfo{ + .depthClampEnable = key.depth_clamp_enable, + .rasterizerDiscardEnable = false, + .polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode), + .lineWidth = 1.0f, + }, + vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT{ + .provokingVertexMode = key.provoking_vtx_last == Liverpool::ProvokingVtxLast::First + ? vk::ProvokingVertexModeEXT::eFirstVertex + : vk::ProvokingVertexModeEXT::eLastVertex, + }, + vk::PipelineRasterizationDepthClipStateCreateInfoEXT{ + .depthClipEnable = key.depth_clip_enable, + }}; - const vk::PipelineRasterizationStateCreateInfo raster_state = { - .pNext = instance.IsProvokingVertexSupported() ? &provoking_vertex_state : nullptr, - .depthClampEnable = false, - .rasterizerDiscardEnable = false, - .polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode), - .lineWidth = 1.0f, - }; + if (!instance.IsProvokingVertexSupported()) { + raster_chain.unlink(); + } + if (!instance.IsDepthClipEnableSupported()) { + raster_chain.unlink(); + } const vk::PipelineMultisampleStateCreateInfo multisampling = { .rasterizationSamples = @@ -128,15 +138,32 @@ GraphicsPipeline::GraphicsPipeline( .sampleShadingEnable = false, }; - const vk::PipelineViewportDepthClipControlCreateInfoEXT clip_control = { - .negativeOneToOne = key.clip_space == Liverpool::ClipSpace::MinusWToW, + const vk::DepthClampRangeEXT depth_clamp_range = { + .minDepthClamp = key.min_depth_clamp, + .maxDepthClamp = key.max_depth_clamp, }; - const vk::PipelineViewportStateCreateInfo viewport_info = { - .pNext = instance.IsDepthClipControlSupported() ? &clip_control : nullptr, + vk::StructureChain viewport_chain = { + vk::PipelineViewportStateCreateInfo{}, + vk::PipelineViewportDepthClipControlCreateInfoEXT{ + .negativeOneToOne = key.clip_space == Liverpool::ClipSpace::MinusWToW, + }, + vk::PipelineViewportDepthClampControlCreateInfoEXT{ + .depthClampMode = key.depth_clamp_user_defined_range + ? vk::DepthClampModeEXT::eUserDefinedRange + : vk::DepthClampModeEXT::eViewportRange, + .pDepthClampRange = &depth_clamp_range, + }, }; - boost::container::static_vector dynamic_states = { + if (!instance.IsDepthClampControlSupported()) { + viewport_chain.unlink(); + } + if (!instance.IsDepthClipControlSupported()) { + viewport_chain.unlink(); + } + + boost::container::static_vector dynamic_states = { vk::DynamicState::eViewportWithCount, vk::DynamicState::eScissorWithCount, vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthTestEnable, vk::DynamicState::eDepthWriteEnable, vk::DynamicState::eDepthCompareOp, @@ -227,11 +254,19 @@ GraphicsPipeline::GraphicsPipeline( }); } + const auto depth_format = + instance.GetSupportedFormat(LiverpoolToVK::DepthFormat(key.z_format, key.stencil_format), + vk::FormatFeatureFlagBits2::eDepthStencilAttachment); const vk::PipelineRenderingCreateInfo pipeline_rendering_ci = { .colorAttachmentCount = key.num_color_attachments, .pColorAttachmentFormats = key.color_formats.data(), - .depthAttachmentFormat = key.depth_format, - .stencilAttachmentFormat = key.stencil_format, + .depthAttachmentFormat = key.z_format != Liverpool::DepthBuffer::ZFormat::Invalid + ? depth_format + : vk::Format::eUndefined, + .stencilAttachmentFormat = + key.stencil_format != Liverpool::DepthBuffer::StencilFormat::Invalid + ? depth_format + : vk::Format::eUndefined, }; std::array attachments; @@ -301,8 +336,8 @@ GraphicsPipeline::GraphicsPipeline( .pVertexInputState = !instance.IsVertexInputDynamicState() ? &vertex_input_info : nullptr, .pInputAssemblyState = &input_assembly, .pTessellationState = &tessellation_state, - .pViewportState = &viewport_info, - .pRasterizationState = &raster_state, + .pViewportState = &viewport_chain.get(), + .pRasterizationState = &raster_chain.get(), .pMultisampleState = &multisampling, .pColorBlendState = &color_blending, .pDynamicState = &dynamic_info, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 340383d91..1ecfa6b42 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -44,13 +44,20 @@ struct GraphicsPipelineKey { Liverpool::ColorControl::LogicOp logic_op; u32 num_samples; u32 mrt_mask; - vk::Format depth_format; - vk::Format stencil_format; + struct { + Liverpool::DepthBuffer::ZFormat z_format : 2; + Liverpool::DepthBuffer::StencilFormat stencil_format : 1; + u32 depth_clamp_enable : 1; + u32 depth_clamp_user_defined_range : 1; + float min_depth_clamp; + float max_depth_clamp; + }; struct { AmdGpu::PrimitiveType prim_type : 5; Liverpool::PolygonMode polygon_mode : 2; Liverpool::ClipSpace clip_space : 1; Liverpool::ProvokingVtxLast provoking_vtx_last : 1; + u32 depth_clip_enable : 1; }; bool operator==(const GraphicsPipelineKey& key) const noexcept { diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index a00e9b2fd..a0a906c24 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -270,6 +270,8 @@ bool Instance::CreateDevice() { } custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); depth_clip_control = add_extension(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); + depth_clip_enable = add_extension(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME); + depth_clamp_control = add_extension(VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME); vertex_input_dynamic_state = add_extension(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); fragment_shader_barycentric = add_extension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME); @@ -363,6 +365,7 @@ bool Instance::CreateDevice() { .dualSrcBlend = features.dualSrcBlend, .logicOp = features.logicOp, .multiDrawIndirect = features.multiDrawIndirect, + .depthClamp = features.depthClamp, .depthBiasClamp = features.depthBiasClamp, .fillModeNonSolid = features.fillModeNonSolid, .depthBounds = features.depthBounds, @@ -419,6 +422,12 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceDepthClipControlFeaturesEXT{ .depthClipControl = true, }, + vk::PhysicalDeviceDepthClipEnableFeaturesEXT{ + .depthClipEnable = true, + }, + vk::PhysicalDeviceDepthClampControlFeaturesEXT{ + .depthClampControl = true, + }, vk::PhysicalDeviceRobustness2FeaturesEXT{ .robustBufferAccess2 = robustness2_features.robustBufferAccess2, .robustImageAccess2 = robustness2_features.robustImageAccess2, @@ -491,6 +500,12 @@ bool Instance::CreateDevice() { if (!depth_clip_control) { device_chain.unlink(); } + if (!depth_clip_enable) { + device_chain.unlink(); + } + if (!depth_clamp_control) { + device_chain.unlink(); + } if (!robustness2) { device_chain.unlink(); } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 40a9133ab..67dcc183a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -109,6 +109,16 @@ public: return depth_clip_control; } + /// Returns true when VK_EXT_depth_clip_enable is supported + bool IsDepthClipEnableSupported() const { + return depth_clip_enable; + } + + /// Returns true when VK_EXT_depth_clamp_control is supported + bool IsDepthClampControlSupported() const { + return depth_clamp_control; + } + /// Returns true when VK_EXT_depth_range_unrestricted is supported bool IsDepthRangeUnrestrictedSupported() const { return depth_range_unrestricted; @@ -409,6 +419,8 @@ private: bool custom_border_color{}; bool fragment_shader_barycentric{}; bool depth_clip_control{}; + bool depth_clip_enable{}; + bool depth_clamp_control{}; bool depth_range_unrestricted{}; bool dynamic_state_3{}; bool vertex_input_dynamic_state{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 69f040bf8..8433eff6c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -285,28 +285,21 @@ bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; - const auto depth_format = instance.GetSupportedFormat( - LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format, - regs.depth_buffer.stencil_info.format), - vk::FormatFeatureFlagBits2::eDepthStencilAttachment); - if (regs.depth_buffer.DepthValid()) { - key.depth_format = depth_format; - } else { - key.depth_format = vk::Format::eUndefined; - } - if (regs.depth_buffer.StencilValid()) { - key.stencil_format = depth_format; - } else { - key.stencil_format = vk::Format::eUndefined; - } - - key.prim_type = regs.primitive_type; - key.polygon_mode = regs.polygon_control.PolyMode(); + key.z_format = regs.depth_buffer.DepthValid() ? regs.depth_buffer.z_info.format + : Liverpool::DepthBuffer::ZFormat::Invalid; + key.stencil_format = regs.depth_buffer.StencilValid() + ? regs.depth_buffer.stencil_info.format + : Liverpool::DepthBuffer::StencilFormat::Invalid; + key.depth_clip_enable = regs.clipper_control.ZclipEnable(); key.clip_space = regs.clipper_control.clip_space; key.provoking_vtx_last = regs.polygon_control.provoking_vtx_last; + key.prim_type = regs.primitive_type; + key.polygon_mode = regs.polygon_control.PolyMode(); key.logic_op = regs.color_control.rop3; key.num_samples = regs.NumSamples(); + RefreshDepthClampRange(); + const bool skip_cb_binding = regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; @@ -493,7 +486,66 @@ bool PipelineCache::RefreshGraphicsKey() { } return true; -} // namespace Vulkan +} + +void PipelineCache::RefreshDepthClampRange() { + auto& regs = liverpool->regs; + auto& key = graphics_key; + + bool depth_clamp_can_use_viewport_range = true; + bool depth_clamp_is_same_on_all_viewports = true; + float zmin = std::numeric_limits::max(); + float zmax = std::numeric_limits::max(); + const auto& vp_ctl = regs.viewport_control; + for (u32 i = 0; i < Liverpool::NumViewports; i++) { + const auto& vp = regs.viewports[i]; + const auto& vp_d = regs.viewport_depths[i]; + if (vp.xscale == 0) { + continue; + } + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; + + float min_depth; + float max_depth; + if (regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW) { + min_depth = zoffset - zscale; + max_depth = zoffset + zscale; + } else { + min_depth = zoffset; + max_depth = zoffset + zscale; + } + if (zmin == std::numeric_limits::max()) { + zmin = vp_d.zmin; + zmax = vp_d.zmax; + } + depth_clamp_is_same_on_all_viewports &= (zmin == vp_d.zmin && zmax == vp_d.zmax); + depth_clamp_can_use_viewport_range &= (min_depth == vp_d.zmin && max_depth == vp_d.zmax); + } + + if (zmin == std::numeric_limits::max()) { + return; + } + + if (!depth_clamp_can_use_viewport_range && !depth_clamp_is_same_on_all_viewports) { + LOG_ERROR(Render_Vulkan, + "Viewport depth clamping configuration cannot be accurately emulated"); + } else if (!depth_clamp_can_use_viewport_range && !instance.IsDepthClampControlSupported()) { + LOG_WARNING( + Render_Vulkan, + "Viewport uses custom clamp range zmin={}, zmax={} which cannot be accurately emulated", + zmin, zmax); + } + + key.depth_clamp_enable = !regs.depth_render_override.disable_viewport_clamp; + if (key.depth_clamp_enable) { + key.depth_clamp_user_defined_range = !depth_clamp_can_use_viewport_range; + if (key.depth_clamp_user_defined_range) { + key.min_depth_clamp = zmin; + key.max_depth_clamp = zmax; + } + } +} bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index ba3407b48..405275439 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -76,6 +76,8 @@ private: bool RefreshGraphicsKey(); bool RefreshComputeKey(); + void RefreshDepthClampRange(); + void DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext); std::optional> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7715cd078..27f3225ea 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1086,12 +1086,6 @@ void Rasterizer::UpdateViewportScissorState() const { viewport.maxDepth = zoffset + zscale; } - if (!regs.depth_render_override.disable_viewport_clamp) { - // Apply depth clamp. - viewport.minDepth = std::max(viewport.minDepth, vp_d.zmin); - viewport.maxDepth = std::min(viewport.maxDepth, vp_d.zmax); - } - if (!instance.IsDepthRangeUnrestrictedSupported()) { // Unrestricted depth range not supported by device. Restrict to valid range. viewport.minDepth = std::max(viewport.minDepth, 0.f);