Skip to main content

GraphicsContext

Struct GraphicsContext 

Source
pub struct GraphicsContext { /* private fields */ }
Expand description

A globally shared graphics context.

§Ownership Pattern

This type uses Arc for shared ownership:

use astrelis_render::GraphicsContext;
use std::sync::Arc;

// Synchronous creation (blocks on async internally)
let ctx = GraphicsContext::new_owned_sync()
    .expect("Failed to create graphics context"); // Returns Arc<Self>
let ctx2 = ctx.clone(); // Cheap clone (Arc)

// Asynchronous creation (for async contexts)
let ctx = GraphicsContext::new_owned().await; // Returns Arc<Self>

Benefits of the Arc pattern:

  • No memory leak
  • Proper cleanup on drop
  • Better for testing (can create/destroy contexts)
  • Arc internally makes cloning cheap

Implementations§

Source§

impl GraphicsContext

Source

pub async fn new_owned() -> Result<Arc<Self>, GraphicsError>

Creates a new graphics context with owned ownership (recommended).

Returns Arc<Self> which can be cheaply cloned and shared. This is the preferred method for new code as it doesn’t leak memory.

§Example
use astrelis_render::GraphicsContext;

let ctx = GraphicsContext::new_owned().await;
let ctx2 = ctx.clone(); // Cheap clone
Source

pub fn new_owned_sync() -> Result<Arc<Self>, GraphicsError>

Creates a new graphics context synchronously with owned ownership (recommended).

Warning: This blocks the current thread until the context is created. For async contexts, use new_owned() instead.

§Errors

Returns GraphicsError if:

  • No suitable GPU adapter is found
  • Required GPU features are not supported
  • Device creation fails
§Example
use astrelis_render::GraphicsContext;

// For examples/tests: use .expect() for simplicity
let ctx = GraphicsContext::new_owned_sync()
    .expect("Failed to create graphics context");

// For production: handle the error properly
let ctx = match GraphicsContext::new_owned_sync() {
    Ok(ctx) => ctx,
    Err(e) => {
        eprintln!("GPU initialization failed: {:?}", e);
        return;
    }
};
Examples found in repository?
examples/window_manager_demo.rs (line 43)
38fn main() {
39    logging::init();
40
41    run_app(|ctx| {
42        let graphics_ctx =
43            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
44        let mut window_manager = WindowManager::new(graphics_ctx);
45        let mut window_colors = HashMap::new();
46
47        // Create 3 windows with different colors
48        let colors = [
49            Color::rgb(0.8, 0.2, 0.2), // Red
50            Color::rgb(0.2, 0.8, 0.2), // Green
51            Color::rgb(0.2, 0.2, 0.8), // Blue
52        ];
53
54        for (i, color) in colors.iter().enumerate() {
55            let window_id = window_manager
56                .create_window_with_descriptor(
57                    ctx,
58                    WindowDescriptor {
59                        title: format!("Window {} - WindowManager Demo", i + 1),
60                        size: Some(WinitPhysicalSize::new(400.0, 300.0)),
61                        ..Default::default()
62                    },
63                    WindowContextDescriptor {
64                        format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
65                        ..Default::default()
66                    },
67                )
68                .expect("Failed to create window");
69
70            window_colors.insert(window_id, *color);
71        }
72
73        Box::new(WindowManagerApp {
74            window_manager,
75            window_colors,
76        })
77    });
78}
More examples
Hide additional examples
examples/multi_window.rs (line 30)
25fn main() {
26    logging::init();
27
28    run_app(|ctx| {
29        let graphics_ctx =
30            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
31
32        let mut windows = HashMap::new();
33
34        // Create 3 windows with different colors
35        let colors = [
36            wgpu::Color {
37                r: 0.8,
38                g: 0.2,
39                b: 0.2,
40                a: 1.0,
41            },
42            wgpu::Color {
43                r: 0.2,
44                g: 0.8,
45                b: 0.2,
46                a: 1.0,
47            },
48            wgpu::Color {
49                r: 0.2,
50                g: 0.2,
51                b: 0.8,
52                a: 1.0,
53            },
54        ];
55
56        for (i, color) in colors.iter().enumerate() {
57            let window = ctx
58                .create_window(WindowDescriptor {
59                    title: format!("Window {} - Multi-Window Example", i + 1),
60                    size: Some(WinitPhysicalSize::new(400.0, 300.0)),
61                    ..Default::default()
62                })
63                .expect("Failed to create window");
64
65            let renderable_window = RenderWindowBuilder::new()
66                .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
67                .with_depth_default()
68                .build(window, graphics_ctx.clone())
69                .expect("Failed to create render window");
70
71            let window_id = renderable_window.id();
72            windows.insert(window_id, (renderable_window, *color));
73        }
74
75        Box::new(App { windows })
76    });
77}
examples/performance_benchmark.rs (line 41)
36fn main() {
37    logging::init();
38
39    run_app(|ctx| {
40        let graphics_ctx =
41            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
42
43        let window = ctx
44            .create_window(WindowDescriptor {
45                title: "Performance Benchmark - Render Stress Test".to_string(),
46                size: Some(WinitPhysicalSize::new(1280.0, 720.0)),
47                ..Default::default()
48            })
49            .expect("Failed to create window");
50
51        let window = RenderWindowBuilder::new()
52            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
53            .with_depth_default()
54            .build(window, graphics_ctx.clone())
55            .expect("Failed to create render window");
56
57        let window_id = window.id();
58
59        println!("\n═══════════════════════════════════════════════════════");
60        println!("  ⚡ PERFORMANCE BENCHMARK - Render Stress Test");
61        println!("═══════════════════════════════════════════════════════");
62        println!("  CONTROLS:");
63        println!("    [Space]  Toggle rendering on/off");
64        println!("    [+/-]    Increase/decrease object count");
65        println!("  Starting with 1000 objects");
66        println!("═══════════════════════════════════════════════════════\n");
67
68        Box::new(PerformanceBenchmark {
69            _context: graphics_ctx,
70            window,
71            window_id,
72            object_count: 1000,
73            rendering: true,
74            frame_count: 0,
75            last_fps_time: Instant::now(),
76            fps: 0.0,
77            last_frame_time: 0.0,
78        })
79    });
80}
examples/camera_demo.rs (line 35)
30fn main() {
31    logging::init();
32
33    run_app(|ctx| {
34        let graphics_ctx =
35            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
36
37        let window = ctx
38            .create_window(WindowDescriptor {
39                title: "Camera Demo - View & Projection".to_string(),
40                size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
41                ..Default::default()
42            })
43            .expect("Failed to create window");
44
45        let window = RenderWindowBuilder::new()
46            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
47            .with_depth_default()
48            .build(window, graphics_ctx.clone())
49            .expect("Failed to create render window");
50
51        let window_id = window.id();
52
53        println!("\n═══════════════════════════════════════════════════════");
54        println!("  📹 CAMERA DEMO - View & Projection");
55        println!("═══════════════════════════════════════════════════════");
56        println!("\n  CAMERA API FEATURES:");
57        println!("    • Orthographic cameras (2D, UI, isometric)");
58        println!("    • Perspective cameras (3D scenes)");
59        println!("    • View matrix (position, rotation, look-at)");
60        println!("    • Projection matrix (FOV, aspect, near/far planes)");
61        println!("    • Screen-to-world coordinate conversion");
62        println!("    • Camera movement helpers");
63        println!("\n  CAMERA TYPES:");
64        println!("    • OrthographicCamera - 2D games, UI overlays");
65        println!("      camera.orthographic(left, right, bottom, top, near, far)");
66        println!("    • PerspectiveCamera - 3D scenes");
67        println!("      camera.perspective(fov, aspect, near, far)");
68        println!("\n  Camera API Usage:");
69        println!("    let camera = Camera::new()");
70        println!("        .position(Vec3::new(0.0, 5.0, 10.0))");
71        println!("        .look_at(Vec3::ZERO)");
72        println!("        .perspective(60.0, aspect, 0.1, 100.0);");
73        println!("    let view_proj = camera.view_projection_matrix();");
74        println!("═══════════════════════════════════════════════════════\n");
75
76        tracing::info!("Camera demo initialized");
77
78        Box::new(CameraDemo {
79            _context: graphics_ctx,
80            window,
81            window_id,
82        })
83    });
84}
examples/render_graph_demo.rs (line 34)
29fn main() {
30    logging::init();
31
32    run_app(|ctx| {
33        let graphics_ctx =
34            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
35
36        let window = ctx
37            .create_window(WindowDescriptor {
38                title: "Render Graph Demo - Multi-Pass Rendering".to_string(),
39                size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
40                ..Default::default()
41            })
42            .expect("Failed to create window");
43
44        let window = RenderWindowBuilder::new()
45            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
46            .with_depth_default()
47            .build(window, graphics_ctx.clone())
48            .expect("Failed to create render window");
49
50        let window_id = window.id();
51
52        println!("\n═══════════════════════════════════════════════════════");
53        println!("  🔀 RENDER GRAPH DEMO - Multi-Pass Rendering");
54        println!("═══════════════════════════════════════════════════════");
55        println!("\n  RENDER GRAPH FEATURES:");
56        println!("    • Declarative pass definition");
57        println!("    • Automatic dependency resolution");
58        println!("    • Resource lifetime management");
59        println!("    • Parallel pass execution");
60        println!("    • Automatic optimization");
61        println!("\n  EXAMPLE PIPELINE:");
62        println!("    1. Shadow Pass → depth texture");
63        println!("    2. Geometry Pass → color + normal + depth");
64        println!("    3. Lighting Pass → lit scene");
65        println!("    4. Post-Processing → bloom, tone mapping");
66        println!("    5. UI Pass → final composite");
67        println!("\n  Render Graph API Usage:");
68        println!("    let mut graph = RenderGraph::new();");
69        println!("    graph.add_pass(\"shadow\", shadow_pass_descriptor);");
70        println!("    graph.add_pass(\"geometry\", geometry_pass_descriptor);");
71        println!("    graph.add_dependency(\"lighting\", \"geometry\");");
72        println!("    graph.compile();");
73        println!("    graph.execute(&mut encoder);");
74        println!("═══════════════════════════════════════════════════════\n");
75
76        tracing::info!("Render graph demo initialized");
77
78        Box::new(RenderGraphDemo {
79            _context: graphics_ctx,
80            window,
81            window_id,
82        })
83    });
84}
examples/mesh_primitives.rs (line 34)
29fn main() {
30    logging::init();
31
32    run_app(|ctx| {
33        let graphics_ctx =
34            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
35
36        let window = ctx
37            .create_window(WindowDescriptor {
38                title: "Mesh Primitives Demo - Geometry API".to_string(),
39                size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
40                ..Default::default()
41            })
42            .expect("Failed to create window");
43
44        let window = RenderWindowBuilder::new()
45            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
46            .with_depth_default()
47            .build(window, graphics_ctx.clone())
48            .expect("Failed to create render window");
49
50        let window_id = window.id();
51
52        println!("\n═══════════════════════════════════════════════════════");
53        println!("  📐 MESH PRIMITIVES DEMO - Geometry API");
54        println!("═══════════════════════════════════════════════════════");
55        println!("\n  MESH API FEATURES:");
56        println!("    • MeshBuilder for custom geometry");
57        println!("    • Primitive generation (cube, sphere, plane, etc.)");
58        println!("    • Flexible vertex formats (Position, Normal, UV, Color)");
59        println!("    • Index buffer optimization");
60        println!("    • Instanced rendering support");
61        println!("\n  EXAMPLE PRIMITIVES:");
62        println!("    • Cube - box with 24 vertices (6 faces × 4 vertices)");
63        println!("    • Sphere - tessellated sphere with UV mapping");
64        println!("    • Plane - quad with optional subdivisions");
65        println!("    • Cylinder - sides + caps");
66        println!("    • Custom - arbitrary vertex/index data");
67        println!("\n  Mesh API Usage:");
68        println!("    let mesh = MeshBuilder::new()");
69        println!("        .with_positions(vertices)");
70        println!("        .with_normals(normals)");
71        println!("        .with_uvs(uvs)");
72        println!("        .with_indices(indices)");
73        println!("        .build(&ctx);");
74        println!("    mesh.draw(&mut pass);");
75        println!("    mesh.draw_instanced(&mut pass, instance_count);");
76        println!("═══════════════════════════════════════════════════════\n");
77
78        tracing::info!("Mesh primitives demo initialized");
79
80        Box::new(MeshPrimitivesDemo {
81            _context: graphics_ctx,
82            window,
83            window_id,
84        })
85    });
86}
Source

