summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/SCsub8
-rw-r--r--main/app_icon.pngbin5569 -> 3770 bytes
-rw-r--r--main/main.cpp461
-rw-r--r--main/performance.cpp5
-rw-r--r--main/performance.h1
-rw-r--r--main/splash.pngbin21504 -> 15311 bytes
-rw-r--r--main/splash_editor.pngbin39571 -> 32577 bytes
-rw-r--r--main/tests/test_main.cpp13
-rw-r--r--main/tests/test_shader_lang.cpp4
-rw-r--r--main/timer_sync.cpp223
-rw-r--r--main/timer_sync.h101
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
index 1d75cdc710..cf31af18a4 100644
--- a/main/app_icon.png
+++ b/main/app_icon.png
Binary files differ
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
index 34be46557f..32960db65f 100644
--- a/main/splash.png
+++ b/main/splash.png
Binary files differ
diff --git a/main/splash_editor.png b/main/splash_editor.png
index d8677f1749..f003995d6f 100644
--- a/main/splash_editor.png
+++ b/main/splash_editor.png
Binary files differ
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