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 <dpiliaiev@igalia.com>
Reviewed-by: Rob Clark <robdclark@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10969>
This commit is contained in:
Danylo Piliaiev 2021-05-24 12:11:46 +03:00
parent 9033916d84
commit e14f525280
17 changed files with 317 additions and 94 deletions

View File

@ -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,

View File

@ -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

View File

@ -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 */

View File

@ -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')

View File

@ -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"

View File

@ -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);

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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')

View File

@ -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@',
],

View File

@ -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],

View File

@ -23,15 +23,12 @@
#include <inttypes.h>
#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);

View File

@ -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

View File

@ -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))

View File

@ -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
};