turnip: emit base instance vs driver param
[mesa.git] / src / freedreno / vulkan / tu_pipeline.c
index 2acf55564341e6fae18758605615aa4c6f1ef704..0c84c5672101f63db9a35d300a77f48cb9d5898c 100644 (file)
@@ -43,6 +43,7 @@ struct tu_pipeline_builder
 {
    struct tu_device *device;
    struct tu_pipeline_cache *cache;
+   struct tu_pipeline_layout *layout;
    const VkAllocationCallbacks *alloc;
    const VkGraphicsPipelineCreateInfo *create_info;
 
@@ -177,7 +178,7 @@ tu6_primtype(VkPrimitiveTopology topology)
    case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
       return DI_PT_TRILIST;
    case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
-      return DI_PT_TRILIST;
+      return DI_PT_TRISTRIP;
    case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
       return DI_PT_TRIFAN;
    case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
@@ -358,18 +359,19 @@ tu6_blend_op(VkBlendOp op)
 }
 
 static void
-tu6_emit_vs_config(struct tu_cs *cs, const struct ir3_shader_variant *vs)
+tu6_emit_vs_config(struct tu_cs *cs, struct tu_shader *shader,
+                   const struct ir3_shader_variant *vs)
 {
    uint32_t sp_vs_ctrl =
       A6XX_SP_VS_CTRL_REG0_THREADSIZE(FOUR_QUADS) |
       A6XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT(vs->info.max_reg + 1) |
       A6XX_SP_VS_CTRL_REG0_MERGEDREGS |
       A6XX_SP_VS_CTRL_REG0_BRANCHSTACK(vs->branchstack);
-   if (vs->num_samp)
+   if (vs->need_pixlod)
       sp_vs_ctrl |= A6XX_SP_VS_CTRL_REG0_PIXLODENABLE;
 
-   uint32_t sp_vs_config = A6XX_SP_VS_CONFIG_NTEX(vs->num_samp) |
-                           A6XX_SP_VS_CONFIG_NSAMP(vs->num_samp);
+   uint32_t sp_vs_config = A6XX_SP_VS_CONFIG_NTEX(shader->texture_map.num_desc) |
+                           A6XX_SP_VS_CONFIG_NSAMP(shader->sampler_map.num_desc);
    if (vs->instrlen)
       sp_vs_config |= A6XX_SP_VS_CONFIG_ENABLED;
 
@@ -381,11 +383,13 @@ tu6_emit_vs_config(struct tu_cs *cs, const struct ir3_shader_variant *vs)
    tu_cs_emit(cs, vs->instrlen);
 
    tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_VS_CNTL, 1);
-   tu_cs_emit(cs, A6XX_HLSQ_VS_CNTL_CONSTLEN(align(vs->constlen, 4)) | 0x100);
+   tu_cs_emit(cs, A6XX_HLSQ_VS_CNTL_CONSTLEN(align(vs->constlen, 4)) |
+                  A6XX_HLSQ_VS_CNTL_ENABLED);
 }
 
 static void
-tu6_emit_hs_config(struct tu_cs *cs, const struct ir3_shader_variant *hs)
+tu6_emit_hs_config(struct tu_cs *cs, struct tu_shader *shader,
+                   const struct ir3_shader_variant *hs)
 {
    uint32_t sp_hs_config = 0;
    if (hs->instrlen)
@@ -403,7 +407,8 @@ tu6_emit_hs_config(struct tu_cs *cs, const struct ir3_shader_variant *hs)
 }
 
 static void
-tu6_emit_ds_config(struct tu_cs *cs, const struct ir3_shader_variant *ds)
+tu6_emit_ds_config(struct tu_cs *cs, struct tu_shader *shader,
+                   const struct ir3_shader_variant *ds)
 {
    uint32_t sp_ds_config = 0;
    if (ds->instrlen)
@@ -418,7 +423,8 @@ tu6_emit_ds_config(struct tu_cs *cs, const struct ir3_shader_variant *ds)
 }
 
 static void
-tu6_emit_gs_config(struct tu_cs *cs, const struct ir3_shader_variant *gs)
+tu6_emit_gs_config(struct tu_cs *cs, struct tu_shader *shader,
+                   const struct ir3_shader_variant *gs)
 {
    uint32_t sp_gs_config = 0;
    if (gs->instrlen)
@@ -436,26 +442,25 @@ tu6_emit_gs_config(struct tu_cs *cs, const struct ir3_shader_variant *gs)
 }
 
 static void
