nc_ui/
painter.rs

1//
2// Copyright 2021-Present (c) Raja Lehtihet & Wael El Oraiby
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9//
10// 2. Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13//
14// 3. Neither the name of the copyright holder nor the names of its contributors
15// may be used to endorse or promote products derived from this software without
16// specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28// POSSIBILITY OF SUCH DAMAGE.
29//
30//
31// Copyright (c) 2021 cohaereo
32//
33// Permission is hereby granted, free of charge, to any
34// person obtaining a copy of this software and associated
35// documentation files (the "Software"), to deal in the
36// Software without restriction, including without
37// limitation the rights to use, copy, modify, merge,
38// publish, distribute, sublicense, and/or sell copies of
39// the Software, and to permit persons to whom the Software
40// is furnished to do so, subject to the following
41// conditions:
42//
43// The above copyright notice and this permission notice
44// shall be included in all copies or substantial portions
45// of the Software.
46//
47// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
48// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
49// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
50// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
51// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
52// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
53// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
54// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
55// DEALINGS IN THE SOFTWARE.
56//
57use crate::*;
58use crate::rs_math3d::*;
59use crate::nc_renderer::*;
60use rs_ctypes::*;
61
62use egui::{
63    paint::{Color32, Mesh, Texture},
64    vec2, ClippedMesh,
65};
66
67render_data! {
68    vertex Vertex {
69        a_pos   : Vec2f,
70        a_tc    : Vec2f,
71        s_rgba  : Color4b,
72
73    }
74
75    uniforms Uniforms {
76        u_screen_size   : Vec2f,
77    }
78}
79
80#[derive(Default)]
81struct UserTexture {
82    size: (usize, usize),
83
84    /// Pending upload (will be emptied later).
85    pixels: Vec<u8>,
86
87    /// Lazily uploaded
88    texture: Option<TexturePtr>,
89
90    /// For user textures there is a choice between
91    /// Linear (default) and Nearest.
92    filtering: bool,
93
94    /// User textures can be modified and this flag
95    /// is used to indicate if pixel data for the
96    /// texture has been updated.
97    dirty: bool,
98}
99
100const VS_SRC: &str = r#"
101    #version 300 es
102    uniform vec2 u_screen_size;
103    in highp vec2 a_pos;
104    in highp vec4 s_rgba;
105    in highp vec2 a_tc;
106    in highp vec3 b;
107    out highp vec4 v_rgba;
108    out vec2 v_tc;
109
110    void main() {
111        gl_Position = vec4(
112            2.0 * a_pos.x / u_screen_size.x - 1.0,
113            1.0 - 2.0 * a_pos.y / u_screen_size.y,
114            0.0,
115            1.0);
116        v_rgba = s_rgba;
117        v_tc = a_tc;
118    }
119"#;
120
121const FS_SRC: &str = r#"
122    #version 300 es
123    uniform lowp sampler2D u_sampler;
124    in highp vec4 v_rgba;
125    in highp vec2 v_tc;
126    in highp vec3 v_b;
127    layout(location = 0) out lowp vec4 f_color;
128
129    void main() {
130        highp vec4 tcol = texture(u_sampler, v_tc);
131        f_color = tcol * v_rgba;
132    }
133"#;
134
135pub struct Painter {
136    driver  : DriverPtr,
137    pipeline: PipelinePtr,
138    index_buffer: DeviceBufferPtr,
139    vertex_buffer: DeviceBufferPtr,
140    canvas_width: u32,
141    canvas_height: u32,
142    egui_texture: Option<TexturePtr>,
143    egui_texture_version: Option<u64>,
144    user_textures: Vec<UserTexture>,
145}
146
147impl Painter {
148    pub fn new(
149        drv: &mut DriverPtr,
150        window: &mut glfw::Window,
151        canvas_width: u32,
152        canvas_height: u32,
153    ) -> Painter {
154
155        let program = drv.create_shader(ShaderDesc {
156            vertex_shader       : String::from(VS_SRC),
157            pixel_shader        : String::from(FS_SRC),
158
159            vertex_attributes   : vec!{
160               Vertex::get_attribute_names(),
161            },
162            vertex_uniforms     : Uniforms::get_uniform_names(),
163            vertex_surfaces     : vec!{},
164
165            pixel_uniforms      : vec!{},
166            pixel_surfaces      : vec!{ String::from("u_sampler") }
167        }).unwrap();
168
169        let vertex_layout = VertexBufferLayout {
170            buffer_id           : 0,
171            vertex_attributes   : Vertex::get_attribute_descriptors(),
172            stride              : Vertex::stride(),
173            divisor             : 0,
174        };
175
176        let pipeline_desc = PipelineDesc {
177            primitive_type      : PrimitiveType::Triangles,
178            shader              : program,
179            buffer_layouts      : vec! { vertex_layout.clone() },
180            uniform_descs       : Uniforms::get_uniform_descriptors(),
181            index_type          : IndexType::UInt16,
182            face_winding        : FaceWinding::CCW,
183            cull_mode           : CullMode::None,
184            depth_write         : true,
185            depth_test          : false,
186            blend               : BlendOp::Add(Blend::default()),
187        };
188
189        let pipeline = drv.create_pipeline(pipeline_desc).unwrap();
190
191        let index_buffer    = drv.create_device_buffer(DeviceBufferDesc::Index(Usage::Dynamic(65536 * 4))).unwrap();
192        let vertex_buffer   = drv.create_device_buffer(DeviceBufferDesc::Vertex(Usage::Dynamic(65536 * 4 * std::mem::size_of::<Vertex>()))).unwrap();
193        Painter {
194            driver: drv.clone(),
195            pipeline,
196            canvas_width,
197            canvas_height,
198            index_buffer,
199            vertex_buffer,
200            egui_texture: None,
201            egui_texture_version: None,
202            user_textures: Default::default(),
203        }
204    }
205
206    pub fn new_user_texture(
207        &mut self,
208        size: (usize, usize),
209        srgba_pixels: &[Color32],
210        filtering: bool,
211    ) -> egui::TextureId {
212        assert_eq!(size.0 * size.1, srgba_pixels.len());
213
214        let mut pixels: Vec<u8> = Vec::with_capacity(srgba_pixels.len() * 4);
215        for srgba in srgba_pixels {
216            pixels.push(srgba.r());
217            pixels.push(srgba.g());
218            pixels.push(srgba.b());
219            pixels.push(srgba.a());
220        }
221
222        let id = egui::TextureId::User(self.user_textures.len() as u64);
223        self.user_textures.push(UserTexture {
224            size,
225            pixels,
226            texture: None,
227            filtering,
228            dirty: true,
229        });
230        id
231    }
232
233    fn upload_egui_texture(&mut self, texture: &Texture) {
234        if self.egui_texture_version == Some(texture.version) {
235            return; // No change
236        }
237
238        println!("uploading egui texture: {}x{}", texture.width, texture.height);
239        let mut pixels: Vec<u8> = Vec::with_capacity(texture.pixels.len() * 4);
240        for &alpha in &texture.pixels {
241            let srgba = Color32::from_white_alpha(alpha);
242
243            pixels.push(srgba.r());
244            pixels.push(srgba.g());
245            pixels.push(srgba.b());
246            pixels.push(srgba.a());
247        }
248
249        let tex_desc    = TextureDesc {
250            sampler_desc    : SamplerDesc::default(texture.width, texture.height)
251                .with_pixel_format(PixelFormat::RGBA8(MinMagFilter::default()
252                    .with_mag_filter(Filter::Linear)
253                    .with_min_filter(Filter::Linear)))
254                .with_wrap_mode(WrapMode::ClampToEdge),
255            payload         : Some(Box::new(pixels))
256        };
257
258        let tex = self.driver.create_texture(tex_desc).unwrap();
259        self.egui_texture           = Some(tex);
260        self.egui_texture_version   = Some(texture.version);
261    }
262
263    fn upload_user_textures(&mut self) {
264        for user_texture in &mut self.user_textures {
265            if !user_texture.texture.is_none() && !user_texture.dirty {
266                continue;
267            }
268
269            let pixels = std::mem::take(&mut user_texture.pixels);
270
271            let tex_desc    = TextureDesc {
272                sampler_desc    : SamplerDesc::default(user_texture.size.0, user_texture.size.1).with_pixel_format(PixelFormat::RGBA8(MinMagFilter::default().with_mag_filter(Filter::Linear).with_min_filter(Filter::Linear))),
273                payload         : Some(Box::new(pixels))
274            };
275
276            if user_texture.texture.is_none() {
277                println!("uploading user texture");
278
279                let tex = self.driver.create_texture(tex_desc).unwrap();
280                user_texture.texture = Some(tex);
281            } else {
282                self.driver.update_texture(&mut user_texture.texture.as_mut().unwrap(), tex_desc.payload.unwrap())
283            }
284            user_texture.dirty = false;
285        }
286    }
287
288    fn get_texture(&self, texture_id: egui::TextureId) -> TexturePtr {
289        match texture_id {
290            egui::TextureId::Egui => {
291                self.egui_texture.as_ref().unwrap().clone()
292            },
293
294            egui::TextureId::User(id) => {
295
296                let id = id as usize;
297                assert!(id < self.user_textures.len());
298                let texture = self.user_textures[id].texture.clone();
299                texture.expect("Should have been uploaded")
300            }
301        }
302    }
303
304    pub fn update_user_texture_data(&mut self, texture_id: egui::TextureId, pixels: &[Color32]) {
305        match texture_id {
306            egui::TextureId::Egui => {}
307            egui::TextureId::User(id) => {
308                let id = id as usize;
309                assert!(id < self.user_textures.len());
310                self.user_textures[id].pixels = Vec::with_capacity(pixels.len() * 4);
311
312                for p in pixels {
313                    self.user_textures[id].pixels.push(p.r());
314                    self.user_textures[id].pixels.push(p.g());
315                    self.user_textures[id].pixels.push(p.b());
316                    self.user_textures[id].pixels.push(p.a());
317                }
318                self.user_textures[id].dirty = true;
319            }
320        }
321    }
322
323    pub fn paint_jobs(
324        &mut self,
325        frame_buffer: Option<FrameBufferPtr>,
326        bg_color: Option<Color32>,
327        meshes: Vec<ClippedMesh>,
328        egui_texture: &Texture,
329        pixels_per_point: f32,
330    ) {
331        self.upload_egui_texture(egui_texture);
332        self.upload_user_textures();
333
334        let screen_size_pixels = vec2(self.canvas_width as f32, self.canvas_height as f32);
335        let screen_size_points = screen_size_pixels / pixels_per_point;
336
337
338        for ClippedMesh(clip_rect, mesh) in meshes {
339
340            let clip_min_x = pixels_per_point * clip_rect.min.x;
341            let clip_min_y = pixels_per_point * clip_rect.min.y;
342            let clip_max_x = pixels_per_point * clip_rect.max.x;
343            let clip_max_y = pixels_per_point * clip_rect.max.y;
344            let clip_min_x = clip_min_x.clamp(0.0, screen_size_pixels.x);
345            let clip_min_y = clip_min_y.clamp(0.0, screen_size_pixels.y);
346            let clip_max_x = clip_max_x.clamp(clip_min_x, screen_size_pixels.x);
347            let clip_max_y = clip_max_y.clamp(clip_min_y, screen_size_pixels.y);
348            let clip_min_x = clip_min_x.round() as i32;
349            let clip_min_y = clip_min_y.round() as i32;
350            let clip_max_x = clip_max_x.round() as i32;
351            let clip_max_y = clip_max_y.round() as i32;
352
353            //scissor Y coordinate is from the bottom
354            self.driver.set_scissor (
355                clip_min_x as u32,
356                self.canvas_height as u32 - clip_max_y as u32,
357                (clip_max_x - clip_min_x) as u32,
358                (clip_max_y - clip_min_y) as u32,
359            );
360
361            if mesh.vertices.len() > 0 {
362                self.paint_mesh(&mesh, Vec2f::new(screen_size_points.x, screen_size_points.y));
363            }
364        }
365    }
366
367    fn paint_mesh(&mut self, mesh: &Mesh, screen_size: Vec2f) {
368        debug_assert!(mesh.is_valid());
369        let indices: Vec<u16> = mesh.indices.iter().map(|idx| *idx as u16).collect();
370
371        let mut vertices: Vec<Vertex> = Vec::with_capacity(mesh.vertices.len());
372        for v in &mesh.vertices {
373            let vert = Vertex {
374                a_pos   : Vec2f::new(v.pos.x, v.pos.y),
375                s_rgba  : color4b(v.color[0], v.color[1], v.color[2], v.color[3]),
376                a_tc    : Vec2f::new(v.uv.x, v.uv.y),
377            };
378
379            vertices.push(vert);
380        }
381
382        self.driver.update_device_buffer(&mut self.vertex_buffer, 0, &vertices);
383        self.driver.update_device_buffer(&mut self.index_buffer, 0, &indices);
384
385        let bindings = Bindings {
386            vertex_buffers  : vec!{ self.vertex_buffer.clone() },
387            index_buffer    : Some(self.index_buffer.clone()),
388
389            vertex_images   : Vec::new(),
390            pixel_images    : Vec::from([self.get_texture(mesh.texture_id)]),
391        };
392
393        let u = Uniforms { u_screen_size: screen_size };
394        self.driver.draw(&self.pipeline, &bindings, &u as *const Uniforms as *const c_void, (indices.len() / 3) as u32, 1);
395    }
396}