pub async fn new_owned_with_descriptor( descriptor: GraphicsContextDescriptor, ) -> Result<Arc<Self>, GraphicsError>

Creates a new graphics context with custom descriptor (owned).

Examples found in repository?
examples/batched_renderer.rs (line 300)
278fn main() {
279    logging::init();
280
281    run_app(|ctx| {
282        let tier_override = parse_tier();
283
284        // Use the capability API to configure GPU requirements.
285        // For auto-detect, request the best capability (graceful degradation).
286        // For a specific tier, require that tier's capability.
287        let descriptor = match tier_override {
288            None => GraphicsContextDescriptor::new().request_capability::<BestBatchCapability2D>(),
289            Some(RenderTier::Direct) => {
290                GraphicsContextDescriptor::new().require_capability::<DirectBatchCapability2D>()
291            }
292            Some(RenderTier::Indirect) => {
293                GraphicsContextDescriptor::new().require_capability::<IndirectBatchCapability2D>()
294            }
295            Some(RenderTier::Bindless) => {
296                GraphicsContextDescriptor::new().require_capability::<BindlessBatchCapability2D>()
297            }
298        };
299        let graphics_ctx =
300            pollster::block_on(GraphicsContext::new_owned_with_descriptor(descriptor))
301                .expect("Failed to create graphics context");
302
303        let window = ctx
304            .create_window(WindowDescriptor {
305                title: "Batched Renderer Example".to_string(),
306                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
307                ..Default::default()
308            })
309            .expect("Failed to create window");
310
311        let surface_format = wgpu::TextureFormat::Bgra8UnormSrgb;
312
313        let renderable_window = RenderWindowBuilder::new()
314            .color_format(surface_format)
315            .with_depth_default()
316            .build(window, graphics_ctx.clone())
317            .expect("Failed to create render window");
318
319        let window_id = renderable_window.id();
320
321        let renderer =
322            create_batch_renderer_2d(graphics_ctx.clone(), surface_format, tier_override);
323
324        tracing::info!("Using render tier: {}", renderer.tier());
325
326        // Create initial depth buffer
327        let depth_texture = graphics_ctx
328            .device()
329            .create_texture(&wgpu::TextureDescriptor {
330                label: Some("example_depth"),
331                size: wgpu::Extent3d {
332                    width: 1,
333                    height: 1,
334                    depth_or_array_layers: 1,
335                },
336                mip_level_count: 1,
337                sample_count: 1,
338                dimension: wgpu::TextureDimension::D2,
339                format: DEPTH_FORMAT,
340                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
341                view_formats: &[],
342            });
343        let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
344
345        let mut windows = HashMap::new();
346        windows.insert(window_id, renderable_window);
347
348        Box::new(App {
349            context: graphics_ctx,
350            windows,
351            renderer,
352            depth_texture,
353            depth_view,
354            depth_width: 1,
355            depth_height: 1,
356            frame_count: 0,
357        })
358    });
359}
More examples
Hide additional examples
examples/profiling_demo.rs (lines 48-51)
40fn main() {
41    logging::init();
42    init_profiling(ProfilingBackend::PuffinHttp);
43
44    run_app(|ctx| {
45        profile_function!();
46
47        // Request TIMESTAMP_QUERY for GPU profiling (best-effort, won't fail if unavailable)
48        let graphics_ctx = pollster::block_on(GraphicsContext::new_owned_with_descriptor(
49            GraphicsContextDescriptor::new()
50                .request_capability::<astrelis_render::gpu_profiling::GpuFrameProfiler>(),
51        ))
52        .expect("Failed to create graphics context");
53
54        let window = ctx
55            .create_window(WindowDescriptor {
56                title: "Profiling Demo — CPU + GPU".to_string(),
57                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
58                ..Default::default()
59            })
60            .expect("Failed to create window");
61
62        #[allow(unused_mut)]
63        let mut window = RenderWindowBuilder::new()
64            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
65            .with_depth_default()
66            .build(window, graphics_ctx.clone())
67            .expect("Failed to create render window");
68
69        let window_id = window.id();
70
71        // Attach GPU profiler to the window — all frames will be automatically profiled
72        let has_gpu_profiling;
73        #[cfg(feature = "gpu-profiling")]
74        {
75            match astrelis_render::gpu_profiling::GpuFrameProfiler::new(&graphics_ctx) {
76                Ok(profiler) => {
77                    let has_timestamps = profiler.has_timestamp_queries();
78                    window.set_gpu_profiler(Arc::new(profiler));
79                    has_gpu_profiling = true;
80                    if has_timestamps {
81                        println!("  GPU profiling: enabled with TIMESTAMP_QUERY (full timing)");
82                    } else {
83                        println!("  GPU profiling: enabled (debug groups only, no timing data)");
84                        println!("                 TIMESTAMP_QUERY not supported by this GPU");
85                    }
86                }
87                Err(e) => {
88                    has_gpu_profiling = false;
89                    tracing::warn!("Failed to create GPU profiler: {e}. GPU profiling disabled.");
90                    println!("  GPU profiling: failed to create profiler");
91                }
92            }
93        }
94        #[cfg(not(feature = "gpu-profiling"))]
95        {
96            has_gpu_profiling = false;
97        }
98
99        println!();
100        println!("═══════════════════════════════════════════════════");
101        println!("  PROFILING DEMO — CPU + GPU");
102        println!("═══════════════════════════════════════════════════");
103        println!();
104        println!("  CPU profiling: enabled (puffin)");
105        if !has_gpu_profiling {
106            #[cfg(not(feature = "gpu-profiling"))]
107            println!("  GPU profiling: disabled (compile with --features gpu-profiling)");
108        }
109        println!();
110        println!("  Open puffin_viewer at 127.0.0.1:8585 to see the flame graph.");
111        println!("  Install with: cargo install puffin_viewer");
112        println!("═══════════════════════════════════════════════════");
113        println!();
114
115        Box::new(ProfilingDemo {
116            context: graphics_ctx,
117            window,
118            window_id,
119            frame_count: 0,
120        })
121    });
122}
Source

pub fn device(&self) -> &Device

Get a reference to the wgpu device.

