Optimizations to the generated bindings

This change gets rid of the call-time method binding query, instead it queries
all method bindings up-front at initialization time so that no extra cost is
added at function call time.

In addition, also converted the "icall" code to a header-only library so one
level of unnecessary call-stack is eliminated.

Also changed binding generator to use real_t instead of double everywhere
(except at the GDNative interface where unfortunately using doubles is hard-coded
on the engine side).

All this comes at a minimal increase in binary size (for the library, as actual
native modules might not even increase in size in practice).
pull/262/head
Daniel Rakos 2019-04-08 00:45:35 +02:00
parent f0fe88bd36
commit 01606fa212
1 changed files with 62 additions and 84 deletions

View File

@ -28,15 +28,15 @@ def generate_bindings(path):
source_file.write(impl) source_file.write(impl)
icall_header_file = open("src/gen/__icalls.hpp", "w+") icall_header_file = open("include/gen/__icalls.hpp", "w+")
icall_header_file.write(generate_icall_header(icalls)) icall_header_file.write(generate_icall_header(icalls))
icall_source_file = open("src/gen/__icalls.cpp", "w+")
icall_source_file.write(generate_icall_implementation(icalls))
register_types_file = open("src/gen/__register_types.cpp", "w+") register_types_file = open("src/gen/__register_types.cpp", "w+")
register_types_file.write(generate_type_registry(classes)) register_types_file.write(generate_type_registry(classes))
init_method_bindings_file = open("src/gen/__init_method_bindings.cpp", "w+")
init_method_bindings_file.write(generate_init_method_bindings(classes))
def is_reference_type(t): def is_reference_type(t):
for c in classes: for c in classes:
@ -58,9 +58,10 @@ def make_gdnative_type(t):
if t == "int": if t == "int":
return "int64_t " return "int64_t "
if t == "float" or t == "real": if t == "float" or t == "real":
return "double " return "real_t "
return strip_name(t) + " " return strip_name(t) + " "
def generate_class_header(used_classes, c): def generate_class_header(used_classes, c):
source = [] source = []
@ -126,8 +127,8 @@ def generate_class_header(used_classes, c):
if c["base_class"] == "": if c["base_class"] == "":
source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };") source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };")
source.append("private:")
source.append("") source.append("")
source.append("private:")
if c["singleton"]: if c["singleton"]:
source.append("\tstatic " + class_name + " *_singleton;") source.append("\tstatic " + class_name + " *_singleton;")
@ -135,8 +136,18 @@ def generate_class_header(used_classes, c):
source.append("\t" + class_name + "();") source.append("\t" + class_name + "();")
source.append("") source.append("")
# Generate method table
source.append("\tstruct ___method_bindings {")
for method in c["methods"]:
source.append("\t\tgodot_method_bind *mb_" + method["name"] + ";")
source.append("\t};")
source.append("\tstatic ___method_bindings ___mb;")
source.append("")
source.append("public:") source.append("public:")
source.append("\tstatic void ___init_method_bindings();")
source.append("") source.append("")
@ -335,7 +346,17 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("") source.append("")
source.append("") source.append("")
# Method table initialization
source.append(class_name + "::___method_bindings " + class_name + "::___mb = {};")
source.append("")
source.append("void " + class_name + "::___init_method_bindings() {")
for method in c["methods"]:
source.append("\t___mb.mb_" + method["name"] + " = godot::api->godot_method_bind_get_method(\"" + c["name"] + "\", \"" + method["name"] + "\");")
source.append("}")
source.append("")
if c["instanciable"]: if c["instanciable"]:
source.append(class_name + " *" + strip_name(c["name"]) + "::_new()") source.append(class_name + " *" + strip_name(c["name"]) + "::_new()")
@ -373,12 +394,6 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("}") source.append("}")
source.append("") source.append("")
continue continue
else:
source.append("\tstatic godot_method_bind *mb = nullptr;")
source.append("\tif (mb == nullptr) {")
source.append("\t\tmb = godot::api->godot_method_bind_get_method(\"" + c["name"] +"\", \"" + method["name"] + "\");")
source.append("\t}")
return_statement = "" return_statement = ""
@ -441,7 +456,7 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("") source.append("")
source.append("\tVariant __result;") source.append("\tVariant __result;")
source.append("\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(mb, ((const Object *) " + core_object_name + ")->_owner, (const godot_variant **) __args, " + size + ", nullptr);") source.append("\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(___mb.mb_" + method["name"] + ", ((const Object *) " + core_object_name + ")->_owner, (const godot_variant **) __args, " + size + ", nullptr);")
source.append("") source.append("")
@ -485,7 +500,7 @@ def generate_class_implementation(icalls, used_classes, c):
icall_name = get_icall_name(icall_sig) icall_name = get_icall_name(icall_sig)
return_statement += icall_name + "(mb, (const Object *) " + core_object_name return_statement += icall_name + "(___mb.mb_" + method["name"] + ", (const Object *) " + core_object_name
for arg in method["arguments"]: for arg in method["arguments"]:
return_statement += ", " + escape_cpp(arg["name"]) + (".ptr()" if is_reference_type(arg["type"]) else "") return_statement += ", " + escape_cpp(arg["name"]) + (".ptr()" if is_reference_type(arg["type"]) else "")
@ -520,60 +535,6 @@ def generate_icall_header(icalls):
source.append("#include <stdint.h>") source.append("#include <stdint.h>")
source.append("") source.append("")
source.append("#include <core/CoreTypes.hpp>")
source.append("#include \"Object.hpp\"")
source.append("")
source.append("")
source.append("namespace godot {")
source.append("")
for icall in icalls:
ret_type = icall[0]
args = icall[1]
method_signature = ""
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst"
for arg in args:
method_signature += ", const "
if is_core_type(arg):
method_signature += arg + "&"
elif arg == "int":
method_signature += "int64_t "
elif arg == "float":
method_signature += "double "
elif is_primitive(arg):
method_signature += arg
else:
method_signature += "Object *"
method_signature += ");"
source.append(method_signature)
source.append("")
source.append("}")
source.append("")
source.append("#endif")
return "\n".join(source)
def generate_icall_implementation(icalls):
source = []
source.append("#include \"__icalls.hpp\"")
source.append("")
source.append("#include <gdnative_api_struct.gen.h>")
source.append("#include <stdint.h>")
source.append("")
source.append("#include <core/GodotGlobal.hpp>") source.append("#include <core/GodotGlobal.hpp>")
source.append("#include <core/CoreTypes.hpp>") source.append("#include <core/CoreTypes.hpp>")
source.append("#include \"Object.hpp\"") source.append("#include \"Object.hpp\"")
@ -587,15 +548,15 @@ def generate_icall_implementation(icalls):
ret_type = icall[0] ret_type = icall[0]
args = icall[1] args = icall[1]
method_signature = "" method_signature = "static inline "
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst" method_signature += get_icall_return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst"
for i, arg in enumerate(args): for i, arg in enumerate(args):
method_signature += ", const " method_signature += ", const "
if is_core_type(arg): if is_core_type(arg):
method_signature += arg + "& " method_signature += arg + "&"
elif arg == "int": elif arg == "int":
method_signature += "int64_t " method_signature += "int64_t "
elif arg == "float": elif arg == "float":
@ -612,7 +573,7 @@ def generate_icall_implementation(icalls):
source.append(method_signature + " {") source.append(method_signature + " {")
if ret_type != "void": if ret_type != "void":
source.append("\t" + ("godot_object *" if is_class_type(ret_type) else return_type(ret_type)) + "ret;") source.append("\t" + ("godot_object *" if is_class_type(ret_type) else get_icall_return_type(ret_type)) + "ret;")
if is_class_type(ret_type): if is_class_type(ret_type):
source.append("\tret = nullptr;") source.append("\tret = nullptr;")
@ -646,15 +607,16 @@ def generate_icall_implementation(icalls):
source.append("\treturn ret;") source.append("\treturn ret;")
source.append("}") source.append("}")
source.append("")
source.append("}") source.append("}")
source.append("") source.append("")
source.append("#endif")
return "\n".join(source) return "\n".join(source)
def generate_type_registry(classes): def generate_type_registry(classes):
source = [] source = []
@ -695,18 +657,34 @@ def generate_type_registry(classes):
return "\n".join(source) return "\n".join(source)
def generate_init_method_bindings(classes):
source = []
for c in classes:
source.append("#include <" + strip_name(c["name"]) + ".hpp>")
source.append("")
source.append("")
source.append("namespace godot {")
source.append("void ___init_method_bindings()")
source.append("{")
for c in classes:
class_name = strip_name(c["name"])
source.append("\t" + strip_name(c["name"]) + "::___init_method_bindings();")
source.append("}")
source.append("")
source.append("}")
return "\n".join(source)
def get_icall_return_type(t):
def return_type(t):
if is_class_type(t): if is_class_type(t):
return "Object *" return "Object *"
if t == "int": if t == "int":