From 49398698b57e25353f1f97de8059d8b97d11c825 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Wed, 17 Jul 2013 20:26:01 -0400 Subject: [PATCH 01/16] initial emscripten port (no audio/files, input broken) --- Makefile.emscripten | 152 ++++++++++++++++++ config.def.h | 4 + driver.c | 2 +- frontend/frontend_emscripten.c | 127 +++++++++++++++ gfx/context/emscriptenegl_ctx.c | 268 ++++++++++++++++++++++++++++++++ gfx/gfx_context.c | 10 +- gfx/gfx_context.h | 1 + input/input_common.c | 3 +- input/sdl_input.c | 15 +- performance.c | 6 + 10 files changed, 581 insertions(+), 7 deletions(-) create mode 100644 Makefile.emscripten create mode 100644 frontend/frontend_emscripten.c create mode 100644 gfx/context/emscriptenegl_ctx.c diff --git a/Makefile.emscripten b/Makefile.emscripten new file mode 100644 index 0000000000..79f77f2c56 --- /dev/null +++ b/Makefile.emscripten @@ -0,0 +1,152 @@ +TARGET = retroarch.html + +OBJ = frontend/frontend_emscripten.o \ + retroarch.o \ + file.o \ + file_path.o \ + driver.o \ + conf/config_file.o \ + settings.o \ + hash.o \ + dynamic.o \ + dynamic_dummy.o \ + message.o \ + rewind.o \ + movie.o \ + gfx/gfx_common.o \ + input/input_common.o \ + core_options.o \ + patch.o \ + compat/compat.o \ + screenshot.o \ + cheats.o \ + audio/utils.o \ + input/overlay.o \ + fifo_buffer.o \ + gfx/scaler/scaler.o \ + gfx/scaler/pixconv.o \ + gfx/scaler/scaler_int.o \ + gfx/scaler/filter.o \ + gfx/state_tracker.o \ + gfx/shader_parse.o \ + gfx/fonts/fonts.o \ + gfx/fonts/bitmapfont.o \ + gfx/image.o \ + audio/resampler.o \ + audio/sinc.o \ + audio/null.o \ + performance.o + +HAVE_OPENGL = 1 +HAVE_RGUI = 1 +HAVE_SDL = 1 +HAVE_SDL_IMAGE = 1 +HAVE_FREETYPE = 1 +HAVE_ZLIB = 1 +HAVE_FBO = 1 + +libretro ?= -lretro + +LIBS = -lm +DEFINES = -I. -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" +LDFLAGS = -L. -static-libgcc -s TOTAL_MEMORY=268435456 -s FULL_ES2=1 + +ifeq ($(SCALER_NO_SIMD), 1) + DEFINES += -DSCALER_NO_SIMD +endif + +ifeq ($(PERF_TEST), 1) + DEFINES += -DPERF_TEST +endif + +ifeq ($(HAVE_RGUI), 1) + DEFINES += -DHAVE_RGUI + OBJ += frontend/menu/menu_common.o frontend/menu/rgui.o frontend/menu/history.o +endif + +ifeq ($(HAVE_SDL), 1) + OBJ += input/sdl_input.o + LIBS += -lSDL + DEFINES += -ISDL -DHAVE_SDL +endif + +ifeq ($(HAVE_THREADS), 1) + OBJ += autosave.o thread.o gfx/thread_wrapper.o + DEFINES += -DHAVE_THREADS +endif + +ifeq ($(HAVE_OPENGL), 1) + OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o + DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL +endif + +ifeq ($(HAVE_ZLIB), 1) + OBJ += gfx/rpng/rpng.o file_extract.o + DEFINES += -DHAVE_ZLIB + ifeq ($(WANT_MINIZ), 1) + OBJ += deps/miniz/miniz.o + DEFINES += -DWANT_MINIZ + else + LIBS += -lz + DEFINES += -DHAVE_ZLIB_DEFLATE + endif +endif + +LIBS += $(libretro) + +ifeq ($(HAVE_FBO), 1) + DEFINES += -DHAVE_FBO +endif + +ifneq ($(V), 1) + Q := @ +endif + +ifeq ($(DEBUG), 1) + LDFLAGS += -O0 -g -s LABEL_DEBUG=1 +else + LDFLAGS += -O2 -ffast-math +endif + +CFLAGS += -Wall -Wno-unused-result -Wno-unused-variable -I. -std=gnu99 + +all: $(TARGET) + +$(TARGET): $(OBJ) + @$(if $(Q), $(shell echo echo LD $@),) + $(Q)$(LD) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + +%.o: %.c + @$(if $(Q), $(shell echo echo CC $<),) + $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +%.o: %.cpp + @$(if $(Q), $(shell echo echo CXX $<),) + $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< + +clean: + rm -f *.o + rm -f deps/miniz/*.o + rm -f frontend/*.o + rm -f frontend/menu/*.o + rm -f audio/*.o + rm -f audio/xaudio-c/*.o + rm -f compat/*.o + rm -f compat/rxml/*.o + rm -f conf/*.o + rm -f gfx/scaler/*.o + rm -f gfx/*.o + rm -f gfx/d3d9/*.o + rm -f gfx/context/*.o + rm -f gfx/math/*.o + rm -f gfx/fonts/*.o + rm -f gfx/py_state/*.o + rm -f gfx/rpng/*.o + rm -f record/*.o + rm -f input/*.o + rm -f $(TARGET) + rm -f retroarch-joyconfig.exe + rm -f tools/*.o + +.PHONY: all clean + diff --git a/config.def.h b/config.def.h index c11957648c..ae2f7523b7 100644 --- a/config.def.h +++ b/config.def.h @@ -128,6 +128,8 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_SL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND +#elif defined(EMSCRIPTEN) +#define AUDIO_DEFAULT_DRIVER AUDIO_NULL #elif defined(HAVE_SDL) #define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_XAUDIO) @@ -195,6 +197,8 @@ enum #define EXT_EXECUTABLES "xex|XEX" #elif defined(GEKKO) #define EXT_EXECUTABLES "dol|DOL" +#else +#define EXT_EXECUTABLES "???" #endif #endif diff --git a/driver.c b/driver.c index 790e23e1f6..338dd28ff6 100644 --- a/driver.c +++ b/driver.c @@ -62,7 +62,7 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_JACK &audio_jack, #endif -#ifdef HAVE_SDL +#if defined(HAVE_SDL) && !defined(EMSCRIPTEN) &audio_sdl, #endif #ifdef HAVE_XAUDIO diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c new file mode 100644 index 0000000000..949870e0c5 --- /dev/null +++ b/frontend/frontend_emscripten.c @@ -0,0 +1,127 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include "../general.h" +#include "../conf/config_file.h" +#include "../file.h" + +#ifdef HAVE_RGUI +#include "../frontend/menu/rgui.h" +#endif + +#if defined(HAVE_RGUI) || defined(HAVE_RMENU) || defined(HAVE_RMENU_XUI) +#define HAVE_MENU +#else +#undef HAVE_MENU +#endif + +static bool menuloop; + +void mainloop(void) +{ + if (g_extern.system.shutdown) + { + RARCH_ERR("Exit...\n"); + emscripten_cancel_main_loop(); + } + else if (menuloop) + { + if (!menu_iterate()) + { + menuloop = false; + driver_set_nonblock_state(driver.nonblock_state); + + if (driver.audio_data && !audio_start_func()) + { + RARCH_ERR("Failed to resume audio driver. Will continue without audio.\n"); + g_extern.audio_active = false; + } + + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_MENU); + } + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_LOAD_GAME)) + { + load_menu_game_prepare(); + + // If ROM load fails, we exit RetroArch. On console it might make more sense to go back to menu though ... + if (load_menu_game()) + g_extern.lifecycle_mode_state |= (1ULL << MODE_GAME); + else + { +#ifdef RARCH_CONSOLE + g_extern.lifecycle_mode_state |= (1ULL << MODE_MENU); +#else + return; +#endif + } + + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_LOAD_GAME); + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_GAME)) + { + bool r; + if (g_extern.is_paused && !g_extern.is_oneshot) + r = rarch_main_idle_iterate(); + else + r = rarch_main_iterate(); + if (!r) + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_MENU)) + { + g_extern.lifecycle_mode_state |= 1ULL << MODE_MENU_PREINIT; + // Menu should always run with vsync on. + video_set_nonblock_state_func(false); + + if (driver.audio_data) + audio_stop_func(); + + menuloop = true; + } + else + { + g_extern.system.shutdown = true; + } +} + +int main(int argc, char *argv[]) +{ + emscripten_set_canvas_size(800, 600); + + rarch_main_clear_state(); + rarch_init_msg_queue(); + + char *_argv[] = { "retroarch", "--menu", "-v" }; + + int init_ret; + if ((init_ret = rarch_main_init(3, _argv))) return init_ret; + +#ifdef HAVE_MENU + menu_init(); + g_extern.lifecycle_mode_state |= 1ULL << MODE_GAME; + + // If we started a ROM directly from command line, + // push it to ROM history. + if (!g_extern.libretro_dummy) + menu_rom_history_push_current(); +#endif + + emscripten_set_main_loop(mainloop, 0, 0); + + return 0; +} diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c new file mode 100644 index 0000000000..2674e8bcc6 --- /dev/null +++ b/gfx/context/emscriptenegl_ctx.c @@ -0,0 +1,268 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2012 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +// VideoCore context, for Rasperry Pi. + +#include "../../driver.h" +#include "../gfx_context.h" +#include "../gl_common.h" +#include "../gfx_common.h" + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +static EGLContext g_egl_ctx; +static EGLSurface g_egl_surf; +static EGLDisplay g_egl_dpy; +static EGLConfig g_config; +static bool g_quit; + +static bool g_inited; + +static unsigned g_fb_width; +static unsigned g_fb_height; + +static void gfx_ctx_swap_interval(unsigned interval) +{ + // no way to control vsync in WebGL + (void)interval; +} + +static void gfx_ctx_check_window(bool *quit, + bool *resize, unsigned *width, unsigned *height, unsigned frame_count) +{ + (void)frame_count; + (void)width; + (void)height; + + *resize = false; + *quit = g_quit; +} + +static void gfx_ctx_swap_buffers(void) +{ + eglSwapBuffers(g_egl_dpy, g_egl_surf); +} + +static void gfx_ctx_set_resize(unsigned width, unsigned height) +{ + (void)width; + (void)height; +} + +static void gfx_ctx_update_window_title(void) +{ + char buf[128]; + gfx_get_fps(buf, sizeof(buf), false); +} + +static void gfx_ctx_get_video_size(unsigned *width, unsigned *height) +{ + *width = g_fb_width; + *height = g_fb_height; +} + +static void gfx_ctx_destroy(void); + +static bool gfx_ctx_init(void) +{ + EGLint width; + EGLint height; + + RARCH_LOG("[VC/EMSCRIPTEN]: Initializing...\n"); + if (g_inited) + { + RARCH_ERR("[VC/EMSCRIPTEN]: Attempted to re-initialize driver.\n"); + return true; + } + + EGLint num_config; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + // get an EGL display connection + g_egl_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!g_egl_dpy) + goto error; + + // initialize the EGL display connection + if (!eglInitialize(g_egl_dpy, NULL, NULL)) + goto error; + + // get an appropriate EGL frame buffer configuration + if (!eglChooseConfig(g_egl_dpy, attribute_list, &g_config, 1, &num_config)) + goto error; + + // create an EGL rendering context + g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, context_attributes); + if (!g_egl_ctx) + goto error; + + // create an EGL window surface + g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, 0, NULL); + if (!g_egl_surf) + goto error; + + // connect the context to the surface + if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) + goto error; + + eglQuerySurface(g_egl_dpy, g_egl_surf, EGL_WIDTH, &width); + eglQuerySurface(g_egl_dpy, g_egl_surf, EGL_HEIGHT, &height); + g_fb_width = width; + g_fb_height = height; + RARCH_LOG("[VC/EMSCRIPTEN]: Dimensions: %ux%u\n", width, height); + + return true; + +error: + gfx_ctx_destroy(); + return false; +} + +static bool gfx_ctx_set_video_mode( + unsigned width, unsigned height, + bool fullscreen) +{ + if (g_inited) + return false; + + g_inited = true; + return true; +} + +static bool gfx_ctx_bind_api(enum gfx_ctx_api api) +{ + switch (api) + { + case GFX_CTX_OPENGL_ES_API: + return eglBindAPI(EGL_OPENGL_ES_API); + default: + return false; + } +} + +static void gfx_ctx_destroy(void) +{ + if (g_egl_dpy) + { + if (g_egl_ctx) + { + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(g_egl_dpy, g_egl_ctx); + } + + if (g_egl_surf) + { + eglDestroySurface(g_egl_dpy, g_egl_surf); + } + + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(g_egl_dpy); + } + + g_egl_ctx = NULL; + g_egl_surf = NULL; + g_egl_dpy = NULL; + g_config = 0; + g_inited = false; +} + +static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) +{ + *input = NULL; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return; + + void *sdlinput = input_sdl.init(); + + if (sdlinput) + { + *input = &input_sdl; + *input_data = sdlinput; + } +} + +static bool gfx_ctx_has_focus(void) +{ + return g_inited; +} + +static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol) +{ + return SDL_GL_GetProcAddress(symbol); +} + +static float gfx_ctx_translate_aspect(unsigned width, unsigned height) +{ + return (float)width / height; +} + +static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video) +{ + return false; +} + +static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle) +{ + return false; +} + +const gfx_ctx_driver_t gfx_ctx_emscripten = { + gfx_ctx_init, + gfx_ctx_destroy, + gfx_ctx_bind_api, + gfx_ctx_swap_interval, + gfx_ctx_set_video_mode, + gfx_ctx_get_video_size, + gfx_ctx_translate_aspect, + gfx_ctx_update_window_title, + gfx_ctx_check_window, + gfx_ctx_set_resize, + gfx_ctx_has_focus, + gfx_ctx_swap_buffers, + gfx_ctx_input_driver, + gfx_ctx_get_proc_address, + gfx_ctx_init_egl_image_buffer, + gfx_ctx_write_egl_image, + NULL, + "emscripten", +}; diff --git a/gfx/gfx_context.c b/gfx/gfx_context.c index 47745ca3f3..ba0aedd0fe 100644 --- a/gfx/gfx_context.c +++ b/gfx/gfx_context.c @@ -13,6 +13,7 @@ * If not, see . */ +#include "../general.h" #include "gfx_context.h" #include @@ -51,14 +52,17 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #if defined(IOS) || defined(OSX) //< Don't use __APPLE__ as it breaks basic SDL builds &gfx_ctx_apple, #endif -#if defined(HAVE_SDL) && defined(HAVE_OPENGL) +#if defined(HAVE_SDL) && defined(HAVE_OPENGL) && !defined(EMSCRIPTEN) &gfx_ctx_sdl_gl, #endif +#ifdef EMSCRIPTEN + &gfx_ctx_emscripten, +#endif }; const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident) { - for (unsigned i = 0; i < sizeof(gfx_ctx_drivers) / sizeof(gfx_ctx_drivers[0]); i++) + for (unsigned i = 0; i < ARRAY_SIZE(gfx_ctx_drivers); i++) { if (strcmp(gfx_ctx_drivers[i]->ident, ident) == 0) return gfx_ctx_drivers[i]; @@ -69,7 +73,7 @@ const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident) const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api) { - for (unsigned i = 0; i < sizeof(gfx_ctx_drivers) / sizeof(gfx_ctx_drivers[0]); i++) + for (unsigned i = 0; i < ARRAY_SIZE(gfx_ctx_drivers); i++) { if (gfx_ctx_drivers[i]->bind_api(api)) { diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index fa70e76e73..6e0219f382 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -108,6 +108,7 @@ extern const gfx_ctx_driver_t gfx_ctx_wgl; extern const gfx_ctx_driver_t gfx_ctx_videocore; extern const gfx_ctx_driver_t gfx_ctx_bbqnx; extern const gfx_ctx_driver_t gfx_ctx_apple; +extern const gfx_ctx_driver_t gfx_ctx_emscripten; extern const gfx_ctx_driver_t gfx_ctx_null; const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize. diff --git a/input/input_common.c b/input/input_common.c index 12be6eaf76..e58eede808 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -16,6 +16,7 @@ #include "input_common.h" #include #include +#include #include "../general.h" #include "../driver.h" @@ -47,7 +48,7 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #if defined(__linux) && !defined(ANDROID) &linuxraw_joypad, #endif -#ifdef HAVE_SDL +#if defined(HAVE_SDL) && !defined(EMSCRIPTEN) &sdl_joypad, #endif #endif diff --git a/input/sdl_input.c b/input/sdl_input.c index d438c3f8a5..1c332b8396 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -24,6 +24,10 @@ #include "../libretro.h" #include "input_common.h" +#if !(SDL_MAJOR_VERSION <= 1 && SDL_MINOR_VERSION <= 2) +#define SDL_GetKeyState SDL_GetKeyboardState +#endif + typedef struct sdl_input { const rarch_joypad_driver_t *joypad; @@ -51,9 +55,8 @@ static bool sdl_key_pressed(int key) int sym = input_translate_rk_to_keysym((enum retro_key)key); - int num_keys; + int num_keys = 0xFFFF; Uint8 *keymap = SDL_GetKeyState(&num_keys); - if (sym < 0 || sym >= num_keys) return false; return keymap[sym]; @@ -216,16 +219,24 @@ static void sdl_input_free(void *data) static void sdl_poll_mouse(sdl_input_t *sdl) { + (void)sdl; +#ifndef EMSCRIPTEN Uint8 btn = SDL_GetRelativeMouseState(&sdl->mouse_x, &sdl->mouse_y); SDL_GetMouseState(&sdl->mouse_abs_x, &sdl->mouse_abs_y); sdl->mouse_l = SDL_BUTTON(SDL_BUTTON_LEFT) & btn ? 1 : 0; sdl->mouse_r = SDL_BUTTON(SDL_BUTTON_RIGHT) & btn ? 1 : 0; sdl->mouse_m = SDL_BUTTON(SDL_BUTTON_MIDDLE) & btn ? 1 : 0; +#endif } static void sdl_input_poll(void *data) { +#ifdef EMSCRIPTEN + SDL_Event event; + while (SDL_PollEvent(&event)); +#else SDL_PumpEvents(); +#endif sdl_input_t *sdl = (sdl_input_t*)data; input_joypad_poll(sdl->joypad); diff --git a/performance.c b/performance.c index 7829fe265d..faad97135d 100644 --- a/performance.c +++ b/performance.c @@ -58,6 +58,10 @@ #include #endif +#ifdef EMSCRIPTEN +#include +#endif + #ifdef PERF_TEST #define MAX_COUNTERS 64 static struct rarch_perf_counter *perf_counters[MAX_COUNTERS]; @@ -145,6 +149,8 @@ rarch_time_t rarch_get_time_usec(void) if (clock_gettime(CLOCK_MONOTONIC, &tv) < 0) return 0; return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000; +#elif defined(EMSCRIPTEN) + return emscripten_get_now() * 1000; #else #error "Your platform does not have a timer function implemented in rarch_get_time_usec(). Cannot continue." #endif From 9486d8154c74a743f3aa478a87fc698c59288405 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Wed, 17 Jul 2013 22:06:52 -0400 Subject: [PATCH 02/16] fix input --- input/sdl_input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/input/sdl_input.c b/input/sdl_input.c index 1c332b8396..a5dd282c85 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -57,6 +57,7 @@ static bool sdl_key_pressed(int key) int num_keys = 0xFFFF; Uint8 *keymap = SDL_GetKeyState(&num_keys); + if (sym < 0 || sym >= num_keys) return false; return keymap[sym]; From c77af5739b39c56d47dc789957bef0fb04aa240a Mon Sep 17 00:00:00 2001 From: ToadKing Date: Wed, 17 Jul 2013 23:09:22 -0400 Subject: [PATCH 03/16] fix compile --- file_ext.h | 2 ++ frontend/frontend_emscripten.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/file_ext.h b/file_ext.h index bdfddeea3a..54bfb79cf5 100644 --- a/file_ext.h +++ b/file_ext.h @@ -42,6 +42,8 @@ #define EXT_EXECUTABLES "dol|DOL" #define SALAMANDER_FILE "boot.dol" #define DEFAULT_EXE_EXT ".dol" +#elif defined(EMSCRIPTEN) +#define EXT_EXECUTABLES "" #endif #endif diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index 949870e0c5..1e90dbd1f1 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -36,7 +36,7 @@ void mainloop(void) if (g_extern.system.shutdown) { RARCH_ERR("Exit...\n"); - emscripten_cancel_main_loop(); + exit(0); } else if (menuloop) { From ff3de2563628046dfc2fe796899223b4f14163d5 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Thu, 18 Jul 2013 23:58:35 -0400 Subject: [PATCH 04/16] fix error on video re-init, other cleanup --- Makefile.emscripten | 4 +++- frontend/frontend_emscripten.c | 4 +--- gfx/context/emscriptenegl_ctx.c | 14 +++++++------- gfx/gl.c | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Makefile.emscripten b/Makefile.emscripten index 79f77f2c56..c7e5612320 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -1,4 +1,4 @@ -TARGET = retroarch.html +TARGET = retroarch.js OBJ = frontend/frontend_emscripten.o \ retroarch.o \ @@ -18,6 +18,7 @@ OBJ = frontend/frontend_emscripten.o \ core_options.o \ patch.o \ compat/compat.o \ + compat/rxml/rxml.o \ screenshot.o \ cheats.o \ audio/utils.o \ @@ -44,6 +45,7 @@ HAVE_SDL_IMAGE = 1 HAVE_FREETYPE = 1 HAVE_ZLIB = 1 HAVE_FBO = 1 +WANT_MINIZ = 1 libretro ?= -lretro diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index 1e90dbd1f1..d03c294fb7 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -106,10 +106,8 @@ int main(int argc, char *argv[]) rarch_main_clear_state(); rarch_init_msg_queue(); - char *_argv[] = { "retroarch", "--menu", "-v" }; - int init_ret; - if ((init_ret = rarch_main_init(3, _argv))) return init_ret; + if ((init_ret = rarch_main_init(argc, argv))) return init_ret; #ifdef HAVE_MENU menu_init(); diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c index 2674e8bcc6..e0778fe973 100644 --- a/gfx/context/emscriptenegl_ctx.c +++ b/gfx/context/emscriptenegl_ctx.c @@ -75,7 +75,8 @@ static void gfx_ctx_set_resize(unsigned width, unsigned height) static void gfx_ctx_update_window_title(void) { char buf[128]; - gfx_get_fps(buf, sizeof(buf), false); + if (gfx_get_fps(buf, sizeof(buf), false)) + RARCH_LOG("%s\n", buf); } static void gfx_ctx_get_video_size(unsigned *width, unsigned *height) @@ -91,10 +92,10 @@ static bool gfx_ctx_init(void) EGLint width; EGLint height; - RARCH_LOG("[VC/EMSCRIPTEN]: Initializing...\n"); + RARCH_LOG("[EMSCRIPTEN/EGL]: Initializing...\n"); if (g_inited) { - RARCH_ERR("[VC/EMSCRIPTEN]: Attempted to re-initialize driver.\n"); + RARCH_LOG("[EMSCRIPTEN/EGL]: Attempted to re-initialize driver.\n"); return true; } @@ -147,7 +148,7 @@ static bool gfx_ctx_init(void) eglQuerySurface(g_egl_dpy, g_egl_surf, EGL_HEIGHT, &height); g_fb_width = width; g_fb_height = height; - RARCH_LOG("[VC/EMSCRIPTEN]: Dimensions: %ux%u\n", width, height); + RARCH_LOG("[EMSCRIPTEN/EGL]: Dimensions: %ux%u\n", width, height); return true; @@ -182,9 +183,10 @@ static void gfx_ctx_destroy(void) { if (g_egl_dpy) { + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (g_egl_ctx) { - eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(g_egl_dpy, g_egl_ctx); } @@ -193,8 +195,6 @@ static void gfx_ctx_destroy(void) eglDestroySurface(g_egl_dpy, g_egl_surf); } - eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(g_egl_dpy); } diff --git a/gfx/gl.c b/gfx/gl.c index bec4389a92..c6a4b571b4 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1844,6 +1844,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo } RARCH_LOG("Found GL context: %s\n", gl->ctx_driver->ident); + while (glGetError() != GL_NO_ERROR); context_get_video_size_func(&gl->full_x, &gl->full_y); RARCH_LOG("Detecting screen resolution %ux%u.\n", gl->full_x, gl->full_y); From b01856d1665586a329b01ba86f444f9dea18bd2a Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sat, 20 Jul 2013 00:30:54 -0400 Subject: [PATCH 05/16] remove reinit hack, turned out to be an emscripten bug --- gfx/gl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gfx/gl.c b/gfx/gl.c index c6a4b571b4..bec4389a92 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1844,7 +1844,6 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo } RARCH_LOG("Found GL context: %s\n", gl->ctx_driver->ident); - while (glGetError() != GL_NO_ERROR); context_get_video_size_func(&gl->full_x, &gl->full_y); RARCH_LOG("Detecting screen resolution %ux%u.\n", gl->full_x, gl->full_y); From 9816334ef4f945135480bfe8bc41a1b76cc2da62 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sat, 20 Jul 2013 04:23:03 -0400 Subject: [PATCH 06/16] add ability to "disable" v-sync, set FPS limit to max --- frontend/frontend_emscripten.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index d03c294fb7..745096e0b4 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -119,7 +119,7 @@ int main(int argc, char *argv[]) menu_rom_history_push_current(); #endif - emscripten_set_main_loop(mainloop, 0, 0); + emscripten_set_main_loop(mainloop, g_settings.video.vsync ? 0 : INT_MAX, 1); return 0; } From 6bb403c04b47e816df66287d29b06a687f419477 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sat, 27 Jul 2013 14:17:22 -0400 Subject: [PATCH 07/16] [EMSCRIPTEN] enable openal, change library name to retro_emscripten --- Makefile.emscripten | 17 ++++++++++++++--- config.def.h | 2 -- libretro-test/Makefile | 8 ++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Makefile.emscripten b/Makefile.emscripten index c7e5612320..797e62c0b2 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -39,6 +39,7 @@ OBJ = frontend/frontend_emscripten.o \ performance.o HAVE_OPENGL = 1 +HAVE_AL = 1 HAVE_RGUI = 1 HAVE_SDL = 1 HAVE_SDL_IMAGE = 1 @@ -47,10 +48,14 @@ HAVE_ZLIB = 1 HAVE_FBO = 1 WANT_MINIZ = 1 -libretro ?= -lretro +ifneq ($(NATIVE_ZLIB),) + WANT_MINIZ = 0 +endif + +libretro ?= -lretro_emscripten LIBS = -lm -DEFINES = -I. -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" +DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" LDFLAGS = -L. -static-libgcc -s TOTAL_MEMORY=268435456 -s FULL_ES2=1 ifeq ($(SCALER_NO_SIMD), 1) @@ -82,6 +87,12 @@ ifeq ($(HAVE_OPENGL), 1) DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL endif +ifeq ($(HAVE_AL), 1) + OBJ += audio/openal.o + DEFINES += -DHAVE_AL + LIBS += -lopenal +endif + ifeq ($(HAVE_ZLIB), 1) OBJ += gfx/rpng/rpng.o file_extract.o DEFINES += -DHAVE_ZLIB @@ -105,7 +116,7 @@ ifneq ($(V), 1) endif ifeq ($(DEBUG), 1) - LDFLAGS += -O0 -g -s LABEL_DEBUG=1 + LDFLAGS += -O0 -g else LDFLAGS += -O2 -ffast-math endif diff --git a/config.def.h b/config.def.h index 8ec9b8d81d..0d647ad290 100644 --- a/config.def.h +++ b/config.def.h @@ -129,8 +129,6 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_SL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND -#elif defined(EMSCRIPTEN) -#define AUDIO_DEFAULT_DRIVER AUDIO_NULL #elif defined(HAVE_SDL) #define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_XAUDIO) diff --git a/libretro-test/Makefile b/libretro-test/Makefile index 15853aaeeb..1998f05f22 100644 --- a/libretro-test/Makefile +++ b/libretro-test/Makefile @@ -1,4 +1,8 @@ +ifneq ($(EMSCRIPTEN),) + platform = emscripten +endif + ifeq ($(platform),) platform = unix ifeq ($(shell uname -a),) @@ -32,6 +36,10 @@ else ifeq ($(platform), qnx) TARGET := $(TARGET_NAME)_libretro_qnx.so fpic := -fPIC SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined +else ifeq ($(platform), emscripten) + TARGET := $(TARGET_NAME)_libretro_emscripten.so + fpic := -fPIC + SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined else CC = gcc TARGET := $(TARGET_NAME)_retro.dll From 747927040679dce395a766cd7cdae63ba985f611 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Wed, 7 Aug 2013 18:37:05 -0400 Subject: [PATCH 08/16] makefile changes --- Makefile.emscripten | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile.emscripten b/Makefile.emscripten index 797e62c0b2..f4ba2700a7 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -56,7 +56,7 @@ libretro ?= -lretro_emscripten LIBS = -lm DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" -LDFLAGS = -L. -static-libgcc -s TOTAL_MEMORY=268435456 -s FULL_ES2=1 +LDFLAGS = -L. -static-libgcc -s TOTAL_MEMORY=268435456 ifeq ($(SCALER_NO_SIMD), 1) DEFINES += -DSCALER_NO_SIMD @@ -157,9 +157,8 @@ clean: rm -f gfx/rpng/*.o rm -f record/*.o rm -f input/*.o - rm -f $(TARGET) - rm -f retroarch-joyconfig.exe rm -f tools/*.o + rm -f $(TARGET) .PHONY: all clean From f4ff5f3ea3d11452f0b9c5a28665fcf3687ef4d2 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 18 Aug 2013 02:50:10 -0400 Subject: [PATCH 09/16] [EMSCRIPTEN] less workarounds due to fixes in emscripten, enable more optimizations --- Makefile.emscripten | 22 ++++++++++++++++------ frontend/frontend_emscripten.c | 27 ++++++++++++++++++++++++--- gfx/context/emscriptenegl_ctx.c | 11 +++++------ input/sdl_input.c | 7 +------ 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/Makefile.emscripten b/Makefile.emscripten index f4ba2700a7..52f9711060 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -47,16 +47,19 @@ HAVE_FREETYPE = 1 HAVE_ZLIB = 1 HAVE_FBO = 1 WANT_MINIZ = 1 +MEMORY = 67108864 +LTO = 0 +FAST_DOUBLES = 1 ifneq ($(NATIVE_ZLIB),) WANT_MINIZ = 0 endif -libretro ?= -lretro_emscripten +libretro = libretro_emscripten.bc LIBS = -lm DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" -LDFLAGS = -L. -static-libgcc -s TOTAL_MEMORY=268435456 +LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) ifeq ($(SCALER_NO_SIMD), 1) DEFINES += -DSCALER_NO_SIMD @@ -105,8 +108,6 @@ ifeq ($(HAVE_ZLIB), 1) endif endif -LIBS += $(libretro) - ifeq ($(HAVE_FBO), 1) DEFINES += -DHAVE_FBO endif @@ -117,8 +118,17 @@ endif ifeq ($(DEBUG), 1) LDFLAGS += -O0 -g + CFLAGS += -O0 -g else - LDFLAGS += -O2 -ffast-math + LDFLAGS += -O2 + # WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake) + ifeq ($(FAST_DOUBLES), 1) + LDFLAGS += -s DOUBLE_MODE=0 + endif + ifeq ($(LTO), 1) + LDFLAGS += --llvm-lto 3 + endif + CFLAGS += -O2 endif CFLAGS += -Wall -Wno-unused-result -Wno-unused-variable -I. -std=gnu99 @@ -127,7 +137,7 @@ all: $(TARGET) $(TARGET): $(OBJ) @$(if $(Q), $(shell echo echo LD $@),) - $(Q)$(LD) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + $(Q)$(LD) -o $@ $(OBJ) $(libretro) $(LIBS) $(LDFLAGS) %.o: %.c @$(if $(Q), $(shell echo echo CC $<),) diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index 745096e0b4..cfa762ae1f 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -31,12 +31,33 @@ static bool menuloop; -void mainloop(void) +static void endloop(void) +{ + g_extern.system.shutdown = false; + menu_free(); + + if (g_extern.config_save_on_exit && *g_extern.config_path) + config_save_file(g_extern.config_path); + + if (g_extern.main_is_init) + rarch_main_deinit(); + + rarch_deinit_msg_queue(); + +#ifdef PERF_TEST + rarch_perf_log(); +#endif + + rarch_main_clear_state(); + + exit(0); +} + +static void mainloop(void) { if (g_extern.system.shutdown) { - RARCH_ERR("Exit...\n"); - exit(0); + endloop(); } else if (menuloop) { diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c index e0778fe973..9c5f2516bd 100644 --- a/gfx/context/emscriptenegl_ctx.c +++ b/gfx/context/emscriptenegl_ctx.c @@ -30,14 +30,12 @@ #include #include -#include #include static EGLContext g_egl_ctx; static EGLSurface g_egl_surf; static EGLDisplay g_egl_dpy; static EGLConfig g_config; -static bool g_quit; static bool g_inited; @@ -58,12 +56,13 @@ static void gfx_ctx_check_window(bool *quit, (void)height; *resize = false; - *quit = g_quit; + *quit = false; } static void gfx_ctx_swap_buffers(void) { - eglSwapBuffers(g_egl_dpy, g_egl_surf); + // no-op in emscripten, no way to force swap/wait for vsync in browsers + //eglSwapBuffers(g_egl_dpy, g_egl_surf); } static void gfx_ctx_set_resize(unsigned width, unsigned height) @@ -209,7 +208,7 @@ static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data { *input = NULL; - if (SDL_Init(SDL_INIT_VIDEO) != 0) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) != 0) return; void *sdlinput = input_sdl.init(); @@ -228,7 +227,7 @@ static bool gfx_ctx_has_focus(void) static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol) { - return SDL_GL_GetProcAddress(symbol); + return eglGetProcAddress(symbol); } static float gfx_ctx_translate_aspect(unsigned width, unsigned height) diff --git a/input/sdl_input.c b/input/sdl_input.c index a5dd282c85..57c9a32b91 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -24,7 +24,7 @@ #include "../libretro.h" #include "input_common.h" -#if !(SDL_MAJOR_VERSION <= 1 && SDL_MINOR_VERSION <= 2) +#ifdef EMSCRIPTEN #define SDL_GetKeyState SDL_GetKeyboardState #endif @@ -232,12 +232,7 @@ static void sdl_poll_mouse(sdl_input_t *sdl) static void sdl_input_poll(void *data) { -#ifdef EMSCRIPTEN - SDL_Event event; - while (SDL_PollEvent(&event)); -#else SDL_PumpEvents(); -#endif sdl_input_t *sdl = (sdl_input_t*)data; input_joypad_poll(sdl->joypad); From 12f4b48e84956009dc20aa9241d71f301f4b5258 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 18 Aug 2013 03:02:42 -0400 Subject: [PATCH 10/16] query for OES_EGL_image before trying to load symbol --- gfx/gl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gfx/gl.c b/gfx/gl.c index bec4389a92..cd96295c9a 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -115,6 +115,9 @@ static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC pglEGLImageTargetTexture2DOES; static bool load_eglimage_proc(gl_t *gl) { + if (!gl_query_extension("OES_EGL_image")) + return false; + LOAD_GL_SYM(EGLImageTargetTexture2DOES); return pglEGLImageTargetTexture2DOES; } From c30d0287d1455eb8f8dfda36f678bea3307579c4 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 25 Aug 2013 15:39:50 -0400 Subject: [PATCH 11/16] [EMSCRIPTEN] buildfix, implement detecting canvas size changes --- Makefile.emscripten | 2 +- gfx/context/emscriptenegl_ctx.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Makefile.emscripten b/Makefile.emscripten index 52f9711060..0c15c8bd81 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -86,7 +86,7 @@ ifeq ($(HAVE_THREADS), 1) endif ifeq ($(HAVE_OPENGL), 1) - OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o + OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o gfx/glsym/rglgen.o gfx/glsym/glsym_es2.o DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL endif diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c index 9c5f2516bd..363152c358 100644 --- a/gfx/context/emscriptenegl_ctx.c +++ b/gfx/context/emscriptenegl_ctx.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -52,10 +53,19 @@ static void gfx_ctx_check_window(bool *quit, bool *resize, unsigned *width, unsigned *height, unsigned frame_count) { (void)frame_count; - (void)width; - (void)height; + int iWidth, iHeight, isFullscreen; - *resize = false; + emscripten_get_canvas_size(&iWidth, &iHeight, &isFullscreen); + *width = (unsigned) iWidth; + *height = (unsigned) iHeight; + + if (*width != g_fb_width || *height != g_fb_height) + *resize = true; + else + *resize = false; + + g_fb_width = (unsigned) iWidth; + g_fb_height = (unsigned) iHeight; *quit = false; } @@ -167,8 +177,10 @@ static bool gfx_ctx_set_video_mode( return true; } -static bool gfx_ctx_bind_api(enum gfx_ctx_api api) +static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor) { + (void)major; + (void)minor; switch (api) { case GFX_CTX_OPENGL_ES_API: From 04be8cbee209c333932aa951d5caf5147f487d2b Mon Sep 17 00:00:00 2001 From: ToadKing Date: Wed, 28 Aug 2013 00:03:25 -0400 Subject: [PATCH 12/16] new audio core, RWebAudio. Glitchy, but works well, even with requestAnimationFrame callbacks --- Makefile.emscripten | 11 +-- config.def.h | 5 + driver.c | 3 + driver.h | 1 + emscripten/RWebAudio.c | 90 ++++++++++++++++++ emscripten/RWebAudio.h | 29 ++++++ emscripten/library_rwebaudio.js | 161 ++++++++++++++++++++++++++++++++ frontend/frontend_emscripten.c | 6 ++ settings.c | 2 + 9 files changed, 300 insertions(+), 8 deletions(-) create mode 100644 emscripten/RWebAudio.c create mode 100644 emscripten/RWebAudio.h create mode 100644 emscripten/library_rwebaudio.js diff --git a/Makefile.emscripten b/Makefile.emscripten index 0c15c8bd81..559638ab75 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -22,6 +22,7 @@ OBJ = frontend/frontend_emscripten.o \ screenshot.o \ cheats.o \ audio/utils.o \ + emscripten/RWebAudio.o \ input/overlay.o \ fifo_buffer.o \ gfx/scaler/scaler.o \ @@ -39,7 +40,6 @@ OBJ = frontend/frontend_emscripten.o \ performance.o HAVE_OPENGL = 1 -HAVE_AL = 1 HAVE_RGUI = 1 HAVE_SDL = 1 HAVE_SDL_IMAGE = 1 @@ -59,7 +59,7 @@ libretro = libretro_emscripten.bc LIBS = -lm DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" -LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) +LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js ifeq ($(SCALER_NO_SIMD), 1) DEFINES += -DSCALER_NO_SIMD @@ -90,12 +90,6 @@ ifeq ($(HAVE_OPENGL), 1) DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL endif -ifeq ($(HAVE_AL), 1) - OBJ += audio/openal.o - DEFINES += -DHAVE_AL - LIBS += -lopenal -endif - ifeq ($(HAVE_ZLIB), 1) OBJ += gfx/rpng/rpng.o file_extract.o DEFINES += -DHAVE_ZLIB @@ -165,6 +159,7 @@ clean: rm -f gfx/fonts/*.o rm -f gfx/py_state/*.o rm -f gfx/rpng/*.o + rm -f gfx/glsym/*.o rm -f record/*.o rm -f input/*.o rm -f tools/*.o diff --git a/config.def.h b/config.def.h index a677cc2778..3294cd73b2 100644 --- a/config.def.h +++ b/config.def.h @@ -63,6 +63,7 @@ enum AUDIO_PS3, AUDIO_XENON360, AUDIO_WII, + AUDIO_RWEBAUDIO, AUDIO_NULL, INPUT_ANDROID, @@ -130,6 +131,8 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_SL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND +#elif defined(EMSCRIPTEN) +#define AUDIO_DEFAULT_DRIVER AUDIO_RWEBAUDIO #elif defined(HAVE_SDL) #define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_XAUDIO) @@ -335,6 +338,8 @@ static const bool rate_control = false; // Rate control delta. Defines how much rate_control is allowed to adjust input rate. #if defined(__QNX__) static const float rate_control_delta = 0.000; +#elif defined(EMSCRIPTEN) +static const float rate_control_delta = 0.002; #else static const float rate_control_delta = 0.005; #endif diff --git a/driver.c b/driver.c index 761e8cdbdb..51e8e6a577 100644 --- a/driver.c +++ b/driver.c @@ -84,6 +84,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef GEKKO &audio_gx, #endif +#ifdef EMSCRIPTEN + &audio_rwebaudio, +#endif #ifdef HAVE_NULLAUDIO &audio_null, #endif diff --git a/driver.h b/driver.h index bc4cdd7d1a..91dd004005 100644 --- a/driver.h +++ b/driver.h @@ -511,6 +511,7 @@ extern const audio_driver_t audio_coreaudio; extern const audio_driver_t audio_xenon360; extern const audio_driver_t audio_ps3; extern const audio_driver_t audio_gx; +extern const audio_driver_t audio_rwebaudio; extern const audio_driver_t audio_null; extern const video_driver_t video_gl; extern const video_driver_t video_psp1; diff --git a/emscripten/RWebAudio.c b/emscripten/RWebAudio.c new file mode 100644 index 0000000000..b384c828a8 --- /dev/null +++ b/emscripten/RWebAudio.c @@ -0,0 +1,90 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../driver.h" +#include "../general.h" + +#include "RWebAudio.h" + +static void ra_free(void *data) +{ + RWebAudioFree(); +} + +static void *ra_init(const char *device, unsigned rate, unsigned latency) +{ + (void)device; + (void)rate; + void *data = RWebAudioInit(latency); + g_settings.audio.out_rate = RWebAudioSampleRate(); + RARCH_LOG("audio out rate: %u\n", g_settings.audio.out_rate); + return data; +} + +static ssize_t ra_write(void *data, const void *buf, size_t size) +{ + (void)data; + return RWebAudioWrite(buf, size); +} + +static bool ra_stop(void *data) +{ + (void)data; + return RWebAudioStop(); +} + +static void ra_set_nonblock_state(void *data, bool state) +{ + (void)data; + RWebAudioSetNonblockState(state); +} + +static bool ra_start(void *data) +{ + (void)data; + return RWebAudioStart(); +} + +static bool ra_use_float(void *data) +{ + (void)data; + return true; +} + +static size_t ra_write_avail(void *data) +{ + (void)data; + return RWebAudioWriteAvail(); +} + +static size_t ra_buffer_size(void *data) +{ + (void)data; + return RWebAudioBufferSize(); +} + +const audio_driver_t audio_rwebaudio = { + ra_init, + ra_write, + ra_stop, + ra_start, + ra_set_nonblock_state, + ra_free, + ra_use_float, + "rwebaudio", + ra_write_avail, + ra_buffer_size, +}; + diff --git a/emscripten/RWebAudio.h b/emscripten/RWebAudio.h new file mode 100644 index 0000000000..fd928cd3d6 --- /dev/null +++ b/emscripten/RWebAudio.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +unsigned RWebAudioSampleRate(void); +void *RWebAudioInit(unsigned latency); +ssize_t RWebAudioWrite(const void *buf, size_t size); +bool RWebAudioStop(void); +bool RWebAudioStart(void); +void RWebAudioSetNonblockState(bool state); +void RWebAudioFree(void); +size_t RWebAudioWriteAvail(void); +size_t RWebAudioBufferSize(void); +int RWebAudioEnoughSpace(void); diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js new file mode 100644 index 0000000000..d2197ceccb --- /dev/null +++ b/emscripten/library_rwebaudio.js @@ -0,0 +1,161 @@ +//"use strict"; + +var LibraryRWebAudio = { + $RA__deps: ['$Browser'], + $RA: { + SCRIPTNODE_BUFFER: 1024, + + context: null, + leftBuffer: null, + rightBuffer: null, + blank: null, + scriptNode: null, + bufferNode: null, + start: 0, + end: 0, + size: 0, + lastWrite: 0, + nonblock: false, + + npot: function(n) { + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; + }, + + process: function(e) { + var left = e.outputBuffer.getChannelData(0); + var right = e.outputBuffer.getChannelData(1); + var samples1 = RA.size; + var samples2 = 0; + samples1 = e.outputBuffer.length > samples1 ? samples1 : e.outputBuffer.length; + + if (samples1 + RA.start > RA.leftBuffer.length) { + samples2 = samples1 + RA.start - RA.leftBuffer.length; + samples1 = samples1 - samples2; + } + + var remaining = e.outputBuffer.length - (samples1 + samples2); + + if (samples1) { + left.set(RA.leftBuffer.subarray(RA.start, RA.start + samples1), 0); + right.set(RA.rightBuffer.subarray(RA.start, RA.start + samples1), 0); + } + + if (samples2) { + left.set(RA.leftBuffer.subarray(0, samples2), samples1); + right.set(RA.rightBuffer.subarray(0, samples2), samples1); + } + + /*if (remaining) { + left.set(RA.blank.subarray(0, remaining), samples1 + samples2); + right.set(RA.blank.subarray(0, remaining), samples1 + samples2); + }*/ + + RA.start = (RA.start + samples1 + samples2) % RA.leftBuffer.length; + RA.size -= samples1 + samples2; + } + }, + + RWebAudioSampleRate: function() { + return RA.context.sampleRate; + }, + + RWebAudioInit: function(latency) { + var ac = window['AudioContext'] || window['webkitAudioContext']; + var bufferSize; + + if (!ac) return 0; + + RA.context = new ac(); + // account for script processor overhead + latency -= 32; + // because we have to guess on how many samples the core will send when + // returning early, we double the buffer size to account for times when it + // sends more than we expect it to without losing samples + bufferSize = RA.npot(RA.context.sampleRate * latency / 1000) * 2; + RA.leftBuffer = new Float32Array(bufferSize); + RA.rightBuffer = new Float32Array(bufferSize); + RA.blank = new Float32Array(RA.SCRIPTNODE_BUFFER); + RA.bufferNode = RA.context.createBufferSource(); + RA.bufferNode.buffer = RA.context.createBuffer(2, RA.SCRIPTNODE_BUFFER, RA.context.sampleRate); + RA.bufferNode.loop = true; + RA.scriptNode = RA.context.createScriptProcessor(RA.SCRIPTNODE_BUFFER, 2, 2); + RA.scriptNode.onaudioprocess = RA.process; + RA.bufferNode.connect(RA.scriptNode); + RA.scriptNode.connect(RA.context.destination); + RA.bufferNode.start(0); + RA.start = RA.end = RA.size = 0; + RA.nonblock = false; + return 1; + }, + + RWebAudioWrite: function (buf, size) { + var samples = size / 8; + var free = RA.leftBuffer.length - RA.size; + if (free < samples) + RA.start = (RA.start + free) % RA.leftBuffer.length; + + for (var i = 0; i < samples; i++) { + RA.leftBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8', 'float') }}}; + RA.rightBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8 + 4', 'float') }}}; + RA.end = (RA.end + 1) % RA.leftBuffer.length; + } + + RA.lastWrite = size; + RA.size += samples; + return size; + }, + + RWebAudioStop: function() { + RA.scriptNode.onaudioprocess = null; + return true; + }, + + RWebAudioStart: function() { + RA.scriptNode.onaudioprocess = RA.process; + return true; + }, + + RWebAudioSetNonblockState: function(state) { + RA.nonblock = state; + }, + + RWebAudioFree: function() { + RA.scriptNode.onaudioprocess = null; + RA.start = RA.end = RA.size = RA.lastWrite = 0; + return; + }, + + RWebAudioWriteAvail: function() { + var free = (RA.leftBuffer.length / 2) - RA.size; + // 4 byte samples, 2 channels + free *= 8; + + if (free < 0) + return 0; + else + return free; + }, + + RWebAudioBufferSize: function() { + return RA.leftBuffer.length / 2; + }, + + RWebAudioEnoughSpace__deps: ['RWebAudioWriteAvail'], + RWebAudioEnoughSpace: function() { + var guess = RA.lastWrite; + var available = _RWebAudioWriteAvail(); + if (RA.nonblock) return true; + if (!guess) return true; + return (guess < available) ? 1 : 0; + } +}; + +autoAddDeps(LibraryRWebAudio, '$RA'); +mergeInto(LibraryManager.library, LibraryRWebAudio); diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index cfa762ae1f..452c7f41ea 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -18,6 +18,7 @@ #include "../general.h" #include "../conf/config_file.h" #include "../file.h" +#include "../emscripten/RWebAudio.h" #ifdef HAVE_RGUI #include "../frontend/menu/rgui.h" @@ -55,6 +56,11 @@ static void endloop(void) static void mainloop(void) { + if (!RWebAudioEnoughSpace()) + { + return; + } + if (g_extern.system.shutdown) { endloop(); diff --git a/settings.c b/settings.c index 4b9dd32237..682774941a 100644 --- a/settings.c +++ b/settings.c @@ -67,6 +67,8 @@ const char *config_get_default_audio(void) return "ps3"; case AUDIO_WII: return "gx"; + case AUDIO_RWEBAUDIO: + return "rwebaudio"; case AUDIO_NULL: return "null"; default: From 336e1eeb51ffda3ae3999f01f26ed57074795ba6 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Mon, 2 Sep 2013 21:29:40 -0400 Subject: [PATCH 13/16] [EMSCRIPTEN] more audio fixes, revert to busywait method --- config.def.h | 2 - emscripten/RWebAudio.c | 4 +- emscripten/RWebAudio.h | 1 - emscripten/library_rwebaudio.js | 284 ++++++++++++++++---------------- frontend/frontend_emscripten.c | 5 - 5 files changed, 143 insertions(+), 153 deletions(-) diff --git a/config.def.h b/config.def.h index 3294cd73b2..32d263a623 100644 --- a/config.def.h +++ b/config.def.h @@ -338,8 +338,6 @@ static const bool rate_control = false; // Rate control delta. Defines how much rate_control is allowed to adjust input rate. #if defined(__QNX__) static const float rate_control_delta = 0.000; -#elif defined(EMSCRIPTEN) -static const float rate_control_delta = 0.002; #else static const float rate_control_delta = 0.005; #endif diff --git a/emscripten/RWebAudio.c b/emscripten/RWebAudio.c index b384c828a8..1040da32d7 100644 --- a/emscripten/RWebAudio.c +++ b/emscripten/RWebAudio.c @@ -28,8 +28,8 @@ static void *ra_init(const char *device, unsigned rate, unsigned latency) (void)device; (void)rate; void *data = RWebAudioInit(latency); - g_settings.audio.out_rate = RWebAudioSampleRate(); - RARCH_LOG("audio out rate: %u\n", g_settings.audio.out_rate); + if (data) + g_settings.audio.out_rate = RWebAudioSampleRate(); return data; } diff --git a/emscripten/RWebAudio.h b/emscripten/RWebAudio.h index fd928cd3d6..6911753923 100644 --- a/emscripten/RWebAudio.h +++ b/emscripten/RWebAudio.h @@ -26,4 +26,3 @@ void RWebAudioSetNonblockState(bool state); void RWebAudioFree(void); size_t RWebAudioWriteAvail(void); size_t RWebAudioBufferSize(void); -int RWebAudioEnoughSpace(void); diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js index d2197ceccb..73448afbf9 100644 --- a/emscripten/library_rwebaudio.js +++ b/emscripten/library_rwebaudio.js @@ -1,160 +1,158 @@ //"use strict"; var LibraryRWebAudio = { - $RA__deps: ['$Browser'], - $RA: { - SCRIPTNODE_BUFFER: 1024, + $RA__deps: ['$Browser'], + $RA: { + BUFFER_SIZE: 256, - context: null, - leftBuffer: null, - rightBuffer: null, - blank: null, - scriptNode: null, - bufferNode: null, - start: 0, - end: 0, - size: 0, - lastWrite: 0, - nonblock: false, - - npot: function(n) { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; - }, + context: null, + buffers: [], + numBuffers: 0, + bufIndex: 0, + bufOffset: 0, + startTime: 0, + nonblock: false, - process: function(e) { - var left = e.outputBuffer.getChannelData(0); - var right = e.outputBuffer.getChannelData(1); - var samples1 = RA.size; - var samples2 = 0; - samples1 = e.outputBuffer.length > samples1 ? samples1 : e.outputBuffer.length; + setStartTime: function() { + if (RA.context.currentTime) { + RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000; + if (RA.startTime === 0) throw 'startTime is 0'; + Module["resumeMainLoop"](); + } else window['setTimeout'](RA.setStartTime, 0); + }, - if (samples1 + RA.start > RA.leftBuffer.length) { - samples2 = samples1 + RA.start - RA.leftBuffer.length; - samples1 = samples1 - samples2; + getCurrentPerfTime: function() { + if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; + else throw 'getCurrentPerfTime() called before start time set'; + }, + + process: function(queueBuffers) { + var currentTime = RA.getCurrentPerfTime(); + for (var i = 0; i < RA.bufIndex; i++) { + if (RA.buffers[i].endTime < currentTime) { + var buf = RA.buffers.splice(i, 1); + RA.buffers[RA.numBuffers - 1] = buf[0]; + i--; + RA.bufIndex--; + } + } + }, + + fillBuffer: function(buf, samples) { + var count = 0; + var leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0); + var rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1); + while (samples && RA.bufOffset !== RA.BUFFER_SIZE) { + leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}}; + rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}}; + RA.bufOffset++; + count++; + samples--; + } + + return count; + }, + + queueAudio: function() { + var index = RA.bufIndex; + + var startTime; + if (RA.bufIndex) startTime = RA.buffers[RA.bufIndex - 1].endTime; + else startTime = RA.context.currentTime; + RA.buffers[index].endTime = startTime + RA.buffers[index].duration; + + var bufferSource = RA.context.createBufferSource(); + bufferSource.buffer = RA.buffers[index]; + bufferSource.connect(RA.context.destination); + bufferSource.start(startTime); + + RA.bufIndex++; + RA.bufOffset = 0; + }, + + block: function() { + do { + RA.process(); + } while (RA.bufIndex === RA.numBuffers - 1); + } + }, + + RWebAudioInit: function(latency) { + var ac = window['AudioContext'] || window['webkitAudioContext']; + + if (!ac) return 0; + + RA.context = new ac(); + + RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0; + if (RA.numBuffers < 2) RA.numBuffers = 2; + + for (var i = 0; i < RA.numBuffers; i++) RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate); + + RA.nonblock = false; + RA.startTime = 0; + // chrome hack to get currentTime running + RA.context.createGain(); + window['setTimeout'](RA.setStartTime, 0); + Module["pauseMainLoop"](); + return 1; + }, + + RWebAudioSampleRate: function() { + return RA.context.sampleRate; + }, + + RWebAudioWrite: function (buf, size) { + RA.process(); + var samples = size / 8; + var count = 0; + + while (samples) { + var fill = RA.fillBuffer(buf, samples); + samples -= fill; + count += fill; + buf += fill * 8; + + if (RA.bufOffset === RA.BUFFER_SIZE) { + if (RA.bufIndex === RA.numBuffers - 1) { + if (RA.nonblock) break; + else RA.block(); + } + RA.queueAudio(); + } } - var remaining = e.outputBuffer.length - (samples1 + samples2); + return count * 8; + }, - if (samples1) { - left.set(RA.leftBuffer.subarray(RA.start, RA.start + samples1), 0); - right.set(RA.rightBuffer.subarray(RA.start, RA.start + samples1), 0); - } + RWebAudioStop: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return true; + }, - if (samples2) { - left.set(RA.leftBuffer.subarray(0, samples2), samples1); - right.set(RA.rightBuffer.subarray(0, samples2), samples1); - } + RWebAudioStart: function() { + return true; + }, - /*if (remaining) { - left.set(RA.blank.subarray(0, remaining), samples1 + samples2); - right.set(RA.blank.subarray(0, remaining), samples1 + samples2); - }*/ + RWebAudioSetNonblockState: function(state) { + RA.nonblock = state; + }, - RA.start = (RA.start + samples1 + samples2) % RA.leftBuffer.length; - RA.size -= samples1 + samples2; - } - }, - - RWebAudioSampleRate: function() { - return RA.context.sampleRate; - }, + RWebAudioFree: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return; + }, - RWebAudioInit: function(latency) { - var ac = window['AudioContext'] || window['webkitAudioContext']; - var bufferSize; + RWebAudioBufferSize: function() { + return RA.numBuffers * RA.BUFFER_SIZE + RA.BUFFER_SIZE; + }, - if (!ac) return 0; - - RA.context = new ac(); - // account for script processor overhead - latency -= 32; - // because we have to guess on how many samples the core will send when - // returning early, we double the buffer size to account for times when it - // sends more than we expect it to without losing samples - bufferSize = RA.npot(RA.context.sampleRate * latency / 1000) * 2; - RA.leftBuffer = new Float32Array(bufferSize); - RA.rightBuffer = new Float32Array(bufferSize); - RA.blank = new Float32Array(RA.SCRIPTNODE_BUFFER); - RA.bufferNode = RA.context.createBufferSource(); - RA.bufferNode.buffer = RA.context.createBuffer(2, RA.SCRIPTNODE_BUFFER, RA.context.sampleRate); - RA.bufferNode.loop = true; - RA.scriptNode = RA.context.createScriptProcessor(RA.SCRIPTNODE_BUFFER, 2, 2); - RA.scriptNode.onaudioprocess = RA.process; - RA.bufferNode.connect(RA.scriptNode); - RA.scriptNode.connect(RA.context.destination); - RA.bufferNode.start(0); - RA.start = RA.end = RA.size = 0; - RA.nonblock = false; - return 1; - }, - - RWebAudioWrite: function (buf, size) { - var samples = size / 8; - var free = RA.leftBuffer.length - RA.size; - if (free < samples) - RA.start = (RA.start + free) % RA.leftBuffer.length; - - for (var i = 0; i < samples; i++) { - RA.leftBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8', 'float') }}}; - RA.rightBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8 + 4', 'float') }}}; - RA.end = (RA.end + 1) % RA.leftBuffer.length; - } - - RA.lastWrite = size; - RA.size += samples; - return size; - }, - - RWebAudioStop: function() { - RA.scriptNode.onaudioprocess = null; - return true; - }, - - RWebAudioStart: function() { - RA.scriptNode.onaudioprocess = RA.process; - return true; - }, - - RWebAudioSetNonblockState: function(state) { - RA.nonblock = state; - }, - - RWebAudioFree: function() { - RA.scriptNode.onaudioprocess = null; - RA.start = RA.end = RA.size = RA.lastWrite = 0; - return; - }, - - RWebAudioWriteAvail: function() { - var free = (RA.leftBuffer.length / 2) - RA.size; - // 4 byte samples, 2 channels - free *= 8; - - if (free < 0) - return 0; - else - return free; - }, - - RWebAudioBufferSize: function() { - return RA.leftBuffer.length / 2; - }, - - RWebAudioEnoughSpace__deps: ['RWebAudioWriteAvail'], - RWebAudioEnoughSpace: function() { - var guess = RA.lastWrite; - var available = _RWebAudioWriteAvail(); - if (RA.nonblock) return true; - if (!guess) return true; - return (guess < available) ? 1 : 0; - } + RWebAudioWriteAvail: function() { + RA.process(); + return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8; + } }; autoAddDeps(LibraryRWebAudio, '$RA'); diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index 452c7f41ea..1941e49984 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -56,11 +56,6 @@ static void endloop(void) static void mainloop(void) { - if (!RWebAudioEnoughSpace()) - { - return; - } - if (g_extern.system.shutdown) { endloop(); From a09dda3a9da71a1f00e07dd1c9e1efbb1b381988 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Tue, 10 Sep 2013 20:21:48 -0400 Subject: [PATCH 14/16] [EMSCRIPTEN] add custom input driver, removes SDL dependency + adds mouse support --- Makefile.emscripten | 18 +-- emscripten/RWebAudio.c => audio/rwebaudio.c | 2 +- config.def.h | 3 + driver.c | 5 +- driver.h | 1 + emscripten/RWebInput.h | 29 ++++ emscripten/library_rwebinput.js | 115 +++++++++++++++ gfx/context/emscriptenegl_ctx.c | 12 +- input/input_common.c | 90 ++++++++++++ input/input_common.h | 1 + input/rwebinput_input.c | 149 ++++++++++++++++++++ settings.c | 2 + 12 files changed, 403 insertions(+), 24 deletions(-) rename emscripten/RWebAudio.c => audio/rwebaudio.c (98%) create mode 100644 emscripten/RWebInput.h create mode 100644 emscripten/library_rwebinput.js create mode 100644 input/rwebinput_input.c diff --git a/Makefile.emscripten b/Makefile.emscripten index 559638ab75..c1e913419c 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -15,6 +15,7 @@ OBJ = frontend/frontend_emscripten.o \ movie.o \ gfx/gfx_common.o \ input/input_common.o \ + input/rwebinput_input.o \ core_options.o \ patch.o \ compat/compat.o \ @@ -22,7 +23,7 @@ OBJ = frontend/frontend_emscripten.o \ screenshot.o \ cheats.o \ audio/utils.o \ - emscripten/RWebAudio.o \ + audio/rwebaudio.o \ input/overlay.o \ fifo_buffer.o \ gfx/scaler/scaler.o \ @@ -41,9 +42,7 @@ OBJ = frontend/frontend_emscripten.o \ HAVE_OPENGL = 1 HAVE_RGUI = 1 -HAVE_SDL = 1 -HAVE_SDL_IMAGE = 1 -HAVE_FREETYPE = 1 +HAVE_SDL = 0 HAVE_ZLIB = 1 HAVE_FBO = 1 WANT_MINIZ = 1 @@ -59,11 +58,7 @@ libretro = libretro_emscripten.bc LIBS = -lm DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" -LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js - -ifeq ($(SCALER_NO_SIMD), 1) - DEFINES += -DSCALER_NO_SIMD -endif +LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js --js-library emscripten/library_rwebinput.js ifeq ($(PERF_TEST), 1) DEFINES += -DPERF_TEST @@ -80,11 +75,6 @@ ifeq ($(HAVE_SDL), 1) DEFINES += -ISDL -DHAVE_SDL endif -ifeq ($(HAVE_THREADS), 1) - OBJ += autosave.o thread.o gfx/thread_wrapper.o - DEFINES += -DHAVE_THREADS -endif - ifeq ($(HAVE_OPENGL), 1) OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o gfx/glsym/rglgen.o gfx/glsym/glsym_es2.o DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL diff --git a/emscripten/RWebAudio.c b/audio/rwebaudio.c similarity index 98% rename from emscripten/RWebAudio.c rename to audio/rwebaudio.c index 1040da32d7..7139a88de4 100644 --- a/emscripten/RWebAudio.c +++ b/audio/rwebaudio.c @@ -16,7 +16,7 @@ #include "../driver.h" #include "../general.h" -#include "RWebAudio.h" +#include "../emscripten/RWebAudio.h" static void ra_free(void *data) { diff --git a/config.def.h b/config.def.h index 32d263a623..9e6903f4e4 100644 --- a/config.def.h +++ b/config.def.h @@ -78,6 +78,7 @@ enum INPUT_LINUXRAW, INPUT_APPLE, INPUT_QNX, + INPUT_RWEBINPUT, INPUT_NULL }; @@ -155,6 +156,8 @@ enum #define INPUT_DEFAULT_DRIVER INPUT_ANDROID #elif defined(_WIN32) #define INPUT_DEFAULT_DRIVER INPUT_DINPUT +#elif defined(EMSCRIPTEN) +#define INPUT_DEFAULT_DRIVER INPUT_RWEBINPUT #elif defined(HAVE_SDL) #define INPUT_DEFAULT_DRIVER INPUT_SDL #elif defined(__CELLOS_LV2__) diff --git a/driver.c b/driver.c index 51e8e6a577..cc5ea7f253 100644 --- a/driver.c +++ b/driver.c @@ -63,7 +63,7 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_JACK &audio_jack, #endif -#if defined(HAVE_SDL) && !defined(EMSCRIPTEN) +#ifdef HAVE_SDL &audio_sdl, #endif #ifdef HAVE_XAUDIO @@ -168,6 +168,9 @@ static const input_driver_t *input_drivers[] = { #ifdef __BLACKBERRY_QNX__ &input_qnx, #endif +#ifdef EMSCRIPTEN + &input_rwebinput, +#endif #ifdef HAVE_NULLINPUT &input_null, #endif diff --git a/driver.h b/driver.h index 91dd004005..8bf5cc4f6b 100644 --- a/driver.h +++ b/driver.h @@ -537,6 +537,7 @@ extern const input_driver_t input_xinput; extern const input_driver_t input_linuxraw; extern const input_driver_t input_apple; extern const input_driver_t input_qnx; +extern const input_driver_t input_rwebinput; extern const input_driver_t input_null; #include "driver_funcs.h" diff --git a/emscripten/RWebInput.h b/emscripten/RWebInput.h new file mode 100644 index 0000000000..103cc7a0cd --- /dev/null +++ b/emscripten/RWebInput.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +typedef struct rwebinput_state +{ + char keys[32]; + int mouse_x; + int mouse_y; + char mouse_l; + char mouse_r; +} rwebinput_state_t; + +int RWebInputInit(void); +rwebinput_state_t *RWebInputPoll(int context); +void RWebInputDestroy(int context); diff --git a/emscripten/library_rwebinput.js b/emscripten/library_rwebinput.js new file mode 100644 index 0000000000..3ca2fd9cd1 --- /dev/null +++ b/emscripten/library_rwebinput.js @@ -0,0 +1,115 @@ +//"use strict"; + +var LibraryRWebInput = { + $RI__deps: ['$Browser'], + $RI: { + temp: null, + contexts: [], + + eventHandler: function(event) { + var i; + switch (event.type) { + case 'mousemove': + var x = event['movementX'] || event['mozMovementX'] || event['webkitMovementX']; + var y = event['movementY'] || event['mozMovementY'] || event['webkitMovementY']; + for (i = 0; i < RI.contexts.length; i++) { + var oldX = {{{ makeGetValue('RI.contexts[i].state', '32', 'i32') }}}; + var oldY = {{{ makeGetValue('RI.contexts[i].state', '36', 'i32') }}}; + x += oldX; + y += oldY; + {{{ makeSetValue('RI.contexts[i].state', '32', 'x', 'i32') }}}; + {{{ makeSetValue('RI.contexts[i].state', '36', 'y', 'i32') }}}; + } + break; + case 'mouseup': + case 'mousedown': + var l, r; + + if (event.buttons & 1) l = 1; + else l = 0; + + if (event.buttons & 2) r = 1; + else r = 0; + + for (i = 0; i < RI.contexts.length; i++) { + {{{ makeSetValue('RI.contexts[i].state', '40', 'l', 'i8') }}}; + {{{ makeSetValue('RI.contexts[i].state', '41', 'r', 'i8') }}}; + } + break; + case 'click': + e.preventDefault(); + break; + case 'keyup': + case 'keydown': + var key = event.keyCode; + var offset = key >> 3; + var bit = 1 << (key & 7); + if (offset >= 32) throw 'key code error! bad code: ' + key; + for (i = 0; i < RI.contexts.length; i++) { + var value = {{{ makeGetValue('RI.contexts[i].state', 'offset', 'i8') }}}; + if (event.type === 'keyup') value &= ~bit; + else value |= bit; + {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; + } + event.preventDefault(); + break; + case 'blur': + case 'visibilitychange': + for (i = 0; i < RI.contexts.length; i++) { + _memset(RI.contexts[i].state, 0, 42); + } + break; + } + } + }, + + RWebInputInit: function(latency) { + if (RI.contexts.length === 0) { + document.addEventListener('keyup', RI.eventHandler, false); + document.addEventListener('keydown', RI.eventHandler, false); + document.addEventListener('mousemove', RI.eventHandler, false); + document.addEventListener('mouseup', RI.eventHandler, false); + document.addEventListener('mousedown', RI.eventHandler, false); + document.addEventListener('click', RI.eventHandler, false); + document.addEventListener('blur', RI.eventHandler, false); + document.addEventListener('onvisbilitychange', RI.eventHandler, false); + } + if (RI.temp === null) RI.temp = _malloc(42); + + var s = _malloc(42); + _memset(s, 0, 42); + RI.contexts.push({ + state: s + }); + return RI.contexts.length; + }, + + RWebInputPoll: function(context) { + context -= 1; + var state = RI.contexts[context].state; + _memcpy(RI.temp, state, 42); + // reset mouse movements + {{{ makeSetValue('RI.contexts[context].state', '32', '0', 'i32') }}}; + {{{ makeSetValue('RI.contexts[context].state', '36', '0', 'i32') }}}; + return RI.temp; + }, + + RWebInputDestroy: function (context) { + if (context === RI.contexts.length) { + RI.contexts.pop(); + if (RI.contexts.length === 0) { + document.removeEventListener('keyup', RI.eventHandler, false); + document.removeEventListener('keydown', RI.eventHandler, false); + document.removeEventListener('mousemove', RI.eventHandler, false); + document.removeEventListener('mouseup', RI.eventHandler, false); + document.removeEventListener('mousedown', RI.eventHandler, false); + document.removeEventListener('click', RI.eventHandler, false); + document.removeEventListener('blur', RI.eventHandler, false); + document.removeEventListener('onvisbilitychange', RI.eventHandler, false); + } + } + } +}; + +autoAddDeps(LibraryRWebInput, '$RI'); +mergeInto(LibraryManager.library, LibraryRWebInput); diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c index 363152c358..f9b89a42ca 100644 --- a/gfx/context/emscriptenegl_ctx.c +++ b/gfx/context/emscriptenegl_ctx.c @@ -31,7 +31,6 @@ #include #include #include -#include static EGLContext g_egl_ctx; static EGLSurface g_egl_surf; @@ -220,15 +219,12 @@ static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data { *input = NULL; - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) != 0) - return; + void *rwebinput = input_rwebinput.init(); - void *sdlinput = input_sdl.init(); - - if (sdlinput) + if (rwebinput) { - *input = &input_sdl; - *input_data = sdlinput; + *input = &input_rwebinput; + *input_data = rwebinput; } } diff --git a/input/input_common.c b/input/input_common.c index e58eede808..9993a99f13 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -513,6 +513,96 @@ const struct rarch_key_map rarch_key_map_dinput[] = { }; #endif +#ifdef EMSCRIPTEN +const struct rarch_key_map rarch_key_map_rwebinput[] = { + { 37, RETROK_LEFT }, + { 39, RETROK_RIGHT }, + { 38, RETROK_UP }, + { 40, RETROK_DOWN }, + { 13, RETROK_RETURN }, + { 9, RETROK_TAB }, + { 45, RETROK_INSERT }, + { 46, RETROK_DELETE }, + { 16, RETROK_RSHIFT }, + { 16, RETROK_LSHIFT }, + { 17, RETROK_LCTRL }, + { 35, RETROK_END }, + { 36, RETROK_HOME }, + { 34, RETROK_PAGEDOWN }, + { 33, RETROK_PAGEUP }, + { 18, RETROK_LALT }, + { 32, RETROK_SPACE }, + { 27, RETROK_ESCAPE }, + { 8, RETROK_BACKSPACE }, + { 13, RETROK_KP_ENTER }, + { 107, RETROK_KP_PLUS }, + { 109, RETROK_KP_MINUS }, + { 106, RETROK_KP_MULTIPLY }, + { 111, RETROK_KP_DIVIDE }, + { 192, RETROK_BACKQUOTE }, + { 19, RETROK_PAUSE }, + { 96, RETROK_KP0 }, + { 97, RETROK_KP1 }, + { 98, RETROK_KP2 }, + { 99, RETROK_KP3 }, + { 100, RETROK_KP4 }, + { 101, RETROK_KP5 }, + { 102, RETROK_KP6 }, + { 103, RETROK_KP7 }, + { 104, RETROK_KP8 }, + { 105, RETROK_KP9 }, + { 48, RETROK_0 }, + { 49, RETROK_1 }, + { 50, RETROK_2 }, + { 51, RETROK_3 }, + { 52, RETROK_4 }, + { 53, RETROK_5 }, + { 54, RETROK_6 }, + { 55, RETROK_7 }, + { 56, RETROK_8 }, + { 57, RETROK_9 }, + { 112, RETROK_F1 }, + { 113, RETROK_F2 }, + { 114, RETROK_F3 }, + { 115, RETROK_F4 }, + { 116, RETROK_F5 }, + { 117, RETROK_F6 }, + { 118, RETROK_F7 }, + { 119, RETROK_F8 }, + { 120, RETROK_F9 }, + { 121, RETROK_F10 }, + { 122, RETROK_F11 }, + { 123, RETROK_F12 }, + { 65, RETROK_a }, + { 66, RETROK_b }, + { 67, RETROK_c }, + { 68, RETROK_d }, + { 69, RETROK_e }, + { 70, RETROK_f }, + { 71, RETROK_g }, + { 72, RETROK_h }, + { 73, RETROK_i }, + { 74, RETROK_j }, + { 75, RETROK_k }, + { 76, RETROK_l }, + { 77, RETROK_m }, + { 78, RETROK_n }, + { 79, RETROK_o }, + { 80, RETROK_p }, + { 81, RETROK_q }, + { 82, RETROK_r }, + { 83, RETROK_s }, + { 84, RETROK_t }, + { 85, RETROK_u }, + { 86, RETROK_v }, + { 87, RETROK_w }, + { 88, RETROK_x }, + { 89, RETROK_y }, + { 90, RETROK_z }, + { 0, RETROK_UNKNOWN }, +}; +#endif + static enum retro_key rarch_keysym_lut[RETROK_LAST]; void input_init_keyboard_lut(const struct rarch_key_map *map) diff --git a/input/input_common.h b/input/input_common.h index 2ce912e98a..64b6447062 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -105,6 +105,7 @@ struct rarch_key_map extern const struct rarch_key_map rarch_key_map_x11[]; extern const struct rarch_key_map rarch_key_map_sdl[]; extern const struct rarch_key_map rarch_key_map_dinput[]; +extern const struct rarch_key_map rarch_key_map_rwebinput[]; void input_init_keyboard_lut(const struct rarch_key_map *map); enum retro_key input_translate_keysym_to_rk(unsigned sym); diff --git a/input/rwebinput_input.c b/input/rwebinput_input.c new file mode 100644 index 0000000000..1a65eec7d3 --- /dev/null +++ b/input/rwebinput_input.c @@ -0,0 +1,149 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "input_common.h" + +#include "../driver.h" + +#include "../boolean.h" +#include "../general.h" + +#include "../emscripten/RWebInput.h" + +static bool uninited = false; + +typedef struct rwebinput_input +{ + rwebinput_state_t state; + int context; +} rwebinput_input_t; + +static void *rwebinput_input_init(void) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); + if (!rwebinput) + return NULL; + + rwebinput->context = RWebInputInit(); + if (!rwebinput->context) + { + free(rwebinput); + return NULL; + } + + input_init_keyboard_lut(rarch_key_map_rwebinput); + + return rwebinput; +} + +static bool rwebinput_key_pressed(rwebinput_input_t *rwebinput, int key) +{ + if (key >= RETROK_LAST) + return false; + + unsigned sym = input_translate_rk_to_keysym((enum retro_key)key); + bool ret = rwebinput->state.keys[sym >> 3] & (1 << (sym & 7)); + return ret; +} + +static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, const struct retro_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + return bind->valid && rwebinput_key_pressed(rwebinput, binds[id].key); + } + else + return false; +} + +static bool rwebinput_bind_button_pressed(void *data, int key) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + return rwebinput_is_pressed(rwebinput, g_settings.input.binds[0], key); +} + +static int16_t rwebinput_mouse_state(rwebinput_input_t *rwebinput, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return (int16_t) rwebinput->state.mouse_x; + case RETRO_DEVICE_ID_MOUSE_Y: + return (int16_t) rwebinput->state.mouse_y; + case RETRO_DEVICE_ID_MOUSE_LEFT: + return rwebinput->state.mouse_l; + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return rwebinput->state.mouse_r; + default: + return 0; + } +} + +static int16_t rwebinput_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return rwebinput_is_pressed(rwebinput, binds[port], id); + + case RETRO_DEVICE_KEYBOARD: + return rwebinput_key_pressed(rwebinput, id); + + case RETRO_DEVICE_MOUSE: + return rwebinput_mouse_state(rwebinput, id); + + default: + return 0; + } +} + +static void rwebinput_input_free(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + uninited = true; + + RWebInputDestroy(rwebinput->context); + + free(data); +} + +static void rwebinput_input_poll(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + rwebinput_state_t *state = RWebInputPoll(rwebinput->context); + memcpy(&rwebinput->state, state, sizeof(rwebinput->state)); +} + +static void rwebinput_grab_mouse(void *data, bool state) +{ + (void)data; + (void)state; +} + +const input_driver_t input_rwebinput = { + rwebinput_input_init, + rwebinput_input_poll, + rwebinput_input_state, + rwebinput_bind_button_pressed, + rwebinput_input_free, + NULL, + "rwebinput", + rwebinput_grab_mouse, +}; + diff --git a/settings.c b/settings.c index 682774941a..4e4331bc63 100644 --- a/settings.c +++ b/settings.c @@ -139,6 +139,8 @@ const char *config_get_default_input(void) return "apple_input"; case INPUT_QNX: return "qnx_input"; + case INPUT_RWEBINPUT: + return "rwebinput"; case INPUT_NULL: return "null"; default: From b874f003ff4335fe13f26e12d5990646beaf7809 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Tue, 10 Sep 2013 22:02:54 -0400 Subject: [PATCH 15/16] [EMSCRIPTEN] undo SDL workarounds no longer needed --- input/input_common.c | 2 +- input/sdl_input.c | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/input/input_common.c b/input/input_common.c index 9993a99f13..cf632e1de9 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -48,7 +48,7 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #if defined(__linux) && !defined(ANDROID) &linuxraw_joypad, #endif -#if defined(HAVE_SDL) && !defined(EMSCRIPTEN) +#ifdef HAVE_SDL &sdl_joypad, #endif #endif diff --git a/input/sdl_input.c b/input/sdl_input.c index 57c9a32b91..d438c3f8a5 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -24,10 +24,6 @@ #include "../libretro.h" #include "input_common.h" -#ifdef EMSCRIPTEN -#define SDL_GetKeyState SDL_GetKeyboardState -#endif - typedef struct sdl_input { const rarch_joypad_driver_t *joypad; @@ -55,7 +51,7 @@ static bool sdl_key_pressed(int key) int sym = input_translate_rk_to_keysym((enum retro_key)key); - int num_keys = 0xFFFF; + int num_keys; Uint8 *keymap = SDL_GetKeyState(&num_keys); if (sym < 0 || sym >= num_keys) return false; @@ -220,14 +216,11 @@ static void sdl_input_free(void *data) static void sdl_poll_mouse(sdl_input_t *sdl) { - (void)sdl; -#ifndef EMSCRIPTEN Uint8 btn = SDL_GetRelativeMouseState(&sdl->mouse_x, &sdl->mouse_y); SDL_GetMouseState(&sdl->mouse_abs_x, &sdl->mouse_abs_y); sdl->mouse_l = SDL_BUTTON(SDL_BUTTON_LEFT) & btn ? 1 : 0; sdl->mouse_r = SDL_BUTTON(SDL_BUTTON_RIGHT) & btn ? 1 : 0; sdl->mouse_m = SDL_BUTTON(SDL_BUTTON_MIDDLE) & btn ? 1 : 0; -#endif } static void sdl_input_poll(void *data) From e6389f991914fa19a62322627c368204ee30fede Mon Sep 17 00:00:00 2001 From: ToadKing Date: Tue, 10 Sep 2013 22:48:24 -0400 Subject: [PATCH 16/16] [EMSCRIPTEN] chrome fixes --- emscripten/library_rwebaudio.js | 13 ++++++++++--- emscripten/library_rwebinput.js | 23 ++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js index 73448afbf9..4e7594ec45 100644 --- a/emscripten/library_rwebaudio.js +++ b/emscripten/library_rwebaudio.js @@ -1,7 +1,7 @@ //"use strict"; var LibraryRWebAudio = { - $RA__deps: ['$Browser'], + $RA__deps: ['$Browser', 'usleep'], $RA: { BUFFER_SIZE: 256, @@ -12,17 +12,24 @@ var LibraryRWebAudio = { bufOffset: 0, startTime: 0, nonblock: false, + currentTimeWorkaround: false, setStartTime: function() { if (RA.context.currentTime) { RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000; - if (RA.startTime === 0) throw 'startTime is 0'; + var time1 = RA.context.currentTime; + _usleep(50); + if (time1 === RA.context.currentTime) { + RA.currentTimeWorkaround = true; + if (RA.startTime === 0) throw 'startTime is 0'; + } Module["resumeMainLoop"](); } else window['setTimeout'](RA.setStartTime, 0); }, getCurrentPerfTime: function() { - if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; + if (!RA.currentTimeWorkaround) return RA.context.currentTime; + else if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; else throw 'getCurrentPerfTime() called before start time set'; }, diff --git a/emscripten/library_rwebinput.js b/emscripten/library_rwebinput.js index 3ca2fd9cd1..7f45a747d8 100644 --- a/emscripten/library_rwebinput.js +++ b/emscripten/library_rwebinput.js @@ -23,22 +23,17 @@ var LibraryRWebInput = { break; case 'mouseup': case 'mousedown': - var l, r; - - if (event.buttons & 1) l = 1; - else l = 0; - - if (event.buttons & 2) r = 1; - else r = 0; - + var value; + var offset; + if (event.button === 0) offset = 40; + else if (event.button === 2) offset = 41; + else break; + if (event.type === 'mouseup') value = 0; + else value = 1; for (i = 0; i < RI.contexts.length; i++) { - {{{ makeSetValue('RI.contexts[i].state', '40', 'l', 'i8') }}}; - {{{ makeSetValue('RI.contexts[i].state', '41', 'r', 'i8') }}}; + {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; } break; - case 'click': - e.preventDefault(); - break; case 'keyup': case 'keydown': var key = event.keyCode; @@ -70,7 +65,6 @@ var LibraryRWebInput = { document.addEventListener('mousemove', RI.eventHandler, false); document.addEventListener('mouseup', RI.eventHandler, false); document.addEventListener('mousedown', RI.eventHandler, false); - document.addEventListener('click', RI.eventHandler, false); document.addEventListener('blur', RI.eventHandler, false); document.addEventListener('onvisbilitychange', RI.eventHandler, false); } @@ -103,7 +97,6 @@ var LibraryRWebInput = { document.removeEventListener('mousemove', RI.eventHandler, false); document.removeEventListener('mouseup', RI.eventHandler, false); document.removeEventListener('mousedown', RI.eventHandler, false); - document.removeEventListener('click', RI.eventHandler, false); document.removeEventListener('blur', RI.eventHandler, false); document.removeEventListener('onvisbilitychange', RI.eventHandler, false); }