mirror of https://github.com/inolen/redream.git
added comments explaining the purpose of emulator.c
This commit is contained in:
parent
a4704f4c50
commit
69ed32c442
100
src/emulator.c
100
src/emulator.c
|
@ -1,3 +1,16 @@
|
|||
/*
|
||||
* client code for the dreamcast machine
|
||||
*
|
||||
* acts as a middle man between the dreamcast guest and local host. the host
|
||||
* interface provides callbacks for user input events, window resize events,
|
||||
* etc. that need to be passed to the dreamcast, while the dreamcast interface
|
||||
* provides callbacks that push frames of video and audio data to be presented
|
||||
* on the host
|
||||
*
|
||||
* this code encapsulates the logic that would otherwise need to be duplicated
|
||||
* for each of the multiple host implementations (sdl, libretro, etc.)
|
||||
*/
|
||||
|
||||
#include "emulator.h"
|
||||
#include "core/option.h"
|
||||
#include "core/profiler.h"
|
||||
|
@ -28,31 +41,36 @@ enum {
|
|||
};
|
||||
|
||||
struct emu {
|
||||
int multi_threaded;
|
||||
struct host *host;
|
||||
struct dreamcast *dc;
|
||||
volatile int running;
|
||||
int debug_menu;
|
||||
volatile int running;
|
||||
volatile int video_width;
|
||||
volatile int video_height;
|
||||
volatile int video_resized;
|
||||
|
||||
struct render_backend *r, *r2;
|
||||
struct microprofile *mp;
|
||||
struct nuklear *nk;
|
||||
struct tr *tr;
|
||||
|
||||
/* video state */
|
||||
/* hosts which support creating multiple gl contexts will render video on
|
||||
a second thread. the original hardware rendered asynchronously as well,
|
||||
so many games use this time to perform additional cpu work. on many
|
||||
games this upwards of doubles the performance */
|
||||
int multi_threaded;
|
||||
thread_t video_thread;
|
||||
|
||||
/* latest context from the dreamcast, ready to be converted and presented */
|
||||
mutex_t pending_mutex;
|
||||
cond_t pending_cond;
|
||||
struct tile_context *pending_ctx;
|
||||
|
||||
/* latest context converted into render commands to be presented locally */
|
||||
volatile int video_state;
|
||||
volatile int video_width;
|
||||
volatile int video_height;
|
||||
|
||||
struct tr_context video_rc;
|
||||
int video_fb_width;
|
||||
int video_fb_height;
|
||||
|
||||
/* offscreen framebuffer the video output is rendered to */
|
||||
framebuffer_handle_t video_fb;
|
||||
texture_handle_t video_tex;
|
||||
sync_handle_t video_sync;
|
||||
|
@ -72,20 +90,18 @@ static void emu_render_frame(struct emu *emu) {
|
|||
emu->video_state = FRAME_RENDERING;
|
||||
|
||||
/* resize the framebuffer at this time if the output size has changed */
|
||||
if (emu->video_fb_width != emu->video_width ||
|
||||
emu->video_fb_height != emu->video_height) {
|
||||
if (emu->video_resized) {
|
||||
r_destroy_framebuffer(r2, emu->video_fb);
|
||||
|
||||
emu->video_fb_width = emu->video_width;
|
||||
emu->video_fb_height = emu->video_height;
|
||||
emu->video_fb = r_create_framebuffer(r2, emu->video_fb_width,
|
||||
emu->video_fb_height, &emu->video_tex);
|
||||
emu->video_fb = r_create_framebuffer(r2, emu->video_width,
|
||||
emu->video_height, &emu->video_tex);
|
||||
emu->video_resized = 0;
|
||||
}
|
||||
|
||||
/* render the current render context to the video framebuffer */
|
||||
framebuffer_handle_t original = r_get_framebuffer(r2);
|
||||
r_bind_framebuffer(r2, emu->video_fb);
|
||||
r_viewport(emu->r, emu->video_fb_width, emu->video_fb_height);
|
||||
r_viewport(emu->r, emu->video_width, emu->video_height);
|
||||
tr_render_context(emu->tr, &emu->video_rc);
|
||||
r_bind_framebuffer(r2, original);
|
||||
|
||||
|
@ -237,13 +253,16 @@ static void emu_paint(struct emu *emu) {
|
|||
nk_render(emu->nk);
|
||||
}
|
||||
|
||||
static void emu_poll_input(void *userdata) {
|
||||
/*
|
||||
* dreamcast guest interface
|
||||
*/
|
||||
static void emu_guest_poll_input(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
input_poll(emu->host);
|
||||
}
|
||||
|
||||
static void emu_finish_render(void *userdata) {
|
||||
static void emu_guest_finish_render(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
if (emu->multi_threaded) {
|
||||
|
@ -259,7 +278,7 @@ static void emu_finish_render(void *userdata) {
|
|||
}
|
||||
}
|
||||
|
||||
static void emu_start_render(void *userdata, struct tile_context *ctx) {
|
||||
static void emu_guest_start_render(void *userdata, struct tile_context *ctx) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
if (emu->video_state != FRAME_WAITING) {
|
||||
|
@ -283,20 +302,24 @@ static void emu_start_render(void *userdata, struct tile_context *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
static void emu_push_audio(void *userdata, const int16_t *data, int frames) {
|
||||
static void emu_guest_push_audio(void *userdata, const int16_t *data,
|
||||
int frames) {
|
||||
struct emu *emu = userdata;
|
||||
audio_push(emu->host, data, frames);
|
||||
}
|
||||
|
||||
static void emu_input_mousemove(void *userdata, int port, int x, int y) {
|
||||
/*
|
||||
* local host interface
|
||||
*/
|
||||
static void emu_host_mousemove(void *userdata, int port, int x, int y) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
mp_mousemove(emu->mp, x, y);
|
||||
nk_mousemove(emu->nk, x, y);
|
||||
}
|
||||
|
||||
static void emu_input_keydown(void *userdata, int port, enum keycode key,
|
||||
int16_t value) {
|
||||
static void emu_host_keydown(void *userdata, int port, enum keycode key,
|
||||
int16_t value) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
if (key == K_F1 && value > 0) {
|
||||
|
@ -311,7 +334,7 @@ static void emu_input_keydown(void *userdata, int port, enum keycode key,
|
|||
}
|
||||
}
|
||||
|
||||
static void emu_video_context_destroyed(void *userdata) {
|
||||
static void emu_host_context_destroyed(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
struct tr_provider *provider = ta_texture_provider(emu->dc->ta);
|
||||
|
||||
|
@ -361,11 +384,11 @@ static void emu_video_context_destroyed(void *userdata) {
|
|||
r_destroy(emu->r);
|
||||
}
|
||||
|
||||
static void emu_video_context_reset(void *userdata) {
|
||||
static void emu_host_context_reset(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
struct tr_provider *provider = ta_texture_provider(emu->dc->ta);
|
||||
|
||||
emu_video_context_destroyed(userdata);
|
||||
emu_host_context_destroyed(userdata);
|
||||
|
||||
emu->running = 1;
|
||||
|
||||
|
@ -387,10 +410,8 @@ static void emu_video_context_reset(void *userdata) {
|
|||
|
||||
emu->tr = tr_create(r2, provider);
|
||||
|
||||
emu->video_fb_width = emu->video_width;
|
||||
emu->video_fb_height = emu->video_height;
|
||||
emu->video_fb = r_create_framebuffer(r2, emu->video_fb_width,
|
||||
emu->video_fb_height, &emu->video_tex);
|
||||
emu->video_fb = r_create_framebuffer(r2, emu->video_width, emu->video_height,
|
||||
&emu->video_tex);
|
||||
|
||||
/* startup video thread */
|
||||
if (emu->multi_threaded) {
|
||||
|
@ -402,11 +423,12 @@ static void emu_video_context_reset(void *userdata) {
|
|||
r_make_current(emu->r);
|
||||
}
|
||||
|
||||
static void emu_video_resized(void *userdata) {
|
||||
static void emu_host_resized(void *userdata) {
|
||||
struct emu *emu = userdata;
|
||||
|
||||
emu->video_width = video_width(emu->host);
|
||||
emu->video_height = video_height(emu->host);
|
||||
emu->video_resized = 1;
|
||||
}
|
||||
|
||||
void emu_run_frame(struct emu *emu) {
|
||||
|
@ -449,19 +471,19 @@ struct emu *emu_create(struct host *host) {
|
|||
/* setup host, bind event callbacks */
|
||||
emu->host = host;
|
||||
emu->host->userdata = emu;
|
||||
emu->host->video_resized = &emu_video_resized;
|
||||
emu->host->video_context_reset = &emu_video_context_reset;
|
||||
emu->host->video_context_destroyed = &emu_video_context_destroyed;
|
||||
emu->host->input_keydown = &emu_input_keydown;
|
||||
emu->host->input_mousemove = &emu_input_mousemove;
|
||||
emu->host->video_resized = &emu_host_resized;
|
||||
emu->host->video_context_reset = &emu_host_context_reset;
|
||||
emu->host->video_context_destroyed = &emu_host_context_destroyed;
|
||||
emu->host->input_keydown = &emu_host_keydown;
|
||||
emu->host->input_mousemove = &emu_host_mousemove;
|
||||
|
||||
/* create dreamcast, bind client callbacks */
|
||||
emu->dc = dc_create();
|
||||
emu->dc->userdata = emu;
|
||||
emu->dc->push_audio = &emu_push_audio;
|
||||
emu->dc->start_render = &emu_start_render;
|
||||
emu->dc->finish_render = &emu_finish_render;
|
||||
emu->dc->poll_input = &emu_poll_input;
|
||||
emu->dc->push_audio = &emu_guest_push_audio;
|
||||
emu->dc->start_render = &emu_guest_start_render;
|
||||
emu->dc->finish_render = &emu_guest_finish_render;
|
||||
emu->dc->poll_input = &emu_guest_poll_input;
|
||||
|
||||
/* start up the video thread */
|
||||
emu->multi_threaded = video_gl_supports_multiple_contexts(emu->host);
|
||||
|
|
|
@ -18,10 +18,6 @@ struct tr {
|
|||
int vertex_type;
|
||||
float face_color[4];
|
||||
float face_offset_color[4];
|
||||
|
||||
/* scratch buffers used when sorting surfaces */
|
||||
int merged[TA_MAX_SURFS];
|
||||
float minz[TA_MAX_SURFS];
|
||||
};
|
||||
|
||||
static int compressed_mipmap_offsets[] = {
|
||||
|
@ -119,10 +115,10 @@ static inline uint32_t float_to_rgba(float r, float g, float b, float a) {
|
|||
(float_to_u8(g) << 8) | float_to_u8(r);
|
||||
}
|
||||
|
||||
static texture_handle_t tr_demand_texture(struct tr *tr,
|
||||
const struct tile_context *ctx,
|
||||
union tsp tsp, union tcw tcw) {
|
||||
PROF_ENTER("gpu", "tr_demand_texture");
|
||||
static texture_handle_t tr_convert_texture(struct tr *tr,
|
||||
const struct tile_context *ctx,
|
||||
union tsp tsp, union tcw tcw) {
|
||||
PROF_ENTER("gpu", "tr_convert_texture");
|
||||
|
||||
/* allow headless tile renderer */
|
||||
if (!tr->provider) {
|
||||
|
@ -607,7 +603,7 @@ static void tr_parse_poly_param(struct tr *tr, const struct tile_context *ctx,
|
|||
|
||||
if (param->type0.pcw.texture) {
|
||||
surf->texture =
|
||||
tr_demand_texture(tr, ctx, param->type0.tsp, param->type0.tcw);
|
||||
tr_convert_texture(tr, ctx, param->type0.tsp, param->type0.tcw);
|
||||
} else {
|
||||
surf->texture = 0;
|
||||
}
|
||||
|
@ -801,15 +797,19 @@ static void tr_parse_vert_param(struct tr *tr, const struct tile_context *ctx,
|
|||
/* FIXME is this true for sprites which come through this path as well? */
|
||||
}
|
||||
|
||||
/* scratch buffers used by surface merge sort */
|
||||
static int sort_tmp[TA_MAX_SURFS];
|
||||
static float sort_minz[TA_MAX_SURFS];
|
||||
|
||||
static void tr_merge_surfs(struct tr *tr, int *low, int *mid, int *high) {
|
||||
int *i = low;
|
||||
int *j = mid + 1;
|
||||
int *k = tr->merged;
|
||||
int *end = tr->merged + array_size(tr->merged);
|
||||
int *k = sort_tmp;
|
||||
int *end = sort_tmp + array_size(sort_tmp);
|
||||
|
||||
while (i <= mid && j <= high) {
|
||||
DCHECK_LT(k, end);
|
||||
if (tr->minz[*i] <= tr->minz[*j]) {
|
||||
if (sort_minz[*i] <= sort_minz[*j]) {
|
||||
*(k++) = *(i++);
|
||||
} else {
|
||||
*(k++) = *(j++);
|
||||
|
@ -826,7 +826,7 @@ static void tr_merge_surfs(struct tr *tr, int *low, int *mid, int *high) {
|
|||
*(k++) = *(j++);
|
||||
}
|
||||
|
||||
memcpy(low, tr->merged, (k - tr->merged) * sizeof(tr->merged[0]));
|
||||
memcpy(low, sort_tmp, (k - sort_tmp) * sizeof(sort_tmp[0]));
|
||||
}
|
||||
|
||||
static void tr_sort_surfs(struct tr *tr, struct tr_list *list, int low,
|
||||
|
@ -850,7 +850,7 @@ static void tr_sort_render_list(struct tr *tr, struct tr_context *rc,
|
|||
for (int i = 0; i < list->num_surfs; i++) {
|
||||
int idx = list->surfs[i];
|
||||
struct ta_surface *surf = &rc->surfs[idx];
|
||||
float *minz = &tr->minz[idx];
|
||||
float *minz = &sort_minz[idx];
|
||||
|
||||
/* the surf coordinates have 1/w for z, so smaller values are
|
||||
further away from the camera */
|
||||
|
@ -955,7 +955,7 @@ void tr_render_context(struct tr *tr, const struct tr_context *rc) {
|
|||
}
|
||||
|
||||
void tr_convert_context(struct tr *tr, const struct tile_context *ctx,
|
||||
struct tr_context *rc) {
|
||||
struct tr_context *rc) {
|
||||
PROF_ENTER("gpu", "tr_convert_context");
|
||||
|
||||
const uint8_t *data = ctx->params;
|
||||
|
@ -987,9 +987,6 @@ void tr_convert_context(struct tr *tr, const struct tile_context *ctx,
|
|||
|
||||
/* global params */
|
||||
case TA_PARAM_POLY_OR_VOL:
|
||||
tr_parse_poly_param(tr, ctx, rc, data);
|
||||
break;
|
||||
|
||||
case TA_PARAM_SPRITE:
|
||||
tr_parse_poly_param(tr, ctx, rc, data);
|
||||
break;
|
||||
|
|
|
@ -9,9 +9,19 @@
|
|||
#include "render/render_backend.h"
|
||||
|
||||
struct tr;
|
||||
struct tr_texture;
|
||||
|
||||
typedef uint64_t tr_texture_key_t;
|
||||
|
||||
/* provides abstraction around providing texture data to the renderer. when
|
||||
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 tr_provider {
|
||||
void *userdata;
|
||||
void (*clear_textures)(void *);
|
||||
struct tr_texture *(*find_texture)(void *, union tsp, union tcw);
|
||||
};
|
||||
|
||||
struct tr_texture {
|
||||
union tsp tsp;
|
||||
union tcw tcw;
|
||||
|
@ -34,15 +44,6 @@ struct tr_texture {
|
|||
texture_handle_t handle;
|
||||
};
|
||||
|
||||
/* provides abstraction around providing texture data to the renderer. when
|
||||
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 tr_provider {
|
||||
void *userdata;
|
||||
void (*clear_textures)(void *);
|
||||
struct tr_texture *(*find_texture)(void *, union tsp, union tcw);
|
||||
};
|
||||
|
||||
struct tr_param {
|
||||
/* offset of parameter in tile_context param stream */
|
||||
int offset;
|
||||
|
@ -87,7 +88,7 @@ struct tr *tr_create(struct render_backend *rb, struct tr_provider *provider);
|
|||
void tr_destroy(struct tr *tr);
|
||||
|
||||
void tr_convert_context(struct tr *tr, const struct tile_context *ctx,
|
||||
struct tr_context *rc);
|
||||
struct tr_context *rc);
|
||||
void tr_render_context(struct tr *tr, const struct tr_context *rc);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue