77TARGET := $(LIBRETRO)_libretro.js
88endif
99endif
10-
11- EOPT = USE_ZLIB=1 # Emscripten specific options
12- EOPTS = $(addprefix -s $(EMPTY), $(EOPT)) # Add '-s ' to each option
10+ TARGET_BASE := $(subst .js,,$(TARGET))
1311
1412OS = Emscripten
1513OBJ :=
@@ -20,7 +18,7 @@ HAVE_PATCH = 1
2018HAVE_DSP_FILTER = 1
2119HAVE_VIDEO_FILTER = 1
2220HAVE_OVERLAY = 1
23- HAVE_NETWORKING = 1
21+ HAVE_NETWORKING ? = 1
2422HAVE_LIBRETRODB = 1
2523HAVE_COMPRESSION = 1
2624HAVE_UPDATE_ASSETS = 1
@@ -32,10 +30,10 @@ HAVE_AUDIOMIXER = 1
3230HAVE_CC_RESAMPLER = 1
3331HAVE_EGL ?= 0
3432HAVE_OPENGLES = 1
35- HAVE_RJPEG = 0
36- HAVE_RPNG = 1
33+ HAVE_RJPEG = 0
34+ HAVE_RPNG = 1
3735HAVE_EMSCRIPTEN = 1
38- HAVE_MENU = 1
36+ HAVE_MENU ? = 1
3937HAVE_GFX_WIDGETS = 1
4038HAVE_RGUI = 1
4139HAVE_SDL = 0
@@ -47,37 +45,63 @@ HAVE_STATIC_AUDIO_FILTERS = 1
4745HAVE_STB_FONT = 1
4846HAVE_CONFIGFILE = 1
4947HAVE_COMMAND = 1
50- HAVE_STDIN_CMD = 1
48+ HAVE_STDIN_CMD ? = 1
5149HAVE_CHEATS = 1
5250HAVE_IBXM = 1
5351HAVE_CORE_INFO_CACHE = 1
5452HAVE_7ZIP = 1
5553HAVE_BSV_MOVIE = 1
56- HAVE_AL = 1
5754HAVE_CHD ?= 0
5855HAVE_NETPLAYDISCOVERY ?= 0
56+
57+ HAVE_AL ?= 1
58+
59+ # enables pthreads, requires special headers on the web server:
60+ # see https://web.dev/articles/coop-coep
5961HAVE_THREADS ?= 0
6062
63+ # requires HAVE_THREADS
64+ HAVE_AUDIOWORKLET ?= 0
65+
6166# WARNING -- READ BEFORE ENABLING
6267# The rwebaudio driver is known to have several audio bugs, such as
6368# minor crackling, or the entire page freezing/crashing.
6469# It works perfectly on chrome, but even firefox has really bad audio quality.
6570# I should also note, the driver on iOS is completely broken (crashes the page).
6671# You have been warned.
67- HAVE_RWEBAUDIO = 0
72+ HAVE_RWEBAUDIO ?= 0
73+
74+ # whether the browser thread is allowed to block to wait for audio to play,
75+ # may lead to the issues mentioned above.
76+ # currently this variable is only used by audioworklet;
77+ # rwebaudio will always busywait and openal will never busywait.
78+ ALLOW_AUDIO_BUSYWAIT ?= 0
79+
80+ # minimal asyncify; better performance than full asyncify,
81+ # but sleeping on the main thread is only possible in some places.
82+ MIN_ASYNC ?= 0
83+
84+ # runs RetroArch on a pthread instead of the browser thread; requires HAVE_THREADS
85+ PROXY_TO_PTHREAD ?= 0
86+
87+ # recommended FS when using HAVE_THREADS
88+ HAVE_WASMFS ?= 0
89+
90+ # enables OPFS (origin private file system) and FETCHFS, requires PROXY_TO_PTHREAD
91+ HAVE_EXTRA_WASMFS ?= 0
92+
93+ # enable javascript filesystem tracking, incompatible with HAVE_WASMFS
94+ FS_DEBUG ?= 0
6895
6996# help diagnose GL problems (can cause issues in normal operation)
7097GL_DEBUG ?= 0
7198
72- # enable javascript filesystem tracking
73- FS_DEBUG = 0
99+ # does nothing on its own, but automatically selected by some other options
100+ WASM_WORKERS = 0
74101
75102HAVE_OPENGLES ?= 1
76103HAVE_OPENGLES3 ?= 0
77104
78- HAVE_WASMFS ?= 0
79- PROXY_TO_PTHREAD ?= 0
80-
81105ASYNC ?= 0
82106LTO ?= 0
83107PTHREAD_POOL_SIZE ?= 4
@@ -102,26 +126,32 @@ OBJDIR := obj-emscripten
102126EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_take_screenshot,\
103127_cmd_toggle_menu,_cmd_reload_config,_cmd_toggle_grab_mouse,_cmd_toggle_game_focus,_cmd_reset,_cmd_toggle_pause,_cmd_pause,_cmd_unpause,\
104128_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,\
105- _cmd_cheat_get_size,_cmd_cheat_apply_cheats,_update_canvas_dimensions,_update_window_hidden,_update_power_state,_update_memory_usage,\
106- EmscriptenSendCommand,EmscriptenReceiveCommandReply
129+ _cmd_cheat_get_size,_cmd_cheat_apply_cheats,EmscriptenSendCommand,EmscriptenReceiveCommandReply
107130
108131EXPORTS := callMain,FS,PATH,ERRNO_CODES,ENV,stringToNewUTF8,UTF8ToString,Browser,EmscriptenSendCommand,EmscriptenReceiveCommandReply
109132
110- LIBS := -s USE_ZLIB=1
133+ LIBS := -s USE_ZLIB=1
134+ CFLAGS := -s USE_ZLIB=1
111135
112- ifeq ($(HAVE_WASMFS), 1)
113- LIBS += -s WASMFS -s FORCE_FILESYSTEM=1 -lfetchfs.js -lopfs.js
114- DEFINES += -DHAVE_WASMFS
136+ ifeq ($(HAVE_EXTRA_WASMFS), 1)
137+ LIBS += -lfetchfs.js -lopfs.js
138+ DEFINES += -DHAVE_EXTRA_WASMFS
139+ override HAVE_WASMFS = 1
115140 ifeq ($(PROXY_TO_PTHREAD), 0)
116- $(error ERROR: WASMFS requires PROXY_TO_PTHREAD)
141+ $(error ERROR: HAVE_EXTRA_WASMFS requires PROXY_TO_PTHREAD)
117142 endif
118143endif
119144
145+ ifeq ($(HAVE_WASMFS), 1)
146+ LIBS += -s WASMFS -s FORCE_FILESYSTEM=1
147+ endif
148+
120149# note: real PROXY_TO_PTHREAD is not used here; we do the pthread management ourselves
121150ifeq ($(PROXY_TO_PTHREAD), 1)
122151 LIBS += -s OFFSCREENCANVAS_SUPPORT
123152 DEFINES += -DPROXY_TO_PTHREAD -DEMSCRIPTEN_STACK_SIZE=$(STACK_SIZE)
124153 override HAVE_THREADS = 1
154+ override WASM_WORKERS = 1
125155 # use the default stack size for the browser thread; the RetroArch thread will be created with the specified stack size
126156 override STACK_SIZE = 4194304
127157else ifeq ($(HAVE_AL), 1)
@@ -165,6 +195,40 @@ ifeq ($(HAVE_RWEBAUDIO), 1)
165195 DEFINES += -DHAVE_RWEBAUDIO
166196endif
167197
198+ ifeq ($(HAVE_AUDIOWORKLET), 1)
199+ LDFLAGS += -s AUDIO_WORKLET=1
200+ DEFINES += -DHAVE_AUDIOWORKLET
201+ override WASM_WORKERS = 1
202+ ifeq ($(HAVE_THREADS), 0)
203+ $(error ERROR: AUDIOWORKLET requires HAVE_THREADS)
204+ endif
205+ ifeq ($(PROXY_TO_PTHREAD), 1)
206+ else ifeq ($(ASYNC), 1)
207+ else
208+ DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
209+ ifeq ($(MIN_ASYNC), 1)
210+ DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
211+ else
212+ DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
213+ endif
214+ ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
215+ DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
216+ endif
217+ endif
218+ endif
219+
220+ ifeq ($(ALLOW_AUDIO_BUSYWAIT), 1)
221+ DEFINES += -DEMSCRIPTEN_AUDIO_BUSYWAIT
222+ endif
223+
224+ # explanation of some of these defines:
225+ # EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK: audio blocking occurs in the main loop instead of in the audio driver functions.
226+ # EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK: along with above, enables external blocking in the write function.
227+ # ALLOW_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
228+ # EMSCRIPTEN_AUDIO_ASYNC_BLOCK: external block uses emscripten_sleep (requires MIN_ASYNC).
229+ # EMSCRIPTEN_AUDIO_FAKE_BLOCK: external block uses main loop timing (doesn't require asyncify).
230+ # when building with either PROXY_TO_PTHREAD or ASYNC (full asyncify), none of the above are required.
231+
168232ifeq ($(HAVE_AL), 1)
169233 LDFLAGS += -lopenal
170234 DEFINES += -DHAVE_AL
@@ -175,11 +239,21 @@ ifeq ($(HAVE_THREADS), 1)
175239 CFLAGS += -pthread -s SHARED_MEMORY
176240endif
177241
242+ ifeq ($(WASM_WORKERS), 1)
243+ LDFLAGS += -s WASM_WORKERS=2
244+ endif
245+
178246ifeq ($(ASYNC), 1)
179- DEFINES += -DEMSCRIPTEN_ASYNCIFY
247+ DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_FULL_ASYNCIFY
180248 LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192
181249 ifeq ($(DEBUG), 1)
182- LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE
250+ #LDFLAGS += -s ASYNCIFY_DEBUG=1 # broken?
251+ endif
252+ else ifeq ($(MIN_ASYNC), 1)
253+ DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_MIN_ASYNCIFY
254+ LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192 -s ASYNCIFY_IGNORE_INDIRECT=1 -s ASYNCIFY_ADD='dynCall_*,emscripten_mainloop' -s ASYNCIFY_REMOVE='threaded_worker'
255+ ifeq ($(DEBUG), 1)
256+ LDFLAGS += -s ASYNCIFY_ADVISE #-s ASYNCIFY_DEBUG=1
183257 endif
184258endif
185259
@@ -202,7 +276,7 @@ ifneq ($(V), 1)
202276endif
203277
204278ifeq ($(DEBUG), 1)
205- LDFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1
279+ LDFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=2 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1
206280 # -O0 in cflags gives "too many locals" errors
207281 CFLAGS += -O1 -g -gsource-map
208282else
@@ -220,25 +294,37 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
220294
221295all: $(TARGET)
222296
223- $(libretro_new) : $(libretro)
224- mv -f $(libretro) $(libretro_new)
297+ $(libretro_new): ;
298+
299+ mv_libretro:
300+ mv -f $(libretro) $(libretro_new) || true
225301
226- $(TARGET): $(RARCH_OBJ) $(libretro_new)
302+ # until emscripten adds something like WASM_WORKERS=2 but for audio worklets, DIY.
303+ ifeq ($(HAVE_AUDIOWORKLET), 1)
304+ $(TARGET): $(RARCH_OBJ) $(libretro_new) mv_libretro
227305 @$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
228306 $(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
307+ $(Q)tr -d '\n' < "$(TARGET_BASE).aw.js" | sed -e "s/[\/&]/\\\\&/g" -e "s/'/\\\\\\\\&/g" > _audioworklet.js
308+ $(Q)sed -i.bak -e "s/\"$(TARGET_BASE)\.aw\.js\"/URL.createObjectURL(new Blob(['$$(cat _audioworklet.js)'],{type:'text\/javascript'}))/" -- "$@"
309+ $(Q)rm -f "$(TARGET_BASE).aw.js" _audioworklet.js "$@".bak
310+ else
311+ $(TARGET): $(RARCH_OBJ) $(libretro_new) mv_libretro
312+ @$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
313+ $(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
314+ endif
229315
230316$(OBJDIR)/%.o: %.c
231317 @mkdir -p $(dir $@)
232318 @$(if $(Q), $(shell echo echo CC $<),)
233- $(Q)$(CC) $(CFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $<
319+ $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
234320
235321$(OBJDIR)/%.o: %.cpp
236322 @mkdir -p $(dir $@)
237323 @$(if $(Q), $(shell echo echo CXX $<),)
238- $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $<
324+ $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $<
239325
240326clean:
241327 rm -rf $(OBJDIR)
242328 rm -f $(TARGET)
243329
244- .PHONY: all clean
330+ .PHONY: all clean mv_libretro
0 commit comments