Skip to content

Commit

Permalink
LibGodot Feature
Browse files Browse the repository at this point in the history
* Based on top of the GDExtensionLoader PR: #91166
* Add a new GodotInstance GDCLASS that provides startup and iteration commands to control a Godot instance.
* Adds a libgodot_create_godot_instance entry point that creates a new Godot instance and returns a GodotInstance object.
* Adds a libgodot_destroy_godot_instance entry point that destroys the Godot instance.
* Allow specifying an external native rendering surface handle to render Godot into the UI of a host application.
  * It is also possible to embed multiple Godot windows into the UI of a host application.
  * Currently supported on MacOS and iOS

Sample Apps: https://github.com/migeran/libgodot_project

Developed by [Migeran](https://migeran.com)

Sponsors & Acknowledgements:

* Initial development sponsored by [Smirk Software](https://www.smirk.gg/)
* Rebasing to Godot 4.3 and further development sponsored by [Xibbon Inc.](https://xibbon.com)
* The GDExtension registration of the host process & build system changes were based
  on @Faolan-Rad's LibGodot PR: #72883

Co-Authored-By: Gabor Koncz <gabor.koncz@migeran.com>
  • Loading branch information
kisg and konczg committed Jul 4, 2024
1 parent 8d94fd2 commit 92f6c34
Show file tree
Hide file tree
Showing 91 changed files with 2,854 additions and 353 deletions.
22 changes: 20 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ env.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix

env.__class__.add_shared_library = methods.add_shared_library
env.__class__.add_library = methods.add_library
env.__class__.add_program = methods.add_program
env.__class__.CommandNoCache = methods.CommandNoCache
env.__class__.Run = methods.Run
env.__class__.disable_warnings = methods.disable_warnings
Expand Down Expand Up @@ -226,6 +225,14 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))

# Advanced options
opts.Add(
EnumVariable(
"library_type",
"Build library type",
"executable",
("executable", "static_library", "shared_library"),
)
)
opts.Add(BoolVariable("dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes", False))
opts.Add(BoolVariable("tests", "Build the unit tests", False))
opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False))
Expand Down Expand Up @@ -317,7 +324,18 @@ if env["import_env_vars"]:
if env.scons_version < (4, 3) and not env["platform"]:
env["platform"] = env["p"]

if env["platform"] == "":
if env["library_type"] == "static_library":
env.Append(CPPDEFINES=["LIBRARY_ENABLED"])
elif env["library_type"] == "shared_library":
env.Append(CPPDEFINES=["LIBRARY_ENABLED"])
env.Append(CCFLAGS=["-fPIC"])
env.Append(STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME=True)
else:
env.__class__.add_program = methods.add_program

