Bladeren bron

Merge pull request #40052 from Faless/js/more_improvements_3.2

[3.2]  HTML5 fixes, audio fallback, fixed FPS.
Rémi Verschelde 5 jaren geleden
bovenliggende
commit
2759fe85dc

+ 13 - 3
platform/javascript/audio_driver_javascript.cpp

@@ -36,6 +36,15 @@
 
 AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
 
+bool AudioDriverJavaScript::is_available() {
+	return EM_ASM_INT({
+		if (!(window.AudioContext || window.webkitAudioContext)) {
+			return 0;
+		}
+		return 1;
+	}) != 0;
+}
+
 const char *AudioDriverJavaScript::get_name() const {
 	return "JavaScript";
 }
@@ -207,12 +216,14 @@ void AudioDriverJavaScript::finish_async() {
 
 	/* clang-format off */
 	EM_ASM({
-		var ref = Module.IDHandler.get($0);
+		const id = $0;
+		var ref = Module.IDHandler.get(id);
 		Module.async_finish.push(new Promise(function(accept, reject) {
 			if (!ref) {
-				console.log("Ref not found!", $0, Module.IDHandler);
+				console.log("Ref not found!", id, Module.IDHandler);
 				setTimeout(accept, 0);
 			} else {
+				Module.IDHandler.remove(id);
 				const context = ref['context'];
 				// Disconnect script and input.
 				ref['script'].disconnect();
@@ -226,7 +237,6 @@ void AudioDriverJavaScript::finish_async() {
 				});
 			}
 		}));
-		Module.IDHandler.remove($0);
 	}, id);
 	/* clang-format on */
 }

+ 1 - 0
platform/javascript/audio_driver_javascript.h

