venus: Refactor pipeline fixup into two stages
authorLina Versace <linyaa@google.com>
Thu, 3 Aug 2023 21:11:38 +0000 (14:11 -0700)
committerLina Versace <linyaa@google.com>
Wed, 18 Oct 2023 19:12:17 +0000 (12:12 -0700)
Function vn_fix_graphics_pipeline_create_infos() had two interleaved
phases: discovery of needed fixes, and application of those fixes.
Move the discovery phase into new function
vn_find_graphics_pipeline_create_info_fixes(). The two-phase approach
will be simplify the implementation of VK_EXT_graphics_pipeline_library.

No intended change in behavior.

Signed-off-by: Lina Versace <linyaa@google.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22419>

src/virtio/vulkan/vn_pipeline.c

index fb58dfe..524cbd0 100644 (file)
@@ -62,6 +62,17 @@ static_assert(
    "vn_graphics_pipeline_create_info_fields::mask is too small");
 
 /**
+ * Description of fixes needed for a single VkGraphicsPipelineCreateInfo
+ * pNext chain.
+ */
+struct vn_graphics_pipeline_fix_desc {
+   /** Erase these fields to prevent the Venus encoder from reading invalid
+    * memory.
+    */
+   struct vn_graphics_pipeline_create_info_fields erase;
+};
+
+/**
  * Temporary storage for fixes in vkCreateGraphicsPipelines.
  *
  * Length of each array is vkCreateGraphicsPipelines::createInfoCount.
@@ -430,307 +441,308 @@ vn_graphics_pipeline_fix_tmp_alloc(const VkAllocationCallbacks *alloc,
    return tmp;
 }
 
-static const VkGraphicsPipelineCreateInfo *
-vn_fix_graphics_pipeline_create_infos(
-   struct vn_device *dev,
-   uint32_t info_count,
-   const VkGraphicsPipelineCreateInfo *infos,
-   const VkAllocationCallbacks *alloc,
-   struct vn_graphics_pipeline_fix_tmp **out_fix_tmp)
+static void
+vn_find_graphics_pipeline_create_info_fixes(
+   const VkGraphicsPipelineCreateInfo *info,
+   struct vn_graphics_pipeline_fix_desc *fix_desc)
 {
-   VN_TRACE_FUNC();
+   memset(fix_desc, 0, sizeof(*fix_desc));
 
-   /* Defer allocation until we need a fix. */
-   struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
-
-   for (uint32_t i = 0; i < info_count; i++) {
-      /* Erase these fields to prevent the Venus encoder from reading invalid
-       * memory.
-       */
-      struct vn_graphics_pipeline_create_info_fields erase = { 0 };
-
-      const VkGraphicsPipelineCreateInfo *info = &infos[i];
-      const VkPipelineRenderingCreateInfo *rendering_info =
-         vk_find_struct_const(info, PIPELINE_RENDERING_CREATE_INFO);
+   const VkPipelineRenderingCreateInfo *rendering_info =
+      vk_find_struct_const(info, PIPELINE_RENDERING_CREATE_INFO);
 
-      VkShaderStageFlags stages = 0;
-      for (uint32_t j = 0; j < info->stageCount; j++) {
-         stages |= info->pStages[j].stage;
-      }
+   VkShaderStageFlags stages = 0;
+   for (uint32_t j = 0; j < info->stageCount; j++) {
+      stages |= info->pStages[j].stage;
+   }
 
-      /* VkDynamicState */
-      struct {
-         bool rasterizer_discard_enable;
-         bool viewport;
-         bool viewport_with_count;
-         bool scissor;
-         bool scissor_with_count;
-         bool vertex_input;
-      } has_dynamic_state = { 0 };
-
-      if (info->pDynamicState) {
-         for (uint32_t j = 0; j < info->pDynamicState->dynamicStateCount;
-              j++) {
-            switch (info->pDynamicState->pDynamicStates[j]) {
-            case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
-               has_dynamic_state.rasterizer_discard_enable = true;
-               break;
-            case VK_DYNAMIC_STATE_VIEWPORT:
-               has_dynamic_state.viewport = true;
-               break;
-            case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
-               has_dynamic_state.viewport_with_count = true;
-               break;
-            case VK_DYNAMIC_STATE_SCISSOR:
-               has_dynamic_state.scissor = true;
-               break;
-            case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
-               has_dynamic_state.scissor_with_count = true;
-               break;
-            case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
-               has_dynamic_state.vertex_input = true;
-               break;
-            default:
-               break;
-            }
+   /* VkDynamicState */
+   struct {
+      bool rasterizer_discard_enable;
+      bool viewport;
+      bool viewport_with_count;
+      bool scissor;
+      bool scissor_with_count;
+      bool vertex_input;
+   } has_dynamic_state = { 0 };
+
+   if (info->pDynamicState) {
+      for (uint32_t j = 0; j < info->pDynamicState->dynamicStateCount; j++) {
+         switch (info->pDynamicState->pDynamicStates[j]) {
+         case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
+            has_dynamic_state.rasterizer_discard_enable = true;
+            break;
+         case VK_DYNAMIC_STATE_VIEWPORT:
+            has_dynamic_state.viewport = true;
+            break;
+         case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
+            has_dynamic_state.viewport_with_count = true;
+            break;
+         case VK_DYNAMIC_STATE_SCISSOR:
+            has_dynamic_state.scissor = true;
+            break;
+         case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
+            has_dynamic_state.scissor_with_count = true;
+            break;
+         case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
+            has_dynamic_state.vertex_input = true;
+            break;
+         default:
+            break;
          }
       }
+   }
 
-      const struct vn_render_pass *pass =
-         vn_render_pass_from_handle(info->renderPass);
-
-      const struct vn_subpass *subpass = NULL;
-      if (pass)
-         subpass = &pass->subpasses[info->subpass];
-
-      /* TODO: Ignore VkPipelineRenderingCreateInfo when not using dynamic
-       * rendering. This requires either a deep rewrite of
-       * VkGraphicsPipelineCreateInfo::pNext or a fix in the generated
-       * protocol code.
-       *
-       * The Vulkan spec (1.3.223) says about VkPipelineRenderingCreateInfo:
-       *    If a graphics pipeline is created with a valid VkRenderPass,
-       *    parameters of this structure are ignored.
-       */
-      const bool has_dynamic_rendering = !pass && rendering_info;
-
-      /* For each pipeline state category, we define a bool.
-       *
-       * The Vulkan spec (1.3.223) says:
-       *    The state required for a graphics pipeline is divided into vertex
-       *    input state, pre-rasterization shader state, fragment shader
-       *    state, and fragment output state.
-       *
-       * Without VK_EXT_graphics_pipeline_library, most states are
-       * unconditionally included in the pipeline. Despite that, we still
-       * reference the state bools in the ignore rules because (a) it makes
-       * the ignore condition easier to validate against the text of the
-       * relevant VUs; and (b) it makes it easier to enable
-       * VK_EXT_graphics_pipeline_library because we won't need to carefully
-       * revisit the text of each VU to untangle the missing pipeline state
-       * bools.
-       */
-
-      /* The spec does not assign a name to this state. We define it just to
-       * deduplicate code.
-       *
-       * The Vulkan spec (1.3.223) says:
-       *    If the value of [...]rasterizerDiscardEnable in the
-       *    pre-rasterization shader state is VK_FALSE or the
-       *    VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE dynamic state is
-       *    enabled fragment shader state and fragment output interface state
-       *    is included in a complete graphics pipeline.
-       */
-      const bool has_raster_state =
-         has_dynamic_state.rasterizer_discard_enable ||
-         (info->pRasterizationState &&
-          info->pRasterizationState->rasterizerDiscardEnable == VK_FALSE);
-
-      /* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT */
-      const bool has_fragment_shader_state = has_raster_state;
+   const struct vn_render_pass *pass =
+      vn_render_pass_from_handle(info->renderPass);
+
+   const struct vn_subpass *subpass = NULL;
+   if (pass)
+      subpass = &pass->subpasses[info->subpass];
+
+   /* TODO: Ignore VkPipelineRenderingCreateInfo when not using dynamic
+    * rendering. This requires either a deep rewrite of
+    * VkGraphicsPipelineCreateInfo::pNext or a fix in the generated
+    * protocol code.
+    *
+    * The Vulkan spec (1.3.223) says about VkPipelineRenderingCreateInfo:
+    *    If a graphics pipeline is created with a valid VkRenderPass,
+    *    parameters of this structure are ignored.
+    */
+   const bool has_dynamic_rendering = !pass && rendering_info;
+
+   /* For each pipeline state category, we define a bool.
+    *
+    * The Vulkan spec (1.3.223) says:
+    *    The state required for a graphics pipeline is divided into vertex
+    *    input state, pre-rasterization shader state, fragment shader
+    *    state, and fragment output state.
+    *
+    * Without VK_EXT_graphics_pipeline_library, most states are
+    * unconditionally included in the pipeline. Despite that, we still
+    * reference the state bools in the ignore rules because (a) it makes
+    * the ignore condition easier to validate against the text of the
+    * relevant VUs; and (b) it makes it easier to enable
+    * VK_EXT_graphics_pipeline_library because we won't need to carefully
+    * revisit the text of each VU to untangle the missing pipeline state
+    * bools.
+    */
+
+   /* The spec does not assign a name to this state. We define it just to
+    * deduplicate code.
+    *
+    * The Vulkan spec (1.3.223) says:
+    *    If the value of [...]rasterizerDiscardEnable in the
+    *    pre-rasterization shader state is VK_FALSE or the
+    *    VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE dynamic state is
+    *    enabled fragment shader state and fragment output interface state
+    *    is included in a complete graphics pipeline.
+    */
+   const bool has_raster_state =
+      has_dynamic_state.rasterizer_discard_enable ||
+      (info->pRasterizationState &&
+       info->pRasterizationState->rasterizerDiscardEnable == VK_FALSE);
+
+   /* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT */
+   const bool has_fragment_shader_state = has_raster_state;
+
+   /* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT */
+   const bool has_fragment_output_state = has_raster_state;
+
+   /* Ignore pTessellationState?
+    *    VUID-VkGraphicsPipelineCreateInfo-pStages-00731
+    */
+   if (info->pTessellationState &&
+       (!(stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
+        !(stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
+      fix_desc->erase.tessellation_state = true;
+   }
 
-      /* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT */
-      const bool has_fragment_output_state = has_raster_state;
+   /* Ignore pViewportState?
+    *    VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750
+    *    VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892
+    */
+   if (info->pViewportState && !has_raster_state) {
+      fix_desc->erase.viewport_state = true;
+   }
 
-      /* Ignore pTessellationState?
-       *    VUID-VkGraphicsPipelineCreateInfo-pStages-00731
-       */
-      if (info->pTessellationState &&
-          (!(stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
-           !(stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
-         erase.tessellation_state = true;
+   /* Ignore pViewports?
+    *    VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04130
+    *
+    * If viewportCount is 0, then venus encoder will ignore pViewports and we
+    * do not need to erase it.
+    */
+   if (!fix_desc->erase.viewport_state && info->pViewportState &&
+       info->pViewportState->pViewports &&
+       info->pViewportState->viewportCount) {
+      const bool has_dynamic_viewport =
+         has_dynamic_state.viewport || has_dynamic_state.viewport_with_count;
+
+      if (has_dynamic_viewport) {
+         fix_desc->erase.viewport_state_viewports = true;
       }
+   }
 
-      /* Ignore pViewportState?
-       *    VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750
-       *    VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892
-       */
-      if (info->pViewportState && has_raster_state) {
-         erase.viewport_state = true;
+   /* Ignore pScissors?
+    *    VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04131
+    *
+    * If scissorCount is 0, then venus encoder will ignore pScissors and we
+    * do not need to erase it.
+    */
+   if (!fix_desc->erase.viewport_state && info->pViewportState &&
+       info->pViewportState->pScissors &&
+       info->pViewportState->scissorCount) {
+      const bool has_dynamic_scissor =
+         has_dynamic_state.scissor || has_dynamic_state.scissor_with_count;
+      if (has_dynamic_scissor) {
+         fix_desc->erase.viewport_state_scissors = true;
       }
+   }
 
-      /* Ignore pViewports?
-       *    VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04130
-       *
-       * If viewportCount is 0, then venus encoder will ignore pViewports and
-       * we do not need to erase it.
-       */
-      if (!erase.viewport_state && info->pViewportState &&
-          info->pViewportState->pViewports &&
-          info->pViewportState->viewportCount) {
-         const bool has_dynamic_viewport =
-            has_dynamic_state.viewport ||
-            has_dynamic_state.viewport_with_count;
-
-         if (has_dynamic_viewport) {
-            erase.viewport_state_viewports = true;
-         }
-      }
+   /* Ignore pMultisampleState?
+    *    VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751
+    */
+   if (info->pMultisampleState && !has_fragment_output_state) {
+      fix_desc->erase.multisample_state = true;
+   }
 
-      /* Ignore pScissors?
-       *    VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04131
-       *
-       * If scissorCount is 0, then venus encoder will ignore pScissors and we
-       * do not need to erase it.
-       */
-      if (!erase.viewport_state && info->pViewportState &&
-          info->pViewportState->pScissors &&
-          info->pViewportState->scissorCount) {
-         const bool has_dynamic_scissor =
-            has_dynamic_state.scissor || has_dynamic_state.scissor_with_count;
-         if (has_dynamic_scissor) {
-            erase.viewport_state_scissors = true;
-         }
+   /* Ignore pDepthStencilState? */
+   if (info->pDepthStencilState) {
+      const bool has_static_attachment =
+         subpass &&
+         (subpass->attachment_aspects &
+          (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
+
+      /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 */
+      bool require_state = has_fragment_shader_state && has_static_attachment;
+
+      if (!require_state) {
+         const bool has_dynamic_attachment =
+            has_dynamic_rendering &&
+            (rendering_info->depthAttachmentFormat != VK_FORMAT_UNDEFINED ||
+             rendering_info->stencilAttachmentFormat != VK_FORMAT_UNDEFINED);
+
+         /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 */
+         require_state = has_fragment_shader_state &&
+                         has_fragment_output_state && has_dynamic_attachment;
       }
 
-      /* Ignore pMultisampleState?
-       *    VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751
-       */
-      if (info->pMultisampleState && !has_fragment_output_state) {
-         erase.multisample_state = true;
-      }
+      fix_desc->erase.depth_stencil_state = !require_state;
+   }
 
-      /* Ignore pDepthStencilState? */
-      if (info->pDepthStencilState) {
-         const bool has_static_attachment =
-            subpass &&
-            (subpass->attachment_aspects &
-             (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
-
-         /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 */
-         bool require_state =
-            has_fragment_shader_state && has_static_attachment;
-
-         if (!require_state) {
-            const bool has_dynamic_attachment =
-               has_dynamic_rendering &&
-               (rendering_info->depthAttachmentFormat !=
-                   VK_FORMAT_UNDEFINED ||
-                rendering_info->stencilAttachmentFormat !=
-                   VK_FORMAT_UNDEFINED);
-
-            /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 */
-            require_state = has_fragment_shader_state &&
-                            has_fragment_output_state &&
-                            has_dynamic_attachment;
-         }
+   /* Ignore pColorBlendState? */
+   if (info->pColorBlendState) {
+      const bool has_static_attachment =
+         subpass && (subpass->attachment_aspects & VK_IMAGE_ASPECT_COLOR_BIT);
 
-         erase.depth_stencil_state = !require_state;
-      }
+      /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 */
+      bool require_state = has_fragment_output_state && has_static_attachment;
 
-      /* Ignore pColorBlendState? */
-      if (info->pColorBlendState) {
-         const bool has_static_attachment =
-            subpass &&
-            (subpass->attachment_aspects & VK_IMAGE_ASPECT_COLOR_BIT);
+      if (!require_state) {
+         const bool has_dynamic_attachment =
+            has_dynamic_rendering && rendering_info->colorAttachmentCount;
 
-         /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 */
-         bool require_state =
-            has_fragment_output_state && has_static_attachment;
+         /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 */
+         require_state = has_fragment_output_state && has_dynamic_attachment;
+      }
 
-         if (!require_state) {
-            const bool has_dynamic_attachment =
-               has_dynamic_rendering && rendering_info->colorAttachmentCount;
+      fix_desc->erase.color_blend_state = !require_state;
+   }
 
-            /* VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 */
-            require_state =
-               has_fragment_output_state && has_dynamic_attachment;
-         }
+   /* Ignore pVertexInputState?
+    * The Vulkan spec (1.3.264) says:
+    * VK_DYNAMIC_STATE_VERTEX_INPUT_EXT specifies that the
+    * pVertexInputState state will be ignored and must be set dynamically
+    * with vkCmdSetVertexInputEXT before any drawing commands
+    */
+   if (info->pVertexInputState && has_dynamic_state.vertex_input) {
+      fix_desc->erase.vertex_input_state = true;
+   }
 
-         erase.color_blend_state = !require_state;
-      }
+   /* Ignore basePipelineHandle?
+    *    VUID-VkGraphicsPipelineCreateInfo-flags-00722
+    *    VUID-VkGraphicsPipelineCreateInfo-flags-00724
+    *    VUID-VkGraphicsPipelineCreateInfo-flags-00725
+    */
+   if (info->basePipelineHandle != VK_NULL_HANDLE &&
+       !(info->flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)) {
+      fix_desc->erase.base_pipeline_handle = true;
+   }
+}
 
-      /* Ignore pVertexInputState?
-       * The Vulkan spec (1.3.264) says:
-       * VK_DYNAMIC_STATE_VERTEX_INPUT_EXT specifies that the
-       * pVertexInputState state will be ignored and must be set dynamically
-       * with vkCmdSetVertexInputEXT before any drawing commands
-       */
-      if (info->pVertexInputState && has_dynamic_state.vertex_input)
-         erase.vertex_input_state = true;
+static const VkGraphicsPipelineCreateInfo *
+vn_fix_graphics_pipeline_create_infos(
+   struct vn_device *dev,
+   uint32_t info_count,
+   const VkGraphicsPipelineCreateInfo *infos,
+   const struct vn_graphics_pipeline_fix_desc fix_descs[info_count],
+   struct vn_graphics_pipeline_fix_tmp **out_fix_tmp,
+   const VkAllocationCallbacks *alloc)
+{
+   VN_TRACE_SCOPE("apply_fixes");
 
-      /* Ignore basePipelineHandle?
-       *    VUID-VkGraphicsPipelineCreateInfo-flags-00722
-       *    VUID-VkGraphicsPipelineCreateInfo-flags-00724
-       *    VUID-VkGraphicsPipelineCreateInfo-flags-00725
-       */
-      if (info->basePipelineHandle != VK_NULL_HANDLE &&
-          !(info->flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)) {
-         erase.base_pipeline_handle = true;
-      }
+   *out_fix_tmp = NULL;
 
-      if (erase.mask != 0)
-         continue;
+   uint32_t erase_mask = 0;
+   for (uint32_t i = 0; i < info_count; i++) {
+      erase_mask |= fix_descs[i].erase.mask;
+   }
 
-      if (!fix_tmp) {
-         fix_tmp = vn_graphics_pipeline_fix_tmp_alloc(alloc, info_count);
+   if (!erase_mask) {
+      /* No fix is needed. */
+      return infos;
+   }
 
-         if (!fix_tmp)
-            return NULL;
+   struct vn_graphics_pipeline_fix_tmp *fix_tmp =
+      vn_graphics_pipeline_fix_tmp_alloc(alloc, info_count);
+   if (!fix_tmp)
+      return NULL;
+
+   memcpy(fix_tmp->infos, infos, info_count * sizeof(infos[0]));
 
-         memcpy(fix_tmp->infos, infos, info_count * sizeof(infos[0]));
+   for (uint32_t i = 0; i < info_count; i++) {
+      if (!fix_descs[i].erase.mask) {
+         /* No fix is needed for this VkGraphicsPipelineCreateInfo chain. */
+         continue;
       }
 
-      if (erase.tessellation_state)
+      if (fix_descs[i].erase.tessellation_state)
          fix_tmp->infos[i].pTessellationState = NULL;
 
-      if (erase.viewport_state)
+      if (fix_descs[i].erase.viewport_state)
          fix_tmp->infos[i].pViewportState = NULL;
 
       if (fix_tmp->infos[i].pViewportState) {
-         if (erase.viewport_state_viewports ||
-             erase.viewport_state_scissors) {
-            fix_tmp->viewport_state_infos[i] = *info->pViewportState;
+         if (fix_descs[i].erase.viewport_state_viewports ||
+             fix_descs[i].erase.viewport_state_scissors) {
+            fix_tmp->viewport_state_infos[i] = *infos[i].pViewportState;
             fix_tmp->infos[i].pViewportState =
                &fix_tmp->viewport_state_infos[i];
          }
 
-         if (erase.viewport_state_viewports)
+         if (fix_descs[i].erase.viewport_state_viewports)
             fix_tmp->viewport_state_infos[i].pViewports = NULL;
 
-         if (erase.viewport_state_scissors)
+         if (fix_descs[i].erase.viewport_state_scissors)
             fix_tmp->viewport_state_infos[i].pScissors = NULL;
       }
 
-      if (erase.multisample_state)
+      if (fix_descs[i].erase.multisample_state)
          fix_tmp->infos[i].pMultisampleState = NULL;
 
-      if (erase.depth_stencil_state)
+      if (fix_descs[i].erase.depth_stencil_state)
          fix_tmp->infos[i].pDepthStencilState = NULL;
 
-      if (erase.color_blend_state)
+      if (fix_descs[i].erase.color_blend_state)
          fix_tmp->infos[i].pColorBlendState = NULL;
 
-      if (erase.vertex_input_state)
+      if (fix_descs[i].erase.vertex_input_state)
          fix_tmp->infos[i].pVertexInputState = NULL;
 
-      if (erase.base_pipeline_handle)
+      if (fix_descs[i].erase.base_pipeline_handle)
          fix_tmp->infos[i].basePipelineHandle = VK_NULL_HANDLE;
    }
 
-   if (!fix_tmp)
-      return infos;
-
    *out_fix_tmp = fix_tmp;
    return fix_tmp->infos;
 }
@@ -772,20 +784,33 @@ vn_CreateGraphicsPipelines(VkDevice device,
    struct vn_device *dev = vn_device_from_handle(device);
    const VkAllocationCallbacks *alloc =
       pAllocator ? pAllocator : &dev->base.base.alloc;
-   struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
    bool want_sync = false;
    VkResult result;
 
+   STACK_ARRAY(struct vn_graphics_pipeline_fix_desc, fix_descs,
+               createInfoCount);
+   struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
+
    memset(pPipelines, 0, sizeof(*pPipelines) * createInfoCount);
 
+   VN_TRACE_BEGIN("find_fixes");
+   for (uint32_t i = 0; i < createInfoCount; i++) {
+      vn_find_graphics_pipeline_create_info_fixes(&pCreateInfos[i],
+                                                  &fix_descs[i]);
+   }
+   VN_TRACE_END();
+
    pCreateInfos = vn_fix_graphics_pipeline_create_infos(
-      dev, createInfoCount, pCreateInfos, alloc, &fix_tmp);
-   if (!pCreateInfos)
+      dev, createInfoCount, pCreateInfos, fix_descs, &fix_tmp, alloc);
+   if (!pCreateInfos) {
+      STACK_ARRAY_FINISH(fix_descs);
       return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+   }
 
    if (!vn_create_pipeline_handles(dev, VN_PIPELINE_TYPE_GRAPHICS,
                                    createInfoCount, pPipelines, alloc)) {
       vk_free(alloc, fix_tmp);
+      STACK_ARRAY_FINISH(fix_descs);
       return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
    }
 
@@ -823,6 +848,7 @@ vn_CreateGraphicsPipelines(VkDevice device,
    }
 
    vk_free(alloc, fix_tmp);
+   STACK_ARRAY_FINISH(fix_descs);
    return vn_result(dev->instance, result);
 }