Skip to main content

pfx_editor_shell/
paint.rs

1use egui::epaint::ClippedPrimitive;
2use egui::{Color32, TexturesDelta};
3use egui_wgpu::{Renderer, RendererOptions, ScreenDescriptor};
4use pfx_gpu::{Gpu, wgpu};
5
6pub struct Painter {
7    renderer: Renderer,
8}
9
10pub fn linear(v: u8) -> f64 {
11    let c = f64::from(v) / 255.0;
12    if c <= 0.04045 {
13        c / 12.92
14    } else {
15        ((c + 0.055) / 1.055).powf(2.4)
16    }
17}
18
19pub fn clear(colour: Color32) -> wgpu::Color {
20    wgpu::Color {
21        r: linear(colour.r()),
22        g: linear(colour.g()),
23        b: linear(colour.b()),
24        a: 1.0,
25    }
26}
27
28impl Painter {
29    pub fn new(gpu: &Gpu, format: wgpu::TextureFormat, options: RendererOptions) -> Painter {
30        Painter {
31            renderer: Renderer::new(&gpu.device, format, options),
32        }
33    }
34
35    pub fn renderer(&mut self) -> &mut Renderer {
36        &mut self.renderer
37    }
38
39    pub fn paint(
40        &mut self,
41        gpu: &Gpu,
42        view: &wgpu::TextureView,
43        screen: &ScreenDescriptor,
44        textures: &TexturesDelta,
45        primitives: &[ClippedPrimitive],
46        ground: Color32,
47    ) {
48        for (id, deltas) in &textures.set {
49            for delta in deltas {
50                self.renderer
51                    .update_texture(&gpu.device, &gpu.queue, *id, delta);
52            }
53        }
54        let mut encoder = gpu
55            .device
56            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
57                label: Some("editor ui"),
58            });
59        let mut commands =
60            self.renderer
61                .update_buffers(&gpu.device, &gpu.queue, &mut encoder, primitives, screen);
62        {
63            let pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
64                multiview_mask: None,
65                label: Some("editor ui"),
66                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
67                    view,
68                    depth_slice: None,
69                    resolve_target: None,
70                    ops: wgpu::Operations {
71                        load: wgpu::LoadOp::Clear(clear(ground)),
72                        store: wgpu::StoreOp::Store,
73                    },
74                })],
75                depth_stencil_attachment: None,
76                timestamp_writes: None,
77                occlusion_query_set: None,
78            });
79            let mut pass = pass.forget_lifetime();
80            self.renderer.render(&mut pass, primitives, screen);
81        }
82        commands.push(encoder.finish());
83        gpu.queue.submit(commands);
84        for id in &textures.free {
85            self.renderer.free_texture(id);
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn the_clear_colour_is_linear() {
96        let colour = clear(Color32::from_rgb(0, 255, 0x80));
97        assert_eq!(colour.r, 0.0);
98        assert_eq!(colour.g, 1.0);
99        assert!((colour.b - 0.2158605).abs() < 1e-6);
100    }
101}