1+ import contextlib
12import itertools
23from 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 )
0 commit comments