@@ -41,6 +41,7 @@ class AudioDriverJavaScript : public AudioDriver {
 	int buffer_length;
 
 public:
+	static bool is_available();
 	void mix_to_js();
 	void process_capture(float sample);
 

+ 29 - 10
platform/javascript/javascript_main.cpp

@@ -30,11 +30,12 @@
 
 #include "core/io/resource_loader.h"
 #include "main/main.h"
-#include "os_javascript.h"
+#include "platform/javascript/os_javascript.h"
 
 #include <emscripten/emscripten.h>
 
 static OS_JavaScript *os = NULL;
+static uint64_t target_ticks = 0;
 
 // Files drop (implemented in JS for now).
 extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p_filec) {
@@ -58,13 +59,32 @@ void exit_callback() {
 }
 
 void main_loop_callback() {
+	uint64_t current_ticks = os->get_ticks_usec();
 
+	bool force_draw = os->check_size_force_redraw();
+	if (force_draw) {
+		Main::force_redraw();
+	} else if (current_ticks < target_ticks && !force_draw) {
+		return; // Skip frame.
+	}
+
+	int target_fps = Engine::get_singleton()->get_target_fps();
+	if (target_fps > 0) {
+		target_ticks += (uint64_t)(1000000 / target_fps);
+	}
 	if (os->main_loop_iterate()) {
 		emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
+		/* clang-format off */
 		EM_ASM({
 			// This will contain the list of operations that need to complete before cleanup.
-			Module.async_finish = [];
+			Module.async_finish = [
+				// Always contains at least one async promise, to avoid firing immediately if nothing is added.
+				new Promise(function(accept, reject) {
+					setTimeout(accept, 0);
+				})
+			];
 		});
+		/* clang-format on */
 		os->get_main_loop()->finish();
 		os->finalize_async(); // Will add all the async finish functions.
 		EM_ASM({
@@ -81,9 +101,6 @@ extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
 }
 
 extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
-
-	OS_JavaScript *os = OS_JavaScript::get_singleton();
-
 	// Set IDBFS status
 	String idbfs_err = String::utf8(p_idbfs_err);
 	if (!idbfs_err.empty()) {
@@ -106,7 +123,6 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
 	EM_ASM({
 		stringToUTF8(Module['locale'], $0, 16);
 	}, locale_ptr);
-
 	/* clang-format on */
 	setenv("LANG", locale_ptr, true);
 
@@ -115,14 +131,19 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
 	ResourceLoader::set_abort_on_missing_resources(false);
 	Main::start();
 	os->get_main_loop()->init();
+	// Immediately run the first iteration.
+	// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
 	main_loop_callback();
 	emscripten_resume_main_loop();
 }
 
 int main(int argc, char *argv[]) {
-
+	// Create and mount userfs immediately.
+	EM_ASM({
+		FS.mkdir('/userfs');
+		FS.mount(IDBFS, {}, '/userfs');
+	});
 	os = new OS_JavaScript(argc, argv);
-	// TODO: Check error return value.
 	Main::setup(argv[0], argc - 1, &argv[1], false);
 	emscripten_set_main_loop(main_loop_callback, -1, false);
 	emscripten_pause_main_loop(); // Will need to wait for FS sync.
@@ -131,8 +152,6 @@ int main(int argc, char *argv[]) {
 	// run the 'main_after_fs_sync' function.
 	/* clang-format off */
 	EM_ASM({
-		FS.mkdir('/userfs');
-		FS.mount(IDBFS, {}, '/userfs');
 		FS.syncfs(true, function(err) {
 			requestAnimationFrame(function() {
 				ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);

+ 35 - 21
platform/javascript/os_javascript.cpp

@@ -94,18 +94,22 @@ static Point2 compute_position_in_canvas(int x, int y) {
 			(int)(canvas_height / element_height * (y - canvas_y)));
 }
 
-static bool cursor_inside_canvas = true;
-
-extern "C" EMSCRIPTEN_KEEPALIVE void _canvas_resize_callback() {
-	OS_JavaScript *os = OS_JavaScript::get_singleton();
+bool OS_JavaScript::check_size_force_redraw() {
 	int canvas_width;
 	int canvas_height;
-	// Update the framebuffer size.
-	emscripten_get_canvas_element_size(os->canvas_id.utf8().get_data(), &canvas_width, &canvas_height);
-	emscripten_set_canvas_element_size(os->canvas_id.utf8().get_data(), canvas_width, canvas_height);
-	Main::force_redraw();
+	emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), &canvas_width, &canvas_height);
+	if (last_width != canvas_width || last_height != canvas_height) {
+		last_width = canvas_width;
+		last_height = canvas_height;
+		// Update the framebuffer size and for redraw.
+		emscripten_set_canvas_element_size(canvas_id.utf8().get_data(), canvas_width, canvas_height);
+		return true;
+	}
+	return false;
 }
 
+static bool cursor_inside_canvas = true;
+
 EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
 
 	OS_JavaScript *os = get_singleton();
@@ -293,7 +297,7 @@ EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboa
 	}
 	os->input->parse_input_event(ev);
 	// Resume audio context after input in case autoplay was denied.
-	os->audio_driver_javascript.resume();
+	os->resume_audio();
 	return true;
 }
 
@@ -386,7 +390,7 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM
 
 	os->input->parse_input_event(ev);
 	// Resume audio context after input in case autoplay was denied.
-	os->audio_driver_javascript.resume();
+	os->resume_audio();
 	// Prevent multi-click text selection and wheel-click scrolling anchor.
 	// Context menu is prevented through contextmenu event.
 	return true;
@@ -738,7 +742,7 @@ EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTo
 		os->input->parse_input_event(ev);
 	}
 	// Resume audio context after input in case autoplay was denied.
-	os->audio_driver_javascript.resume();
+	os->resume_audio();
 	return true;
 }
 
@@ -1062,12 +1066,6 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
 		Module.listeners['drop'] = Module.drop_handler; // Defined in native/utils.js
 		canvas.addEventListener('dragover', Module.listeners['dragover'], false);
 		canvas.addEventListener('drop', Module.listeners['drop'], false);
-		// Resize
-		const resize_callback = cwrap('_canvas_resize_callback', null, []);
-		Module.resize_observer = new window['ResizeObserver'](function(elements) {
-			resize_callback();
-		});
-		Module.resize_observer.observe(canvas);
 		// Quit request
 		Module['request_quit'] = function() {
 			send_notification(notifications[notifications.length - 1]);
@@ -1101,6 +1099,12 @@ MainLoop *OS_JavaScript::get_main_loop() const {
 	return main_loop;
 }
 
+void OS_JavaScript::resume_audio() {
+	if (audio_driver_javascript) {
+		audio_driver_javascript->resume();
+	}
+}
+
 bool OS_JavaScript::main_loop_iterate() {
 
 	if (is_userfs_persistent() && sync_wait_time >= 0) {
@@ -1167,10 +1171,10 @@ void OS_JavaScript::finalize_async() {
 			}
 		});
 		Module.listeners = {};
-		Module.resize_observer.unobserve(canvas);
-		delete Module.resize_observer;
 	});
-	audio_driver_javascript.finish_async();
+	if (audio_driver_javascript) {
+		audio_driver_javascript->finish_async();
+	}
 }
 
 void OS_JavaScript::finalize() {
@@ -1180,6 +1184,9 @@ void OS_JavaScript::finalize() {
 	emscripten_webgl_commit_frame();
 	memdelete(visual_server);
 	emscripten_webgl_destroy_context(webgl_ctx);
+	if (audio_driver_javascript) {
+		memdelete(audio_driver_javascript);
+	}
 }
 
 // Miscellaneous
@@ -1409,6 +1416,9 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
 	last_click_ms = 0;
 	last_click_pos = Point2(-100, -100);
 
+	last_width = 0;
+	last_height = 0;
+
 	window_maximized = false;
 	entering_fullscreen = false;
 	just_exited_fullscreen = false;
@@ -1416,11 +1426,15 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
 
 	main_loop = NULL;
 	visual_server = NULL;
+	audio_driver_javascript = NULL;
 
 	idb_available = false;
 	sync_wait_time = -1;
 
-	AudioDriverManager::add_driver(&audio_driver_javascript);
+	if (AudioDriverJavaScript::is_available()) {
+		audio_driver_javascript = memnew(AudioDriverJavaScript);
+		AudioDriverManager::add_driver(audio_driver_javascript);
+	}
 
 	Vector<Logger *> loggers;
 	loggers.push_back(memnew(StdLogger));

+ 8 - 1
platform/javascript/os_javascript.h

@@ -61,9 +61,12 @@ class OS_JavaScript : public OS_Unix {
 	double last_click_ms;
 	int last_click_button_index;
 
+	int last_width;
+	int last_height;
+
 	MainLoop *main_loop;
 	int video_driver_index;
-	AudioDriverJavaScript audio_driver_javascript;
+	AudioDriverJavaScript *audio_driver_javascript;
 	VisualServer *visual_server;
 
 	bool idb_available;
@@ -90,6 +93,8 @@ class OS_JavaScript : public OS_Unix {
 	static void file_access_close_callback(const String &p_file, int p_flags);
 
 protected:
+	void resume_audio();
+
 	virtual int get_current_video_driver() const;
 
 	virtual void initialize_core();
@@ -105,6 +110,7 @@ protected:
 public:
 	String canvas_id;
 	void finalize_async();
+	bool check_size_force_redraw();
 
 	// Override return type to make writing static callbacks less tedious.
 	static OS_JavaScript *get_singleton();
@@ -159,6 +165,7 @@ public:
 	String get_executable_path() const;
 	virtual Error shell_open(String p_uri);
 	virtual String get_name() const;
+	virtual void add_frame_delay(bool p_can_draw) {}
 	virtual bool can_draw() const;
 
 	virtual String get_cache_path() const;

+ 1 - 0
servers/audio_server.cpp

@@ -184,6 +184,7 @@ void AudioDriverManager::initialize(int p_driver) {
 	GLOBAL_DEF_RST("audio/enable_audio_input", false);
 	GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
 	GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
+	GLOBAL_DEF_RST("audio/output_latency.web", 50); // Safer default output_latency for web.
 
 	int failed_driver = -1;