[Mesa-dev] [PATCH 23/25] ddebug: optionally handle transfer commands like draws
Nicolai Hähnle
nhaehnle at gmail.com
Sun Oct 22 19:08:06 UTC 2017
From: Nicolai Hähnle <nicolai.haehnle at amd.com>
Transfer commands can have associated GPU operations.
Enabled by passing GALLIUM_DDEBUG=transfers.
---
src/gallium/drivers/ddebug/dd_context.c | 65 ---------
src/gallium/drivers/ddebug/dd_draw.c | 234 ++++++++++++++++++++++++++++++++
src/gallium/drivers/ddebug/dd_pipe.h | 46 +++++++
src/gallium/drivers/ddebug/dd_screen.c | 9 +-
4 files changed, 288 insertions(+), 66 deletions(-)
diff --git a/src/gallium/drivers/ddebug/dd_context.c b/src/gallium/drivers/ddebug/dd_context.c
index 558708df58d..dd7b3e086cd 100644
--- a/src/gallium/drivers/ddebug/dd_context.c
+++ b/src/gallium/drivers/ddebug/dd_context.c
@@ -601,80 +601,20 @@ dd_context_destroy(struct pipe_context *_pipe)
}
}
u_log_context_destroy(&dctx->log);
pipe->destroy(pipe);
FREE(dctx);
}
/********************************************************************
- * transfer
- */
-
-static void *
-dd_context_transfer_map(struct pipe_context *_pipe,
- struct pipe_resource *resource, unsigned level,
- unsigned usage, const struct pipe_box *box,
- struct pipe_transfer **transfer)
-{
- struct pipe_context *pipe = dd_context(_pipe)->pipe;
-
- return pipe->transfer_map(pipe, resource, level, usage, box, transfer);
-}
-
-static void
-dd_context_transfer_flush_region(struct pipe_context *_pipe,
- struct pipe_transfer *transfer,
- const struct pipe_box *box)
-{
- struct pipe_context *pipe = dd_context(_pipe)->pipe;
-
- pipe->transfer_flush_region(pipe, transfer, box);
-}
-
-static void
-dd_context_transfer_unmap(struct pipe_context *_pipe,
- struct pipe_transfer *transfer)
-{
- struct pipe_context *pipe = dd_context(_pipe)->pipe;
-
- pipe->transfer_unmap(pipe, transfer);
-}
-
-static void
-dd_context_buffer_subdata(struct pipe_context *_pipe,
- struct pipe_resource *resource,
- unsigned usage, unsigned offset,
- unsigned size, const void *data)
-{
- struct pipe_context *pipe = dd_context(_pipe)->pipe;
-
- pipe->buffer_subdata(pipe, resource, usage, offset, size, data);
-}
-
-static void
-dd_context_texture_subdata(struct pipe_context *_pipe,
- struct pipe_resource *resource,
- unsigned level, unsigned usage,
- const struct pipe_box *box,
- const void *data, unsigned stride,
- unsigned layer_stride)
-{
- struct pipe_context *pipe = dd_context(_pipe)->pipe;
-
- pipe->texture_subdata(pipe, resource, level, usage, box, data,
- stride, layer_stride);
-}
-
-
-/********************************************************************
* miscellaneous
*/
static void
dd_context_texture_barrier(struct pipe_context *_pipe, unsigned flags)
{
struct pipe_context *pipe = dd_context(_pipe)->pipe;
pipe->texture_barrier(pipe, flags);
}
@@ -884,25 +824,20 @@ dd_context_create(struct dd_screen *dscreen, struct pipe_context *pipe)
CTX_INIT(set_shader_buffers);
CTX_INIT(set_shader_images);
CTX_INIT(set_vertex_buffers);
CTX_INIT(create_stream_output_target);
CTX_INIT(stream_output_target_destroy);
CTX_INIT(set_stream_output_targets);
CTX_INIT(create_sampler_view);
CTX_INIT(sampler_view_destroy);
CTX_INIT(create_surface);
CTX_INIT(surface_destroy);
- CTX_INIT(transfer_map);
- CTX_INIT(transfer_flush_region);
- CTX_INIT(transfer_unmap);
- CTX_INIT(buffer_subdata);
- CTX_INIT(texture_subdata);
CTX_INIT(texture_barrier);
CTX_INIT(memory_barrier);
CTX_INIT(resource_commit);
/* create_video_codec */
/* create_video_buffer */
/* set_compute_resources */
/* set_global_binding */
CTX_INIT(get_sample_position);
CTX_INIT(invalidate_resource);
CTX_INIT(get_device_reset_status);
diff --git a/src/gallium/drivers/ddebug/dd_draw.c b/src/gallium/drivers/ddebug/dd_draw.c
index 182d6f297a1..a25017114d8 100644
--- a/src/gallium/drivers/ddebug/dd_draw.c
+++ b/src/gallium/drivers/ddebug/dd_draw.c
@@ -461,20 +461,70 @@ dd_dump_clear_buffer(struct dd_draw_state *dstate, struct call_clear_buffer *inf
DUMP_M(uint, info, size);
DUMP_M(uint, info, clear_value_size);
fprintf(f, " clear_value:");
for (i = 0; i < info->clear_value_size; i++)
fprintf(f, " %02x", value[i]);
fprintf(f, "\n");
}
static void
+dd_dump_transfer_map(struct call_transfer_map *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP_M_ADDR(transfer, info, transfer);
+ DUMP_M(ptr, info, transfer_ptr);
+ DUMP_M(ptr, info, ptr);
+}
+
+static void
+dd_dump_transfer_flush_region(struct call_transfer_flush_region *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP_M_ADDR(transfer, info, transfer);
+ DUMP_M(ptr, info, transfer_ptr);
+ DUMP_M_ADDR(box, info, box);
+}
+
+static void
+dd_dump_transfer_unmap(struct call_transfer_unmap *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP_M_ADDR(transfer, info, transfer);
+ DUMP_M(ptr, info, transfer_ptr);
+}
+
+static void
+dd_dump_buffer_subdata(struct call_buffer_subdata *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP_M(resource, info, resource);
+ DUMP_M(transfer_usage, info, usage);
+ DUMP_M(uint, info, offset);
+ DUMP_M(uint, info, size);
+ DUMP_M(ptr, info, data);
+}
+
+static void
+dd_dump_texture_subdata(struct call_texture_subdata *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP_M(resource, info, resource);
+ DUMP_M(uint, info, level);
+ DUMP_M(transfer_usage, info, usage);
+ DUMP_M_ADDR(box, info, box);
+ DUMP_M(ptr, info, data);
+ DUMP_M(uint, info, stride);
+ DUMP_M(uint, info, layer_stride);
+}
+
+static void
dd_dump_clear_texture(struct dd_draw_state *dstate, FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
/* TODO */
}
static void
dd_dump_clear_render_target(struct dd_draw_state *dstate, FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
@@ -533,20 +583,35 @@ dd_dump_call(FILE *f, struct dd_draw_state *state, struct dd_call *call)
break;
case CALL_CLEAR_DEPTH_STENCIL:
dd_dump_clear_depth_stencil(state, f);
break;
case CALL_GENERATE_MIPMAP:
dd_dump_generate_mipmap(state, f);
break;
case CALL_GET_QUERY_RESULT_RESOURCE:
dd_dump_get_query_result_resource(&call->info.get_query_result_resource, f);
break;
+ case CALL_TRANSFER_MAP:
+ dd_dump_transfer_map(&call->info.transfer_map, f);
+ break;
+ case CALL_TRANSFER_FLUSH_REGION:
+ dd_dump_transfer_flush_region(&call->info.transfer_flush_region, f);
+ break;
+ case CALL_TRANSFER_UNMAP:
+ dd_dump_transfer_unmap(&call->info.transfer_unmap, f);
+ break;
+ case CALL_BUFFER_SUBDATA:
+ dd_dump_buffer_subdata(&call->info.buffer_subdata, f);
+ break;
+ case CALL_TEXTURE_SUBDATA:
+ dd_dump_texture_subdata(&call->info.texture_subdata, f);
+ break;
}
}
static void
dd_kill_process(void)
{
sync();
fprintf(stderr, "dd: Aborting the process...\n");
fflush(stdout);
fflush(stderr);
@@ -591,20 +656,35 @@ dd_unreference_copy_of_call(struct dd_call *dst)
case CALL_CLEAR_RENDER_TARGET:
break;
case CALL_CLEAR_DEPTH_STENCIL:
break;
case CALL_GENERATE_MIPMAP:
pipe_resource_reference(&dst->info.generate_mipmap.res, NULL);
break;
case CALL_GET_QUERY_RESULT_RESOURCE:
pipe_resource_reference(&dst->info.get_query_result_resource.resource, NULL);
break;
+ case CALL_TRANSFER_MAP:
+ pipe_resource_reference(&dst->info.transfer_map.transfer.resource, NULL);
+ break;
+ case CALL_TRANSFER_FLUSH_REGION:
+ pipe_resource_reference(&dst->info.transfer_flush_region.transfer.resource, NULL);
+ break;
+ case CALL_TRANSFER_UNMAP:
+ pipe_resource_reference(&dst->info.transfer_unmap.transfer.resource, NULL);
+ break;
+ case CALL_BUFFER_SUBDATA:
+ pipe_resource_reference(&dst->info.buffer_subdata.resource, NULL);
+ break;
+ case CALL_TEXTURE_SUBDATA:
+ pipe_resource_reference(&dst->info.texture_subdata.resource, NULL);
+ break;
}
}
static void
dd_init_copy_of_draw_state(struct dd_draw_state_copy *state)
{
unsigned i,j;
/* Just clear pointers to gallium objects. Don't clear the whole structure,
* because it would kill performance with its size of 130 KB.
@@ -1385,27 +1465,181 @@ dd_context_clear_texture(struct pipe_context *_pipe,
struct pipe_context *pipe = dctx->pipe;
struct dd_draw_record *record = dd_create_record(dctx);
record->call.type = CALL_CLEAR_TEXTURE;
dd_before_draw(dctx, record);
pipe->clear_texture(pipe, res, level, box, data);
dd_after_draw(dctx, record);
}
+/********************************************************************
+ * transfer
+ */
+
+static void *
+dd_context_transfer_map(struct pipe_context *_pipe,
+ struct pipe_resource *resource, unsigned level,
+ unsigned usage, const struct pipe_box *box,
+ struct pipe_transfer **transfer)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record =
+ dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
+
+ if (record) {
+ record->call.type = CALL_TRANSFER_MAP;
+
+ dd_before_draw(dctx, record);
+ }
+ void *ptr = pipe->transfer_map(pipe, resource, level, usage, box, transfer);
+ if (record) {
+ record->call.info.transfer_map.transfer_ptr = *transfer;
+ record->call.info.transfer_map.ptr = ptr;
+ if (*transfer) {
+ record->call.info.transfer_map.transfer = **transfer;
+ record->call.info.transfer_map.transfer.resource = NULL;
+ pipe_resource_reference(&record->call.info.transfer_map.transfer.resource,
+ (*transfer)->resource);
+ } else {
+ memset(&record->call.info.transfer_map.transfer, 0, sizeof(struct pipe_transfer));
+ }
+
+ dd_after_draw(dctx, record);
+ }
+ return ptr;
+}
+
+static void
+dd_context_transfer_flush_region(struct pipe_context *_pipe,
+ struct pipe_transfer *transfer,
+ const struct pipe_box *box)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record =
+ dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
+
+ if (record) {
+ record->call.type = CALL_TRANSFER_FLUSH_REGION;
+ record->call.info.transfer_flush_region.transfer_ptr = transfer;
+ record->call.info.transfer_flush_region.box = *box;
+ record->call.info.transfer_flush_region.transfer = *transfer;
+ record->call.info.transfer_flush_region.transfer.resource = NULL;
+ pipe_resource_reference(
+ &record->call.info.transfer_flush_region.transfer.resource,
+ transfer->resource);
+
+ dd_before_draw(dctx, record);
+ }
+ pipe->transfer_flush_region(pipe, transfer, box);
+ if (record)
+ dd_after_draw(dctx, record);
+}
+
+static void
+dd_context_transfer_unmap(struct pipe_context *_pipe,
+ struct pipe_transfer *transfer)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record =
+ dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
+
+ if (record) {
+ record->call.type = CALL_TRANSFER_UNMAP;
+ record->call.info.transfer_unmap.transfer_ptr = transfer;
+ record->call.info.transfer_unmap.transfer = *transfer;
+ record->call.info.transfer_unmap.transfer.resource = NULL;
+ pipe_resource_reference(
+ &record->call.info.transfer_unmap.transfer.resource,
+ transfer->resource);
+
+ dd_before_draw(dctx, record);
+ }
+ pipe->transfer_unmap(pipe, transfer);
+ if (record)
+ dd_after_draw(dctx, record);
+}
+
+static void
+dd_context_buffer_subdata(struct pipe_context *_pipe,
+ struct pipe_resource *resource,
+ unsigned usage, unsigned offset,
+ unsigned size, const void *data)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record =
+ dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
+
+ if (record) {
+ record->call.type = CALL_BUFFER_SUBDATA;
+ record->call.info.buffer_subdata.resource = NULL;
+ pipe_resource_reference(&record->call.info.buffer_subdata.resource, resource);
+ record->call.info.buffer_subdata.usage = usage;
+ record->call.info.buffer_subdata.offset = offset;
+ record->call.info.buffer_subdata.size = size;
+ record->call.info.buffer_subdata.data = data;
+
+ dd_before_draw(dctx, record);
+ }
+ pipe->buffer_subdata(pipe, resource, usage, offset, size, data);
+ if (record)
+ dd_after_draw(dctx, record);
+}
+
+static void
+dd_context_texture_subdata(struct pipe_context *_pipe,
+ struct pipe_resource *resource,
+ unsigned level, unsigned usage,
+ const struct pipe_box *box,
+ const void *data, unsigned stride,
+ unsigned layer_stride)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record =
+ dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
+
+ if (record) {
+ record->call.type = CALL_TEXTURE_SUBDATA;
+ record->call.info.texture_subdata.resource = NULL;
+ pipe_resource_reference(&record->call.info.texture_subdata.resource, resource);
+ record->call.info.texture_subdata.level = level;
+ record->call.info.texture_subdata.usage = usage;
+ record->call.info.texture_subdata.box = *box;
+ record->call.info.texture_subdata.data = data;
+ record->call.info.texture_subdata.stride = stride;
+ record->call.info.texture_subdata.layer_stride = layer_stride;
+
+ dd_before_draw(dctx, record);
+ }
+ pipe->texture_subdata(pipe, resource, level, usage, box, data,
+ stride, layer_stride);
+ if (record)
+ dd_after_draw(dctx, record);
+}
+
void
dd_init_draw_functions(struct dd_context *dctx)
{
CTX_INIT(flush);
CTX_INIT(draw_vbo);
CTX_INIT(launch_grid);
CTX_INIT(resource_copy_region);
CTX_INIT(blit);
CTX_INIT(clear);
CTX_INIT(clear_render_target);
CTX_INIT(clear_depth_stencil);
CTX_INIT(clear_buffer);
CTX_INIT(clear_texture);
CTX_INIT(flush_resource);
CTX_INIT(generate_mipmap);
CTX_INIT(get_query_result_resource);
+ CTX_INIT(transfer_map);
+ CTX_INIT(transfer_flush_region);
+ CTX_INIT(transfer_unmap);
+ CTX_INIT(buffer_subdata);
+ CTX_INIT(texture_subdata);
}
diff --git a/src/gallium/drivers/ddebug/dd_pipe.h b/src/gallium/drivers/ddebug/dd_pipe.h
index 607ebbb2b96..07c4d55017f 100644
--- a/src/gallium/drivers/ddebug/dd_pipe.h
+++ b/src/gallium/drivers/ddebug/dd_pipe.h
@@ -45,39 +45,45 @@ enum dd_dump_mode {
DD_DUMP_APITRACE_CALL,
};
struct dd_screen
{
struct pipe_screen base;
struct pipe_screen *screen;
unsigned timeout_ms;
enum dd_dump_mode dump_mode;
bool flush_always;
+ bool transfers;
bool verbose;
unsigned skip_count;
unsigned apitrace_dump_call;
};
enum call_type
{
CALL_DRAW_VBO,
CALL_LAUNCH_GRID,
CALL_RESOURCE_COPY_REGION,
CALL_BLIT,
CALL_FLUSH_RESOURCE,
CALL_CLEAR,
CALL_CLEAR_BUFFER,
CALL_CLEAR_TEXTURE,
CALL_CLEAR_RENDER_TARGET,
CALL_CLEAR_DEPTH_STENCIL,
CALL_GENERATE_MIPMAP,
CALL_GET_QUERY_RESULT_RESOURCE,
+ CALL_TRANSFER_MAP,
+ CALL_TRANSFER_FLUSH_REGION,
+ CALL_TRANSFER_UNMAP,
+ CALL_BUFFER_SUBDATA,
+ CALL_TEXTURE_SUBDATA,
};
struct call_resource_copy_region
{
struct pipe_resource *dst;
unsigned dst_level;
unsigned dstx, dsty, dstz;
struct pipe_resource *src;
unsigned src_level;
struct pipe_box src_box;
@@ -117,34 +123,74 @@ struct call_draw_info {
struct call_get_query_result_resource {
struct pipe_query *query;
enum pipe_query_type query_type;
boolean wait;
enum pipe_query_value_type result_type;
int index;
struct pipe_resource *resource;
unsigned offset;
};
+struct call_transfer_map {
+ struct pipe_transfer *transfer_ptr;
+ struct pipe_transfer transfer;
+ void *ptr;
+};
+
+struct call_transfer_flush_region {
+ struct pipe_transfer *transfer_ptr;
+ struct pipe_transfer transfer;
+ struct pipe_box box;
+};
+
+struct call_transfer_unmap {
+ struct pipe_transfer *transfer_ptr;
+ struct pipe_transfer transfer;
+};
+
+struct call_buffer_subdata {
+ struct pipe_resource *resource;
+ unsigned usage;
+ unsigned offset;
+ unsigned size;
+ const void *data;
+};
+
+struct call_texture_subdata {
+ struct pipe_resource *resource;
+ unsigned level;
+ unsigned usage;
+ struct pipe_box box;
+ const void *data;
+ unsigned stride;
+ unsigned layer_stride;
+};
+
struct dd_call
{
enum call_type type;
union {
struct call_draw_info draw_vbo;
struct pipe_grid_info launch_grid;
struct call_resource_copy_region resource_copy_region;
struct pipe_blit_info blit;
struct pipe_resource *flush_resource;
struct call_clear clear;
struct call_clear_buffer clear_buffer;
struct call_generate_mipmap generate_mipmap;
struct call_get_query_result_resource get_query_result_resource;
+ struct call_transfer_map transfer_map;
+ struct call_transfer_flush_region transfer_flush_region;
+ struct call_transfer_unmap transfer_unmap;
+ struct call_buffer_subdata buffer_subdata;
+ struct call_texture_subdata texture_subdata;
} info;
};
struct dd_query
{
unsigned type;
struct pipe_query *query;
};
struct dd_state
diff --git a/src/gallium/drivers/ddebug/dd_screen.c b/src/gallium/drivers/ddebug/dd_screen.c
index 11d1d8c1e9c..5b2be28a969 100644
--- a/src/gallium/drivers/ddebug/dd_screen.c
+++ b/src/gallium/drivers/ddebug/dd_screen.c
@@ -421,47 +421,51 @@ match_uint(const char **cur, unsigned *value)
return true;
}
struct pipe_screen *
ddebug_screen_create(struct pipe_screen *screen)
{
struct dd_screen *dscreen;
const char *option;
bool flush = false;
bool verbose = false;
+ bool transfers = false;
unsigned timeout = 1000;
unsigned apitrace_dump_call = 0;
enum dd_dump_mode mode = DD_DUMP_ONLY_HANGS;
option = debug_get_option("GALLIUM_DDEBUG", NULL);
if (!option)
return screen;
if (!strcmp(option, "help")) {
puts("Gallium driver debugger");
puts("");
puts("Usage:");
puts("");
- puts(" GALLIUM_DDEBUG=\"[<timeout in ms>] [(always|apitrace <call#)] [flush] [verbose]\"");
+ puts(" GALLIUM_DDEBUG=\"[<timeout in ms>] [(always|apitrace <call#)] [flush] [transfers] [verbose]\"");
puts(" GALLIUM_DDEBUG_SKIP=[count]");
puts("");
puts("Dump context and driver information of draw calls into");
puts("$HOME/"DD_DIR"/. By default, watch for GPU hangs and only dump information");
puts("about draw calls related to the hang.");
puts("");
puts("<timeout in ms>");
puts(" Change the default timeout for GPU hang detection (default=1000ms).");
puts(" Setting this to 0 will disable GPU hang detection entirely.");
puts("");
puts("always");
puts(" Dump information about all draw calls.");
puts("");
+ puts("transfers");
+ puts(" Also dump and do hang detection on transfers.");
+ puts("");
puts("apitrace <call#>");
puts(" Dump information about the draw call corresponding to the given");
puts(" apitrace call number and exit.");
puts("");
puts("flush");
puts(" Flush after every draw call.");
puts("");
puts("verbose");
puts(" Write additional information to stderr.");
puts("");
@@ -478,20 +482,22 @@ ddebug_screen_create(struct pipe_screen *screen)
if (match_word(&option, "always")) {
if (mode == DD_DUMP_APITRACE_CALL) {
printf("ddebug: both 'always' and 'apitrace' specified\n");
exit(1);
}
mode = DD_DUMP_ALL_CALLS;
} else if (match_word(&option, "flush")) {
flush = true;
+ } else if (match_word(&option, "transfers")) {
+ transfers = true;
} else if (match_word(&option, "verbose")) {
verbose = true;
} else if (match_word(&option, "apitrace")) {
if (mode != DD_DUMP_ONLY_HANGS) {
printf("ddebug: 'apitrace' can only appear once and not mixed with 'always'\n");
exit(1);
}
if (!match_uint(&option, &apitrace_dump_call)) {
printf("ddebug: expected call number after 'apitrace'\n");
@@ -549,20 +555,21 @@ ddebug_screen_create(struct pipe_screen *screen)
SCR_INIT(get_compiler_options);
SCR_INIT(get_driver_uuid);
SCR_INIT(get_device_uuid);
#undef SCR_INIT
dscreen->screen = screen;
dscreen->timeout_ms = timeout;
dscreen->dump_mode = mode;
dscreen->flush_always = flush;
+ dscreen->transfers = transfers;
dscreen->verbose = verbose;
dscreen->apitrace_dump_call = apitrace_dump_call;
switch (dscreen->dump_mode) {
case DD_DUMP_ALL_CALLS:
fprintf(stderr, "Gallium debugger active. Logging all calls.\n");
break;
case DD_DUMP_APITRACE_CALL:
fprintf(stderr, "Gallium debugger active. Going to dump an apitrace call.\n");
break;
--
2.11.0
More information about the mesa-dev
mailing list