use shdrlib::{AssetManager, Language};
use std::sync::Arc;
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
};
struct App {
window: Option<Arc<Window>>,
assets: Option<AssetManager>,
surface: Option<wgpu::Surface<'static>>,
config: Option<wgpu::SurfaceConfiguration>,
}
impl Default for App {
fn default() -> Self {
Self {
window: None,
assets: None,
surface: None,
config: None,
}
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_some() {
return;
}
println!("🔥 shdrlib Window Render Demo 🔥\n");
let window_attributes = Window::default_attributes()
.with_title("shdrlib - Live Triangle Render")
.with_inner_size(winit::dpi::LogicalSize::new(800, 600));
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
println!("✅ Window created\n");
let mut assets = AssetManager::new();
let gpu_info = assets.gpu_info();
println!("GPU: {} ({:?})", gpu_info.name, gpu_info.backend);
println!("Driver: {}\n", gpu_info.driver_info);
let surface = assets.instance().create_surface(window.clone()).unwrap();
let size = window.inner_size();
let surface_caps = surface.get_capabilities(assets.adapter());
let surface_format = surface_caps.formats.iter()
.copied()
.find(|f| f.is_srgb())
.unwrap_or(surface_caps.formats[0]);
println!("Surface format: {:?}\n", surface_format);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(assets.device(), &config);
let triangle_shader = r#"
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec3<f32>,
}
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput {
var positions = array<vec2<f32>, 3>(
vec2<f32>(0.0, 0.6),
vec2<f32>(-0.6, -0.6),
vec2<f32>(0.6, -0.6)
);
var colors = array<vec3<f32>, 3>(
vec3<f32>(1.0, 0.0, 0.0), // Red
vec3<f32>(0.0, 1.0, 0.0), // Green
vec3<f32>(0.0, 0.0, 1.0) // Blue
);
var output: VertexOutput;
output.position = vec4<f32>(positions[in_vertex_index], 0.0, 1.0);
output.color = colors[in_vertex_index];
return output;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(in.color, 1.0);
}
"#;
println!("📝 Compiling shaders...");
assets.add_shader(
"triangle_vert",
triangle_shader,
naga::ShaderStage::Vertex,
Language::WGSL,
).expect("Failed to compile vertex shader");
assets.add_shader(
"triangle_frag",
triangle_shader,
naga::ShaderStage::Fragment,
Language::WGSL,
).expect("Failed to compile fragment shader");
println!("✅ Shaders compiled\n");
println!("🔧 Building pipeline...");
assets.create_pipeline(
"triangle",
"triangle_vert",
Some("triangle_frag"),
vec![],
surface_format,
).expect("Failed to create pipeline");
println!("✅ Pipeline ready\n");
println!("🎨 Starting render loop...\n");
self.window = Some(window);
self.assets = Some(assets);
self.surface = Some(surface);
self.config = Some(config);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
println!("\n👋 Closing window...");
event_loop.exit();
}
WindowEvent::Resized(physical_size) => {
if let (Some(surface), Some(config), Some(assets)) =
(&self.surface, &mut self.config, &self.assets) {
if physical_size.width > 0 && physical_size.height > 0 {
config.width = physical_size.width;
config.height = physical_size.height;
surface.configure(assets.device(), config);
}
}
}
WindowEvent::RedrawRequested => {
if let (Some(surface), Some(assets), Some(window)) =
(&self.surface, &self.assets, &self.window) {
let output = match surface.get_current_texture() {
Ok(texture) => texture,
Err(e) => {
eprintln!("Failed to get surface texture: {:?}", e);
return;
}
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let duo = assets.get_pipeline("triangle").unwrap();
let mut encoder = assets.device().create_command_encoder(
&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
}
);
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.05,
g: 0.05,
b: 0.08,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&duo.pipeline);
render_pass.draw(0..3, 0..1);
}
assets.queue().submit(Some(encoder.finish()));
output.present();
window.request_redraw();
}
}
_ => {}
}
}
}
fn main() {
let event_loop = EventLoop::new().unwrap();
let mut app = App::default();
event_loop.run_app(&mut app).unwrap();
}