1use crate::engine::renderer_model::DrawCommand;
4use anyhow::Result;
5use std::sync::Arc;
6use std::{env, fmt::Debug};
7use wgpu::util::DeviceExt;
8use winit::window::Window;
9
10use super::glyph::text::{TextRenderer, TextSection};
11
12pub struct GpuRenderer {
14 surface: wgpu::Surface<'static>,
16 device: wgpu::Device,
18 queue: wgpu::Queue,
20 config: wgpu::SurfaceConfiguration,
22 size: winit::dpi::PhysicalSize<u32>,
24 scale_factor: f64,
26 render_pipeline: wgpu::RenderPipeline,
28 vertex_buffer: Option<wgpu::Buffer>,
30 vertices: Vec<Vertex>,
32 num_vertices: u32,
34
35 text_renderer: Option<TextRenderer>,
37
38 enable_text_culling: bool,
40}
41
42#[repr(C)]
43#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
44struct Vertex {
45 position: [f32; 3],
46 color: [f32; 4],
47}
48
49impl Vertex {
50 fn desc() -> wgpu::VertexBufferLayout<'static> {
51 wgpu::VertexBufferLayout {
52 array_stride: size_of::<Vertex>() as wgpu::BufferAddress,
53 step_mode: wgpu::VertexStepMode::Vertex,
54 attributes: &[
55 wgpu::VertexAttribute {
56 offset: 0,
57 shader_location: 0,
58 format: wgpu::VertexFormat::Float32x3,
59 },
60 wgpu::VertexAttribute {
61 offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
62 shader_location: 1,
63 format: wgpu::VertexFormat::Float32x4,
64 },
65 ],
66 }
67 }
68}
69
70impl GpuRenderer {
71 pub async fn new(window: Arc<Window>, font_path: Option<&str>) -> Result<Self> {
73 let size = window.inner_size();
74 let scale_factor = window.scale_factor();
75
76 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
82 backends: select_wgpu_backends(),
83 flags: Default::default(),
84 memory_budget_thresholds: Default::default(),
85 backend_options: Default::default(),
86 display: None,
87 });
88
89 let surface = instance.create_surface(window.clone())?;
92
93 let adapter = instance
95 .request_adapter(&wgpu::RequestAdapterOptions {
96 power_preference: wgpu::PowerPreference::default(),
97 compatible_surface: Some(&surface),
98 force_fallback_adapter: false,
99 })
100 .await?;
101
102 let (device, queue) = adapter
104 .request_device(&wgpu::DeviceDescriptor {
105 label: None,
106 required_features: wgpu::Features::empty(),
107 required_limits: wgpu::Limits::default(),
108 experimental_features: Default::default(),
109 memory_hints: wgpu::MemoryHints::default(),
110 trace: Default::default(),
111 })
112 .await?;
113
114 let surface_caps = surface.get_capabilities(&adapter);
117 let surface_format = surface_caps
118 .formats
119 .iter()
120 .copied()
121 .find(|f| f.is_srgb())
122 .unwrap_or(surface_caps.formats[0]);
123
124 let config = wgpu::SurfaceConfiguration {
125 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
126 format: surface_format,
127 width: size.width,
128 height: size.height,
129 present_mode: surface_caps.present_modes[0],
130 alpha_mode: surface_caps.alpha_modes[0],
131 view_formats: vec![],
132 desired_maximum_frame_latency: 2,
133 };
134 surface.configure(&device, &config);
135
136 let main_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
140 label: Some("Main Shader"),
141 source: wgpu::ShaderSource::Wgsl(include_str!("shader/main.wgsl").into()),
142 });
143
144 let render_pipeline_layout =
146 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
147 label: Some("Render Pipeline Layout"),
148 bind_group_layouts: &[],
149 immediate_size: 0,
150 });
151
152 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
153 label: Some("Render Pipeline"),
154 layout: Some(&render_pipeline_layout),
155 cache: None,
156 vertex: wgpu::VertexState {
157 module: &main_shader,
158 entry_point: Some("vs_main"),
159 buffers: &[Vertex::desc()],
160 compilation_options: wgpu::PipelineCompilationOptions::default(),
161 },
162 fragment: Some(wgpu::FragmentState {
163 module: &main_shader,
164 entry_point: Some("fs_main"),
165 targets: &[Some(wgpu::ColorTargetState {
166 format: config.format,
167 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
168 write_mask: wgpu::ColorWrites::ALL,
169 })],
170 compilation_options: wgpu::PipelineCompilationOptions::default(),
171 }),
172 primitive: wgpu::PrimitiveState {
173 topology: wgpu::PrimitiveTopology::TriangleList,
174 strip_index_format: None,
175 front_face: wgpu::FrontFace::Ccw,
176 cull_mode: None, polygon_mode: wgpu::PolygonMode::Fill,
178 unclipped_depth: false,
179 conservative: false,
180 },
181 depth_stencil: None,
182 multisample: wgpu::MultisampleState {
183 count: 1,
184 mask: !0,
185 alpha_to_coverage_enabled: false,
186 },
187 multiview_mask: None,
188 });
189 let text_renderer = if let Some(p) = font_path {
193 match std::fs::read(p) {
194 Ok(bytes) => {
195 match TextRenderer::new_from_bytes(&device, &queue, config.format, bytes) {
196 Ok(t) => Some(t),
197 Err(e) => {
198 log::warn!(target:"PRender::gpu::font" ,"failed to init text renderer from provided font: {}", e);
199 None
200 }
201 }
202 }
203 Err(e) => {
204 log::warn!(target:"PRender::gpu::font" ,"failed to read font path '{}': {}", p, e);
205 None
206 }
207 }
208 } else {
209 match TextRenderer::new_from_device(&device, &queue, config.format) {
210 Ok(t) => Some(t),
211 Err(e) => {
212 log::warn!(target:"PRender::gpu::font" ,"no system font found for text renderer: {}", e);
213 None
214 }
215 }
216 };
217
218 let enable_text_culling = std::env::var("ORINIUM_TEXT_CULL")
220 .map(|v| v != "0")
221 .unwrap_or(true);
222
223 Ok(Self {
224 surface,
225 device,
226 queue,
227 config,
228 size,
229 scale_factor,
230 render_pipeline,
231 vertex_buffer: None,
232 vertices: vec![],
233 num_vertices: 0,
234 text_renderer,
235 enable_text_culling,
236 })
237 }
238
239 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
241 if new_size.width > 0 && new_size.height > 0 {
242 log::info!(target:"PRender::gpu::resized", "Resized: {}x{}", new_size.width, new_size.height);
243
244 let old_size = self.size;
245
246 self.size = new_size;
247
248 self.config.width = new_size.width;
249 self.config.height = new_size.height;
250
251 self.surface.configure(&self.device, &self.config);
252
253 self.update_vertices(old_size, new_size);
254
255 if let Some(tr) = &mut self.text_renderer {
256 tr.resize_view(
257 self.config.width as f32,
258 self.config.height as f32,
259 &self.queue,
260 );
261 }
262 }
263 }
264
265 pub fn parse_draw_commands(&mut self, commands: &[DrawCommand]) {
267 let screen_width = self.size.width as f32;
268 let screen_height = self.size.height as f32;
269
270 let mut vertices = Vec::new();
272 let mut sections: Vec<TextSection> = Vec::new();
274 let sf = self.scale_factor as f32;
276 let mut transform_stack: Vec<(f32, f32)> = vec![(0.0, 0.0)];
278 let current_transform = |stack: &Vec<(f32, f32)>| -> (f32, f32) {
279 let mut dx = 0.0;
280 let mut dy = 0.0;
281 for (x, y) in stack.iter() {
282 dx += x;
283 dy += y;
284 }
285 (dx, dy)
286 };
287 #[derive(Clone, Copy)]
289 struct ClipRect {
290 x: f32,
291 y: f32,
292 w: f32,
293 h: f32,
294 }
295 let mut clip_stack: Vec<ClipRect> = vec![ClipRect {
296 x: 0.0,
297 y: 0.0,
298 w: screen_width,
299 h: screen_height,
300 }];
301 let current_clip = |stack: &Vec<ClipRect>| -> ClipRect { *stack.last().unwrap() };
302
303 for command in commands {
304 match command {
305 DrawCommand::PushTransform { dx, dy } => {
307 transform_stack.push((*dx, *dy));
308 }
309 DrawCommand::PopTransform => {
310 if transform_stack.len() > 1 {
311 transform_stack.pop();
312 }
313 }
314
315 DrawCommand::PushClip {
317 x,
318 y,
319 width: w,
320 height: h,
321 } => {
322 let (tdx, tdy) = current_transform(&transform_stack);
323 let new_clip = ClipRect {
324 x: x + tdx,
325 y: y + tdy,
326 w: *w,
327 h: *h,
328 };
329
330 let parent = current_clip(&clip_stack);
332
333 let x1 = new_clip.x.max(parent.x);
334 let y1 = new_clip.y.max(parent.y);
335 let x2 = (new_clip.x + new_clip.w).min(parent.x + parent.w);
336 let y2 = (new_clip.y + new_clip.h).min(parent.y + parent.h);
337
338 clip_stack.push(ClipRect {
339 x: x1,
340 y: y1,
341 w: (x2 - x1).max(0.0),
342 h: (y2 - y1).max(0.0),
343 });
344 }
345
346 DrawCommand::PopClip => {
347 if clip_stack.len() > 1 {
348 clip_stack.pop();
349 }
350 }
351
352 DrawCommand::DrawRect {
354 x,
355 y,
356 width: w,
357 height: h,
358 color,
359 } => {
360 let (tdx, tdy) = current_transform(&transform_stack);
362 let mut x1 = (x + tdx) * sf;
363 let mut y1 = (y + tdy) * sf;
364 let mut x2 = x1 + w * sf;
365 let mut y2 = y1 + h * sf;
366
367 let clip = current_clip(&clip_stack);
369
370 if x2 <= clip.x * sf
372 || x1 >= (clip.x + clip.w) * sf
373 || y2 <= clip.y * sf
374 || y1 >= (clip.y + clip.h) * sf
375 {
376 continue;
377 }
378
379 x1 = x1.max(clip.x * sf);
381 y1 = y1.max(clip.y * sf);
382 x2 = x2.min((clip.x + clip.w) * sf);
383 y2 = y2.min((clip.y + clip.h) * sf);
384
385 let ndc = |v, max| (v / max) * 2.0 - 1.0;
387
388 let px1 = ndc(x1, screen_width);
389 let py1 = -ndc(y1, screen_height);
390 let px2 = ndc(x2, screen_width);
391 let py2 = -ndc(y2, screen_height);
392
393 let color = color.to_linear_f32_array();
394
395 #[rustfmt::skip]
396 vertices.extend_from_slice(&[
397 Vertex { position: [px1, py1, 0.0], color },
398 Vertex { position: [px1, py2, 0.0], color },
399 Vertex { position: [px2, py1, 0.0], color },
400
401 Vertex { position: [px2, py1, 0.0], color },
402 Vertex { position: [px1, py2, 0.0], color },
403 Vertex { position: [px2, py2, 0.0], color },
404 ]);
405 }
406
407 DrawCommand::DrawText { x, y, text, style } => {
409 let (tdx, tdy) = current_transform(&transform_stack);
410
411 let clip = current_clip(&clip_stack);
412
413 let tw = clip.w;
414 let th = clip.h;
415
416 let font_size = &style.font_size;
417
418 let mut skip_text = false;
420 if self.enable_text_culling {
421 let sx1 = (x + tdx) * sf;
423 let sy1 = (y + tdy) * sf;
424 let est_w = if !tw.is_finite() || tw <= 0.0 {
426 (*font_size * sf) * (text.len().max(1) as f32) * 0.5
428 } else {
429 tw * sf
430 };
431 let est_h = if !th.is_finite() || th <= 0.0 {
432 (*font_size * sf) * 1.2 * (text.lines().count() as f32).max(1.0)
434 } else {
435 th * sf
436 };
437 let sx2 = sx1 + est_w;
438 let sy2 = sy1 + est_h;
439
440 let clip_l = clip.x * sf;
441 let clip_t = clip.y * sf;
442 let clip_r = (clip.x + clip.w) * sf;
443 let clip_b = (clip.y + clip.h) * sf;
444
445 if sx2 <= clip_l || sx1 >= clip_r || sy2 <= clip_t || sy1 >= clip_b {
446 skip_text = true;
447 }
448 }
449
450 if skip_text {
451 continue;
452 }
453
454 let section = if let Some(tr) = &mut self.text_renderer {
456 let mut render_text_style = *style;
457 render_text_style.font_size = *font_size * sf;
458 let buffer = tr.create_buffer_for_text(text, render_text_style);
459
460 TextSection {
461 screen_position: ((*x + tdx) * sf, (*y + tdy) * sf),
462 clip_origin: (clip.x * sf, clip.y * sf),
463 bounds: (tw * sf, th * sf),
464 buffer,
465 }
466 } else {
467 continue;
469 };
470 sections.push(section);
471 }
472
473 DrawCommand::DrawPolygon { points, color } => {
475 let (tdx, tdy) = current_transform(&transform_stack);
477 let transformed_points: Vec<(f32, f32)> = points
478 .iter()
479 .map(|(px, py)| ((px + tdx) * sf, (py + tdy) * sf))
480 .collect();
481
482 let clip = current_clip(&clip_stack);
484 let clip_l = clip.x * sf;
486 let clip_t = clip.y * sf;
487 let clip_r = (clip.x + clip.w) * sf;
488 let clip_b = (clip.y + clip.h) * sf;
489
490 let mut min_x = f32::INFINITY;
492 let mut min_y = f32::INFINITY;
493 let mut max_x = f32::NEG_INFINITY;
494 let mut max_y = f32::NEG_INFINITY;
495 for (x, y) in transformed_points.iter() {
496 min_x = min_x.min(*x);
497 min_y = min_y.min(*y);
498 max_x = max_x.max(*x);
499 max_y = max_y.max(*y);
500 }
501 if max_x <= clip_l || min_x >= clip_r || max_y <= clip_t || min_y >= clip_b {
502 continue;
504 }
505
506 let clip_against_edge = |poly: &Vec<(f32, f32)>, edge: u8| -> Vec<(f32, f32)> {
508 let mut out: Vec<(f32, f32)> = Vec::new();
510 if poly.is_empty() {
511 return out;
512 }
513 let len = poly.len();
514 for i in 0..len {
515 let (sx, sy) = poly[i];
516 let (ex, ey) = poly[(i + 1) % len];
517 let inside = |x: f32, y: f32| -> bool {
519 match edge {
520 0 => x >= clip_l, 1 => x <= clip_r, 2 => y >= clip_t, 3 => y <= clip_b, _ => true,
525 }
526 };
527 let s_in = inside(sx, sy);
528 let e_in = inside(ex, ey);
529
530 if s_in && e_in {
531 out.push((ex, ey));
533 } else if s_in && !e_in {
534 let (ix, iy) = match edge {
537 0 | 1 => {
538 let x_edge = if edge == 0 { clip_l } else { clip_r };
540 let dx = ex - sx;
541 if dx.abs() < f32::EPSILON {
542 (x_edge, sy)
543 } else {
544 let t = (x_edge - sx) / dx;
545 (x_edge, sy + t * (ey - sy))
546 }
547 }
548 2 | 3 => {
549 let y_edge = if edge == 2 { clip_t } else { clip_b };
551 let dy = ey - sy;
552 if dy.abs() < f32::EPSILON {
553 (sx, y_edge)
554 } else {
555 let t = (y_edge - sy) / dy;
556 (sx + t * (ex - sx), y_edge)
557 }
558 }
559 _ => (ex, ey),
560 };
561 out.push((ix, iy));
562 } else if !s_in && e_in {
563 let (ix, iy) = match edge {
565 0 | 1 => {
566 let x_edge = if edge == 0 { clip_l } else { clip_r };
567 let dx = ex - sx;
568 if dx.abs() < f32::EPSILON {
569 (x_edge, sy)
570 } else {
571 let t = (x_edge - sx) / dx;
572 (x_edge, sy + t * (ey - sy))
573 }
574 }
575 2 | 3 => {
576 let y_edge = if edge == 2 { clip_t } else { clip_b };
577 let dy = ey - sy;
578 if dy.abs() < f32::EPSILON {
579 (sx, y_edge)
580 } else {
581 let t = (y_edge - sy) / dy;
582 (sx + t * (ex - sx), y_edge)
583 }
584 }
585 _ => (ex, ey),
586 };
587 out.push((ix, iy));
588 out.push((ex, ey));
589 } else {
590 }
592 }
593 out
594 };
595
596 if transformed_points.len() < 3 {
598 continue;
599 }
600
601 let ndc = |v: f32, max: f32| (v / max) * 2.0 - 1.0;
603
604 let color_arr = color.to_linear_f32_array();
605
606 let v0 = transformed_points[0];
607 for i in 1..(transformed_points.len() - 1) {
608 let tri = vec![v0, transformed_points[i], transformed_points[i + 1]];
609 let mut poly = tri;
611 poly = clip_against_edge(&poly, 0); if poly.is_empty() {
613 continue;
614 }
615 poly = clip_against_edge(&poly, 1); if poly.is_empty() {
617 continue;
618 }
619 poly = clip_against_edge(&poly, 2); if poly.is_empty() {
621 continue;
622 }
623 poly = clip_against_edge(&poly, 3); if poly.is_empty() {
625 continue;
626 }
627
628 for j in 1..(poly.len() - 1) {
630 let p1 = poly[0];
631 let p2 = poly[j];
632 let p3 = poly[j + 1];
633
634 let px1 = ndc(p1.0, screen_width);
635 let py1 = -ndc(p1.1, screen_height);
636 let px2 = ndc(p2.0, screen_width);
637 let py2 = -ndc(p2.1, screen_height);
638 let px3 = ndc(p3.0, screen_width);
639 let py3 = -ndc(p3.1, screen_height);
640
641 vertices.push(Vertex {
642 position: [px1, py1, 0.0],
643 color: color_arr,
644 });
645 vertices.push(Vertex {
646 position: [px2, py2, 0.0],
647 color: color_arr,
648 });
649 vertices.push(Vertex {
650 position: [px3, py3, 0.0],
651 color: color_arr,
652 });
653 }
654 }
655 }
656
657 #[allow(unused)]
659 DrawCommand::DrawEllipse {
660 center,
661 radius_x,
662 radius_y,
663 color,
664 } => {
665 let (tdx, tdy) = current_transform(&transform_stack);
667 let cx = center.0 + tdx;
668 let cy = center.1 + tdy;
669
670 let clip = current_clip(&clip_stack);
672
673 todo!("Ellipse drawing with clipping is not implemented yet");
674 }
675 }
676 }
677
678 self.set_vertex_buffer(vertices);
679
680 if let Some(tr) = &mut self.text_renderer {
682 tr.queue(&self.device, &self.queue, §ions).unwrap();
683 }
684 }
685
686 pub fn render(&mut self) -> Result<()> {
692 let current_surface_texture = self.surface.get_current_texture();
694
695 let output = if let wgpu::CurrentSurfaceTexture::Success(frame) = current_surface_texture {
696 frame
697 } else {
698 anyhow::bail!(
699 "`surface.get_current_texture` hasn't succeeded: {:?}.",
700 current_surface_texture
701 );
702 };
703 let view = output
704 .texture
705 .create_view(&wgpu::TextureViewDescriptor::default());
706
707 let mut encoder = self
712 .device
713 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
714 label: Some("Render Encoder"),
715 });
716
717 {
719 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
720 label: Some("Render Pass"),
721 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
722 view: &view,
723 resolve_target: None,
724 ops: wgpu::Operations {
725 load: wgpu::LoadOp::Clear(wgpu::Color {
727 r: 1.0,
728 g: 1.0,
729 b: 1.0,
730 a: 1.0,
731 }),
732 store: wgpu::StoreOp::Store,
733 },
734 depth_slice: None,
735 })],
736 depth_stencil_attachment: None,
737 occlusion_query_set: None,
738 timestamp_writes: None,
739 multiview_mask: None,
740 });
741
742 render_pass.set_pipeline(&self.render_pipeline);
744 if let Some(ref vertex_buffer) = self.vertex_buffer {
746 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
747 render_pass.draw(0..self.num_vertices, 0..1);
748 }
749 }
750
751 if let Some(tr) = &mut self.text_renderer {
753 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
754 label: Some("Text Render Pass"),
755 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
756 view: &view,
757 resolve_target: None,
758 ops: wgpu::Operations {
759 load: wgpu::LoadOp::Load,
760 store: wgpu::StoreOp::Store,
761 },
762 depth_slice: None,
763 })],
764 depth_stencil_attachment: None,
765 occlusion_query_set: None,
766 timestamp_writes: None,
767 multiview_mask: None,
768 });
769 tr.draw(&mut rpass);
770 }
771
772 self.queue.submit(std::iter::once(encoder.finish()));
774
775 output.present();
777
778 Ok(())
779 }
780
781 fn update_vertices(
782 &mut self,
783 old_size: winit::dpi::PhysicalSize<u32>,
784 new_size: winit::dpi::PhysicalSize<u32>,
785 ) {
786 let old_w = old_size.width as f32;
787 let old_h = old_size.height as f32;
788 let new_w = new_size.width as f32;
789 let new_h = new_size.height as f32;
790
791 let mut new_vertices = self.vertices.clone();
792
793 for vertex in new_vertices.iter_mut() {
794 let logical_x = (vertex.position[0] + 1.0) / 2.0 * old_w;
796 let logical_y = -(vertex.position[1] - 1.0) / 2.0 * old_h;
797
798 vertex.position[0] = (logical_x / new_w) * 2.0 - 1.0;
800 vertex.position[1] = -((logical_y / new_h) * 2.0 - 1.0);
801 }
802 self.set_vertex_buffer(new_vertices);
803 }
804
805 fn set_vertex_buffer(&mut self, vertices: Vec<Vertex>) {
806 if !vertices.is_empty() {
808 self.vertex_buffer = Some(self.device.create_buffer_init(
809 &wgpu::util::BufferInitDescriptor {
810 label: Some("Vertex Buffer"),
811 contents: bytemuck::cast_slice(&vertices),
812 usage: wgpu::BufferUsages::VERTEX,
813 },
814 ));
815 self.num_vertices = vertices.len() as u32;
816 }
817 self.vertices = vertices;
818 }
819
820 pub fn set_scale_factor(&mut self, scale_factor: f64) {
821 self.scale_factor = scale_factor;
822 }
823}
824
825fn select_wgpu_backends() -> wgpu::Backends {
826 if let Ok(value) = env::var("ORINIUM_WGPU_BACKEND") {
827 match value.to_lowercase().as_str() {
828 "gl" | "opengl" => return wgpu::Backends::GL,
829 "vulkan" | "vk" => return wgpu::Backends::VULKAN,
830 "metal" => return wgpu::Backends::METAL,
831 "dx12" | "d3d12" => return wgpu::Backends::DX12,
832 "primary" => return wgpu::Backends::PRIMARY,
833 _ => {}
834 }
835 }
836
837 let is_wsl = env::var_os("WSL_DISTRO_NAME").is_some() || env::var_os("WSL_INTEROP").is_some();
838 let is_wayland = env::var_os("WAYLAND_DISPLAY").is_some();
839
840 if is_wsl && is_wayland {
841 return wgpu::Backends::GL;
843 }
844
845 wgpu::Backends::PRIMARY
846}