@@ -10,7 +10,7 @@ use bevy_render::{
1010 render_graph:: { NodeRunError , RenderGraphContext } ,
1111 render_phase:: { TrackedRenderPass , ViewBinnedRenderPhases } ,
1212 render_resource:: { CommandEncoderDescriptor , RenderPassDescriptor , StoreOp } ,
13- renderer:: RenderContext ,
13+ renderer:: { CurrentView , RenderContext , RenderContextParam } ,
1414 view:: ViewDepthTexture ,
1515} ;
1616use tracing:: error;
@@ -271,3 +271,249 @@ fn run_deferred_prepass<'w>(
271271
272272 Ok ( ( ) )
273273}
274+
275+ // =============================================================================
276+ // Schedule-based systems (new approach)
277+ // =============================================================================
278+
279+ /// Type alias for the deferred prepass view query.
280+ type DeferredPrepassViewQuery = (
281+ & ' static ExtractedCamera ,
282+ & ' static ExtractedView ,
283+ & ' static ViewDepthTexture ,
284+ & ' static ViewPrepassTextures ,
285+ Option < & ' static MainPassResolutionOverride > ,
286+ Has < OcclusionCulling > ,
287+ Has < NoIndirectDrawing > ,
288+ ) ;
289+
290+ /// Render system for the early deferred G-buffer prepass.
291+ ///
292+ /// This is the schedule-based replacement for [`EarlyDeferredGBufferPrepassNode`].
293+ /// It draws meshes that were visible last frame to the G-buffer.
294+ /// If occlusion culling isn't in use, this simply draws all meshes.
295+ pub fn early_deferred_prepass (
296+ world : & World ,
297+ view : CurrentView ,
298+ view_query : Query < DeferredPrepassViewQuery > ,
299+ opaque_deferred_phases : Res < ViewBinnedRenderPhases < Opaque3dDeferred > > ,
300+ alpha_mask_deferred_phases : Res < ViewBinnedRenderPhases < AlphaMask3dDeferred > > ,
301+ mut ctx : RenderContextParam ,
302+ ) {
303+ let view_entity = view. entity ( ) ;
304+ let Ok ( ( camera, extracted_view, view_depth_texture, view_prepass_textures, resolution_override, _, _) ) =
305+ view_query. get ( view_entity)
306+ else {
307+ return ;
308+ } ;
309+
310+ run_deferred_prepass_system (
311+ world,
312+ view_entity,
313+ camera,
314+ extracted_view,
315+ view_depth_texture,
316+ view_prepass_textures,
317+ resolution_override,
318+ false ,
319+ & opaque_deferred_phases,
320+ & alpha_mask_deferred_phases,
321+ & mut ctx,
322+ "early deferred prepass" ,
323+ ) ;
324+ }
325+
326+ /// Render system for the late deferred G-buffer prepass.
327+ ///
328+ /// This is the schedule-based replacement for [`LateDeferredGBufferPrepassNode`].
329+ /// It runs after occlusion culling against meshes that were visible last frame.
330+ /// If occlusion culling isn't in use, this is a no-op.
331+ pub fn late_deferred_prepass (
332+ world : & World ,
333+ view : CurrentView ,
334+ view_query : Query < DeferredPrepassViewQuery > ,
335+ opaque_deferred_phases : Res < ViewBinnedRenderPhases < Opaque3dDeferred > > ,
336+ alpha_mask_deferred_phases : Res < ViewBinnedRenderPhases < AlphaMask3dDeferred > > ,
337+ mut ctx : RenderContextParam ,
338+ ) {
339+ let view_entity = view. entity ( ) ;
340+ let Ok ( (
341+ camera,
342+ extracted_view,
343+ view_depth_texture,
344+ view_prepass_textures,
345+ resolution_override,
346+ occlusion_culling,
347+ no_indirect_drawing,
348+ ) ) = view_query. get ( view_entity)
349+ else {
350+ return ;
351+ } ;
352+
353+ // We only need a late prepass if we have occlusion culling and indirect drawing
354+ if !occlusion_culling || no_indirect_drawing {
355+ return ;
356+ }
357+
358+ run_deferred_prepass_system (
359+ world,
360+ view_entity,
361+ camera,
362+ extracted_view,
363+ view_depth_texture,
364+ view_prepass_textures,
365+ resolution_override,
366+ true ,
367+ & opaque_deferred_phases,
368+ & alpha_mask_deferred_phases,
369+ & mut ctx,
370+ "late deferred prepass" ,
371+ ) ;
372+ }
373+
374+ /// Shared implementation for deferred prepass systems.
375+ #[ allow( clippy:: too_many_arguments) ]
376+ fn run_deferred_prepass_system (
377+ world : & World ,
378+ view_entity : Entity ,
379+ camera : & ExtractedCamera ,
380+ extracted_view : & ExtractedView ,
381+ view_depth_texture : & ViewDepthTexture ,
382+ view_prepass_textures : & ViewPrepassTextures ,
383+ resolution_override : Option < & MainPassResolutionOverride > ,
384+ is_late : bool ,
385+ opaque_deferred_phases : & ViewBinnedRenderPhases < Opaque3dDeferred > ,
386+ alpha_mask_deferred_phases : & ViewBinnedRenderPhases < AlphaMask3dDeferred > ,
387+ ctx : & mut RenderContextParam ,
388+ label : & ' static str ,
389+ ) {
390+ let ( Some ( opaque_deferred_phase) , Some ( alpha_mask_deferred_phase) ) = (
391+ opaque_deferred_phases. get ( & extracted_view. retained_view_entity ) ,
392+ alpha_mask_deferred_phases. get ( & extracted_view. retained_view_entity ) ,
393+ ) else {
394+ return ;
395+ } ;
396+
397+ #[ cfg( feature = "trace" ) ]
398+ let _deferred_span = info_span ! ( "deferred_prepass" ) . entered ( ) ;
399+
400+ let mut color_attachments = vec ! [ ] ;
401+ color_attachments. push (
402+ view_prepass_textures
403+ . normal
404+ . as_ref ( )
405+ . map ( |normals_texture| normals_texture. get_attachment ( ) ) ,
406+ ) ;
407+ color_attachments. push (
408+ view_prepass_textures
409+ . motion_vectors
410+ . as_ref ( )
411+ . map ( |motion_vectors_texture| motion_vectors_texture. get_attachment ( ) ) ,
412+ ) ;
413+
414+ // If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
415+ // Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
416+ // Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
417+ // Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
418+ // For webgl2 we fallback to manually clearing
419+ #[ cfg( all( feature = "webgl" , target_arch = "wasm32" , not( feature = "webgpu" ) ) ) ]
420+ if !is_late {
421+ if let Some ( deferred_texture) = & view_prepass_textures. deferred {
422+ ctx. command_encoder ( ) . clear_texture (
423+ & deferred_texture. texture . texture ,
424+ & bevy_render:: render_resource:: ImageSubresourceRange :: default ( ) ,
425+ ) ;
426+ }
427+ }
428+
429+ color_attachments. push (
430+ view_prepass_textures
431+ . deferred
432+ . as_ref ( )
433+ . map ( |deferred_texture| {
434+ if is_late {
435+ deferred_texture. get_attachment ( )
436+ } else {
437+ #[ cfg( all( feature = "webgl" , target_arch = "wasm32" , not( feature = "webgpu" ) ) ) ]
438+ {
439+ bevy_render:: render_resource:: RenderPassColorAttachment {
440+ view : & deferred_texture. texture . default_view ,
441+ resolve_target : None ,
442+ ops : bevy_render:: render_resource:: Operations {
443+ load : bevy_render:: render_resource:: LoadOp :: Load ,
444+ store : StoreOp :: Store ,
445+ } ,
446+ depth_slice : None ,
447+ }
448+ }
449+ #[ cfg( any(
450+ not( feature = "webgl" ) ,
451+ not( target_arch = "wasm32" ) ,
452+ feature = "webgpu"
453+ ) ) ]
454+ deferred_texture. get_attachment ( )
455+ }
456+ } ) ,
457+ ) ;
458+
459+ color_attachments. push (
460+ view_prepass_textures
461+ . deferred_lighting_pass_id
462+ . as_ref ( )
463+ . map ( |deferred_lighting_pass_id| deferred_lighting_pass_id. get_attachment ( ) ) ,
464+ ) ;
465+
466+ // If all color attachments are none: clear the color attachment list so that no fragment shader is required
467+ if color_attachments. iter ( ) . all ( Option :: is_none) {
468+ color_attachments. clear ( ) ;
469+ }
470+
471+ let depth_stencil_attachment = Some ( view_depth_texture. get_attachment ( StoreOp :: Store ) ) ;
472+
473+ let mut render_pass = ctx. begin_tracked_render_pass ( RenderPassDescriptor {
474+ label : Some ( label) ,
475+ color_attachments : & color_attachments,
476+ depth_stencil_attachment,
477+ timestamp_writes : None ,
478+ occlusion_query_set : None ,
479+ } ) ;
480+
481+ if let Some ( viewport) =
482+ Viewport :: from_viewport_and_override ( camera. viewport . as_ref ( ) , resolution_override)
483+ {
484+ render_pass. set_camera_viewport ( & viewport) ;
485+ }
486+
487+ // Opaque draws
488+ if !opaque_deferred_phase. multidrawable_meshes . is_empty ( )
489+ || !opaque_deferred_phase. batchable_meshes . is_empty ( )
490+ || !opaque_deferred_phase. unbatchable_meshes . is_empty ( )
491+ {
492+ #[ cfg( feature = "trace" ) ]
493+ let _opaque_prepass_span = info_span ! ( "opaque_deferred_prepass" ) . entered ( ) ;
494+ if let Err ( err) = opaque_deferred_phase. render ( & mut render_pass, world, view_entity) {
495+ error ! ( "Error encountered while rendering the opaque deferred phase {err:?}" ) ;
496+ }
497+ }
498+
499+ // Alpha masked draws
500+ if !alpha_mask_deferred_phase. is_empty ( ) {
501+ #[ cfg( feature = "trace" ) ]
502+ let _alpha_mask_deferred_span = info_span ! ( "alpha_mask_deferred_prepass" ) . entered ( ) ;
503+ if let Err ( err) = alpha_mask_deferred_phase. render ( & mut render_pass, world, view_entity) {
504+ error ! ( "Error encountered while rendering the alpha mask deferred phase {err:?}" ) ;
505+ }
506+ }
507+
508+ // Drop the render pass before the texture copy
509+ drop ( render_pass) ;
510+
511+ // After rendering to the view depth texture, copy it to the prepass depth texture
512+ if let Some ( prepass_depth_texture) = & view_prepass_textures. depth {
513+ ctx. command_encoder ( ) . copy_texture_to_texture (
514+ view_depth_texture. texture . as_image_copy ( ) ,
515+ prepass_depth_texture. texture . texture . as_image_copy ( ) ,
516+ view_prepass_textures. size ,
517+ ) ;
518+ }
519+ }
0 commit comments