[v5,5/5] panfrost: Add support for KHR_partial_update()

Submitted by Boris Brezillon on July 2, 2019, 1:50 p.m.

Details

Message ID 20190702135002.23852-6-boris.brezillon@collabora.com
State New
Headers show
Series "EGL_KHR_partial_update support" ( rev: 2 ) in Mesa

Not browsing as part of any series.

Commit Message

Boris Brezillon July 2, 2019, 1:50 p.m.
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@collabora.com>
---
Changes in v5:
- rename the second panfrost_blit_wallpaper() argument
- add extra comment to explain how the set_damage_region() logic works
- clarify why checking for negative box->{width,heigh} is not needed in
  panfrost_draw_wallpaper()
---
 src/gallium/drivers/panfrost/pan_blit.c     | 10 +--
 src/gallium/drivers/panfrost/pan_context.c  | 63 +++++++++++++-
 src/gallium/drivers/panfrost/pan_job.c      | 11 +++
 src/gallium/drivers/panfrost/pan_job.h      |  5 ++
 src/gallium/drivers/panfrost/pan_resource.c | 91 +++++++++++++++++++++
 src/gallium/drivers/panfrost/pan_resource.h | 12 ++-
 src/gallium/drivers/panfrost/pan_screen.c   |  1 +
 7 files changed, 186 insertions(+), 7 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/gallium/drivers/panfrost/pan_blit.c b/src/gallium/drivers/panfrost/pan_blit.c
index 67912a4b130f..226f67e674f5 100644
--- a/src/gallium/drivers/panfrost/pan_blit.c
+++ b/src/gallium/drivers/panfrost/pan_blit.c
@@ -103,7 +103,7 @@  panfrost_blit(struct pipe_context *pipe,
  */
 
 void
-panfrost_blit_wallpaper(struct panfrost_context *ctx)
+panfrost_blit_wallpaper(struct panfrost_context *ctx, struct pipe_box *box)
 {
         struct pipe_blit_info binfo = { };
 
@@ -116,11 +116,11 @@  panfrost_blit_wallpaper(struct panfrost_context *ctx)
 
 	binfo.src.resource = binfo.dst.resource = ctx->pipe_framebuffer.cbufs[0]->texture;
 	binfo.src.level = binfo.dst.level = level;
-	binfo.src.box.x = binfo.dst.box.x = 0;
-	binfo.src.box.y = binfo.dst.box.y = 0;
+	binfo.src.box.x = binfo.dst.box.x = box->x;
+	binfo.src.box.y = binfo.dst.box.y = box->y;
 	binfo.src.box.z = binfo.dst.box.z = layer;
-	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.box.width = binfo.dst.box.width = box->width;
+	binfo.src.box.height = binfo.dst.box.height = box->height;
 	binfo.src.box.depth = binfo.dst.box.depth = 1;
 
 	binfo.src.format = binfo.dst.format = ctx->pipe_framebuffer.cbufs[0]->format;
diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index 88e70c978818..7462e490e229 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -1472,7 +1472,68 @@  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
+         *
+         *                    ____________________________
+         *                    |       |     3     |      |
+         *                    |       |___________|      |
+         *                    |       |   damage  |      |
+         *                    |   1   |    rect   |   2  |
+         *                    |       |___________|      |
+         *                    |       |     4     |      |
+         *                    |_______|___________|______|
+         */
+        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++) {
+                /* Width and height are always >= 0 even if width is declared as a
+                 * signed integer: u_box_2d() helper takes unsigned args and
+                 * panfrost_set_damage_region() is taking care of clamping
+                 * negative values.
+                 */
+                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 2f7fe9e3cc3d..4f855c9935a4 100644
--- a/src/gallium/drivers/panfrost/pan_job.c
+++ b/src/gallium/drivers/panfrost/pan_job.c
@@ -300,6 +300,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 8db7e45af1b6..50155f8b68df 100644
--- a/src/gallium/drivers/panfrost/pan_resource.c
+++ b/src/gallium/drivers/panfrost/pan_resource.c
@@ -364,6 +364,95 @@  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;
+	}
+
+        /* We keep track of 2 different things here:
+         * 1 the damage extent: the quad including all damage regions. Will be
+         *   used restrict the rendering area
+         * 2 the biggest damage rectangle: when there are more than one damage
+         *   rect we keep the biggest one and will generate 4 wallpaper quads
+         *   out of it (see panfrost_draw_wallpaper() for more details). We
+         *   might want to do something smarter at some point.
+         *
+         *                _________________________________
+         *                |                               |
+         *                |    _________________________  |
+         *                |   | rect1|         _________| |
+         *                |   |______|_____   | rect 3: | |
+         *                |   |    | rect2 |  | biggest | |
+         *                |   |    |_______|  |  rect   | |
+         *                |   |_______________|_________| |
+         *                |        damage extent          |
+         *                |_______________________________|
+         *                            resource
+         */
+        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 x = rect[0], w = rect[2];
+                int y = res->height0 - (rect[1] + rect[3]), h = rect[3];
+
+                /* Clamp x,y,w,h to prevent negative values. */
+                if (x < 0) {
+                        h += x;
+                        x = 0;
+                }
+                if (y < 0) {
+                        w += y;
+                        y = 0;
+                }
+                w = MAX2(w, 0);
+                h = MAX2(h, 0);
+
+                if (damage_rect->width * damage_rect->height < w * h)
+                       u_box_2d(x, y, w, h, damage_rect);
+
+                /* FIXME: Looks like aligning on a tile is not enough, but
+                 * aligning on twice the tile size seems to works. We don't
+                 * know exactly what happens here but this deserves extra
+                 * investigation to figure it out.
+                 */
+                ss.minx = x & ~((MALI_TILE_LENGTH * 2) - 1);
+                ss.miny = y & ~((MALI_TILE_LENGTH * 2) - 1);
+                ss.maxx = MIN2(ALIGN(x + w, MALI_TILE_LENGTH * 2),
+                               res->width0);
+                ss.maxy = MIN2(ALIGN(y + h, 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)
@@ -420,6 +509,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 89a4396c0939..c9fc9f691518 100644
--- a/src/gallium/drivers/panfrost/pan_resource.h
+++ b/src/gallium/drivers/panfrost/pan_resource.h
@@ -95,6 +95,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;
@@ -146,6 +150,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 *box);
+
+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 d6b1bc89fc19..31497a6a7bee 100644
--- a/src/gallium/drivers/panfrost/pan_screen.c
+++ b/src/gallium/drivers/panfrost/pan_screen.c
@@ -600,6 +600,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;

Comments