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
impl GraphicsContext
Sourcepub async fn new_owned() -> Result<Arc<Self>, GraphicsError>
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 cloneSourcepub fn new_owned_sync() -> Result<Arc<Self>, GraphicsError>
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?
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
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}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}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}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}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}Sourcepub async fn new_owned_with_descriptor(
descriptor: GraphicsContextDescriptor,
) -> Result<Arc<Self>, GraphicsError>
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?
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
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}Sourcepub fn device(&self) -> &Device
pub fn device(&self) -> &Device
Get a reference to the wgpu device.
Examples found in repository?
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
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}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}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}Sourcepub fn queue(&self) -> &Queue
pub fn queue(&self) -> &Queue
Get a reference to the wgpu queue.
Examples found in repository?
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
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 }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}Sourcepub fn info(&self) -> AdapterInfo
pub fn info(&self) -> AdapterInfo
Get device info
Examples found in repository?
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}Sourcepub fn wgpu_features(&self) -> Features
pub fn wgpu_features(&self) -> Features
Get raw wgpu device features
Sourcepub fn gpu_features(&self) -> GpuFeatures
pub fn gpu_features(&self) -> GpuFeatures
Get the enabled GPU features (high-level wrapper).
Sourcepub fn has_feature(&self, feature: GpuFeatures) -> bool
pub fn has_feature(&self, feature: GpuFeatures) -> bool
Check if a specific GPU feature is enabled.
Sourcepub fn has_all_features(&self, features: GpuFeatures) -> bool
pub fn has_all_features(&self, features: GpuFeatures) -> bool
Check if all specified GPU features are enabled.
Sourcepub fn require_feature(&self, feature: GpuFeatures)
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.
Sourcepub fn supports_texture_format(
&self,
format: TextureFormat,
usages: TextureUsages,
) -> bool
pub fn supports_texture_format( &self, format: TextureFormat, usages: TextureUsages, ) -> bool
Sourcepub fn texture_format_capabilities(
&self,
format: TextureFormat,
) -> TextureFormatFeatures
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
impl AsWgpu for GraphicsContext
Source§impl RenderContext for GraphicsContext
impl RenderContext for GraphicsContext
Source§fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> GpuBuffer
fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> GpuBuffer
Source§fn write_buffer(&self, buffer: &GpuBuffer, offset: u64, data: &[u8])
fn write_buffer(&self, buffer: &GpuBuffer, offset: u64, data: &[u8])
Source§fn create_texture(&self, desc: &TextureDescriptor<'_>) -> GpuTexture
fn create_texture(&self, desc: &TextureDescriptor<'_>) -> GpuTexture
Source§fn create_shader_module(
&self,
desc: &ShaderModuleDescriptor<'_>,
) -> GpuShaderModule
fn create_shader_module( &self, desc: &ShaderModuleDescriptor<'_>, ) -> GpuShaderModule
Source§fn create_render_pipeline(
&self,
desc: &RenderPipelineDescriptor<'_>,
) -> GpuRenderPipeline
fn create_render_pipeline( &self, desc: &RenderPipelineDescriptor<'_>, ) -> GpuRenderPipeline
Source§fn create_compute_pipeline(
&self,
desc: &ComputePipelineDescriptor<'_>,
) -> GpuComputePipeline
fn create_compute_pipeline( &self, desc: &ComputePipelineDescriptor<'_>, ) -> GpuComputePipeline
Source§fn create_bind_group_layout(
&self,
desc: &BindGroupLayoutDescriptor<'_>,
) -> GpuBindGroupLayout
fn create_bind_group_layout( &self, desc: &BindGroupLayoutDescriptor<'_>, ) -> GpuBindGroupLayout
Source§fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> GpuBindGroup
fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> GpuBindGroup
Source§fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> GpuSampler
fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> GpuSampler
Auto Trait Implementations§
impl Freeze for GraphicsContext
impl !RefUnwindSafe for GraphicsContext
impl Send for GraphicsContext
impl Sync for GraphicsContext
impl Unpin for GraphicsContext
impl !UnwindSafe for GraphicsContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&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
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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