diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/input_default.cpp | 122 | ||||
-rw-r--r-- | main/input_default.h | 17 | ||||
-rw-r--r-- | main/main.cpp | 269 | ||||
-rw-r--r-- | main/main.h | 1 | ||||
-rw-r--r-- | main/tests/test_oa_hash_map.cpp | 6 |
5 files changed, 371 insertions, 44 deletions
diff --git a/main/input_default.cpp b/main/input_default.cpp index 3c40be5082..29d30110e3 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -194,8 +194,6 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; - js.mapping = -1; - js.hat_current = 0; if (p_connected) { @@ -258,6 +256,11 @@ Vector3 InputDefault::get_gyroscope() const { void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { + _parse_input_event_impl(p_event, false); +} + +void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { + _THREAD_SAFE_METHOD_ Ref<InputEventKey> k = p_event; @@ -281,25 +284,30 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { mouse_button_mask &= ~(1 << (mb->get_button_index() - 1)); } - if (main_loop && emulate_touch && mb->get_button_index() == 1) { + Point2 pos = mb->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) { Ref<InputEventScreenTouch> touch_event; touch_event.instance(); touch_event->set_pressed(mb->is_pressed()); touch_event->set_position(mb->get_position()); main_loop->input_event(touch_event); } - - Point2 pos = mb->get_global_position(); - if (mouse_pos != pos) { - set_mouse_position(pos); - } } Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (main_loop && emulate_touch && mm->get_button_mask() & 1) { + Point2 pos = mm->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) { Ref<InputEventScreenDrag> drag_event; drag_event.instance(); @@ -311,6 +319,58 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { } } + if (emulate_mouse_from_touch) { + + Ref<InputEventScreenTouch> st = p_event; + + if (st.is_valid()) { + bool translate = false; + if (st->is_pressed()) { + if (mouse_from_touch_index == -1) { + translate = true; + mouse_from_touch_index = st->get_index(); + } + } else { + if (st->get_index() == mouse_from_touch_index) { + translate = true; + mouse_from_touch_index = -1; + } + } + + if (translate) { + Ref<InputEventMouseButton> button_event; + button_event.instance(); + + button_event->set_position(st->get_position()); + button_event->set_global_position(st->get_position()); + button_event->set_pressed(st->is_pressed()); + button_event->set_button_index(BUTTON_LEFT); + if (st->is_pressed()) { + button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1)); + } else { + button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + } + + _parse_input_event_impl(button_event, true); + } + } + + Ref<InputEventScreenDrag> sd = p_event; + + if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) { + Ref<InputEventMouseMotion> motion_event; + motion_event.instance(); + + motion_event->set_position(sd->get_position()); + motion_event->set_global_position(sd->get_position()); + motion_event->set_relative(sd->get_relative()); + motion_event->set_speed(sd->get_speed()); + motion_event->set_button_mask(mouse_button_mask); + + _parse_input_event_impl(motion_event, true); + } + } + Ref<InputEventJoypadButton> jb = p_event; if (jb.is_valid()) { @@ -495,14 +555,44 @@ void InputDefault::action_release(const StringName &p_action) { action_state[p_action] = action; } -void InputDefault::set_emulate_touch(bool p_emulate) { +void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) { + + emulate_touch_from_mouse = p_emulate; +} + +bool InputDefault::is_emulating_touch_from_mouse() const { + + return emulate_touch_from_mouse; +} + +// Calling this whenever the game window is focused helps unstucking the "touch mouse" +// if the OS or its abstraction class hasn't properly reported that touch pointers raised +void InputDefault::ensure_touch_mouse_raised() { + + if (mouse_from_touch_index != -1) { + mouse_from_touch_index = -1; + + Ref<InputEventMouseButton> button_event; + button_event.instance(); + + button_event->set_position(mouse_pos); + button_event->set_global_position(mouse_pos); + button_event->set_pressed(false); + button_event->set_button_index(BUTTON_LEFT); + button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + + _parse_input_event_impl(button_event, true); + } +} + +void InputDefault::set_emulate_mouse_from_touch(bool p_emulate) { - emulate_touch = p_emulate; + emulate_mouse_from_touch = p_emulate; } -bool InputDefault::is_emulating_touchscreen() const { +bool InputDefault::is_emulating_mouse_from_touch() const { - return emulate_touch; + return emulate_mouse_from_touch; } Input::CursorShape InputDefault::get_default_cursor_shape() { @@ -539,7 +629,9 @@ void InputDefault::set_mouse_in_window(bool p_in_window) { InputDefault::InputDefault() { mouse_button_mask = 0; - emulate_touch = false; + emulate_touch_from_mouse = false; + emulate_mouse_from_touch = false; + mouse_from_touch_index = -1; main_loop = NULL; hat_map_default[HAT_UP].type = TYPE_BUTTON; @@ -797,12 +889,12 @@ InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) { JoyEvent ret; ret.type = -1; + ret.index = 0; int i = 0; while (buttons[i]) { if (p_to == buttons[i]) { - //printf("mapping button %s\n", buttons[i]); ret.type = TYPE_BUTTON; ret.index = i; ret.value = 0; diff --git a/main/input_default.h b/main/input_default.h index 6dd88cd31e..2e3cae8520 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -60,7 +60,10 @@ class InputDefault : public Input { Map<StringName, Action> action_state; - bool emulate_touch; + bool emulate_touch_from_mouse; + bool emulate_mouse_from_touch; + + int mouse_from_touch_index; struct VibrationInfo { float weak_magnitude; @@ -97,7 +100,6 @@ class InputDefault : public Input { int hat_current; Joypad() { - for (int i = 0; i < JOY_AXIS_MAX; i++) { last_axis[i] = 0.0f; @@ -110,6 +112,7 @@ class InputDefault : public Input { last_hat = HAT_MASK_CENTER; filter = 0.01f; mapping = -1; + hat_current = 0; } }; @@ -176,6 +179,8 @@ private: void _axis_event(int p_device, int p_axis, float p_value); float _handle_deadzone(int p_device, int p_axis, float p_value); + void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); + public: virtual bool is_key_pressed(int p_scancode) const; virtual bool is_mouse_button_pressed(int p_button) const; @@ -225,8 +230,12 @@ public: void iteration(float p_step); - void set_emulate_touch(bool p_emulate); - virtual bool is_emulating_touchscreen() const; + void set_emulate_touch_from_mouse(bool p_emulate); + virtual bool is_emulating_touch_from_mouse() const; + void ensure_touch_mouse_raised(); + + void set_emulate_mouse_from_touch(bool p_emulate); + virtual bool is_emulating_mouse_from_touch() const; virtual CursorShape get_default_cursor_shape(); virtual void set_default_cursor_shape(CursorShape p_shape); diff --git a/main/main.cpp b/main/main.cpp index 3542133719..5959deb495 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -730,6 +730,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (debug_mode == "local") { script_debugger = memnew(ScriptDebuggerLocal); + OS::get_singleton()->initialize_debugging(); } FileAccessNetwork::configure(); @@ -955,6 +956,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60)); + Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); GLOBAL_DEF("debug/settings/stdout/print_fps", false); @@ -1141,13 +1143,16 @@ Error Main::setup2(Thread::ID p_main_tid_override) { GLOBAL_DEF("application/config/icon", String()); ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp")); - if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false))) { - if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !(editor || project_manager)) { - //only if no touchscreen ui hint, set emulation - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); - if (id) - id->set_emulate_touch(true); + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + if (bool(GLOBAL_DEF("input/pointing_devices/emulate_touch_from_mouse", false)) && !(editor || project_manager)) { + if (!OS::get_singleton()->has_touchscreen_ui_hint()) { + //only if no touchscreen ui hint, set emulation + id->set_emulate_touch_from_mouse(true); + } } + + id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input/pointing_devices/emulate_mouse_from_touch", true))); } MAIN_PRINT("Main: Load Remaps"); @@ -1225,6 +1230,229 @@ Error Main::setup2(Thread::ID p_main_tid_override) { return OK; } +// everything the main loop needs to know about frame timings +struct _FrameTime { + float animation_step; // time to advance animations for (argument to process()) + int physics_steps; // number of times to iterate the physics engine + + void clamp_animation(float min_animation_step, float max_animation_step) { + if (animation_step < min_animation_step) { + animation_step = min_animation_step; + } else if (animation_step > max_animation_step) { + animation_step = max_animation_step; + } + } +}; + +class _TimerSync { + // wall clock time measured on the main thread + uint64_t last_cpu_ticks_usec; + uint64_t current_cpu_ticks_usec; + + // logical game time since last physics timestep + float time_accum; + + // current difference between wall clock time and reported sum of animation_steps + float time_deficit; + + // number of frames back for keeping accumulated physics steps roughly constant. + // value of 12 chosen because that is what is required to make 144 Hz monitors + // behave well with 60 Hz physics updates. The only worse commonly available refresh + // would be 85, requiring CONTROL_STEPS = 17. + static const int CONTROL_STEPS = 12; + + // sum of physics steps done over the last (i+1) frames + int accumulated_physics_steps[CONTROL_STEPS]; + + // typical value for accumulated_physics_steps[i] is either this or this plus one + int typical_physics_steps[CONTROL_STEPS]; + +protected: + // returns the fraction of p_frame_slice required for the timer to overshoot + // before advance_core considers changing the physics_steps return from + // the typical values as defined by typical_physics_steps + float get_physics_jitter_fix() { + return Engine::get_singleton()->get_physics_jitter_fix(); + } + + // gets our best bet for the average number of physics steps per render frame + // return value: number of frames back this data is consistent + int get_average_physics_steps(float &p_min, float &p_max) { + p_min = typical_physics_steps[0]; + p_max = p_min + 1; + + for (int i = 1; i < CONTROL_STEPS; ++i) { + const float typical_lower = typical_physics_steps[i]; + const float current_min = typical_lower / (i + 1); + if (current_min > p_max) + return i; // bail out of further restrictions would void the interval + else if (current_min > p_min) + p_min = current_min; + const float current_max = (typical_lower + 1) / (i + 1); + if (current_max < p_min) + return i; + else if (current_max < p_max) + p_max = current_max; + } + + return CONTROL_STEPS; + } + + // advance physics clock by p_animation_step, return appropriate number of steps to simulate + _FrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_animation_step) { + _FrameTime ret; + + ret.animation_step = p_animation_step; + + // simple determination of number of physics iteration + time_accum += ret.animation_step; + ret.physics_steps = floor(time_accum * p_iterations_per_second); + + int min_typical_steps = typical_physics_steps[0]; + int max_typical_steps = min_typical_steps + 1; + + // given the past recorded steps and typcial steps to match, calculate bounds for this + // step to be typical + bool update_typical = false; + + for (int i = 0; i < CONTROL_STEPS - 1; ++i) { + int steps_left_to_match_typical = typical_physics_steps[i + 1] - accumulated_physics_steps[i]; + if (steps_left_to_match_typical > max_typical_steps || + steps_left_to_match_typical + 1 < min_typical_steps) { + update_typical = true; + break; + } + + if (steps_left_to_match_typical > min_typical_steps) + min_typical_steps = steps_left_to_match_typical; + if (steps_left_to_match_typical + 1 < max_typical_steps) + max_typical_steps = steps_left_to_match_typical + 1; + } + + // try to keep it consistent with previous iterations + if (ret.physics_steps < min_typical_steps) { + const int max_possible_steps = floor((time_accum)*p_iterations_per_second + get_physics_jitter_fix()); + if (max_possible_steps < min_typical_steps) { + ret.physics_steps = max_possible_steps; + update_typical = true; + } else { + ret.physics_steps = min_typical_steps; + } + } else if (ret.physics_steps > max_typical_steps) { + const int min_possible_steps = floor((time_accum)*p_iterations_per_second - get_physics_jitter_fix()); + if (min_possible_steps > max_typical_steps) { + ret.physics_steps = min_possible_steps; + update_typical = true; + } else { + ret.physics_steps = max_typical_steps; + } + } + + time_accum -= ret.physics_steps * p_frame_slice; + + // keep track of accumulated step counts + for (int i = CONTROL_STEPS - 2; i >= 0; --i) { + accumulated_physics_steps[i + 1] = accumulated_physics_steps[i] + ret.physics_steps; + } + accumulated_physics_steps[0] = ret.physics_steps; + + if (update_typical) { + for (int i = CONTROL_STEPS - 1; i >= 0; --i) { + if (typical_physics_steps[i] > accumulated_physics_steps[i]) { + typical_physics_steps[i] = accumulated_physics_steps[i]; + } else if (typical_physics_steps[i] < accumulated_physics_steps[i] - 1) { + typical_physics_steps[i] = accumulated_physics_steps[i] - 1; + } + } + } + + return ret; + } + + // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero + _FrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_animation_step) { + if (fixed_fps != -1) + p_animation_step = 1.0 / fixed_fps; + + // compensate for last deficit + p_animation_step += time_deficit; + + _FrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_animation_step); + + // we will do some clamping on ret.animation_step and need to sync those changes to time_accum, + // that's easiest if we just remember their fixed difference now + const double animation_minus_accum = ret.animation_step - time_accum; + + // first, least important clamping: keep ret.animation_step consistent with typical_physics_steps. + // this smoothes out the animation steps and culls small but quick variations. + { + float min_average_physics_steps, max_average_physics_steps; + int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps); + if (consistent_steps > 3) { + ret.clamp_animation(min_average_physics_steps * p_frame_slice, max_average_physics_steps * p_frame_slice); + } + } + + // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise + float max_clock_deviation = get_physics_jitter_fix() * p_frame_slice; + ret.clamp_animation(p_animation_step - max_clock_deviation, p_animation_step + max_clock_deviation); + + // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and animation + ret.clamp_animation(animation_minus_accum, animation_minus_accum + p_frame_slice); + + // restore time_accum + time_accum = ret.animation_step - animation_minus_accum; + + // track deficit + time_deficit = p_animation_step - ret.animation_step; + + return ret; + } + + // determine wall clock step since last iteration + float get_cpu_animation_step() { + uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec; + last_cpu_ticks_usec = current_cpu_ticks_usec; + + return cpu_ticks_elapsed / 1000000.0; + } + +public: + explicit _TimerSync() : + last_cpu_ticks_usec(0), + current_cpu_ticks_usec(0), + time_accum(0), + time_deficit(0) { + for (int i = CONTROL_STEPS - 1; i >= 0; --i) { + typical_physics_steps[i] = i; + accumulated_physics_steps[i] = i; + } + } + + // start the clock + void init(uint64_t p_cpu_ticks_usec) { + current_cpu_ticks_usec = last_cpu_ticks_usec = p_cpu_ticks_usec; + } + + // set measured wall clock time + void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) { + current_cpu_ticks_usec = p_cpu_ticks_usec; + } + + // advance one frame, return timesteps to take + _FrameTime advance(float p_frame_slice, int p_iterations_per_second) { + float cpu_animation_step = get_cpu_animation_step(); + + return advance_checked(p_frame_slice, p_iterations_per_second, cpu_animation_step); + } + + void before_start_render() { + VisualServer::get_singleton()->sync(); + } +}; + +static _TimerSync _timer_sync; + bool Main::start() { ERR_FAIL_COND_V(!_start_success, false); @@ -1239,6 +1467,8 @@ bool Main::start() { String _export_preset; bool export_debug = false; + _timer_sync.init(OS::get_singleton()->get_ticks_usec()); + List<String> args = OS::get_singleton()->get_cmdline_args(); for (int i = 0; i < args.size(); i++) { //parameters that do not have an argument to the right @@ -1704,7 +1934,6 @@ bool Main::start() { uint64_t Main::last_ticks = 0; uint64_t Main::target_ticks = 0; -float Main::time_accum = 0; uint32_t Main::frames = 0; uint32_t Main::frame = 0; bool Main::force_redraw_requested = false; @@ -1717,14 +1946,15 @@ bool Main::iteration() { uint64_t ticks = OS::get_singleton()->get_ticks_usec(); Engine::get_singleton()->_frame_ticks = ticks; + _timer_sync.set_cpu_ticks_usec(ticks); uint64_t ticks_elapsed = ticks - last_ticks; - double step = (double)ticks_elapsed / 1000000.0; - if (fixed_fps != -1) - step = 1.0 / fixed_fps; + int physics_fps = Engine::get_singleton()->get_iterations_per_second(); + float frame_slice = 1.0 / physics_fps; - float frame_slice = 1.0 / Engine::get_singleton()->get_iterations_per_second(); + _FrameTime advance = _timer_sync.advance(frame_slice, physics_fps); + double step = advance.animation_step; Engine::get_singleton()->_frame_step = step; @@ -1740,20 +1970,19 @@ bool Main::iteration() { last_ticks = ticks; - if (fixed_fps == -1 && step > frame_slice * 8) - step = frame_slice * 8; - - time_accum += step; + static const int max_physics_steps = 8; + if (fixed_fps == -1 && advance.physics_steps > max_physics_steps) { + step -= (advance.physics_steps - max_physics_steps) * frame_slice; + advance.physics_steps = max_physics_steps; + } float time_scale = Engine::get_singleton()->get_time_scale(); bool exit = false; - int iters = 0; - Engine::get_singleton()->_in_physics = true; - while (time_accum > frame_slice) { + for (int iters = 0; iters < advance.physics_steps; ++iters) { uint64_t physics_begin = OS::get_singleton()->get_ticks_usec(); @@ -1775,12 +2004,10 @@ bool Main::iteration() { Physics2DServer::get_singleton()->end_sync(); Physics2DServer::get_singleton()->step(frame_slice * time_scale); - time_accum -= frame_slice; message_queue->flush(); physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin); // keep the largest one for reference physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max); - iters++; Engine::get_singleton()->_physics_frames++; } @@ -1791,7 +2018,7 @@ bool Main::iteration() { OS::get_singleton()->get_main_loop()->idle(step * time_scale); message_queue->flush(); - VisualServer::get_singleton()->sync(); //sync if still drawing from previous frames. + _timer_sync.before_start_render(); //sync if still drawing from previous frames. if (OS::get_singleton()->can_draw() && !disable_render_loop) { diff --git a/main/main.h b/main/main.h index 8b805fa1d0..c20592bf3b 100644 --- a/main/main.h +++ b/main/main.h @@ -44,7 +44,6 @@ class Main { static void print_help(const char *p_binary); static uint64_t last_ticks; static uint64_t target_ticks; - static float time_accum; static uint32_t frames; static uint32_t frame; static bool force_redraw_requested; diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp index ac65fdf19c..0e34faace7 100644 --- a/main/tests/test_oa_hash_map.cpp +++ b/main/tests/test_oa_hash_map.cpp @@ -49,7 +49,7 @@ MainLoop *test() { map.set(42, 11880); int value; - map.lookup(42, &value); + map.lookup(42, value); OS::get_singleton()->print("capacity %d\n", map.get_capacity()); OS::get_singleton()->print("elements %d\n", map.get_num_elements()); @@ -72,7 +72,7 @@ MainLoop *test() { uint32_t num_elems = 0; for (int i = 0; i < 500; i++) { int tmp; - if (map.lookup(i, &tmp)) + if (map.lookup(i, tmp) && tmp == i * 2) num_elems++; } @@ -88,7 +88,7 @@ MainLoop *test() { map.set("Godot rocks", 42); for (OAHashMap<String, int>::Iterator it = map.iter(); it.valid; it = map.next_iter(it)) { - OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.data); + OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.value); } } |