Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/os/os.cpp
9903 views
1
/**************************************************************************/
2
/* os.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "os.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/io/dir_access.h"
35
#include "core/io/file_access.h"
36
#include "core/io/json.h"
37
#include "core/os/midi_driver.h"
38
#include "core/version_generated.gen.h"
39
40
#include <cstdarg>
41
42
#ifdef MINGW_ENABLED
43
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
44
#include "thirdparty/mingw-std-threads/mingw.thread.h"
45
#define THREADING_NAMESPACE mingw_stdthread
46
#else
47
#include <thread>
48
#define THREADING_NAMESPACE std
49
#endif
50
51
OS *OS::singleton = nullptr;
52
uint64_t OS::target_ticks = 0;
53
54
OS *OS::get_singleton() {
55
return singleton;
56
}
57
58
uint64_t OS::get_ticks_msec() const {
59
return get_ticks_usec() / 1000ULL;
60
}
61
62
double OS::get_unix_time() const {
63
return 0;
64
}
65
66
void OS::_set_logger(CompositeLogger *p_logger) {
67
if (_logger) {
68
memdelete(_logger);
69
}
70
_logger = p_logger;
71
}
72
73
void OS::add_logger(Logger *p_logger) {
74
if (!_logger) {
75
Vector<Logger *> loggers;
76
loggers.push_back(p_logger);
77
_logger = memnew(CompositeLogger(loggers));
78
} else {
79
_logger->add_logger(p_logger);
80
}
81
}
82
83
String OS::get_identifier() const {
84
return get_name().to_lower();
85
}
86
87
void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
88
if (!_stderr_enabled) {
89
return;
90
}
91
92
if (_logger) {
93
_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type, p_script_backtraces);
94
}
95
}
96
97
void OS::print(const char *p_format, ...) {
98
if (!_stdout_enabled) {
99
return;
100
}
101
102
va_list argp;
103
va_start(argp, p_format);
104
105
if (_logger) {
106
_logger->logv(p_format, argp, false);
107
}
108
109
va_end(argp);
110
}
111
112
void OS::print_rich(const char *p_format, ...) {
113
if (!_stdout_enabled) {
114
return;
115
}
116
117
va_list argp;
118
va_start(argp, p_format);
119
120
if (_logger) {
121
_logger->logv(p_format, argp, false);
122
}
123
124
va_end(argp);
125
}
126
127
void OS::printerr(const char *p_format, ...) {
128
if (!_stderr_enabled) {
129
return;
130
}
131
132
va_list argp;
133
va_start(argp, p_format);
134
135
if (_logger) {
136
_logger->logv(p_format, argp, true);
137
}
138
139
va_end(argp);
140
}
141
142
void OS::alert(const String &p_alert, const String &p_title) {
143
fprintf(stderr, "%s: %s\n", p_title.utf8().get_data(), p_alert.utf8().get_data());
144
}
145
146
void OS::set_low_processor_usage_mode(bool p_enabled) {
147
low_processor_usage_mode = p_enabled;
148
}
149
150
bool OS::is_in_low_processor_usage_mode() const {
151
return low_processor_usage_mode;
152
}
153
154
void OS::set_low_processor_usage_mode_sleep_usec(int p_usec) {
155
low_processor_usage_mode_sleep_usec = p_usec;
156
}
157
158
int OS::get_low_processor_usage_mode_sleep_usec() const {
159
return low_processor_usage_mode_sleep_usec;
160
}
161
162
void OS::set_delta_smoothing(bool p_enabled) {
163
_delta_smoothing_enabled = p_enabled;
164
}
165
166
bool OS::is_delta_smoothing_enabled() const {
167
return _delta_smoothing_enabled;
168
}
169
170
String OS::get_executable_path() const {
171
return _execpath;
172
}
173
174
int OS::get_process_id() const {
175
return -1;
176
}
177
178
bool OS::is_stdout_verbose() const {
179
return _verbose_stdout;
180
}
181
182
bool OS::is_stdout_debug_enabled() const {
183
return _debug_stdout;
184
}
185
186
bool OS::is_stdout_enabled() const {
187
return _stdout_enabled;
188
}
189
190
bool OS::is_stderr_enabled() const {
191
return _stderr_enabled;
192
}
193
194
void OS::set_stdout_enabled(bool p_enabled) {
195
_stdout_enabled = p_enabled;
196
}
197
198
void OS::set_stderr_enabled(bool p_enabled) {
199
_stderr_enabled = p_enabled;
200
}
201
202
String OS::multibyte_to_string(const String &p_encoding, const PackedByteArray &p_array) const {
203
return String();
204
}
205
206
PackedByteArray OS::string_to_multibyte(const String &p_encoding, const String &p_string) const {
207
return PackedByteArray();
208
}
209
210
int OS::get_exit_code() const {
211
return _exit_code;
212
}
213
214
void OS::set_exit_code(int p_code) {
215
_exit_code = p_code;
216
}
217
218
String OS::get_locale() const {
219
return "en";
220
}
221
222
// Non-virtual helper to extract the 2 or 3-letter language code from
223
// `get_locale()` in a way that's consistent for all platforms.
224
String OS::get_locale_language() const {
225
return get_locale().left(3).remove_char('_');
226
}
227
228
// Embedded PCK offset.
229
uint64_t OS::get_embedded_pck_offset() const {
230
return 0;
231
}
232
233
// Default boot screen rect scale mode is "Keep Aspect Centered"
234
Rect2 OS::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
235
Rect2 screenrect;
236
if (p_window_size.width > p_window_size.height) {
237
// Scale horizontally.
238
screenrect.size.y = p_window_size.height;
239
screenrect.size.x = p_imgrect_size.x * p_window_size.height / p_imgrect_size.y;
240
screenrect.position.x = (p_window_size.width - screenrect.size.x) / 2;
241
} else {
242
// Scale vertically.
243
screenrect.size.x = p_window_size.width;
244
screenrect.size.y = p_imgrect_size.y * p_window_size.width / p_imgrect_size.x;
245
screenrect.position.y = (p_window_size.height - screenrect.size.y) / 2;
246
}
247
return screenrect;
248
}
249
250
// Helper function to ensure that a dir name/path will be valid on the OS
251
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) const {
252
String safe_dir_name = p_dir_name;
253
Vector<String> invalid_chars = String(": * ? \" < > |").split(" ");
254
if (p_allow_paths) {
255
// Dir separators are allowed, but disallow ".." to avoid going up the filesystem
256
invalid_chars.push_back("..");
257
safe_dir_name = safe_dir_name.replace_char('\\', '/').replace("//", "/").strip_edges();
258
} else {
259
invalid_chars.push_back("/");
260
invalid_chars.push_back("\\");
261
safe_dir_name = safe_dir_name.strip_edges();
262
263
// These directory names are invalid.
264
if (safe_dir_name == ".") {
265
safe_dir_name = "dot";
266
} else if (safe_dir_name == "..") {
267
safe_dir_name = "twodots";
268
}
269
}
270
271
for (int i = 0; i < invalid_chars.size(); i++) {
272
safe_dir_name = safe_dir_name.replace(invalid_chars[i], "-");
273
}
274
275
// Trim trailing periods from the returned value as it's not valid for folder names on Windows.
276
// This check is still applied on non-Windows platforms so the returned value is consistent across platforms.
277
return safe_dir_name.rstrip(".");
278
}
279
280
// Path to data, config, cache, etc. OS-specific folders
281
282
// Get properly capitalized engine name for system paths
283
String OS::get_godot_dir_name() const {
284
// Default to lowercase, so only override when different case is needed
285
return String(GODOT_VERSION_SHORT_NAME).to_lower();
286
}
287
288
// OS equivalent of XDG_DATA_HOME
289
String OS::get_data_path() const {
290
return ".";
291
}
292
293
// OS equivalent of XDG_CONFIG_HOME
294
String OS::get_config_path() const {
295
return ".";
296
}
297
298
// OS equivalent of XDG_CACHE_HOME
299
String OS::get_cache_path() const {
300
return ".";
301
}
302
303
String OS::get_temp_path() const {
304
return ".";
305
}
306
307
// Path to macOS .app bundle resources
308
String OS::get_bundle_resource_dir() const {
309
return ".";
310
}
311
312
// Path to macOS .app bundle embedded icon
313
String OS::get_bundle_icon_path() const {
314
return String();
315
}
316
317
// OS specific path for user://
318
String OS::get_user_data_dir(const String &p_user_dir) const {
319
return ".";
320
}
321
322
String OS::get_user_data_dir() const {
323
String appname = get_safe_dir_name(GLOBAL_GET("application/config/name"));
324
if (!appname.is_empty()) {
325
bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir");
326
if (use_custom_dir) {
327
String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true);
328
if (custom_dir.is_empty()) {
329
custom_dir = appname;
330
}
331
return get_user_data_dir(custom_dir);
332
} else {
333
return get_user_data_dir(get_godot_dir_name().path_join("app_userdata").path_join(appname));
334
}
335
} else {
336
return get_user_data_dir(get_godot_dir_name().path_join("app_userdata").path_join("[unnamed project]"));
337
}
338
}
339
340
// Absolute path to res://
341
String OS::get_resource_dir() const {
342
return ProjectSettings::get_singleton()->get_resource_path();
343
}
344
345
// Access system-specific dirs like Documents, Downloads, etc.
346
String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
347
return ".";
348
}
349
350
void OS::create_lock_file() {
351
if (Engine::get_singleton()->is_recovery_mode_hint()) {
352
return;
353
}
354
355
String lock_file_path = get_user_data_dir().path_join(".recovery_mode_lock");
356
Ref<FileAccess> lock_file = FileAccess::open(lock_file_path, FileAccess::WRITE);
357
if (lock_file.is_valid()) {
358
lock_file->close();
359
}
360
}
361
362
void OS::remove_lock_file() {
363
String lock_file_path = get_user_data_dir().path_join(".recovery_mode_lock");
364
DirAccess::remove_absolute(lock_file_path);
365
}
366
367
Error OS::shell_open(const String &p_uri) {
368
return ERR_UNAVAILABLE;
369
}
370
371
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
372
p_path = p_path.trim_prefix("file://");
373
374
if (!DirAccess::dir_exists_absolute(p_path)) {
375
p_path = p_path.get_base_dir();
376
}
377
378
p_path = String("file://") + p_path;
379
380
return shell_open(p_path);
381
}
382
// implement these with the canvas?
383
384
uint64_t OS::get_static_memory_usage() const {
385
return Memory::get_mem_usage();
386
}
387
388
uint64_t OS::get_static_memory_peak_usage() const {
389
return Memory::get_mem_max_usage();
390
}
391
392
Error OS::set_cwd(const String &p_cwd) {
393
return ERR_CANT_OPEN;
394
}
395
396
Dictionary OS::get_memory_info() const {
397
Dictionary meminfo;
398
399
meminfo["physical"] = -1;
400
meminfo["free"] = -1;
401
meminfo["available"] = -1;
402
meminfo["stack"] = -1;
403
404
return meminfo;
405
}
406
407
void OS::yield() {
408
}
409
410
void OS::ensure_user_data_dir() {
411
String dd = get_user_data_dir();
412
if (DirAccess::exists(dd)) {
413
return;
414
}
415
416
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
417
Error err = da->make_dir_recursive(dd);
418
ERR_FAIL_COND_MSG(err != OK, vformat("Error attempting to create data dir: %s.", dd));
419
}
420
421
String OS::get_model_name() const {
422
return "GenericDevice";
423
}
424
425
void OS::set_cmdline(const char *p_execpath, const List<String> &p_args, const List<String> &p_user_args) {
426
_execpath = String::utf8(p_execpath);
427
_cmdline = p_args;
428
_user_args = p_user_args;
429
}
430
431
String OS::get_unique_id() const {
432
return "";
433
}
434
435
int OS::get_processor_count() const {
436
return THREADING_NAMESPACE::thread::hardware_concurrency();
437
}
438
439
String OS::get_processor_name() const {
440
return "";
441
}
442
443
void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
444
has_server_feature_callback = p_callback;
445
}
446
447
bool OS::has_feature(const String &p_feature) {
448
// Feature tags are always lowercase for consistency.
449
if (p_feature == get_identifier()) {
450
return true;
451
}
452
453
if (p_feature == "movie") {
454
return _writing_movie;
455
}
456
457
#ifdef DEBUG_ENABLED
458
if (p_feature == "debug") {
459
return true;
460
}
461
#endif // DEBUG_ENABLED
462
463
#ifdef TOOLS_ENABLED
464
if (p_feature == "editor") {
465
return true;
466
}
467
if (p_feature == "editor_hint") {
468
return _in_editor;
469
} else if (p_feature == "editor_runtime") {
470
return !_in_editor;
471
} else if (p_feature == "embedded_in_editor") {
472
return _embedded_in_editor;
473
}
474
#else
475
if (p_feature == "template") {
476
return true;
477
}
478
#ifdef DEBUG_ENABLED
479
if (p_feature == "template_debug") {
480
return true;
481
}
482
#else
483
if (p_feature == "template_release" || p_feature == "release") {
484
return true;
485
}
486
#endif // DEBUG_ENABLED
487
#endif // TOOLS_ENABLED
488
489
#ifdef REAL_T_IS_DOUBLE
490
if (p_feature == "double") {
491
return true;
492
}
493
#else
494
if (p_feature == "single") {
495
return true;
496
}
497
#endif // REAL_T_IS_DOUBLE
498
499
if (sizeof(void *) == 8 && p_feature == "64") {
500
return true;
501
}
502
if (sizeof(void *) == 4 && p_feature == "32") {
503
return true;
504
}
505
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
506
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
507
#if defined(MACOS_ENABLED)
508
if (p_feature == "universal") {
509
return true;
510
}
511
#endif
512
if (p_feature == "x86_64") {
513
return true;
514
}
515
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
516
if (p_feature == "x86_32") {
517
return true;
518
}
519
#endif
520
if (p_feature == "x86") {
521
return true;
522
}
523
#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
524
#if defined(__aarch64__) || defined(_M_ARM64)
525
#if defined(MACOS_ENABLED)
526
if (p_feature == "universal") {
527
return true;
528
}
529
#endif
530
if (p_feature == "arm64") {
531
return true;
532
}
533
#elif defined(__arm__) || defined(_M_ARM)
534
if (p_feature == "arm32") {
535
return true;
536
}
537
#endif
538
#if defined(__ARM_ARCH_7A__)
539
if (p_feature == "armv7a" || p_feature == "armv7") {
540
return true;
541
}
542
#endif
543
#if defined(__ARM_ARCH_7S__)
544
if (p_feature == "armv7s" || p_feature == "armv7") {
545
return true;
546
}
547
#endif
548
if (p_feature == "arm") {
549
return true;
550
}
551
#elif defined(__riscv)
552
#if __riscv_xlen == 8
553
if (p_feature == "rv64") {
554
return true;
555
}
556
#endif
557
if (p_feature == "riscv") {
558
return true;
559
}
560
#elif defined(__powerpc__)
561
#if defined(__powerpc64__)
562
if (p_feature == "ppc64") {
563
return true;
564
}
565
#endif
566
if (p_feature == "ppc") {
567
return true;
568
}
569
#elif defined(__wasm__)
570
#if defined(__wasm64__)
571
if (p_feature == "wasm64") {
572
return true;
573
}
574
#elif defined(__wasm32__)
575
if (p_feature == "wasm32") {
576
return true;
577
}
578
#endif
579
if (p_feature == "wasm") {
580
return true;
581
}
582
#elif defined(__loongarch64)
583
if (p_feature == "loongarch64") {
584
return true;
585
}
586
#endif
587
588
#if defined(IOS_SIMULATOR) || defined(VISIONOS_SIMULATOR)
589
if (p_feature == "simulator") {
590
return true;
591
}
592
#endif
593
594
if (p_feature == "threads") {
595
#ifdef THREADS_ENABLED
596
return true;
597
#else
598
return false;
599
#endif
600
}
601
if (p_feature == "nothreads") {
602
#ifdef THREADS_ENABLED
603
return false;
604
#else
605
return true;
606
#endif
607
}
608
609
if (_check_internal_feature_support(p_feature)) {
610
return true;
611
}
612
613
if (has_server_feature_callback && has_server_feature_callback(p_feature)) {
614
return true;
615
}
616
617
if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) {
618
return true;
619
}
620
621
return false;
622
}
623
624
bool OS::is_sandboxed() const {
625
return false;
626
}
627
628
void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) {
629
restart_on_exit = p_restart;
630
restart_commandline = p_restart_arguments;
631
}
632
633
bool OS::is_restart_on_exit_set() const {
634
return restart_on_exit;
635
}
636
637
List<String> OS::get_restart_on_exit_arguments() const {
638
return restart_commandline;
639
}
640
641
PackedStringArray OS::get_connected_midi_inputs() {
642
if (MIDIDriver::get_singleton()) {
643
return MIDIDriver::get_singleton()->get_connected_inputs();
644
}
645
646
PackedStringArray list;
647
ERR_FAIL_V_MSG(list, vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
648
}
649
650
void OS::open_midi_inputs() {
651
if (MIDIDriver::get_singleton()) {
652
MIDIDriver::get_singleton()->open();
653
} else {
654
ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
655
}
656
}
657
658
void OS::close_midi_inputs() {
659
if (MIDIDriver::get_singleton()) {
660
MIDIDriver::get_singleton()->close();
661
} else {
662
ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
663
}
664
}
665
666
uint64_t OS::get_frame_delay(bool p_can_draw) const {
667
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
668
669
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
670
// previous frame time into account for a smoother result.
671
uint64_t dynamic_delay = 0;
672
if (is_in_low_processor_usage_mode() || !p_can_draw) {
673
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
674
}
675
const int max_fps = Engine::get_singleton()->get_max_fps();
676
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
677
// Override the low processor usage mode sleep delay if the target FPS is lower.
678
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
679
}
680
681
return frame_delay + dynamic_delay;
682
}
683
684
void OS::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
685
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
686
if (frame_delay) {
687
// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
688
// the actual frame time into account.
689
// Due to the high fluctuation of the actual sleep duration, it's not recommended
690
// to use this as a FPS limiter.
691
delay_usec(frame_delay * 1000);
692
}
693
694
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
695
// previous frame time into account for a smoother result.
696
uint64_t dynamic_delay = 0;
697
if (is_in_low_processor_usage_mode() || !p_can_draw) {
698
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
699
}
700
const int max_fps = Engine::get_singleton()->get_max_fps();
701
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
702
// Override the low processor usage mode sleep delay if the target FPS is lower.
703
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
704
}
705
706
if (dynamic_delay > 0) {
707
target_ticks += dynamic_delay;
708
uint64_t current_ticks = get_ticks_usec();
709
710
if (current_ticks < target_ticks) {
711
delay_usec(target_ticks - current_ticks);
712
}
713
714
current_ticks = get_ticks_usec();
715
target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
716
}
717
}
718
719
Error OS::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
720
return default_rfs.synchronize_with_server(p_server_host, p_port, p_password, r_project_path);
721
}
722
723
OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
724
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
725
return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression.
726
#elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
727
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // By rule, X86 hardware prefers S3TC and derivatives.
728
#else
729
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // Override in platform if needed.
730
#endif
731
}
732
733
void OS::set_use_benchmark(bool p_use_benchmark) {
734
use_benchmark = p_use_benchmark;
735
}
736
737
bool OS::is_use_benchmark_set() {
738
return use_benchmark;
739
}
740
741
void OS::set_benchmark_file(const String &p_benchmark_file) {
742
benchmark_file = p_benchmark_file;
743
}
744
745
String OS::get_benchmark_file() {
746
return benchmark_file;
747
}
748
749
void OS::benchmark_begin_measure(const String &p_context, const String &p_what) {
750
#ifdef TOOLS_ENABLED
751
Pair<String, String> mark_key(p_context, p_what);
752
ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what));
753
754
benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec();
755
#endif
756
}
757
void OS::benchmark_end_measure(const String &p_context, const String &p_what) {
758
#ifdef TOOLS_ENABLED
759
Pair<String, String> mark_key(p_context, p_what);
760
ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what));
761
762
uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key];
763
double total_f = double(total) / double(1000000);
764
benchmark_marks_final[mark_key] = total_f;
765
#endif
766
}
767
768
void OS::benchmark_dump() {
769
#ifdef TOOLS_ENABLED
770
if (!use_benchmark) {
771
return;
772
}
773
774
if (!benchmark_file.is_empty()) {
775
Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE);
776
if (f.is_valid()) {
777
Dictionary benchmark_marks;
778
for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
779
const String mark_key = vformat("[%s] %s", E.key.first, E.key.second);
780
benchmark_marks[mark_key] = E.value;
781
}
782
783
Ref<JSON> json;
784
json.instantiate();
785
f->store_string(json->stringify(benchmark_marks, "\t", false, true));
786
}
787
} else {
788
HashMap<String, String> results;
789
for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
790
if (E.key.first == "Startup" && !results.has(E.key.first)) {
791
results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first.
792
}
793
794
results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000));
795
}
796
797
print_line("BENCHMARK:");
798
for (const KeyValue<String, String> &E : results) {
799
print_line(vformat("\t[%s]\n%s", E.key, E.value));
800
}
801
}
802
#endif
803
}
804
805
OS::OS() {
806
singleton = this;
807
808
Vector<Logger *> loggers;
809
loggers.push_back(memnew(StdLogger));
810
_set_logger(memnew(CompositeLogger(loggers)));
811
}
812
813
OS::~OS() {
814
if (_logger) {
815
memdelete(_logger);
816
}
817
singleton = nullptr;
818
}
819
820