GPU Acceleration Test - WebAssembly Demo
View on GitHub
This project demonstrates GPU acceleration using modern graphics APIs, implemented in Rust. It features two branches: the main branch for native desktop applications and the web-integration branch for browser-based WebAssembly deployment.
GPU Acceleration in Rust: From Desktop to Web with WebAssembly
Modern graphics programming has evolved significantly, with GPU acceleration becoming essential for high-performance applications. This project demonstrates GPU acceleration using Rust and how it can be adapted to run in web browsers through WebAssembly.
The Main Branch: Native GPU Acceleration
The main branch is a native desktop application that leverages hardware-accelerated graphics through the wgpu library, providing maximum performance by directly accessing the system's GPU capabilities.
// main/src/lib.rs
pub mod texture;
pub mod vertex;
pub mod render_device;
pub mod system_info;
pub mod state;
pub mod debug;
pub mod device_selector;
Key components include the application entry point, rendering pipeline, geometry definitions, shaders, debug overlay, and device selection for CPU/GPU rendering.
Project Overview
Web Integration Branch
The web-integration branch extends the core rendering engine to work in web browsers through WebAssembly, allowing GPU-accelerated rendering to run directly in modern browsers without plugins.
Technical Stack:
- Graphics API: WebGPU/WebGL via wgpu
- WebAssembly: wasm-bindgen, wasm-pack
- JavaScript: web-sys, js-sys
- Shader: WGSL (WebGPU Shading Language)
The web integration uses conditional compilation to separate platform-specific code:
#[cfg(target_arch = "wasm32")]
// Web-specific code
#[cfg(not(target_arch = "wasm32"))]
// Desktop-specific code
Web-specific features include canvas integration, WebGL context management, and browser event handling.
Main Branch (Desktop)
The main branch focuses on native desktop performance, using platform-specific graphics APIs for maximum efficiency.
Technical Stack:
- Graphics API: wgpu (abstracting over Vulkan, Metal, DirectX)
- Window Management: winit
- UI Framework: egui (for debug overlays)
- System Information: sysinfo
The desktop version includes hardware-specific optimizations, detailed system information, and an interactive debug overlay.
How It Works
Both versions share the same core rendering pipeline:
- Initialize the graphics context (WebGL for web, native API for desktop)
- Create shader programs using WGSL
- Set up vertex and index buffers for 3D geometry
- Create texture resources
- Implement a render loop with transformation matrices
- Display performance metrics
The web version compiles to WebAssembly and interfaces with WebGL, while the desktop version compiles to native code and interfaces directly with graphics drivers.
Shared Components
Both branches share several key components:
1. Vertex Definitions
// From vertex.rs
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
pub position: [f32; 3],
pub tex_coords: [f32; 2],
pub normal: [f32; 3],
}
2. Render Device Abstraction
// From render_device.rs
#[derive(Clone, Debug, PartialEq)]
pub enum RenderDevice {
CPU,
GPU(String),
}
3. Transformation Logic
// Create matrices for 3D rendering
let projection = Mat4::perspective_rh(45.0 * (PI / 180.0), aspect, 0.1, 100.0);
let view = Mat4::look_at_rh(
Vec3::new(0.0, 0.0, 3.0), // Camera position
Vec3::new(0.0, 0.0, 0.0), // Look at origin
Vec3::new(0.0, 1.0, 0.0), // Up vector
);
Browser Compatibility
The web version requires a browser with WebGL 2 support:
- Chrome/Edge (version 79+)
- Firefox (version 71+)
- Safari (version 15+)
A dedicated GPU is recommended for optimal performance.
Deep Dive: Main Branch Architecture
The main branch leverages hardware-accelerated graphics through wgpu, with these key components:
- Rendering Pipeline: Manages GPU resources and shader execution
- 3D Geometry: Defines vertices with positions, texture coordinates, and normals
- Transformation: Uses matrix math for model-view-projection transformations
- Debug Overlay: Provides real-time performance metrics
- Device Selection: Allows choosing between CPU and GPU rendering
Initialization Process
// Main application initialization
fn main() -> Result<()> {
// Create wgpu instance
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
..Default::default()
});
// Get system info and select rendering device
let mut system_info = get_system_info(&instance);
let render_device = prompt_device_selection(&mut system_info);
// Create and run the application
let event_loop = EventLoop::new().unwrap();
event_loop.run_app(&mut App { /* ... */ })?;
Ok(())
}
Shader Implementation
The application uses WGSL (WebGPU Shading Language) for its shaders:
// Vertex shader
struct VertexInput {
@location(0) position: vec3,
@location(1) tex_coords: vec2,
@location(2) normal: vec3,
};
@vertex
fn vs_main(model: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = uniforms.model_view_proj * vec4(model.position, 1.0);
out.normal = model.normal;
return out;
}
// Fragment shader
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4 {
return textureSample(t_diffuse, s_diffuse, in.tex_coords);
}
3D Geometry and Animation
The application renders a textured 3D cube with position, texture coordinates, and normal vectors. The cube rotates continuously, with transformation matrices handling the 3D perspective and camera positioning.
Debug Overlay
The application includes a comprehensive debug overlay implemented with egui:
- Displays real-time FPS and frame time metrics
- Shows CPU and GPU information
- Provides details about the rendering pipeline
- Includes visualization of coordinate axes
- Can be toggled on/off with the F1 key
Input Handling
User input is processed in the State::input()
method:
- Keyboard events for toggling debug overlay (F1)
- Window resize events to update the rendering surface
- Mouse events for potential camera control (not fully implemented)
Performance Optimization
The application includes several performance optimizations:
- Indexed drawing to reduce vertex data
- Depth buffering for correct 3D rendering
- Efficient uniform buffer updates
- Hardware-specific adapter selection
- Frame time tracking for performance monitoring
Device Selection
The application allows choosing between CPU and GPU rendering:
- The
RenderDevice
enum represents either CPU or GPU rendering - The
prompt_device_selection()
function displays available devices and lets the user choose - The selected device affects adapter selection in the initialization process
- This allows testing performance differences between software and hardware rendering
Technical Challenges
The main branch addresses several technical challenges:
- Cross-Platform Rendering: Using wgpu to abstract over different graphics APIs (Vulkan, Metal, DirectX)
- Hardware Detection: Identifying and selecting appropriate rendering devices
- Performance Monitoring: Implementing real-time metrics for GPU and CPU usage
- 3D Mathematics: Handling transformation matrices and 3D geometry
- Resource Management: Properly creating and destroying GPU resources
Code Structure
The codebase is organized into modular components:
- main.rs: Application entry point and event loop
- state.rs: Core rendering state and pipeline management
- vertex.rs: Vertex definitions and geometry creation
- texture.rs: Texture loading and management
- shader.wgsl: GPU shader code
- debug.rs: Debug overlay and performance monitoring
- system_info.rs: Hardware detection and information
- device_selector.rs: User interface for device selection
- render_device.rs: Abstraction for rendering devices
This modular approach makes the code maintainable and extensible, allowing for easy addition of new features or optimization of existing ones.
Deep Dive: Web Integration Architecture
The web-integration branch adapts the core architecture to run in browsers through WebAssembly:
WebAssembly Integration
// Web-specific module
#[cfg(target_arch = "wasm32")]
pub mod web;
// Exposed functions for JavaScript
#[wasm_bindgen]
pub fn init() {
init_logging();
}
#[wasm_bindgen]
pub async fn initialize(canvas: HtmlCanvasElement) -> Result {
// Initialize WebGL context
}
#[wasm_bindgen]
pub fn start_render_loop() -> Result<(), JsValue> {
// Start animation frame loop
}
WebGL Rendering Pipeline
The web version adapts the main branch's rendering pipeline to use WebGL 2:
- GLSL ES 3.0 shaders converted from the main branch's WGSL
- WebGL buffer objects for geometry data
- Uniform variables for transformations
- Browser's requestAnimationFrame for timing
Key Differences Between Branches
1. Graphics API
- Main Branch: Uses native backends (Vulkan, Metal, DirectX)
- Web Branch: Uses WebGL 2 with browser limitations
2. Rendering Architecture
- Main Branch: Uses WGSL shaders and native rendering pipeline
- Web Branch: Uses GLSL ES 3.0 shaders and WebGL pipeline
3. Platform Integration
- Main Branch: Interfaces with native window system and event handling
- Web Branch: Interfaces with browser DOM and requestAnimationFrame
4. Build System
- Main Branch: Standard Cargo build for native binaries
- Web Branch: wasm-pack for WebAssembly compilation
The codebase uses conditional compilation to handle these differences:
#[cfg(target_arch = "wasm32")]
// Web-specific code
#[cfg(not(target_arch = "wasm32"))]
// Desktop-specific code
Conclusion
This project showcases Rust's power in creating cross-platform, high-performance graphics applications using WebAssembly.
Key highlights:
- Cross-platform graphics development with Rust
- High-performance web applications via WebAssembly
- Compatible for desktop and web platforms