if env["platform"] != "":
selected_platform = env["platform"]
else:
# Missing `platform` argument, try to detect platform automatically
if (
sys.platform.startswith("linux")
Expand Down
5 changes: 5 additions & 0 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ const PackedStringArray ProjectSettings::get_required_features() {
// Returns the features supported by this build of Godot. Includes all required features.
const PackedStringArray ProjectSettings::_get_supported_features() {
PackedStringArray features = get_required_features();

#ifdef LIBRARY_ENABLED
features.append("LibGodot");
#endif

#ifdef MODULE_MONO_ENABLED
features.append("C#");
#endif
Expand Down
71 changes: 71 additions & 0 deletions core/extension/gdextension_function_loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**************************************************************************/
/* gdextension_function_loader.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "gdextension_function_loader.h"
#include "core/config/project_settings.h"
#include "gdextension.h"

Error GDExtensionFunctionLoader::open_library(const String &p_path) {
ERR_FAIL_COND_V_MSG(!p_path.begins_with("libgodot://"), ERR_FILE_NOT_FOUND, "Function based GDExtensions should have a path starting with libgodot://");
ERR_FAIL_COND_V_MSG(!initialization_function, ERR_DOES_NOT_EXIST, "Initialization function is required for function based GDExtensions.");

library_path = p_path;

return OK;
}

Error GDExtensionFunctionLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) {
ERR_FAIL_COND_V_MSG(!initialization_function, ERR_DOES_NOT_EXIST, "Initialization function is required for function based GDExtensions.");
GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);

if (ret) {
return OK;
} else {
ERR_PRINT("GDExtension initialization function for '" + library_path + "' returned an error.");
return FAILED;
}
}

void GDExtensionFunctionLoader::close_library() {
initialization_function = nullptr;
library_path.clear();
}

bool GDExtensionFunctionLoader::is_library_open() const {
return !library_path.is_empty();
}

bool GDExtensionFunctionLoader::has_library_changed() const {
return false;
}

void GDExtensionFunctionLoader::set_initialization_function(GDExtensionInitializationFunction p_initialization_function) {
initialization_function = p_initialization_function;
}
58 changes: 58 additions & 0 deletions core/extension/gdextension_function_loader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**************************************************************************/
/* gdextension_function_loader.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#ifndef GDEXTENSION_FUNCTION_LOADER_H
#define GDEXTENSION_FUNCTION_LOADER_H

#include "core/extension/gdextension_loader.h"
#include "core/os/shared_object.h"
#include <functional>

class GDExtension;

class GDExtensionFunctionLoader : public GDExtensionLoader {
friend class GDExtensionManager;
friend class GDExtension;

private:
String library_path;
GDExtensionInitializationFunction initialization_function = nullptr;

public:
Error open_library(const String &p_path) override;
Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override;
void close_library() override;
bool is_library_open() const override;
bool has_library_changed() const override;

void set_initialization_function(GDExtensionInitializationFunction initialization_function);
};

#endif // GDEXTENSION_FUNCTION_LOADER_H
11 changes: 10 additions & 1 deletion core/extension/gdextension_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "gdextension_manager.h"

#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_function_loader.h"
#include "core/extension/gdextension_library_loader.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
Expand Down Expand Up @@ -72,7 +73,14 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
Ref<GDExtensionLibraryLoader> loader;
loader.instantiate();
return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
return load_extension_with_loader(p_path, loader);
}

GDExtensionManager::LoadStatus GDExtensionManager::load_function_extension(const String &p_path, GDExtensionConstPtr<const GDExtensionInitializationFunction> p_init_func) {
Ref<GDExtensionFunctionLoader> func_loader;
func_loader.instantiate();
func_loader->set_initialization_function((GDExtensionInitializationFunction) * (p_init_func.data));
return load_extension_with_loader(p_path, func_loader);
}

GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
Expand Down Expand Up @@ -280,6 +288,7 @@ GDExtensionManager *GDExtensionManager::get_singleton() {

void GDExtensionManager::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension);
ClassDB::bind_method(D_METHOD("load_function_extension", "path", "init_func"), &GDExtensionManager::load_function_extension);
ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension);
ClassDB::bind_method(D_METHOD("unload_extension", "path"), &GDExtensionManager::unload_extension);
ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &GDExtensionManager::is_extension_loaded);
Expand Down
4 changes: 4 additions & 0 deletions core/extension/gdextension_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#define GDEXTENSION_MANAGER_H

#include "core/extension/gdextension.h"
#include "core/variant/native_ptr.h"

GDVIRTUAL_NATIVE_PTR(GDExtensionInitializationFunction)

class GDExtensionManager : public Object {
GDCLASS(GDExtensionManager, Object);
Expand Down Expand Up @@ -63,6 +66,7 @@ class GDExtensionManager : public Object {

public:
LoadStatus load_extension(const String &p_path);
LoadStatus load_function_extension(const String &p_path, GDExtensionConstPtr<const GDExtensionInitializationFunction> p_init_func);
LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
LoadStatus reload_extension(const String &p_path);
LoadStatus unload_extension(const String &p_path);
Expand Down
81 changes: 81 additions & 0 deletions core/extension/godot_instance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**************************************************************************/
/* godot_instance.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "godot_instance.h"
#include "core/extension/gdextension_manager.h"
#include "main/main.h"
#include "servers/display_server.h"

void GodotInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("start"), &GodotInstance::start);
ClassDB::bind_method(D_METHOD("is_started"), &GodotInstance::is_started);
ClassDB::bind_method(D_METHOD("iteration"), &GodotInstance::iteration);
}

GodotInstance::GodotInstance() {
}

GodotInstance::~GodotInstance() {
}

bool GodotInstance::initialize(GDExtensionInitializationFunction p_init_func) {
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
GDExtensionConstPtr<const GDExtensionInitializationFunction> ptr((const GDExtensionInitializationFunction *)&p_init_func);
GDExtensionManager::LoadStatus status = gdextension_manager->load_function_extension("libgodot://main", ptr);
return status == GDExtensionManager::LoadStatus::LOAD_STATUS_OK;
}

bool GodotInstance::start() {
Error err = Main::setup2();
if (err != OK) {
return false;
}
started = Main::start() == EXIT_SUCCESS;
if (started) {
OS::get_singleton()->get_main_loop()->initialize();
}
return started;
}

bool GodotInstance::is_started() {
return started;
}

bool GodotInstance::iteration() {
DisplayServer::get_singleton()->process_events();
return Main::iteration();
}

void GodotInstance::stop() {
if (started) {
OS::get_singleton()->get_main_loop()->finalize();
}
started = false;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**************************************************************************/
/* rendering_context_driver_vulkan_macos.h */
/* godot_instance.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
Expand Down Expand Up @@ -28,31 +28,30 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H
#define RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H
#ifndef GODOT_INSTANCE_H
#define GODOT_INSTANCE_H

#ifdef VULKAN_ENABLED
#include "core/extension/gdextension_interface.h"
#include "core/object/class_db.h"
#include "core/object/object.h"

#include "drivers/vulkan/rendering_context_driver_vulkan.h"
class GodotInstance : public Object {
GDCLASS(GodotInstance, Object);

#import <QuartzCore/CAMetalLayer.h>
static void _bind_methods();

class RenderingContextDriverVulkanMacOS : public RenderingContextDriverVulkan {
private:
virtual const char *_get_platform_surface_extension() const override final;

protected:
SurfaceID surface_create(const void *p_platform_data) override final;
bool started = false;

public:
struct WindowPlatformData {
CAMetalLayer *const *layer_ptr;
};
GodotInstance();
~GodotInstance();

RenderingContextDriverVulkanMacOS();
~RenderingContextDriverVulkanMacOS();
};
bool initialize(GDExtensionInitializationFunction p_init_func);

#endif // VULKAN_ENABLED
bool start();
bool is_started();
bool iteration();
void stop();
};

#endif // RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H
#endif // GODOT_INSTANCE_H
Loading

0 comments on commit 92f6c34

Please sign in to comment.