From e14f525280ee9f09988239adbead1c9dff5476f0 Mon Sep 17 00:00:00 2001 From: Danylo Piliaiev Date: Mon, 24 May 2021 12:11:46 +0300 Subject: [PATCH] util/u_trace: make u_trace usable for other than gallium drivers With little modifications u_trace could be usable for Vulkan drivers. Beside removing dependencies on gallium, the other notable change is the passing of opaque flush_data pointer via u_trace_flush. There is data which becomes available only at this point which other drivers may want to pass. For example Vulkan drivers would want to pass at least submission id (for perfetto) and a sync object to wait on in u_trace_read_ts. Signed-off-by: Danylo Piliaiev Reviewed-by: Rob Clark Part-of: --- src/gallium/auxiliary/meson.build | 12 +-- src/gallium/auxiliary/util/u_trace_gallium.c | 96 +++++++++++++++++++ src/gallium/auxiliary/util/u_trace_gallium.h | 57 +++++++++++ src/gallium/auxiliary/util/u_tracepoints.py | 2 +- .../drivers/freedreno/freedreno_batch.h | 2 +- .../drivers/freedreno/freedreno_context.c | 25 +++-- .../drivers/freedreno/freedreno_context.h | 2 +- .../drivers/freedreno/freedreno_gmem.c | 3 +- .../drivers/freedreno/freedreno_perfetto.cc | 14 +++ .../freedreno/freedreno_tracepoints.py | 2 +- src/gallium/drivers/freedreno/meson.build | 2 +- src/util/meson.build | 6 ++ .../auxiliary/util => util/perf}/u_trace.c | 77 ++++++++------- .../auxiliary/util => util/perf}/u_trace.h | 79 ++++++++------- .../auxiliary/util => util/perf}/u_trace.py | 30 +++++- .../util => util/perf}/u_trace_priv.h | 2 +- src/{gallium/auxiliary => }/util/u_fifo.h | 0 17 files changed, 317 insertions(+), 94 deletions(-) create mode 100644 src/gallium/auxiliary/util/u_trace_gallium.c create mode 100644 src/gallium/auxiliary/util/u_trace_gallium.h rename src/{gallium/auxiliary/util => util/perf}/u_trace.c (84%) rename src/{gallium/auxiliary/util => util/perf}/u_trace.h (80%) rename src/{gallium/auxiliary/util => util/perf}/u_trace.py (91%) rename src/{gallium/auxiliary/util => util/perf}/u_trace_priv.h (95%) rename src/{gallium/auxiliary => }/util/u_fifo.h (100%) diff --git a/src/gallium/auxiliary/meson.build b/src/gallium/auxiliary/meson.build index ca272fe428d..1f16c598674 100644 --- a/src/gallium/auxiliary/meson.build +++ b/src/gallium/auxiliary/meson.build @@ -260,7 +260,6 @@ files_libgallium = files( 'util/u_dump_defines.c', 'util/u_dump.h', 'util/u_dump_state.c', - 'util/u_fifo.h', 'util/u_framebuffer.c', 'util/u_framebuffer.h', 'util/u_gen_mipmap.c', @@ -315,9 +314,6 @@ files_libgallium = files( 'util/u_texture.h', 'util/u_tile.c', 'util/u_tile.h', - 'util/u_trace.c', - 'util/u_trace.h', - 'util/u_trace_priv.h', 'util/u_transfer.c', 'util/u_transfer.h', 'util/u_transfer_helper.c', @@ -325,6 +321,8 @@ files_libgallium = files( 'util/u_threaded_context.c', 'util/u_threaded_context.h', 'util/u_threaded_context_calls.h', + 'util/u_trace_gallium.c', + 'util/u_trace_gallium.h', 'util/u_upload_mgr.c', 'util/u_upload_mgr.h', 'util/u_vbuf.c', @@ -484,15 +482,13 @@ if with_dri2 and with_platform_x11 endif endif -u_trace_py = files('util/u_trace.py') - files_libgallium += custom_target( 'u_tracepoints.c', input: 'util/u_tracepoints.py', output: 'u_tracepoints.c', command: [ prog_python, '@INPUT@', - '-p', join_paths(meson.source_root(), 'src/gallium/auxiliary/util/'), + '-p', join_paths(meson.source_root(), 'src/util/perf/'), '-C', '@OUTPUT@', ], depend_files: u_trace_py, @@ -504,7 +500,7 @@ files_u_tracepoints = custom_target( output: 'u_tracepoints.h', command: [ prog_python, '@INPUT@', - '-p', join_paths(meson.source_root(), 'src/gallium/auxiliary/util/'), + '-p', join_paths(meson.source_root(), 'src/util/perf/'), '-H', '@OUTPUT@', ], depend_files: u_trace_py, diff --git a/src/gallium/auxiliary/util/u_trace_gallium.c b/src/gallium/auxiliary/util/u_trace_gallium.c new file mode 100644 index 00000000000..dbc7f4d30cb --- /dev/null +++ b/src/gallium/auxiliary/util/u_trace_gallium.c @@ -0,0 +1,96 @@ +/* + * Copyright © 2020 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "u_trace_gallium.h" +#include "u_inlines.h" +#include "pipe/p_state.h" +#include "pipe/p_context.h" +#include "pipe/p_screen.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static void * +u_trace_pipe_create_ts_buffer(struct u_trace_context *utctx, uint32_t size) +{ + struct pipe_context *ctx = utctx->pctx; + + struct pipe_resource tmpl = { + .target = PIPE_BUFFER, + .format = PIPE_FORMAT_R8_UNORM, + .bind = PIPE_BIND_QUERY_BUFFER | PIPE_BIND_LINEAR, + .width0 = size, + .height0 = 1, + .depth0 = 1, + .array_size = 1, + }; + + return ctx->screen->resource_create(ctx->screen, &tmpl); +} + +static void +u_trace_pipe_delete_ts_buffer(struct u_trace_context *utctx, void *timestamps) +{ + struct pipe_resource *buffer = timestamps; + pipe_resource_reference(&buffer, NULL); +} + +void +u_trace_pipe_context_init(struct u_trace_context *utctx, + struct pipe_context *pctx, + u_trace_record_ts record_timestamp, + u_trace_read_ts read_timestamp, + u_trace_delete_flush_data delete_flush_data) +{ + u_trace_context_init(utctx, pctx, + u_trace_pipe_create_ts_buffer, + u_trace_pipe_delete_ts_buffer, + record_timestamp, + read_timestamp, + delete_flush_data); +} + +void __trace_surface(struct u_trace *ut, const struct pipe_surface *psurf); +void __trace_framebuffer(struct u_trace *ut, const struct pipe_framebuffer_state *pfb); + +inline void +trace_framebuffer_state(struct u_trace *ut, const struct pipe_framebuffer_state *pfb) +{ + if (likely(!ut->enabled)) + return; + + __trace_framebuffer(ut, pfb); + for (unsigned i = 0; i < pfb->nr_cbufs; i++) { + if (pfb->cbufs[i]) { + __trace_surface(ut, pfb->cbufs[i]); + } + } + if (pfb->zsbuf) { + __trace_surface(ut, pfb->zsbuf); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/src/gallium/auxiliary/util/u_trace_gallium.h b/src/gallium/auxiliary/util/u_trace_gallium.h new file mode 100644 index 00000000000..f4df9be9f5c --- /dev/null +++ b/src/gallium/auxiliary/util/u_trace_gallium.h @@ -0,0 +1,57 @@ +/* + * Copyright © 2020 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _U_TRACE_GALLIUM_H +#define _U_TRACE_GALLIUM_H + +#include "util/perf/u_trace.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Gallium specific u_trace helpers */ + +struct pipe_context; +struct pipe_framebuffer_state; + +void +u_trace_pipe_context_init(struct u_trace_context *utctx, + struct pipe_context *pctx, + u_trace_record_ts record_timestamp, + u_trace_read_ts read_timestamp, + u_trace_delete_flush_data delete_flush_data); + +/* + * In some cases it is useful to have composite tracepoints like this, + * to log more complex data structures. + */ + +void +trace_framebuffer_state(struct u_trace *ut, const struct pipe_framebuffer_state *pfb); + +#ifdef __cplusplus +} +#endif + +#endif /* _U_TRACE_GALLIUM_H */ diff --git a/src/gallium/auxiliary/util/u_tracepoints.py b/src/gallium/auxiliary/util/u_tracepoints.py index f8a70d05c09..a9b83215c39 100644 --- a/src/gallium/auxiliary/util/u_tracepoints.py +++ b/src/gallium/auxiliary/util/u_tracepoints.py @@ -89,4 +89,4 @@ Tracepoint('grid_info', '__entry->grid_x', '__entry->grid_y', '__entry->grid_z'], ) -utrace_generate(cpath=args.src, hpath=args.hdr) +utrace_generate(cpath=args.src, hpath=args.hdr, ctx_param='struct pipe_context *pctx') diff --git a/src/gallium/drivers/freedreno/freedreno_batch.h b/src/gallium/drivers/freedreno/freedreno_batch.h index 842537f8c47..7b554c3cceb 100644 --- a/src/gallium/drivers/freedreno/freedreno_batch.h +++ b/src/gallium/drivers/freedreno/freedreno_batch.h @@ -31,7 +31,7 @@ #include "util/simple_mtx.h" #include "util/u_inlines.h" #include "util/u_queue.h" -#include "util/u_trace.h" +#include "util/perf/u_trace.h" #include "freedreno_context.h" #include "freedreno_fence.h" diff --git a/src/gallium/drivers/freedreno/freedreno_context.c b/src/gallium/drivers/freedreno/freedreno_context.c index b0f85e9ffc5..cd95721746c 100644 --- a/src/gallium/drivers/freedreno/freedreno_context.c +++ b/src/gallium/drivers/freedreno/freedreno_context.c @@ -38,6 +38,7 @@ #include "freedreno_state.h" #include "freedreno_texture.h" #include "freedreno_util.h" +#include "util/u_trace_gallium.h" static void fd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep, @@ -451,30 +452,32 @@ fd_get_device_reset_status(struct pipe_context *pctx) } static void -fd_trace_record_ts(struct u_trace *ut, struct pipe_resource *timestamps, +fd_trace_record_ts(struct u_trace *ut, void *timestamps, unsigned idx) { struct fd_batch *batch = container_of(ut, struct fd_batch, trace); struct fd_ringbuffer *ring = batch->nondraw ? batch->draw : batch->gmem; + struct pipe_resource *buffer = timestamps; if (ring->cur == batch->last_timestamp_cmd) { - uint64_t *ts = fd_bo_map(fd_resource(timestamps)->bo); + uint64_t *ts = fd_bo_map(fd_resource(buffer)->bo); ts[idx] = U_TRACE_NO_TIMESTAMP; return; } unsigned ts_offset = idx * sizeof(uint64_t); - batch->ctx->record_timestamp(ring, fd_resource(timestamps)->bo, ts_offset); + batch->ctx->record_timestamp(ring, fd_resource(buffer)->bo, ts_offset); batch->last_timestamp_cmd = ring->cur; } static uint64_t fd_trace_read_ts(struct u_trace_context *utctx, - struct pipe_resource *timestamps, unsigned idx) + void *timestamps, unsigned idx, void *flush_data) { struct fd_context *ctx = container_of(utctx, struct fd_context, trace_context); - struct fd_bo *ts_bo = fd_resource(timestamps)->bo; + struct pipe_resource *buffer = timestamps; + struct fd_bo *ts_bo = fd_resource(buffer)->bo; /* Only need to stall on results for the first entry: */ if (idx == 0) { @@ -497,6 +500,12 @@ fd_trace_read_ts(struct u_trace_context *utctx, return ctx->ts_to_ns(ts[idx]); } +static void +fd_trace_delete_flush_data(struct u_trace_context *utctx, void *flush_data) +{ + /* We don't use flush_data at the moment. */ +} + /* TODO we could combine a few of these small buffers (solid_vbuf, * blit_texcoord_vbuf, and vsc_size_mem, into a single buffer and * save a tiny bit of memory @@ -667,8 +676,10 @@ fd_context_init(struct fd_context *ctx, struct pipe_screen *pscreen, ctx->current_scissor = &ctx->disabled_scissor; - u_trace_context_init(&ctx->trace_context, pctx, fd_trace_record_ts, - fd_trace_read_ts); + u_trace_pipe_context_init(&ctx->trace_context, pctx, + fd_trace_record_ts, + fd_trace_read_ts, + fd_trace_delete_flush_data); fd_autotune_init(&ctx->autotune, screen->dev); diff --git a/src/gallium/drivers/freedreno/freedreno_context.h b/src/gallium/drivers/freedreno/freedreno_context.h index 6633e7992cf..d07fa618aa2 100644 --- a/src/gallium/drivers/freedreno/freedreno_context.h +++ b/src/gallium/drivers/freedreno/freedreno_context.h @@ -34,7 +34,7 @@ #include "util/u_blitter.h" #include "util/u_string.h" #include "util/u_threaded_context.h" -#include "util/u_trace.h" +#include "util/perf/u_trace.h" #include "freedreno_autotune.h" #include "freedreno_gmem.h" diff --git a/src/gallium/drivers/freedreno/freedreno_gmem.c b/src/gallium/drivers/freedreno/freedreno_gmem.c index 47f4e6f6754..3cf36bef139 100644 --- a/src/gallium/drivers/freedreno/freedreno_gmem.c +++ b/src/gallium/drivers/freedreno/freedreno_gmem.c @@ -33,6 +33,7 @@ #include "util/u_memory.h" #include "util/u_string.h" #include "u_tracepoints.h" +#include "util/u_trace_gallium.h" #include "freedreno_context.h" #include "freedreno_fence.h" @@ -761,7 +762,7 @@ fd_gmem_render_tiles(struct fd_batch *batch) flush_ring(batch); - u_trace_flush(&batch->trace); + u_trace_flush(&batch->trace, NULL, false); } /* Determine a worst-case estimate (ie. assuming we don't eliminate an diff --git a/src/gallium/drivers/freedreno/freedreno_perfetto.cc b/src/gallium/drivers/freedreno/freedreno_perfetto.cc index bc10a510b03..20b7c513edd 100644 --- a/src/gallium/drivers/freedreno/freedreno_perfetto.cc +++ b/src/gallium/drivers/freedreno/freedreno_perfetto.cc @@ -322,6 +322,7 @@ fd_perfetto_submit(struct fd_context *ctx) void fd_start_render_pass(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_render_pass *payload) { stage_start(pctx, ts_ns, SURFACE_STAGE_ID); @@ -342,6 +343,7 @@ fd_start_render_pass(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_render_pass(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_render_pass *payload) { stage_end(pctx, ts_ns, SURFACE_STAGE_ID); @@ -349,6 +351,7 @@ fd_end_render_pass(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_binning_ib(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_binning_ib *payload) { stage_start(pctx, ts_ns, BINNING_STAGE_ID); @@ -356,6 +359,7 @@ fd_start_binning_ib(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_binning_ib(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_binning_ib *payload) { stage_end(pctx, ts_ns, BINNING_STAGE_ID); @@ -363,6 +367,7 @@ fd_end_binning_ib(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_draw_ib(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_draw_ib *payload) { stage_start( @@ -372,6 +377,7 @@ fd_start_draw_ib(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_draw_ib(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_draw_ib *payload) { stage_end( @@ -381,6 +387,7 @@ fd_end_draw_ib(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_blit(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_blit *payload) { stage_start(pctx, ts_ns, BLIT_STAGE_ID); @@ -388,6 +395,7 @@ fd_start_blit(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_blit(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_blit *payload) { stage_end(pctx, ts_ns, BLIT_STAGE_ID); @@ -395,6 +403,7 @@ fd_end_blit(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_compute(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_compute *payload) { stage_start(pctx, ts_ns, COMPUTE_STAGE_ID); @@ -402,6 +411,7 @@ fd_start_compute(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_compute(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_compute *payload) { stage_end(pctx, ts_ns, COMPUTE_STAGE_ID); @@ -409,6 +419,7 @@ fd_end_compute(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_clear_restore(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_clear_restore *payload) { stage_start(pctx, ts_ns, CLEAR_RESTORE_STAGE_ID); @@ -416,6 +427,7 @@ fd_start_clear_restore(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_clear_restore(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_clear_restore *payload) { stage_end(pctx, ts_ns, CLEAR_RESTORE_STAGE_ID); @@ -423,6 +435,7 @@ fd_end_clear_restore(struct pipe_context *pctx, uint64_t ts_ns, void fd_start_resolve(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_start_resolve *payload) { stage_start(pctx, ts_ns, RESOLVE_STAGE_ID); @@ -430,6 +443,7 @@ fd_start_resolve(struct pipe_context *pctx, uint64_t ts_ns, void fd_end_resolve(struct pipe_context *pctx, uint64_t ts_ns, + const void *flush_data, const struct trace_end_resolve *payload) { stage_end(pctx, ts_ns, RESOLVE_STAGE_ID); diff --git a/src/gallium/drivers/freedreno/freedreno_tracepoints.py b/src/gallium/drivers/freedreno/freedreno_tracepoints.py index ea7f01120ce..b0a5a016c6b 100644 --- a/src/gallium/drivers/freedreno/freedreno_tracepoints.py +++ b/src/gallium/drivers/freedreno/freedreno_tracepoints.py @@ -141,4 +141,4 @@ Tracepoint('start_compute', Tracepoint('end_compute', tp_perfetto='fd_end_compute') -utrace_generate(cpath=args.src, hpath=args.hdr) +utrace_generate(cpath=args.src, hpath=args.hdr, ctx_param='struct pipe_context *pctx') diff --git a/src/gallium/drivers/freedreno/meson.build b/src/gallium/drivers/freedreno/meson.build index 6cf833d335b..ed231384501 100644 --- a/src/gallium/drivers/freedreno/meson.build +++ b/src/gallium/drivers/freedreno/meson.build @@ -223,7 +223,7 @@ freedreno_tracepoints = custom_target( output: ['freedreno_tracepoints.c', 'freedreno_tracepoints.h'], command: [ prog_python, '@INPUT@', - '-p', join_paths(meson.source_root(), 'src/gallium/auxiliary/util/'), + '-p', join_paths(meson.source_root(), 'src/util/perf/'), '-C', '@OUTPUT0@', '-H', '@OUTPUT1@', ], diff --git a/src/util/meson.build b/src/util/meson.build index 88f06e23268..1d4f85f889f 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -77,6 +77,9 @@ files_mesa_util = files( 'os_misc.h', 'os_socket.c', 'os_socket.h', + 'perf/u_trace.h', + 'perf/u_trace.c', + 'perf/u_trace_priv.h', 'u_process.c', 'u_process.h', 'u_qsort.cpp', @@ -116,6 +119,7 @@ files_mesa_util = files( 'u_atomic.h', 'u_dynarray.h', 'u_endian.h', + 'u_fifo.h', 'u_queue.c', 'u_queue.h', 'u_string.h', @@ -212,6 +216,8 @@ if with_perfetto deps_for_libmesa_util += dep_perfetto endif +u_trace_py = files('perf/u_trace.py') + _libmesa_util = static_library( 'mesa_util', [files_mesa_util, files_debug_stack, format_srgb], diff --git a/src/gallium/auxiliary/util/u_trace.c b/src/util/perf/u_trace.c similarity index 84% rename from src/gallium/auxiliary/util/u_trace.c rename to src/util/perf/u_trace.c index a3d0ef7824a..4cc783ebbc7 100644 --- a/src/gallium/auxiliary/util/u_trace.c +++ b/src/util/perf/u_trace.c @@ -23,15 +23,12 @@ #include -#include "pipe/p_context.h" -#include "pipe/p_state.h" - #include "util/list.h" #include "util/ralloc.h" #include "util/u_debug.h" #include "util/u_inlines.h" +#include "util/u_fifo.h" -#include "u_fifo.h" #include "u_trace.h" #define __NEEDS_TRACE_PRIV @@ -77,7 +74,7 @@ struct u_trace_chunk { /* table of driver recorded 64b timestamps, index matches index * into traces table */ - struct pipe_resource *timestamps; + void *timestamps; /** * For trace payload, we sub-allocate from ralloc'd buffers which @@ -90,6 +87,14 @@ struct u_trace_chunk { bool last; /* this chunk is last in batch */ bool eof; /* this chunk is last in frame */ + + void *flush_data; /* assigned by u_trace_flush */ + + /** + * Several chunks reference a single flush_data instance thus only + * one chunk should be designated to free the data. + */ + bool free_flush_data; }; static void @@ -97,7 +102,7 @@ free_chunk(void *ptr) { struct u_trace_chunk *chunk = ptr; - pipe_resource_reference(&chunk->timestamps, NULL); + chunk->utctx->delete_timestamp_buffer(chunk->utctx, chunk->timestamps); list_del(&chunk->node); } @@ -134,20 +139,7 @@ get_chunk(struct u_trace *ut) ralloc_set_destructor(chunk, free_chunk); chunk->utctx = ut->utctx; - - struct pipe_resource tmpl = { - .target = PIPE_BUFFER, - .format = PIPE_FORMAT_R8_UNORM, - .bind = PIPE_BIND_QUERY_BUFFER | PIPE_BIND_LINEAR, - .width0 = TIMESTAMP_BUF_SIZE, - .height0 = 1, - .depth0 = 1, - .array_size = 1, - }; - - struct pipe_screen *pscreen = ut->utctx->pctx->screen; - chunk->timestamps = pscreen->resource_create(pscreen, &tmpl); - + chunk->timestamps = ut->utctx->create_timestamp_buffer(ut->utctx, TIMESTAMP_BUF_SIZE); chunk->last = true; list_addtail(&chunk->node, &ut->trace_chunks); @@ -155,8 +147,8 @@ get_chunk(struct u_trace *ut) return chunk; } -DEBUG_GET_ONCE_BOOL_OPTION(trace, "GALLIUM_GPU_TRACE", false) -DEBUG_GET_ONCE_FILE_OPTION(trace_file, "GALLIUM_GPU_TRACEFILE", NULL, "w") +DEBUG_GET_ONCE_BOOL_OPTION(trace, "GPU_TRACE", false) +DEBUG_GET_ONCE_FILE_OPTION(trace_file, "GPU_TRACEFILE", NULL, "w") static FILE * get_tracefile(void) @@ -193,13 +185,19 @@ queue_init(struct u_trace_context *utctx) void u_trace_context_init(struct u_trace_context *utctx, - struct pipe_context *pctx, - u_trace_record_ts record_timestamp, - u_trace_read_ts read_timestamp) + void *pctx, + u_trace_create_ts_buffer create_timestamp_buffer, + u_trace_delete_ts_buffer delete_timestamp_buffer, + u_trace_record_ts record_timestamp, + u_trace_read_ts read_timestamp, + u_trace_delete_flush_data delete_flush_data) { utctx->pctx = pctx; + utctx->create_timestamp_buffer = create_timestamp_buffer; + utctx->delete_timestamp_buffer = delete_timestamp_buffer; utctx->record_timestamp = record_timestamp; - utctx->read_timestamp = read_timestamp; + utctx->read_timestamp = read_timestamp; + utctx->delete_flush_data = delete_flush_data; utctx->last_time_ns = 0; utctx->first_time_ns = 0; @@ -213,7 +211,7 @@ u_trace_context_init(struct u_trace_context *utctx, list_add(&utctx->node, &ctx_list); #endif - if (!(utctx->out || ut_perfetto_enabled)) + if (!u_trace_context_tracing(utctx)) return; queue_init(utctx); @@ -225,7 +223,7 @@ u_trace_context_fini(struct u_trace_context *utctx) #ifdef HAVE_PERFETTO list_del(&utctx->node); #endif - if (!utctx->out) + if (!utctx->queue.jobs) return; util_queue_finish(&utctx->queue); util_queue_destroy(&utctx->queue); @@ -264,7 +262,7 @@ process_chunk(void *job, void *gdata, int thread_index) for (unsigned idx = 0; idx < chunk->num_traces; idx++) { const struct u_trace_event *evt = &chunk->traces[idx]; - uint64_t ns = utctx->read_timestamp(utctx, chunk->timestamps, idx); + uint64_t ns = utctx->read_timestamp(utctx, chunk->timestamps, idx, chunk->flush_data); int32_t delta; if (!utctx->first_time_ns) @@ -291,7 +289,7 @@ process_chunk(void *job, void *gdata, int thread_index) } #ifdef HAVE_PERFETTO if (evt->tp->perfetto) { - evt->tp->perfetto(utctx->pctx, ns, evt->payload); + evt->tp->perfetto(utctx->pctx, ns, chunk->flush_data, evt->payload); } #endif } @@ -306,6 +304,10 @@ process_chunk(void *job, void *gdata, int thread_index) utctx->first_time_ns = 0; } + if (chunk->free_flush_data && utctx->delete_flush_data) { + utctx->delete_flush_data(utctx, chunk->flush_data); + } + if (utctx->out && chunk->eof) { fprintf(utctx->out, "END OF FRAME %u\n", utctx->frame_nr++); } @@ -350,7 +352,7 @@ u_trace_init(struct u_trace *ut, struct u_trace_context *utctx) { ut->utctx = utctx; list_inithead(&ut->trace_chunks); - ut->enabled = !!utctx->out; + ut->enabled = u_trace_context_tracing(utctx); } void @@ -401,8 +403,19 @@ u_trace_append(struct u_trace *ut, const struct u_tracepoint *tp) } void -u_trace_flush(struct u_trace *ut) +u_trace_flush(struct u_trace *ut, void *flush_data, bool free_data) { + list_for_each_entry(struct u_trace_chunk, chunk, &ut->trace_chunks, node) { + chunk->flush_data = flush_data; + chunk->free_flush_data = false; + } + + if (free_data && !list_is_empty(&ut->trace_chunks)) { + struct u_trace_chunk *last_chunk = + list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node); + last_chunk->free_flush_data = true; + } + /* transfer batch's log chunks to context: */ list_splicetail(&ut->trace_chunks, &ut->utctx->flushed_trace_chunks); list_inithead(&ut->trace_chunks); diff --git a/src/gallium/auxiliary/util/u_trace.h b/src/util/perf/u_trace.h similarity index 80% rename from src/gallium/auxiliary/util/u_trace.h rename to src/util/perf/u_trace.h index c44593d7821..f67a359dbfa 100644 --- a/src/gallium/auxiliary/util/u_trace.h +++ b/src/util/perf/u_trace.h @@ -30,9 +30,6 @@ #include "util/u_queue.h" -#include "pipe/p_context.h" -#include "pipe/p_state.h" - #ifdef __cplusplus extern "C" { #endif @@ -73,14 +70,25 @@ struct u_trace_context; struct u_trace; struct u_trace_chunk; -struct pipe_resource; - /** * Special reserved value to indicate that no timestamp was captured, * and that the timestamp of the previous trace should be reused. */ #define U_TRACE_NO_TIMESTAMP ((uint64_t)0) +/** + * Driver provided callback to create a timestamp buffer which will be + * read by u_trace_read_ts function. + */ +typedef void* (*u_trace_create_ts_buffer)(struct u_trace_context *utctx, + uint32_t timestamps_count); + +/** + * Driver provided callback to delete a timestamp buffer. + */ +typedef void (*u_trace_delete_ts_buffer)(struct u_trace_context *utctx, + void *timestamps); + /** * Driver provided callback to emit commands to capture a 64b timestamp * into the specified timestamps buffer, at the specified index. @@ -90,7 +98,7 @@ struct pipe_resource; * GL_TIMESTAMP queries should be appropriate. */ typedef void (*u_trace_record_ts)(struct u_trace *ut, - struct pipe_resource *timestamps, unsigned idx); + void *timestamps, unsigned idx); /** * Driver provided callback to read back a previously recorded timestamp. @@ -98,6 +106,8 @@ typedef void (*u_trace_record_ts)(struct u_trace *ut, * the timestamps. (The timestamps will be read back in order, so it is * safe to only synchronize on idx==0.) * + * flush_data is data provided by the driver via u_trace_flush. + * * The returned timestamp should be in units of nanoseconds. The same * timebase as GL_TIMESTAMP queries should be used. * @@ -109,16 +119,26 @@ typedef void (*u_trace_record_ts)(struct u_trace *ut, * capturing the same timestamp multiple times in a row. */ typedef uint64_t (*u_trace_read_ts)(struct u_trace_context *utctx, - struct pipe_resource *timestamps, unsigned idx); + void *timestamps, unsigned idx, void *flush_data); + +/** + * Driver provided callback to delete flush data. + */ +typedef void (*u_trace_delete_flush_data)(struct u_trace_context *utctx, + void *flush_data); /** * The trace context provides tracking for "in-flight" traces, once the * cmdstream that records timestamps has been flushed. */ struct u_trace_context { - struct pipe_context *pctx; + void *pctx; + + u_trace_create_ts_buffer create_timestamp_buffer; + u_trace_delete_ts_buffer delete_timestamp_buffer; u_trace_record_ts record_timestamp; u_trace_read_ts read_timestamp; + u_trace_delete_flush_data delete_flush_data; FILE *out; @@ -165,9 +185,12 @@ struct u_trace { }; void u_trace_context_init(struct u_trace_context *utctx, - struct pipe_context *pctx, - u_trace_record_ts record_timestamp, - u_trace_read_ts read_timestamp); + void *pctx, + u_trace_create_ts_buffer create_timestamp_buffer, + u_trace_delete_ts_buffer delete_timestamp_buffer, + u_trace_record_ts record_timestamp, + u_trace_read_ts read_timestamp, + u_trace_delete_flush_data delete_flush_data); void u_trace_context_fini(struct u_trace_context *utctx); /** @@ -186,10 +209,16 @@ void u_trace_fini(struct u_trace *ut); * is that all the tracepoints are "executed" by the GPU following any previously * flushed u_trace batch. * + * flush_data is a way for driver to pass additional data, which becomes available + * only at the point of flush, to the u_trace_read_ts callback and perfetto. + * The typical example of such data would be a fence to wait on in u_trace_read_ts, + * and a submission_id to pass into perfetto. + * The destruction of the data is done via u_trace_delete_flush_data. + * * This should typically be called when the corresponding cmdstream (containing * the timestamp reads) is flushed to the kernel. */ -void u_trace_flush(struct u_trace *ut); +void u_trace_flush(struct u_trace *ut, void *flush_data, bool free_data); #ifdef HAVE_PERFETTO extern int ut_perfetto_enabled; @@ -200,30 +229,10 @@ void u_trace_perfetto_stop(void); # define ut_perfetto_enabled 0 #endif -/* - * TODO in some cases it is useful to have composite tracepoints like this, - * to log more complex data structures.. but this is probably not where they - * should live: - */ - -void __trace_surface(struct u_trace *ut, const struct pipe_surface *psurf); -void __trace_framebuffer(struct u_trace *ut, const struct pipe_framebuffer_state *pfb); - -static inline void -trace_framebuffer_state(struct u_trace *ut, const struct pipe_framebuffer_state *pfb) +static inline bool +u_trace_context_tracing(struct u_trace_context *utctx) { - if (likely(!ut->enabled)) - return; - - __trace_framebuffer(ut, pfb); - for (unsigned i = 0; i < pfb->nr_cbufs; i++) { - if (pfb->cbufs[i]) { - __trace_surface(ut, pfb->cbufs[i]); - } - } - if (pfb->zsbuf) { - __trace_surface(ut, pfb->zsbuf); - } + return !!utctx->out || (ut_perfetto_enabled > 0); } #ifdef __cplusplus diff --git a/src/gallium/auxiliary/util/u_trace.py b/src/util/perf/u_trace.py similarity index 91% rename from src/gallium/auxiliary/util/u_trace.py rename to src/util/perf/u_trace.py index 67470a8dd96..e6b74d498e0 100644 --- a/src/gallium/auxiliary/util/u_trace.py +++ b/src/util/perf/u_trace.py @@ -75,6 +75,19 @@ class Header(object): HEADERS.append(self) + +FORWARD_DECLS = [] + +class ForwardDecl(object): + """Class that represents a forward declaration + """ + def __init__(self, decl): + assert isinstance(decl, str) + self.decl = decl + + FORWARD_DECLS.append(self) + + hdr_template = """\ /* Copyright (C) 2020 Google, Inc. * @@ -106,12 +119,16 @@ hdr_template = """\ #include "${header.hdr}" % endfor -#include "util/u_trace.h" +#include "util/perf/u_trace.h" #ifdef __cplusplus extern "C" { #endif +% for declaration in FORWARD_DECLS: +${declaration.decl}; +% endfor + % for trace_name, trace in TRACEPOINTS.items(): /* * ${trace_name} @@ -134,7 +151,7 @@ struct trace_${trace_name} { }; % if trace.tp_perfetto is not None: #ifdef HAVE_PERFETTO -void ${trace.tp_perfetto}(struct pipe_context *pctx, uint64_t ts_ns, const struct trace_${trace_name} *payload); +void ${trace.tp_perfetto}(${ctx_param}, uint64_t ts_ns, const void *flush_data, const struct trace_${trace_name} *payload); #endif % endif void __trace_${trace_name}(struct u_trace *ut @@ -198,7 +215,7 @@ src_template = """\ #include "${hdr}" #define __NEEDS_TRACE_PRIV -#include "util/u_trace_priv.h" +#include "util/perf/u_trace_priv.h" % for trace_name, trace in TRACEPOINTS.items(): /* @@ -223,7 +240,7 @@ static const struct u_tracepoint __tp_${trace_name} = { __print_${trace_name}, % if trace.tp_perfetto is not None: #ifdef HAVE_PERFETTO - (void (*)(struct pipe_context *, uint64_t, const void *))${trace.tp_perfetto}, + (void (*)(void *pctx, uint64_t, const void *, const void *))${trace.tp_perfetto}, #endif % endif }; @@ -243,12 +260,13 @@ void __trace_${trace_name}(struct u_trace *ut % endfor """ -def utrace_generate(cpath, hpath): +def utrace_generate(cpath, hpath, ctx_param): if cpath is not None: hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h' with open(cpath, 'w') as f: f.write(Template(src_template).render( hdr=hdr, + ctx_param=ctx_param, HEADERS=HEADERS, TRACEPOINTS=TRACEPOINTS)) @@ -257,5 +275,7 @@ def utrace_generate(cpath, hpath): with open(hpath, 'w') as f: f.write(Template(hdr_template).render( hdrname=hdr.rstrip('.h').upper(), + ctx_param=ctx_param, HEADERS=HEADERS, + FORWARD_DECLS=FORWARD_DECLS, TRACEPOINTS=TRACEPOINTS)) diff --git a/src/gallium/auxiliary/util/u_trace_priv.h b/src/util/perf/u_trace_priv.h similarity index 95% rename from src/gallium/auxiliary/util/u_trace_priv.h rename to src/util/perf/u_trace_priv.h index 3e764ed6e9f..242c63f22e0 100644 --- a/src/gallium/auxiliary/util/u_trace_priv.h +++ b/src/util/perf/u_trace_priv.h @@ -47,7 +47,7 @@ struct u_tracepoint { /** * Callback to emit a perfetto event, such as render-stage trace */ - void (*perfetto)(struct pipe_context *pctx, uint64_t ts_ns, const void *payload); + void (*perfetto)(void *pctx, uint64_t ts_ns, const void *flush_data, const void *payload); #endif }; diff --git a/src/gallium/auxiliary/util/u_fifo.h b/src/util/u_fifo.h similarity index 100% rename from src/gallium/auxiliary/util/u_fifo.h rename to src/util/u_fifo.h