Merge pull request #6113 from ToadKing/more-emscripten-fixes

Emscripten fixes Part 2
This commit is contained in:
Twinaphex 2018-01-15 12:27:57 +01:00 committed by GitHub
commit 09c49aad11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 121 additions and 119 deletions

7
.gitignore vendored
View File

@ -132,3 +132,10 @@ obj-unix/
/pkg/msvc/msvc-2010/Release Cg/*.pdb /pkg/msvc/msvc-2010/Release Cg/*.pdb
retroarch.cfg retroarch.cfg
Makefile.local Makefile.local
# Emscripten artifacts
retroarch.js
retroarch.js.mem
*.bc
*.wasm

View File

@ -21,13 +21,15 @@ HAVE_SDL = 0
HAVE_SDL2 = 0 HAVE_SDL2 = 0
HAVE_ZLIB = 1 HAVE_ZLIB = 1
WANT_ZLIB = 1 WANT_ZLIB = 1
HAVE_SHADERPIPELINE = 1
HAVE_STATIC_VIDEO_FILTERS = 1 HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1 HAVE_STATIC_AUDIO_FILTERS = 1
MEMORY = 536870912 MEMORY = 536870912
# XXX: setting this to 1/2 currently crashes Firefox nightly
PRECISE_F32 = 2 PRECISE_F32 = 2
OBJDIR := obj-emscripten
ifneq ($(NATIVE_ZLIB),) ifneq ($(NATIVE_ZLIB),)
WANT_ZLIB = 0 WANT_ZLIB = 0
endif endif
@ -65,7 +67,7 @@ ifeq ($(DEBUG), 1)
LDFLAGS += -O0 -g LDFLAGS += -O0 -g
CFLAGS += -O0 -g CFLAGS += -O0 -g
else else
LDFLAGS += -O2 LDFLAGS += -O2 -s WASM=1
# WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake) # WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake)
LDFLAGS += -s PRECISE_F32=$(PRECISE_F32) LDFLAGS += -s PRECISE_F32=$(PRECISE_F32)
ifeq ($(LTO), 1) ifeq ($(LTO), 1)
@ -74,48 +76,29 @@ else
CFLAGS += -O2 CFLAGS += -O2
endif endif
CFLAGS += -DHAVE_RPNG -Wall -Wno-unused-result -Wno-unused-variable -I. -Ilibretro-common/include -std=gnu99 -s USE_ZLIB=1 \ CFLAGS += -DHAVE_RPNG -Wall -I. -Ilibretro-common/include -std=gnu99 -s USE_ZLIB=1 \
-s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_take_screenshot']" -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_take_screenshot']"
RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
all: $(TARGET) all: $(TARGET)
$(TARGET): $(OBJ) $(TARGET): $(RARCH_OBJ)
@$(if $(Q), $(shell echo echo LD $@),) @$(if $(Q), $(shell echo echo LD $@),)
$(Q)$(LD) -o $@ $(OBJ) $(libretro) $(LIBS) $(LDFLAGS) $(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro) $(LIBS) $(LDFLAGS)
%.o: %.c $(OBJDIR)/%.o: %.c
@mkdir -p $(dir $@)
@$(if $(Q), $(shell echo echo CC $<),) @$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $< $(Q)$(CC) $(CFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $<
%.o: %.cpp $(OBJDIR)/%.o: %.cpp
@mkdir -p $(dir $@)
@$(if $(Q), $(shell echo echo CXX $<),) @$(if $(Q), $(shell echo echo CXX $<),)
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $< $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $<
clean: clean:
rm -f *.o rm -rf $(OBJDIR)
rm -f deps/libz/*.o
rm -f frontend/*.o
rm -f menu/*.o
rm -f menu/disp/*.o
rm -f audio/*.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/d3d/*.o
rm -f gfx/drivers_context/*.o
rm -f gfx/math/*.o
rm -f gfx/drivers_font/*.o
rm -f gfx/drivers_font_renderer/*.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
rm -f libretro-common/*/*.o
rm -f $(TARGET) rm -f $(TARGET)
rm -f *.d
.PHONY: all clean .PHONY: all clean

View File

