Skip to content

Commit dba36ba

Browse files
committed
fix select under load
1 parent 19cab0b commit dba36ba

1 file changed

Lines changed: 43 additions & 1 deletion

File tree

webgpu/scene.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22
import os
33
import pathlib
4+
import threading
45
from base64 import b64decode
56

67
from . import platform
@@ -71,6 +72,9 @@ def __init__(
7172
self._js_engine = None
7273
self._on_event_proxy = None
7374
self._on_camera_changed_proxy = None
75+
self._select_lock = threading.Lock()
76+
self._pending_select = None
77+
self._select_running = False
7478
self._use_js_engine = (
7579
_default_use_js_engine if use_js_engine is None else bool(use_js_engine)
7680
)
@@ -101,6 +105,9 @@ def __setstate__(self, state):
101105
self._js_engine = None
102106
self._on_event_proxy = None
103107
self._on_camera_changed_proxy = None
108+
self._select_lock = threading.Lock()
109+
self._pending_select = None
110+
self._select_running = False
104111
self._use_js_engine = state.get("use_js_engine", _default_use_js_engine)
105112
self._show_gui_controls = state.get("show_gui_controls", True)
106113

@@ -434,8 +441,43 @@ def get_position(self, x: int, y: int):
434441
return p
435442
return None
436443

437-
@debounce
438444
def select(self, x: int, y: int):
445+
"""Queue an object selection at (x, y).
446+
447+
A single worker processes only the most recent request, so a backlog
448+
(e.g. hover moves piled up while Python was busy) collapses to the last
449+
position instead of replaying every queued move. Never runs two selects
450+
concurrently.
451+
"""
452+
if self._render_mutex is None:
453+
return
454+
if self.canvas is None or self.canvas.height == 0:
455+
return
456+
# Pyodide: JS is only reachable from this thread — run synchronously.
457+
if is_pyodide:
458+
self._do_select(int(x), int(y))
459+
return
460+
with self._select_lock:
461+
self._pending_select = (int(x), int(y))
462+
if self._select_running:
463+
return
464+
self._select_running = True
465+
threading.Thread(target=self._select_worker, daemon=True).start()
466+
467+
def _select_worker(self):
468+
while True:
469+
with self._select_lock:
470+
pending = self._pending_select
471+
self._pending_select = None
472+
if pending is None:
473+
self._select_running = False
474+
return
475+
try:
476+
self._do_select(*pending)
477+
except Exception as e:
478+
print(f"warning: select failed: {e}")
479+
480+
def _do_select(self, x: int, y: int):
439481
"""Perform an object selection at (x, y) and dispatch callbacks on matching renderers."""
440482
if self._render_mutex is None:
441483
return

0 commit comments

Comments
 (0)