-tu6_emit_fs_config(struct tu_cs *cs, const struct ir3_shader_variant *fs)
+tu6_emit_fs_config(struct tu_cs *cs, struct tu_shader *shader,
+                   const struct ir3_shader_variant *fs)
 {
    uint32_t sp_fs_ctrl =
       A6XX_SP_FS_CTRL_REG0_THREADSIZE(FOUR_QUADS) | 0x1000000 |
       A6XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT(fs->info.max_reg + 1) |
       A6XX_SP_FS_CTRL_REG0_MERGEDREGS |
       A6XX_SP_FS_CTRL_REG0_BRANCHSTACK(fs->branchstack);
-   if (fs->total_in > 0 || fs->frag_coord)
+   if (fs->total_in > 0)
       sp_fs_ctrl |= A6XX_SP_FS_CTRL_REG0_VARYING;
-   if (fs->num_samp > 0)
+   if (fs->need_pixlod)
       sp_fs_ctrl |= A6XX_SP_FS_CTRL_REG0_PIXLODENABLE;
 
-   uint32_t sp_fs_config = A6XX_SP_FS_CONFIG_NTEX(fs->num_samp) |
-                           A6XX_SP_FS_CONFIG_NSAMP(fs->num_samp);
+   uint32_t sp_fs_config = A6XX_SP_FS_CONFIG_NTEX(shader->texture_map.num_desc) |
+                           A6XX_SP_FS_CONFIG_NSAMP(shader->sampler_map.num_desc) |
+                           A6XX_SP_FS_CONFIG_NIBO(fs->image_mapping.num_ibo);
    if (fs->instrlen)
       sp_fs_config |= A6XX_SP_FS_CONFIG_ENABLED;
 
-   tu_cs_emit_pkt4(cs, REG_A6XX_SP_UNKNOWN_A99E, 1);
-   tu_cs_emit(cs, 0x7fc0);
-
    tu_cs_emit_pkt4(cs, REG_A6XX_SP_UNKNOWN_A9A8, 1);
    tu_cs_emit(cs, 0);
 
@@ -470,7 +475,57 @@ tu6_emit_fs_config(struct tu_cs *cs, const struct ir3_shader_variant *fs)
    tu_cs_emit(cs, fs->instrlen);
 
    tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_FS_CNTL, 1);
-   tu_cs_emit(cs, A6XX_HLSQ_FS_CNTL_CONSTLEN(align(fs->constlen, 4)) | 0x100);
+   tu_cs_emit(cs, A6XX_HLSQ_FS_CNTL_CONSTLEN(align(fs->constlen, 4)) |
+                  A6XX_HLSQ_FS_CNTL_ENABLED);
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_IBO_COUNT, 1);
+   tu_cs_emit(cs, fs->image_mapping.num_ibo);
+}
+
+static void
+tu6_emit_cs_config(struct tu_cs *cs, const struct tu_shader *shader,
+                   const struct ir3_shader_variant *v)
+{
+   tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_UPDATE_CNTL, 1);
+   tu_cs_emit(cs, 0xff);
+
+   unsigned constlen = align(v->constlen, 4);
+   tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CS_CNTL, 1);
+   tu_cs_emit(cs, A6XX_HLSQ_CS_CNTL_CONSTLEN(constlen) |
+              A6XX_HLSQ_CS_CNTL_ENABLED);
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_CONFIG, 2);
+   tu_cs_emit(cs, A6XX_SP_CS_CONFIG_ENABLED |
+              A6XX_SP_CS_CONFIG_NIBO(v->image_mapping.num_ibo) |
+              A6XX_SP_CS_CONFIG_NTEX(shader->texture_map.num_desc) |
+              A6XX_SP_CS_CONFIG_NSAMP(shader->sampler_map.num_desc));
+   tu_cs_emit(cs, v->instrlen);
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_CTRL_REG0, 1);
+   tu_cs_emit(cs, A6XX_SP_CS_CTRL_REG0_THREADSIZE(FOUR_QUADS) |
+              A6XX_SP_CS_CTRL_REG0_FULLREGFOOTPRINT(v->info.max_reg + 1) |
+              A6XX_SP_CS_CTRL_REG0_MERGEDREGS |
+              A6XX_SP_CS_CTRL_REG0_BRANCHSTACK(v->branchstack) |
+              COND(v->need_pixlod, A6XX_SP_CS_CTRL_REG0_PIXLODENABLE));
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_UNKNOWN_A9B1, 1);
+   tu_cs_emit(cs, 0x41);
+
+   uint32_t local_invocation_id =
+      ir3_find_sysval_regid(v, SYSTEM_VALUE_LOCAL_INVOCATION_ID);
+   uint32_t work_group_id =
+      ir3_find_sysval_regid(v, SYSTEM_VALUE_WORK_GROUP_ID);
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CS_CNTL_0, 2);
+   tu_cs_emit(cs,
+              A6XX_HLSQ_CS_CNTL_0_WGIDCONSTID(work_group_id) |
+              A6XX_HLSQ_CS_CNTL_0_UNK0(regid(63, 0)) |
+              A6XX_HLSQ_CS_CNTL_0_UNK1(regid(63, 0)) |
+              A6XX_HLSQ_CS_CNTL_0_LOCALIDREGID(local_invocation_id));
+   tu_cs_emit(cs, 0x2fc);             /* HLSQ_CS_UNKNOWN_B998 */
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_IBO_COUNT, 1);
+   tu_cs_emit(cs, v->image_mapping.num_ibo);
 }
 
 static void
@@ -677,46 +732,63 @@ tu6_emit_vpc_varying_modes(struct tu_cs *cs,
 }
 
 static void
