panfrost: Add support for KHR_partial_update()
authorBoris Brezillon <boris.brezillon@collabora.com>
Mon, 12 Aug 2019 10:07:08 +0000 (12:07 +0200)
committerBoris Brezillon <boris.brezillon@collabora.com>
Tue, 13 Aug 2019 12:41:10 +0000 (14:41 +0200)
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>
Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
src/gallium/drivers/panfrost/pan_blit.c
src/gallium/drivers/panfrost/pan_context.c
src/gallium/drivers/panfrost/pan_job.c
src/gallium/drivers/panfrost/pan_job.h
src/gallium/drivers/panfrost/pan_resource.c
src/gallium/drivers/panfrost/pan_resource.h
src/gallium/drivers/panfrost/pan_screen.c

index 461bd8eec351ab85509c2120fa204c335028609d..4be8c044ee2f93ed33217660513546fedd9da365 100644 (file)
@@ -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;
index 409c852a228e229f6de69602f5b036ff33f2a7f2..7b80da2795ab83da3dfb1027dd92e99ee3ca6ff6 100644 (file)
@@ -1379,7 +1379,80 @@ 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);
+
+        /* 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.
+         */
+        batch->minx = batch->minx & ~((MALI_TILE_LENGTH * 2) - 1);
+        batch->miny = batch->miny & ~((MALI_TILE_LENGTH * 2) - 1);
+        batch->maxx = MIN2(ALIGN_POT(batch->maxx, MALI_TILE_LENGTH * 2),
+                           rsrc->base.width0);
+        batch->maxy = MIN2(ALIGN_POT(batch->maxy, MALI_TILE_LENGTH * 2),
+                           rsrc->base.height0);
+
+        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;
 }
 
index 661f8ae154e269e6aee93d0d5dd67553729f0dda..bfa73a7f334f0996e638184d3ee9e83e759b629d 100644 (file)
@@ -419,6 +419,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)
 {
index a5ea5bf41cc152adf61e1f06083bb975bb565e2c..0f559b861a01ae1e3448589fc774a34685f1a4d8 100644 (file)
@@ -168,6 +168,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
index f74a39555b45fa114a78346cef7f4e82df3d66d5..1f5b23145cf3026a641b5c725b0b532d1b5e90bf 100644 (file)
@@ -403,6 +403,84 @@ panfrost_resource_create_bo(struct panfrost_screen *screen, struct panfrost_reso
         pres->bo = panfrost_drm_create_bo(screen, bo_size, PAN_ALLOCATE_DELAY_MMAP);
 }
 
+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,
+                                    const struct pipe_box *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++) {
+                int x = rects[i].x, w = rects[i].width, h = rects[i].height;
+                int y = res->height0 - (rects[i].y + h);
+
+                /* 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);
+
+                damage_extent->minx = MIN2(damage_extent->minx, x);
+                damage_extent->miny = MIN2(damage_extent->miny, y);
+                damage_extent->maxx = MAX2(damage_extent->maxx,
+                                           MIN2(x + w, res->width0));
+                damage_extent->maxy = MAX2(damage_extent->maxy,
+                                           MIN2(y + h, res->height0));
+        }
+}
+
 static struct pipe_resource *
 panfrost_resource_create(struct pipe_screen *screen,
                          const struct pipe_resource *template)
@@ -437,6 +515,8 @@ panfrost_resource_create(struct pipe_screen *screen,
         util_range_init(&so->valid_buffer_range);
 
         panfrost_resource_create_bo(pscreen, so);
+        panfrost_resource_reset_damage(so);
+
         return (struct pipe_resource *)so;
 }
 
index e51ba8959c3af485abccb40231eb1c72f7652d54..6ed3d1fd60e848125ad4790a602f53adc62eb538 100644 (file)
@@ -65,6 +65,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;
@@ -134,6 +138,13 @@ 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,
+                                    const struct pipe_box *rects);
 
 #endif /* PAN_RESOURCE_H */
index 510828ce555aab98545647899ff127cb4d4a6aa5..d56897729a382ddb2e6221c6ba7a024093b7b49f 100644 (file)
@@ -659,6 +659,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;