migueldeicaza / SwiftGodot

New Godot bindings for Swift
https://migueldeicaza.github.io/SwiftGodotDocs/tutorials/swiftgodot-tutorials/
MIT License
1.19k stars 77 forks source link

Reducing the size of SwiftGodot for deployments: godotFrameworksCtor. #555

Open migueldeicaza opened 1 month ago

migueldeicaza commented 1 month ago

SwiftGodot comes in two forms today, a dynamic library that contains everything that a user might want to use, and a static library that would only use what the user consumes.

The dynamic library can not be further reduced for the general case use (although it can be reduced for specific uses - either to reduce build times, or to manually control the size #554). For instance, for Godot on iPad, I only ever need about 240 types instead of the 876 that Godot supports.

The static library use would in theory be perfect for this scenario, but sadly, to support the resurfacing arbitrary objects, I do keep a hash table that references every type. So while not every method is consumed, a bunch of useless stuff ends up coming into the binary via this generated code:

var godotFrameworkCtors = [
    "AESContext": AESContext.self, //(nativeHandle:),
    "AStar2D": AStar2D.self, //(nativeHandle:),
    "AStar3D": AStar3D.self, //(nativeHandle:),
    "AStarGrid2D": AStarGrid2D.self, //(nativeHandle:),
    "AcceptDialog": AcceptDialog.self, //(nativeHandle:),
...

Used by lookupObject like this:

func lookupObject<T: Object> (nativeHandle: UnsafeRawPointer) -> T? {
    if let a = objectFromHandle(nativeHandle: nativeHandle) {
        return a as? T 
    }
    var className: String = ""
    var sc: StringName.ContentType = StringName.zero
    if gi.object_get_class_name (nativeHandle, library, &sc) != 0 {
        let sn = StringName(content: sc)
        className = String(sn) 
    } else {
        let copy = nativeHandle
        let _result: GString = GString ()
        gi.object_method_bind_ptrcall (Object.method_get_class, UnsafeMutableRawPointer (mutating: copy), nil, &_result.content)
        className = _result.description
    }
    if let ctor = godotFrameworkCtors [className] {
        return ctor.init (nativeHandle: nativeHandle) as? T
    }
    if let userTypeCtor = userTypes [className] { 
        if let created = userTypeCtor (nativeHandle) as? T {
            return created 
        } else { 
            print ("Found a custom type for \(className) but the constructor failed to return an instance of it as a \(T.self)")
        }
    } 

    return T.init (nativeHandle: nativeHandle)
}

So the question is whether we can find a way of invoking the ctor.init(nativeHandle:) for a given T, without relying on that hash table.

See also: https://github.com/migueldeicaza/SwiftGodot/issues/14