Skip to content

Commit af5bbcf

Browse files
authored
Merge pull request #1948 from pmp-p/wasm-static-multiphase-init
Wasm static multiphase init for dynamic loading
2 parents 0cc57b6 + 2370ea5 commit af5bbcf

File tree

4 files changed

+175
-38
lines changed

4 files changed

+175
-38
lines changed

src_c/_blit_info.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#ifndef _BLIT_INFO_H
2+
#define _BLIT_INFO_H
13
#define NO_PYGAME_C_API
24
#include "_surface.h"
35

@@ -19,3 +21,4 @@ typedef struct {
1921
SDL_BlendMode src_blend;
2022
SDL_BlendMode dst_blend;
2123
} SDL_BlitInfo;
24+
#endif // BLIT_INFO_H

src_c/static.c

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ PyInit_sdl2(void);
131131
PyMODINIT_FUNC
132132
PyInit_mixer(void);
133133

134-
PyMODINIT_FUNC
135-
PyInit_context(void);
134+
// PyMODINIT_FUNC
135+
// PyInit_context(void);
136136

137137
PyMODINIT_FUNC
138138
PyInit_controller(void);
@@ -152,41 +152,138 @@ PyInit_pixelcopy(void);
152152
PyMODINIT_FUNC
153153
PyInit_gfxdraw(void);
154154