-tu6_emit_fs_system_values(struct tu_cs *cs,
-                          const struct ir3_shader_variant *fs)
-{
-   const uint32_t frontfacing_regid =
-      ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRONT_FACE);
-   const uint32_t sampleid_regid =
-      ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_ID);
-   const uint32_t samplemaskin_regid =
-      ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_MASK_IN);
-   const uint32_t fragcoord_xy_regid =
-      ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRAG_COORD);
-   const uint32_t fragcoord_zw_regid = (fragcoord_xy_regid != regid(63, 0))
-                                          ? (fragcoord_xy_regid + 2)
-                                          : fragcoord_xy_regid;
-   const uint32_t varyingcoord_regid =
-      ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_PIXEL);
+tu6_emit_fs_inputs(struct tu_cs *cs, const struct ir3_shader_variant *fs)
+{
+   uint32_t face_regid, coord_regid, zwcoord_regid, samp_id_regid;
+   uint32_t ij_pix_regid, ij_samp_regid, ij_cent_regid, ij_size_regid;
+   uint32_t smask_in_regid;
+
+   bool sample_shading = fs->per_samp; /* TODO | key->sample_shading; */
+   bool enable_varyings = fs->total_in > 0;
+
+   samp_id_regid   = ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_ID);
+   smask_in_regid  = ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_MASK_IN);
+   face_regid      = ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRONT_FACE);
+   coord_regid     = ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRAG_COORD);
+   zwcoord_regid   = VALIDREG(coord_regid) ? coord_regid + 2 : regid(63, 0);
+   ij_pix_regid    = ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_PIXEL);
+   ij_samp_regid   = ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_SAMPLE);
+   ij_cent_regid   = ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_CENTROID);
+   ij_size_regid   = ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_SIZE);
+
+   if (fs->num_sampler_prefetch > 0) {
+      assert(VALIDREG(ij_pix_regid));
+      /* also, it seems like ij_pix is *required* to be r0.x */
+      assert(ij_pix_regid == regid(0, 0));
+   }
+
+   tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_PREFETCH_CNTL, 1 + fs->num_sampler_prefetch);
+   tu_cs_emit(cs, A6XX_SP_FS_PREFETCH_CNTL_COUNT(fs->num_sampler_prefetch) |
+         A6XX_SP_FS_PREFETCH_CNTL_UNK4(regid(63, 0)) |
+         0x7000);    // XXX);
+   for (int i = 0; i < fs->num_sampler_prefetch; i++) {
+      const struct ir3_sampler_prefetch *prefetch = &fs->sampler_prefetch[i];
+      tu_cs_emit(cs, A6XX_SP_FS_PREFETCH_CMD_SRC(prefetch->src) |
+                     A6XX_SP_FS_PREFETCH_CMD_SAMP_ID(prefetch->samp_id) |
+                     A6XX_SP_FS_PREFETCH_CMD_TEX_ID(prefetch->tex_id) |
+                     A6XX_SP_FS_PREFETCH_CMD_DST(prefetch->dst) |
+                     A6XX_SP_FS_PREFETCH_CMD_WRMASK(prefetch->wrmask) |
+                     COND(prefetch->half_precision, A6XX_SP_FS_PREFETCH_CMD_HALF) |
+                     A6XX_SP_FS_PREFETCH_CMD_CMD(prefetch->cmd));
+   }
 
    tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CONTROL_1_REG, 5);
    tu_cs_emit(cs, 0x7);
-   tu_cs_emit(cs, A6XX_HLSQ_CONTROL_2_REG_FACEREGID(frontfacing_regid) |
-                     A6XX_HLSQ_CONTROL_2_REG_SAMPLEID(sampleid_regid) |
-                     A6XX_HLSQ_CONTROL_2_REG_SAMPLEMASK(samplemaskin_regid) |
-                     A6XX_HLSQ_CONTROL_2_REG_SIZE(regid(63, 0)));
-   tu_cs_emit(cs,
-                 A6XX_HLSQ_CONTROL_3_REG_BARY_IJ_PIXEL(varyingcoord_regid) |
-                 A6XX_HLSQ_CONTROL_3_REG_BARY_IJ_CENTROID(regid(63, 0)) |
-                 0xfc00fc00);
-   tu_cs_emit(cs,
-              A6XX_HLSQ_CONTROL_4_REG_XYCOORDREGID(fragcoord_xy_regid) |
-                 A6XX_HLSQ_CONTROL_4_REG_ZWCOORDREGID(fragcoord_zw_regid) |
-                 A6XX_HLSQ_CONTROL_4_REG_BARY_IJ_PIXEL_PERSAMP(regid(63, 0)) |
-                 0x0000fc00);
+   tu_cs_emit(cs, A6XX_HLSQ_CONTROL_2_REG_FACEREGID(face_regid) |
+                  A6XX_HLSQ_CONTROL_2_REG_SAMPLEID(samp_id_regid) |
+                  A6XX_HLSQ_CONTROL_2_REG_SAMPLEMASK(smask_in_regid) |
+                  A6XX_HLSQ_CONTROL_2_REG_SIZE(ij_size_regid));
+   tu_cs_emit(cs, A6XX_HLSQ_CONTROL_3_REG_BARY_IJ_PIXEL(ij_pix_regid) |
+                  A6XX_HLSQ_CONTROL_3_REG_BARY_IJ_CENTROID(ij_cent_regid) |
+                  0xfc00fc00);
+   tu_cs_emit(cs, A6XX_HLSQ_CONTROL_4_REG_XYCOORDREGID(coord_regid) |
+                  A6XX_HLSQ_CONTROL_4_REG_ZWCOORDREGID(zwcoord_regid) |
+                  A6XX_HLSQ_CONTROL_4_REG_BARY_IJ_PIXEL_PERSAMP(ij_samp_regid) |
+                  0x0000fc00);
    tu_cs_emit(cs, 0xfc);
