diff options
-rw-r--r-- | src/gallium/drivers/ddebug/dd_context.c | 65 | ||||
-rw-r--r-- | src/gallium/drivers/ddebug/dd_draw.c | 790 | ||||
-rw-r--r-- | src/gallium/drivers/ddebug/dd_pipe.h | 45 | ||||
-rw-r--r-- | src/gallium/drivers/ddebug/dd_screen.c | 161 |
4 files changed, 546 insertions, 515 deletions
diff --git a/src/gallium/drivers/ddebug/dd_context.c b/src/gallium/drivers/ddebug/dd_context.c index 2abbff933f6..558708df58d 100644 --- a/src/gallium/drivers/ddebug/dd_context.c +++ b/src/gallium/drivers/ddebug/dd_context.c @@ -564,30 +564,33 @@ dd_context_set_stream_output_targets(struct pipe_context *_pipe, pipe->set_stream_output_targets(pipe, num_targets, tgs, offsets); } +void +dd_thread_join(struct dd_context *dctx) +{ + mtx_lock(&dctx->mutex); + dctx->kill_thread = true; + cnd_signal(&dctx->cond); + mtx_unlock(&dctx->mutex); + thrd_join(dctx->thread, NULL); +} + static void dd_context_destroy(struct pipe_context *_pipe) { struct dd_context *dctx = dd_context(_pipe); struct pipe_context *pipe = dctx->pipe; - if (dctx->thread) { - mtx_lock(&dctx->mutex); - dctx->kill_thread = 1; - mtx_unlock(&dctx->mutex); - thrd_join(dctx->thread, NULL); - mtx_destroy(&dctx->mutex); - assert(!dctx->records); - } + dd_thread_join(dctx); + mtx_destroy(&dctx->mutex); + cnd_destroy(&dctx->cond); - if (dctx->fence) { - pipe->transfer_unmap(pipe, dctx->fence_transfer); - pipe_resource_reference(&dctx->fence, NULL); - } + assert(list_empty(&dctx->records)); + assert(!dctx->record_pending); if (pipe->set_log_context) { pipe->set_log_context(pipe, NULL); - if (dd_screen(dctx->base.screen)->mode == DD_DUMP_ALL_CALLS) { + if (dd_screen(dctx->base.screen)->dump_mode == DD_DUMP_ALL_CALLS) { FILE *f = dd_get_file_stream(dd_screen(dctx->base.screen), 0); if (f) { fprintf(f, "Remainder of driver log:\n\n"); @@ -921,39 +924,19 @@ dd_context_create(struct dd_screen *dscreen, struct pipe_context *pipe) dctx->draw_state.sample_mask = ~0; - if (dscreen->mode == DD_DETECT_HANGS_PIPELINED) { - dctx->fence = pipe_buffer_create(dscreen->screen, PIPE_BIND_CUSTOM, - PIPE_USAGE_STAGING, 4); - if (!dctx->fence) - goto fail; - - dctx->mapped_fence = pipe_buffer_map(pipe, dctx->fence, - PIPE_TRANSFER_READ_WRITE | - PIPE_TRANSFER_PERSISTENT | - PIPE_TRANSFER_COHERENT, - &dctx->fence_transfer); - if (!dctx->mapped_fence) - goto fail; - - *dctx->mapped_fence = 0; - - (void) mtx_init(&dctx->mutex, mtx_plain); - dctx->thread = u_thread_create(dd_thread_pipelined_hang_detect, dctx); - if (!dctx->thread) { - mtx_destroy(&dctx->mutex); - goto fail; - } + list_inithead(&dctx->records); + (void) mtx_init(&dctx->mutex, mtx_plain); + (void) cnd_init(&dctx->cond); + dctx->thread = u_thread_create(dd_thread_main, dctx); + if (!dctx->thread) { + mtx_destroy(&dctx->mutex); + goto fail; } return &dctx->base; fail: - if (dctx) { - if (dctx->mapped_fence) - pipe_transfer_unmap(pipe, dctx->fence_transfer); - pipe_resource_reference(&dctx->fence, NULL); - FREE(dctx); - } + FREE(dctx); pipe->destroy(pipe); return NULL; } diff --git a/src/gallium/drivers/ddebug/dd_draw.c b/src/gallium/drivers/ddebug/dd_draw.c index a15801beb1d..99c9c929b2e 100644 --- a/src/gallium/drivers/ddebug/dd_draw.c +++ b/src/gallium/drivers/ddebug/dd_draw.c @@ -39,16 +39,10 @@ #include <inttypes.h> -FILE * -dd_get_file_stream(struct dd_screen *dscreen, unsigned apitrace_call_number) +static void +dd_write_header(FILE *f, struct pipe_screen *screen, unsigned apitrace_call_number) { - struct pipe_screen *screen = dscreen->screen; char cmd_line[4096]; - - FILE *f = dd_get_debug_file(dscreen->verbose); - if (!f) - return NULL; - 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)); @@ -56,8 +50,19 @@ dd_get_file_stream(struct dd_screen *dscreen, unsigned apitrace_call_number) 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); + 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; } @@ -77,12 +82,6 @@ dd_dump_dmesg(FILE *f) pclose(p); } -static void -dd_close_file_stream(FILE *f) -{ - fclose(f); -} - static unsigned dd_num_active_viewports(struct dd_draw_state *dstate) { @@ -550,29 +549,6 @@ dd_dump_call(FILE *f, struct dd_draw_state *state, struct dd_call *call) } static void -dd_write_report(struct dd_context *dctx, struct dd_call *call, unsigned flags, - bool dump_dmesg) -{ - FILE *f = dd_get_file_stream(dd_screen(dctx->base.screen), - dctx->draw_state.apitrace_call_number); - - if (!f) - return; - - dd_dump_call(f, &dctx->draw_state, call); - dd_dump_driver_state(dctx, f, flags); - - fprintf(f,"\n\n**************************************************" - "***************************\n"); - fprintf(f, "Context Log:\n\n"); - u_log_new_page_print(&dctx->log, f); - - if (dump_dmesg) - dd_dump_dmesg(f); - dd_close_file_stream(f); -} - -static void dd_kill_process(void) { sync(); @@ -582,54 +558,6 @@ dd_kill_process(void) exit(1); } -static bool -dd_flush_and_check_hang(struct dd_context *dctx, - struct pipe_fence_handle **flush_fence, - unsigned flush_flags) -{ - 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; - - assert(timeout_ms > 0); - - pipe->flush(pipe, &fence, flush_flags); - if (flush_fence) - screen->fence_reference(screen, flush_fence, fence); - if (!fence) - return false; - - idle = screen->fence_finish(screen, pipe, fence, timeout_ms * 1000000); - screen->fence_reference(screen, &fence, NULL); - if (!idle) - fprintf(stderr, "dd: GPU hang detected!\n"); - return !idle; -} - -static void -dd_flush_and_handle_hang(struct dd_context *dctx, - struct pipe_fence_handle **fence, unsigned flags, - const char *cause) -{ - if (dd_flush_and_check_hang(dctx, fence, flags)) { - FILE *f = dd_get_file_stream(dd_screen(dctx->base.screen), - dctx->draw_state.apitrace_call_number); - - if (f) { - fprintf(f, "dd: %s.\n", cause); - dd_dump_driver_state(dctx, f, - PIPE_DUMP_DEVICE_STATUS_REGISTERS); - dd_dump_dmesg(f); - dd_close_file_stream(f); - } - - /* Terminate the process to prevent future hangs. */ - dd_kill_process(); - } -} - static void dd_unreference_copy_of_call(struct dd_call *dst) { @@ -679,89 +607,6 @@ dd_unreference_copy_of_call(struct dd_call *dst) } static void -dd_copy_call(struct dd_call *dst, struct dd_call *src) -{ - dst->type = src->type; - - switch (src->type) { - case CALL_DRAW_VBO: - pipe_so_target_reference(&dst->info.draw_vbo.draw.count_from_stream_output, - src->info.draw_vbo.draw.count_from_stream_output); - pipe_resource_reference(&dst->info.draw_vbo.indirect.buffer, - src->info.draw_vbo.indirect.buffer); - pipe_resource_reference(&dst->info.draw_vbo.indirect.indirect_draw_count, - src->info.draw_vbo.indirect.indirect_draw_count); - - 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; - - if (src->info.draw_vbo.draw.index_size && - !src->info.draw_vbo.draw.has_user_indices) { - pipe_resource_reference(&dst->info.draw_vbo.draw.index.resource, - src->info.draw_vbo.draw.index.resource); - } - - dst->info.draw_vbo = src->info.draw_vbo; - if (!src->info.draw_vbo.draw.indirect) - dst->info.draw_vbo.draw.indirect = NULL; - else - dst->info.draw_vbo.draw.indirect = &dst->info.draw_vbo.indirect; - break; - case CALL_LAUNCH_GRID: - pipe_resource_reference(&dst->info.launch_grid.indirect, - src->info.launch_grid.indirect); - dst->info.launch_grid = src->info.launch_grid; - break; - case CALL_RESOURCE_COPY_REGION: - pipe_resource_reference(&dst->info.resource_copy_region.dst, - src->info.resource_copy_region.dst); - pipe_resource_reference(&dst->info.resource_copy_region.src, - src->info.resource_copy_region.src); - dst->info.resource_copy_region = src->info.resource_copy_region; - break; - case CALL_BLIT: - pipe_resource_reference(&dst->info.blit.dst.resource, - src->info.blit.dst.resource); - pipe_resource_reference(&dst->info.blit.src.resource, - src->info.blit.src.resource); - dst->info.blit = src->info.blit; - break; - case CALL_FLUSH_RESOURCE: - pipe_resource_reference(&dst->info.flush_resource, - src->info.flush_resource); - break; - case CALL_CLEAR: - dst->info.clear = src->info.clear; - break; - case CALL_CLEAR_BUFFER: - pipe_resource_reference(&dst->info.clear_buffer.res, - src->info.clear_buffer.res); - dst->info.clear_buffer = src->info.clear_buffer; - 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, - src->info.generate_mipmap.res); - dst->info.generate_mipmap = src->info.generate_mipmap; - break; - case CALL_GET_QUERY_RESULT_RESOURCE: - pipe_resource_reference(&dst->info.get_query_result_resource.resource, - src->info.get_query_result_resource.resource); - dst->info.get_query_result_resource = src->info.get_query_result_resource; - dst->info.get_query_result_resource.query = NULL; - break; - } -} - -static void dd_init_copy_of_draw_state(struct dd_draw_state_copy *state) { unsigned i,j; @@ -935,137 +780,225 @@ dd_copy_draw_state(struct dd_draw_state *dst, struct dd_draw_state *src) } static void -dd_free_record(struct dd_draw_record **record) +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) { - struct dd_draw_record *next = (*record)->next; + dd_dump_call(f, &record->draw_state.base, &record->call); - u_log_page_destroy((*record)->log_page); - dd_unreference_copy_of_call(&(*record)->call); - dd_unreference_copy_of_draw_state(&(*record)->draw_state); - FREE(*record); - *record = next; + 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_dump_record(struct dd_context *dctx, struct dd_draw_record *record, - uint32_t hw_sequence_no, int64_t now) +dd_maybe_dump_record(struct dd_screen *dscreen, struct dd_draw_record *record) { - FILE *f = dd_get_file_stream(dd_screen(dctx->base.screen), - record->draw_state.base.apitrace_call_number); - if (!f) + 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; - fprintf(f, "Draw call sequence # = %u\n", record->sequence_no); - fprintf(f, "HW reached sequence # = %u\n", hw_sequence_no); - fprintf(f, "Elapsed time = %"PRIi64" ms\n\n", - (now - record->timestamp) / 1000); - - dd_dump_call(f, &record->draw_state.base, &record->call); + 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; + } - fprintf(f,"\n\n**************************************************" - "***************************\n"); - fprintf(f, "Context Log:\n\n"); - u_log_page_print(record->log_page, f); + dd_write_header(f, dscreen->screen, record->draw_state.base.apitrace_call_number); + dd_write_record(f, record); - dctx->pipe->dump_debug_state(dctx->pipe, f, - PIPE_DUMP_DEVICE_STATUS_REGISTERS); - dd_dump_dmesg(f); fclose(f); } -int -dd_thread_pipelined_hang_detect(void *input) +static const char * +dd_fence_state(struct pipe_screen *screen, struct pipe_fence_handle *fence, + bool *not_reached) +{ + if (!fence) + return "---"; + + bool ok = screen->fence_finish(screen, NULL, fence, 0); + + if (not_reached && !ok) + *not_reached = true; + + return ok ? "YES" : "NO "; +} + +static void +dd_report_hang(struct dd_context *dctx) { - struct dd_context *dctx = (struct dd_context *)input; 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; - mtx_lock(&dctx->mutex); + fprintf(stderr, "GPU hang detected, collecting information...\n\n"); - while (!dctx->kill_thread) { - struct dd_draw_record **record = &dctx->records; + fprintf(stderr, "Draw # driver prev BOP TOP BOP dump file\n" + "-------------------------------------------------------------\n"); - /* Loop over all records. */ - while (*record) { - int64_t now; + 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 the fence has been signalled, release the record and all older - * records. - */ - if (*dctx->mapped_fence >= (*record)->sequence_no) { - while (*record) - dd_free_record(record); - break; - } + if (stop_output) { + dd_maybe_dump_record(dscreen, record); + num_later++; + continue; + } - /* The fence hasn't been signalled. Check the timeout. */ - now = os_time_get(); - if (os_time_timeout((*record)->timestamp, - (*record)->timestamp + dscreen->timeout_ms * 1000, - now)) { - fprintf(stderr, "GPU hang detected.\n"); + 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); - /* Get the oldest unsignalled draw call. */ - while ((*record)->next && - *dctx->mapped_fence < (*record)->next->sequence_no) - record = &(*record)->next; + fprintf(stderr, "%-9u %s %s %s %s ", + record->draw_call, driver ? "YES" : "NO ", prev_bop, top, bop); - dd_dump_record(dctx, *record, *dctx->mapped_fence, now); - dd_kill_process(); + 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); } - record = &(*record)->next; + fclose(f); } - /* Unlock and sleep before starting all over again. */ - mtx_unlock(&dctx->mutex); - os_time_sleep(10000); /* 10 ms */ - mtx_lock(&dctx->mutex); + if (top_not_reached) + stop_output = true; + encountered_hang = true; } - /* Thread termination. */ - while (dctx->records) - dd_free_record(&dctx->records); + 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, + dscreen->timeout_ms * 1000*1000)) || + (fence2 && + !screen->fence_finish(screen, NULL, fence2, + 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 void -dd_pipelined_process_draw(struct dd_context *dctx, struct dd_call *call) +static struct dd_draw_record * +dd_create_record(struct dd_context *dctx) { - struct pipe_context *pipe = dctx->pipe; struct dd_draw_record *record; - /* Make a record of the draw call. */ record = MALLOC_STRUCT(dd_draw_record); if (!record) - return; + return NULL; - /* Update the fence with the GPU. - * - * radeonsi/clear_buffer waits in the command processor until shaders are - * idle before writing to memory. That's a necessary condition for isolating - * draw calls. - */ - dctx->sequence_no++; - pipe->clear_buffer(pipe, dctx->fence, 0, 4, &dctx->sequence_no, 4); + record->dctx = dctx; + record->draw_call = dctx->num_draw_calls; - /* Initialize the record. */ - record->timestamp = os_time_get(); - record->sequence_no = dctx->sequence_no; - record->log_page = u_log_new_page(&dctx->log); - - memset(&record->call, 0, sizeof(record->call)); - dd_copy_call(&record->call, call); + 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); - /* Add the record to the list. */ - mtx_lock(&dctx->mutex); - record->next = dctx->records; - dctx->records = record; - mtx_unlock(&dctx->mutex); + return record; } static void @@ -1075,78 +1008,96 @@ dd_context_flush(struct pipe_context *_pipe, 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_DETECT_HANGS_PIPELINED: /* nothing to do here */ - case DD_DUMP_ALL_CALLS: - case DD_DUMP_APITRACE_CALL: - 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; + + 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); + + 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_write_report(dctx, call, - PIPE_DUMP_DEVICE_STATUS_REGISTERS, - true); - - /* Terminate the process to prevent future hangs. */ - dd_kill_process(); - } else { - u_log_page_destroy(u_log_new_page(&dctx->log)); - } - break; - case DD_DETECT_HANGS_PIPELINED: - dd_pipelined_process_draw(dctx, call); - break; - case DD_DUMP_ALL_CALLS: - if (!dscreen->no_flush) - pipe->flush(pipe, NULL, 0); - dd_write_report(dctx, call, 0, false); - break; - case DD_DUMP_APITRACE_CALL: - if (dscreen->apitrace_dump_call == - dctx->draw_state.apitrace_call_number) { - dd_write_report(dctx, call, 0, false); - /* No need to continue. */ - exit(0); - } else { - u_log_page_destroy(u_log_new_page(&dctx->log)); - } - break; - default: - assert(0); - } + 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); + + 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", @@ -1159,20 +1110,36 @@ dd_context_draw_vbo(struct pipe_context *_pipe, { 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.draw = *info; if (info->indirect) { - call.info.draw_vbo.indirect = *info->indirect; - call.info.draw_vbo.draw.indirect = &call.info.draw_vbo.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(&call.info.draw_vbo.indirect, 0, sizeof(*info->indirect)); + 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 @@ -1181,14 +1148,16 @@ dd_context_launch_grid(struct pipe_context *_pipe, { 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_LAUNCH_GRID; - call.info.launch_grid = *info; + 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); + dd_before_draw(dctx, record); pipe->launch_grid(pipe, info); - dd_after_draw(dctx, &call); + dd_after_draw(dctx, record); } static void @@ -1200,23 +1169,25 @@ dd_context_resource_copy_region(struct pipe_context *_pipe, { 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 @@ -1224,14 +1195,18 @@ dd_context_blit(struct pipe_context *_pipe, const struct pipe_blit_info *info) { 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 @@ -1245,21 +1220,22 @@ dd_context_generate_mipmap(struct pipe_context *_pipe, { 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); boolean result; - call.type = CALL_GENERATE_MIPMAP; - call.info.generate_mipmap.res = res; - call.info.generate_mipmap.format = format; - call.info.generate_mipmap.base_level = base_level; - call.info.generate_mipmap.last_level = last_level; - call.info.generate_mipmap.first_layer = first_layer; - call.info.generate_mipmap.last_layer = last_layer; + 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); + dd_before_draw(dctx, record); result = pipe->generate_mipmap(pipe, res, format, base_level, last_level, first_layer, last_layer); - dd_after_draw(dctx, &call); + dd_after_draw(dctx, record); return result; } @@ -1275,25 +1251,25 @@ dd_context_get_query_result_resource(struct pipe_context *_pipe, struct dd_context *dctx = dd_context(_pipe); struct dd_query *dquery = dd_query(query); struct pipe_context *pipe = dctx->pipe; - struct dd_call call; - - call.type = CALL_GET_QUERY_RESULT_RESOURCE; - call.info.get_query_result_resource.query = query; - call.info.get_query_result_resource.wait = wait; - call.info.get_query_result_resource.result_type = result_type; - call.info.get_query_result_resource.index = index; - call.info.get_query_result_resource.resource = resource; - call.info.get_query_result_resource.offset = offset; - - /* In pipelined mode, the query may be deleted by the time we need to - * print it. - */ - call.info.get_query_result_resource.query_type = dquery->type; - - dd_before_draw(dctx); + 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, &call); + dd_after_draw(dctx, record); } static void @@ -1302,14 +1278,15 @@ dd_context_flush_resource(struct pipe_context *_pipe, { 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 @@ -1319,17 +1296,17 @@ dd_context_clear(struct pipe_context *_pipe, unsigned buffers, { 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 @@ -1342,14 +1319,14 @@ dd_context_clear_render_target(struct pipe_context *_pipe, { 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); + dd_before_draw(dctx, record); pipe->clear_render_target(pipe, dst, color, dstx, dsty, width, height, render_condition_enabled); - dd_after_draw(dctx, &call); + dd_after_draw(dctx, record); } static void @@ -1361,15 +1338,15 @@ dd_context_clear_depth_stencil(struct pipe_context *_pipe, { 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, render_condition_enabled); - dd_after_draw(dctx, &call); + dd_after_draw(dctx, record); } static void @@ -1379,18 +1356,19 @@ dd_context_clear_buffer(struct pipe_context *_pipe, struct pipe_resource *res, { 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 @@ -1402,13 +1380,13 @@ dd_context_clear_texture(struct pipe_context *_pipe, { 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_TEXTURE; + record->call.type = CALL_CLEAR_TEXTURE; - dd_before_draw(dctx); + dd_before_draw(dctx, record); pipe->clear_texture(pipe, res, level, box, data); - dd_after_draw(dctx, &call); + dd_after_draw(dctx, record); } void diff --git a/src/gallium/drivers/ddebug/dd_pipe.h b/src/gallium/drivers/ddebug/dd_pipe.h index 252dbffac86..d1965be9a14 100644 --- a/src/gallium/drivers/ddebug/dd_pipe.h +++ b/src/gallium/drivers/ddebug/dd_pipe.h @@ -33,11 +33,14 @@ #include "pipe/p_screen.h" #include "dd_util.h" #include "os/os_thread.h" +#include "util/list.h" #include "util/u_log.h" +#include "util/u_queue.h" -enum dd_mode { - DD_DETECT_HANGS, - DD_DETECT_HANGS_PIPELINED, +struct dd_context; + +enum dd_dump_mode { + DD_DUMP_ONLY_HANGS, DD_DUMP_ALL_CALLS, DD_DUMP_APITRACE_CALL, }; @@ -47,8 +50,8 @@ struct dd_screen struct pipe_screen base; struct pipe_screen *screen; unsigned timeout_ms; - enum dd_mode mode; - bool no_flush; + enum dd_dump_mode dump_mode; + bool flush_always; bool verbose; unsigned skip_count; unsigned apitrace_dump_call; @@ -218,13 +221,19 @@ struct dd_draw_state_copy }; struct dd_draw_record { - struct dd_draw_record *next; + struct list_head list; + struct dd_context *dctx; + + unsigned draw_call; - int64_t timestamp; - uint32_t sequence_no; + struct pipe_fence_handle *prev_bottom_of_pipe; + struct pipe_fence_handle *top_of_pipe; + struct pipe_fence_handle *bottom_of_pipe; struct dd_call call; struct dd_draw_state_copy draw_state; + + struct util_queue_fence driver_finished; struct u_log_page *log_page; }; @@ -252,17 +261,16 @@ struct dd_context * * An independent, separate thread loops over the list of records and checks * their fences. Records with signalled fences are freed. On fence timeout, - * the thread dumps the record of the oldest unsignalled fence. + * the thread dumps the records of in-flight draws. */ thrd_t thread; mtx_t mutex; - int kill_thread; - struct pipe_resource *fence; - struct pipe_transfer *fence_transfer; - uint32_t *mapped_fence; - uint32_t sequence_no; - struct dd_draw_record *records; - int max_log_buffer_size; + cnd_t cond; + struct dd_draw_record *record_pending; /* currently inside the driver */ + struct list_head records; /* oldest record first */ + unsigned num_records; + bool kill_thread; + bool api_stalled; }; @@ -271,8 +279,11 @@ dd_context_create(struct dd_screen *dscreen, struct pipe_context *pipe); void dd_init_draw_functions(struct dd_context *dctx); + +void +dd_thread_join(struct dd_context *dctx); int -dd_thread_pipelined_hang_detect(void *input); +dd_thread_main(void *input); FILE * dd_get_file_stream(struct dd_screen *dscreen, unsigned apitrace_call_number); diff --git a/src/gallium/drivers/ddebug/dd_screen.c b/src/gallium/drivers/ddebug/dd_screen.c index caf31f6df0f..11d1d8c1e9c 100644 --- a/src/gallium/drivers/ddebug/dd_screen.c +++ b/src/gallium/drivers/ddebug/dd_screen.c @@ -28,6 +28,7 @@ #include "dd_pipe.h" #include "dd_public.h" #include "util/u_memory.h" +#include <ctype.h> #include <stdio.h> @@ -381,15 +382,55 @@ dd_screen_destroy(struct pipe_screen *_screen) FREE(dscreen); } +static void +skip_space(const char **p) +{ + while (isspace(**p)) + (*p)++; +} + +static bool +match_word(const char **cur, const char *word) +{ + size_t len = strlen(word); + if (strncmp(*cur, word, len) != 0) + return false; + + const char *p = *cur + len; + if (*p) { + if (!isspace(*p)) + return false; + + *cur = p + 1; + } else { + *cur = p; + } + + return true; +} + +static bool +match_uint(const char **cur, unsigned *value) +{ + char *end; + unsigned v = strtoul(*cur, &end, 0); + if (end == *cur || (*end && !isspace(*end))) + return false; + *cur = end; + *value = v; + return true; +} + struct pipe_screen * ddebug_screen_create(struct pipe_screen *screen) { struct dd_screen *dscreen; const char *option; - bool no_flush; - unsigned timeout = 0; + bool flush = false; + bool verbose = false; + unsigned timeout = 1000; unsigned apitrace_dump_call = 0; - enum dd_mode mode; + enum dd_dump_mode mode = DD_DUMP_ONLY_HANGS; option = debug_get_option("GALLIUM_DDEBUG", NULL); if (!option) @@ -400,53 +441,70 @@ ddebug_screen_create(struct pipe_screen *screen) puts(""); puts("Usage:"); puts(""); - puts(" GALLIUM_DDEBUG=\"always [noflush] [verbose]\""); - puts(" Flush and dump context and driver information after every draw call into"); - puts(" $HOME/"DD_DIR"/."); + puts(" GALLIUM_DDEBUG=\"[<timeout in ms>] [(always|apitrace <call#)] [flush] [verbose]\""); + puts(" GALLIUM_DDEBUG_SKIP=[count]"); puts(""); - puts(" GALLIUM_DDEBUG=\"[timeout in ms] [noflush] [verbose]\""); - puts(" Flush and detect a device hang after every draw call based on the given"); - puts(" fence timeout and dump context and driver information into"); - puts(" $HOME/"DD_DIR"/ when a hang is detected."); + 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(" GALLIUM_DDEBUG=\"pipelined [timeout in ms] [verbose]\""); - puts(" Detect a device hang after every draw call based on the given fence"); - puts(" timeout without flushes and dump context and driver information into"); - puts(" $HOME/"DD_DIR"/ when a hang is detected."); + 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(" GALLIUM_DDEBUG=\"apitrace [call#] [verbose]\""); - puts(" Dump apitrace draw call information into $HOME/"DD_DIR"/. Implies 'noflush'."); + puts("always"); + puts(" Dump information about all draw calls."); puts(""); - puts(" If 'noflush' is specified, do not flush on every draw call. In hang"); - puts(" detection mode, this only detect hangs in pipe->flush."); - puts(" If 'verbose' is specified, additional information is written to stderr."); + puts("apitrace <call#>"); + puts(" Dump information about the draw call corresponding to the given"); + puts(" apitrace call number and exit."); puts(""); - puts(" GALLIUM_DDEBUG_SKIP=[count]"); - puts(" Skip flush and hang detection for the given initial number of draw calls."); + puts("flush"); + puts(" Flush after every draw call."); + puts(""); + puts("verbose"); + puts(" Write additional information to stderr."); + puts(""); + puts("GALLIUM_DDEBUG_SKIP=count"); + puts(" Skip dumping on the first count draw calls (only relevant with 'always')."); puts(""); exit(0); } - no_flush = strstr(option, "noflush") != NULL; - - if (!strncmp(option, "always", 6)) { - mode = DD_DUMP_ALL_CALLS; - } else if (!strncmp(option, "apitrace", 8)) { - mode = DD_DUMP_APITRACE_CALL; - no_flush = true; - - if (sscanf(option+8, "%u", &apitrace_dump_call) != 1) - return screen; - } else if (!strncmp(option, "pipelined", 9)) { - mode = DD_DETECT_HANGS_PIPELINED; - - if (sscanf(option+10, "%u", &timeout) != 1) - return screen; - } else { - mode = DD_DETECT_HANGS; - - if (sscanf(option, "%u", &timeout) != 1) - return screen; + for (;;) { + skip_space(&option); + if (!*option) + break; + + 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, "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"); + exit(1); + } + + mode = DD_DUMP_APITRACE_CALL; + } else if (match_uint(&option, &timeout)) { + /* no-op */ + } else { + printf("ddebug: bad options: %s\n", option); + exit(1); + } } dscreen = CALLOC_STRUCT(dd_screen); @@ -496,27 +554,28 @@ ddebug_screen_create(struct pipe_screen *screen) dscreen->screen = screen; dscreen->timeout_ms = timeout; - dscreen->mode = mode; - dscreen->no_flush = no_flush; - dscreen->verbose = strstr(option, "verbose") != NULL; + dscreen->dump_mode = mode; + dscreen->flush_always = flush; + dscreen->verbose = verbose; dscreen->apitrace_dump_call = apitrace_dump_call; - switch (dscreen->mode) { + switch (dscreen->dump_mode) { case DD_DUMP_ALL_CALLS: fprintf(stderr, "Gallium debugger active. Logging all calls.\n"); break; - case DD_DETECT_HANGS: - case DD_DETECT_HANGS_PIPELINED: - fprintf(stderr, "Gallium debugger active. " - "The hang detection timeout is %i ms.\n", timeout); - break; case DD_DUMP_APITRACE_CALL: fprintf(stderr, "Gallium debugger active. Going to dump an apitrace call.\n"); break; default: - assert(0); + fprintf(stderr, "Gallium debugger active.\n"); + break; } + if (dscreen->timeout_ms > 0) + fprintf(stderr, "Hang detection timeout is %ums.\n", dscreen->timeout_ms); + else + fprintf(stderr, "Hang detection is disabled.\n"); + dscreen->skip_count = debug_get_num_option("GALLIUM_DDEBUG_SKIP", 0); if (dscreen->skip_count > 0) { fprintf(stderr, "Gallium debugger skipping the first %u draw calls.\n", |