Examples found in repository?
examples/batched_renderer.rs (line 56)
48    fn ensure_depth_buffer(&mut self, width: u32, height: u32) {
49        if self.depth_width == width && self.depth_height == height {
50            return;
51        }
52        let w = width.max(1);
53        let h = height.max(1);
54        let texture = self
55            .context
56            .device()
57            .create_texture(&wgpu::TextureDescriptor {
58                label: Some("example_depth"),
59                size: wgpu::Extent3d {
60                    width: w,
61                    height: h,
62                    depth_or_array_layers: 1,
63                },
64                mip_level_count: 1,
65                sample_count: 1,
66                dimension: wgpu::TextureDimension::D2,
67                format: DEPTH_FORMAT,
68                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
69                view_formats: &[],
70            });
71        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
72        self.depth_texture = texture;
73        self.depth_view = view;
74        self.depth_width = w;
75        self.depth_height = h;
76    }
77
78    /// Build an orthographic projection matrix for the given viewport size.
79    /// Maps (0,0) at top-left to (width, height) at bottom-right.
80    /// Z range: 0.0 (far) to 1.0 (near), matching GreaterEqual depth compare.
81    fn ortho_projection(width: f32, height: f32) -> [[f32; 4]; 4] {
82        [
83            [2.0 / width, 0.0, 0.0, 0.0],
84            [0.0, -2.0 / height, 0.0, 0.0],
85            [0.0, 0.0, 1.0, 0.0],
86            [-1.0, 1.0, 0.0, 1.0],
87        ]
88    }
89
90    /// Generate the demo instances for the current frame.
91    fn build_instances(&self, width: f32, height: f32) -> Vec<UnifiedInstance2D> {
92        let t = self.frame_count as f32 / 60.0;
93        let mut instances = Vec::new();
94
95        // --- Background panel (gray, full viewport, furthest back) ---
96        instances.push(UnifiedInstance2D {
97            position: [10.0, 10.0],
98            size: [width - 20.0, height - 20.0],
99            color: [0.15, 0.15, 0.18, 1.0],
100            border_radius: 12.0,
101            z_depth: 0.01,
102            draw_type: DrawType2D::Quad as u32,
103            ..Default::default()
104        });
105
106        // --- Grid of colored quads ---
107        let cols = 5;
108        let rows = 3;
109        let margin = 30.0;
110        let gap = 10.0;
111        let cell_w = (width - 2.0 * margin - (cols as f32 - 1.0) * gap) / cols as f32;
112        let cell_h = (height * 0.5 - margin - (rows as f32 - 1.0) * gap) / rows as f32;
113
114        for row in 0..rows {
115            for col in 0..cols {
116                let x = margin + col as f32 * (cell_w + gap);
117                let y = margin + row as f32 * (cell_h + gap);
118                let idx = row * cols + col;
119
120                // Hue-shift color based on grid position
121                let hue = (idx as f32 / (rows * cols) as f32) * 360.0;
122                let (r, g, b) = hsl_to_rgb(hue, 0.7, 0.55);
123
124                instances.push(UnifiedInstance2D {
125                    position: [x, y],
126                    size: [cell_w, cell_h],
127                    color: [r, g, b, 1.0],
128                    border_radius: 6.0,
129                    z_depth: 0.1 + idx as f32 * 0.001,
130                    draw_type: DrawType2D::Quad as u32,
131                    ..Default::default()
132                });
133            }
134        }
135
136        // --- Animated floating rounded rect ---
137        let float_x = width * 0.5 + (t * 0.8).sin() * width * 0.25 - 60.0;
138        let float_y = height * 0.35 + (t * 1.2).cos() * 30.0;
139        instances.push(UnifiedInstance2D {
140            position: [float_x, float_y],
141            size: [120.0, 50.0],
142            color: [1.0, 0.85, 0.2, 0.9],
143            border_radius: 25.0,
144            z_depth: 0.8,
145            draw_type: DrawType2D::Quad as u32,
146            ..Default::default()
147        });
148
149        // --- Border-only outlines (bottom area) ---
150        let outline_y = height * 0.6;
151        for i in 0..4 {
152            let x = margin + i as f32 * 140.0;
153            let thickness = 1.0 + i as f32;
154            let radius = 4.0 + i as f32 * 8.0;
155            instances.push(UnifiedInstance2D {
156                position: [x, outline_y],
157                size: [120.0, 80.0],
158                color: [0.4, 0.8, 1.0, 1.0],
159                border_radius: radius,
160                border_thickness: thickness,
161                z_depth: 0.5,
162                draw_type: DrawType2D::Quad as u32,
163                ..Default::default()
164            });
165        }
166
167        // --- Overlapping transparent quads (demonstrating depth + alpha) ---
168        let overlap_x = width * 0.5 - 100.0;
169        let overlap_y = height * 0.75;
170        let colors = [
171            [1.0, 0.3, 0.3, 0.6],
172            [0.3, 1.0, 0.3, 0.6],
173            [0.3, 0.3, 1.0, 0.6],
174        ];
175        for (i, color) in colors.iter().enumerate() {
176            let offset = i as f32 * 40.0;
177            instances.push(UnifiedInstance2D {
178                position: [overlap_x + offset, overlap_y + offset * 0.5],
179                size: [120.0, 80.0],
180                color: *color,
181                border_radius: 8.0,
182                z_depth: 0.6 + i as f32 * 0.05,
183                draw_type: DrawType2D::Quad as u32,
184                ..Default::default()
185            });
186        }
187
188        // --- Pulsing circle (via large border_radius) ---
189        let pulse = ((t * 2.0).sin() * 0.5 + 0.5) * 0.4 + 0.6;
190        let circle_size = 60.0 * pulse;
191        instances.push(UnifiedInstance2D {
192            position: [width - margin - circle_size, outline_y + 10.0],
193            size: [circle_size, circle_size],
194            color: [1.0, 0.5, 0.0, 0.95],
195            border_radius: circle_size * 0.5,
196            z_depth: 0.7,
197            draw_type: DrawType2D::Quad as u32,
198            ..Default::default()
199        });
200
201        // --- Small shader-clipped quad (demonstrating clip rect) ---
202        let clip_x = margin;
203        let clip_y = height * 0.75;
204        instances.push(UnifiedInstance2D {
205            position: [clip_x, clip_y],
206            size: [200.0, 60.0],
207            color: [0.9, 0.2, 0.7, 1.0],
208            border_radius: 4.0,
209            z_depth: 0.55,
210            draw_type: DrawType2D::Quad as u32,
211            // Clip to a smaller region to demonstrate shader clipping
212            clip_min: [clip_x + 20.0, clip_y + 10.0],
213            clip_max: [clip_x + 160.0, clip_y + 50.0],
214            ..Default::default()
215        });
216
217        instances
218    }
219}
220
221/// Simple HSL to RGB conversion.
222fn hsl_to_rgb(h: f32, s: f32, l: f32) -> (f32, f32, f32) {
223    let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
224    let h_prime = h / 60.0;
225    let x = c * (1.0 - (h_prime % 2.0 - 1.0).abs());
226    let (r1, g1, b1) = if h_prime < 1.0 {
227        (c, x, 0.0)
228    } else if h_prime < 2.0 {
229        (x, c, 0.0)
230    } else if h_prime < 3.0 {
231        (0.0, c, x)
232    } else if h_prime < 4.0 {
233        (0.0, x, c)
234    } else if h_prime < 5.0 {
235        (x, 0.0, c)
236    } else {
237        (c, 0.0, x)
238    };
239    let m = l - c * 0.5;
240    (r1 + m, g1 + m, b1 + m)
241}
242
243/// Parse the `--tier` CLI argument to select a render tier.
244///
245/// The three tiers represent increasing levels of GPU capability:
246/// - **Tier 1 (Direct):** One `draw()` call per texture group. Works on all hardware.
247/// - **Tier 2 (Indirect):** Uses `multi_draw_indirect()` per texture group, batching
248///   draw calls into GPU-side indirect buffers. Requires `MULTI_DRAW_INDIRECT`.
249/// - **Tier 3 (Bindless):** Single `multi_draw_indirect()` per frame using texture
250///   binding arrays. Requires `TEXTURE_BINDING_ARRAY` + `MULTI_DRAW_INDIRECT`.
251///
252/// Passing `--tier auto` (or omitting the flag) lets the engine choose the best
253/// tier supported by the current GPU.
254fn parse_tier() -> Option<RenderTier> {
255    let args: Vec<String> = std::env::args().collect();
256    for (i, arg) in args.iter().enumerate() {
257        if arg == "--tier"
258            && let Some(value) = args.get(i + 1)
259        {
260            return match value.as_str() {
261                "1" | "direct" => Some(RenderTier::Direct),
262                "2" | "indirect" => Some(RenderTier::Indirect),
263                "3" | "bindless" => Some(RenderTier::Bindless),
264                "auto" => None,
265                other => {
266                    eprintln!(
267                        "Unknown tier '{other}'. Options: 1|direct, 2|indirect, 3|bindless, auto"
268                    );
269                    std::process::exit(1);
270                }
271            };
272        }
273    }
274    // Default: auto-detect
275    None
276}
277
278fn main() {
279    logging::init();
280
281    run_app(|ctx| {
282        let tier_override = parse_tier();
283
284        // Use the capability API to configure GPU requirements.
285        // For auto-detect, request the best capability (graceful degradation).
286        // For a specific tier, require that tier's capability.
287        let descriptor = match tier_override {
288            None => GraphicsContextDescriptor::new().request_capability::<BestBatchCapability2D>(),
289            Some(RenderTier::Direct) => {
290                GraphicsContextDescriptor::new().require_capability::<DirectBatchCapability2D>()
291            }
292            Some(RenderTier::Indirect) => {
293                GraphicsContextDescriptor::new().require_capability::<IndirectBatchCapability2D>()
294            }
295            Some(RenderTier::Bindless) => {
296                GraphicsContextDescriptor::new().require_capability::<BindlessBatchCapability2D>()
297            }
298        };
299        let graphics_ctx =
300            pollster::block_on(GraphicsContext::new_owned_with_descriptor(descriptor))
301                .expect("Failed to create graphics context");
302
303        let window = ctx
304            .create_window(WindowDescriptor {
305                title: "Batched Renderer Example".to_string(),
306                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
307                ..Default::default()
308            })
309            .expect("Failed to create window");
310
311        let surface_format = wgpu::TextureFormat::Bgra8UnormSrgb;
312
313        let renderable_window = RenderWindowBuilder::new()
314            .color_format(surface_format)
315            .with_depth_default()
316            .build(window, graphics_ctx.clone())
317            .expect("Failed to create render window");
318
319        let window_id = renderable_window.id();
320
321        let renderer =
322            create_batch_renderer_2d(graphics_ctx.clone(), surface_format, tier_override);
323
324        tracing::info!("Using render tier: {}", renderer.tier());
325
326        // Create initial depth buffer
327        let depth_texture = graphics_ctx
328            .device()
329            .create_texture(&wgpu::TextureDescriptor {
330                label: Some("example_depth"),
331                size: wgpu::Extent3d {
332                    width: 1,
333                    height: 1,
334                    depth_or_array_layers: 1,
335                },
336                mip_level_count: 1,
337                sample_count: 1,
338                dimension: wgpu::TextureDimension::D2,
339                format: DEPTH_FORMAT,
340                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
341                view_formats: &[],
342            });
343        let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
344
345        let mut windows = HashMap::new();
346        windows.insert(window_id, renderable_window);
347
348        Box::new(App {
349            context: graphics_ctx,
350            windows,
351            renderer,
352            depth_texture,
353            depth_view,
354            depth_width: 1,
355            depth_height: 1,
356            frame_count: 0,
357        })
358    });
359}
More examples
Hide additional examples
examples/sprite_sheet.rs (line 199)
152fn main() {
153    logging::init();
154
155    run_app(|ctx| {
156        let graphics_ctx =
157            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
158        let mut windows = HashMap::new();
159
160        let scale = Window::platform_dpi() as f32;
161        let window = ctx
162            .create_window(WindowDescriptor {
163                title: "Sprite Sheet Animation Example".to_string(),
164                size: Some(WinitPhysicalSize::new(400.0 * scale, 400.0 * scale)),
165                ..Default::default()
166            })
167            .expect("Failed to create window");
168
169        let renderable_window = RenderWindowBuilder::new()
170            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
171            .with_depth_default()
172            .build(window, graphics_ctx.clone())
173            .expect("Failed to create render window");
174
175        let window_id = renderable_window.id();
176        windows.insert(window_id, renderable_window);
177
178        // Generate sprite sheet
179        let (sprite_data, tex_width, tex_height) = generate_sprite_sheet_data();
180        let sprite_sheet = SpriteSheet::from_data(
181            &graphics_ctx,
182            &sprite_data,
183            tex_width,
184            tex_height,
185            SpriteSheetDescriptor {
186                sprite_width: 64,
187                sprite_height: 64,
188                columns: 4,
189                rows: 1,
190                ..Default::default()
191            },
192        );
193
194        // Create animation (4 frames at 8 fps)
195        let animation = SpriteAnimation::new(4, 8.0);
196
197        // Create shader module
198        let shader = graphics_ctx
199            .device()
200            .create_shader_module(wgpu::ShaderModuleDescriptor {
201                label: Some("Sprite Shader"),
202                source: wgpu::ShaderSource::Wgsl(SHADER.into()),
203            });
204
205        // Create bind group layout
206        let bind_group_layout =
207            graphics_ctx
208                .device()
209                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
210                    label: Some("Sprite Bind Group Layout"),
211                    entries: &[
212                        wgpu::BindGroupLayoutEntry {
213                            binding: 0,
214                            visibility: wgpu::ShaderStages::VERTEX,
215                            ty: wgpu::BindingType::Buffer {
216                                ty: wgpu::BufferBindingType::Uniform,
217                                has_dynamic_offset: false,
218                                min_binding_size: None,
219                            },
220                            count: None,
221                        },
222                        wgpu::BindGroupLayoutEntry {
223                            binding: 1,
224                            visibility: wgpu::ShaderStages::FRAGMENT,
225                            ty: wgpu::BindingType::Texture {
226                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
227                                view_dimension: wgpu::TextureViewDimension::D2,
228                                multisampled: false,
229                            },
230                            count: None,
231                        },
232                        wgpu::BindGroupLayoutEntry {
233                            binding: 2,
234                            visibility: wgpu::ShaderStages::FRAGMENT,
235                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
236                            count: None,
237                        },
238                    ],
239                });
240
241        // Create pipeline layout
242        let pipeline_layout =
243            graphics_ctx
244                .device()
245                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
246                    label: Some("Sprite Pipeline Layout"),
247                    bind_group_layouts: &[&bind_group_layout],
248                    push_constant_ranges: &[],
249                });
250
251        // Create render pipeline
252        let pipeline =
253            graphics_ctx
254                .device()
255                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
256                    label: Some("Sprite Pipeline"),
257                    layout: Some(&pipeline_layout),
258                    vertex: wgpu::VertexState {
259                        module: &shader,
260                        entry_point: Some("vs_main"),
261                        buffers: &[wgpu::VertexBufferLayout {
262                            array_stride: std::mem::size_of::<Vertex>() as u64,
263                            step_mode: wgpu::VertexStepMode::Vertex,
264                            attributes: &[
265                                wgpu::VertexAttribute {
266                                    offset: 0,
267                                    shader_location: 0,
268                                    format: wgpu::VertexFormat::Float32x2,
269                                },
270                                wgpu::VertexAttribute {
271                                    offset: 8,
272                                    shader_location: 1,
273                                    format: wgpu::VertexFormat::Float32x2,
274                                },
275                            ],
276                        }],
277                        compilation_options: wgpu::PipelineCompilationOptions::default(),
278                    },
279                    fragment: Some(wgpu::FragmentState {
280                        module: &shader,
281                        entry_point: Some("fs_main"),
282                        targets: &[Some(wgpu::ColorTargetState {
283                            format: wgpu::TextureFormat::Bgra8UnormSrgb,
284                            blend: Some(wgpu::BlendState::ALPHA_BLENDING),
285                            write_mask: wgpu::ColorWrites::ALL,
286                        })],
287                        compilation_options: wgpu::PipelineCompilationOptions::default(),
288                    }),
289                    primitive: wgpu::PrimitiveState {
290                        topology: wgpu::PrimitiveTopology::TriangleList,
291                        ..Default::default()
292                    },
293                    depth_stencil: None,
294                    multisample: wgpu::MultisampleState::default(),
295                    multiview: None,
296                    cache: None,
297                });
298
299        // Create uniform buffer
300        let uniforms = Uniforms {
301            mvp: [
302                [1.0, 0.0, 0.0, 0.0],
303                [0.0, 1.0, 0.0, 0.0],
304                [0.0, 0.0, 1.0, 0.0],
305                [0.0, 0.0, 0.0, 1.0],
306            ],
307        };
308        let uniform_buffer =
309            graphics_ctx
310                .device()
311                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
312                    label: Some("Uniform Buffer"),
313                    contents: bytemuck::cast_slice(&[uniforms]),
314                    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
315                });
316
317        // Create sampler
318        let sampler = graphics_ctx
319            .device()
320            .create_sampler(&wgpu::SamplerDescriptor {
321                label: Some("Sprite Sampler"),
322                mag_filter: wgpu::FilterMode::Linear,
323                min_filter: wgpu::FilterMode::Linear,
324                ..Default::default()
325            });
326
327        // Create bind group
328        let bind_group = graphics_ctx
329            .device()
330            .create_bind_group(&wgpu::BindGroupDescriptor {
331                label: Some("Sprite Bind Group"),
332                layout: &bind_group_layout,
333                entries: &[
334                    wgpu::BindGroupEntry {
335                        binding: 0,
336                        resource: uniform_buffer.as_entire_binding(),
337                    },
338                    wgpu::BindGroupEntry {
339                        binding: 1,
340                        resource: wgpu::BindingResource::TextureView(sprite_sheet.view()),
341                    },
342                    wgpu::BindGroupEntry {
343                        binding: 2,
344                        resource: wgpu::BindingResource::Sampler(&sampler),
345                    },
346                ],
347            });
348
349        // Initial vertex buffer (will be updated each frame with new UVs)
350        let vertices = create_quad_vertices(0.0, 0.0, 1.0, 1.0);
351        let vertex_buffer =
352            graphics_ctx
353                .device()
354                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
355                    label: Some("Vertex Buffer"),
356                    contents: bytemuck::cast_slice(&vertices),
357                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
358                });
359
360        Box::new(App {
361            _context: graphics_ctx,
362            windows,
363            pipeline,
364            bind_group,
365            vertex_buffer,
366            _uniform_buffer: uniform_buffer,
367            sprite_sheet,
368            animation,
369            last_update: Instant::now(),
370        })
371    });
372}
examples/textured_window.rs (line 44)
22fn main() {
23    logging::init();
24
25    run_app(|ctx| {
26        let graphics_ctx =
27            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
28
29        let window = ctx
30            .create_window(WindowDescriptor {
31                title: "Textured Window".to_string(),
32                ..Default::default()
33            })
34            .expect("Failed to create window");
35
36        let window = RenderWindowBuilder::new()
37            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
38            .with_depth_default()
39            .build(window, graphics_ctx.clone())
40            .expect("Failed to create render window");
41
42        // --- Shader ---
43        let shader = graphics_ctx
44            .device()
45            .create_shader_module(wgpu::ShaderModuleDescriptor {
46                label: Some("Texture Shader"),
47                source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
48            });
49
50        // --- Texture Upload ---
51        // Create a 256x256 RGBA texture with a procedural gradient pattern.
52        // Rgba8UnormSrgb applies sRGB gamma correction so colors appear correct on screen.
53        let texture_size = wgpu::Extent3d {
54            width: 256,
55            height: 256,
56            depth_or_array_layers: 1,
57        };
58
59        let texture = graphics_ctx
60            .device()
61            .create_texture(&wgpu::TextureDescriptor {
62                label: Some("Example Texture"),
63                size: texture_size,
64                mip_level_count: 1,
65                sample_count: 1,
66                dimension: wgpu::TextureDimension::D2,
67                format: wgpu::TextureFormat::Rgba8UnormSrgb,
68                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
69                view_formats: &[],
70            });
71
72        let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
73        for y in 0..256 {
74            for x in 0..256 {
75                let idx = ((y * 256 + x) * 4) as usize;
76                texture_data[idx] = x as u8;
77                texture_data[idx + 1] = y as u8;
78                texture_data[idx + 2] = ((x + y) / 2) as u8;
79                texture_data[idx + 3] = 255;
80            }
81        }
82
83        graphics_ctx.queue().write_texture(
84            wgpu::TexelCopyTextureInfo {
85                texture: &texture,
86                mip_level: 0,
87                origin: wgpu::Origin3d::ZERO,
88                aspect: wgpu::TextureAspect::All,
89            },
90            &texture_data,
91            wgpu::TexelCopyBufferLayout {
92                offset: 0,
93                bytes_per_row: Some(256 * 4),
94                rows_per_image: Some(256),
95            },
96            texture_size,
97        );
98
99        // --- Sampler ---
100        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
101        // ClampToEdge prevents sampling beyond texture edges; Linear mag filter
102        // gives smooth results when the quad is larger than the texture.
103        let sampler = graphics_ctx
104            .device()
105            .create_sampler(&wgpu::SamplerDescriptor {
106                address_mode_u: wgpu::AddressMode::ClampToEdge,
107                address_mode_v: wgpu::AddressMode::ClampToEdge,
108                address_mode_w: wgpu::AddressMode::ClampToEdge,
109                mag_filter: wgpu::FilterMode::Linear,
110                min_filter: wgpu::FilterMode::Nearest,
111                mipmap_filter: wgpu::FilterMode::Nearest,
112                ..Default::default()
113            });
114
115        // --- Bind Group ---
116        // Binding 0: texture (Float filterable = supports linear sampling)
117        // Binding 1: sampler (Filtering = pairs with filterable textures)
118        let bind_group_layout =
119            graphics_ctx
120                .device()
121                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
122                    label: Some("Texture Bind Group Layout"),
123                    entries: &[
124                        wgpu::BindGroupLayoutEntry {
125                            binding: 0,
126                            visibility: wgpu::ShaderStages::FRAGMENT,
127                            ty: wgpu::BindingType::Texture {
128                                multisampled: false,
129                                view_dimension: wgpu::TextureViewDimension::D2,
130                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
131                            },
132                            count: None,
133                        },
134                        wgpu::BindGroupLayoutEntry {
135                            binding: 1,
136                            visibility: wgpu::ShaderStages::FRAGMENT,
137                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
138                            count: None,
139                        },
140                    ],
141                });
142
143        let bind_group = graphics_ctx
144            .device()
145            .create_bind_group(&wgpu::BindGroupDescriptor {
146                label: Some("Texture Bind Group"),
147                layout: &bind_group_layout,
148                entries: &[
149                    wgpu::BindGroupEntry {
150                        binding: 0,
151                        resource: wgpu::BindingResource::TextureView(&texture_view),
152                    },
153                    wgpu::BindGroupEntry {
154                        binding: 1,
155                        resource: wgpu::BindingResource::Sampler(&sampler),
156                    },
157                ],
158            });
159
160        // --- Pipeline ---
161        let pipeline_layout =
162            graphics_ctx
163                .device()
164                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
165                    label: Some("Render Pipeline Layout"),
166                    bind_group_layouts: &[&bind_group_layout],
167                    push_constant_ranges: &[],
168                });
169
170        let pipeline =
171            graphics_ctx
172                .device()
173                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
174                    label: Some("Render Pipeline"),
175                    layout: Some(&pipeline_layout),
176                    vertex: wgpu::VertexState {
177                        module: &shader,
178                        entry_point: Some("vs_main"),
179                        buffers: &[wgpu::VertexBufferLayout {
180                            // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
181                            array_stride: 4 * 4,
182                            step_mode: wgpu::VertexStepMode::Vertex,
183                            attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
184                        }],
185                        compilation_options: wgpu::PipelineCompilationOptions::default(),
186                    },
187                    fragment: Some(wgpu::FragmentState {
188                        module: &shader,
189                        entry_point: Some("fs_main"),
190                        targets: &[Some(wgpu::ColorTargetState {
191                            format: wgpu::TextureFormat::Bgra8UnormSrgb,
192                            blend: Some(wgpu::BlendState::REPLACE),
193                            write_mask: wgpu::ColorWrites::ALL,
194                        })],
195                        compilation_options: wgpu::PipelineCompilationOptions::default(),
196                    }),
197                    primitive: wgpu::PrimitiveState {
198                        topology: wgpu::PrimitiveTopology::TriangleList,
199                        strip_index_format: None,
200                        front_face: wgpu::FrontFace::Ccw,
201                        cull_mode: Some(wgpu::Face::Back),
202                        polygon_mode: wgpu::PolygonMode::Fill,
203                        unclipped_depth: false,
204                        conservative: false,
205                    },
206                    depth_stencil: None,
207                    multisample: wgpu::MultisampleState {
208                        count: 1,
209                        mask: !0,
210                        alpha_to_coverage_enabled: false,
211                    },
212                    multiview: None,
213                    cache: None,
214                });
215
216        #[rustfmt::skip]
217        let vertices: &[f32] = &[
218            -0.8, -0.8,  0.0, 1.0,
219             0.8, -0.8,  1.0, 1.0,
220             0.8,  0.8,  1.0, 0.0,
221            -0.8, -0.8,  0.0, 1.0,
222             0.8,  0.8,  1.0, 0.0,
223            -0.8,  0.8,  0.0, 0.0,
224        ];
225
226        let vertex_buffer = graphics_ctx
227            .device()
228            .create_buffer(&wgpu::BufferDescriptor {
229                label: Some("Vertex Buffer"),
230                size: std::mem::size_of_val(vertices) as u64,
231                usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
232                mapped_at_creation: false,
233            });
234
235        graphics_ctx
236            .queue()
237            .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
238
239        let window_id = window.id();
240
241        Box::new(App {
242            window,
243            window_id,
244            pipeline,
245            bind_group,
246            vertex_buffer,
247        })
248    });
249}
examples/image_blitting.rs (line 191)
163fn main() {
164    logging::init();
165
166    run_app(|ctx| {
167        let graphics_ctx =
168            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
169        let mut windows = HashMap::new();
170
171        let scale = Window::platform_dpi() as f32;
172        let window = ctx
173            .create_window(WindowDescriptor {
174                title: "Image Blitting Example".to_string(),
175                size: Some(WinitPhysicalSize::new(800.0 * scale, 600.0 * scale)),
176                ..Default::default()
177            })
178            .expect("Failed to create window");
179
180        let renderable_window = RenderWindowBuilder::new()
181            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
182            .with_depth_default()
183            .build(window, graphics_ctx.clone())
184            .expect("Failed to create render window");
185
186        let window_id = renderable_window.id();
187        windows.insert(window_id, renderable_window);
188
189        // Create shader module
190        let shader = graphics_ctx
191            .device()
192            .create_shader_module(wgpu::ShaderModuleDescriptor {
193                label: Some("Blit Shader"),
194                source: wgpu::ShaderSource::Wgsl(SHADER.into()),
195            });
196
197        // Create bind group layout
198        let bind_group_layout =
199            graphics_ctx
200                .device()
201                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
202                    label: Some("Blit Bind Group Layout"),
203                    entries: &[
204                        wgpu::BindGroupLayoutEntry {
205                            binding: 0,
206                            visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
207                            ty: wgpu::BindingType::Buffer {
208                                ty: wgpu::BufferBindingType::Uniform,
209                                has_dynamic_offset: false,
210                                min_binding_size: None,
211                            },
212                            count: None,
213                        },
214                        wgpu::BindGroupLayoutEntry {
215                            binding: 1,
216                            visibility: wgpu::ShaderStages::FRAGMENT,
217                            ty: wgpu::BindingType::Texture {
218                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
219                                view_dimension: wgpu::TextureViewDimension::D2,
220                                multisampled: false,
221                            },
222                            count: None,
223                        },
224                        wgpu::BindGroupLayoutEntry {
225                            binding: 2,
226                            visibility: wgpu::ShaderStages::FRAGMENT,
227                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
228                            count: None,
229                        },
230                    ],
231                });
232
233        // Create pipeline layout
234        let pipeline_layout =
235            graphics_ctx
236                .device()
237                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
238                    label: Some("Blit Pipeline Layout"),
239                    bind_group_layouts: &[&bind_group_layout],
240                    push_constant_ranges: &[],
241                });
242
243        // Create render pipeline
244        let pipeline =
245            graphics_ctx
246                .device()
247                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
248                    label: Some("Blit Pipeline"),
249                    layout: Some(&pipeline_layout),
250                    vertex: wgpu::VertexState {
251                        module: &shader,
252                        entry_point: Some("vs_main"),
253                        buffers: &[wgpu::VertexBufferLayout {
254                            array_stride: std::mem::size_of::<Vertex>() as u64,
255                            step_mode: wgpu::VertexStepMode::Vertex,
256                            attributes: &[
257                                wgpu::VertexAttribute {
258                                    offset: 0,
259                                    shader_location: 0,
260                                    format: wgpu::VertexFormat::Float32x2,
261                                },
262                                wgpu::VertexAttribute {
263                                    offset: 8,
264                                    shader_location: 1,
265                                    format: wgpu::VertexFormat::Float32x2,
266                                },
267                            ],
268                        }],
269                        compilation_options: wgpu::PipelineCompilationOptions::default(),
270                    },
271                    fragment: Some(wgpu::FragmentState {
272                        module: &shader,
273                        entry_point: Some("fs_main"),
274                        targets: &[Some(wgpu::ColorTargetState {
275                            format: wgpu::TextureFormat::Bgra8UnormSrgb,
276                            blend: Some(wgpu::BlendState::ALPHA_BLENDING),
277                            write_mask: wgpu::ColorWrites::ALL,
278                        })],
279                        compilation_options: wgpu::PipelineCompilationOptions::default(),
280                    }),
281                    primitive: wgpu::PrimitiveState {
282                        topology: wgpu::PrimitiveTopology::TriangleList,
283                        strip_index_format: None,
284                        front_face: wgpu::FrontFace::Ccw,
285                        cull_mode: None,
286                        polygon_mode: wgpu::PolygonMode::Fill,
287                        unclipped_depth: false,
288                        conservative: false,
289                    },
290                    depth_stencil: None,
291                    multisample: wgpu::MultisampleState::default(),
292                    multiview: None,
293                    cache: None,
294                });
295
296        // Create vertex buffer for a fullscreen quad
297        let vertices = [
298            Vertex {
299                position: [-0.8, -0.8],
300                uv: [0.0, 1.0],
301            },
302            Vertex {
303                position: [0.8, -0.8],
304                uv: [1.0, 1.0],
305            },
306            Vertex {
307                position: [0.8, 0.8],
308                uv: [1.0, 0.0],
309            },
310            Vertex {
311                position: [-0.8, -0.8],
312                uv: [0.0, 1.0],
313            },
314            Vertex {
315                position: [0.8, 0.8],
316                uv: [1.0, 0.0],
317            },
318            Vertex {
319                position: [-0.8, 0.8],
320                uv: [0.0, 0.0],
321            },
322        ];
323        let vertex_buffer =
324            graphics_ctx
325                .device()
326                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
327                    label: Some("Vertex Buffer"),
328                    contents: bytemuck::cast_slice(&vertices),
329                    usage: wgpu::BufferUsages::VERTEX,
330                });
331
332        // Create uniform buffer
333        let uniforms = Uniforms {
334            mvp: [
335                [1.0, 0.0, 0.0, 0.0],
336                [0.0, 1.0, 0.0, 0.0],
337                [0.0, 0.0, 1.0, 0.0],
338                [0.0, 0.0, 0.0, 1.0],
339            ],
340            tint: [1.0, 1.0, 1.0, 1.0],
341        };
342        let uniform_buffer =
343            graphics_ctx
344                .device()
345                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
346                    label: Some("Uniform Buffer"),
347                    contents: bytemuck::cast_slice(&[uniforms]),
348                    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
349                });
350
351        // Create CPU-side image buffer
352        let mut image_buffer = ImageBuffer::new(256, 256);
353        image_buffer.clear(30, 30, 40, 255);
354
355        // Create GPU texture
356        let texture = graphics_ctx
357            .device()
358            .create_texture(&wgpu::TextureDescriptor {
359                label: Some("Blit Texture"),
360                size: wgpu::Extent3d {
361                    width: 256,
362                    height: 256,
363                    depth_or_array_layers: 1,
364                },
365                mip_level_count: 1,
366                sample_count: 1,
367                dimension: wgpu::TextureDimension::D2,
368                format: wgpu::TextureFormat::Rgba8UnormSrgb,
369                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
370                view_formats: &[],
371            });
372
373        // Create texture view and sampler
374        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
375        let sampler = graphics_ctx
376            .device()
377            .create_sampler(&wgpu::SamplerDescriptor {
378                label: Some("Blit Sampler"),
379                address_mode_u: wgpu::AddressMode::ClampToEdge,
380                address_mode_v: wgpu::AddressMode::ClampToEdge,
381                address_mode_w: wgpu::AddressMode::ClampToEdge,
382                mag_filter: wgpu::FilterMode::Nearest, // Pixel-perfect rendering
383                min_filter: wgpu::FilterMode::Nearest,
384                mipmap_filter: wgpu::FilterMode::Nearest,
385                ..Default::default()
386            });
387
388        // Create bind group
389        let bind_group = graphics_ctx
390            .device()
391            .create_bind_group(&wgpu::BindGroupDescriptor {
392                label: Some("Blit Bind Group"),
393                layout: &bind_group_layout,
394                entries: &[
395                    wgpu::BindGroupEntry {
396                        binding: 0,
397                        resource: uniform_buffer.as_entire_binding(),
398                    },
399                    wgpu::BindGroupEntry {
400                        binding: 1,
401                        resource: wgpu::BindingResource::TextureView(&texture_view),
402                    },
403                    wgpu::BindGroupEntry {
404                        binding: 2,
405                        resource: wgpu::BindingResource::Sampler(&sampler),
406                    },
407                ],
408            });
409
410        Box::new(App {
411            context: graphics_ctx,
412            windows,
413            pipeline,
414            _bind_group_layout: bind_group_layout,
415            vertex_buffer,
416            texture,
417            bind_group,
418            _uniform_buffer: uniform_buffer,
419            image_buffer,
420            start_time: Instant::now(),
421        })
422    });
423}
Source