@ -224,6 +224,7 @@ enum joypad_driver_enum
JOYPAD_DOS, JOYPAD_DOS,
JOYPAD_HID, JOYPAD_HID,
JOYPAD_QNX, JOYPAD_QNX,
JOYPAD_RWEBPAD,
JOYPAD_NULL JOYPAD_NULL
}; };
@ -454,6 +455,8 @@ static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_DOS;
static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_HID; static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_HID;
#elif defined(__QNX__) #elif defined(__QNX__)
static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_QNX; static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_QNX;
#elif defined(EMSCRIPTEN)
static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_RWEBPAD;
#else #else
static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_NULL; static enum joypad_driver_enum JOYPAD_DEFAULT_DRIVER = JOYPAD_NULL;
#endif #endif
@ -783,9 +786,9 @@ const char *config_get_default_input(void)
case INPUT_COCOA: case INPUT_COCOA:
return "cocoa"; return "cocoa";
case INPUT_QNX: case INPUT_QNX:
return "qnx_input"; return "qnx_input";
case INPUT_RWEBINPUT: case INPUT_RWEBINPUT:
return "rwebinput"; return "rwebinput";
case INPUT_DOS: case INPUT_DOS:
return "dos"; return "dos";
case INPUT_NULL: case INPUT_NULL:
@ -846,6 +849,8 @@ const char *config_get_default_joypad(void)
return "hid"; return "hid";
case JOYPAD_QNX: case JOYPAD_QNX:
return "qnx"; return "qnx";
case JOYPAD_RWEBPAD:
return "rwebpad";
case JOYPAD_DOS: case JOYPAD_DOS:
return "dos"; return "dos";
case JOYPAD_NULL: case JOYPAD_NULL:

View File

@ -23,13 +23,14 @@ var LibraryRWebAudio = {
getCurrentPerfTime: function() { getCurrentPerfTime: function() {
if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000;
else throw 'getCurrentPerfTime() called before start time set'; else return 0;
}, },
process: function(queueBuffers) { process: function(queueBuffers) {
var currentTime = RA.getCurrentPerfTime(); var currentTime = RA.getCurrentPerfTime();
for (var i = 0; i < RA.bufIndex; i++) { for (var i = 0; i < RA.bufIndex; i++) {
if (RA.buffers[i].endTime < currentTime) { if (RA.buffers[i].endTime !== 0 && RA.buffers[i].endTime < currentTime) {
RA.buffers[i].endTime = 0;
var buf = RA.buffers.splice(i, 1); var buf = RA.buffers.splice(i, 1);
RA.buffers[RA.numBuffers - 1] = buf[0]; RA.buffers[RA.numBuffers - 1] = buf[0];
i--; i--;
@ -40,8 +41,8 @@ var LibraryRWebAudio = {
fillBuffer: function(buf, samples) { fillBuffer: function(buf, samples) {
var count = 0; var count = 0;
var leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0); const leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0);
var rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1); const rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1);
while (samples && RA.bufOffset !== RA.BUFFER_SIZE) { while (samples && RA.bufOffset !== RA.BUFFER_SIZE) {
leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}}; leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}};
rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}}; rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}};
@ -73,7 +74,7 @@ var LibraryRWebAudio = {
block: function() { block: function() {
do { do {
RA.process(); RA.process();
} while (RA.bufIndex === RA.numBuffers - 1); } while (RA.bufIndex === RA.numBuffers);
} }
}, },
@ -87,7 +88,10 @@ var LibraryRWebAudio = {
RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0; RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0;
if (RA.numBuffers < 2) RA.numBuffers = 2; 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); for (var i = 0; i < RA.numBuffers; i++) {
RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate);
RA.buffers[i].endTime = 0
}
RA.nonblock = false; RA.nonblock = false;
RA.startTime = 0; RA.startTime = 0;
@ -97,7 +101,7 @@ var LibraryRWebAudio = {
Module["pauseMainLoop"](); Module["pauseMainLoop"]();
return 1; return 1;
}, },
RWebAudioSampleRate: function() { RWebAudioSampleRate: function() {
return RA.context.sampleRate; return RA.context.sampleRate;
}, },
@ -108,16 +112,17 @@ var LibraryRWebAudio = {
var count = 0; var count = 0;
while (samples) { while (samples) {
if (RA.bufIndex === RA.numBuffers) {
if (RA.nonblock) break;
else RA.block();
}
var fill = RA.fillBuffer(buf, samples); var fill = RA.fillBuffer(buf, samples);
samples -= fill; samples -= fill;
count += fill; count += fill;
buf += fill * 8; buf += fill * 8;
if (RA.bufOffset === RA.BUFFER_SIZE) { if (RA.bufOffset === RA.BUFFER_SIZE) {
if (RA.bufIndex === RA.numBuffers - 1) {
if (RA.nonblock) break;
else RA.block();
}
RA.queueAudio(); RA.queueAudio();
} }
} }
@ -142,16 +147,21 @@ var LibraryRWebAudio = {
RWebAudioFree: function() { RWebAudioFree: function() {
RA.bufIndex = 0; RA.bufIndex = 0;
RA.bufOffset = 0; RA.bufOffset = 0;
return;
}, },
RWebAudioBufferSize: function() { RWebAudioBufferSize: function() {
return RA.numBuffers * RA.BUFFER_SIZE + RA.BUFFER_SIZE; return RA.numBuffers * RA.BUFFER_SIZE * 8;
}, },
RWebAudioWriteAvail: function() { RWebAudioWriteAvail: function() {
RA.process(); RA.process();
return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8; return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8;
},
RWebAudioRecalibrateTime: function() {
if (RA.startTime) {
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
}
} }
}; };

View File

@ -39,14 +39,42 @@
#include "../../defaults.h" #include "../../defaults.h"
#include "../../content.h" #include "../../content.h"
#include "../../retroarch.h" #include "../../retroarch.h"
#include "../../verbosity.h"
#include "../../command.h" #include "../../command.h"
#include "../../tasks/tasks_internal.h" #include "../../tasks/tasks_internal.h"
#include "../../file_path_special.h" #include "../../file_path_special.h"
void RWebAudioRecalibrateTime(void);
static unsigned emscripten_fullscreen_reinit;
static EM_BOOL emscripten_fullscreenchange_cb(int event_type,
const EmscriptenFullscreenChangeEvent *fullscreen_change_event,
void *user_data)
{
(void)event_type;
(void)fullscreen_change_event;
(void)user_data;
emscripten_fullscreen_reinit = 5;
return EM_TRUE;
}
static void emscripten_mainloop(void) static void emscripten_mainloop(void)
{ {
unsigned sleep_ms = 0; unsigned sleep_ms = 0;
int ret = runloop_iterate(&sleep_ms); int ret;
RWebAudioRecalibrateTime();
if (emscripten_fullscreen_reinit != 0)
{
if (--emscripten_fullscreen_reinit == 0)
command_event(CMD_EVENT_REINIT, NULL);
}
ret = runloop_iterate(&sleep_ms);
if (ret == 1 && sleep_ms > 0) if (ret == 1 && sleep_ms > 0)
retro_sleep(sleep_ms); retro_sleep(sleep_ms);
@ -162,19 +190,24 @@ static void frontend_emscripten_get_env(int *argc, char *argv[],
if (!string_is_empty(dir_path)) if (!string_is_empty(dir_path))
path_mkdir(dir_path); path_mkdir(dir_path);
} }
snprintf(g_defaults.settings.menu, sizeof(g_defaults.settings.menu), "rgui");
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
settings_t *settings = config_get_ptr(); EMSCRIPTEN_RESULT r;
emscripten_set_canvas_element_size("#canvas", 800, 600); emscripten_set_canvas_element_size("#canvas", 800, 600);
emscripten_set_element_css_size("#canvas", 800.0, 600.0); emscripten_set_element_css_size("#canvas", 800.0, 600.0);
emscripten_set_main_loop(emscripten_mainloop, 0, 0);
rarch_main(argc, argv, NULL); rarch_main(argc, argv, NULL);
emscripten_set_main_loop(emscripten_mainloop,
settings->bools.video_vsync ? 0 : INT_MAX, 1); r = emscripten_set_fullscreenchange_callback("#document", NULL, false,
emscripten_fullscreenchange_cb);
if (r != EMSCRIPTEN_RESULT_SUCCESS)
{
RARCH_ERR(
"[EMSCRIPTEN/CTX] failed to create fullscreen callback: %d\n", r);
}
return 0; return 0;
} }

View File

@ -51,8 +51,11 @@ static int emscripten_initial_height;
static void gfx_ctx_emscripten_swap_interval(void *data, unsigned interval) static void gfx_ctx_emscripten_swap_interval(void *data, unsigned interval)
{ {
(void)data; (void)data;
/* no way to control VSync in WebGL. */
(void)interval; if (interval == 0)
emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0);
else
emscripten_set_main_loop_timing(EM_TIMING_RAF, (int)interval);
} }
static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height)
@ -138,8 +141,13 @@ static void gfx_ctx_emscripten_check_window(void *data, bool *quit,
static void gfx_ctx_emscripten_swap_buffers(void *data, void *data2) static void gfx_ctx_emscripten_swap_buffers(void *data, void *data2)
{ {
(void)data; emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
/* no-op in emscripten, no way to force swap/wait for VSync in browsers */
/* doesn't really do anything in WebGL, but it might if we use WebGL workers
* in the future */
#ifdef HAVE_EGL
egl_swap_buffers(&emscripten->egl);
#endif
} }
static void gfx_ctx_emscripten_get_video_size(void *data, static void gfx_ctx_emscripten_get_video_size(void *data,

View File

@ -567,7 +567,6 @@ static void rwebinput_input_free(void *data)
static void rwebinput_process_keyboard_events(rwebinput_input_t *rwebinput, static void rwebinput_process_keyboard_events(rwebinput_input_t *rwebinput,
rwebinput_keyboard_event_t *event) rwebinput_keyboard_event_t *event)
{ {
uint32_t crc;
uint32_t keycode; uint32_t keycode;
unsigned translated_keycode; unsigned translated_keycode;
uint32_t character = 0; uint32_t character = 0;

View File

@ -86,7 +86,7 @@
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span> <span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
</button> </button>
<input class="btn btn-primary disabled" style="display: none" type="file" id="btnRom" name="upload" onclick="document.getElementById('btnAdd').click();" onchange="selectFiles(event.target.files)" multiple /> <input class="btn btn-primary disabled" style="display: none" type="file" id="btnRom" name="upload" onclick="document.getElementById('btnAdd').click();" onchange="selectFiles(event.target.files)" multiple />
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" onclick="keyPress(112);" title="Menu toggle" disabled> <button class="btn btn-primary disabled tooltip-enable" id="btnMenu" onclick="keyPress('F1');" title="Menu toggle" disabled>
<span class="fa fa-bars" id="btnMenu"></span> <span class="sr-only">Menu</span> <span class="fa fa-bars" id="btnMenu"></span> <span class="sr-only">Menu</span>
</button> </button>
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" onclick="Module.requestFullScreen()" title="Fullscreen" disabled> <button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" onclick="Module.requestFullScreen()" title="Fullscreen" disabled>
@ -105,27 +105,14 @@
</div> </div>
</nav> </nav>
<div class="bg-inverse webplayer-container"> <div class="bg-inverse webplayer-container">
<div class="container"> <div class="webplayer_border text-xs-center" id="canvas_div">
<div class="webplayer_border text-xs-center" id="canvas_div"> <div class="showMenu">
<div class="showMenu"> <button type="button" class="btn btn-link">
<button type="button" class="btn btn-link"> <span class="fa fa-chevron-down" id="icnShowMenu"></span> <span class="sr-only">Show Top Navigation</span>
<span class="fa fa-chevron-down" id="icnShowMenu"></span> <span class="sr-only">Show Top Navigation</span> </button>
</button> </div>
</div> <canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas> <img class="webplayer-preview img-fluid" src="media/canvas.png" width="960px" height="720px" alt="RetroArch Logo">
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960px" height="720px" alt="RetroArch Logo">
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-sm-12 form-group btn-group text-xs-center p-t-2">
<div class="card">
<div class="view overlay hm-white-slight" align="center">
<textarea id="output" rows="15"></textarea>
</div>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -112,3 +112,8 @@ textarea {
#icnShowMenu { #icnShowMenu {
color: #565656 !important; color: #565656 !important;
} }
/* fix weird white bar in chrome when fullscreen */
:-webkit-full-screen {
height: 100%;
}

View File

@ -184,24 +184,13 @@ var Module =
arguments: ["-v", "--menu"], arguments: ["-v", "--menu"],
preRun: [], preRun: [],
postRun: [], postRun: [],
print: (function() print: function(text)
{ {
var element = document.getElementById('output'); console.log(text);
element.value = ''; // clear browser cache },
return function(text)
{
text = Array.prototype.slice.call(arguments).join(' ');
element.value += text + "\n";
element.scrollTop = 99999; // focus on bottom
};
})(),
printErr: function(text) printErr: function(text)
{ {
var text = Array.prototype.slice.call(arguments).join(' '); console.log(text);
var element = document.getElementById('output');
element.value += text + "\n";
element.scrollTop = 99999; // focus on bottom
}, },
canvas: document.getElementById('canvas'), canvas: document.getElementById('canvas'),
totalDependencies: 0, totalDependencies: 0,
@ -306,31 +295,7 @@ function keyPress(k)
} }
kp = function(k, event) { kp = function(k, event) {
var oEvent = document.createEvent('KeyboardEvent'); var oEvent = new KeyboardEvent(event, { code: k });
// Chromium Hack
Object.defineProperty(oEvent, 'keyCode', {
get : function() {
return this.keyCodeVal;
}
});
Object.defineProperty(oEvent, 'which', {
get : function() {
return this.keyCodeVal;
}
});
if (oEvent.initKeyboardEvent) {
oEvent.initKeyboardEvent(event, true, true, document.defaultView, false, false, false, false, k, k);
} else {
oEvent.initKeyEvent(event, true, true, document.defaultView, false, false, false, false, k, 0);
}
oEvent.keyCodeVal = k;
if (oEvent.keyCode !== k) {
alert("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ")");
}
document.dispatchEvent(oEvent); document.dispatchEvent(oEvent);
document.getElementById('canvas').focus(); document.getElementById('canvas').focus();