Skip to content

Commit 4d20aa6

Browse files
committed
fixes for clipping plane rendering in python render path (ngapp)
1 parent 068f11a commit 4d20aa6

1 file changed

Lines changed: 27 additions & 18 deletions

File tree

ngsolve_webgpu/clipping.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def __init__(
6767
self.cut_trigs = None
6868
self.trig_counter = None
6969
self._js_compute = False
70+
self._last_clipping_bytes = None
7071

7172
self.n_tets = None
7273
if component is None:
@@ -126,7 +127,11 @@ def update(self, options: RenderOptions):
126127
)
127128
self.gpu_objects.complex_settings.update(options)
128129
if not self._js_compute:
129-
self.build_clip_plane()
130+
# Only recompute clipping triangles when the plane actually changed
131+
clipping_bytes = bytes(self._clipping.uniforms)
132+
if clipping_bytes != self._last_clipping_bytes:
133+
self._last_clipping_bytes = clipping_bytes
134+
self.build_clip_plane()
130135
if self.symmetry:
131136
self.n_instances = self._original_n_instances * self.symmetry.n_copies
132137
self.shader_defines["SYMMETRY"] = "1"
@@ -288,6 +293,10 @@ def build_clip_plane(self):
288293
label="cut_trigs_counter",
289294
reuse=self.cut_trigs_counter,
290295
)
296+
# Explicitly reset counter to 0 — buffer_from_array's reuse optimization
297+
# skips the write when _data matches, but the GPU buffer was modified by
298+
# the previous fill pass's atomicAdd.
299+
write_array_to_buffer(self.trig_counter, np.array([0], dtype=np.uint32))
291300
shader_code = read_shader_file(self.compute_shader)
292301
run_compute_shader(
293302
shader_code, self.get_bindings(compute=True, count=True), 1024, "build_clip_plane", defines=self.data.mesh_data.get_shader_defines()
@@ -335,15 +344,13 @@ def get_export_compute_passes(self, options, buffer_registry):
335344
saved_clipping = self.clipping
336345
self.clipping = self._clipping
337346

338-
# Start with a minimal output buffer — the JS engine will resize
339-
# after the first count pass via the count_then_fill mechanism.
340-
# Live mode keeps Python's existing (correctly-sized) buffer because
341-
# the JS engine binds to the live proxy directly.
342-
min_size = 64 # minimum 1 SubTrig element
343-
if not buffer_registry.live and self.cut_trigs.size > min_size:
344-
self.cut_trigs.destroy()
347+
# The JS engine owns the output buffer (cut_trigs) and indirect buffer
348+
# entirely via the countThenFill protocol. Python only needs minimal
349+
# placeholders so the buffer registry can assign stable IDs.
350+
min_size = 64
351+
if self.cut_trigs is None or self.cut_trigs.size < min_size:
345352
self.cut_trigs = self.device.createBuffer(
346-
size=min_size, usage=BufferUsage.STORAGE, label="cut_trigs_export"
353+
size=min_size, usage=BufferUsage.STORAGE, label="cut_trigs"
347354
)
348355

349356
fill_bindings = self.get_bindings(compute=True, count=False)
@@ -355,16 +362,18 @@ def get_export_compute_passes(self, options, buffer_registry):
355362
trig_counter_id = buffer_registry.get_id(self.trig_counter)
356363
cut_trigs_id = buffer_registry.get_id(self.cut_trigs)
357364

358-
# Create indirect args buffer
359-
indirect_data = np.array([self.n_vertices, 0, 0, 0], dtype=np.uint32)
360-
self._indirect_buffer = buffer_from_array(
361-
indirect_data,
362-
usage=BufferUsage.INDIRECT | BufferUsage.STORAGE | BufferUsage.COPY_DST,
363-
label="clip_indirect",
364-
)
365+
# Indirect buffer: also JS-owned. Create a minimal placeholder for
366+
# the registry ID; the JS engine creates the real one in initPipelines.
367+
if not hasattr(self, '_indirect_buffer') or self._indirect_buffer is None:
368+
self._indirect_buffer = self.device.createBuffer(
369+
size=16,
370+
usage=BufferUsage.INDIRECT | BufferUsage.STORAGE | BufferUsage.COPY_DST,
371+
label="clip_indirect",
372+
)
365373
indirect_buf_id = buffer_registry._next_id("indirect")
366-
buffer_registry._buffers[id(self._indirect_buffer)] = (indirect_buf_id, self._indirect_buffer, "indirect")
367-
374+
buffer_registry._buffers[id(self._indirect_buffer)] = (
375+
indirect_buf_id, self._indirect_buffer, "indirect"
376+
)
368377
self._export_indirect_buf_id = indirect_buf_id
369378

370379
fill_pass_id = f"clip_fill_{self._id}"

0 commit comments

Comments
 (0)