1use crate::engine::renderer_model::DrawCommand;
4use crate::platform::cache_stats::CacheStatsSnapshot;
5use anyhow::Result;
6use winit::dpi::PhysicalSize;
7
8use super::draw_sink::{DrawError, DrawSink};
9use std::env;
10use std::sync::Arc;
11use wgpu::util::DeviceExt;
12use winit::window::Window;
13
14use super::image::ImageRenderer;
15use super::mesh::{self, MeshBuilder, Vertex};
16use super::text::global_font;
17use super::text::text_renderer::TextRenderer;
18
19pub struct GpuRenderer {
21 surface: wgpu::Surface<'static>,
23 device: wgpu::Device,
25 queue: wgpu::Queue,
27 config: wgpu::SurfaceConfiguration,
29 size: winit::dpi::PhysicalSize<u32>,
31 scale_factor: f64,
33 render_pipeline: wgpu::RenderPipeline,
35 vertex_buffer: Option<wgpu::Buffer>,
37 mesh_builder: MeshBuilder,
39 draw_items: Vec<mesh::DrawItem>,
41
42 text_renderer: Option<TextRenderer>,
44
45 image_renderer: ImageRenderer,
47
48 enable_text_culling: bool,
50}
51
52impl Vertex {
53 fn desc() -> wgpu::VertexBufferLayout<'static> {
54 wgpu::VertexBufferLayout {
55 array_stride: size_of::<Vertex>() as wgpu::BufferAddress,
56 step_mode: wgpu::VertexStepMode::Vertex,
57 attributes: &[
58 wgpu::VertexAttribute {
59 offset: 0,
60 shader_location: 0,
61 format: wgpu::VertexFormat::Float32x3,
62 },
63 wgpu::VertexAttribute {
64 offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
65 shader_location: 1,
66 format: wgpu::VertexFormat::Float32x4,
67 },
68 ],
69 }
70 }
71}
72
73impl GpuRenderer {
74 pub async fn new(window: Arc<Window>, font_path: Option<&str>) -> Result<Self> {
76 let size = window.inner_size();
77 let scale_factor = window.scale_factor();
78
79 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
85 backends: select_wgpu_backends(),
86 flags: Default::default(),
87 memory_budget_thresholds: Default::default(),
88 backend_options: Default::default(),
89 display: None,
90 });
91
92 let surface = instance.create_surface(Arc::clone(&window))?;
95
96 let adapter = instance
98 .request_adapter(&wgpu::RequestAdapterOptions {
99 power_preference: wgpu::PowerPreference::default(),
100 compatible_surface: Some(&surface),
101 force_fallback_adapter: false,
102 apply_limit_buckets: true,
106 })
107 .await?;
108
109 let (device, queue) = adapter
111 .request_device(&wgpu::DeviceDescriptor {
112 label: None,
113 required_features: wgpu::Features::empty(),
114 required_limits: wgpu::Limits::default(),
115 experimental_features: Default::default(),
116 memory_hints: wgpu::MemoryHints::default(),
117 trace: Default::default(),
118 })
119 .await?;
120
121 let surface_caps = surface.get_capabilities(&adapter);
124 let surface_format = surface_caps
125 .formats
126 .iter()
127 .copied()
128 .find(|f| f.is_srgb())
129 .unwrap_or(surface_caps.formats[0]);
130
131 let config = wgpu::SurfaceConfiguration {
132 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
133 format: surface_format,
134 color_space: wgpu::SurfaceColorSpace::Auto,
137 width: size.width,
138 height: size.height,
139 present_mode: surface_caps.present_modes[0],
140 alpha_mode: surface_caps.alpha_modes[0],
141 view_formats: vec![],
142 desired_maximum_frame_latency: 2,
143 };
144 surface.configure(&device, &config);
145
146 let main_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
150 label: Some("Main Shader"),
151 source: wgpu::ShaderSource::Wgsl(include_str!("shader/main.wgsl").into()),
152 });
153
154 let render_pipeline_layout =
156 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
157 label: Some("Render Pipeline Layout"),
158 bind_group_layouts: &[],
159 immediate_size: 0,
160 });
161
162 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
163 label: Some("Render Pipeline"),
164 layout: Some(&render_pipeline_layout),
165 cache: None,
166 vertex: wgpu::VertexState {
167 module: &main_shader,
168 entry_point: Some("vs_main"),
169 buffers: &[Some(Vertex::desc())],
170 compilation_options: wgpu::PipelineCompilationOptions::default(),
171 },
172 fragment: Some(wgpu::FragmentState {
173 module: &main_shader,
174 entry_point: Some("fs_main"),
175 targets: &[Some(wgpu::ColorTargetState {
176 format: config.format,
177 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
178 write_mask: wgpu::ColorWrites::ALL,
179 })],
180 compilation_options: wgpu::PipelineCompilationOptions::default(),
181 }),
182 primitive: wgpu::PrimitiveState {
183 topology: wgpu::PrimitiveTopology::TriangleList,
184 strip_index_format: None,
185 front_face: wgpu::FrontFace::Ccw,
186 cull_mode: None, polygon_mode: wgpu::PolygonMode::Fill,
188 unclipped_depth: false,
189 conservative: false,
190 },
191 depth_stencil: None,
192 multisample: wgpu::MultisampleState {
193 count: 1,
194 mask: !0,
195 alpha_to_coverage_enabled: false,
196 },
197 multiview_mask: None,
198 });
199 if let Some(p) = font_path {
203 global_font::load_global_font_path(p);
204 }
205 let text_renderer = match TextRenderer::new(&device, &queue, config.format) {
206 Ok(t) => Some(t),
207 Err(e) => {
208 log::warn!(target:"PRender::gpu::font" ,"no system font found for text renderer: {}", e);
209 None
210 }
211 };
212 let image_renderer = ImageRenderer::new(&device, config.format);
213
214 let enable_text_culling = std::env::var("ORINIUM_TEXT_CULL").map_or(true, |v| v != "0");
216
217 Ok(Self {
218 surface,
219 device,
220 queue,
221 config,
222 size,
223 scale_factor,
224 render_pipeline,
225 vertex_buffer: None,
226 mesh_builder: MeshBuilder::new(
227 size.width as f32,
228 size.height as f32,
229 scale_factor as f32,
230 ),
231 draw_items: Vec::new(),
232 text_renderer,
233 image_renderer,
234 enable_text_culling,
235 })
236 }
237
238 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
240 if new_size.width > 0 && new_size.height > 0 {
241 log::info!(target:"PRender::gpu::resized", "Resized: {}x{}", new_size.width, new_size.height);
242
243 self.size = new_size;
244
245 self.config.width = new_size.width;
246 self.config.height = new_size.height;
247
248 self.surface.configure(&self.device, &self.config);
249
250 if let Some(tr) = &mut self.text_renderer {
251 tr.resize_view(
252 self.config.width as f32,
253 self.config.height as f32,
254 &self.queue,
255 );
256 }
257 }
258 }
259
260 pub fn cache_stats(&self) -> Vec<CacheStatsSnapshot> {
264 self.text_renderer
265 .as_ref()
266 .map(|renderer| renderer.cache_stats().to_vec())
267 .unwrap_or_default()
268 }
269
270 pub fn parse_draw_commands(&mut self, commands: &[DrawCommand]) {
272 self.mesh_builder
273 .set_screen_size(self.size.width as f32, self.size.height as f32);
274 self.mesh_builder.set_scale_factor(self.scale_factor as f32);
275 self.mesh_builder.set_text_culling(self.enable_text_culling);
276
277 let mut text_source: Option<&mut dyn mesh::TextLayoutSource> = match &mut self.text_renderer
278 {
279 Some(tr) => Some(tr),
280 None => None,
281 };
282 let built = self.mesh_builder.build(commands, &mut text_source);
283 let vertices = built.vertices.clone();
284 let sections = built.sections.clone();
285 let images = built.images.clone();
286 self.draw_items.clone_from(&built.draw_items);
287
288 self.set_vertex_buffer(&vertices);
289 self.image_renderer.prepare(
290 &self.device,
291 &self.queue,
292 &images,
293 self.size.width as f32,
294 self.size.height as f32,
295 );
296
297 if let Some(tr) = &mut self.text_renderer {
299 tr.queue(&self.device, &self.queue, §ions).unwrap();
300 }
301 }
302
303 pub fn render(&mut self) -> Result<()> {
309 let current_surface_texture = self.surface.get_current_texture();
311
312 let output = if let wgpu::CurrentSurfaceTexture::Success(frame) = current_surface_texture {
313 frame
314 } else {
315 anyhow::bail!(
316 "`surface.get_current_texture` hasn't succeeded: {:?}.",
317 current_surface_texture
318 );
319 };
320 let view = output
321 .texture
322 .create_view(&wgpu::TextureViewDescriptor::default());
323
324 let mut encoder = self
329 .device
330 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
331 label: Some("Render Encoder"),
332 });
333
334 {
336 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
337 label: Some("Render Pass"),
338 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
339 view: &view,
340 resolve_target: None,
341 ops: wgpu::Operations {
342 load: wgpu::LoadOp::Clear(wgpu::Color {
344 r: 1.0,
345 g: 1.0,
346 b: 1.0,
347 a: 1.0,
348 }),
349 store: wgpu::StoreOp::Store,
350 },
351 depth_slice: None,
352 })],
353 depth_stencil_attachment: None,
354 occlusion_query_set: None,
355 timestamp_writes: None,
356 multiview_mask: None,
357 });
358
359 self.draw_in_order(&mut render_pass);
360 }
361
362 self.queue.submit(std::iter::once(encoder.finish()));
364
365 self.queue.present(output);
367
368 Ok(())
369 }
370
371 fn draw_in_order<'a>(&mut self, render_pass: &mut wgpu::RenderPass<'a>) {
374 let mut i = 0;
375 while i < self.draw_items.len() {
376 match self.draw_items[i] {
377 mesh::DrawItem::Fill {
378 vertex_start,
379 vertex_count,
380 } => {
381 let start = vertex_start;
382 let mut count = vertex_count;
383 i += 1;
384 while i < self.draw_items.len() {
385 if let mesh::DrawItem::Fill {
386 vertex_start: s,
387 vertex_count: c,
388 } = self.draw_items[i]
389 {
390 if s == start + count {
391 count += c;
392 i += 1;
393 } else {
394 break;
395 }
396 } else {
397 break;
398 }
399 }
400 if let Some(ref vertex_buffer) = self.vertex_buffer {
401 render_pass.set_pipeline(&self.render_pipeline);
402 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
403 render_pass.draw(start..start + count, 0..1);
404 }
405 }
406 mesh::DrawItem::Image(index) => {
407 self.image_renderer.draw_at(render_pass, index);
408 i += 1;
409 }
410 mesh::DrawItem::Text(_) => {
411 let mut start = 0u32;
412 let mut end = 0u32;
413 let mut any = false;
414 while i < self.draw_items.len() {
415 if let mesh::DrawItem::Text(idx) = self.draw_items[i] {
416 if let Some(tr) = &self.text_renderer
417 && let Some((s, c)) = tr.section_range(idx)
418 {
419 if !any {
420 start = s;
421 any = true;
422 }
423 end = s + c;
424 }
425 i += 1;
426 } else {
427 break;
428 }
429 }
430 if let Some(tr) = &mut self.text_renderer
431 && any
432 && end > start
433 {
434 tr.draw_range(render_pass, start, end - start);
435 }
436 }
437 }
438 }
439 }
440
441 fn set_vertex_buffer(&mut self, vertices: &[Vertex]) {
442 if !vertices.is_empty() {
444 self.vertex_buffer = Some(self.device.create_buffer_init(
445 &wgpu::util::BufferInitDescriptor {
446 label: Some("Vertex Buffer"),
447 contents: bytemuck::cast_slice(vertices),
448 usage: wgpu::BufferUsages::VERTEX,
449 },
450 ));
451 } else {
452 self.vertex_buffer = None;
453 }
454 }
455
456 pub fn set_scale_factor(&mut self, scale_factor: f64) {
457 self.scale_factor = scale_factor;
458 }
459}
460
461impl DrawSink for GpuRenderer {
467 fn upload(&mut self, commands: &[DrawCommand]) {
468 self.parse_draw_commands(commands);
469 }
470
471 fn present(&mut self) -> Result<(), DrawError> {
472 self.render()
473 .map_err(|e| DrawError::Surface(format!("{e:#}")))
474 }
475
476 fn resize(&mut self, size: (u32, u32)) {
477 GpuRenderer::resize(self, PhysicalSize::new(size.0, size.1));
478 }
479
480 fn set_scale_factor(&mut self, scale_factor: f64) {
481 GpuRenderer::set_scale_factor(self, scale_factor);
482 }
483}
484
485fn select_wgpu_backends() -> wgpu::Backends {
486 if let Ok(value) = env::var("ORINIUM_WGPU_BACKEND") {
487 match value.to_lowercase().as_str() {
488 "gl" | "opengl" => return wgpu::Backends::GL,
489 "vulkan" | "vk" => return wgpu::Backends::VULKAN,
490 "metal" => return wgpu::Backends::METAL,
491 "dx12" | "d3d12" => return wgpu::Backends::DX12,
492 "primary" => return wgpu::Backends::PRIMARY,
493 _ => {}
494 }
495 }
496
497 let is_wsl = env::var_os("WSL_DISTRO_NAME").is_some() || env::var_os("WSL_INTEROP").is_some();
498 let is_wayland = env::var_os("WAYLAND_DISPLAY").is_some();
499
500 if is_wsl && is_wayland {
501 return wgpu::Backends::GL;
503 }
504
505 wgpu::Backends::PRIMARY
506}