webgpu 1.2.1.dev0__py3-none-any.whl → 1.2.2.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webgpu/_version.py +3 -3
- webgpu/canvas.py +6 -4
- webgpu/link/base.py +26 -6
- webgpu/link/websocket.py +38 -4
- webgpu/platform.py +29 -0
- webgpu/scene.py +14 -5
- webgpu/vectors.py +3 -10
- {webgpu-1.2.1.dev0.dist-info → webgpu-1.2.2.dev0.dist-info}/METADATA +1 -1
- {webgpu-1.2.1.dev0.dist-info → webgpu-1.2.2.dev0.dist-info}/RECORD +12 -12
- {webgpu-1.2.1.dev0.dist-info → webgpu-1.2.2.dev0.dist-info}/WHEEL +0 -0
- {webgpu-1.2.1.dev0.dist-info → webgpu-1.2.2.dev0.dist-info}/licenses/LICENSE +0 -0
- {webgpu-1.2.1.dev0.dist-info → webgpu-1.2.2.dev0.dist-info}/top_level.txt +0 -0
webgpu/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 2,
|
|
21
|
+
__version__ = version = '1.2.2.dev0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 2, 2, 'dev0')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gb9e08e45e'
|
webgpu/canvas.py
CHANGED
|
@@ -147,10 +147,12 @@ class Canvas:
|
|
|
147
147
|
self.update_html_canvas(canvas)
|
|
148
148
|
|
|
149
149
|
def __del__(self):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
disconnect = getattr(self._resize_observer, "disconnect", None)
|
|
151
|
+
if callable(disconnect):
|
|
152
|
+
disconnect()
|
|
153
|
+
disconnect = getattr(self._intersection_observer, "disconnect", None)
|
|
154
|
+
if callable(disconnect):
|
|
155
|
+
disconnect()
|
|
154
156
|
|
|
155
157
|
def update_html_canvas(self, html_canvas):
|
|
156
158
|
"""Reconfigure the canvas with the current HTML canvas element. This is necessary when the HTML canvas element changes, disappears (e.g. when switching a tab) and appears again."""
|
webgpu/link/base.py
CHANGED
|
@@ -475,13 +475,13 @@ class LinkBaseAsync(LinkBase):
|
|
|
475
475
|
_callback_loop: asyncio.AbstractEventLoop
|
|
476
476
|
_callback_queue: asyncio.Queue
|
|
477
477
|
_callback_thread: threading.Thread
|
|
478
|
+
_callback_task: asyncio.Task | None
|
|
478
479
|
|
|
479
480
|
def __init__(self):
|
|
480
481
|
super().__init__()
|
|
481
482
|
self._send_loop = asyncio.new_event_loop()
|
|
482
483
|
self._callback_loop = asyncio.new_event_loop()
|
|
483
|
-
self.
|
|
484
|
-
|
|
484
|
+
self._callback_task = None
|
|
485
485
|
self._callback_thread = threading.Thread(target=self._start_callback_thread, daemon=True)
|
|
486
486
|
self._callback_thread.start()
|
|
487
487
|
|
|
@@ -516,7 +516,15 @@ class LinkBaseAsync(LinkBase):
|
|
|
516
516
|
event = threading.Event()
|
|
517
517
|
self._requests[request_id] = event, key
|
|
518
518
|
|
|
519
|
-
|
|
519
|
+
coro = self._send_async(data)
|
|
520
|
+
try:
|
|
521
|
+
asyncio.run_coroutine_threadsafe(coro, self._send_loop)
|
|
522
|
+
except RuntimeError:
|
|
523
|
+
coro.close()
|
|
524
|
+
# Event loop is closed — connection is dead, clean up and bail.
|
|
525
|
+
if event:
|
|
526
|
+
self._requests.pop(request_id, None)
|
|
527
|
+
return None
|
|
520
528
|
if event:
|
|
521
529
|
event.wait()
|
|
522
530
|
return self._requests.pop(request_id)
|
|
@@ -530,16 +538,28 @@ class LinkBaseAsync(LinkBase):
|
|
|
530
538
|
try:
|
|
531
539
|
func, args = await self._callback_queue.get()
|
|
532
540
|
func(*args)
|
|
541
|
+
except asyncio.CancelledError:
|
|
542
|
+
break
|
|
543
|
+
except RuntimeError:
|
|
544
|
+
break
|
|
533
545
|
except asyncio.QueueEmpty:
|
|
534
546
|
pass
|
|
535
547
|
except Exception as e:
|
|
536
548
|
print("error in callback", type(e), str(e))
|
|
537
|
-
# await asyncio.sleep(0.01)
|
|
538
549
|
|
|
539
550
|
try:
|
|
540
551
|
self._callback_loop = asyncio.new_event_loop()
|
|
541
552
|
asyncio.set_event_loop(self._callback_loop)
|
|
542
|
-
self.
|
|
543
|
-
self._callback_loop.
|
|
553
|
+
self._callback_queue = asyncio.Queue()
|
|
554
|
+
self._callback_task = self._callback_loop.create_task(handle_callbacks())
|
|
555
|
+
try:
|
|
556
|
+
self._callback_loop.run_forever()
|
|
557
|
+
finally:
|
|
558
|
+
if self._callback_task is not None and not self._callback_task.done():
|
|
559
|
+
self._callback_task.cancel()
|
|
560
|
+
self._callback_loop.run_until_complete(
|
|
561
|
+
asyncio.gather(self._callback_task, return_exceptions=True)
|
|
562
|
+
)
|
|
563
|
+
self._callback_loop.close()
|
|
544
564
|
except Exception as e:
|
|
545
565
|
print("exception in _start_callback_thread", e)
|
webgpu/link/websocket.py
CHANGED
|
@@ -22,7 +22,7 @@ class WebsocketLinkBase(LinkBaseAsync):
|
|
|
22
22
|
self._start_handling_messages = threading.Event()
|
|
23
23
|
self._send_loop = asyncio.new_event_loop()
|
|
24
24
|
|
|
25
|
-
self._websocket_thread = threading.Thread(target=self._connect)
|
|
25
|
+
self._websocket_thread = threading.Thread(target=self._connect, daemon=True)
|
|
26
26
|
self._websocket_thread.start()
|
|
27
27
|
|
|
28
28
|
def wait_for_server_running(self):
|
|
@@ -89,7 +89,7 @@ class WebsocketLinkServer(WebsocketLinkBase):
|
|
|
89
89
|
self._connection = websocket
|
|
90
90
|
self._event_is_connected.set()
|
|
91
91
|
async for message in websocket:
|
|
92
|
-
thread = threading.Thread(target=self._on_message, args=(message,))
|
|
92
|
+
thread = threading.Thread(target=self._on_message, args=(message,), daemon=True)
|
|
93
93
|
thread.start()
|
|
94
94
|
finally:
|
|
95
95
|
self._connection = None
|
|
@@ -118,7 +118,41 @@ class WebsocketLinkServer(WebsocketLinkBase):
|
|
|
118
118
|
self._send_loop.run_until_complete(start_websocket())
|
|
119
119
|
except Exception as e:
|
|
120
120
|
print("exception in _start_websocket_server", e)
|
|
121
|
-
|
|
121
|
+
finally:
|
|
122
|
+
pending = [
|
|
123
|
+
task for task in asyncio.all_tasks(self._send_loop)
|
|
124
|
+
if not task.done()
|
|
125
|
+
]
|
|
126
|
+
for task in pending:
|
|
127
|
+
task.cancel()
|
|
128
|
+
if pending:
|
|
129
|
+
self._send_loop.run_until_complete(
|
|
130
|
+
asyncio.gather(*pending, return_exceptions=True)
|
|
131
|
+
)
|
|
132
|
+
self._send_loop.run_until_complete(self._send_loop.shutdown_asyncgens())
|
|
133
|
+
self._send_loop.run_until_complete(self._send_loop.shutdown_default_executor())
|
|
134
|
+
self._send_loop.close()
|
|
122
135
|
|
|
123
136
|
def stop(self):
|
|
124
|
-
|
|
137
|
+
try:
|
|
138
|
+
if not self._stop.done():
|
|
139
|
+
self._send_loop.call_soon_threadsafe(self._stop.set_result, None)
|
|
140
|
+
except RuntimeError:
|
|
141
|
+
pass # Event loop already closed
|
|
142
|
+
if threading.current_thread() is not self._websocket_thread:
|
|
143
|
+
self._websocket_thread.join(timeout=2)
|
|
144
|
+
|
|
145
|
+
# Stop the callback event loop so the _callback_thread exits.
|
|
146
|
+
try:
|
|
147
|
+
self._callback_loop.call_soon_threadsafe(self._callback_loop.stop)
|
|
148
|
+
except RuntimeError:
|
|
149
|
+
pass # Event loop already closed
|
|
150
|
+
if threading.current_thread() is not self._callback_thread:
|
|
151
|
+
self._callback_thread.join(timeout=1)
|
|
152
|
+
|
|
153
|
+
# Unblock any threads stuck waiting for websocket RPC responses.
|
|
154
|
+
for rid, val in list(self._requests.items()):
|
|
155
|
+
if isinstance(val, tuple):
|
|
156
|
+
event, key = val
|
|
157
|
+
if isinstance(event, threading.Event):
|
|
158
|
+
event.set()
|
webgpu/platform.py
CHANGED
|
@@ -206,3 +206,32 @@ def init_pyodide(link_):
|
|
|
206
206
|
|
|
207
207
|
LinkBase.register_serializer(BaseWebGPUHandle, lambda _, v: v.handle)
|
|
208
208
|
LinkBase.register_serializer(BaseWebGPUObject, lambda _, v: v.__dict__ or None)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def reset():
|
|
212
|
+
"""Reset the platform globals so that init can be called again.
|
|
213
|
+
|
|
214
|
+
Used by test runners that start and stop multiple app instances
|
|
215
|
+
in the same process.
|
|
216
|
+
"""
|
|
217
|
+
global js, websocket_server, link, create_proxy, destroy_proxy
|
|
218
|
+
if websocket_server is not None:
|
|
219
|
+
try:
|
|
220
|
+
websocket_server.stop()
|
|
221
|
+
except RuntimeError:
|
|
222
|
+
pass # Event loop already closed
|
|
223
|
+
js = None
|
|
224
|
+
websocket_server = None
|
|
225
|
+
link = None
|
|
226
|
+
if not is_pyodide:
|
|
227
|
+
create_proxy = None
|
|
228
|
+
destroy_proxy = None
|
|
229
|
+
|
|
230
|
+
# Reset cached WebGPU device so it is re-requested on the new connection.
|
|
231
|
+
from . import utils as _utils
|
|
232
|
+
_utils._device = None
|
|
233
|
+
|
|
234
|
+
# Reset the cached font atlas — its GPU texture is tied to the old
|
|
235
|
+
# connection and would deadlock when accessed on a new one.
|
|
236
|
+
from . import font as _font
|
|
237
|
+
_font._default_font_atlas = None
|
webgpu/scene.py
CHANGED
|
@@ -97,7 +97,13 @@ class Scene:
|
|
|
97
97
|
self.options.timestamp = time.time()
|
|
98
98
|
self.options.update_buffers()
|
|
99
99
|
for obj in self.render_objects:
|
|
100
|
-
obj.
|
|
100
|
+
if not obj.active:
|
|
101
|
+
continue
|
|
102
|
+
try:
|
|
103
|
+
obj._update_and_create_render_pipeline(self.options)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f'Warning: failed to init renderer {type(obj).__name__}: {e}')
|
|
106
|
+
obj.active = False
|
|
101
107
|
|
|
102
108
|
camera = self.options.camera
|
|
103
109
|
self._js_render = platform.create_proxy(self._render_direct)
|
|
@@ -308,8 +314,11 @@ class Scene:
|
|
|
308
314
|
self.options.camera._render_function = None
|
|
309
315
|
self.options.camera._get_position_function = None
|
|
310
316
|
self.input_handler.unregister_callbacks()
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
self.canvas.
|
|
317
|
+
if hasattr(self, '_js_render'):
|
|
318
|
+
platform.destroy_proxy(self._js_render)
|
|
319
|
+
del self._js_render
|
|
320
|
+
if self.render in self.canvas._on_resize_callbacks:
|
|
321
|
+
self.canvas._on_resize_callbacks.remove(self.render)
|
|
322
|
+
if self.__on_update_html_canvas in self.canvas._on_update_html_canvas:
|
|
323
|
+
self.canvas._on_update_html_canvas.remove(self.__on_update_html_canvas)
|
|
315
324
|
self.canvas = None
|
webgpu/vectors.py
CHANGED
|
@@ -31,13 +31,8 @@ class BaseVectorRenderer(Renderer):
|
|
|
31
31
|
super().__init__(label=label)
|
|
32
32
|
self.gpu_objects.colormap = Colormap()
|
|
33
33
|
|
|
34
|
-
def update(self,
|
|
35
|
-
|
|
36
|
-
return
|
|
37
|
-
self._timestamp = timestamp
|
|
38
|
-
|
|
39
|
-
self.gpu_objects.colormap.options = self.options
|
|
40
|
-
self.gpu_objects.colormap.update(timestamp)
|
|
34
|
+
def update(self, options):
|
|
35
|
+
pass
|
|
41
36
|
|
|
42
37
|
def get_bindings(self):
|
|
43
38
|
return [
|
|
@@ -68,9 +63,7 @@ class VectorRenderer(BaseVectorRenderer):
|
|
|
68
63
|
)
|
|
69
64
|
self.size = size or 1 / 10 * np.linalg.norm(self.bounding_box[1] - self.bounding_box[0])
|
|
70
65
|
|
|
71
|
-
def update(self,
|
|
72
|
-
super().update(timestamp)
|
|
73
|
-
|
|
66
|
+
def update(self, options):
|
|
74
67
|
self._buffers = {
|
|
75
68
|
"points": buffer_from_array(self.points),
|
|
76
69
|
"vectors": buffer_from_array(self.vectors),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: webgpu
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2.dev0
|
|
4
4
|
Summary: A Python based WebGPU API using pyodide
|
|
5
5
|
Author-email: Matthias Hochsteger <mhochsteger@cerbsim.com>, Christopher Lackner <clackner@cerbsim.com>
|
|
6
6
|
License-Expression: LGPL-2.1-or-later
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
webgpu/__init__.py,sha256=h0nQ2lBaCOWz6UpveZuFnJophgAKbVFUsL2QO1_uoTw,302
|
|
2
|
-
webgpu/_version.py,sha256=
|
|
2
|
+
webgpu/_version.py,sha256=nSwG2ivutkcNO6WO5b1Wwu5l2YnLq5Jz8hN6lYWD6v0,541
|
|
3
3
|
webgpu/camera.py,sha256=jt4SNNtmZhy4PfbTlGvY30eEwy3wkckjmiTA0s8QrtE,11125
|
|
4
|
-
webgpu/canvas.py,sha256=
|
|
4
|
+
webgpu/canvas.py,sha256=GRQ96ENrFDyiTQQcX4_1Q-yeHWdn8hsocMbIeYOXd5g,12233
|
|
5
5
|
webgpu/clipping.py,sha256=RituZ-WJdUX7IubAhGORfQ5NM56QnsMau3G9ld2F6fI,4066
|
|
6
6
|
webgpu/colormap.py,sha256=FwpEZE1MU-ws8TndYpvFl_VaBR1U_bw6pbsYxaueSZM,13169
|
|
7
7
|
webgpu/draw.py,sha256=0JRBRf0HcA6O8yTGYXCnI8TvnhVMjn4VZ8mFQp9xO4M,555
|
|
@@ -13,21 +13,21 @@ webgpu/jupyter_pyodide.py,sha256=e7u8nvUwfHF6ME07RAiO8L-oto7owyBzwsa2RBJIh0I,969
|
|
|
13
13
|
webgpu/labels.py,sha256=OSYvBvjjXRIX4fPOcAtDt6Xk6GdI1ghekOLIIYxNIhg,4209
|
|
14
14
|
webgpu/light.py,sha256=sideBOi2vSWj1OJUvhCTG47N8S06GhkJAUBnfGGgaqA,2798
|
|
15
15
|
webgpu/lilgui.py,sha256=xLOrt3RZEMNXO4OgSnBcZZXelLRYdg3laKIMpqBHyIM,1979
|
|
16
|
-
webgpu/platform.py,sha256=
|
|
16
|
+
webgpu/platform.py,sha256=OaDxjAJaHEHZcd1DUYMxZDP2a72gnYZpQncym_RVLvI,6940
|
|
17
17
|
webgpu/renderer.py,sha256=6fwj9VpR22FrV2Ua3F5Etdt1zli8MeXHknFQfBUTdMA,12482
|
|
18
|
-
webgpu/scene.py,sha256=
|
|
18
|
+
webgpu/scene.py,sha256=Si_XpQiuuDv3h6bss75kAZnRNN30hgtEzyDkEvxgdd4,12170
|
|
19
19
|
webgpu/shapes.py,sha256=Yf-ca0iZm_cAb0l4T95ZM1JCB1CXoqkzFT8mQPxA0vM,15716
|
|
20
20
|
webgpu/testing.py,sha256=z25Q_mO-HzdedFDmd2JXHemK3Hl_shQQ42IBTRLKTR0,13014
|
|
21
21
|
webgpu/triangles.py,sha256=EVYU6kjz56LJCVEXeOE6ZEaa4oZ9RPeYdf_-hXMneoY,1865
|
|
22
22
|
webgpu/uniforms.py,sha256=5wm06tjZsEAtIxkdCe-5W0hQl3yFc6w4V-X_2SQIa1A,3031
|
|
23
23
|
webgpu/utils.py,sha256=pV9j8Os-XV79TPcKri_WQqUnv9ST_Dim__JoHzBKS6o,20038
|
|
24
|
-
webgpu/vectors.py,sha256=
|
|
24
|
+
webgpu/vectors.py,sha256=tjPijM9npqhz_Mu-2qzI_61ZQPnBC7nf8-t-Z9c2o_M,2738
|
|
25
25
|
webgpu/webgpu_api.py,sha256=EgudES5MxMWMZHKovF9-bnfNEWyCQPTQkXNiXFh6l9s,51491
|
|
26
26
|
webgpu/link/__init__.py,sha256=3i6KrP6iOJFJSm7_VUTdLV91A0h8qXNtuUiaEsxoS3I,107
|
|
27
|
-
webgpu/link/base.py,sha256=
|
|
27
|
+
webgpu/link/base.py,sha256=n_zd8rNvXKp_UY64S0X-haXKAS4jseVomBHBv-OE1fY,18722
|
|
28
28
|
webgpu/link/link.js,sha256=kN4csmhMvinnVwskITgOceQcvPpOVoqg6jEQD0hdD3A,19225
|
|
29
29
|
webgpu/link/proxy.py,sha256=FdnKKpudTJGjzchPuOkiDrx4wj5HosrUPGfZaw7rgOU,2519
|
|
30
|
-
webgpu/link/websocket.py,sha256=
|
|
30
|
+
webgpu/link/websocket.py,sha256=DfSfGSSDwTFGSbEAc9yTVRDOvkYwRZ8UIs_oGo7pY30,5337
|
|
31
31
|
webgpu/shaders/camera.wgsl,sha256=zryLSCd_rF04oC5WeNbNgETCzUM-40O6HAT_ksc9e2s,704
|
|
32
32
|
webgpu/shaders/clipping.wgsl,sha256=XiSITaxuJtNHUrJ-Z8qpXgtFS8K4u-NXiobhm0V5xkQ,1049
|
|
33
33
|
webgpu/shaders/colormap.wgsl,sha256=l6VEGD3ap-cB3168nENCNoypa0bMfPD8AnSXVstC_r0,2195
|
|
@@ -37,8 +37,8 @@ webgpu/shaders/shapes.wgsl,sha256=g6ZzqSP_ngfh575ed6s-D3BtGbdC53a0f7DZV90PC0k,29
|
|
|
37
37
|
webgpu/shaders/text.wgsl,sha256=25jld-LVAGIJbYiPIiSWWrzL_l2zWUHpu9a0C7_JvaA,2204
|
|
38
38
|
webgpu/shaders/triangulation.wgsl,sha256=EFMDTdvI7PJ1Fgrg4LQi5bUYRp-TLeZYhUDpkMDj6OA,1340
|
|
39
39
|
webgpu/shaders/vector.wgsl,sha256=SJL_d8fky_lzFOL-YyJoAqad5mbuBHEPnBW_0fRBLzo,3862
|
|
40
|
-
webgpu-1.2.
|
|
41
|
-
webgpu-1.2.
|
|
42
|
-
webgpu-1.2.
|
|
43
|
-
webgpu-1.2.
|
|
44
|
-
webgpu-1.2.
|
|
40
|
+
webgpu-1.2.2.dev0.dist-info/licenses/LICENSE,sha256=oZDcnIBDdV2Q-LCnX6ZrnkLUr0yYC_XdxjPwEk2zzuc,26430
|
|
41
|
+
webgpu-1.2.2.dev0.dist-info/METADATA,sha256=LRYVYTjQNOhnwcNnlSbgtdAOZV_F4DcflH1Fa2XeD0k,3477
|
|
42
|
+
webgpu-1.2.2.dev0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
43
|
+
webgpu-1.2.2.dev0.dist-info/top_level.txt,sha256=x6CotXenAnd3P6fHgyUW3ezc05V65HLlYqqMHbA7kYQ,7
|
|
44
|
+
webgpu-1.2.2.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|