Merge pull request #40052 from Faless/js/more_improvements_3.2
[3.2] HTML5 fixes, audio fallback, fixed FPS.
This commit is contained in:
commit
2759fe85dc
6 changed files with 87 additions and 35 deletions
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 : ""]);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue