diff --git a/CMakeLists.txt b/CMakeLists.txt index b7d965a6..0b4079d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,7 @@ set(REDREAM_SOURCES src/core/profiler.cc src/core/ringbuf.cc src/core/rb_tree.c + src/core/sort.c src/core/string.c src/hw/aica/aica.c src/hw/arm7/arm7.c @@ -335,6 +336,16 @@ target_link_libraries(recc ${REDREAM_LIBS}) target_compile_definitions(recc PRIVATE ${REDREAM_DEFS} MICROPROFILE_ENABLED=0) target_compile_options(recc PRIVATE ${REDREAM_FLAGS}) +add_executable(retrace $ + src/null_host.c + tools/retrace/depth.c + tools/retrace/main.c) +target_include_directories(retrace SYSTEM PUBLIC ${REDREAM_INCLUDE_DIRS}) +target_include_directories(retrace PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) +target_link_libraries(retrace ${REDREAM_LIBS}) +target_compile_definitions(retrace PRIVATE ${REDREAM_DEFS} MICROPROFILE_ENABLED=0) +target_compile_options(retrace PRIVATE ${REDREAM_FLAGS}) + endif() #-------------------------------------------------- diff --git a/src/core/sort.c b/src/core/sort.c new file mode 100644 index 00000000..a333cbaf --- /dev/null +++ b/src/core/sort.c @@ -0,0 +1,46 @@ +#include +#include +#include "core/sort.h" + +static void merge(void *in, void *out, size_t size, int l, int m, int r, + sort_cmp cmp) { + int i = l; + int j = m; + int k = l; + + while (k < r) { + if ((i < m && cmp(in + i * size, in + j * size)) || j >= r) { + memcpy(out + k * size, in + i * size, size); + k++; + i++; + } else { + memcpy(out + k * size, in + j * size, size); + k++; + j++; + } + } +} + +static void mergesort_r(void *in, void *out, size_t size, int l, int r, + sort_cmp cmp) { + if ((r - l) < 2) { + return; + } + + int m = (l + r) / 2; + mergesort_r(out, in, size, l, m, cmp); + mergesort_r(out, in, size, m, r, cmp); + merge(in, out, size, l, m, r, cmp); +} + +void mergesort_fixed(void *data, void *tmp, int num, size_t size, + sort_cmp cmp) { + memcpy(tmp, data, num * size); + mergesort_r(tmp, data, size, 0, num, cmp); +} + +void mergesort(void *data, int num, size_t size, sort_cmp cmp) { + void *tmp = malloc(num * size); + mergesort_fixed(data, tmp, num, size, cmp); + free(tmp); +} diff --git a/src/core/sort.h b/src/core/sort.h new file mode 100644 index 00000000..1e0c3eb9 --- /dev/null +++ b/src/core/sort.h @@ -0,0 +1,12 @@ +#ifndef SORT_H +#define SORT_H + +#include + +/* returns if a is <= b */ +typedef int (*sort_cmp)(const void *, const void *); + +void mergesort_fixed(void *data, void *tmp, int num, size_t size, sort_cmp cmp); +void mergesort(void *data, int num, size_t size, sort_cmp cmp); + +#endif diff --git a/src/hw/pvr/trace.c b/src/hw/pvr/trace.c index 34249fd5..e4b8f465 100644 --- a/src/hw/pvr/trace.c +++ b/src/hw/pvr/trace.c @@ -175,6 +175,24 @@ void trace_destroy(struct trace *trace) { free(trace); } +void trace_copy_context(const struct trace_cmd *cmd, struct tile_context *ctx) { + CHECK_EQ(cmd->type, TRACE_CMD_CONTEXT); + + ctx->autosort = cmd->context.autosort; + ctx->stride = cmd->context.stride; + ctx->pal_pxl_format = cmd->context.pal_pxl_format; + ctx->bg_isp = cmd->context.bg_isp; + ctx->bg_tsp = cmd->context.bg_tsp; + ctx->bg_tcw = cmd->context.bg_tcw; + ctx->bg_depth = cmd->context.bg_depth; + ctx->video_width = cmd->context.video_width; + ctx->video_height = cmd->context.video_height; + memcpy(ctx->bg_vertices, cmd->context.bg_vertices, + cmd->context.bg_vertices_size); + memcpy(ctx->params, cmd->context.params, cmd->context.params_size); + ctx->size = cmd->context.params_size; +} + struct trace *trace_parse(const char *filename) { struct trace *trace = calloc(1, sizeof(struct trace)); diff --git a/src/hw/pvr/trace.h b/src/hw/pvr/trace.h index 553ea6fe..0d5ca74f 100644 --- a/src/hw/pvr/trace.h +++ b/src/hw/pvr/trace.h @@ -63,16 +63,17 @@ struct trace_writer { void get_next_trace_filename(char *filename, size_t size); -void trace_writer_close(struct trace_writer *writer); -void trace_writer_render_context(struct trace_writer *writer, - struct tile_context *ctx); +struct trace *trace_parse(const char *filename); +void trace_copy_context(const struct trace_cmd *cmd, struct tile_context *ctx); +void trace_destroy(struct trace *trace); + +struct trace_writer *trace_writer_open(const char *filename); void trace_writer_insert_texture(struct trace_writer *writer, union tsp tsp, union tcw tcw, unsigned frame, const uint8_t *palette, int palette_size, const uint8_t *texture, int texture_size); -struct trace_writer *trace_writer_open(const char *filename); - -void trace_destroy(struct trace *trace); -struct trace *trace_parse(const char *filename); +void trace_writer_render_context(struct trace_writer *writer, + struct tile_context *ctx); +void trace_writer_close(struct trace_writer *writer); #endif diff --git a/src/tracer.c b/src/tracer.c index 1a62d118..46836055 100644 --- a/src/tracer.c +++ b/src/tracer.c @@ -149,25 +149,6 @@ static void tracer_add_texture(struct tracer *tracer, tex->palette_size = cmd->texture.palette_size; } -static void tracer_copy_context(const struct trace_cmd *cmd, - struct tile_context *ctx) { - CHECK_EQ(cmd->type, TRACE_CMD_CONTEXT); - - ctx->autosort = cmd->context.autosort; - ctx->stride = cmd->context.stride; - ctx->pal_pxl_format = cmd->context.pal_pxl_format; - ctx->bg_isp = cmd->context.bg_isp; - ctx->bg_tsp = cmd->context.bg_tsp; - ctx->bg_tcw = cmd->context.bg_tcw; - ctx->bg_depth = cmd->context.bg_depth; - ctx->video_width = cmd->context.video_width; - ctx->video_height = cmd->context.video_height; - memcpy(ctx->bg_vertices, cmd->context.bg_vertices, - cmd->context.bg_vertices_size); - memcpy(ctx->params, cmd->context.params, cmd->context.params_size); - ctx->size = cmd->context.params_size; -} - static void tracer_prev_param(struct tracer *tracer) { int i = tracer->current_param; @@ -225,7 +206,7 @@ static void tracer_prev_context(struct tracer *tracer) { tracer->current_cmd = prev; tracer->current_param = -1; tracer->scroll_to_param = 0; - tracer_copy_context(tracer->current_cmd, &tracer->ctx); + trace_copy_context(tracer->current_cmd, &tracer->ctx); tr_convert_context(tracer->r, tracer, &tracer_find_texture, &tracer->ctx, &tracer->rc); } @@ -264,7 +245,7 @@ static void tracer_next_context(struct tracer *tracer) { tracer->current_cmd = next; tracer->current_param = -1; tracer->scroll_to_param = 0; - tracer_copy_context(tracer->current_cmd, &tracer->ctx); + trace_copy_context(tracer->current_cmd, &tracer->ctx); tr_convert_context(tracer->r, tracer, &tracer_find_texture, &tracer->ctx, &tracer->rc); } diff --git a/tools/retrace/depth.c b/tools/retrace/depth.c new file mode 100644 index 00000000..5fb1bb37 --- /dev/null +++ b/tools/retrace/depth.c @@ -0,0 +1,181 @@ +#include +#include +#include "core/assert.h" +#include "core/sort.h" +#include "hw/pvr/tr.h" +#include "hw/pvr/trace.h" + +struct depth_entry { + /* vertex index */ + int n; + + /* depth buffer value */ + union { + float f; + uint32_t i; + } d; +}; + +typedef void (*depth_cb)(float, float, float, struct depth_entry *); + +struct test { + const char *name; + depth_cb depth; + sort_cmp cmp; + int match; + int total; +}; + +static struct tr_texture *find_texture(void *userdata, union tsp tsp, + union tcw tcw) { + /* return a non-zero handle so it doesn't try to create a texture with + the render backend (which is NULL) */ + static struct tr_texture tex; + tex.handle = 1; + return &tex; +} + +static int depth_cmp(const void *a, const void *b) { + const struct depth_entry *ea = (const struct depth_entry *)a; + const struct depth_entry *eb = (const struct depth_entry *)b; + return ea->d.i <= eb->d.i; +} + +static int depth_cmpf(const void *a, const void *b) { + const struct depth_entry *ea = (const struct depth_entry *)a; + const struct depth_entry *eb = (const struct depth_entry *)b; + return ea->d.f <= eb->d.f; +} + +static void test_context(struct trace_cmd *cmd, struct test *tests, + int num_tests) { + CHECK_EQ(cmd->type, TRACE_CMD_CONTEXT); + + struct tile_context *ctx = calloc(1, sizeof(struct tile_context)); + struct tr_context *rc = calloc(1, sizeof(struct tr_context)); + + /* parse the context */ + trace_copy_context(cmd, ctx); + tr_convert_context(NULL, NULL, &find_texture, ctx, rc); + + /* sort each vertex by the original w */ + struct depth_entry *original = + calloc(rc->num_verts, sizeof(struct depth_entry)); + float minw = FLT_MAX; + float maxw = -FLT_MAX; + + for (int i = 0; i < rc->num_verts; i++) { + struct ta_vertex *vert = &rc->verts[i]; + struct depth_entry *entry = &original[i]; + entry->n = i; + entry->d.f = 1.0f / vert->xyz[2]; + + minw = MIN(minw, entry->d.f); + maxw = MAX(maxw, entry->d.f); + } + + mergesort(original, rc->num_verts, sizeof(struct depth_entry), &depth_cmpf); + + for (int i = 0; i < num_tests; i++) { + struct test *test = &tests[i]; + + /* calculate the depth for each vertex using the test's depth function */ + struct depth_entry *tmp = calloc(rc->num_verts, sizeof(struct depth_entry)); + + for (int j = 0; j < rc->num_verts; j++) { + struct ta_vertex *vert = &rc->verts[j]; + struct depth_entry *entry = &tmp[j]; + entry->n = j; + test->depth(1.0f / vert->xyz[2], minw, maxw, entry); + } + + /* sort the vertices based on the depth value */ + mergesort(tmp, rc->num_verts, sizeof(struct depth_entry), test->cmp); + + /* compare sorted results with original results */ + for (int j = 0; j < rc->num_verts; j++) { + if (original[j].n == tmp[j].n) { + test->match++; + } + } + + test->total += rc->num_verts; + + free(tmp); + } + + free(original); + free(rc); + free(ctx); +} + +static void test_flt(float w, float minw, float maxw, + struct depth_entry *entry) { + entry->d.f = (w - minw) / (maxw - minw); +} + +static void test_int(float w, float minw, float maxw, + struct depth_entry *entry) { + entry->d.i = ((w - minw) / (maxw - minw)) * ((1 << 24) - 1); +} + +static void test_log2(float w, float minw, float maxw, + struct depth_entry *entry) { + entry->d.i = (log2(1.0f + w - minw) / log2(maxw - minw)) * ((1 << 24) - 1); +} + +static void test_log2_fixed(float w, float minw, float maxw, + struct depth_entry *entry) { + entry->d.i = (log2(1.0f + w) / 17.0f) * ((1 << 24) - 1); +} + +int cmd_depth(int argc, const char **argv) { + if (argc < 1) { + return 0; + } + + const char *filename = argv[0]; + struct trace *trace = trace_parse(filename); + + struct test tests[] = { + {"32-bit float", &test_flt, &depth_cmpf, 0, 0}, + {"24-bit int", &test_int, &depth_cmp, 0, 0}, + {"24-bit int using log2", &test_log2, &depth_cmp, 0, 0}, + {"24-bit int using log2 w/ fixed max", &test_log2_fixed, &depth_cmp, 0, + 0}, + }; + int num_tests = array_size(tests); + + /* check each context in the trace */ + struct trace_cmd *next = trace->cmds; + while (next) { + if (next->type == TRACE_CMD_CONTEXT) { + test_context(next, tests, num_tests); + break; + } + next = next->next; + } + + trace_destroy(trace); + + /* print results */ + LOG_INFO("===-----------------------------------------------------==="); + LOG_INFO("depth test results"); + LOG_INFO("===-----------------------------------------------------==="); + LOG_INFO(""); + + int max_name_length = 0; + for (int i = 0; i < num_tests; i++) { + struct test *test = &tests[i]; + int l = (int)strlen(test->name); + max_name_length = MAX(max_name_length, l); + } + + for (int i = 0; i < num_tests; i++) { + struct test *test = &tests[i]; + LOG_INFO("%-*s %f%%", max_name_length, test->name, + ((float)test->match / test->total) * 100.0f); + } + + return 1; +} diff --git a/tools/retrace/main.c b/tools/retrace/main.c new file mode 100644 index 00000000..fb065202 --- /dev/null +++ b/tools/retrace/main.c @@ -0,0 +1,28 @@ +#include "core/log.h" + +extern int cmd_depth(int argc, const char **argv); + +static void print_help() { + LOG_INFO("usage: retrace [ ...]"); + LOG_INFO("the available commands are:"); + LOG_INFO(" depth compare depth function accuracies"); +} + +int main(int argc, const char **argv) { + int res = 0; + + if (argc >= 2) { + const char *cmd = argv[1]; + + if (!strcmp(cmd, "depth")) { + res = cmd_depth(argc - 2, argv + 2); + } + } + + if (!res) { + print_help(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}