-}
 
-static void
-tu6_emit_fs_inputs(struct tu_cs *cs, const struct ir3_shader_variant *fs)
-{
    tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_UNKNOWN_B980, 1);
-   tu_cs_emit(cs, fs->total_in > 0 ? 3 : 1);
+   tu_cs_emit(cs, enable_varyings ? 3 : 1);
 
    tu_cs_emit_pkt4(cs, REG_A6XX_SP_UNKNOWN_A982, 1);
    tu_cs_emit(cs, 0); /* XXX */
@@ -724,33 +796,41 @@ tu6_emit_fs_inputs(struct tu_cs *cs, const struct ir3_shader_variant *fs)
    tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_UPDATE_CNTL, 1);
    tu_cs_emit(cs, 0xff); /* XXX */
 
-   uint32_t gras_cntl = 0;
-   if (fs->total_in > 0)
-      gras_cntl |= A6XX_GRAS_CNTL_VARYING;
-   if (fs->frag_coord) {
-      gras_cntl |= A6XX_GRAS_CNTL_SIZE | A6XX_GRAS_CNTL_XCOORD |
-                   A6XX_GRAS_CNTL_YCOORD | A6XX_GRAS_CNTL_ZCOORD |
-                   A6XX_GRAS_CNTL_WCOORD;
-   }
-
    tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_CNTL, 1);
-   tu_cs_emit(cs, gras_cntl);
-
-   uint32_t rb_render_control = 0;
-   if (fs->total_in > 0) {
-      rb_render_control =
-         A6XX_RB_RENDER_CONTROL0_VARYING | A6XX_RB_RENDER_CONTROL0_UNK10;
-   }
-   if (fs->frag_coord) {
-      rb_render_control |=
-         A6XX_RB_RENDER_CONTROL0_SIZE | A6XX_RB_RENDER_CONTROL0_XCOORD |
-         A6XX_RB_RENDER_CONTROL0_YCOORD | A6XX_RB_RENDER_CONTROL0_ZCOORD |
-         A6XX_RB_RENDER_CONTROL0_WCOORD;
-   }
+   tu_cs_emit(cs,
+         CONDREG(ij_pix_regid, A6XX_GRAS_CNTL_VARYING) |
+         CONDREG(ij_cent_regid, A6XX_GRAS_CNTL_CENTROID) |
+         CONDREG(ij_samp_regid, A6XX_GRAS_CNTL_PERSAMP_VARYING) |
+         COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_GRAS_CNTL_SIZE) |
+         COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_GRAS_CNTL_SIZE_PERSAMP) |
+         COND(fs->frag_coord,
+               A6XX_GRAS_CNTL_SIZE |
+               A6XX_GRAS_CNTL_XCOORD |
+               A6XX_GRAS_CNTL_YCOORD |
+               A6XX_GRAS_CNTL_ZCOORD |
+               A6XX_GRAS_CNTL_WCOORD) |
+         COND(fs->frag_face, A6XX_GRAS_CNTL_SIZE));
 
    tu_cs_emit_pkt4(cs, REG_A6XX_RB_RENDER_CONTROL0, 2);
-   tu_cs_emit(cs, rb_render_control);
-   tu_cs_emit(cs, (fs->frag_face ? A6XX_RB_RENDER_CONTROL1_FACENESS : 0));
+   tu_cs_emit(cs,
+         CONDREG(ij_pix_regid, A6XX_RB_RENDER_CONTROL0_VARYING) |
+         CONDREG(ij_cent_regid, A6XX_RB_RENDER_CONTROL0_CENTROID) |
+         CONDREG(ij_samp_regid, A6XX_RB_RENDER_CONTROL0_PERSAMP_VARYING) |
+         COND(enable_varyings, A6XX_RB_RENDER_CONTROL0_UNK10) |
+         COND(VALIDREG(ij_size_regid) && !sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE) |
+         COND(VALIDREG(ij_size_regid) &&  sample_shading, A6XX_RB_RENDER_CONTROL0_SIZE_PERSAMP) |
+         COND(fs->frag_coord,
+               A6XX_RB_RENDER_CONTROL0_SIZE |
+               A6XX_RB_RENDER_CONTROL0_XCOORD |
+               A6XX_RB_RENDER_CONTROL0_YCOORD |
+               A6XX_RB_RENDER_CONTROL0_ZCOORD |
+               A6XX_RB_RENDER_CONTROL0_WCOORD) |
+         COND(fs->frag_face, A6XX_RB_RENDER_CONTROL0_SIZE));
+   tu_cs_emit(cs,
+         CONDREG(smask_in_regid, A6XX_RB_RENDER_CONTROL1_SAMPLEMASK) |
+         CONDREG(samp_id_regid, A6XX_RB_RENDER_CONTROL1_SAMPLEID) |
+         CONDREG(ij_size_regid, A6XX_RB_RENDER_CONTROL1_SIZE) |
+         COND(fs->frag_face, A6XX_RB_RENDER_CONTROL1_FACENESS));
 }
 
 static void
@@ -758,8 +838,11 @@ tu6_emit_fs_outputs(struct tu_cs *cs,
                     const struct ir3_shader_variant *fs,
                     uint32_t mrt_count)
 {
-   const uint32_t fragdepth_regid =
-      ir3_find_output_regid(fs, FRAG_RESULT_DEPTH);
+   uint32_t smask_regid, posz_regid;
+
+   posz_regid      = ir3_find_output_regid(fs, FRAG_RESULT_DEPTH);
+   smask_regid     = ir3_find_output_regid(fs, FRAG_RESULT_SAMPLE_MASK);
+
    uint32_t fragdata_regid[8];
    if (fs->color0_mrt) {
       fragdata_regid[0] = ir3_find_output_regid(fs, FRAG_RESULT_COLOR);
@@ -771,8 +854,9 @@ tu6_emit_fs_outputs(struct tu_cs *cs,
    }
 
    tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_OUTPUT_CNTL0, 2);
-   tu_cs_emit(
-      cs, A6XX_SP_FS_OUTPUT_CNTL0_DEPTH_REGID(fragdepth_regid) | 0xfcfc0000);
+   tu_cs_emit(cs, A6XX_SP_FS_OUTPUT_CNTL0_DEPTH_REGID(posz_regid) |
+                  A6XX_SP_FS_OUTPUT_CNTL0_SAMPMASK_REGID(smask_regid) |
+                  0xfc000000);
    tu_cs_emit(cs, A6XX_SP_FS_OUTPUT_CNTL1_MRT(mrt_count));
 
    tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_OUTPUT_REG(0), 8);
@@ -784,7 +868,8 @@ tu6_emit_fs_outputs(struct tu_cs *cs,
    }
 
    tu_cs_emit_pkt4(cs, REG_A6XX_RB_FS_OUTPUT_CNTL0, 2);
-   tu_cs_emit(cs, fs->writes_pos ? A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_Z : 0);
+   tu_cs_emit(cs, COND(fs->writes_pos, A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_Z) |
+                  COND(fs->writes_smask, A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_SAMPMASK));
    tu_cs_emit(cs, A6XX_RB_FS_OUTPUT_CNTL1_MRT(mrt_count));
 
    uint32_t gras_su_depth_plane_cntl = 0;
