From 0a62a874fc5b7387fb4e1da9183fb2c5a9d4b700 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sun, 26 Mar 2023 13:00:25 -0700 Subject: [PATCH] freedreno: Re-work dirty-resource tracking If a resource is dirty but already tracked by the current batch, no need to process it at draw time. Note that the batch could change (ie. new fb state bound, etc) after the check if we need resource dirty tracking, but in these cases all the dirty-resource state is marked dirty. Signed-off-by: Rob Clark Part-of: --- src/gallium/drivers/freedreno/freedreno_context.h | 49 +++++++------------ src/gallium/drivers/freedreno/freedreno_draw.c | 8 ++-- src/gallium/drivers/freedreno/freedreno_resource.h | 55 ++++++++++++++++++++++ src/gallium/drivers/freedreno/freedreno_state.c | 28 ++++++----- src/gallium/drivers/freedreno/freedreno_texture.c | 2 + 5 files changed, 94 insertions(+), 48 deletions(-) diff --git a/src/gallium/drivers/freedreno/freedreno_context.h b/src/gallium/drivers/freedreno/freedreno_context.h index 00cf149..6d861cb 100644 --- a/src/gallium/drivers/freedreno/freedreno_context.h +++ b/src/gallium/drivers/freedreno/freedreno_context.h @@ -177,9 +177,6 @@ enum fd_dirty_3d_state { FD_DIRTY_BLEND_DUAL = BIT(26), FD_DIRTY_BLEND_COHERENT = BIT(27), #define NUM_DIRTY_BITS 28 - - /* additional flag for state requires updated resource tracking: */ - FD_DIRTY_RESOURCE = BIT(31), }; /* per shader-stage dirty state: */ @@ -395,9 +392,15 @@ struct fd_context { /* which state objects need to be re-emit'd: */ BITMASK_ENUM(fd_dirty_3d_state) dirty dt; + /* As above, but also needs draw time resource tracking: */ + BITMASK_ENUM(fd_dirty_3d_state) dirty_resource dt; + /* per shader-stage dirty status: */ BITMASK_ENUM(fd_dirty_shader_state) dirty_shader[PIPE_SHADER_TYPES] dt; + /* As above, but also needs draw time resource tracking: */ + BITMASK_ENUM(fd_dirty_shader_state) dirty_shader_resource[PIPE_SHADER_TYPES] dt; + void *compute dt; struct pipe_blend_state *blend dt; struct pipe_rasterizer_state *rasterizer dt; @@ -589,31 +592,6 @@ fd_stream_output_target(struct pipe_stream_output_target *target) return (struct fd_stream_output_target *)target; } -/** - * Does the dirty state require resource tracking, ie. in general - * does it reference some resource. There are some special cases: - * - * - FD_DIRTY_CONST can reference a resource, but cb0 is handled - * specially as if it is not a user-buffer, we expect it to be - * coming from const_uploader, so we can make some assumptions - * that future transfer_map will be UNSYNCRONIZED - * - FD_DIRTY_ZSA controls how the framebuffer is accessed - * - FD_DIRTY_BLEND needs to update GMEM reason - * - * TODO if we can make assumptions that framebuffer state is bound - * first, before blend/zsa/etc state we can move some of the ZSA/ - * BLEND state handling from draw time to bind time. I think this - * is true of mesa/st, perhaps we can just document it to be a - * frontend requirement? - */ -static inline bool -fd_context_dirty_resource(enum fd_dirty_3d_state dirty) -{ - return dirty & (FD_DIRTY_FRAMEBUFFER | FD_DIRTY_ZSA | - FD_DIRTY_SSBO | FD_DIRTY_IMAGE | FD_DIRTY_VTXBUF | - FD_DIRTY_TEX | FD_DIRTY_STREAMOUT | FD_DIRTY_QUERY); -} - /* Mark specified non-shader-stage related state as dirty: */ static inline void fd_context_dirty(struct fd_context *ctx, BITMASK_ENUM(fd_dirty_3d_state) dirty) @@ -623,11 +601,11 @@ fd_context_dirty(struct fd_context *ctx, BITMASK_ENUM(fd_dirty_3d_state) dirty) assert(ffs(dirty) <= ARRAY_SIZE(ctx->gen_dirty_map)); ctx->gen_dirty |= ctx->gen_dirty_map[ffs(dirty) - 1]; - - if (fd_context_dirty_resource(dirty)) - dirty |= FD_DIRTY_RESOURCE; - ctx->dirty |= dirty; + + /* These are still not handled at bind time: */ + if (dirty & (FD_DIRTY_FRAMEBUFFER | FD_DIRTY_QUERY | FD_DIRTY_ZSA)) + ctx->dirty_resource |= dirty; } static inline enum fd_dirty_3d_state @@ -667,14 +645,17 @@ fd_context_all_dirty(struct fd_context *ctx) assert_dt { ctx->last.dirty = true; ctx->dirty = (enum fd_dirty_3d_state) ~0; + ctx->dirty_resource = (enum fd_dirty_3d_state) ~0; /* NOTE: don't use ~0 for gen_dirty, because the gen specific * emit code will loop over all the bits: */ ctx->gen_dirty = ctx->gen_all_dirty; - for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) + for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) { ctx->dirty_shader[i] = (enum fd_dirty_shader_state) ~0; + ctx->dirty_shader_resource[i] = (enum fd_dirty_shader_state) ~0; + } } static inline void @@ -682,9 +663,11 @@ fd_context_all_clean(struct fd_context *ctx) assert_dt { ctx->last.dirty = false; ctx->dirty = (enum fd_dirty_3d_state)0; + ctx->dirty_resource = (enum fd_dirty_3d_state)0; ctx->gen_dirty = 0; for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) { ctx->dirty_shader[i] = (enum fd_dirty_shader_state)0; + ctx->dirty_shader_resource[i] = (enum fd_dirty_shader_state)0; } } diff --git a/src/gallium/drivers/freedreno/freedreno_draw.c b/src/gallium/drivers/freedreno/freedreno_draw.c index 8c1d0aa..c651c88 100644 --- a/src/gallium/drivers/freedreno/freedreno_draw.c +++ b/src/gallium/drivers/freedreno/freedreno_draw.c @@ -70,7 +70,7 @@ batch_draw_tracking_for_dirty_bits(struct fd_batch *batch) assert_dt { struct fd_context *ctx = batch->ctx; struct pipe_framebuffer_state *pfb = &batch->framebuffer; - enum fd_dirty_3d_state dirty = ctx->dirty; + enum fd_dirty_3d_state dirty = ctx->dirty_resource; unsigned buffers = 0, restore_buffers = 0; if (dirty & (FD_DIRTY_FRAMEBUFFER | FD_DIRTY_ZSA)) { @@ -133,7 +133,7 @@ batch_draw_tracking_for_dirty_bits(struct fd_batch *batch) assert_dt } u_foreach_bit (s, ctx->bound_shader_stages) { - enum fd_dirty_shader_state dirty_shader = ctx->dirty_shader[s]; + enum fd_dirty_shader_state dirty_shader = ctx->dirty_shader_resource[s]; /* Mark constbuf as being read: */ if (dirty_shader & FD_DIRTY_SHADER_CONST) { @@ -204,7 +204,7 @@ needs_draw_tracking(struct fd_batch *batch, const struct pipe_draw_info *info, { struct fd_context *ctx = batch->ctx; - if (ctx->dirty & FD_DIRTY_RESOURCE) + if (ctx->dirty_resource) return true; if (info->index_size && !batch_references_resource(batch, info->index.resource)) @@ -240,7 +240,7 @@ batch_draw_tracking(struct fd_batch *batch, const struct pipe_draw_info *info, fd_screen_lock(ctx->screen); - if (ctx->dirty & FD_DIRTY_RESOURCE) + if (ctx->dirty_resource) batch_draw_tracking_for_dirty_bits(batch); /* Mark index buffer as being read */ diff --git a/src/gallium/drivers/freedreno/freedreno_resource.h b/src/gallium/drivers/freedreno/freedreno_resource.h index 6ec3776..c356798 100644 --- a/src/gallium/drivers/freedreno/freedreno_resource.h +++ b/src/gallium/drivers/freedreno/freedreno_resource.h @@ -381,6 +381,61 @@ fd_batch_resource_read(struct fd_batch *batch, fd_batch_resource_read_slowpath(batch, rsc); } +static inline bool +needs_dirty_resource(struct fd_context *ctx, struct pipe_resource *prsc, bool write) + assert_dt +{ + if (!prsc) + return false; + + struct fd_resource *rsc = fd_resource(prsc); + + /* Switching between draw and non_draw will dirty all state, so if + * we pick the wrong one, all the bits in the dirty_resource state + * will be set anyways.. so no harm, no foul. + */ + struct fd_batch *batch = ctx->batch_nondraw ? ctx->batch_nondraw : ctx->batch; + + if (!batch) + return false; + + if (write) + return rsc->track->write_batch != batch; + + return !fd_batch_references_resource(batch, rsc); +} + +static inline void +fd_dirty_resource(struct fd_context *ctx, struct pipe_resource *prsc, + BITMASK_ENUM(fd_dirty_3d_state) dirty, bool write) + assert_dt +{ + if (ctx->dirty_resource & dirty) + return; + + if (!needs_dirty_resource(ctx, prsc, write)) + return; + + ctx->dirty_resource |= dirty; +} + +static inline void +fd_dirty_shader_resource(struct fd_context *ctx, struct pipe_resource *prsc, + enum pipe_shader_type shader, + BITMASK_ENUM(fd_dirty_shader_state) dirty, + bool write) + assert_dt +{ + if (ctx->dirty_shader_resource[shader] & dirty) + return; + + if (!needs_dirty_resource(ctx, prsc, write)) + return; + + ctx->dirty_shader_resource[shader] |= dirty; + ctx->dirty_resource |= dirty_shader_to_dirty_state(dirty); +} + static inline enum fdl_view_type fdl_type_from_pipe_target(enum pipe_texture_target target) { switch (target) { diff --git a/src/gallium/drivers/freedreno/freedreno_state.c b/src/gallium/drivers/freedreno/freedreno_state.c index 5ca825a..a5da323 100644 --- a/src/gallium/drivers/freedreno/freedreno_state.c +++ b/src/gallium/drivers/freedreno/freedreno_state.c @@ -147,11 +147,7 @@ fd_set_constant_buffer(struct pipe_context *pctx, enum pipe_shader_type shader, fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_CONST); fd_resource_set_usage(cb->buffer, FD_DIRTY_CONST); - - if (index > 0) { - assert(!cb->user_buffer); - ctx->dirty |= FD_DIRTY_RESOURCE; - } + fd_dirty_shader_resource(ctx, cb->buffer, shader, FD_DIRTY_SHADER_CONST, false); } void @@ -176,11 +172,15 @@ fd_set_shader_buffers(struct pipe_context *pctx, enum pipe_shader_type shader, buf->buffer_size = buffers[i].buffer_size; pipe_resource_reference(&buf->buffer, buffers[i].buffer); + bool write = writable_bitmask & BIT(i); + fd_resource_set_usage(buffers[i].buffer, FD_DIRTY_SSBO); + fd_dirty_shader_resource(ctx, buffers[i].buffer, shader, + FD_DIRTY_SHADER_SSBO, write); so->enabled_mask |= BIT(n); - if (writable_bitmask & BIT(i)) { + if (write) { struct fd_resource *rsc = fd_resource(buf->buffer); util_range_add(&rsc->b.b, &rsc->valid_buffer_range, buf->buffer_offset, @@ -222,12 +222,14 @@ fd_set_shader_images(struct pipe_context *pctx, enum pipe_shader_type shader, util_copy_image_view(buf, &images[i]); if (buf->resource) { + bool write = buf->access & PIPE_IMAGE_ACCESS_WRITE; + fd_resource_set_usage(buf->resource, FD_DIRTY_IMAGE); + fd_dirty_shader_resource(ctx, buf->resource, shader, + FD_DIRTY_SHADER_IMAGE, write); so->enabled_mask |= BIT(n); - if ((buf->access & PIPE_IMAGE_ACCESS_WRITE) && - (buf->resource->target == PIPE_BUFFER)) { - + if (write && (buf->resource->target == PIPE_BUFFER)) { struct fd_resource *rsc = fd_resource(buf->resource); util_range_add(&rsc->b.b, &rsc->valid_buffer_range, buf->u.buf.offset, @@ -488,6 +490,7 @@ fd_set_vertex_buffers(struct pipe_context *pctx, unsigned start_slot, for (unsigned i = 0; i < count; i++) { assert(!vb[i].is_user_buffer); fd_resource_set_usage(vb[i].buffer.resource, FD_DIRTY_VTXBUF); + fd_dirty_resource(ctx, vb[i].buffer.resource, FD_DIRTY_VTXBUF, false); /* Robust buffer access: Return undefined data (the start of the buffer) * instead of process termination or a GPU hang in case of overflow. @@ -677,6 +680,11 @@ fd_set_stream_output_targets(struct pipe_context *pctx, unsigned num_targets, so->reset |= (reset << i); + if (targets[i]) { + fd_resource_set_usage(targets[i]->buffer, FD_DIRTY_STREAMOUT); + fd_dirty_resource(ctx, targets[i]->buffer, FD_DIRTY_STREAMOUT, true); + } + if (!changed && !reset) continue; @@ -688,8 +696,6 @@ fd_set_stream_output_targets(struct pipe_context *pctx, unsigned num_targets, ctx->streamout.verts_written = 0; } - if (so->targets[i]) - fd_resource_set_usage(so->targets[i]->buffer, FD_DIRTY_STREAMOUT); pipe_so_target_reference(&so->targets[i], targets[i]); } diff --git a/src/gallium/drivers/freedreno/freedreno_texture.c b/src/gallium/drivers/freedreno/freedreno_texture.c index 577303b..89fa3f1 100644 --- a/src/gallium/drivers/freedreno/freedreno_texture.c +++ b/src/gallium/drivers/freedreno/freedreno_texture.c @@ -93,6 +93,8 @@ fd_set_sampler_views(struct pipe_context *pctx, enum pipe_shader_type shader, if (tex->textures[p]) { fd_resource_set_usage(tex->textures[p]->texture, FD_DIRTY_TEX); + fd_dirty_shader_resource(ctx, tex->textures[p]->texture, + shader, FD_DIRTY_SHADER_TEX, false); tex->valid_textures |= (1 << p); } else { tex->valid_textures &= ~(1 << p); -- 2.7.4