diff --git a/Common/CommonFuncs.h b/Common/CommonFuncs.h index d3916a8ad836..9e2381a1f218 100644 --- a/Common/CommonFuncs.h +++ b/Common/CommonFuncs.h @@ -71,7 +71,7 @@ #if defined(_MSC_VER) #include -#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) +#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) #include #endif @@ -90,7 +90,7 @@ inline u32 __rotl(u32 x, int shift) { #if defined(_MSC_VER) return _rotl(x, shift); -#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) +#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) return __rold(x, shift); #else shift &= 31; @@ -113,7 +113,7 @@ inline u64 __rotl64(u64 x, unsigned int shift){ inline u32 __rotr(u32 x, int shift) { #if defined(_MSC_VER) return _rotr(x, shift); -#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) +#elif (PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)) && !defined(__EMSCRIPTEN__) return __rord(x, shift); #else shift &= 31; diff --git a/Common/GPU/OpenGL/GLCommon.h b/Common/GPU/OpenGL/GLCommon.h index 26f9220d7f57..38b9feb19770 100644 --- a/Common/GPU/OpenGL/GLCommon.h +++ b/Common/GPU/OpenGL/GLCommon.h @@ -5,6 +5,10 @@ #if PPSSPP_PLATFORM(IOS) #include #include +#elif defined(__EMSCRIPTEN__) +#include +#include +#define GL_BGRA_EXT 0x80E1 #elif defined(USING_GLES2) #include #include diff --git a/Common/GPU/OpenGL/GLMemory.cpp b/Common/GPU/OpenGL/GLMemory.cpp index 5bbb40e9b5d9..19ea197464c1 100644 --- a/Common/GPU/OpenGL/GLMemory.cpp +++ b/Common/GPU/OpenGL/GLMemory.cpp @@ -32,7 +32,7 @@ void *GLRBuffer::Map(GLBufferStrategy strategy) { if (!hasStorage_) { GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); #ifdef USING_GLES2 -#ifdef GL_EXT_buffer_storage +#if defined(GL_EXT_buffer_storage) && !defined(__EMSCRIPTEN__) glBufferStorageEXT(target_, size_, nullptr, storageFlags); #endif #else diff --git a/Common/GPU/OpenGL/GLProfiler.cpp b/Common/GPU/OpenGL/GLProfiler.cpp index 27a2a695d5b2..5eb135edceff 100644 --- a/Common/GPU/OpenGL/GLProfiler.cpp +++ b/Common/GPU/OpenGL/GLProfiler.cpp @@ -121,8 +121,10 @@ void GLProfiler::BeginFrame() { } GLuint64 startTime = 0, endTime = 0; +#ifndef __EMSCRIPTEN__ glGetQueryObjectui64v(queries_[scope.startQueryId], GL_QUERY_RESULT, &startTime); glGetQueryObjectui64v(queries_[scope.endQueryId], GL_QUERY_RESULT, &endTime); +#endif // Times are in nanoseconds, convert to milliseconds double milliseconds = (double)(endTime - startTime) / 1000000.0; @@ -154,7 +156,9 @@ void GLProfiler::Begin(const char *fmt, ...) { scopeStack_.push_back(scopes_.size()); scopes_.push_back(scope); +#ifndef __EMSCRIPTEN__ glQueryCounter(queries_[numQueries_], GL_TIMESTAMP); +#endif numQueries_++; } @@ -174,6 +178,8 @@ void GLProfiler::End() { GLProfilerScope &scope = scopes_[scopeId]; scope.endQueryId = numQueries_; +#ifndef __EMSCRIPTEN__ glQueryCounter(queries_[numQueries_], GL_TIMESTAMP); +#endif numQueries_++; } diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index f2f9ba914f27..1ab32afcc7d0 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -214,7 +214,7 @@ void GLQueueRunner::RunInitSteps(const FastVec &steps, bool skipGLC } else if (gl_extensions.VersionGEThan(3, 0, 0)) { glBindFragDataLocation(program->program, 0, "fragColor0"); } -#elif !PPSSPP_PLATFORM(IOS) +#elif !PPSSPP_PLATFORM(IOS) && !defined(__EMSCRIPTEN__) if (gl_extensions.GLES3 && step.create_program.support_dual_source) { // For GLES2, we use gl_SecondaryFragColorEXT as fragColor1. _dbg_assert_msg_(gl_extensions.EXT_blend_func_extended, "EXT_blend_func_extended required for dual src"); @@ -1477,7 +1477,7 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { _assert_msg_(caps_.framebufferCopySupported, "Image copy extension expected"); #if defined(USING_GLES2) -#if !PPSSPP_PLATFORM(IOS) +#if !PPSSPP_PLATFORM(IOS) && !defined(__EMSCRIPTEN__) glCopyImageSubDataOES( srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ, diff --git a/Common/GPU/OpenGL/gl3stub.c b/Common/GPU/OpenGL/gl3stub.c index 9f47314c35d3..7c32d097e2a4 100644 --- a/Common/GPU/OpenGL/gl3stub.c +++ b/Common/GPU/OpenGL/gl3stub.c @@ -19,7 +19,7 @@ #include "Common/GPU/OpenGL/GLCommon.h" #if defined(USING_GLES2) -#if !PPSSPP_PLATFORM(IOS) +#if !PPSSPP_PLATFORM(IOS) && !defined(__EMSCRIPTEN__) #include "EGL/egl.h" GLboolean gl3stubInit() { diff --git a/Common/GPU/OpenGL/gl3stub.h b/Common/GPU/OpenGL/gl3stub.h index 70f0217f7753..26587a53e664 100644 --- a/Common/GPU/OpenGL/gl3stub.h +++ b/Common/GPU/OpenGL/gl3stub.h @@ -47,8 +47,8 @@ extern "C" { * otherwise. */ GLboolean gl3stubInit(); -#if !PPSSPP_PLATFORM(IOS) - +#if !PPSSPP_PLATFORM(IOS) && !defined(__EMSCRIPTEN__) + /*------------------------------------------------------------------------- * Data type definitions *-----------------------------------------------------------------------*/ diff --git a/Common/MemArenaPosix.cpp b/Common/MemArenaPosix.cpp index a413f4eaf2a4..ed26b8a727f4 100644 --- a/Common/MemArenaPosix.cpp +++ b/Common/MemArenaPosix.cpp @@ -132,6 +132,8 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) void MemArena::ReleaseView(s64 offset, void* view, size_t size) { #ifndef NO_MMAP munmap(view, size); +#else + free(view); #endif } diff --git a/Common/TimeUtil.cpp b/Common/TimeUtil.cpp index 344990ae2bf5..f22da563bbba 100644 --- a/Common/TimeUtil.cpp +++ b/Common/TimeUtil.cpp @@ -13,6 +13,7 @@ #ifdef __EMSCRIPTEN__ #include +#include #endif // __EMSCRIPTEN__ #ifdef _WIN32 @@ -267,7 +268,7 @@ void sleep_ms(int ms, const char *reason) { #elif defined(HAVE_LIBNX) svcSleepThread(ms * 1000000); #elif defined(__EMSCRIPTEN__) - emscripten_sleep(ms); + emscripten_thread_sleep(ms); #else usleep(ms * 1000); #endif @@ -285,7 +286,7 @@ void sleep_us(int us, const char *reason) { #elif defined(HAVE_LIBNX) svcSleepThread(us * 1000); #elif defined(__EMSCRIPTEN__) - emscripten_sleep(us / 1000); + emscripten_thread_sleep(us / 1000); #else usleep(us); #endif @@ -342,7 +343,10 @@ void sleep_precise(double seconds, const char *reason) { #elif defined(HAVE_LIBNX) svcSleepThread((int64_t)(seconds * 1000000000.0)); #elif defined(__EMSCRIPTEN__) - emscripten_sleep(seconds * 1000.0); + if (emscripten_is_main_browser_thread()) + emscripten_sleep((unsigned int)(seconds * 1000.0)); + else + emscripten_thread_sleep(seconds * 1000.0); #else usleep(seconds * 1000000.0); #endif diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index de9970a8455f..00940ced7dee 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -32,7 +32,10 @@ #include "Core/FileSystems/BlockDevices.h" #include "Core/FileSystems/ISOFileSystem.h" #include "Core/Util/PathUtil.h" + +#ifndef __EMSCRIPTEN__ #include "libchdr/chd.h" +#endif extern "C" { @@ -946,6 +949,10 @@ bool NPDRMDemoBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached) return true; } +#ifndef __EMSCRIPTEN__ + +// static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; + struct CHDImpl { chd_file *chd = nullptr; const chd_header *header = nullptr; @@ -1115,3 +1122,5 @@ bool CHDFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) { } return true; } + +#endif //ifndef __EMSCRIPTEN__ diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 9cc9bc7af6ad..a90555dcdff3 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -455,12 +455,17 @@ static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep, // Wait until we've caught up. // If we're ending the frame here, we'll defer the sleep until after the command buffers // have been handed off to the render thread, for some more overlap. +#ifdef __EMSCRIPTEN__ + WaitUntil(curFrameTime, nextFrameTime, "display-wait"); + curFrameTime = time_now_d(); +#else if (endOfFrame) { g_frameTiming.DeferWaitUntil(nextFrameTime, &curFrameTime); } else { WaitUntil(curFrameTime, nextFrameTime, "display-wait"); curFrameTime = time_now_d(); // I guess we could also just set it to nextFrameTime... } +#endif } } diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index ac8bd0871874..139bc9d67cb3 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -116,7 +116,7 @@ void IRApplyRounding(MIPSState *mips) { if (fcr1Bits) { int rmode = fcr1Bits & 3; bool ftz = (fcr1Bits & 0x01000000) != 0; -#if PPSSPP_ARCH(SSE2) +#if PPSSPP_ARCH(SSE2) && !defined(__EMSCRIPTEN__) u32 csr = _mm_getcsr() & ~0x6000; // Translate the rounding mode bits to X86, the same way as in Asm.cpp. if (rmode & 1) { @@ -147,7 +147,7 @@ void IRApplyRounding(MIPSState *mips) { } void IRRestoreRounding() { -#if PPSSPP_ARCH(SSE2) +#if PPSSPP_ARCH(SSE2) && !defined(__EMSCRIPTEN__) // TODO: We should avoid this if we didn't apply rounding in the first place. // In the meantime, clear out FTZ and rounding mode bits. u32 csr = _mm_getcsr(); diff --git a/libretro/LibretroGLContext.h b/libretro/LibretroGLContext.h index f02d84792822..7f3d97eb7022 100644 --- a/libretro/LibretroGLContext.h +++ b/libretro/LibretroGLContext.h @@ -7,7 +7,9 @@ class LibretroGLContext : public LibretroHWRenderContext { public: LibretroGLContext() -#ifdef USING_GLES2 +#ifdef __EMSCRIPTEN__ + : LibretroHWRenderContext(RETRO_HW_CONTEXT_OPENGLES3, 3, 0) +#elif defined(USING_GLES2) : LibretroHWRenderContext(RETRO_HW_CONTEXT_OPENGLES2) #else : LibretroHWRenderContext(RETRO_HW_CONTEXT_OPENGL) diff --git a/libretro/Makefile b/libretro/Makefile index de50cbd8c8a4..57fa8f335d15 100644 --- a/libretro/Makefile +++ b/libretro/Makefile @@ -304,9 +304,15 @@ else ifeq ($(platform), emscripten) GLES = 1 TARGET := $(TARGET_NAME)_libretro_emscripten.bc PLATCFLAGS += -msimd128 -msse3 -mssse3 -msse4.1 -pthread -D__EMSCRIPTEN__ -DNO_MMAP + PLATCFLAGS += -O3 PLATFORM_EXT = emscripten STATIC_LINKING = 1 + FFMPEGINCFLAGS += -I$(FFMPEGDIR)/linux/$(TARGET_ARCH)/include + FFMPEGLIBDIR := $(FFMPEGDIR)/linux/$(TARGET_ARCH)/lib + FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale + + # Windows MSVC all architectures else ifneq (,$(findstring windows_msvc2019,$(platform))) @@ -424,11 +430,7 @@ else GLFLAGS += -DHAVE_OPENGL endif -COREFLAGS += -D__LIBRETRO__ -DPPSSPP -DWITH_UPNP -DGLEW_STATIC -DGLEW_NO_GLU -DMINIUPNP_STATICLIB -ifneq ($(platform), emscripten) - # ffmpeg does not work with emscripten - COREFLAGS += -DUSE_FFMPEG -endif +COREFLAGS += -D__LIBRETRO__ -DPPSSPP -DWITH_UPNP -DGLEW_STATIC -DGLEW_NO_GLU -DMINIUPNP_STATICLIB -DUSE_FFMPEG ifeq ($(DEBUG), 1) ifneq (,$(findstring msvc,$(platform))) @@ -438,8 +440,6 @@ ifeq ($(DEBUG), 1) CPUOPTS += -O0 -g endif CPUOPTS += -D_DEBUG -else ifeq ($(platform), emscripten) - CPUOPTS += -O3 -DNDEBUG else CPUOPTS += -O2 -DNDEBUG endif diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 6bdb4effac48..338eea79eff8 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -270,6 +270,8 @@ SOURCES_C += \ COREFLAGS += -DSTACK_LINE_READER_BUFFER_SIZE=1024 COREFLAGS += -DHTTPS_NOT_AVAILABLE +ifneq ($(platform), emscripten) + COREFLAGS += -DZ7_ST INCFLAGS += -I$(EXTDIR)/libchdr/deps/lzma-24.05/include @@ -295,6 +297,8 @@ SOURCES_C += \ $(EXTDIR)/libchdr/src/libchdr_flac.c \ $(EXTDIR)/libchdr/src/libchdr_huffman.c +endif + INCFLAGS += -I$(EXTDIR)/at3_standalone SOURCES_CXX += \ diff --git a/libretro/emscripten/build_emscripten.sh b/libretro/emscripten/build_emscripten.sh new file mode 100755 index 000000000000..cfc2eb2cc0af --- /dev/null +++ b/libretro/emscripten/build_emscripten.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +cd "$(dirname "$0")" +cd ../../ + +git submodule update --init --recursive + +cd ffmpeg +cp ../libretro/emscripten/linux_wasm32.sh ./ +bash linux_wasm32.sh +cd ../ + +sed -i 's|return getauxval(hwcap_type);|return 1;|g' ext/cpu_features/src/hwcaps.c +sed -i 's|#include ||g' ext/cpu_features/src/hwcaps.c + +sed -i 's|#ifndef __linux__|#ifdef SO_NOSIGPIPE|g' ext/aemu_postoffice/client/sock_impl_linux.c + +cd ext/armips/ext/ +rm -rf filesystem +git clone https://github.com/gulrak/filesystem.git filesystem + +cd ../../../libretro/ +emmake make platform=emscripten -j$(nproc) + +mkdir -p emscripten/build/ +cp *.bc emscripten/build/ +cp ../ffmpeg/linux/wasm32/lib/*.a emscripten/build/ diff --git a/libretro/emscripten/linux_wasm32.sh b/libretro/emscripten/linux_wasm32.sh new file mode 100755 index 000000000000..1ba3cd2acac4 --- /dev/null +++ b/libretro/emscripten/linux_wasm32.sh @@ -0,0 +1,117 @@ +#!/bin/sh + +rm -f config.h +echo "Building for Linux wasm32" + +set -e + +ARCH="wasm32" + +GENERAL=" + --disable-shared \ + --enable-static" + +MODULES="\ + --disable-avdevice \ + --disable-filters \ + --disable-programs \ + --disable-network \ + --disable-avfilter \ + --disable-postproc \ + --disable-encoders \ + --disable-doc \ + --disable-ffplay \ + --disable-ffprobe \ + --disable-ffserver \ + --disable-ffmpeg" + +VIDEO_DECODERS="\ + --enable-decoder=h264 \ + --enable-decoder=mpeg4 \ + --enable-decoder=h263 \ + --enable-decoder=h263p \ + --enable-decoder=mpeg2video \ + --enable-decoder=mjpeg \ + --enable-decoder=mjpegb" + +AUDIO_DECODERS="\ + --enable-decoder=aac \ + --enable-decoder=aac_latm \ + --enable-decoder=atrac3 \ + --enable-decoder=atrac3p \ + --enable-decoder=mp3 \ + --enable-decoder=pcm_s16le \ + --enable-decoder=pcm_s8" + +DEMUXERS="\ + --enable-demuxer=h264 \ + --enable-demuxer=h263 \ + --enable-demuxer=m4v \ + --enable-demuxer=mpegps \ + --enable-demuxer=mpegvideo \ + --enable-demuxer=avi \ + --enable-demuxer=mp3 \ + --enable-demuxer=aac \ + --enable-demuxer=pmp \ + --enable-demuxer=oma \ + --enable-demuxer=pcm_s16le \ + --enable-demuxer=pcm_s8 \ + --enable-demuxer=wav" + +VIDEO_ENCODERS="\ + --enable-encoder=ffv1 \ + --enable-encoder=huffyuv \ + --enable-encoder=mpeg4" + +AUDIO_ENCODERS="\ + --enable-encoder=pcm_s16le" + +MUXERS="\ + --enable-muxer=avi" + +PARSERS="\ + --enable-parser=h264 \ + --enable-parser=mpeg4video \ + --enable-parser=mpegvideo \ + --enable-parser=aac \ + --enable-parser=aac_latm \ + --enable-parser=mpegaudio" + +PROTOCOLS="\ + --enable-protocol=file" + +emconfigure ./configure \ + --prefix=./linux/${ARCH} \ + ${GENERAL} \ + --extra-cflags="-pthread -O3" \ + --enable-zlib \ + --disable-yasm \ + --disable-everything \ + ${MODULES} \ + ${VIDEO_DECODERS} \ + ${AUDIO_DECODERS} \ + ${VIDEO_ENCODERS} \ + ${AUDIO_ENCODERS} \ + ${DEMUXERS} \ + ${MUXERS} \ + ${PARSERS} \ + ${PROTOCOLS} \ + --target-os=none \ + --arch=x86_32 \ + --enable-cross-compile \ + --disable-asm \ + --disable-stripping \ + --disable-programs \ + --disable-doc \ + --disable-debug \ + --disable-runtime-cpudetect \ + --nm=emnm \ + --ar=emar \ + --ranlib=emranlib \ + --cc=emcc \ + --cxx=em++ \ + --objcc=emcc \ + --dep-cc=emcc + +emmake make clean +emmake make install -j$(nproc) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 3b2f10317ec1..f5528ea71351 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1,4 +1,5 @@ #include "ppsspp_config.h" +#include #include #include #include @@ -477,12 +478,39 @@ static std::string map_psp_language_to_i18n_locale(int val) } static void check_dynamic_variables(CoreParameter &coreParam) { +#ifdef __EMSCRIPTEN__ + struct retro_throttle_state throttle = { RETRO_THROTTLE_NONE, 0.0f }; + if (environ_cb(RETRO_ENVIRONMENT_GET_THROTTLE_STATE, &throttle)) { + switch (throttle.mode) { + case RETRO_THROTTLE_FAST_FORWARD: + if (throttle.rate > 0.0f) { + coreParam.fpsLimit = FPSLimit::ANALOG; + coreParam.analogFpsLimit = (int)(throttle.rate + 0.5f); + coreParam.fastForward = false; + } else { + coreParam.fpsLimit = FPSLimit::NORMAL; + coreParam.fastForward = true; + } + break; + case RETRO_THROTTLE_SLOW_MOTION: + coreParam.fpsLimit = FPSLimit::ANALOG; + coreParam.analogFpsLimit = std::max(1, (int)(throttle.rate + 0.5f)); + coreParam.fastForward = false; + break; + default: + coreParam.fpsLimit = FPSLimit::NORMAL; + coreParam.fastForward = false; + break; + } + } +#else if (g_Config.bForceLagSync) { bool isFastForwarding; if (environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &isFastForwarding)) coreParam.fastForward = isFastForwarding; } +#endif } static void check_variables(CoreParameter &coreParam) @@ -528,7 +556,6 @@ static void check_variables(CoreParameter &coreParam) g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED; } -#ifndef __EMSCRIPTEN__ var.key = "ppsspp_cpu_core"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { @@ -546,9 +573,6 @@ static void check_variables(CoreParameter &coreParam) // to experiment in future iOS versions or something... g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; } -#else - g_Config.iCpuCore = (int)CPUCore::INTERPRETER; -#endif var.key = "ppsspp_fast_memory"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) @@ -1524,9 +1548,11 @@ bool retro_load_game(const struct retro_game_info *game) useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES; - // default to interpreter to allow startup in platforms w/o JIT capability - // TODO: I guess we should auto detect? And also, default to IR Interpreter... +#ifdef __EMSCRIPTEN__ + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; +#else g_Config.iCpuCore = (int)CPUCore::INTERPRETER; +#endif CoreParameter coreParam = {}; coreParam.enableSound = true; @@ -2015,6 +2041,8 @@ bool System_GetPropertyBool(SystemProperty prop) #if PPSSPP_PLATFORM(IOS) bool can_jit; return (environ_cb(RETRO_ENVIRONMENT_GET_JIT_CAPABLE, &can_jit) && can_jit); +#elif defined(__EMSCRIPTEN__) + return false; #else return true; #endif diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index a8a1bd370720..21a7c8faf7d2 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -135,7 +135,11 @@ struct retro_core_option_v2_definition option_defs_us[] = { { "Interpreter", NULL }, { NULL, NULL }, }, +#ifdef __EMSCRIPTEN__ + "IR JIT" +#else "JIT" +#endif }, { "ppsspp_fast_memory",