pub fn queue(&self) -> &Queue

Get a reference to the wgpu queue.

Examples found in repository?
examples/sprite_sheet.rs (line 422)
404    fn update(
405        &mut self,
406        _ctx: &mut astrelis_winit::app::AppCtx,
407        _time: &astrelis_winit::FrameTime,
408    ) {
409        let now = Instant::now();
410        let dt = now.duration_since(self.last_update).as_secs_f32();
411        self.last_update = now;
412
413        // Update animation
414        if self.animation.update(dt) {
415            // Frame changed - update vertex buffer with new UVs
416            let frame = self.animation.current_frame();
417            let uv = self.sprite_sheet.sprite_uv(frame);
418            let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
419
420            // Get context from first window
421            if let Some(window) = self.windows.values().next() {
422                window.context().graphics_context().queue().write_buffer(
423                    &self.vertex_buffer,
424                    0,
425                    bytemuck::cast_slice(&vertices),
426                );
427            }
428        }
429    }
More examples
Hide additional examples
examples/image_blitting.rs (line 465)
426    fn update(
427        &mut self,
428        _ctx: &mut astrelis_winit::app::AppCtx,
429        _time: &astrelis_winit::FrameTime,
430    ) {
431        let time = self.start_time.elapsed().as_secs_f32();
432
433        // Animate the CPU-side image buffer
434        self.image_buffer.clear(30, 30, 40, 255);
435
436        // Draw animated gradient background
437        let phase = (time * 0.5).sin() * 0.5 + 0.5;
438        let r1 = (50.0 + phase * 50.0) as u8;
439        let b1 = (80.0 + (1.0 - phase) * 50.0) as u8;
440        self.image_buffer
441            .gradient_h(0, 256, [r1, 40, b1], [40, r1, b1]);
442
443        // Draw bouncing circles
444        for i in 0..5 {
445            let offset = i as f32 * 0.4;
446            let x = 128.0 + (time * 2.0 + offset).sin() * 80.0;
447            let y = 128.0 + (time * 3.0 + offset).cos() * 80.0;
448            let hue = (time * 0.5 + offset) % 1.0;
449            let (r, g, b) = hsv_to_rgb(hue, 0.8, 1.0);
450            self.image_buffer
451                .fill_circle(x as i32, y as i32, 20, [r, g, b, 255]);
452        }
453
454        // Draw animated rectangles
455        for i in 0..3 {
456            let x = ((time * (1.0 + i as f32 * 0.3)).sin() * 100.0 + 128.0) as u32;
457            let y = 20 + i * 80;
458            let w = 30 + (time.sin() * 10.0) as u32;
459            let h = 20;
460            self.image_buffer
461                .fill_rect(x.saturating_sub(w / 2), y, w, h, [255, 255, 255, 200]);
462        }
463
464        // Upload to GPU (this is the "blit" operation)
465        self.context.queue().write_texture(
466            wgpu::TexelCopyTextureInfo {
467                texture: &self.texture,
468                mip_level: 0,
469                origin: wgpu::Origin3d::ZERO,
470                aspect: wgpu::TextureAspect::All,
471            },
472            &self.image_buffer.pixels,
473            wgpu::TexelCopyBufferLayout {
474                offset: 0,
475                bytes_per_row: Some(self.image_buffer.width * 4),
476                rows_per_image: Some(self.image_buffer.height),
477            },
478            wgpu::Extent3d {
479                width: self.image_buffer.width,
480                height: self.image_buffer.height,
481                depth_or_array_layers: 1,
482            },
483        );
484    }
examples/textured_window.rs (line 83)
22fn main() {
23    logging::init();
24
25    run_app(|ctx| {
26        let graphics_ctx =
27            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
28
29        let window = ctx
30            .create_window(WindowDescriptor {
31                title: "Textured Window".to_string(),
32                ..Default::default()
33            })
34            .expect("Failed to create window");
35
36        let window = RenderWindowBuilder::new()
37            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
38            .with_depth_default()
39            .build(window, graphics_ctx.clone())
40            .expect("Failed to create render window");
41
42        // --- Shader ---
43        let shader = graphics_ctx
44            .device()
45            .create_shader_module(wgpu::ShaderModuleDescriptor {
46                label: Some("Texture Shader"),
47                source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
48            });
49
50        // --- Texture Upload ---
51        // Create a 256x256 RGBA texture with a procedural gradient pattern.
52        // Rgba8UnormSrgb applies sRGB gamma correction so colors appear correct on screen.
53        let texture_size = wgpu::Extent3d {
54            width: 256,
55            height: 256,
56            depth_or_array_layers: 1,
57        };
58
59        let texture = graphics_ctx
60            .device()
61            .create_texture(&wgpu::TextureDescriptor {
62                label: Some("Example Texture"),
63                size: texture_size,
64                mip_level_count: 1,
65                sample_count: 1,
66                dimension: wgpu::TextureDimension::D2,
67                format: wgpu::TextureFormat::Rgba8UnormSrgb,
68                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
69                view_formats: &[],
70            });
71
72        let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
73        for y in 0..256 {
74            for x in 0..256 {
75                let idx = ((y * 256 + x) * 4) as usize;
76                texture_data[idx] = x as u8;
77                texture_data[idx + 1] = y as u8;
78                texture_data[idx + 2] = ((x + y) / 2) as u8;
79                texture_data[idx + 3] = 255;
80            }
81        }
82
83        graphics_ctx.queue().write_texture(
84            wgpu::TexelCopyTextureInfo {
85                texture: &texture,
86                mip_level: 0,
87                origin: wgpu::Origin3d::ZERO,
88                aspect: wgpu::TextureAspect::All,
89            },
90            &texture_data,
91            wgpu::TexelCopyBufferLayout {
92                offset: 0,
93                bytes_per_row: Some(256 * 4),
94                rows_per_image: Some(256),
95            },
96            texture_size,
97        );
98
99        // --- Sampler ---
100        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
101        // ClampToEdge prevents sampling beyond texture edges; Linear mag filter
102        // gives smooth results when the quad is larger than the texture.
103        let sampler = graphics_ctx
104            .device()
105            .create_sampler(&wgpu::SamplerDescriptor {
106                address_mode_u: wgpu::AddressMode::ClampToEdge,
107                address_mode_v: wgpu::AddressMode::ClampToEdge,
108                address_mode_w: wgpu::AddressMode::ClampToEdge,
109                mag_filter: wgpu::FilterMode::Linear,
110                min_filter: wgpu::FilterMode::Nearest,
111                mipmap_filter: wgpu::FilterMode::Nearest,
112                ..Default::default()
113            });
114
115        // --- Bind Group ---
116        // Binding 0: texture (Float filterable = supports linear sampling)
117        // Binding 1: sampler (Filtering = pairs with filterable textures)
118        let bind_group_layout =
119            graphics_ctx
120                .device()
121                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
122                    label: Some("Texture Bind Group Layout"),
123                    entries: &[
124                        wgpu::BindGroupLayoutEntry {
125                            binding: 0,
126                            visibility: wgpu::ShaderStages::FRAGMENT,
127                            ty: wgpu::BindingType::Texture {
128                                multisampled: false,
129                                view_dimension: wgpu::TextureViewDimension::D2,
130                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
131                            },
132                            count: None,
133                        },
134                        wgpu::BindGroupLayoutEntry {
135                            binding: 1,
136                            visibility: wgpu::ShaderStages::FRAGMENT,
137                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
138                            count: None,
139                        },
140                    ],
141                });
142
143        let bind_group = graphics_ctx
144            .device()
145            .create_bind_group(&wgpu::BindGroupDescriptor {
146                label: Some("Texture Bind Group"),
147                layout: &bind_group_layout,
148                entries: &[
149                    wgpu::BindGroupEntry {
150                        binding: 0,
151                        resource: wgpu::BindingResource::TextureView(&texture_view),
152                    },
153                    wgpu::BindGroupEntry {
154                        binding: 1,
155                        resource: wgpu::BindingResource::Sampler(&sampler),
156                    },
157                ],
158            });
159
160        // --- Pipeline ---
161        let pipeline_layout =
162            graphics_ctx
163                .device()
164                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
165                    label: Some("Render Pipeline Layout"),
166                    bind_group_layouts: &[&bind_group_layout],
167                    push_constant_ranges: &[],
168                });
169
170        let pipeline =
171            graphics_ctx
172                .device()
173                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
174                    label: Some("Render Pipeline"),
175                    layout: Some(&pipeline_layout),
176                    vertex: wgpu::VertexState {
177                        module: &shader,
178                        entry_point: Some("vs_main"),
179                        buffers: &[wgpu::VertexBufferLayout {
180                            // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
181                            array_stride: 4 * 4,
182                            step_mode: wgpu::VertexStepMode::Vertex,
183                            attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
184                        }],
185                        compilation_options: wgpu::PipelineCompilationOptions::default(),
186                    },
187                    fragment: Some(wgpu::FragmentState {
188                        module: &shader,
189                        entry_point: Some("fs_main"),
190                        targets: &[Some(wgpu::ColorTargetState {
191                            format: wgpu::TextureFormat::Bgra8UnormSrgb,
192                            blend: Some(wgpu::BlendState::REPLACE),
193                            write_mask: wgpu::ColorWrites::ALL,
194                        })],
195                        compilation_options: wgpu::PipelineCompilationOptions::default(),
196                    }),
197                    primitive: wgpu::PrimitiveState {
198                        topology: wgpu::PrimitiveTopology::TriangleList,
199                        strip_index_format: None,
200                        front_face: wgpu::FrontFace::Ccw,
201                        cull_mode: Some(wgpu::Face::Back),
202                        polygon_mode: wgpu::PolygonMode::Fill,
203                        unclipped_depth: false,
204                        conservative: false,
205                    },
206                    depth_stencil: None,
207                    multisample: wgpu::MultisampleState {
208                        count: 1,
209                        mask: !0,
210                        alpha_to_coverage_enabled: false,
211                    },
212                    multiview: None,
213                    cache: None,
214                });
215
216        #[rustfmt::skip]
217        let vertices: &[f32] = &[
218            -0.8, -0.8,  0.0, 1.0,
219             0.8, -0.8,  1.0, 1.0,
220             0.8,  0.8,  1.0, 0.0,
221            -0.8, -0.8,  0.0, 1.0,
222             0.8,  0.8,  1.0, 0.0,
223            -0.8,  0.8,  0.0, 0.0,
224        ];
225
226        let vertex_buffer = graphics_ctx
227            .device()
228            .create_buffer(&wgpu::BufferDescriptor {
229                label: Some("Vertex Buffer"),
230                size: std::mem::size_of_val(vertices) as u64,
231                usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
232                mapped_at_creation: false,
233            });
234
235        graphics_ctx
236            .queue()
237            .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
238
239        let window_id = window.id();
240
241        Box::new(App {
242            window,
243            window_id,
244            pipeline,
245            bind_group,
246            vertex_buffer,
247        })
248    });
249}
Source