155+
PyMODINIT_FUNC
156+
PyInit_audio(void);
157+
158+
// pygame_static module
159+
155160
void
156-
PyGame_static_init()
161+
load_submodule(const char *parent, PyObject *mod, const char *alias)
157162
{
158-
PyImport_AppendInittab("pygame_base", PyInit_base);
159-
PyImport_AppendInittab("pygame_color", PyInit_color);
160-
PyImport_AppendInittab("pygame_constants", PyInit_constants);
161-
PyImport_AppendInittab("pygame_rect", PyInit_rect);
162-
PyImport_AppendInittab("pygame_surflock", PyInit_surflock);
163-
PyImport_AppendInittab("pygame_rwobject", PyInit_rwobject);
164-
PyImport_AppendInittab("pygame_bufferproxy", PyInit_bufferproxy);
165-
PyImport_AppendInittab("pygame_math", PyInit_pg_math);
166-
PyImport_AppendInittab("pygame_surface", PyInit_surface);
167-
PyImport_AppendInittab("pygame_pixelcopy", PyInit_pixelcopy);
168-
PyImport_AppendInittab("pygame_transform", PyInit_transform);
169-
PyImport_AppendInittab("pygame_display", PyInit_display);
170-
PyImport_AppendInittab("pygame__freetype", PyInit__freetype);
171-
PyImport_AppendInittab("pygame_font", PyInit_font);
172-
PyImport_AppendInittab("pygame_draw", PyInit_draw);
173-
PyImport_AppendInittab("pygame_gfxdraw", PyInit_gfxdraw);
174-
PyImport_AppendInittab("pygame_imageext", PyInit_imageext);
175-
PyImport_AppendInittab("pygame_image", PyInit_image);
176-
PyImport_AppendInittab("pygame_mask", PyInit_mask);
177-
PyImport_AppendInittab("pygame_mixer_music", PyInit_mixer_music);
178-
PyImport_AppendInittab("pygame_mixer", PyInit_pg_mixer);
179-
PyImport_AppendInittab("pygame_mouse", PyInit_mouse);
180-
PyImport_AppendInittab("pygame_key", PyInit_key);
181-
PyImport_AppendInittab("pygame_event", PyInit_event);
182-
PyImport_AppendInittab("pygame_joystick", PyInit_joystick);
183-
PyImport_AppendInittab("pygame_time", PyInit_pg_time);
184-
PyImport_AppendInittab("pygame_sdl2_video", PyInit_video);
185-
PyImport_AppendInittab("pygame_context", PyInit_context);
186-
PyImport_AppendInittab("pygame_sprite", PyInit__sprite);
187-
PyImport_AppendInittab("pygame__sdl2_sdl2", PyInit_sdl2);
188-
PyImport_AppendInittab("pygame__sdl2_sdl2_mixer", PyInit_mixer);
189-
PyImport_AppendInittab("pygame__sdl2_controller", PyInit_controller);
163+
char fqn[1024];
164+
snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias);
165+
166+
PyObject *modules = PyImport_GetModuleDict();
167+
168+
PyObject *pmod = PyDict_GetItemString(modules, parent);
169+
170+
if (!mod) {
171+
snprintf(fqn, sizeof(fqn), "ERROR: %s.%s", parent, alias);
172+
puts(fqn);
173+
PyErr_Print();
174+
PyErr_Clear();
175+
}
176+
else {
177+
PyDict_SetItemString(modules, fqn, mod);
178+
PyDict_SetItemString(PyModule_GetDict(mod), "__name__",
179+
PyUnicode_FromString(fqn));
180+
PyModule_AddObjectRef(pmod, alias, mod);
181+
Py_XDECREF(mod);
182+
}
183+
}
184+
185+
void
186+
load_submodule_mphase(const char *parent, PyObject *mdef, PyObject *spec,
187+
const char *alias)
188+
{
189+
char fqn[1024];
190+
snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias);
191+
192+
PyObject *modules = PyImport_GetModuleDict();
193+
194+
Py_DECREF(PyObject_GetAttrString(spec, "name"));
195+
196+
PyObject_SetAttrString(spec, "name", PyUnicode_FromString(alias));
197+
198+
PyObject *pmod = PyDict_GetItemString(modules, parent);
199+
200+
PyObject *mod = PyModule_FromDefAndSpec((PyModuleDef *)mdef, spec);
201+
202+
PyDict_SetItemString(PyModule_GetDict(mod), "__package__",
203+
PyUnicode_FromString(parent));
204+
205+
// TODO SET PACKAGE
206+
207+
PyModule_ExecDef(mod, (PyModuleDef *)mdef);
208+
209+
if (!pmod) {
210+
snprintf(fqn, sizeof(fqn), "ERROR: %s.%s", parent, alias);
211+
puts(fqn);
212+
PyErr_Print();
213+
PyErr_Clear();
214+
}
215+
else {
216+
PyDict_SetItemString(modules, fqn, mod);
217+
PyDict_SetItemString(PyModule_GetDict(mod), "__name__",
218+
PyUnicode_FromString(fqn));
219+
PyModule_AddObjectRef(pmod, alias, mod);
220+
Py_XDECREF(mod);
221+
}
222+
}
223+
224+
static PyObject *
225+
mod_pygame_import_cython(PyObject *self, PyObject *spec)
226+
{
227+
load_submodule_mphase("pygame._sdl2", PyInit_sdl2(), spec, "sdl2");
228+
load_submodule_mphase("pygame._sdl2", PyInit_mixer(), spec, "mixer");
229+
load_submodule_mphase("pygame._sdl2", PyInit_controller(), spec,
230+
"controller");
231+
load_submodule_mphase("pygame._sdl2", PyInit_audio(), spec, "audio");
232+
load_submodule_mphase("pygame._sdl2", PyInit_video(), spec, "video");
233+
// depends on pygame._sdl2.video
234+
load_submodule_mphase("pygame", PyInit__sprite(), spec, "_sprite");
235+
Py_RETURN_NONE;
236+
}
237+
238+
static PyMethodDef mod_pygame_static_methods[] = {
239+
{"import_cython", (PyCFunction)mod_pygame_import_cython, METH_O,
240+
"pygame._sdl2.sdl2"},
241+
{NULL, NULL, 0, NULL}};
242+
243+
static struct PyModuleDef mod_pygame_static = {PyModuleDef_HEAD_INIT,
244+
"pygame_static", NULL, -1,
245+
mod_pygame_static_methods};
246+
247+
PyMODINIT_FUNC
248+
PyInit_pygame_static()
249+
{
250+
load_submodule("pygame", PyInit_base(), "base");
251+
load_submodule("pygame", PyInit_constants(), "constants");
252+
load_submodule("pygame", PyInit_surflock(), "surflock");
253+
load_submodule("pygame", PyInit_rwobject(), "rwobject");
254+
load_submodule("pygame", PyInit_pg_math(), "math");
255+
load_submodule("pygame", PyInit_display(), "display");
256+
load_submodule("pygame", PyInit_surface(), "surface");
257+
// load_submodule("pygame", PyInit_context(), "context");
258+
load_submodule("pygame", PyInit_key(), "key");
259+
260+
load_submodule("pygame", PyInit_rect(), "rect");
261+
load_submodule("pygame", PyInit_gfxdraw(), "gfxdraw");
262+
load_submodule("pygame", PyInit_pg_time(), "time");
263+
load_submodule("pygame", PyInit__freetype(), "_freetype");
264+
265+
load_submodule("pygame", PyInit_imageext(), "imageext");
266+
267+
load_submodule("pygame", PyInit_image(), "image");
268+
load_submodule("pygame", PyInit_font(), "font");
269+
load_submodule("pygame", PyInit_pixelcopy(), "pixelcopy");
270+
271+
load_submodule("pygame", PyInit_color(), "color");
272+
load_submodule("pygame", PyInit_bufferproxy(), "bufferproxy");
273+
274+
load_submodule("pygame", PyInit_transform(), "transform");
275+
load_submodule("pygame", PyInit_draw(), "draw");
276+
277+
load_submodule("pygame", PyInit_mask(), "mask");
278+
load_submodule("pygame", PyInit_mouse(), "mouse");
279+
load_submodule("pygame", PyInit_event(), "event");
280+
load_submodule("pygame", PyInit_joystick(), "joystick");
281+
282+
load_submodule("pygame", PyInit_pg_mixer(), "mixer");
283+
284+
load_submodule("pygame.mixer", PyInit_mixer_music(), "music");
285+
286+
return PyModule_Create(&mod_pygame_static);
190287
}
191288

