piet_glow/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later OR MPL-2.0
2// This file is a part of `piet-hardware`.
3//
4// `piet-hardware` is free software: you can redistribute it and/or modify it under the
5// terms of either:
6//
7// * GNU Lesser General Public License as published by the Free Software Foundation, either
8//   version 3 of the License, or (at your option) any later version.
9// * Mozilla Public License as published by the Mozilla Foundation, version 2.
10//
11// `piet-hardware` is distributed in the hope that it will be useful, but WITHOUT ANY
12// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13// PURPOSE. See the GNU Lesser General Public License or the Mozilla Public License for more
14// details.
15//
16// You should have received a copy of the GNU Lesser General Public License and the Mozilla
17// Public License along with `piet-hardware`. If not, see <https://www.gnu.org/licenses/>.
18
19//! A GPU-accelerated backend for piet that uses the [`glow`] crate.
20//!
21//! [`glow`]: https://crates.io/crates/glow
22
23pub use glow;
24pub use piet_hardware::piet;
25
26use glow::HasContext;
27
28use piet::{kurbo, Error as Pierror, IntoBrush};
29use piet_hardware::gpu_types::{AreaCapture, BufferPush, SubtextureWrite, TextureWrite};
30
31use std::borrow::Cow;
32use std::cell::Cell;
33use std::fmt;
34use std::mem;
35use std::rc::Rc;
36
37macro_rules! c {
38    ($e:expr) => {{
39        ($e) as f32
40    }};
41}
42
43const VERTEX_SHADER: &str = include_str!("./shaders/glow.v.glsl");
44const FRAGMENT_SHADER: &str = include_str!("./shaders/glow.f.glsl");
45
46#[derive(Debug, Clone, Copy)]
47enum Uniforms {
48    Transform = 0,
49    ViewportSize = 1,
50    ImageTexture = 2,
51    MaskTexture = 3,
52}
53
54impl Uniforms {
55    fn as_index(self) -> usize {
56        self as usize
57    }
58
59    fn as_name(self) -> &'static str {
60        match self {
61            Uniforms::Transform => "uTransform",
62            Uniforms::ViewportSize => "uViewportSize",
63            Uniforms::ImageTexture => "uImage",
64            Uniforms::MaskTexture => "uMask",
65        }
66    }
67}
68
69const UNIFORM_COUNT: usize = 4;
70const UNIFORMS: [Uniforms; UNIFORM_COUNT] = [
71    Uniforms::Transform,
72    Uniforms::ViewportSize,
73    Uniforms::ImageTexture,
74    Uniforms::MaskTexture,
75];
76
77use Uniforms::*;
78
79/// A wrapper around a `glow` context.
80struct GpuContext<H: HasContext + ?Sized> {
81    /// A compiled shader program for rendering.
82    render_program: H::Program,
83
84    /// The uniform locations.
85    uniforms: Box<[H::UniformLocation]>,
86
87    /// Do we need to check the indices?
88    check_indices: bool,
89
90    /// The underlying context.
91    context: Rc<H>,
92}
93
94impl<H: HasContext + ?Sized> fmt::Debug for GpuContext<H> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.debug_struct("GpuContext")
97            .field("robust", &!self.check_indices)
98            .finish_non_exhaustive()
99    }
100}
101
102impl<H: HasContext + ?Sized> GpuContext<H> {
103    fn uniform(&self, uniform: Uniforms) -> &H::UniformLocation {
104        self.uniforms.get(uniform.as_index()).unwrap()
105    }
106}
107
108impl<H: HasContext + ?Sized> Drop for GpuContext<H> {
109    fn drop(&mut self) {
110        unsafe {
111            self.context.delete_program(self.render_program);
112        }
113    }
114}
115
116/// A wrapper around a `glow` texture.
117struct GlTexture<H: HasContext + ?Sized> {
118    /// The OpenGL context.
119    context: Rc<H>,
120
121    /// The underlying texture.
122    texture: H::Texture,
123}
124
125impl<H: HasContext + ?Sized> Drop for GlTexture<H> {
126    fn drop(&mut self) {
127        unsafe {
128            self.context.delete_texture(self.texture);
129        }
130    }
131}
132
133/// A wrapper around a `glow` vertex buffer.
134struct GlVertexBuffer<H: HasContext + ?Sized> {
135    /// The context.
136    context: Rc<H>,
137
138    /// The underlying vertex buffer.
139    vbo: H::Buffer,
140
141    /// The index buffer.
142    ebo: H::Buffer,
143
144    /// The vertex array object.
145    vao: H::VertexArray,
146
147    /// The number of indices.
148    num_indices: Cell<usize>,
149}
150
151impl<H: HasContext + ?Sized> Drop for GlVertexBuffer<H> {
152    fn drop(&mut self) {
153        unsafe {
154            self.context.delete_buffer(self.vbo);
155            self.context.delete_buffer(self.ebo);
156            self.context.delete_vertex_array(self.vao);
157        }
158    }
159}
160
161#[derive(Debug)]
162struct GlError(String);
163
164impl From<String> for GlError {
165    fn from(s: String) -> Self {
166        GlError(s)
167    }
168}
169
170impl fmt::Display for GlError {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        write!(f, "gl error: {}", self.0)
173    }
174}
175
176impl std::error::Error for GlError {}
177
178impl<H: HasContext + ?Sized> piet_hardware::GpuContext for GpuContext<H> {
179    type Device = ();
180    type Queue = ();
181    type Texture = GlTexture<H>;
182    type VertexBuffer = GlVertexBuffer<H>;
183    type Error = GlError;
184
185    fn clear(&mut self, _: &(), _: &(), color: piet_hardware::piet::Color) {
186        let (r, g, b, a) = color.as_rgba();
187
188        unsafe {
189            self.context.disable(glow::SCISSOR_TEST);
190            self.context.clear_color(c!(r), c!(g), c!(b), c!(a));
191            self.context.clear(glow::COLOR_BUFFER_BIT);
192        }
193    }
194
195    fn flush(&mut self) -> Result<(), Self::Error> {
196        unsafe {
197            self.context.flush();
198        }
199
200        Ok(())
201    }
202
203    fn create_texture(
204        &mut self,
205        _: &(),
206        interpolation: piet_hardware::piet::InterpolationMode,
207        repeat: piet_hardware::RepeatStrategy,
208    ) -> Result<Self::Texture, Self::Error> {
209        unsafe {
210            let texture = self.context.create_texture().gl_err()?;
211
212            // Bind the texture.
213            self.context.bind_texture(glow::TEXTURE_2D, Some(texture));
214            let _guard = CallOnDrop(|| {
215                self.context.bind_texture(glow::TEXTURE_2D, None);
216            });
217
218            let (min_filter, mag_filter) = match interpolation {
219                piet::InterpolationMode::NearestNeighbor => (glow::NEAREST, glow::NEAREST),
220                piet::InterpolationMode::Bilinear => (glow::LINEAR, glow::LINEAR),
221            };
222
223            self.context.tex_parameter_i32(
224                glow::TEXTURE_2D,
225                glow::TEXTURE_MIN_FILTER,
226                min_filter as i32,
227            );
228            self.context.tex_parameter_i32(
229                glow::TEXTURE_2D,
230                glow::TEXTURE_MAG_FILTER,
231                mag_filter as i32,
232            );
233
234            let (wrap_s, wrap_t) = match repeat {
235                piet_hardware::RepeatStrategy::Color(_color) => {
236                    #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm32")))]
237                    {
238                        let (r, g, b, a) = _color.as_rgba();
239                        self.context.tex_parameter_f32_slice(
240                            glow::TEXTURE_2D,
241                            glow::TEXTURE_BORDER_COLOR,
242                            &[c!(r), c!(g), c!(b), c!(a)],
243                        );
244                    }
245
246                    (glow::CLAMP_TO_BORDER, glow::CLAMP_TO_BORDER)
247                }
248                piet_hardware::RepeatStrategy::Repeat => (glow::REPEAT, glow::REPEAT),
249                piet_hardware::RepeatStrategy::Clamp => (glow::CLAMP_TO_EDGE, glow::CLAMP_TO_EDGE),
250                _ => panic!("unsupported repeat strategy: {repeat:?}"),
251            };
252
253            self.context
254                .tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, wrap_s as i32);
255            self.context
256                .tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, wrap_t as i32);
257
258            gl_error(&*self.context);
259
260            Ok(GlTexture {
261                context: self.context.clone(),
262                texture,
263            })
264        }
265    }
266
267    fn write_texture(
268        &mut self,
269        TextureWrite {
270            device: (),
271            queue: (),
272            texture,
273            size: (width, height),
274            format,
275            data,
276        }: TextureWrite<'_, Self>,
277    ) {
278        let data_width = match format {
279            piet::ImageFormat::Grayscale => 1,
280            piet::ImageFormat::Rgb => 3,
281            piet::ImageFormat::RgbaSeparate | piet::ImageFormat::RgbaPremul => 4,
282            _ => panic!("unsupported image format: {format:?}"),
283        };
284
285        if let Some(data) = data {
286            let total_len = usize::try_from(width)
287                .ok()
288                .and_then(|width| width.checked_mul(height.try_into().ok()?))
289                .and_then(|total| total.checked_mul(data_width.try_into().ok()?))
290                .expect("image data too large");
291            assert_eq!(data.len(), total_len);
292        }
293
294        let grayscale_data;
295        let mut data = data;
296
297        unsafe {
298            self.context
299                .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
300            let _guard = CallOnDrop(|| {
301                self.context.bind_texture(glow::TEXTURE_2D, None);
302            });
303
304            let (internal_format, format, data_type) = match format {
305                piet::ImageFormat::Grayscale => {
306                    // TODO: Figure out the best way of working around grayscale being broken.
307                    grayscale_data =
308                        data.map(|data| data.iter().flat_map(|&v| [v, v, v]).collect::<Vec<_>>());
309                    data = grayscale_data.as_deref();
310
311                    (glow::RGB8, glow::RGB, glow::UNSIGNED_BYTE)
312                }
313                piet::ImageFormat::Rgb => (glow::RGB8, glow::RGB, glow::UNSIGNED_BYTE),
314                piet::ImageFormat::RgbaPremul => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
315                piet::ImageFormat::RgbaSeparate => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
316                _ => panic!("unsupported image format: {format:?}"),
317            };
318
319            // Set texture parameters.
320            self.context.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
321
322            self.context.tex_image_2d(
323                glow::TEXTURE_2D,
324                0,
325                internal_format as i32,
326                width as i32,
327                height as i32,
328                0,
329                format,
330                data_type,
331                data,
332            );
333        }
334
335        gl_error(&*self.context);
336    }
337
338    fn write_subtexture(
339        &mut self,
340        SubtextureWrite {
341            device: (),
342            queue: (),
343            texture,
344            offset: (x, y),
345            size: (width, height),
346            format,
347            data,
348        }: SubtextureWrite<'_, Self>,
349    ) {
350        let data_width = match format {
351            piet::ImageFormat::Grayscale => 1,
352            piet::ImageFormat::Rgb => 3,
353            piet::ImageFormat::RgbaSeparate | piet::ImageFormat::RgbaPremul => 4,
354            _ => panic!("unsupported image format: {format:?}"),
355        };
356
357        let total_len = (width * height * data_width) as usize;
358        assert_eq!(data.len(), total_len);
359
360        unsafe {
361            self.context
362                .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
363            let _guard = CallOnDrop(|| {
364                self.context.bind_texture(glow::TEXTURE_2D, None);
365            });
366
367            let (format, data_type) = match format {
368                piet::ImageFormat::Grayscale => (glow::RED, glow::UNSIGNED_BYTE),
369                piet::ImageFormat::Rgb => (glow::RGB, glow::UNSIGNED_BYTE),
370                piet::ImageFormat::RgbaPremul => (glow::RGBA, glow::UNSIGNED_BYTE),
371                piet::ImageFormat::RgbaSeparate => (glow::RGBA, glow::UNSIGNED_BYTE),
372                _ => panic!("unsupported image format: {format:?}"),
373            };
374
375            self.context.tex_sub_image_2d(
376                glow::TEXTURE_2D,
377                0,
378                x as i32,
379                y as i32,
380                width as i32,
381                height as i32,
382                format,
383                data_type,
384                glow::PixelUnpackData::Slice(data),
385            );
386        }
387
388        gl_error(&*self.context);
389    }
390
391    fn set_texture_interpolation(
392        &mut self,
393        _: &(),
394        texture: &Self::Texture,
395        interpolation: piet_hardware::piet::InterpolationMode,
396    ) {
397        unsafe {
398            self.context
399                .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
400            let _guard = CallOnDrop(|| {
401                self.context.bind_texture(glow::TEXTURE_2D, None);
402            });
403
404            let (min_filter, mag_filter) = match interpolation {
405                piet::InterpolationMode::NearestNeighbor => (glow::NEAREST, glow::NEAREST),
406                piet::InterpolationMode::Bilinear => (glow::LINEAR, glow::LINEAR),
407            };
408
409            self.context.tex_parameter_i32(
410                glow::TEXTURE_2D,
411                glow::TEXTURE_MIN_FILTER,
412                min_filter as i32,
413            );
414            self.context.tex_parameter_i32(
415                glow::TEXTURE_2D,
416                glow::TEXTURE_MAG_FILTER,
417                mag_filter as i32,
418            );
419        }
420    }
421
422    fn capture_area(
423        &mut self,
424        AreaCapture {
425            device: (),
426            queue: (),
427            texture,
428            offset,
429            size,
430            bitmap_scale: scale,
431        }: AreaCapture<'_, Self>,
432    ) -> Result<(), Self::Error> {
433        let (x, y) = offset;
434        let (width, height) = size;
435
436        // Scale up by the bitmap scale.
437        let (x, y, width, height) = (
438            (x as f64 * scale).floor() as u32,
439            (y as f64 * scale).floor() as u32,
440            (width as f64 * scale).ceil() as u32,
441            (height as f64 * scale).ceil() as u32,
442        );
443
444        // Create a buffer to hold the pixels.
445        // A little bit more at the end is allocated for the inversion.
446        let buffer_size = (width * (height + 1) * 4) as usize;
447        let mut pixels = vec![0u8; buffer_size];
448        let scratch_start = pixels.len() - (width * 4) as usize;
449
450        // Read the pixels, making sure to invert the y axis.
451        unsafe {
452            self.context
453                .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
454            let _guard = CallOnDrop(|| {
455                self.context.bind_texture(glow::TEXTURE_2D, None);
456            });
457
458            self.context.pixel_store_i32(glow::PACK_ALIGNMENT, 1);
459            self.context.read_pixels(
460                x as i32,
461                y as i32,
462                width as i32,
463                height as i32,
464                glow::RGBA,
465                glow::UNSIGNED_BYTE,
466                glow::PixelPackData::Slice(&mut pixels),
467            );
468        }
469
470        // If we got an error, propagate it.
471        if unsafe { self.context.get_error() } != glow::NO_ERROR {
472            // TODO: If this is an MSAA texture, maybe try the framebuffer trick.
473            gl_error(&*self.context);
474            return Err(GlError("failed to read pixels".into()));
475        }
476
477        // Invert the y axis, making sure to use the little bit at the end of the buffer as
478        // temporary storage.
479        let stride = width as usize * 4;
480        for row in 0..(height / 2) as usize {
481            let top = row * stride;
482            let bottom = (height as usize - row - 1) * stride;
483
484            pixels.copy_within(top..top + stride, scratch_start);
485            pixels.copy_within(bottom..bottom + stride, top);
486            pixels.copy_within(scratch_start..scratch_start + stride, bottom);
487        }
488
489        // Upload the pixels to the texture.
490        unsafe {
491            self.context
492                .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
493            let _guard = CallOnDrop(|| {
494                self.context.bind_texture(glow::TEXTURE_2D, None);
495            });
496
497            self.context.tex_image_2d(
498                glow::TEXTURE_2D,
499                0,
500                glow::RGBA8 as _,
501                width as i32,
502                height as i32,
503                0,
504                glow::RGBA,
505                glow::UNSIGNED_BYTE,
506                Some(&pixels),
507            );
508        }
509
510        Ok(())
511    }
512
513    fn max_texture_size(&mut self, _: &()) -> (u32, u32) {
514        unsafe {
515            let size = self.context.get_parameter_i32(glow::MAX_TEXTURE_SIZE);
516            (size as u32, size as u32)
517        }
518    }
519
520    fn create_vertex_buffer(&mut self, _: &()) -> Result<Self::VertexBuffer, Self::Error> {
521        use piet_hardware::Vertex;
522
523        unsafe {
524            let vbo = self.context.create_buffer().gl_err()?;
525            let ebo = self.context.create_buffer().gl_err()?;
526            let vao = self.context.create_vertex_array().gl_err()?;
527
528            // Bind the buffers.
529            self.context.bind_vertex_array(Some(vao));
530            let _guard = CallOnDrop(|| {
531                self.context.bind_vertex_array(None);
532            });
533            self.context.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
534            self.context
535                .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
536
537            // Set up vertex attributes.
538            let vertex_attributes = [
539                (
540                    "aPosition",
541                    2,
542                    glow::FLOAT,
543                    bytemuck::offset_of!(Vertex, pos),
544                ),
545                ("aUv", 2, glow::FLOAT, bytemuck::offset_of!(Vertex, uv)),
546                (
547                    "aColor",
548                    4,
549                    glow::UNSIGNED_BYTE,
550                    bytemuck::offset_of!(Vertex, color),
551                ),
552            ];
553
554            let stride = std::mem::size_of::<Vertex>() as i32;
555            for (name, size, data_type, offset) in vertex_attributes {
556                let location = self
557                    .context
558                    .get_attrib_location(self.render_program, name)
559                    .ok_or_else(|| {
560                        GlError(format!("failed to get attribute location for {name}"))
561                    })?;
562
563                self.context.enable_vertex_attrib_array(location);
564                self.context.vertex_attrib_pointer_f32(
565                    location,
566                    size,
567                    data_type,
568                    false,
569                    stride,
570                    offset as i32,
571                );
572            }
573
574            gl_error(&*self.context);
575
576            Ok(GlVertexBuffer {
577                context: self.context.clone(),
578                vbo,
579                ebo,
580                vao,
581                num_indices: Cell::new(0),
582            })
583        }
584    }
585
586    fn write_vertices(
587        &mut self,
588        _: &(),
589        _: &(),
590        buffer: &Self::VertexBuffer,
591        vertices: &[piet_hardware::Vertex],
592        indices: &[u32],
593    ) {
594        // Make sure we don't cause undefined behavior on platforms without robust buffer access.
595        if self.check_indices {
596            assert!(indices.iter().all(|&i| i < vertices.len() as u32));
597        } else {
598            debug_assert!(indices.iter().all(|&i| i < vertices.len() as u32));
599        }
600
601        unsafe {
602            self.context.bind_vertex_array(Some(buffer.vao));
603            let _guard = CallOnDrop(|| {
604                self.context.bind_vertex_array(None);
605            });
606
607            self.context.buffer_data_u8_slice(
608                glow::ARRAY_BUFFER,
609                bytemuck::cast_slice(vertices),
610                glow::DYNAMIC_DRAW,
611            );
612
613            self.context.buffer_data_u8_slice(
614                glow::ELEMENT_ARRAY_BUFFER,
615                bytemuck::cast_slice(indices),
616                glow::DYNAMIC_DRAW,
617            );
618
619            gl_error(&*self.context);
620
621            buffer.num_indices.set(indices.len());
622        }
623    }
624
625    fn push_buffers(
626        &mut self,
627        BufferPush {
628            device: (),
629            queue: (),
630            vertex_buffer,
631            current_texture,
632            mask_texture,
633            transform,
634            viewport_size: size,
635            clip,
636        }: BufferPush<'_, Self>,
637    ) -> Result<(), Self::Error> {
638        unsafe {
639            // Use our program.
640            self.context.use_program(Some(self.render_program));
641            let _unbind_program = CallOnDrop(|| {
642                self.context.use_program(None);
643            });
644
645            // Set viewport size.
646            self.context.viewport(0, 0, size.0 as i32, size.1 as i32);
647            self.context.uniform_2_f32(
648                Some(self.uniform(ViewportSize)),
649                size.0 as f32,
650                size.1 as f32,
651            );
652
653            // Set scissor rectangle.
654            match clip {
655                Some(clip) => {
656                    self.context.enable(glow::SCISSOR_TEST);
657                    self.context.scissor(
658                        clip.x0 as i32,
659                        size.1 as i32 - clip.y1 as i32,
660                        clip.width() as i32,
661                        clip.height() as i32,
662                    )
663                }
664                None => self.context.disable(glow::SCISSOR_TEST),
665            }
666
667            // Set the transform.
668            let [a, b, c, d, e, f] = transform.as_coeffs();
669            let transform = [
670                c!(a),
671                c!(b),
672                c!(0.0),
673                c!(c),
674                c!(d),
675                c!(0.0),
676                c!(e),
677                c!(f),
678                c!(1.0),
679            ];
680            self.context.uniform_matrix_3_f32_slice(
681                Some(self.uniform(Transform)),
682                false,
683                &transform,
684            );
685
686            // Set the image texture.
687            self.context.active_texture(glow::TEXTURE1);
688            self.context
689                .bind_texture(glow::TEXTURE_2D, Some(current_texture.texture));
690            self.context
691                .uniform_1_i32(Some(self.uniform(ImageTexture)), 1);
692
693            // Set the mask texture.
694            self.context.active_texture(glow::TEXTURE0);
695            self.context
696                .bind_texture(glow::TEXTURE_2D, Some(mask_texture.texture));
697            self.context
698                .uniform_1_i32(Some(self.uniform(MaskTexture)), 0);
699
700            // Enable blending.
701            self.context.enable(glow::BLEND);
702            self.context
703                .blend_equation_separate(glow::FUNC_ADD, glow::FUNC_ADD);
704            self.context.blend_func_separate(
705                glow::SRC_ALPHA,
706                glow::ONE_MINUS_SRC_ALPHA,
707                glow::ONE,
708                glow::ONE_MINUS_SRC_ALPHA,
709            );
710            self.context.enable(glow::MULTISAMPLE);
711
712            // Set the vertex array.
713            self.context.bind_vertex_array(Some(vertex_buffer.vao));
714            let _unbind_vao = CallOnDrop(|| {
715                self.context.bind_vertex_array(None);
716            });
717
718            // Draw the triangles.
719            self.context.draw_elements(
720                glow::TRIANGLES,
721                vertex_buffer.num_indices.get() as i32,
722                glow::UNSIGNED_INT,
723                0,
724            );
725
726            gl_error(&*self.context);
727
728            Ok(())
729        }
730    }
731}
732
733/// A wrapper around a [`glow`] context with cached information.
734#[derive(Debug)]
735pub struct GlContext<H: HasContext + ?Sized> {
736    text: Text,
737    source: piet_hardware::Source<GpuContext<H>>,
738}
739
740impl<H: HasContext + ?Sized> GlContext<H> {
741    /// Create a new [`GlContext`] from a [`glow`] context.
742    ///
743    /// # Safety
744    ///
745    /// The context must be current while calling new, and the context must be current
746    /// when this type is dropped.
747    pub unsafe fn new(context: H) -> Result<Self, Pierror>
748    where
749        H: Sized,
750    {
751        // Get the current version.
752        let version = context.version();
753
754        // Check that the version is supported.
755        let has_supported_version = if version.is_embedded {
756            version.major >= 3
757        } else {
758            version.major >= 4 || (version.major >= 3 && version.minor >= 3)
759        };
760        if !has_supported_version {
761            return Err(Pierror::BackendError(
762                "OpenGL version 3.3 (or 3.0 ES) or higher is required".into(),
763            ));
764        }
765
766        let shader_header = if version.is_embedded {
767            "#version 300 es"
768        } else {
769            "#version 330 core"
770        };
771
772        let format_shader = |shader| format!("{shader_header}\n{shader}");
773
774        // Create a program to use for text rendering.
775        let program = compile_program(
776            &context,
777            &format_shader(VERTEX_SHADER),
778            &format_shader(FRAGMENT_SHADER),
779        )
780        .map_err(|e| Pierror::BackendError(e.into()))?;
781
782        // Get the uniform locations.
783        let uniforms = UNIFORMS
784            .iter()
785            .map(|uniform| {
786                context
787                    .get_uniform_location(program, uniform.as_name())
788                    .ok_or_else(|| {
789                        Pierror::BackendError(
790                            format!("failed to get uniform location for {}", uniform.as_name())
791                                .into(),
792                        )
793                    })
794            })
795            .collect::<Result<Box<[_]>, _>>()?;
796
797        let robust_buffer = context
798            .supported_extensions()
799            .contains("GL_ARB_robust_buffer_access_behavior")
800            || context
801                .supported_extensions()
802                .contains("GL_KHR_robust_buffer_access_behavior");
803
804        piet_hardware::Source::new(
805            GpuContext {
806                context: Rc::new(context),
807                uniforms,
808                check_indices: !robust_buffer,
809                render_program: program,
810            },
811            &(),
812            &(),
813        )
814        .map(|source| GlContext {
815            text: Text(source.text().clone()),
816            source,
817        })
818    }
819
820    /// Get a reference to the underlying [`glow`] context.
821    pub fn context(&self) -> &H {
822        &self.source.context().context
823    }
824
825    /// Get a render context.
826    ///
827    /// # Safety
828    ///
829    /// The context must be current while calling this method, as well as any of the
830    /// [`piet::RenderContext`] methods.
831    pub unsafe fn render_context(&mut self, width: u32, height: u32) -> RenderContext<'_, H> {
832        RenderContext {
833            context: self.source.render_context(&(), &(), width, height),
834            text: &mut self.text,
835        }
836    }
837}
838
839/// The whole point.
840pub struct RenderContext<'a, H: HasContext + ?Sized> {
841    context: piet_hardware::RenderContext<'a, 'static, 'static, GpuContext<H>>,
842    text: &'a mut Text,
843}
844
845impl<'a, H: HasContext + ?Sized> RenderContext<'a, H> {
846    /// Get the tolerance for flattening curves.
847    #[inline]
848    pub fn tolerance(&self) -> f64 {
849        self.context.tolerance()
850    }
851
852    /// Set the tolerance for flattening curves.
853    #[inline]
854    pub fn set_tolerance(&mut self, tolerance: f64) {
855        self.context.set_tolerance(tolerance)
856    }
857
858    /// Get the current bitmap scale.
859    #[inline]
860    pub fn bitmap_scale(&self) -> f64 {
861        self.context.bitmap_scale()
862    }
863
864    /// Set the current bitmap scale.
865    #[inline]
866    pub fn set_bitmap_scale(&mut self, scale: f64) {
867        self.context.set_bitmap_scale(scale)
868    }
869}
870
871impl<H: HasContext + ?Sized> piet::RenderContext for RenderContext<'_, H> {
872    type Brush = Brush<H>;
873
874    type Text = Text;
875
876    type TextLayout = TextLayout;
877
878    type Image = Image<H>;
879
880    fn status(&mut self) -> Result<(), Pierror> {
881        self.context.status()
882    }
883
884    fn solid_brush(&mut self, color: piet::Color) -> Self::Brush {
885        Brush(self.context.solid_brush(color))
886    }
887
888    fn gradient(
889        &mut self,
890        gradient: impl Into<piet::FixedGradient>,
891    ) -> Result<Self::Brush, Pierror> {
892        self.context.gradient(gradient).map(Brush)
893    }
894
895    fn clear(&mut self, region: impl Into<Option<kurbo::Rect>>, color: piet::Color) {
896        self.context.clear(region, color)
897    }
898
899    fn stroke(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>, width: f64) {
900        let brush = brush.make_brush(self, || shape.bounding_box());
901        self.context.stroke(shape, &brush.as_ref().0, width)
902    }
903
904    fn stroke_styled(
905        &mut self,
906        shape: impl kurbo::Shape,
907        brush: &impl IntoBrush<Self>,
908        width: f64,
909        style: &piet::StrokeStyle,
910    ) {
911        let brush = brush.make_brush(self, || shape.bounding_box());
912        self.context
913            .stroke_styled(shape, &brush.as_ref().0, width, style)
914    }
915
916    fn fill(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>) {
917        let brush = brush.make_brush(self, || shape.bounding_box());
918        self.context.fill(shape, &brush.as_ref().0)
919    }
920
921    fn fill_even_odd(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>) {
922        let brush = brush.make_brush(self, || shape.bounding_box());
923        self.context.fill_even_odd(shape, &brush.as_ref().0)
924    }
925
926    fn clip(&mut self, shape: impl kurbo::Shape) {
927        self.context.clip(shape)
928    }
929
930    fn text(&mut self) -> &mut Self::Text {
931        self.text
932    }
933
934    fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<kurbo::Point>) {
935        self.context.draw_text(&layout.0, pos)
936    }
937
938    fn save(&mut self) -> Result<(), Pierror> {
939        self.context.save()
940    }
941
942    fn restore(&mut self) -> Result<(), Pierror> {
943        self.context.restore()
944    }
945
946    fn finish(&mut self) -> Result<(), Pierror> {
947        self.context.finish()?;
948
949        // Free all of the resources as well.
950        self.context.source_mut().gpu_flushed();
951
952        Ok(())
953    }
954
955    fn transform(&mut self, transform: kurbo::Affine) {
956        self.context.transform(transform)
957    }
958
959    fn make_image(
960        &mut self,
961        width: usize,
962        height: usize,
963        buf: &[u8],
964        format: piet::ImageFormat,
965    ) -> Result<Self::Image, Pierror> {
966        self.context
967            .make_image(width, height, buf, format)
968            .map(Image)
969    }
970
971    fn draw_image(
972        &mut self,
973        image: &Self::Image,
974        dst_rect: impl Into<kurbo::Rect>,
975        interp: piet::InterpolationMode,
976    ) {
977        self.context.draw_image(&image.0, dst_rect, interp)
978    }
979
980    fn draw_image_area(
981        &mut self,
982        image: &Self::Image,
983        src_rect: impl Into<kurbo::Rect>,
984        dst_rect: impl Into<kurbo::Rect>,
985        interp: piet::InterpolationMode,
986    ) {
987        self.context
988            .draw_image_area(&image.0, src_rect, dst_rect, interp)
989    }
990
991    fn capture_image_area(
992        &mut self,
993        src_rect: impl Into<kurbo::Rect>,
994    ) -> Result<Self::Image, Pierror> {
995        self.context.capture_image_area(src_rect).map(Image)
996    }
997
998    fn blurred_rect(&mut self, rect: kurbo::Rect, blur_radius: f64, brush: &impl IntoBrush<Self>) {
999        let brush = brush.make_brush(self, || rect);
1000        self.context
1001            .blurred_rect(rect, blur_radius, &brush.as_ref().0)
1002    }
1003
1004    fn current_transform(&self) -> kurbo::Affine {
1005        self.context.current_transform()
1006    }
1007}
1008
1009/// The brush type.
1010#[derive(Debug)]
1011pub struct Brush<H: HasContext + ?Sized>(piet_hardware::Brush<GpuContext<H>>);
1012
1013impl<H: HasContext + ?Sized> Clone for Brush<H> {
1014    fn clone(&self) -> Self {
1015        Brush(self.0.clone())
1016    }
1017}
1018
1019impl<H: HasContext + ?Sized> IntoBrush<RenderContext<'_, H>> for Brush<H> {
1020    fn make_brush<'a>(
1021        &'a self,
1022        _piet: &mut RenderContext<'_, H>,
1023        _bbox: impl FnOnce() -> kurbo::Rect,
1024    ) -> Cow<'a, Brush<H>> {
1025        Cow::Borrowed(self)
1026    }
1027}
1028
1029/// The image type.
1030#[derive(Debug)]
1031pub struct Image<H: HasContext + ?Sized>(piet_hardware::Image<GpuContext<H>>);
1032
1033impl<H: HasContext + ?Sized> Clone for Image<H> {
1034    fn clone(&self) -> Self {
1035        Image(self.0.clone())
1036    }
1037}
1038
1039impl<H: HasContext + ?Sized> piet::Image for Image<H> {
1040    fn size(&self) -> kurbo::Size {
1041        self.0.size()
1042    }
1043}
1044
1045/// The text layout type.
1046#[derive(Debug, Clone)]
1047pub struct TextLayout(piet_hardware::TextLayout);
1048
1049impl piet::TextLayout for TextLayout {
1050    fn size(&self) -> kurbo::Size {
1051        self.0.size()
1052    }
1053
1054    fn line_text(&self, line_number: usize) -> Option<&str> {
1055        self.0.line_text(line_number)
1056    }
1057
1058    fn line_metric(&self, line_number: usize) -> Option<piet::LineMetric> {
1059        self.0.line_metric(line_number)
1060    }
1061
1062    fn line_count(&self) -> usize {
1063        self.0.line_count()
1064    }
1065
1066    fn hit_test_point(&self, point: kurbo::Point) -> piet::HitTestPoint {
1067        self.0.hit_test_point(point)
1068    }
1069
1070    fn trailing_whitespace_width(&self) -> f64 {
1071        self.0.trailing_whitespace_width()
1072    }
1073
1074    fn image_bounds(&self) -> kurbo::Rect {
1075        self.0.image_bounds()
1076    }
1077
1078    fn text(&self) -> &str {
1079        self.0.text()
1080    }
1081
1082    fn hit_test_text_position(&self, idx: usize) -> piet::HitTestPosition {
1083        self.0.hit_test_text_position(idx)
1084    }
1085}
1086
1087/// The text layout builder type.
1088#[derive(Debug)]
1089pub struct TextLayoutBuilder(piet_hardware::TextLayoutBuilder);
1090
1091impl piet::TextLayoutBuilder for TextLayoutBuilder {
1092    type Out = TextLayout;
1093
1094    fn max_width(self, width: f64) -> Self {
1095        Self(self.0.max_width(width))
1096    }
1097
1098    fn alignment(self, alignment: piet::TextAlignment) -> Self {
1099        Self(self.0.alignment(alignment))
1100    }
1101
1102    fn default_attribute(self, attribute: impl Into<piet::TextAttribute>) -> Self {
1103        Self(self.0.default_attribute(attribute))
1104    }
1105
1106    fn range_attribute(
1107        self,
1108        range: impl std::ops::RangeBounds<usize>,
1109        attribute: impl Into<piet::TextAttribute>,
1110    ) -> Self {
1111        Self(self.0.range_attribute(range, attribute))
1112    }
1113
1114    fn build(self) -> Result<Self::Out, Pierror> {
1115        Ok(TextLayout(self.0.build()?))
1116    }
1117}
1118
1119/// The text engine type.
1120#[derive(Debug, Clone)]
1121pub struct Text(piet_hardware::Text);
1122
1123impl Text {
1124    /// Get the DPI scale.
1125    pub fn dpi(&self) -> f64 {
1126        self.0.dpi()
1127    }
1128
1129    /// Set the DPI scale.
1130    pub fn set_dpi(&mut self, dpi: f64) {
1131        self.0.set_dpi(dpi)
1132    }
1133}
1134
1135impl piet::Text for Text {
1136    type TextLayoutBuilder = TextLayoutBuilder;
1137    type TextLayout = TextLayout;
1138
1139    fn font_family(&mut self, family_name: &str) -> Option<piet::FontFamily> {
1140        self.0.font_family(family_name)
1141    }
1142
1143    fn load_font(&mut self, data: &[u8]) -> Result<piet::FontFamily, Pierror> {
1144        self.0.load_font(data)
1145    }
1146
1147    fn new_text_layout(&mut self, text: impl piet::TextStorage) -> Self::TextLayoutBuilder {
1148        TextLayoutBuilder(self.0.new_text_layout(text))
1149    }
1150}
1151
1152fn compile_program<H: HasContext + ?Sized>(
1153    context: &H,
1154    vertex_shader: &str,
1155    fragment_shader: &str,
1156) -> Result<H::Program, GlError> {
1157    unsafe {
1158        let vertex_shader = compile_shader(context, glow::VERTEX_SHADER, vertex_shader)?;
1159        let fragment_shader = compile_shader(context, glow::FRAGMENT_SHADER, fragment_shader)?;
1160
1161        let program = context.create_program().gl_err()?;
1162        let _call_on_drop = CallOnDrop(|| context.delete_program(program));
1163
1164        context.attach_shader(program, vertex_shader);
1165        context.attach_shader(program, fragment_shader);
1166        let _unlink_shaders = CallOnDrop(|| {
1167            context.detach_shader(program, vertex_shader);
1168            context.detach_shader(program, fragment_shader);
1169            context.delete_shader(vertex_shader);
1170            context.delete_shader(fragment_shader);
1171        });
1172        context.link_program(program);
1173
1174        if !context.get_program_link_status(program) {
1175            let log = context.get_program_info_log(program);
1176            return Err(GlError(log));
1177        }
1178
1179        mem::forget(_call_on_drop);
1180        Ok(program)
1181    }
1182}
1183
1184unsafe fn compile_shader<H: HasContext + ?Sized>(
1185    context: &H,
1186    shader_type: u32,
1187    source: &str,
1188) -> Result<H::Shader, GlError> {
1189    let shader = context.create_shader(shader_type).gl_err()?;
1190    let _call_on_drop = CallOnDrop(|| context.delete_shader(shader));
1191
1192    context.shader_source(shader, source);
1193    context.compile_shader(shader);
1194
1195    if !context.get_shader_compile_status(shader) {
1196        let log = context.get_shader_info_log(shader);
1197        return Err(GlError(log));
1198    }
1199
1200    mem::forget(_call_on_drop);
1201    Ok(shader)
1202}
1203
1204fn gl_error(h: &(impl HasContext + ?Sized)) {
1205    let err = unsafe { h.get_error() };
1206
1207    if err != glow::NO_ERROR {
1208        let error_str = match err {
1209            glow::INVALID_ENUM => "GL_INVALID_ENUM",
1210            glow::INVALID_VALUE => "GL_INVALID_VALUE",
1211            glow::INVALID_OPERATION => "GL_INVALID_OPERATION",
1212            glow::STACK_OVERFLOW => "GL_STACK_OVERFLOW",
1213            glow::STACK_UNDERFLOW => "GL_STACK_UNDERFLOW",
1214            glow::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
1215            glow::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
1216            glow::CONTEXT_LOST => "GL_CONTEXT_LOST",
1217            _ => "Unknown GL error",
1218        };
1219
1220        tracing::error!("GL error: {}", error_str)
1221    }
1222}
1223
1224trait ResultExt<T, E> {
1225    fn gl_err(self) -> Result<T, GlError>;
1226}
1227
1228impl<T, E: Into<GlError>> ResultExt<T, E> for Result<T, E> {
1229    fn gl_err(self) -> Result<T, GlError> {
1230        self.map_err(Into::into)
1231    }
1232}
1233
1234struct CallOnDrop<F: FnMut()>(F);
1235
1236impl<F: FnMut()> Drop for CallOnDrop<F> {
1237    fn drop(&mut self) {
1238        (self.0)();
1239    }
1240}