Skip to content

Commit 46c6f3a

Browse files
authored
Merge pull request #9 from CERBSim/render_pass_optimization
pack all renders in one render pass
2 parents 0b50886 + 519db11 commit 46c6f3a

3 files changed

Lines changed: 51 additions & 24 deletions

File tree

webgpu/renderer.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import itertools
23
from typing import Callable
34

@@ -73,12 +74,14 @@ class RenderOptions:
7374
command_encoder: CommandEncoder
7475
timestamp: float
7576
model_view_proj: object # numpy array, updated by update_buffers
77+
render_pass: object = None
7678

7779
def __init__(self, camera: Camera, light: Light):
7880
self.light = light
7981
self.camera = camera
8082
self._camera_uniforms = None
8183
self.model_view_proj = None
84+
self.render_pass = None
8285
self._extra_binding_providers = []
8386

8487
def __getstate__(self):
@@ -89,6 +92,7 @@ def __setstate__(self, state):
8992
self.light = state["light"]
9093
self._camera_uniforms = None
9194
self.model_view_proj = None
95+
self.render_pass = None
9296
self._extra_binding_providers = []
9397

9498
def add_bindings(self, provider):
@@ -138,6 +142,25 @@ def begin_render_pass(self, **kwargs):
138142

139143
return render_pass_encoder
140144

145+
@contextlib.contextmanager
146+
def render_pass_scope(self):
147+
"""Yield the render pass a renderer should record into.
148+
149+
When the Scene has opened a shared frame pass (``self.render_pass`` is
150+
set), reuse it and do NOT end it here — the Scene ends it once, after
151+
all objects have been recorded, so the MSAA target is resolved a single
152+
time per frame. When no shared pass is active (e.g. a renderer rendered
153+
in isolation), open and close a private pass for backwards behaviour.
154+
"""
155+
if self.render_pass is not None:
156+
yield self.render_pass
157+
else:
158+
render_pass = self.begin_render_pass()
159+
try:
160+
yield render_pass
161+
finally:
162+
render_pass.end()
163+
141164
def begin_select_pass(self, x, y, **kwargs):
142165
load_op = self.command_encoder.getLoadOp()
143166

@@ -500,13 +523,12 @@ def create_render_pipeline(self, options: RenderOptions) -> None:
500523
self._last_transparent = self.transparent
501524

502525
def render(self, options: RenderOptions) -> None:
503-
render_pass = options.begin_render_pass()
504-
render_pass.setPipeline(self.pipeline)
505-
render_pass.setBindGroup(0, self.group)
506-
for i, vertex_buffer in enumerate(self.vertex_buffers):
507-
render_pass.setVertexBuffer(i, vertex_buffer)
508-
render_pass.draw(self.n_vertices, self.n_instances)
509-
render_pass.end()
526+
with options.render_pass_scope() as render_pass:
527+
render_pass.setPipeline(self.pipeline)
528+
render_pass.setBindGroup(0, self.group)
529+
for i, vertex_buffer in enumerate(self.vertex_buffers):
530+
render_pass.setVertexBuffer(i, vertex_buffer)
531+
render_pass.draw(self.n_vertices, self.n_instances)
510532

511533
def render_opaque(self, options: RenderOptions) -> None:
512534
self.render(options)

webgpu/scene.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,18 @@ def _render_objects(self, to_canvas=True, update_pipelines=True):
425425
print("warning: object still needs update after update was done:", obj)
426426

427427
options.command_encoder = self.device.createCommandEncoder()
428-
for obj in self.render_objects:
429-
if obj.active:
430-
obj.render_opaque(options)
431-
for obj in self.render_objects:
432-
if obj.active:
433-
obj.render_transparent(options)
428+
render_pass = options.begin_render_pass()
429+
options.render_pass = render_pass
430+
try:
431+
for obj in self.render_objects:
432+
if obj.active:
433+
obj.render_opaque(options)
434+
for obj in self.render_objects:
435+
if obj.active:
436+
obj.render_transparent(options)
437+
finally:
438+
render_pass.end()
439+
options.render_pass = None
434440

435441
if to_canvas:
436442
target_texture = self.canvas.target_texture

webgpu/shapes.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -486,17 +486,16 @@ def get_shader_code(self) -> str:
486486
return read_shader_file("shapes.wgsl")
487487

488488
def render(self, options: RenderOptions) -> None:
489-
render_pass = options.begin_render_pass()
490-
render_pass.setPipeline(self.pipeline)
491-
render_pass.setBindGroup(0, self.group)
492-
for i, vertex_buffer in enumerate(self.vertex_buffers):
493-
render_pass.setVertexBuffer(i, vertex_buffer)
494-
render_pass.setIndexBuffer(self.triangle_buffer, IndexFormat.uint32)
495-
render_pass.drawIndexed(
496-
self.n_vertices,
497-
self.n_instances,
498-
)
499-
render_pass.end()
489+
with options.render_pass_scope() as render_pass:
490+
render_pass.setPipeline(self.pipeline)
491+
render_pass.setBindGroup(0, self.group)
492+
for i, vertex_buffer in enumerate(self.vertex_buffers):
493+
render_pass.setVertexBuffer(i, vertex_buffer)
494+
render_pass.setIndexBuffer(self.triangle_buffer, IndexFormat.uint32)
495+
render_pass.drawIndexed(
496+
self.n_vertices,
497+
self.n_instances,
498+
)
500499

501500
def select(self, options: RenderOptions, x, y) -> None:
502501
render_pass = options.begin_select_pass(x, y)

0 commit comments

Comments
 (0)