#include "util/u_dump.h"
#include "util/u_format.h"
+#include "util/u_framebuffer.h"
+#include "util/u_helpers.h"
+#include "util/u_inlines.h"
+#include "util/u_memory.h"
+#include "tgsi/tgsi_parse.h"
#include "tgsi/tgsi_scan.h"
+#include "util/os_time.h"
+#include <inttypes.h>
-enum call_type
-{
- CALL_DRAW_VBO,
- CALL_RESOURCE_COPY_REGION,
- CALL_BLIT,
- CALL_FLUSH_RESOURCE,
- CALL_CLEAR,
- CALL_CLEAR_BUFFER,
- CALL_CLEAR_RENDER_TARGET,
- CALL_CLEAR_DEPTH_STENCIL,
-};
-
-struct call_resource_copy_region
-{
- struct pipe_resource *dst;
- unsigned dst_level;
- unsigned dstx, dsty, dstz;
- struct pipe_resource *src;
- unsigned src_level;
- const struct pipe_box *src_box;
-};
-
-struct call_clear
-{
- unsigned buffers;
- const union pipe_color_union *color;
- double depth;
- unsigned stencil;
-};
-
-struct call_clear_buffer
-{
- struct pipe_resource *res;
- unsigned offset;
- unsigned size;
- const void *clear_value;
- int clear_value_size;
-};
-
-struct dd_call
-{
- enum call_type type;
-
- union {
- struct pipe_draw_info draw_vbo;
- 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;
- } info;
-};
-
-static FILE *
-dd_get_file_stream(struct dd_context *dctx)
-{
- struct pipe_screen *screen = dctx->pipe->screen;
- FILE *f = dd_get_debug_file();
- if (!f)
- return NULL;
-
+static void
+dd_write_header(FILE *f, struct pipe_screen *screen, unsigned apitrace_call_number)
+{
+ char cmd_line[4096];
+ if (os_get_command_line(cmd_line, sizeof(cmd_line)))
+ fprintf(f, "Command: %s\n", cmd_line);
fprintf(f, "Driver vendor: %s\n", screen->get_vendor(screen));
fprintf(f, "Device vendor: %s\n", screen->get_device_vendor(screen));
fprintf(f, "Device name: %s\n\n", screen->get_name(screen));
+
+ if (apitrace_call_number)
+ fprintf(f, "Last apitrace call: %u\n\n", apitrace_call_number);
+}
+
+FILE *
+dd_get_file_stream(struct dd_screen *dscreen, unsigned apitrace_call_number)
+{
+ struct pipe_screen *screen = dscreen->screen;
+
+ FILE *f = dd_get_debug_file(dscreen->verbose);
+ if (!f)
+ return NULL;
+
+ dd_write_header(f, screen, apitrace_call_number);
return f;
}
static void
-dd_close_file_stream(FILE *f)
+dd_dump_dmesg(FILE *f)
{
- fclose(f);
+ char line[2000];
+ FILE *p = popen("dmesg | tail -n60", "r");
+
+ if (!p)
+ return;
+
+ fprintf(f, "\nLast 60 lines of dmesg:\n\n");
+ while (fgets(line, sizeof(line), p))
+ fputs(line, f);
+
+ pclose(p);
}
static unsigned
-dd_num_active_viewports(struct dd_context *dctx)
+dd_num_active_viewports(struct dd_draw_state *dstate)
{
struct tgsi_shader_info info;
const struct tgsi_token *tokens;
- if (dctx->shaders[PIPE_SHADER_GEOMETRY])
- tokens = dctx->shaders[PIPE_SHADER_GEOMETRY]->state.shader.tokens;
- else if (dctx->shaders[PIPE_SHADER_TESS_EVAL])
- tokens = dctx->shaders[PIPE_SHADER_TESS_EVAL]->state.shader.tokens;
- else if (dctx->shaders[PIPE_SHADER_VERTEX])
- tokens = dctx->shaders[PIPE_SHADER_VERTEX]->state.shader.tokens;
+ if (dstate->shaders[PIPE_SHADER_GEOMETRY])
+ tokens = dstate->shaders[PIPE_SHADER_GEOMETRY]->state.shader.tokens;
+ else if (dstate->shaders[PIPE_SHADER_TESS_EVAL])
+ tokens = dstate->shaders[PIPE_SHADER_TESS_EVAL]->state.shader.tokens;
+ else if (dstate->shaders[PIPE_SHADER_VERTEX])
+ tokens = dstate->shaders[PIPE_SHADER_VERTEX]->state.shader.tokens;
else
return 1;
- tgsi_scan_shader(tokens, &info);
- return info.writes_viewport_index ? PIPE_MAX_VIEWPORTS : 1;
+ if (tokens) {
+ tgsi_scan_shader(tokens, &info);
+ if (info.writes_viewport_index)
+ return PIPE_MAX_VIEWPORTS;
+ }
+
+ return 1;
}
#define COLOR_RESET "\033[0m"
fprintf(f, "\n"); \
} while(0)
-static void
-print_named_value(FILE *f, const char *name, int value)
-{
- fprintf(f, COLOR_STATE "%s" COLOR_RESET " = %i\n", name, value);
-}
+#define PRINT_NAMED(type, name, value) \
+do { \
+ fprintf(f, COLOR_STATE "%s" COLOR_RESET " = ", name); \
+ util_dump_##type(f, value); \
+ fprintf(f, "\n"); \
+} while (0)
static void
-print_named_xvalue(FILE *f, const char *name, int value)
+util_dump_uint(FILE *f, unsigned i)
{
- fprintf(f, COLOR_STATE "%s" COLOR_RESET " = 0x%08x\n", name, value);
+ fprintf(f, "%u", i);
}
static void
-util_dump_uint(FILE *f, unsigned i)
+util_dump_int(FILE *f, int i)
{
- fprintf(f, "%u", i);
+ fprintf(f, "%d", i);
}
static void
}
static void
-util_dump_query(FILE *f, struct dd_query *query)
-{
- if (query->type >= PIPE_QUERY_DRIVER_SPECIFIC)
- fprintf(f, "PIPE_QUERY_DRIVER_SPECIFIC + %i",
- query->type - PIPE_QUERY_DRIVER_SPECIFIC);
- else
- fprintf(f, "%s", util_dump_query_type(query->type, false));
-}
-
-static void
-dd_dump_render_condition(struct dd_context *dctx, FILE *f)
+dd_dump_render_condition(struct dd_draw_state *dstate, FILE *f)
{
- if (dctx->render_cond.query) {
+ if (dstate->render_cond.query) {
fprintf(f, "render condition:\n");
- DUMP_M(query, &dctx->render_cond, query);
- DUMP_M(uint, &dctx->render_cond, condition);
- DUMP_M(uint, &dctx->render_cond, mode);
+ DUMP_M(query_type, &dstate->render_cond, query->type);
+ DUMP_M(uint, &dstate->render_cond, condition);
+ DUMP_M(uint, &dstate->render_cond, mode);
fprintf(f, "\n");
}
}
static void
-dd_dump_draw_vbo(struct dd_context *dctx, struct pipe_draw_info *info, FILE *f)
+dd_dump_shader(struct dd_draw_state *dstate, enum pipe_shader_type sh, FILE *f)
{
- int sh, i;
+ int i;
const char *shader_str[PIPE_SHADER_TYPES];
shader_str[PIPE_SHADER_VERTEX] = "VERTEX";
shader_str[PIPE_SHADER_FRAGMENT] = "FRAGMENT";
shader_str[PIPE_SHADER_COMPUTE] = "COMPUTE";
+ if (sh == PIPE_SHADER_TESS_CTRL &&
+ !dstate->shaders[PIPE_SHADER_TESS_CTRL] &&
+ dstate->shaders[PIPE_SHADER_TESS_EVAL])
+ fprintf(f, "tess_state: {default_outer_level = {%f, %f, %f, %f}, "
+ "default_inner_level = {%f, %f}}\n",
+ dstate->tess_default_levels[0],
+ dstate->tess_default_levels[1],
+ dstate->tess_default_levels[2],
+ dstate->tess_default_levels[3],
+ dstate->tess_default_levels[4],
+ dstate->tess_default_levels[5]);
+
+ if (sh == PIPE_SHADER_FRAGMENT)
+ if (dstate->rs) {
+ unsigned num_viewports = dd_num_active_viewports(dstate);
+
+ if (dstate->rs->state.rs.clip_plane_enable)
+ DUMP(clip_state, &dstate->clip_state);
+
+ for (i = 0; i < num_viewports; i++)
+ DUMP_I(viewport_state, &dstate->viewports[i], i);
+
+ if (dstate->rs->state.rs.scissor)
+ for (i = 0; i < num_viewports; i++)
+ DUMP_I(scissor_state, &dstate->scissors[i], i);
+
+ DUMP(rasterizer_state, &dstate->rs->state.rs);
+
+ if (dstate->rs->state.rs.poly_stipple_enable)
+ DUMP(poly_stipple, &dstate->polygon_stipple);
+ fprintf(f, "\n");
+ }
+
+ if (!dstate->shaders[sh])
+ return;
+
+ fprintf(f, COLOR_SHADER "begin shader: %s" COLOR_RESET "\n", shader_str[sh]);
+ DUMP(shader_state, &dstate->shaders[sh]->state.shader);
+
+ for (i = 0; i < PIPE_MAX_CONSTANT_BUFFERS; i++)
+ if (dstate->constant_buffers[sh][i].buffer ||
+ dstate->constant_buffers[sh][i].user_buffer) {
+ DUMP_I(constant_buffer, &dstate->constant_buffers[sh][i], i);
+ if (dstate->constant_buffers[sh][i].buffer)
+ DUMP_M(resource, &dstate->constant_buffers[sh][i], buffer);
+ }
+
+ for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
+ if (dstate->sampler_states[sh][i])
+ DUMP_I(sampler_state, &dstate->sampler_states[sh][i]->state.sampler, i);
+
+ for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
+ if (dstate->sampler_views[sh][i]) {
+ DUMP_I(sampler_view, dstate->sampler_views[sh][i], i);
+ DUMP_M(resource, dstate->sampler_views[sh][i], texture);
+ }
+
+ for (i = 0; i < PIPE_MAX_SHADER_IMAGES; i++)
+ if (dstate->shader_images[sh][i].resource) {
+ DUMP_I(image_view, &dstate->shader_images[sh][i], i);
+ if (dstate->shader_images[sh][i].resource)
+ DUMP_M(resource, &dstate->shader_images[sh][i], resource);
+ }
+
+ for (i = 0; i < PIPE_MAX_SHADER_BUFFERS; i++)
+ if (dstate->shader_buffers[sh][i].buffer) {
+ DUMP_I(shader_buffer, &dstate->shader_buffers[sh][i], i);
+ if (dstate->shader_buffers[sh][i].buffer)
+ DUMP_M(resource, &dstate->shader_buffers[sh][i], buffer);
+ }
+
+ fprintf(f, COLOR_SHADER "end shader: %s" COLOR_RESET "\n\n", shader_str[sh]);
+}
+
+static void
+dd_dump_draw_vbo(struct dd_draw_state *dstate, struct pipe_draw_info *info, FILE *f)
+{
+ int sh, i;
+
DUMP(draw_info, info);
- if (info->indexed) {
- DUMP(index_buffer, &dctx->index_buffer);
- if (dctx->index_buffer.buffer)
- DUMP_M(resource, &dctx->index_buffer, buffer);
- }
if (info->count_from_stream_output)
DUMP_M(stream_output_target, info,
count_from_stream_output);
- if (info->indirect)
- DUMP_M(resource, info, indirect);
+ if (info->indirect) {
+ DUMP_M(resource, info, indirect->buffer);
+ if (info->indirect->indirect_draw_count)
+ DUMP_M(resource, info, indirect->indirect_draw_count);
+ }
+
fprintf(f, "\n");
/* TODO: dump active queries */
- dd_dump_render_condition(dctx, f);
+ dd_dump_render_condition(dstate, f);
for (i = 0; i < PIPE_MAX_ATTRIBS; i++)
- if (dctx->vertex_buffers[i].buffer ||
- dctx->vertex_buffers[i].user_buffer) {
- DUMP_I(vertex_buffer, &dctx->vertex_buffers[i], i);
- if (dctx->vertex_buffers[i].buffer)
- DUMP_M(resource, &dctx->vertex_buffers[i], buffer);
+ if (dstate->vertex_buffers[i].buffer.resource) {
+ DUMP_I(vertex_buffer, &dstate->vertex_buffers[i], i);
+ if (!dstate->vertex_buffers[i].is_user_buffer)
+ DUMP_M(resource, &dstate->vertex_buffers[i], buffer.resource);
}
- if (dctx->velems) {
- print_named_value(f, "num vertex elements",
- dctx->velems->state.velems.count);
- for (i = 0; i < dctx->velems->state.velems.count; i++) {
+ if (dstate->velems) {
+ PRINT_NAMED(uint, "num vertex elements",
+ dstate->velems->state.velems.count);
+ for (i = 0; i < dstate->velems->state.velems.count; i++) {
fprintf(f, " ");
- DUMP_I(vertex_element, &dctx->velems->state.velems.velems[i], i);
+ DUMP_I(vertex_element, &dstate->velems->state.velems.velems[i], i);
}
}
- print_named_value(f, "num stream output targets", dctx->num_so_targets);
- for (i = 0; i < dctx->num_so_targets; i++)
- if (dctx->so_targets[i]) {
- DUMP_I(stream_output_target, dctx->so_targets[i], i);
- DUMP_M(resource, dctx->so_targets[i], buffer);
- fprintf(f, " offset = %i\n", dctx->so_offsets[i]);
+ PRINT_NAMED(uint, "num stream output targets", dstate->num_so_targets);
+ for (i = 0; i < dstate->num_so_targets; i++)
+ if (dstate->so_targets[i]) {
+ DUMP_I(stream_output_target, dstate->so_targets[i], i);
+ DUMP_M(resource, dstate->so_targets[i], buffer);
+ fprintf(f, " offset = %i\n", dstate->so_offsets[i]);
}
fprintf(f, "\n");
if (sh == PIPE_SHADER_COMPUTE)
continue;
- if (sh == PIPE_SHADER_TESS_CTRL &&
- !dctx->shaders[PIPE_SHADER_TESS_CTRL] &&
- dctx->shaders[PIPE_SHADER_TESS_EVAL])
- fprintf(f, "tess_state: {default_outer_level = {%f, %f, %f, %f}, "
- "default_inner_level = {%f, %f}}\n",
- dctx->tess_default_levels[0],
- dctx->tess_default_levels[1],
- dctx->tess_default_levels[2],
- dctx->tess_default_levels[3],
- dctx->tess_default_levels[4],
- dctx->tess_default_levels[5]);
-
- if (sh == PIPE_SHADER_FRAGMENT)
- if (dctx->rs) {
- unsigned num_viewports = dd_num_active_viewports(dctx);
-
- if (dctx->rs->state.rs.clip_plane_enable)
- DUMP(clip_state, &dctx->clip_state);
-
- for (i = 0; i < num_viewports; i++)
- DUMP_I(viewport_state, &dctx->viewports[i], i);
-
- if (dctx->rs->state.rs.scissor)
- for (i = 0; i < num_viewports; i++)
- DUMP_I(scissor_state, &dctx->scissors[i], i);
-
- DUMP(rasterizer_state, &dctx->rs->state.rs);
-
- if (dctx->rs->state.rs.poly_stipple_enable)
- DUMP(poly_stipple, &dctx->polygon_stipple);
- fprintf(f, "\n");
- }
-
- if (!dctx->shaders[sh])
- continue;
-
- fprintf(f, COLOR_SHADER "begin shader: %s" COLOR_RESET "\n", shader_str[sh]);
- DUMP(shader_state, &dctx->shaders[sh]->state.shader);
-
- for (i = 0; i < PIPE_MAX_CONSTANT_BUFFERS; i++)
- if (dctx->constant_buffers[sh][i].buffer ||
- dctx->constant_buffers[sh][i].user_buffer) {
- DUMP_I(constant_buffer, &dctx->constant_buffers[sh][i], i);
- if (dctx->constant_buffers[sh][i].buffer)
- DUMP_M(resource, &dctx->constant_buffers[sh][i], buffer);
- }
-
- for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
- if (dctx->sampler_states[sh][i])
- DUMP_I(sampler_state, &dctx->sampler_states[sh][i]->state.sampler, i);
-
- for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
- if (dctx->sampler_views[sh][i]) {
- DUMP_I(sampler_view, dctx->sampler_views[sh][i], i);
- DUMP_M(resource, dctx->sampler_views[sh][i], texture);
- }
-
- /* TODO: print shader images */
- /* TODO: print shader buffers */
-
- fprintf(f, COLOR_SHADER "end shader: %s" COLOR_RESET "\n\n", shader_str[sh]);
+ dd_dump_shader(dstate, sh, f);
}
- if (dctx->dsa)
- DUMP(depth_stencil_alpha_state, &dctx->dsa->state.dsa);
- DUMP(stencil_ref, &dctx->stencil_ref);
+ if (dstate->dsa)
+ DUMP(depth_stencil_alpha_state, &dstate->dsa->state.dsa);
+ DUMP(stencil_ref, &dstate->stencil_ref);
- if (dctx->blend)
- DUMP(blend_state, &dctx->blend->state.blend);
- DUMP(blend_color, &dctx->blend_color);
+ if (dstate->blend)
+ DUMP(blend_state, &dstate->blend->state.blend);
+ DUMP(blend_color, &dstate->blend_color);
- print_named_value(f, "min_samples", dctx->min_samples);
- print_named_xvalue(f, "sample_mask", dctx->sample_mask);
+ PRINT_NAMED(uint, "min_samples", dstate->min_samples);
+ PRINT_NAMED(hex, "sample_mask", dstate->sample_mask);
fprintf(f, "\n");
- DUMP(framebuffer_state, &dctx->framebuffer_state);
- for (i = 0; i < dctx->framebuffer_state.nr_cbufs; i++)
- if (dctx->framebuffer_state.cbufs[i]) {
+ DUMP(framebuffer_state, &dstate->framebuffer_state);
+ for (i = 0; i < dstate->framebuffer_state.nr_cbufs; i++)
+ if (dstate->framebuffer_state.cbufs[i]) {
fprintf(f, " " COLOR_STATE "cbufs[%i]:" COLOR_RESET "\n ", i);
- DUMP(surface, dctx->framebuffer_state.cbufs[i]);
+ DUMP(surface, dstate->framebuffer_state.cbufs[i]);
fprintf(f, " ");
- DUMP(resource, dctx->framebuffer_state.cbufs[i]->texture);
+ DUMP(resource, dstate->framebuffer_state.cbufs[i]->texture);
}
- if (dctx->framebuffer_state.zsbuf) {
+ if (dstate->framebuffer_state.zsbuf) {
fprintf(f, " " COLOR_STATE "zsbuf:" COLOR_RESET "\n ");
- DUMP(surface, dctx->framebuffer_state.zsbuf);
+ DUMP(surface, dstate->framebuffer_state.zsbuf);
fprintf(f, " ");
- DUMP(resource, dctx->framebuffer_state.zsbuf->texture);
+ DUMP(resource, dstate->framebuffer_state.zsbuf->texture);
}
fprintf(f, "\n");
}
static void
-dd_dump_resource_copy_region(struct dd_context *dctx,
+dd_dump_launch_grid(struct dd_draw_state *dstate, struct pipe_grid_info *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ DUMP(grid_info, info);
+ fprintf(f, "\n");
+
+ dd_dump_shader(dstate, PIPE_SHADER_COMPUTE, f);
+ fprintf(f, "\n");
+}
+
+static void
+dd_dump_resource_copy_region(struct dd_draw_state *dstate,
struct call_resource_copy_region *info,
FILE *f)
{
DUMP_M(uint, info, dstz);
DUMP_M(resource, info, src);
DUMP_M(uint, info, src_level);
- DUMP_M(box, info, src_box);
+ DUMP_M_ADDR(box, info, src_box);
}
static void
-dd_dump_blit(struct dd_context *dctx, struct pipe_blit_info *info, FILE *f)
+dd_dump_blit(struct dd_draw_state *dstate, struct pipe_blit_info *info, FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
DUMP_M(resource, info, dst.resource);
DUMP_M(uint, info, render_condition_enable);
if (info->render_condition_enable)
- dd_dump_render_condition(dctx, f);
+ dd_dump_render_condition(dstate, f);
}
static void
-dd_dump_flush_resource(struct dd_context *dctx, struct pipe_resource *res,
+dd_dump_generate_mipmap(struct dd_draw_state *dstate, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__+8);
+ /* TODO */
+}
+
+static void
+dd_dump_get_query_result_resource(struct call_get_query_result_resource *info, FILE *f)
+{
+ fprintf(f, "%s:\n", __func__ + 8);
+ DUMP_M(query_type, info, query_type);
+ DUMP_M(uint, info, wait);
+ DUMP_M(query_value_type, info, result_type);
+ DUMP_M(int, info, index);
+ DUMP_M(resource, info, resource);
+ DUMP_M(uint, info, offset);
+}
+
+static void
+dd_dump_flush_resource(struct dd_draw_state *dstate, struct pipe_resource *res,
FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
}
static void
-dd_dump_clear(struct dd_context *dctx, struct call_clear *info, FILE *f)
+dd_dump_clear(struct dd_draw_state *dstate, struct call_clear *info, FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
DUMP_M(uint, info, buffers);
- DUMP_M(color_union, info, color);
+ DUMP_M_ADDR(color_union, info, color);
DUMP_M(double, info, depth);
DUMP_M(hex, info, stencil);
}
static void
-dd_dump_clear_buffer(struct dd_context *dctx, struct call_clear_buffer *info,
+dd_dump_clear_buffer(struct dd_draw_state *dstate, struct call_clear_buffer *info,
FILE *f)
{
int i;
}
static void
-dd_dump_clear_render_target(struct dd_context *dctx, FILE *f)
+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);
/* TODO */
}
static void
-dd_dump_clear_depth_stencil(struct dd_context *dctx, FILE *f)
+dd_dump_clear_depth_stencil(struct dd_draw_state *dstate, FILE *f)
{
fprintf(f, "%s:\n", __func__+8);
/* TODO */
}
static void
-dd_dump_call(struct dd_context *dctx, struct dd_call *call, unsigned flags)
+dd_dump_call(FILE *f, struct dd_draw_state *state, struct dd_call *call)
{
- FILE *f = dd_get_file_stream(dctx);
-
- if (!f)
- return;
-
switch (call->type) {
case CALL_DRAW_VBO:
- dd_dump_draw_vbo(dctx, &call->info.draw_vbo, f);
+ dd_dump_draw_vbo(state, &call->info.draw_vbo.draw, f);
+ break;
+ case CALL_LAUNCH_GRID:
+ dd_dump_launch_grid(state, &call->info.launch_grid, f);
break;
case CALL_RESOURCE_COPY_REGION:
- dd_dump_resource_copy_region(dctx, &call->info.resource_copy_region, f);
+ dd_dump_resource_copy_region(state,
+ &call->info.resource_copy_region, f);
break;
case CALL_BLIT:
- dd_dump_blit(dctx, &call->info.blit, f);
+ dd_dump_blit(state, &call->info.blit, f);
break;
case CALL_FLUSH_RESOURCE:
- dd_dump_flush_resource(dctx, call->info.flush_resource, f);
+ dd_dump_flush_resource(state, call->info.flush_resource, f);
break;
case CALL_CLEAR:
- dd_dump_clear(dctx, &call->info.clear, f);
+ dd_dump_clear(state, &call->info.clear, f);
break;
case CALL_CLEAR_BUFFER:
- dd_dump_clear_buffer(dctx, &call->info.clear_buffer, f);
+ dd_dump_clear_buffer(state, &call->info.clear_buffer, f);
+ break;
+ case CALL_CLEAR_TEXTURE:
+ dd_dump_clear_texture(state, f);
break;
case CALL_CLEAR_RENDER_TARGET:
- dd_dump_clear_render_target(dctx, f);
+ dd_dump_clear_render_target(state, f);
break;
case CALL_CLEAR_DEPTH_STENCIL:
- dd_dump_clear_depth_stencil(dctx, f);
+ 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;
}
-
- dd_dump_driver_state(dctx, f, flags);
- dd_close_file_stream(f);
}
static void
fprintf(stderr, "dd: Aborting the process...\n");
fflush(stdout);
fflush(stderr);
- abort();
+ exit(1);
}
-static bool
-dd_flush_and_check_hang(struct dd_context *dctx,
- struct pipe_fence_handle **flush_fence,
- unsigned flush_flags)
+static void
+dd_unreference_copy_of_call(struct dd_call *dst)
{
- struct pipe_fence_handle *fence = NULL;
- struct pipe_context *pipe = dctx->pipe;
- struct pipe_screen *screen = pipe->screen;
- uint64_t timeout_ms = dd_screen(dctx->base.screen)->timeout_ms;
- bool idle;
+ switch (dst->type) {
+ case CALL_DRAW_VBO:
+ pipe_so_target_reference(&dst->info.draw_vbo.draw.count_from_stream_output, NULL);
+ pipe_resource_reference(&dst->info.draw_vbo.indirect.buffer, NULL);
+ pipe_resource_reference(&dst->info.draw_vbo.indirect.indirect_draw_count, NULL);
+ if (dst->info.draw_vbo.draw.index_size &&
+ !dst->info.draw_vbo.draw.has_user_indices)
+ pipe_resource_reference(&dst->info.draw_vbo.draw.index.resource, NULL);
+ else
+ dst->info.draw_vbo.draw.index.user = NULL;
+ break;
+ case CALL_LAUNCH_GRID:
+ pipe_resource_reference(&dst->info.launch_grid.indirect, NULL);
+ break;
+ case CALL_RESOURCE_COPY_REGION:
+ pipe_resource_reference(&dst->info.resource_copy_region.dst, NULL);
+ pipe_resource_reference(&dst->info.resource_copy_region.src, NULL);
+ break;
+ case CALL_BLIT:
+ pipe_resource_reference(&dst->info.blit.dst.resource, NULL);
+ pipe_resource_reference(&dst->info.blit.src.resource, NULL);
+ break;
+ case CALL_FLUSH_RESOURCE:
+ pipe_resource_reference(&dst->info.flush_resource, NULL);
+ break;
+ case CALL_CLEAR:
+ break;
+ case CALL_CLEAR_BUFFER:
+ pipe_resource_reference(&dst->info.clear_buffer.res, NULL);
+ break;
+ case CALL_CLEAR_TEXTURE:
+ break;
+ 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.
+ */
+ memset(state->base.vertex_buffers, 0,
+ sizeof(state->base.vertex_buffers));
+ memset(state->base.so_targets, 0,
+ sizeof(state->base.so_targets));
+ memset(state->base.constant_buffers, 0,
+ sizeof(state->base.constant_buffers));
+ memset(state->base.sampler_views, 0,
+ sizeof(state->base.sampler_views));
+ memset(state->base.shader_images, 0,
+ sizeof(state->base.shader_images));
+ memset(state->base.shader_buffers, 0,
+ sizeof(state->base.shader_buffers));
+ memset(&state->base.framebuffer_state, 0,
+ sizeof(state->base.framebuffer_state));
+
+ memset(state->shaders, 0, sizeof(state->shaders));
+
+ state->base.render_cond.query = &state->render_cond;
+
+ for (i = 0; i < PIPE_SHADER_TYPES; i++) {
+ state->base.shaders[i] = &state->shaders[i];
+ for (j = 0; j < PIPE_MAX_SAMPLERS; j++)
+ state->base.sampler_states[i][j] = &state->sampler_states[i][j];
+ }
+
+ state->base.velems = &state->velems;
+ state->base.rs = &state->rs;
+ state->base.dsa = &state->dsa;
+ state->base.blend = &state->blend;
+}
+
+static void
+dd_unreference_copy_of_draw_state(struct dd_draw_state_copy *state)
+{
+ struct dd_draw_state *dst = &state->base;
+ unsigned i,j;
+
+ for (i = 0; i < ARRAY_SIZE(dst->vertex_buffers); i++)
+ pipe_vertex_buffer_unreference(&dst->vertex_buffers[i]);
+ for (i = 0; i < ARRAY_SIZE(dst->so_targets); i++)
+ pipe_so_target_reference(&dst->so_targets[i], NULL);
+
+ for (i = 0; i < PIPE_SHADER_TYPES; i++) {
+ if (dst->shaders[i])
+ tgsi_free_tokens(dst->shaders[i]->state.shader.tokens);
+
+ for (j = 0; j < PIPE_MAX_CONSTANT_BUFFERS; j++)
+ pipe_resource_reference(&dst->constant_buffers[i][j].buffer, NULL);
+ for (j = 0; j < PIPE_MAX_SAMPLERS; j++)
+ pipe_sampler_view_reference(&dst->sampler_views[i][j], NULL);
+ for (j = 0; j < PIPE_MAX_SHADER_IMAGES; j++)
+ pipe_resource_reference(&dst->shader_images[i][j].resource, NULL);
+ for (j = 0; j < PIPE_MAX_SHADER_BUFFERS; j++)
+ pipe_resource_reference(&dst->shader_buffers[i][j].buffer, NULL);
+ }
+
+ util_unreference_framebuffer_state(&dst->framebuffer_state);
+}
+
+static void
+dd_copy_draw_state(struct dd_draw_state *dst, struct dd_draw_state *src)
+{
+ unsigned i,j;
+
+ if (src->render_cond.query) {
+ *dst->render_cond.query = *src->render_cond.query;
+ dst->render_cond.condition = src->render_cond.condition;
+ dst->render_cond.mode = src->render_cond.mode;
+ } else {
+ dst->render_cond.query = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(src->vertex_buffers); i++) {
+ pipe_vertex_buffer_reference(&dst->vertex_buffers[i],
+ &src->vertex_buffers[i]);
+ }
+
+ dst->num_so_targets = src->num_so_targets;
+ for (i = 0; i < src->num_so_targets; i++)
+ pipe_so_target_reference(&dst->so_targets[i], src->so_targets[i]);
+ memcpy(dst->so_offsets, src->so_offsets, sizeof(src->so_offsets));
+
+ for (i = 0; i < PIPE_SHADER_TYPES; i++) {
+ if (!src->shaders[i]) {
+ dst->shaders[i] = NULL;
+ continue;
+ }
+
+ if (src->shaders[i]) {
+ dst->shaders[i]->state.shader = src->shaders[i]->state.shader;
+ if (src->shaders[i]->state.shader.tokens) {
+ dst->shaders[i]->state.shader.tokens =
+ tgsi_dup_tokens(src->shaders[i]->state.shader.tokens);
+ } else {
+ dst->shaders[i]->state.shader.ir.nir = NULL;
+ }
+ } else {
+ dst->shaders[i] = NULL;
+ }
+
+ for (j = 0; j < PIPE_MAX_CONSTANT_BUFFERS; j++) {
+ pipe_resource_reference(&dst->constant_buffers[i][j].buffer,
+ src->constant_buffers[i][j].buffer);
+ memcpy(&dst->constant_buffers[i][j], &src->constant_buffers[i][j],
+ sizeof(src->constant_buffers[i][j]));
+ }
+
+ for (j = 0; j < PIPE_MAX_SAMPLERS; j++) {
+ pipe_sampler_view_reference(&dst->sampler_views[i][j],
+ src->sampler_views[i][j]);
+ if (src->sampler_states[i][j])
+ dst->sampler_states[i][j]->state.sampler =
+ src->sampler_states[i][j]->state.sampler;
+ else
+ dst->sampler_states[i][j] = NULL;
+ }
+
+ for (j = 0; j < PIPE_MAX_SHADER_IMAGES; j++) {
+ pipe_resource_reference(&dst->shader_images[i][j].resource,
+ src->shader_images[i][j].resource);
+ memcpy(&dst->shader_images[i][j], &src->shader_images[i][j],
+ sizeof(src->shader_images[i][j]));
+ }
+
+ for (j = 0; j < PIPE_MAX_SHADER_BUFFERS; j++) {
+ pipe_resource_reference(&dst->shader_buffers[i][j].buffer,
+ src->shader_buffers[i][j].buffer);
+ memcpy(&dst->shader_buffers[i][j], &src->shader_buffers[i][j],
+ sizeof(src->shader_buffers[i][j]));
+ }
+ }
+
+ if (src->velems)
+ dst->velems->state.velems = src->velems->state.velems;
+ else
+ dst->velems = NULL;
+
+ if (src->rs)
+ dst->rs->state.rs = src->rs->state.rs;
+ else
+ dst->rs = NULL;
+
+ if (src->dsa)
+ dst->dsa->state.dsa = src->dsa->state.dsa;
+ else
+ dst->dsa = NULL;
+
+ if (src->blend)
+ dst->blend->state.blend = src->blend->state.blend;
+ else
+ dst->blend = NULL;
+
+ dst->blend_color = src->blend_color;
+ dst->stencil_ref = src->stencil_ref;
+ dst->sample_mask = src->sample_mask;
+ dst->min_samples = src->min_samples;
+ dst->clip_state = src->clip_state;
+ util_copy_framebuffer_state(&dst->framebuffer_state, &src->framebuffer_state);
+ memcpy(dst->scissors, src->scissors, sizeof(src->scissors));
+ memcpy(dst->viewports, src->viewports, sizeof(src->viewports));
+ memcpy(dst->tess_default_levels, src->tess_default_levels,
+ sizeof(src->tess_default_levels));
+ dst->apitrace_call_number = src->apitrace_call_number;
+}
+
+static void
+dd_free_record(struct pipe_screen *screen, struct dd_draw_record *record)
+{
+ u_log_page_destroy(record->log_page);
+ dd_unreference_copy_of_call(&record->call);
+ dd_unreference_copy_of_draw_state(&record->draw_state);
+ screen->fence_reference(screen, &record->prev_bottom_of_pipe, NULL);
+ screen->fence_reference(screen, &record->top_of_pipe, NULL);
+ screen->fence_reference(screen, &record->bottom_of_pipe, NULL);
+ util_queue_fence_destroy(&record->driver_finished);
+ FREE(record);
+}
+
+static void
+dd_write_record(FILE *f, struct dd_draw_record *record)
+{
+ PRINT_NAMED(ptr, "pipe", record->dctx->pipe);
+ PRINT_NAMED(ns, "time before (API call)", record->time_before);
+ PRINT_NAMED(ns, "time after (driver done)", record->time_after);
+ fprintf(f, "\n");
+
+ dd_dump_call(f, &record->draw_state.base, &record->call);
+
+ if (record->log_page) {
+ fprintf(f,"\n\n**************************************************"
+ "***************************\n");
+ fprintf(f, "Context Log:\n\n");
+ u_log_page_print(record->log_page, f);
+ }
+}
+
+static void
+dd_maybe_dump_record(struct dd_screen *dscreen, struct dd_draw_record *record)
+{
+ if (dscreen->dump_mode == DD_DUMP_ONLY_HANGS ||
+ (dscreen->dump_mode == DD_DUMP_APITRACE_CALL &&
+ dscreen->apitrace_dump_call != record->draw_state.base.apitrace_call_number))
+ return;
+
+ char name[512];
+ dd_get_debug_filename_and_mkdir(name, sizeof(name), dscreen->verbose);
+ FILE *f = fopen(name, "w");
+ if (!f) {
+ fprintf(stderr, "dd: failed to open %s\n", name);
+ return;
+ }
+
+ dd_write_header(f, dscreen->screen, record->draw_state.base.apitrace_call_number);
+ dd_write_record(f, record);
- assert(timeout_ms > 0);
+ fclose(f);
+}
- pipe->flush(pipe, &fence, flush_flags);
- if (flush_fence)
- screen->fence_reference(screen, flush_fence, fence);
+static const char *
+dd_fence_state(struct pipe_screen *screen, struct pipe_fence_handle *fence,
+ bool *not_reached)
+{
if (!fence)
- return false;
+ return "---";
+
+ bool ok = screen->fence_finish(screen, NULL, fence, 0);
- idle = screen->fence_finish(screen, fence, timeout_ms * 1000000);
- screen->fence_reference(screen, &fence, NULL);
- if (!idle)
- fprintf(stderr, "dd: GPU hang detected!\n");
- return !idle;
+ if (not_reached && !ok)
+ *not_reached = true;
+
+ return ok ? "YES" : "NO ";
}
static void
-dd_flush_and_handle_hang(struct dd_context *dctx,
- struct pipe_fence_handle **fence, unsigned flags,
- const char *cause)
+dd_report_hang(struct dd_context *dctx)
{
- if (dd_flush_and_check_hang(dctx, fence, flags)) {
- FILE *f = dd_get_file_stream(dctx);
+ struct dd_screen *dscreen = dd_screen(dctx->base.screen);
+ struct pipe_screen *screen = dscreen->screen;
+ bool encountered_hang = false;
+ bool stop_output = false;
+ unsigned num_later = 0;
+
+ fprintf(stderr, "GPU hang detected, collecting information...\n\n");
+
+ fprintf(stderr, "Draw # driver prev BOP TOP BOP dump file\n"
+ "-------------------------------------------------------------\n");
+
+ list_for_each_entry(struct dd_draw_record, record, &dctx->records, list) {
+ if (!encountered_hang &&
+ screen->fence_finish(screen, NULL, record->bottom_of_pipe, 0)) {
+ dd_maybe_dump_record(dscreen, record);
+ continue;
+ }
+
+ if (stop_output) {
+ dd_maybe_dump_record(dscreen, record);
+ num_later++;
+ continue;
+ }
+
+ bool driver = util_queue_fence_is_signalled(&record->driver_finished);
+ bool top_not_reached = false;
+ const char *prev_bop = dd_fence_state(screen, record->prev_bottom_of_pipe, NULL);
+ const char *top = dd_fence_state(screen, record->top_of_pipe, &top_not_reached);
+ const char *bop = dd_fence_state(screen, record->bottom_of_pipe, NULL);
+
+ fprintf(stderr, "%-9u %s %s %s %s ",
+ record->draw_call, driver ? "YES" : "NO ", prev_bop, top, bop);
- if (f) {
- fprintf(f, "dd: %s.\n", cause);
- dd_dump_driver_state(dctx, f, PIPE_DEBUG_DEVICE_IS_HUNG);
- dd_close_file_stream(f);
+ char name[512];
+ dd_get_debug_filename_and_mkdir(name, sizeof(name), false);
+
+ FILE *f = fopen(name, "w");
+ if (!f) {
+ fprintf(stderr, "fopen failed\n");
+ } else {
+ fprintf(stderr, "%s\n", name);
+
+ dd_write_header(f, dscreen->screen, record->draw_state.base.apitrace_call_number);
+ dd_write_record(f, record);
+
+ if (!encountered_hang) {
+ dd_dump_driver_state(dctx, f, PIPE_DUMP_DEVICE_STATUS_REGISTERS);
+ dd_dump_dmesg(f);
+ }
+
+ fclose(f);
}
- /* Terminate the process to prevent future hangs. */
- dd_kill_process();
+ if (top_not_reached)
+ stop_output = true;
+ encountered_hang = true;
}
+
+ if (num_later || dctx->record_pending) {
+ fprintf(stderr, "... and %u%s additional draws.\n", num_later,
+ dctx->record_pending ? "+1 (pending)" : "");
+ }
+
+ fprintf(stderr, "\nDone.\n");
+ dd_kill_process();
+}
+
+int
+dd_thread_main(void *input)
+{
+ struct dd_context *dctx = (struct dd_context *)input;
+ struct dd_screen *dscreen = dd_screen(dctx->base.screen);
+ struct pipe_screen *screen = dscreen->screen;
+
+ mtx_lock(&dctx->mutex);
+
+ for (;;) {
+ struct list_head records;
+ struct pipe_fence_handle *fence;
+ struct pipe_fence_handle *fence2 = NULL;
+
+ list_replace(&dctx->records, &records);
+ list_inithead(&dctx->records);
+ dctx->num_records = 0;
+
+ if (dctx->api_stalled)
+ cnd_signal(&dctx->cond);
+
+ if (!list_empty(&records)) {
+ /* Wait for the youngest draw. This means hangs can take a bit longer
+ * to detect, but it's more efficient this way. */
+ struct dd_draw_record *youngest =
+ LIST_ENTRY(struct dd_draw_record, records.prev, list);
+ fence = youngest->bottom_of_pipe;
+ } else if (dctx->record_pending) {
+ /* Wait for pending fences, in case the driver ends up hanging internally. */
+ fence = dctx->record_pending->prev_bottom_of_pipe;
+ fence2 = dctx->record_pending->top_of_pipe;
+ } else if (dctx->kill_thread) {
+ break;
+ } else {
+ cnd_wait(&dctx->cond, &dctx->mutex);
+ continue;
+ }
+ mtx_unlock(&dctx->mutex);
+
+ /* Fences can be NULL legitimately when timeout detection is disabled. */
+ if ((fence &&
+ !screen->fence_finish(screen, NULL, fence,
+ (uint64_t)dscreen->timeout_ms * 1000*1000)) ||
+ (fence2 &&
+ !screen->fence_finish(screen, NULL, fence2,
+ (uint64_t)dscreen->timeout_ms * 1000*1000))) {
+ mtx_lock(&dctx->mutex);
+ list_splice(&records, &dctx->records);
+ dd_report_hang(dctx);
+ /* we won't actually get here */
+ mtx_unlock(&dctx->mutex);
+ }
+
+ list_for_each_entry_safe(struct dd_draw_record, record, &records, list) {
+ dd_maybe_dump_record(dscreen, record);
+ list_del(&record->list);
+ dd_free_record(screen, record);
+ }
+
+ mtx_lock(&dctx->mutex);
+ }
+ mtx_unlock(&dctx->mutex);
+ return 0;
+}
+
+static struct dd_draw_record *
+dd_create_record(struct dd_context *dctx)
+{
+ struct dd_draw_record *record;
+
+ record = MALLOC_STRUCT(dd_draw_record);
+ if (!record)
+ return NULL;
+
+ record->dctx = dctx;
+ record->draw_call = dctx->num_draw_calls;
+
+ record->prev_bottom_of_pipe = NULL;
+ record->top_of_pipe = NULL;
+ record->bottom_of_pipe = NULL;
+ record->log_page = NULL;
+ util_queue_fence_init(&record->driver_finished);
+
+ dd_init_copy_of_draw_state(&record->draw_state);
+ dd_copy_draw_state(&record->draw_state.base, &dctx->draw_state);
+
+ return record;
}
static void
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- switch (dd_screen(dctx->base.screen)->mode) {
- case DD_DETECT_HANGS:
- dd_flush_and_handle_hang(dctx, fence, flags,
- "GPU hang detected in pipe->flush()");
- break;
- case DD_DUMP_ALL_CALLS:
- pipe->flush(pipe, fence, flags);
- break;
- default:
- assert(0);
+ pipe->flush(pipe, fence, flags);
+}
+
+static void
+dd_before_draw(struct dd_context *dctx, struct dd_draw_record *record)
+{
+ struct dd_screen *dscreen = dd_screen(dctx->base.screen);
+ struct pipe_context *pipe = dctx->pipe;
+ struct pipe_screen *screen = dscreen->screen;
+
+ record->time_before = os_time_get_nano();
+
+ if (dscreen->timeout_ms > 0) {
+ if (dscreen->flush_always && dctx->num_draw_calls >= dscreen->skip_count) {
+ pipe->flush(pipe, &record->prev_bottom_of_pipe, 0);
+ screen->fence_reference(screen, &record->top_of_pipe, record->prev_bottom_of_pipe);
+ } else {
+ pipe->flush(pipe, &record->prev_bottom_of_pipe,
+ PIPE_FLUSH_DEFERRED | PIPE_FLUSH_BOTTOM_OF_PIPE);
+ pipe->flush(pipe, &record->top_of_pipe,
+ PIPE_FLUSH_DEFERRED | PIPE_FLUSH_TOP_OF_PIPE);
+ }
+
+ mtx_lock(&dctx->mutex);
+ dctx->record_pending = record;
+ if (list_empty(&dctx->records))
+ cnd_signal(&dctx->cond);
+ mtx_unlock(&dctx->mutex);
}
}
static void
-dd_before_draw(struct dd_context *dctx)
+dd_after_draw_async(void *data)
{
+ struct dd_draw_record *record = (struct dd_draw_record *)data;
+ struct dd_context *dctx = record->dctx;
struct dd_screen *dscreen = dd_screen(dctx->base.screen);
- if (dscreen->mode == DD_DETECT_HANGS &&
- !dscreen->no_flush &&
- dctx->num_draw_calls >= dscreen->skip_count)
- dd_flush_and_handle_hang(dctx, NULL, 0,
- "GPU hang most likely caused by internal "
- "driver commands");
+ record->log_page = u_log_new_page(&dctx->log);
+ record->time_after = os_time_get_nano();
+
+ if (!util_queue_fence_is_signalled(&record->driver_finished))
+ util_queue_fence_signal(&record->driver_finished);
+
+ if (dscreen->dump_mode == DD_DUMP_APITRACE_CALL &&
+ dscreen->apitrace_dump_call > dctx->draw_state.apitrace_call_number) {
+ dd_thread_join(dctx);
+ /* No need to continue. */
+ exit(0);
+ }
}
static void
-dd_after_draw(struct dd_context *dctx, struct dd_call *call)
+dd_after_draw(struct dd_context *dctx, struct dd_draw_record *record)
{
struct dd_screen *dscreen = dd_screen(dctx->base.screen);
+ struct pipe_context *pipe = dctx->pipe;
- if (dctx->num_draw_calls >= dscreen->skip_count) {
- switch (dscreen->mode) {
- case DD_DETECT_HANGS:
- if (!dscreen->no_flush &&
- dd_flush_and_check_hang(dctx, NULL, 0)) {
- dd_dump_call(dctx, call, PIPE_DEBUG_DEVICE_IS_HUNG);
+ if (dscreen->timeout_ms > 0) {
+ unsigned flush_flags;
+ if (dscreen->flush_always && dctx->num_draw_calls >= dscreen->skip_count)
+ flush_flags = 0;
+ else
+ flush_flags = PIPE_FLUSH_DEFERRED | PIPE_FLUSH_BOTTOM_OF_PIPE;
+ pipe->flush(pipe, &record->bottom_of_pipe, flush_flags);
- /* Terminate the process to prevent future hangs. */
- dd_kill_process();
- }
- break;
- case DD_DUMP_ALL_CALLS:
- dd_dump_call(dctx, call, 0);
- break;
- default:
- assert(0);
- }
+ assert(record == dctx->record_pending);
}
+ if (pipe->callback) {
+ util_queue_fence_reset(&record->driver_finished);
+ pipe->callback(pipe, dd_after_draw_async, record, true);
+ } else {
+ dd_after_draw_async(record);
+ }
+
+ mtx_lock(&dctx->mutex);
+ if (unlikely(dctx->num_records > 10000)) {
+ dctx->api_stalled = true;
+ /* Since this is only a heuristic to prevent the API thread from getting
+ * too far ahead, we don't need a loop here. */
+ cnd_wait(&dctx->cond, &dctx->mutex);
+ dctx->api_stalled = false;
+ }
+
+ if (list_empty(&dctx->records))
+ cnd_signal(&dctx->cond);
+
+ list_addtail(&record->list, &dctx->records);
+ dctx->record_pending = NULL;
+ dctx->num_records++;
+ mtx_unlock(&dctx->mutex);
+
++dctx->num_draw_calls;
if (dscreen->skip_count && dctx->num_draw_calls % 10000 == 0)
fprintf(stderr, "Gallium debugger reached %u draw calls.\n",
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
+
+ record->call.type = CALL_DRAW_VBO;
+ record->call.info.draw_vbo.draw = *info;
+ record->call.info.draw_vbo.draw.count_from_stream_output = NULL;
+ pipe_so_target_reference(&record->call.info.draw_vbo.draw.count_from_stream_output,
+ info->count_from_stream_output);
+ if (info->index_size && !info->has_user_indices) {
+ record->call.info.draw_vbo.draw.index.resource = NULL;
+ pipe_resource_reference(&record->call.info.draw_vbo.draw.index.resource,
+ info->index.resource);
+ }
- call.type = CALL_DRAW_VBO;
- call.info.draw_vbo = *info;
+ if (info->indirect) {
+ record->call.info.draw_vbo.indirect = *info->indirect;
+ record->call.info.draw_vbo.draw.indirect = &record->call.info.draw_vbo.indirect;
+
+ record->call.info.draw_vbo.indirect.buffer = NULL;
+ pipe_resource_reference(&record->call.info.draw_vbo.indirect.buffer,
+ info->indirect->buffer);
+ record->call.info.draw_vbo.indirect.indirect_draw_count = NULL;
+ pipe_resource_reference(&record->call.info.draw_vbo.indirect.indirect_draw_count,
+ info->indirect->indirect_draw_count);
+ } else {
+ memset(&record->call.info.draw_vbo.indirect, 0, sizeof(*info->indirect));
+ }
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->draw_vbo(pipe, info);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
+}
+
+static void
+dd_context_launch_grid(struct pipe_context *_pipe,
+ const struct pipe_grid_info *info)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record = dd_create_record(dctx);
+
+ record->call.type = CALL_LAUNCH_GRID;
+ record->call.info.launch_grid = *info;
+ record->call.info.launch_grid.indirect = NULL;
+ pipe_resource_reference(&record->call.info.launch_grid.indirect, info->indirect);
+
+ dd_before_draw(dctx, record);
+ pipe->launch_grid(pipe, info);
+ dd_after_draw(dctx, record);
}
static void
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
-
- call.type = CALL_RESOURCE_COPY_REGION;
- call.info.resource_copy_region.dst = dst;
- call.info.resource_copy_region.dst_level = dst_level;
- call.info.resource_copy_region.dstx = dstx;
- call.info.resource_copy_region.dsty = dsty;
- call.info.resource_copy_region.dstz = dstz;
- call.info.resource_copy_region.src = src;
- call.info.resource_copy_region.src_level = src_level;
- call.info.resource_copy_region.src_box = src_box;
-
- dd_before_draw(dctx);
+ struct dd_draw_record *record = dd_create_record(dctx);
+
+ record->call.type = CALL_RESOURCE_COPY_REGION;
+ record->call.info.resource_copy_region.dst = NULL;
+ pipe_resource_reference(&record->call.info.resource_copy_region.dst, dst);
+ record->call.info.resource_copy_region.dst_level = dst_level;
+ record->call.info.resource_copy_region.dstx = dstx;
+ record->call.info.resource_copy_region.dsty = dsty;
+ record->call.info.resource_copy_region.dstz = dstz;
+ record->call.info.resource_copy_region.src = NULL;
+ pipe_resource_reference(&record->call.info.resource_copy_region.src, src);
+ record->call.info.resource_copy_region.src_level = src_level;
+ record->call.info.resource_copy_region.src_box = *src_box;
+
+ dd_before_draw(dctx, record);
pipe->resource_copy_region(pipe,
dst, dst_level, dstx, dsty, dstz,
src, src_level, src_box);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
}
static void
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_BLIT;
- call.info.blit = *info;
+ record->call.type = CALL_BLIT;
+ record->call.info.blit = *info;
+ record->call.info.blit.dst.resource = NULL;
+ pipe_resource_reference(&record->call.info.blit.dst.resource, info->dst.resource);
+ record->call.info.blit.src.resource = NULL;
+ pipe_resource_reference(&record->call.info.blit.src.resource, info->src.resource);
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->blit(pipe, info);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
+}
+
+static boolean
+dd_context_generate_mipmap(struct pipe_context *_pipe,
+ struct pipe_resource *res,
+ enum pipe_format format,
+ unsigned base_level,
+ unsigned last_level,
+ unsigned first_layer,
+ unsigned last_layer)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record = dd_create_record(dctx);
+ boolean result;
+
+ record->call.type = CALL_GENERATE_MIPMAP;
+ record->call.info.generate_mipmap.res = NULL;
+ pipe_resource_reference(&record->call.info.generate_mipmap.res, res);
+ record->call.info.generate_mipmap.format = format;
+ record->call.info.generate_mipmap.base_level = base_level;
+ record->call.info.generate_mipmap.last_level = last_level;
+ record->call.info.generate_mipmap.first_layer = first_layer;
+ record->call.info.generate_mipmap.last_layer = last_layer;
+
+ dd_before_draw(dctx, record);
+ result = pipe->generate_mipmap(pipe, res, format, base_level, last_level,
+ first_layer, last_layer);
+ dd_after_draw(dctx, record);
+ return result;
+}
+
+static void
+dd_context_get_query_result_resource(struct pipe_context *_pipe,
+ struct pipe_query *query,
+ boolean wait,
+ enum pipe_query_value_type result_type,
+ int index,
+ struct pipe_resource *resource,
+ unsigned offset)
+{
+ struct dd_context *dctx = dd_context(_pipe);
+ struct dd_query *dquery = dd_query(query);
+ struct pipe_context *pipe = dctx->pipe;
+ struct dd_draw_record *record = dd_create_record(dctx);
+
+ record->call.type = CALL_GET_QUERY_RESULT_RESOURCE;
+ record->call.info.get_query_result_resource.query = query;
+ record->call.info.get_query_result_resource.wait = wait;
+ record->call.info.get_query_result_resource.result_type = result_type;
+ record->call.info.get_query_result_resource.index = index;
+ record->call.info.get_query_result_resource.resource = NULL;
+ pipe_resource_reference(&record->call.info.get_query_result_resource.resource,
+ resource);
+ record->call.info.get_query_result_resource.offset = offset;
+
+ /* The query may be deleted by the time we need to print it. */
+ record->call.info.get_query_result_resource.query_type = dquery->type;
+
+ dd_before_draw(dctx, record);
+ pipe->get_query_result_resource(pipe, dquery->query, wait,
+ result_type, index, resource, offset);
+ dd_after_draw(dctx, record);
}
static void
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_FLUSH_RESOURCE;
- call.info.flush_resource = resource;
+ record->call.type = CALL_FLUSH_RESOURCE;
+ record->call.info.flush_resource = NULL;
+ pipe_resource_reference(&record->call.info.flush_resource, resource);
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->flush_resource(pipe, resource);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
}
static void
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_CLEAR;
- call.info.clear.buffers = buffers;
- call.info.clear.color = color;
- call.info.clear.depth = depth;
- call.info.clear.stencil = stencil;
+ record->call.type = CALL_CLEAR;
+ record->call.info.clear.buffers = buffers;
+ record->call.info.clear.color = *color;
+ record->call.info.clear.depth = depth;
+ record->call.info.clear.stencil = stencil;
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->clear(pipe, buffers, color, depth, stencil);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
}
static void
struct pipe_surface *dst,
const union pipe_color_union *color,
unsigned dstx, unsigned dsty,
- unsigned width, unsigned height)
+ unsigned width, unsigned height,
+ bool render_condition_enabled)
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_CLEAR_RENDER_TARGET;
+ record->call.type = CALL_CLEAR_RENDER_TARGET;
- dd_before_draw(dctx);
- pipe->clear_render_target(pipe, dst, color, dstx, dsty, width, height);
- dd_after_draw(dctx, &call);
+ dd_before_draw(dctx, record);
+ pipe->clear_render_target(pipe, dst, color, dstx, dsty, width, height,
+ render_condition_enabled);
+ dd_after_draw(dctx, record);
}
static void
dd_context_clear_depth_stencil(struct pipe_context *_pipe,
struct pipe_surface *dst, unsigned clear_flags,
double depth, unsigned stencil, unsigned dstx,
- unsigned dsty, unsigned width, unsigned height)
+ unsigned dsty, unsigned width, unsigned height,
+ bool render_condition_enabled)
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_CLEAR_DEPTH_STENCIL;
+ record->call.type = CALL_CLEAR_DEPTH_STENCIL;
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->clear_depth_stencil(pipe, dst, clear_flags, depth, stencil,
- dstx, dsty, width, height);
- dd_after_draw(dctx, &call);
+ dstx, dsty, width, height,
+ render_condition_enabled);
+ dd_after_draw(dctx, record);
}
static void
{
struct dd_context *dctx = dd_context(_pipe);
struct pipe_context *pipe = dctx->pipe;
- struct dd_call call;
+ struct dd_draw_record *record = dd_create_record(dctx);
- call.type = CALL_CLEAR_BUFFER;
- call.info.clear_buffer.res = res;
- call.info.clear_buffer.offset = offset;
- call.info.clear_buffer.size = size;
- call.info.clear_buffer.clear_value = clear_value;
- call.info.clear_buffer.clear_value_size = clear_value_size;
+ record->call.type = CALL_CLEAR_BUFFER;
+ record->call.info.clear_buffer.res = NULL;
+ pipe_resource_reference(&record->call.info.clear_buffer.res, res);
+ record->call.info.clear_buffer.offset = offset;
+ record->call.info.clear_buffer.size = size;
+ record->call.info.clear_buffer.clear_value = clear_value;
+ record->call.info.clear_buffer.clear_value_size = clear_value_size;
- dd_before_draw(dctx);
+ dd_before_draw(dctx, record);
pipe->clear_buffer(pipe, res, offset, size, clear_value, clear_value_size);
- dd_after_draw(dctx, &call);
+ dd_after_draw(dctx, record);
+}
+
+static void
+dd_context_clear_texture(struct pipe_context *_pipe,
+ struct pipe_resource *res,
+ unsigned level,
+ const struct pipe_box *box,
+ const void *data)
+{
+ struct dd_context *dctx = dd_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
{
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);
- /* launch_grid */
+ 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);
}