[Mesa-dev] [PATCH v4 5/5] panfrost: Add support for KHR_partial_update()

Boris Brezillon boris.brezillon at collabora.com
Tue Jun 25 16:37:49 UTC 2019


Implement ->set_damage_region() region to support partial updates.

This is a dummy implementation in that it does not try to merge
damage rects. It also does not deal with distinct regions and instead
pick the largest quad as the only damage rect and generate up to 4
reload rects out of it (the left/right/top/bottom regions surrounding
the biggest damage rect).

We also do not try to reduce the number of draws by passing all quad
vertices to the blit request (would require extending u_blitter)

Signed-off-by: Boris Brezillon <boris.brezillon at collabora.com>
---
 src/gallium/drivers/panfrost/pan_blit.c     | 14 ++---
 src/gallium/drivers/panfrost/pan_context.c  | 49 ++++++++++++++++-
 src/gallium/drivers/panfrost/pan_job.c      | 11 ++++
 src/gallium/drivers/panfrost/pan_job.h      |  5 ++
 src/gallium/drivers/panfrost/pan_resource.c | 58 +++++++++++++++++++++
 src/gallium/drivers/panfrost/pan_resource.h | 12 ++++-
 src/gallium/drivers/panfrost/pan_screen.c   |  1 +
 7 files changed, 141 insertions(+), 9 deletions(-)

