astrelis-render

Crates.ioastrelis-render
lib.rsastrelis-render
version0.1.0
created_at2025-12-04 14:29:09.944372+00
updated_at2026-01-21 12:00:49.147586+00
descriptionAstrelis Core Rendering Module
homepage
repositoryhttps://github.com/hxyulin/astrelis
max_upload_size
id1966536
size482,469
(hxyulin)

documentation

README

astrelis-render

Modular rendering framework for the Astrelis game engine.

Overview

astrelis-render provides a modular, extensible architecture for managing GPU resources and rendering. It wraps WGPU with higher-level abstractions while maintaining low-level control when needed.

Architecture

Core Modules

  • context - Graphics context management (device, queue, adapter)
  • window - Window rendering contexts and surface management
  • frame - Frame lifecycle and render pass builders
  • renderer - Low-level extensible renderer for resource management

Design Philosophy

  1. Modular: Each component has a clear responsibility
  2. Extensible: Easy to build higher-level renderers (TextRenderer, SceneRenderer, etc.)
  3. Type-Safe: Leverages Rust's type system for safety
  4. Zero-Cost: Minimal overhead over raw WGPU

Quick Start

Basic Setup

use astrelis_render::{GraphicsContext, RenderableWindow, WindowContextDescriptor};
use astrelis_winit::{app::run_app, window::WindowDescriptor};

run_app(|ctx| {
    // Create graphics context
    let graphics_ctx = GraphicsContext::new_sync();
    
    // Create window
    let window = ctx.create_window(WindowDescriptor::default())?;
    let window = RenderableWindow::new(window, graphics_ctx);
    
    Box::new(MyApp { window })
});

Using the Renderer API

use astrelis_render::Renderer;

let graphics_ctx = GraphicsContext::new_sync();
let renderer = Renderer::new(graphics_ctx);

// Create shader
let shader = renderer.create_shader(Some("My Shader"), shader_source);

// Create vertex buffer
let vertices: &[f32] = &[/* ... */];
let vertex_buffer = renderer.create_vertex_buffer(Some("Vertices"), vertices);

// Create texture
let texture = renderer.create_texture_2d(
    Some("My Texture"),
    width, height,
    wgpu::TextureFormat::Rgba8UnormSrgb,
    wgpu::TextureUsages::TEXTURE_BINDING,
    texture_data,
);

// Create sampler
let sampler = renderer.create_linear_sampler(Some("Sampler"));

// Create bind group
let bind_group_layout = renderer.create_bind_group_layout(
    Some("Layout"),
    &[/* entries */],
);

let bind_group = renderer.create_bind_group(
    Some("Bind Group"),
    &bind_group_layout,
    &[/* entries */],
);

Render Loop

impl App for MyApp {
    fn update(&mut self, _ctx: &mut AppCtx) {
        // Global logic
    }
    
    fn render(&mut self, _ctx: &mut AppCtx, window_id: WindowId, events: &mut EventBatch) {
        if window_id != self.window_id {
            return;
        }
        
        // Handle events
        events.dispatch(|event| {
            match event {
                Event::WindowResized(size) => {
                    self.window.resized(*size);
                    HandleStatus::consumed()
                }
                _ => HandleStatus::ignored()
            }
        });
        
        // Begin frame
        let mut frame = self.window.begin_drawing();
        
        {
            // Create render pass
            let mut render_pass = RenderPassBuilder::new()
                .label("Main Pass")
                .color_attachment(
                    None, // Use window surface
                    None,
                    wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
                        store: wgpu::StoreOp::Store,
                    },
                )
                .build(&mut frame);
            
            let pass = render_pass.descriptor();
            // ... render commands
        }
        
        frame.finish();
    }
}

API Reference

GraphicsContext

Manages the WGPU instance, adapter, device, and queue.

// Create with defaults
let ctx = GraphicsContext::new_sync();

// Create with custom descriptor
let ctx = GraphicsContext::new_with_descriptor(
    GraphicsContextDescriptor {
        backends: wgpu::Backends::VULKAN | wgpu::Backends::METAL,
        power_preference: wgpu::PowerPreference::HighPerformance,
        features: wgpu::Features::PUSH_CONSTANTS,
        ..Default::default()
    }
).await;

// Query device info
let info = ctx.info();
let limits = ctx.limits();
let features = ctx.features();

WindowContext

Manages a window surface and its configuration.

let window_ctx = WindowContext::new(
    window,
    graphics_ctx,
    WindowContextDescriptor {
        format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
        present_mode: Some(wgpu::PresentMode::Mailbox),
        alpha_mode: Some(wgpu::CompositeAlphaMode::Opaque),
    },
);

// Handle resize
window_ctx.resized(new_size);

// Reconfigure surface
window_ctx.reconfigure_surface(new_config);

// Access surface
let surface = window_ctx.surface();
let config = window_ctx.surface_config();

Renderer

Low-level API for creating GPU resources.

Buffer Creation

// Vertex buffer
let vertex_buffer = renderer.create_vertex_buffer(label, vertices);

// Index buffer
let index_buffer = renderer.create_index_buffer(label, indices);

// Uniform buffer
let uniform_buffer = renderer.create_uniform_buffer(label, &uniforms);

// Update uniform
renderer.update_uniform_buffer(&uniform_buffer, &new_uniforms);

Texture Creation

// 2D texture with data
let texture = renderer.create_texture_2d(
    label,
    width, height,
    format,
    usage,
    data,
);

// Custom texture descriptor
let texture = renderer.create_texture(&descriptor);

Sampler Creation

// Linear sampler
let sampler = renderer.create_linear_sampler(label);

// Nearest sampler
let sampler = renderer.create_nearest_sampler(label);

// Custom sampler
let sampler = renderer.create_sampler(&descriptor);

Bind Groups

// Create layout
let layout = renderer.create_bind_group_layout(label, &entries);

// Create bind group
let bind_group = renderer.create_bind_group(label, &layout, &entries);

Pipeline Creation

// Create shader
let shader = renderer.create_shader(label, source);

// Create pipeline layout
let pipeline_layout = renderer.create_pipeline_layout(
    label,
    &[&bind_group_layout],
    &push_constant_ranges,
);

// Create render pipeline
let pipeline = renderer.create_render_pipeline(&descriptor);

// Create compute pipeline
let compute_pipeline = renderer.create_compute_pipeline(&descriptor);

Command Submission

// Create encoder
let mut encoder = renderer.create_command_encoder(label);

// ... record commands

// Submit
renderer.submit(std::iter::once(encoder.finish()));

RenderPassBuilder

Builder for creating render passes with automatic encoder management.

let mut render_pass = RenderPassBuilder::new()
    .label("My Pass")
    .color_attachment(
        Some(&texture_view),
        None, // No resolve target
        wgpu::Operations {
            load: wgpu::LoadOp::Clear(color),
            store: wgpu::StoreOp::Store,
        },
    )
    .depth_stencil_attachment(
        &depth_view,
        Some(wgpu::Operations {
            load: wgpu::LoadOp::Clear(1.0),
            store: wgpu::StoreOp::Store,
        }),
        None, // No stencil
    )
    .build(&mut frame);

// Use the render pass
let pass = render_pass.descriptor();
pass.set_pipeline(&pipeline);
// ... render commands

// Encoder automatically returned to frame when dropped

WGPU Re-export

All WGPU types are re-exported for convenience:

use astrelis_render::wgpu;

// Instead of:
// use wgpu::TextureFormat;

// You can use:
use astrelis_render::wgpu::TextureFormat;

Building Higher-Level Renderers

The Renderer is designed as a foundation for specialized renderers:

pub struct TextRenderer {
    renderer: Renderer,
    pipeline: wgpu::RenderPipeline,
    font_atlas: wgpu::Texture,
    // ...
}

impl TextRenderer {
    pub fn new(context: &'static GraphicsContext) -> Self {
        let renderer = Renderer::new(context);
        
        // Use renderer API to create resources
        let shader = renderer.create_shader(/* ... */);
        let font_atlas = renderer.create_texture_2d(/* ... */);
        
        Self { renderer, pipeline, font_atlas }
    }
    
    pub fn draw_text(&mut self, text: &str, position: Vec2) {
        // High-level text rendering API
    }
}

Example Renderer Types

  • TextRenderer - Text rendering with font atlases
  • SpriteRenderer - 2D sprite batching
  • SceneRenderer - 3D scene rendering with lighting
  • UIRenderer - Immediate-mode UI rendering
  • ParticleRenderer - GPU particle systems
  • DebugRenderer - Debug shapes and lines

Examples

See the examples/ directory for complete examples:

  • renderer_api.rs - Demonstrates the low-level Renderer API
  • textured_window.rs - Basic textured quad rendering
  • multi_window.rs - Multiple windows with different content

Run an example:

cargo run --package astrelis-render --example renderer_api

Features

  • Modular Architecture - Clean separation of concerns
  • Type-Safe - Compile-time safety with Rust
  • Zero-Cost Abstractions - Minimal overhead
  • Multi-Window Support - Render to multiple windows
  • Extensible - Easy to build specialized renderers
  • WGPU Integration - Full access to WGPU features
  • Profiling Integration - Built-in profiling support

License

Part of the Astrelis game engine.

Commit count: 38

cargo fmt