-
-
Notifications
You must be signed in to change notification settings - Fork 689
Description
Godot version
4.1-rc2
godot-cpp version
master (80986f8)
System information
Linux
Issue description
@Faless was the one to discover this with the webrtc-native GDExtension. However, you can see it even with the test project: with gcc on Linux, the debug template build is ~5mb larger than when using godot-cpp on 4.0.3-stable tag.
It's PR #1050 that led to this problem.
The reason is that before PR #1050, if you didn't use a class in your GDExtension, it wouldn't be included in the final binary - the linker just leaves it out automatically. However, an unintended consequence of that PR is that now all classes are always included in the final binary.
That PR aimed to solve a bug where the godot-cpp wrapper class that was created for a Godot object was (before the PR) the class of the argument or return value. So, if you called get_node("MyNode") it would always wrap the object in the Node wrapper class, even if the node was actually a Sprite2D. And because Godot caches the wrapper class, it would forever be stuck as a Node wrapper. If you had another method that passed in the same object as a Sprite2D, it would unsafely cast the wrapper to Sprite2D even if it wasn't actually one. Despite being unsafe, this actually works in a surprising number of situations! However, there are case where it causes problems, like with virtual methods. (And it's also plain incorrect C++ :-))
Anyway, that PR solves the problem by making a big HashMap of the binding callbacks for all the Godot engine classes, so when an object is sent over to godot-cpp, it can use the correct wrapper class (ie. the one matching its class on the Godot side). It's this HashMap that's causing all classes to be "used".
I've thought up 4-ish possible solutions (although, there's probably more - please share your ideas!):
- We make a scons tool that scans the user's GDExtension code for includes, and only puts the classes in the
HashMapthat were discovered in the scan (and all their parent classes). I don't think this would be the hackiest thing in the world? Header file includes are pretty easy to scan for. And it means that GDExtensions will work as expected out-of-the-box, but you can optionally take this extra step (which we could include in an official godot-cpp template) that optimizes the size of the extension. However, I know that not all people want to use scons, and they'd need to do their own optimization step. - We stop caching the wrappers on the Godot side, and just always make a new one when requested. And then
Object::cast<T>()would also create a new wrapper when doing a valid downcast. However, this would likely be slower, since we'd be creating and throwing away way more wrappers. And, actually, I'm not sure how those extra wrappers would get cleaned up? - We allow
Object::cast_to<T>()to "upgrade" the wrapper to one that's further down the inheritance tree, and overwrite the one in the cache, if it's a valid downcast. This would also be somewhat slower (and maybe surprising, because you'll get a different object than you put in) and also leaves the open question of who cleans up the old wrappers. - We just accept that GDExtensions with godot-cpp need to be ~1-5mb bigger than they were before :-)
What do you think?
Steps to reproduce
- Compile the test/demo project with current godot-cpp master and check the resulting binary size
- Compile the test/demo project with godot-cpp 4.0.3-stable and check the resulting binary size.
Minimal reproduction project
n/a