tu: Rewrite tessellation modes handling
authorConnor Abbott <cwabbott0@gmail.com>
Wed, 6 Sep 2023 15:24:00 +0000 (17:24 +0200)
committerMarge Bot <emma+marge@anholt.net>
Mon, 25 Sep 2023 19:03:56 +0000 (19:03 +0000)
Before this, we combined the modes after compiling the shaders when
constructing the pipeline. But that's a bit awkward with shader objects,
where there is no good place to put state derived from TCS and TES but
not the other stages. However, shader objects leaves us with an out:
when compiling separately, the modes must be on one of the shaders. So
instead we just copy the modes earlier, in the NIR shaders, and then get
them from the appropriate shader later. That way there is no extra
overhead when fast-linking, as there currently is, and we don't need to
create an awkward separate object just for this.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25276>

src/freedreno/vulkan/tu_cmd_buffer.cc
src/freedreno/vulkan/tu_cmd_buffer.h
src/freedreno/vulkan/tu_pipeline.cc
src/freedreno/vulkan/tu_pipeline.h
src/freedreno/vulkan/tu_shader.cc
src/freedreno/vulkan/tu_shader.h

index bb8006d..fb9b42d 100644 (file)
@@ -758,8 +758,9 @@ tu6_update_msaa_disable(struct tu_cmd_buffer *cmd)
       topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP ||
       topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY ||
       (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
-       cmd->state.pipeline &&
-       cmd->state.pipeline->base.tess.patch_type == IR3_TESS_ISOLINES);
+       cmd->state.shaders[MESA_SHADER_TESS_EVAL] &&
+       cmd->state.shaders[MESA_SHADER_TESS_EVAL]->variant &&
+       cmd->state.shaders[MESA_SHADER_TESS_EVAL]->variant->key.tessellation == IR3_TESS_ISOLINES);
    bool msaa_disable = is_line &&
       cmd->vk.dynamic_graphics_state.rs.line.mode == VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT;
 
@@ -2962,7 +2963,25 @@ tu_bind_tcs(struct tu_cmd_buffer *cmd, struct tu_shader *tcs)
 static void
 tu_bind_tes(struct tu_cmd_buffer *cmd, struct tu_shader *tes)
 {
-   cmd->state.shaders[MESA_SHADER_TESS_EVAL] = tes;
+   if (cmd->state.shaders[MESA_SHADER_TESS_EVAL] != tes) {
+      cmd->state.shaders[MESA_SHADER_TESS_EVAL] = tes;
+      cmd->state.dirty |= TU_CMD_DIRTY_TES;
+
+      if (!cmd->state.tess_params.valid ||
+          cmd->state.tess_params.output_upper_left !=
+          tes->tes.tess_output_upper_left ||
+          cmd->state.tess_params.output_lower_left !=
+          tes->tes.tess_output_lower_left ||
+          cmd->state.tess_params.spacing != tes->tes.tess_spacing) {
+         cmd->state.tess_params.output_upper_left =
+            tes->tes.tess_output_upper_left;
+         cmd->state.tess_params.output_lower_left =
+            tes->tes.tess_output_lower_left;
+         cmd->state.tess_params.spacing = tes->tes.tess_spacing;
+         cmd->state.tess_params.valid = true;
+         cmd->state.dirty |= TU_CMD_DIRTY_TESS_PARAMS;
+      }
+   }
 }
 
 static void
@@ -3084,23 +3103,6 @@ tu_CmdBindPipeline(VkCommandBuffer commandBuffer,
       cmd->state.per_view_viewport = pipeline->program.per_view_viewport;
       cmd->state.dirty |= TU_CMD_DIRTY_PER_VIEW_VIEWPORT;
    }
