tx2_core/
renderer.rs

1use winit::window::Window;
2use wgpu::util::DeviceExt;
3
4pub struct Renderer {
5    surface: wgpu::Surface<'static>,
6    device: wgpu::Device,
7    queue: wgpu::Queue,
8    config: wgpu::SurfaceConfiguration,
9    pub size: winit::dpi::PhysicalSize<u32>,
10}
11
12impl Renderer {
13    pub async fn new(window: &Window) -> Self {
14        let size = window.inner_size();
15
16        // The instance is a handle to our GPU
17        // Backends::all => Vulkan + Metal + DX12 + Browser WebGPU
18        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
19            backends: wgpu::Backends::all(),
20            ..Default::default()
21        });
22        
23        // The surface needs to live as long as the window that created it.
24        // Since we own the window in App, and Renderer lives in App, we need to be careful with lifetimes.
25        // For simplicity here, we use unsafe to extend the lifetime to 'static, assuming Renderer
26        // will be dropped before the Window.
27        // In a real app we might use Arc<Window> or similar.
28        let surface = unsafe { 
29            let surface = instance.create_surface(window).unwrap();
30            std::mem::transmute::<wgpu::Surface<'_>, wgpu::Surface<'static>>(surface)
31        };
32
33        let adapter = instance.request_adapter(
34            &wgpu::RequestAdapterOptions {
35                power_preference: wgpu::PowerPreference::default(),
36                compatible_surface: Some(&surface),
37                force_fallback_adapter: false,
38            },
39        ).await.unwrap();
40
41        let (device, queue) = adapter.request_device(
42            &wgpu::DeviceDescriptor {
43                required_features: wgpu::Features::empty(),
44                required_limits: wgpu::Limits::default(),
45                label: None,
46            },
47            None, // Trace path
48        ).await.unwrap();
49
50        let surface_caps = surface.get_capabilities(&adapter);
51        let surface_format = surface_caps.formats.iter()
52            .copied()
53            .filter(|f| f.is_srgb())
54            .next()
55            .unwrap_or(surface_caps.formats[0]);
56
57        let config = wgpu::SurfaceConfiguration {
58            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
59            format: surface_format,
60            width: size.width,
61            height: size.height,
62            present_mode: surface_caps.present_modes[0],
63            alpha_mode: surface_caps.alpha_modes[0],
64            view_formats: vec![],
65            desired_maximum_frame_latency: 2,
66        };
67
68        surface.configure(&device, &config);
69
70        Self {
71            surface,
72            device,
73            queue,
74            config,
75            size,
76        }
77    }
78
79    pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
80        if new_size.width > 0 && new_size.height > 0 {
81            self.size = new_size;
82            self.config.width = new_size.width;
83            self.config.height = new_size.height;
84            self.surface.configure(&self.device, &self.config);
85        }
86    }
87
88    pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
89        let output = self.surface.get_current_texture()?;
90        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
91        
92        let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
93            label: Some("Render Encoder"),
94        });
95
96        {
97            let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
98                label: Some("Render Pass"),
99                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
100                    view: &view,
101                    resolve_target: None,
102                    ops: wgpu::Operations {
103                        load: wgpu::LoadOp::Clear(wgpu::Color {
104                            r: 0.1,
105                            g: 0.2,
106                            b: 0.3,
107                            a: 1.0,
108                        }),
109                        store: wgpu::StoreOp::Store,
110                    },
111                })],
112                depth_stencil_attachment: None,
113                timestamp_writes: None,
114                occlusion_query_set: None,
115            });
116        }
117
118        self.queue.submit(std::iter::once(encoder.finish()));
119        output.present();
120
121        Ok(())
122    }
123}