Skip to main content

astrelis_render/batched/
mod.rs

1//! Batched deferred indirect renderer.
2//!
3//! Provides three runtime-selected render backends behind a unified [`BatchRenderer2D`] trait:
4//!
5//! | Tier | Name | Draw Strategy |
6//! |------|------|---------------|
7//! | 1 | Direct | Per-texture `draw()` calls |
8//! | 2 | Indirect | `multi_draw_indirect()` per texture group |
9//! | 3 | Bindless | Single `multi_draw_indirect()` per frame |
10//!
11//! All tiers share a unified [`UnifiedInstance2D`] format and a single shader pipeline.
12
13#[allow(dead_code)]
14mod bindless;
15pub mod capability;
16#[allow(dead_code)]
17mod direct;
18#[allow(dead_code)]
19mod indirect;
20#[allow(dead_code)]
21mod pipeline;
22#[allow(dead_code)]
23mod texture_array;
24mod traits;
25mod types;
26
27pub use capability::{
28    BestBatchCapability2D, BindlessBatchCapability2D, DirectBatchCapability2D,
29    IndirectBatchCapability2D,
30};
31pub use traits::*;
32pub use types::*;
33
34use std::sync::Arc;
35
36use crate::context::GraphicsContext;
37use crate::features::GpuFeatures;
38
39/// Maximum number of textures in the bindless binding array (Tier 3).
40const BINDLESS_MAX_TEXTURES: u32 = 256;
41
42/// Detect the highest supported render tier for the given context.
43pub fn detect_render_tier(context: &GraphicsContext) -> RenderTier {
44    let features = context.gpu_features();
45
46    let has_indirect_first_instance = features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE);
47    let has_texture_binding_array = features.contains(GpuFeatures::TEXTURE_BINDING_ARRAY);
48    let has_partially_bound = features.contains(GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY);
49    let has_non_uniform_indexing = features
50        .contains(GpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING);
51
52    if has_indirect_first_instance
53        && has_texture_binding_array
54        && has_partially_bound
55        && has_non_uniform_indexing
56    {
57        RenderTier::Bindless
58    } else if has_indirect_first_instance {
59        RenderTier::Indirect
60    } else {
61        RenderTier::Direct
62    }
63}
64
65/// Create a 2D batch renderer for the given context.
66///
67/// Automatically selects the highest supported tier unless `tier_override` is specified.
68/// The `surface_format` is used for pipeline color target configuration.
69pub fn create_batch_renderer_2d(
70    context: Arc<GraphicsContext>,
71    surface_format: wgpu::TextureFormat,
72    tier_override: Option<RenderTier>,
73) -> Box<dyn BatchRenderer2D> {
74    let tier = tier_override.unwrap_or_else(|| detect_render_tier(&context));
75
76    tracing::info!("Creating batch renderer 2D: {tier}");
77
78    match tier {
79        RenderTier::Direct => Box::new(direct::DirectBatchRenderer2D::new(context, surface_format)),
80        RenderTier::Indirect => Box::new(indirect::IndirectBatchRenderer2D::new(
81            context,
82            surface_format,
83        )),
84        RenderTier::Bindless => Box::new(bindless::BindlessBatchRenderer2D::new(
85            context,
86            surface_format,
87        )),
88    }
89}