22//! (`Depth32Float`, Greater), the scene's materials, textures, lights and shadow maps in
33//! storage buffers, and `advanced3d.wgsl`'s physically based fragment shader. Opaque triangles
44//! are drawn with depth writes, then the sorted transparent ones blended over (premultiplied).
5- //! Colour (`Rgba16Float`) and camera depth (`R32Float` ) stay on the GPU for `adv3d.wgsl`'s
5+ //! Colour (`Rgba16Float`) and camera depth (`R32Uint` holding f32 bits ) stay on the GPU for `adv3d.wgsl`'s
66//! compute kernels, which run the rest of `effectcraft_render::three_d::adv::render_prepared`:
77//! the 2×2 supersampling resolve, the motion-blur sub-sample average (nearest depth), the
88//! depth-based iris depth of field (the Classic 3D bokeh kernel's row spans and prefix-sum
@@ -22,20 +22,44 @@ use crate::context::{Enc, GpuContext, GpuImage};
2222/// Pipelines (built on first use).
2323pub ( crate ) struct Pipes {
2424 bgl : wgpu:: BindGroupLayout ,
25- opaque : wgpu:: RenderPipeline ,
26- transparent : wgpu:: RenderPipeline ,
25+ /// (opaque, transparent); `None` when the adapter can't rasterise ([`raster_unsupported`]):
26+ /// scenes then render on the CPU.
27+ raster : Option < ( wgpu:: RenderPipeline , wgpu:: RenderPipeline ) > ,
2728 post : Post ,
2829}
2930
3031fn pipes ( g : & GpuContext ) -> & Pipes {
31- g. adv3d . get_or_init ( || Pipes :: new ( & g. device ) )
32+ g. adv3d . get_or_init ( || Pipes :: new ( & g. device , g . adv3d_raster ) )
3233}
3334
3435const COLOR : wgpu:: TextureFormat = wgpu:: TextureFormat :: Rgba16Float ;
35- const DEPTH_OUT : wgpu:: TextureFormat = wgpu:: TextureFormat :: R32Float ;
36+ /// Camera depth as the bits of an f32: integer targets render on every backend, while `R32Float`
37+ /// needs `EXT_color_buffer_float` on GLES (absent on Mesa's llvmpipe, for one).
38+ const DEPTH_OUT : wgpu:: TextureFormat = wgpu:: TextureFormat :: R32Uint ;
39+ /// [`DEPTH_OUT`]'s clear value: the bits of −1.0 ("nothing drawn").
40+ const NO_DEPTH : f64 = 0xBF80_0000u32 as f64 ;
3641const DEPTH : wgpu:: TextureFormat = wgpu:: TextureFormat :: Depth32Float ;
3742const VERTEX_SIZE : u64 = 52 ;
3843
44+ /// Why `adapter` can't run the rasteriser, or `None` when it can. Building the pipelines anyway
45+ /// would be a validation error, which release builds only log (and then draw nothing), so the
46+ /// caller keeps the scenes on the CPU instead.
47+ pub ( crate ) fn raster_unsupported ( adapter : & wgpu:: Adapter ) -> Option < String > {
48+ use wgpu:: TextureFormatFeatureFlags as F ;
49+ let needs = [ ( COLOR , F :: BLENDABLE ) , ( DEPTH_OUT , F :: empty ( ) ) , ( DEPTH , F :: empty ( ) ) ] ;
50+ for ( format, flags) in needs {
51+ let f = adapter. get_texture_format_features ( format) ;
52+ if !f. allowed_usages . contains ( wgpu:: TextureUsages :: RENDER_ATTACHMENT ) || !f. flags . contains ( flags) {
53+ return Some ( format ! ( "{format:?} is not a {}render target" , if flags. is_empty( ) { "" } else { "blendable " } ) ) ;
54+ }
55+ }
56+ // The transparent pipeline blends colour and masks the depth target off.
57+ if !adapter. get_downlevel_capabilities ( ) . flags . contains ( wgpu:: DownlevelFlags :: INDEPENDENT_BLEND ) {
58+ return Some ( "no independent blending" . into ( ) ) ;
59+ }
60+ None
61+ }
62+
3963fn storage ( binding : u32 ) -> wgpu:: BindGroupLayoutEntry {
4064 wgpu:: BindGroupLayoutEntry {
4165 binding,
@@ -46,7 +70,7 @@ fn storage(binding: u32) -> wgpu::BindGroupLayoutEntry {
4670}
4771
4872impl Pipes {
49- fn new ( device : & wgpu:: Device ) -> Pipes {
73+ fn new ( device : & wgpu:: Device , raster : bool ) -> Pipes {
5074 let module = device. create_shader_module ( wgpu:: ShaderModuleDescriptor {
5175 label : Some ( "effectcraft advanced 3d" ) ,
5276 source : wgpu:: ShaderSource :: Wgsl ( include_str ! ( "shaders/advanced3d.wgsl" ) . into ( ) ) ,
@@ -110,7 +134,7 @@ impl Pipes {
110134 cache : None ,
111135 } )
112136 } ;
113- Pipes { opaque : make ( false ) , transparent : make ( true ) , bgl, post : Post :: new ( device) }
137+ Pipes { raster : raster . then ( || ( make ( false ) , make ( true ) ) ) , bgl, post : Post :: new ( device) }
114138 }
115139}
116140
@@ -222,9 +246,9 @@ fn half_to_f32(h: u16) -> f32 {
222246 }
223247}
224248
225- /// Record the render pass of one scene into `e`: (colour `Rgba16Float`, camera depth `R32Float `
249+ /// Record the render pass of one scene into `e`: (colour `Rgba16Float`, camera depth `R32Uint `
226250/// with −1 where nothing was drawn) at the scene's raster size. `None` when the device can't
227- /// (size or buffer limits).
251+ /// (render targets, size or buffer limits).
228252fn raster_into ( e : & mut Enc , s : & Scene ) -> Option < ( wgpu:: Texture , wgpu:: Texture ) > {
229253 let g = e. g ;
230254 if !g. fits ( s. width , s. height ) || s. indices . is_empty ( ) {
@@ -236,6 +260,7 @@ fn raster_into(e: &mut Enc, s: &Scene) -> Option<(wgpu::Texture, wgpu::Texture)>
236260 return None ;
237261 }
238262 let p = pipes ( g) ;
263+ let Some ( ( opaque, transparent) ) = & p. raster else { return None } ;
239264 let dev = & g. device ;
240265 let vb: Vec < u8 > = s
241266 . vertices
@@ -294,7 +319,7 @@ fn raster_into(e: &mut Enc, s: &Scene) -> Option<(wgpu::Texture, wgpu::Texture)>
294319 view : & zv,
295320 depth_slice : None ,
296321 resolve_target : None ,
297- ops : wgpu:: Operations { load : wgpu:: LoadOp :: Clear ( wgpu:: Color { r : - 1.0 , g : 0.0 , b : 0.0 , a : 0.0 } ) , store : wgpu:: StoreOp :: Store } ,
322+ ops : wgpu:: Operations { load : wgpu:: LoadOp :: Clear ( wgpu:: Color { r : NO_DEPTH , g : 0.0 , b : 0.0 , a : 0.0 } ) , store : wgpu:: StoreOp :: Store } ,
298323 } ) ,
299324 ] ,
300325 depth_stencil_attachment : Some ( wgpu:: RenderPassDepthStencilAttachment {
@@ -311,11 +336,11 @@ fn raster_into(e: &mut Enc, s: &Scene) -> Option<(wgpu::Texture, wgpu::Texture)>
311336 pass. set_index_buffer ( ibuf. slice ( ..) , wgpu:: IndexFormat :: Uint32 ) ;
312337 let n = s. indices . len ( ) as u32 ;
313338 if s. opaque_count > 0 {
314- pass. set_pipeline ( & p . opaque ) ;
339+ pass. set_pipeline ( opaque) ;
315340 pass. draw_indexed ( 0 ..s. opaque_count , 0 , 0 ..1 ) ;
316341 }
317342 if n > s. opaque_count {
318- pass. set_pipeline ( & p . transparent ) ;
343+ pass. set_pipeline ( transparent) ;
319344 pass. draw_indexed ( s. opaque_count ..n, 0 , 0 ..1 ) ;
320345 }
321346 }
@@ -424,11 +449,11 @@ impl Post {
424449 label : Some ( "effectcraft advanced 3d post" ) ,
425450 source : wgpu:: ShaderSource :: Wgsl ( include_str ! ( "shaders/adv3d.wgsl" ) . into ( ) ) ,
426451 } ) ;
427- let tex = |binding| wgpu:: BindGroupLayoutEntry {
452+ let tex = |binding, sample_type | wgpu:: BindGroupLayoutEntry {
428453 binding,
429454 visibility : wgpu:: ShaderStages :: COMPUTE ,
430455 ty : wgpu:: BindingType :: Texture {
431- sample_type : wgpu :: TextureSampleType :: Float { filterable : false } ,
456+ sample_type,
432457 view_dimension : wgpu:: TextureViewDimension :: D2 ,
433458 multisampled : false ,
434459 } ,
@@ -447,8 +472,8 @@ impl Post {
447472 ty : wgpu:: BindingType :: Buffer { ty : wgpu:: BufferBindingType :: Uniform , has_dynamic_offset : false , min_binding_size : None } ,
448473 count : None ,
449474 } ,
450- tex ( 1 ) ,
451- tex ( 2 ) ,
475+ tex ( 1 , wgpu :: TextureSampleType :: Float { filterable : false } ) ,
476+ tex ( 2 , wgpu :: TextureSampleType :: Uint ) ,
452477 buf ( 3 , false ) ,
453478 buf ( 4 , false ) ,
454479 buf ( 5 , false ) ,
0 commit comments