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 45)
41fn main() {
42    logging::init();
43
44    run_app(|ctx| {
45        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
46        let mut window_manager = WindowManager::new(graphics_ctx);
47        let mut window_colors = HashMap::new();
48
49        // Create 3 windows with different colors
50        let colors = [
51            Color::rgb(0.8, 0.2, 0.2), // Red
52            Color::rgb(0.2, 0.8, 0.2), // Green
53            Color::rgb(0.2, 0.2, 0.8), // Blue
54        ];
55
56        for (i, color) in colors.iter().enumerate() {
57            let window_id = window_manager.create_window_with_descriptor(
58                ctx,
59                WindowDescriptor {
60                    title: format!("Window {} - WindowManager Demo", i + 1),
61                    size: Some(WinitPhysicalSize::new(400.0, 300.0)),
62                    ..Default::default()
63                },
64                WindowContextDescriptor {
65                    format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
66                    ..Default::default()
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 31)
27fn main() {
28    logging::init();
29
30    run_app(|ctx| {
31        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
32
33        let mut windows = HashMap::new();
34
35        // Create 3 windows with different colors
36        let colors = [
37            wgpu::Color {
38                r: 0.8,
39                g: 0.2,
40                b: 0.2,
41                a: 1.0,
42            },
43            wgpu::Color {
44                r: 0.2,
45                g: 0.8,
46                b: 0.2,
47                a: 1.0,
48            },
49            wgpu::Color {
50                r: 0.2,
51                g: 0.2,
52                b: 0.8,
53                a: 1.0,
54            },
55        ];
56
57        for (i, color) in colors.iter().enumerate() {
58            let window = ctx
59                .create_window(WindowDescriptor {
60                    title: format!("Window {} - Multi-Window Example", i + 1),
61                    size: Some(WinitPhysicalSize::new(400.0, 300.0)),
62                    ..Default::default()
63                })
64                .expect("Failed to create window");
65
66            let renderable_window = RenderableWindow::new_with_descriptor(
67                window,
68                graphics_ctx.clone(),
69                WindowContextDescriptor {
70                    format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
71                    ..Default::default()
72                },
73            ).expect("Failed to create renderable window");
74
75            let window_id = renderable_window.id();
76            windows.insert(window_id, (renderable_window, *color));
77        }
78
79        Box::new(App {
80            windows,
81        })
82    });
83}
examples/performance_benchmark.rs (line 40)
36fn main() {
37    logging::init();
38
39    run_app(|ctx| {
40        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
41
42        let window = ctx
43            .create_window(WindowDescriptor {
44                title: "Performance Benchmark - Render Stress Test".to_string(),
45                size: Some(WinitPhysicalSize::new(1280.0, 720.0)),
46                ..Default::default()
47            })
48            .expect("Failed to create window");
49
50        let window = RenderableWindow::new_with_descriptor(
51            window,
52            graphics_ctx.clone(),
53            WindowContextDescriptor {
54                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
55                ..Default::default()
56            },
57        ).expect("Failed to create renderable window");
58
59        let window_id = window.id();
60
61        println!("\n═══════════════════════════════════════════════════════");
62        println!("  ⚡ PERFORMANCE BENCHMARK - Render Stress Test");
63        println!("═══════════════════════════════════════════════════════");
64        println!("  CONTROLS:");
65        println!("    [Space]  Toggle rendering on/off");
66        println!("    [+/-]    Increase/decrease object count");
67        println!("  Starting with 1000 objects");
68        println!("═══════════════════════════════════════════════════════\n");
69
70        Box::new(PerformanceBenchmark {
71            _context: graphics_ctx,
72            window,
73            window_id,
74            object_count: 1000,
75            rendering: true,
76            frame_count: 0,
77            last_fps_time: Instant::now(),
78            fps: 0.0,
79            last_frame_time: 0.0,
80        })
81    });
82}
examples/camera_demo.rs (line 34)
30fn main() {
31    logging::init();
32
33    run_app(|ctx| {
34        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
35
36        let window = ctx
37            .create_window(WindowDescriptor {
38                title: "Camera Demo - View & Projection".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 = RenderableWindow::new_with_descriptor(
45            window,
46            graphics_ctx.clone(),
47            WindowContextDescriptor {
48                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
49                ..Default::default()
50            },
51        ).expect("Failed to create renderable window");
52
53        let window_id = window.id();
54
55        println!("\n═══════════════════════════════════════════════════════");
56        println!("  📹 CAMERA DEMO - View & Projection");
57        println!("═══════════════════════════════════════════════════════");
58        println!("\n  CAMERA API FEATURES:");
59        println!("    • Orthographic cameras (2D, UI, isometric)");
60        println!("    • Perspective cameras (3D scenes)");
61        println!("    • View matrix (position, rotation, look-at)");
62        println!("    • Projection matrix (FOV, aspect, near/far planes)");
63        println!("    • Screen-to-world coordinate conversion");
64        println!("    • Camera movement helpers");
65        println!("\n  CAMERA TYPES:");
66        println!("    • OrthographicCamera - 2D games, UI overlays");
67        println!("      camera.orthographic(left, right, bottom, top, near, far)");
68        println!("    • PerspectiveCamera - 3D scenes");
69        println!("      camera.perspective(fov, aspect, near, far)");
70        println!("\n  Camera API Usage:");
71        println!("    let camera = Camera::new()");
72        println!("        .position(Vec3::new(0.0, 5.0, 10.0))");
73        println!("        .look_at(Vec3::ZERO)");
74        println!("        .perspective(60.0, aspect, 0.1, 100.0);");
75        println!("    let view_proj = camera.view_projection_matrix();");
76        println!("═══════════════════════════════════════════════════════\n");
77
78        tracing::info!("Camera demo initialized");
79
80        Box::new(CameraDemo {
81            _context: graphics_ctx,
82            window,
83            window_id,
84        })
85    });
86}
examples/render_graph_demo.rs (line 33)
29fn main() {
30    logging::init();
31
32    run_app(|ctx| {
33        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
34
35        let window = ctx
36            .create_window(WindowDescriptor {
37                title: "Render Graph Demo - Multi-Pass Rendering".to_string(),
38                size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
39                ..Default::default()
40            })
41            .expect("Failed to create window");
42
43        let window = RenderableWindow::new_with_descriptor(
44            window,
45            graphics_ctx.clone(),
46            WindowContextDescriptor {
47                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
48                ..Default::default()
49            },
50        ).expect("Failed to create renderable window");
51
52        let window_id = window.id();
53
54        println!("\n═══════════════════════════════════════════════════════");
55        println!("  🔀 RENDER GRAPH DEMO - Multi-Pass Rendering");
56        println!("═══════════════════════════════════════════════════════");
57        println!("\n  RENDER GRAPH FEATURES:");
58        println!("    • Declarative pass definition");
59        println!("    • Automatic dependency resolution");
60        println!("    • Resource lifetime management");
61        println!("    • Parallel pass execution");
62        println!("    • Automatic optimization");
63        println!("\n  EXAMPLE PIPELINE:");
64        println!("    1. Shadow Pass → depth texture");
65        println!("    2. Geometry Pass → color + normal + depth");
66        println!("    3. Lighting Pass → lit scene");
67        println!("    4. Post-Processing → bloom, tone mapping");
68        println!("    5. UI Pass → final composite");
69        println!("\n  Render Graph API Usage:");
70        println!("    let mut graph = RenderGraph::new();");
71        println!("    graph.add_pass(\"shadow\", shadow_pass_descriptor);");
72        println!("    graph.add_pass(\"geometry\", geometry_pass_descriptor);");
73        println!("    graph.add_dependency(\"lighting\", \"geometry\");");
74        println!("    graph.compile();");
75        println!("    graph.execute(&mut encoder);");
76        println!("═══════════════════════════════════════════════════════\n");
77
78        tracing::info!("Render graph demo initialized");
79
80        Box::new(RenderGraphDemo {
81            _context: graphics_ctx,
82            window,
83            window_id,
84        })
85    });
86}
examples/mesh_primitives.rs (line 33)
29fn main() {
30    logging::init();
31
32    run_app(|ctx| {
33        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
34
35        let window = ctx
36            .create_window(WindowDescriptor {
37                title: "Mesh Primitives Demo - Geometry API".to_string(),
38                size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
39                ..Default::default()
40            })
41            .expect("Failed to create window");
42
43        let window = RenderableWindow::new_with_descriptor(
44            window,
45            graphics_ctx.clone(),
46            WindowContextDescriptor {
47                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
48                ..Default::default()
49            },
50        ).expect("Failed to create renderable window");
51
52        let window_id = window.id();
53
54        println!("\n═══════════════════════════════════════════════════════");
55        println!("  📐 MESH PRIMITIVES DEMO - Geometry API");
56        println!("═══════════════════════════════════════════════════════");
57        println!("\n  MESH API FEATURES:");
58        println!("    • MeshBuilder for custom geometry");
59        println!("    • Primitive generation (cube, sphere, plane, etc.)");
60        println!("    • Flexible vertex formats (Position, Normal, UV, Color)");
61        println!("    • Index buffer optimization");
62        println!("    • Instanced rendering support");
63        println!("\n  EXAMPLE PRIMITIVES:");
64        println!("    • Cube - box with 24 vertices (6 faces × 4 vertices)");
65        println!("    • Sphere - tessellated sphere with UV mapping");
66        println!("    • Plane - quad with optional subdivisions");
67        println!("    • Cylinder - sides + caps");
68        println!("    • Custom - arbitrary vertex/index data");
69        println!("\n  Mesh API Usage:");
70        println!("    let mesh = MeshBuilder::new()");
71        println!("        .with_positions(vertices)");
72        println!("        .with_normals(normals)");
73        println!("        .with_uvs(uvs)");
74        println!("        .with_indices(indices)");
75        println!("        .build(&ctx);");
76        println!("    mesh.draw(&mut pass);");
77        println!("    mesh.draw_instanced(&mut pass, instance_count);");
78        println!("═══════════════════════════════════════════════════════\n");
79
80        tracing::info!("Mesh primitives demo initialized");
81
82        Box::new(MeshPrimitivesDemo {
83            _context: graphics_ctx,
84            window,
85            window_id,
86        })
87    });
88}
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 295)
275fn main() {
276    logging::init();
277
278    run_app(|ctx| {
279        let tier_override = parse_tier();
280
281        // Use the capability API to configure GPU requirements.
282        // For auto-detect, request the best capability (graceful degradation).
283        // For a specific tier, require that tier's capability.
284        let descriptor = match tier_override {
285            None => GraphicsContextDescriptor::new()
286                .request_capability::<BestBatchCapability2D>(),
287            Some(RenderTier::Direct) => GraphicsContextDescriptor::new()
288                .require_capability::<DirectBatchCapability2D>(),
289            Some(RenderTier::Indirect) => GraphicsContextDescriptor::new()
290                .require_capability::<IndirectBatchCapability2D>(),
291            Some(RenderTier::Bindless) => GraphicsContextDescriptor::new()
292                .require_capability::<BindlessBatchCapability2D>(),
293        };
294        let graphics_ctx =
295            pollster::block_on(GraphicsContext::new_owned_with_descriptor(descriptor))
296                .expect("Failed to create graphics context");
297
298        let window = ctx
299            .create_window(WindowDescriptor {
300                title: "Batched Renderer Example".to_string(),
301                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
302                ..Default::default()
303            })
304            .expect("Failed to create window");
305
306        let surface_format = wgpu::TextureFormat::Bgra8UnormSrgb;
307
308        let renderable_window = RenderableWindow::new_with_descriptor(
309            window,
310            graphics_ctx.clone(),
311            WindowContextDescriptor {
312                format: Some(surface_format),
313                ..Default::default()
314            },
315        )
316        .expect("Failed to create renderable window");
317
318        let window_id = renderable_window.id();
319
320        let renderer = create_batch_renderer_2d(
321            graphics_ctx.clone(),
322            surface_format,
323            tier_override,
324        );
325
326        tracing::info!("Using render tier: {}", renderer.tier());
327
328        // Create initial depth buffer
329        let depth_texture = graphics_ctx
330            .device()
331            .create_texture(&wgpu::TextureDescriptor {
332                label: Some("example_depth"),
333                size: wgpu::Extent3d {
334                    width: 1,
335                    height: 1,
336                    depth_or_array_layers: 1,
337                },
338                mip_level_count: 1,
339                sample_count: 1,
340                dimension: wgpu::TextureDimension::D2,
341                format: DEPTH_FORMAT,
342                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
343                view_formats: &[],
344            });
345        let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
346
347        let mut windows = HashMap::new();
348        windows.insert(window_id, renderable_window);
349
350        Box::new(App {
351            context: graphics_ctx,
352            windows,
353            renderer,
354            depth_texture,
355            depth_view,
356            depth_width: 1,
357            depth_height: 1,
358            frame_count: 0,
359        })
360    });
361}
More examples
Hide additional examples
examples/profiling_demo.rs (lines 47-50)
39fn main() {
40    logging::init();
41    init_profiling(ProfilingBackend::PuffinHttp);
42
43    run_app(|ctx| {
44        profile_function!();
45
46        // Request TIMESTAMP_QUERY for GPU profiling (best-effort, won't fail if unavailable)
47        let graphics_ctx = pollster::block_on(GraphicsContext::new_owned_with_descriptor(
48            GraphicsContextDescriptor::new()
49                .request_capability::<astrelis_render::gpu_profiling::GpuFrameProfiler>(),
50        ))
51        .expect("Failed to create graphics context");
52
53        let window = ctx
54            .create_window(WindowDescriptor {
55                title: "Profiling Demo — CPU + GPU".to_string(),
56                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
57                ..Default::default()
58            })
59            .expect("Failed to create window");
60
61        #[allow(unused_mut)]
62        let mut window = RenderableWindow::new_with_descriptor(
63            window,
64            graphics_ctx.clone(),
65            WindowContextDescriptor {
66                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
67                ..Default::default()
68            },
69        )
70        .expect("Failed to create renderable window");
71
72        let window_id = window.id();
73
74        // Attach GPU profiler to the window — all frames will be automatically profiled
75        let has_gpu_profiling;
76        #[cfg(feature = "gpu-profiling")]
77        {
78            match astrelis_render::gpu_profiling::GpuFrameProfiler::new(&graphics_ctx) {
79                Ok(profiler) => {
80                    let has_timestamps = profiler.has_timestamp_queries();
81                    window.set_gpu_profiler(Arc::new(profiler));
82                    has_gpu_profiling = true;
83                    if has_timestamps {
84                        println!("  GPU profiling: enabled with TIMESTAMP_QUERY (full timing)");
85                    } else {
86                        println!("  GPU profiling: enabled (debug groups only, no timing data)");
87                        println!("                 TIMESTAMP_QUERY not supported by this GPU");
88                    }
89                }
90                Err(e) => {
91                    has_gpu_profiling = false;
92                    tracing::warn!("Failed to create GPU profiler: {e}. GPU profiling disabled.");
93                    println!("  GPU profiling: failed to create profiler");
94                }
95            }
96        }
97        #[cfg(not(feature = "gpu-profiling"))]
98        {
99            has_gpu_profiling = false;
100        }
101
102        println!();
103        println!("═══════════════════════════════════════════════════");
104        println!("  PROFILING DEMO — CPU + GPU");
105        println!("═══════════════════════════════════════════════════");
106        println!();
107        println!("  CPU profiling: enabled (puffin)");
108        if !has_gpu_profiling {
109            #[cfg(not(feature = "gpu-profiling"))]
110            println!("  GPU profiling: disabled (compile with --features gpu-profiling)");
111        }
112        println!();
113        println!("  Open puffin_viewer at 127.0.0.1:8585 to see the flame graph.");
114        println!("  Install with: cargo install puffin_viewer");
115        println!("═══════════════════════════════════════════════════");
116        println!();
117
118        Box::new(ProfilingDemo {
119            context: graphics_ctx,
120            window,
121            window_id,
122            frame_count: 0,
123        })
124    });
125}
Source

pub fn device(&self) -> &Device

Get a reference to the wgpu device.

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

pub fn queue(&self) -> &Queue

Get a reference to the wgpu queue.

Examples found in repository?
examples/sprite_sheet.rs (line 381)
367    fn update(&mut self, _ctx: &mut astrelis_winit::app::AppCtx, _time: &astrelis_winit::FrameTime) {
368        let now = Instant::now();
369        let dt = now.duration_since(self.last_update).as_secs_f32();
370        self.last_update = now;
371
372        // Update animation
373        if self.animation.update(dt) {
374            // Frame changed - update vertex buffer with new UVs
375            let frame = self.animation.current_frame();
376            let uv = self.sprite_sheet.sprite_uv(frame);
377            let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
378            
379            // Get context from first window
380            if let Some(window) = self.windows.values().next() {
381                window.context().graphics_context().queue().write_buffer(
382                    &self.vertex_buffer,
383                    0,
384                    bytemuck::cast_slice(&vertices),
385                );
386            }
387        }
388    }
More examples
Hide additional examples
examples/image_blitting.rs (line 423)
391    fn update(&mut self, _ctx: &mut astrelis_winit::app::AppCtx, _time: &astrelis_winit::FrameTime) {
392        let time = self.start_time.elapsed().as_secs_f32();
393        
394        // Animate the CPU-side image buffer
395        self.image_buffer.clear(30, 30, 40, 255);
396        
397        // Draw animated gradient background
398        let phase = (time * 0.5).sin() * 0.5 + 0.5;
399        let r1 = (50.0 + phase * 50.0) as u8;
400        let b1 = (80.0 + (1.0 - phase) * 50.0) as u8;
401        self.image_buffer.gradient_h(0, 256, r1, 40, b1, 40, r1, b1);
402        
403        // Draw bouncing circles
404        for i in 0..5 {
405            let offset = i as f32 * 0.4;
406            let x = 128.0 + (time * 2.0 + offset).sin() * 80.0;
407            let y = 128.0 + (time * 3.0 + offset).cos() * 80.0;
408            let hue = (time * 0.5 + offset) % 1.0;
409            let (r, g, b) = hsv_to_rgb(hue, 0.8, 1.0);
410            self.image_buffer.fill_circle(x as i32, y as i32, 20, r, g, b, 255);
411        }
412        
413        // Draw animated rectangles
414        for i in 0..3 {
415            let x = ((time * (1.0 + i as f32 * 0.3)).sin() * 100.0 + 128.0) as u32;
416            let y = 20 + i * 80;
417            let w = 30 + (time.sin() * 10.0) as u32;
418            let h = 20;
419            self.image_buffer.fill_rect(x.saturating_sub(w/2), y, w, h, 255, 255, 255, 200);
420        }
421        
422        // Upload to GPU (this is the "blit" operation)
423        self.context.queue().write_texture(
424            wgpu::TexelCopyTextureInfo {
425                texture: &self.texture,
426                mip_level: 0,
427                origin: wgpu::Origin3d::ZERO,
428                aspect: wgpu::TextureAspect::All,
429            },
430            &self.image_buffer.pixels,
431            wgpu::TexelCopyBufferLayout {
432                offset: 0,
433                bytes_per_row: Some(self.image_buffer.width * 4),
434                rows_per_image: Some(self.image_buffer.height),
435            },
436            wgpu::Extent3d {
437                width: self.image_buffer.width,
438                height: self.image_buffer.height,
439                depth_or_array_layers: 1,
440            },
441        );
442    }
examples/textured_window.rs (line 91)
28fn main() {
29    logging::init();
30
31    run_app(|ctx| {
32        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
33
34        let window = ctx
35            .create_window(WindowDescriptor {
36                title: "Textured Window".to_string(),
37                ..Default::default()
38            })
39            .expect("Failed to create window");
40
41        let window = RenderableWindow::new_with_descriptor(
42            window,
43            graphics_ctx.clone(),
44            WindowContextDescriptor {
45                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
46                ..Default::default()
47            },
48        ).expect("Failed to create renderable window");
49
50        // --- Shader ---
51        let shader = graphics_ctx
52            .device()
53            .create_shader_module(wgpu::ShaderModuleDescriptor {
54                label: Some("Texture Shader"),
55                source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
56            });
57
58        // --- Texture Upload ---
59        // Create a 256x256 RGBA texture with a procedural gradient pattern.
60        // Rgba8UnormSrgb applies sRGB gamma correction so colors appear correct on screen.
61        let texture_size = wgpu::Extent3d {
62            width: 256,
63            height: 256,
64            depth_or_array_layers: 1,
65        };
66
67        let texture = graphics_ctx
68            .device()
69            .create_texture(&wgpu::TextureDescriptor {
70                label: Some("Example Texture"),
71                size: texture_size,
72                mip_level_count: 1,
73                sample_count: 1,
74                dimension: wgpu::TextureDimension::D2,
75                format: wgpu::TextureFormat::Rgba8UnormSrgb,
76                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
77                view_formats: &[],
78            });
79
80        let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
81        for y in 0..256 {
82            for x in 0..256 {
83                let idx = ((y * 256 + x) * 4) as usize;
84                texture_data[idx] = x as u8;
85                texture_data[idx + 1] = y as u8;
86                texture_data[idx + 2] = ((x + y) / 2) as u8;
87                texture_data[idx + 3] = 255;
88            }
89        }
90
91        graphics_ctx.queue().write_texture(
92            wgpu::TexelCopyTextureInfo {
93                texture: &texture,
94                mip_level: 0,
95                origin: wgpu::Origin3d::ZERO,
96                aspect: wgpu::TextureAspect::All,
97            },
98            &texture_data,
99            wgpu::TexelCopyBufferLayout {
100                offset: 0,
101                bytes_per_row: Some(256 * 4),
102                rows_per_image: Some(256),
103            },
104            texture_size,
105        );
106
107        // --- Sampler ---
108        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
109        // ClampToEdge prevents sampling beyond texture edges; Linear mag filter
110        // gives smooth results when the quad is larger than the texture.
111        let sampler = graphics_ctx
112            .device()
113            .create_sampler(&wgpu::SamplerDescriptor {
114                address_mode_u: wgpu::AddressMode::ClampToEdge,
115                address_mode_v: wgpu::AddressMode::ClampToEdge,
116                address_mode_w: wgpu::AddressMode::ClampToEdge,
117                mag_filter: wgpu::FilterMode::Linear,
118                min_filter: wgpu::FilterMode::Nearest,
119                mipmap_filter: wgpu::FilterMode::Nearest,
120                ..Default::default()
121            });
122
123        // --- Bind Group ---
124        // Binding 0: texture (Float filterable = supports linear sampling)
125        // Binding 1: sampler (Filtering = pairs with filterable textures)
126        let bind_group_layout =
127            graphics_ctx
128                .device()
129                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
130                    label: Some("Texture Bind Group Layout"),
131                    entries: &[
132                        wgpu::BindGroupLayoutEntry {
133                            binding: 0,
134                            visibility: wgpu::ShaderStages::FRAGMENT,
135                            ty: wgpu::BindingType::Texture {
136                                multisampled: false,
137                                view_dimension: wgpu::TextureViewDimension::D2,
138                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
139                            },
140                            count: None,
141                        },
142                        wgpu::BindGroupLayoutEntry {
143                            binding: 1,
144                            visibility: wgpu::ShaderStages::FRAGMENT,
145                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
146                            count: None,
147                        },
148                    ],
149                });
150
151        let bind_group = graphics_ctx
152            .device()
153            .create_bind_group(&wgpu::BindGroupDescriptor {
154                label: Some("Texture Bind Group"),
155                layout: &bind_group_layout,
156                entries: &[
157                    wgpu::BindGroupEntry {
158                        binding: 0,
159                        resource: wgpu::BindingResource::TextureView(&texture_view),
160                    },
161                    wgpu::BindGroupEntry {
162                        binding: 1,
163                        resource: wgpu::BindingResource::Sampler(&sampler),
164                    },
165                ],
166            });
167
168        // --- Pipeline ---
169        let pipeline_layout =
170            graphics_ctx
171                .device()
172                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
173                    label: Some("Render Pipeline Layout"),
174                    bind_group_layouts: &[&bind_group_layout],
175                    push_constant_ranges: &[],
176                });
177
178        let pipeline =
179            graphics_ctx
180                .device()
181                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
182                    label: Some("Render Pipeline"),
183                    layout: Some(&pipeline_layout),
184                    vertex: wgpu::VertexState {
185                        module: &shader,
186                        entry_point: Some("vs_main"),
187                        buffers: &[wgpu::VertexBufferLayout {
188                            // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
189                            array_stride: 4 * 4,
190                            step_mode: wgpu::VertexStepMode::Vertex,
191                            attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
192                        }],
193                        compilation_options: wgpu::PipelineCompilationOptions::default(),
194                    },
195                    fragment: Some(wgpu::FragmentState {
196                        module: &shader,
197                        entry_point: Some("fs_main"),
198                        targets: &[Some(wgpu::ColorTargetState {
199                            format: wgpu::TextureFormat::Bgra8UnormSrgb,
200                            blend: Some(wgpu::BlendState::REPLACE),
201                            write_mask: wgpu::ColorWrites::ALL,
202                        })],
203                        compilation_options: wgpu::PipelineCompilationOptions::default(),
204                    }),
205                    primitive: wgpu::PrimitiveState {
206                        topology: wgpu::PrimitiveTopology::TriangleList,
207                        strip_index_format: None,
208                        front_face: wgpu::FrontFace::Ccw,
209                        cull_mode: Some(wgpu::Face::Back),
210                        polygon_mode: wgpu::PolygonMode::Fill,
211                        unclipped_depth: false,
212                        conservative: false,
213                    },
214                    depth_stencil: None,
215                    multisample: wgpu::MultisampleState {
216                        count: 1,
217                        mask: !0,
218                        alpha_to_coverage_enabled: false,
219                    },
220                    multiview: None,
221                    cache: None,
222                });
223
224        #[rustfmt::skip]
225        let vertices: &[f32] = &[
226            -0.8, -0.8,  0.0, 1.0,
227             0.8, -0.8,  1.0, 1.0,
228             0.8,  0.8,  1.0, 0.0,
229            -0.8, -0.8,  0.0, 1.0,
230             0.8,  0.8,  1.0, 0.0,
231            -0.8,  0.8,  0.0, 0.0,
232        ];
233
234        let vertex_buffer = graphics_ctx.device().create_buffer(&wgpu::BufferDescriptor {
235            label: Some("Vertex Buffer"),
236            size: (vertices.len() * std::mem::size_of::<f32>()) as u64,
237            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
238            mapped_at_creation: false,
239        });
240
241        graphics_ctx
242            .queue()
243            .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
244
245        let window_id = window.id();
246
247        Box::new(App {
248            window,
249            window_id,
250            pipeline,
251            bind_group,
252            vertex_buffer,
253        })
254    });
255}
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 262)
37fn main() {
38    logging::init();
39
40    run_app(|ctx| {
41        let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
42        let renderer = Renderer::new(graphics_ctx.clone());
43
44        let window = ctx
45            .create_window(WindowDescriptor {
46                title: "Renderer API Example".to_string(),
47                size: Some(WinitPhysicalSize::new(800.0, 600.0)),
48                ..Default::default()
49            })
50            .expect("Failed to create window");
51
52        let window = RenderableWindow::new_with_descriptor(
53            window,
54            graphics_ctx.clone(),
55            WindowContextDescriptor {
56                format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
57                ..Default::default()
58            },
59        ).expect("Failed to create renderable window");
60
61        let window_id = window.id();
62
63        // Create shader using Renderer API
64        let shader = renderer.create_shader(Some("Color Shader"), SHADER_SOURCE);
65
66        // Create texture using Renderer helper
67        let texture_data = create_gradient_texture();
68        let texture = renderer.create_texture_2d(
69            Some("Gradient Texture"),
70            256,
71            256,
72            wgpu::TextureFormat::Rgba8UnormSrgb,
73            wgpu::TextureUsages::TEXTURE_BINDING,
74            &texture_data,
75        );
76
77        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
78        let sampler = renderer.create_linear_sampler(Some("Linear Sampler"));
79
80        // Create bind group using Renderer API
81        let bind_group_layout = renderer.create_bind_group_layout(
82            Some("Texture Bind Group Layout"),
83            &[
84                wgpu::BindGroupLayoutEntry {
85                    binding: 0,
86                    visibility: wgpu::ShaderStages::FRAGMENT,
87                    ty: wgpu::BindingType::Texture {
88                        multisampled: false,
89                        view_dimension: wgpu::TextureViewDimension::D2,
90                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
91                    },
92                    count: None,
93                },
94                wgpu::BindGroupLayoutEntry {
95                    binding: 1,
96                    visibility: wgpu::ShaderStages::FRAGMENT,
97                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
98                    count: None,
99                },
100            ],
101        );
102
103        let bind_group = renderer.create_bind_group(
104            Some("Texture Bind Group"),
105            &bind_group_layout,
106            &[
107                wgpu::BindGroupEntry {
108                    binding: 0,
109                    resource: wgpu::BindingResource::TextureView(&texture_view),
110                },
111                wgpu::BindGroupEntry {
112                    binding: 1,
113                    resource: wgpu::BindingResource::Sampler(&sampler),
114                },
115            ],
116        );
117
118        let pipeline_layout = renderer.create_pipeline_layout(
119            Some("Render Pipeline Layout"),
120            &[&bind_group_layout],
121            &[],
122        );
123
124        // Create pipeline using Renderer API with BlendMode
125        let pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
126            label: Some("Render Pipeline"),
127            layout: Some(&pipeline_layout),
128            vertex: wgpu::VertexState {
129                module: &shader,
130                entry_point: Some("vs_main"),
131                buffers: &[wgpu::VertexBufferLayout {
132                    // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
133                    array_stride: 4 * 4,
134                    step_mode: wgpu::VertexStepMode::Vertex,
135                    attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
136                }],
137                compilation_options: wgpu::PipelineCompilationOptions::default(),
138            },
139            fragment: Some(wgpu::FragmentState {
140                module: &shader,
141                entry_point: Some("fs_main"),
142                // Use BlendMode for transparent rendering
143                targets: &[Some(
144                    BlendMode::Alpha.to_color_target_state(wgpu::TextureFormat::Rgba8UnormSrgb),
145                )],
146                compilation_options: wgpu::PipelineCompilationOptions::default(),
147            }),
148            primitive: wgpu::PrimitiveState {
149                topology: wgpu::PrimitiveTopology::TriangleList,
150                strip_index_format: None,
151                front_face: wgpu::FrontFace::Ccw,
152                cull_mode: Some(wgpu::Face::Back),
153                polygon_mode: wgpu::PolygonMode::Fill,
154                unclipped_depth: false,
155                conservative: false,
156            },
157            depth_stencil: None,
158            multisample: wgpu::MultisampleState {
159                count: 1,
160                mask: !0,
161                alpha_to_coverage_enabled: false,
162            },
163            multiview: None,
164            cache: None,
165        });
166
167        #[rustfmt::skip]
168        let vertices: &[f32] = &[
169            -0.8, -0.8,  0.0, 1.0,
170             0.8, -0.8,  1.0, 1.0,
171             0.8,  0.8,  1.0, 0.0,
172            -0.8, -0.8,  0.0, 1.0,
173             0.8,  0.8,  1.0, 0.0,
174            -0.8,  0.8,  0.0, 0.0,
175        ];
176
177        // Create vertex buffer using Renderer helper
178        let vertex_buffer = renderer.create_vertex_buffer(Some("Vertex Buffer"), vertices);
179
180        // Create offscreen framebuffer using the new Framebuffer abstraction
181        let offscreen_fb = Framebuffer::builder(400, 300)
182            .format(wgpu::TextureFormat::Rgba8UnormSrgb)
183            .label("Offscreen FB")
184            .build(&graphics_ctx);
185
186        // Create blit shader and pipeline for rendering framebuffer to surface
187        let blit_shader = renderer.create_shader(Some("Blit Shader"), BLIT_SHADER_SOURCE);
188
189        let blit_bind_group_layout = renderer.create_bind_group_layout(
190            Some("Blit Bind Group Layout"),
191            &[
192                wgpu::BindGroupLayoutEntry {
193                    binding: 0,
194                    visibility: wgpu::ShaderStages::FRAGMENT,
195                    ty: wgpu::BindingType::Texture {
196                        multisampled: false,
197                        view_dimension: wgpu::TextureViewDimension::D2,
198                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
199                    },
200                    count: None,
201                },
202                wgpu::BindGroupLayoutEntry {
203                    binding: 1,
204                    visibility: wgpu::ShaderStages::FRAGMENT,
205                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
206                    count: None,
207                },
208            ],
209        );
210
211        let blit_bind_group = renderer.create_bind_group(
212            Some("Blit Bind Group"),
213            &blit_bind_group_layout,
214            &[
215                wgpu::BindGroupEntry {
216                    binding: 0,
217                    resource: wgpu::BindingResource::TextureView(offscreen_fb.color_view()),
218                },
219                wgpu::BindGroupEntry {
220                    binding: 1,
221                    resource: wgpu::BindingResource::Sampler(&sampler),
222                },
223            ],
224        );
225
226        let blit_pipeline_layout = renderer.create_pipeline_layout(
227            Some("Blit Pipeline Layout"),
228            &[&blit_bind_group_layout],
229            &[],
230        );
231
232        let blit_pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
233            label: Some("Blit Pipeline"),
234            layout: Some(&blit_pipeline_layout),
235            vertex: wgpu::VertexState {
236                module: &blit_shader,
237                entry_point: Some("vs_main"),
238                buffers: &[],
239                compilation_options: wgpu::PipelineCompilationOptions::default(),
240            },
241            fragment: Some(wgpu::FragmentState {
242                module: &blit_shader,
243                entry_point: Some("fs_main"),
244                // Use PremultipliedAlpha for framebuffer blitting
245                targets: &[Some(
246                    BlendMode::PremultipliedAlpha
247                        .to_color_target_state(wgpu::TextureFormat::Bgra8UnormSrgb),
248                )],
249                compilation_options: wgpu::PipelineCompilationOptions::default(),
250            }),
251            primitive: wgpu::PrimitiveState {
252                topology: wgpu::PrimitiveTopology::TriangleList,
253                ..Default::default()
254            },
255            depth_stencil: None,
256            multisample: wgpu::MultisampleState::default(),
257            multiview: None,
258            cache: None,
259        });
260
261        tracing::info!("Renderer initialized successfully");
262        tracing::info!("Device: {:?}", renderer.context().info());
263
264        Box::new(RendererApp {
265            context: graphics_ctx,
266            renderer,
267            window,
268            window_id,
269            pipeline,
270            bind_group,
271            vertex_buffer,
272            offscreen_fb,
273            blit_pipeline,
274            blit_bind_group,
275            time: 0.0,
276        })
277    });
278}
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,