mirror of https://github.com/inolen/redream.git
remove multitheading code from ta, and add into emulator
This commit is contained in:
parent
824be359eb
commit
9b1fc09861
165
src/emulator.c
165
src/emulator.c
|
@ -49,6 +49,11 @@ struct emu {
|
|||
volatile int running;
|
||||
int debug_menu;
|
||||
|
||||
/* last tile context submitted by the dreamcast to be rendered */
|
||||
mutex_t pending_mutex;
|
||||
cond_t pending_cond;
|
||||
struct tile_ctx *pending_ctx;
|
||||
|
||||
/* pool of offscreen framebuffers used for rendering the video display */
|
||||
mutex_t frames_mutex;
|
||||
struct frame frames[MAX_FRAMES];
|
||||
|
@ -96,6 +101,43 @@ static int emu_launch_gdi(struct emu *emu, const char *path) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* multithreaded, offscreen video rendering
|
||||
*/
|
||||
static void emu_cancel_render(struct emu *emu) {
|
||||
mutex_lock(emu->pending_mutex);
|
||||
|
||||
emu->pending_ctx = NULL;
|
||||
cond_signal(emu->pending_cond);
|
||||
|
||||
mutex_unlock(emu->pending_mutex);
|
||||
}
|
||||
|
||||
static void emu_finish_render(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
/* ideally, the video thread has parsed the pending context, uploaded its
|
||||
textures, etc. during the estimated render time. however, if it hasn't
|
||||
finished, the emulation thread must be paused to avoid altering
|
||||
the yet-to-be-uploaded texture memory */
|
||||
mutex_lock(emu->pending_mutex);
|
||||
|
||||
emu->pending_ctx = NULL;
|
||||
|
||||
mutex_unlock(emu->pending_mutex);
|
||||
}
|
||||
|
||||
static void emu_start_render(void *userdata, struct tile_ctx *ctx) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
mutex_lock(emu->pending_mutex);
|
||||
|
||||
emu->pending_ctx = ctx;
|
||||
cond_signal(emu->pending_cond);
|
||||
|
||||
mutex_unlock(emu->pending_mutex);
|
||||
}
|
||||
|
||||
static struct frame *emu_pop_frame(struct emu *emu) {
|
||||
mutex_lock(emu->frames_mutex);
|
||||
|
||||
|
@ -178,6 +220,66 @@ static void emu_create_frames(struct emu *emu, struct render_backend *r) {
|
|||
}
|
||||
}
|
||||
|
||||
static void *emu_video_thread(void *data) {
|
||||
struct emu *emu = data;
|
||||
|
||||
/* create additional renderer on this thread for rendering the tile contexts
|
||||
to offscreen framebuffers */
|
||||
struct render_backend *r = r_create_from(emu->r);
|
||||
struct tr *tr = tr_create(r, ta_texture_provider(emu->dc->ta));
|
||||
|
||||
struct tile_ctx *pending_ctx;
|
||||
struct tile_render_context *rc = malloc(sizeof(struct tile_render_context));
|
||||
|
||||
emu_create_frames(emu, r);
|
||||
|
||||
while (emu->running) {
|
||||
/* wait for the next tile context provided by emu_start_render */
|
||||
mutex_lock(emu->pending_mutex);
|
||||
|
||||
if (!emu->pending_ctx) {
|
||||
cond_wait(emu->pending_cond, emu->pending_mutex);
|
||||
|
||||
/* exit thread if shutting down */
|
||||
if (!emu->pending_ctx) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the context, uploading its textures to the render backend */
|
||||
tr_parse_context(tr, emu->pending_ctx, rc);
|
||||
|
||||
/* after the context has been parsed, release the mutex to let
|
||||
emu_finish_render complete */
|
||||
mutex_unlock(emu->pending_mutex);
|
||||
|
||||
/* render the context to the first free framebuffer */
|
||||
struct frame *frame = emu_alloc_frame(emu, r);
|
||||
r_bind_framebuffer(r, frame->fb);
|
||||
r_clear_viewport(r);
|
||||
tr_render_context(tr, rc);
|
||||
|
||||
/* insert fence for main thread to synchronize on in order to ensure that
|
||||
the context has completely rendered */
|
||||
frame->fb_sync = r_insert_sync(r);
|
||||
|
||||
/* push frame to the presentation queue for the main thread */
|
||||
emu_push_front_frame(emu, frame);
|
||||
|
||||
/* update frame-based profiler stats */
|
||||
prof_flip();
|
||||
}
|
||||
|
||||
emu_destroy_frames(emu, r);
|
||||
|
||||
free(rc);
|
||||
|
||||
tr_destroy(tr);
|
||||
r_destroy(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void emu_keydown(void *data, int device_index, enum keycode code,
|
||||
int16_t value) {
|
||||
struct emu *emu = data;
|
||||
|
@ -331,58 +433,6 @@ static void emu_paint(struct emu *emu) {
|
|||
}
|
||||
}
|
||||
|
||||
static void *emu_video_thread(void *data) {
|
||||
struct emu *emu = data;
|
||||
|
||||
/* create additional renderer on this thread for rendering the tile contexts
|
||||
to offscreen framebuffers */
|
||||
struct render_backend *r = r_create_from(emu->r);
|
||||
struct tr *tr = tr_create(r, ta_texture_provider(emu->dc->ta));
|
||||
|
||||
struct tile_ctx *pending_ctx;
|
||||
struct tile_render_context *rc = malloc(sizeof(struct tile_render_context));
|
||||
|
||||
emu_create_frames(emu, r);
|
||||
|
||||
while (emu->running) {
|
||||
/* wait for the main thread to publish the next ta context to be rendered */
|
||||
if (!ta_lock_pending_context(emu->dc->ta, &pending_ctx, 1000)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* parse the context, uploading textures it uses to the render backend */
|
||||
tr_parse_context(tr, pending_ctx, rc);
|
||||
|
||||
/* after uploading the textures, unlock to let the main thread resume */
|
||||
ta_unlock_pending_context(emu->dc->ta);
|
||||
|
||||
/* render the context to the first free framebuffer */
|
||||
struct frame *frame = emu_alloc_frame(emu, r);
|
||||
r_bind_framebuffer(r, frame->fb);
|
||||
r_clear_viewport(r);
|
||||
tr_render_context(tr, rc);
|
||||
|
||||
/* insert fence for main thread to synchronize on in order to ensure that
|
||||
the context has completely rendered */
|
||||
frame->fb_sync = r_insert_sync(r);
|
||||
|
||||
/* push frame to the presentation queue for the main thread */
|
||||
emu_push_front_frame(emu, frame);
|
||||
|
||||
/* update frame-based profiler stats */
|
||||
prof_flip();
|
||||
}
|
||||
|
||||
emu_destroy_frames(emu, r);
|
||||
|
||||
free(rc);
|
||||
|
||||
tr_destroy(tr);
|
||||
r_destroy(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void emu_run(struct emu *emu, const char *path) {
|
||||
/* load gdi / bin if specified */
|
||||
if (path) {
|
||||
|
@ -460,6 +510,7 @@ void emu_run(struct emu *emu, const char *path) {
|
|||
|
||||
/* wait for video thread to exit */
|
||||
void *result;
|
||||
emu_cancel_render(emu);
|
||||
thread_join(video_thread, &result);
|
||||
}
|
||||
|
||||
|
@ -474,6 +525,8 @@ void emu_destroy(struct emu *emu) {
|
|||
/* destroy render backend */
|
||||
{
|
||||
mutex_destroy(emu->frames_mutex);
|
||||
cond_destroy(emu->pending_cond);
|
||||
mutex_destroy(emu->pending_mutex);
|
||||
nk_destroy(emu->nk);
|
||||
mp_destroy(emu->mp);
|
||||
r_destroy(emu->r);
|
||||
|
@ -498,13 +551,19 @@ struct emu *emu_create(struct window *win) {
|
|||
win_add_listener(emu->win, &emu->listener);
|
||||
|
||||
/* create dreamcast */
|
||||
emu->dc = dc_create();
|
||||
struct dreamcast_client client;
|
||||
client.userdata = emu;
|
||||
client.start_render = &emu_start_render;
|
||||
client.finish_render = &emu_finish_render;
|
||||
emu->dc = dc_create(&client);
|
||||
|
||||
/* create render backend */
|
||||
{
|
||||
emu->r = r_create(emu->win);
|
||||
emu->mp = mp_create(emu->win, emu->r);
|
||||
emu->nk = nk_create(emu->win, emu->r);
|
||||
emu->pending_mutex = mutex_create();
|
||||
emu->pending_cond = cond_create();
|
||||
emu->frames_mutex = mutex_create();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
|
||||
DEFINE_OPTION_INT(gdb, 0, "Run gdb debug server");
|
||||
|
||||
void dc_finish_render(struct dreamcast *dc) {
|
||||
if (dc->client.finish_render) {
|
||||
dc->client.finish_render(dc->client.userdata);
|
||||
}
|
||||
}
|
||||
|
||||
void dc_start_render(struct dreamcast *dc, struct tile_ctx *ctx) {
|
||||
if (dc->client.start_render) {
|
||||
dc->client.start_render(dc->client.userdata, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void dc_joy_remove(struct dreamcast *dc, int joystick_index) {
|
||||
list_for_each_entry(dev, &dc->devices, struct device, it) {
|
||||
if (dev->window_if && dev->window_if->joy_remove) {
|
||||
|
@ -192,9 +204,13 @@ void dc_destroy(struct dreamcast *dc) {
|
|||
free(dc);
|
||||
}
|
||||
|
||||
struct dreamcast *dc_create() {
|
||||
struct dreamcast *dc_create(const struct dreamcast_client *client) {
|
||||
struct dreamcast *dc = calloc(1, sizeof(struct dreamcast));
|
||||
|
||||
if (client) {
|
||||
dc->client = *client;
|
||||
}
|
||||
|
||||
dc->debugger = OPTION_gdb ? debugger_create(dc) : NULL;
|
||||
dc->memory = memory_create(dc);
|
||||
dc->scheduler = scheduler_create(dc);
|
||||
|
|
|
@ -23,6 +23,7 @@ struct pvr;
|
|||
struct scheduler;
|
||||
struct sh4;
|
||||
struct ta;
|
||||
struct tile_ctx;
|
||||
|
||||
/*
|
||||
* register callbacks
|
||||
|
@ -132,7 +133,17 @@ struct device {
|
|||
/*
|
||||
* machine
|
||||
*/
|
||||
typedef void (*start_render_cb)(void *, struct tile_ctx *);
|
||||
typedef void (*finish_render_cb)(void *);
|
||||
|
||||
struct dreamcast_client {
|
||||
void *userdata;
|
||||
start_render_cb start_render;
|
||||
finish_render_cb finish_render;
|
||||
};
|
||||
|
||||
struct dreamcast {
|
||||
struct dreamcast_client client;
|
||||
struct debugger *debugger;
|
||||
struct memory *memory;
|
||||
struct scheduler *scheduler;
|
||||
|
@ -150,7 +161,7 @@ struct dreamcast {
|
|||
struct list devices;
|
||||
};
|
||||
|
||||
struct dreamcast *dc_create();
|
||||
struct dreamcast *dc_create(const struct dreamcast_client *client);
|
||||
void dc_destroy(struct dreamcast *dc);
|
||||
|
||||
void *dc_create_device(struct dreamcast *dc, size_t size, const char *name,
|
||||
|
@ -180,5 +191,7 @@ void dc_keydown(struct dreamcast *dc, int device_index, enum keycode code,
|
|||
int16_t value);
|
||||
void dc_joy_add(struct dreamcast *dc, int joystick_index);
|
||||
void dc_joy_remove(struct dreamcast *dc, int joystick_index);
|
||||
void dc_start_render(struct dreamcast *dc, struct tile_ctx *ctx);
|
||||
void dc_finish_render(struct dreamcast *dc);
|
||||
|
||||
#endif
|
||||
|
|
211
src/hw/pvr/ta.c
211
src/hw/pvr/ta.c
|
@ -10,7 +10,6 @@
|
|||
#include "hw/sh4/sh4.h"
|
||||
#include "sys/exception_handler.h"
|
||||
#include "sys/filesystem.h"
|
||||
#include "sys/thread.h"
|
||||
#include "ui/nuklear.h"
|
||||
|
||||
DEFINE_AGGREGATE_COUNTER(ta_data);
|
||||
|
@ -38,6 +37,7 @@ struct ta {
|
|||
struct device;
|
||||
struct texture_provider provider;
|
||||
uint8_t *video_ram;
|
||||
struct trace_writer *trace_writer;
|
||||
|
||||
/* yuv data converter state */
|
||||
uint8_t *yuv_data;
|
||||
|
@ -52,24 +52,12 @@ struct ta {
|
|||
struct list live_contexts;
|
||||
struct tile_ctx *curr_context;
|
||||
|
||||
/* texture cache entry pool */
|
||||
struct ta_texture_entry entries[8192];
|
||||
struct list free_entries;
|
||||
struct rb_tree live_entries;
|
||||
/* texture cache state */
|
||||
unsigned frame;
|
||||
int num_textures;
|
||||
|
||||
/* each time the STARTRENDER register is written to, the current TA param
|
||||
buffer and PVR register state required to render the params is saved to
|
||||
this "pending context". the pending context is not actually rendered on
|
||||
the emulation thread, instead it's made available to the graphics thread
|
||||
through ta_lock_pending_context and ta_unlock_pending_context functions.
|
||||
please read through the comments there, as well as the comments in
|
||||
ta_start_render, ta_render_timer and ta_end_render to understand how
|
||||
access to this context as well as the texture data it depends on is
|
||||
synchronized */
|
||||
struct tile_ctx *pending_context;
|
||||
|
||||
/* textures for the pending context are uploaded to the render backend by
|
||||
the graphics thread in parallel to the main emulation thread executing,
|
||||
/* textures for the current context are uploaded to the render backend by
|
||||
the video thread in parallel to the main emulation thread executing,
|
||||
which may erroneously write to a texture before receiving the end of
|
||||
render interrupts. in order to avoid race conditions around the texture's
|
||||
dirty state in these situations, textures are not immediately marked dirty
|
||||
|
@ -78,16 +66,9 @@ struct ta {
|
|||
struct list invalidated_entries;
|
||||
int num_invalidated;
|
||||
|
||||
/* primitives used for synchronizing access to the pending context and any
|
||||
invalidated textures between the graphics and emulation threads */
|
||||
mutex_t pending_mutex;
|
||||
cond_t pending_cond;
|
||||
|
||||
/* debug info */
|
||||
unsigned frame;
|
||||
int frames_skipped;
|
||||
int num_textures;
|
||||
struct trace_writer *trace_writer;
|
||||
struct ta_texture_entry entries[8192];
|
||||
struct list free_entries;
|
||||
struct rb_tree live_entries;
|
||||
};
|
||||
|
||||
int g_param_sizes[0x100 * TA_NUM_PARAMS * TA_NUM_VERTS];
|
||||
|
@ -328,24 +309,6 @@ static struct ta_texture_entry *ta_find_texture(struct ta *ta, union tsp tsp,
|
|||
live_it, &ta_entry_cb);
|
||||
}
|
||||
|
||||
static struct texture_entry *ta_texture_provider_find_texture(void *data,
|
||||
union tsp tsp,
|
||||
union tcw tcw) {
|
||||
struct ta *ta = (struct ta *)data;
|
||||
struct ta_texture_entry *entry = ta_find_texture(ta, tsp, tcw);
|
||||
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sanity check that the texture source is valid for the current frame. video
|
||||
ram will be modified between frames, if these values don't match something
|
||||
is broken in the thread synchronization */
|
||||
CHECK_EQ(entry->frame, ta->frame);
|
||||
|
||||
return (struct texture_entry *)entry;
|
||||
}
|
||||
|
||||
static struct tile_ctx *ta_get_context(struct ta *ta, uint32_t addr) {
|
||||
list_for_each_entry(ctx, &ta->live_contexts, struct tile_ctx, it) {
|
||||
if (ctx->addr == addr) {
|
||||
|
@ -542,14 +505,11 @@ static void ta_register_texture_source(struct ta *ta, union tsp tsp,
|
|||
}
|
||||
}
|
||||
|
||||
static void ta_register_texture_sources(struct ta *ta, struct tile_ctx *ctx,
|
||||
int *num_polys) {
|
||||
static void ta_register_texture_sources(struct ta *ta, struct tile_ctx *ctx) {
|
||||
const uint8_t *data = ctx->params;
|
||||
const uint8_t *end = ctx->params + ctx->size;
|
||||
int vertex_type = 0;
|
||||
|
||||
*num_polys = 0;
|
||||
|
||||
while (data < end) {
|
||||
union pcw pcw = *(union pcw *)data;
|
||||
|
||||
|
@ -563,8 +523,6 @@ static void ta_register_texture_sources(struct ta *ta, struct tile_ctx *ctx,
|
|||
if (param->type0.pcw.texture) {
|
||||
ta_register_texture_source(ta, param->type0.tsp, param->type0.tcw);
|
||||
}
|
||||
|
||||
(*num_polys)++;
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -671,83 +629,63 @@ static void ta_save_state(struct ta *ta, struct tile_ctx *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
static void ta_end_render(struct ta *ta) {
|
||||
static void ta_finish_render(void *data) {
|
||||
struct tile_ctx *ctx = data;
|
||||
struct ta *ta = ctx->userdata;
|
||||
|
||||
/* ensure the client has finished rendering */
|
||||
dc_finish_render(ta->dc);
|
||||
|
||||
/* texture entries are only valid between each start / finish render pair,
|
||||
increment frame number again to invalidate */
|
||||
ta->frame++;
|
||||
|
||||
/* return context back to pool */
|
||||
ta_free_context(ta, ctx);
|
||||
|
||||
/* let the game know rendering is complete */
|
||||
holly_raise_interrupt(ta->holly, HOLLY_INTC_PCEOVINT);
|
||||
holly_raise_interrupt(ta->holly, HOLLY_INTC_PCEOIINT);
|
||||
holly_raise_interrupt(ta->holly, HOLLY_INTC_PCEOTINT);
|
||||
}
|
||||
|
||||
static void ta_render_timer(void *data) {
|
||||
struct ta *ta = data;
|
||||
|
||||
/* ideally, the graphics thread has parsed the pending context, uploaded its
|
||||
textures, etc. during the estimated render time. however, if it hasn't
|
||||
finished, the emulation thread must be paused to avoid altering
|
||||
the yet-to-be-uploaded texture memory */
|
||||
mutex_lock(ta->pending_mutex);
|
||||
|
||||
/* if the graphics thread didn't actually attempt to parse the frame, which
|
||||
often happens when running unthrottled, skip it */
|
||||
if (ta->pending_context) {
|
||||
ta_free_context(ta, ta->pending_context);
|
||||
ta->pending_context = NULL;
|
||||
ta->frames_skipped++;
|
||||
}
|
||||
|
||||
/* texture entries are only valid between each start / end render, increment
|
||||
frame number again to invalidate */
|
||||
ta->frame++;
|
||||
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
|
||||
ta_end_render(ta);
|
||||
}
|
||||
|
||||
static void ta_start_render(struct ta *ta, struct tile_ctx *ctx) {
|
||||
prof_counter_add(COUNTER_ta_renders, 1);
|
||||
|
||||
mutex_lock(ta->pending_mutex);
|
||||
|
||||
/* now that access to texture data is locked, mark any textures dirty
|
||||
that were invalidated by a memory watch on the emulation thread */
|
||||
ta_dirty_invalidated_textures(ta);
|
||||
|
||||
/* remove context from pool */
|
||||
ta_unlink_context(ta, ctx);
|
||||
|
||||
/* increment internal frame number. this frame number is assigned to the
|
||||
/* incement internal frame number. this frame number is assigned to the
|
||||
context and each texture source it registers to assert synchronization
|
||||
between the emulator and graphics thread is working as expected */
|
||||
between the emulator and video thread is working as expected */
|
||||
ta->frame++;
|
||||
|
||||
/* now that the video thread is sure to not be accessing the texture data,
|
||||
mark any textures dirty that were invalidated by a memory watch */
|
||||
ta_dirty_invalidated_textures(ta);
|
||||
|
||||
/* register the source of each texture referenced by the context with the
|
||||
tile renderer. note, uploading the texture to the render backend happens
|
||||
lazily while rendering the context. this registration just lets the
|
||||
backend know where the texture's source data is */
|
||||
ta_register_texture_sources(ta, ctx);
|
||||
|
||||
/* save off required state that may be modified by the time the context is
|
||||
rendered */
|
||||
ta_save_state(ta, ctx);
|
||||
|
||||
/* register the source of each texture referenced by the context with the
|
||||
tile renderer. note, the process of actually uploading the texture to the
|
||||
render backend happens lazily while rendering the context (keeping all
|
||||
backend operations on the same thread). this registration just lets the
|
||||
backend know where the texture's source data is */
|
||||
int num_polys = 0;
|
||||
ta_register_texture_sources(ta, ctx, &num_polys);
|
||||
/* let the client know to start rendering the context */
|
||||
dc_start_render(ta->dc, ctx);
|
||||
|
||||
/* give each frame 10 ms to finish rendering
|
||||
TODO figure out a heuristic involving the number of polygons rendered */
|
||||
int64_t ns = INT64_C(10000000);
|
||||
scheduler_start_timer(ta->scheduler, &ta_render_timer, ta, ns);
|
||||
int64_t end = INT64_C(10000000);
|
||||
ctx->userdata = ta;
|
||||
scheduler_start_timer(ta->scheduler, &ta_finish_render, ctx, end);
|
||||
|
||||
if (ta->trace_writer) {
|
||||
trace_writer_render_context(ta->trace_writer, ctx);
|
||||
}
|
||||
|
||||
/* signal to the graphics thread that a new frame is available to be parsed */
|
||||
CHECK(ta->pending_context == NULL);
|
||||
ta->pending_context = ctx;
|
||||
|
||||
cond_signal(ta->pending_cond);
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
}
|
||||
|
||||
static void ta_yuv_init(struct ta *ta) {
|
||||
|
@ -936,21 +874,6 @@ static void ta_toggle_tracing(struct ta *ta) {
|
|||
}
|
||||
}
|
||||
|
||||
static void ta_debug_menu_toggle_tracing(struct ta *ta) {
|
||||
/* this is called from the graphics thread. need to take the context lock as
|
||||
the emulation thread may be modifying the pending context, registering
|
||||
textures for it, etc. */
|
||||
mutex_lock(ta->pending_mutex);
|
||||
ta_toggle_tracing(ta);
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
}
|
||||
|
||||
static void ta_debug_menu_clear_textures(struct ta *ta) {
|
||||
mutex_lock(ta->pending_mutex);
|
||||
ta_clear_textures(ta);
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
}
|
||||
|
||||
static void ta_debug_menu(struct device *dev, struct nk_context *ctx) {
|
||||
struct ta *ta = (struct ta *)dev;
|
||||
|
||||
|
@ -959,17 +882,16 @@ static void ta_debug_menu(struct device *dev, struct nk_context *ctx) {
|
|||
if (nk_menu_begin_label(ctx, "TA", NK_TEXT_LEFT, nk_vec2(140.0f, 200.0f))) {
|
||||
nk_layout_row_dynamic(ctx, DEBUG_MENU_HEIGHT, 1);
|
||||
|
||||
nk_value_int(ctx, "frames skipped", ta->frames_skipped);
|
||||
nk_value_int(ctx, "num textures", ta->num_textures);
|
||||
|
||||
if (!ta->trace_writer && nk_button_label(ctx, "start trace")) {
|
||||
ta_debug_menu_toggle_tracing(ta);
|
||||
ta_toggle_tracing(ta);
|
||||
} else if (ta->trace_writer && nk_button_label(ctx, "stop trace")) {
|
||||
ta_debug_menu_toggle_tracing(ta);
|
||||
ta_toggle_tracing(ta);
|
||||
}
|
||||
|
||||
if (nk_button_label(ctx, "clear texture cache")) {
|
||||
ta_debug_menu_clear_textures(ta);
|
||||
ta_clear_textures(ta);
|
||||
}
|
||||
|
||||
nk_menu_end(ctx);
|
||||
|
@ -1016,44 +938,33 @@ void ta_build_tables() {
|
|||
}
|
||||
}
|
||||
|
||||
void ta_unlock_pending_context(struct ta *ta) {
|
||||
/* after the pending mutex is released, this context is no longer valid
|
||||
as the emulation thread will resume */
|
||||
ta_free_context(ta, ta->pending_context);
|
||||
ta->pending_context = NULL;
|
||||
static struct texture_entry *ta_texture_provider_find_texture(void *data,
|
||||
union tsp tsp,
|
||||
union tcw tcw) {
|
||||
struct ta *ta = (struct ta *)data;
|
||||
struct ta_texture_entry *entry = ta_find_texture(ta, tsp, tcw);
|
||||
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
}
|
||||
|
||||
int ta_lock_pending_context(struct ta *ta, struct tile_ctx **pending_ctx,
|
||||
int wait_ms) {
|
||||
mutex_lock(ta->pending_mutex);
|
||||
|
||||
/* wait for the next available context */
|
||||
if (!ta->pending_context) {
|
||||
int res = cond_timedwait(ta->pending_cond, ta->pending_mutex, wait_ms);
|
||||
|
||||
if (!res || !ta->pending_context) {
|
||||
mutex_unlock(ta->pending_mutex);
|
||||
return 0;
|
||||
}
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sanity check the context is from the current frame */
|
||||
CHECK_EQ(ta->pending_context->frame, ta->frame);
|
||||
/* sanity check that the texture source is valid for the current frame. video
|
||||
ram will be modified between frames, if these values don't match something
|
||||
is broken in the thread synchronization */
|
||||
CHECK_EQ(entry->frame, ta->frame);
|
||||
|
||||
*pending_ctx = ta->pending_context;
|
||||
|
||||
return 1;
|
||||
return (struct texture_entry *)entry;
|
||||
}
|
||||
|
||||
struct texture_provider *ta_texture_provider(struct ta *ta) {
|
||||
if (!ta->provider.userdata) {
|
||||
ta->provider.userdata = ta;
|
||||
ta->provider.find_texture = &ta_texture_provider_find_texture;
|
||||
}
|
||||
return &ta->provider;
|
||||
}
|
||||
|
||||
void ta_destroy(struct ta *ta) {
|
||||
cond_destroy(ta->pending_cond);
|
||||
mutex_destroy(ta->pending_mutex);
|
||||
dc_destroy_window_interface(ta->window_if);
|
||||
dc_destroy_device((struct device *)ta);
|
||||
}
|
||||
|
@ -1065,8 +976,6 @@ struct ta *ta_create(struct dreamcast *dc) {
|
|||
ta->window_if = dc_create_window_interface(&ta_debug_menu, NULL, NULL, NULL);
|
||||
ta->provider =
|
||||
(struct texture_provider){ta, &ta_texture_provider_find_texture};
|
||||
ta->pending_mutex = mutex_create();
|
||||
ta->pending_cond = cond_create();
|
||||
|
||||
return ta;
|
||||
}
|
||||
|
|
|
@ -6,14 +6,24 @@
|
|||
#include "hw/pvr/ta_types.h"
|
||||
|
||||
struct dreamcast;
|
||||
struct ta;
|
||||
struct texture_provider;
|
||||
|
||||
DECLARE_COUNTER(ta_renders);
|
||||
|
||||
AM_DECLARE(ta_fifo_map);
|
||||
|
||||
#define TA_CODEBOOK_SIZE (256 * 8)
|
||||
|
||||
extern int g_param_sizes[0x100 * TA_NUM_PARAMS * TA_NUM_VERTS];
|
||||
extern int g_poly_types[0x100 * TA_NUM_PARAMS * TA_NUM_LISTS];
|
||||
extern int g_vertex_types[0x100 * TA_NUM_PARAMS * TA_NUM_LISTS];
|
||||
|
||||
void ta_build_tables();
|
||||
|
||||
struct ta *ta_create(struct dreamcast *dc);
|
||||
void ta_destroy(struct ta *ta);
|
||||
|
||||
static inline int ta_get_param_size(union pcw pcw, int vertex_type) {
|
||||
return g_param_sizes[pcw.obj_control * TA_NUM_PARAMS * TA_NUM_VERTS +
|
||||
pcw.para_type * TA_NUM_VERTS + vertex_type];
|
||||
|
@ -94,20 +104,6 @@ static inline int ta_texture_size(union tsp tsp, union tcw tcw) {
|
|||
return texture_size;
|
||||
}
|
||||
|
||||
struct ta;
|
||||
|
||||
void ta_build_tables();
|
||||
|
||||
DECLARE_COUNTER(ta_renders);
|
||||
|
||||
AM_DECLARE(ta_fifo_map);
|
||||
|
||||
struct ta *ta_create(struct dreamcast *dc);
|
||||
void ta_destroy(struct ta *ta);
|
||||
|
||||
struct texture_provider *ta_texture_provider(struct ta *ta);
|
||||
int ta_lock_pending_context(struct ta *ta, struct tile_ctx **pending_ctx,
|
||||
int wait_ms);
|
||||
void ta_unlock_pending_context(struct ta *ta);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -439,6 +439,7 @@ union vert_param {
|
|||
|
||||
struct tile_ctx {
|
||||
uint32_t addr;
|
||||
void *userdata;
|
||||
|
||||
/* pvr / ta state */
|
||||
unsigned frame;
|
||||
|
|
|
@ -128,7 +128,7 @@ static texture_handle_t tr_demand_texture(struct tr *tr,
|
|||
texture generation */
|
||||
|
||||
struct texture_entry *entry =
|
||||
tr->provider->find_texture(tr->provider->data, tsp, tcw);
|
||||
tr->provider->find_texture(tr->provider->userdata, tsp, tcw);
|
||||
CHECK_NOTNULL(entry);
|
||||
|
||||
/* if there's a non-dirty handle, return it */
|
||||
|
|
|
@ -38,7 +38,7 @@ struct texture_entry {
|
|||
emulating the actual ta, textures will be provided from guest memory, but
|
||||
when playing back traces the textures will come from the trace itself */
|
||||
struct texture_provider {
|
||||
void *data;
|
||||
void *userdata;
|
||||
struct texture_entry *(*find_texture)(void *, union tsp, union tcw);
|
||||
};
|
||||
|
||||
|
|
|
@ -724,7 +724,8 @@ sync_handle_t r_insert_sync(struct render_backend *r) {
|
|||
}
|
||||
|
||||
void r_destroy_texture(struct render_backend *r, texture_handle_t handle) {
|
||||
/* lookup texture entry */
|
||||
/* lookup texture entry
|
||||
FIXME need common hashtable */
|
||||
int entry;
|
||||
for (entry = 0; entry < MAX_TEXTURES; entry++) {
|
||||
struct texture *tex = &r->textures[entry];
|
||||
|
|
|
@ -127,7 +127,7 @@ static void run_sh4_test(struct dreamcast *dc, const struct sh4_test *test) {
|
|||
}
|
||||
|
||||
TEST(sh4_x64) {
|
||||
struct dreamcast *dc = dc_create();
|
||||
struct dreamcast *dc = dc_create(NULL);
|
||||
CHECK_NOTNULL(dc);
|
||||
|
||||
/* clang-format off */
|
||||
|
|
Loading…
Reference in New Issue