pub fn adapter(&self) -> &Adapter

Get a reference to the wgpu adapter.

Source

pub fn instance(&self) -> &Instance

Get a reference to the wgpu instance.

Source

pub fn info(&self) -> AdapterInfo

Get device info

Examples found in repository?
examples/renderer_api.rs (line 260)
37fn main() {
38    logging::init();
39
40    run_app(|ctx| {
41        let graphics_ctx =
42            GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
43        let renderer = Renderer::new(graphics_ctx.clone());
44
45        let window = ctx
46            .create_window(WindowDescriptor {
47                title: "Renderer API Example".to_string(),
48                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
49                ..Default::default()
50            })
51            .expect("Failed to create window");
52
53        let window = RenderWindowBuilder::new()
54            .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
55            .with_depth_default()
56            .build(window, graphics_ctx.clone())
57            .expect("Failed to create render window");
58
59        let window_id = window.id();
60
61        // Create shader using Renderer API
62        let shader = renderer.create_shader(Some("Color Shader"), SHADER_SOURCE);
63
64        // Create texture using Renderer helper
65        let texture_data = create_gradient_texture();
66        let texture = renderer.create_texture_2d(
67            Some("Gradient Texture"),
68            256,
69            256,
70            wgpu::TextureFormat::Rgba8UnormSrgb,
71            wgpu::TextureUsages::TEXTURE_BINDING,
72            &texture_data,
73        );
74
75        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
76        let sampler = renderer.create_linear_sampler(Some("Linear Sampler"));
77
78        // Create bind group using Renderer API
79        let bind_group_layout = renderer.create_bind_group_layout(
80            Some("Texture Bind Group Layout"),
81            &[
82                wgpu::BindGroupLayoutEntry {
83                    binding: 0,
84                    visibility: wgpu::ShaderStages::FRAGMENT,
85                    ty: wgpu::BindingType::Texture {
86                        multisampled: false,
87                        view_dimension: wgpu::TextureViewDimension::D2,
88                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
89                    },
90                    count: None,
91                },
92                wgpu::BindGroupLayoutEntry {
93                    binding: 1,
94                    visibility: wgpu::ShaderStages::FRAGMENT,
95                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
96                    count: None,
97                },
98            ],
99        );
100
101        let bind_group = renderer.create_bind_group(
102            Some("Texture Bind Group"),
103            &bind_group_layout,
104            &[
105                wgpu::BindGroupEntry {
106                    binding: 0,
107                    resource: wgpu::BindingResource::TextureView(&texture_view),
108                },
109                wgpu::BindGroupEntry {
110                    binding: 1,
111                    resource: wgpu::BindingResource::Sampler(&sampler),
112                },
113            ],
114        );
115
116        let pipeline_layout = renderer.create_pipeline_layout(
117            Some("Render Pipeline Layout"),
118            &[&bind_group_layout],
119            &[],
120        );
121
122        // Create pipeline using Renderer API with BlendMode
123        let pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
124            label: Some("Render Pipeline"),
125            layout: Some(&pipeline_layout),
126            vertex: wgpu::VertexState {
127                module: &shader,
128                entry_point: Some("vs_main"),
129                buffers: &[wgpu::VertexBufferLayout {
130                    // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
131                    array_stride: 4 * 4,
132                    step_mode: wgpu::VertexStepMode::Vertex,
133                    attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
134                }],
135                compilation_options: wgpu::PipelineCompilationOptions::default(),
136            },
137            fragment: Some(wgpu::FragmentState {
138                module: &shader,
139                entry_point: Some("fs_main"),
140                // Use BlendMode for transparent rendering
141                targets: &[Some(
142                    BlendMode::Alpha.to_color_target_state(wgpu::TextureFormat::Rgba8UnormSrgb),
143                )],
144                compilation_options: wgpu::PipelineCompilationOptions::default(),
145            }),
146            primitive: wgpu::PrimitiveState {
147                topology: wgpu::PrimitiveTopology::TriangleList,
148                strip_index_format: None,
149                front_face: wgpu::FrontFace::Ccw,
150                cull_mode: Some(wgpu::Face::Back),
151                polygon_mode: wgpu::PolygonMode::Fill,
152                unclipped_depth: false,
153                conservative: false,
154            },
155            depth_stencil: None,
156            multisample: wgpu::MultisampleState {
157                count: 1,
158                mask: !0,
159                alpha_to_coverage_enabled: false,
160            },
161            multiview: None,
162            cache: None,
163        });
164
165        #[rustfmt::skip]
166        let vertices: &[f32] = &[
167            -0.8, -0.8,  0.0, 1.0,
168             0.8, -0.8,  1.0, 1.0,
169             0.8,  0.8,  1.0, 0.0,
170            -0.8, -0.8,  0.0, 1.0,
171             0.8,  0.8,  1.0, 0.0,
172            -0.8,  0.8,  0.0, 0.0,
173        ];
174
175        // Create vertex buffer using Renderer helper
176        let vertex_buffer = renderer.create_vertex_buffer(Some("Vertex Buffer"), vertices);
177
178        // Create offscreen framebuffer using the new Framebuffer abstraction
179        let offscreen_fb = Framebuffer::builder(400, 300)
180            .format(wgpu::TextureFormat::Rgba8UnormSrgb)
181            .label("Offscreen FB")
182            .build(&graphics_ctx);
183
184        // Create blit shader and pipeline for rendering framebuffer to surface
185        let blit_shader = renderer.create_shader(Some("Blit Shader"), BLIT_SHADER_SOURCE);
186
187        let blit_bind_group_layout = renderer.create_bind_group_layout(
188            Some("Blit Bind Group Layout"),
189            &[
190                wgpu::BindGroupLayoutEntry {
191                    binding: 0,
192                    visibility: wgpu::ShaderStages::FRAGMENT,
193                    ty: wgpu::BindingType::Texture {
194                        multisampled: false,
195                        view_dimension: wgpu::TextureViewDimension::D2,
196                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
197                    },
198                    count: None,
199                },
200                wgpu::BindGroupLayoutEntry {
201                    binding: 1,
202                    visibility: wgpu::ShaderStages::FRAGMENT,
203                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
204                    count: None,
205                },
206            ],
207        );
208
209        let blit_bind_group = renderer.create_bind_group(
210            Some("Blit Bind Group"),
211            &blit_bind_group_layout,
212            &[
213                wgpu::BindGroupEntry {
214                    binding: 0,
215                    resource: wgpu::BindingResource::TextureView(offscreen_fb.color_view()),
216                },
217                wgpu::BindGroupEntry {
218                    binding: 1,
219                    resource: wgpu::BindingResource::Sampler(&sampler),
220                },
221            ],
222        );
223
224        let blit_pipeline_layout = renderer.create_pipeline_layout(
225            Some("Blit Pipeline Layout"),
226            &[&blit_bind_group_layout],
227            &[],
228        );
229
230        let blit_pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
231            label: Some("Blit Pipeline"),
232            layout: Some(&blit_pipeline_layout),
233            vertex: wgpu::VertexState {
234                module: &blit_shader,
235                entry_point: Some("vs_main"),
236                buffers: &[],
237                compilation_options: wgpu::PipelineCompilationOptions::default(),
238            },
239            fragment: Some(wgpu::FragmentState {
240                module: &blit_shader,
241                entry_point: Some("fs_main"),
242                // Use PremultipliedAlpha for framebuffer blitting
243                targets: &[Some(
244                    BlendMode::PremultipliedAlpha
245                        .to_color_target_state(wgpu::TextureFormat::Bgra8UnormSrgb),
246                )],
247                compilation_options: wgpu::PipelineCompilationOptions::default(),
248            }),
249            primitive: wgpu::PrimitiveState {
250                topology: wgpu::PrimitiveTopology::TriangleList,
251                ..Default::default()
252            },
253            depth_stencil: None,
254            multisample: wgpu::MultisampleState::default(),
255            multiview: None,
256            cache: None,
257        });
258
259        tracing::info!("Renderer initialized successfully");
260        tracing::info!("Device: {:?}", renderer.context().info());
261
262        Box::new(RendererApp {
263            _context: graphics_ctx,
264            _renderer: renderer,
265            window,
266            window_id,
267            pipeline,
268            bind_group,
269            vertex_buffer,
270            offscreen_fb,
271            blit_pipeline,
272            blit_bind_group,
273            time: 0.0,
274        })
275    });
276}
Source