diff --git a/src/gallium/drivers/panfrost/pan_blit.c b/src/gallium/drivers/panfrost/pan_blit.c
index 5859f92f9d1b..3a45277ee287 100644
--- a/src/gallium/drivers/panfrost/pan_blit.c
+++ b/src/gallium/drivers/panfrost/pan_blit.c
@@ -105,18 +105,18 @@ panfrost_blit(struct pipe_context *pipe,
  */
 
 void
-panfrost_blit_wallpaper(struct panfrost_context *ctx)
+panfrost_blit_wallpaper(struct panfrost_context *ctx, struct pipe_box *rect)
 {
         struct pipe_blit_info binfo = { };
 
         panfrost_blitter_save(ctx);
 
-	binfo.src.resource = binfo.dst.resource = ctx->pipe_framebuffer.cbufs[0]->texture;
-	binfo.src.level = binfo.dst.level = 0;
-	binfo.src.box.x = binfo.dst.box.x = 0;
-	binfo.src.box.y = binfo.dst.box.y = 0;
-	binfo.src.box.width = binfo.dst.box.width = ctx->pipe_framebuffer.width;
-	binfo.src.box.height = binfo.dst.box.height = ctx->pipe_framebuffer.height;
+        binfo.src.resource = binfo.dst.resource = ctx->pipe_framebuffer.cbufs[0]->texture;
+        binfo.src.level = binfo.dst.level = 0;
+        binfo.src.box.x = binfo.dst.box.x = rect->x;
+        binfo.src.box.y = binfo.dst.box.y = rect->y;
+        binfo.src.box.width = binfo.dst.box.width = rect->width;
+        binfo.src.box.height = binfo.dst.box.height = rect->height;
 
 	/* This avoids an assert due to missing nir_texop_txb support */
 	//binfo.src.box.depth = binfo.dst.box.depth = 1;
diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index de6dd38c5566..c1075c6693e8 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -1528,7 +1528,54 @@ panfrost_draw_wallpaper(struct pipe_context *pipe)
         struct panfrost_job *batch = panfrost_get_job_for_fbo(ctx);
 
         ctx->wallpaper_batch = batch;
-        panfrost_blit_wallpaper(ctx);
+
+        /* Clamp the rendering area to the damage extent. The
+         * KHR_partial_update() spec states that trying to render outside of
+         * the damage region is "undefined behavior", so we should be safe.
+         */
+        panfrost_job_intersection_scissor(batch, rsrc->damage.extent.minx,
+                                          rsrc->damage.extent.miny,
+                                          rsrc->damage.extent.maxx,
+                                          rsrc->damage.extent.maxy);
+
+        struct pipe_scissor_state damage;
+        struct pipe_box rects[4];
+
+        /* Clamp the damage box to the rendering area. */
+        damage.minx = MAX2(batch->minx, rsrc->damage.biggest_rect.x);
+        damage.miny = MAX2(batch->miny, rsrc->damage.biggest_rect.y);
+        damage.maxx = MIN2(batch->maxx,
+                           rsrc->damage.biggest_rect.x +
+                           rsrc->damage.biggest_rect.width);
+        damage.maxy = MIN2(batch->maxy,
+                           rsrc->damage.biggest_rect.y +
+                           rsrc->damage.biggest_rect.height);
+
+        /* One damage rectangle means we can end up with at most 4 reload
+         * regions:
+         * 1: left region, only exists if damage.x > 0
+         * 2: right region, only exists if damage.x + damage.width < fb->width
+         * 3: top region, only exists if damage.y > 0. The intersection with
+         *    the left and right regions are dropped
+         * 4: bottom region, only exists if damage.y + damage.height < fb->height.
+         *    The intersection with the left and right regions are dropped
+         */
+        u_box_2d(batch->minx, batch->miny, damage.minx - batch->minx,
+                 batch->maxy - batch->miny, &rects[0]);
+        u_box_2d(damage.maxx, batch->miny, batch->maxx - damage.maxx,
+                 batch->maxy - batch->miny, &rects[1]);
+        u_box_2d(damage.minx, batch->miny, damage.maxx - damage.minx,
+                 damage.miny - batch->miny, &rects[2]);
+        u_box_2d(damage.minx, damage.maxy, damage.maxx - damage.minx,
+                 batch->maxy - damage.maxy, &rects[3]);
+
+        for (unsigned i = 0; i < 4; i++) {
+                if (!rects[i].width || !rects[i].height)
+                        continue;
+
+                /* Blit the wallpaper in */
+                panfrost_blit_wallpaper(ctx, &rects[i]);
+        }
         ctx->wallpaper_batch = NULL;
 }
 
diff --git a/src/gallium/drivers/panfrost/pan_job.c b/src/gallium/drivers/panfrost/pan_job.c
index 22f0f492b5d8..5fa81dbb896d 100644
--- a/src/gallium/drivers/panfrost/pan_job.c
+++ b/src/gallium/drivers/panfrost/pan_job.c
@@ -295,6 +295,17 @@ panfrost_job_union_scissor(struct panfrost_job *job,
         job->maxy = MAX2(job->maxy, maxy);
 }
 
+void
+panfrost_job_intersection_scissor(struct panfrost_job *job,
+                                  unsigned minx, unsigned miny,
+                                  unsigned maxx, unsigned maxy)
+{
+        job->minx = MAX2(job->minx, minx);
+        job->miny = MAX2(job->miny, miny);
+        job->maxx = MIN2(job->maxx, maxx);
+        job->maxy = MIN2(job->maxy, maxy);
+}
+
 void
 panfrost_job_init(struct panfrost_context *ctx)
 {
diff --git a/src/gallium/drivers/panfrost/pan_job.h b/src/gallium/drivers/panfrost/pan_job.h
index b4c9db9828e2..f98572387ed0 100644
--- a/src/gallium/drivers/panfrost/pan_job.h
+++ b/src/gallium/drivers/panfrost/pan_job.h
@@ -152,6 +152,11 @@ panfrost_job_union_scissor(struct panfrost_job *job,
                 unsigned minx, unsigned miny,
                 unsigned maxx, unsigned maxy);
 
+void
+panfrost_job_intersection_scissor(struct panfrost_job *job,
+                                  unsigned minx, unsigned miny,
+                                  unsigned maxx, unsigned maxy);
+
 /* Scoreboarding */
 
 void
diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c
index 1a691a5be343..95f8177a9f51 100644
--- a/src/gallium/drivers/panfrost/pan_resource.c
+++ b/src/gallium/drivers/panfrost/pan_resource.c
@@ -312,6 +312,62 @@ panfrost_create_bo(struct panfrost_screen *screen, const struct pipe_resource *t
         return bo;
 }
 
+static void
+panfrost_resource_reset_damage(struct panfrost_resource *pres)
+{
+        /* We set the damage extent to the full resource size but keep the
+         * damage box empty so that the FB content is reloaded by default.
+         */
+        memset(&pres->damage, 0, sizeof(pres->damage));
+        pres->damage.extent.maxx = pres->base.width0;
+        pres->damage.extent.maxy = pres->base.height0;
+}
+
+void
+panfrost_resource_set_damage_region(struct pipe_screen *screen,
+                                    struct pipe_resource *res,
+                                    unsigned int nrects, int *rects)
+{
+        struct panfrost_resource *pres = pan_resource(res);
+        struct pipe_box *damage_rect = &pres->damage.biggest_rect;
+        struct pipe_scissor_state *damage_extent = &pres->damage.extent;
+        unsigned int i;
+
+	if (!nrects) {
+		panfrost_resource_reset_damage(pres);
+		return;
+	}
+
+        memset(&pres->damage, 0, sizeof(pres->damage));
+        damage_extent->minx = 0xffff;
+        damage_extent->miny = 0xffff;
+        for (i = 0; i < nrects; i++) {
+                struct pipe_scissor_state ss;
+                int *rect = &rects[i * 4];
+                int y = res->height0 - (rect[1] + rect[3]);
+                struct pipe_box box;
+
+                u_box_2d(rect[0], y, rect[2], rect[3], &box);
+                if (damage_rect->width * damage_rect->height < box.width * box.height)
+                       *damage_rect = box;
+
+                /* Looks like aligning on a tile is not enough, but aligning on
+                 * twice the tile size works.
+                 */
+                ss.minx = rect[0] & ~((MALI_TILE_LENGTH * 2) - 1);
+                ss.miny = y & ~((MALI_TILE_LENGTH * 2) - 1);
+                ss.maxx = MIN2(ALIGN(rect[0] + rect[2], MALI_TILE_LENGTH * 2),
+                               res->width0);
+                ss.maxy = MIN2(ALIGN(y + rect[3], MALI_TILE_LENGTH * 2),
+                               res->height0);
+
+                damage_extent->minx = MIN2(damage_extent->minx, ss.minx);
+                damage_extent->miny = MIN2(damage_extent->miny, ss.miny);
+                damage_extent->maxx = MAX2(damage_extent->maxx, ss.maxx);
+                damage_extent->maxy = MAX2(damage_extent->maxy, ss.maxy);
+        }
+}
+
 static struct pipe_resource *
 panfrost_resource_create(struct pipe_screen *screen,
                          const struct pipe_resource *template)
@@ -368,6 +424,8 @@ panfrost_resource_create(struct pipe_screen *screen,
                 so->bo = panfrost_create_bo(pscreen, template);
         }
 
+        panfrost_resource_reset_damage(so);
+
         return (struct pipe_resource *)so;
 }
 
diff --git a/src/gallium/drivers/panfrost/pan_resource.h b/src/gallium/drivers/panfrost/pan_resource.h
index 632250fa2aa9..095c078e6f18 100644
--- a/src/gallium/drivers/panfrost/pan_resource.h
+++ b/src/gallium/drivers/panfrost/pan_resource.h
@@ -98,6 +98,10 @@ panfrost_bo_unreference(struct pipe_screen *screen, struct panfrost_bo *bo);
 
 struct panfrost_resource {
         struct pipe_resource base;
+        struct {
+                struct pipe_box biggest_rect;
+                struct pipe_scissor_state extent;
+        } damage;
 
         struct panfrost_bo *bo;
         struct renderonly_scanout *scanout;
@@ -144,6 +148,12 @@ panfrost_blit(struct pipe_context *pipe,
               const struct pipe_blit_info *info);
 
 void
-panfrost_blit_wallpaper(struct panfrost_context *ctx);
+panfrost_blit_wallpaper(struct panfrost_context *ctx,
+                        struct pipe_box *damage);
+
+void
+panfrost_resource_set_damage_region(struct pipe_screen *screen,
+                                    struct pipe_resource *res,
+                                    unsigned int nrects, int *rects);
 
 #endif /* PAN_RESOURCE_H */
diff --git a/src/gallium/drivers/panfrost/pan_screen.c b/src/gallium/drivers/panfrost/pan_screen.c
index 15e68fbe2c10..3f27378088d6 100644
--- a/src/gallium/drivers/panfrost/pan_screen.c
+++ b/src/gallium/drivers/panfrost/pan_screen.c
@@ -599,6 +599,7 @@ panfrost_create_screen(int fd, struct renderonly *ro)
         screen->base.get_compiler_options = panfrost_screen_get_compiler_options;
         screen->base.fence_reference = panfrost_fence_reference;
         screen->base.fence_finish = panfrost_fence_finish;
+        screen->base.set_damage_region = panfrost_resource_set_damage_region;
 
 	screen->last_fragment_flushed = true;
         screen->last_job = NULL;
-- 
2.20.1



More information about the mesa-dev mailing list