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

Alyssa Rosenzweig alyssa.rosenzweig at collabora.com
Tue Jul 2 16:47:43 UTC 2019


Very nice work!

Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig at collabora.com>

On Tue, Jul 02, 2019 at 03:50:02PM +0200, Boris Brezillon wrote:
> 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>
> ---
> 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(-)
> 
> 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;
> -- 
> 2.21.0
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20190702/64b92a78/attachment-0001.sig>


More information about the mesa-dev mailing list