diff --git a/src/panfrost/vulkan/csf/panvk_vX_cmd_draw.c b/src/panfrost/vulkan/csf/panvk_vX_cmd_draw.c index 45505665021..7d84314dde3 100644 --- a/src/panfrost/vulkan/csf/panvk_vX_cmd_draw.c +++ b/src/panfrost/vulkan/csf/panvk_vX_cmd_draw.c @@ -212,11 +212,18 @@ prepare_fs_driver_set(struct panvk_cmd_buffer *cmdbuf) return VK_SUCCESS; } +/* This value has been selected to get + * dEQP-VK.draw.renderpass.inverted_depth_ranges.nodepthclamp_deltazero passing. + */ +#define MIN_DEPTH_CLIP_RANGE 37.7E-06f + static void prepare_sysvals(struct panvk_cmd_buffer *cmdbuf) { struct panvk_graphics_sysvals *sysvals = &cmdbuf->state.gfx.sysvals; struct vk_color_blend_state *cb = &cmdbuf->vk.dynamic_graphics_state.cb; + const struct vk_rasterization_state *rs = + &cmdbuf->vk.dynamic_graphics_state.rs; if (is_dirty(cmdbuf, CB_BLEND_CONSTANTS)) { for (unsigned i = 0; i < ARRAY_SIZE(cb->blend_constants); i++) @@ -225,7 +232,8 @@ prepare_sysvals(struct panvk_cmd_buffer *cmdbuf) cmdbuf->state.gfx.push_uniforms = 0; } - if (is_dirty(cmdbuf, VP_VIEWPORTS)) { + if (is_dirty(cmdbuf, VP_VIEWPORTS) || is_dirty(cmdbuf, RS_CULL_MODE) || + is_dirty(cmdbuf, RS_DEPTH_CLAMP_ENABLE)) { VkViewport *viewport = &cmdbuf->vk.dynamic_graphics_state.vp.viewports[0]; /* Upload the viewport scale. Defined as (px/2, py/2, pz) at the start of @@ -251,6 +259,38 @@ prepare_sysvals(struct panvk_cmd_buffer *cmdbuf) sysvals->viewport.offset.x = (0.5f * viewport->width) + viewport->x; sysvals->viewport.offset.y = (0.5f * viewport->height) + viewport->y; sysvals->viewport.offset.z = viewport->minDepth; + + /* Doing the viewport transform in the vertex shader and then depth + * clipping with the viewport depth range gets a similar result to + * clipping in clip-space, but loses precision when the viewport depth + * range is very small. When minDepth == maxDepth, this completely + * flattens the clip-space depth and results in never clipping. + * + * To work around this, set a lower limit on depth range when clipping is + * enabled. This results in slightly incorrect fragment depth values, and + * doesn't help with the precision loss, but at least clipping isn't + * completely broken. + */ + if (vk_rasterization_state_depth_clip_enable(rs) && + fabsf(sysvals->viewport.scale.z) < MIN_DEPTH_CLIP_RANGE) { + float z_min = viewport->minDepth; + float z_max = viewport->maxDepth; + float z_sign = z_min <= z_max ? 1.0f : -1.0f; + + sysvals->viewport.scale.z = z_sign * MIN_DEPTH_CLIP_RANGE; + + /* Middle of the user range is + * z_range_center = z_min + (z_max - z_min) * 0.5f, + * and we want to set the offset to + * z_offset = z_range_center - viewport.scale.z * 0.5f + * which, when expanding, gives us + * z_offset = (z_max + z_min - viewport.scale.z) * 0.5f + */ + float z_offset = (z_max + z_min - sysvals->viewport.scale.z) * 0.5f; + /* Bump offset off-center if necessary, to not go out of range */ + sysvals->viewport.offset.z = CLAMP(z_offset, 0.0f, 1.0f); + } + cmdbuf->state.gfx.push_uniforms = 0; } } @@ -577,11 +617,14 @@ prepare_vp(struct panvk_cmd_buffer *cmdbuf) cs_move64_to(b, cs_sr_reg64(b, 42), scissor_box); } - if (is_dirty(cmdbuf, VP_VIEWPORTS)) { - cs_move32_to(b, cs_sr_reg32(b, 44), - fui(MIN2(viewport->minDepth, viewport->maxDepth))); - cs_move32_to(b, cs_sr_reg32(b, 45), - fui(MAX2(viewport->minDepth, viewport->maxDepth))); + if (is_dirty(cmdbuf, VP_VIEWPORTS) || is_dirty(cmdbuf, RS_CULL_MODE) || + is_dirty(cmdbuf, RS_DEPTH_CLAMP_ENABLE)) { + struct panvk_graphics_sysvals *sysvals = &cmdbuf->state.gfx.sysvals; + + float z_min = sysvals->viewport.offset.z; + float z_max = z_min + sysvals->viewport.scale.z; + cs_move32_to(b, cs_sr_reg32(b, 44), fui(MIN2(z_min, z_max))); + cs_move32_to(b, cs_sr_reg32(b, 45), fui(MAX2(z_min, z_max))); } }