-
-   if (pipeline->active_stages & MESA_SHADER_TESS_CTRL) {
-      if (!cmd->state.tess_params.valid ||
-          cmd->state.tess_params.output_upper_left !=
-          pipeline->program.tess_output_upper_left ||
-          cmd->state.tess_params.output_lower_left !=
-          pipeline->program.tess_output_lower_left ||
-          cmd->state.tess_params.spacing != pipeline->program.tess_spacing) {
-         cmd->state.tess_params.output_upper_left =
-            pipeline->program.tess_output_upper_left;
-         cmd->state.tess_params.output_lower_left =
-            pipeline->program.tess_output_lower_left;
-         cmd->state.tess_params.spacing = pipeline->program.tess_spacing;
-         cmd->state.tess_params.valid = true;
-         cmd->state.dirty |= TU_CMD_DIRTY_TESS_PARAMS;
-      }
-   }
 }
 
 static void
@@ -4738,7 +4740,7 @@ tu6_draw_common(struct tu_cmd_buffer *cmd,
                    MESA_VK_DYNAMIC_IA_PRIMITIVE_TOPOLOGY) ||
        BITSET_TEST(cmd->vk.dynamic_graphics_state.dirty,
                    MESA_VK_DYNAMIC_RS_LINE_MODE) ||
-       (cmd->state.dirty & TU_CMD_DIRTY_PIPELINE)) {
+       (cmd->state.dirty & TU_CMD_DIRTY_TES)) {
       tu6_update_msaa_disable(cmd);
    }
 
@@ -4859,22 +4861,22 @@ tu_draw_initiator(struct tu_cmd_buffer *cmd, enum pc_di_src_sel src_sel)
    if (pipeline->active_stages & VK_SHADER_STAGE_GEOMETRY_BIT)
       initiator |= CP_DRAW_INDX_OFFSET_0_GS_ENABLE;
 
-   switch (pipeline->tess.patch_type) {
-   case IR3_TESS_TRIANGLES:
-      initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_TRIANGLES) |
-                   CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
-      break;
-   case IR3_TESS_ISOLINES:
-      initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_ISOLINES) |
-                   CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
-      break;
-   case IR3_TESS_NONE:
-      initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_QUADS);
-      break;
-   case IR3_TESS_QUADS:
-      initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_QUADS) |
-                   CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
-      break;
+   const struct tu_shader *tes = cmd->state.shaders[MESA_SHADER_TESS_EVAL];
+   if (tes->variant) {
+      switch (tes->variant->key.tessellation) {
+      case IR3_TESS_TRIANGLES:
+         initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_TRIANGLES) |
+                      CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
+         break;
+      case IR3_TESS_ISOLINES:
+         initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_ISOLINES) |
+                      CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
+         break;
+      case IR3_TESS_QUADS:
+         initiator |= CP_DRAW_INDX_OFFSET_0_PATCH_TYPE(TESS_QUADS) |
+                      CP_DRAW_INDX_OFFSET_0_TESS_ENABLE;
+         break;
+      }
    }
    return initiator;
 }
index cd82e6d..19090c4 100644 (file)
@@ -69,9 +69,10 @@ enum tu_cmd_dirty_bits
    TU_CMD_DIRTY_SUBPASS = BIT(7),
    TU_CMD_DIRTY_FDM = BIT(8),
    TU_CMD_DIRTY_PER_VIEW_VIEWPORT = BIT(9),
-   TU_CMD_DIRTY_PIPELINE = BIT(10),
+   TU_CMD_DIRTY_TES = BIT(10),
+   TU_CMD_DIRTY_PIPELINE = BIT(11),
    /* all draw states were disabled and need to be re-enabled: */
-   TU_CMD_DIRTY_DRAW_STATE = BIT(11)
+   TU_CMD_DIRTY_DRAW_STATE = BIT(12)
 };
 
 /* There are only three cache domains we have to care about: the CCU, or
index 51f0f42..2d69cb7 100644 (file)
@@ -977,6 +977,7 @@ static unsigned
 tu6_patch_control_points_size(struct tu_device *dev,
                               const struct tu_shader *vs,
                               const struct tu_shader *tcs,
+                              const struct tu_shader *tes,
                               const struct tu_pipeline *pipeline,
                               uint32_t patch_control_points)
 {
@@ -991,6 +992,7 @@ void
 tu6_emit_patch_control_points(struct tu_cs *cs,
                               const struct tu_shader *vs,
                               const struct tu_shader *tcs,
+                              const struct tu_shader *tes,
                               const struct tu_pipeline *pipeline,
                               uint32_t patch_control_points)
 {
@@ -1060,7 +1062,7 @@ tu6_emit_patch_control_points(struct tu_cs *cs,
    tu_cs_emit(cs, wave_input_size);
 
    /* maximum number of patches that can fit in tess factor/param buffers */
