Skip to content

Commit

Permalink
Implement Feature Build Profiles
Browse files Browse the repository at this point in the history
This PR is a continuation of #50381 (which was implemented exactly a year ago!)

* Add a visual interface to select which classes should not be built into Godot (well, they are built if something else uses them, but if not used the optimizer will remove them out).
* Add a detection system to scan the project and figure out the actual classes used.
* Added the ability for SCons to load build profiles.

Obligatory Screen:

A simple test with a couple of nodes in the scene resulted in a 25% reduction for the final binary size

TODO:

* Script languages need to implement used class detection (left for another PR).
* Options to disable servers or server functionalities (like 2D or 3D physics, navigation, etc). Are missing, that should also greatly aid in reducing binary size.
* Options to disable some modules would be desired.
* More options to disable drivers (OpenGL, Vulkan, etc) would be desired.

In general this PR is a starting point for more contributors to improve and enhance this functionality.
  • Loading branch information
reduz committed Jul 22, 2022
1 parent 5fec0d2 commit 6236a68
Show file tree
Hide file tree
Showing 17 changed files with 1,267 additions and 23 deletions.
24 changes: 22 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all
opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
opts.Add("disable_classes", "Disable given classes (comma separated)", "")
opts.Add("build_feature_profile", "Path to a file containing a feature build profile", "")
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
Expand Down Expand Up @@ -720,7 +720,27 @@ if selected_platform in platform_list:

if env["tools"]:
env.Append(CPPDEFINES=["TOOLS_ENABLED"])
methods.write_disabled_classes(env["disable_classes"].split(","))

disabled_classes = []

if env["build_feature_profile"] != "":
print("Using build feature profile: " + env["build_feature_profile"])
import json

try:
ft = json.load(open(env["build_feature_profile"]))
if "disabled_classes" in ft:
disabled_classes = ft["disabled_classes"]
if "disabled_build_options" in ft:
dbo = ft["disabled_build_options"]
for c in dbo:
env[c] = dbo[c]
except:
print("Error opening feature build profile: " + env["build_feature_profile"])
Exit(255)

methods.write_disabled_classes(disabled_classes)

if env["disable_3d"]:
if env["tools"]:
print(
Expand Down
26 changes: 26 additions & 0 deletions core/io/resource_format_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,22 @@ String ResourceLoaderBinary::get_unicode_string() {
return s;
}

void ResourceLoaderBinary::get_classes_used(Ref<FileAccess> p_f, HashSet<StringName> *p_classes) {
open(p_f, false, true);
if (error) {
return;
}

for (int i = 0; i < internal_resources.size(); i++) {
p_f->seek(internal_resources[i].offset);
String t = get_unicode_string();
ERR_FAIL_COND(p_f->get_error() != OK);
if (t != String()) {
p_classes->insert(t);
}
}
}

void ResourceLoaderBinary::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) {
open(p_f, false, true);
if (error) {
Expand Down Expand Up @@ -1337,6 +1353,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
return OK;
}

void ResourceFormatLoaderBinary::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'.");

ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
loader.get_classes_used(f, r_classes);
}

String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
if (f.is_null()) {
Expand Down
2 changes: 2 additions & 0 deletions core/io/resource_format_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class ResourceLoaderBinary {
void open(Ref<FileAccess> p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
String recognize(Ref<FileAccess> p_f);
void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
void get_classes_used(Ref<FileAccess> p_f, HashSet<StringName> *p_classes);

ResourceLoaderBinary() {}
};
Expand All @@ -112,6 +113,7 @@ class ResourceFormatLoaderBinary : public ResourceFormatLoader {
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
Expand Down
10 changes: 10 additions & 0 deletions core/io/resource_importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons

return pat.metadata;
}
void ResourceFormatImporter::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);

if (err != OK) {
return;
}

ResourceLoader::get_classes_used(pat.path, r_classes);
}

void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
PathAndType pat;
Expand Down
2 changes: 1 addition & 1 deletion core/io/resource_importer.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ class ResourceFormatImporter : public ResourceFormatLoader {
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;

virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); }
virtual String get_import_group_file(const String &p_path) const;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
virtual bool exists(const String &p_path) const;

virtual int get_import_order(const String &p_path) const;
Expand Down
28 changes: 28 additions & 0 deletions core/io/resource_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ bool ResourceFormatLoader::handles_type(const String &p_type) const {
return false;
}

void ResourceFormatLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
Vector<String> ret;
if (GDVIRTUAL_CALL(_get_classes_used, p_path, ret)) {
for (int i = 0; i < ret.size(); i++) {
r_classes->insert(ret[i]);
}
return;
}

String res = get_resource_type(p_path);
if (!res.is_empty()) {
r_classes->insert(res);
}
}

String ResourceFormatLoader::get_resource_type(const String &p_path) const {
String ret;

Expand Down Expand Up @@ -180,6 +195,7 @@ void ResourceFormatLoader::_bind_methods() {
GDVIRTUAL_BIND(_get_dependencies, "path", "add_types");
GDVIRTUAL_BIND(_rename_dependencies, "path", "renames");
GDVIRTUAL_BIND(_exists, "path");
GDVIRTUAL_BIND(_get_classes_used, "path");
GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode");
}

Expand Down Expand Up @@ -730,6 +746,18 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const HashMap<St
return OK; // ??
}

void ResourceLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
String local_path = _validate_local_path(p_path);

for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
continue;
}

return loader[i]->get_classes_used(p_path, r_classes);
}
}

String ResourceLoader::get_resource_type(const String &p_path) {
String local_path = _validate_local_path(p_path);

Expand Down
3 changes: 3 additions & 0 deletions core/io/resource_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ResourceFormatLoader : public RefCounted {
GDVIRTUAL1RC(String, _get_resource_type, String)
GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
GDVIRTUAL2RC(Vector<String>, _get_dependencies, String, bool)
GDVIRTUAL1RC(Vector<String>, _get_classes_used, String)
GDVIRTUAL2RC(int64_t, _rename_dependencies, String, Dictionary)
GDVIRTUAL1RC(bool, _exists, String)

Expand All @@ -67,6 +68,7 @@ class ResourceFormatLoader : public RefCounted {
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
Expand Down Expand Up @@ -170,6 +172,7 @@ class ResourceLoader {
static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions);
static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false);
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
static void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
static String get_resource_type(const String &p_path);
static ResourceUID::ID get_resource_uid(const String &p_path);
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
Expand Down
7 changes: 7 additions & 0 deletions core/object/class_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,13 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
compat_classes[p_class] = p_fallback;
}

StringName ClassDB::get_compatibility_class(const StringName &p_class) {
if (compat_classes.has(p_class)) {
return compat_classes[p_class];
}
return StringName();
}

Object *ClassDB::instantiate(const StringName &p_class) {
ClassInfo *ti;
{
Expand Down
15 changes: 8 additions & 7 deletions core/object/class_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ class ClassDB {
static bool is_resource_extension(const StringName &p_extension);

static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
static StringName get_compatibility_class(const StringName &p_class);

static void set_current_api(APIType p_api);
static APIType get_current_api();
Expand Down Expand Up @@ -418,16 +419,16 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {

#endif

#define GDREGISTER_CLASS(m_class) \
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
::ClassDB::register_class<m_class>(); \
#define GDREGISTER_CLASS(m_class) \
if (m_class::_class_is_enabled) { \
::ClassDB::register_class<m_class>(); \
}
#define GDREGISTER_VIRTUAL_CLASS(m_class) \
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
::ClassDB::register_class<m_class>(true); \
#define GDREGISTER_VIRTUAL_CLASS(m_class) \
if (m_class::_class_is_enabled) { \
::ClassDB::register_class<m_class>(true); \
}
#define GDREGISTER_ABSTRACT_CLASS(m_class) \
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
if (m_class::_class_is_enabled) { \
::ClassDB::register_abstract_class<m_class>(); \
}

Expand Down
3 changes: 3 additions & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ private:
friend class ::ClassDB; \
\
public: \
static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \
virtual String get_class() const override { \
if (_get_extension()) { \
return _get_extension()->class_name.operator String(); \
Expand Down Expand Up @@ -667,6 +668,8 @@ class Object {
_FORCE_INLINE_ static void register_custom_data_to_otdb() {}

public:
static constexpr bool _class_is_enabled = true;

void notify_property_list_changed();

static void *get_class_ptr_static() {
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/ResourceFormatLoader.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
<description>
</description>
</method>
<method name="_get_classes_used" qualifiers="virtual const">
<return type="PackedStringArray" />
<argument index="0" name="path" type="String" />
<description>
</description>
</method>
<method name="_get_dependencies" qualifiers="virtual const">
<return type="PackedStringArray" />
<argument index="0" name="path" type="String" />
Expand Down
Loading

0 comments on commit 6236a68

Please sign in to comment.