Skip to main content

gcrecomp_runtime/graphics/
renderer.rs

1// Main renderer
2use crate::graphics::framebuffer::FrameBuffer;
3use crate::graphics::gx::GXProcessor;
4use crate::graphics::shaders::ShaderManager;
5use crate::graphics::upscaler::Upscaler;
6use anyhow::Result;
7use std::sync::Arc;
8use wgpu::*;
9
10pub struct Renderer {
11    device: Device,
12    queue: Queue,
13    surface: Surface<'static>,
14    config: SurfaceConfiguration,
15    upscaler: Upscaler,
16    frame_buffers: Vec<FrameBuffer>,
17    current_resolution: (u32, u32),
18    target_resolution: (u32, u32),
19    gx_processor: GXProcessor,
20    shader_manager: ShaderManager,
21    _window: Arc<winit::window::Window>,
22}
23
24impl Renderer {
25    pub fn new(window: Arc<winit::window::Window>) -> Result<Self> {
26        let instance = Instance::new(InstanceDescriptor::default());
27        // SAFETY: The window is stored in Arc in the struct, ensuring it outlives the surface
28        let surface = instance.create_surface(window.clone())?;
29
30        let adapter = pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
31            power_preference: PowerPreference::HighPerformance,
32            compatible_surface: Some(&surface),
33            force_fallback_adapter: false,
34        }))
35        .ok_or_else(|| anyhow::anyhow!("Failed to find suitable GPU adapter"))?;
36
37        let (device, queue) = pollster::block_on(adapter.request_device(
38            &DeviceDescriptor {
39                label: None,
40                required_features: Features::empty(),
41                required_limits: Limits::default(),
42            },
43            None,
44        ))?;
45
46        let size = window.inner_size();
47        let config = surface
48            .get_default_config(&adapter, size.width, size.height)
49            .ok_or_else(|| anyhow::anyhow!("Failed to get surface config"))?;
50
51        surface.configure(&device, &config);
52
53        let upscaler = Upscaler::new(&device, &config)?;
54        let gx_processor = GXProcessor::new();
55        let mut shader_manager = ShaderManager::new();
56
57        // Load default shaders
58        let default_vert = r#"
59            struct VertexInput {
60                @location(0) position: vec3<f32>,
61                @location(1) tex_coord: vec2<f32>,
62            }
63            struct VertexOutput {
64                @builtin(position) position: vec4<f32>,
65                @location(0) tex_coord: vec2<f32>,
66            }
67            @vertex
68            fn main(input: VertexInput) -> VertexOutput {
69                var output: VertexOutput;
70                output.position = vec4<f32>(input.position, 1.0);
71                output.tex_coord = input.tex_coord;
72                return output;
73            }
74        "#;
75
76        let default_frag = r#"
77            @group(0) @binding(0) var texture: texture_2d<f32>;
78            @group(0) @binding(1) var sampler: sampler;
79            struct FragmentInput {
80                @location(0) tex_coord: vec2<f32>,
81            }
82            @fragment
83            fn main(input: FragmentInput) -> @location(0) vec4<f32> {
84                return textureSample(texture, sampler, input.tex_coord);
85            }
86        "#;
87
88        shader_manager.load_shader(&device, "default_vertex", default_vert)?;
89        shader_manager.load_shader(&device, "default_fragment", default_frag)?;
90
91        Ok(Self {
92            device,
93            queue,
94            surface,
95            config,
96            upscaler,
97            frame_buffers: Vec::new(),
98            current_resolution: (640, 480), // GameCube native
99            target_resolution: (size.width, size.height),
100            gx_processor,
101            shader_manager,
102            _window: window,
103        })
104    }
105
106    pub fn resize(&mut self, width: u32, height: u32) {
107        self.target_resolution = (width, height);
108        self.config.width = width;
109        self.config.height = height;
110        self.surface.configure(&self.device, &self.config);
111    }
112
113    pub fn set_resolution(&mut self, width: u32, height: u32) {
114        self.current_resolution = (width, height);
115    }
116
117    pub fn set_upscale_factor(&mut self, factor: f32) -> Result<()> {
118        let target_w = (self.current_resolution.0 as f32 * factor) as u32;
119        let target_h = (self.current_resolution.1 as f32 * factor) as u32;
120        self.target_resolution = (target_w, target_h);
121        Ok(())
122    }
123
124    pub fn begin_frame(&mut self) -> Result<wgpu::SurfaceTexture> {
125        let output = self.surface.get_current_texture()?;
126        Ok(output)
127    }
128
129    pub fn end_frame(&mut self, frame: wgpu::SurfaceTexture) {
130        frame.present();
131    }
132
133    pub fn device(&self) -> &Device {
134        &self.device
135    }
136
137    pub fn queue(&self) -> &Queue {
138        &self.queue
139    }
140}