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}