diff options
Diffstat (limited to 'components/canvas')
-rw-r--r-- | components/canvas/Cargo.toml | 13 | ||||
-rw-r--r-- | components/canvas/backend.rs | 243 | ||||
-rw-r--r-- | components/canvas/canvas_data.rs | 506 | ||||
-rw-r--r-- | components/canvas/canvas_paint_thread.rs | 359 | ||||
-rw-r--r-- | components/canvas/lib.rs | 8 | ||||
-rw-r--r-- | components/canvas/raqote_backend.rs | 532 | ||||
-rw-r--r-- | components/canvas/webgl_limits.rs | 259 | ||||
-rw-r--r-- | components/canvas/webgl_mode/inprocess.rs | 147 | ||||
-rw-r--r-- | components/canvas/webgl_mode/mod.rs | 7 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 3250 | ||||
-rw-r--r-- | components/canvas/webxr.rs | 337 |
11 files changed, 932 insertions, 4729 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 7e7b00efe11..6084fc6e434 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -11,24 +11,15 @@ rust-version.workspace = true name = "canvas" path = "lib.rs" -[features] -webgl_backtrace = ["canvas_traits/webgl_backtrace"] -webxr = ["dep:webxr", "dep:webxr-api"] - [dependencies] app_units = { workspace = true } -bitflags = { workspace = true } -byteorder = { workspace = true } canvas_traits = { workspace = true } compositing_traits = { workspace = true } crossbeam-channel = { workspace = true } cssparser = { workspace = true } euclid = { workspace = true } -fnv = { workspace = true } font-kit = "0.14" fonts = { path = "../fonts" } -glow = { workspace = true } -half = "2" ipc-channel = { workspace = true } log = { workspace = true } lyon_geom = "1.0.4" @@ -40,9 +31,5 @@ raqote = "0.8.5" servo_arc = { workspace = true } snapshot = { workspace = true } stylo = { workspace = true } -surfman = { workspace = true } unicode-script = { workspace = true } -webrender = { workspace = true } webrender_api = { workspace = true } -webxr = { path = "../webxr", features = ["ipc"], optional = true } -webxr-api = { workspace = true, features = ["ipc"], optional = true } diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs new file mode 100644 index 00000000000..53acbea8b8d --- /dev/null +++ b/components/canvas/backend.rs @@ -0,0 +1,243 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use canvas_traits::canvas::{ + CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, +}; +use euclid::Angle; +use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use lyon_geom::Arc; +use style::color::AbsoluteColor; + +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; + +pub(crate) trait Backend: Clone + Sized { + type Pattern<'a>: PatternHelpers + Clone; + type StrokeOptions: StrokeOptionsHelpers + Clone; + type Color: Clone; + type DrawOptions: DrawOptionsHelpers + Clone; + type CompositionOp; + type DrawTarget: GenericDrawTarget<Self>; + type PathBuilder: GenericPathBuilder<Self>; + type SourceSurface; + type Bytes<'a>: AsRef<[u8]>; + type Path: PathHelpers<Self> + Clone; + type GradientStop; + type GradientStops; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp; + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool; + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>); + fn set_fill_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_stroke_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_global_composition( + &mut self, + op: CompositionOrBlending, + state: &mut CanvasPaintState<'_, Self>, + ); + fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget; + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self>; +} + +// This defines required methods for a DrawTarget (currently only implemented for raqote). The +// prototypes are derived from the now-removed Azure backend's methods. +pub(crate) trait GenericDrawTarget<B: Backend> { + fn clear_rect(&mut self, rect: &Rect<f32>); + fn copy_surface( + &mut self, + surface: B::SourceSurface, + source: Rect<i32>, + destination: Point2D<i32>, + ); + fn create_path_builder(&self) -> B::PathBuilder; + fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self; + fn create_source_surface_from_data(&self, data: &[u8]) -> Option<B::SourceSurface>; + fn draw_surface( + &mut self, + surface: B::SourceSurface, + dest: Rect<f64>, + source: Rect<f64>, + filter: Filter, + draw_options: &B::DrawOptions, + ); + fn draw_surface_with_shadow( + &self, + surface: B::SourceSurface, + dest: &Point2D<f32>, + color: &B::Color, + offset: &Vector2D<f32>, + sigma: f32, + operator: B::CompositionOp, + ); + fn fill(&mut self, path: &B::Path, pattern: B::Pattern<'_>, draw_options: &B::DrawOptions); + fn fill_text( + &mut self, + text_runs: Vec<TextRun>, + start: Point2D<f32>, + pattern: &B::Pattern<'_>, + draw_options: &B::DrawOptions, + ); + fn fill_rect( + &mut self, + rect: &Rect<f32>, + pattern: B::Pattern<'_>, + draw_options: Option<&B::DrawOptions>, + ); + fn get_size(&self) -> Size2D<i32>; + fn get_transform(&self) -> Transform2D<f32>; + fn pop_clip(&mut self); + fn push_clip(&mut self, path: &B::Path); + fn set_transform(&mut self, matrix: &Transform2D<f32>); + fn stroke( + &mut self, + path: &B::Path, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_line( + &mut self, + start: Point2D<f32>, + end: Point2D<f32>, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_rect( + &mut self, + rect: &Rect<f32>, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn surface(&self) -> B::SourceSurface; + fn bytes(&'_ self) -> B::Bytes<'_>; +} + +/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. +pub(crate) trait GenericPathBuilder<B: Backend> { + fn arc( + &mut self, + origin: Point2D<f32>, + radius: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ); + fn bezier_curve_to( + &mut self, + control_point1: &Point2D<f32>, + control_point2: &Point2D<f32>, + control_point3: &Point2D<f32>, + ); + fn close(&mut self); + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + origin: Point2D<f32>, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ) { + let mut start = Angle::radians(start_angle); + let mut end = Angle::radians(end_angle); + + // Wrap angles mod 2 * PI if necessary + if !anticlockwise && start > end + Angle::two_pi() || + anticlockwise && end > start + Angle::two_pi() + { + start = start.positive(); + end = end.positive(); + } + + // Calculate the total arc we're going to sweep. + let sweep = match anticlockwise { + true => { + if end - start == Angle::two_pi() { + -Angle::two_pi() + } else if end > start { + -(Angle::two_pi() - (end - start)) + } else { + -(start - end) + } + }, + false => { + if start - end == Angle::two_pi() { + Angle::two_pi() + } else if start > end { + Angle::two_pi() - (start - end) + } else { + end - start + } + }, + }; + + let arc: Arc<f32> = Arc { + center: origin, + radii: Vector2D::new(radius_x, radius_y), + start_angle: start, + sweep_angle: sweep, + x_rotation: Angle::radians(rotation_angle), + }; + + self.line_to(arc.from()); + + arc.for_each_quadratic_bezier(&mut |q| { + self.quadratic_curve_to(&q.ctrl, &q.to); + }); + } + fn get_current_point(&mut self) -> Option<Point2D<f32>>; + fn line_to(&mut self, point: Point2D<f32>); + fn move_to(&mut self, point: Point2D<f32>); + fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>); + fn svg_arc( + &mut self, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + large_arc: bool, + sweep: bool, + end_point: Point2D<f32>, + ); + fn finish(&mut self) -> B::Path; +} + +pub(crate) trait PatternHelpers { + fn is_zero_size_gradient(&self) -> bool; + fn draw_rect(&self, rect: &Rect<f32>) -> Rect<f32>; +} + +pub(crate) trait StrokeOptionsHelpers { + fn set_line_width(&mut self, _val: f32); + fn set_miter_limit(&mut self, _val: f32); + fn set_line_join(&mut self, val: LineJoinStyle); + fn set_line_cap(&mut self, val: LineCapStyle); + fn set_line_dash(&mut self, items: Vec<f32>); + fn set_line_dash_offset(&mut self, offset: f32); +} + +pub(crate) trait DrawOptionsHelpers { + fn set_alpha(&mut self, val: f32); +} + +pub(crate) trait PathHelpers<B: Backend> { + fn transformed_copy_to_builder(&self, transform: &Transform2D<f32>) -> B::PathBuilder; + + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool; + + fn copy_to_builder(&self) -> B::PathBuilder; +} diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 2667b7f6b44..ea30589d0af 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::marker::PhantomData; use std::mem; use std::sync::Arc; @@ -16,7 +17,6 @@ use fonts::{ }; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::warn; -use num_traits::ToPrimitive; use range::Range; use servo_arc::Arc as ServoArc; use snapshot::Snapshot; @@ -26,14 +26,17 @@ use unicode_script::Script; use webrender_api::units::RectExt as RectExt_; use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; -use crate::raqote_backend::Repetition; +use crate::backend::{ + Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers as _, +}; // Asserts on WR texture cache update for zero sized image with raw data. // https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475 const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1); -fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Path { - let mut build_ref = PathBuilderRef { +fn to_path<B: Backend>(path: &[PathSegment], mut builder: B::PathBuilder) -> B::Path { + let mut build_ref = PathBuilderRef::<B> { builder: &mut builder, transform: Transform2D::identity(), }; @@ -112,20 +115,20 @@ fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Pa /// draw the path, we convert it back to userspace and draw it /// with the correct transform applied. /// TODO: De-abstract now that Azure is removed? -enum PathState { +enum PathState<B: Backend> { /// Path builder in user-space. If a transform has been applied /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePathBuilder(Box<dyn GenericPathBuilder>, Option<Transform2D<f32>>), + UserSpacePathBuilder(B::PathBuilder, Option<Transform2D<f32>>), /// Path builder in device-space. - DeviceSpacePathBuilder(Box<dyn GenericPathBuilder>), + DeviceSpacePathBuilder(B::PathBuilder), /// Path in user-space. If a transform has been applied but /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePath(Path, Option<Transform2D<f32>>), + UserSpacePath(B::Path, Option<Transform2D<f32>>), } -impl PathState { +impl<B: Backend> PathState<B> { fn is_path(&self) -> bool { match *self { PathState::UserSpacePath(..) => true, @@ -133,7 +136,7 @@ impl PathState { } } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { match *self { PathState::UserSpacePath(ref p, _) => p, PathState::UserSpacePathBuilder(..) | PathState::DeviceSpacePathBuilder(..) => { @@ -143,84 +146,14 @@ impl PathState { } } -pub trait Backend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp; - fn need_to_draw_shadow(&self, color: &Color) -> bool; - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>); - fn set_fill_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_stroke_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_global_composition( - &mut self, - op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, - ); - fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget>; - fn recreate_paint_state<'a>(&self, state: &CanvasPaintState<'a>) -> CanvasPaintState<'a>; -} - -/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. -/// TODO: De-abstract now that Azure is removed? -pub trait GenericPathBuilder { - fn arc( - &mut self, - origin: Point2D<f32>, - radius: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn bezier_curve_to( - &mut self, - control_point1: &Point2D<f32>, - control_point2: &Point2D<f32>, - control_point3: &Point2D<f32>, - ); - fn close(&mut self); - #[allow(clippy::too_many_arguments)] - fn ellipse( - &mut self, - origin: Point2D<f32>, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn get_current_point(&mut self) -> Option<Point2D<f32>>; - fn line_to(&mut self, point: Point2D<f32>); - fn move_to(&mut self, point: Point2D<f32>); - fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>); - fn svg_arc( - &mut self, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - large_arc: bool, - sweep: bool, - end_point: Point2D<f32>, - ); - fn finish(&mut self) -> Path; -} - /// A wrapper around a stored PathBuilder and an optional transformation that should be /// applied to any points to ensure they are in the matching device space. -struct PathBuilderRef<'a> { - builder: &'a mut Box<dyn GenericPathBuilder>, +struct PathBuilderRef<'a, B: Backend> { + builder: &'a mut B::PathBuilder, transform: Transform2D<f32>, } -impl PathBuilderRef<'_> { +impl<B: Backend> PathBuilderRef<'_, B> { fn line_to(&mut self, pt: &Point2D<f32>) { let pt = self.transform.transform_point(*pt); self.builder.line_to(pt); @@ -341,7 +274,7 @@ impl PathBuilderRef<'_> { } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D<f32>, radius_x: f32, @@ -430,9 +363,9 @@ impl UnshapedTextRun<'_> { } } -pub struct TextRun { - pub font: FontRef, - pub glyphs: Arc<GlyphStore>, +pub(crate) struct TextRun { + pub(crate) font: FontRef, + pub(crate) glyphs: Arc<GlyphStore>, } impl TextRun { @@ -458,149 +391,31 @@ impl TextRun { } } -// This defines required methods for a DrawTarget (currently only implemented for raqote). The -// prototypes are derived from the now-removed Azure backend's methods. -pub trait GenericDrawTarget { - fn clear_rect(&mut self, rect: &Rect<f32>); - fn copy_surface( - &mut self, - surface: SourceSurface, - source: Rect<i32>, - destination: Point2D<i32>, - ); - fn create_gradient_stops(&self, gradient_stops: Vec<GradientStop>) -> GradientStops; - fn create_path_builder(&self) -> Box<dyn GenericPathBuilder>; - fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Box<dyn GenericDrawTarget>; - fn create_source_surface_from_data(&self, data: &[u8]) -> Option<SourceSurface>; - fn draw_surface( - &mut self, - surface: SourceSurface, - dest: Rect<f64>, - source: Rect<f64>, - filter: Filter, - draw_options: &DrawOptions, - ); - fn draw_surface_with_shadow( - &self, - surface: SourceSurface, - dest: &Point2D<f32>, - color: &Color, - offset: &Vector2D<f32>, - sigma: f32, - operator: CompositionOp, - ); - fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); - fn fill_text( - &mut self, - text_runs: Vec<TextRun>, - start: Point2D<f32>, - pattern: &Pattern, - draw_options: &DrawOptions, - ); - fn fill_rect(&mut self, rect: &Rect<f32>, pattern: Pattern, draw_options: Option<&DrawOptions>); - fn get_size(&self) -> Size2D<i32>; - fn get_transform(&self) -> Transform2D<f32>; - fn pop_clip(&mut self); - fn push_clip(&mut self, path: &Path); - fn set_transform(&mut self, matrix: &Transform2D<f32>); - fn snapshot(&self) -> SourceSurface; - fn stroke( - &mut self, - path: &Path, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_line( - &mut self, - start: Point2D<f32>, - end: Point2D<f32>, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_rect( - &mut self, - rect: &Rect<f32>, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn snapshot_data(&self) -> &[u8]; -} - -pub enum GradientStop { - Raqote(raqote::GradientStop), -} - -pub enum GradientStops { - Raqote(Vec<raqote::GradientStop>), -} - -#[derive(Clone)] -pub enum Color { - Raqote(raqote::SolidSource), -} - -#[derive(Clone)] -pub enum CompositionOp { - Raqote(raqote::BlendMode), -} - -#[derive(Clone)] -pub enum SourceSurface { - Raqote(Vec<u8>), // TODO: See if we can avoid the alloc (probably?) -} - -#[derive(Clone)] -pub enum Path { - Raqote(raqote::Path), -} - -#[derive(Clone)] -pub enum Pattern<'a> { - Raqote(crate::raqote_backend::Pattern<'a>), -} - -#[derive(Clone)] -pub enum DrawOptions { - Raqote(raqote::DrawOptions), -} - -#[derive(Clone)] -pub enum StrokeOptions { - Raqote(raqote::StrokeStyle), -} - #[derive(Clone, Copy)] -pub enum Filter { +pub(crate) enum Filter { Bilinear, Nearest, } -pub struct CanvasData<'a> { - backend: Box<dyn Backend>, - drawtarget: Box<dyn GenericDrawTarget>, - path_state: Option<PathState>, - state: CanvasPaintState<'a>, - saved_states: Vec<CanvasPaintState<'a>>, +pub(crate) struct CanvasData<'a, B: Backend> { + backend: B, + drawtarget: B::DrawTarget, + path_state: Option<PathState<B>>, + state: CanvasPaintState<'a, B>, + saved_states: Vec<CanvasPaintState<'a, B>>, compositor_api: CrossProcessCompositorApi, image_key: ImageKey, font_context: Arc<FontContext>, } -fn create_backend() -> Box<dyn Backend> { - Box::new(crate::raqote_backend::RaqoteBackend) -} - -impl<'a> CanvasData<'a> { - pub fn new( +impl<'a, B: Backend> CanvasData<'a, B> { + pub(crate) fn new( size: Size2D<u64>, compositor_api: CrossProcessCompositorApi, font_context: Arc<FontContext>, - ) -> CanvasData<'a> { + backend: B, + ) -> CanvasData<'a, B> { let size = size.max(MIN_WR_IMAGE_SIZE); - let backend = create_backend(); let draw_target = backend.create_drawtarget(size); let image_key = compositor_api.generate_image_key().unwrap(); let descriptor = ImageDescriptor { @@ -611,13 +426,13 @@ impl<'a> CanvasData<'a> { flags: ImageDescriptorFlags::empty(), }; let data = - SerializableImageData::Raw(IpcSharedMemory::from_bytes(draw_target.snapshot_data())); + SerializableImageData::Raw(IpcSharedMemory::from_bytes(draw_target.bytes().as_ref())); compositor_api.update_images(vec![ImageUpdate::AddImage(image_key, descriptor, data)]); CanvasData { + state: backend.new_paint_state(), backend, drawtarget: draw_target, path_state: None, - state: CanvasPaintState::default(), saved_states: vec![], compositor_api, image_key, @@ -625,11 +440,11 @@ impl<'a> CanvasData<'a> { } } - pub fn image_key(&self) -> ImageKey { + pub(crate) fn image_key(&self) -> ImageKey { self.image_key } - pub fn draw_image( + pub(crate) fn draw_image( &mut self, image_data: &[u8], image_size: Size2D<u64>, @@ -648,8 +463,8 @@ impl<'a> CanvasData<'a> { }; let draw_options = self.state.draw_options.clone(); - let writer = |draw_target: &mut dyn GenericDrawTarget| { - write_image( + let writer = |draw_target: &mut B::DrawTarget| { + write_image::<B>( draw_target, image_data, source_rect.size, @@ -669,15 +484,15 @@ impl<'a> CanvasData<'a> { // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow(&rect, writer); } else { - writer(&mut *self.drawtarget); + writer(&mut self.drawtarget); } } - pub fn save_context_state(&mut self) { + pub(crate) fn save_context_state(&mut self) { self.saved_states.push(self.state.clone()); } - pub fn restore_context_state(&mut self) { + pub(crate) fn restore_context_state(&mut self) { if let Some(state) = self.saved_states.pop() { let _ = mem::replace(&mut self.state, state); self.drawtarget.set_transform(&self.state.transform); @@ -685,7 +500,7 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text_with_size( + pub(crate) fn fill_text_with_size( &mut self, text: String, x: f64, @@ -764,7 +579,7 @@ impl<'a> CanvasData<'a> { } /// <https://html.spec.whatwg.org/multipage/#text-preparation-algorithm> - pub fn fill_text( + pub(crate) fn fill_text( &mut self, text: String, x: f64, @@ -782,7 +597,7 @@ impl<'a> CanvasData<'a> { /// <https://html.spec.whatwg.org/multipage/#text-preparation-algorithm> /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-measuretext> - pub fn measure_text(&mut self, text: String) -> TextMetrics { + pub(crate) fn measure_text(&mut self, text: String) -> TextMetrics { // > Step 2: Replace all ASCII whitespace in text with U+0020 SPACE characters. let text = replace_ascii_whitespace(text); let Some(ref font_style) = self.state.font_style else { @@ -934,49 +749,15 @@ impl<'a> CanvasData<'a> { point2(x + anchor_x, y + anchor_y) } - pub fn fill_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn fill_rect(&mut self, rect: &Rect<f32>) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let draw_rect = match &self.state.fill_style { - Pattern::Raqote(pattern) => match pattern { - crate::raqote_backend::Pattern::Surface(pattern) => { - let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); - let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); - - match pattern.repetition() { - Repetition::NoRepeat => { - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatX => { - draw_rect.size.width = rect.size.width; - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatY => { - draw_rect.size.height = rect.size.height; - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - }, - Repetition::Repeat => { - draw_rect = *rect; - }, - } - - draw_rect - }, - crate::raqote_backend::Pattern::Color(..) | - crate::raqote_backend::Pattern::LinearGradient(..) | - crate::raqote_backend::Pattern::RadialGradient(..) => *rect, - }, - }; + let draw_rect = self.state.fill_style.draw_rect(rect); if self.need_to_draw_shadow() { - self.draw_with_shadow(&draw_rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(&draw_rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.fill_rect( &draw_rect, self.state.fill_style.clone(), @@ -992,17 +773,17 @@ impl<'a> CanvasData<'a> { } } - pub fn clear_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn clear_rect(&mut self, rect: &Rect<f32>) { self.drawtarget.clear_rect(rect); } - pub fn stroke_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn stroke_rect(&mut self, rect: &Rect<f32>) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } if self.need_to_draw_shadow() { - self.draw_with_shadow(rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.stroke_rect( rect, self.state.stroke_style.clone(), @@ -1030,12 +811,12 @@ impl<'a> CanvasData<'a> { } } - pub fn begin_path(&mut self) { + pub(crate) fn begin_path(&mut self) { // Erase any traces of previous paths that existed before this. self.path_state = None; } - pub fn close_path(&mut self) { + pub(crate) fn close_path(&mut self) { self.path_builder().close(); } @@ -1097,14 +878,14 @@ impl<'a> CanvasData<'a> { assert!(self.path_state.as_ref().unwrap().is_path()) } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { self.path_state .as_ref() .expect("Should have called ensure_path()") .path() } - pub fn fill(&mut self) { + pub(crate) fn fill(&mut self) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1113,16 +894,16 @@ impl<'a> CanvasData<'a> { self.drawtarget.fill( &self.path().clone(), self.state.fill_style.clone(), - &self.state.draw_options, + &self.state.draw_options.clone(), ); } - pub fn fill_path(&mut self, path: &[PathSegment]) { + pub(crate) fn fill_path(&mut self, path: &[PathSegment]) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.fill( &path, @@ -1131,7 +912,7 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke(&mut self) { + pub(crate) fn stroke(&mut self) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1145,12 +926,12 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke_path(&mut self, path: &[PathSegment]) { + pub(crate) fn stroke_path(&mut self, path: &[PathSegment]) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.stroke( &path, @@ -1160,18 +941,18 @@ impl<'a> CanvasData<'a> { ); } - pub fn clip(&mut self) { + pub(crate) fn clip(&mut self) { self.ensure_path(); let path = self.path().clone(); self.drawtarget.push_clip(&path); } - pub fn clip_path(&mut self, path: &[PathSegment]) { - let path = to_path(path, self.drawtarget.create_path_builder()); + pub(crate) fn clip_path(&mut self, path: &[PathSegment]) { + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.push_clip(&path); } - pub fn is_point_in_path( + pub(crate) fn is_point_in_path( &mut self, x: f64, y: f64, @@ -1190,7 +971,7 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn is_point_in_path_( + pub(crate) fn is_point_in_path_( &mut self, path: &[PathSegment], x: f64, @@ -1202,7 +983,7 @@ impl<'a> CanvasData<'a> { Some(PathState::UserSpacePath(_, Some(transform))) => transform, Some(_) | None => &self.drawtarget.get_transform(), }; - let result = to_path(path, self.drawtarget.create_path_builder()).contains_point( + let result = to_path::<B>(path, self.drawtarget.create_path_builder()).contains_point( x, y, path_transform, @@ -1210,15 +991,15 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn move_to(&mut self, point: &Point2D<f32>) { + pub(crate) fn move_to(&mut self, point: &Point2D<f32>) { self.path_builder().move_to(point); } - pub fn line_to(&mut self, point: &Point2D<f32>) { + pub(crate) fn line_to(&mut self, point: &Point2D<f32>) { self.path_builder().line_to(point); } - fn path_builder(&mut self) -> PathBuilderRef { + fn path_builder(&mut self) -> PathBuilderRef<B> { if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( self.drawtarget.create_path_builder(), @@ -1283,18 +1064,18 @@ impl<'a> CanvasData<'a> { } } - pub fn rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn rect(&mut self, rect: &Rect<f32>) { self.path_builder().rect(rect); } - pub fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, endpoint: &Point2D<f32>) { + pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, endpoint: &Point2D<f32>) { if self.path_state.is_none() { self.move_to(cp); } self.path_builder().quadratic_curve_to(cp, endpoint); } - pub fn bezier_curve_to( + pub(crate) fn bezier_curve_to( &mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, @@ -1306,7 +1087,7 @@ impl<'a> CanvasData<'a> { self.path_builder().bezier_curve_to(cp1, cp2, endpoint); } - pub fn arc( + pub(crate) fn arc( &mut self, center: &Point2D<f32>, radius: f32, @@ -1318,12 +1099,12 @@ impl<'a> CanvasData<'a> { .arc(center, radius, start_angle, end_angle, ccw); } - pub fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { + pub(crate) fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { self.path_builder().arc_to(cp1, cp2, radius); } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D<f32>, radius_x: f32, @@ -1344,45 +1125,45 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_fill_style(style, &mut self.state, &*self.drawtarget); + .set_fill_style(style, &mut self.state, &self.drawtarget); } - pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_stroke_style(style, &mut self.state, &*self.drawtarget); + .set_stroke_style(style, &mut self.state, &self.drawtarget); } - pub fn set_line_width(&mut self, width: f32) { + pub(crate) fn set_line_width(&mut self, width: f32) { self.state.stroke_opts.set_line_width(width); } - pub fn set_line_cap(&mut self, cap: LineCapStyle) { + pub(crate) fn set_line_cap(&mut self, cap: LineCapStyle) { self.state.stroke_opts.set_line_cap(cap); } - pub fn set_line_join(&mut self, join: LineJoinStyle) { + pub(crate) fn set_line_join(&mut self, join: LineJoinStyle) { self.state.stroke_opts.set_line_join(join); } - pub fn set_miter_limit(&mut self, limit: f32) { + pub(crate) fn set_miter_limit(&mut self, limit: f32) { self.state.stroke_opts.set_miter_limit(limit); } - pub fn set_line_dash(&mut self, items: Vec<f32>) { + pub(crate) fn set_line_dash(&mut self, items: Vec<f32>) { self.state.stroke_opts.set_line_dash(items); } - pub fn set_line_dash_offset(&mut self, offset: f32) { + pub(crate) fn set_line_dash_offset(&mut self, offset: f32) { self.state.stroke_opts.set_line_dash_offset(offset); } - pub fn get_transform(&self) -> Transform2D<f32> { + pub(crate) fn get_transform(&self) -> Transform2D<f32> { self.drawtarget.get_transform() } - pub fn set_transform(&mut self, transform: &Transform2D<f32>) { + pub(crate) fn set_transform(&mut self, transform: &Transform2D<f32>) { // If there is an in-progress path, store the existing transformation required // to move between device and user space. match self.path_state.as_mut() { @@ -1398,32 +1179,28 @@ impl<'a> CanvasData<'a> { self.drawtarget.set_transform(transform) } - pub fn set_global_alpha(&mut self, alpha: f32) { + pub(crate) fn set_global_alpha(&mut self, alpha: f32) { self.state.draw_options.set_alpha(alpha); } - pub fn set_global_composition(&mut self, op: CompositionOrBlending) { + pub(crate) fn set_global_composition(&mut self, op: CompositionOrBlending) { self.backend.set_global_composition(op, &mut self.state); } - pub fn recreate(&mut self, size: Option<Size2D<u64>>) { + pub(crate) fn recreate(&mut self, size: Option<Size2D<u64>>) { let size = size .unwrap_or_else(|| self.drawtarget.get_size().to_u64()) .max(MIN_WR_IMAGE_SIZE); self.drawtarget = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); - self.state = self.backend.recreate_paint_state(&self.state); + self.state = self.backend.new_paint_state(); self.saved_states.clear(); self.update_image_rendering(); } - pub fn snapshot(&self) { - self.drawtarget.snapshot_data(); - } - /// Update image in WebRender - pub fn update_image_rendering(&mut self) { + pub(crate) fn update_image_rendering(&mut self) { let descriptor = ImageDescriptor { size: self.drawtarget.get_size().cast_unit(), stride: None, @@ -1432,7 +1209,7 @@ impl<'a> CanvasData<'a> { flags: ImageDescriptorFlags::empty(), }; let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes( - self.drawtarget.snapshot_data(), + self.drawtarget.bytes().as_ref(), )); self.compositor_api @@ -1444,7 +1221,7 @@ impl<'a> CanvasData<'a> { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - pub fn put_image_data(&mut self, mut imagedata: Vec<u8>, rect: Rect<u64>) { + pub(crate) fn put_image_data(&mut self, mut imagedata: Vec<u8>, rect: Rect<u64>) { assert_eq!(imagedata.len() % 4, 0); assert_eq!(rect.size.area() as usize, imagedata.len() / 4); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); @@ -1459,31 +1236,31 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_shadow_offset_x(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_x(&mut self, value: f64) { self.state.shadow_offset_x = value; } - pub fn set_shadow_offset_y(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_y(&mut self, value: f64) { self.state.shadow_offset_y = value; } - pub fn set_shadow_blur(&mut self, value: f64) { + pub(crate) fn set_shadow_blur(&mut self, value: f64) { self.state.shadow_blur = value; } - pub fn set_shadow_color(&mut self, value: AbsoluteColor) { + pub(crate) fn set_shadow_color(&mut self, value: AbsoluteColor) { self.backend.set_shadow_color(value, &mut self.state); } - pub fn set_font(&mut self, font_style: FontStyleStruct) { + pub(crate) fn set_font(&mut self, font_style: FontStyleStruct) { self.state.font_style = Some(ServoArc::new(font_style)) } - pub fn set_text_align(&mut self, text_align: TextAlign) { + pub(crate) fn set_text_align(&mut self, text_align: TextAlign) { self.state.text_align = text_align; } - pub fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + pub(crate) fn set_text_baseline(&mut self, text_baseline: TextBaseline) { self.state.text_baseline = text_baseline; } @@ -1495,7 +1272,7 @@ impl<'a> CanvasData<'a> { self.state.shadow_blur != 0.0f64) } - fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> Box<dyn GenericDrawTarget> { + fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> B::DrawTarget { let mut draw_target = self.drawtarget.create_similar_draw_target(&Size2D::new( source_rect.size.width as i32, source_rect.size.height as i32, @@ -1509,13 +1286,13 @@ impl<'a> CanvasData<'a> { fn draw_with_shadow<F>(&self, rect: &Rect<f32>, draw_shadow_source: F) where - F: FnOnce(&mut dyn GenericDrawTarget), + F: FnOnce(&mut B::DrawTarget), { let shadow_src_rect = self.state.transform.outer_transformed_rect(rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); - draw_shadow_source(&mut *new_draw_target); + draw_shadow_source(&mut new_draw_target); self.drawtarget.draw_surface_with_shadow( - new_draw_target.snapshot(), + new_draw_target.surface(), &Point2D::new(shadow_src_rect.origin.x, shadow_src_rect.origin.y), &self.state.shadow_color, &Vector2D::new( @@ -1531,7 +1308,7 @@ impl<'a> CanvasData<'a> { /// canvas_size: The size of the canvas we're reading from /// read_rect: The area of the canvas we want to read from #[allow(unsafe_code)] - pub fn read_pixels( + pub(crate) fn read_pixels( &self, read_rect: Option<Rect<u64>>, canvas_size: Option<Size2D<u64>>, @@ -1546,11 +1323,11 @@ impl<'a> CanvasData<'a> { { vec![] } else { - let bytes = self.drawtarget.snapshot_data(); - pixels::rgba8_get_rect(bytes, canvas_size, read_rect).to_vec() + pixels::rgba8_get_rect(self.drawtarget.bytes().as_ref(), canvas_size, read_rect) + .to_vec() } } else { - self.drawtarget.snapshot_data().to_vec() + self.drawtarget.bytes().as_ref().to_vec() }; Snapshot::from_vec( @@ -1564,7 +1341,7 @@ impl<'a> CanvasData<'a> { } } -impl Drop for CanvasData<'_> { +impl<B: Backend> Drop for CanvasData<'_, B> { fn drop(&mut self) { self.compositor_api .update_images(vec![ImageUpdate::DeleteImage(self.image_key)]); @@ -1575,20 +1352,21 @@ const HANGING_BASELINE_DEFAULT: f32 = 0.8; const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; #[derive(Clone)] -pub struct CanvasPaintState<'a> { - pub draw_options: DrawOptions, - pub fill_style: Pattern<'a>, - pub stroke_style: Pattern<'a>, - pub stroke_opts: StrokeOptions, +pub(crate) struct CanvasPaintState<'a, B: Backend> { + pub(crate) draw_options: B::DrawOptions, + pub(crate) fill_style: B::Pattern<'a>, + pub(crate) stroke_style: B::Pattern<'a>, + pub(crate) stroke_opts: B::StrokeOptions, /// The current 2D transform matrix. - pub transform: Transform2D<f32>, - pub shadow_offset_x: f64, - pub shadow_offset_y: f64, - pub shadow_blur: f64, - pub shadow_color: Color, - pub font_style: Option<ServoArc<FontStyleStruct>>, - pub text_align: TextAlign, - pub text_baseline: TextBaseline, + pub(crate) transform: Transform2D<f32>, + pub(crate) shadow_offset_x: f64, + pub(crate) shadow_offset_y: f64, + pub(crate) shadow_blur: f64, + pub(crate) shadow_color: B::Color, + pub(crate) font_style: Option<ServoArc<FontStyleStruct>>, + pub(crate) text_align: TextAlign, + pub(crate) text_baseline: TextBaseline, + pub(crate) _backend: PhantomData<B>, } /// It writes an image to the destination target @@ -1598,14 +1376,14 @@ pub struct CanvasPaintState<'a> { /// dest_rect: Area of the destination target where the pixels will be copied /// smoothing_enabled: It determines if smoothing is applied to the image result /// premultiply: Determines whenever the image data should be premultiplied or not -fn write_image( - draw_target: &mut dyn GenericDrawTarget, +fn write_image<B: Backend>( + draw_target: &mut B::DrawTarget, mut image_data: Vec<u8>, image_size: Size2D<f64>, dest_rect: Rect<f64>, smoothing_enabled: bool, premultiply: bool, - draw_options: &DrawOptions, + draw_options: &B::DrawOptions, ) { if image_data.is_empty() { return; @@ -1634,25 +1412,11 @@ fn write_image( draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options); } -pub trait RectToi32 { - fn to_i32(&self) -> Rect<i32>; +pub(crate) trait RectToi32 { fn ceil(&self) -> Rect<f64>; } impl RectToi32 for Rect<f64> { - fn to_i32(&self) -> Rect<i32> { - Rect::new( - Point2D::new( - self.origin.x.to_i32().unwrap(), - self.origin.y.to_i32().unwrap(), - ), - Size2D::new( - self.size.width.to_i32().unwrap(), - self.size.height.to_i32().unwrap(), - ), - ) - } - fn ceil(&self) -> Rect<f64> { Rect::new( Point2D::new(self.origin.x.ceil(), self.origin.y.ceil()), @@ -1661,22 +1425,6 @@ impl RectToi32 for Rect<f64> { } } -pub trait RectExt { - fn to_u64(&self) -> Rect<u64>; -} - -impl RectExt for Rect<f64> { - fn to_u64(&self) -> Rect<u64> { - self.cast() - } -} - -impl RectExt for Rect<u32> { - fn to_u64(&self) -> Rect<u64> { - self.cast() - } -} - fn replace_ascii_whitespace(text: String) -> String { text.chars() .map(|c| match c { diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index bb940d7ef81..82a221d560d 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -11,18 +11,21 @@ use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::*; use compositing_traits::CrossProcessCompositorApi; use crossbeam_channel::{Sender, select, unbounded}; -use euclid::default::Size2D; +use euclid::default::{Point2D, Rect, Size2D, Transform2D}; use fonts::{FontContext, SystemFontServiceProxy}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; use net_traits::ResourceThreads; +use style::color::AbsoluteColor; +use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::ImageKey; use crate::canvas_data::*; +use crate::raqote_backend::RaqoteBackend; pub struct CanvasPaintThread<'a> { - canvases: HashMap<CanvasId, CanvasData<'a>>, + canvases: HashMap<CanvasId, Canvas<'a>>, next_canvas_id: CanvasId, compositor_api: CrossProcessCompositorApi, font_context: Arc<FontContext>, @@ -113,10 +116,14 @@ impl<'a> CanvasPaintThread<'a> { let canvas_id = self.next_canvas_id; self.next_canvas_id.0 += 1; - let canvas_data = - CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone()); + let canvas_data = CanvasData::new( + size, + self.compositor_api.clone(), + self.font_context.clone(), + RaqoteBackend, + ); let image_key = canvas_data.image_key(); - self.canvases.insert(canvas_id, canvas_data); + self.canvases.insert(canvas_id, Canvas::Raqote(canvas_data)); (canvas_id, image_key) } @@ -276,7 +283,347 @@ impl<'a> CanvasPaintThread<'a> { } } - fn canvas(&mut self, canvas_id: CanvasId) -> &mut CanvasData<'a> { + fn canvas(&mut self, canvas_id: CanvasId) -> &mut Canvas<'a> { self.canvases.get_mut(&canvas_id).expect("Bogus canvas id") } } + +enum Canvas<'a> { + Raqote(CanvasData<'a, RaqoteBackend>), +} + +impl Canvas<'_> { + fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_fill_style(style), + } + } + + fn fill(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill(), + } + } + + fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option<f64>, is_rtl: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_text(text, x, y, max_width, is_rtl), + } + } + + fn fill_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_rect(rect), + } + } + + fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_stroke_style(style), + } + } + + fn stroke_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_rect(rect), + } + } + + fn begin_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.begin_path(), + } + } + + fn close_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.close_path(), + } + } + + fn fill_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_path(path), + } + } + + fn stroke(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke(), + } + } + + fn stroke_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_path(path), + } + } + + fn clip(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip(), + } + } + + fn is_point_in_path(&mut self, x: f64, y: f64, fill_rule: FillRule, chan: IpcSender<bool>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.is_point_in_path(x, y, fill_rule, chan), + } + } + + fn is_point_in_path_( + &mut self, + path: &[PathSegment], + x: f64, + y: f64, + fill_rule: FillRule, + chan: IpcSender<bool>, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.is_point_in_path_(path, x, y, fill_rule, chan) + }, + } + } + + fn clear_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect), + } + } + + fn draw_image( + &mut self, + data: &[u8], + size: Size2D<u64>, + dest_rect: Rect<f64>, + source_rect: Rect<f64>, + smoothing_enabled: bool, + is_premultiplied: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.draw_image( + data, + size, + dest_rect, + source_rect, + smoothing_enabled, + is_premultiplied, + ), + } + } + + fn read_pixels( + &mut self, + read_rect: Option<Rect<u64>>, + canvas_size: Option<Size2D<u64>>, + ) -> snapshot::Snapshot { + match self { + Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect, canvas_size), + } + } + + fn move_to(&mut self, point: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.move_to(point), + } + } + + fn line_to(&mut self, point: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.line_to(point), + } + } + + fn rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.rect(rect), + } + } + + fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, pt: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.quadratic_curve_to(cp, pt), + } + } + + fn bezier_curve_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, pt: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.bezier_curve_to(cp1, cp2, pt), + } + } + + fn arc(&mut self, center: &Point2D<f32>, radius: f32, start: f32, end: f32, ccw: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc(center, radius, start, end, ccw), + } + } + + fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc_to(cp1, cp2, radius), + } + } + + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + center: &Point2D<f32>, + radius_x: f32, + radius_y: f32, + rotation: f32, + start: f32, + end: f32, + ccw: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.ellipse(center, radius_x, radius_y, rotation, start, end, ccw) + }, + } + } + + fn restore_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.restore_context_state(), + } + } + + fn save_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.save_context_state(), + } + } + + fn set_line_width(&mut self, width: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_width(width), + } + } + + fn set_line_cap(&mut self, cap: LineCapStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_cap(cap), + } + } + + fn set_line_join(&mut self, join: LineJoinStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_join(join), + } + } + + fn set_miter_limit(&mut self, limit: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_miter_limit(limit), + } + } + + fn set_line_dash(&mut self, items: Vec<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash(items), + } + } + + fn set_line_dash_offset(&mut self, offset: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash_offset(offset), + } + } + + fn set_transform(&mut self, matrix: &Transform2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_transform(matrix), + } + } + + fn set_global_alpha(&mut self, alpha: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_alpha(alpha), + } + } + + fn set_global_composition(&mut self, op: CompositionOrBlending) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_composition(op), + } + } + + fn set_shadow_offset_x(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_x(value), + } + } + + fn set_shadow_offset_y(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_y(value), + } + } + + fn set_shadow_blur(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_blur(value), + } + } + + fn set_shadow_color(&mut self, color: AbsoluteColor) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_color(color), + } + } + + fn set_font(&mut self, font_style: FontStyleStruct) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_font(font_style), + } + } + + fn set_text_align(&mut self, text_align: TextAlign) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_align(text_align), + } + } + + fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_baseline(text_baseline), + } + } + + fn measure_text(&mut self, text: String) -> TextMetrics { + match self { + Canvas::Raqote(canvas_data) => canvas_data.measure_text(text), + } + } + + fn clip_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip_path(path), + } + } + + fn get_transform(&self) -> Transform2D<f32> { + match self { + Canvas::Raqote(canvas_data) => canvas_data.get_transform(), + } + } + + fn put_image_data(&mut self, unwrap: Vec<u8>, rect: Rect<u64>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.put_image_data(unwrap, rect), + } + } + + fn update_image_rendering(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(), + } + } + + fn recreate(&mut self, size: Option<Size2D<u64>>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.recreate(size), + } + } +} diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index d2c62c1d8b6..91ab58b0e8b 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -4,14 +4,8 @@ #![deny(unsafe_code)] +mod backend; mod raqote_backend; -pub use webgl_mode::WebGLComm; - pub mod canvas_data; pub mod canvas_paint_thread; -mod webgl_limits; -mod webgl_mode; -pub mod webgl_thread; -#[cfg(feature = "webxr")] -mod webxr; diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 12137e41f41..efe0ffd05b8 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -7,20 +7,19 @@ use std::collections::HashMap; use canvas_traits::canvas::*; use cssparser::color::clamp_unit_f32; -use euclid::Angle; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use font_kit::font::Font; use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; use log::warn; -use lyon_geom::Arc; use range::Range; use raqote::PathOp; use style::color::AbsoluteColor; -use crate::canvas_data::{ - self, Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, Filter, GenericDrawTarget, - GenericPathBuilder, GradientStop, GradientStops, Path, SourceSurface, StrokeOptions, TextRun, +use crate::backend::{ + Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers, }; +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; thread_local! { /// The shared font cache used by all canvases that render on a thread. It would be nicer @@ -30,80 +29,85 @@ thread_local! { static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, Font>> = RefCell::default(); } -#[derive(Default)] -pub struct RaqoteBackend; +#[derive(Clone, Default)] +pub(crate) struct RaqoteBackend; impl Backend for RaqoteBackend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp { - CompositionOp::Raqote(opts.as_raqote().blend_mode) + type Pattern<'a> = Pattern<'a>; + type StrokeOptions = raqote::StrokeStyle; + type Color = raqote::SolidSource; + type DrawOptions = raqote::DrawOptions; + type CompositionOp = raqote::BlendMode; + type DrawTarget = raqote::DrawTarget; + type PathBuilder = PathBuilder; + type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?) + type Bytes<'a> = &'a [u8]; + type Path = raqote::Path; + type GradientStop = raqote::GradientStop; + type GradientStops = Vec<raqote::GradientStop>; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp { + opts.blend_mode } - fn need_to_draw_shadow(&self, color: &Color) -> bool { - color.as_raqote().a != 0 + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool { + color.a != 0 } - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>) { - state.shadow_color = Color::Raqote(color.to_raqote_style()); + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>) { + state.shadow_color = color.to_raqote_style(); } fn set_fill_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.fill_style = canvas_data::Pattern::Raqote(pattern); + state.fill_style = pattern; } } fn set_stroke_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.stroke_style = canvas_data::Pattern::Raqote(pattern); + state.stroke_style = pattern; } } fn set_global_composition( &mut self, op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, + state: &mut CanvasPaintState<'_, Self>, ) { - state.draw_options.as_raqote_mut().blend_mode = op.to_raqote_style(); - } - - fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget> { - Box::new(raqote::DrawTarget::new( - size.width as i32, - size.height as i32, - )) + state.draw_options.blend_mode = op.to_raqote_style(); } - fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> { - CanvasPaintState::default() + fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget { + raqote::DrawTarget::new(size.width as i32, size.height as i32) } -} -impl Default for CanvasPaintState<'_> { - fn default() -> Self { + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { - draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()), - fill_style: canvas_data::Pattern::Raqote(pattern.clone()), - stroke_style: canvas_data::Pattern::Raqote(pattern), - stroke_opts: StrokeOptions::Raqote(Default::default()), + draw_options: raqote::DrawOptions::new(), + fill_style: pattern.clone(), + stroke_style: pattern, + stroke_opts: Default::default(), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, - shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), + shadow_color: raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0), font_style: None, text_align: TextAlign::default(), text_baseline: TextBaseline::default(), + _backend: std::marker::PhantomData, } } } @@ -230,136 +234,122 @@ impl Repetition { } } -impl canvas_data::Pattern<'_> { - pub fn source(&self) -> raqote::Source { +pub fn source<'a>(pattern: &Pattern<'a>) -> raqote::Source<'a> { + match pattern { + Pattern::Color(a, r, g, b) => raqote::Source::Solid( + raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), + ), + Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( + pattern.gradient.clone(), + pattern.start, + pattern.end, + raqote::Spread::Pad, + ), + Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( + pattern.gradient.clone(), + pattern.center1, + pattern.radius1, + pattern.center2, + pattern.radius2, + raqote::Spread::Pad, + ), + Pattern::Surface(pattern) => raqote::Source::Image( + pattern.image, + pattern.extend, + pattern.filter, + pattern.transform, + ), + } +} + +impl PatternHelpers for Pattern<'_> { + fn is_zero_size_gradient(&self) -> bool { match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::Color(a, r, g, b) => raqote::Source::Solid( - raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), - ), - Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( - pattern.gradient.clone(), - pattern.start, - pattern.end, - raqote::Spread::Pad, - ), - Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( - pattern.gradient.clone(), - pattern.center1, - pattern.radius1, - pattern.center2, - pattern.radius2, - raqote::Spread::Pad, - ), - Pattern::Surface(pattern) => raqote::Source::Image( - pattern.image, - pattern.extend, - pattern.filter, - pattern.transform, - ), + Pattern::RadialGradient(pattern) => { + let centers_equal = pattern.center1 == pattern.center2; + let radii_equal = pattern.radius1 == pattern.radius2; + (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() }, + Pattern::LinearGradient(pattern) => { + (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() + }, + Pattern::Color(..) | Pattern::Surface(..) => false, } } - pub fn is_zero_size_gradient(&self) -> bool { + + fn draw_rect(&self, rect: &Rect<f32>) -> Rect<f32> { match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::RadialGradient(pattern) => { - let centers_equal = pattern.center1 == pattern.center2; - let radii_equal = pattern.radius1 == pattern.radius2; - (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() - }, - Pattern::LinearGradient(pattern) => { - (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() - }, - Pattern::Color(..) | Pattern::Surface(..) => false, + Pattern::Surface(pattern) => { + let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); + let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); + + match pattern.repetition() { + Repetition::NoRepeat => { + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatX => { + draw_rect.size.width = rect.size.width; + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatY => { + draw_rect.size.height = rect.size.height; + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + }, + Repetition::Repeat => { + draw_rect = *rect; + }, + } + + draw_rect }, + Pattern::Color(..) | Pattern::LinearGradient(..) | Pattern::RadialGradient(..) => *rect, } } } -impl StrokeOptions { - pub fn set_line_width(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.width = _val, - } - } - pub fn set_miter_limit(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.miter_limit = _val, - } +impl StrokeOptionsHelpers for raqote::StrokeStyle { + fn set_line_width(&mut self, _val: f32) { + self.width = _val; } - pub fn set_line_join(&mut self, val: LineJoinStyle) { - match self { - StrokeOptions::Raqote(options) => options.join = val.to_raqote_style(), - } + fn set_miter_limit(&mut self, _val: f32) { + self.miter_limit = _val; } - pub fn set_line_cap(&mut self, val: LineCapStyle) { - match self { - StrokeOptions::Raqote(options) => options.cap = val.to_raqote_style(), - } + fn set_line_join(&mut self, val: LineJoinStyle) { + self.join = val.to_raqote_style(); } - pub fn set_line_dash(&mut self, items: Vec<f32>) { - match self { - StrokeOptions::Raqote(options) => options.dash_array = items, - } + fn set_line_cap(&mut self, val: LineCapStyle) { + self.cap = val.to_raqote_style(); } - pub fn set_line_dash_offset(&mut self, offset: f32) { - match self { - StrokeOptions::Raqote(options) => options.dash_offset = offset, - } + fn set_line_dash(&mut self, items: Vec<f32>) { + self.dash_array = items; } - pub fn as_raqote(&self) -> &raqote::StrokeStyle { - match self { - StrokeOptions::Raqote(options) => options, - } + fn set_line_dash_offset(&mut self, offset: f32) { + self.dash_offset = offset; } } -impl DrawOptions { - pub fn set_alpha(&mut self, val: f32) { - match self { - DrawOptions::Raqote(draw_options) => draw_options.alpha = val, - } - } - pub fn as_raqote(&self) -> &raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } - } - fn as_raqote_mut(&mut self) -> &mut raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } +impl DrawOptionsHelpers for raqote::DrawOptions { + fn set_alpha(&mut self, val: f32) { + self.alpha = val; } } -impl Path { - pub fn transformed_copy_to_builder( - &self, - transform: &Transform2D<f32>, - ) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone().transform(transform), - )))) +impl PathHelpers<RaqoteBackend> for raqote::Path { + fn transformed_copy_to_builder(&self, transform: &Transform2D<f32>) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from( + self.clone().transform(transform), + ))) } - pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool { - self.as_raqote() - .clone() + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool { + self.clone() .transform(path_transform) .contains_point(0.1, x as f32, y as f32) } - pub fn copy_to_builder(&self) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone(), - )))) - } - - pub fn as_raqote(&self) -> &raqote::Path { - match self { - Path::Raqote(p) => p, - } + fn copy_to_builder(&self) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from(self.clone()))) } } @@ -373,7 +363,7 @@ fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote: stops } -impl GenericDrawTarget for raqote::DrawTarget { +impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget { fn clear_rect(&mut self, rect: &Rect<f32>) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -385,59 +375,47 @@ impl GenericDrawTarget for raqote::DrawTarget { let mut options = raqote::DrawOptions::new(); options.blend_mode = raqote::BlendMode::Clear; let pattern = Pattern::Color(0, 0, 0, 0); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - &DrawOptions::Raqote(options), - ); + <Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), pattern, &options); } #[allow(unsafe_code)] fn copy_surface( &mut self, - surface: SourceSurface, + surface: <RaqoteBackend as Backend>::SourceSurface, source: Rect<i32>, destination: Point2D<i32>, ) { let mut dt = raqote::DrawTarget::new(source.size.width, source.size.height); - let data = surface.as_raqote(); + let data = surface; let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }; dt.get_data_mut().copy_from_slice(s); raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } - // TODO(pylbrecht) - // Somehow a duplicate of `create_gradient_stops()` with different types. - // It feels cumbersome to convert GradientStop back and forth just to use - // `create_gradient_stops()`, so I'll leave this here for now. - fn create_gradient_stops(&self, gradient_stops: Vec<GradientStop>) -> GradientStops { - let mut stops = gradient_stops - .into_iter() - .map(|item| *item.as_raqote()) - .collect::<Vec<raqote::GradientStop>>(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - GradientStops::Raqote(stops) - } - fn create_path_builder(&self) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder::new()) + fn create_path_builder(&self) -> <RaqoteBackend as Backend>::PathBuilder { + PathBuilder::new() } - fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Box<dyn GenericDrawTarget> { - Box::new(raqote::DrawTarget::new(size.width, size.height)) + fn create_similar_draw_target( + &self, + size: &Size2D<i32>, + ) -> <RaqoteBackend as Backend>::DrawTarget { + raqote::DrawTarget::new(size.width, size.height) } - fn create_source_surface_from_data(&self, data: &[u8]) -> Option<SourceSurface> { - Some(SourceSurface::Raqote(data.to_vec())) + fn create_source_surface_from_data( + &self, + data: &[u8], + ) -> Option<<RaqoteBackend as Backend>::SourceSurface> { + Some(data.to_vec()) } #[allow(unsafe_code)] fn draw_surface( &mut self, - surface: SourceSurface, + surface: <RaqoteBackend as Backend>::SourceSurface, dest: Rect<f64>, source: Rect<f64>, filter: Filter, - draw_options: &DrawOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { - let surface_data = surface.as_raqote(); + let surface_data = surface; let image = raqote::Image { width: source.size.width as i32, height: source.size.height as i32, @@ -470,33 +448,29 @@ impl GenericDrawTarget for raqote::DrawTarget { dest.size.height as f32, ); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - draw_options, - ); + <Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), pattern, draw_options); } fn draw_surface_with_shadow( &self, - _surface: SourceSurface, + _surface: <RaqoteBackend as Backend>::SourceSurface, _dest: &Point2D<f32>, - _color: &Color, + _color: &<RaqoteBackend as Backend>::Color, _offset: &Vector2D<f32>, _sigma: f32, - _operator: CompositionOp, + _operator: <RaqoteBackend as Backend>::CompositionOp, ) { warn!("no support for drawing shadows"); } - fn fill(&mut self, path: &Path, pattern: canvas_data::Pattern, draw_options: &DrawOptions) { - match draw_options.as_raqote().blend_mode { + fn fill( + &mut self, + path: &<RaqoteBackend as Backend>::Path, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, + ) { + match draw_options.blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::Clear | raqote::BlendMode::SrcAtop | @@ -505,26 +479,19 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | raqote::BlendMode::DstIn | raqote::BlendMode::DstAtop => { - let mut options = *draw_options.as_raqote(); + let mut options = *draw_options; self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; - self.fill(path.as_raqote(), &pattern.source(), &options); + self.fill(path, &source(&pattern), &options); self.pop_layer(); }, - _ => warn!( - "unrecognized blend mode: {:?}", - draw_options.as_raqote().blend_mode - ), + _ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode), } } @@ -532,8 +499,8 @@ impl GenericDrawTarget for raqote::DrawTarget { &mut self, text_runs: Vec<TextRun>, start: Point2D<f32>, - pattern: &canvas_data::Pattern, - draw_options: &DrawOptions, + pattern: &<RaqoteBackend as Backend>::Pattern<'_>, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut advance = 0.; for run in text_runs.iter() { @@ -581,8 +548,8 @@ impl GenericDrawTarget for raqote::DrawTarget { run.font.descriptor.pt_size.to_f32_px(), &ids, &positions, - &pattern.source(), - draw_options.as_raqote(), + &source(pattern), + draw_options, ); }) } @@ -591,8 +558,8 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill_rect( &mut self, rect: &Rect<f32>, - pattern: canvas_data::Pattern, - draw_options: Option<&DrawOptions>, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + draw_options: Option<&<RaqoteBackend as Backend>::DrawOptions>, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -602,16 +569,16 @@ impl GenericDrawTarget for raqote::DrawTarget { rect.size.height, ); let draw_options = if let Some(options) = draw_options { - *options.as_raqote() + *options } else { raqote::DrawOptions::new() }; - GenericDrawTarget::fill( + <Self as GenericDrawTarget<RaqoteBackend>>::fill( self, - &Path::Raqote(pb.finish()), + &pb.finish(), pattern, - &DrawOptions::Raqote(draw_options), + &draw_options, ); } fn get_size(&self) -> Size2D<i32> { @@ -623,41 +590,36 @@ impl GenericDrawTarget for raqote::DrawTarget { fn pop_clip(&mut self) { self.pop_clip(); } - fn push_clip(&mut self, path: &Path) { - self.push_clip(path.as_raqote()); + fn push_clip(&mut self, path: &<RaqoteBackend as Backend>::Path) { + self.push_clip(path); } fn set_transform(&mut self, matrix: &Transform2D<f32>) { self.set_transform(matrix); } - fn snapshot(&self) -> SourceSurface { - SourceSurface::Raqote(self.snapshot_data().to_vec()) + fn surface(&self) -> <RaqoteBackend as Backend>::SourceSurface { + self.bytes().to_vec() } fn stroke( &mut self, - path: &Path, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + path: &<RaqoteBackend as Backend>::Path, + pattern: Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { - self.stroke( - path.as_raqote(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), - ); + self.stroke(path, &source(&pattern), stroke_options, draw_options); } fn stroke_line( &mut self, start: Point2D<f32>, end: Point2D<f32>, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.move_to(start.x, start.y); pb.line_to(end.x, end.y); - let mut stroke_options = stroke_options.as_raqote().clone(); + let mut stroke_options = stroke_options.clone(); let cap = match stroke_options.join { raqote::LineJoin::Round => raqote::LineCap::Round, _ => raqote::LineCap::Butt, @@ -666,17 +628,17 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), + &source(&pattern), &stroke_options, - draw_options.as_raqote(), + draw_options, ); } fn stroke_rect( &mut self, rect: &Rect<f32>, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -688,13 +650,13 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), + &source(&pattern), + stroke_options, + draw_options, ); } #[allow(unsafe_code)] - fn snapshot_data(&self) -> &[u8] { + fn bytes(&self) -> &[u8] { let v = self.get_data(); unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) } } @@ -709,7 +671,7 @@ impl Filter { } } -struct PathBuilder(Option<raqote::PathBuilder>); +pub(crate) struct PathBuilder(Option<raqote::PathBuilder>); impl PathBuilder { fn new() -> PathBuilder { @@ -717,7 +679,7 @@ impl PathBuilder { } } -impl GenericPathBuilder for PathBuilder { +impl GenericPathBuilder<RaqoteBackend> for PathBuilder { fn arc( &mut self, origin: Point2D<f32>, @@ -726,7 +688,8 @@ impl GenericPathBuilder for PathBuilder { end_angle: f32, anticlockwise: bool, ) { - self.ellipse( + <PathBuilder as GenericPathBuilder<RaqoteBackend>>::ellipse( + self, origin, radius, radius, @@ -736,6 +699,7 @@ impl GenericPathBuilder for PathBuilder { anticlockwise, ); } + fn bezier_curve_to( &mut self, control_point1: &Point2D<f32>, @@ -751,66 +715,10 @@ impl GenericPathBuilder for PathBuilder { control_point3.y, ); } + fn close(&mut self) { self.0.as_mut().unwrap().close(); } - fn ellipse( - &mut self, - origin: Point2D<f32>, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ) { - let mut start = Angle::radians(start_angle); - let mut end = Angle::radians(end_angle); - - // Wrap angles mod 2 * PI if necessary - if !anticlockwise && start > end + Angle::two_pi() || - anticlockwise && end > start + Angle::two_pi() - { - start = start.positive(); - end = end.positive(); - } - - // Calculate the total arc we're going to sweep. - let sweep = match anticlockwise { - true => { - if end - start == Angle::two_pi() { - -Angle::two_pi() - } else if end > start { - -(Angle::two_pi() - (end - start)) - } else { - -(start - end) - } - }, - false => { - if start - end == Angle::two_pi() { - Angle::two_pi() - } else if start > end { - Angle::two_pi() - (start - end) - } else { - end - start - } - }, - }; - - let arc: Arc<f32> = Arc { - center: origin, - radii: Vector2D::new(radius_x, radius_y), - start_angle: start, - sweep_angle: sweep, - x_rotation: Angle::radians(rotation_angle), - }; - - self.line_to(arc.from()); - - arc.for_each_quadratic_bezier(&mut |q| { - self.quadratic_curve_to(&q.ctrl, &q.to); - }); - } fn svg_arc( &mut self, @@ -840,9 +748,9 @@ impl GenericPathBuilder for PathBuilder { fn get_current_point(&mut self) -> Option<Point2D<f32>> { let path = self.finish(); - self.0 = Some(path.as_raqote().clone().into()); + self.0 = Some(path.clone().into()); - path.as_raqote().ops.iter().last().and_then(|op| match op { + path.ops.iter().last().and_then(|op| match op { PathOp::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)), PathOp::CubicTo(_, _, point) => Some(Point2D::new(point.x, point.y)), PathOp::QuadTo(_, point) => Some(Point2D::new(point.x, point.y)), @@ -864,8 +772,8 @@ impl GenericPathBuilder for PathBuilder { end_point.y, ); } - fn finish(&mut self) -> Path { - Path::Raqote(self.0.take().unwrap().finish()) + fn finish(&mut self) -> raqote::Path { + self.0.take().unwrap().finish() } } @@ -977,14 +885,6 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle { } } -impl Color { - fn as_raqote(&self) -> &raqote::SolidSource { - match self { - Color::Raqote(s) => s, - } - } -} - impl ToRaqoteStyle for AbsoluteColor { type Target = raqote::SolidSource; @@ -1054,19 +954,3 @@ impl ToRaqoteStyle for CompositionStyle { } } } - -impl SourceSurface { - fn as_raqote(&self) -> &Vec<u8> { - match self { - SourceSurface::Raqote(s) => s, - } - } -} - -impl GradientStop { - fn as_raqote(&self) -> &raqote::GradientStop { - match self { - GradientStop::Raqote(s) => s, - } - } -} diff --git a/components/canvas/webgl_limits.rs b/components/canvas/webgl_limits.rs deleted file mode 100644 index f683b6efff6..00000000000 --- a/components/canvas/webgl_limits.rs +++ /dev/null @@ -1,259 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use canvas_traits::webgl::{GLLimits, WebGLVersion}; -use glow::{self as gl, Context as Gl, HasContext}; -type GLenum = u32; - -pub trait GLLimitsDetect { - fn detect(gl: &Gl, webgl_version: WebGLVersion) -> Self; -} - -impl GLLimitsDetect for GLLimits { - fn detect(gl: &Gl, webgl_version: WebGLVersion) -> GLLimits { - let max_vertex_attribs = gl.get_integer(gl::MAX_VERTEX_ATTRIBS); - let max_tex_size = gl.get_integer(gl::MAX_TEXTURE_SIZE); - let max_cube_map_tex_size = gl.get_integer(gl::MAX_CUBE_MAP_TEXTURE_SIZE); - let max_combined_texture_image_units = gl.get_integer(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS); - let max_renderbuffer_size = gl.get_integer(gl::MAX_RENDERBUFFER_SIZE); - let max_texture_image_units = gl.get_integer(gl::MAX_TEXTURE_IMAGE_UNITS); - let max_vertex_texture_image_units = gl.get_integer(gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS); - - // TODO: better value for this? - let max_client_wait_timeout_webgl = std::time::Duration::new(1, 0); - - // Based on: - // https://searchfox.org/mozilla-central/rev/5a744713370ec47969595e369fd5125f123e6d24/dom/canvas/WebGLContextValidate.cpp#523-558 - let ( - max_fragment_uniform_vectors, - max_varying_vectors, - max_vertex_uniform_vectors, - max_vertex_output_vectors, - max_fragment_input_vectors, - ); - if gl.version().is_embedded { - max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_VECTORS); - max_varying_vectors = gl.get_integer(gl::MAX_VARYING_VECTORS); - max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_VECTORS); - max_vertex_output_vectors = gl - .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_varying_vectors); - max_fragment_input_vectors = gl - .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_vertex_output_vectors); - } else { - max_fragment_uniform_vectors = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4; - max_vertex_uniform_vectors = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS) / 4; - - max_fragment_input_vectors = gl - .try_get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS) - .or_else(|| gl.try_get_integer(gl::MAX_VARYING_COMPONENTS)) - .map(|c| c / 4) - .unwrap_or_else(|| gl.get_integer(gl::MAX_VARYING_VECTORS)); - max_vertex_output_vectors = gl - .try_get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS) - .map(|c| c / 4) - .unwrap_or(max_fragment_input_vectors); - max_varying_vectors = max_vertex_output_vectors - .min(max_fragment_input_vectors) - .max(4); - }; - - let ( - max_uniform_block_size, - max_uniform_buffer_bindings, - min_program_texel_offset, - max_program_texel_offset, - max_transform_feedback_separate_attribs, - max_draw_buffers, - max_color_attachments, - max_combined_uniform_blocks, - max_combined_vertex_uniform_components, - max_combined_fragment_uniform_components, - max_vertex_uniform_blocks, - max_vertex_uniform_components, - max_fragment_uniform_blocks, - max_fragment_uniform_components, - max_3d_texture_size, - max_array_texture_layers, - uniform_buffer_offset_alignment, - max_element_index, - max_elements_indices, - max_elements_vertices, - max_fragment_input_components, - max_samples, - max_server_wait_timeout, - max_texture_lod_bias, - max_varying_components, - max_vertex_output_components, - ); - if webgl_version == WebGLVersion::WebGL2 { - max_uniform_block_size = gl.get_integer64(gl::MAX_UNIFORM_BLOCK_SIZE); - max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS); - min_program_texel_offset = gl.get_signed_integer(gl::MIN_PROGRAM_TEXEL_OFFSET); - max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET); - max_transform_feedback_separate_attribs = - gl.get_integer(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); - max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS); - max_draw_buffers = gl - .get_integer(gl::MAX_DRAW_BUFFERS) - .min(max_color_attachments); - max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS); - max_combined_vertex_uniform_components = - gl.get_integer64(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); - max_combined_fragment_uniform_components = - gl.get_integer64(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); - max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS); - max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS); - max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS); - max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS); - uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT); - max_3d_texture_size = gl.get_integer(gl::MAX_3D_TEXTURE_SIZE); - max_array_texture_layers = gl.get_integer(gl::MAX_ARRAY_TEXTURE_LAYERS); - max_element_index = gl - .try_get_integer64(gl::MAX_ELEMENT_INDEX) - .unwrap_or(u32::MAX as u64); // requires GL 4.3 - max_elements_indices = gl.get_integer(gl::MAX_ELEMENTS_INDICES); - max_elements_vertices = gl.get_integer(gl::MAX_ELEMENTS_VERTICES); - max_fragment_input_components = gl.get_integer(gl::MAX_FRAGMENT_INPUT_COMPONENTS); - max_samples = gl.get_integer(gl::MAX_SAMPLES); - max_server_wait_timeout = - std::time::Duration::from_nanos(gl.get_integer64(gl::MAX_SERVER_WAIT_TIMEOUT)); - max_texture_lod_bias = gl.get_float(gl::MAX_TEXTURE_LOD_BIAS); - max_varying_components = gl.try_get_integer(gl::MAX_VARYING_COMPONENTS).unwrap_or( - // macOS Core Profile is buggy. The spec says this value is 4 * MAX_VARYING_VECTORS. - max_varying_vectors * 4, - ); - max_vertex_output_components = gl.get_integer(gl::MAX_VERTEX_OUTPUT_COMPONENTS); - } else { - max_uniform_block_size = 0; - max_uniform_buffer_bindings = 0; - min_program_texel_offset = 0; - max_program_texel_offset = 0; - max_transform_feedback_separate_attribs = 0; - max_color_attachments = 1; - max_draw_buffers = 1; - max_combined_uniform_blocks = 0; - max_combined_vertex_uniform_components = 0; - max_combined_fragment_uniform_components = 0; - max_vertex_uniform_blocks = 0; - max_vertex_uniform_components = 0; - max_fragment_uniform_blocks = 0; - max_fragment_uniform_components = 0; - uniform_buffer_offset_alignment = 0; - max_3d_texture_size = 0; - max_array_texture_layers = 0; - max_element_index = 0; - max_elements_indices = 0; - max_elements_vertices = 0; - max_fragment_input_components = 0; - max_samples = 0; - max_server_wait_timeout = std::time::Duration::default(); - max_texture_lod_bias = 0.0; - max_varying_components = 0; - max_vertex_output_components = 0; - } - - GLLimits { - max_vertex_attribs, - max_tex_size, - max_cube_map_tex_size, - max_combined_texture_image_units, - max_fragment_uniform_vectors, - max_renderbuffer_size, - max_texture_image_units, - max_varying_vectors, - max_vertex_texture_image_units, - max_vertex_uniform_vectors, - max_client_wait_timeout_webgl, - max_transform_feedback_separate_attribs, - max_vertex_output_vectors, - max_fragment_input_vectors, - max_uniform_buffer_bindings, - min_program_texel_offset, - max_program_texel_offset, - max_color_attachments, - max_draw_buffers, - max_uniform_block_size, - max_combined_uniform_blocks, - max_combined_vertex_uniform_components, - max_combined_fragment_uniform_components, - max_vertex_uniform_blocks, - max_vertex_uniform_components, - max_fragment_uniform_blocks, - max_fragment_uniform_components, - max_3d_texture_size, - max_array_texture_layers, - uniform_buffer_offset_alignment, - max_element_index, - max_elements_indices, - max_elements_vertices, - max_fragment_input_components, - max_samples, - max_server_wait_timeout, - max_texture_lod_bias, - max_varying_components, - max_vertex_output_components, - } - } -} - -trait GLExt { - fn try_get_integer(self, parameter: GLenum) -> Option<u32>; - fn try_get_integer64(self, parameter: GLenum) -> Option<u64>; - fn try_get_signed_integer(self, parameter: GLenum) -> Option<i32>; - fn try_get_float(self, parameter: GLenum) -> Option<f32>; - fn get_integer(self, parameter: GLenum) -> u32; - fn get_integer64(self, parameter: GLenum) -> u64; - fn get_signed_integer(self, parameter: GLenum) -> i32; - fn get_float(self, parameter: GLenum) -> f32; -} - -macro_rules! create_fun { - ($tryer:ident, $getter:ident, $gltype:ty, $glcall:ident, $rstype:ty) => { - #[allow(unsafe_code)] - fn $tryer(self, parameter: GLenum) -> Option<$rstype> { - let mut value = [<$gltype>::default()]; - unsafe { - self.$glcall(parameter, &mut value); - } - if unsafe { self.get_error() } != gl::NO_ERROR { - None - } else { - Some(value[0] as $rstype) - } - } - - fn $getter(self, parameter: GLenum) -> $rstype { - self.$tryer(parameter).unwrap() - } - }; -} - -impl GLExt for &Gl { - create_fun!( - try_get_integer, - get_integer, - i32, - get_parameter_i32_slice, - u32 - ); - create_fun!( - try_get_integer64, - get_integer64, - i64, - get_parameter_i64_slice, - u64 - ); - create_fun!( - try_get_signed_integer, - get_signed_integer, - i32, - get_parameter_i32_slice, - i32 - ); - create_fun!(try_get_float, get_float, f32, get_parameter_f32_slice, f32); -} diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs deleted file mode 100644 index 566da2c58c8..00000000000 --- a/components/canvas/webgl_mode/inprocess.rs +++ /dev/null @@ -1,147 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::default::Default; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; - -use canvas_traits::webgl::{GlType, WebGLContextId, WebGLMsg, WebGLThreads, webgl_channel}; -use compositing_traits::rendering_context::RenderingContext; -use compositing_traits::{ - WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource, -}; -use euclid::default::Size2D; -use fnv::FnvHashMap; -use log::debug; -use surfman::chains::{SwapChainAPI, SwapChains, SwapChainsAPI}; -use surfman::{Device, SurfaceTexture}; -use webrender::RenderApiSender; -use webrender_api::DocumentId; -#[cfg(feature = "webxr")] -use webxr::SurfmanGL as WebXRSurfman; -#[cfg(feature = "webxr")] -use webxr_api::LayerGrandManager as WebXRLayerGrandManager; - -use crate::webgl_thread::{WebGLThread, WebGLThreadInit}; - -pub struct WebGLComm { - pub webgl_threads: WebGLThreads, - pub image_handler: Box<dyn WebrenderExternalImageApi>, - #[cfg(feature = "webxr")] - pub webxr_layer_grand_manager: WebXRLayerGrandManager<WebXRSurfman>, -} - -impl WebGLComm { - /// Creates a new `WebGLComm` object. - pub fn new( - rendering_context: Rc<dyn RenderingContext>, - webrender_api_sender: RenderApiSender, - webrender_doc: DocumentId, - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - api_type: GlType, - ) -> WebGLComm { - debug!("WebGLThreads::new()"); - let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap(); - let webrender_swap_chains = SwapChains::new(); - #[cfg(feature = "webxr")] - let webxr_init = crate::webxr::WebXRBridgeInit::new(sender.clone()); - #[cfg(feature = "webxr")] - let webxr_layer_grand_manager = webxr_init.layer_grand_manager(); - let connection = rendering_context - .connection() - .expect("Failed to get connection"); - let adapter = connection - .create_adapter() - .expect("Failed to create adapter"); - - // This implementation creates a single `WebGLThread` for all the pipelines. - let init = WebGLThreadInit { - webrender_api_sender, - webrender_doc, - external_images, - sender: sender.clone(), - receiver, - webrender_swap_chains: webrender_swap_chains.clone(), - connection, - adapter, - api_type, - #[cfg(feature = "webxr")] - webxr_init, - }; - - let external = WebGLExternalImages::new(rendering_context, webrender_swap_chains); - - WebGLThread::run_on_own_thread(init); - - WebGLComm { - webgl_threads: WebGLThreads(sender), - image_handler: Box::new(external), - #[cfg(feature = "webxr")] - webxr_layer_grand_manager, - } - } -} - -/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. -struct WebGLExternalImages { - rendering_context: Rc<dyn RenderingContext>, - swap_chains: SwapChains<WebGLContextId, Device>, - locked_front_buffers: FnvHashMap<WebGLContextId, SurfaceTexture>, -} - -impl WebGLExternalImages { - fn new( - rendering_context: Rc<dyn RenderingContext>, - swap_chains: SwapChains<WebGLContextId, Device>, - ) -> Self { - Self { - rendering_context, - swap_chains, - locked_front_buffers: FnvHashMap::default(), - } - } - - fn lock_swap_chain(&mut self, id: WebGLContextId) -> Option<(u32, Size2D<i32>)> { - debug!("... locking chain {:?}", id); - let front_buffer = self.swap_chains.get(id)?.take_surface()?; - - if let Some((surface_texture, gl_texture, size)) = - self.rendering_context.create_texture(front_buffer) - { - self.locked_front_buffers.insert(id, surface_texture); - - Some((gl_texture, size)) - } else { - None - } - } - - fn unlock_swap_chain(&mut self, id: WebGLContextId) -> Option<()> { - debug!("... unlocked chain {:?}", id); - let locked_front_buffer = self.locked_front_buffers.remove(&id)?; - if let Some(locked_front_buffer) = - self.rendering_context.destroy_texture(locked_front_buffer) - { - self.swap_chains - .get(id)? - .recycle_surface(locked_front_buffer); - Some(()) - } else { - None - } - } -} - -impl WebrenderExternalImageApi for WebGLExternalImages { - fn lock(&mut self, id: u64) -> (WebrenderImageSource, Size2D<i32>) { - let id = WebGLContextId(id); - let (texture_id, size) = self.lock_swap_chain(id).unwrap_or_default(); - (WebrenderImageSource::TextureHandle(texture_id), size) - } - - fn unlock(&mut self, id: u64) { - let id = WebGLContextId(id); - self.unlock_swap_chain(id); - } -} diff --git a/components/canvas/webgl_mode/mod.rs b/components/canvas/webgl_mode/mod.rs deleted file mode 100644 index 8bc74f6e244..00000000000 --- a/components/canvas/webgl_mode/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -mod inprocess; - -pub use self::inprocess::WebGLComm; diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs deleted file mode 100644 index b1ac2b2d3c4..00000000000 --- a/components/canvas/webgl_thread.rs +++ /dev/null @@ -1,3250 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#![allow(unsafe_code)] -use std::borrow::Cow; -use std::num::NonZeroU32; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; -use std::{slice, thread}; - -use bitflags::bitflags; -use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; -use canvas_traits::webgl; -#[cfg(feature = "webxr")] -use canvas_traits::webgl::WebXRCommand; -use canvas_traits::webgl::{ - ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment, - GLContextAttributes, GLLimits, GlType, InternalFormatIntVec, ProgramLinkInfo, TexDataType, - TexFormat, WebGLBufferId, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId, - WebGLCreateContextResult, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLMsg, - WebGLMsgSender, WebGLProgramId, WebGLQueryId, WebGLReceiver, WebGLRenderbufferId, - WebGLSLVersion, WebGLSamplerId, WebGLSender, WebGLShaderId, WebGLSyncId, WebGLTextureId, - WebGLVersion, WebGLVertexArrayId, YAxisTreatment, -}; -use compositing_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType}; -use euclid::default::Size2D; -use fnv::FnvHashMap; -use glow::{ - self as gl, ActiveTransformFeedback, Context as Gl, HasContext, NativeTransformFeedback, - NativeUniformLocation, NativeVertexArray, PixelUnpackData, ShaderPrecisionFormat, - bytes_per_type, components_per_format, -}; -use half::f16; -use ipc_channel::ipc::IpcSharedMemory; -use log::{debug, error, trace, warn}; -use pixels::{self, PixelFormat, unmultiply_inplace}; -use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI}; -use surfman::{ - self, Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device, - GLVersion, SurfaceAccess, SurfaceInfo, SurfaceType, -}; -use webrender::{RenderApi, RenderApiSender, Transaction}; -use webrender_api::units::DeviceIntSize; -use webrender_api::{ - DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, - ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey, -}; - -use crate::webgl_limits::GLLimitsDetect; -#[cfg(feature = "webxr")] -use crate::webxr::{WebXRBridge, WebXRBridgeContexts, WebXRBridgeInit}; - -type GLint = i32; - -fn native_uniform_location(location: i32) -> Option<NativeUniformLocation> { - location.try_into().ok().map(NativeUniformLocation) -} - -pub(crate) struct GLContextData { - pub(crate) ctx: Context, - pub(crate) gl: Rc<glow::Context>, - state: GLState, - attributes: GLContextAttributes, -} - -#[derive(Debug)] -pub struct GLState { - _webgl_version: WebGLVersion, - _gl_version: GLVersion, - requested_flags: ContextAttributeFlags, - // This is the WebGL view of the color mask - // The GL view may be different: if the GL context supports alpha - // but the WebGL context doesn't, then color_write_mask.3 might be true - // but the GL color write mask is false. - color_write_mask: [bool; 4], - clear_color: (f32, f32, f32, f32), - scissor_test_enabled: bool, - // The WebGL view of the stencil write mask (see comment re `color_write_mask`) - stencil_write_mask: (u32, u32), - stencil_test_enabled: bool, - stencil_clear_value: i32, - // The WebGL view of the depth write mask (see comment re `color_write_mask`) - depth_write_mask: bool, - depth_test_enabled: bool, - depth_clear_value: f64, - // True when the default framebuffer is bound to DRAW_FRAMEBUFFER - drawing_to_default_framebuffer: bool, - default_vao: Option<NativeVertexArray>, -} - -impl GLState { - // Are we faking having no alpha / depth / stencil? - fn fake_no_alpha(&self) -> bool { - self.drawing_to_default_framebuffer & - !self.requested_flags.contains(ContextAttributeFlags::ALPHA) - } - - fn fake_no_depth(&self) -> bool { - self.drawing_to_default_framebuffer & - !self.requested_flags.contains(ContextAttributeFlags::DEPTH) - } - - fn fake_no_stencil(&self) -> bool { - self.drawing_to_default_framebuffer & - !self - .requested_flags - .contains(ContextAttributeFlags::STENCIL) - } - - // We maintain invariants between the GLState object and the GL state. - fn restore_invariant(&self, gl: &Gl) { - self.restore_clear_color_invariant(gl); - self.restore_scissor_invariant(gl); - self.restore_alpha_invariant(gl); - self.restore_depth_invariant(gl); - self.restore_stencil_invariant(gl); - } - - fn restore_clear_color_invariant(&self, gl: &Gl) { - let (r, g, b, a) = self.clear_color; - unsafe { gl.clear_color(r, g, b, a) }; - } - - fn restore_scissor_invariant(&self, gl: &Gl) { - if self.scissor_test_enabled { - unsafe { gl.enable(gl::SCISSOR_TEST) }; - } else { - unsafe { gl.disable(gl::SCISSOR_TEST) }; - } - } - - fn restore_alpha_invariant(&self, gl: &Gl) { - let [r, g, b, a] = self.color_write_mask; - if self.fake_no_alpha() { - unsafe { gl.color_mask(r, g, b, false) }; - } else { - unsafe { gl.color_mask(r, g, b, a) }; - } - } - - fn restore_depth_invariant(&self, gl: &Gl) { - unsafe { - if self.fake_no_depth() { - gl.depth_mask(false); - gl.disable(gl::DEPTH_TEST); - } else { - gl.depth_mask(self.depth_write_mask); - if self.depth_test_enabled { - gl.enable(gl::DEPTH_TEST); - } else { - gl.disable(gl::DEPTH_TEST); - } - } - } - } - - fn restore_stencil_invariant(&self, gl: &Gl) { - unsafe { - if self.fake_no_stencil() { - gl.stencil_mask(0); - gl.disable(gl::STENCIL_TEST); - } else { - let (f, b) = self.stencil_write_mask; - gl.stencil_mask_separate(gl::FRONT, f); - gl.stencil_mask_separate(gl::BACK, b); - if self.stencil_test_enabled { - gl.enable(gl::STENCIL_TEST); - } else { - gl.disable(gl::STENCIL_TEST); - } - } - } - } -} - -impl Default for GLState { - fn default() -> GLState { - GLState { - _gl_version: GLVersion { major: 1, minor: 0 }, - _webgl_version: WebGLVersion::WebGL1, - requested_flags: ContextAttributeFlags::empty(), - color_write_mask: [true, true, true, true], - clear_color: (0., 0., 0., 0.), - scissor_test_enabled: false, - // Should these be 0xFFFF_FFFF? - stencil_write_mask: (0, 0), - stencil_test_enabled: false, - stencil_clear_value: 0, - depth_write_mask: true, - depth_test_enabled: false, - depth_clear_value: 1., - default_vao: None, - drawing_to_default_framebuffer: true, - } - } -} - -/// A WebGLThread manages the life cycle and message multiplexing of -/// a set of WebGLContexts living in the same thread. -pub(crate) struct WebGLThread { - /// The GPU device. - device: Device, - /// Channel used to generate/update or delete `ImageKey`s. - webrender_api: RenderApi, - webrender_doc: DocumentId, - /// Map of live WebGLContexts. - contexts: FnvHashMap<WebGLContextId, GLContextData>, - /// Cached information for WebGLContexts. - cached_context_info: FnvHashMap<WebGLContextId, WebGLContextInfo>, - /// Current bound context. - bound_context_id: Option<WebGLContextId>, - /// List of registered webrender external images. - /// We use it to get an unique ID for new WebGLContexts. - external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - /// The receiver that will be used for processing WebGL messages. - receiver: crossbeam_channel::Receiver<WebGLMsg>, - /// The receiver that should be used to send WebGL messages for processing. - sender: WebGLSender<WebGLMsg>, - /// The swap chains used by webrender - webrender_swap_chains: SwapChains<WebGLContextId, Device>, - /// Whether this context is a GL or GLES context. - api_type: GlType, - #[cfg(feature = "webxr")] - /// The bridge to WebXR - pub webxr_bridge: WebXRBridge, -} - -/// The data required to initialize an instance of the WebGLThread type. -pub(crate) struct WebGLThreadInit { - pub webrender_api_sender: RenderApiSender, - pub webrender_doc: DocumentId, - pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, - pub sender: WebGLSender<WebGLMsg>, - pub receiver: WebGLReceiver<WebGLMsg>, - pub webrender_swap_chains: SwapChains<WebGLContextId, Device>, - pub connection: Connection, - pub adapter: Adapter, - pub api_type: GlType, - #[cfg(feature = "webxr")] - pub webxr_init: WebXRBridgeInit, -} - -// A size at which it should be safe to create GL contexts -const SAFE_VIEWPORT_DIMS: [u32; 2] = [1024, 1024]; - -impl WebGLThread { - /// Create a new instance of WebGLThread. - pub(crate) fn new( - WebGLThreadInit { - webrender_api_sender, - webrender_doc, - external_images, - sender, - receiver, - webrender_swap_chains, - connection, - adapter, - api_type, - #[cfg(feature = "webxr")] - webxr_init, - }: WebGLThreadInit, - ) -> Self { - WebGLThread { - device: connection - .create_device(&adapter) - .expect("Couldn't open WebGL device!"), - webrender_api: webrender_api_sender.create_api(), - webrender_doc, - contexts: Default::default(), - cached_context_info: Default::default(), - bound_context_id: None, - external_images, - sender, - receiver: receiver.into_inner(), - webrender_swap_chains, - api_type, - #[cfg(feature = "webxr")] - webxr_bridge: WebXRBridge::new(webxr_init), - } - } - - /// Perform all initialization required to run an instance of WebGLThread - /// in parallel on its own dedicated thread. - pub(crate) fn run_on_own_thread(init: WebGLThreadInit) { - thread::Builder::new() - .name("WebGL".to_owned()) - .spawn(move || { - let mut data = WebGLThread::new(init); - data.process(); - }) - .expect("Thread spawning failed"); - } - - fn process(&mut self) { - let webgl_chan = WebGLChan(self.sender.clone()); - while let Ok(msg) = self.receiver.recv() { - let exit = self.handle_msg(msg, &webgl_chan); - if exit { - break; - } - } - } - - /// Handles a generic WebGLMsg message - fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool { - trace!("processing {:?}", msg); - match msg { - WebGLMsg::CreateContext(version, size, attributes, result_sender) => { - let result = self.create_webgl_context(version, size, attributes); - - result_sender - .send(result.map(|(id, limits)| { - let image_key = self - .cached_context_info - .get_mut(&id) - .expect("Where's the cached context info?") - .image_key; - - let data = Self::make_current_if_needed( - &self.device, - id, - &self.contexts, - &mut self.bound_context_id, - ) - .expect("WebGLContext not found"); - let glsl_version = Self::get_glsl_version(&data.gl); - let api_type = if data.gl.version().is_embedded { - GlType::Gles - } else { - GlType::Gl - }; - - // FIXME(nox): Should probably be done by surfman. - if api_type != GlType::Gles { - // Points sprites are enabled by default in OpenGL 3.2 core - // and in GLES. Rather than doing version detection, it does - // not hurt to enable them anyways. - - unsafe { - // XXX: Do we even need to this? - const GL_POINT_SPRITE: u32 = 0x8861; - data.gl.enable(GL_POINT_SPRITE); - let err = data.gl.get_error(); - if err != 0 { - warn!("Error enabling GL point sprites: {}", err); - } - - data.gl.enable(gl::PROGRAM_POINT_SIZE); - let err = data.gl.get_error(); - if err != 0 { - warn!("Error enabling GL program point size: {}", err); - } - } - } - - WebGLCreateContextResult { - sender: WebGLMsgSender::new(id, webgl_chan.clone()), - limits, - glsl_version, - api_type, - image_key, - } - })) - .unwrap(); - }, - WebGLMsg::ResizeContext(ctx_id, size, sender) => { - let _ = sender.send(self.resize_webgl_context(ctx_id, size)); - }, - WebGLMsg::RemoveContext(ctx_id) => { - self.remove_webgl_context(ctx_id); - }, - WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => { - self.handle_webgl_command(ctx_id, command, backtrace); - }, - WebGLMsg::WebXRCommand(_command) => { - #[cfg(feature = "webxr")] - self.handle_webxr_command(_command); - }, - WebGLMsg::SwapBuffers(swap_ids, sender, sent_time) => { - self.handle_swap_buffers(swap_ids, sender, sent_time); - }, - WebGLMsg::Exit(sender) => { - // Call remove_context functions in order to correctly delete WebRender image keys. - let context_ids: Vec<WebGLContextId> = self.contexts.keys().copied().collect(); - for id in context_ids { - self.remove_webgl_context(id); - } - - // Block on shutting-down WebRender. - self.webrender_api.shut_down(true); - if let Err(e) = sender.send(()) { - warn!("Failed to send response to WebGLMsg::Exit ({e})"); - } - return true; - }, - } - - false - } - - #[cfg(feature = "webxr")] - /// Handles a WebXR message - fn handle_webxr_command(&mut self, command: WebXRCommand) { - trace!("processing {:?}", command); - let mut contexts = WebXRBridgeContexts { - contexts: &mut self.contexts, - bound_context_id: &mut self.bound_context_id, - }; - match command { - WebXRCommand::CreateLayerManager(sender) => { - let result = self - .webxr_bridge - .create_layer_manager(&mut self.device, &mut contexts); - let _ = sender.send(result); - }, - WebXRCommand::DestroyLayerManager(manager_id) => { - self.webxr_bridge.destroy_layer_manager(manager_id); - }, - WebXRCommand::CreateLayer(manager_id, context_id, layer_init, sender) => { - let result = self.webxr_bridge.create_layer( - manager_id, - &mut self.device, - &mut contexts, - context_id, - layer_init, - ); - let _ = sender.send(result); - }, - WebXRCommand::DestroyLayer(manager_id, context_id, layer_id) => { - self.webxr_bridge.destroy_layer( - manager_id, - &mut self.device, - &mut contexts, - context_id, - layer_id, - ); - }, - WebXRCommand::BeginFrame(manager_id, layers, sender) => { - let result = self.webxr_bridge.begin_frame( - manager_id, - &mut self.device, - &mut contexts, - &layers[..], - ); - let _ = sender.send(result); - }, - WebXRCommand::EndFrame(manager_id, layers, sender) => { - let result = self.webxr_bridge.end_frame( - manager_id, - &mut self.device, - &mut contexts, - &layers[..], - ); - let _ = sender.send(result); - }, - } - } - - /// Handles a WebGLCommand for a specific WebGLContext - fn handle_webgl_command( - &mut self, - context_id: WebGLContextId, - command: WebGLCommand, - backtrace: WebGLCommandBacktrace, - ) { - if self.cached_context_info.get_mut(&context_id).is_none() { - return; - } - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ); - if let Some(data) = data { - WebGLImpl::apply( - &self.device, - &data.ctx, - &data.gl, - &mut data.state, - &data.attributes, - command, - backtrace, - ); - } - } - - /// Creates a new WebGLContext - fn create_webgl_context( - &mut self, - webgl_version: WebGLVersion, - requested_size: Size2D<u32>, - attributes: GLContextAttributes, - ) -> Result<(WebGLContextId, webgl::GLLimits), String> { - debug!( - "WebGLThread::create_webgl_context({:?}, {:?}, {:?})", - webgl_version, requested_size, attributes - ); - - // Creating a new GLContext may make the current bound context_id dirty. - // Clear it to ensure that make_current() is called in subsequent commands. - self.bound_context_id = None; - - let requested_flags = - attributes.to_surfman_context_attribute_flags(webgl_version, self.api_type); - // Some GL implementations seem to only allow famebuffers - // to have alpha, depth and stencil if their creating context does. - // WebGL requires all contexts to be able to create framebuffers with - // alpha, depth and stencil. So we always create a context with them, - // and fake not having them if requested. - let flags = requested_flags | - ContextAttributeFlags::ALPHA | - ContextAttributeFlags::DEPTH | - ContextAttributeFlags::STENCIL; - let context_attributes = &ContextAttributes { - version: webgl_version.to_surfman_version(self.api_type), - flags, - }; - - let context_descriptor = self - .device - .create_context_descriptor(context_attributes) - .map_err(|err| format!("Failed to create context descriptor: {:?}", err))?; - - let safe_size = Size2D::new( - requested_size.width.min(SAFE_VIEWPORT_DIMS[0]).max(1), - requested_size.height.min(SAFE_VIEWPORT_DIMS[1]).max(1), - ); - let surface_type = SurfaceType::Generic { - size: safe_size.to_i32(), - }; - let surface_access = self.surface_access(); - - let mut ctx = self - .device - .create_context(&context_descriptor, None) - .map_err(|err| format!("Failed to create the GL context: {:?}", err))?; - let surface = self - .device - .create_surface(&ctx, surface_access, surface_type) - .map_err(|err| format!("Failed to create the initial surface: {:?}", err))?; - self.device - .bind_surface_to_context(&mut ctx, surface) - .map_err(|err| format!("Failed to bind initial surface: {:?}", err))?; - // https://github.com/pcwalton/surfman/issues/7 - self.device - .make_context_current(&ctx) - .map_err(|err| format!("Failed to make new context current: {:?}", err))?; - - let id = WebGLContextId( - self.external_images - .lock() - .expect("Lock poisoned?") - .next_id(WebrenderImageHandlerType::WebGL) - .0, - ); - - self.webrender_swap_chains - .create_attached_swap_chain(id, &mut self.device, &mut ctx, surface_access) - .map_err(|err| format!("Failed to create swap chain: {:?}", err))?; - - let swap_chain = self - .webrender_swap_chains - .get(id) - .expect("Failed to get the swap chain"); - - debug!( - "Created webgl context {:?}/{:?}", - id, - self.device.context_id(&ctx), - ); - - let gl = unsafe { - Rc::new(match self.api_type { - GlType::Gl => glow::Context::from_loader_function(|symbol_name| { - self.device.get_proc_address(&ctx, symbol_name) - }), - GlType::Gles => glow::Context::from_loader_function(|symbol_name| { - self.device.get_proc_address(&ctx, symbol_name) - }), - }) - }; - - let limits = GLLimits::detect(&gl, webgl_version); - - let size = clamp_viewport(&gl, requested_size); - if safe_size != size { - debug!("Resizing swap chain from {:?} to {:?}", safe_size, size); - swap_chain - .resize(&mut self.device, &mut ctx, size.to_i32()) - .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?; - } - - let descriptor = self.device.context_descriptor(&ctx); - let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor); - let gl_version = descriptor_attributes.version; - let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA); - let image_buffer_kind = current_wr_image_buffer_kind(&self.device); - - self.device.make_context_current(&ctx).unwrap(); - let framebuffer = self - .device - .context_surface_info(&ctx) - .map_err(|err| format!("Failed to get context surface info: {:?}", err))? - .ok_or_else(|| "Failed to get context surface info".to_string())? - .framebuffer_object; - - unsafe { - gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer); - gl.viewport(0, 0, size.width as i32, size.height as i32); - gl.scissor(0, 0, size.width as i32, size.height as i32); - gl.clear_color(0., 0., 0., !has_alpha as u32 as f32); - gl.clear_depth(1.); - gl.clear_stencil(0); - gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); - gl.clear_color(0., 0., 0., 0.); - debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - } - - let default_vao = if let Some(vao) = WebGLImpl::create_vertex_array(&gl) { - WebGLImpl::bind_vertex_array(&gl, Some(vao.glow())); - Some(vao.glow()) - } else { - None - }; - - let state = GLState { - _gl_version: gl_version, - _webgl_version: webgl_version, - requested_flags, - default_vao, - ..Default::default() - }; - debug!("Created state {:?}", state); - - state.restore_invariant(&gl); - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - - self.contexts.insert( - id, - GLContextData { - ctx, - gl, - state, - attributes, - }, - ); - - let image_key = Self::create_wr_external_image( - &mut self.webrender_api, - self.webrender_doc, - size.to_i32(), - has_alpha, - id, - image_buffer_kind, - ); - - self.cached_context_info - .insert(id, WebGLContextInfo { image_key }); - - Ok((id, limits)) - } - - /// Resizes a WebGLContext - fn resize_webgl_context( - &mut self, - context_id: WebGLContextId, - requested_size: Size2D<u32>, - ) -> Result<(), String> { - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ) - .expect("Missing WebGL context!"); - - let size = clamp_viewport(&data.gl, requested_size); - - // Check to see if any of the current framebuffer bindings are the surface we're about to - // throw out. If so, we'll have to reset them after destroying the surface. - let framebuffer_rebinding_info = - FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl); - - // Resize the swap chains - if let Some(swap_chain) = self.webrender_swap_chains.get(context_id) { - let alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; - swap_chain - .resize(&mut self.device, &mut data.ctx, size.to_i32()) - .map_err(|err| format!("Failed to resize swap chain: {:?}", err))?; - swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color) - .map_err(|err| format!("Failed to clear resized swap chain: {:?}", err))?; - } else { - error!("Failed to find swap chain"); - } - - // Reset framebuffer bindings as appropriate. - framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - let has_alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - self.update_wr_image_for_context(context_id, size.to_i32(), has_alpha); - - Ok(()) - } - - /// Removes a WebGLContext and releases attached resources. - fn remove_webgl_context(&mut self, context_id: WebGLContextId) { - // Release webrender image keys. - if let Some(info) = self.cached_context_info.remove(&context_id) { - let mut txn = Transaction::new(); - txn.delete_image(info.image_key); - self.webrender_api.send_transaction(self.webrender_doc, txn) - } - - // We need to make the context current so its resources can be disposed of. - Self::make_current_if_needed( - &self.device, - context_id, - &self.contexts, - &mut self.bound_context_id, - ); - - #[cfg(feature = "webxr")] - { - // Destroy WebXR layers associated with this context - let webxr_context_id = webxr_api::ContextId::from(context_id); - let mut webxr_contexts = WebXRBridgeContexts { - contexts: &mut self.contexts, - bound_context_id: &mut self.bound_context_id, - }; - self.webxr_bridge.destroy_all_layers( - &mut self.device, - &mut webxr_contexts, - webxr_context_id, - ); - } - - // Release GL context. - let mut data = match self.contexts.remove(&context_id) { - Some(data) => data, - None => return, - }; - - // Destroy the swap chains - self.webrender_swap_chains - .destroy(context_id, &mut self.device, &mut data.ctx) - .unwrap(); - - // Destroy the context - self.device.destroy_context(&mut data.ctx).unwrap(); - - // Removing a GLContext may make the current bound context_id dirty. - self.bound_context_id = None; - } - - fn handle_swap_buffers( - &mut self, - context_ids: Vec<WebGLContextId>, - completed_sender: WebGLSender<u64>, - _sent_time: u64, - ) { - debug!("handle_swap_buffers()"); - for context_id in context_ids { - let data = Self::make_current_if_needed_mut( - &self.device, - context_id, - &mut self.contexts, - &mut self.bound_context_id, - ) - .expect("Where's the GL data?"); - - // Ensure there are no pending GL errors from other parts of the pipeline. - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - // Check to see if any of the current framebuffer bindings are the surface we're about - // to swap out. If so, we'll have to reset them after destroying the surface. - let framebuffer_rebinding_info = - FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - debug!("Getting swap chain for {:?}", context_id); - let swap_chain = self - .webrender_swap_chains - .get(context_id) - .expect("Where's the swap chain?"); - - debug!("Swapping {:?}", context_id); - swap_chain - .swap_buffers( - &mut self.device, - &mut data.ctx, - if data.attributes.preserve_drawing_buffer { - PreserveBuffer::Yes(&data.gl) - } else { - PreserveBuffer::No - }, - ) - .unwrap(); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - if !data.attributes.preserve_drawing_buffer { - debug!("Clearing {:?}", context_id); - let alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32]; - swap_chain - .clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color) - .unwrap(); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - } - - // Rebind framebuffers as appropriate. - debug!("Rebinding {:?}", context_id); - framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl); - debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR); - - let SurfaceInfo { - size, - framebuffer_object, - id, - .. - } = self - .device - .context_surface_info(&data.ctx) - .unwrap() - .unwrap(); - debug!( - "... rebound framebuffer {:?}, new back buffer surface is {:?}", - framebuffer_object, id - ); - - let has_alpha = data - .state - .requested_flags - .contains(ContextAttributeFlags::ALPHA); - self.update_wr_image_for_context(context_id, size, has_alpha); - } - - #[allow(unused)] - let mut end_swap = 0; - completed_sender.send(end_swap).unwrap(); - } - - /// Which access mode to use - fn surface_access(&self) -> SurfaceAccess { - SurfaceAccess::GPUOnly - } - - /// Gets a reference to a Context for a given WebGLContextId and makes it current if required. - pub(crate) fn make_current_if_needed<'a>( - device: &Device, - context_id: WebGLContextId, - contexts: &'a FnvHashMap<WebGLContextId, GLContextData>, - bound_id: &mut Option<WebGLContextId>, - ) -> Option<&'a GLContextData> { - let data = contexts.get(&context_id); - - if let Some(data) = data { - if Some(context_id) != *bound_id { - device.make_context_current(&data.ctx).unwrap(); - *bound_id = Some(context_id); - } - } - - data - } - - /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required. - pub(crate) fn make_current_if_needed_mut<'a>( - device: &Device, - context_id: WebGLContextId, - contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>, - bound_id: &mut Option<WebGLContextId>, - ) -> Option<&'a mut GLContextData> { - let data = contexts.get_mut(&context_id); - - if let Some(ref data) = data { - if Some(context_id) != *bound_id { - device.make_context_current(&data.ctx).unwrap(); - *bound_id = Some(context_id); - } - } - - data - } - - /// Creates a `webrender_api::ImageKey` that uses shared textures. - fn create_wr_external_image( - webrender_api: &mut RenderApi, - webrender_doc: DocumentId, - size: Size2D<i32>, - alpha: bool, - context_id: WebGLContextId, - image_buffer_kind: ImageBufferKind, - ) -> ImageKey { - let descriptor = Self::image_descriptor(size, alpha); - let data = Self::external_image_data(context_id, image_buffer_kind); - - let image_key = webrender_api.generate_image_key(); - let mut txn = Transaction::new(); - txn.add_image(image_key, descriptor, data, None); - webrender_api.send_transaction(webrender_doc, txn); - - image_key - } - - /// Tell WebRender to invalidate any cached tiles for a given `WebGLContextId` - /// when the underlying surface has changed e.g due to resize or buffer swap - fn update_wr_image_for_context( - &mut self, - context_id: WebGLContextId, - size: Size2D<i32>, - has_alpha: bool, - ) { - let info = self.cached_context_info.get(&context_id).unwrap(); - let image_buffer_kind = current_wr_image_buffer_kind(&self.device); - - let descriptor = Self::image_descriptor(size, has_alpha); - let image_data = Self::external_image_data(context_id, image_buffer_kind); - - let mut txn = Transaction::new(); - txn.update_image(info.image_key, descriptor, image_data, &DirtyRect::All); - self.webrender_api.send_transaction(self.webrender_doc, txn); - } - - /// Helper function to create a `ImageDescriptor`. - fn image_descriptor(size: Size2D<i32>, alpha: bool) -> ImageDescriptor { - let mut flags = ImageDescriptorFlags::empty(); - flags.set(ImageDescriptorFlags::IS_OPAQUE, !alpha); - ImageDescriptor { - size: DeviceIntSize::new(size.width, size.height), - stride: None, - format: ImageFormat::BGRA8, - offset: 0, - flags, - } - } - - /// Helper function to create a `ImageData::External` instance. - fn external_image_data( - context_id: WebGLContextId, - image_buffer_kind: ImageBufferKind, - ) -> ImageData { - let data = ExternalImageData { - id: ExternalImageId(context_id.0), - channel_index: 0, - image_type: ExternalImageType::TextureHandle(image_buffer_kind), - normalized_uvs: false, - }; - ImageData::External(data) - } - - /// Gets the GLSL Version supported by a GLContext. - fn get_glsl_version(gl: &Gl) -> WebGLSLVersion { - let version = unsafe { gl.get_parameter_string(gl::SHADING_LANGUAGE_VERSION) }; - // Fomat used by SHADING_LANGUAGE_VERSION query : major.minor[.release] [vendor info] - let mut values = version.split(&['.', ' '][..]); - let major = values - .next() - .and_then(|v| v.parse::<u32>().ok()) - .unwrap_or(1); - let minor = values - .next() - .and_then(|v| v.parse::<u32>().ok()) - .unwrap_or(20); - - WebGLSLVersion { major, minor } - } -} - -/// Helper struct to store cached WebGLContext information. -struct WebGLContextInfo { - /// Currently used WebRender image key. - image_key: ImageKey, -} - -// TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`? -fn current_wr_image_buffer_kind(device: &Device) -> ImageBufferKind { - match device.surface_gl_texture_target() { - gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect, - _ => ImageBufferKind::Texture2D, - } -} - -/// WebGL Commands Implementation -pub struct WebGLImpl; - -impl WebGLImpl { - pub fn apply( - device: &Device, - ctx: &Context, - gl: &Gl, - state: &mut GLState, - attributes: &GLContextAttributes, - command: WebGLCommand, - _backtrace: WebGLCommandBacktrace, - ) { - debug!("WebGLImpl::apply({:?})", command); - - // Ensure there are no pending GL errors from other parts of the pipeline. - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - - match command { - WebGLCommand::GetContextAttributes(ref sender) => sender.send(*attributes).unwrap(), - WebGLCommand::ActiveTexture(target) => unsafe { gl.active_texture(target) }, - WebGLCommand::AttachShader(program_id, shader_id) => unsafe { - gl.attach_shader(program_id.glow(), shader_id.glow()) - }, - WebGLCommand::DetachShader(program_id, shader_id) => unsafe { - gl.detach_shader(program_id.glow(), shader_id.glow()) - }, - WebGLCommand::BindAttribLocation(program_id, index, ref name) => unsafe { - gl.bind_attrib_location(program_id.glow(), index, &to_name_in_compiled_shader(name)) - }, - WebGLCommand::BlendColor(r, g, b, a) => unsafe { gl.blend_color(r, g, b, a) }, - WebGLCommand::BlendEquation(mode) => unsafe { gl.blend_equation(mode) }, - WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => unsafe { - gl.blend_equation_separate(mode_rgb, mode_alpha) - }, - WebGLCommand::BlendFunc(src, dest) => unsafe { gl.blend_func(src, dest) }, - WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => unsafe { - gl.blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha) - }, - WebGLCommand::BufferData(buffer_type, ref receiver, usage) => unsafe { - gl.buffer_data_u8_slice(buffer_type, &receiver.recv().unwrap(), usage) - }, - WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => unsafe { - gl.buffer_sub_data_u8_slice(buffer_type, offset as i32, &receiver.recv().unwrap()) - }, - WebGLCommand::CopyBufferSubData(src, dst, src_offset, dst_offset, size) => { - unsafe { - gl.copy_buffer_sub_data( - src, - dst, - src_offset as i32, - dst_offset as i32, - size as i32, - ) - }; - }, - WebGLCommand::GetBufferSubData(buffer_type, offset, length, ref sender) => unsafe { - let ptr = gl.map_buffer_range( - buffer_type, - offset as i32, - length as i32, - gl::MAP_READ_BIT, - ); - let data: &[u8] = slice::from_raw_parts(ptr as _, length); - sender.send(data).unwrap(); - gl.unmap_buffer(buffer_type); - }, - WebGLCommand::Clear(mask) => { - unsafe { gl.clear(mask) }; - }, - WebGLCommand::ClearColor(r, g, b, a) => { - state.clear_color = (r, g, b, a); - unsafe { gl.clear_color(r, g, b, a) }; - }, - WebGLCommand::ClearDepth(depth) => { - let value = depth.clamp(0., 1.) as f64; - state.depth_clear_value = value; - unsafe { gl.clear_depth(value) } - }, - WebGLCommand::ClearStencil(stencil) => { - state.stencil_clear_value = stencil; - unsafe { gl.clear_stencil(stencil) }; - }, - WebGLCommand::ColorMask(r, g, b, a) => { - state.color_write_mask = [r, g, b, a]; - state.restore_alpha_invariant(gl); - }, - WebGLCommand::CopyTexImage2D( - target, - level, - internal_format, - x, - y, - width, - height, - border, - ) => unsafe { - gl.copy_tex_image_2d(target, level, internal_format, x, y, width, height, border) - }, - WebGLCommand::CopyTexSubImage2D( - target, - level, - xoffset, - yoffset, - x, - y, - width, - height, - ) => unsafe { - gl.copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height) - }, - WebGLCommand::CullFace(mode) => unsafe { gl.cull_face(mode) }, - WebGLCommand::DepthFunc(func) => unsafe { gl.depth_func(func) }, - WebGLCommand::DepthMask(flag) => { - state.depth_write_mask = flag; - state.restore_depth_invariant(gl); - }, - WebGLCommand::DepthRange(near, far) => unsafe { - gl.depth_range(near.clamp(0., 1.) as f64, far.clamp(0., 1.) as f64) - }, - WebGLCommand::Disable(cap) => match cap { - gl::SCISSOR_TEST => { - state.scissor_test_enabled = false; - state.restore_scissor_invariant(gl); - }, - gl::DEPTH_TEST => { - state.depth_test_enabled = false; - state.restore_depth_invariant(gl); - }, - gl::STENCIL_TEST => { - state.stencil_test_enabled = false; - state.restore_stencil_invariant(gl); - }, - _ => unsafe { gl.disable(cap) }, - }, - WebGLCommand::Enable(cap) => match cap { - gl::SCISSOR_TEST => { - state.scissor_test_enabled = true; - state.restore_scissor_invariant(gl); - }, - gl::DEPTH_TEST => { - state.depth_test_enabled = true; - state.restore_depth_invariant(gl); - }, - gl::STENCIL_TEST => { - state.stencil_test_enabled = true; - state.restore_stencil_invariant(gl); - }, - _ => unsafe { gl.enable(cap) }, - }, - WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => { - let attach = |attachment| unsafe { - gl.framebuffer_renderbuffer( - target, - attachment, - renderbuffertarget, - rb.map(WebGLRenderbufferId::glow), - ) - }; - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment); - } - }, - WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => { - let attach = |attachment| unsafe { - gl.framebuffer_texture_2d( - target, - attachment, - textarget, - texture.map(WebGLTextureId::glow), - level, - ) - }; - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment) - } - }, - WebGLCommand::FrontFace(mode) => unsafe { gl.front_face(mode) }, - WebGLCommand::DisableVertexAttribArray(attrib_id) => unsafe { - gl.disable_vertex_attrib_array(attrib_id) - }, - WebGLCommand::EnableVertexAttribArray(attrib_id) => unsafe { - gl.enable_vertex_attrib_array(attrib_id) - }, - WebGLCommand::Hint(name, val) => unsafe { gl.hint(name, val) }, - WebGLCommand::LineWidth(width) => { - unsafe { gl.line_width(width) }; - // In OpenGL Core Profile >3.2, any non-1.0 value will generate INVALID_VALUE. - if width != 1.0 { - let _ = unsafe { gl.get_error() }; - } - }, - WebGLCommand::PixelStorei(name, val) => unsafe { gl.pixel_store_i32(name, val) }, - WebGLCommand::PolygonOffset(factor, units) => unsafe { - gl.polygon_offset(factor, units) - }, - WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => { - let len = bytes_per_type(pixel_type) * - components_per_format(format) * - rect.size.area() as usize; - let mut pixels = vec![0; len]; - unsafe { - // We don't want any alignment padding on pixel rows. - gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1); - gl.read_pixels( - rect.origin.x as i32, - rect.origin.y as i32, - rect.size.width as i32, - rect.size.height as i32, - format, - pixel_type, - glow::PixelPackData::Slice(Some(&mut pixels)), - ) - }; - let alpha_mode = match (attributes.alpha, attributes.premultiplied_alpha) { - (true, premultiplied) => snapshot::AlphaMode::Transparent { premultiplied }, - (false, _) => snapshot::AlphaMode::Opaque, - }; - sender - .send((IpcSharedMemory::from_bytes(&pixels), alpha_mode)) - .unwrap(); - }, - WebGLCommand::ReadPixelsPP(rect, format, pixel_type, offset) => unsafe { - gl.read_pixels( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - format, - pixel_type, - glow::PixelPackData::BufferOffset(offset as u32), - ); - }, - WebGLCommand::RenderbufferStorage(target, format, width, height) => unsafe { - gl.renderbuffer_storage(target, format, width, height) - }, - WebGLCommand::RenderbufferStorageMultisample( - target, - samples, - format, - width, - height, - ) => unsafe { - gl.renderbuffer_storage_multisample(target, samples, format, width, height) - }, - WebGLCommand::SampleCoverage(value, invert) => unsafe { - gl.sample_coverage(value, invert) - }, - WebGLCommand::Scissor(x, y, width, height) => { - // FIXME(nox): Kinda unfortunate that some u32 values could - // end up as negative numbers here, but I don't even think - // that can happen in the real world. - unsafe { gl.scissor(x, y, width as i32, height as i32) }; - }, - WebGLCommand::StencilFunc(func, ref_, mask) => unsafe { - gl.stencil_func(func, ref_, mask) - }, - WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => unsafe { - gl.stencil_func_separate(face, func, ref_, mask) - }, - WebGLCommand::StencilMask(mask) => { - state.stencil_write_mask = (mask, mask); - state.restore_stencil_invariant(gl); - }, - WebGLCommand::StencilMaskSeparate(face, mask) => { - if face == gl::FRONT { - state.stencil_write_mask.0 = mask; - } else { - state.stencil_write_mask.1 = mask; - } - state.restore_stencil_invariant(gl); - }, - WebGLCommand::StencilOp(fail, zfail, zpass) => unsafe { - gl.stencil_op(fail, zfail, zpass) - }, - WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => unsafe { - gl.stencil_op_separate(face, fail, zfail, zpass) - }, - WebGLCommand::GetRenderbufferParameter(target, pname, ref chan) => { - Self::get_renderbuffer_parameter(gl, target, pname, chan) - }, - WebGLCommand::CreateTransformFeedback(ref sender) => { - let value = unsafe { gl.create_transform_feedback() }.ok(); - sender - .send(value.map(|ntf| ntf.0.get()).unwrap_or_default()) - .unwrap() - }, - WebGLCommand::DeleteTransformFeedback(id) => { - if let Some(tf) = NonZeroU32::new(id) { - unsafe { gl.delete_transform_feedback(NativeTransformFeedback(tf)) }; - } - }, - WebGLCommand::IsTransformFeedback(id, ref sender) => { - let value = NonZeroU32::new(id) - .map(|id| unsafe { gl.is_transform_feedback(NativeTransformFeedback(id)) }) - .unwrap_or_default(); - sender.send(value).unwrap() - }, - WebGLCommand::BindTransformFeedback(target, id) => { - unsafe { - gl.bind_transform_feedback( - target, - NonZeroU32::new(id).map(NativeTransformFeedback), - ) - }; - }, - WebGLCommand::BeginTransformFeedback(mode) => { - unsafe { gl.begin_transform_feedback(mode) }; - }, - WebGLCommand::EndTransformFeedback() => { - unsafe { gl.end_transform_feedback() }; - }, - WebGLCommand::PauseTransformFeedback() => { - unsafe { gl.pause_transform_feedback() }; - }, - WebGLCommand::ResumeTransformFeedback() => { - unsafe { gl.resume_transform_feedback() }; - }, - WebGLCommand::GetTransformFeedbackVarying(program, index, ref sender) => { - let ActiveTransformFeedback { size, tftype, name } = - unsafe { gl.get_transform_feedback_varying(program.glow(), index) }.unwrap(); - // We need to split, because the name starts with '_u' prefix. - let name = from_name_in_compiled_shader(&name); - sender.send((size, tftype, name)).unwrap(); - }, - WebGLCommand::TransformFeedbackVaryings(program, ref varyings, buffer_mode) => { - let varyings: Vec<String> = varyings - .iter() - .map(|varying| to_name_in_compiled_shader(varying)) - .collect(); - let varyings_refs: Vec<&str> = varyings.iter().map(String::as_ref).collect(); - unsafe { - gl.transform_feedback_varyings( - program.glow(), - varyings_refs.as_slice(), - buffer_mode, - ) - }; - }, - WebGLCommand::GetFramebufferAttachmentParameter( - target, - attachment, - pname, - ref chan, - ) => Self::get_framebuffer_attachment_parameter(gl, target, attachment, pname, chan), - WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, ref chan) => { - Self::shader_precision_format(gl, shader_type, precision_type, chan) - }, - WebGLCommand::GetExtensions(ref chan) => Self::get_extensions(gl, chan), - WebGLCommand::GetFragDataLocation(program_id, ref name, ref sender) => { - let location = unsafe { - gl.get_frag_data_location(program_id.glow(), &to_name_in_compiled_shader(name)) - }; - sender.send(location).unwrap(); - }, - WebGLCommand::GetUniformLocation(program_id, ref name, ref chan) => { - Self::uniform_location(gl, program_id, name, chan) - }, - WebGLCommand::GetShaderInfoLog(shader_id, ref chan) => { - Self::shader_info_log(gl, shader_id, chan) - }, - WebGLCommand::GetProgramInfoLog(program_id, ref chan) => { - Self::program_info_log(gl, program_id, chan) - }, - WebGLCommand::CompileShader(shader_id, ref source) => { - Self::compile_shader(gl, shader_id, source) - }, - WebGLCommand::CreateBuffer(ref chan) => Self::create_buffer(gl, chan), - WebGLCommand::CreateFramebuffer(ref chan) => Self::create_framebuffer(gl, chan), - WebGLCommand::CreateRenderbuffer(ref chan) => Self::create_renderbuffer(gl, chan), - WebGLCommand::CreateTexture(ref chan) => Self::create_texture(gl, chan), - WebGLCommand::CreateProgram(ref chan) => Self::create_program(gl, chan), - WebGLCommand::CreateShader(shader_type, ref chan) => { - Self::create_shader(gl, shader_type, chan) - }, - WebGLCommand::DeleteBuffer(id) => unsafe { gl.delete_buffer(id.glow()) }, - WebGLCommand::DeleteFramebuffer(id) => unsafe { gl.delete_framebuffer(id.glow()) }, - WebGLCommand::DeleteRenderbuffer(id) => unsafe { gl.delete_renderbuffer(id.glow()) }, - WebGLCommand::DeleteTexture(id) => unsafe { gl.delete_texture(id.glow()) }, - WebGLCommand::DeleteProgram(id) => unsafe { gl.delete_program(id.glow()) }, - WebGLCommand::DeleteShader(id) => unsafe { gl.delete_shader(id.glow()) }, - WebGLCommand::BindBuffer(target, id) => unsafe { - gl.bind_buffer(target, id.map(WebGLBufferId::glow)) - }, - WebGLCommand::BindFramebuffer(target, request) => { - Self::bind_framebuffer(gl, target, request, ctx, device, state) - }, - WebGLCommand::BindRenderbuffer(target, id) => unsafe { - gl.bind_renderbuffer(target, id.map(WebGLRenderbufferId::glow)) - }, - WebGLCommand::BindTexture(target, id) => unsafe { - gl.bind_texture(target, id.map(WebGLTextureId::glow)) - }, - WebGLCommand::BlitFrameBuffer( - src_x0, - src_y0, - src_x1, - src_y1, - dst_x0, - dst_y0, - dst_x1, - dst_y1, - mask, - filter, - ) => unsafe { - gl.blit_framebuffer( - src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter, - ); - }, - WebGLCommand::Uniform1f(uniform_id, v) => unsafe { - gl.uniform_1_f32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1fv(uniform_id, ref v) => unsafe { - gl.uniform_1_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1i(uniform_id, v) => unsafe { - gl.uniform_1_i32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1iv(uniform_id, ref v) => unsafe { - gl.uniform_1_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1ui(uniform_id, v) => unsafe { - gl.uniform_1_u32(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform1uiv(uniform_id, ref v) => unsafe { - gl.uniform_1_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2f(uniform_id, x, y) => unsafe { - gl.uniform_2_f32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2fv(uniform_id, ref v) => unsafe { - gl.uniform_2_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2i(uniform_id, x, y) => unsafe { - gl.uniform_2_i32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2iv(uniform_id, ref v) => unsafe { - gl.uniform_2_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform2ui(uniform_id, x, y) => unsafe { - gl.uniform_2_u32(native_uniform_location(uniform_id).as_ref(), x, y) - }, - WebGLCommand::Uniform2uiv(uniform_id, ref v) => unsafe { - gl.uniform_2_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3f(uniform_id, x, y, z) => unsafe { - gl.uniform_3_f32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3fv(uniform_id, ref v) => unsafe { - gl.uniform_3_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3i(uniform_id, x, y, z) => unsafe { - gl.uniform_3_i32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3iv(uniform_id, ref v) => unsafe { - gl.uniform_3_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform3ui(uniform_id, x, y, z) => unsafe { - gl.uniform_3_u32(native_uniform_location(uniform_id).as_ref(), x, y, z) - }, - WebGLCommand::Uniform3uiv(uniform_id, ref v) => unsafe { - gl.uniform_3_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_f32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4fv(uniform_id, ref v) => unsafe { - gl.uniform_4_f32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_i32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4iv(uniform_id, ref v) => unsafe { - gl.uniform_4_i32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::Uniform4ui(uniform_id, x, y, z, w) => unsafe { - gl.uniform_4_u32(native_uniform_location(uniform_id).as_ref(), x, y, z, w) - }, - WebGLCommand::Uniform4uiv(uniform_id, ref v) => unsafe { - gl.uniform_4_u32_slice(native_uniform_location(uniform_id).as_ref(), v) - }, - WebGLCommand::UniformMatrix2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3x2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3x2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4x2fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4x2_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix2x3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2x3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix4x3fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_4x3_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix2x4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_2x4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::UniformMatrix3x4fv(uniform_id, ref v) => unsafe { - gl.uniform_matrix_3x4_f32_slice( - native_uniform_location(uniform_id).as_ref(), - false, - v, - ) - }, - WebGLCommand::ValidateProgram(program_id) => unsafe { - gl.validate_program(program_id.glow()) - }, - WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_f32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribI(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_i32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribU(attrib_id, x, y, z, w) => unsafe { - gl.vertex_attrib_4_u32(attrib_id, x, y, z, w) - }, - WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => unsafe { - gl.vertex_attrib_pointer_f32( - attrib_id, - size, - gl::FLOAT, - normalized, - stride, - offset as _, - ) - }, - WebGLCommand::VertexAttribPointer( - attrib_id, - size, - data_type, - normalized, - stride, - offset, - ) => unsafe { - gl.vertex_attrib_pointer_f32( - attrib_id, - size, - data_type, - normalized, - stride, - offset as _, - ) - }, - WebGLCommand::SetViewport(x, y, width, height) => unsafe { - gl.viewport(x, y, width, height) - }, - WebGLCommand::TexImage2D { - target, - level, - internal_format, - size, - format, - data_type, - effective_data_type, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - ref data, - } => { - let pixels = prepare_pixels( - internal_format, - data_type, - size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - Cow::Borrowed(data), - ); - - unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - gl.tex_image_2d( - target, - level as i32, - internal_format.as_gl_constant() as i32, - size.width as i32, - size.height as i32, - 0, - format.as_gl_constant(), - effective_data_type, - PixelUnpackData::Slice(Some(&pixels)), - ); - } - }, - WebGLCommand::TexImage2DPBO { - target, - level, - internal_format, - size, - format, - effective_data_type, - unpacking_alignment, - offset, - } => unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - - gl.tex_image_2d( - target, - level as i32, - internal_format.as_gl_constant() as i32, - size.width as i32, - size.height as i32, - 0, - format.as_gl_constant(), - effective_data_type, - PixelUnpackData::BufferOffset(offset as u32), - ); - }, - WebGLCommand::TexSubImage2D { - target, - level, - xoffset, - yoffset, - size, - format, - data_type, - effective_data_type, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - ref data, - } => { - let pixels = prepare_pixels( - format, - data_type, - size, - unpacking_alignment, - alpha_treatment, - y_axis_treatment, - pixel_format, - Cow::Borrowed(data), - ); - - unsafe { - gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32); - gl.tex_sub_image_2d( - target, - level as i32, - xoffset, - yoffset, - size.width as i32, - size.height as i32, - format.as_gl_constant(), - effective_data_type, - glow::PixelUnpackData::Slice(Some(&pixels)), - ); - } - }, - WebGLCommand::CompressedTexImage2D { - target, - level, - internal_format, - size, - ref data, - } => unsafe { - gl.compressed_tex_image_2d( - target, - level as i32, - internal_format as i32, - size.width as i32, - size.height as i32, - 0, - data.len() as i32, - data, - ) - }, - WebGLCommand::CompressedTexSubImage2D { - target, - level, - xoffset, - yoffset, - size, - format, - ref data, - } => { - unsafe { - gl.compressed_tex_sub_image_2d( - target, - level, - xoffset, - yoffset, - size.width as i32, - size.height as i32, - format, - glow::CompressedPixelUnpackData::Slice(data), - ) - }; - }, - WebGLCommand::TexStorage2D(target, levels, internal_format, width, height) => unsafe { - gl.tex_storage_2d( - target, - levels as i32, - internal_format.as_gl_constant(), - width as i32, - height as i32, - ) - }, - WebGLCommand::TexStorage3D(target, levels, internal_format, width, height, depth) => unsafe { - gl.tex_storage_3d( - target, - levels as i32, - internal_format.as_gl_constant(), - width as i32, - height as i32, - depth as i32, - ) - }, - WebGLCommand::DrawingBufferWidth(ref sender) => { - let size = device - .context_surface_info(ctx) - .unwrap() - .expect("Where's the front buffer?") - .size; - sender.send(size.width).unwrap() - }, - WebGLCommand::DrawingBufferHeight(ref sender) => { - let size = device - .context_surface_info(ctx) - .unwrap() - .expect("Where's the front buffer?") - .size; - sender.send(size.height).unwrap() - }, - WebGLCommand::Finish(ref sender) => Self::finish(gl, sender), - WebGLCommand::Flush => unsafe { gl.flush() }, - WebGLCommand::GenerateMipmap(target) => unsafe { gl.generate_mipmap(target) }, - WebGLCommand::CreateVertexArray(ref chan) => { - let id = Self::create_vertex_array(gl); - let _ = chan.send(id); - }, - WebGLCommand::DeleteVertexArray(id) => { - Self::delete_vertex_array(gl, id); - }, - WebGLCommand::BindVertexArray(id) => { - let id = id.map(WebGLVertexArrayId::glow).or(state.default_vao); - Self::bind_vertex_array(gl, id); - }, - WebGLCommand::GetParameterBool(param, ref sender) => { - let value = match param { - webgl::ParameterBool::DepthWritemask => state.depth_write_mask, - _ => unsafe { gl.get_parameter_bool(param as u32) }, - }; - sender.send(value).unwrap() - }, - WebGLCommand::FenceSync(ref sender) => { - let value = unsafe { gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0).unwrap() }; - sender.send(WebGLSyncId::from_glow(value)).unwrap(); - }, - WebGLCommand::IsSync(sync_id, ref sender) => { - let value = unsafe { gl.is_sync(sync_id.glow()) }; - sender.send(value).unwrap(); - }, - WebGLCommand::ClientWaitSync(sync_id, flags, timeout, ref sender) => { - let value = unsafe { gl.client_wait_sync(sync_id.glow(), flags, timeout as _) }; - sender.send(value).unwrap(); - }, - WebGLCommand::WaitSync(sync_id, flags, timeout) => { - unsafe { gl.wait_sync(sync_id.glow(), flags, timeout as u64) }; - }, - WebGLCommand::GetSyncParameter(sync_id, param, ref sender) => { - let value = unsafe { gl.get_sync_parameter_i32(sync_id.glow(), param) }; - sender.send(value as u32).unwrap(); - }, - WebGLCommand::DeleteSync(sync_id) => { - unsafe { gl.delete_sync(sync_id.glow()) }; - }, - WebGLCommand::GetParameterBool4(param, ref sender) => { - let value = match param { - webgl::ParameterBool4::ColorWritemask => state.color_write_mask, - }; - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt(param, ref sender) => { - let value = match param { - webgl::ParameterInt::AlphaBits if state.fake_no_alpha() => 0, - webgl::ParameterInt::DepthBits if state.fake_no_depth() => 0, - webgl::ParameterInt::StencilBits if state.fake_no_stencil() => 0, - webgl::ParameterInt::StencilWritemask => state.stencil_write_mask.0 as i32, - webgl::ParameterInt::StencilBackWritemask => state.stencil_write_mask.1 as i32, - _ => unsafe { gl.get_parameter_i32(param as u32) }, - }; - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt2(param, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_parameter_i32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterInt4(param, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_parameter_i32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterFloat(param, ref sender) => { - let mut value = [0.]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value[0]).unwrap() - }, - WebGLCommand::GetParameterFloat2(param, ref sender) => { - let mut value = [0.; 2]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetParameterFloat4(param, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_parameter_f32_slice(param as u32, &mut value); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetProgramValidateStatus(program, ref sender) => sender - .send(unsafe { gl.get_program_validate_status(program.glow()) }) - .unwrap(), - WebGLCommand::GetProgramActiveUniforms(program, ref sender) => sender - .send(unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) }) - .unwrap(), - WebGLCommand::GetCurrentVertexAttrib(index, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_vertex_attrib_parameter_f32_slice( - index, - gl::CURRENT_VERTEX_ATTRIB, - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetTexParameterFloat(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_f32(target, param as u32) }) - .unwrap(); - }, - WebGLCommand::GetTexParameterInt(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) }) - .unwrap(); - }, - WebGLCommand::GetTexParameterBool(target, param, ref sender) => { - sender - .send(unsafe { gl.get_tex_parameter_i32(target, param as u32) } != 0) - .unwrap(); - }, - WebGLCommand::GetInternalFormatIntVec(target, internal_format, param, ref sender) => { - match param { - InternalFormatIntVec::Samples => { - let mut count = [0; 1]; - unsafe { - gl.get_internal_format_i32_slice( - target, - internal_format, - gl::NUM_SAMPLE_COUNTS, - &mut count, - ) - }; - assert!(count[0] >= 0); - - let mut values = vec![0; count[0] as usize]; - unsafe { - gl.get_internal_format_i32_slice( - target, - internal_format, - param as u32, - &mut values, - ) - }; - sender.send(values).unwrap() - }, - } - }, - WebGLCommand::TexParameteri(target, param, value) => unsafe { - gl.tex_parameter_i32(target, param, value) - }, - WebGLCommand::TexParameterf(target, param, value) => unsafe { - gl.tex_parameter_f32(target, param, value) - }, - WebGLCommand::LinkProgram(program_id, ref sender) => { - return sender.send(Self::link_program(gl, program_id)).unwrap(); - }, - WebGLCommand::UseProgram(program_id) => unsafe { - gl.use_program(program_id.map(|p| p.glow())) - }, - WebGLCommand::DrawArrays { mode, first, count } => unsafe { - gl.draw_arrays(mode, first, count) - }, - WebGLCommand::DrawArraysInstanced { - mode, - first, - count, - primcount, - } => unsafe { gl.draw_arrays_instanced(mode, first, count, primcount) }, - WebGLCommand::DrawElements { - mode, - count, - type_, - offset, - } => unsafe { gl.draw_elements(mode, count, type_, offset as _) }, - WebGLCommand::DrawElementsInstanced { - mode, - count, - type_, - offset, - primcount, - } => unsafe { - gl.draw_elements_instanced(mode, count, type_, offset as i32, primcount) - }, - WebGLCommand::VertexAttribDivisor { index, divisor } => unsafe { - gl.vertex_attrib_divisor(index, divisor) - }, - WebGLCommand::GetUniformBool(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0] != 0).unwrap(); - }, - WebGLCommand::GetUniformBool2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformBool3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0, value[2] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformBool4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0]; - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformInt2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformInt4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_i32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint(program_id, loc, ref sender) => { - let mut value = [0]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformUint2(program_id, loc, ref sender) => { - let mut value = [0; 2]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint3(program_id, loc, ref sender) => { - let mut value = [0; 3]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformUint4(program_id, loc, ref sender) => { - let mut value = [0; 4]; - unsafe { - gl.get_uniform_u32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat(program_id, loc, ref sender) => { - let mut value = [0.]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value[0]).unwrap(); - }, - WebGLCommand::GetUniformFloat2(program_id, loc, ref sender) => { - let mut value = [0.; 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat3(program_id, loc, ref sender) => { - let mut value = [0.; 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat4(program_id, loc, ref sender) => { - let mut value = [0.; 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat9(program_id, loc, ref sender) => { - let mut value = [0.; 9]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat16(program_id, loc, ref sender) => { - let mut value = [0.; 16]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap(); - }, - WebGLCommand::GetUniformFloat2x3(program_id, loc, ref sender) => { - let mut value = [0.; 2 * 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat2x4(program_id, loc, ref sender) => { - let mut value = [0.; 2 * 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat3x2(program_id, loc, ref sender) => { - let mut value = [0.; 3 * 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat3x4(program_id, loc, ref sender) => { - let mut value = [0.; 3 * 4]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat4x2(program_id, loc, ref sender) => { - let mut value = [0.; 4 * 2]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformFloat4x3(program_id, loc, ref sender) => { - let mut value = [0.; 4 * 3]; - unsafe { - gl.get_uniform_f32( - program_id.glow(), - &NativeUniformLocation(loc as u32), - &mut value, - ); - } - sender.send(value).unwrap() - }, - WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => { - let name = to_name_in_compiled_shader(name); - let index = unsafe { gl.get_uniform_block_index(program_id.glow(), &name) }; - // TODO(#34300): use Option<u32> - sender.send(index.unwrap_or(gl::INVALID_INDEX)).unwrap(); - }, - WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => { - let names = names - .iter() - .map(|name| to_name_in_compiled_shader(name)) - .collect::<Vec<_>>(); - let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>(); - let indices = unsafe { - gl.get_uniform_indices(program_id.glow(), &name_strs) - .iter() - .map(|index| index.unwrap_or(gl::INVALID_INDEX)) - .collect() - }; - sender.send(indices).unwrap(); - }, - WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => { - let results = - unsafe { gl.get_active_uniforms_parameter(program_id.glow(), indices, pname) }; - sender.send(results).unwrap(); - }, - WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => { - let name = - unsafe { gl.get_active_uniform_block_name(program_id.glow(), block_idx) }; - sender.send(name).unwrap(); - }, - WebGLCommand::GetActiveUniformBlockParameter( - program_id, - block_idx, - pname, - ref sender, - ) => { - let size = match pname { - gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe { - gl.get_active_uniform_block_parameter_i32( - program_id.glow(), - block_idx, - gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS, - ) as usize - }, - _ => 1, - }; - let mut result = vec![0; size]; - unsafe { - gl.get_active_uniform_block_parameter_i32_slice( - program_id.glow(), - block_idx, - pname, - &mut result, - ) - }; - sender.send(result).unwrap(); - }, - WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => unsafe { - gl.uniform_block_binding(program_id.glow(), block_idx, block_binding) - }, - WebGLCommand::InitializeFramebuffer { - color, - depth, - stencil, - } => Self::initialize_framebuffer(gl, state, color, depth, stencil), - WebGLCommand::BeginQuery(target, query_id) => { - unsafe { gl.begin_query(target, query_id.glow()) }; - }, - WebGLCommand::EndQuery(target) => { - unsafe { gl.end_query(target) }; - }, - WebGLCommand::DeleteQuery(query_id) => { - unsafe { gl.delete_query(query_id.glow()) }; - }, - WebGLCommand::GenerateQuery(ref sender) => { - // TODO(#34300): use Option<WebGLQueryId> - let id = unsafe { gl.create_query().unwrap() }; - sender.send(WebGLQueryId::from_glow(id)).unwrap() - }, - WebGLCommand::GetQueryState(ref sender, query_id, pname) => { - let value = unsafe { gl.get_query_parameter_u32(query_id.glow(), pname) }; - sender.send(value).unwrap() - }, - WebGLCommand::GenerateSampler(ref sender) => { - let id = unsafe { gl.create_sampler().unwrap() }; - sender.send(WebGLSamplerId::from_glow(id)).unwrap() - }, - WebGLCommand::DeleteSampler(sampler_id) => { - unsafe { gl.delete_sampler(sampler_id.glow()) }; - }, - WebGLCommand::BindSampler(unit, sampler_id) => { - unsafe { gl.bind_sampler(unit, Some(sampler_id.glow())) }; - }, - WebGLCommand::SetSamplerParameterInt(sampler_id, pname, value) => { - unsafe { gl.sampler_parameter_i32(sampler_id.glow(), pname, value) }; - }, - WebGLCommand::SetSamplerParameterFloat(sampler_id, pname, value) => { - unsafe { gl.sampler_parameter_f32(sampler_id.glow(), pname, value) }; - }, - WebGLCommand::GetSamplerParameterInt(sampler_id, pname, ref sender) => { - let value = unsafe { gl.get_sampler_parameter_i32(sampler_id.glow(), pname) }; - sender.send(value).unwrap(); - }, - WebGLCommand::GetSamplerParameterFloat(sampler_id, pname, ref sender) => { - let value = unsafe { gl.get_sampler_parameter_f32(sampler_id.glow(), pname) }; - sender.send(value).unwrap(); - }, - WebGLCommand::BindBufferBase(target, index, id) => { - // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210 - // BindBufferBase/Range will fail (on some drivers) if the buffer name has - // never been bound. (GenBuffers makes a name, but BindBuffer initializes - // that name as a real buffer object) - let id = id.map(WebGLBufferId::glow); - unsafe { - gl.bind_buffer(target, id); - gl.bind_buffer(target, None); - gl.bind_buffer_base(target, index, id); - } - }, - WebGLCommand::BindBufferRange(target, index, id, offset, size) => { - // https://searchfox.org/mozilla-central/rev/13b081a62d3f3e3e3120f95564529257b0bf451c/dom/canvas/WebGLContextBuffers.cpp#208-210 - // BindBufferBase/Range will fail (on some drivers) if the buffer name has - // never been bound. (GenBuffers makes a name, but BindBuffer initializes - // that name as a real buffer object) - let id = id.map(WebGLBufferId::glow); - unsafe { - gl.bind_buffer(target, id); - gl.bind_buffer(target, None); - gl.bind_buffer_range(target, index, id, offset as i32, size as i32); - } - }, - WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_f32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferiv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_i32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferuiv(buffer, draw_buffer, ref value) => unsafe { - gl.clear_buffer_u32_slice(buffer, draw_buffer as u32, value) - }, - WebGLCommand::ClearBufferfi(buffer, draw_buffer, depth, stencil) => unsafe { - gl.clear_buffer_depth_stencil(buffer, draw_buffer as u32, depth, stencil) - }, - WebGLCommand::InvalidateFramebuffer(target, ref attachments) => unsafe { - gl.invalidate_framebuffer(target, attachments) - }, - WebGLCommand::InvalidateSubFramebuffer(target, ref attachments, x, y, w, h) => unsafe { - gl.invalidate_sub_framebuffer(target, attachments, x, y, w, h) - }, - WebGLCommand::FramebufferTextureLayer(target, attachment, tex_id, level, layer) => { - let tex_id = tex_id.map(WebGLTextureId::glow); - let attach = |attachment| unsafe { - gl.framebuffer_texture_layer(target, attachment, tex_id, level, layer) - }; - - if attachment == gl::DEPTH_STENCIL_ATTACHMENT { - attach(gl::DEPTH_ATTACHMENT); - attach(gl::STENCIL_ATTACHMENT); - } else { - attach(attachment) - } - }, - WebGLCommand::ReadBuffer(buffer) => unsafe { gl.read_buffer(buffer) }, - WebGLCommand::DrawBuffers(ref buffers) => unsafe { gl.draw_buffers(buffers) }, - } - - // If debug asertions are enabled, then check the error state. - #[cfg(debug_assertions)] - { - let error = unsafe { gl.get_error() }; - if error != gl::NO_ERROR { - error!("Last GL operation failed: {:?}", command); - if error == gl::INVALID_FRAMEBUFFER_OPERATION { - let framebuffer_bindings = - unsafe { gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING) }; - debug!( - "(thread {:?}) Current draw framebuffer binding: {:?}", - ::std::thread::current().id(), - framebuffer_bindings - ); - } - #[cfg(feature = "webgl_backtrace")] - { - error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace); - if let Some(backtrace) = _backtrace.js_backtrace { - error!("JS backtrace from failed WebGL API:\n{}", backtrace); - } - } - // TODO(servo#30568) revert to panic!() once underlying bug is fixed - log::warn!( - "debug assertion failed! Unexpected WebGL error: 0x{:x} ({}) [{:?}]", - error, - error, - command - ); - } - } - } - - fn initialize_framebuffer(gl: &Gl, state: &GLState, color: bool, depth: bool, stencil: bool) { - let bits = [ - (color, gl::COLOR_BUFFER_BIT), - (depth, gl::DEPTH_BUFFER_BIT), - (stencil, gl::STENCIL_BUFFER_BIT), - ] - .iter() - .fold(0, |bits, &(enabled, bit)| { - bits | if enabled { bit } else { 0 } - }); - - unsafe { - gl.disable(gl::SCISSOR_TEST); - gl.color_mask(true, true, true, true); - gl.clear_color(0., 0., 0., 0.); - gl.depth_mask(true); - gl.clear_depth(1.); - gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF); - gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF); - gl.clear_stencil(0); - gl.clear(bits); - } - - state.restore_invariant(gl); - } - - fn link_program(gl: &Gl, program: WebGLProgramId) -> ProgramLinkInfo { - unsafe { gl.link_program(program.glow()) }; - let linked = unsafe { gl.get_program_link_status(program.glow()) }; - if !linked { - return ProgramLinkInfo { - linked: false, - active_attribs: vec![].into(), - active_uniforms: vec![].into(), - active_uniform_blocks: vec![].into(), - transform_feedback_length: Default::default(), - transform_feedback_mode: Default::default(), - }; - } - let num_active_attribs = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_ATTRIBUTES) }; - let active_attribs = (0..num_active_attribs as u32) - .map(|i| { - let active_attribute = - unsafe { gl.get_active_attribute(program.glow(), i) }.unwrap(); - let name = &active_attribute.name; - let location = if name.starts_with("gl_") { - None - } else { - unsafe { gl.get_attrib_location(program.glow(), name) } - }; - ActiveAttribInfo { - name: from_name_in_compiled_shader(name), - size: active_attribute.size, - type_: active_attribute.atype, - location, - } - }) - .collect::<Vec<_>>() - .into(); - - let num_active_uniforms = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) }; - let active_uniforms = (0..num_active_uniforms as u32) - .map(|i| { - let active_uniform = unsafe { gl.get_active_uniform(program.glow(), i) }.unwrap(); - let is_array = active_uniform.name.ends_with("[0]"); - let active_uniform_name = active_uniform - .name - .strip_suffix("[0]") - .unwrap_or_else(|| &active_uniform.name); - ActiveUniformInfo { - base_name: from_name_in_compiled_shader(active_uniform_name).into(), - size: if is_array { - Some(active_uniform.size) - } else { - None - }, - type_: active_uniform.utype, - bind_index: None, - } - }) - .collect::<Vec<_>>() - .into(); - - let num_active_uniform_blocks = - unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORM_BLOCKS) }; - let active_uniform_blocks = (0..num_active_uniform_blocks as u32) - .map(|i| { - let name = unsafe { gl.get_active_uniform_block_name(program.glow(), i) }; - let size = unsafe { - gl.get_active_uniform_block_parameter_i32( - program.glow(), - i, - gl::UNIFORM_BLOCK_DATA_SIZE, - ) - }; - ActiveUniformBlockInfo { name, size } - }) - .collect::<Vec<_>>() - .into(); - - let transform_feedback_length = unsafe { - gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_VARYINGS) - }; - let transform_feedback_mode = unsafe { - gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_BUFFER_MODE) - }; - - ProgramLinkInfo { - linked: true, - active_attribs, - active_uniforms, - active_uniform_blocks, - transform_feedback_length, - transform_feedback_mode, - } - } - - fn finish(gl: &Gl, chan: &WebGLSender<()>) { - unsafe { gl.finish() }; - chan.send(()).unwrap(); - } - - fn shader_precision_format( - gl: &Gl, - shader_type: u32, - precision_type: u32, - chan: &WebGLSender<(i32, i32, i32)>, - ) { - let ShaderPrecisionFormat { - range_min, - range_max, - precision, - } = unsafe { - gl.get_shader_precision_format(shader_type, precision_type) - .unwrap_or_else(|| { - ShaderPrecisionFormat::common_desktop_hardware( - precision_type, - gl.version().is_embedded, - ) - }) - }; - chan.send((range_min, range_max, precision)).unwrap(); - } - - fn get_extensions(gl: &Gl, chan: &WebGLSender<String>) { - let mut ext_count = [0]; - unsafe { - gl.get_parameter_i32_slice(gl::NUM_EXTENSIONS, &mut ext_count); - } - // Fall back to the depricated extensions API if that fails - if unsafe { gl.get_error() } != gl::NO_ERROR { - chan.send(unsafe { gl.get_parameter_string(gl::EXTENSIONS) }) - .unwrap(); - return; - } - let ext_count = ext_count[0] as usize; - let mut extensions = Vec::with_capacity(ext_count); - for idx in 0..ext_count { - extensions.push(unsafe { gl.get_parameter_indexed_string(gl::EXTENSIONS, idx as u32) }) - } - let extensions = extensions.join(" "); - chan.send(extensions).unwrap(); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 - fn get_framebuffer_attachment_parameter( - gl: &Gl, - target: u32, - attachment: u32, - pname: u32, - chan: &WebGLSender<i32>, - ) { - let parameter = - unsafe { gl.get_framebuffer_attachment_parameter_i32(target, attachment, pname) }; - chan.send(parameter).unwrap(); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 - fn get_renderbuffer_parameter(gl: &Gl, target: u32, pname: u32, chan: &WebGLSender<i32>) { - let parameter = unsafe { gl.get_renderbuffer_parameter_i32(target, pname) }; - chan.send(parameter).unwrap(); - } - - fn uniform_location(gl: &Gl, program_id: WebGLProgramId, name: &str, chan: &WebGLSender<i32>) { - let location = unsafe { - gl.get_uniform_location(program_id.glow(), &to_name_in_compiled_shader(name)) - }; - // (#34300): replace this with WebGLUniformId - chan.send(location.map(|l| l.0).unwrap_or_default() as i32) - .unwrap(); - } - - fn shader_info_log(gl: &Gl, shader_id: WebGLShaderId, chan: &WebGLSender<String>) { - let log = unsafe { gl.get_shader_info_log(shader_id.glow()) }; - chan.send(log).unwrap(); - } - - fn program_info_log(gl: &Gl, program_id: WebGLProgramId, chan: &WebGLSender<String>) { - let log = unsafe { gl.get_program_info_log(program_id.glow()) }; - chan.send(log).unwrap(); - } - - fn create_buffer(gl: &Gl, chan: &WebGLSender<Option<WebGLBufferId>>) { - let buffer = unsafe { gl.create_buffer() } - .ok() - .map(WebGLBufferId::from_glow); - chan.send(buffer).unwrap(); - } - - fn create_framebuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLFramebufferId>>) { - let framebuffer = unsafe { gl.create_framebuffer() } - .ok() - .map(WebGLFramebufferId::from_glow); - chan.send(framebuffer).unwrap(); - } - - fn create_renderbuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLRenderbufferId>>) { - let renderbuffer = unsafe { gl.create_renderbuffer() } - .ok() - .map(WebGLRenderbufferId::from_glow); - chan.send(renderbuffer).unwrap(); - } - - fn create_texture(gl: &Gl, chan: &WebGLSender<Option<WebGLTextureId>>) { - let texture = unsafe { gl.create_texture() } - .ok() - .map(WebGLTextureId::from_glow); - chan.send(texture).unwrap(); - } - - fn create_program(gl: &Gl, chan: &WebGLSender<Option<WebGLProgramId>>) { - let program = unsafe { gl.create_program() } - .ok() - .map(WebGLProgramId::from_glow); - chan.send(program).unwrap(); - } - - fn create_shader(gl: &Gl, shader_type: u32, chan: &WebGLSender<Option<WebGLShaderId>>) { - let shader = unsafe { gl.create_shader(shader_type) } - .ok() - .map(WebGLShaderId::from_glow); - chan.send(shader).unwrap(); - } - - fn create_vertex_array(gl: &Gl) -> Option<WebGLVertexArrayId> { - let vao = unsafe { gl.create_vertex_array() } - .ok() - .map(WebGLVertexArrayId::from_glow); - if vao.is_none() { - let code = unsafe { gl.get_error() }; - warn!("Failed to create vertex array with error code {:x}", code); - } - vao - } - - fn bind_vertex_array(gl: &Gl, vao: Option<NativeVertexArray>) { - unsafe { gl.bind_vertex_array(vao) } - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - } - - fn delete_vertex_array(gl: &Gl, vao: WebGLVertexArrayId) { - unsafe { gl.delete_vertex_array(vao.glow()) }; - debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR); - } - - #[inline] - fn bind_framebuffer( - gl: &Gl, - target: u32, - request: WebGLFramebufferBindingRequest, - ctx: &Context, - device: &Device, - state: &mut GLState, - ) { - let id = match request { - WebGLFramebufferBindingRequest::Explicit(id) => Some(id.glow()), - WebGLFramebufferBindingRequest::Default => { - device - .context_surface_info(ctx) - .unwrap() - .expect("No surface attached!") - .framebuffer_object - }, - }; - - debug!("WebGLImpl::bind_framebuffer: {:?}", id); - unsafe { gl.bind_framebuffer(target, id) }; - - if (target == gl::FRAMEBUFFER) || (target == gl::DRAW_FRAMEBUFFER) { - state.drawing_to_default_framebuffer = - request == WebGLFramebufferBindingRequest::Default; - state.restore_invariant(gl); - } - } - - #[inline] - fn compile_shader(gl: &Gl, shader_id: WebGLShaderId, source: &str) { - unsafe { - gl.shader_source(shader_id.glow(), source); - gl.compile_shader(shader_id.glow()); - } - } -} - -/// ANGLE adds a `_u` prefix to variable names: -/// -/// <https://chromium.googlesource.com/angle/angle/+/855d964bd0d05f6b2cb303f625506cf53d37e94f> -/// -/// To avoid hard-coding this we would need to use the `sh::GetAttributes` and `sh::GetUniforms` -/// API to look up the `x.name` and `x.mappedName` members. -const ANGLE_NAME_PREFIX: &str = "_u"; - -/// Adds `_u` prefix to variable names -fn to_name_in_compiled_shader(s: &str) -> String { - map_dot_separated(s, |s, mapped| { - mapped.push_str(ANGLE_NAME_PREFIX); - mapped.push_str(s); - }) -} - -/// Removes `_u` prefix from variable names -fn from_name_in_compiled_shader(s: &str) -> String { - map_dot_separated(s, |s, mapped| { - mapped.push_str(if let Some(stripped) = s.strip_prefix(ANGLE_NAME_PREFIX) { - stripped - } else { - s - }) - }) -} - -fn map_dot_separated<F: Fn(&str, &mut String)>(s: &str, f: F) -> String { - let mut iter = s.split('.'); - let mut mapped = String::new(); - f(iter.next().unwrap(), &mut mapped); - for s in iter { - mapped.push('.'); - f(s, &mut mapped); - } - mapped -} - -#[allow(clippy::too_many_arguments)] -fn prepare_pixels( - internal_format: TexFormat, - data_type: TexDataType, - size: Size2D<u32>, - unpacking_alignment: u32, - alpha_treatment: Option<AlphaTreatment>, - y_axis_treatment: YAxisTreatment, - pixel_format: Option<PixelFormat>, - mut pixels: Cow<[u8]>, -) -> Cow<[u8]> { - match alpha_treatment { - Some(AlphaTreatment::Premultiply) => { - if let Some(pixel_format) = pixel_format { - match pixel_format { - PixelFormat::BGRA8 | PixelFormat::RGBA8 => {}, - _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), - } - premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut()); - } else { - premultiply_inplace(internal_format, data_type, pixels.to_mut()); - } - }, - Some(AlphaTreatment::Unmultiply) => { - assert!(pixel_format.is_some()); - unmultiply_inplace::<false>(pixels.to_mut()); - }, - None => {}, - } - - if let Some(pixel_format) = pixel_format { - pixels = image_to_tex_image_data( - pixel_format, - internal_format, - data_type, - pixels.into_owned(), - ) - .into(); - } - - if y_axis_treatment == YAxisTreatment::Flipped { - // FINISHME: Consider doing premultiply and flip in a single mutable Vec. - pixels = flip_pixels_y( - internal_format, - data_type, - size.width as usize, - size.height as usize, - unpacking_alignment as usize, - pixels.into_owned(), - ) - .into(); - } - - pixels -} - -/// Translates an image in rgba8 (red in the first byte) format to -/// the format that was requested of TexImage. -fn image_to_tex_image_data( - pixel_format: PixelFormat, - format: TexFormat, - data_type: TexDataType, - mut pixels: Vec<u8>, -) -> Vec<u8> { - // hint for vector allocation sizing. - let pixel_count = pixels.len() / 4; - - match pixel_format { - PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels), - PixelFormat::RGBA8 => {}, - _ => unimplemented!("unsupported pixel format ({:?})", pixel_format), - } - - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) | - (TexFormat::RGBA8, TexDataType::UnsignedByte) => pixels, - (TexFormat::RGB, TexDataType::UnsignedByte) | - (TexFormat::RGB8, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let rgb = { - let rgb = &pixels[i * 4..i * 4 + 3]; - [rgb[0], rgb[1], rgb[2]] - }; - pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb); - } - pixels.truncate(pixel_count * 3); - pixels - }, - (TexFormat::Alpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4 + 3]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::Luminance, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let p = pixels[i * 4]; - pixels[i] = p; - } - pixels.truncate(pixel_count); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for i in 0..pixel_count { - let (lum, a) = { - let rgba = &pixels[i * 4..i * 4 + 4]; - (rgba[0], rgba[3]) - }; - pixels[i * 2] = lum; - pixels[i * 2 + 1] = a; - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - ((rgba[0] as u16 & 0xf0) << 8) | - ((rgba[1] as u16 & 0xf0) << 4) | - (rgba[2] as u16 & 0xf0) | - ((rgba[3] as u16 & 0xf0) >> 4) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for i in 0..pixel_count { - let p = { - let rgba = &pixels[i * 4..i * 4 + 4]; - ((rgba[0] as u16 & 0xf8) << 8) | - ((rgba[1] as u16 & 0xf8) << 3) | - ((rgba[2] as u16 & 0xf8) >> 2) | - ((rgba[3] as u16) >> 7) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGB, TexDataType::UnsignedShort565) => { - for i in 0..pixel_count { - let p = { - let rgb = &pixels[i * 4..i * 4 + 3]; - ((rgb[0] as u16 & 0xf8) << 8) | - ((rgb[1] as u16 & 0xfc) << 3) | - ((rgb[2] as u16 & 0xf8) >> 3) - }; - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::RGBA, TexDataType::Float) | (TexFormat::RGBA32f, TexDataType::Float) => { - let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16); - for rgba8 in pixels.chunks(4) { - rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - rgbaf32 - }, - - (TexFormat::RGB, TexDataType::Float) | (TexFormat::RGB32f, TexDataType::Float) => { - let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12); - for rgba8 in pixels.chunks(4) { - rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap(); - rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap(); - } - rgbf32 - }, - - (TexFormat::Alpha, TexDataType::Float) | (TexFormat::Alpha32f, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[3] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::Luminance, TexDataType::Float) | - (TexFormat::Luminance32f, TexDataType::Float) => { - for rgba8 in pixels.chunks_mut(4) { - let p = rgba8[0] as f32; - NativeEndian::write_f32(rgba8, p); - } - pixels - }, - - (TexFormat::LuminanceAlpha, TexDataType::Float) | - (TexFormat::LuminanceAlpha32f, TexDataType::Float) => { - let mut data = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap(); - data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap(); - } - data - }, - - (TexFormat::RGBA, TexDataType::HalfFloat) | - (TexFormat::RGBA16f, TexDataType::HalfFloat) => { - let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8); - for rgba8 in pixels.chunks(4) { - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits()) - .unwrap(); - rgbaf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).to_bits()) - .unwrap(); - } - rgbaf16 - }, - - (TexFormat::RGB, TexDataType::HalfFloat) | (TexFormat::RGB16f, TexDataType::HalfFloat) => { - let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6); - for rgba8 in pixels.chunks(4) { - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits()) - .unwrap(); - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits()) - .unwrap(); - rgbf16 - .write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits()) - .unwrap(); - } - rgbf16 - }, - (TexFormat::Alpha, TexDataType::HalfFloat) | - (TexFormat::Alpha16f, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4 + 3] as f32).to_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::Luminance, TexDataType::HalfFloat) | - (TexFormat::Luminance16f, TexDataType::HalfFloat) => { - for i in 0..pixel_count { - let p = f16::from_f32(pixels[i * 4] as f32).to_bits(); - NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p); - } - pixels.truncate(pixel_count * 2); - pixels - }, - (TexFormat::LuminanceAlpha, TexDataType::HalfFloat) | - (TexFormat::LuminanceAlpha16f, TexDataType::HalfFloat) => { - for rgba8 in pixels.chunks_mut(4) { - let lum = f16::from_f32(rgba8[0] as f32).to_bits(); - let a = f16::from_f32(rgba8[3] as f32).to_bits(); - NativeEndian::write_u16(&mut rgba8[0..2], lum); - NativeEndian::write_u16(&mut rgba8[2..4], a); - } - pixels - }, - - // Validation should have ensured that we only hit the - // above cases, but we haven't turned the (format, type) - // into an enum yet so there's a default case here. - _ => unreachable!("Unsupported formats {:?} {:?}", format, data_type), - } -} - -fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) { - match (format, data_type) { - (TexFormat::RGBA, TexDataType::UnsignedByte) => { - pixels::rgba8_premultiply_inplace(pixels); - }, - (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { - for la in pixels.chunks_mut(2) { - la[0] = pixels::multiply_u8_color(la[0], la[1]); - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { - for rgba in pixels.chunks_mut(2) { - if NativeEndian::read_u16(rgba) & 1 == 0 { - NativeEndian::write_u16(rgba, 0); - } - } - }, - (TexFormat::RGBA, TexDataType::UnsignedShort4444) => { - for rgba in pixels.chunks_mut(2) { - let pix = NativeEndian::read_u16(rgba); - let extend_to_8_bits = |val| (val | (val << 4)) as u8; - let r = extend_to_8_bits((pix >> 12) & 0x0f); - let g = extend_to_8_bits((pix >> 8) & 0x0f); - let b = extend_to_8_bits((pix >> 4) & 0x0f); - let a = extend_to_8_bits(pix & 0x0f); - NativeEndian::write_u16( - rgba, - (((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8) | - (((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4) | - ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) | - ((a & 0x0f) as u16), - ); - } - }, - // Other formats don't have alpha, so return their data untouched. - _ => {}, - } -} - -/// Flips the pixels in the Vec on the Y axis. -fn flip_pixels_y( - internal_format: TexFormat, - data_type: TexDataType, - width: usize, - height: usize, - unpacking_alignment: usize, - pixels: Vec<u8>, -) -> Vec<u8> { - let cpp = (data_type.element_size() * internal_format.components() / - data_type.components_per_element()) as usize; - - let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1); - - let mut flipped = Vec::<u8>::with_capacity(pixels.len()); - - for y in 0..height { - let flipped_y = height - 1 - y; - let start = flipped_y * stride; - - flipped.extend_from_slice(&pixels[start..(start + width * cpp)]); - flipped.extend(vec![0u8; stride - width * cpp]); - } - - flipped -} - -// Clamp a size to the current GL context's max viewport -fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> { - let mut max_viewport = [i32::MAX, i32::MAX]; - let mut max_renderbuffer = [i32::MAX]; - - unsafe { - gl.get_parameter_i32_slice(gl::MAX_VIEWPORT_DIMS, &mut max_viewport); - gl.get_parameter_i32_slice(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer); - debug_assert_eq!(gl.get_error(), gl::NO_ERROR); - } - Size2D::new( - size.width - .min(max_viewport[0] as u32) - .min(max_renderbuffer[0] as u32) - .max(1), - size.height - .min(max_viewport[1] as u32) - .min(max_renderbuffer[0] as u32) - .max(1), - ) -} - -trait ToSurfmanVersion { - fn to_surfman_version(self, api_type: GlType) -> GLVersion; -} - -impl ToSurfmanVersion for WebGLVersion { - fn to_surfman_version(self, api_type: GlType) -> GLVersion { - if api_type == GlType::Gles { - return GLVersion::new(3, 0); - } - match self { - // We make use of GL_PACK_PIXEL_BUFFER, which needs at least GL2.1 - // We make use of compatibility mode, which needs at most GL3.0 - WebGLVersion::WebGL1 => GLVersion::new(2, 1), - // The WebGL2 conformance tests use std140 layout, which needs at GL3.1 - WebGLVersion::WebGL2 => GLVersion::new(3, 2), - } - } -} - -trait SurfmanContextAttributeFlagsConvert { - fn to_surfman_context_attribute_flags( - &self, - webgl_version: WebGLVersion, - api_type: GlType, - ) -> ContextAttributeFlags; -} - -impl SurfmanContextAttributeFlagsConvert for GLContextAttributes { - fn to_surfman_context_attribute_flags( - &self, - webgl_version: WebGLVersion, - api_type: GlType, - ) -> ContextAttributeFlags { - let mut flags = ContextAttributeFlags::empty(); - flags.set(ContextAttributeFlags::ALPHA, self.alpha); - flags.set(ContextAttributeFlags::DEPTH, self.depth); - flags.set(ContextAttributeFlags::STENCIL, self.stencil); - if (webgl_version == WebGLVersion::WebGL1) && (api_type == GlType::Gl) { - flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true); - } - flags - } -} - -bitflags! { - struct FramebufferRebindingFlags: u8 { - const REBIND_READ_FRAMEBUFFER = 0x1; - const REBIND_DRAW_FRAMEBUFFER = 0x2; - } -} - -struct FramebufferRebindingInfo { - flags: FramebufferRebindingFlags, - viewport: [GLint; 4], -} - -impl FramebufferRebindingInfo { - fn detect(device: &Device, context: &Context, gl: &Gl) -> FramebufferRebindingInfo { - unsafe { - let read_framebuffer = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING); - let draw_framebuffer = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING); - - let context_surface_framebuffer = device - .context_surface_info(context) - .unwrap() - .unwrap() - .framebuffer_object; - - let mut flags = FramebufferRebindingFlags::empty(); - if context_surface_framebuffer == read_framebuffer { - flags.insert(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER); - } - if context_surface_framebuffer == draw_framebuffer { - flags.insert(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER); - } - - let mut viewport = [0; 4]; - gl.get_parameter_i32_slice(gl::VIEWPORT, &mut viewport); - - FramebufferRebindingInfo { flags, viewport } - } - } - - fn apply(self, device: &Device, context: &Context, gl: &Gl) { - if self.flags.is_empty() { - return; - } - - let context_surface_framebuffer = device - .context_surface_info(context) - .unwrap() - .unwrap() - .framebuffer_object; - if self - .flags - .contains(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER) - { - unsafe { gl.bind_framebuffer(gl::READ_FRAMEBUFFER, context_surface_framebuffer) }; - } - if self - .flags - .contains(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER) - { - unsafe { gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, context_surface_framebuffer) }; - } - - unsafe { - gl.viewport( - self.viewport[0], - self.viewport[1], - self.viewport[2], - self.viewport[3], - ) - }; - } -} diff --git a/components/canvas/webxr.rs b/components/canvas/webxr.rs deleted file mode 100644 index d43303e7393..00000000000 --- a/components/canvas/webxr.rs +++ /dev/null @@ -1,337 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::collections::HashMap; -use std::num::NonZeroU32; - -use canvas_traits::webgl::{ - WebGLContextId, WebGLMsg, WebGLSender, WebXRCommand, WebXRLayerManagerId, webgl_channel, -}; -use fnv::FnvHashMap; -use surfman::{Context, Device}; -use webxr::SurfmanGL as WebXRSurfman; -use webxr_api::{ - ContextId as WebXRContextId, Error as WebXRError, GLContexts as WebXRContexts, - GLTypes as WebXRTypes, LayerGrandManager as WebXRLayerGrandManager, - LayerGrandManagerAPI as WebXRLayerGrandManagerAPI, LayerId as WebXRLayerId, - LayerInit as WebXRLayerInit, LayerManager as WebXRLayerManager, - LayerManagerAPI as WebXRLayerManagerAPI, LayerManagerFactory as WebXRLayerManagerFactory, - SubImages as WebXRSubImages, -}; - -use crate::webgl_thread::{GLContextData, WebGLThread}; - -/// Bridge between WebGL and WebXR -pub(crate) struct WebXRBridge { - factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>, - managers: HashMap<WebXRLayerManagerId, Box<dyn WebXRLayerManagerAPI<WebXRSurfman>>>, - next_manager_id: NonZeroU32, -} - -impl WebXRBridge { - pub(crate) fn new(init: WebXRBridgeInit) -> WebXRBridge { - let WebXRBridgeInit { - factory_receiver, .. - } = init; - let managers = HashMap::new(); - let next_manager_id = NonZeroU32::MIN; - WebXRBridge { - factory_receiver, - managers, - next_manager_id, - } - } -} - -impl WebXRBridge { - #[allow(unsafe_code)] - pub(crate) fn create_layer_manager( - &mut self, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - ) -> Result<WebXRLayerManagerId, WebXRError> { - let factory = self - .factory_receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)?; - let manager = factory.build(device, contexts)?; - let manager_id = WebXRLayerManagerId::new(self.next_manager_id); - self.next_manager_id = self - .next_manager_id - .checked_add(1) - .expect("next_manager_id should not overflow"); - self.managers.insert(manager_id, manager); - Ok(manager_id) - } - - pub(crate) fn destroy_layer_manager(&mut self, manager_id: WebXRLayerManagerId) { - self.managers.remove(&manager_id); - } - - pub(crate) fn create_layer( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - layer_init: WebXRLayerInit, - ) -> Result<WebXRLayerId, WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.create_layer(device, contexts, context_id, layer_init) - } - - pub(crate) fn destroy_layer( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - layer_id: WebXRLayerId, - ) { - if let Some(manager) = self.managers.get_mut(&manager_id) { - manager.destroy_layer(device, contexts, context_id, layer_id); - } - } - - pub(crate) fn destroy_all_layers( - &mut self, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - context_id: WebXRContextId, - ) { - for manager in self.managers.values_mut() { - #[allow(clippy::unnecessary_to_owned)] // Needs mutable borrow later in destroy - for (other_id, layer_id) in manager.layers().to_vec() { - if other_id == context_id { - manager.destroy_layer(device, contexts, context_id, layer_id); - } - } - } - } - - pub(crate) fn begin_frame( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<Vec<WebXRSubImages>, WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.begin_frame(device, contexts, layers) - } - - pub(crate) fn end_frame( - &mut self, - manager_id: WebXRLayerManagerId, - device: &mut Device, - contexts: &mut dyn WebXRContexts<WebXRSurfman>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<(), WebXRError> { - let manager = self - .managers - .get_mut(&manager_id) - .ok_or(WebXRError::NoMatchingDevice)?; - manager.end_frame(device, contexts, layers) - } -} - -pub(crate) struct WebXRBridgeInit { - sender: WebGLSender<WebGLMsg>, - factory_receiver: crossbeam_channel::Receiver<WebXRLayerManagerFactory<WebXRSurfman>>, - factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>, -} - -impl WebXRBridgeInit { - pub(crate) fn new(sender: WebGLSender<WebGLMsg>) -> WebXRBridgeInit { - let (factory_sender, factory_receiver) = crossbeam_channel::unbounded(); - WebXRBridgeInit { - sender, - factory_sender, - factory_receiver, - } - } - - pub(crate) fn layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> { - WebXRLayerGrandManager::new(WebXRBridgeGrandManager { - sender: self.sender.clone(), - factory_sender: self.factory_sender.clone(), - }) - } -} - -struct WebXRBridgeGrandManager { - sender: WebGLSender<WebGLMsg>, - // WebXR layer manager factories use generic trait objects under the - // hood, which aren't deserializable (even using typetag) - // so we can't send them over the regular webgl channel. - // Fortunately, the webgl thread runs in the same process as - // the webxr threads, so we can use a crossbeam channel to send - // factories. - factory_sender: crossbeam_channel::Sender<WebXRLayerManagerFactory<WebXRSurfman>>, -} - -impl WebXRLayerGrandManagerAPI<WebXRSurfman> for WebXRBridgeGrandManager { - fn create_layer_manager( - &self, - factory: WebXRLayerManagerFactory<WebXRSurfman>, - ) -> Result<WebXRLayerManager, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self.factory_sender.send(factory); - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayerManager( - sender, - ))); - let sender = self.sender.clone(); - let manager_id = receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)??; - let layers = Vec::new(); - Ok(WebXRLayerManager::new(WebXRBridgeManager { - manager_id, - sender, - layers, - })) - } - - fn clone_layer_grand_manager(&self) -> WebXRLayerGrandManager<WebXRSurfman> { - WebXRLayerGrandManager::new(WebXRBridgeGrandManager { - sender: self.sender.clone(), - factory_sender: self.factory_sender.clone(), - }) - } -} - -struct WebXRBridgeManager { - sender: WebGLSender<WebGLMsg>, - manager_id: WebXRLayerManagerId, - layers: Vec<(WebXRContextId, WebXRLayerId)>, -} - -impl<GL: WebXRTypes> WebXRLayerManagerAPI<GL> for WebXRBridgeManager { - fn create_layer( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - context_id: WebXRContextId, - init: WebXRLayerInit, - ) -> Result<WebXRLayerId, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::CreateLayer( - self.manager_id, - context_id, - init, - sender, - ))); - let layer_id = receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)??; - self.layers.push((context_id, layer_id)); - Ok(layer_id) - } - - fn destroy_layer( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - context_id: WebXRContextId, - layer_id: WebXRLayerId, - ) { - self.layers.retain(|&ids| ids != (context_id, layer_id)); - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayer( - self.manager_id, - context_id, - layer_id, - ))); - } - - fn layers(&self) -> &[(WebXRContextId, WebXRLayerId)] { - &self.layers[..] - } - - fn begin_frame( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<Vec<WebXRSubImages>, WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::BeginFrame( - self.manager_id, - layers.to_vec(), - sender, - ))); - receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)? - } - - fn end_frame( - &mut self, - _: &mut GL::Device, - _: &mut dyn WebXRContexts<GL>, - layers: &[(WebXRContextId, WebXRLayerId)], - ) -> Result<(), WebXRError> { - let (sender, receiver) = webgl_channel().ok_or(WebXRError::CommunicationError)?; - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::EndFrame( - self.manager_id, - layers.to_vec(), - sender, - ))); - receiver - .recv() - .map_err(|_| WebXRError::CommunicationError)? - } -} - -impl Drop for WebXRBridgeManager { - fn drop(&mut self) { - let _ = self - .sender - .send(WebGLMsg::WebXRCommand(WebXRCommand::DestroyLayerManager( - self.manager_id, - ))); - } -} - -pub(crate) struct WebXRBridgeContexts<'a> { - pub(crate) contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>, - pub(crate) bound_context_id: &'a mut Option<WebGLContextId>, -} - -impl WebXRContexts<WebXRSurfman> for WebXRBridgeContexts<'_> { - fn context(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&mut Context> { - let data = WebGLThread::make_current_if_needed_mut( - device, - WebGLContextId::from(context_id), - self.contexts, - self.bound_context_id, - )?; - Some(&mut data.ctx) - } - - fn bindings(&mut self, device: &Device, context_id: WebXRContextId) -> Option<&glow::Context> { - let data = WebGLThread::make_current_if_needed( - device, - WebGLContextId::from(context_id), - self.contexts, - self.bound_context_id, - )?; - Some(&data.gl) - } -} |