panvk: hack to improve depth clipping with small viewport depth range

Fixes 'dEQP-VK.draw.renderpass.inverted_depth_ranges.nodepthclamp_deltazero'.
This is an unfortunate fix, but it's not a situation that's likely to
come up in practice. The proprietary driver does something similar.

Signed-off-by: Benjamin Lee <benjamin.lee@collabora.com>
Co-authored-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Lars-Ivar Hesselberg Simonsen <lars-ivar.simonsen@arm.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31879>
This commit is contained in:
Benjamin Lee 2024-10-28 13:28:18 -07:00 committed by Marge Bot
parent fd5c94b8c7
commit 7ca01506c9

View file

@ -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)));
}
}