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