mirror of
https://github.com/godotengine/godot.git
synced 2024-10-22 21:21:53 +00:00
Compare commits
66 Commits
0c00c5a6d4
...
de6002fabd
Author | SHA1 | Date | |
---|---|---|---|
|
de6002fabd | ||
|
152e24c7de | ||
|
ca7840073c | ||
|
b3bcb2dc14 | ||
|
a4ed24acef | ||
|
7815ccbdd5 | ||
|
66d19abdfa | ||
|
22822f71ac | ||
|
6ec3dc1fb5 | ||
|
7dbea98c49 | ||
|
8b5c20e2b0 | ||
|
55aeff19dc | ||
|
1a9628f937 | ||
|
677ebad7fc | ||
|
a14e9e99e5 | ||
|
178342b058 | ||
|
77da16ce69 | ||
|
291e4b78e2 | ||
|
b6547b0d06 | ||
|
7a438dc72e | ||
|
cb3c01a5fe | ||
|
c145e85011 | ||
|
ecc2eb73fc | ||
|
975f42227f | ||
|
4630cbc487 | ||
|
8be0061458 | ||
|
4ce5235fbd | ||
|
16d68c6be4 | ||
|
29fa4b18f1 | ||
|
5fb22327ee | ||
|
5e65747d90 | ||
|
4ad424234f | ||
|
fbd1643176 | ||
|
916d480686 | ||
|
5c65f80199 | ||
|
83ac274e25 | ||
|
44fa552343 | ||
|
80f0b33313 | ||
|
8e0c0d7837 | ||
|
58a7f9b4d8 | ||
|
f8c4a683d7 | ||
|
af6d260c17 | ||
|
17642692c5 | ||
|
35a20fa96a | ||
|
8c3e46b13b | ||
|
155cf6a5b6 | ||
|
610635e1c8 | ||
|
79f654ced5 | ||
|
e3790de461 | ||
|
4b37fb3f88 | ||
|
274076c5be | ||
|
25687c5b99 | ||
|
4e6d9813b2 | ||
|
009446a277 | ||
|
ebe1a2d7ec | ||
|
acab2d6c1c | ||
|
c12001a9dc | ||
|
de128812f3 | ||
|
140c6a612e | ||
|
12d2c05936 | ||
|
9f6c88de89 | ||
|
8f9ed35f8b | ||
|
4ef07cb9a5 | ||
|
705f51f97e | ||
|
fa48ed9945 | ||
|
1b376b32cd |
|
@ -799,8 +799,8 @@ if env["lto"] != "none":
|
|||
# This needs to come after `configure`, otherwise we don't have env.msvc.
|
||||
if not env.msvc:
|
||||
# Specifying GNU extensions support explicitly, which are supported by
|
||||
# both GCC and Clang. Both currently default to gnu11 and gnu++17.
|
||||
env.Prepend(CFLAGS=["-std=gnu11"])
|
||||
# both GCC and Clang. Both currently default to gnu17 and gnu++17.
|
||||
env.Prepend(CFLAGS=["-std=gnu17"])
|
||||
env.Prepend(CXXFLAGS=["-std=gnu++17"])
|
||||
else:
|
||||
# MSVC started offering C standard support with Visual Studio 2019 16.8, which covers all
|
||||
|
@ -809,7 +809,7 @@ else:
|
|||
if cc_version_major < 16:
|
||||
print_warning("Visual Studio 2017 cannot specify a C-Standard.")
|
||||
else:
|
||||
env.Prepend(CFLAGS=["/std:c11"])
|
||||
env.Prepend(CFLAGS=["/std:c17"])
|
||||
# MSVC is non-conforming with the C++ standard by default, so we enable more conformance.
|
||||
# Note that this is still not complete conformance, as certain Windows-related headers
|
||||
# don't compile under complete conformance.
|
||||
|
|
|
@ -494,6 +494,7 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f
|
|||
}
|
||||
|
||||
void ProjectSettings::_convert_to_last_version(int p_from_version) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (p_from_version <= 3) {
|
||||
// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
|
||||
for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
|
||||
|
@ -507,6 +508,22 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (p_from_version <= 5) {
|
||||
// Converts the device in events from -1 (emulated events) to -3 (all events).
|
||||
for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
|
||||
if (String(E.key).begins_with("input/")) {
|
||||
Dictionary action = E.value.variant;
|
||||
Array events = action["events"];
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
Ref<InputEvent> x = events[i];
|
||||
if (x->get_device() == -1) { // -1 was the previous value (GH-97707).
|
||||
x->set_device(InputEvent::DEVICE_ID_ALL_DEVICES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // DISABLE_DEPRECATED
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1460,6 +1477,7 @@ ProjectSettings::ProjectSettings() {
|
|||
GLOBAL_DEF("display/window/size/transparent", false);
|
||||
GLOBAL_DEF("display/window/size/extend_to_title", false);
|
||||
GLOBAL_DEF("display/window/size/no_focus", false);
|
||||
GLOBAL_DEF("display/window/size/sharp_corners", false);
|
||||
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
|
||||
|
|
|
@ -556,6 +556,11 @@ String OS::get_cache_dir() const {
|
|||
return ::OS::get_singleton()->get_cache_path();
|
||||
}
|
||||
|
||||
String OS::get_tmp_dir() const {
|
||||
// Exposed as `get_tmp_dir()` instead of `get_tmp_path()` for consistency with other exposed OS methods.
|
||||
return ::OS::get_singleton()->get_tmp_path();
|
||||
}
|
||||
|
||||
bool OS::is_debug_build() const {
|
||||
#ifdef DEBUG_ENABLED
|
||||
return true;
|
||||
|
@ -678,6 +683,7 @@ void OS::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_config_dir"), &OS::get_config_dir);
|
||||
ClassDB::bind_method(D_METHOD("get_data_dir"), &OS::get_data_dir);
|
||||
ClassDB::bind_method(D_METHOD("get_cache_dir"), &OS::get_cache_dir);
|
||||
ClassDB::bind_method(D_METHOD("get_tmp_dir"), &OS::get_tmp_dir);
|
||||
ClassDB::bind_method(D_METHOD("get_unique_id"), &OS::get_unique_id);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_keycode_string", "code"), &OS::get_keycode_string);
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#define CORE_BIND_H
|
||||
|
||||
#include "core/debugger/engine_profiler.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/object/script_language.h"
|
||||
|
@ -246,6 +245,7 @@ public:
|
|||
String get_config_dir() const;
|
||||
String get_data_dir() const;
|
||||
String get_cache_dir() const;
|
||||
String get_tmp_dir() const;
|
||||
|
||||
Error set_thread_name(const String &p_name);
|
||||
::Thread::ID get_thread_caller_id() const;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/extension/gdextension.h"
|
||||
#include "core/extension/gdextension_compat_hashes.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/xml_parser.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/script_language_extension.h"
|
||||
|
|
|
@ -35,9 +35,6 @@
|
|||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
const int InputEvent::DEVICE_ID_EMULATION = -1;
|
||||
const int InputEvent::DEVICE_ID_INTERNAL = -2;
|
||||
|
||||
void InputEvent::set_device(int p_device) {
|
||||
device = p_device;
|
||||
emit_changed();
|
||||
|
|
|
@ -62,8 +62,9 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static const int DEVICE_ID_EMULATION;
|
||||
static const int DEVICE_ID_INTERNAL;
|
||||
inline static constexpr int DEVICE_ID_EMULATION = -1;
|
||||
inline static constexpr int DEVICE_ID_INTERNAL = -2;
|
||||
inline static constexpr int DEVICE_ID_ALL_DEVICES = -3; // Signify that a given Action can be triggered by any device.
|
||||
|
||||
void set_device(int p_device);
|
||||
int get_device() const;
|
||||
|
|
|
@ -39,8 +39,6 @@
|
|||
|
||||
InputMap *InputMap::singleton = nullptr;
|
||||
|
||||
int InputMap::ALL_DEVICES = -1;
|
||||
|
||||
void InputMap::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
|
||||
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
|
||||
|
@ -163,7 +161,7 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
|
|||
int i = 0;
|
||||
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
|
||||
int device = E->get()->get_device();
|
||||
if (device == ALL_DEVICES || device == p_event->get_device()) {
|
||||
if (device == InputEvent::DEVICE_ID_ALL_DEVICES || device == p_event->get_device()) {
|
||||
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
|
||||
if (r_event_index) {
|
||||
*r_event_index = i;
|
||||
|
|
|
@ -43,11 +43,6 @@ class InputMap : public Object {
|
|||
GDCLASS(InputMap, Object);
|
||||
|
||||
public:
|
||||
/**
|
||||
* A special value used to signify that a given Action can be triggered by any device
|
||||
*/
|
||||
static int ALL_DEVICES;
|
||||
|
||||
struct Action {
|
||||
int id;
|
||||
float deadzone;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
@ -323,6 +324,33 @@ Ref<DirAccess> DirAccess::create(AccessType p_access) {
|
|||
return da;
|
||||
}
|
||||
|
||||
Ref<DirAccess> DirAccess::create_tmp(const String &p_prefix, bool p_keep) {
|
||||
Ref<DirAccess> dir_access = DirAccess::open(OS::get_singleton()->get_tmp_path());
|
||||
String hash;
|
||||
while (true) {
|
||||
hash = itos(Math::rand()).sha256_text().substr(0, 10);
|
||||
if (!DirAccess::exists(hash)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String path = (p_prefix.is_empty()
|
||||
? ""
|
||||
: p_prefix + "-") +
|
||||
hash;
|
||||
Error err = dir_access->make_dir(path);
|
||||
if (err != OK) {
|
||||
return Ref<DirAccess>();
|
||||
}
|
||||
err = dir_access->change_dir(path);
|
||||
if (err != OK) {
|
||||
return Ref<DirAccess>();
|
||||
}
|
||||
dir_access->_is_tmp = true;
|
||||
dir_access->_tmp_keep_after_free = p_keep;
|
||||
dir_access->_tmp_path = dir_access->get_current_dir();
|
||||
return dir_access;
|
||||
}
|
||||
|
||||
Error DirAccess::get_open_error() {
|
||||
return last_dir_open_error;
|
||||
}
|
||||
|
@ -552,9 +580,35 @@ bool DirAccess::is_case_sensitive(const String &p_path) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
void DirAccess::_delete_tmp() {
|
||||
// Remove created tmp directory.
|
||||
if (!_is_tmp || _tmp_keep_after_free) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DirAccess::exists(_tmp_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Error err;
|
||||
{
|
||||
Ref<DirAccess> dir_access = DirAccess::open(_tmp_path, &err);
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
err = dir_access->erase_contents_recursive();
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DirAccess::remove_absolute(_tmp_path);
|
||||
}
|
||||
|
||||
void DirAccess::_bind_methods() {
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("create_tmp", "prefix", "keep"), &DirAccess::create_tmp, DEFVAL(""), DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next);
|
||||
|
@ -598,3 +652,7 @@ void DirAccess::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
|
||||
}
|
||||
|
||||
DirAccess::~DirAccess() {
|
||||
_delete_tmp();
|
||||
}
|
||||
|
|
|
@ -61,6 +61,11 @@ private:
|
|||
bool include_navigational = false;
|
||||
bool include_hidden = false;
|
||||
|
||||
bool _is_tmp = false;
|
||||
bool _tmp_keep_after_free = false;
|
||||
String _tmp_path;
|
||||
void _delete_tmp();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
@ -128,6 +133,7 @@ public:
|
|||
static Ref<DirAccess> create_for_path(const String &p_path);
|
||||
|
||||
static Ref<DirAccess> create(AccessType p_access);
|
||||
static Ref<DirAccess> create_tmp(const String &p_prefix = "", bool p_keep = false);
|
||||
static Error get_open_error();
|
||||
|
||||
template <typename T>
|
||||
|
@ -162,7 +168,7 @@ public:
|
|||
virtual bool is_case_sensitive(const String &p_path) const;
|
||||
|
||||
DirAccess() {}
|
||||
virtual ~DirAccess() {}
|
||||
virtual ~DirAccess();
|
||||
};
|
||||
|
||||
#endif // DIR_ACCESS_H
|
||||
|
|
|
@ -68,6 +68,19 @@ void FileAccess::_set_access_type(AccessType p_access) {
|
|||
_access_type = p_access;
|
||||
}
|
||||
|
||||
void FileAccess::_delete_tmp() {
|
||||
// Remove created tmp file.
|
||||
if (!_is_tmp_file || _tmp_keep_after_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(_tmp_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DirAccess::remove_absolute(_tmp_path);
|
||||
}
|
||||
|
||||
Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
||||
Ref<FileAccess> ret;
|
||||
if (p_path.begins_with("res://")) {
|
||||
|
@ -83,6 +96,42 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
Ref<FileAccess> FileAccess::create_tmp(int p_mode_flags, const String &p_extension, bool p_keep, Error *r_error) {
|
||||
const String TMP_DIR = OS::get_singleton()->get_tmp_path();
|
||||
String extension = p_extension;
|
||||
if (extension.begins_with(".")) {
|
||||
extension = extension.replace_first(".", "");
|
||||
}
|
||||
String hash;
|
||||
String tmp_file_path;
|
||||
while (true) {
|
||||
hash = itos(Math::rand()).sha256_text().substr(0, 10);
|
||||
tmp_file_path = TMP_DIR.path_join(hash + (extension.is_empty() ? "" : "." + extension));
|
||||
if (!DirAccess::exists(tmp_file_path)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Error err;
|
||||
{
|
||||
// Create file first with WRITE mode.
|
||||
// Otherwise, it would fail to open with a READ mode.
|
||||
Ref<FileAccess> ret = FileAccess::open(tmp_file_path, FileAccess::ModeFlags::WRITE, &err);
|
||||
if (err != OK) {
|
||||
*r_error = err;
|
||||
return Ref<FileAccess>();
|
||||
}
|
||||
ret->flush();
|
||||
}
|
||||
// Open then the tmp file with the correct mode flag.
|
||||
Ref<FileAccess> ret = FileAccess::open(tmp_file_path, p_mode_flags, &err);
|
||||
if (ret.is_valid()) {
|
||||
ret->_is_tmp_file = true;
|
||||
ret->_tmp_keep_after_use = p_keep;
|
||||
ret->_tmp_path = ret->get_path_absolute();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
|
||||
return open_internal(p_path, p_mode_flags);
|
||||
}
|
||||
|
@ -810,6 +859,7 @@ void FileAccess::_bind_methods() {
|
|||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("create_tmp", "mode_flags", "extension", "keep"), &FileAccess::_create_tmp, DEFVAL(""), DEFVAL(false));
|
||||
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string);
|
||||
|
@ -897,3 +947,7 @@ void FileAccess::_bind_methods() {
|
|||
BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
|
||||
BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
|
||||
}
|
||||
|
||||
FileAccess::~FileAccess() {
|
||||
_delete_tmp();
|
||||
}
|
||||
|
|
|
@ -122,6 +122,11 @@ private:
|
|||
|
||||
static Ref<FileAccess> _open(const String &p_path, ModeFlags p_mode_flags);
|
||||
|
||||
bool _is_tmp_file = false;
|
||||
bool _tmp_keep_after_use = false;
|
||||
String _tmp_path;
|
||||
void _delete_tmp();
|
||||
|
||||
public:
|
||||
static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
|
||||
|
||||
|
@ -228,13 +233,18 @@ public:
|
|||
static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path, &last_file_open_error); }
|
||||
static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path, &last_file_open_error); }
|
||||
|
||||
static Ref<FileAccess> create_tmp(int p_mode_flags, const String &p_extension = "", bool p_keep = false, Error *r_error = nullptr);
|
||||
static Ref<FileAccess> _create_tmp(int p_mode_flags, const String &p_extension = "", bool p_keep = false) {
|
||||
return create_tmp(p_mode_flags, p_extension, p_keep, &last_file_open_error);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void make_default(AccessType p_access) {
|
||||
create_func[p_access] = _create_builtin<T>;
|
||||
}
|
||||
|
||||
FileAccess() {}
|
||||
virtual ~FileAccess() {}
|
||||
virtual ~FileAccess();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(FileAccess::CompressionMode);
|
||||
|
|
|
@ -121,7 +121,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
|
|||
d.get_key_list(&keys);
|
||||
|
||||
if (p_sort_keys) {
|
||||
keys.sort();
|
||||
keys.sort_custom<StringLikeVariantOrder>();
|
||||
}
|
||||
|
||||
bool first_key = true;
|
||||
|
|
|
@ -1268,6 +1268,11 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String
|
|||
return;
|
||||
}
|
||||
|
||||
// res files not supported for GDExtension.
|
||||
if (p_type == "GDExtension") {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> extensions;
|
||||
ClassDB::get_extensions_for_type(p_type, &extensions);
|
||||
|
||||
|
|
|
@ -276,6 +276,10 @@ String OS::get_cache_path() const {
|
|||
return ".";
|
||||
}
|
||||
|
||||
String OS::get_tmp_path() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
// Path to macOS .app bundle resources
|
||||
String OS::get_bundle_resource_dir() const {
|
||||
return ".";
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#define OS_H
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/logger.h"
|
||||
#include "core/io/remote_filesystem_client.h"
|
||||
#include "core/os/time_enums.h"
|
||||
|
@ -275,6 +274,7 @@ public:
|
|||
virtual String get_data_path() const;
|
||||
virtual String get_config_path() const;
|
||||
virtual String get_cache_path() const;
|
||||
virtual String get_tmp_path() const;
|
||||
virtual String get_bundle_resource_dir() const;
|
||||
virtual String get_bundle_icon_path() const;
|
||||
|
||||
|
|
|
@ -228,32 +228,41 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p
|
|||
return 10;
|
||||
}
|
||||
|
||||
const String cache_key = p_locale_a + "|" + p_locale_b;
|
||||
const int *cached_result = locale_compare_cache.getptr(cache_key);
|
||||
if (cached_result) {
|
||||
return *cached_result;
|
||||
}
|
||||
|
||||
String locale_a = _standardize_locale(p_locale_a, true);
|
||||
String locale_b = _standardize_locale(p_locale_b, true);
|
||||
|
||||
if (locale_a == locale_b) {
|
||||
// Exact match.
|
||||
locale_compare_cache.insert(cache_key, 10);
|
||||
return 10;
|
||||
}
|
||||
|
||||
Vector<String> locale_a_elements = locale_a.split("_");
|
||||
Vector<String> locale_b_elements = locale_b.split("_");
|
||||
if (locale_a_elements[0] == locale_b_elements[0]) {
|
||||
// Matching language, both locales have extra parts.
|
||||
// Return number of matching elements.
|
||||
int matching_elements = 1;
|
||||
for (int i = 1; i < locale_a_elements.size(); i++) {
|
||||
for (int j = 1; j < locale_b_elements.size(); j++) {
|
||||
if (locale_a_elements[i] == locale_b_elements[j]) {
|
||||
matching_elements++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matching_elements;
|
||||
} else {
|
||||
if (locale_a_elements[0] != locale_b_elements[0]) {
|
||||
// No match.
|
||||
locale_compare_cache.insert(cache_key, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Matching language, both locales have extra parts.
|
||||
// Return number of matching elements.
|
||||
int matching_elements = 1;
|
||||
for (int i = 1; i < locale_a_elements.size(); i++) {
|
||||
for (int j = 1; j < locale_b_elements.size(); j++) {
|
||||
if (locale_a_elements[i] == locale_b_elements[j]) {
|
||||
matching_elements++;
|
||||
}
|
||||
}
|
||||
}
|
||||
locale_compare_cache.insert(cache_key, matching_elements);
|
||||
return matching_elements;
|
||||
}
|
||||
|
||||
String TranslationServer::get_locale_name(const String &p_locale) const {
|
||||
|
|
|
@ -46,6 +46,8 @@ class TranslationServer : public Object {
|
|||
Ref<TranslationDomain> doc_domain;
|
||||
HashMap<StringName, Ref<TranslationDomain>> custom_domains;
|
||||
|
||||
mutable HashMap<String, int> locale_compare_cache;
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
static TranslationServer *singleton;
|
||||
|
|
|
@ -854,6 +854,19 @@ struct StringLikeVariantComparator {
|
|||
static bool compare(const Variant &p_lhs, const Variant &p_rhs);
|
||||
};
|
||||
|
||||
struct StringLikeVariantOrder {
|
||||
static _ALWAYS_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) {
|
||||
if (p_lhs.is_string() && p_rhs.is_string()) {
|
||||
return p_lhs.operator String() < p_rhs.operator String();
|
||||
}
|
||||
return p_lhs < p_rhs;
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ bool operator()(const Variant &p_lhs, const Variant &p_rhs) const {
|
||||
return compare(p_lhs, p_rhs);
|
||||
}
|
||||
};
|
||||
|
||||
Variant::ObjData &Variant::_get_obj() {
|
||||
return *reinterpret_cast<ObjData *>(&_data._mem[0]);
|
||||
}
|
||||
|
|
|
@ -2245,7 +2245,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
|
|||
} else {
|
||||
List<Variant> keys;
|
||||
dict.get_key_list(&keys);
|
||||
keys.sort();
|
||||
keys.sort_custom<StringLikeVariantOrder>();
|
||||
|
||||
if (keys.is_empty()) {
|
||||
// Avoid unnecessary line break.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
[CanvasModulate] applies a color tint to all nodes on a canvas. Only one can be used to tint a canvas, but [CanvasLayer]s can be used to render things independently.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="2D lights and shadows">$DOCS_URL/tutorials/2d/2d_lights_and_shadows.html</link>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)" keywords="colour">
|
||||
|
|
|
@ -1365,7 +1365,7 @@
|
|||
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the parent control layout direction.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection">
|
||||
<constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the current locale.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
|
||||
|
@ -1374,6 +1374,14 @@
|
|||
<constant name="LAYOUT_DIRECTION_RTL" value="3" enum="LayoutDirection">
|
||||
Right-to-left layout direction.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_SYSTEM_LOCALE" value="4" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the system locale.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_MAX" value="5" enum="LayoutDirection">
|
||||
Represents the size of the [enum LayoutDirection] enum.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection" deprecated="Use [constant LAYOUT_DIRECTION_APPLICATION_LOCALE] instead.">
|
||||
</constant>
|
||||
<constant name="TEXT_DIRECTION_INHERITED" value="3" enum="TextDirection">
|
||||
Text writing direction is the same as layout direction.
|
||||
</constant>
|
||||
|
|
|
@ -104,6 +104,16 @@
|
|||
[b]Note:[/b] This method is implemented on macOS, Linux, and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_tmp" qualifiers="static">
|
||||
<return type="DirAccess" />
|
||||
<param index="0" name="prefix" type="String" default="""" />
|
||||
<param index="1" name="keep" type="bool" default="false" />
|
||||
<description>
|
||||
Creates a temporary directory.
|
||||
If [param keep] is [code]true[/code], the directory will outlive the [DirAccess] instance. When [code]false[/code], the directory will be deleted, including all its files.
|
||||
If the returned value is [code]null[/code], use [method get_open_error] to get some context.
|
||||
</description>
|
||||
</method>
|
||||
<method name="current_is_dir" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
|
|
|
@ -2099,7 +2099,11 @@
|
|||
<constant name="WINDOW_FLAG_MOUSE_PASSTHROUGH" value="7" enum="WindowFlags">
|
||||
All mouse events are passed to the underlying window of the same application.
|
||||
</constant>
|
||||
<constant name="WINDOW_FLAG_MAX" value="8" enum="WindowFlags">
|
||||
<constant name="WINDOW_FLAG_SHARP_CORNERS" value="8" enum="WindowFlags">
|
||||
Window style is overridden, forcing sharp corners.
|
||||
[b]Note:[/b] This flag is implemented only on Windows (11).
|
||||
</constant>
|
||||
<constant name="WINDOW_FLAG_MAX" value="9" enum="WindowFlags">
|
||||
Max value of the [enum WindowFlags].
|
||||
</constant>
|
||||
<constant name="WINDOW_EVENT_MOUSE_ENTER" value="0" enum="WindowEvent">
|
||||
|
|
|
@ -50,6 +50,17 @@
|
|||
[b]Note:[/b] [FileAccess] will automatically close when it's freed, which happens when it goes out of scope or when it gets assigned with [code]null[/code]. In C# the reference must be disposed after we are done using it, this can be done with the [code]using[/code] statement or calling the [code]Dispose[/code] method directly.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_tmp" qualifiers="static">
|
||||
<return type="FileAccess" />
|
||||
<param index="0" name="mode_flags" type="int" />
|
||||
<param index="1" name="extension" type="String" default="""" />
|
||||
<param index="2" name="keep" type="bool" default="false" />
|
||||
<description>
|
||||
Creates a temporary file.
|
||||
If [param extension] is not empty, it will be appended to the temporary file name.
|
||||
If [param keep] is [code]true[/code], the file will outlive the [FileAccess] instance.
|
||||
</description>
|
||||
</method>
|
||||
<method name="eof_reached" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<method name="inspect_native_shader_code">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code.
|
||||
Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code. See also [method Shader.inspect_native_shader_code].
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
<description>
|
||||
Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [param delta] variable should be constant. [param delta] is in seconds.
|
||||
It is only called if physics processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_physics_process].
|
||||
Processing happens in order of [member process_physics_priority], lower priority values are called first. Nodes with the same priority are processed in tree order, or top to bottom as seen in the editor (also known as pre-order traversal).
|
||||
Corresponds to the [constant NOTIFICATION_PHYSICS_PROCESS] notification in [method Object._notification].
|
||||
[b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
|
||||
</description>
|
||||
|
@ -83,6 +84,7 @@
|
|||
<description>
|
||||
Called during the processing step of the main loop. Processing happens at every frame and as fast as possible, so the [param delta] time since the previous frame is not constant. [param delta] is in seconds.
|
||||
It is only called if processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process].
|
||||
Processing happens in order of [member process_priority], lower priority values are called first. Nodes with the same priority are processed in tree order, or top to bottom as seen in the editor (also known as pre-order traversal).
|
||||
Corresponds to the [constant NOTIFICATION_PROCESS] notification in [method Object._notification].
|
||||
[b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
|
||||
</description>
|
||||
|
@ -1015,10 +1017,10 @@
|
|||
The node's processing behavior (see [enum ProcessMode]). To check if the node can process in its current mode, use [method can_process].
|
||||
</member>
|
||||
<member name="process_physics_priority" type="int" setter="set_physics_process_priority" getter="get_physics_process_priority" default="0">
|
||||
Similar to [member process_priority] but for [constant NOTIFICATION_PHYSICS_PROCESS], [method _physics_process] or the internal version.
|
||||
Similar to [member process_priority] but for [constant NOTIFICATION_PHYSICS_PROCESS], [method _physics_process], or [constant NOTIFICATION_INTERNAL_PHYSICS_PROCESS].
|
||||
</member>
|
||||
<member name="process_priority" type="int" setter="set_process_priority" getter="get_process_priority" default="0">
|
||||
The node's execution order of the process callbacks ([method _process], [method _physics_process], and internal processing). Nodes whose priority value is [i]lower[/i] call their process callbacks first, regardless of tree order.
|
||||
The node's execution order of the process callbacks ([method _process], [constant NOTIFICATION_PROCESS], and [constant NOTIFICATION_INTERNAL_PROCESS]). Nodes whose priority value is [i]lower[/i] call their process callbacks first, regardless of tree order.
|
||||
</member>
|
||||
<member name="process_thread_group" type="int" setter="set_process_thread_group" getter="get_process_thread_group" enum="Node.ProcessThreadGroup" default="0">
|
||||
Set the process thread group for this node (basically, whether it receives [constant NOTIFICATION_PROCESS], [constant NOTIFICATION_PHYSICS_PROCESS], [method _process] or [method _physics_process] (and the internal versions) on the main thread or in a sub-thread.
|
||||
|
|
|
@ -526,6 +526,12 @@
|
|||
[b]Note:[/b] Thread IDs are not deterministic and may be reused across application restarts.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_tmp_dir" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns the [i]global[/i] temporary data directory according to the operating system's standards.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_unique_id" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
|
|
|
@ -875,6 +875,10 @@
|
|||
[b]Note:[/b] Certain window managers can be configured to ignore the non-resizable status of a window. Do not rely on this setting as a guarantee that the window will [i]never[/i] be resizable.
|
||||
[b]Note:[/b] This setting is ignored on iOS.
|
||||
</member>
|
||||
<member name="display/window/size/sharp_corners" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the main window uses sharp corners by default.
|
||||
[b]Note:[/b] This property is implemented only on Windows (11).
|
||||
</member>
|
||||
<member name="display/window/size/transparent" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], enables a window manager hint that the main window background [i]can[/i] be transparent. This does not make the background actually transparent. For the background to be transparent, the root viewport must also be made transparent by enabling [member rendering/viewport/transparent_background].
|
||||
[b]Note:[/b] To use a transparent splash screen, set [member application/boot_splash/bg_color] to [code]Color(0, 0, 0, 0)[/code].
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
If argument [param get_groups] is true, parameter grouping hints will be provided.
|
||||
</description>
|
||||
</method>
|
||||
<method name="inspect_native_shader_code">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code. See also [method Material.inspect_native_shader_code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_default_texture_parameter">
|
||||
<return type="void" />
|
||||
<param index="0" name="name" type="StringName" />
|
||||
|
|
|
@ -509,6 +509,12 @@
|
|||
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
|
||||
Text [Color] for a [constant TreeItem.CELL_MODE_CHECK] mode cell when it's non-editable (see [method TreeItem.set_editable]).
|
||||
</theme_item>
|
||||
<theme_item name="font_hovered_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
|
||||
Text [Color] used when the item is hovered.
|
||||
</theme_item>
|
||||
<theme_item name="font_hovered_dimmed_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
|
||||
Text [Color] used when the item is hovered, while a button of the same item is hovered as the same time.
|
||||
</theme_item>
|
||||
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
|
||||
The tint of text outline of the item.
|
||||
</theme_item>
|
||||
|
@ -645,6 +651,9 @@
|
|||
<theme_item name="updown" data_type="icon" type="Texture2D">
|
||||
The updown arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell.
|
||||
</theme_item>
|
||||
<theme_item name="button_hover" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when a button in the tree is hovered.
|
||||
</theme_item>
|
||||
<theme_item name="button_pressed" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when a button in the tree is pressed.
|
||||
</theme_item>
|
||||
|
@ -666,6 +675,12 @@
|
|||
<theme_item name="focus" data_type="style" type="StyleBox">
|
||||
The focused style for the [Tree], drawn on top of everything.
|
||||
</theme_item>
|
||||
<theme_item name="hovered" data_type="style" type="StyleBox">
|
||||
[StyleBox] for the item being hovered.
|
||||
</theme_item>
|
||||
<theme_item name="hovered_dimmed" data_type="style" type="StyleBox">
|
||||
[StyleBox] for the item being hovered, while a button of the same item is hovered as the same time.
|
||||
</theme_item>
|
||||
<theme_item name="panel" data_type="style" type="StyleBox">
|
||||
The background style for the [Tree].
|
||||
</theme_item>
|
||||
|
|
|
@ -659,6 +659,11 @@
|
|||
If [member ProjectSettings.display/window/subwindows/embed_subwindows] is [code]false[/code], the position is in absolute screen coordinates. This typically applies to editor plugins. If the setting is [code]true[/code], the window's position is in the coordinates of its parent [Viewport].
|
||||
[b]Note:[/b] This property only works if [member initial_position] is set to [constant WINDOW_INITIAL_POSITION_ABSOLUTE].
|
||||
</member>
|
||||
<member name="sharp_corners" type="bool" setter="set_flag" getter="get_flag" default="false">
|
||||
If [code]true[/code], the [Window] will override the OS window style to display sharp corners.
|
||||
[b]Note:[/b] This property is implemented only on Windows (11).
|
||||
[b]Note:[/b] This property only works with native windows.
|
||||
</member>
|
||||
<member name="size" type="Vector2i" setter="set_size" getter="get_size" default="Vector2i(100, 100)">
|
||||
The window's size in pixels.
|
||||
</member>
|
||||
|
@ -842,7 +847,12 @@
|
|||
All mouse events are passed to the underlying window of the same application.
|
||||
[b]Note:[/b] This flag has no effect in embedded windows.
|
||||
</constant>
|
||||
<constant name="FLAG_MAX" value="8" enum="Flags">
|
||||
<constant name="FLAG_SHARP_CORNERS" value="8" enum="Flags">
|
||||
Window style is overridden, forcing sharp corners.
|
||||
[b]Note:[/b] This flag has no effect in embedded windows.
|
||||
[b]Note:[/b] This flag is implemented only on Windows (11).
|
||||
</constant>
|
||||
<constant name="FLAG_MAX" value="9" enum="Flags">
|
||||
Max value of the [enum Flags].
|
||||
</constant>
|
||||
<constant name="CONTENT_SCALE_MODE_DISABLED" value="0" enum="ContentScaleMode">
|
||||
|
@ -878,7 +888,7 @@
|
|||
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the parent window layout direction.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection">
|
||||
<constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the current locale.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
|
||||
|
@ -887,6 +897,14 @@
|
|||
<constant name="LAYOUT_DIRECTION_RTL" value="3" enum="LayoutDirection">
|
||||
Right-to-left layout direction.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_SYSTEM_LOCALE" value="4" enum="LayoutDirection">
|
||||
Automatic layout direction, determined from the system locale.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_MAX" value="5" enum="LayoutDirection">
|
||||
Represents the size of the [enum LayoutDirection] enum.
|
||||
</constant>
|
||||
<constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection" deprecated="Use [constant LAYOUT_DIRECTION_APPLICATION_LOCALE] instead.">
|
||||
</constant>
|
||||
<constant name="WINDOW_INITIAL_POSITION_ABSOLUTE" value="0" enum="WindowInitialPosition">
|
||||
Initial window position is determined by [member position].
|
||||
</constant>
|
||||
|
|
|
@ -2003,6 +2003,8 @@ static D3D12_BARRIER_LAYOUT _rd_texture_layout_to_d3d12_barrier_layout(RDD::Text
|
|||
switch (p_texture_layout) {
|
||||
case RDD::TEXTURE_LAYOUT_UNDEFINED:
|
||||
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||
case RDD::TEXTURE_LAYOUT_GENERAL:
|
||||
return D3D12_BARRIER_LAYOUT_COMMON;
|
||||
case RDD::TEXTURE_LAYOUT_STORAGE_OPTIMAL:
|
||||
return D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS;
|
||||
case RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
|
@ -6175,6 +6177,8 @@ uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) {
|
|||
return false;
|
||||
case API_TRAIT_CLEARS_WITH_COPY_ENGINE:
|
||||
return false;
|
||||
case API_TRAIT_USE_GENERAL_IN_COPY_QUEUES:
|
||||
return true;
|
||||
default:
|
||||
return RenderingDeviceDriver::api_trait_get(p_trait);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "storage/texture_storage.h"
|
||||
|
||||
|
|
|
@ -230,6 +230,32 @@ TextureStorage::TextureStorage() {
|
|||
sdf_shader.shader_version = sdf_shader.shader.version_create();
|
||||
}
|
||||
|
||||
// Initialize texture placeholder data for the `texture_*_placeholder_initialize()` methods.
|
||||
|
||||
constexpr int placeholder_size = 4;
|
||||
texture_2d_placeholder = Image::create_empty(placeholder_size, placeholder_size, false, Image::FORMAT_RGBA8);
|
||||
// Draw a magenta/black checkerboard pattern.
|
||||
for (int i = 0; i < placeholder_size * placeholder_size; i++) {
|
||||
const int x = i % placeholder_size;
|
||||
const int y = i / placeholder_size;
|
||||
texture_2d_placeholder->set_pixel(x, y, (x + y) % 2 == 0 ? Color(1, 0, 1) : Color(0, 0, 0));
|
||||
}
|
||||
|
||||
texture_2d_array_placeholder.push_back(texture_2d_placeholder);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
cubemap_placeholder.push_back(texture_2d_placeholder);
|
||||
}
|
||||
|
||||
Ref<Image> texture_2d_placeholder_rotated;
|
||||
texture_2d_placeholder_rotated.instantiate();
|
||||
texture_2d_placeholder_rotated->copy_from(texture_2d_placeholder);
|
||||
texture_2d_placeholder_rotated->rotate_90(CLOCKWISE);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Alternate checkerboard pattern on odd layers (by using a copy that is rotated 90 degrees).
|
||||
texture_3d_placeholder.push_back(i % 2 == 0 ? texture_2d_placeholder : texture_2d_placeholder_rotated);
|
||||
}
|
||||
|
||||
#ifdef GL_API_ENABLED
|
||||
if (RasterizerGLES3::is_gles_over_gl()) {
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
|
@ -1014,46 +1040,19 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
|
|||
}
|
||||
|
||||
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
|
||||
//this could be better optimized to reuse an existing image , done this way
|
||||
//for now to get it working
|
||||
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
|
||||
image->fill(Color(1, 0, 1, 1));
|
||||
|
||||
texture_2d_initialize(p_texture, image);
|
||||
texture_2d_initialize(p_texture, texture_2d_placeholder);
|
||||
}
|
||||
|
||||
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) {
|
||||
//this could be better optimized to reuse an existing image , done this way
|
||||
//for now to get it working
|
||||
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
|
||||
image->fill(Color(1, 0, 1, 1));
|
||||
|
||||
Vector<Ref<Image>> images;
|
||||
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) {
|
||||
if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
|
||||
images.push_back(image);
|
||||
texture_2d_layered_initialize(p_texture, texture_2d_array_placeholder, p_layered_type);
|
||||
} else {
|
||||
//cube
|
||||
for (int i = 0; i < 6; i++) {
|
||||
images.push_back(image);
|
||||
}
|
||||
texture_2d_layered_initialize(p_texture, cubemap_placeholder, p_layered_type);
|
||||
}
|
||||
|
||||
texture_2d_layered_initialize(p_texture, images, p_layered_type);
|
||||
}
|
||||
|
||||
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
|
||||
//this could be better optimized to reuse an existing image , done this way
|
||||
//for now to get it working
|
||||
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
|
||||
image->fill(Color(1, 0, 1, 1));
|
||||
|
||||
Vector<Ref<Image>> images;
|
||||
//cube
|
||||
for (int i = 0; i < 4; i++) {
|
||||
images.push_back(image);
|
||||
}
|
||||
|
||||
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images);
|
||||
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, texture_3d_placeholder);
|
||||
}
|
||||
|
||||
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "platform_gl.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
|
@ -521,6 +522,11 @@ public:
|
|||
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
|
||||
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
|
||||
|
||||
Ref<Image> texture_2d_placeholder;
|
||||
Vector<Ref<Image>> texture_2d_array_placeholder;
|
||||
Vector<Ref<Image>> cubemap_placeholder;
|
||||
Vector<Ref<Image>> texture_3d_placeholder;
|
||||
|
||||
//these two APIs can be used together or in combination with the others.
|
||||
virtual void texture_2d_placeholder_initialize(RID p_texture) override;
|
||||
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;
|
||||
|
|
|
@ -96,6 +96,22 @@ _FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
|
|||
return p_a;
|
||||
}
|
||||
|
||||
enum StageResourceUsage : uint32_t {
|
||||
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
};
|
||||
|
||||
typedef LocalVector<__unsafe_unretained id<MTLResource>> ResourceVector;
|
||||
typedef HashMap<StageResourceUsage, ResourceVector> ResourceUsageMap;
|
||||
|
||||
enum class MDCommandBufferStateType {
|
||||
None,
|
||||
Render,
|
||||
|
@ -230,6 +246,7 @@ public:
|
|||
uint32_t index_offset = 0;
|
||||
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
|
||||
LocalVector<NSUInteger> vertex_offsets;
|
||||
ResourceUsageMap resource_usage;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint8_t {
|
||||
DIRTY_NONE = 0b0000'0000,
|
||||
|
@ -271,8 +288,14 @@ public:
|
|||
blend_constants.reset();
|
||||
vertex_buffers.clear();
|
||||
vertex_offsets.clear();
|
||||
// Keep the keys, as they are likely to be used again.
|
||||
for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
|
||||
kv.value.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void end_encoding();
|
||||
|
||||
_FORCE_INLINE_ void mark_viewport_dirty() {
|
||||
if (viewports.is_empty()) {
|
||||
return;
|
||||
|
@ -356,13 +379,20 @@ public:
|
|||
} render;
|
||||
|
||||
// State specific for a compute pass.
|
||||
struct {
|
||||
struct ComputeState {
|
||||
MDComputePipeline *pipeline = nullptr;
|
||||
id<MTLComputeCommandEncoder> encoder = nil;
|
||||
ResourceUsageMap resource_usage;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
pipeline = nil;
|
||||
encoder = nil;
|
||||
// Keep the keys, as they are likely to be used again.
|
||||
for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
|
||||
kv.value.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void end_encoding();
|
||||
} compute;
|
||||
|
||||
// State specific to a blit pass.
|
||||
|
@ -632,19 +662,6 @@ public:
|
|||
MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *p_vert, MDLibrary *p_frag);
|
||||
};
|
||||
|
||||
enum StageResourceUsage : uint32_t {
|
||||
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
|
||||
p_a = StageResourceUsage(uint32_t(p_a) | p_b);
|
||||
return p_a;
|
||||
|
@ -667,7 +684,13 @@ struct HashMapComparatorDefault<RDD::ShaderID> {
|
|||
|
||||
struct BoundUniformSet {
|
||||
id<MTLBuffer> buffer;
|
||||
HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
|
||||
ResourceUsageMap usage_to_resources;
|
||||
|
||||
/// Perform a 2-way merge each key of `ResourceVector` resources from this set into the
|
||||
/// destination set.
|
||||
///
|
||||
/// Assumes the vectors of resources are sorted.
|
||||
void merge_into(ResourceUsageMap &p_dst) const;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
void MDCommandBuffer::begin() {
|
||||
DEV_ASSERT(commandBuffer == nil);
|
||||
commandBuffer = queue.commandBuffer;
|
||||
commandBuffer = queue.commandBufferWithUnretainedReferences;
|
||||
}
|
||||
|
||||
void MDCommandBuffer::end() {
|
||||
|
@ -390,6 +390,38 @@ void MDCommandBuffer::render_set_blend_constants(const Color &p_constants) {
|
|||
}
|
||||
}
|
||||
|
||||
void BoundUniformSet::merge_into(ResourceUsageMap &p_dst) const {
|
||||
for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : usage_to_resources) {
|
||||
ResourceVector *resources = p_dst.getptr(keyval.key);
|
||||
if (resources == nullptr) {
|
||||
resources = &p_dst.insert(keyval.key, ResourceVector())->value;
|
||||
}
|
||||
// Reserve space for the new resources, assuming they are all added.
|
||||
resources->reserve(resources->size() + keyval.value.size());
|
||||
|
||||
uint32_t i = 0, j = 0;
|
||||
__unsafe_unretained id<MTLResource> *resources_ptr = resources->ptr();
|
||||
const __unsafe_unretained id<MTLResource> *keyval_ptr = keyval.value.ptr();
|
||||
// 2-way merge.
|
||||
while (i < resources->size() && j < keyval.value.size()) {
|
||||
if (resources_ptr[i] < keyval_ptr[j]) {
|
||||
i++;
|
||||
} else if (resources_ptr[i] > keyval_ptr[j]) {
|
||||
resources->insert(i, keyval_ptr[j]);
|
||||
i++;
|
||||
j++;
|
||||
} else {
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
// Append the remaining resources.
|
||||
for (; j < keyval.value.size(); j++) {
|
||||
resources->push_back(keyval_ptr[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MDCommandBuffer::_render_bind_uniform_sets() {
|
||||
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
||||
if (!render.dirty.has_flag(RenderState::DIRTY_UNIFORMS)) {
|
||||
|
@ -408,7 +440,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
|
|||
// Find the index of the next set bit.
|
||||
int index = __builtin_ctzll(set_uniforms);
|
||||
// Clear the set bit.
|
||||
set_uniforms &= ~(1ULL << index);
|
||||
set_uniforms &= (set_uniforms - 1);
|
||||
MDUniformSet *set = render.uniform_sets[index];
|
||||
if (set == nullptr || set->index >= (uint32_t)shader->sets.size()) {
|
||||
continue;
|
||||
|
@ -416,17 +448,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
|
|||
UniformSet const &set_info = shader->sets[set->index];
|
||||
|
||||
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
|
||||
|
||||
for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bus.bound_resources) {
|
||||
MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_VERTEX);
|
||||
if (usage != 0) {
|
||||
[enc useResource:keyval.key usage:usage stages:MTLRenderStageVertex];
|
||||
}
|
||||
usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
|
||||
if (usage != 0) {
|
||||
[enc useResource:keyval.key usage:usage stages:MTLRenderStageFragment];
|
||||
}
|
||||
}
|
||||
bus.merge_into(render.resource_usage);
|
||||
|
||||
// Set the buffer for the vertex stage.
|
||||
{
|
||||
|
@ -545,8 +567,7 @@ void MDCommandBuffer::_end_render_pass() {
|
|||
// see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407
|
||||
}
|
||||
|
||||
[render.encoder endEncoding];
|
||||
render.encoder = nil;
|
||||
render.end_encoding();
|
||||
}
|
||||
|
||||
void MDCommandBuffer::_render_clear_render_area() {
|
||||
|
@ -792,10 +813,59 @@ void MDCommandBuffer::render_draw_indirect_count(RDD::BufferID p_indirect_buffer
|
|||
ERR_FAIL_MSG("not implemented");
|
||||
}
|
||||
|
||||
void MDCommandBuffer::RenderState::end_encoding() {
|
||||
if (encoder == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind all resources.
|
||||
for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
|
||||
if (keyval.value.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLResourceUsage vert_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_VERTEX);
|
||||
MTLResourceUsage frag_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
|
||||
if (vert_usage == frag_usage) {
|
||||
[encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex | MTLRenderStageFragment];
|
||||
} else {
|
||||
if (vert_usage != 0) {
|
||||
[encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex];
|
||||
}
|
||||
if (frag_usage != 0) {
|
||||
[encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:frag_usage stages:MTLRenderStageFragment];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[encoder endEncoding];
|
||||
encoder = nil;
|
||||
}
|
||||
|
||||
void MDCommandBuffer::ComputeState::end_encoding() {
|
||||
if (encoder == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind all resources.
|
||||
for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
|
||||
if (keyval.value.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
MTLResourceUsage usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
|
||||
if (usage != 0) {
|
||||
[encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:usage];
|
||||
}
|
||||
}
|
||||
|
||||
[encoder endEncoding];
|
||||
encoder = nil;
|
||||
}
|
||||
|
||||
void MDCommandBuffer::render_end_pass() {
|
||||
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
||||
|
||||
[render.encoder endEncoding];
|
||||
render.end_encoding();
|
||||
render.reset();
|
||||
type = MDCommandBufferStateType::None;
|
||||
}
|
||||
|
@ -813,13 +883,7 @@ void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set,
|
|||
|
||||
MDUniformSet *set = (MDUniformSet *)(p_uniform_set.id);
|
||||
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
|
||||
|
||||
for (KeyValue<id<MTLResource>, StageResourceUsage> &keyval : bus.bound_resources) {
|
||||
MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
|
||||
if (usage != 0) {
|
||||
[enc useResource:keyval.key usage:usage];
|
||||
}
|
||||
}
|
||||
bus.merge_into(compute.resource_usage);
|
||||
|
||||
uint32_t const *offset = set_info.offsets.getptr(RDD::SHADER_STAGE_COMPUTE);
|
||||
if (offset) {
|
||||
|
@ -848,7 +912,7 @@ void MDCommandBuffer::compute_dispatch_indirect(RDD::BufferID p_indirect_buffer,
|
|||
void MDCommandBuffer::_end_compute_dispatch() {
|
||||
DEV_ASSERT(type == MDCommandBufferStateType::Compute);
|
||||
|
||||
[compute.encoder endEncoding];
|
||||
compute.end_encoding();
|
||||
compute.reset();
|
||||
type = MDCommandBufferStateType::None;
|
||||
}
|
||||
|
@ -1052,7 +1116,20 @@ BoundUniformSet &MDUniformSet::boundUniformSetForShader(MDShader *p_shader, id<M
|
|||
}
|
||||
}
|
||||
|
||||
BoundUniformSet bs = { .buffer = enc_buffer, .bound_resources = bound_resources };
|
||||
SearchArray<__unsafe_unretained id<MTLResource>> search;
|
||||
ResourceUsageMap usage_to_resources;
|
||||
for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
|
||||
ResourceVector *resources = usage_to_resources.getptr(keyval.value);
|
||||
if (resources == nullptr) {
|
||||
resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
|
||||
}
|
||||
int64_t pos = search.bisect(resources->ptr(), resources->size(), keyval.key, true);
|
||||
if (pos == resources->size() || (*resources)[pos] != keyval.key) {
|
||||
resources->insert(pos, keyval.key);
|
||||
}
|
||||
}
|
||||
|
||||
BoundUniformSet bs = { .buffer = enc_buffer, .usage_to_resources = usage_to_resources };
|
||||
bound_uniforms.insert(p_shader, bs);
|
||||
return bound_uniforms.get(p_shader);
|
||||
}
|
||||
|
@ -1211,8 +1288,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
|
|||
varyings.layer = uint(attributes.a_position.w);
|
||||
return varyings;
|
||||
}
|
||||
)",
|
||||
ClearAttKey::DEPTH_INDEX];
|
||||
)", ClearAttKey::DEPTH_INDEX];
|
||||
|
||||
return new_func(msl, @"vertClear", nil);
|
||||
}
|
||||
|
|
|
@ -2060,6 +2060,10 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
|
|||
|
||||
case BT::Sampler: {
|
||||
primary.dataType = MTLDataTypeSampler;
|
||||
primary.arrayLength = 1;
|
||||
for (uint32_t const &a : a_type.array) {
|
||||
primary.arrayLength *= a;
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
|
@ -2067,7 +2071,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
|
|||
} break;
|
||||
}
|
||||
|
||||
// Find array length.
|
||||
// Find array length of image.
|
||||
if (basetype == BT::Image || basetype == BT::SampledImage) {
|
||||
primary.arrayLength = 1;
|
||||
for (uint32_t const &a : a_type.array) {
|
||||
|
|
|
@ -225,6 +225,10 @@ String OS_Unix::get_version() const {
|
|||
return "";
|
||||
}
|
||||
|
||||
String OS_Unix::get_tmp_path() const {
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
double OS_Unix::get_unix_time() const {
|
||||
struct timeval tv_now;
|
||||
gettimeofday(&tv_now, nullptr);
|
||||
|
|
|
@ -72,6 +72,8 @@ public:
|
|||
virtual String get_distribution_name() const override;
|
||||
virtual String get_version() const override;
|
||||
|
||||
virtual String get_tmp_path() const override;
|
||||
|
||||
virtual DateTime get_datetime(bool p_utc) const override;
|
||||
virtual TimeZoneInfo get_time_zone_info() const override;
|
||||
|
||||
|
|
|
@ -266,6 +266,7 @@ static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
|
|||
|
||||
static VkImageLayout RD_TO_VK_LAYOUT[RDD::TEXTURE_LAYOUT_MAX] = {
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, // TEXTURE_LAYOUT_UNDEFINED
|
||||
VK_IMAGE_LAYOUT_GENERAL, // TEXTURE_LAYOUT_GENERAL
|
||||
VK_IMAGE_LAYOUT_GENERAL, // TEXTURE_LAYOUT_STORAGE_OPTIMAL
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||||
|
@ -2636,11 +2637,13 @@ bool RenderingDeviceDriverVulkan::command_buffer_begin(CommandBufferID p_cmd_buf
|
|||
bool RenderingDeviceDriverVulkan::command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) {
|
||||
// Reset is implicit (VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT).
|
||||
|
||||
Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
|
||||
|
||||
VkCommandBufferInheritanceInfo inheritance_info = {};
|
||||
inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
||||
inheritance_info.renderPass = (VkRenderPass)p_render_pass.id;
|
||||
inheritance_info.subpass = p_subpass;
|
||||
inheritance_info.framebuffer = (VkFramebuffer)p_framebuffer.id;
|
||||
inheritance_info.framebuffer = framebuffer->vk_framebuffer;
|
||||
|
||||
VkCommandBufferBeginInfo cmd_buf_begin_info = {};
|
||||
cmd_buf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
|
@ -2950,12 +2953,16 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
|
|||
fb_create_info.height = surface->height;
|
||||
fb_create_info.layers = 1;
|
||||
|
||||
VkFramebuffer framebuffer;
|
||||
VkFramebuffer vk_framebuffer;
|
||||
for (uint32_t i = 0; i < image_count; i++) {
|
||||
fb_create_info.pAttachments = &swap_chain->image_views[i];
|
||||
err = vkCreateFramebuffer(vk_device, &fb_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER), &framebuffer);
|
||||
err = vkCreateFramebuffer(vk_device, &fb_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER), &vk_framebuffer);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
|
||||
|
||||
Framebuffer *framebuffer = memnew(Framebuffer);
|
||||
framebuffer->vk_framebuffer = vk_framebuffer;
|
||||
framebuffer->swap_chain_image = swap_chain->images[i];
|
||||
framebuffer->swap_chain_image_subresource_range = view_create_info.subresourceRange;
|
||||
swap_chain->framebuffers.push_back(RDD::FramebufferID(framebuffer));
|
||||
}
|
||||
|
||||
|
@ -3024,7 +3031,10 @@ RDD::FramebufferID RenderingDeviceDriverVulkan::swap_chain_acquire_framebuffer(C
|
|||
command_queue->pending_semaphores_for_fence.push_back(semaphore_index);
|
||||
|
||||
// Return the corresponding framebuffer to the new current image.
|
||||
return swap_chain->framebuffers[swap_chain->image_index];
|
||||
FramebufferID framebuffer_id = swap_chain->framebuffers[swap_chain->image_index];
|
||||
Framebuffer *framebuffer = (Framebuffer *)(framebuffer_id.id);
|
||||
framebuffer->swap_chain_acquired = true;
|
||||
return framebuffer_id;
|
||||
}
|
||||
|
||||
RDD::RenderPassID RenderingDeviceDriverVulkan::swap_chain_get_render_pass(SwapChainID p_swap_chain) {
|
||||
|
@ -3093,11 +3103,15 @@ RDD::FramebufferID RenderingDeviceDriverVulkan::framebuffer_create(RenderPassID
|
|||
}
|
||||
#endif
|
||||
|
||||
return FramebufferID(vk_framebuffer);
|
||||
Framebuffer *framebuffer = memnew(Framebuffer);
|
||||
framebuffer->vk_framebuffer = vk_framebuffer;
|
||||
return FramebufferID(framebuffer);
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverVulkan::framebuffer_free(FramebufferID p_framebuffer) {
|
||||
vkDestroyFramebuffer(vk_device, (VkFramebuffer)p_framebuffer.id, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER));
|
||||
Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
|
||||
vkDestroyFramebuffer(vk_device, framebuffer->vk_framebuffer, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER));
|
||||
memdelete(framebuffer);
|
||||
}
|
||||
|
||||
/****************/
|
||||
|
@ -4315,10 +4329,25 @@ void RenderingDeviceDriverVulkan::render_pass_free(RenderPassID p_render_pass) {
|
|||
static_assert(ARRAYS_COMPATIBLE_FIELDWISE(RDD::RenderPassClearValue, VkClearValue));
|
||||
|
||||
void RenderingDeviceDriverVulkan::command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) {
|
||||
Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
|
||||
if (framebuffer->swap_chain_acquired) {
|
||||
// Insert a barrier to wait for the acquisition of the framebuffer before the render pass begins.
|
||||
VkImageMemoryBarrier image_barrier = {};
|
||||
image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
image_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_barrier.image = framebuffer->swap_chain_image;
|
||||
image_barrier.subresourceRange = framebuffer->swap_chain_image_subresource_range;
|
||||
vkCmdPipelineBarrier((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
|
||||
framebuffer->swap_chain_acquired = false;
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo render_pass_begin = {};
|
||||
render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
render_pass_begin.renderPass = (VkRenderPass)p_render_pass.id;
|
||||
render_pass_begin.framebuffer = (VkFramebuffer)p_framebuffer.id;
|
||||
render_pass_begin.framebuffer = framebuffer->vk_framebuffer;
|
||||
|
||||
render_pass_begin.renderArea.offset.x = p_rect.position.x;
|
||||
render_pass_begin.renderArea.offset.y = p_rect.position.y;
|
||||
|
|
|
@ -366,6 +366,15 @@ public:
|
|||
/**** FRAMEBUFFER ****/
|
||||
/*********************/
|
||||
|
||||
struct Framebuffer {
|
||||
VkFramebuffer vk_framebuffer = VK_NULL_HANDLE;
|
||||
|
||||
// Only filled in by a framebuffer created by a swap chain. Unused otherwise.
|
||||
VkImage swap_chain_image = VK_NULL_HANDLE;
|
||||
VkImageSubresourceRange swap_chain_image_subresource_range = {};
|
||||
bool swap_chain_acquired = false;
|
||||
};
|
||||
|
||||
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
|
||||
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "editor_profiler.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "editor_visual_profiler.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
|
|
|
@ -248,11 +248,16 @@ void EditorFileSystem::_first_scan_filesystem() {
|
|||
ep.step(TTR("Scanning file structure..."), 0, true);
|
||||
nb_files_total = _scan_new_dir(first_scan_root_dir, d);
|
||||
|
||||
// Preloading GDExtensions file extensions to prevent looping on all the resource loaders
|
||||
// for each files in _first_scan_process_scripts.
|
||||
List<String> gdextension_extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("GDExtension", &gdextension_extensions);
|
||||
|
||||
// This loads the global class names from the scripts and ensures that even if the
|
||||
// global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer.
|
||||
// At the same time, to prevent looping multiple times in all files, it looks for extensions.
|
||||
ep.step(TTR("Loading global class names..."), 1, true);
|
||||
_first_scan_process_scripts(first_scan_root_dir, existing_class_names, extensions);
|
||||
_first_scan_process_scripts(first_scan_root_dir, gdextension_extensions, existing_class_names, extensions);
|
||||
|
||||
// Removing invalid global class to prevent having invalid paths in ScriptServer.
|
||||
_remove_invalid_global_class_names(existing_class_names);
|
||||
|
@ -276,16 +281,16 @@ void EditorFileSystem::_first_scan_filesystem() {
|
|||
ep.step(TTR("Starting file scan..."), 5, true);
|
||||
}
|
||||
|
||||
void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) {
|
||||
void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) {
|
||||
for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) {
|
||||
_first_scan_process_scripts(scan_sub_dir, p_existing_class_names, p_extensions);
|
||||
_first_scan_process_scripts(scan_sub_dir, p_gdextension_extensions, p_existing_class_names, p_extensions);
|
||||
}
|
||||
|
||||
for (const String &scan_file : p_scan_dir->files) {
|
||||
// Optimization to skip the ResourceLoader::get_resource_type for files
|
||||
// that are not scripts. Some loader get_resource_type methods read the file
|
||||
// which can be very slow on large projects.
|
||||
String ext = scan_file.get_extension().to_lower();
|
||||
const String ext = scan_file.get_extension().to_lower();
|
||||
bool is_script = false;
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
if (ScriptServer::get_language(i)->get_extension() == ext) {
|
||||
|
@ -293,24 +298,29 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!is_script) {
|
||||
continue; // Not a script.
|
||||
if (is_script) {
|
||||
const String path = p_scan_dir->full_path.path_join(scan_file);
|
||||
const String type = ResourceLoader::get_resource_type(path);
|
||||
|
||||
if (ClassDB::is_parent_class(type, SNAME("Script"))) {
|
||||
String script_class_extends;
|
||||
String script_class_icon_path;
|
||||
String script_class_name = _get_global_script_class(type, path, &script_class_extends, &script_class_icon_path);
|
||||
_register_global_class_script(path, path, type, script_class_name, script_class_extends, script_class_icon_path);
|
||||
|
||||
if (!script_class_name.is_empty()) {
|
||||
p_existing_class_names.insert(script_class_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String path = p_scan_dir->full_path.path_join(scan_file);
|
||||
String type = ResourceLoader::get_resource_type(path);
|
||||
|
||||
if (ClassDB::is_parent_class(type, SNAME("Script"))) {
|
||||
String script_class_extends;
|
||||
String script_class_icon_path;
|
||||
String script_class_name = _get_global_script_class(type, path, &script_class_extends, &script_class_icon_path);
|
||||
_register_global_class_script(path, path, type, script_class_name, script_class_extends, script_class_icon_path);
|
||||
|
||||
if (!script_class_name.is_empty()) {
|
||||
p_existing_class_names.insert(script_class_name);
|
||||
// Check for GDExtensions.
|
||||
if (p_gdextension_extensions.find(ext)) {
|
||||
const String path = p_scan_dir->full_path.path_join(scan_file);
|
||||
const String type = ResourceLoader::get_resource_type(path);
|
||||
if (type == SNAME("GDExtension")) {
|
||||
p_extensions.insert(path);
|
||||
}
|
||||
} else if (type == SNAME("GDExtension")) {
|
||||
p_extensions.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ class EditorFileSystem : public Node {
|
|||
|
||||
void _scan_filesystem();
|
||||
void _first_scan_filesystem();
|
||||
void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions);
|
||||
void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions);
|
||||
|
||||
HashSet<String> late_update_files;
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "core/input/input.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/object/class_db.h"
|
||||
|
|
|
@ -54,6 +54,10 @@ String EditorPaths::get_cache_dir() const {
|
|||
return cache_dir;
|
||||
}
|
||||
|
||||
String EditorPaths::get_tmp_dir() const {
|
||||
return tmp_dir;
|
||||
}
|
||||
|
||||
String EditorPaths::get_project_data_dir() const {
|
||||
return project_data_dir;
|
||||
}
|
||||
|
@ -160,6 +164,7 @@ EditorPaths::EditorPaths() {
|
|||
config_dir = data_dir;
|
||||
cache_path = exe_path;
|
||||
cache_dir = data_dir.path_join("cache");
|
||||
tmp_dir = data_dir.path_join("tmp");
|
||||
} else {
|
||||
// Typically XDG_DATA_HOME or %APPDATA%.
|
||||
data_path = OS::get_singleton()->get_data_path();
|
||||
|
@ -174,6 +179,7 @@ EditorPaths::EditorPaths() {
|
|||
} else {
|
||||
cache_dir = cache_path.path_join(OS::get_singleton()->get_godot_dir_name());
|
||||
}
|
||||
tmp_dir = OS::get_singleton()->get_tmp_path();
|
||||
}
|
||||
|
||||
paths_valid = (!data_path.is_empty() && !config_path.is_empty() && !cache_path.is_empty());
|
||||
|
|
|
@ -42,6 +42,7 @@ class EditorPaths : public Object {
|
|||
String data_dir; // Editor data (templates, shader cache, etc.).
|
||||
String config_dir; // Editor config (settings, profiles, themes, etc.).
|
||||
String cache_dir; // Editor cache (thumbnails, tmp generated files).
|
||||
String tmp_dir; // Editor temporary directory.
|
||||
String project_data_dir; // Project-specific data (metadata, shader cache, etc.).
|
||||
bool self_contained = false; // Self-contained means everything goes to `editor_data` dir.
|
||||
String self_contained_file; // Self-contained file with configuration.
|
||||
|
@ -61,6 +62,7 @@ public:
|
|||
String get_data_dir() const;
|
||||
String get_config_dir() const;
|
||||
String get_cache_dir() const;
|
||||
String get_tmp_dir() const;
|
||||
String get_project_data_dir() const;
|
||||
String get_export_templates_dir() const;
|
||||
String get_debug_keystore_path() const;
|
||||
|
|
|
@ -121,7 +121,7 @@ String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, boo
|
|||
}
|
||||
|
||||
String EventListenerLineEdit::get_device_string(int p_device) {
|
||||
if (p_device == InputMap::ALL_DEVICES) {
|
||||
if (p_device == InputEvent::DEVICE_ID_ALL_DEVICES) {
|
||||
return TTR("All Devices");
|
||||
}
|
||||
return TTR("Device") + " " + itos(p_device);
|
||||
|
|
|
@ -1355,6 +1355,13 @@ EditorFileDialog::Access EditorFileDialog::get_access() const {
|
|||
void EditorFileDialog::_make_dir_confirm() {
|
||||
const String stripped_dirname = makedirname->get_text().strip_edges();
|
||||
|
||||
if (stripped_dirname.is_empty()) {
|
||||
error_dialog->set_text(TTR("The path specified is invalid."));
|
||||
error_dialog->popup_centered(Size2(250, 50) * EDSCALE);
|
||||
makedirname->set_text(""); // Reset label.
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir_access->dir_exists(stripped_dirname)) {
|
||||
error_dialog->set_text(TTR("Could not create folder. File with that name already exists."));
|
||||
error_dialog->popup_centered(Size2(250, 50) * EDSCALE);
|
||||
|
|
|
@ -551,18 +551,18 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
|
|||
}
|
||||
|
||||
void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {
|
||||
// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
|
||||
// and option index 1 corresponds to device 0, etc...
|
||||
event->set_device(p_option_button_index - 1);
|
||||
// Option index 0 corresponds to "All Devices" (value of -3).
|
||||
// Otherwise subtract 1 as option index 1 corresponds to device 0, etc...
|
||||
event->set_device(p_option_button_index == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : p_option_button_index - 1);
|
||||
event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
|
||||
}
|
||||
|
||||
void InputEventConfigurationDialog::_set_current_device(int p_device) {
|
||||
device_id_option->select(p_device + 1);
|
||||
device_id_option->select(p_device == InputEvent::DEVICE_ID_ALL_DEVICES ? 0 : p_device + 1);
|
||||
}
|
||||
|
||||
int InputEventConfigurationDialog::_get_current_device() const {
|
||||
return device_id_option->get_selected() - 1;
|
||||
return device_id_option->get_selected() == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : device_id_option->get_selected() - 1;
|
||||
}
|
||||
|
||||
void InputEventConfigurationDialog::_notification(int p_what) {
|
||||
|
@ -705,11 +705,12 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
|
|||
|
||||
device_id_option = memnew(OptionButton);
|
||||
device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
for (int i = -1; i < 8; i++) {
|
||||
device_id_option->add_item(EventListenerLineEdit::get_device_string(InputEvent::DEVICE_ID_ALL_DEVICES));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
|
||||
}
|
||||
device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
|
||||
_set_current_device(InputMap::ALL_DEVICES);
|
||||
_set_current_device(InputEvent::DEVICE_ID_ALL_DEVICES);
|
||||
device_container->add_child(device_id_option);
|
||||
|
||||
device_container->hide();
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access_memory.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
|
|
|
@ -44,7 +44,10 @@ LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() {
|
|||
|
||||
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
|
||||
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
||||
mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
|
||||
// Fade out probes when camera gets too close to them.
|
||||
mat->set_distance_fade(StandardMaterial3D::DISTANCE_FADE_PIXEL_DITHER);
|
||||
mat->set_distance_fade_min_distance(0.5);
|
||||
mat->set_distance_fade_max_distance(1.5);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
|
||||
|
|
|
@ -396,6 +396,7 @@ void ShaderEditorPlugin::_setup_popup_menu(PopupMenuType p_type, PopupMenu *p_me
|
|||
if (p_type == FILE) {
|
||||
p_menu->add_separator();
|
||||
p_menu->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
|
||||
p_menu->add_item(TTR("Inspect Native Shader Code..."), FILE_INSPECT_NATIVE_SHADER_CODE);
|
||||
p_menu->add_separator();
|
||||
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE);
|
||||
} else {
|
||||
|
@ -554,6 +555,12 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
|
|||
EditorNode::get_singleton()->push_item(edited_shaders[index].shader_inc.ptr());
|
||||
}
|
||||
} break;
|
||||
case FILE_INSPECT_NATIVE_SHADER_CODE: {
|
||||
int index = shader_tabs->get_current_tab();
|
||||
if (edited_shaders[index].shader.is_valid()) {
|
||||
edited_shaders[index].shader->inspect_native_shader_code();
|
||||
}
|
||||
} break;
|
||||
case FILE_CLOSE: {
|
||||
_close_shader(shader_tabs->get_current_tab());
|
||||
} break;
|
||||
|
@ -754,6 +761,7 @@ void ShaderEditorPlugin::_set_file_specific_items_disabled(bool p_disabled) {
|
|||
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE), p_disabled);
|
||||
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE_AS), p_disabled);
|
||||
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT), p_disabled);
|
||||
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT_NATIVE_SHADER_CODE), p_disabled);
|
||||
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_CLOSE), p_disabled);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ class ShaderEditorPlugin : public EditorPlugin {
|
|||
FILE_SAVE,
|
||||
FILE_SAVE_AS,
|
||||
FILE_INSPECT,
|
||||
FILE_INSPECT_NATIVE_SHADER_CODE,
|
||||
FILE_CLOSE,
|
||||
CLOSE_ALL,
|
||||
CLOSE_OTHER_TABS,
|
||||
|
|
|
@ -2128,12 +2128,11 @@ void VisualShaderEditor::_update_nodes() {
|
|||
}
|
||||
}
|
||||
|
||||
Array keys = added.keys();
|
||||
keys.sort();
|
||||
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
const Variant &key = keys.get(i);
|
||||
List<Variant> keys;
|
||||
added.get_key_list(&keys);
|
||||
keys.sort_custom<StringLikeVariantOrder>();
|
||||
|
||||
for (const Variant &key : keys) {
|
||||
const Dictionary &value = (Dictionary)added[key];
|
||||
|
||||
add_custom_type(value["name"], value["type"], value["script"], value["description"], value["return_icon_type"], value["category"], value["highend"]);
|
||||
|
|
|
@ -88,7 +88,7 @@ void ProjectListItemControl::_notification(int p_what) {
|
|||
draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
}
|
||||
if (is_hovering) {
|
||||
draw_style_box(get_theme_stylebox(SNAME("hover"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
}
|
||||
|
||||
draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
|
||||
|
|
|
@ -945,6 +945,8 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
|||
|
||||
p_theme->set_color("custom_button_font_highlight", "Tree", p_config.font_hover_color);
|
||||
p_theme->set_color(SceneStringName(font_color), "Tree", p_config.font_color);
|
||||
p_theme->set_color("font_hovered_color", "Tree", p_config.mono_color);
|
||||
p_theme->set_color("font_hovered_dimmed_color", "Tree", p_config.font_color);
|
||||
p_theme->set_color("font_selected_color", "Tree", p_config.mono_color);
|
||||
p_theme->set_color("font_disabled_color", "Tree", p_config.font_disabled_color);
|
||||
p_theme->set_color("font_outline_color", "Tree", p_config.font_outline_color);
|
||||
|
@ -997,7 +999,13 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
|||
Ref<StyleBoxFlat> style_tree_hover = p_config.base_style->duplicate();
|
||||
style_tree_hover->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.4));
|
||||
style_tree_hover->set_border_width_all(0);
|
||||
p_theme->set_stylebox("hover", "Tree", style_tree_hover);
|
||||
p_theme->set_stylebox("hovered", "Tree", style_tree_hover);
|
||||
p_theme->set_stylebox("button_hover", "Tree", style_tree_hover);
|
||||
|
||||
Ref<StyleBoxFlat> style_tree_hover_dimmed = p_config.base_style->duplicate();
|
||||
style_tree_hover_dimmed->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.2));
|
||||
style_tree_hover_dimmed->set_border_width_all(0);
|
||||
p_theme->set_stylebox("hovered_dimmed", "Tree", style_tree_hover_dimmed);
|
||||
|
||||
p_theme->set_stylebox("selected_focus", "Tree", style_tree_focus);
|
||||
p_theme->set_stylebox("selected", "Tree", style_tree_selected);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/io/file_access_zip.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/ip.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
|
@ -2412,6 +2413,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
if (bool(GLOBAL_GET("display/window/size/no_focus"))) {
|
||||
window_flags |= DisplayServer::WINDOW_FLAG_NO_FOCUS_BIT;
|
||||
}
|
||||
if (bool(GLOBAL_GET("display/window/size/sharp_corners"))) {
|
||||
window_flags |= DisplayServer::WINDOW_FLAG_SHARP_CORNERS_BIT;
|
||||
}
|
||||
window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int());
|
||||
int initial_position_type = GLOBAL_GET("display/window/size/initial_position_type").operator int();
|
||||
if (initial_position_type == 0) { // Absolute.
|
||||
|
@ -3197,6 +3201,10 @@ Error Main::setup2(bool p_show_boot_logo) {
|
|||
}
|
||||
|
||||
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
|
||||
|
||||
if (editor) {
|
||||
id->set_emulate_mouse_from_touch(true);
|
||||
}
|
||||
}
|
||||
|
||||
OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot");
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "image_compress_basisu.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
|
|
@ -217,7 +217,7 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
|
|||
|
||||
List<Variant> keys;
|
||||
dict.get_key_list(&keys);
|
||||
keys.sort();
|
||||
keys.sort_custom<StringLikeVariantOrder>();
|
||||
|
||||
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
|
||||
if (E->prev()) {
|
||||
|
|
|
@ -1624,15 +1624,17 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
|
|||
valid = false;
|
||||
}
|
||||
|
||||
annotation->info = &valid_annotations[annotation->name];
|
||||
if (valid) {
|
||||
annotation->info = &valid_annotations[annotation->name];
|
||||
|
||||
if (!annotation->applies_to(p_valid_targets)) {
|
||||
if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
|
||||
push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name));
|
||||
} else {
|
||||
push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
|
||||
if (!annotation->applies_to(p_valid_targets)) {
|
||||
if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
|
||||
push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name));
|
||||
} else {
|
||||
push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
@export
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
Annotation "@export" cannot be applied to a function.
|
|
@ -0,0 +1,3 @@
|
|||
@hello_world
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_PARSER_ERROR
|
||||
Unrecognized annotation: "@hello_world".
|
|
@ -32,7 +32,7 @@ partial class EventSignals
|
|||
add => backing_MySignal += value;
|
||||
remove => backing_MySignal -= value;
|
||||
}
|
||||
protected void OnMySignal(string str, int num)
|
||||
protected void EmitSignalMySignal(string str, int num)
|
||||
{
|
||||
EmitSignal(SignalName.MySignal, str, num);
|
||||
}
|
||||
|
|
|
@ -807,7 +807,7 @@ partial class ExportedFields
|
|||
properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.@_fieldRid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotGenericDictionary, hint: (global::Godot.PropertyHint)38, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.@_fieldEmptyInt64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
return properties;
|
||||
|
|
|
@ -925,7 +925,7 @@ partial class ExportedProperties
|
|||
properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.@PropertyRid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotGenericDictionary, hint: (global::Godot.PropertyHint)38, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
|
||||
return properties;
|
||||
}
|
||||
|
|
|
@ -791,7 +791,7 @@ namespace Godot.SourceGenerators
|
|||
}
|
||||
}
|
||||
|
||||
hint = PropertyHint.DictionaryType;
|
||||
hint = PropertyHint.TypeString;
|
||||
|
||||
hintString = keyHintString != null && valueHintString != null ? $"{keyHintString};{valueHintString}" : null;
|
||||
return hintString != null;
|
||||
|
|
|
@ -282,7 +282,7 @@ namespace Godot.SourceGenerators
|
|||
.Append(" -= value;\n")
|
||||
.Append("}\n");
|
||||
|
||||
// Generate On{EventName} method to raise the event
|
||||
// Generate EmitSignal{EventName} method to raise the event
|
||||
|
||||
var invokeMethodSymbol = signalDelegate.InvokeMethodData.Method;
|
||||
int paramCount = invokeMethodSymbol.Parameters.Length;
|
||||
|
@ -291,7 +291,7 @@ namespace Godot.SourceGenerators
|
|||
"private" :
|
||||
"protected";
|
||||
|
||||
source.Append($" {raiseMethodModifiers} void On{signalName}(");
|
||||
source.Append($" {raiseMethodModifiers} void EmitSignal{signalName}(");
|
||||
for (int i = 0; i < paramCount; i++)
|
||||
{
|
||||
var paramSymbol = invokeMethodSymbol.Parameters[i];
|
||||
|
|
|
@ -3275,10 +3275,10 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
|
|||
|
||||
p_output.append(CLOSE_BLOCK_L1);
|
||||
|
||||
// Generate On{EventName} method to raise the event.
|
||||
// Generate EmitSignal{EventName} method to raise the event.
|
||||
if (!p_itype.is_singleton) {
|
||||
p_output.append(MEMBER_BEGIN "protected void ");
|
||||
p_output << "On" << p_isignal.proxy_name;
|
||||
p_output << "EmitSignal" << p_isignal.proxy_name;
|
||||
if (is_parameterless) {
|
||||
p_output.append("()\n" OPEN_BLOCK_L1 INDENT2);
|
||||
p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ");\n";
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/file_access_memory.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "video_stream_theora.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "image_saver_tinyexr.h"
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <zlib.h> // Should come before including tinyexr.
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#ifndef IMAGE_SAVER_TINYEXR_H
|
||||
#define IMAGE_SAVER_TINYEXR_H
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/io/image.h"
|
||||
|
||||
Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
|
||||
Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "godot_plugin_config.h"
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
|
|
@ -131,6 +131,18 @@ public class GodotIO {
|
|||
return activity.getCacheDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getTmpDir() {
|
||||
File tmpDir = new File(getCacheDir() + "/tmp");
|
||||
|
||||
if (!tmpDir.exists()) {
|
||||
if (!tmpDir.mkdirs()) {
|
||||
Log.e(TAG, "Unable to create tmp dir");
|
||||
}
|
||||
}
|
||||
|
||||
return tmpDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getDataDir() {
|
||||
return activity.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
|
|||
|
||||
_open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I");
|
||||
_get_cache_dir = p_env->GetMethodID(cls, "getCacheDir", "()Ljava/lang/String;");
|
||||
_get_tmp_dir = p_env->GetMethodID(cls, "getTmpDir", "()Ljava/lang/String;");
|
||||
_get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;");
|
||||
_get_display_cutouts = p_env->GetMethodID(cls, "getDisplayCutouts", "()[I"),
|
||||
_get_display_safe_area = p_env->GetMethodID(cls, "getDisplaySafeArea", "()[I"),
|
||||
|
@ -106,6 +107,17 @@ String GodotIOJavaWrapper::get_cache_dir() {
|
|||
}
|
||||
}
|
||||
|
||||
String GodotIOJavaWrapper::get_tmp_dir() {
|
||||
if (_get_tmp_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, String());
|
||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_tmp_dir);
|
||||
return jstring_to_string(s, env);
|
||||
} else {
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
String GodotIOJavaWrapper::get_user_data_dir() {
|
||||
if (_get_data_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
|
|
@ -48,6 +48,7 @@ private:
|
|||
jmethodID _open_URI = 0;
|
||||
jmethodID _get_cache_dir = 0;
|
||||
jmethodID _get_data_dir = 0;
|
||||
jmethodID _get_tmp_dir = 0;
|
||||
jmethodID _get_display_cutouts = 0;
|
||||
jmethodID _get_display_safe_area = 0;
|
||||
jmethodID _get_locale = 0;
|
||||
|
@ -71,6 +72,7 @@ public:
|
|||
|
||||
Error open_uri(const String &p_uri);
|
||||
String get_cache_dir();
|
||||
String get_tmp_dir();
|
||||
String get_user_data_dir();
|
||||
String get_locale();
|
||||
String get_model();
|
||||
|
|
|
@ -677,6 +677,19 @@ String OS_Android::get_cache_path() const {
|
|||
return ".";
|
||||
}
|
||||
|
||||
String OS_Android::get_tmp_path() const {
|
||||
if (!tmp_dir_cache.is_empty()) {
|
||||
return tmp_dir_cache;
|
||||
}
|
||||
|
||||
String tmp_dir = godot_io_java->get_tmp_dir();
|
||||
if (!tmp_dir.is_empty()) {
|
||||
tmp_dir_cache = _remove_symlink(tmp_dir);
|
||||
return tmp_dir_cache;
|
||||
}
|
||||
return ".";
|
||||
}
|
||||
|
||||
String OS_Android::get_unique_id() const {
|
||||
String unique_id = godot_io_java->get_unique_id();
|
||||
if (!unique_id.is_empty()) {
|
||||
|
|
|
@ -58,6 +58,7 @@ private:
|
|||
|
||||
mutable String data_dir_cache;
|
||||
mutable String cache_dir_cache;
|
||||
mutable String tmp_dir_cache;
|
||||
mutable String remote_fs_dir;
|
||||
|
||||
AudioDriverOpenSL audio_driver_android;
|
||||
|
@ -148,6 +149,7 @@ public:
|
|||
virtual String get_user_data_dir() const override;
|
||||
virtual String get_data_path() const override;
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_tmp_path() const override;
|
||||
virtual String get_resource_dir() const override;
|
||||
virtual String get_locale() const override;
|
||||
virtual String get_model_name() const override;
|
||||
|
|
|
@ -117,6 +117,7 @@ public:
|
|||
virtual String get_user_data_dir() const override;
|
||||
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_tmp_path() const override;
|
||||
|
||||
virtual String get_locale() const override;
|
||||
|
||||
|
|
|
@ -336,6 +336,22 @@ String OS_IOS::get_cache_path() const {
|
|||
return ret;
|
||||
}
|
||||
|
||||
String OS_IOS::get_tmp_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
|
||||
isDirectory:YES];
|
||||
if (url) {
|
||||
NSString *url_path = [url absoluteString];
|
||||
ret.parse_utf8([url_path UTF8String]);
|
||||
if (ret.begins_with("file://")) {
|
||||
ret = ret.replace_first("file://", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_IOS::get_locale() const {
|
||||
NSString *preferedLanguage = [NSLocale preferredLanguages].firstObject;
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/os/os.h"
|
||||
|
|
|
@ -92,6 +92,7 @@ public:
|
|||
virtual String get_config_path() const override;
|
||||
virtual String get_data_path() const override;
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_tmp_path() const override;
|
||||
virtual String get_bundle_resource_dir() const override;
|
||||
virtual String get_bundle_icon_path() const override;
|
||||
virtual String get_godot_dir_name() const override;
|
||||
|
|
|
@ -267,6 +267,22 @@ String OS_MacOS::get_cache_path() const {
|
|||
return get_config_path();
|
||||
}
|
||||
|
||||
String OS_MacOS::get_tmp_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
|
||||
isDirectory:YES];
|
||||
if (url) {
|
||||
NSString *url_path = [url absoluteString];
|
||||
ret.parse_utf8([url_path UTF8String]);
|
||||
if (ret.begins_with("file://")) {
|
||||
ret = ret.replace_first("file://", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_MacOS::get_bundle_resource_dir() const {
|
||||
String ret;
|
||||
|
||||
|
|
|
@ -67,6 +67,18 @@
|
|||
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
|
||||
#endif
|
||||
|
||||
#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
|
||||
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
|
||||
#endif
|
||||
|
||||
#ifndef DWMWCP_DEFAULT
|
||||
#define DWMWCP_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#ifndef DWMWCP_DONOTROUND
|
||||
#define DWMWCP_DONOTROUND 1
|
||||
#endif
|
||||
|
||||
#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
@ -1483,6 +1495,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
|
|||
if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
|
||||
wd.always_on_top = true;
|
||||
}
|
||||
if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) {
|
||||
wd.sharp_corners = true;
|
||||
}
|
||||
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
|
||||
wd.no_focus = true;
|
||||
}
|
||||
|
@ -2297,6 +2312,12 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
|
|||
wd.always_on_top = p_enabled;
|
||||
_update_window_style(p_window);
|
||||
} break;
|
||||
case WINDOW_FLAG_SHARP_CORNERS: {
|
||||
wd.sharp_corners = p_enabled;
|
||||
DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
|
||||
::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
|
||||
_update_window_style(p_window);
|
||||
} break;
|
||||
case WINDOW_FLAG_TRANSPARENT: {
|
||||
if (p_enabled) {
|
||||
// Enable per-pixel alpha.
|
||||
|
@ -3994,6 +4015,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
native_menu->_menu_activate(HMENU(lParam), (int)wParam);
|
||||
} break;
|
||||
case WM_CREATE: {
|
||||
{
|
||||
DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
|
||||
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
|
||||
}
|
||||
if (is_dark_mode_supported() && dark_title_available) {
|
||||
BOOL value = is_dark_mode();
|
||||
|
||||
|
@ -5645,6 +5670,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
|
|||
wd_transient_parent->transient_children.insert(id);
|
||||
}
|
||||
|
||||
wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT;
|
||||
{
|
||||
DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
|
||||
::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
|
||||
}
|
||||
|
||||
if (is_dark_mode_supported() && dark_title_available) {
|
||||
BOOL value = is_dark_mode();
|
||||
::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "drivers/unix/ip_unix.h"
|
||||
#include "drivers/wasapi/audio_driver_wasapi.h"
|
||||
|
@ -473,6 +474,7 @@ class DisplayServerWindows : public DisplayServer {
|
|||
bool exclusive = false;
|
||||
bool context_created = false;
|
||||
bool mpass = false;
|
||||
bool sharp_corners = false;
|
||||
|
||||
// Used to transfer data between events using timer.
|
||||
WPARAM saved_wparam;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef NATIVE_MENU_WINDOWS_H
|
||||
#define NATIVE_MENU_WINDOWS_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/display/native_menu.h"
|
||||
|
|
|
@ -1850,16 +1850,35 @@ String OS_Windows::get_cache_path() const {
|
|||
if (has_environment("LOCALAPPDATA")) {
|
||||
cache_path_cache = get_environment("LOCALAPPDATA").replace("\\", "/");
|
||||
}
|
||||
if (cache_path_cache.is_empty() && has_environment("TEMP")) {
|
||||
cache_path_cache = get_environment("TEMP").replace("\\", "/");
|
||||
}
|
||||
if (cache_path_cache.is_empty()) {
|
||||
cache_path_cache = get_config_path();
|
||||
cache_path_cache = get_tmp_path();
|
||||
}
|
||||
}
|
||||
return cache_path_cache;
|
||||
}
|
||||
|
||||
String OS_Windows::get_tmp_path() const {
|
||||
static String tmp_path_cache;
|
||||
if (tmp_path_cache.is_empty()) {
|
||||
{
|
||||
// Get the tmp path from the Windows API.
|
||||
void *get_temp_path = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetTempPathW");
|
||||
if (get_temp_path) {
|
||||
Vector<WCHAR> tmp_path;
|
||||
tmp_path.resize(MAX_PATH);
|
||||
DWORD tmp_path_length = GetTempPathW(MAX_PATH, (LPWSTR)tmp_path.ptrw());
|
||||
if (tmp_path_length > 0 && tmp_path_length < MAX_PATH) {
|
||||
tmp_path_cache = String::utf16((const char16_t *)tmp_path.ptr(), tmp_path_length / sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tmp_path_cache.is_empty()) {
|
||||
tmp_path_cache = get_config_path();
|
||||
}
|
||||
}
|
||||
return tmp_path_cache;
|
||||
}
|
||||
|
||||
// Get properly capitalized engine name for system paths
|
||||
String OS_Windows::get_godot_dir_name() const {
|
||||
return String(VERSION_SHORT_NAME).capitalize();
|
||||
|
|
|
@ -215,6 +215,7 @@ public:
|
|||
virtual String get_config_path() const override;
|
||||
virtual String get_data_path() const override;
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_tmp_path() const override;
|
||||
virtual String get_godot_dir_name() const override;
|
||||
|
||||
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
|
||||
|
|
|
@ -167,17 +167,16 @@ void Path2D::_curve_changed() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
PathFollow2D *follow = Object::cast_to<PathFollow2D>(get_child(i));
|
||||
if (follow) {
|
||||
follow->path_changed();
|
||||
}
|
||||
}
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_paths_hint()) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void Path2D::set_curve(const Ref<Curve2D> &p_curve) {
|
||||
|
|
|
@ -1602,19 +1602,16 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const {
|
|||
PackedStringArray LightmapGI::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
|
||||
|
||||
#ifndef MODULE_LIGHTMAPPER_RD_ENABLED
|
||||
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
|
||||
warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
|
||||
#else
|
||||
warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));
|
||||
#endif
|
||||
return warnings;
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_LIGHTMAPPER_RD_ENABLED
|
||||
if (!DisplayServer::get_singleton()->can_create_rendering_device()) {
|
||||
warnings.push_back(vformat(RTR("Lightmaps can only be baked from a GPU that supports the RenderingDevice backends.\nYour GPU (%s) does not support RenderingDevice, as it does not support Vulkan, Direct3D 12, or Metal.\nLightmap baking will not be available on this device, although rendering existing baked lightmaps will work."), RenderingServer::get_singleton()->get_video_adapter_name()));
|
||||
return warnings;
|
||||
}
|
||||
#elif defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
|
||||
warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
|
||||
#else
|
||||
warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));
|
||||
#endif
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
|
|
@ -245,21 +245,7 @@ void ColorPicker::finish_shaders() {
|
|||
}
|
||||
|
||||
void ColorPicker::set_focus_on_line_edit() {
|
||||
bool has_hardware_keyboard = true;
|
||||
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
|
||||
has_hardware_keyboard = DisplayServer::get_singleton()->has_hardware_keyboard();
|
||||
#endif // ANDROID_ENABLED || IOS_ENABLED
|
||||
if (has_hardware_keyboard) {
|
||||
callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
|
||||
} else {
|
||||
// A hack to avoid showing the virtual keyboard when the ColorPicker window popups and
|
||||
// no hardware keyboard is detected on Android and IOS.
|
||||
// This will only focus the LineEdit without editing, the virtual keyboard will only be visible when
|
||||
// we touch the LineEdit to enter edit mode.
|
||||
callable_mp(c_text, &LineEdit::set_editable).call_deferred(false);
|
||||
callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
|
||||
callable_mp(c_text, &LineEdit::set_editable).call_deferred(true);
|
||||
}
|
||||
callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
|
||||
}
|
||||
|
||||
void ColorPicker::_update_controls() {
|
||||
|
@ -2103,7 +2089,9 @@ void ColorPickerButton::pressed() {
|
|||
float v_offset = show_above ? -minsize.y : get_size().y;
|
||||
popup->set_position(get_screen_position() + Vector2(h_offset, v_offset));
|
||||
popup->popup();
|
||||
picker->set_focus_on_line_edit();
|
||||
if (DisplayServer::get_singleton()->has_hardware_keyboard()) {
|
||||
picker->set_focus_on_line_edit();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerButton::_notification(int p_what) {
|
||||
|
|
|
@ -3042,7 +3042,7 @@ void Control::set_layout_direction(Control::LayoutDirection p_direction) {
|
|||
if (data.layout_dir == p_direction) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX((int)p_direction, 4);
|
||||
ERR_FAIL_INDEX(p_direction, LAYOUT_DIRECTION_MAX);
|
||||
|
||||
data.layout_dir = p_direction;
|
||||
|
||||
|
@ -3115,13 +3115,20 @@ bool Control::is_layout_rtl() const {
|
|||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
|
||||
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
} else {
|
||||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
|
||||
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
} else {
|
||||
String locale = OS::get_singleton()->get_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else {
|
||||
const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
|
||||
}
|
||||
|
@ -3574,7 +3581,7 @@ void Control::_bind_methods() {
|
|||
ADD_GROUP("Layout", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), "set_layout_direction", "get_layout_direction");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
|
||||
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
|
||||
|
||||
|
@ -3723,9 +3730,14 @@ void Control::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(ANCHOR_END);
|
||||
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_APPLICATION_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_SYSTEM_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_MAX);
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
BIND_ENUM_CONSTANT(TEXT_DIRECTION_INHERITED);
|
||||
BIND_ENUM_CONSTANT(TEXT_DIRECTION_AUTO);
|
||||
|
|
|
@ -140,9 +140,14 @@ public:
|
|||
|
||||
enum LayoutDirection {
|
||||
LAYOUT_DIRECTION_INHERITED,
|
||||
LAYOUT_DIRECTION_LOCALE,
|
||||
LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
LAYOUT_DIRECTION_LTR,
|
||||
LAYOUT_DIRECTION_RTL
|
||||
LAYOUT_DIRECTION_RTL,
|
||||
LAYOUT_DIRECTION_SYSTEM_LOCALE,
|
||||
LAYOUT_DIRECTION_MAX,
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
LAYOUT_DIRECTION_LOCALE = LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
#endif // DISABLE_DEPRECATED
|
||||
};
|
||||
|
||||
enum TextDirection {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user