diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/SCsub | 8 | ||||
-rw-r--r-- | main/app_icon.png | bin | 5569 -> 3770 bytes | |||
-rw-r--r-- | main/main.cpp | 461 | ||||
-rw-r--r-- | main/performance.cpp | 5 | ||||
-rw-r--r-- | main/performance.h | 1 | ||||
-rw-r--r-- | main/splash.png | bin | 21504 -> 15311 bytes | |||
-rw-r--r-- | main/splash_editor.png | bin | 39571 -> 32577 bytes | |||
-rw-r--r-- | main/tests/test_main.cpp | 13 | ||||
-rw-r--r-- | main/tests/test_shader_lang.cpp | 4 | ||||
-rw-r--r-- | main/timer_sync.cpp | 223 | ||||
-rw-r--r-- | main/timer_sync.h | 101 |
11 files changed, 462 insertions, 354 deletions
diff --git a/main/SCsub b/main/SCsub index e2bf03234f..0692175799 100644 --- a/main/SCsub +++ b/main/SCsub @@ -131,20 +131,20 @@ env.add_source_files(env.main_sources, "*.cpp") controller_databases = ["#main/gamecontrollerdb.txt", "#main/gamecontrollerdb_205.txt", "#main/gamecontrollerdb_204.txt", "#main/godotcontrollerdb.txt"] env.Depends("#main/default_controller_mappings.gen.cpp", controller_databases) -env.Command("#main/default_controller_mappings.gen.cpp", controller_databases, make_default_controller_mappings) +env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_databases, make_default_controller_mappings) env.main_sources.append("#main/default_controller_mappings.gen.cpp") Export('env') env.Depends("#main/splash.gen.h", "#main/splash.png") -env.Command("#main/splash.gen.h", "#main/splash.png", make_splash) +env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", make_splash) env.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png") -env.Command("#main/splash_editor.gen.h", "#main/splash_editor.png", make_splash_editor) +env.CommandNoCache("#main/splash_editor.gen.h", "#main/splash_editor.png", make_splash_editor) env.Depends("#main/app_icon.gen.h", "#main/app_icon.png") -env.Command("#main/app_icon.gen.h", "#main/app_icon.png", make_app_icon) +env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", make_app_icon) SConscript('tests/SCsub') diff --git a/main/app_icon.png b/main/app_icon.png Binary files differindex 1d75cdc710..cf31af18a4 100644 --- a/main/app_icon.png +++ b/main/app_icon.png diff --git a/main/main.cpp b/main/main.cpp index 1c5540fd19..23acb60c89 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -82,6 +82,8 @@ #include "version.h" #include "version_hash.gen.h" +#include "main/timer_sync.h" + static ProjectSettings *globals = NULL; static Engine *engine = NULL; static InputMap *input_map = NULL; @@ -261,9 +263,10 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print("Standalone tools:\n"); OS::get_singleton()->print(" -s, --script <script> Run a script.\n"); + OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n"); #ifdef TOOLS_ENABLED - OS::get_singleton()->print(" --export <target> Export the project using the given export target.\n"); - OS::get_singleton()->print(" --export-debug Use together with --export, enables debug mode for the template.\n"); + OS::get_singleton()->print(" --export <target> Export the project using the given export target. Export only main pack if path ends with .pck or .zip'.\n"); + OS::get_singleton()->print(" --export-debug <target> Like --export, but use debug template.\n"); OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n"); OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n"); OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects).\n"); @@ -337,7 +340,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph String video_driver = ""; String audio_driver = ""; - String game_path = "."; + String project_path = "."; bool upwards = false; String debug_mode; String debug_host; @@ -536,6 +539,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--build-solutions") { // Build the scripting solution such C# auto_build_solutions = true; + editor = true; #endif } else if (I->get() == "--no-window") { // disable window creation, Windows only @@ -553,7 +557,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (OS::get_singleton()->set_cwd(p) == OK) { //nothing } else { - game_path = I->next()->get(); //use game_path instead + project_path = I->next()->get(); //use project_path instead } N = I->next()->next(); } else { @@ -576,7 +580,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (OS::get_singleton()->set_cwd(path) == OK) { // path already specified, don't override } else { - game_path = path; + project_path = path; } #ifdef TOOLS_ENABLED editor = true; @@ -672,35 +676,20 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--disable-crash-handler") { OS::get_singleton()->disable_crash_handler(); } else { - - //test for game path - bool gpfound = false; - - if (!I->get().begins_with("-") && game_path == "") { - DirAccess *da = DirAccess::open(I->get()); - if (da != NULL) { - game_path = I->get(); - gpfound = true; - memdelete(da); - } - } - - if (!gpfound) { - main_args.push_back(I->get()); - } + main_args.push_back(I->get()); } I = N; } - if (globals->setup(game_path, main_pack, upwards) == OK) { + if (globals->setup(project_path, main_pack, upwards) == OK) { found_project = true; } else { #ifdef TOOLS_ENABLED editor = false; #else - OS::get_singleton()->print("Error: Could not load game path '%s'.\n", game_path.ascii().get_data()); + OS::get_singleton()->print("Error: Could not load game path '%s'.\n", project_path.ascii().get_data()); goto error; #endif @@ -726,6 +715,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph memdelete(sdr); } else { script_debugger = sdr; + sdr->set_allow_focus_steal_pid(allow_focus_steal_pid); } } else if (debug_mode == "local") { @@ -878,6 +868,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/allow_per_pixel_transparency", false); video_mode.use_vsync = GLOBAL_DEF("display/window/vsync/use_vsync", true); + OS::get_singleton()->_use_vsync = video_mode.use_vsync; + video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency", false); video_mode.layered_splash = GLOBAL_DEF("display/window/per_pixel_transparency_splash", false); @@ -991,7 +983,7 @@ error: video_driver = ""; audio_driver = ""; - game_path = ""; + project_path = ""; args.clear(); main_args.clear(); @@ -1150,14 +1142,14 @@ Error Main::setup2(Thread::ID p_main_tid_override) { 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 (bool(GLOBAL_DEF("input_devices/pointing/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))); + id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input_devices/pointing/emulate_mouse_from_touch", true))); } MAIN_PRINT("Main: Load Remaps"); @@ -1188,10 +1180,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) { #endif - if (allow_focus_steal_pid) { - OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid); - } - MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts"); register_platform_apis(); @@ -1236,227 +1224,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } // 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; +static MainTimerSync main_timer_sync; bool Main::start() { @@ -1471,8 +1240,9 @@ bool Main::start() { String test; String _export_preset; bool export_debug = false; + bool check_only = false; - _timer_sync.init(OS::get_singleton()->get_ticks_usec()); + main_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++) { @@ -1493,6 +1263,8 @@ bool Main::start() { bool parsed_pair = true; if (args[i] == "-s" || args[i] == "--script") { script = args[i + 1]; + } else if (args[i] == "--check-only") { + check_only = true; } else if (args[i] == "--test") { test = args[i + 1]; #ifdef TOOLS_ENABLED @@ -1615,6 +1387,10 @@ bool Main::start() { ERR_EXPLAIN("Can't load script: " + script); ERR_FAIL_COND_V(script_res.is_null(), false); + if (check_only) { + return false; + } + if (script_res->can_instance() /*&& script_res->inherits_from("SceneTreeScripted")*/) { StringName instance_type = script_res->get_instance_base_type(); @@ -1676,6 +1452,91 @@ bool Main::start() { } #endif + if (!project_manager && !editor) { // game + if (game_path != "" || script != "") { + //autoload + List<PropertyInfo> props; + ProjectSettings::get_singleton()->get_property_list(&props); + + //first pass, add the constants so they exist before any script is loaded + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + String name = s.get_slicec('/', 1); + String path = ProjectSettings::get_singleton()->get(s); + bool global_var = false; + if (path.begins_with("*")) { + global_var = true; + } + + if (global_var) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(name, Variant()); + } + } + } + + //second pass, load into global constants + List<Node *> to_add; + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + String name = s.get_slicec('/', 1); + String path = ProjectSettings::get_singleton()->get(s); + bool global_var = false; + if (path.begins_with("*")) { + global_var = true; + path = path.substr(1, path.length() - 1); + } + + RES res = ResourceLoader::load(path); + ERR_EXPLAIN("Can't autoload: " + path); + ERR_CONTINUE(res.is_null()); + Node *n = NULL; + if (res->is_class("PackedScene")) { + Ref<PackedScene> ps = res; + n = ps->instance(); + } else if (res->is_class("Script")) { + Ref<Script> s = res; + StringName ibt = s->get_instance_base_type(); + bool valid_type = ClassDB::is_parent_class(ibt, "Node"); + ERR_EXPLAIN("Script does not inherit a Node: " + path); + ERR_CONTINUE(!valid_type); + + Object *obj = ClassDB::instance(ibt); + + ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt)); + ERR_CONTINUE(obj == NULL); + + n = Object::cast_to<Node>(obj); + n->set_script(s.get_ref_ptr()); + } + + ERR_EXPLAIN("Path in autoload not a node or script: " + path); + ERR_CONTINUE(!n); + n->set_name(name); + + //defer so references are all valid on _ready() + to_add.push_back(n); + + if (global_var) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(name, n); + } + } + } + + for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) { + + sml->get_root()->add_child(E->get()); + } + } + } + #ifdef TOOLS_ENABLED EditorNode *editor_node = NULL; @@ -1695,9 +1556,6 @@ bool Main::start() { } #endif - { - } - if (!editor && !project_manager) { //standard helpers that can be changed from main config @@ -1810,89 +1668,6 @@ bool Main::start() { } if (!project_manager && !editor) { // game - if (game_path != "" || script != "") { - //autoload - List<PropertyInfo> props; - ProjectSettings::get_singleton()->get_property_list(&props); - - //first pass, add the constants so they exist before any script is loaded - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - String s = E->get().name; - if (!s.begins_with("autoload/")) - continue; - String name = s.get_slicec('/', 1); - String path = ProjectSettings::get_singleton()->get(s); - bool global_var = false; - if (path.begins_with("*")) { - global_var = true; - } - - if (global_var) { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->add_global_constant(name, Variant()); - } - } - } - - //second pass, load into global constants - List<Node *> to_add; - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - String s = E->get().name; - if (!s.begins_with("autoload/")) - continue; - String name = s.get_slicec('/', 1); - String path = ProjectSettings::get_singleton()->get(s); - bool global_var = false; - if (path.begins_with("*")) { - global_var = true; - path = path.substr(1, path.length() - 1); - } - - RES res = ResourceLoader::load(path); - ERR_EXPLAIN("Can't autoload: " + path); - ERR_CONTINUE(res.is_null()); - Node *n = NULL; - if (res->is_class("PackedScene")) { - Ref<PackedScene> ps = res; - n = ps->instance(); - } else if (res->is_class("Script")) { - Ref<Script> s = res; - StringName ibt = s->get_instance_base_type(); - bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_EXPLAIN("Script does not inherit a Node: " + path); - ERR_CONTINUE(!valid_type); - - Object *obj = ClassDB::instance(ibt); - - ERR_EXPLAIN("Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt)); - ERR_CONTINUE(obj == NULL); - - n = Object::cast_to<Node>(obj); - n->set_script(s.get_ref_ptr()); - } - - ERR_EXPLAIN("Path in autoload not a node or script: " + path); - ERR_CONTINUE(!n); - n->set_name(name); - - //defer so references are all valid on _ready() - to_add.push_back(n); - - if (global_var) { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->add_global_constant(name, n); - } - } - } - - for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) { - - sml->get_root()->add_child(E->get()); - } - } - if (game_path != "") { Node *scene = NULL; Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path); @@ -1951,15 +1726,16 @@ 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); + main_timer_sync.set_cpu_ticks_usec(ticks); + main_timer_sync.set_fixed_fps(fixed_fps); uint64_t ticks_elapsed = ticks - last_ticks; int physics_fps = Engine::get_singleton()->get_iterations_per_second(); float frame_slice = 1.0 / physics_fps; - _FrameTime advance = _timer_sync.advance(frame_slice, physics_fps); - double step = advance.animation_step; + MainFrameTime advance = main_timer_sync.advance(frame_slice, physics_fps); + double step = advance.idle_step; Engine::get_singleton()->_frame_step = step; @@ -2023,7 +1799,7 @@ bool Main::iteration() { OS::get_singleton()->get_main_loop()->idle(step * time_scale); message_queue->flush(); - _timer_sync.before_start_render(); //sync if still drawing from previous frames. + VisualServer::get_singleton()->sync(); //sync if still drawing from previous frames. if (OS::get_singleton()->can_draw() && !disable_render_loop) { @@ -2039,9 +1815,6 @@ bool Main::iteration() { } } - if (AudioServer::get_singleton()) - AudioServer::get_singleton()->update(); - idle_process_ticks = OS::get_singleton()->get_ticks_usec() - idle_begin; idle_process_max = MAX(idle_process_ticks, idle_process_max); uint64_t frame_time = OS::get_singleton()->get_ticks_usec() - ticks; diff --git a/main/performance.cpp b/main/performance.cpp index fc915e2e76..70e0a5f7aa 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -32,6 +32,7 @@ #include "message_queue.h" #include "os/os.h" #include "scene/main/scene_tree.h" +#include "servers/audio_server.h" #include "servers/physics_2d_server.h" #include "servers/physics_server.h" #include "servers/visual_server.h" @@ -68,6 +69,7 @@ void Performance::_bind_methods() { BIND_ENUM_CONSTANT(PHYSICS_3D_ACTIVE_OBJECTS); BIND_ENUM_CONSTANT(PHYSICS_3D_COLLISION_PAIRS); BIND_ENUM_CONSTANT(PHYSICS_3D_ISLAND_COUNT); + BIND_ENUM_CONSTANT(AUDIO_OUTPUT_LATENCY); BIND_ENUM_CONSTANT(MONITOR_MAX); } @@ -104,6 +106,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const { "physics_3d/active_objects", "physics_3d/collision_pairs", "physics_3d/islands", + "audio/output_latency", }; @@ -147,6 +150,7 @@ float Performance::get_monitor(Monitor p_monitor) const { case PHYSICS_3D_ACTIVE_OBJECTS: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ACTIVE_OBJECTS); case PHYSICS_3D_COLLISION_PAIRS: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_COLLISION_PAIRS); case PHYSICS_3D_ISLAND_COUNT: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ISLAND_COUNT); + case AUDIO_OUTPUT_LATENCY: return AudioServer::get_singleton()->get_output_latency(); default: {} } @@ -186,6 +190,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, + MONITOR_TYPE_TIME, }; diff --git a/main/performance.h b/main/performance.h index 464226b517..de00df5ff9 100644 --- a/main/performance.h +++ b/main/performance.h @@ -77,6 +77,7 @@ public: PHYSICS_3D_COLLISION_PAIRS, PHYSICS_3D_ISLAND_COUNT, //physics + AUDIO_OUTPUT_LATENCY, MONITOR_MAX }; diff --git a/main/splash.png b/main/splash.png Binary files differindex 34be46557f..32960db65f 100644 --- a/main/splash.png +++ b/main/splash.png diff --git a/main/splash_editor.png b/main/splash_editor.png Binary files differindex d8677f1749..f003995d6f 100644 --- a/main/splash_editor.png +++ b/main/splash_editor.png diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index c9431a1a09..cbc1107acb 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -50,15 +50,20 @@ const char **tests_get_names() { static const char *test_names[] = { "string", - "containers", "math", + "physics", + "physics_2d", "render", - "multimesh", + "oa_hash_map", "gui", "io", "shaderlang", - "physics", - "oa_hash_map", + "gd_tokenizer", + "gd_parser", + "gd_compiler", + "gd_bytecode", + "image", + "ordered_hash_map", NULL }; diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp index 63032597ed..7103b436e1 100644 --- a/main/tests/test_shader_lang.cpp +++ b/main/tests/test_shader_lang.cpp @@ -321,8 +321,8 @@ MainLoop *test() { dt["fragment"].built_ins["ALBEDO"] = SL::TYPE_VEC3; dt["fragment"].can_discard = true; - Set<String> rm; - rm.insert("popo"); + Vector<StringName> rm; + rm.push_back("popo"); Set<String> types; types.insert("spatial"); diff --git a/main/timer_sync.cpp b/main/timer_sync.cpp new file mode 100644 index 0000000000..9f4f6ed271 --- /dev/null +++ b/main/timer_sync.cpp @@ -0,0 +1,223 @@ +/*************************************************************************/ +/* timer_sync.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "timer_sync.h" + +void MainFrameTime::clamp_idle(float min_idle_step, float max_idle_step) { + if (idle_step < min_idle_step) { + idle_step = min_idle_step; + } else if (idle_step > max_idle_step) { + idle_step = max_idle_step; + } +} + +///////////////////////////////// + +// 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 MainTimerSync::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 MainTimerSync::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_idle_step, return appropriate number of steps to simulate +MainFrameTime MainTimerSync::advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step) { + MainFrameTime ret; + + ret.idle_step = p_idle_step; + + // simple determination of number of physics iteration + time_accum += ret.idle_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 +MainFrameTime MainTimerSync::advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step) { + if (fixed_fps != -1) + p_idle_step = 1.0 / fixed_fps; + + // compensate for last deficit + p_idle_step += time_deficit; + + MainFrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_idle_step); + + // we will do some clamping on ret.idle_step and need to sync those changes to time_accum, + // that's easiest if we just remember their fixed difference now + const double idle_minus_accum = ret.idle_step - time_accum; + + // first, least important clamping: keep ret.idle_step consistent with typical_physics_steps. + // this smoothes out the idle 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_idle(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_idle(p_idle_step - max_clock_deviation, p_idle_step + max_clock_deviation); + + // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and idle + ret.clamp_idle(idle_minus_accum, idle_minus_accum + p_frame_slice); + + // restore time_accum + time_accum = ret.idle_step - idle_minus_accum; + + // track deficit + time_deficit = p_idle_step - ret.idle_step; + + return ret; +} + +// determine wall clock step since last iteration +float MainTimerSync::get_cpu_idle_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; +} + +MainTimerSync::MainTimerSync() : + last_cpu_ticks_usec(0), + current_cpu_ticks_usec(0), + time_accum(0), + time_deficit(0), + fixed_fps(0) { + for (int i = CONTROL_STEPS - 1; i >= 0; --i) { + typical_physics_steps[i] = i; + accumulated_physics_steps[i] = i; + } +} + +// start the clock +void MainTimerSync::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 MainTimerSync::set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) { + current_cpu_ticks_usec = p_cpu_ticks_usec; +} + +void MainTimerSync::set_fixed_fps(int p_fixed_fps) { + fixed_fps = p_fixed_fps; +} + +// advance one frame, return timesteps to take +MainFrameTime MainTimerSync::advance(float p_frame_slice, int p_iterations_per_second) { + float cpu_idle_step = get_cpu_idle_step(); + + return advance_checked(p_frame_slice, p_iterations_per_second, cpu_idle_step); +} diff --git a/main/timer_sync.h b/main/timer_sync.h new file mode 100644 index 0000000000..75fbe6a9dc --- /dev/null +++ b/main/timer_sync.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* timer_sync.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TIMER_SYNC_H +#define TIMER_SYNC_H + +#include "core/engine.h" + +struct MainFrameTime { + float idle_step; // time to advance idles for (argument to process()) + int physics_steps; // number of times to iterate the physics engine + + void clamp_idle(float min_idle_step, float max_idle_step); +}; + +class MainTimerSync { + // 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 idle_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]; + + int fixed_fps; + +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(); + + // 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); + + // advance physics clock by p_idle_step, return appropriate number of steps to simulate + MainFrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step); + + // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero + MainFrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step); + + // determine wall clock step since last iteration + float get_cpu_idle_step(); + +public: + MainTimerSync(); + + // start the clock + void init(uint64_t p_cpu_ticks_usec); + // set measured wall clock time + void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec); + //set fixed fps + void set_fixed_fps(int p_fixed_fps); + + // advance one frame, return timesteps to take + MainFrameTime advance(float p_frame_slice, int p_iterations_per_second); +}; + +#endif // TIMER_SYNC_H |