pub fn limits(&self) -> Limits

Get device limits

Source

pub fn wgpu_features(&self) -> Features

Get raw wgpu device features

Source

pub fn gpu_features(&self) -> GpuFeatures

Get the enabled GPU features (high-level wrapper).

Source

pub fn has_feature(&self, feature: GpuFeatures) -> bool

Check if a specific GPU feature is enabled.

Source

pub fn has_all_features(&self, features: GpuFeatures) -> bool

Check if all specified GPU features are enabled.

Source

pub fn require_feature(&self, feature: GpuFeatures)

Assert that a feature is available, panicking with a clear message if not.

Use this before operations that require specific features.

Source

pub fn supports_texture_format( &self, format: TextureFormat, usages: TextureUsages, ) -> bool

Check if a texture format is supported for the given usages.

§Example
let supported = ctx.supports_texture_format(
    wgpu::TextureFormat::Rgba8Unorm,
    wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING
);
Source

pub fn texture_format_capabilities( &self, format: TextureFormat, ) -> TextureFormatFeatures

Get texture format capabilities.

Returns detailed information about what operations are supported for a given texture format.

Trait Implementations§

Source§

impl AsWgpu for GraphicsContext

Source§

type WgpuType = Device

The underlying wgpu type.
Source§

fn as_wgpu(&self) -> &Self::WgpuType

Get a reference to the underlying wgpu type.
Source§

impl RenderContext for GraphicsContext

Source§

fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> GpuBuffer

Create a GPU buffer. Read more
Source§

fn write_buffer(&self, buffer: &GpuBuffer, offset: u64, data: &[u8])

Write data to a buffer. Read more
Source§

fn create_texture(&self, desc: &TextureDescriptor<'_>) -> GpuTexture

Create a GPU texture.
Source§

fn create_shader_module( &self, desc: &ShaderModuleDescriptor<'_>, ) -> GpuShaderModule

Create a shader module from source code.
Source§

fn create_render_pipeline( &self, desc: &RenderPipelineDescriptor<'_>, ) -> GpuRenderPipeline

Create a render pipeline.
Source§

fn create_compute_pipeline( &self, desc: &ComputePipelineDescriptor<'_>, ) -> GpuComputePipeline

Create a compute pipeline.
Source§

fn create_bind_group_layout( &self, desc: &BindGroupLayoutDescriptor<'_>, ) -> GpuBindGroupLayout

Create a bind group layout.
Source§

fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> GpuBindGroup

Create a bind group.
Source§

fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> GpuSampler

Create a texture sampler.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,