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?
38fn main() {
39 logging::init();
40
41 run_app(|ctx| {
42 let graphics_ctx =
43 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
44 let mut window_manager = WindowManager::new(graphics_ctx);
45 let mut window_colors = HashMap::new();
46
47 // Create 3 windows with different colors
48 let colors = [
49 Color::rgb(0.8, 0.2, 0.2), // Red
50 Color::rgb(0.2, 0.8, 0.2), // Green
51 Color::rgb(0.2, 0.2, 0.8), // Blue
52 ];
53
54 for (i, color) in colors.iter().enumerate() {
55 let window_id = window_manager
56 .create_window_with_descriptor(
57 ctx,
58 WindowDescriptor {
59 title: format!("Window {} - WindowManager Demo", i + 1),
60 size: Some(WinitPhysicalSize::new(400.0, 300.0)),
61 ..Default::default()
62 },
63 WindowContextDescriptor {
64 format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
65 ..Default::default()
66 },
67 )
68 .expect("Failed to create window");
69
70 window_colors.insert(window_id, *color);
71 }
72
73 Box::new(WindowManagerApp {
74 window_manager,
75 window_colors,
76 })
77 });
78}More examples
25fn main() {
26 logging::init();
27
28 run_app(|ctx| {
29 let graphics_ctx =
30 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
31
32 let mut windows = HashMap::new();
33
34 // Create 3 windows with different colors
35 let colors = [
36 wgpu::Color {
37 r: 0.8,
38 g: 0.2,
39 b: 0.2,
40 a: 1.0,
41 },
42 wgpu::Color {
43 r: 0.2,
44 g: 0.8,
45 b: 0.2,
46 a: 1.0,
47 },
48 wgpu::Color {
49 r: 0.2,
50 g: 0.2,
51 b: 0.8,
52 a: 1.0,
53 },
54 ];
55
56 for (i, color) in colors.iter().enumerate() {
57 let window = ctx
58 .create_window(WindowDescriptor {
59 title: format!("Window {} - Multi-Window Example", i + 1),
60 size: Some(WinitPhysicalSize::new(400.0, 300.0)),
61 ..Default::default()
62 })
63 .expect("Failed to create window");
64
65 let renderable_window = RenderWindowBuilder::new()
66 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
67 .with_depth_default()
68 .build(window, graphics_ctx.clone())
69 .expect("Failed to create render window");
70
71 let window_id = renderable_window.id();
72 windows.insert(window_id, (renderable_window, *color));
73 }
74
75 Box::new(App { windows })
76 });
77}36fn main() {
37 logging::init();
38
39 run_app(|ctx| {
40 let graphics_ctx =
41 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
42
43 let window = ctx
44 .create_window(WindowDescriptor {
45 title: "Performance Benchmark - Render Stress Test".to_string(),
46 size: Some(WinitPhysicalSize::new(1280.0, 720.0)),
47 ..Default::default()
48 })
49 .expect("Failed to create window");
50
51 let window = RenderWindowBuilder::new()
52 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
53 .with_depth_default()
54 .build(window, graphics_ctx.clone())
55 .expect("Failed to create render window");
56
57 let window_id = window.id();
58
59 println!("\n═══════════════════════════════════════════════════════");
60 println!(" ⚡ PERFORMANCE BENCHMARK - Render Stress Test");
61 println!("═══════════════════════════════════════════════════════");
62 println!(" CONTROLS:");
63 println!(" [Space] Toggle rendering on/off");
64 println!(" [+/-] Increase/decrease object count");
65 println!(" Starting with 1000 objects");
66 println!("═══════════════════════════════════════════════════════\n");
67
68 Box::new(PerformanceBenchmark {
69 _context: graphics_ctx,
70 window,
71 window_id,
72 object_count: 1000,
73 rendering: true,
74 frame_count: 0,
75 last_fps_time: Instant::now(),
76 fps: 0.0,
77 last_frame_time: 0.0,
78 })
79 });
80}30fn main() {
31 logging::init();
32
33 run_app(|ctx| {
34 let graphics_ctx =
35 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
36
37 let window = ctx
38 .create_window(WindowDescriptor {
39 title: "Camera Demo - View & Projection".to_string(),
40 size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
41 ..Default::default()
42 })
43 .expect("Failed to create window");
44
45 let window = RenderWindowBuilder::new()
46 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
47 .with_depth_default()
48 .build(window, graphics_ctx.clone())
49 .expect("Failed to create render window");
50
51 let window_id = window.id();
52
53 println!("\n═══════════════════════════════════════════════════════");
54 println!(" 📹 CAMERA DEMO - View & Projection");
55 println!("═══════════════════════════════════════════════════════");
56 println!("\n CAMERA API FEATURES:");
57 println!(" • Orthographic cameras (2D, UI, isometric)");
58 println!(" • Perspective cameras (3D scenes)");
59 println!(" • View matrix (position, rotation, look-at)");
60 println!(" • Projection matrix (FOV, aspect, near/far planes)");
61 println!(" • Screen-to-world coordinate conversion");
62 println!(" • Camera movement helpers");
63 println!("\n CAMERA TYPES:");
64 println!(" • OrthographicCamera - 2D games, UI overlays");
65 println!(" camera.orthographic(left, right, bottom, top, near, far)");
66 println!(" • PerspectiveCamera - 3D scenes");
67 println!(" camera.perspective(fov, aspect, near, far)");
68 println!("\n Camera API Usage:");
69 println!(" let camera = Camera::new()");
70 println!(" .position(Vec3::new(0.0, 5.0, 10.0))");
71 println!(" .look_at(Vec3::ZERO)");
72 println!(" .perspective(60.0, aspect, 0.1, 100.0);");
73 println!(" let view_proj = camera.view_projection_matrix();");
74 println!("═══════════════════════════════════════════════════════\n");
75
76 tracing::info!("Camera demo initialized");
77
78 Box::new(CameraDemo {
79 _context: graphics_ctx,
80 window,
81 window_id,
82 })
83 });
84}29fn main() {
30 logging::init();
31
32 run_app(|ctx| {
33 let graphics_ctx =
34 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
35
36 let window = ctx
37 .create_window(WindowDescriptor {
38 title: "Render Graph Demo - Multi-Pass Rendering".to_string(),
39 size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
40 ..Default::default()
41 })
42 .expect("Failed to create window");
43
44 let window = RenderWindowBuilder::new()
45 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
46 .with_depth_default()
47 .build(window, graphics_ctx.clone())
48 .expect("Failed to create render window");
49
50 let window_id = window.id();
51
52 println!("\n═══════════════════════════════════════════════════════");
53 println!(" 🔀 RENDER GRAPH DEMO - Multi-Pass Rendering");
54 println!("═══════════════════════════════════════════════════════");
55 println!("\n RENDER GRAPH FEATURES:");
56 println!(" • Declarative pass definition");
57 println!(" • Automatic dependency resolution");
58 println!(" • Resource lifetime management");
59 println!(" • Parallel pass execution");
60 println!(" • Automatic optimization");
61 println!("\n EXAMPLE PIPELINE:");
62 println!(" 1. Shadow Pass → depth texture");
63 println!(" 2. Geometry Pass → color + normal + depth");
64 println!(" 3. Lighting Pass → lit scene");
65 println!(" 4. Post-Processing → bloom, tone mapping");
66 println!(" 5. UI Pass → final composite");
67 println!("\n Render Graph API Usage:");
68 println!(" let mut graph = RenderGraph::new();");
69 println!(" graph.add_pass(\"shadow\", shadow_pass_descriptor);");
70 println!(" graph.add_pass(\"geometry\", geometry_pass_descriptor);");
71 println!(" graph.add_dependency(\"lighting\", \"geometry\");");
72 println!(" graph.compile();");
73 println!(" graph.execute(&mut encoder);");
74 println!("═══════════════════════════════════════════════════════\n");
75
76 tracing::info!("Render graph demo initialized");
77
78 Box::new(RenderGraphDemo {
79 _context: graphics_ctx,
80 window,
81 window_id,
82 })
83 });
84}29fn main() {
30 logging::init();
31
32 run_app(|ctx| {
33 let graphics_ctx =
34 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
35
36 let window = ctx
37 .create_window(WindowDescriptor {
38 title: "Mesh Primitives Demo - Geometry API".to_string(),
39 size: Some(WinitPhysicalSize::new(1024.0, 768.0)),
40 ..Default::default()
41 })
42 .expect("Failed to create window");
43
44 let window = RenderWindowBuilder::new()
45 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
46 .with_depth_default()
47 .build(window, graphics_ctx.clone())
48 .expect("Failed to create render window");
49
50 let window_id = window.id();
51
52 println!("\n═══════════════════════════════════════════════════════");
53 println!(" 📐 MESH PRIMITIVES DEMO - Geometry API");
54 println!("═══════════════════════════════════════════════════════");
55 println!("\n MESH API FEATURES:");
56 println!(" • MeshBuilder for custom geometry");
57 println!(" • Primitive generation (cube, sphere, plane, etc.)");
58 println!(" • Flexible vertex formats (Position, Normal, UV, Color)");
59 println!(" • Index buffer optimization");
60 println!(" • Instanced rendering support");
61 println!("\n EXAMPLE PRIMITIVES:");
62 println!(" • Cube - box with 24 vertices (6 faces × 4 vertices)");
63 println!(" • Sphere - tessellated sphere with UV mapping");
64 println!(" • Plane - quad with optional subdivisions");
65 println!(" • Cylinder - sides + caps");
66 println!(" • Custom - arbitrary vertex/index data");
67 println!("\n Mesh API Usage:");
68 println!(" let mesh = MeshBuilder::new()");
69 println!(" .with_positions(vertices)");
70 println!(" .with_normals(normals)");
71 println!(" .with_uvs(uvs)");
72 println!(" .with_indices(indices)");
73 println!(" .build(&ctx);");
74 println!(" mesh.draw(&mut pass);");
75 println!(" mesh.draw_instanced(&mut pass, instance_count);");
76 println!("═══════════════════════════════════════════════════════\n");
77
78 tracing::info!("Mesh primitives demo initialized");
79
80 Box::new(MeshPrimitivesDemo {
81 _context: graphics_ctx,
82 window,
83 window_id,
84 })
85 });
86}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?
278fn main() {
279 logging::init();
280
281 run_app(|ctx| {
282 let tier_override = parse_tier();
283
284 // Use the capability API to configure GPU requirements.
285 // For auto-detect, request the best capability (graceful degradation).
286 // For a specific tier, require that tier's capability.
287 let descriptor = match tier_override {
288 None => GraphicsContextDescriptor::new().request_capability::<BestBatchCapability2D>(),
289 Some(RenderTier::Direct) => {
290 GraphicsContextDescriptor::new().require_capability::<DirectBatchCapability2D>()
291 }
292 Some(RenderTier::Indirect) => {
293 GraphicsContextDescriptor::new().require_capability::<IndirectBatchCapability2D>()
294 }
295 Some(RenderTier::Bindless) => {
296 GraphicsContextDescriptor::new().require_capability::<BindlessBatchCapability2D>()
297 }
298 };
299 let graphics_ctx =
300 pollster::block_on(GraphicsContext::new_owned_with_descriptor(descriptor))
301 .expect("Failed to create graphics context");
302
303 let window = ctx
304 .create_window(WindowDescriptor {
305 title: "Batched Renderer Example".to_string(),
306 size: Some(WinitPhysicalSize::new(800.0, 600.0)),
307 ..Default::default()
308 })
309 .expect("Failed to create window");
310
311 let surface_format = wgpu::TextureFormat::Bgra8UnormSrgb;
312
313 let renderable_window = RenderWindowBuilder::new()
314 .color_format(surface_format)
315 .with_depth_default()
316 .build(window, graphics_ctx.clone())
317 .expect("Failed to create render window");
318
319 let window_id = renderable_window.id();
320
321 let renderer =
322 create_batch_renderer_2d(graphics_ctx.clone(), surface_format, tier_override);
323
324 tracing::info!("Using render tier: {}", renderer.tier());
325
326 // Create initial depth buffer
327 let depth_texture = graphics_ctx
328 .device()
329 .create_texture(&wgpu::TextureDescriptor {
330 label: Some("example_depth"),
331 size: wgpu::Extent3d {
332 width: 1,
333 height: 1,
334 depth_or_array_layers: 1,
335 },
336 mip_level_count: 1,
337 sample_count: 1,
338 dimension: wgpu::TextureDimension::D2,
339 format: DEPTH_FORMAT,
340 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
341 view_formats: &[],
342 });
343 let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
344
345 let mut windows = HashMap::new();
346 windows.insert(window_id, renderable_window);
347
348 Box::new(App {
349 context: graphics_ctx,
350 windows,
351 renderer,
352 depth_texture,
353 depth_view,
354 depth_width: 1,
355 depth_height: 1,
356 frame_count: 0,
357 })
358 });
359}More examples
40fn main() {
41 logging::init();
42 init_profiling(ProfilingBackend::PuffinHttp);
43
44 run_app(|ctx| {
45 profile_function!();
46
47 // Request TIMESTAMP_QUERY for GPU profiling (best-effort, won't fail if unavailable)
48 let graphics_ctx = pollster::block_on(GraphicsContext::new_owned_with_descriptor(
49 GraphicsContextDescriptor::new()
50 .request_capability::<astrelis_render::gpu_profiling::GpuFrameProfiler>(),
51 ))
52 .expect("Failed to create graphics context");
53
54 let window = ctx
55 .create_window(WindowDescriptor {
56 title: "Profiling Demo — CPU + GPU".to_string(),
57 size: Some(WinitPhysicalSize::new(800.0, 600.0)),
58 ..Default::default()
59 })
60 .expect("Failed to create window");
61
62 #[allow(unused_mut)]
63 let mut window = RenderWindowBuilder::new()
64 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
65 .with_depth_default()
66 .build(window, graphics_ctx.clone())
67 .expect("Failed to create render window");
68
69 let window_id = window.id();
70
71 // Attach GPU profiler to the window — all frames will be automatically profiled
72 let has_gpu_profiling;
73 #[cfg(feature = "gpu-profiling")]
74 {
75 match astrelis_render::gpu_profiling::GpuFrameProfiler::new(&graphics_ctx) {
76 Ok(profiler) => {
77 let has_timestamps = profiler.has_timestamp_queries();
78 window.set_gpu_profiler(Arc::new(profiler));
79 has_gpu_profiling = true;
80 if has_timestamps {
81 println!(" GPU profiling: enabled with TIMESTAMP_QUERY (full timing)");
82 } else {
83 println!(" GPU profiling: enabled (debug groups only, no timing data)");
84 println!(" TIMESTAMP_QUERY not supported by this GPU");
85 }
86 }
87 Err(e) => {
88 has_gpu_profiling = false;
89 tracing::warn!("Failed to create GPU profiler: {e}. GPU profiling disabled.");
90 println!(" GPU profiling: failed to create profiler");
91 }
92 }
93 }
94 #[cfg(not(feature = "gpu-profiling"))]
95 {
96 has_gpu_profiling = false;
97 }
98
99 println!();
100 println!("═══════════════════════════════════════════════════");
101 println!(" PROFILING DEMO — CPU + GPU");
102 println!("═══════════════════════════════════════════════════");
103 println!();
104 println!(" CPU profiling: enabled (puffin)");
105 if !has_gpu_profiling {
106 #[cfg(not(feature = "gpu-profiling"))]
107 println!(" GPU profiling: disabled (compile with --features gpu-profiling)");
108 }
109 println!();
110 println!(" Open puffin_viewer at 127.0.0.1:8585 to see the flame graph.");
111 println!(" Install with: cargo install puffin_viewer");
112 println!("═══════════════════════════════════════════════════");
113 println!();
114
115 Box::new(ProfilingDemo {
116 context: graphics_ctx,
117 window,
118 window_id,
119 frame_count: 0,
120 })
121 });
122}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
55 .context
56 .device()
57 .create_texture(&wgpu::TextureDescriptor {
58 label: Some("example_depth"),
59 size: wgpu::Extent3d {
60 width: w,
61 height: h,
62 depth_or_array_layers: 1,
63 },
64 mip_level_count: 1,
65 sample_count: 1,
66 dimension: wgpu::TextureDimension::D2,
67 format: DEPTH_FORMAT,
68 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
69 view_formats: &[],
70 });
71 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
72 self.depth_texture = texture;
73 self.depth_view = view;
74 self.depth_width = w;
75 self.depth_height = h;
76 }
77
78 /// Build an orthographic projection matrix for the given viewport size.
79 /// Maps (0,0) at top-left to (width, height) at bottom-right.
80 /// Z range: 0.0 (far) to 1.0 (near), matching GreaterEqual depth compare.
81 fn ortho_projection(width: f32, height: f32) -> [[f32; 4]; 4] {
82 [
83 [2.0 / width, 0.0, 0.0, 0.0],
84 [0.0, -2.0 / height, 0.0, 0.0],
85 [0.0, 0.0, 1.0, 0.0],
86 [-1.0, 1.0, 0.0, 1.0],
87 ]
88 }
89
90 /// Generate the demo instances for the current frame.
91 fn build_instances(&self, width: f32, height: f32) -> Vec<UnifiedInstance2D> {
92 let t = self.frame_count as f32 / 60.0;
93 let mut instances = Vec::new();
94
95 // --- Background panel (gray, full viewport, furthest back) ---
96 instances.push(UnifiedInstance2D {
97 position: [10.0, 10.0],
98 size: [width - 20.0, height - 20.0],
99 color: [0.15, 0.15, 0.18, 1.0],
100 border_radius: 12.0,
101 z_depth: 0.01,
102 draw_type: DrawType2D::Quad as u32,
103 ..Default::default()
104 });
105
106 // --- Grid of colored quads ---
107 let cols = 5;
108 let rows = 3;
109 let margin = 30.0;
110 let gap = 10.0;
111 let cell_w = (width - 2.0 * margin - (cols as f32 - 1.0) * gap) / cols as f32;
112 let cell_h = (height * 0.5 - margin - (rows as f32 - 1.0) * gap) / rows as f32;
113
114 for row in 0..rows {
115 for col in 0..cols {
116 let x = margin + col as f32 * (cell_w + gap);
117 let y = margin + row as f32 * (cell_h + gap);
118 let idx = row * cols + col;
119
120 // Hue-shift color based on grid position
121 let hue = (idx as f32 / (rows * cols) as f32) * 360.0;
122 let (r, g, b) = hsl_to_rgb(hue, 0.7, 0.55);
123
124 instances.push(UnifiedInstance2D {
125 position: [x, y],
126 size: [cell_w, cell_h],
127 color: [r, g, b, 1.0],
128 border_radius: 6.0,
129 z_depth: 0.1 + idx as f32 * 0.001,
130 draw_type: DrawType2D::Quad as u32,
131 ..Default::default()
132 });
133 }
134 }
135
136 // --- Animated floating rounded rect ---
137 let float_x = width * 0.5 + (t * 0.8).sin() * width * 0.25 - 60.0;
138 let float_y = height * 0.35 + (t * 1.2).cos() * 30.0;
139 instances.push(UnifiedInstance2D {
140 position: [float_x, float_y],
141 size: [120.0, 50.0],
142 color: [1.0, 0.85, 0.2, 0.9],
143 border_radius: 25.0,
144 z_depth: 0.8,
145 draw_type: DrawType2D::Quad as u32,
146 ..Default::default()
147 });
148
149 // --- Border-only outlines (bottom area) ---
150 let outline_y = height * 0.6;
151 for i in 0..4 {
152 let x = margin + i as f32 * 140.0;
153 let thickness = 1.0 + i as f32;
154 let radius = 4.0 + i as f32 * 8.0;
155 instances.push(UnifiedInstance2D {
156 position: [x, outline_y],
157 size: [120.0, 80.0],
158 color: [0.4, 0.8, 1.0, 1.0],
159 border_radius: radius,
160 border_thickness: thickness,
161 z_depth: 0.5,
162 draw_type: DrawType2D::Quad as u32,
163 ..Default::default()
164 });
165 }
166
167 // --- Overlapping transparent quads (demonstrating depth + alpha) ---
168 let overlap_x = width * 0.5 - 100.0;
169 let overlap_y = height * 0.75;
170 let colors = [
171 [1.0, 0.3, 0.3, 0.6],
172 [0.3, 1.0, 0.3, 0.6],
173 [0.3, 0.3, 1.0, 0.6],
174 ];
175 for (i, color) in colors.iter().enumerate() {
176 let offset = i as f32 * 40.0;
177 instances.push(UnifiedInstance2D {
178 position: [overlap_x + offset, overlap_y + offset * 0.5],
179 size: [120.0, 80.0],
180 color: *color,
181 border_radius: 8.0,
182 z_depth: 0.6 + i as f32 * 0.05,
183 draw_type: DrawType2D::Quad as u32,
184 ..Default::default()
185 });
186 }
187
188 // --- Pulsing circle (via large border_radius) ---
189 let pulse = ((t * 2.0).sin() * 0.5 + 0.5) * 0.4 + 0.6;
190 let circle_size = 60.0 * pulse;
191 instances.push(UnifiedInstance2D {
192 position: [width - margin - circle_size, outline_y + 10.0],
193 size: [circle_size, circle_size],
194 color: [1.0, 0.5, 0.0, 0.95],
195 border_radius: circle_size * 0.5,
196 z_depth: 0.7,
197 draw_type: DrawType2D::Quad as u32,
198 ..Default::default()
199 });
200
201 // --- Small shader-clipped quad (demonstrating clip rect) ---
202 let clip_x = margin;
203 let clip_y = height * 0.75;
204 instances.push(UnifiedInstance2D {
205 position: [clip_x, clip_y],
206 size: [200.0, 60.0],
207 color: [0.9, 0.2, 0.7, 1.0],
208 border_radius: 4.0,
209 z_depth: 0.55,
210 draw_type: DrawType2D::Quad as u32,
211 // Clip to a smaller region to demonstrate shader clipping
212 clip_min: [clip_x + 20.0, clip_y + 10.0],
213 clip_max: [clip_x + 160.0, clip_y + 50.0],
214 ..Default::default()
215 });
216
217 instances
218 }
219}
220
221/// Simple HSL to RGB conversion.
222fn hsl_to_rgb(h: f32, s: f32, l: f32) -> (f32, f32, f32) {
223 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
224 let h_prime = h / 60.0;
225 let x = c * (1.0 - (h_prime % 2.0 - 1.0).abs());
226 let (r1, g1, b1) = if h_prime < 1.0 {
227 (c, x, 0.0)
228 } else if h_prime < 2.0 {
229 (x, c, 0.0)
230 } else if h_prime < 3.0 {
231 (0.0, c, x)
232 } else if h_prime < 4.0 {
233 (0.0, x, c)
234 } else if h_prime < 5.0 {
235 (x, 0.0, c)
236 } else {
237 (c, 0.0, x)
238 };
239 let m = l - c * 0.5;
240 (r1 + m, g1 + m, b1 + m)
241}
242
243/// Parse the `--tier` CLI argument to select a render tier.
244///
245/// The three tiers represent increasing levels of GPU capability:
246/// - **Tier 1 (Direct):** One `draw()` call per texture group. Works on all hardware.
247/// - **Tier 2 (Indirect):** Uses `multi_draw_indirect()` per texture group, batching
248/// draw calls into GPU-side indirect buffers. Requires `MULTI_DRAW_INDIRECT`.
249/// - **Tier 3 (Bindless):** Single `multi_draw_indirect()` per frame using texture
250/// binding arrays. Requires `TEXTURE_BINDING_ARRAY` + `MULTI_DRAW_INDIRECT`.
251///
252/// Passing `--tier auto` (or omitting the flag) lets the engine choose the best
253/// tier supported by the current GPU.
254fn parse_tier() -> Option<RenderTier> {
255 let args: Vec<String> = std::env::args().collect();
256 for (i, arg) in args.iter().enumerate() {
257 if arg == "--tier"
258 && let Some(value) = args.get(i + 1)
259 {
260 return match value.as_str() {
261 "1" | "direct" => Some(RenderTier::Direct),
262 "2" | "indirect" => Some(RenderTier::Indirect),
263 "3" | "bindless" => Some(RenderTier::Bindless),
264 "auto" => None,
265 other => {
266 eprintln!(
267 "Unknown tier '{other}'. Options: 1|direct, 2|indirect, 3|bindless, auto"
268 );
269 std::process::exit(1);
270 }
271 };
272 }
273 }
274 // Default: auto-detect
275 None
276}
277
278fn main() {
279 logging::init();
280
281 run_app(|ctx| {
282 let tier_override = parse_tier();
283
284 // Use the capability API to configure GPU requirements.
285 // For auto-detect, request the best capability (graceful degradation).
286 // For a specific tier, require that tier's capability.
287 let descriptor = match tier_override {
288 None => GraphicsContextDescriptor::new().request_capability::<BestBatchCapability2D>(),
289 Some(RenderTier::Direct) => {
290 GraphicsContextDescriptor::new().require_capability::<DirectBatchCapability2D>()
291 }
292 Some(RenderTier::Indirect) => {
293 GraphicsContextDescriptor::new().require_capability::<IndirectBatchCapability2D>()
294 }
295 Some(RenderTier::Bindless) => {
296 GraphicsContextDescriptor::new().require_capability::<BindlessBatchCapability2D>()
297 }
298 };
299 let graphics_ctx =
300 pollster::block_on(GraphicsContext::new_owned_with_descriptor(descriptor))
301 .expect("Failed to create graphics context");
302
303 let window = ctx
304 .create_window(WindowDescriptor {
305 title: "Batched Renderer Example".to_string(),
306 size: Some(WinitPhysicalSize::new(800.0, 600.0)),
307 ..Default::default()
308 })
309 .expect("Failed to create window");
310
311 let surface_format = wgpu::TextureFormat::Bgra8UnormSrgb;
312
313 let renderable_window = RenderWindowBuilder::new()
314 .color_format(surface_format)
315 .with_depth_default()
316 .build(window, graphics_ctx.clone())
317 .expect("Failed to create render window");
318
319 let window_id = renderable_window.id();
320
321 let renderer =
322 create_batch_renderer_2d(graphics_ctx.clone(), surface_format, tier_override);
323
324 tracing::info!("Using render tier: {}", renderer.tier());
325
326 // Create initial depth buffer
327 let depth_texture = graphics_ctx
328 .device()
329 .create_texture(&wgpu::TextureDescriptor {
330 label: Some("example_depth"),
331 size: wgpu::Extent3d {
332 width: 1,
333 height: 1,
334 depth_or_array_layers: 1,
335 },
336 mip_level_count: 1,
337 sample_count: 1,
338 dimension: wgpu::TextureDimension::D2,
339 format: DEPTH_FORMAT,
340 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
341 view_formats: &[],
342 });
343 let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
344
345 let mut windows = HashMap::new();
346 windows.insert(window_id, renderable_window);
347
348 Box::new(App {
349 context: graphics_ctx,
350 windows,
351 renderer,
352 depth_texture,
353 depth_view,
354 depth_width: 1,
355 depth_height: 1,
356 frame_count: 0,
357 })
358 });
359}More examples
152fn main() {
153 logging::init();
154
155 run_app(|ctx| {
156 let graphics_ctx =
157 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
158 let mut windows = HashMap::new();
159
160 let scale = Window::platform_dpi() as f32;
161 let window = ctx
162 .create_window(WindowDescriptor {
163 title: "Sprite Sheet Animation Example".to_string(),
164 size: Some(WinitPhysicalSize::new(400.0 * scale, 400.0 * scale)),
165 ..Default::default()
166 })
167 .expect("Failed to create window");
168
169 let renderable_window = RenderWindowBuilder::new()
170 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
171 .with_depth_default()
172 .build(window, graphics_ctx.clone())
173 .expect("Failed to create render window");
174
175 let window_id = renderable_window.id();
176 windows.insert(window_id, renderable_window);
177
178 // Generate sprite sheet
179 let (sprite_data, tex_width, tex_height) = generate_sprite_sheet_data();
180 let sprite_sheet = SpriteSheet::from_data(
181 &graphics_ctx,
182 &sprite_data,
183 tex_width,
184 tex_height,
185 SpriteSheetDescriptor {
186 sprite_width: 64,
187 sprite_height: 64,
188 columns: 4,
189 rows: 1,
190 ..Default::default()
191 },
192 );
193
194 // Create animation (4 frames at 8 fps)
195 let animation = SpriteAnimation::new(4, 8.0);
196
197 // Create shader module
198 let shader = graphics_ctx
199 .device()
200 .create_shader_module(wgpu::ShaderModuleDescriptor {
201 label: Some("Sprite Shader"),
202 source: wgpu::ShaderSource::Wgsl(SHADER.into()),
203 });
204
205 // Create bind group layout
206 let bind_group_layout =
207 graphics_ctx
208 .device()
209 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
210 label: Some("Sprite Bind Group Layout"),
211 entries: &[
212 wgpu::BindGroupLayoutEntry {
213 binding: 0,
214 visibility: wgpu::ShaderStages::VERTEX,
215 ty: wgpu::BindingType::Buffer {
216 ty: wgpu::BufferBindingType::Uniform,
217 has_dynamic_offset: false,
218 min_binding_size: None,
219 },
220 count: None,
221 },
222 wgpu::BindGroupLayoutEntry {
223 binding: 1,
224 visibility: wgpu::ShaderStages::FRAGMENT,
225 ty: wgpu::BindingType::Texture {
226 sample_type: wgpu::TextureSampleType::Float { filterable: true },
227 view_dimension: wgpu::TextureViewDimension::D2,
228 multisampled: false,
229 },
230 count: None,
231 },
232 wgpu::BindGroupLayoutEntry {
233 binding: 2,
234 visibility: wgpu::ShaderStages::FRAGMENT,
235 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
236 count: None,
237 },
238 ],
239 });
240
241 // Create pipeline layout
242 let pipeline_layout =
243 graphics_ctx
244 .device()
245 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
246 label: Some("Sprite Pipeline Layout"),
247 bind_group_layouts: &[&bind_group_layout],
248 push_constant_ranges: &[],
249 });
250
251 // Create render pipeline
252 let pipeline =
253 graphics_ctx
254 .device()
255 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
256 label: Some("Sprite Pipeline"),
257 layout: Some(&pipeline_layout),
258 vertex: wgpu::VertexState {
259 module: &shader,
260 entry_point: Some("vs_main"),
261 buffers: &[wgpu::VertexBufferLayout {
262 array_stride: std::mem::size_of::<Vertex>() as u64,
263 step_mode: wgpu::VertexStepMode::Vertex,
264 attributes: &[
265 wgpu::VertexAttribute {
266 offset: 0,
267 shader_location: 0,
268 format: wgpu::VertexFormat::Float32x2,
269 },
270 wgpu::VertexAttribute {
271 offset: 8,
272 shader_location: 1,
273 format: wgpu::VertexFormat::Float32x2,
274 },
275 ],
276 }],
277 compilation_options: wgpu::PipelineCompilationOptions::default(),
278 },
279 fragment: Some(wgpu::FragmentState {
280 module: &shader,
281 entry_point: Some("fs_main"),
282 targets: &[Some(wgpu::ColorTargetState {
283 format: wgpu::TextureFormat::Bgra8UnormSrgb,
284 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
285 write_mask: wgpu::ColorWrites::ALL,
286 })],
287 compilation_options: wgpu::PipelineCompilationOptions::default(),
288 }),
289 primitive: wgpu::PrimitiveState {
290 topology: wgpu::PrimitiveTopology::TriangleList,
291 ..Default::default()
292 },
293 depth_stencil: None,
294 multisample: wgpu::MultisampleState::default(),
295 multiview: None,
296 cache: None,
297 });
298
299 // Create uniform buffer
300 let uniforms = Uniforms {
301 mvp: [
302 [1.0, 0.0, 0.0, 0.0],
303 [0.0, 1.0, 0.0, 0.0],
304 [0.0, 0.0, 1.0, 0.0],
305 [0.0, 0.0, 0.0, 1.0],
306 ],
307 };
308 let uniform_buffer =
309 graphics_ctx
310 .device()
311 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
312 label: Some("Uniform Buffer"),
313 contents: bytemuck::cast_slice(&[uniforms]),
314 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
315 });
316
317 // Create sampler
318 let sampler = graphics_ctx
319 .device()
320 .create_sampler(&wgpu::SamplerDescriptor {
321 label: Some("Sprite Sampler"),
322 mag_filter: wgpu::FilterMode::Linear,
323 min_filter: wgpu::FilterMode::Linear,
324 ..Default::default()
325 });
326
327 // Create bind group
328 let bind_group = graphics_ctx
329 .device()
330 .create_bind_group(&wgpu::BindGroupDescriptor {
331 label: Some("Sprite Bind Group"),
332 layout: &bind_group_layout,
333 entries: &[
334 wgpu::BindGroupEntry {
335 binding: 0,
336 resource: uniform_buffer.as_entire_binding(),
337 },
338 wgpu::BindGroupEntry {
339 binding: 1,
340 resource: wgpu::BindingResource::TextureView(sprite_sheet.view()),
341 },
342 wgpu::BindGroupEntry {
343 binding: 2,
344 resource: wgpu::BindingResource::Sampler(&sampler),
345 },
346 ],
347 });
348
349 // Initial vertex buffer (will be updated each frame with new UVs)
350 let vertices = create_quad_vertices(0.0, 0.0, 1.0, 1.0);
351 let vertex_buffer =
352 graphics_ctx
353 .device()
354 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
355 label: Some("Vertex Buffer"),
356 contents: bytemuck::cast_slice(&vertices),
357 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
358 });
359
360 Box::new(App {
361 _context: graphics_ctx,
362 windows,
363 pipeline,
364 bind_group,
365 vertex_buffer,
366 _uniform_buffer: uniform_buffer,
367 sprite_sheet,
368 animation,
369 last_update: Instant::now(),
370 })
371 });
372}22fn main() {
23 logging::init();
24
25 run_app(|ctx| {
26 let graphics_ctx =
27 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
28
29 let window = ctx
30 .create_window(WindowDescriptor {
31 title: "Textured Window".to_string(),
32 ..Default::default()
33 })
34 .expect("Failed to create window");
35
36 let window = RenderWindowBuilder::new()
37 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
38 .with_depth_default()
39 .build(window, graphics_ctx.clone())
40 .expect("Failed to create render window");
41
42 // --- Shader ---
43 let shader = graphics_ctx
44 .device()
45 .create_shader_module(wgpu::ShaderModuleDescriptor {
46 label: Some("Texture Shader"),
47 source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
48 });
49
50 // --- Texture Upload ---
51 // Create a 256x256 RGBA texture with a procedural gradient pattern.
52 // Rgba8UnormSrgb applies sRGB gamma correction so colors appear correct on screen.
53 let texture_size = wgpu::Extent3d {
54 width: 256,
55 height: 256,
56 depth_or_array_layers: 1,
57 };
58
59 let texture = graphics_ctx
60 .device()
61 .create_texture(&wgpu::TextureDescriptor {
62 label: Some("Example Texture"),
63 size: texture_size,
64 mip_level_count: 1,
65 sample_count: 1,
66 dimension: wgpu::TextureDimension::D2,
67 format: wgpu::TextureFormat::Rgba8UnormSrgb,
68 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
69 view_formats: &[],
70 });
71
72 let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
73 for y in 0..256 {
74 for x in 0..256 {
75 let idx = ((y * 256 + x) * 4) as usize;
76 texture_data[idx] = x as u8;
77 texture_data[idx + 1] = y as u8;
78 texture_data[idx + 2] = ((x + y) / 2) as u8;
79 texture_data[idx + 3] = 255;
80 }
81 }
82
83 graphics_ctx.queue().write_texture(
84 wgpu::TexelCopyTextureInfo {
85 texture: &texture,
86 mip_level: 0,
87 origin: wgpu::Origin3d::ZERO,
88 aspect: wgpu::TextureAspect::All,
89 },
90 &texture_data,
91 wgpu::TexelCopyBufferLayout {
92 offset: 0,
93 bytes_per_row: Some(256 * 4),
94 rows_per_image: Some(256),
95 },
96 texture_size,
97 );
98
99 // --- Sampler ---
100 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
101 // ClampToEdge prevents sampling beyond texture edges; Linear mag filter
102 // gives smooth results when the quad is larger than the texture.
103 let sampler = graphics_ctx
104 .device()
105 .create_sampler(&wgpu::SamplerDescriptor {
106 address_mode_u: wgpu::AddressMode::ClampToEdge,
107 address_mode_v: wgpu::AddressMode::ClampToEdge,
108 address_mode_w: wgpu::AddressMode::ClampToEdge,
109 mag_filter: wgpu::FilterMode::Linear,
110 min_filter: wgpu::FilterMode::Nearest,
111 mipmap_filter: wgpu::FilterMode::Nearest,
112 ..Default::default()
113 });
114
115 // --- Bind Group ---
116 // Binding 0: texture (Float filterable = supports linear sampling)
117 // Binding 1: sampler (Filtering = pairs with filterable textures)
118 let bind_group_layout =
119 graphics_ctx
120 .device()
121 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
122 label: Some("Texture Bind Group Layout"),
123 entries: &[
124 wgpu::BindGroupLayoutEntry {
125 binding: 0,
126 visibility: wgpu::ShaderStages::FRAGMENT,
127 ty: wgpu::BindingType::Texture {
128 multisampled: false,
129 view_dimension: wgpu::TextureViewDimension::D2,
130 sample_type: wgpu::TextureSampleType::Float { filterable: true },
131 },
132 count: None,
133 },
134 wgpu::BindGroupLayoutEntry {
135 binding: 1,
136 visibility: wgpu::ShaderStages::FRAGMENT,
137 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
138 count: None,
139 },
140 ],
141 });
142
143 let bind_group = graphics_ctx
144 .device()
145 .create_bind_group(&wgpu::BindGroupDescriptor {
146 label: Some("Texture Bind Group"),
147 layout: &bind_group_layout,
148 entries: &[
149 wgpu::BindGroupEntry {
150 binding: 0,
151 resource: wgpu::BindingResource::TextureView(&texture_view),
152 },
153 wgpu::BindGroupEntry {
154 binding: 1,
155 resource: wgpu::BindingResource::Sampler(&sampler),
156 },
157 ],
158 });
159
160 // --- Pipeline ---
161 let pipeline_layout =
162 graphics_ctx
163 .device()
164 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
165 label: Some("Render Pipeline Layout"),
166 bind_group_layouts: &[&bind_group_layout],
167 push_constant_ranges: &[],
168 });
169
170 let pipeline =
171 graphics_ctx
172 .device()
173 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
174 label: Some("Render Pipeline"),
175 layout: Some(&pipeline_layout),
176 vertex: wgpu::VertexState {
177 module: &shader,
178 entry_point: Some("vs_main"),
179 buffers: &[wgpu::VertexBufferLayout {
180 // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
181 array_stride: 4 * 4,
182 step_mode: wgpu::VertexStepMode::Vertex,
183 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
184 }],
185 compilation_options: wgpu::PipelineCompilationOptions::default(),
186 },
187 fragment: Some(wgpu::FragmentState {
188 module: &shader,
189 entry_point: Some("fs_main"),
190 targets: &[Some(wgpu::ColorTargetState {
191 format: wgpu::TextureFormat::Bgra8UnormSrgb,
192 blend: Some(wgpu::BlendState::REPLACE),
193 write_mask: wgpu::ColorWrites::ALL,
194 })],
195 compilation_options: wgpu::PipelineCompilationOptions::default(),
196 }),
197 primitive: wgpu::PrimitiveState {
198 topology: wgpu::PrimitiveTopology::TriangleList,
199 strip_index_format: None,
200 front_face: wgpu::FrontFace::Ccw,
201 cull_mode: Some(wgpu::Face::Back),
202 polygon_mode: wgpu::PolygonMode::Fill,
203 unclipped_depth: false,
204 conservative: false,
205 },
206 depth_stencil: None,
207 multisample: wgpu::MultisampleState {
208 count: 1,
209 mask: !0,
210 alpha_to_coverage_enabled: false,
211 },
212 multiview: None,
213 cache: None,
214 });
215
216 #[rustfmt::skip]
217 let vertices: &[f32] = &[
218 -0.8, -0.8, 0.0, 1.0,
219 0.8, -0.8, 1.0, 1.0,
220 0.8, 0.8, 1.0, 0.0,
221 -0.8, -0.8, 0.0, 1.0,
222 0.8, 0.8, 1.0, 0.0,
223 -0.8, 0.8, 0.0, 0.0,
224 ];
225
226 let vertex_buffer = graphics_ctx
227 .device()
228 .create_buffer(&wgpu::BufferDescriptor {
229 label: Some("Vertex Buffer"),
230 size: std::mem::size_of_val(vertices) as u64,
231 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
232 mapped_at_creation: false,
233 });
234
235 graphics_ctx
236 .queue()
237 .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
238
239 let window_id = window.id();
240
241 Box::new(App {
242 window,
243 window_id,
244 pipeline,
245 bind_group,
246 vertex_buffer,
247 })
248 });
249}163fn main() {
164 logging::init();
165
166 run_app(|ctx| {
167 let graphics_ctx =
168 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
169 let mut windows = HashMap::new();
170
171 let scale = Window::platform_dpi() as f32;
172 let window = ctx
173 .create_window(WindowDescriptor {
174 title: "Image Blitting Example".to_string(),
175 size: Some(WinitPhysicalSize::new(800.0 * scale, 600.0 * scale)),
176 ..Default::default()
177 })
178 .expect("Failed to create window");
179
180 let renderable_window = RenderWindowBuilder::new()
181 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
182 .with_depth_default()
183 .build(window, graphics_ctx.clone())
184 .expect("Failed to create render window");
185
186 let window_id = renderable_window.id();
187 windows.insert(window_id, renderable_window);
188
189 // Create shader module
190 let shader = graphics_ctx
191 .device()
192 .create_shader_module(wgpu::ShaderModuleDescriptor {
193 label: Some("Blit Shader"),
194 source: wgpu::ShaderSource::Wgsl(SHADER.into()),
195 });
196
197 // Create bind group layout
198 let bind_group_layout =
199 graphics_ctx
200 .device()
201 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
202 label: Some("Blit Bind Group Layout"),
203 entries: &[
204 wgpu::BindGroupLayoutEntry {
205 binding: 0,
206 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
207 ty: wgpu::BindingType::Buffer {
208 ty: wgpu::BufferBindingType::Uniform,
209 has_dynamic_offset: false,
210 min_binding_size: None,
211 },
212 count: None,
213 },
214 wgpu::BindGroupLayoutEntry {
215 binding: 1,
216 visibility: wgpu::ShaderStages::FRAGMENT,
217 ty: wgpu::BindingType::Texture {
218 sample_type: wgpu::TextureSampleType::Float { filterable: true },
219 view_dimension: wgpu::TextureViewDimension::D2,
220 multisampled: false,
221 },
222 count: None,
223 },
224 wgpu::BindGroupLayoutEntry {
225 binding: 2,
226 visibility: wgpu::ShaderStages::FRAGMENT,
227 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
228 count: None,
229 },
230 ],
231 });
232
233 // Create pipeline layout
234 let pipeline_layout =
235 graphics_ctx
236 .device()
237 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
238 label: Some("Blit Pipeline Layout"),
239 bind_group_layouts: &[&bind_group_layout],
240 push_constant_ranges: &[],
241 });
242
243 // Create render pipeline
244 let pipeline =
245 graphics_ctx
246 .device()
247 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
248 label: Some("Blit Pipeline"),
249 layout: Some(&pipeline_layout),
250 vertex: wgpu::VertexState {
251 module: &shader,
252 entry_point: Some("vs_main"),
253 buffers: &[wgpu::VertexBufferLayout {
254 array_stride: std::mem::size_of::<Vertex>() as u64,
255 step_mode: wgpu::VertexStepMode::Vertex,
256 attributes: &[
257 wgpu::VertexAttribute {
258 offset: 0,
259 shader_location: 0,
260 format: wgpu::VertexFormat::Float32x2,
261 },
262 wgpu::VertexAttribute {
263 offset: 8,
264 shader_location: 1,
265 format: wgpu::VertexFormat::Float32x2,
266 },
267 ],
268 }],
269 compilation_options: wgpu::PipelineCompilationOptions::default(),
270 },
271 fragment: Some(wgpu::FragmentState {
272 module: &shader,
273 entry_point: Some("fs_main"),
274 targets: &[Some(wgpu::ColorTargetState {
275 format: wgpu::TextureFormat::Bgra8UnormSrgb,
276 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
277 write_mask: wgpu::ColorWrites::ALL,
278 })],
279 compilation_options: wgpu::PipelineCompilationOptions::default(),
280 }),
281 primitive: wgpu::PrimitiveState {
282 topology: wgpu::PrimitiveTopology::TriangleList,
283 strip_index_format: None,
284 front_face: wgpu::FrontFace::Ccw,
285 cull_mode: None,
286 polygon_mode: wgpu::PolygonMode::Fill,
287 unclipped_depth: false,
288 conservative: false,
289 },
290 depth_stencil: None,
291 multisample: wgpu::MultisampleState::default(),
292 multiview: None,
293 cache: None,
294 });
295
296 // Create vertex buffer for a fullscreen quad
297 let vertices = [
298 Vertex {
299 position: [-0.8, -0.8],
300 uv: [0.0, 1.0],
301 },
302 Vertex {
303 position: [0.8, -0.8],
304 uv: [1.0, 1.0],
305 },
306 Vertex {
307 position: [0.8, 0.8],
308 uv: [1.0, 0.0],
309 },
310 Vertex {
311 position: [-0.8, -0.8],
312 uv: [0.0, 1.0],
313 },
314 Vertex {
315 position: [0.8, 0.8],
316 uv: [1.0, 0.0],
317 },
318 Vertex {
319 position: [-0.8, 0.8],
320 uv: [0.0, 0.0],
321 },
322 ];
323 let vertex_buffer =
324 graphics_ctx
325 .device()
326 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
327 label: Some("Vertex Buffer"),
328 contents: bytemuck::cast_slice(&vertices),
329 usage: wgpu::BufferUsages::VERTEX,
330 });
331
332 // Create uniform buffer
333 let uniforms = Uniforms {
334 mvp: [
335 [1.0, 0.0, 0.0, 0.0],
336 [0.0, 1.0, 0.0, 0.0],
337 [0.0, 0.0, 1.0, 0.0],
338 [0.0, 0.0, 0.0, 1.0],
339 ],
340 tint: [1.0, 1.0, 1.0, 1.0],
341 };
342 let uniform_buffer =
343 graphics_ctx
344 .device()
345 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
346 label: Some("Uniform Buffer"),
347 contents: bytemuck::cast_slice(&[uniforms]),
348 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
349 });
350
351 // Create CPU-side image buffer
352 let mut image_buffer = ImageBuffer::new(256, 256);
353 image_buffer.clear(30, 30, 40, 255);
354
355 // Create GPU texture
356 let texture = graphics_ctx
357 .device()
358 .create_texture(&wgpu::TextureDescriptor {
359 label: Some("Blit Texture"),
360 size: wgpu::Extent3d {
361 width: 256,
362 height: 256,
363 depth_or_array_layers: 1,
364 },
365 mip_level_count: 1,
366 sample_count: 1,
367 dimension: wgpu::TextureDimension::D2,
368 format: wgpu::TextureFormat::Rgba8UnormSrgb,
369 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
370 view_formats: &[],
371 });
372
373 // Create texture view and sampler
374 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
375 let sampler = graphics_ctx
376 .device()
377 .create_sampler(&wgpu::SamplerDescriptor {
378 label: Some("Blit Sampler"),
379 address_mode_u: wgpu::AddressMode::ClampToEdge,
380 address_mode_v: wgpu::AddressMode::ClampToEdge,
381 address_mode_w: wgpu::AddressMode::ClampToEdge,
382 mag_filter: wgpu::FilterMode::Nearest, // Pixel-perfect rendering
383 min_filter: wgpu::FilterMode::Nearest,
384 mipmap_filter: wgpu::FilterMode::Nearest,
385 ..Default::default()
386 });
387
388 // Create bind group
389 let bind_group = graphics_ctx
390 .device()
391 .create_bind_group(&wgpu::BindGroupDescriptor {
392 label: Some("Blit Bind Group"),
393 layout: &bind_group_layout,
394 entries: &[
395 wgpu::BindGroupEntry {
396 binding: 0,
397 resource: uniform_buffer.as_entire_binding(),
398 },
399 wgpu::BindGroupEntry {
400 binding: 1,
401 resource: wgpu::BindingResource::TextureView(&texture_view),
402 },
403 wgpu::BindGroupEntry {
404 binding: 2,
405 resource: wgpu::BindingResource::Sampler(&sampler),
406 },
407 ],
408 });
409
410 Box::new(App {
411 context: graphics_ctx,
412 windows,
413 pipeline,
414 _bind_group_layout: bind_group_layout,
415 vertex_buffer,
416 texture,
417 bind_group,
418 _uniform_buffer: uniform_buffer,
419 image_buffer,
420 start_time: Instant::now(),
421 })
422 });
423}Sourcepub fn queue(&self) -> &Queue
pub fn queue(&self) -> &Queue
Get a reference to the wgpu queue.
Examples found in repository?
404 fn update(
405 &mut self,
406 _ctx: &mut astrelis_winit::app::AppCtx,
407 _time: &astrelis_winit::FrameTime,
408 ) {
409 let now = Instant::now();
410 let dt = now.duration_since(self.last_update).as_secs_f32();
411 self.last_update = now;
412
413 // Update animation
414 if self.animation.update(dt) {
415 // Frame changed - update vertex buffer with new UVs
416 let frame = self.animation.current_frame();
417 let uv = self.sprite_sheet.sprite_uv(frame);
418 let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
419
420 // Get context from first window
421 if let Some(window) = self.windows.values().next() {
422 window.context().graphics_context().queue().write_buffer(
423 &self.vertex_buffer,
424 0,
425 bytemuck::cast_slice(&vertices),
426 );
427 }
428 }
429 }More examples
426 fn update(
427 &mut self,
428 _ctx: &mut astrelis_winit::app::AppCtx,
429 _time: &astrelis_winit::FrameTime,
430 ) {
431 let time = self.start_time.elapsed().as_secs_f32();
432
433 // Animate the CPU-side image buffer
434 self.image_buffer.clear(30, 30, 40, 255);
435
436 // Draw animated gradient background
437 let phase = (time * 0.5).sin() * 0.5 + 0.5;
438 let r1 = (50.0 + phase * 50.0) as u8;
439 let b1 = (80.0 + (1.0 - phase) * 50.0) as u8;
440 self.image_buffer
441 .gradient_h(0, 256, [r1, 40, b1], [40, r1, b1]);
442
443 // Draw bouncing circles
444 for i in 0..5 {
445 let offset = i as f32 * 0.4;
446 let x = 128.0 + (time * 2.0 + offset).sin() * 80.0;
447 let y = 128.0 + (time * 3.0 + offset).cos() * 80.0;
448 let hue = (time * 0.5 + offset) % 1.0;
449 let (r, g, b) = hsv_to_rgb(hue, 0.8, 1.0);
450 self.image_buffer
451 .fill_circle(x as i32, y as i32, 20, [r, g, b, 255]);
452 }
453
454 // Draw animated rectangles
455 for i in 0..3 {
456 let x = ((time * (1.0 + i as f32 * 0.3)).sin() * 100.0 + 128.0) as u32;
457 let y = 20 + i * 80;
458 let w = 30 + (time.sin() * 10.0) as u32;
459 let h = 20;
460 self.image_buffer
461 .fill_rect(x.saturating_sub(w / 2), y, w, h, [255, 255, 255, 200]);
462 }
463
464 // Upload to GPU (this is the "blit" operation)
465 self.context.queue().write_texture(
466 wgpu::TexelCopyTextureInfo {
467 texture: &self.texture,
468 mip_level: 0,
469 origin: wgpu::Origin3d::ZERO,
470 aspect: wgpu::TextureAspect::All,
471 },
472 &self.image_buffer.pixels,
473 wgpu::TexelCopyBufferLayout {
474 offset: 0,
475 bytes_per_row: Some(self.image_buffer.width * 4),
476 rows_per_image: Some(self.image_buffer.height),
477 },
478 wgpu::Extent3d {
479 width: self.image_buffer.width,
480 height: self.image_buffer.height,
481 depth_or_array_layers: 1,
482 },
483 );
484 }22fn main() {
23 logging::init();
24
25 run_app(|ctx| {
26 let graphics_ctx =
27 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
28
29 let window = ctx
30 .create_window(WindowDescriptor {
31 title: "Textured Window".to_string(),
32 ..Default::default()
33 })
34 .expect("Failed to create window");
35
36 let window = RenderWindowBuilder::new()
37 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
38 .with_depth_default()
39 .build(window, graphics_ctx.clone())
40 .expect("Failed to create render window");
41
42 // --- Shader ---
43 let shader = graphics_ctx
44 .device()
45 .create_shader_module(wgpu::ShaderModuleDescriptor {
46 label: Some("Texture Shader"),
47 source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
48 });
49
50 // --- Texture Upload ---
51 // Create a 256x256 RGBA texture with a procedural gradient pattern.
52 // Rgba8UnormSrgb applies sRGB gamma correction so colors appear correct on screen.
53 let texture_size = wgpu::Extent3d {
54 width: 256,
55 height: 256,
56 depth_or_array_layers: 1,
57 };
58
59 let texture = graphics_ctx
60 .device()
61 .create_texture(&wgpu::TextureDescriptor {
62 label: Some("Example Texture"),
63 size: texture_size,
64 mip_level_count: 1,
65 sample_count: 1,
66 dimension: wgpu::TextureDimension::D2,
67 format: wgpu::TextureFormat::Rgba8UnormSrgb,
68 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
69 view_formats: &[],
70 });
71
72 let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
73 for y in 0..256 {
74 for x in 0..256 {
75 let idx = ((y * 256 + x) * 4) as usize;
76 texture_data[idx] = x as u8;
77 texture_data[idx + 1] = y as u8;
78 texture_data[idx + 2] = ((x + y) / 2) as u8;
79 texture_data[idx + 3] = 255;
80 }
81 }
82
83 graphics_ctx.queue().write_texture(
84 wgpu::TexelCopyTextureInfo {
85 texture: &texture,
86 mip_level: 0,
87 origin: wgpu::Origin3d::ZERO,
88 aspect: wgpu::TextureAspect::All,
89 },
90 &texture_data,
91 wgpu::TexelCopyBufferLayout {
92 offset: 0,
93 bytes_per_row: Some(256 * 4),
94 rows_per_image: Some(256),
95 },
96 texture_size,
97 );
98
99 // --- Sampler ---
100 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
101 // ClampToEdge prevents sampling beyond texture edges; Linear mag filter
102 // gives smooth results when the quad is larger than the texture.
103 let sampler = graphics_ctx
104 .device()
105 .create_sampler(&wgpu::SamplerDescriptor {
106 address_mode_u: wgpu::AddressMode::ClampToEdge,
107 address_mode_v: wgpu::AddressMode::ClampToEdge,
108 address_mode_w: wgpu::AddressMode::ClampToEdge,
109 mag_filter: wgpu::FilterMode::Linear,
110 min_filter: wgpu::FilterMode::Nearest,
111 mipmap_filter: wgpu::FilterMode::Nearest,
112 ..Default::default()
113 });
114
115 // --- Bind Group ---
116 // Binding 0: texture (Float filterable = supports linear sampling)
117 // Binding 1: sampler (Filtering = pairs with filterable textures)
118 let bind_group_layout =
119 graphics_ctx
120 .device()
121 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
122 label: Some("Texture Bind Group Layout"),
123 entries: &[
124 wgpu::BindGroupLayoutEntry {
125 binding: 0,
126 visibility: wgpu::ShaderStages::FRAGMENT,
127 ty: wgpu::BindingType::Texture {
128 multisampled: false,
129 view_dimension: wgpu::TextureViewDimension::D2,
130 sample_type: wgpu::TextureSampleType::Float { filterable: true },
131 },
132 count: None,
133 },
134 wgpu::BindGroupLayoutEntry {
135 binding: 1,
136 visibility: wgpu::ShaderStages::FRAGMENT,
137 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
138 count: None,
139 },
140 ],
141 });
142
143 let bind_group = graphics_ctx
144 .device()
145 .create_bind_group(&wgpu::BindGroupDescriptor {
146 label: Some("Texture Bind Group"),
147 layout: &bind_group_layout,
148 entries: &[
149 wgpu::BindGroupEntry {
150 binding: 0,
151 resource: wgpu::BindingResource::TextureView(&texture_view),
152 },
153 wgpu::BindGroupEntry {
154 binding: 1,
155 resource: wgpu::BindingResource::Sampler(&sampler),
156 },
157 ],
158 });
159
160 // --- Pipeline ---
161 let pipeline_layout =
162 graphics_ctx
163 .device()
164 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
165 label: Some("Render Pipeline Layout"),
166 bind_group_layouts: &[&bind_group_layout],
167 push_constant_ranges: &[],
168 });
169
170 let pipeline =
171 graphics_ctx
172 .device()
173 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
174 label: Some("Render Pipeline"),
175 layout: Some(&pipeline_layout),
176 vertex: wgpu::VertexState {
177 module: &shader,
178 entry_point: Some("vs_main"),
179 buffers: &[wgpu::VertexBufferLayout {
180 // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
181 array_stride: 4 * 4,
182 step_mode: wgpu::VertexStepMode::Vertex,
183 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
184 }],
185 compilation_options: wgpu::PipelineCompilationOptions::default(),
186 },
187 fragment: Some(wgpu::FragmentState {
188 module: &shader,
189 entry_point: Some("fs_main"),
190 targets: &[Some(wgpu::ColorTargetState {
191 format: wgpu::TextureFormat::Bgra8UnormSrgb,
192 blend: Some(wgpu::BlendState::REPLACE),
193 write_mask: wgpu::ColorWrites::ALL,
194 })],
195 compilation_options: wgpu::PipelineCompilationOptions::default(),
196 }),
197 primitive: wgpu::PrimitiveState {
198 topology: wgpu::PrimitiveTopology::TriangleList,
199 strip_index_format: None,
200 front_face: wgpu::FrontFace::Ccw,
201 cull_mode: Some(wgpu::Face::Back),
202 polygon_mode: wgpu::PolygonMode::Fill,
203 unclipped_depth: false,
204 conservative: false,
205 },
206 depth_stencil: None,
207 multisample: wgpu::MultisampleState {
208 count: 1,
209 mask: !0,
210 alpha_to_coverage_enabled: false,
211 },
212 multiview: None,
213 cache: None,
214 });
215
216 #[rustfmt::skip]
217 let vertices: &[f32] = &[
218 -0.8, -0.8, 0.0, 1.0,
219 0.8, -0.8, 1.0, 1.0,
220 0.8, 0.8, 1.0, 0.0,
221 -0.8, -0.8, 0.0, 1.0,
222 0.8, 0.8, 1.0, 0.0,
223 -0.8, 0.8, 0.0, 0.0,
224 ];
225
226 let vertex_buffer = graphics_ctx
227 .device()
228 .create_buffer(&wgpu::BufferDescriptor {
229 label: Some("Vertex Buffer"),
230 size: std::mem::size_of_val(vertices) as u64,
231 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
232 mapped_at_creation: false,
233 });
234
235 graphics_ctx
236 .queue()
237 .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
238
239 let window_id = window.id();
240
241 Box::new(App {
242 window,
243 window_id,
244 pipeline,
245 bind_group,
246 vertex_buffer,
247 })
248 });
249}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 =
42 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
43 let renderer = Renderer::new(graphics_ctx.clone());
44
45 let window = ctx
46 .create_window(WindowDescriptor {
47 title: "Renderer API Example".to_string(),
48 size: Some(WinitPhysicalSize::new(800.0, 600.0)),
49 ..Default::default()
50 })
51 .expect("Failed to create window");
52
53 let window = RenderWindowBuilder::new()
54 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
55 .with_depth_default()
56 .build(window, graphics_ctx.clone())
57 .expect("Failed to create render window");
58
59 let window_id = window.id();
60
61 // Create shader using Renderer API
62 let shader = renderer.create_shader(Some("Color Shader"), SHADER_SOURCE);
63
64 // Create texture using Renderer helper
65 let texture_data = create_gradient_texture();
66 let texture = renderer.create_texture_2d(
67 Some("Gradient Texture"),
68 256,
69 256,
70 wgpu::TextureFormat::Rgba8UnormSrgb,
71 wgpu::TextureUsages::TEXTURE_BINDING,
72 &texture_data,
73 );
74
75 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
76 let sampler = renderer.create_linear_sampler(Some("Linear Sampler"));
77
78 // Create bind group using Renderer API
79 let bind_group_layout = renderer.create_bind_group_layout(
80 Some("Texture Bind Group Layout"),
81 &[
82 wgpu::BindGroupLayoutEntry {
83 binding: 0,
84 visibility: wgpu::ShaderStages::FRAGMENT,
85 ty: wgpu::BindingType::Texture {
86 multisampled: false,
87 view_dimension: wgpu::TextureViewDimension::D2,
88 sample_type: wgpu::TextureSampleType::Float { filterable: true },
89 },
90 count: None,
91 },
92 wgpu::BindGroupLayoutEntry {
93 binding: 1,
94 visibility: wgpu::ShaderStages::FRAGMENT,
95 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
96 count: None,
97 },
98 ],
99 );
100
101 let bind_group = renderer.create_bind_group(
102 Some("Texture Bind Group"),
103 &bind_group_layout,
104 &[
105 wgpu::BindGroupEntry {
106 binding: 0,
107 resource: wgpu::BindingResource::TextureView(&texture_view),
108 },
109 wgpu::BindGroupEntry {
110 binding: 1,
111 resource: wgpu::BindingResource::Sampler(&sampler),
112 },
113 ],
114 );
115
116 let pipeline_layout = renderer.create_pipeline_layout(
117 Some("Render Pipeline Layout"),
118 &[&bind_group_layout],
119 &[],
120 );
121
122 // Create pipeline using Renderer API with BlendMode
123 let pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
124 label: Some("Render Pipeline"),
125 layout: Some(&pipeline_layout),
126 vertex: wgpu::VertexState {
127 module: &shader,
128 entry_point: Some("vs_main"),
129 buffers: &[wgpu::VertexBufferLayout {
130 // 4 floats × 4 bytes = 16 bytes per vertex (2×f32 pos + 2×f32 UV)
131 array_stride: 4 * 4,
132 step_mode: wgpu::VertexStepMode::Vertex,
133 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
134 }],
135 compilation_options: wgpu::PipelineCompilationOptions::default(),
136 },
137 fragment: Some(wgpu::FragmentState {
138 module: &shader,
139 entry_point: Some("fs_main"),
140 // Use BlendMode for transparent rendering
141 targets: &[Some(
142 BlendMode::Alpha.to_color_target_state(wgpu::TextureFormat::Rgba8UnormSrgb),
143 )],
144 compilation_options: wgpu::PipelineCompilationOptions::default(),
145 }),
146 primitive: wgpu::PrimitiveState {
147 topology: wgpu::PrimitiveTopology::TriangleList,
148 strip_index_format: None,
149 front_face: wgpu::FrontFace::Ccw,
150 cull_mode: Some(wgpu::Face::Back),
151 polygon_mode: wgpu::PolygonMode::Fill,
152 unclipped_depth: false,
153 conservative: false,
154 },
155 depth_stencil: None,
156 multisample: wgpu::MultisampleState {
157 count: 1,
158 mask: !0,
159 alpha_to_coverage_enabled: false,
160 },
161 multiview: None,
162 cache: None,
163 });
164
165 #[rustfmt::skip]
166 let vertices: &[f32] = &[
167 -0.8, -0.8, 0.0, 1.0,
168 0.8, -0.8, 1.0, 1.0,
169 0.8, 0.8, 1.0, 0.0,
170 -0.8, -0.8, 0.0, 1.0,
171 0.8, 0.8, 1.0, 0.0,
172 -0.8, 0.8, 0.0, 0.0,
173 ];
174
175 // Create vertex buffer using Renderer helper
176 let vertex_buffer = renderer.create_vertex_buffer(Some("Vertex Buffer"), vertices);
177
178 // Create offscreen framebuffer using the new Framebuffer abstraction
179 let offscreen_fb = Framebuffer::builder(400, 300)
180 .format(wgpu::TextureFormat::Rgba8UnormSrgb)
181 .label("Offscreen FB")
182 .build(&graphics_ctx);
183
184 // Create blit shader and pipeline for rendering framebuffer to surface
185 let blit_shader = renderer.create_shader(Some("Blit Shader"), BLIT_SHADER_SOURCE);
186
187 let blit_bind_group_layout = renderer.create_bind_group_layout(
188 Some("Blit Bind Group Layout"),
189 &[
190 wgpu::BindGroupLayoutEntry {
191 binding: 0,
192 visibility: wgpu::ShaderStages::FRAGMENT,
193 ty: wgpu::BindingType::Texture {
194 multisampled: false,
195 view_dimension: wgpu::TextureViewDimension::D2,
196 sample_type: wgpu::TextureSampleType::Float { filterable: true },
197 },
198 count: None,
199 },
200 wgpu::BindGroupLayoutEntry {
201 binding: 1,
202 visibility: wgpu::ShaderStages::FRAGMENT,
203 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
204 count: None,
205 },
206 ],
207 );
208
209 let blit_bind_group = renderer.create_bind_group(
210 Some("Blit Bind Group"),
211 &blit_bind_group_layout,
212 &[
213 wgpu::BindGroupEntry {
214 binding: 0,
215 resource: wgpu::BindingResource::TextureView(offscreen_fb.color_view()),
216 },
217 wgpu::BindGroupEntry {
218 binding: 1,
219 resource: wgpu::BindingResource::Sampler(&sampler),
220 },
221 ],
222 );
223
224 let blit_pipeline_layout = renderer.create_pipeline_layout(
225 Some("Blit Pipeline Layout"),
226 &[&blit_bind_group_layout],
227 &[],
228 );
229
230 let blit_pipeline = renderer.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
231 label: Some("Blit Pipeline"),
232 layout: Some(&blit_pipeline_layout),
233 vertex: wgpu::VertexState {
234 module: &blit_shader,
235 entry_point: Some("vs_main"),
236 buffers: &[],
237 compilation_options: wgpu::PipelineCompilationOptions::default(),
238 },
239 fragment: Some(wgpu::FragmentState {
240 module: &blit_shader,
241 entry_point: Some("fs_main"),
242 // Use PremultipliedAlpha for framebuffer blitting
243 targets: &[Some(
244 BlendMode::PremultipliedAlpha
245 .to_color_target_state(wgpu::TextureFormat::Bgra8UnormSrgb),
246 )],
247 compilation_options: wgpu::PipelineCompilationOptions::default(),
248 }),
249 primitive: wgpu::PrimitiveState {
250 topology: wgpu::PrimitiveTopology::TriangleList,
251 ..Default::default()
252 },
253 depth_stencil: None,
254 multisample: wgpu::MultisampleState::default(),
255 multiview: None,
256 cache: None,
257 });
258
259 tracing::info!("Renderer initialized successfully");
260 tracing::info!("Device: {:?}", renderer.context().info());
261
262 Box::new(RendererApp {
263 _context: graphics_ctx,
264 _renderer: renderer,
265 window,
266 window_id,
267 pipeline,
268 bind_group,
269 vertex_buffer,
270 offscreen_fb,
271 blit_pipeline,
272 blit_bind_group,
273 time: 0.0,
274 })
275 });
276}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 UnsafeUnpin 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