1use frus_core::Scene;
5
6use crate::compositor::{preferred_sample_count, Painters};
7
8const CLEAR_COLOR: wgpu::Color = wgpu::Color {
10 r: 0.05,
11 g: 0.05,
12 b: 0.08,
13 a: 1.0,
14};
15
16pub struct Renderer {
18 surface: wgpu::Surface<'static>,
19 device: wgpu::Device,
20 queue: wgpu::Queue,
21 config: wgpu::SurfaceConfiguration,
22 painters: Painters,
23}
24
25impl Renderer {
26 pub async fn new(
31 target: impl Into<wgpu::SurfaceTarget<'static>>,
32 width: u32,
33 height: u32,
34 ) -> anyhow::Result<Self> {
35 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
36 backends: wgpu::Backends::PRIMARY,
37 ..wgpu::InstanceDescriptor::new_without_display_handle()
38 });
39
40 let surface = instance.create_surface(target)?;
41
42 let adapter = instance
43 .request_adapter(&wgpu::RequestAdapterOptions {
44 power_preference: wgpu::PowerPreference::HighPerformance,
45 compatible_surface: Some(&surface),
46 force_fallback_adapter: false,
47 ..Default::default()
48 })
49 .await?;
50
51 log::info!("Adaptateur GPU : {:?}", adapter.get_info());
52
53 let required_limits = wgpu::Limits::downlevel_defaults().using_resolution(adapter.limits());
57
58 let (device, queue) = adapter
59 .request_device(&wgpu::DeviceDescriptor {
60 label: Some("frus.device"),
61 required_features: wgpu::Features::empty(),
62 required_limits,
63 memory_hints: wgpu::MemoryHints::Performance,
64 ..Default::default()
65 })
66 .await?;
67
68 let caps = surface.get_capabilities(&adapter);
69 let format = caps
70 .formats
71 .iter()
72 .copied()
73 .find(|f| f.is_srgb())
74 .unwrap_or(caps.formats[0]);
75
76 let config = wgpu::SurfaceConfiguration {
77 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
78 format,
79 color_space: wgpu::SurfaceColorSpace::Auto,
80 width,
81 height,
82 present_mode: caps.present_modes[0],
83 alpha_mode: caps.alpha_modes[0],
84 view_formats: vec![],
85 desired_maximum_frame_latency: 2,
86 };
87 surface.configure(&device, &config);
88
89 let sample_count = preferred_sample_count(&adapter, format);
91 log::info!("MSAA: {sample_count}×");
92
93 let mut painters = Painters::new(&device, &queue, format, sample_count);
94 painters.warm_up(&device, &queue, format);
96
97 Ok(Self {
98 surface,
99 device,
100 queue,
101 config,
102 painters,
103 })
104 }
105
106 pub fn resize(&mut self, width: u32, height: u32) {
109 if width > 0 && height > 0 {
110 self.config.width = width;
111 self.config.height = height;
112 self.surface.configure(&self.device, &self.config);
113 }
114 }
115
116 pub fn reconfigure(&mut self) {
118 self.surface.configure(&self.device, &self.config);
119 }
120
121 pub fn render(&mut self, scene: &Scene) -> RenderOutcome {
123 let frame = match self.surface.get_current_texture() {
124 wgpu::CurrentSurfaceTexture::Success(frame)
125 | wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
126 wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => {
127 return RenderOutcome::Skipped;
128 }
129 wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
130 return RenderOutcome::NeedsReconfigure;
131 }
132 wgpu::CurrentSurfaceTexture::Validation => {
133 log::warn!("frame skipped: surface validation error");
134 return RenderOutcome::Skipped;
135 }
136 };
137 let view = frame
138 .texture
139 .create_view(&wgpu::TextureViewDescriptor::default());
140
141 self.painters.render(
142 &self.device,
143 &self.queue,
144 self.config.format,
145 &view,
146 self.config.width,
147 self.config.height,
148 scene,
149 Some(CLEAR_COLOR),
150 );
151
152 self.queue.present(frame);
153 RenderOutcome::Presented
154 }
155}
156
157pub enum RenderOutcome {
159 Presented,
161 Skipped,
164 NeedsReconfigure,
166}