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

[3.2]  HTML5 fixes, audio fallback, fixed FPS.
This commit is contained in:
Rémi Verschelde 2020-07-02 20:24:44 +02:00 committed by GitHub
commit 2759fe85dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 35 deletions

View file

@ -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 */
}

View file

@ -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);

View file

@ -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 : ""]);

View file

@ -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));

View file

@ -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;

View file

@ -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;