@@ -891,6 +976,10 @@ static void
 tu6_emit_immediates(struct tu_cs *cs, const struct ir3_shader_variant *v,
                     uint32_t opcode, enum a6xx_state_block block)
 {
+   /* dummy variant */
+   if (!v->shader)
+      return;
+
    const struct ir3_const_state *const_state = &v->shader->const_state;
    uint32_t base = const_state->offsets.immediate;
    int size = const_state->immediates_count;
@@ -907,7 +996,7 @@ tu6_emit_immediates(struct tu_cs *cs, const struct ir3_shader_variant *v,
    tu_cs_emit(cs, CP_LOAD_STATE6_0_DST_OFF(base) |
                   CP_LOAD_STATE6_0_STATE_TYPE(ST6_CONSTANTS) |
                   CP_LOAD_STATE6_0_STATE_SRC(SS6_DIRECT) |
-                  CP_LOAD_STATE6_0_STATE_BLOCK(SB6_FS_SHADER) |
+                  CP_LOAD_STATE6_0_STATE_BLOCK(block) |
                   CP_LOAD_STATE6_0_NUM_UNIT(size));
    tu_cs_emit(cs, CP_LOAD_STATE6_1_EXT_SRC_ADDR(0));
    tu_cs_emit(cs, CP_LOAD_STATE6_2_EXT_SRC_ADDR_HI(0));
@@ -954,21 +1043,20 @@ tu6_emit_program(struct tu_cs *cs,
       fs = &dummy_variant;
    }
 
-   tu6_emit_vs_config(cs, vs);
-   tu6_emit_hs_config(cs, hs);
-   tu6_emit_ds_config(cs, ds);
-   tu6_emit_gs_config(cs, gs);
-   tu6_emit_fs_config(cs, fs);
+   tu6_emit_vs_config(cs, builder->shaders[MESA_SHADER_VERTEX], vs);
+   tu6_emit_hs_config(cs, builder->shaders[MESA_SHADER_TESS_CTRL], hs);
+   tu6_emit_ds_config(cs, builder->shaders[MESA_SHADER_TESS_EVAL], ds);
+   tu6_emit_gs_config(cs, builder->shaders[MESA_SHADER_GEOMETRY], gs);
+   tu6_emit_fs_config(cs, builder->shaders[MESA_SHADER_FRAGMENT], fs);
 
    tu6_emit_vs_system_values(cs, vs);
    tu6_emit_vpc(cs, vs, fs, binning_pass);
    tu6_emit_vpc_varying_modes(cs, fs, binning_pass);
-   tu6_emit_fs_system_values(cs, fs);
    tu6_emit_fs_inputs(cs, fs);
    tu6_emit_fs_outputs(cs, fs, builder->color_attachment_count);
 
    tu6_emit_shader_object(cs, MESA_SHADER_VERTEX, vs, binary_bo,
-                          builder->shader_offsets[MESA_SHADER_VERTEX]);
+      binning_pass ? builder->binning_vs_offset : builder->shader_offsets[MESA_SHADER_VERTEX]);
 
    tu6_emit_shader_object(cs, MESA_SHADER_FRAGMENT, fs, binary_bo,
                           builder->shader_offsets[MESA_SHADER_FRAGMENT]);
@@ -989,9 +1077,7 @@ tu6_emit_vertex_input(struct tu_cs *cs,
 {
    uint32_t vfd_decode_idx = 0;
 
-   /* why do we go beyond inputs_count? */
-   assert(vs->inputs_count + 1 <= MAX_VERTEX_ATTRIBS);
-   for (uint32_t i = 0; i <= vs->inputs_count; i++) {
+   for (uint32_t i = 0; i < vs->inputs_count; i++) {
       if (vs->inputs[i].sysval || !vs->inputs[i].compmask)
          continue;
 
@@ -1032,6 +1118,7 @@ tu6_emit_vertex_input(struct tu_cs *cs,
       offsets[vfd_decode_idx] = vi_attr->offset;
 
       vfd_decode_idx++;
+      assert(vfd_decode_idx <= MAX_VERTEX_ATTRIBS);
    }
 
    tu_cs_emit_pkt4(cs, REG_A6XX_VFD_CONTROL_0, 1);
@@ -1126,7 +1213,7 @@ tu6_emit_gras_unknowns(struct tu_cs *cs)
    tu_cs_emit(cs, 0x80);
    tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_UNKNOWN_8001, 1);
    tu_cs_emit(cs, 0x0);
-   tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_UNKNOWN_8004, 1);
+   tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_LAYER_CNTL, 1);
    tu_cs_emit(cs, 0x0);
 }
 
@@ -1407,13 +1494,12 @@ tu6_emit_blend_constants(struct tu_cs *cs, const float constants[4])
 }
 
 static VkResult