192289
#endif // defined(BUILD_STATIC)
@@ -224,6 +321,8 @@ PyGame_static_init()
224321
#undef pgSurface_SetSurface
225322

226323
#include "surface.c"
324+
#include "simd_blitters_avx2.c"
325+
#include "simd_blitters_sse2.c"
227326

228327
#undef pgVidInfo_Type
229328
#undef pgVidInfo_New

src_py/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,40 @@ def warn(self):
8484
print(message)
8585

8686

87+
# This is a special loader for WebAssembly platform
88+
# where pygame is in fact statically linked
89+
# mixing single phase (C) and multiphase modules (cython)
90+
if sys.platform in ("wasi", "emscripten"):
91+
try:
92+
import pygame_static
93+
except ModuleNotFoundError:
94+
pygame_static = None
95+
96+
if pygame_static:
97+
pygame = sys.modules[__name__]
98+
99+
pygame.Color = pygame.color.Color
100+
101+
Vector2 = pygame.math.Vector2
102+
Vector3 = pygame.math.Vector3
103+
104+
Rect = pygame.rect.Rect
105+
106+
BufferProxy = pygame.bufferproxy.BufferProxy
107+
108+
# cython modules use multiphase initialisation when not in builtin Inittab.
109+
110+
from pygame import _sdl2
111+
112+
import importlib.machinery
113+
114+
loader = importlib.machinery.FrozenImporter
115+
spec = importlib.machinery.ModuleSpec("", loader)
116+
pygame_static.import_cython(spec)
117+
del loader, spec
118+
del pygame_static
119+
120+
87121
# we need to import like this, each at a time. the cleanest way to import
88122
# our modules is with the import command (not the __import__ function)
89123
# isort: skip_file

src_py/_sdl2/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
from .sdl2 import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
2-
from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
3-
from .video import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
1+
if __import__("sys").platform not in ("wasi", "emscripten"):
2+
from .sdl2 import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
3+
from .audio import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
4+
from .video import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]

0 commit comments

Comments
 (0)