Skip to content

Commit 55f4baf

Browse files
committed
Fix int/float vs builtins containing int/float size issues
1 parent b2ef713 commit 55f4baf

File tree

10 files changed

+102
-37
lines changed

10 files changed

+102
-37
lines changed

meson.build

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ if (
2626
endif
2727

2828

29+
# Godot can be build in two mode: real number in builtins (e.g. Vector2) as
30+
# 32bits (float, the default) or 64bits (double).
31+
# On top of that pointer size is of course different between 32 and 64 bits platforms.
32+
if get_option('real_is_double')
33+
if host_machine.cpu_family() == 'x86_64'
34+
godot_build_config = 'double_64'
35+
else
36+
godot_build_config = 'double_32'
37+
endif
38+
else
39+
if host_machine.cpu_family() == 'x86_64'
40+
godot_build_config = 'float_64'
41+
else
42+
godot_build_config = 'float_32'
43+
endif
44+
endif
45+
2946
# We stick with Godot's platform naming
3047
# Note darwin may refere to iOS and macOS (because it's sooo clever to designate
3148
# two totally diferent things with the same name...) but we only support macOS

meson_options.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
option('cpython_distrib_version', type: 'string', value: '3.9.7', description: 'Version of CPython to ship')
22
option('godot_headers', type: 'string', value: 'godot_headers', description: 'Path to Godot extension headers directory')
3+
option('real_is_double', type: 'boolean', value: false, description: 'Built for Godot with 64bits floating point numbers (i.e. double) in builtins instead of 32bits (i.e. float)')

scripts/extension_api_parser/api.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from .builtins import BuiltinSpec
88
from .classes import ClassSpec
99
from .type import (
10+
TYPES_DB,
1011
TypeInUse,
12+
TypeSpec,
1113
register_builtins_in_types_db,
1214
register_classes_in_types_db,
1315
register_global_enums_in_types_db,
@@ -118,6 +120,8 @@ class ExtensionApi:
118120
version_build: str # e.g. "official"
119121
version_full_name: str # e.g. "Godot Engine v4.0.alpha13.official"
120122

123+
variant_size: int
124+
121125
classes: List[ClassSpec]
122126
builtins: List[BuiltinSpec]
123127
global_constants: List[GlobalConstantSpec]
@@ -126,6 +130,23 @@ class ExtensionApi:
126130
singletons: List[SingletonSpec]
127131
native_structures: List[NativeStructureSpec]
128132

133+
# Expose scalars
134+
135+
@property
136+
def bool_spec(self):
137+
return TYPES_DB["bool"]
138+
# return next(builtin for builtin in self.builtins if builtin.name == "int")
139+
140+
@property
141+
def int_spec(self):
142+
return TYPES_DB["int"]
143+
# return next(builtin for builtin in self.builtins if builtin.name == "int")
144+
145+
@property
146+
def float_spec(self):
147+
return TYPES_DB["float"]
148+
# return next(builtin for builtin in self.builtins if builtin.name == "int")
149+
129150

130151
def merge_builtins_size_info(api_json: dict, build_config: BuildConfig) -> None:
131152
# Builtins size depend of the build config, hence it is stored separatly from
@@ -153,6 +174,18 @@ def merge_builtins_size_info(api_json: dict, build_config: BuildConfig) -> None:
153174
for item_member in item["members"]:
154175
if item_member["name"] == member["member"]:
155176
item_member["offset"] = member["offset"]
177+
# Float builtin in extension_api.json is always 64bits long,
178+
# however builtins made of floating point number can be made of
179+
# 32bits (C float) or 64bits (C double)
180+
if item_member["type"] == "float":
181+
if build_config in (BuildConfig.FLOAT_32, BuildConfig.FLOAT_64):
182+
item_member["type"] = "meta:float"
183+
else:
184+
assert build_config in (BuildConfig.DOUBLE_32, BuildConfig.DOUBLE_64)
185+
item_member["type"] = "meta:double"
186+
elif item_member["type"] == "int":
187+
# Builtins containing int is always made of int32
188+
item_member["type"] = "meta:int32"
156189
break
157190
else:
158191
raise RuntimeError(f"Don't member {member} doesn't seem to be part of {name} !")
@@ -166,7 +199,7 @@ def parse_extension_api_json(path: Path, build_config: BuildConfig) -> Extension
166199
api_json = json.loads(path.read_text(encoding="utf8"))
167200
assert isinstance(api_json, dict)
168201

169-
variant_size = merge_builtins_size_info(api_json, build_config)
202+
merge_builtins_size_info(api_json, build_config)
170203

171204
api = ExtensionApi(
172205
version_major=api_json["header"]["version_major"],
@@ -175,6 +208,7 @@ def parse_extension_api_json(path: Path, build_config: BuildConfig) -> Extension
175208
version_status=api_json["header"]["version_status"],
176209
version_build=api_json["header"]["version_build"],
177210
version_full_name=api_json["header"]["version_full_name"],
211+
variant_size=api_json["variant_size"],
178212
classes=[ClassSpec.parse(x) for x in api_json["classes"]],
179213
builtins=[BuiltinSpec.parse(x) for x in api_json["builtin_classes"]],
180214
global_constants=[GlobalConstantSpec.parse(x) for x in api_json["global_constants"]],

scripts/extension_api_parser/type.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
class TypeSpec:
1919
# Type used within Godot `extension_api.json`
2020
gdapi_type: str
21-
# Type used when calling C api functions
22-
c_type: str
2321
# Type used for PEP 484 Python typing
2422
py_type: str
23+
# Type used when calling C api functions
24+
c_type: str
2525
# Type used in Cython, basically similar to c_type for scalars&enums
2626
# and to py_type for Godot objects&builtins
27-
cy_type: str = None
27+
cy_type: str
2828
# Type is a Godot object (i.e. defined in api.json)
2929
is_object: bool = False
3030
# Type is a Godot builtin (e.g. Vector2)
@@ -40,7 +40,6 @@ class TypeSpec:
4040
variant_type_name: str = "<n/a>"
4141

4242
def __post_init__(self):
43-
self.cy_type = self.cy_type or self.py_type
4443
if self.is_scalar:
4544
assert not self.is_object
4645
assert not self.is_builtin
@@ -59,27 +58,32 @@ def __post_init__(self):
5958

6059

6160
TYPES_DB: Dict[str, TypeSpec] = {
62-
"Variant": TypeSpec(gdapi_type="Variant", c_type="CVariant", py_type="GDAny", is_builtin=True),
61+
"Variant": TypeSpec(
62+
gdapi_type="Variant", c_type="CVariant", cy_type="object", py_type="GDAny", is_builtin=True
63+
),
6364
# Types marked as `meta` are used in the classes method args/return types
6465
"meta:int8": TypeSpec(
6566
gdapi_type="int8",
6667
c_type="int8_t",
68+
cy_type="int8_t",
6769
py_type="int",
6870
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
6971
is_scalar=True,
7072
is_stack_only=True,
7173
),
72-
"meta:int32": TypeSpec(
73-
gdapi_type="int32",
74-
c_type="int32_t",
74+
"meta:int16": TypeSpec(
75+
gdapi_type="int16",
76+
c_type="int16_t",
77+
cy_type="int16_t",
7578
py_type="int",
7679
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
7780
is_scalar=True,
7881
is_stack_only=True,
7982
),
80-
"meta:int16": TypeSpec(
81-
gdapi_type="int16",
82-
c_type="int16_t",
83+
"meta:int32": TypeSpec(
84+
gdapi_type="int32",
85+
c_type="int32_t",
86+
cy_type="int32_t",
8387
py_type="int",
8488
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
8589
is_scalar=True,
@@ -88,6 +92,7 @@ def __post_init__(self):
8892
"meta:int64": TypeSpec(
8993
gdapi_type="int64",
9094
c_type="int64_t",
95+
cy_type="int64_t",
9196
py_type="int",
9297
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
9398
is_scalar=True,
@@ -96,6 +101,7 @@ def __post_init__(self):
96101
"meta:uint8": TypeSpec(
97102
gdapi_type="uint8",
98103
c_type="uint8_t",
104+
cy_type="uint8_t",
99105
py_type="int",
100106
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
101107
is_scalar=True,
@@ -104,6 +110,7 @@ def __post_init__(self):
104110
"meta:uint16": TypeSpec(
105111
gdapi_type="uint16",
106112
c_type="uint16_t",
113+
cy_type="uint16_t",
107114
py_type="int",
108115
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
109116
is_scalar=True,
@@ -112,6 +119,7 @@ def __post_init__(self):
112119
"meta:uint32": TypeSpec(
113120
gdapi_type="uint32",
114121
c_type="uint32_t",
122+
cy_type="uint32_t",
115123
py_type="int",
116124
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
117125
is_scalar=True,
@@ -120,6 +128,17 @@ def __post_init__(self):
120128
"meta:uint64": TypeSpec(
121129
gdapi_type="uint64",
122130
c_type="uint64_t",
131+
cy_type="uint64_t",
132+
py_type="int",
133+
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
134+
is_scalar=True,
135+
is_stack_only=True,
136+
),
137+
# int is always 8bytes long
138+
"int": TypeSpec(
139+
gdapi_type="int",
140+
c_type="uint64_t",
141+
cy_type="uint64_t",
123142
py_type="int",
124143
variant_type_name="GDNATIVE_VARIANT_TYPE_INT",
125144
is_scalar=True,
@@ -128,6 +147,7 @@ def __post_init__(self):
128147
"meta:float": TypeSpec(
129148
gdapi_type="float",
130149
c_type="float",
150+
cy_type="float",
131151
py_type="float",
132152
variant_type_name="GDNATIVE_VARIANT_TYPE_FLOAT",
133153
is_scalar=True,
@@ -136,6 +156,7 @@ def __post_init__(self):
136156
"meta:double": TypeSpec(
137157
gdapi_type="double",
138158
c_type="double",
159+
cy_type="double",
139160
py_type="float",
140161
variant_type_name="GDNATIVE_VARIANT_TYPE_FLOAT",
141162
is_scalar=True,
@@ -159,6 +180,7 @@ def register_builtins_in_types_db(builtins: Iterable["BuiltinSpec"]) -> None:
159180
# Cython provide a `bint` type for boolean, however it is defined in C as
160181
# a `int` (so 32bits), so I guess it won't work for Godot's 8bits bool
161182
c_type=f"uint{spec.size*8}_t",
183+
cy_type=f"uint{spec.size*8}_t",
162184
is_stack_only=True,
163185
is_scalar=True,
164186
variant_type_name=spec.variant_type_name,
@@ -194,6 +216,7 @@ def register_classes_in_types_db(classes: Iterable["ClassSpec"]) -> None:
194216
gdapi_type=spec.original_name,
195217
py_type=spec.name,
196218
c_type="Object",
219+
cy_type="Object",
197220
variant_type_name="GDNATIVE_VARIANT_TYPE_OBJECT",
198221
)
199222
TYPES_DB[ts.gdapi_type] = ts
@@ -202,6 +225,7 @@ def register_classes_in_types_db(classes: Iterable["ClassSpec"]) -> None:
202225
gdapi_type=f"enum::{spec.original_name}.{e.original_name}",
203226
py_type=f"{spec.name}.{e.name}",
204227
c_type="int",
228+
cy_type=f"{spec.name}.{e.name}",
205229
is_scalar=True,
206230
is_stack_only=True,
207231
is_enum=True,
@@ -216,6 +240,7 @@ def register_global_enums_in_types_db(enums: Iterable["GlobalEnumSpec"]) -> None
216240
gdapi_type=f"enum::{spec.original_name}",
217241
py_type=spec.name,
218242
c_type="int",
243+
cy_type=spec.name,
219244
is_scalar=True,
220245
is_stack_only=True,
221246
is_enum=True,
@@ -230,7 +255,6 @@ class TypeInUse:
230255

231256
def __repr__(self) -> str:
232257
try:
233-
raise KeyError
234258
resolved = self.resolve()
235259
except KeyError:
236260
resolved = "<not resolved yet>"
@@ -242,7 +266,6 @@ def resolve(self) -> TypeSpec:
242266
try:
243267
return TYPES_DB[self.type_name]
244268
except KeyError:
245-
breakpoint()
246269
return
247270

248271
def __getattr__(self, name: str):

scripts/generate_tmpl.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,13 @@ def generate_builtins_pyi(api: ExtensionApi) -> str:
7474
parser.add_argument(
7575
"--build-config",
7676
required=True,
77-
type=BuildConfig,
78-
choices=BuildConfig,
79-
help=f"choices: {', '.join([x.value for x in BuildConfig])}",
77+
choices=[x.value for x in BuildConfig],
8078
metavar="CONFIG",
8179
)
8280

8381
args = parser.parse_args()
8482

85-
api = parse_extension_api_json(path=args.input, build_config=args.build_config)
83+
api = parse_extension_api_json(path=args.input, build_config=BuildConfig(args.build_config))
8684

8785
# We use # in the name to simulate folder hierarchy in the meson build
8886
*_, name = args.output.name.rsplit("#", 1)

src/_pythonscript.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ cdef api void _pythonscript_initialize() with gil:
4343
cooked_sys_version = '.'.join(map(str, sys.version_info))
4444
print(f"Pythonscript {pythonscript_version} (CPython {cooked_sys_version})")
4545
print(f"PYTHONPATH: {sys.path}")
46-
v = Vector2(3., 2.)
47-
v2 = Vector2(v)
46+
v = Vector2(66.8, 77.99)
47+
v.x = 42
4848
print("===========>", v.x, v.y)
49+
v2 = Vector2(v)
4950
print("+++++++++++>", v2.x, v2.y)
5051

5152

src/godot/_builtins.pyx.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{%- from '_builtins_pyx/class.j2' import render_spec -%}
1+
{%- from '_builtins_pyx/class.j2' import render_spec with context -%}
22
# /!\ Autogenerated code, modifications will be lost /!\
33
# see `scripts/generate_tmpl.py`
44

src/godot/_builtins_pyx/class.j2

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
{%- from '_builtins_pyx/constructor.j2' import render_constructor -%}
2-
{%- from '_builtins_pyx/gdapi.j2' import render_gdapi -%}
1+
{%- from '_builtins_pyx/constructor.j2' import render_constructor with context -%}
2+
{%- from '_builtins_pyx/gdapi.j2' import render_gdapi with context -%}
33

44
{% macro render_spec(spec) -%}
55

@@ -52,7 +52,7 @@ cdef class {{ spec.name }}:
5252
def {{ m.name }}(self) -> {{ m.type.py_type }}:
5353
{% if m.offset is not none %}
5454
{% if m.type.is_scalar %}
55-
return (<{{ m.type.c_type }}*>self._gd_data)[{{ m.offset }}]
55+
return (<{{ m.type.c_type }}*>(&self._gd_data[{{ m.offset }}]))[0]
5656
{# TODO: ensure m.type doesn't need a destructor #}
5757
{% else %}
5858
{# TODO: Find a way to avoid this copy ? #}
@@ -63,7 +63,7 @@ cdef class {{ spec.name }}:
6363
@{{ m.name }}.setter
6464
def {{ m.name }}(self, {{ m.type.py_type }} value) -> None:
6565
{% if m.type.is_scalar %}
66-
(<{{ m.type.c_type }}*>self._gd_data)[{{ m.offset }}] = value
66+
(<{{ m.type.c_type }}*>(&self._gd_data[{{ m.offset }}]))[0] = value
6767
{# TODO: ensure m.type doesn't need a destructor #}
6868
{% else %}
6969
self._gd_data = value._gd_data

src/godot/_builtins_pyx/constructor.j2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
def __cinit__({{ spec.name }} self, x=None, y=None):
1010
cdef GDNativeTypePtr args[2]
11-
cdef float args_x
12-
cdef float args_y
11+
cdef {{ api.float_spec.cy_type }} args_x
12+
cdef {{ api.float_spec.cy_type }} args_y
1313
if y is None:
1414
if x is None:
1515
__{{ spec.name }}_constructor_0(self._gd_data, NULL)

src/meson.build

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,13 @@ generate_tmpl_base_input = [
1717
]
1818

1919

20-
if host_machine.cpu_family() == 'x86_64'
21-
generate_tmpl_build_config = 'double_64'
22-
elif host_machine.cpu_family() == 'x86'
23-
generate_tmpl_build_config = 'double_32'
24-
else
25-
error('Unsupported CPU familly' + host_machine.cpu_family())
26-
endif
27-
28-
2920
generate_tmpl_cmd = [
3021
python,
3122
'@INPUT0@', # generate_tmpl.py
3223
'--input',
3324
'@INPUT1@', # extension_api.json
3425
'--build-config',
35-
generate_tmpl_build_config,
26+
godot_build_config,
3627
'--output',
3728
'@OUTPUT0@',
3829
]

0 commit comments

Comments
 (0)