-tu_pipeline_builder_create_pipeline(struct tu_pipeline_builder *builder,
-                                    struct tu_pipeline **out_pipeline)
+tu_pipeline_create(struct tu_device *dev,
+                   const VkAllocationCallbacks *pAllocator,
+                   struct tu_pipeline **out_pipeline)
 {
-   struct tu_device *dev = builder->device;
-
    struct tu_pipeline *pipeline =
-      vk_zalloc2(&dev->alloc, builder->alloc, sizeof(*pipeline), 8,
+      vk_zalloc2(&dev->alloc, pAllocator, sizeof(*pipeline), 8,
                  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
    if (!pipeline)
       return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -1423,7 +1509,7 @@ tu_pipeline_builder_create_pipeline(struct tu_pipeline_builder *builder,
    /* reserve the space now such that tu_cs_begin_sub_stream never fails */
    VkResult result = tu_cs_reserve_space(dev, &pipeline->cs, 2048);
    if (result != VK_SUCCESS) {
-      vk_free2(&dev->alloc, builder->alloc, pipeline);
+      vk_free2(&dev->alloc, pAllocator, pipeline);
       return result;
    }
 
@@ -1456,7 +1542,8 @@ tu_pipeline_builder_compile_shaders(struct tu_pipeline_builder *builder)
          continue;
 
       struct tu_shader *shader =
-         tu_shader_create(builder->device, stage, stage_info, builder->alloc);
+         tu_shader_create(builder->device, stage, stage_info, builder->layout,
+                          builder->alloc);
       if (!shader)
          return VK_ERROR_OUT_OF_HOST_MEMORY;
 
@@ -1555,12 +1642,13 @@ tu_pipeline_builder_parse_shader_stages(struct tu_pipeline_builder *builder,
       struct ir3_shader *shader = builder->shaders[i]->variants[0].shader;
 
       link->ubo_state = shader->ubo_state;
+      link->const_state = shader->const_state;
       link->constlen = builder->shaders[i]->variants[0].constlen;
-      link->offset_ubo = shader->const_state.offsets.ubo;
-      link->num_ubo = shader->const_state.num_ubos;
       link->texture_map = builder->shaders[i]->texture_map;
       link->sampler_map = builder->shaders[i]->sampler_map;
       link->ubo_map = builder->shaders[i]->ubo_map;
+      link->ssbo_map = builder->shaders[i]->ssbo_map;
+      link->image_mapping =  builder->shaders[i]->variants[0].image_mapping;
    }
 }
 
@@ -1778,7 +1866,8 @@ static VkResult
 tu_pipeline_builder_build(struct tu_pipeline_builder *builder,
                           struct tu_pipeline **pipeline)
 {
-   VkResult result = tu_pipeline_builder_create_pipeline(builder, pipeline);
+   VkResult result = tu_pipeline_create(builder->device, builder->alloc,
+                                        pipeline);
    if (result != VK_SUCCESS)
       return result;
 
@@ -1829,11 +1918,14 @@ tu_pipeline_builder_init_graphics(
    const VkGraphicsPipelineCreateInfo *create_info,
    const VkAllocationCallbacks *alloc)
 {
+   TU_FROM_HANDLE(tu_pipeline_layout, layout, create_info->layout);
+
    *builder = (struct tu_pipeline_builder) {
       .device = dev,
       .cache = cache,
       .create_info = create_info,
       .alloc = alloc,
+      .layout = layout,
    };
 
    builder->rasterizer_discard =
@@ -1852,8 +1944,9 @@ tu_pipeline_builder_init_graphics(
       builder->use_depth_stencil_attachment =
          subpass->depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED;
 
-      assert(subpass->color_count ==
-             create_info->pColorBlendState->attachmentCount);
+      assert(subpass->color_count == 0 ||
+             !create_info->pColorBlendState ||
+             subpass->color_count == create_info->pColorBlendState->attachmentCount);
       builder->color_attachment_count = subpass->color_count;
       for (uint32_t i = 0; i < subpass->color_count; i++) {
          const uint32_t a = subpass->color_attachments[i].attachment;
@@ -1866,6 +1959,32 @@ tu_pipeline_builder_init_graphics(
    }
 }
 
+static VkResult
+tu_graphics_pipeline_create(VkDevice device,
+                            VkPipelineCache pipelineCache,
+                            const VkGraphicsPipelineCreateInfo *pCreateInfo,
+                            const VkAllocationCallbacks *pAllocator,
+                            VkPipeline *pPipeline)
+{
+   TU_FROM_HANDLE(tu_device, dev, device);
+   TU_FROM_HANDLE(tu_pipeline_cache, cache, pipelineCache);
+
+   struct tu_pipeline_builder builder;
+   tu_pipeline_builder_init_graphics(&builder, dev, cache,
+                                     pCreateInfo, pAllocator);
+
+   struct tu_pipeline *pipeline = NULL;
+   VkResult result = tu_pipeline_builder_build(&builder, &pipeline);
+   tu_pipeline_builder_finish(&builder);
+
+   if (result == VK_SUCCESS)
+      *pPipeline = tu_pipeline_to_handle(pipeline);
+   else
+      *pPipeline = NULL;
+
+   return result;
+}
+
 VkResult
 tu_CreateGraphicsPipelines(VkDevice device,
                            VkPipelineCache pipelineCache,
@@ -1874,62 +1993,150 @@ tu_CreateGraphicsPipelines(VkDevice device,
                            const VkAllocationCallbacks *pAllocator,
                            VkPipeline *pPipelines)
 {
-   TU_FROM_HANDLE(tu_device, dev, device);
-   TU_FROM_HANDLE(tu_pipeline_cache, cache, pipelineCache);
    VkResult final_result = VK_SUCCESS;
 
    for (uint32_t i = 0; i < count; i++) {
-      struct tu_pipeline_builder builder;
-      tu_pipeline_builder_init_graphics(&builder, dev, cache,
-                                        &pCreateInfos[i], pAllocator);
-
-      struct tu_pipeline *pipeline = NULL;
-      VkResult result = tu_pipeline_builder_build(&builder, &pipeline);
-      tu_pipeline_builder_finish(&builder);
-
-      if (result == VK_SUCCESS) {
-         pPipelines[i] = tu_pipeline_to_handle(pipeline);
-      } else {
-         pPipelines[i] = NULL;
+      VkResult result = tu_graphics_pipeline_create(device, pipelineCache,
+                                                    &pCreateInfos[i], pAllocator,
+                                                    &pPipelines[i]);
+
+      if (result != VK_SUCCESS)
          final_result = result;
-      }
    }
 
    return final_result;
 }
 
+static void
+tu6_emit_compute_program(struct tu_cs *cs,
+                         struct tu_shader *shader,
+                         const struct tu_bo *binary_bo)
+{
+   const struct ir3_shader_variant *v = &shader->variants[0];
+
+   tu6_emit_cs_config(cs, shader, v);
+
+   /* The compute program is the only one in the pipeline, so 0 offset. */
+   tu6_emit_shader_object(cs, MESA_SHADER_COMPUTE, v, binary_bo, 0);
+
+   tu6_emit_immediates(cs, v, CP_LOAD_STATE6_FRAG, SB6_CS_SHADER);
+}
+
 static VkResult
-tu_compute_pipeline_create(VkDevice _device,
+tu_compute_upload_shader(VkDevice device,
+                         struct tu_pipeline *pipeline,
+                         struct tu_shader *shader)
+{
+   TU_FROM_HANDLE(tu_device, dev, device);
+   struct tu_bo *bo = &pipeline->program.binary_bo;
+   struct ir3_shader_variant *v = &shader->variants[0];
+
+   uint32_t shader_size = sizeof(uint32_t) * v->info.sizedwords;
+   VkResult result =
+      tu_bo_init_new(dev, bo, shader_size);
+   if (result != VK_SUCCESS)
+      return result;
+
+   result = tu_bo_map(dev, bo);
+   if (result != VK_SUCCESS)
+      return result;
+
+   memcpy(bo->map, shader->binary, shader_size);
+
+   return VK_SUCCESS;
+}
+
+
+static VkResult
+tu_compute_pipeline_create(VkDevice device,
                            VkPipelineCache _cache,
                            const VkComputePipelineCreateInfo *pCreateInfo,
                            const VkAllocationCallbacks *pAllocator,
                            VkPipeline *pPipeline)
 {
+   TU_FROM_HANDLE(tu_device, dev, device);
+   TU_FROM_HANDLE(tu_pipeline_layout, layout, pCreateInfo->layout);
+   const VkPipelineShaderStageCreateInfo *stage_info = &pCreateInfo->stage;
+   VkResult result;
+
+   struct tu_pipeline *pipeline;
+
+   result = tu_pipeline_create(dev, pAllocator, &pipeline);
+   if (result != VK_SUCCESS)
+      return result;
+
+   pipeline->layout = layout;
+
+   struct tu_shader_compile_options options;
+   tu_shader_compile_options_init(&options, NULL);
+
+   struct tu_shader *shader =
+      tu_shader_create(dev, MESA_SHADER_COMPUTE, stage_info, layout, pAllocator);
+   if (!shader) {
+      result = VK_ERROR_OUT_OF_HOST_MEMORY;
+      goto fail;
+   }
+
+   result = tu_shader_compile(dev, shader, NULL, &options, pAllocator);
+   if (result != VK_SUCCESS)
+      return result;
+
+   struct tu_program_descriptor_linkage *link = &pipeline->program.link[MESA_SHADER_COMPUTE];
+   struct ir3_shader_variant *v = &shader->variants[0];
+
+   link->ubo_state = v->shader->ubo_state;
+   link->const_state = v->shader->const_state;
+   link->constlen = v->constlen;
+   link->texture_map = shader->texture_map;
+   link->sampler_map = shader->sampler_map;
+   link->ubo_map = shader->ubo_map;
+   link->ssbo_map = shader->ssbo_map;
+   link->image_mapping =  v->image_mapping;
+
+   result = tu_compute_upload_shader(device, pipeline, shader);
+   if (result != VK_SUCCESS)
+      return result;
+
+   for (int i = 0; i < 3; i++)
+      pipeline->compute.local_size[i] = v->shader->nir->info.cs.local_size[i];
+
+   struct tu_cs prog_cs;
+   tu_cs_begin_sub_stream(dev, &pipeline->cs, 512, &prog_cs);
+   tu6_emit_compute_program(&prog_cs, shader, &pipeline->program.binary_bo);
+   pipeline->program.state_ib = tu_cs_end_sub_stream(&pipeline->cs, &prog_cs);
+
+   *pPipeline = tu_pipeline_to_handle(pipeline);
    return VK_SUCCESS;
+
+fail:
+   tu_shader_destroy(dev, shader, pAllocator);
+   if (result != VK_SUCCESS) {
+      tu_pipeline_finish(pipeline, dev, pAllocator);
+      vk_free2(&dev->alloc, pAllocator, pipeline);
+   }
+
+   return result;
 }
 
 VkResult
-tu_CreateComputePipelines(VkDevice _device,
+tu_CreateComputePipelines(VkDevice device,
                           VkPipelineCache pipelineCache,
                           uint32_t count,
                           const VkComputePipelineCreateInfo *pCreateInfos,
                           const VkAllocationCallbacks *pAllocator,
                           VkPipeline *pPipelines)
 {
-   VkResult result = VK_SUCCESS;
+   VkResult final_result = VK_SUCCESS;
 
-   unsigned i = 0;
-   for (; i < count; i++) {
-      VkResult r;
-      r = tu_compute_pipeline_create(_device, pipelineCache, &pCreateInfos[i],
-                                     pAllocator, &pPipelines[i]);
-      if (r != VK_SUCCESS) {
-         result = r;
-         pPipelines[i] = VK_NULL_HANDLE;
-      }
+   for (uint32_t i = 0; i < count; i++) {
+      VkResult result = tu_compute_pipeline_create(device, pipelineCache,
+                                                   &pCreateInfos[i],
+                                                   pAllocator, &pPipelines[i]);
+      if (result != VK_SUCCESS)
+         final_result = result;
    }
 
-   return result;
+   return final_result;
 }
 
 void