From 1df4e5d3a4253fe20b13c09b4ee6a36bf620499d Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Mon, 10 Jul 2017 00:18:56 -0400 Subject: [PATCH] remove unnecessary secondary GL context --- src/emulator.c | 88 +++++++++++++++++------------------------ src/guest/sh4/sh4_mmu.c | 2 +- src/host/host.h | 4 +- src/host/retro_host.c | 10 +---- src/host/sdl_host.c | 54 +++++++++++-------------- 5 files changed, 63 insertions(+), 95 deletions(-) diff --git a/src/emulator.c b/src/emulator.c index af314567..3acddc7b 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -59,15 +59,16 @@ struct emu { volatile int video_resized; volatile unsigned frame; - struct render_backend *r, *r2; + struct render_backend *r; struct imgui *imgui; struct microprofile *mp; struct trace_writer *trace_writer; - /* 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 */ + /* when running with multiple threads, rendering is done asynchronously on a + secondary video thread, to avoid blocking the emulated cpu. the original + hardware rendered asynchronously as well, so many games use this time to + perform additional cpu work. on some games this upwards of doubles the + performance */ int multi_threaded; thread_t video_thread; @@ -338,37 +339,29 @@ static void emu_start_tracing(struct emu *emu) { * the dreamcast, converting it into a renderable tr_context, and then rendering * and presenting it */ -static struct render_backend *emu_video_renderer(struct emu *emu) { - /* when using multi-threaded rendering, the video thread has its own render - backend instance */ - return emu->multi_threaded ? emu->r2 : emu->r; -} - static void emu_render_frame(struct emu *emu) { - struct render_backend *r2 = emu_video_renderer(emu); - prof_counter_add(COUNTER_frames, 1); /* resize the framebuffer at this time if the output size has changed */ if (emu->video_resized) { - r_destroy_framebuffer(r2, emu->video_fb); + r_destroy_framebuffer(emu->r, emu->video_fb); - emu->video_fb = r_create_framebuffer(r2, emu->video_width, + emu->video_fb = r_create_framebuffer(emu->r, 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); + framebuffer_handle_t original = r_get_framebuffer(emu->r); + r_bind_framebuffer(emu->r, emu->video_fb); r_viewport(emu->r, emu->video_width, emu->video_height); - tr_render_context(r2, &emu->pending_rc); - r_bind_framebuffer(r2, original); + tr_render_context(emu->r, &emu->pending_rc); + r_bind_framebuffer(emu->r, original); /* insert fence for main thread to synchronize on in order to ensure that the context has completely rendered */ if (emu->multi_threaded) { - emu->video_sync = r_insert_sync(r2); + emu->video_sync = r_insert_sync(emu->r); } /* update frame-based profiler stats */ @@ -378,31 +371,34 @@ static void emu_render_frame(struct emu *emu) { static void *emu_video_thread(void *data) { struct emu *emu = data; - /* make secondary context active for this thread */ - video_bind_context(emu->host, emu->r2); - while (1) { mutex_lock(emu->pending_mutex); /* wait for the next tile context provided by emu_start_render */ - while (!emu->pending_ctx) { + while (emu->running && !emu->pending_ctx) { cond_wait(emu->pending_cond, emu->pending_mutex); } /* check for shutdown */ if (!emu->running) { + emu->pending_ctx = NULL; mutex_unlock(emu->pending_mutex); break; } + video_bind_context(emu->host, emu->r); + /* convert the context, uploading its textures to the render backend */ - tr_convert_context(emu->r2, emu, &emu_find_texture, emu->pending_ctx, + tr_convert_context(emu->r, emu, &emu_find_texture, emu->pending_ctx, &emu->pending_rc); emu->pending_ctx = NULL; /* render the parsed context to an offscreen framebuffer */ emu_render_frame(emu); + /* release the context for the main thread to use */ + video_unbind_context(emu->host); + /* after the context has been rendered, release the mutex to unblock emu_guest_finish_render @@ -416,10 +412,6 @@ static void *emu_video_thread(void *data) { mutex_unlock(emu->pending_mutex); } - /* unbind context from this thread before it dies, otherwise the main thread - may not be able to bind it in order to clean it up */ - video_unbind_context(emu->host); - return NULL; } @@ -680,7 +672,6 @@ static void emu_host_context_destroyed(void *userdata) { /* destroy the video thread */ if (emu->multi_threaded) { mutex_lock(emu->pending_mutex); - emu->pending_ctx = (struct tile_context *)(intptr_t)0xdeadbeef; cond_signal(emu->pending_cond); mutex_unlock(emu->pending_mutex); @@ -689,30 +680,23 @@ static void emu_host_context_destroyed(void *userdata) { } /* destroy video renderer objects */ - struct render_backend *r2 = emu_video_renderer(emu); - video_bind_context(emu->host, r2); - rb_for_each_entry_safe(tex, &emu->live_textures, struct emu_texture, live_it) { - r_destroy_texture(r2, tex->handle); + r_destroy_texture(emu->r, tex->handle); emu_free_texture(emu, tex); } - r_destroy_framebuffer(r2, emu->video_fb); + r_destroy_framebuffer(emu->r, emu->video_fb); if (emu->video_sync) { - r_destroy_sync(r2, emu->video_sync); + r_destroy_sync(emu->r, emu->video_sync); } if (emu->multi_threaded) { - r_destroy(emu->r2); cond_destroy(emu->pending_cond); mutex_destroy(emu->pending_mutex); } - /* destroy primary renderer */ - video_bind_context(emu->host, emu->r); - mp_destroy(emu->mp); imgui_destroy(emu->imgui); r_destroy(emu->r); @@ -734,17 +718,10 @@ static void emu_host_context_reset(void *userdata) { if (emu->multi_threaded) { emu->pending_mutex = mutex_create(); emu->pending_cond = cond_create(); - emu->r2 = video_create_renderer_from(emu->host, emu->r); } - struct render_backend *r2 = emu_video_renderer(emu); - video_bind_context(emu->host, r2); - - emu->video_fb = r_create_framebuffer(r2, emu->video_width, emu->video_height, - &emu->video_tex); - - /* make primary renderer active for the current thread */ - video_bind_context(emu->host, emu->r); + emu->video_fb = r_create_framebuffer(emu->r, emu->video_width, + emu->video_height, &emu->video_tex); /* startup video thread */ if (emu->multi_threaded) { @@ -764,6 +741,11 @@ static void emu_host_resized(void *userdata) { void emu_run_frame(struct emu *emu) { static const int64_t MACHINE_STEP = HZ_TO_NANO(1000); + /* unbind the video context, making it available for the video thread */ + if (emu->multi_threaded) { + video_unbind_context(emu->host); + } + /* run dreamcast up until its next vblank */ unsigned start_frame = emu->frame; while (emu->frame == start_frame) { @@ -779,6 +761,10 @@ void emu_run_frame(struct emu *emu) { /* render the latest frame */ int64_t now = time_nanoseconds(); + if (emu->multi_threaded) { + video_bind_context(emu->host, emu->r); + } + prof_update(now); emu_paint(emu); @@ -834,8 +820,8 @@ struct emu *emu_create(struct host *host) { emu->dc->vertical_blank = &emu_guest_vertical_blank; emu->dc->poll_input = &emu_guest_poll_input; - /* start up the video thread */ - emu->multi_threaded = video_supports_multiple_contexts(emu->host); + /* start up secondary video thread */ + emu->multi_threaded = video_supports_multiple_threads(emu->host); /* enable debug menu by default */ emu->debug_menu = 1; diff --git a/src/guest/sh4/sh4_mmu.c b/src/guest/sh4/sh4_mmu.c index 57a2ad51..0bcf46bd 100644 --- a/src/guest/sh4/sh4_mmu.c +++ b/src/guest/sh4/sh4_mmu.c @@ -8,7 +8,7 @@ #define TLB_INDEX(addr) (((addr) >> 8) & 0x3f) -#define PAGE_SIZE(entry) (((entry)->lo.SZ1 << 1) | (entry)->lo.SZ0) +/*#define PAGE_SIZE(entry) (((entry)->lo.SZ1 << 1) | (entry)->lo.SZ0)*/ enum { PAGE_SIZE_1KB, diff --git a/src/host/host.h b/src/host/host.h index 446d6c58..1175db67 100644 --- a/src/host/host.h +++ b/src/host/host.h @@ -26,11 +26,9 @@ void audio_push(struct host *host, const int16_t *data, int frames); int video_width(struct host *host); int video_height(struct host *host); -int video_supports_multiple_contexts(struct host *host); +int video_supports_multiple_threads(struct host *host); struct render_backend *video_create_renderer(struct host *host); -struct render_backend *video_create_renderer_from(struct host *host, - struct render_backend *from); void video_destroy_renderer(struct host *host, struct render_backend *r); void video_bind_context(struct host *host, struct render_backend *r); diff --git a/src/host/retro_host.c b/src/host/retro_host.c index 9cf3c419..9a1a6025 100644 --- a/src/host/retro_host.c +++ b/src/host/retro_host.c @@ -98,24 +98,18 @@ void video_unbind_context(struct host *host) { } void video_bind_context(struct host *base, struct render_backend *r) { - video_context_t ctx = r_context(r); - CHECK_EQ(ctx, NULL); + CHECK(0); } void video_destroy_renderer(struct host *base, struct render_backend *r) { r_destroy(r); } -struct render_backend *video_create_renderer_from(struct host *base, - struct render_backend *from) { - CHECK(0); -} - struct render_backend *video_create_renderer(struct host *base) { return r_create(NULL); } -int video_supports_multiple_contexts(struct host *base) { +int video_supports_multiple_threads(struct host *host) { return 0; } diff --git a/src/host/sdl_host.c b/src/host/sdl_host.c index 6e2d17d0..bb842d13 100644 --- a/src/host/sdl_host.c +++ b/src/host/sdl_host.c @@ -32,6 +32,7 @@ struct sdl_host { struct host; struct SDL_Window *win; + int closed; int video_width; int video_height; @@ -223,6 +224,12 @@ static void video_resized(struct sdl_host *host) { } static void video_gl_destroy_context(struct host *base, video_context_t ctx) { + struct sdl_host *host = (struct sdl_host *)base; + + /* make sure the context is no longer active */ + int res = SDL_GL_MakeCurrent(host->win, NULL); + CHECK_EQ(res, 0); + SDL_GL_DeleteContext(ctx); } @@ -243,39 +250,32 @@ static video_context_t video_gl_create_context(struct sdl_host *host) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GLContext ctx = SDL_GL_CreateContext(host->win); - CHECK_NOTNULL(ctx, "OpenGL context creation failed: %s", SDL_GetError()); + CHECK_NOTNULL(ctx, "video_gl_create_context failed: %s", SDL_GetError()); /* disable vsync */ int res = SDL_GL_SetSwapInterval(0); - CHECK_EQ(res, 0, "Failed to disable vsync"); + CHECK_EQ(res, 0, "video_gl_create_context failed to disable vsync"); /* link in gl functions at runtime */ res = gladLoadGLLoader((GLADloadproc)&SDL_GL_GetProcAddress); - CHECK_EQ(res, 1, "GL initialization failed"); + CHECK_EQ(res, 1, "video_gl_create_context failed to link"); + return (video_context_t)ctx; } -static video_context_t video_gl_create_context_from(struct sdl_host *host, - video_context_t from) { - int res = SDL_GL_MakeCurrent(host->win, from); - CHECK_EQ(res, 0); - - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - - return video_gl_create_context(host); -} - void video_unbind_context(struct host *base) { struct sdl_host *host = (struct sdl_host *)base; + int res = SDL_GL_MakeCurrent(host->win, NULL); - CHECK_EQ(res, 0); + CHECK_EQ(res, 0, "video_unbind_context failed: %s", SDL_GetError()); } void video_bind_context(struct host *base, struct render_backend *r) { struct sdl_host *host = (struct sdl_host *)base; video_context_t ctx = r_context(r); + int res = SDL_GL_MakeCurrent(host->win, ctx); - CHECK_EQ(res, 0); + CHECK_EQ(res, 0, "video_bind_context failed: %s", SDL_GetError()); } void video_destroy_renderer(struct host *base, struct render_backend *r) { @@ -284,26 +284,14 @@ void video_destroy_renderer(struct host *base, struct render_backend *r) { video_gl_destroy_context(base, ctx); } -struct render_backend *video_create_renderer_from(struct host *base, - struct render_backend *from) { - struct sdl_host *host = (struct sdl_host *)base; - video_context_t from_ctx = r_context(from); - video_context_t ctx = video_gl_create_context_from(host, from_ctx); - return r_create(ctx); -} - struct render_backend *video_create_renderer(struct host *base) { struct sdl_host *host = (struct sdl_host *)base; video_context_t ctx = video_gl_create_context(host); return r_create(ctx); } -int video_supports_multiple_contexts(struct host *host) { -#if PLATFORM_ANDROID - return 0; -#else +int video_supports_multiple_threads(struct host *host) { return 1; -#endif } int video_height(struct host *base) { @@ -520,8 +508,9 @@ static void input_handle_controller_removed(struct sdl_host *host, int port) { return; } - LOG_INFO("controller '%s' removed from port %d", SDL_GameControllerName(ctrl), - port); + const char *name = SDL_GameControllerName(ctrl); + LOG_INFO("controller '%s' removed from port %d", name, port); + SDL_GameControllerClose(ctrl); host->controllers[port] = NULL; } @@ -773,13 +762,14 @@ struct sdl_host *host_create() { /* init sdl and create window */ int res = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); - CHECK_GE(res, 0, "SDL initialization failed: %s", SDL_GetError()); + CHECK_GE(res, 0, "host_create sdl initialization failed: %s", SDL_GetError()); host->win = SDL_CreateWindow("redream", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, VIDEO_DEFAULT_WIDTH, VIDEO_DEFAULT_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); - CHECK_NOTNULL(host->win, "Window creation failed: %s", SDL_GetError()); + CHECK_NOTNULL(host->win, "host_create window creation failed: %s", + SDL_GetError()); /* immediately poll window size for platforms like Android where the window starts fullscreen, ignoring the default width and height */