mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
* samples/database: include compat/strl.h in the scan test The test calls strlcpy(), which mingw-w64 does not declare, so it did not build for Windows. * PS1 scan: PSX.EXE means no serial only as the BOOT target A root directory can list a PSX.EXE ahead of the executable SYSTEM.CNF boots: Tomb Raider (USA) lists one before SLUS_001.52. Stopping at the first PSX.EXE in the probe window sent such discs to a whole-track CRC, eight seconds for a CHD. Look for the BOOT target first, matched without regard to case so cdrom:\psx.exe counts. A disc that boots PSX.EXE, or has no SYSTEM.CNF so the BIOS boots PSX.EXE, has no serial and still falls back to its CRC (#15241); any other disc is searched for its serial with PSX.EXE directory records ignored. * 3DS scan: identify .3ds, .cci and .cxi by the NCCH product code These images took the default whole-file CRC, 0.5-4 GB a game. The product code in the NCCH header (partition 0 of a cart image, or the start of a bare NCCH) is the serial the 3DS database is keyed on, and the header is plaintext in encrypted dumps too. A code held by one record matches it outright. Revisions and regions that share a code fall back to the file's CRC to choose, and to the first record when no CRC matches; with scan_serial_and_crc on every match is confirmed by CRC, as for PSP and Dreamcast, and a file none of whose records carry its CRC goes on to the plain CRC lookup with the CRC already computed. A code no record carries, or a header without valid magic such as a CIA, keeps the CRC path, in first-match mode too. Like the CRC lookup, the serial pass visits only databases an installed core claims the extension for, unless Scan Without Core Match is on. * scan: run ITERATE steps while the shared I/O window lasts A file takes about twenty ITERATE steps, and the handler ran one per call. On the regular task queue that is one step per frame: 1000 NES zips took 335 s of pacing, 400 arcade sets 1639 s. The ITERATE states now run in a loop under the same window the BEGIN states use, which times the steps rather than counting them and grants every call at least one. The scan test's unclaimed-database lane spends the window before each check, so that it still counts steps. * scan: hash file CRCs a quantum at a time across steps A step that needed a CRC computed all of it in one handler call: a whole CHD track with LZMA decode, or a multi-gigabyte 3DS image. On the regular queue that froze a frame, and on the threaded queue it held the worker for seconds. Each CRC site now opens its span and returns, and later steps hash 256 KiB quanta while the shared I/O window lasts, at least one per call. The lookup that wanted the CRC runs when it is in, at the same entry. The one-shot CRC helpers, which nothing else called, are gone. * scan: walk each database once for its crc and serial indexes The first file to reach the serial lookup built a crc index and then a serial index for every database, a full walk each: 47 MB of Mobile - J2ME twice in one handler call, the second for an index that came out empty. The crc walk now reads serials as well. It notes whether a database has any, so the serial lookup passes over one with none, and for the serial lookup it builds the serial index in the same pass. Index contents, matches and the split of the index budget are unchanged. * scan: let a database's indexes be built a run of records at a time A database's crc and serial indexes were built by one walk and a qsort in a single call: 50-70 ms for Mobile - J2ME. libretrodb_scan_begin(), _step() and _end() walk a run of records per call, and database_info_index_job_*() builds both indexes over as many calls as it takes, each walking a run of records or merging a run of entries in a resumable merge sort. libretrodb_scan_field2() and database_info_index_new() are built on them and answer as before. * scan: build each database's indexes over steps The first file to reach a database built its indexes in one handler call: 52-68 ms on the regular queue for Mobile - J2ME, 13-17 frames' worth of the 4 ms window. The build now runs over the steps that follow the lookup that began it, a run of records walked or entries merged at a time while the window lasts, and the lookup takes the indexes when they are done. A database counts as size-checked only then. Cancelling frees a build in flight. The one-shot builders, database_info_crc_index_new(), database_info_serial_index_new(), database_info_index_new() and libretrodb_scan_field() and _field2(), lose their last caller here and go; the tests build through the job and the stepped scan. * task_queue samples: build on macOS, run on GCD in a macOS job No sample ran the GCD runner and nothing in CI built HAVE_GCD. Apple's ld has no --wrap, so on Darwin the switch and init-failure tests redirect the wrapped calls from task_queue.c by macro instead, and libdispatch comes from libSystem there. The switch test gets a GCD=1 build, and a macOS job runs every task_queue sample plain, under ASan+UBSan and TSan, threaded and, where it has a GCD=1 build, on GCD. * task_queue: a switch waits out a wake from another thread task_queue_wake() is documented as safe from any thread, but a switch between the threaded and the unthreaded queue frees the threaded runner's lock and condition variable while a waker on another thread can still be inside it. Toggling Threaded Video makes that switch at the next frame. Two seq_cst counters now keep them apart without a lock: a switch that finds a waker inside is put off to the next task_queue_check(), and a waker that finds a switch under way leaves its task to it. A wake on the thread that drives the queue cannot meet a switch and skips the counters. A switch now ends every park, and the new runner takes wakes again before it runs any task, so a parked task loses nothing to a wake dropped during the switch. A wake dropped while a switch is put off and then taken back, or for a task whose park a switch keeps because it leaves a stuck handler behind, waits for that park's horizon. The switch test holds a waker inside the queue across a switch, wakes from four threads through 1000 switches, and holds the main thread where the new runner first runs a task while that task's first pass wakes it. GCD builds leave out the threaded runner's own code, which they never used and which warned there. The switch test also checks that its realloc() wrapper ran, since on macOS that wrap is a macro rather than a link option. * scan: a scan of one file only adds to its playlist A Content Location that is a file is scanned as one file, which the Scan button, --scan=FILE and iOS "open in" already rely on. Such a scan cleared the playlist when Overwrite was on, and recorded the file, and its core, as what the playlist refreshes with, so Refresh Playlist later rescanned that file alone. Overwrite is now ignored for a file, and the playlist's scan record and default core are left as they were. Validate Existing Entries is skipped too: a file scan's extensions can differ from its folder's, so validating with them could drop entries the folder scan added. A later folder scan or Refresh still prunes. With no database and an automatic target playlist, a file is filed under its folder's name, as a scan of that folder files it, rather than in a playlist named after the file. A Content Location that is missing, as on a drive that is offline, counts as a file, so Overwrite no longer empties its playlist while the drive is away. * menu: Content Location takes a file or a folder Content Scan's Content Location now opens one browser that lists folders and files in every scan method: OK on a file picks it, <Use This Directory> picks the folder. In Automatic it lists every file, as an Automatic scan reads every file; Custom keeps listing the scan's extensions. The Scan Single File toggle, which switched the browser to files and was shown only in Custom with Search Recursively off, goes with its setting and strings, and a scan of a file no longer turns Search Recursively off behind the user's back. Import Content drops Scan Directory and Scan File, as the Scan Directory list already had, leaving Content Scan as the one way in; Scan File no longer started a scan. On Apple, File > Open and the iOS document picker opened from Content Location set the location as OK on a file does. * task_queue: hold pushes from other threads for the main thread task_queue_push() from another thread could meet a switch between the threaded and the unthreaded queue, which frees the lock and condition variable the push takes. On the unthreaded queue, with no switch at all, it put the task on a list the main thread walks without a lock. Windows pushes from the video thread under threaded video, and HID builds from libusb's thread. Such a push now goes through the switch's gate: when the gate is shut, or the unthreaded queue is up, the task goes onto a lock-free list that the next gather pushes on the main thread, and the push returns true. A push is also held while earlier ones still wait, so a thread's pushes start in the order it made them. A BLOCKING task refused there is cancelled, and its callback still runs. The switch test pushes from four threads on the unthreaded queue and through 1000 switches, checks a held BLOCKING push, and holds the main thread inside the gather that reopens the queue to show that a thread's next push does not pass its held one. * task_queue: cancel by ident and find safely from other threads task_queue_cancel_task() and task_queue_find() from another thread met the same switch and the same unlocked lists as a push did. task_queue_cancel_ident() cancels by ident from any thread: a locked walk when the threaded queue is up with nothing held, otherwise an ident left for the next gather, which applies it after its pushes, to a held push as well. A held push cancelled so retires cancelled without running, and its callback runs. A find from another thread goes through the switch's gate and reports false when it cannot walk the lists. The queue's own workers and GCD threads skip the gate, and a find on the main thread also sees held pushes. Each worker keeps its thread id in a slot of its own and clears it before it reports that it left, so a thread that reuses a joined worker's id is not taken for one. The switch test cancels and finds from other threads on both queues and across a put-off switch, and holds the main thread in the gather to show that a thread's cancel of its own held push is not lost. Once extra workers start, finds from handlers on the one worker and on an extra still skip the gate, and a find with a joined extra worker's id from inside deinit is refused. * autoconfig: make connects reported on other threads on the main thread libusb's hotplug thread and the Wii's HID poll thread report pads from their own threads, where task_queue_find() can no longer answer exactly: at a switch, or on the unthreaded queue, it reports false. Off the main thread, input_autoconfigure_connect_ex() and input_autoconfigure_disconnect() now copy the request into a main-thread task and return true. Its callback makes the call on the main thread, in the order the requests were made, so the duplicate check, the port's sequence number and the settings it reads are the main thread's. A new sample drives task_autodetect.c on the task queue with the rest stubbed, reports pads from another thread, and checks that every find runs on the main thread, that a pad reported twice queues one connect, that a disconnect handed off is made and that a cancelled request is not. * task_queue: wait, reset and deinit see pushes held for the main thread On the unthreaded queue every push from another thread is held for the next gather. A wait with nothing running returned without running it, and a reset, the exit drain's included, left it to run normally afterwards. Cancels held for the gather were never freed at deinit. The unthreaded wait now drains held pushes first, and a reset drains them into the runner before it cancels, so a held task meets a queued one's fate. Deinit applies and frees held cancels; held pushes stay held for the next runner's first gather, so no callback runs inside deinit. * task_queue: make the Threaded Tasks flag atomic Task handlers on the worker read it through task_queue_is_threaded(), and the main thread writes it when the setting changes, so changing the setting while a task ran was a data race. ThreadSanitizer shows it on a database scan whose queue is switched under it. * task_queue: a parallel task names the extra workers it needs retro_task_t gains parallel_workers, which task_init() clears. A parallel push starts the extra workers it finds missing, up to the larger of task_queue_set_parallel_workers() and the task's number, at most 8. They serve every parallel task and stay until deinit, as before, so the pool only grows; 0 keeps the frontend's number. The started count is atomic, so a push that finds enough takes no lock, and one that needs more claims the next entries under running_lock. The parallel test has a task asking for three, a later one asking for fewer, one asking for more than eight, a plain task whose number is ignored, the frontend's number holding for a task asking for fewer, and a task that leaves the number alone. * task_queue: let deinit leave extra workers behind in detachable handlers A parallel task may now also be detachable, and then runs on the extra workers like any other parallel task. Deinit gives every worker the one bound it gave the one worker, the extra workers first. One still inside a detachable handler past it is left behind to finish alone, as the one worker was, and one inside any other handler is still waited for. The primitives of an init are held by deinit and by each worker it leaves behind, and the last of them to let go frees them, so the one worker and several extras can be left behind together. A worker left behind, the one or an extra, now runs the task's cleanup once the handler returns, then frees the task, so a detachable task releases what it holds however it ends. Its callback still never runs; its cleanup, like its handler, may touch only what the task owns. The parallel test runs a detachable parallel task on an extra worker, leaves one stuck past the bound, waits for a slow one that is not detachable, and leaves the one worker and two extras behind together with a new queue running beside them. Each task left behind is cleaned up once, by its own worker, after its handler returns, and one not left behind at its retirement as before. The switch test switches to the unthreaded queue and back with extra workers live. * task_queue: on Emscripten, sleep after a pass on the one worker only On Emscripten every worker sleeps 1 ms after each handler call, a pthreads workaround from when the one worker ran every task. The extra workers run parallel tasks, which work in many short passes, and the sleep cost each of those passes 1 ms, more than the pass itself. The extra workers no longer sleep; the one worker still does. * emscripten: ask for at most 4 extra workers, and start 5 threads In the browser a pthread the pool has not started is made on demand by the main thread, and with the JS file system, where every thread's file calls go through the main thread, 8 extra workers made each of those calls about 40% dearer than 4 did. The frontend asked for cores - 3 extra workers, up to 8; on Emscripten it now asks for at most 4, and the build starts 5 pthreads up front, enough for the one worker and those 4. * scan: read small plain files on parallel reader tasks, in list order A database scan on the threaded queue pushes min(4, cores) reader tasks, none below 2, as parallel tasks that ask for that many extra workers, in HAVE_THREADS builds with lock-free atomics, except on Wii U, whose C library and FAT layer take no locks, and on Vita, where Vita3K's sceIo tables take none. With Threaded Tasks off none starts and the scan reads every file itself, stepped as before. The scan hands the plain files and archives it would CRC whole to the readers, up to 64 per reader ahead of it, each with its own copy of the path. A reader claims them in list order, opens and sizes each, leaves one over 128 MiB to the scan unread and CRCs the rest a quantum per call. The scan takes each result at its own place in the content list, so the databases see files in the same order as before, parks while the next one is still being read, and reads itself whatever a reader could not. The scan never waits for its readers. At its end, on cancel and once the queue is unthreaded, each reader leaves at its next call and hands back the file in hand. Each reader drops its reference to what they share in its cleanup, and the last reference frees it. A queue deinit leaves a reader stuck in a read behind after its 1 s bound and never calls it again; the worker left with it runs its cleanup once the read returns, which hands back the file in hand and drops the reference. The scan test covers list order, files over the cutoff, failed pushes, cancels and quits with a reader stuck in a read, and queue switches under a scan; CI runs it under TSan, without threads and with no readers. * scan: run the command-line scan with no menu on the threaded queue A command-line scan with no menu waits for the task queue to run dry, and only a scan on the threaded queue gets readers. In threaded builds that run now uses the threaded queue whatever Threaded Tasks says; the setting is not changed. * menu: Import Content offers Scan Directory and Scan File Both entries sit beside Content Scan when the database is built in. OK on a file in Scan File's browser scans that file, as the Scan action does. The load_content_filter harness checks that the entries are listed and that OK on a file starts its scan. * manual scan: Content Location holds a full file path The location can now be a file, so it is kept in a PATH_MAX_LENGTH buffer, as is the scan task's copy. A file whose path does not fit is refused in the browser instead of being joined short. The load_content_filter harness sets a location deeper than a folder buffer and checks that it is kept whole. --------- Co-authored-by: LibretroAdmin <[email protected]>
370 lines
11 KiB
Makefile
370 lines
11 KiB
Makefile
HAVE_STATIC_DUMMY ?= 0
|
|
ifeq ($(TARGET),)
|
|
ifeq ($(LIBRETRO),)
|
|
TARGET := retroarch.js
|
|
LIBRETRO = dummy
|
|
else
|
|
TARGET := $(LIBRETRO)_libretro.js
|
|
endif
|
|
endif
|
|
TARGET_BASE := $(subst .js,,$(TARGET))
|
|
|
|
OS = Emscripten
|
|
OBJ :=
|
|
DEFINES := -DRARCH_INTERNAL -DHAVE_MAIN
|
|
DEFINES += -DHAVE_FILTERS_BUILTIN -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORE_INFO
|
|
|
|
HAVE_STATESTREAM ?= 1
|
|
HAVE_PATCH = 1
|
|
HAVE_DSP_FILTER = 1
|
|
HAVE_VIDEO_FILTER = 1
|
|
HAVE_OVERLAY = 1
|
|
HAVE_NETWORKING ?= 1
|
|
HAVE_LIBRETRODB = 1
|
|
HAVE_COMPRESSION = 1
|
|
HAVE_UPDATE_ASSETS = 1
|
|
HAVE_ONLINE_UPDATER = 1
|
|
HAVE_GLSL = 1
|
|
HAVE_SCREENSHOTS = 1
|
|
HAVE_REWIND ?= 1
|
|
HAVE_RUNAHEAD ?= 1
|
|
HAVE_AUDIOMIXER = 1
|
|
HAVE_RVORBIS = 1
|
|
HAVE_CC_RESAMPLER ?= 1
|
|
HAVE_EGL ?= 0
|
|
HAVE_OPENGLES = 1
|
|
HAVE_RJPEG = 0
|
|
HAVE_RPNG = 1
|
|
HAVE_RWEBP = 1
|
|
HAVE_RDDS = 1
|
|
HAVE_EMSCRIPTEN = 1
|
|
HAVE_MENU ?= 1
|
|
HAVE_GFX_WIDGETS = 1
|
|
HAVE_RGUI = 1
|
|
HAVE_SDL = 0
|
|
HAVE_SDL2 = 0
|
|
HAVE_SHADERPIPELINE = 1
|
|
HAVE_STATIC_VIDEO_FILTERS = 1
|
|
HAVE_STATIC_AUDIO_FILTERS = 1
|
|
HAVE_CONFIGFILE = 1
|
|
HAVE_COMMAND = 1
|
|
HAVE_STDIN_CMD ?= 1
|
|
HAVE_CHEATS = 1
|
|
HAVE_RMODTRACKER = 1
|
|
HAVE_CORE_INFO_CACHE = 1
|
|
HAVE_7ZIP = 1
|
|
HAVE_RZSTD = 1
|
|
HAVE_BSV_MOVIE ?= 1
|
|
HAVE_CHD ?= 0
|
|
HAVE_RCHD = 1
|
|
HAVE_NETPLAYDISCOVERY ?= 0
|
|
|
|
# enables pthreads, requires special headers on the web server:
|
|
# see https://web.dev/articles/coop-coep
|
|
HAVE_THREADS ?= 0
|
|
|
|
# requires HAVE_THREADS
|
|
HAVE_AUDIOWORKLET ?= 0
|
|
|
|
# doesn't work on PROXY_TO_PTHREAD
|
|
HAVE_RWEBAUDIO ?= 1
|
|
|
|
# requires ASYNC or PROXY_TO_PTHREAD. Prefer another driver.
|
|
HAVE_AL ?= 0
|
|
|
|
# whether the browser thread is allowed to block to wait for audio to play, not CPU usage-friendly!
|
|
# currently this variable is only used by rwebaudio and audioworklet; openal will never busywait.
|
|
ALLOW_AUDIO_BUSYWAIT ?= 0
|
|
|
|
# asyncify: enable fibers and sleeping on the browser thread,
|
|
# this has a noticeable impact on compile time, output wasm size, and speed.
|
|
# see https://emscripten.org/docs/porting/asyncify.html for more info
|
|
ASYNC ?= 0
|
|
|
|
# minimal asyncify: better performance than full asyncify,
|
|
# but sleeping on the browser thread is only possible in some places.
|
|
MIN_ASYNC ?= 0
|
|
|
|
# JSPI: experimental asyncify alternative, see https://webassembly.org/features/
|
|
# currently (emscripten 4.0.15) this requires a patched emscripten toolchain.
|
|
JSPI ?= 0
|
|
|
|
# runs RetroArch on a pthread instead of the browser thread; implies HAVE_THREADS
|
|
PROXY_TO_PTHREAD ?= 0
|
|
|
|
# recommended FS when using HAVE_THREADS
|
|
HAVE_WASMFS ?= 0
|
|
|
|
# enables OPFS (origin private file system) and FETCHFS, requires PROXY_TO_PTHREAD or JSPI
|
|
HAVE_EXTRA_WASMFS ?= 0
|
|
|
|
# use the closure compiler to further minify output JS
|
|
CLOSURE_COMPILER ?= 0
|
|
|
|
# enable javascript filesystem tracking, incompatible with HAVE_WASMFS
|
|
FS_DEBUG ?= 0
|
|
|
|
# help diagnose GL problems (can cause issues in normal operation)
|
|
GL_DEBUG ?= 0
|
|
|
|
# enable runtime assertions (always enabled in debug builds)
|
|
# please use this when testing new emscripten-specific features!
|
|
ASSERTIONS ?= 0
|
|
|
|
# enable GLES 3.0 (WebGL 2).
|
|
# if left disabled, GLES 2.0 (WebGL 1) will be used.
|
|
HAVE_OPENGLES3 ?= 0
|
|
|
|
# does nothing on its own, but automatically selected by some other options
|
|
WASM_WORKERS = 0
|
|
|
|
LTO ?= 0
|
|
# the task worker and the 4 extra workers retroarch.c asks for at most
|
|
PTHREAD_POOL_SIZE ?= 5
|
|
|
|
ASYNCIFY_ADD ?= dynCall_*,emscripten_mainloop
|
|
ASYNCIFY_REMOVE ?= threaded_worker
|
|
|
|
STACK_SIZE ?= 4194304
|
|
INITIAL_HEAP ?= 134217728
|
|
|
|
# 4194304 ----- 4 MiB (Stack: recommended)
|
|
# 8388608 ----- 8 MiB
|
|
# 16777216 ---- 16 MiB
|
|
# 33554432 ---- 32 MiB
|
|
# 67108864 ---- 64 MiB
|
|
# 134217728 --- 128 MiB (Heap: recommended) (Stack: recommended for some cores [mupen64plus_next])
|
|
# 268435456 --- 256 MiB (Heap: recommended for some cores [mupen64plus_next])
|
|
# 536870912 --- 512 MiB (Heap: needed for some cores [mednafen_psx(_hw)])
|
|
# 1073741824 -- 1 GiB
|
|
# 1610612736 -- 1.5 GiB
|
|
# 2147483648 -- 2 GiB
|
|
|
|
OBJDIR := obj-emscripten
|
|
|
|
EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_toggle_fullscreen,_cmd_take_screenshot,\
|
|
_cmd_toggle_menu,_cmd_reload_config,_cmd_toggle_grab_mouse,_cmd_toggle_game_focus,_cmd_reset,_cmd_toggle_pause,_cmd_pause,_cmd_unpause,\
|
|
_cmd_set_volume,_cmd_set_shader,_cmd_cheat_set_code,_cmd_cheat_get_code,_cmd_cheat_toggle_index,_cmd_cheat_get_code_state,_cmd_cheat_realloc,\
|
|
_cmd_cheat_get_size,_cmd_cheat_apply_cheats,EmscriptenSendCommand,EmscriptenReceiveCommandReply
|
|
|
|
EXPORTS := callMain,FS,PATH,ERRNO_CODES,ENV,stringToNewUTF8,UTF8ToString,Browser,EmscriptenSendCommand,EmscriptenReceiveCommandReply
|
|
|
|
LIBS := -s USE_ZLIB=1
|
|
CFLAGS := -s USE_ZLIB=1
|
|
|
|
ifeq ($(HAVE_EXTRA_WASMFS), 1)
|
|
LIBS += -lfetchfs.js -lopfs.js
|
|
DEFINES += -DHAVE_EXTRA_WASMFS
|
|
override HAVE_WASMFS = 1
|
|
ifeq ($(PROXY_TO_PTHREAD), 0)
|
|
# note: currently HAVE_EXTRA_WASMFS+JSPI requires ASSERTIONS to be turned off
|
|
ifeq ($(JSPI), 0)
|
|
$(error ERROR: HAVE_EXTRA_WASMFS requires PROXY_TO_PTHREAD or JSPI)
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(HAVE_WASMFS), 1)
|
|
LIBS += -s WASMFS -s FORCE_FILESYSTEM=1
|
|
endif
|
|
|
|
# note: real PROXY_TO_PTHREAD is not used here; we do the pthread management ourselves
|
|
ifeq ($(PROXY_TO_PTHREAD), 1)
|
|
LIBS += -s OFFSCREENCANVAS_SUPPORT
|
|
DEFINES += -DPROXY_TO_PTHREAD -DEMSCRIPTEN_STACK_SIZE=$(STACK_SIZE)
|
|
override HAVE_THREADS = 1
|
|
override WASM_WORKERS = 1
|
|
# use the default stack size for the browser thread; the RetroArch thread will be created with the specified stack size
|
|
override STACK_SIZE = 4194304
|
|
else ifeq ($(HAVE_AL), 1)
|
|
override ASYNC = 1
|
|
override MIN_ASYNC = 0
|
|
endif
|
|
|
|
ifeq ($(HAVE_SDL2), 1)
|
|
LIBS += -s USE_SDL=2
|
|
DEFINES += -DHAVE_SDL2
|
|
endif
|
|
|
|
LDFLAGS := -L. --no-heap-copy -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
|
|
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) -s EXIT_RUNTIME=1 \
|
|
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
|
|
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
|
|
-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \
|
|
-s ENVIRONMENT=web,worker -s WASM_BIGINT=1 \
|
|
-s FETCH=1 \
|
|
--extern-pre-js emscripten/extern_pre.js \
|
|
--js-library emscripten/library_rwebcam.js \
|
|
--js-library emscripten/library_platform_emscripten.js
|
|
|
|
ifeq ($(HAVE_OPENGLES), 1)
|
|
ifeq ($(HAVE_OPENGLES3), 1)
|
|
LDFLAGS += -s FULL_ES3=1 -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2
|
|
else
|
|
LDFLAGS += -s FULL_ES2=1 -s MIN_WEBGL_VERSION=1 -s MAX_WEBGL_VERSION=2
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(GL_DEBUG), 1)
|
|
LDFLAGS += -s GL_ASSERTIONS=1 -s GL_DEBUG=1
|
|
endif
|
|
|
|
ifeq ($(FS_DEBUG), 1)
|
|
LDFLAGS += -s FS_DEBUG=1
|
|
endif
|
|
|
|
ifeq ($(HAVE_RWEBAUDIO), 1)
|
|
LDFLAGS += --js-library emscripten/library_rwebaudio.js
|
|
DEFINES += -DHAVE_RWEBAUDIO
|
|
ifeq ($(PROXY_TO_PTHREAD), 1)
|
|
$(error ERROR: RWEBAUDIO is incompatible with PROXY_TO_PTHREAD)
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(HAVE_AUDIOWORKLET), 1)
|
|
LDFLAGS += -s AUDIO_WORKLET=1
|
|
DEFINES += -DHAVE_AUDIOWORKLET
|
|
override WASM_WORKERS = 1
|
|
ifeq ($(HAVE_THREADS), 0)
|
|
$(error ERROR: AUDIOWORKLET requires HAVE_THREADS)
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(HAVE_AL), 1)
|
|
LDFLAGS += -lopenal
|
|
DEFINES += -DHAVE_AL
|
|
endif
|
|
|
|
ifeq ($(PROXY_TO_PTHREAD), 1)
|
|
else ifeq ($(JSPI), 1)
|
|
else ifeq ($(ASYNC), 1)
|
|
else
|
|
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
|
|
ifeq ($(MIN_ASYNC), 1)
|
|
DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
|
|
else
|
|
DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
|
|
endif
|
|
ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
|
|
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(ALLOW_AUDIO_BUSYWAIT), 1)
|
|
DEFINES += -DEMSCRIPTEN_AUDIO_BUSYWAIT
|
|
endif
|
|
|
|
# explanation of some of these defines:
|
|
# EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK: audio blocking occurs in the main loop instead of in the audio driver functions.
|
|
# EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK: along with above, enables external blocking in the write function.
|
|
# EMSCRIPTEN_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
|
|
# EMSCRIPTEN_AUDIO_ASYNC_BLOCK: external block uses emscripten_sleep (requires MIN_ASYNC).
|
|
# EMSCRIPTEN_AUDIO_FAKE_BLOCK: external block uses main loop timing (doesn't require asyncify).
|
|
# when building with either PROXY_TO_PTHREAD or ASYNC (full asyncify), none of the above are required.
|
|
|
|
ifeq ($(HAVE_THREADS), 1)
|
|
LDFLAGS += -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD_POOL_SIZE)
|
|
CFLAGS += -pthread -s SHARED_MEMORY
|
|
endif
|
|
|
|
ifeq ($(WASM_WORKERS), 1)
|
|
LDFLAGS += -s WASM_WORKERS=1
|
|
endif
|
|
|
|
ifeq ($(JSPI), 1)
|
|
CFLAGS += -fwasm-exceptions
|
|
DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_FULL_ASYNCIFY -DEMSCRIPTEN_JSPI
|
|
LDFLAGS += -s JSPI=1 -fwasm-exceptions
|
|
else ifeq ($(MIN_ASYNC), 1)
|
|
DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_MIN_ASYNCIFY
|
|
LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192 -s ASYNCIFY_IGNORE_INDIRECT=1 -s ASYNCIFY_ADD='$(ASYNCIFY_ADD)' -s ASYNCIFY_REMOVE='$(ASYNCIFY_REMOVE)'
|
|
ifeq ($(DEBUG), 1)
|
|
LDFLAGS += -s ASYNCIFY_ADVISE #-s ASYNCIFY_DEBUG=1
|
|
endif
|
|
else ifeq ($(ASYNC), 1)
|
|
DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_FULL_ASYNCIFY
|
|
LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192
|
|
ifeq ($(DEBUG), 1)
|
|
#LDFLAGS += -s ASYNCIFY_DEBUG=1 # broken?
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(CLOSURE_COMPILER), 1)
|
|
LDFLAGS += --closure 1 --closure-args=--externs=emscripten/closure_externs.js --post-js emscripten/closure_post.js
|
|
endif
|
|
|
|
include Makefile.common
|
|
|
|
DEF_FLAGS += $(INCLUDE_DIRS) -Ideps
|
|
|
|
CFLAGS += $(DEF_FLAGS)
|
|
CXXFLAGS += $(DEF_FLAGS) -D__STDC_CONSTANT_MACROS
|
|
|
|
libretro =
|
|
libretro_new =
|
|
|
|
ifeq ($(HAVE_STATIC_DUMMY),1)
|
|
DEFINES += -DHAVE_STATIC_DUMMY
|
|
else
|
|
libretro = libretro_emscripten.bc
|
|
libretro_new = libretro_emscripten.a
|
|
endif
|
|
|
|
ifneq ($(V), 1)
|
|
Q := @
|
|
endif
|
|
|
|
ifeq ($(DEBUG), 1)
|
|
LDFLAGS += -O0 -g -gsource-map -s STACK_OVERFLOW_CHECK=2
|
|
# -O0 in cflags gives "too many locals" errors
|
|
CFLAGS += -O1 -g -gsource-map
|
|
override ASSERTIONS = 1
|
|
# "WASM_WORKERS is not currently compatible with `-fsanitize` tools"
|
|
ifeq ($(WASM_WORKERS), 0)
|
|
CFLAGS += -fsanitize=undefined
|
|
LDFLAGS += -fsanitize=undefined
|
|
endif
|
|
else
|
|
LDFLAGS += -O3
|
|
# WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake)
|
|
ifeq ($(LTO), 1)
|
|
LDFLAGS += -flto
|
|
endif
|
|
CFLAGS += -O3
|
|
endif
|
|
|
|
ifneq ($(ASSERTIONS), 0)
|
|
LDFLAGS += -s ASSERTIONS=$(ASSERTIONS)
|
|
endif
|
|
|
|
CFLAGS += -Wall -I. -Ilibretro-common/include -std=gnu99
|
|
|
|
RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
|
|
|
|
all: $(TARGET)
|
|
|
|
$(libretro_new): ;
|
|
|
|
mv_libretro:
|
|
$(Q)mv -f $(libretro) $(libretro_new) || true
|
|
|
|
$(TARGET): $(RARCH_OBJ) $(libretro_new) mv_libretro
|
|
@$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
|
|
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
|
|
|
|
$(OBJDIR)/%.o: %.c
|
|
@mkdir -p $(dir $@)
|
|
@$(if $(Q), $(shell echo echo CC $<),)
|
|
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
|
|
|
$(OBJDIR)/%.o: %.cpp
|
|
@mkdir -p $(dir $@)
|
|
@$(if $(Q), $(shell echo echo CXX $<),)
|
|
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $<
|
|
|
|
clean:
|
|
rm -rf $(OBJDIR)
|
|
rm -f $(TARGET)
|
|
|
|
.PHONY: all clean mv_libretro
|