diff --git a/src/freedreno/vulkan/tu_cmd_buffer.cc b/src/freedreno/vulkan/tu_cmd_buffer.cc index c8aa4dbb032..3e4af0d83e7 100644 --- a/src/freedreno/vulkan/tu_cmd_buffer.cc +++ b/src/freedreno/vulkan/tu_cmd_buffer.cc @@ -6225,6 +6225,31 @@ tu6_stencil_written_on_depth_fail( } } +/* Returns true if the stencil write result may change based on the result of a + * depth test. + */ +static bool +tu6_stencil_written_based_on_depth_test( + const struct vk_stencil_test_face_state *face) +{ + switch (face->op.compare) { + case VK_COMPARE_OP_ALWAYS: + /* The stencil op always passes, no need to worry about failOp. */ + return face->op.depth_fail != VK_STENCIL_OP_KEEP || + face->op.pass != VK_STENCIL_OP_KEEP; + case VK_COMPARE_OP_NEVER: + /* The stencil op always fails, so failOp will always be used. */ + return face->op.fail != VK_STENCIL_OP_KEEP; + default: + /* If the stencil test fails, depth may fail as well, so we can write + * stencil when the depth fails if failOp is not VK_STENCIL_OP_KEEP. + */ + return face->op.fail != VK_STENCIL_OP_KEEP || + face->op.pass != VK_STENCIL_OP_KEEP || + face->op.depth_fail != VK_STENCIL_OP_KEEP; + } +} + /* Various frontends (ANGLE, zink at least) will enable stencil testing with * what works out to be no-op writes. Simplify what they give us into flags * that LRZ can use. @@ -6240,6 +6265,7 @@ tu6_update_simplified_stencil_state(struct tu_cmd_buffer *cmd) cmd->state.stencil_front_write = false; cmd->state.stencil_back_write = false; cmd->state.stencil_written_on_depth_fail = false; + cmd->state.stencil_written_based_on_depth_test = false; return; } @@ -6272,6 +6298,11 @@ tu6_update_simplified_stencil_state(struct tu_cmd_buffer *cmd) tu6_stencil_written_on_depth_fail(&ds->stencil.front)) || (cmd->state.stencil_back_write && tu6_stencil_written_on_depth_fail(&ds->stencil.back)); + cmd->state.stencil_written_based_on_depth_test = + (cmd->state.stencil_front_write && + tu6_stencil_written_based_on_depth_test(&ds->stencil.front)) || + (cmd->state.stencil_back_write && + tu6_stencil_written_based_on_depth_test(&ds->stencil.back)); } static bool diff --git a/src/freedreno/vulkan/tu_cmd_buffer.h b/src/freedreno/vulkan/tu_cmd_buffer.h index 6f095f415be..4bc23194064 100644 --- a/src/freedreno/vulkan/tu_cmd_buffer.h +++ b/src/freedreno/vulkan/tu_cmd_buffer.h @@ -549,6 +549,7 @@ struct tu_cmd_state bool stencil_front_write; bool stencil_back_write; bool stencil_written_on_depth_fail; + bool stencil_written_based_on_depth_test; bool pipeline_sysmem_single_prim_mode; bool pipeline_has_tess; bool pipeline_disable_gmem; diff --git a/src/freedreno/vulkan/tu_lrz.cc b/src/freedreno/vulkan/tu_lrz.cc index 414de6cc714..6d84f4bd421 100644 --- a/src/freedreno/vulkan/tu_lrz.cc +++ b/src/freedreno/vulkan/tu_lrz.cc @@ -964,6 +964,23 @@ tu6_calculate_lrz_state(struct tu_cmd_buffer *cmd, } } + /* If the stencil test behavior depends on the result of the depth test, we + * have to skip LRZ for the rest of the RP for basically the same reason as + * the blending case above (LRZ testing enabled on previous draws may result + * in skipping their Z changes which feed into this draw, so we can't let + * later Z writes affect any of them). + * + * Because the LRZ test runs first, failing the LRZ test may result in + * skipping the stencil test and subsequent stencil write. This is ok if + * stencil is only written when the depth test passes, because then the LRZ + * test will also pass, but if it may be written when the depth or stencil + * test fails then we need to disable the LRZ test for the draw as well. + */ + if (cmd->state.stencil_written_based_on_depth_test) { + tu_lrz_write_disable_reason(cmd, "stencil write based on depth test"); + cmd->state.lrz.disable_write_for_rp = true; + } + if (disable_lrz) cmd->state.lrz.valid = false;