#[repr(C)]pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}Expand description
An RGBA color with f32 components in the 0.0..=1.0 range.
Colors are stored in linear RGBA order and can be constructed from floats,
u8 values, or hex codes:
use astrelis_render::Color;
let red = Color::rgb(1.0, 0.0, 0.0);
let semi_transparent = Color::rgba(1.0, 1.0, 1.0, 0.5);
let from_hex = Color::from_hex(0xFF8800);
let from_bytes = Color::from_rgba_u8(128, 64, 32, 255);The struct is #[repr(C)] and implements bytemuck::Pod, so it can be
used directly in GPU uniform/vertex buffers.
Fields§
§r: f32§g: f32§b: f32§a: f32Implementations§
Source§impl Color
impl Color
pub const WHITE: Color
pub const BLACK: Color
pub const RED: Color
pub const GREEN: Color
pub const BLUE: Color
pub const YELLOW: Color
pub const CYAN: Color
pub const MAGENTA: Color
pub const TRANSPARENT: Color
Sourcepub const fn rgb(r: f32, g: f32, b: f32) -> Self
pub const fn rgb(r: f32, g: f32, b: f32) -> Self
Create a color from RGB components with full opacity (alpha = 1.0).
Examples found in repository?
examples/sprite_sheet.rs (line 415)
390 fn render(
391 &mut self,
392 _ctx: &mut astrelis_winit::app::AppCtx,
393 window_id: WindowId,
394 events: &mut astrelis_winit::event::EventBatch,
395 ) {
396 let Some(window) = self.windows.get_mut(&window_id) else {
397 return;
398 };
399
400 // Handle resize
401 events.dispatch(|event| {
402 if let astrelis_winit::event::Event::WindowResized(size) = event {
403 window.resized(*size);
404 astrelis_winit::event::HandleStatus::consumed()
405 } else {
406 astrelis_winit::event::HandleStatus::ignored()
407 }
408 });
409
410 let mut frame = window.begin_drawing();
411
412 // Render with automatic scoping (no manual {} block needed)
413 frame.clear_and_render(
414 RenderTarget::Surface,
415 Color::rgb(0.1, 0.1, 0.15),
416 |pass| {
417 let pass = pass.wgpu_pass();
418 pass.set_pipeline(&self.pipeline);
419 pass.set_bind_group(0, &self.bind_group, &[]);
420 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
421 pass.draw(0..6, 0..1);
422 },
423 );
424
425 frame.finish();
426 }More examples
examples/image_blitting.rs (line 469)
444 fn render(
445 &mut self,
446 _ctx: &mut astrelis_winit::app::AppCtx,
447 window_id: WindowId,
448 events: &mut astrelis_winit::event::EventBatch,
449 ) {
450 let Some(window) = self.windows.get_mut(&window_id) else {
451 return;
452 };
453
454 // Handle resize
455 events.dispatch(|event| {
456 if let astrelis_winit::event::Event::WindowResized(size) = event {
457 window.resized(*size);
458 astrelis_winit::event::HandleStatus::consumed()
459 } else {
460 astrelis_winit::event::HandleStatus::ignored()
461 }
462 });
463
464 let mut frame = window.begin_drawing();
465
466 // Render with automatic scoping (no manual {} block needed)
467 frame.clear_and_render(
468 RenderTarget::Surface,
469 Color::rgb(0.05, 0.05, 0.08),
470 |pass| {
471 let pass = pass.wgpu_pass();
472 pass.set_pipeline(&self.pipeline);
473 pass.set_bind_group(0, &self.bind_group, &[]);
474 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
475 pass.draw(0..6, 0..1);
476 },
477 );
478
479 frame.finish();
480 }examples/window_manager_demo.rs (line 51)
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}examples/textured_window.rs (line 289)
262 fn render(
263 &mut self,
264 _ctx: &mut astrelis_winit::app::AppCtx,
265 window_id: WindowId,
266 events: &mut astrelis_winit::event::EventBatch,
267 ) {
268 if window_id != self.window_id {
269 return;
270 }
271
272 // Handle window resize events
273 events.dispatch(|event| {
274 if let astrelis_winit::event::Event::WindowResized(size) = event {
275 self.window.resized(*size);
276 astrelis_winit::event::HandleStatus::consumed()
277 } else {
278 astrelis_winit::event::HandleStatus::ignored()
279 }
280 });
281
282 // --- Render Loop ---
283 let mut frame = self.window.begin_drawing();
284
285 // clear_and_render handles render pass scoping automatically:
286 // the pass is created, the closure runs, then the pass drops.
287 frame.clear_and_render(
288 RenderTarget::Surface,
289 Color::rgb(0.1, 0.2, 0.3),
290 |pass| {
291 let pass = pass.wgpu_pass();
292 pass.set_pipeline(&self.pipeline);
293 pass.set_bind_group(0, &self.bind_group, &[]);
294 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
295 pass.draw(0..6, 0..1);
296 },
297 );
298
299 frame.finish();
300 }examples/profiling_demo.rs (line 165)
146 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
147 new_frame();
148 profile_function!();
149
150 if window_id != self.window_id {
151 return;
152 }
153
154 events.dispatch(|event| {
155 if let astrelis_winit::event::Event::WindowResized(size) = event {
156 self.window.resized(*size);
157 astrelis_winit::event::HandleStatus::consumed()
158 } else {
159 astrelis_winit::event::HandleStatus::ignored()
160 }
161 });
162
163 // Cycle the background color to visualize frames
164 let t = (self.frame_count as f32 * 0.01).sin() * 0.5 + 0.5;
165 let clear_color = Color::rgb(0.05 + t * 0.1, 0.05, 0.15 + (1.0 - t) * 0.1);
166
167 // GPU profiling is now automatic — no special handling needed!
168 // If a profiler is attached, with_pass/clear_and_render auto-create GPU scopes,
169 // and FrameContext::Drop auto-resolves queries and ends the profiler frame.
170 let mut frame = self.window.begin_drawing();
171
172 {
173 profile_scope!("render_frame");
174 frame.clear_and_render(RenderTarget::Surface, clear_color, |_pass| {
175 profile_scope!("draw_commands");
176 // In a real app, you would issue draw calls here.
177 });
178 }
179
180 frame.finish();
181
182 // Simulate some post-render work
183 {
184 profile_scope!("post_render");
185 let _ = &self.context;
186 }
187 }examples/renderer_api.rs (line 306)
286 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
287 if window_id != self.window_id {
288 return;
289 }
290
291 // Handle window-specific resize events
292 events.dispatch(|event| {
293 if let astrelis_winit::event::Event::WindowResized(size) = event {
294 self.window.resized(*size);
295 astrelis_winit::event::HandleStatus::consumed()
296 } else {
297 astrelis_winit::event::HandleStatus::ignored()
298 }
299 });
300
301 let mut frame = self.window.begin_drawing();
302
303 // Pass 1: Render to offscreen framebuffer with automatic scoping
304 frame.clear_and_render(
305 RenderTarget::Framebuffer(&self.offscreen_fb),
306 Color::rgb(0.2, 0.1, 0.3),
307 |pass| {
308 let pass = pass.wgpu_pass();
309 pass.set_pipeline(&self.pipeline);
310 pass.set_bind_group(0, &self.bind_group, &[]);
311 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
312 pass.draw(0..6, 0..1);
313 },
314 );
315
316 // Pass 2: Blit framebuffer to surface with automatic scoping
317 frame.clear_and_render(
318 RenderTarget::Surface,
319 Color::rgb(0.1, 0.2, 0.3),
320 |pass| {
321 let pass = pass.wgpu_pass();
322 pass.set_pipeline(&self.blit_pipeline);
323 pass.set_bind_group(0, &self.blit_bind_group, &[]);
324 // Draw fullscreen triangle
325 pass.draw(0..3, 0..1);
326 },
327 );
328
329 frame.finish();
330 }Sourcepub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self
Create a color from RGBA components.
Examples found in repository?
examples/multi_window.rs (line 118)
91 fn render(
92 &mut self,
93 _ctx: &mut astrelis_winit::app::AppCtx,
94 window_id: WindowId,
95 events: &mut astrelis_winit::event::EventBatch,
96 ) {
97 // Get the window and color for this specific window
98 let Some((window, color)) = self.windows.get_mut(&window_id) else {
99 return;
100 };
101
102 // Handle resize: each window dispatches events independently.
103 events.dispatch(|event| {
104 if let astrelis_winit::event::Event::WindowResized(size) = event {
105 window.resized(*size);
106 astrelis_winit::event::HandleStatus::consumed()
107 } else {
108 astrelis_winit::event::HandleStatus::ignored()
109 }
110 });
111
112 // Render this specific window
113 let mut frame = window.begin_drawing();
114
115 // Render with automatic scoping (no manual {} block needed)
116 frame.clear_and_render(
117 RenderTarget::Surface,
118 astrelis_render::Color::rgba(color.r as f32, color.g as f32, color.b as f32, color.a as f32),
119 |_pass| {
120 // Just clearing - no rendering commands needed
121 },
122 );
123
124 frame.finish();
125 }More examples
examples/batched_renderer.rs (line 437)
372 fn render(
373 &mut self,
374 _ctx: &mut astrelis_winit::app::AppCtx,
375 window_id: WindowId,
376 events: &mut astrelis_winit::event::EventBatch,
377 ) {
378 // Handle resize and get dimensions (scoped to release window borrow)
379 let (phys_width, phys_height) = {
380 let Some(window) = self.windows.get_mut(&window_id) else {
381 return;
382 };
383
384 events.dispatch(|event| {
385 if let astrelis_winit::event::Event::WindowResized(size) = event {
386 window.resized(*size);
387 astrelis_winit::event::HandleStatus::consumed()
388 } else {
389 astrelis_winit::event::HandleStatus::ignored()
390 }
391 });
392
393 let phys = window.physical_size();
394 (phys.width, phys.height)
395 };
396
397 let width = phys_width as f32;
398 let height = phys_height as f32;
399
400 if width < 1.0 || height < 1.0 {
401 return;
402 }
403
404 // Ensure depth buffer matches viewport
405 self.ensure_depth_buffer(phys_width, phys_height);
406
407 // Build instances and prepare GPU data
408 let instances = self.build_instances(width, height);
409 let batch = DrawBatch2D {
410 instances,
411 textures: vec![],
412 projection: Self::ortho_projection(width, height),
413 };
414 self.renderer.prepare(&batch);
415
416 let stats = self.renderer.stats();
417 if self.frame_count % 120 == 0 {
418 tracing::info!(
419 "Frame {}: {} instances ({} opaque, {} transparent), {} draw calls",
420 self.frame_count,
421 stats.instance_count,
422 stats.opaque_count,
423 stats.transparent_count,
424 stats.draw_calls,
425 );
426 }
427
428 // Re-borrow window for rendering
429 let window = self.windows.get_mut(&window_id).unwrap();
430 let mut frame = window.begin_drawing();
431
432 // Use RenderPassBuilder with depth stencil attachment
433 frame.with_pass(
434 astrelis_render::RenderPassBuilder::new()
435 .label("batched_example_pass")
436 .target(astrelis_render::RenderTarget::Surface)
437 .clear_color(astrelis_render::Color::rgba(0.08, 0.08, 0.1, 1.0))
438 .clear_depth(0.0) // 0.0 = far with GreaterEqual
439 .depth_stencil_attachment(
440 &self.depth_view,
441 Some(wgpu::Operations {
442 load: wgpu::LoadOp::Clear(0.0),
443 store: wgpu::StoreOp::Store,
444 }),
445 None,
446 ),
447 |pass| {
448 self.renderer.render(pass.wgpu_pass());
449 },
450 );
451
452 frame.finish();
453 }Sourcepub fn from_rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self
pub fn from_rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self
Create a color from 8-bit RGBA values (0–255 mapped to 0.0–1.0).
Sourcepub fn from_rgb_u8(r: u8, g: u8, b: u8) -> Self
pub fn from_rgb_u8(r: u8, g: u8, b: u8) -> Self
Create a color from 8-bit RGB values with full opacity.
Examples found in repository?
examples/camera_demo.rs (line 108)
91 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
92 if window_id != self.window_id {
93 return;
94 }
95
96 events.dispatch(|event| {
97 if let astrelis_winit::event::Event::WindowResized(size) = event {
98 self.window.resized(*size);
99 astrelis_winit::event::HandleStatus::consumed()
100 } else {
101 astrelis_winit::event::HandleStatus::ignored()
102 }
103 });
104
105 let mut frame = self.window.begin_drawing();
106 frame.clear_and_render(
107 RenderTarget::Surface,
108 Color::from_rgb_u8(20, 30, 40),
109 |_pass| {},
110 );
111 frame.finish();
112 }More examples
examples/mesh_primitives.rs (line 110)
93 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
94 if window_id != self.window_id {
95 return;
96 }
97
98 events.dispatch(|event| {
99 if let astrelis_winit::event::Event::WindowResized(size) = event {
100 self.window.resized(*size);
101 astrelis_winit::event::HandleStatus::consumed()
102 } else {
103 astrelis_winit::event::HandleStatus::ignored()
104 }
105 });
106
107 let mut frame = self.window.begin_drawing();
108 frame.clear_and_render(
109 RenderTarget::Surface,
110 Color::from_rgb_u8(20, 30, 40),
111 |_pass| {},
112 );
113 frame.finish();
114 }examples/render_graph_demo.rs (line 108)
91 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
92 if window_id != self.window_id {
93 return;
94 }
95
96 events.dispatch(|event| {
97 if let astrelis_winit::event::Event::WindowResized(size) = event {
98 self.window.resized(*size);
99 astrelis_winit::event::HandleStatus::consumed()
100 } else {
101 astrelis_winit::event::HandleStatus::ignored()
102 }
103 });
104
105 let mut frame = self.window.begin_drawing();
106 frame.clear_and_render(
107 RenderTarget::Surface,
108 Color::from_rgb_u8(20, 30, 40),
109 |_pass| {},
110 );
111 frame.finish();
112 }examples/material_system.rs (line 131)
107 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
108 if window_id != self.window_id {
109 return;
110 }
111
112 // Handle resize
113 events.dispatch(|event| {
114 if let astrelis_winit::event::Event::WindowResized(size) = event {
115 self.window.resized(*size);
116 astrelis_winit::event::HandleStatus::consumed()
117 } else {
118 astrelis_winit::event::HandleStatus::ignored()
119 }
120 });
121
122 // In a real application, materials would be bound during rendering:
123 // material.bind(&mut render_pass);
124 // draw_mesh(&mesh);
125
126 // Begin frame
127 let mut frame = self.window.begin_drawing();
128
129 frame.clear_and_render(
130 RenderTarget::Surface,
131 Color::from_rgb_u8(20, 30, 40),
132 |_pass| {
133 // Materials would be applied here in actual rendering
134 // This is a conceptual demonstration
135 },
136 );
137
138 frame.finish();
139 }examples/performance_benchmark.rs (line 160)
103 fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
104 if window_id != self.window_id {
105 return;
106 }
107
108 let frame_start = Instant::now();
109
110 // Handle resize
111 events.dispatch(|event| {
112 if let Event::WindowResized(size) = event {
113 self.window.resized(*size);
114 HandleStatus::consumed()
115 } else {
116 HandleStatus::ignored()
117 }
118 });
119
120 // Handle keyboard input
121 events.dispatch(|event| {
122 if let Event::KeyInput(key) = event {
123 if key.state == astrelis_winit::event::ElementState::Pressed {
124 match &key.logical_key {
125 Key::Named(NamedKey::Space) => {
126 self.rendering = !self.rendering;
127 println!("Rendering: {}", self.rendering);
128 return HandleStatus::consumed();
129 }
130 Key::Character(c) if c == "+" || c == "=" => {
131 self.object_count = (self.object_count + 500).min(50000);
132 println!("Object count: {}", self.object_count);
133 return HandleStatus::consumed();
134 }
135 Key::Character(c) if c == "-" => {
136 self.object_count = self.object_count.saturating_sub(500).max(100);
137 println!("Object count: {}", self.object_count);
138 return HandleStatus::consumed();
139 }
140 _ => {}
141 }
142 }
143 }
144 HandleStatus::ignored()
145 });
146
147 // Begin frame
148 let mut frame = self.window.begin_drawing();
149
150 if self.rendering {
151 // Simulate rendering thousands of objects
152 // In a real scenario, this would involve:
153 // - Instanced draw calls
154 // - Uniform buffer updates
155 // - Texture binding
156 // - Shader state changes
157
158 frame.clear_and_render(
159 RenderTarget::Surface,
160 Color::from_rgb_u8(10, 10, 15),
161 |_pass| {
162 // Actual rendering would happen here
163 // For benchmark purposes, we're measuring the overhead
164 // of the render pass itself with clear operations
165 },
166 );
167 } else {
168 frame.clear_and_render(
169 RenderTarget::Surface,
170 Color::from_rgb_u8(10, 10, 15),
171 |_pass| {},
172 );
173 }
174
175 frame.finish();
176
177 let frame_end = Instant::now();
178 self.last_frame_time = frame_end.duration_since(frame_start).as_secs_f32() * 1000.0;
179 }Sourcepub fn from_hex_alpha(hex: u32) -> Self
pub fn from_hex_alpha(hex: u32) -> Self
Create a color from a 32-bit RGBA hex value (e.g. 0xFF880080).
Trait Implementations§
impl Copy for Color
impl Pod for Color
impl StructuralPartialEq for Color
Auto Trait Implementations§
impl Freeze for Color
impl RefUnwindSafe for Color
impl Send for Color
impl Sync for Color
impl Unpin for Color
impl UnwindSafe for Color
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
Mutably borrows from an owned value. Read more
Source§impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
Source§type Bits = T
type Bits = T
Self must have the same layout as the specified Bits except for
the possible invalid bit patterns being checked during
is_valid_bit_pattern.Source§fn is_valid_bit_pattern(_bits: &T) -> bool
fn is_valid_bit_pattern(_bits: &T) -> bool
If this function returns true, then it must be valid to reinterpret
bits
as &Self.Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
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>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
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>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read 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>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more