-   uint32_t subdraw_size = MIN2(TU_TESS_FACTOR_SIZE / ir3_tess_factor_stride(pipeline->tess.patch_type),
+   uint32_t subdraw_size = MIN2(TU_TESS_FACTOR_SIZE / ir3_tess_factor_stride(tes->variant->key.tessellation),
                         TU_TESS_PARAM_SIZE / (tcs->variant->output_size * 4));
    /* convert from # of patches to draw count */
    subdraw_size *= patch_control_points;
@@ -1927,19 +1929,39 @@ tu_pipeline_builder_compile_shaders(struct tu_pipeline_builder *builder,
       nir_shaders = tu_nir_cache_insert(builder->cache, nir_shaders);
    }
 
+   /* With pipelines, tessellation modes can be set on either shader, for
+    * compatibility with HLSL and GLSL, and the driver is supposed to merge
+    * them. Shader objects requires modes to be set on at least the TES except
+    * for OutputVertices which has to be set at least on the TCS. Make sure
+    * all modes are set on the TES when compiling together multiple shaders,
+    * and then from this point on we will use the modes in the TES (and output
+    * vertices on the TCS).
+    */
+   if (nir[MESA_SHADER_TESS_EVAL]) {
+      nir_shader *tcs = nir[MESA_SHADER_TESS_CTRL];
+      nir_shader *tes = nir[MESA_SHADER_TESS_EVAL];
+
+      if (tes->info.tess._primitive_mode == TESS_PRIMITIVE_UNSPECIFIED)
+         tes->info.tess._primitive_mode = tcs->info.tess._primitive_mode;
+
+      tes->info.tess.point_mode |= tcs->info.tess.point_mode;
+      tes->info.tess.ccw |= tcs->info.tess.ccw;
+
+      if (tes->info.tess.spacing == TESS_SPACING_UNSPECIFIED) {
+         tes->info.tess.spacing = tcs->info.tess.spacing;
+      }
+
+      if (tcs->info.tess.tcs_vertices_out == 0)
+         tcs->info.tess.tcs_vertices_out = tes->info.tess.tcs_vertices_out;
+
+      ir3_key.tessellation = tu6_get_tessmode(tes);
+   }
+
    for (gl_shader_stage stage = MESA_SHADER_VERTEX; stage < ARRAY_SIZE(nir);
         stage = (gl_shader_stage) (stage + 1)) {
       if (!nir[stage])
          continue;
 
-      /* In SPIR-V generated from GLSL, the primitive mode is specified in the
-       * tessellation evaluation shader, but in SPIR-V generated from HLSL,
-       * the mode is specified in the tessellation control shader. */
-      if ((stage == MESA_SHADER_TESS_EVAL || stage == MESA_SHADER_TESS_CTRL) &&
-          ir3_key.tessellation == IR3_TESS_NONE) {
-         ir3_key.tessellation = tu6_get_tessmode(nir[stage]);
-      }
-
       if (stage > MESA_SHADER_TESS_CTRL) {
          if (stage == MESA_SHADER_FRAGMENT) {
             ir3_key.tcs_store_primid = ir3_key.tcs_store_primid ||
@@ -2050,12 +2072,6 @@ done:
       }
    }
 
-   if (shaders[MESA_SHADER_TESS_CTRL] &&
-       shaders[MESA_SHADER_TESS_CTRL]->variant) {
-      pipeline->tess.patch_type =
-         shaders[MESA_SHADER_TESS_CTRL]->variant->key.tessellation;
-   }
-
    if (shaders[MESA_SHADER_VERTEX]) {
       const struct ir3_shader_variant *vs =
          shaders[MESA_SHADER_VERTEX]->variant;
@@ -2153,11 +2169,6 @@ tu_pipeline_builder_parse_libraries(struct tu_pipeline_builder *builder,
       }
 
       if (library->state &
-          VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) {
-         pipeline->tess = library->base.tess;
-      }
-
-      if (library->state &
           VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) {
          pipeline->ds = library->base.ds;
          pipeline->lrz.lrz_status |= library->base.lrz.lrz_status;
@@ -2346,43 +2357,6 @@ tu_pipeline_builder_parse_shader_stages(struct tu_pipeline_builder *builder,
       uint32_t hs_base = hs_const->offsets.primitive_param;
       pipeline->program.hs_param_dwords =
          MIN2((hs_constlen - hs_base) * 4, 8);
-
-      /* In SPIR-V generated from GLSL, the tessellation primitive params are
-       * are specified in the tess eval shader, but in SPIR-V generated from
-       * HLSL, they are specified in the tess control shader. */
-      const struct ir3_shader_variant *tess =
-         ds->tess.spacing == TESS_SPACING_UNSPECIFIED ? hs : ds;
-      if (tess->tess.point_mode) {
-         pipeline->program.tess_output_lower_left =
-            pipeline->program.tess_output_upper_left = TESS_POINTS;
-      } else if (tess->tess.primitive_mode == TESS_PRIMITIVE_ISOLINES) {
-         pipeline->program.tess_output_lower_left =
-            pipeline->program.tess_output_upper_left = TESS_LINES;
-      } else if (tess->tess.ccw) {
-         /* Tessellation orientation in HW is specified with a lower-left
-          * origin, we need to swap them if the origin is upper-left.
-          */
-         pipeline->program.tess_output_lower_left = TESS_CCW_TRIS;
-         pipeline->program.tess_output_upper_left = TESS_CW_TRIS;
-      } else {
-         pipeline->program.tess_output_lower_left = TESS_CW_TRIS;
-         pipeline->program.tess_output_upper_left = TESS_CCW_TRIS;
-      }
-
-      switch (tess->tess.spacing) {
-      case TESS_SPACING_EQUAL:
-         pipeline->program.tess_spacing = TESS_EQUAL;
-         break;
-      case TESS_SPACING_FRACTIONAL_ODD:
-         pipeline->program.tess_spacing = TESS_FRACTIONAL_ODD;
-         break;
-      case TESS_SPACING_FRACTIONAL_EVEN:
-         pipeline->program.tess_spacing = TESS_FRACTIONAL_EVEN;
-         break;
-      case TESS_SPACING_UNSPECIFIED:
-      default:
-         unreachable("invalid tess spacing");
-      }
    }
 
    const struct ir3_shader_variant *last_shader;
@@ -3496,6 +3470,7 @@ tu_pipeline_builder_emit_state(struct tu_pipeline_builder *builder,
                    pipeline_contains_all_shader_state(pipeline),
                    pipeline->shaders[MESA_SHADER_VERTEX],
                    pipeline->shaders[MESA_SHADER_TESS_CTRL],
+                   pipeline->shaders[MESA_SHADER_TESS_EVAL],
                    pipeline,
                    builder->graphics_state.ts->patch_control_points);
 #undef DRAW_STATE
@@ -3678,6 +3653,7 @@ tu_emit_draw_state(struct tu_cmd_buffer *cmd)
                    cmd->state.dirty & TU_CMD_DIRTY_PIPELINE,
                    cmd->state.shaders[MESA_SHADER_VERTEX],
                    cmd->state.shaders[MESA_SHADER_TESS_CTRL],
+                   cmd->state.shaders[MESA_SHADER_TESS_EVAL],
                    &cmd->state.pipeline->base,
                    cmd->vk.dynamic_graphics_state.ts.patch_control_points);
 #undef DRAW_STATE
index 097125e..7d6b2d4 100644 (file)
@@ -115,10 +115,6 @@ struct tu_pipeline
    struct tu_draw_state dynamic_state[TU_DYNAMIC_STATE_COUNT];
 
    struct {
-      unsigned patch_type;
-   } tess;
-
-   struct {
       bool raster_order_attachment_access;
    } ds;
 
@@ -158,9 +154,6 @@ struct tu_pipeline
       struct tu_program_descriptor_linkage link[MESA_SHADER_STAGES];
 
       bool per_view_viewport;
-
-      enum a6xx_tess_output tess_output_upper_left, tess_output_lower_left;
-      enum a6xx_tess_spacing tess_spacing;
    } program;
 
    struct tu_lrz_pipeline lrz;
index dc62737..b24b17d 100644 (file)
@@ -2097,6 +2097,9 @@ tu_shader_serialize(struct vk_pipeline_cache_object *object,
    }
 
    switch (shader->variant->type) {
+   case MESA_SHADER_TESS_EVAL:
+      blob_write_bytes(blob, &shader->tes, sizeof(shader->tes));
+      break;
    case MESA_SHADER_FRAGMENT:
       blob_write_bytes(blob, &shader->fs, sizeof(shader->fs));
       break;
@@ -2132,6 +2135,9 @@ tu_shader_deserialize(struct vk_pipeline_cache *cache,
       shader->safe_const_variant = ir3_retrieve_variant(blob, dev->compiler, NULL);
 
    switch (shader->variant->type) {
+   case MESA_SHADER_TESS_EVAL:
+      blob_copy_bytes(blob, &shader->tes, sizeof(shader->tes));
+      break;
    case MESA_SHADER_FRAGMENT:
       blob_copy_bytes(blob, &shader->fs, sizeof(shader->fs));
       break;
@@ -2297,6 +2303,42 @@ tu_shader_create(struct tu_device *dev,
    shader->view_mask = key->multiview_mask;
 
    switch (shader->variant->type) {
+   case MESA_SHADER_TESS_EVAL: {
+      const struct ir3_shader_variant *tes = shader->variant;
+      if (tes->tess.point_mode) {
+         shader->tes.tess_output_lower_left =
+            shader->tes.tess_output_upper_left = TESS_POINTS;
+      } else if (tes->tess.primitive_mode == TESS_PRIMITIVE_ISOLINES) {
+         shader->tes.tess_output_lower_left =
+            shader->tes.tess_output_upper_left = TESS_LINES;
+      } else if (tes->tess.ccw) {
+         /* Tessellation orientation in HW is specified with a lower-left
+          * origin, we need to swap them if the origin is upper-left.
+          */
+         shader->tes.tess_output_lower_left = TESS_CCW_TRIS;
+         shader->tes.tess_output_upper_left = TESS_CW_TRIS;
+      } else {
+         shader->tes.tess_output_lower_left = TESS_CW_TRIS;
+         shader->tes.tess_output_upper_left = TESS_CCW_TRIS;
+      }
+
+      switch (tes->tess.spacing) {
+      case TESS_SPACING_EQUAL:
+         shader->tes.tess_spacing = TESS_EQUAL;
+         break;
+      case TESS_SPACING_FRACTIONAL_ODD:
+         shader->tes.tess_spacing = TESS_FRACTIONAL_ODD;
+         break;
+      case TESS_SPACING_FRACTIONAL_EVEN:
+         shader->tes.tess_spacing = TESS_FRACTIONAL_EVEN;
+         break;
+      case TESS_SPACING_UNSPECIFIED:
+      default:
+         unreachable("invalid tess spacing");
+      }
+
+      break;
+   }
    case MESA_SHADER_FRAGMENT: {
       const struct ir3_shader_variant *fs = shader->variant;
       shader->fs.per_samp = fs->per_samp || ir3_key->sample_shading;
index 15e5e9d..b086c27 100644 (file)
@@ -65,6 +65,12 @@ struct tu_shader
 
    union {
       struct {
+         unsigned patch_type;
+         enum a6xx_tess_output tess_output_upper_left, tess_output_lower_left;
+         enum a6xx_tess_spacing tess_spacing;
+      } tes;
+
+      struct {
          bool per_samp;
          bool has_fdm;