nvgx_ogl/
lib.rs

1#[macro_use]
2#[allow(unused)]
3extern crate anyhow;
4
5pub mod fb;
6
7use std::{ops::Range, sync::Arc};
8
9use nvgx::{
10    utils::{premul_color, xform_to_3x4},
11    *,
12};
13use renderer::GLArrayBuffer;
14use slab::Slab;
15
16mod renderer;
17
18pub struct RenderConfig {
19    antialias: bool,
20}
21
22impl RenderConfig {
23    pub fn antialias(mut self, antialias: bool) -> Self {
24        self.antialias = antialias;
25        self
26    }
27}
28
29impl Default for RenderConfig {
30    fn default() -> Self {
31        Self { antialias: true }
32    }
33}
34
35struct Shader {
36    prog: gl::types::GLuint,
37    frag: gl::types::GLuint,
38    vert: gl::types::GLuint,
39    loc_viewsize: i32,
40    loc_tex: i32,
41    loc_frag: u32,
42}
43
44impl Drop for Shader {
45    fn drop(&mut self) {
46        unsafe {
47            gl::DeleteProgram(self.prog);
48            gl::DeleteShader(self.vert);
49            gl::DeleteShader(self.frag);
50        }
51    }
52}
53
54impl Shader {
55    fn load() -> anyhow::Result<Shader> {
56        unsafe {
57            let mut status: gl::types::GLint = std::mem::zeroed();
58            let prog = gl::CreateProgram();
59            let vert = gl::CreateShader(gl::VERTEX_SHADER);
60            let frag = gl::CreateShader(gl::FRAGMENT_SHADER);
61            let vert_source =
62                std::ffi::CString::from_vec_unchecked(include_bytes!("shader.vert").to_vec());
63            let frag_source =
64                std::ffi::CString::from_vec_unchecked(include_bytes!("shader.frag").to_vec());
65
66            gl::ShaderSource(
67                vert,
68                1,
69                [vert_source.as_ptr()].as_ptr() as *const *const i8,
70                std::ptr::null(),
71            );
72            gl::ShaderSource(
73                frag,
74                1,
75                [frag_source.as_ptr()].as_ptr() as *const *const i8,
76                std::ptr::null(),
77            );
78
79            gl::CompileShader(vert);
80            gl::GetShaderiv(vert, gl::COMPILE_STATUS, &mut status);
81            if status != gl::TRUE as i32 {
82                return Err(shader_error(vert, "shader.vert"));
83            }
84
85            gl::CompileShader(frag);
86            gl::GetShaderiv(frag, gl::COMPILE_STATUS, &mut status);
87            if status != gl::TRUE as i32 {
88                return Err(shader_error(vert, "shader.frag"));
89            }
90
91            gl::AttachShader(prog, vert);
92            gl::AttachShader(prog, frag);
93
94            let name_vertex = std::ffi::CString::new("vertex").unwrap();
95            let name_tcoord = std::ffi::CString::new("tcoord").unwrap();
96            gl::BindAttribLocation(prog, 0, name_vertex.as_ptr() as *const i8);
97            gl::BindAttribLocation(prog, 1, name_tcoord.as_ptr() as *const i8);
98
99            gl::LinkProgram(prog);
100            gl::GetProgramiv(prog, gl::LINK_STATUS, &mut status);
101            if status != gl::TRUE as i32 {
102                return Err(program_error(prog));
103            }
104
105            let name_viewsize = std::ffi::CString::new("viewSize").unwrap();
106            let name_tex = std::ffi::CString::new("tex").unwrap();
107            let name_frag = std::ffi::CString::new("frag").unwrap();
108
109            Ok(Shader {
110                prog,
111                frag,
112                vert,
113                loc_viewsize: gl::GetUniformLocation(prog, name_viewsize.as_ptr() as *const i8),
114                loc_tex: gl::GetUniformLocation(prog, name_tex.as_ptr() as *const i8),
115                loc_frag: gl::GetUniformBlockIndex(prog, name_frag.as_ptr() as *const i8),
116            })
117        }
118    }
119}
120
121enum ShaderType {
122    FillGradient,
123    FillImage,
124    Simple,
125    Image,
126}
127
128#[derive(PartialEq, Eq)]
129enum CallType {
130    Fill(PathFillType),
131    ConvexFill,
132    Stroke,
133    Triangles,
134    #[cfg(feature = "wirelines")]
135    Lines,
136}
137
138struct Blend {
139    src_rgb: gl::types::GLenum,
140    dst_rgb: gl::types::GLenum,
141    src_alpha: gl::types::GLenum,
142    dst_alpha: gl::types::GLenum,
143}
144
145impl From<CompositeOperationState> for Blend {
146    fn from(state: CompositeOperationState) -> Self {
147        Blend {
148            src_rgb: convert_blend_factor(state.src_rgb),
149            dst_rgb: convert_blend_factor(state.dst_rgb),
150            src_alpha: convert_blend_factor(state.src_alpha),
151            dst_alpha: convert_blend_factor(state.dst_alpha),
152        }
153    }
154}
155
156struct Call {
157    call_type: CallType,
158    vert_buff: Option<Arc<GLArrayBuffer>>,
159    instances: Option<(Arc<GLArrayBuffer>, GLSlice)>,
160    image: Option<usize>,
161    path_range: Range<usize>,
162    triangle: GLSlice,
163    uniform_offset: usize,
164    blend_func: Blend,
165}
166
167struct Texture {
168    tex: gl::types::GLuint,
169    width: u32,
170    height: u32,
171    texture_type: TextureType,
172    flags: ImageFlags,
173}
174
175impl Drop for Texture {
176    fn drop(&mut self) {
177        unsafe { gl::DeleteTextures(1, &self.tex) }
178    }
179}
180
181#[derive(Default, Clone, Copy)]
182struct GLSlice {
183    offset: u32,
184    count: u32,
185}
186
187impl From<VertexSlice> for GLSlice {
188    fn from(value: VertexSlice) -> Self {
189        return Self {
190            offset: value.offset as u32,
191            count: value.count as u32,
192        };
193    }
194}
195
196impl From<Range<u32>> for GLSlice {
197    fn from(value: Range<u32>) -> Self {
198        return Self {
199            offset: value.start as u32,
200            count: value.count() as u32,
201        };
202    }
203}
204
205#[derive(Default)]
206struct GLPath {
207    fill: GLSlice,
208    stroke: GLSlice,
209}
210
211#[derive(Default)]
212#[allow(dead_code)]
213struct FragUniforms {
214    scissor_mat: [f32; 12],
215    paint_mat: [f32; 12],
216    inner_color: Color,
217    outer_color: Color,
218    scissor_ext: [f32; 2],
219    scissor_scale: [f32; 2],
220    extent: [f32; 2],
221    radius: f32,
222    feather: f32,
223    stroke_mult: f32,
224    stroke_thr: f32,
225    tex_type: i32,
226    type_: i32,
227}
228
229#[derive(Clone, Copy, Debug)]
230struct DefaultFBO {
231    fbo: gl::types::GLint,
232    rbo: gl::types::GLint,
233}
234
235pub struct Renderer {
236    shader: Shader,
237    textures: Slab<Texture>,
238    view: Extent,
239    vert_buf: GLArrayBuffer,
240    inst_buf: GLArrayBuffer,
241    frag_buf: gl::types::GLuint,
242    frag_size: usize,
243    calls: Vec<Call>,
244    paths: Vec<GLPath>,
245    uniforms: Vec<u8>,
246    config: RenderConfig,
247    default_fbo: DefaultFBO,
248}
249
250impl Drop for Renderer {
251    fn drop(&mut self) {
252        unsafe {
253            gl::DeleteBuffers(1, &self.frag_buf);
254        }
255    }
256}
257
258impl Renderer {
259    pub fn create(config: RenderConfig) -> anyhow::Result<Renderer> {
260        unsafe {
261            let shader = Shader::load()?;
262
263            let vert_buf = GLArrayBuffer::new(BufferUsage::Vertex);
264            let inst_buf = GLArrayBuffer::new(BufferUsage::Instance);
265            inst_buf.update_data(bytemuck::bytes_of(&Transform::identity()));
266
267            gl::UniformBlockBinding(shader.prog, shader.loc_frag, 0);
268            let mut frag_buf: gl::types::GLuint = std::mem::zeroed();
269            gl::GenBuffers(1, &mut frag_buf);
270
271            let mut align = std::mem::zeroed();
272            gl::GetIntegerv(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT, &mut align);
273
274            let frag_size = std::mem::size_of::<FragUniforms>() + (align as usize)
275                - std::mem::size_of::<FragUniforms>() % (align as usize);
276
277            gl::Finish();
278
279            let mut default_fbo: gl::types::GLint = 0;
280            let mut default_rbo: gl::types::GLint = 0;
281            gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut default_fbo as *mut _);
282            gl::GetIntegerv(gl::RENDERBUFFER_BINDING, &mut default_rbo as *mut _);
283
284            Ok(Renderer {
285                shader,
286                textures: Default::default(),
287                view: Default::default(),
288                vert_buf,
289                inst_buf,
290                frag_buf,
291                frag_size,
292                calls: Default::default(),
293                paths: Default::default(),
294                uniforms: Default::default(),
295                config,
296                default_fbo: DefaultFBO {
297                    fbo: default_fbo,
298                    rbo: default_rbo,
299                },
300            })
301        }
302    }
303
304    fn set_uniforms(&self, offset: usize, img: Option<usize>) {
305        unsafe {
306            gl::BindBufferRange(
307                gl::UNIFORM_BUFFER,
308                0,
309                self.frag_buf,
310                (offset * self.frag_size) as isize,
311                std::mem::size_of::<FragUniforms>() as isize,
312            );
313
314            if let Some(img) = img {
315                if let Some(texture) = self.textures.get(img) {
316                    gl::BindTexture(gl::TEXTURE_2D, texture.tex);
317                }
318            } else {
319                gl::BindTexture(gl::TEXTURE_2D, 0);
320            }
321        }
322    }
323
324    #[inline]
325    fn do_fill(&self, call: &Call, fill_type: PathFillType, inst_count: i32) {
326        let paths = &self.paths[call.path_range.clone()];
327        unsafe {
328            gl::Enable(gl::STENCIL_TEST);
329            gl::StencilMask(0xff);
330            gl::StencilFunc(gl::ALWAYS, 0, 0xff);
331            gl::ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
332
333            self.set_uniforms(call.uniform_offset, call.image);
334            if fill_type == PathFillType::Winding {
335                gl::StencilOpSeparate(gl::FRONT, gl::KEEP, gl::KEEP, gl::INCR_WRAP);
336                gl::StencilOpSeparate(gl::BACK, gl::KEEP, gl::KEEP, gl::DECR_WRAP);
337            } else {
338                gl::StencilOpSeparate(gl::FRONT_AND_BACK, gl::KEEP, gl::KEEP, gl::INVERT);
339            }
340            gl::Disable(gl::CULL_FACE);
341            for path in paths {
342                gl::DrawArraysInstanced(
343                    gl::TRIANGLE_FAN,
344                    path.fill.offset as i32,
345                    path.fill.count as i32,
346                    inst_count,
347                );
348            }
349            gl::Enable(gl::CULL_FACE);
350
351            gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
352
353            self.set_uniforms(call.uniform_offset + 1, call.image);
354
355            gl::StencilFunc(gl::EQUAL, 0x00, 0xff);
356            gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP);
357            for path in paths {
358                gl::DrawArraysInstanced(
359                    gl::TRIANGLE_STRIP,
360                    path.stroke.offset as i32,
361                    path.stroke.count as i32,
362                    inst_count,
363                );
364            }
365
366            gl::StencilFunc(gl::NOTEQUAL, 0x00, 0xff);
367            gl::StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
368            gl::DrawArraysInstanced(
369                gl::TRIANGLE_STRIP,
370                call.triangle.offset as i32,
371                call.triangle.count as i32,
372                inst_count,
373            );
374
375            gl::Disable(gl::STENCIL_TEST);
376        }
377    }
378
379    #[inline]
380    unsafe fn do_convex_fill(&self, call: &Call, inst_count: i32) {
381        let paths = &self.paths[call.path_range.clone()];
382        self.set_uniforms(call.uniform_offset, call.image);
383        for path in paths {
384            unsafe {
385                gl::DrawArraysInstanced(
386                    gl::TRIANGLE_FAN,
387                    path.fill.offset as i32,
388                    path.fill.count as i32,
389                    inst_count
390                );
391                if path.stroke.count > 0 {
392                    gl::DrawArraysInstanced(
393                        gl::TRIANGLE_STRIP,
394                        path.stroke.offset as i32,
395                        path.stroke.count as i32,
396                        inst_count
397                    );
398                }
399            }
400        }
401    }
402
403    #[inline]
404    unsafe fn do_stroke(&self, call: &Call, inst_count: i32) {
405        let paths = &self.paths[call.path_range.clone()];
406        self.set_uniforms(call.uniform_offset, call.image);
407        for path in paths {
408            unsafe {
409                gl::DrawArraysInstanced(
410                    gl::TRIANGLE_STRIP,
411                    path.stroke.offset as i32,
412                    path.stroke.count as i32,
413                    inst_count
414                );
415            }
416        }
417    }
418
419    #[inline]
420    fn do_triangles(&self, call: &Call, inst_count: i32) {
421        self.set_uniforms(call.uniform_offset, call.image);
422        unsafe {
423            gl::DrawArraysInstanced(
424                gl::TRIANGLES,
425                call.triangle.offset as i32,
426                call.triangle.count as i32,
427                inst_count
428            );
429        }
430    }
431
432    #[cfg(feature = "wirelines")]
433    #[inline]
434    unsafe fn do_lines(&self, call: &Call, inst_count: i32) {
435        let paths = &self.paths[call.path_range.clone()];
436        self.set_uniforms(call.uniform_offset, call.image);
437        for path in paths {
438            unsafe {
439                gl::DrawArraysInstanced(
440                    gl::LINE_STRIP,
441                    path.stroke.offset as i32,
442                    path.stroke.count as i32,
443                    inst_count
444                );
445            }
446        }
447    }
448
449    fn convert_paint(
450        &self,
451        paint: &PaintPattern,
452        scissor: &Scissor,
453        width: f32,
454        fringe: f32,
455        stroke_thr: f32,
456    ) -> FragUniforms {
457        let mut frag = FragUniforms {
458            scissor_mat: Default::default(),
459            paint_mat: Default::default(),
460            inner_color: premul_color(paint.inner_color),
461            outer_color: premul_color(paint.outer_color),
462            scissor_ext: Default::default(),
463            scissor_scale: Default::default(),
464            extent: Default::default(),
465            radius: 0.0,
466            feather: 0.0,
467            stroke_mult: 0.0,
468            stroke_thr,
469            tex_type: 0,
470            type_: 0,
471        };
472
473        if scissor.extent.width < -0.5 || scissor.extent.height < -0.5 {
474            frag.scissor_ext[0] = 1.0;
475            frag.scissor_ext[1] = 1.0;
476            frag.scissor_scale[0] = 1.0;
477            frag.scissor_scale[1] = 1.0;
478        } else {
479            frag.scissor_mat = xform_to_3x4(scissor.xform.inverse());
480            frag.scissor_ext[0] = scissor.extent.width;
481            frag.scissor_ext[1] = scissor.extent.height;
482            frag.scissor_scale[0] = (scissor.xform.0[0] * scissor.xform.0[0]
483                + scissor.xform.0[2] * scissor.xform.0[2])
484                .sqrt()
485                / fringe;
486            frag.scissor_scale[1] = (scissor.xform.0[1] * scissor.xform.0[1]
487                + scissor.xform.0[3] * scissor.xform.0[3])
488                .sqrt()
489                / fringe;
490        }
491
492        frag.extent = [paint.extent.width, paint.extent.height];
493        frag.stroke_mult = (width * 0.5 + fringe * 0.5) / fringe;
494
495        let mut invxform = Transform::default();
496
497        if let Some(img) = paint.image {
498            if let Some(texture) = self.textures.get(img) {
499                if texture.flags.contains(ImageFlags::FLIPY) {
500                    let m1 = Transform::translate(0.0, frag.extent[1] * 0.5) * paint.xform;
501                    let m2 = Transform::scale(1.0, -1.0) * m1;
502                    let m1 = Transform::translate(0.0, -frag.extent[1] * 0.5) * m2;
503                    invxform = m1.inverse();
504                } else {
505                    invxform = paint.xform.inverse();
506                };
507
508                frag.type_ = ShaderType::FillImage as i32;
509                match texture.texture_type {
510                    TextureType::RGBA | TextureType::BGRA => {
511                        frag.tex_type = if texture.flags.contains(ImageFlags::PREMULTIPLIED) {
512                            0
513                        } else {
514                            1
515                        }
516                    }
517                    TextureType::Alpha => frag.tex_type = 2,
518                }
519            }
520        } else {
521            frag.type_ = ShaderType::FillGradient as i32;
522            frag.radius = paint.radius;
523            frag.feather = paint.feather;
524            invxform = paint.xform.inverse();
525        }
526
527        frag.paint_mat = xform_to_3x4(invxform);
528
529        frag
530    }
531
532    fn append_uniforms(&mut self, uniforms: FragUniforms) {
533        self.uniforms
534            .resize(self.uniforms.len() + self.frag_size, 0);
535        unsafe {
536            let idx = self.uniforms.len() - self.frag_size;
537            let p = self.uniforms.as_mut_ptr().add(idx) as *mut FragUniforms;
538            *p = uniforms;
539        }
540    }
541
542    #[inline]
543    fn get_uniform_offset(&self) -> usize {
544        return self.uniforms.len() / self.frag_size;
545    }
546}
547
548fn shader_error(shader: gl::types::GLuint, filename: &str) -> anyhow::Error {
549    unsafe {
550        let mut data: [gl::types::GLchar; 512 + 1] = std::mem::zeroed();
551        let mut len: gl::types::GLsizei = std::mem::zeroed();
552        gl::GetShaderInfoLog(shader, 512, &mut len, data.as_mut_ptr());
553        if len > 512 {
554            len = 512;
555        }
556        data[len as usize] = 0;
557        let err_msg = std::ffi::CStr::from_ptr(data.as_ptr());
558        anyhow!(
559            "failed to compile shader: {}: {}",
560            filename,
561            err_msg.to_string_lossy()
562        )
563    }
564}
565
566fn program_error(prog: gl::types::GLuint) -> anyhow::Error {
567    unsafe {
568        let mut data: [gl::types::GLchar; 512 + 1] = std::mem::zeroed();
569        let mut len: gl::types::GLsizei = std::mem::zeroed();
570        gl::GetProgramInfoLog(prog, 512, &mut len, data.as_mut_ptr());
571        if len > 512 {
572            len = 512;
573        }
574        data[len as usize] = 0;
575        let err_msg = std::ffi::CStr::from_ptr(data.as_ptr());
576        anyhow!("failed to link program: {}", err_msg.to_string_lossy())
577    }
578}
579
580fn convert_blend_factor(factor: BlendFactor) -> gl::types::GLenum {
581    match factor {
582        BlendFactor::Zero => gl::ZERO,
583        BlendFactor::One => gl::ONE,
584        BlendFactor::SrcColor => gl::SRC_COLOR,
585        BlendFactor::OneMinusSrcColor => gl::ONE_MINUS_SRC_COLOR,
586        BlendFactor::DstColor => gl::DST_COLOR,
587        BlendFactor::OneMinusDstColor => gl::ONE_MINUS_DST_COLOR,
588        BlendFactor::SrcAlpha => gl::SRC_ALPHA,
589        BlendFactor::OneMinusSrcAlpha => gl::ONE_MINUS_SRC_ALPHA,
590        BlendFactor::DstAlpha => gl::DST_ALPHA,
591        BlendFactor::OneMinusDstAlpha => gl::ONE_MINUS_DST_ALPHA,
592        BlendFactor::SrcAlphaSaturate => gl::SRC_ALPHA_SATURATE,
593    }
594}