+static bool
+radv_is_depth_write_enabled(const VkPipelineDepthStencilStateCreateInfo *pCreateInfo)
+{
+ return pCreateInfo->depthTestEnable &&
+ pCreateInfo->depthWriteEnable &&
+ pCreateInfo->depthCompareOp != VK_COMPARE_OP_NEVER;
+}
+
+static bool
+radv_writes_stencil(const VkStencilOpState *state)
+{
+ return state->writeMask &&
+ (state->failOp != VK_STENCIL_OP_KEEP ||
+ state->passOp != VK_STENCIL_OP_KEEP ||
+ state->depthFailOp != VK_STENCIL_OP_KEEP);
+}
+
+static bool
+radv_is_stencil_write_enabled(const VkPipelineDepthStencilStateCreateInfo *pCreateInfo)
+{
+ return pCreateInfo->stencilTestEnable &&
+ (radv_writes_stencil(&pCreateInfo->front) ||
+ radv_writes_stencil(&pCreateInfo->back));
+}
+
+static bool
+radv_is_ds_write_enabled(const VkPipelineDepthStencilStateCreateInfo *pCreateInfo)
+{
+ return radv_is_depth_write_enabled(pCreateInfo) ||
+ radv_is_stencil_write_enabled(pCreateInfo);
+}
+
+static bool
+radv_order_invariant_stencil_op(VkStencilOp op)
+{
+ /* REPLACE is normally order invariant, except when the stencil
+ * reference value is written by the fragment shader. Tracking this
+ * interaction does not seem worth the effort, so be conservative.
+ */
+ return op != VK_STENCIL_OP_INCREMENT_AND_CLAMP &&
+ op != VK_STENCIL_OP_DECREMENT_AND_CLAMP &&
+ op != VK_STENCIL_OP_REPLACE;
+}
+
+static bool
+radv_order_invariant_stencil_state(const VkStencilOpState *state)
+{
+ /* Compute whether, assuming Z writes are disabled, this stencil state
+ * is order invariant in the sense that the set of passing fragments as
+ * well as the final stencil buffer result does not depend on the order
+ * of fragments.
+ */
+ return !state->writeMask ||
+ /* The following assumes that Z writes are disabled. */
+ (state->compareOp == VK_COMPARE_OP_ALWAYS &&
+ radv_order_invariant_stencil_op(state->passOp) &&
+ radv_order_invariant_stencil_op(state->depthFailOp)) ||
+ (state->compareOp == VK_COMPARE_OP_NEVER &&
+ radv_order_invariant_stencil_op(state->failOp));
+}
+
+static bool
+radv_pipeline_out_of_order_rast(struct radv_pipeline *pipeline,
+ struct radv_blend_state *blend,
+ const VkGraphicsPipelineCreateInfo *pCreateInfo)
+{
+ RADV_FROM_HANDLE(radv_render_pass, pass, pCreateInfo->renderPass);
+ struct radv_subpass *subpass = pass->subpasses + pCreateInfo->subpass;
+ unsigned colormask = blend->cb_target_enabled_4bit;
+
+ if (!pipeline->device->physical_device->out_of_order_rast_allowed)
+ return false;
+
+ /* Be conservative if a logic operation is enabled with color buffers. */
+ if (colormask && pCreateInfo->pColorBlendState->logicOpEnable)
+ return false;
+
+ /* Default depth/stencil invariance when no attachment is bound. */
+ struct radv_dsa_order_invariance dsa_order_invariant = {
+ .zs = true, .pass_set = true
+ };
+
+ if (pCreateInfo->pDepthStencilState &&
+ subpass->depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED) {
+ const VkPipelineDepthStencilStateCreateInfo *vkds =
+ pCreateInfo->pDepthStencilState;
+ struct radv_render_pass_attachment *attachment =
+ pass->attachments + subpass->depth_stencil_attachment.attachment;
+ bool has_stencil = vk_format_is_stencil(attachment->format);
+ struct radv_dsa_order_invariance order_invariance[2];
+ struct radv_shader_variant *ps =
+ pipeline->shaders[MESA_SHADER_FRAGMENT];
+
+ /* Compute depth/stencil order invariance in order to know if
+ * it's safe to enable out-of-order.
+ */
+ bool zfunc_is_ordered =
+ vkds->depthCompareOp == VK_COMPARE_OP_NEVER ||
+ vkds->depthCompareOp == VK_COMPARE_OP_LESS ||
+ vkds->depthCompareOp == VK_COMPARE_OP_LESS_OR_EQUAL ||
+ vkds->depthCompareOp == VK_COMPARE_OP_GREATER ||
+ vkds->depthCompareOp == VK_COMPARE_OP_GREATER_OR_EQUAL;
+
+ bool nozwrite_and_order_invariant_stencil =
+ !radv_is_ds_write_enabled(vkds) ||
+ (!radv_is_depth_write_enabled(vkds) &&
+ radv_order_invariant_stencil_state(&vkds->front) &&
+ radv_order_invariant_stencil_state(&vkds->back));
+
+ order_invariance[1].zs =
+ nozwrite_and_order_invariant_stencil ||
+ (!radv_is_stencil_write_enabled(vkds) &&
+ zfunc_is_ordered);
+ order_invariance[0].zs =
+ !radv_is_depth_write_enabled(vkds) || zfunc_is_ordered;
+
+ order_invariance[1].pass_set =
+ nozwrite_and_order_invariant_stencil ||
+ (!radv_is_stencil_write_enabled(vkds) &&
+ (vkds->depthCompareOp == VK_COMPARE_OP_ALWAYS ||
+ vkds->depthCompareOp == VK_COMPARE_OP_NEVER));
+ order_invariance[0].pass_set =
+ !radv_is_depth_write_enabled(vkds) ||
+ (vkds->depthCompareOp == VK_COMPARE_OP_ALWAYS ||
+ vkds->depthCompareOp == VK_COMPARE_OP_NEVER);
+
+ dsa_order_invariant = order_invariance[has_stencil];
+ if (!dsa_order_invariant.zs)
+ return false;
+
+ /* The set of PS invocations is always order invariant,
+ * except when early Z/S tests are requested.
+ */
+ if (ps &&
+ ps->info.info.ps.writes_memory &&
+ ps->info.fs.early_fragment_test &&
+ !dsa_order_invariant.pass_set)
+ return false;
+
+ /* Determine if out-of-order rasterization should be disabled
+ * when occlusion queries are used.
+ */
+ pipeline->graphics.disable_out_of_order_rast_for_occlusion =
+ !dsa_order_invariant.pass_set;
+ }
+
+ /* No color buffers are enabled for writing. */
+ if (!colormask)
+ return true;
+
+ unsigned blendmask = colormask & blend->blend_enable_4bit;
+
+ if (blendmask) {
+ /* Only commutative blending. */
+ if (blendmask & ~blend->commutative_4bit)
+ return false;
+
+ if (!dsa_order_invariant.pass_set)
+ return false;
+ }
+
+ if (colormask & ~blendmask)
+ return false;
+
+ return true;
+}
+