From 6ad3944290e9b027d3da3e7934b838d68671a5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Molinari?= Date: Tue, 31 Mar 2026 14:48:37 +0200 Subject: [PATCH] panfrost: Add infrastructure for fullscreen draw calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Panfrost blits are implemented using u_blitter which exposes the draw_rectangle handler for drivers to blit with dedicated GPU support. By default, it ends up blitting with a draw_vbo call on the pipe. In Panfrost, draw_vbo then emits a full-featured draw call using an indexed or malloc vertex shader job on pre-CSF GPUs or a RUN_IDVS instruction on CSF GPUs. Since v9, Mali GPUs expose a lighter draw call with the FullScreenJob descriptor on pre-CSF GPUs or with the RUN_FULLSCREEN instruction on CSF GPUs. These draw calls emit a quad primitive into the polygon list and run tiling of a fullscreen fragment job without vertex processing. This commit adds the infrastructure to implement blits using fullscreen draw calls. It supports all types of blits apart from the instanced ones. Partial blits are supported using scissors and textured blits are supported using varying interpolation and fragment shading. Signed-off-by: Loïc Molinari Reviewed-by: Ashley Smith Acked-by: Boris Brezillon Part-of: --- src/gallium/drivers/panfrost/pan_blit.c | 71 ++++++++++++++++++++ src/gallium/drivers/panfrost/pan_cmdstream.c | 38 +++++++++++ src/gallium/drivers/panfrost/pan_context.c | 2 +- src/gallium/drivers/panfrost/pan_csf.c | 8 +++ src/gallium/drivers/panfrost/pan_csf.h | 4 ++ src/gallium/drivers/panfrost/pan_jm.c | 8 +++ src/gallium/drivers/panfrost/pan_jm.h | 4 ++ src/gallium/drivers/panfrost/pan_resource.h | 2 + src/gallium/drivers/panfrost/pan_screen.h | 8 +++ 9 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/gallium/drivers/panfrost/pan_blit.c b/src/gallium/drivers/panfrost/pan_blit.c index 3ec8933e37c..2430195cdb1 100644 --- a/src/gallium/drivers/panfrost/pan_blit.c +++ b/src/gallium/drivers/panfrost/pan_blit.c @@ -10,6 +10,77 @@ #include "pan_trace.h" #include "pan_util.h" +static void +panfrost_blitter_draw_rectangle(struct blitter_context *blitter, + void *vertex_elements_cso, + blitter_get_vs_func get_vs, + int x1, int y1, int x2, int y2, + float depth, unsigned num_instances, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib) +{ + assert(num_instances); + + struct pipe_context *ctx = blitter->pipe; + struct panfrost_context *pctx = pan_context(ctx); + struct panfrost_screen *scr = pan_screen(ctx->screen); + + /* Always fallback for now. */ + goto fallback; + + /* Map viewport to the dest rect of the framebuffer. The tiler will then be + * configured to use it as scissor box in order to clip fullscreen + * fragments lying outside. + * + * Note that: tx = x1 + ((x2 - x1) / 2) = (x2 + x1) / 2 + * ty = y1 + ((y2 - y1) / 2) = (y2 + y1) / 2 + */ + const struct pipe_viewport_state viewport_state = { + .scale = { 0.5f * (x2 - x1), 0.5f * (y2 - y1), 1.0f }, + .translate = { 0.5f * (x2 + x1), 0.5f * (y2 + y1), 0.0f }, + .swizzle_x = PIPE_VIEWPORT_SWIZZLE_POSITIVE_X, + .swizzle_y = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Y, + .swizzle_z = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Z, + .swizzle_w = PIPE_VIEWPORT_SWIZZLE_POSITIVE_W + }; + ctx->set_viewport_states(ctx, 0, 1, &viewport_state); + + /* Map texture coordinates to the fullscreen framebuffer. */ + struct blitter_attrib fs_attrib; + if (type == UTIL_BLITTER_ATTRIB_TEXCOORD_XY || + type == UTIL_BLITTER_ATTRIB_TEXCOORD_XYZW) { + float dfdx = (attrib->texcoord.x2 - attrib->texcoord.x1) / (x2 - x1); + float dfdy = (attrib->texcoord.y2 - attrib->texcoord.y1) / (y2 - y1); + float w = pctx->pipe_framebuffer.width; + float h = pctx->pipe_framebuffer.height; + fs_attrib = *attrib; + fs_attrib.texcoord.x1 -= dfdx * x1; + fs_attrib.texcoord.y1 -= dfdy * y1; + fs_attrib.texcoord.x2 += dfdx * (w - x2); + fs_attrib.texcoord.y2 += dfdy * (h - y2); + }; + + scr->vtbl.draw_fullscreen(pan_context(ctx), get_vs(blitter), type, + &fs_attrib); + return; + + fallback: + /* Fallback to draw_vbo. */ + util_blitter_draw_rectangle(blitter, vertex_elements_cso, get_vs, x1, y1, + x2, y2, depth, num_instances, type, attrib); +} + +struct blitter_context * +panfrost_blitter_create(struct pipe_context *pipe) +{ + struct blitter_context *blitter; + + blitter = util_blitter_create(pipe); + blitter->draw_rectangle = panfrost_blitter_draw_rectangle; + + return blitter; +} + void panfrost_blitter_save(struct panfrost_context *ctx, const enum panfrost_blitter_op blitter_op) diff --git a/src/gallium/drivers/panfrost/pan_cmdstream.c b/src/gallium/drivers/panfrost/pan_cmdstream.c index 58b044c5295..41227ef3fe6 100644 --- a/src/gallium/drivers/panfrost/pan_cmdstream.c +++ b/src/gallium/drivers/panfrost/pan_cmdstream.c @@ -3665,6 +3665,43 @@ panfrost_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info, } } +static void +panfrost_draw_fullscreen(struct panfrost_context *ctx, + struct panfrost_uncompiled_shader *vs, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib) +{ + assert(!ctx->active_queries); + assert(!ctx->streamout.num_targets); + + PAN_TRACE_FUNC(PAN_TRACE_GL_CMDSTREAM); + + ctx->draw_calls++; + + struct panfrost_batch *batch = prepare_draw(&ctx->base, MESA_PRIM_QUADS); + if (!batch) { + mesa_loge("prepare_draw failed"); + return; + } + + /* Fullscreen draw calls don't configure any position or varying shader but + * link info is needed. The active primitive update takes care of the + * fragment shader variant update. */ + ctx->uncompiled[MESA_SHADER_VERTEX] = vs; + panfrost_update_shader_variant(ctx, MESA_SHADER_VERTEX); + panfrost_update_active_prim(ctx, MESA_PRIM_QUADS); + + /* Clear the dirty vertex flag to ensure the shader state update doesn't + * emit any vertex info. */ + ctx->dirty &= ~PAN_DIRTY_VERTEX; + panfrost_update_state_3d(batch); + panfrost_update_shader_state(batch, MESA_SHADER_FRAGMENT); + panfrost_clean_state_3d(ctx); + + JOBX(launch_draw_fullscreen)(batch, type, attrib); + batch->draw_count++; +} + /* Launch grid is the compute equivalent of draw_vbo, so in this routine, we * construct the COMPUTE job and some of its payload. */ @@ -4683,6 +4720,7 @@ GENX(panfrost_cmdstream_screen_init)(struct panfrost_screen *screen) screen->vtbl.emit_write_timestamp = emit_write_timestamp; screen->vtbl.select_tile_size = GENX(pan_select_tile_size); screen->vtbl.get_conv_desc = get_conv_desc; + screen->vtbl.draw_fullscreen = panfrost_draw_fullscreen; pan_blend_shader_cache_init(&dev->blend_shaders, panfrost_device_gpu_id(dev), dev->kmod.dev->props.gpu_variant, diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c index e5dd965cb5a..c6c66d11330 100644 --- a/src/gallium/drivers/panfrost/pan_context.c +++ b/src/gallium/drivers/panfrost/pan_context.c @@ -1100,7 +1100,7 @@ panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags) goto failed; } - ctx->blitter = util_blitter_create(gallium); + ctx->blitter = panfrost_blitter_create(gallium); ctx->writers = _mesa_hash_table_create(gallium, _mesa_hash_pointer, _mesa_key_pointer_equal); diff --git a/src/gallium/drivers/panfrost/pan_csf.c b/src/gallium/drivers/panfrost/pan_csf.c index 35207942b62..48ee647585d 100644 --- a/src/gallium/drivers/panfrost/pan_csf.c +++ b/src/gallium/drivers/panfrost/pan_csf.c @@ -1461,6 +1461,14 @@ GENX(csf_launch_draw_indirect)(struct panfrost_batch *batch, } } +void +GENX(csf_launch_draw_fullscreen)(struct panfrost_batch *batch, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib) +{ + UNREACHABLE("draw fullscreen not implemented for csf"); +} + #define POSITION_FIFO_SIZE (64 * 1024) static enum drm_panthor_group_priority diff --git a/src/gallium/drivers/panfrost/pan_csf.h b/src/gallium/drivers/panfrost/pan_csf.h index b7be8be2339..d649b58dc56 100644 --- a/src/gallium/drivers/panfrost/pan_csf.h +++ b/src/gallium/drivers/panfrost/pan_csf.h @@ -83,6 +83,7 @@ struct panfrost_csf_context { #if defined(PAN_ARCH) && PAN_ARCH >= 10 #include "genxml/gen_macros.h" +#include "util/u_blitter.h" struct panfrost_batch; struct panfrost_context; @@ -120,6 +121,9 @@ void GENX(csf_launch_draw_indirect)(struct panfrost_batch *batch, const struct pipe_draw_info *info, unsigned drawid_offset, const struct pipe_draw_indirect_info *indirect); +void GENX(csf_launch_draw_fullscreen)(struct panfrost_batch *batch, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib); void GENX(csf_emit_write_timestamp)(struct panfrost_batch *batch, struct panfrost_resource *dst, diff --git a/src/gallium/drivers/panfrost/pan_jm.c b/src/gallium/drivers/panfrost/pan_jm.c index 620800d4efb..fdcd060d7af 100644 --- a/src/gallium/drivers/panfrost/pan_jm.c +++ b/src/gallium/drivers/panfrost/pan_jm.c @@ -1020,6 +1020,14 @@ GENX(jm_launch_draw_indirect)(struct panfrost_batch *batch, UNREACHABLE("draw indirect not implemented for jm"); } +void +GENX(jm_launch_draw_fullscreen)(struct panfrost_batch *batch, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib) +{ + UNREACHABLE("draw fullscreen not implemented for jm"); +} + void GENX(jm_emit_write_timestamp)(struct panfrost_batch *batch, struct panfrost_resource *dst, unsigned offset) diff --git a/src/gallium/drivers/panfrost/pan_jm.h b/src/gallium/drivers/panfrost/pan_jm.h index 22ca685d67a..947b2a45acb 100644 --- a/src/gallium/drivers/panfrost/pan_jm.h +++ b/src/gallium/drivers/panfrost/pan_jm.h @@ -26,6 +26,7 @@ struct panfrost_jm_batch { #if defined(PAN_ARCH) && PAN_ARCH < 10 #include "genxml/gen_macros.h" +#include "util/u_blitter.h" struct panfrost_batch; struct panfrost_context; @@ -75,6 +76,9 @@ void GENX(jm_launch_draw_indirect)(struct panfrost_batch *batch, const struct pipe_draw_info *info, unsigned drawid_offset, const struct pipe_draw_indirect_info *indirect); +void GENX(jm_launch_draw_fullscreen)(struct panfrost_batch *batch, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib); void GENX(jm_emit_write_timestamp)(struct panfrost_batch *batch, struct panfrost_resource *dst, diff --git a/src/gallium/drivers/panfrost/pan_resource.h b/src/gallium/drivers/panfrost/pan_resource.h index 5220b5a11d4..95bf4075c24 100644 --- a/src/gallium/drivers/panfrost/pan_resource.h +++ b/src/gallium/drivers/panfrost/pan_resource.h @@ -167,6 +167,8 @@ enum { PAN_RENDER_CLEAR = PAN_SAVE_FRAGMENT_STATE | PAN_SAVE_FRAGMENT_CONSTANT, }; +struct blitter_context *panfrost_blitter_create(struct pipe_context *pipe); + /* Callers should ensure that all AFBC/AFRC resources that will be used in the * blit operation are legalized before calling blitter operations, otherwise * we may trigger a recursive blit */ diff --git a/src/gallium/drivers/panfrost/pan_screen.h b/src/gallium/drivers/panfrost/pan_screen.h index 14eb7ea59fd..29e576256dc 100644 --- a/src/gallium/drivers/panfrost/pan_screen.h +++ b/src/gallium/drivers/panfrost/pan_screen.h @@ -15,6 +15,7 @@ #include "util/disk_cache.h" #include "util/log.h" #include "util/set.h" +#include "util/u_blitter.h" #include "util/u_dynarray.h" #include "pan_device.h" @@ -30,6 +31,7 @@ struct panfrost_batch; struct panfrost_context; struct panfrost_resource; struct panfrost_compiled_shader; +struct panfrost_uncompiled_shader; struct pan_fb_info; struct pan_blend_state; @@ -92,6 +94,12 @@ struct panfrost_vtable { /* construct a render target blend descriptor */ uint64_t (*get_conv_desc)(enum pipe_format fmt, unsigned rt, unsigned force_size, bool dithered); + + /* Run a fullscreen draw call (for blits) */ + void (*draw_fullscreen)(struct panfrost_context *ctx, + struct panfrost_uncompiled_shader *vs, + enum blitter_attrib_type type, + const struct blitter_attrib *attrib); }; struct panfrost_screen {