panfrost: Workaround ISSUE_TSIX_2033
authorAlyssa Rosenzweig <alyssa@collabora.com>
Tue, 5 Oct 2021 23:20:03 +0000 (19:20 -0400)
committerAlyssa Rosenzweig <alyssa@collabora.com>
Sat, 23 Oct 2021 01:55:32 +0000 (21:55 -0400)
According to mali_kbase, all Bifrost and Valhall GPUs are affected by
issue TSIX_2033. This hardware bug breaks the INTERSECT frame shader
mode when forcing clean_tile_writes. What does that mean?

The hardware considers a tile "clean" if it has been cleared but not
drawn to. Setting clean_tile_write forces the hardware to write back
such "clean" tiles to main memory.

Bifrost hardware supports frame shaders, which insert a rectangle into
every tile according to a configured rule. Frame shaders are used in
Panfrost to implement tile reloads (i.e. LOAD_OP_LOAD). Two modes are
relevant to the current discussion: ALWAYS, which always inserts a frame
shader, and INTERSECT, which tries to only insert where there is
geometry. Normally, we use INTERSECT for tile reloads as it is more
efficient than ALWAYS-- it allows us to skip reloads of tiles that are
discarded and never written back to memory.

From a software perspective, Panfrost's current logic is correct: if we
clear, we set clean_tile_writes, else we use an INTERSECT frame shader.
There is no software interaction between the two.

Unfortunately, there is a hardware interaction. The hardware forces
clean_tile_writes in certain circumstances when AFBC is used.
Ordinarily, this is a hardware implementation detail and invisible to
software. Unfortunately, this implicit clean tile write is enough to
trigger the hardware bug when using INTERSECT. As such, we need to
detect this case and use ALWAYS instead of INTERSECT for correct
results.

Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Cc: mesa-stable
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/13205>

src/panfrost/lib/pan_cs.c

index c5ae546..b4375a1 100644 (file)
@@ -551,6 +551,68 @@ pan_emit_rt(const struct pan_fb_info *fb,
         }
 }
 
+#if PAN_ARCH >= 6
+/* All Bifrost and Valhall GPUs are affected by issue TSIX-2033:
+ *
+ *      Forcing clean_tile_writes breaks INTERSECT readbacks
+ *
+ * To workaround, use the frame shader mode ALWAYS instead of INTERSECT if
+ * clean tile writes is forced. Since INTERSECT is a hint that the hardware may
+ * ignore, this cannot affect correctness, only performance */
+
+static enum mali_pre_post_frame_shader_mode
+pan_fix_frame_shader_mode(enum mali_pre_post_frame_shader_mode mode, bool force_clean_tile)
+{
+        if (force_clean_tile && mode == MALI_PRE_POST_FRAME_SHADER_MODE_INTERSECT)
+                return MALI_PRE_POST_FRAME_SHADER_MODE_ALWAYS;
+        else
+                return mode;
+}
+
+/* Regardless of clean_tile_write_enable, the hardware writes clean tiles if
+ * the effective tile size differs from the superblock size of any enabled AFBC
+ * render target. Check this condition. */
+
+static bool
+pan_force_clean_write_rt(const struct pan_image_view *rt, unsigned tile_size)
+{
+        if (!drm_is_afbc(rt->image->layout.modifier))
+                return false;
+
+        unsigned superblock = panfrost_block_dim(rt->image->layout.modifier, true, 0);
+
+        assert(superblock >= 16);
+        assert(tile_size <= 16*16);
+
+        /* Tile size and superblock differ unless they are both 16x16 */
+        return !(superblock == 16 && tile_size == 16*16);
+}
+
+static bool
+pan_force_clean_write(const struct pan_fb_info *fb, unsigned tile_size)
+{
+        /* Maximum tile size */
+        assert(tile_size <= 16*16);
+
+        for (unsigned i = 0; i < fb->rt_count; ++i) {
+                if (fb->rts[i].view && !fb->rts[i].discard &&
+                    pan_force_clean_write_rt(fb->rts[i].view, tile_size))
+                        return true;
+        }
+
+        if (fb->zs.view.zs && !fb->zs.discard.z &&
+            pan_force_clean_write_rt(fb->zs.view.zs, tile_size))
+                return true;
+
+        if (fb->zs.view.s && !fb->zs.discard.s &&
+            pan_force_clean_write_rt(fb->zs.view.s, tile_size))
+                return true;
+
+        return false;
+}
+
+#endif
+
 static unsigned
 pan_emit_mfbd(const struct panfrost_device *dev,
               const struct pan_fb_info *fb,
@@ -574,11 +636,13 @@ pan_emit_mfbd(const struct panfrost_device *dev,
 
         pan_section_pack(fbd, FRAMEBUFFER, PARAMETERS, cfg) {
 #if PAN_ARCH >= 6
+                bool force_clean_write = pan_force_clean_write(fb, tile_size);
+
                 cfg.sample_locations =
                         panfrost_sample_positions(dev, pan_sample_pattern(fb->nr_samples));
-                cfg.pre_frame_0 = fb->bifrost.pre_post.modes[0];
-                cfg.pre_frame_1 = fb->bifrost.pre_post.modes[1];
-                cfg.post_frame = fb->bifrost.pre_post.modes[2];
+                cfg.pre_frame_0 = pan_fix_frame_shader_mode(fb->bifrost.pre_post.modes[0], force_clean_write);
+                cfg.pre_frame_1 = pan_fix_frame_shader_mode(fb->bifrost.pre_post.modes[1], force_clean_write);
+                cfg.post_frame  = pan_fix_frame_shader_mode(fb->bifrost.pre_post.modes[2], force_clean_write);
                 cfg.frame_shader_dcds = fb->bifrost.pre_post.dcds.gpu;
                 cfg.tiler = tiler_ctx->bifrost;
 #endif