aboutsummaryrefslogtreecommitdiffstats
path: root/components/canvas
diff options
context:
space:
mode:
Diffstat (limited to 'components/canvas')
-rw-r--r--components/canvas/Cargo.toml13
-rw-r--r--components/canvas/backend.rs243
-rw-r--r--components/canvas/canvas_data.rs506
-rw-r--r--components/canvas/canvas_paint_thread.rs359
-rw-r--r--components/canvas/lib.rs8
-rw-r--r--components/canvas/raqote_backend.rs532
-rw-r--r--components/canvas/webgl_limits.rs259
-rw-r--r--components/canvas/webgl_mode/inprocess.rs147
-rw-r--r--components/canvas/webgl_mode/mod.rs7
-rw-r--r--components/canvas/webgl_thread.rs3250
-rw-r--r--components/canvas/webxr.rs337
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)
- }
-}