Improve C API

I ran `cargo fmt` on the project, which in hindsight was a poor choice.
I'm not correcting it right now.
This commit is contained in:
Michael Pfaff 2024-08-09 03:13:29 -04:00
parent ca750dda7b
commit 5e41738e4c
108 changed files with 10547 additions and 6871 deletions

View File

@ -68,8 +68,10 @@ default-members = [
] ]
[patch.crates-io] [patch.crates-io]
font-kit = { git = "https://git.pfaff.dev/michael/font-kit" }
pathfinder_geometry = { path = "geometry" } pathfinder_geometry = { path = "geometry" }
pathfinder_simd = { path = "simd" } pathfinder_simd = { path = "simd" }
skribo = { git = "https://git.pfaff.dev/michael/skribo" }
[patch."https://github.com/servo/pathfinder"] [patch."https://github.com/servo/pathfinder"]
pathfinder_content = { path = "content" } pathfinder_content = { path = "content" }

View File

@ -6,18 +6,18 @@ edition = "2018"
[features] [features]
metal = ["dep:core-foundation", "dep:io-surface", "dep:metal", "dep:pathfinder_metal"] metal = ["dep:core-foundation", "dep:io-surface", "dep:metal", "dep:pathfinder_metal"]
svg = ["dep:pathfinder_svg", "dep:usvg"]
[lib] [lib]
crate-type = ["staticlib", "cdylib"] crate-type = ["staticlib", "cdylib"]
name = "pathfinder" name = "pathfinder"
[dependencies] [dependencies]
font-kit = "0.6" font-kit = "0.13"
foreign-types = "0.3" foreign-types = "0.3"
gl = "0.14" gl = "0.14"
libc = "0.2" libc = "0.2"
simple_logger = "4.3" simple_logger = "4.3"
usvg = "0.9"
[dependencies.log] [dependencies.log]
version = "0.4" version = "0.4"
@ -53,6 +53,11 @@ path = "../simd"
[dependencies.pathfinder_svg] [dependencies.pathfinder_svg]
path = "../svg" path = "../svg"
optional = true
[dependencies.usvg]
version = "0.10"
optional = true
[target.'cfg(target_os = "macos")'.dependencies.core-foundation] [target.'cfg(target_os = "macos")'.dependencies.core-foundation]
version = "0.6" version = "0.6"

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ keywords = ["pathfinder", "canvas", "vector", "graphics", "gpu"]
crate-type = ["rlib", "staticlib"] crate-type = ["rlib", "staticlib"]
[dependencies] [dependencies]
font-kit = { version = "0.6", optional = true } font-kit = { version = "0.13", optional = true }
[dependencies.pathfinder_color] [dependencies.pathfinder_color]
path = "../color" path = "../color"

View File

@ -10,14 +10,14 @@
//! A simple API for Pathfinder that mirrors a subset of HTML canvas. //! A simple API for Pathfinder that mirrors a subset of HTML canvas.
pub use pathfinder_color::{ColorF, ColorU, rgbaf, rgbau, rgbf, rgbu};
pub use pathfinder_color::{color_slice_to_u8_slice, u8_slice_to_color_slice, u8_vec_to_color_vec}; pub use pathfinder_color::{color_slice_to_u8_slice, u8_slice_to_color_slice, u8_vec_to_color_vec};
pub use pathfinder_color::{rgbaf, rgbau, rgbf, rgbu, ColorF, ColorU};
pub use pathfinder_content::fill::FillRule; pub use pathfinder_content::fill::FillRule;
pub use pathfinder_content::stroke::LineCap;
pub use pathfinder_content::outline::ArcDirection; pub use pathfinder_content::outline::ArcDirection;
pub use pathfinder_content::stroke::LineCap;
pub use pathfinder_geometry::rect::{RectF, RectI}; pub use pathfinder_geometry::rect::{RectF, RectI};
pub use pathfinder_geometry::transform2d::Transform2F; pub use pathfinder_geometry::transform2d::Transform2F;
pub use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i}; pub use pathfinder_geometry::vector::{vec2f, vec2i, IntoVector2F, Vector2F, Vector2I};
use pathfinder_content::dash::OutlineDash; use pathfinder_content::dash::OutlineDash;
use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter}; use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter};
@ -25,25 +25,25 @@ use pathfinder_content::gradient::Gradient;
use pathfinder_content::outline::{Contour, Outline}; use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::pattern::{Image, Pattern}; use pathfinder_content::pattern::{Image, Pattern};
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_content::stroke::{LineJoin as StrokeLineJoin}; use pathfinder_content::stroke::LineJoin as StrokeLineJoin;
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_renderer::paint::{Paint, PaintCompositeOp}; use pathfinder_renderer::paint::{Paint, PaintCompositeOp};
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene}; use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene};
use std::borrow::Cow; use std::borrow::Cow;
use std::default::Default; use std::default::Default;
use std::f32::consts::PI;
use std::f32; use std::f32;
use std::f32::consts::PI;
use std::fmt::{Debug, Error as FmtError, Formatter}; use std::fmt::{Debug, Error as FmtError, Formatter};
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
pub use text::CanvasFontContext; pub use text::CanvasFontContext;
#[cfg(feature = "pf-text")]
use skribo::FontCollection;
#[cfg(not(feature = "pf-text"))] #[cfg(not(feature = "pf-text"))]
use crate::text::FontCollection; use crate::text::FontCollection;
#[cfg(feature = "pf-text")]
use skribo::FontCollection;
#[cfg(feature = "pf-text")] #[cfg(feature = "pf-text")]
pub use text::TextMetrics; pub use text::TextMetrics;
@ -108,11 +108,16 @@ impl Canvas {
self.scene self.scene
} }
pub fn get_context_2d(self, canvas_font_context: CanvasFontContext) pub fn get_context_2d(
-> CanvasRenderingContext2D { self,
canvas_font_context: CanvasFontContext,
) -> CanvasRenderingContext2D {
#[cfg(feature = "pf-text")] #[cfg(feature = "pf-text")]
let default_font_collection = let default_font_collection = canvas_font_context
canvas_font_context.0.borrow().default_font_collection.clone(); .0
.borrow()
.default_font_collection
.clone();
#[cfg(not(feature = "pf-text"))] #[cfg(not(feature = "pf-text"))]
let default_font_collection = Arc::new(FontCollection); let default_font_collection = Arc::new(FontCollection);
CanvasRenderingContext2D { CanvasRenderingContext2D {
@ -131,7 +136,8 @@ impl Canvas {
/// This resets the canvas' bounds. Do not call this mid-draw. /// This resets the canvas' bounds. Do not call this mid-draw.
pub fn set_size(&mut self, new_size: Vector2F) { pub fn set_size(&mut self, new_size: Vector2F) {
self.scene.set_bounds(RectF::default()); self.scene.set_bounds(RectF::default());
self.scene.set_view_box(RectF::new(Vector2F::zero(), new_size)); self.scene
.set_view_box(RectF::new(Vector2F::zero(), new_size));
} }
} }
@ -272,12 +278,18 @@ impl CanvasRenderingContext2D {
// Fill and stroke styles // Fill and stroke styles
#[inline] #[inline]
pub fn set_fill_style<FS>(&mut self, new_fill_style: FS) where FS: Into<FillStyle> { pub fn set_fill_style<FS>(&mut self, new_fill_style: FS)
where
FS: Into<FillStyle>,
{
self.current_state.fill_paint = new_fill_style.into().into_paint(); self.current_state.fill_paint = new_fill_style.into().into_paint();
} }
#[inline] #[inline]
pub fn set_stroke_style<FS>(&mut self, new_stroke_style: FS) where FS: Into<FillStyle> { pub fn set_stroke_style<FS>(&mut self, new_stroke_style: FS)
where
FS: Into<FillStyle>,
{
self.current_state.stroke_paint = new_stroke_style.into().into_paint(); self.current_state.stroke_paint = new_stroke_style.into().into_paint();
} }
@ -336,9 +348,11 @@ impl CanvasRenderingContext2D {
let mut outline = path.into_outline(); let mut outline = path.into_outline();
if !self.current_state.line_dash.is_empty() { if !self.current_state.line_dash.is_empty() {
let mut dash = OutlineDash::new(&outline, let mut dash = OutlineDash::new(
&self.current_state.line_dash, &outline,
self.current_state.line_dash_offset); &self.current_state.line_dash,
self.current_state.line_dash_offset,
);
dash.dash(); dash.dash();
outline = dash.into_outline(); outline = dash.into_outline();
} }
@ -373,23 +387,29 @@ impl CanvasRenderingContext2D {
let transform = self.current_state.transform; let transform = self.current_state.transform;
let clip_path = self.current_state.clip_path; let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); let blend_mode = self
.current_state
.global_composite_operation
.to_blend_mode();
outline.transform(&transform); outline.transform(&transform);
if !self.current_state.shadow_color.is_fully_transparent() { if !self.current_state.shadow_color.is_fully_transparent() {
let mut outline = outline.clone(); let mut outline = outline.clone();
outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset)); outline.transform(&Transform2F::from_translation(
self.current_state.shadow_offset,
));
let shadow_blur_info = let shadow_blur_info = push_shadow_blur_render_targets_if_needed(
push_shadow_blur_render_targets_if_needed(&mut self.canvas.scene, &mut self.canvas.scene,
&self.current_state, &self.current_state,
outline.bounds()); outline.bounds(),
);
if let Some(ref shadow_blur_info) = shadow_blur_info { if let Some(ref shadow_blur_info) = shadow_blur_info {
outline.transform(&Transform2F::from_translation(-shadow_blur_info.bounds outline.transform(&Transform2F::from_translation(
.origin() -shadow_blur_info.bounds.origin().to_f32(),
.to_f32())); ));
} }
// Per spec the shadow must respect the alpha of the shadowed path, but otherwise have // Per spec the shadow must respect the alpha of the shadowed path, but otherwise have
@ -412,9 +432,11 @@ impl CanvasRenderingContext2D {
path.set_blend_mode(blend_mode); path.set_blend_mode(blend_mode);
self.canvas.scene.push_draw_path(path); self.canvas.scene.push_draw_path(path);
composite_shadow_blur_render_targets_if_needed(&mut self.canvas.scene, composite_shadow_blur_render_targets_if_needed(
shadow_blur_info, &mut self.canvas.scene,
clip_path); shadow_blur_info,
clip_path,
);
} }
let mut path = DrawPath::new(outline, paint_id); let mut path = DrawPath::new(outline, paint_id);
@ -423,10 +445,11 @@ impl CanvasRenderingContext2D {
path.set_blend_mode(blend_mode); path.set_blend_mode(blend_mode);
self.canvas.scene.push_draw_path(path); self.canvas.scene.push_draw_path(path);
fn push_shadow_blur_render_targets_if_needed(scene: &mut Scene, fn push_shadow_blur_render_targets_if_needed(
current_state: &State, scene: &mut Scene,
outline_bounds: RectF) current_state: &State,
-> Option<ShadowBlurRenderTargetInfo> { outline_bounds: RectF,
) -> Option<ShadowBlurRenderTargetInfo> {
if current_state.shadow_blur == 0.0 { if current_state.shadow_blur == 0.0 {
return None; return None;
} }
@ -447,9 +470,11 @@ impl CanvasRenderingContext2D {
}) })
} }
fn composite_shadow_blur_render_targets_if_needed(scene: &mut Scene, fn composite_shadow_blur_render_targets_if_needed(
info: Option<ShadowBlurRenderTargetInfo>, scene: &mut Scene,
clip_path: Option<ClipPathId>) { info: Option<ShadowBlurRenderTargetInfo>,
clip_path: Option<ClipPathId>,
) {
let info = match info { let info = match info {
None => return, None => return,
Some(info) => info, Some(info) => info,
@ -460,15 +485,21 @@ impl CanvasRenderingContext2D {
paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32())); paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32()));
let sigma = info.sigma; let sigma = info.sigma;
paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma })); paint_x.set_filter(Some(PatternFilter::Blur {
paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma })); direction: BlurDirection::X,
sigma,
}));
paint_y.set_filter(Some(PatternFilter::Blur {
direction: BlurDirection::Y,
sigma,
}));
let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x)); let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x));
let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y)); let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y));
// TODO(pcwalton): Apply clip as necessary. // TODO(pcwalton): Apply clip as necessary.
let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0), let outline_x =
info.bounds.size().to_f32())); Outline::from_rect(RectF::new(vec2f(0.0, 0.0), info.bounds.size().to_f32()));
let path_x = DrawPath::new(outline_x, paint_id_x); let path_x = DrawPath::new(outline_x, paint_id_x);
let outline_y = Outline::from_rect(info.bounds.to_f32()); let outline_y = Outline::from_rect(info.bounds.to_f32());
let mut path_y = DrawPath::new(outline_y, paint_id_y); let mut path_y = DrawPath::new(outline_y, paint_id_y);
@ -479,7 +510,6 @@ impl CanvasRenderingContext2D {
scene.pop_render_target(); scene.pop_render_target();
scene.push_draw_path(path_y); scene.push_draw_path(path_y);
} }
} }
// Transformations // Transformations
@ -490,7 +520,10 @@ impl CanvasRenderingContext2D {
} }
#[inline] #[inline]
pub fn scale<S>(&mut self, scale: S) where S: IntoVector2F { pub fn scale<S>(&mut self, scale: S)
where
S: IntoVector2F,
{
self.current_state.transform *= Transform2F::from_scale(scale) self.current_state.transform *= Transform2F::from_scale(scale)
} }
@ -540,14 +573,20 @@ impl CanvasRenderingContext2D {
#[inline] #[inline]
pub fn draw_image<I, L>(&mut self, image: I, dest_location: L) pub fn draw_image<I, L>(&mut self, image: I, dest_location: L)
where I: CanvasImageSource, L: CanvasImageDestLocation { where
I: CanvasImageSource,
L: CanvasImageDestLocation,
{
let pattern = image.to_pattern(self, Transform2F::default()); let pattern = image.to_pattern(self, Transform2F::default());
let src_rect = RectF::new(vec2f(0.0, 0.0), pattern.size().to_f32()); let src_rect = RectF::new(vec2f(0.0, 0.0), pattern.size().to_f32());
self.draw_subimage(pattern, src_rect, dest_location) self.draw_subimage(pattern, src_rect, dest_location)
} }
pub fn draw_subimage<I, L>(&mut self, image: I, src_location: RectF, dest_location: L) pub fn draw_subimage<I, L>(&mut self, image: I, src_location: RectF, dest_location: L)
where I: CanvasImageSource, L: CanvasImageDestLocation { where
I: CanvasImageSource,
L: CanvasImageDestLocation,
{
let dest_size = dest_location.size().unwrap_or(src_location.size()); let dest_size = dest_location.size().unwrap_or(src_location.size());
let scale = dest_size / src_location.size(); let scale = dest_size / src_location.size();
let offset = dest_location.origin() - src_location.origin() * scale; let offset = dest_location.origin() - src_location.origin() * scale;
@ -563,7 +602,9 @@ impl CanvasRenderingContext2D {
// Pixel manipulation // Pixel manipulation
pub fn put_image_data<L>(&mut self, image_data: ImageData, dest_location: L) pub fn put_image_data<L>(&mut self, image_data: ImageData, dest_location: L)
where L: CanvasImageDestLocation { where
L: CanvasImageDestLocation,
{
let origin = dest_location.origin(); let origin = dest_location.origin();
let size = dest_location.size().unwrap_or(image_data.size.to_f32()); let size = dest_location.size().unwrap_or(image_data.size.to_f32());
let pattern = Pattern::from_image(image_data.into_image()); let pattern = Pattern::from_image(image_data.into_image());
@ -610,8 +651,11 @@ impl CanvasRenderingContext2D {
// Extensions // Extensions
pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F) pub fn create_pattern_from_canvas(
-> Pattern { &mut self,
canvas: Canvas,
transform: Transform2F,
) -> Pattern {
let subscene_size = canvas.size(); let subscene_size = canvas.size();
let subscene = canvas.into_scene(); let subscene = canvas.into_scene();
let render_target = RenderTarget::new(subscene_size, String::new()); let render_target = RenderTarget::new(subscene_size, String::new());
@ -724,7 +768,10 @@ pub struct Path2D {
impl Path2D { impl Path2D {
#[inline] #[inline]
pub fn new() -> Path2D { pub fn new() -> Path2D {
Path2D { outline: Outline::new(), current_contour: Contour::new() } Path2D {
outline: Outline::new(),
current_contour: Contour::new(),
}
} }
#[inline] #[inline]
@ -754,14 +801,17 @@ impl Path2D {
} }
#[inline] #[inline]
pub fn arc(&mut self, pub fn arc(
center: Vector2F, &mut self,
radius: f32, center: Vector2F,
start_angle: f32, radius: f32,
end_angle: f32, start_angle: f32,
direction: ArcDirection) { end_angle: f32,
direction: ArcDirection,
) {
let transform = Transform2F::from_scale(radius).translate(center); let transform = Transform2F::from_scale(radius).translate(center);
self.current_contour.push_arc(&transform, start_angle, end_angle, direction); self.current_contour
.push_arc(&transform, start_angle, end_angle, direction);
} }
#[inline] #[inline]
@ -775,10 +825,11 @@ impl Path2D {
let center = ctrl + bisector * (hypot / bisector.length()); let center = ctrl + bisector * (hypot / bisector.length());
let transform = Transform2F::from_scale(radius).translate(center); let transform = Transform2F::from_scale(radius).translate(center);
let chord = LineSegment2F::new(vu0.yx() * vec2f(-1.0, 1.0), vu1.yx() * vec2f( 1.0, -1.0)); let chord = LineSegment2F::new(vu0.yx() * vec2f(-1.0, 1.0), vu1.yx() * vec2f(1.0, -1.0));
// FIXME(pcwalton): Is clockwise direction correct? // FIXME(pcwalton): Is clockwise direction correct?
self.current_contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); self.current_contour
.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
pub fn rect(&mut self, rect: RectF) { pub fn rect(&mut self, rect: RectF) {
@ -790,17 +841,23 @@ impl Path2D {
self.current_contour.close(); self.current_contour.close();
} }
pub fn ellipse<A>(&mut self, pub fn ellipse<A>(
center: Vector2F, &mut self,
axes: A, center: Vector2F,
rotation: f32, axes: A,
start_angle: f32, rotation: f32,
end_angle: f32) start_angle: f32,
where A: IntoVector2F { end_angle: f32,
) where
A: IntoVector2F,
{
self.flush_current_contour(); self.flush_current_contour();
let transform = Transform2F::from_scale(axes).rotate(rotation).translate(center); let transform = Transform2F::from_scale(axes)
self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW); .rotate(rotation)
.translate(center);
self.current_contour
.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
if end_angle - start_angle >= 2.0 * PI { if end_angle - start_angle >= 2.0 * PI {
self.current_contour.close(); self.current_contour.close();
@ -826,7 +883,8 @@ impl Path2D {
fn flush_current_contour(&mut self) { fn flush_current_contour(&mut self) {
if !self.current_contour.is_empty() { if !self.current_contour.is_empty() {
self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new())); self.outline
.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
} }
} }
} }
@ -948,8 +1006,11 @@ pub enum ImageSmoothingQuality {
} }
pub trait CanvasImageSource { pub trait CanvasImageSource {
fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) fn to_pattern(
-> Pattern; self,
dest_context: &mut CanvasRenderingContext2D,
transform: Transform2F,
) -> Pattern;
} }
pub trait CanvasImageDestLocation { pub trait CanvasImageDestLocation {
@ -967,8 +1028,11 @@ impl CanvasImageSource for Pattern {
impl CanvasImageSource for Canvas { impl CanvasImageSource for Canvas {
#[inline] #[inline]
fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) fn to_pattern(
-> Pattern { self,
dest_context: &mut CanvasRenderingContext2D,
transform: Transform2F,
) -> Pattern {
dest_context.create_pattern_from_canvas(self, transform) dest_context.create_pattern_from_canvas(self, transform)
} }
} }
@ -1003,7 +1067,10 @@ pub struct ImageData {
impl ImageData { impl ImageData {
#[inline] #[inline]
pub fn new(size: Vector2I) -> ImageData { pub fn new(size: Vector2I) -> ImageData {
ImageData { data: vec![ColorU::transparent_black(); size.area() as usize], size } ImageData {
data: vec![ColorU::transparent_black(); size.area() as usize],
size,
}
} }
#[inline] #[inline]

View File

@ -3,8 +3,8 @@
// For this file only, any copyright is dedicated to the Public Domain. // For this file only, any copyright is dedicated to the Public Domain.
// https://creativecommons.org/publicdomain/zero/1.0/ // https://creativecommons.org/publicdomain/zero/1.0/
use pathfinder_geometry::vector::{Vector2F, vec2f};
use super::Path2D; use super::Path2D;
use pathfinder_geometry::vector::{vec2f, Vector2F};
#[test] #[test]
pub fn test_path2d_formatting() { pub fn test_path2d_formatting() {

View File

@ -10,7 +10,7 @@
use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline}; use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline};
use font_kit::canvas::RasterizationOptions; use font_kit::canvas::RasterizationOptions;
use font_kit::error::{GlyphLoadingError, FontLoadingError, SelectionError}; use font_kit::error::{FontLoadingError, GlyphLoadingError, SelectionError};
use font_kit::family_name::FamilyName; use font_kit::family_name::FamilyName;
use font_kit::handle::Handle; use font_kit::handle::Handle;
use font_kit::hinting::HintingOptions; use font_kit::hinting::HintingOptions;
@ -20,7 +20,7 @@ use font_kit::source::{Source, SystemSource};
use font_kit::sources::mem::MemSource; use font_kit::sources::mem::MemSource;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util; use pathfinder_geometry::util;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::PaintId; use pathfinder_renderer::paint::PaintId;
use pathfinder_text::{FontContext, FontRenderOptions, TextRenderMode}; use pathfinder_text::{FontContext, FontRenderOptions, TextRenderMode};
use skribo::{FontCollection, FontFamily, FontRef, Layout as SkriboLayout, TextStyle}; use skribo::{FontCollection, FontFamily, FontRef, Layout as SkriboLayout, TextStyle};
@ -36,8 +36,13 @@ impl CanvasRenderingContext2D {
/// fill the text that you passed into `measure_text()` with the layout-related style /// fill the text that you passed into `measure_text()` with the layout-related style
/// properties set at the time you called that function. This allows Pathfinder to skip having /// properties set at the time you called that function. This allows Pathfinder to skip having
/// to lay out the text again. /// to lay out the text again.
pub fn fill_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized { pub fn fill_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError>
let paint = self.current_state.resolve_paint(&self.current_state.fill_paint); where
T: ToTextLayout + ?Sized,
{
let paint = self
.current_state
.resolve_paint(&self.current_state.fill_paint);
let paint_id = self.canvas.scene.push_paint(&paint); let paint_id = self.canvas.scene.push_paint(&paint);
self.fill_or_stroke_text(text, position, paint_id, TextRenderMode::Fill) self.fill_or_stroke_text(text, position, paint_id, TextRenderMode::Fill)
} }
@ -48,8 +53,13 @@ impl CanvasRenderingContext2D {
/// stroke the text that you passed into `measure_text()` with the layout-related style /// stroke the text that you passed into `measure_text()` with the layout-related style
/// properties set at the time you called that function. This allows Pathfinder to skip having /// properties set at the time you called that function. This allows Pathfinder to skip having
/// to lay out the text again. /// to lay out the text again.
pub fn stroke_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized { pub fn stroke_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError>
let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint); where
T: ToTextLayout + ?Sized,
{
let paint = self
.current_state
.resolve_paint(&self.current_state.stroke_paint);
let paint_id = self.canvas.scene.push_paint(&paint); let paint_id = self.canvas.scene.push_paint(&paint);
let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style()); let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style());
self.fill_or_stroke_text(text, position, paint_id, render_mode) self.fill_or_stroke_text(text, position, paint_id, render_mode)
@ -60,39 +70,53 @@ impl CanvasRenderingContext2D {
/// As an extension, the returned `TextMetrics` object contains all the layout data for the /// As an extension, the returned `TextMetrics` object contains all the layout data for the
/// string and can be used in its place when calling `fill_text()` and `stroke_text()` to avoid /// string and can be used in its place when calling `fill_text()` and `stroke_text()` to avoid
/// needlessly performing layout multiple times. /// needlessly performing layout multiple times.
pub fn measure_text<T>(&self, text: &T) -> TextMetrics where T: ToTextLayout + ?Sized { pub fn measure_text<T>(&self, text: &T) -> TextMetrics
where
T: ToTextLayout + ?Sized,
{
text.layout(CanvasState(&self.current_state)).into_owned() text.layout(CanvasState(&self.current_state)).into_owned()
} }
fn fill_or_stroke_text<T>(&mut self, fn fill_or_stroke_text<T>(
text: &T, &mut self,
mut position: Vector2F, text: &T,
paint_id: PaintId, mut position: Vector2F,
render_mode: TextRenderMode) -> Result<(), GlyphLoadingError> paint_id: PaintId,
where T: ToTextLayout + ?Sized { render_mode: TextRenderMode,
) -> Result<(), GlyphLoadingError>
where
T: ToTextLayout + ?Sized,
{
let layout = text.layout(CanvasState(&self.current_state)); let layout = text.layout(CanvasState(&self.current_state));
let clip_path = self.current_state.clip_path; let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); let blend_mode = self
.current_state
.global_composite_operation
.to_blend_mode();
position += layout.text_origin(); position += layout.text_origin();
let transform = self.current_state.transform * Transform2F::from_translation(position); let transform = self.current_state.transform * Transform2F::from_translation(position);
self.canvas_font_context self.canvas_font_context
.0 .0
.borrow_mut() .borrow_mut()
.font_context .font_context
.push_layout(&mut self.canvas.scene, .push_layout(
&layout.skribo_layout, &mut self.canvas.scene,
&TextStyle { size: layout.font_size }, &layout.skribo_layout,
&FontRenderOptions { &TextStyle {
transform, size: layout.font_size,
render_mode, },
hinting_options: HintingOptions::None, &FontRenderOptions {
clip_path, transform,
blend_mode, render_mode,
paint_id, hinting_options: HintingOptions::None,
})?; clip_path,
blend_mode,
paint_id,
},
)?;
Ok(()) Ok(())
} }
@ -105,9 +129,12 @@ impl CanvasRenderingContext2D {
} }
#[inline] #[inline]
pub fn set_font<FC>(&mut self, font_collection: FC) -> Result<(), FontError> where FC: IntoFontCollection { pub fn set_font<FC>(&mut self, font_collection: FC) -> Result<(), FontError>
where
FC: IntoFontCollection,
{
let font_collection = font_collection.into_font_collection(&self.canvas_font_context)?; let font_collection = font_collection.into_font_collection(&self.canvas_font_context)?;
self.current_state.font_collection = font_collection; self.current_state.font_collection = font_collection;
Ok(()) Ok(())
} }
@ -155,7 +182,13 @@ pub trait ToTextLayout {
impl ToTextLayout for str { impl ToTextLayout for str {
fn layout(&self, state: CanvasState) -> Cow<TextMetrics> { fn layout(&self, state: CanvasState) -> Cow<TextMetrics> {
Cow::Owned(TextMetrics::layout(self, &state.0.font_collection, state.0.font_size, state.0.text_align, state.0.text_baseline)) Cow::Owned(TextMetrics::layout(
self,
&state.0.font_collection,
state.0.font_size,
state.0.text_align,
state.0.text_baseline,
))
} }
} }
@ -168,10 +201,12 @@ impl ToTextLayout for String {
impl ToTextLayout for Rc<SkriboLayout> { impl ToTextLayout for Rc<SkriboLayout> {
fn layout(&self, state: CanvasState) -> Cow<TextMetrics> { fn layout(&self, state: CanvasState) -> Cow<TextMetrics> {
Cow::Owned(TextMetrics::new((*self).clone(), Cow::Owned(TextMetrics::new(
state.0.font_size, (*self).clone(),
state.0.text_align, state.0.font_size,
state.0.text_baseline)) state.0.text_align,
state.0.text_baseline,
))
} }
} }
@ -203,8 +238,9 @@ pub(super) struct CanvasFontContextData {
impl CanvasFontContext { impl CanvasFontContext {
pub fn new(font_source: Arc<dyn Source>) -> CanvasFontContext { pub fn new(font_source: Arc<dyn Source>) -> CanvasFontContext {
let mut default_font_collection = FontCollection::new(); let mut default_font_collection = FontCollection::new();
if let Ok(default_font) = font_source.select_best_match(&[FamilyName::SansSerif], if let Ok(default_font) =
&Properties::new()) { font_source.select_best_match(&[FamilyName::SansSerif], &Properties::new())
{
if let Ok(default_font) = default_font.load() { if let Ok(default_font) = default_font.load() {
default_font_collection.add_family(FontFamily::new_from_font(default_font)); default_font_collection.add_family(FontFamily::new_from_font(default_font));
} }
@ -224,7 +260,10 @@ impl CanvasFontContext {
} }
/// A convenience method to create a font context with a set of in-memory fonts. /// A convenience method to create a font context with a set of in-memory fonts.
pub fn from_fonts<I>(fonts: I) -> CanvasFontContext where I: Iterator<Item = Handle> { pub fn from_fonts<I>(fonts: I) -> CanvasFontContext
where
I: Iterator<Item = Handle>,
{
CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap())) CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap()))
} }
@ -236,7 +275,8 @@ impl CanvasFontContext {
this.font_source this.font_source
.select_by_postscript_name(postscript_name) .select_by_postscript_name(postscript_name)
.map_err(FontError::NotFound)? .map_err(FontError::NotFound)?
.load().map_err(FontError::LoadError) .load()
.map_err(FontError::LoadError)
} }
} }
@ -298,11 +338,12 @@ struct VerticalMetrics {
} }
impl TextMetrics { impl TextMetrics {
pub fn new(skribo_layout: Rc<SkriboLayout>, pub fn new(
font_size: f32, skribo_layout: Rc<SkriboLayout>,
align: TextAlign, font_size: f32,
baseline: TextBaseline) align: TextAlign,
-> TextMetrics { baseline: TextBaseline,
) -> TextMetrics {
TextMetrics { TextMetrics {
skribo_layout, skribo_layout,
font_size, font_size,
@ -317,10 +358,18 @@ impl TextMetrics {
} }
} }
pub fn layout(string: &str, font_collection: &FontCollection, font_size: f32, text_align: TextAlign, text_baseline: TextBaseline) -> TextMetrics { pub fn layout(
let skribo_layout = Rc::new(skribo::layout(&TextStyle { size: font_size }, string: &str,
font_collection, font_collection: &FontCollection,
string)); font_size: f32,
text_align: TextAlign,
text_baseline: TextBaseline,
) -> TextMetrics {
let skribo_layout = Rc::new(skribo::layout(
&TextStyle { size: font_size },
font_collection,
string,
));
TextMetrics::new(skribo_layout, font_size, text_align, text_baseline) TextMetrics::new(skribo_layout, font_size, text_align, text_baseline)
} }
@ -342,11 +391,11 @@ impl TextMetrics {
self.text_y_offset.set(Some(match self.baseline { self.text_y_offset.set(Some(match self.baseline {
TextBaseline::Alphabetic => 0.0, TextBaseline::Alphabetic => 0.0,
TextBaseline::Top => vertical_metrics.em_height_ascent, TextBaseline::Top => vertical_metrics.em_height_ascent,
TextBaseline::Middle => { TextBaseline::Middle => util::lerp(
util::lerp(vertical_metrics.em_height_ascent, vertical_metrics.em_height_ascent,
vertical_metrics.em_height_descent, vertical_metrics.em_height_descent,
0.5) 0.5,
} ),
TextBaseline::Bottom => vertical_metrics.em_height_descent, TextBaseline::Bottom => vertical_metrics.em_height_descent,
TextBaseline::Ideographic => vertical_metrics.ideographic_baseline, TextBaseline::Ideographic => vertical_metrics.ideographic_baseline,
TextBaseline::Hanging => vertical_metrics.hanging_baseline, TextBaseline::Hanging => vertical_metrics.hanging_baseline,
@ -368,39 +417,56 @@ impl TextMetrics {
let font_metrics = last_glyph.font.font.metrics(); let font_metrics = last_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32; let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap(); let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap();
self.width.set(Some(last_glyph.offset.x() + self.width.set(Some(
glyph_rect.max_x() * scale_factor)); last_glyph.offset.x() + glyph_rect.max_x() * scale_factor,
));
} }
} }
} }
self.width.get().unwrap() self.width.get().unwrap()
} }
fn populate_vertical_metrics_if_necessary(&self) { fn populate_vertical_metrics_if_necessary(&self) {
if self.vertical_metrics.get().is_none() { if self.vertical_metrics.get().is_none() {
self.vertical_metrics.set(Some(VerticalMetrics::measure(&self.skribo_layout))); self.vertical_metrics
.set(Some(VerticalMetrics::measure(&self.skribo_layout)));
} }
} }
pub fn font_bounding_box_ascent(&self) -> f32 { pub fn font_bounding_box_ascent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary(); self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().font_bounding_box_ascent - self.text_y_offset() self.vertical_metrics
.get()
.unwrap()
.font_bounding_box_ascent
- self.text_y_offset()
} }
pub fn font_bounding_box_descent(&self) -> f32 { pub fn font_bounding_box_descent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary(); self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().font_bounding_box_descent - self.text_y_offset() self.vertical_metrics
.get()
.unwrap()
.font_bounding_box_descent
- self.text_y_offset()
} }
pub fn actual_bounding_box_ascent(&self) -> f32 { pub fn actual_bounding_box_ascent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary(); self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().actual_bounding_box_ascent - self.text_y_offset() self.vertical_metrics
.get()
.unwrap()
.actual_bounding_box_ascent
- self.text_y_offset()
} }
pub fn actual_bounding_box_descent(&self) -> f32 { pub fn actual_bounding_box_descent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary(); self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().actual_bounding_box_descent - self.text_y_offset() self.vertical_metrics
.get()
.unwrap()
.actual_bounding_box_descent
- self.text_y_offset()
} }
pub fn em_height_ascent(&self) -> f32 { pub fn em_height_ascent(&self) -> f32 {
@ -421,14 +487,20 @@ impl TextMetrics {
let glyph_id = first_glyph.glyph_id; let glyph_id = first_glyph.glyph_id;
let font_metrics = first_glyph.font.font.metrics(); let font_metrics = first_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32; let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = first_glyph.font.font.raster_bounds( let glyph_rect = first_glyph
glyph_id, .font
font_metrics.units_per_em as f32, .font
Transform2F::default(), .raster_bounds(
HintingOptions::None, glyph_id,
RasterizationOptions::GrayscaleAa).unwrap(); font_metrics.units_per_em as f32,
self.actual_left_extent.set(Some(first_glyph.offset.x() + Transform2F::default(),
glyph_rect.min_x() as f32 * scale_factor)); HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
self.actual_left_extent.set(Some(
first_glyph.offset.x() + glyph_rect.min_x() as f32 * scale_factor,
));
} }
} }
} }
@ -443,14 +515,20 @@ impl TextMetrics {
let glyph_id = last_glyph.glyph_id; let glyph_id = last_glyph.glyph_id;
let font_metrics = last_glyph.font.font.metrics(); let font_metrics = last_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32; let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = last_glyph.font.font.raster_bounds( let glyph_rect = last_glyph
glyph_id, .font
font_metrics.units_per_em as f32, .font
Transform2F::default(), .raster_bounds(
HintingOptions::None, glyph_id,
RasterizationOptions::GrayscaleAa).unwrap(); font_metrics.units_per_em as f32,
self.actual_right_extent.set(Some(last_glyph.offset.x() + Transform2F::default(),
glyph_rect.max_x() as f32 * scale_factor)); HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
self.actual_right_extent.set(Some(
last_glyph.offset.x() + glyph_rect.max_x() as f32 * scale_factor,
));
} }
} }
} }
@ -471,7 +549,6 @@ impl TextMetrics {
self.populate_vertical_metrics_if_necessary(); self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().ideographic_baseline - self.text_y_offset() self.vertical_metrics.get().unwrap().ideographic_baseline - self.text_y_offset()
} }
} }
impl VerticalMetrics { impl VerticalMetrics {
@ -498,28 +575,30 @@ impl VerticalMetrics {
let font_metrics = font.metrics(); let font_metrics = font.metrics();
let scale_factor = skribo_layout.size / font_metrics.units_per_em as f32; let scale_factor = skribo_layout.size / font_metrics.units_per_em as f32;
vertical_metrics.em_height_ascent = vertical_metrics.em_height_ascent =
(font_metrics.ascent * (font_metrics.ascent * scale_factor).max(vertical_metrics.em_height_ascent);
scale_factor).max(vertical_metrics.em_height_ascent); vertical_metrics.em_height_descent = (font_metrics.descent * scale_factor)
vertical_metrics.em_height_descent = .min(vertical_metrics.em_height_descent);
(font_metrics.descent * vertical_metrics.font_bounding_box_ascent = (font_metrics.bounding_box.max_y()
scale_factor).min(vertical_metrics.em_height_descent); * scale_factor)
vertical_metrics.font_bounding_box_ascent = .max(vertical_metrics.font_bounding_box_ascent);
(font_metrics.bounding_box.max_y() *
scale_factor).max(vertical_metrics.font_bounding_box_ascent);
vertical_metrics.font_bounding_box_descent = vertical_metrics.font_bounding_box_descent =
(font_metrics.bounding_box.min_y() * (font_metrics.bounding_box.min_y() * scale_factor)
scale_factor).min(vertical_metrics.font_bounding_box_descent); .min(vertical_metrics.font_bounding_box_descent);
last_font = Some(font); last_font = Some(font);
} }
} }
let font = last_font.as_ref().unwrap(); let font = last_font.as_ref().unwrap();
let glyph_rect = font.raster_bounds(glyph.glyph_id, let glyph_rect = font
skribo_layout.size, .raster_bounds(
Transform2F::default(), glyph.glyph_id,
HintingOptions::None, skribo_layout.size,
RasterizationOptions::GrayscaleAa).unwrap(); Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
vertical_metrics.actual_bounding_box_ascent = vertical_metrics.actual_bounding_box_ascent =
(glyph_rect.max_y() as f32).max(vertical_metrics.actual_bounding_box_ascent); (glyph_rect.max_y() as f32).max(vertical_metrics.actual_bounding_box_ascent);
vertical_metrics.actual_bounding_box_descent = vertical_metrics.actual_bounding_box_descent =
@ -533,7 +612,10 @@ impl VerticalMetrics {
/// Various things that can be conveniently converted into font collections for use with /// Various things that can be conveniently converted into font collections for use with
/// `CanvasRenderingContext2D::set_font()`. /// `CanvasRenderingContext2D::set_font()`.
pub trait IntoFontCollection { pub trait IntoFontCollection {
fn into_font_collection(self, font_context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError>; fn into_font_collection(
self,
font_context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError>;
} }
impl IntoFontCollection for Arc<FontCollection> { impl IntoFontCollection for Arc<FontCollection> {
@ -565,14 +647,20 @@ impl IntoFontCollection for Vec<FontFamily> {
impl IntoFontCollection for Font { impl IntoFontCollection for Font {
#[inline] #[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> { fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
Ok(FontFamily::new_from_font(self).into_font_collection(context)?) Ok(FontFamily::new_from_font(self).into_font_collection(context)?)
} }
} }
impl<'a> IntoFontCollection for &'a [Font] { impl<'a> IntoFontCollection for &'a [Font] {
#[inline] #[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> { fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
let mut family = FontFamily::new(); let mut family = FontFamily::new();
for font in self { for font in self {
family.add_font(FontRef::new((*font).clone())) family.add_font(FontRef::new((*font).clone()))
@ -583,14 +671,22 @@ impl<'a> IntoFontCollection for &'a [Font] {
impl<'a> IntoFontCollection for &'a str { impl<'a> IntoFontCollection for &'a str {
#[inline] #[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> { fn into_font_collection(
context.get_font_by_postscript_name(self)?.into_font_collection(context) self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
context
.get_font_by_postscript_name(self)?
.into_font_collection(context)
} }
} }
impl<'a, 'b> IntoFontCollection for &'a [&'b str] { impl<'a, 'b> IntoFontCollection for &'a [&'b str] {
#[inline] #[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> { fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
let mut font_collection = FontCollection::new(); let mut font_collection = FontCollection::new();
for postscript_name in self { for postscript_name in self {
let font = context.get_font_by_postscript_name(postscript_name)?; let font = context.get_font_by_postscript_name(postscript_name)?;

View File

@ -127,13 +127,13 @@ impl ColorF {
let c = (1.0 - f32::abs(2.0 * l - 1.0)) * s; let c = (1.0 - f32::abs(2.0 * l - 1.0)) * s;
let xc = F32x4::new(c * (1.0 - f32::abs(h % 2.0 - 1.0)), c, 0.0, a); let xc = F32x4::new(c * (1.0 - f32::abs(h % 2.0 - 1.0)), c, 0.0, a);
let rgba = match f32::ceil(h) as i32 { let rgba = match f32::ceil(h) as i32 {
1 => xc.yxzw(), 1 => xc.yxzw(),
2 => xc.xyzw(), 2 => xc.xyzw(),
3 => xc.zyxw(), 3 => xc.zyxw(),
4 => xc.zxyw(), 4 => xc.zxyw(),
5 => xc.xzyw(), 5 => xc.xzyw(),
0 | 6 => xc.yzxw(), 0 | 6 => xc.yzxw(),
_ => xc.zzzw(), _ => xc.zzzw(),
}; };
let m = l - 0.5 * c; let m = l - 0.5 * c;
ColorF(rgba + F32x4::new(m, m, m, 0.0)) ColorF(rgba + F32x4::new(m, m, m, 0.0))
@ -162,7 +162,12 @@ impl ColorF {
#[inline] #[inline]
pub fn to_u8(&self) -> ColorU { pub fn to_u8(&self) -> ColorU {
let color = (self.0 * F32x4::splat(255.0)).to_i32x4(); let color = (self.0 * F32x4::splat(255.0)).to_i32x4();
ColorU { r: color[0] as u8, g: color[1] as u8, b: color[2] as u8, a: color[3] as u8 } ColorU {
r: color[0] as u8,
g: color[1] as u8,
b: color[2] as u8,
a: color[3] as u8,
}
} }
#[inline] #[inline]
@ -226,9 +231,7 @@ impl Debug for ColorF {
#[inline] #[inline]
pub fn color_slice_to_u8_slice(slice: &[ColorU]) -> &[u8] { pub fn color_slice_to_u8_slice(slice: &[ColorU]) -> &[u8] {
unsafe { unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4) }
slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4)
}
} }
#[inline] #[inline]

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Add, Mul, Deref}; use std::ops::{Add, Deref, Mul};
/// ColorMatrix filter/transformation /// ColorMatrix filter/transformation
/// ///
@ -34,22 +34,22 @@ impl ColorMatrix {
/// See the `hueRotate` attribute of the `feColorMatrix` element in the SVG specification. /// See the `hueRotate` attribute of the `feColorMatrix` element in the SVG specification.
pub fn hue_rotate(angle: f32) -> ColorMatrix { pub fn hue_rotate(angle: f32) -> ColorMatrix {
let a = ColorMatrix::from_rows([ let a = ColorMatrix::from_rows([
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0, 0.0],
]); ]);
let b = ColorMatrix::from_rows([ let b = ColorMatrix::from_rows([
[ 0.787, -0.715, -0.072, 0.0, 0.0], [0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0], [-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0], [-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
]); ]);
let c = ColorMatrix::from_rows([ let c = ColorMatrix::from_rows([
[-0.213, -0.715, 0.928, 0.0, 0.0], [-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.143, 0.140, -0.283, 0.0, 0.0], [0.143, 0.140, -0.283, 0.0, 0.0],
[-0.787, 0.715, 0.072, 0.0, 0.0], [-0.787, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
]); ]);
a + b * angle.cos() + c * angle.sin() a + b * angle.cos() + c * angle.sin()
} }
@ -59,16 +59,16 @@ impl ColorMatrix {
/// See the `saturate` attribute of the `feColorMatrix` element in the SVG specification. /// See the `saturate` attribute of the `feColorMatrix` element in the SVG specification.
pub fn saturate(saturation: f32) -> ColorMatrix { pub fn saturate(saturation: f32) -> ColorMatrix {
let a = ColorMatrix::from_rows([ let a = ColorMatrix::from_rows([
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0], [0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0, 0.0],
]); ]);
let b = ColorMatrix::from_rows([ let b = ColorMatrix::from_rows([
[ 0.787, -0.715, -0.072, 0.0, 0.0], [0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0], [-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0], [-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
]); ]);
a + b * saturation a + b * saturation
} }
@ -79,10 +79,10 @@ impl ColorMatrix {
/// specification. /// specification.
pub fn luminance_to_alpha() -> ColorMatrix { pub fn luminance_to_alpha() -> ColorMatrix {
ColorMatrix::from_rows([ ColorMatrix::from_rows([
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.2125, 0.7154, 0.0721, 0.0, 0.0], [0.2125, 0.7154, 0.0721, 0.0, 0.0],
]) ])
} }
} }

View File

@ -18,7 +18,7 @@ use arrayvec::ArrayVec;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::util::lerp; use pathfinder_geometry::util::lerp;
use pathfinder_geometry::vector::{Vector2F, Vector4F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F, Vector4F};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::Debug; use std::fmt::Debug;
use std::mem; use std::mem;
@ -491,8 +491,10 @@ pub(crate) fn rect_is_inside_polygon(rect: RectF, polygon_points: &[Vector2F]) -
} }
/// Clips a line segment to an axis-aligned rectangle using Cohen-Sutherland clipping. /// Clips a line segment to an axis-aligned rectangle using Cohen-Sutherland clipping.
pub fn clip_line_segment_to_rect(mut line_segment: LineSegment2F, rect: RectF) pub fn clip_line_segment_to_rect(
-> Option<LineSegment2F> { mut line_segment: LineSegment2F,
rect: RectF,
) -> Option<LineSegment2F> {
let mut outcode_from = compute_outcode(line_segment.from(), rect); let mut outcode_from = compute_outcode(line_segment.from(), rect);
let mut outcode_to = compute_outcode(line_segment.to(), rect); let mut outcode_to = compute_outcode(line_segment.to(), rect);
@ -512,29 +514,45 @@ pub fn clip_line_segment_to_rect(mut line_segment: LineSegment2F, rect: RectF)
}; };
if outcode.contains(Outcode::LEFT) { if outcode.contains(Outcode::LEFT) {
point = vec2f(rect.min_x(), point = vec2f(
lerp(line_segment.from_y(), rect.min_x(),
line_segment.to_y(), lerp(
(rect.min_x() - line_segment.from_x()) / line_segment.from_y(),
(line_segment.to_x() - line_segment.from_x()))); line_segment.to_y(),
(rect.min_x() - line_segment.from_x())
/ (line_segment.to_x() - line_segment.from_x()),
),
);
} else if outcode.contains(Outcode::RIGHT) { } else if outcode.contains(Outcode::RIGHT) {
point = vec2f(rect.max_x(), point = vec2f(
lerp(line_segment.from_y(), rect.max_x(),
line_segment.to_y(), lerp(
(rect.max_x() - line_segment.from_x()) / line_segment.from_y(),
(line_segment.to_x() - line_segment.from_x()))); line_segment.to_y(),
(rect.max_x() - line_segment.from_x())
/ (line_segment.to_x() - line_segment.from_x()),
),
);
} else if outcode.contains(Outcode::TOP) { } else if outcode.contains(Outcode::TOP) {
point = vec2f(lerp(line_segment.from_x(), point = vec2f(
line_segment.to_x(), lerp(
(rect.min_y() - line_segment.from_y()) / line_segment.from_x(),
(line_segment.to_y() - line_segment.from_y())), line_segment.to_x(),
rect.min_y()); (rect.min_y() - line_segment.from_y())
/ (line_segment.to_y() - line_segment.from_y()),
),
rect.min_y(),
);
} else if outcode.contains(Outcode::BOTTOM) { } else if outcode.contains(Outcode::BOTTOM) {
point = vec2f(lerp(line_segment.from_x(), point = vec2f(
line_segment.to_x(), lerp(
(rect.max_y() - line_segment.from_y()) / line_segment.from_x(),
(line_segment.to_y() - line_segment.from_y())), line_segment.to_x(),
rect.max_y()); (rect.max_y() - line_segment.from_y())
/ (line_segment.to_y() - line_segment.from_y()),
),
rect.max_y(),
);
} }
if clip_from { if clip_from {

View File

@ -38,7 +38,11 @@ impl<'a> OutlineDash<'a> {
/// <https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset>. /// <https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset>.
#[inline] #[inline]
pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> { pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> {
OutlineDash { input, output: Outline::new(), state: DashState::new(dashes, offset) } OutlineDash {
input,
output: Outline::new(),
state: DashState::new(dashes, offset),
}
} }
/// Performs the dashing operation. /// Performs the dashing operation.
@ -66,9 +70,16 @@ struct ContourDash<'a, 'b, 'c> {
} }
impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> { impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
fn new(input: &'a Contour, output: &'b mut Outline, state: &'c mut DashState<'a>) fn new(
-> ContourDash<'a, 'b, 'c> { input: &'a Contour,
ContourDash { input, output, state } output: &'b mut Outline,
state: &'c mut DashState<'a>,
) -> ContourDash<'a, 'b, 'c> {
ContourDash {
input,
output,
state,
}
} }
fn dash(&mut self) { fn dash(&mut self) {
@ -95,13 +106,16 @@ impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
} }
if self.state.is_on() { if self.state.is_on() {
self.state.output.push_segment(&current_segment, PushSegmentFlags::empty()); self.state
.output
.push_segment(&current_segment, PushSegmentFlags::empty());
} }
self.state.distance_left -= distance; self.state.distance_left -= distance;
if self.state.distance_left < EPSILON { if self.state.distance_left < EPSILON {
if self.state.is_on() { if self.state.is_on() {
self.output.push_contour(mem::replace(&mut self.state.output, Contour::new())); self.output
.push_contour(mem::replace(&mut self.state.output, Contour::new()));
} }
self.state.current_dash_index += 1; self.state.current_dash_index += 1;

View File

@ -10,7 +10,7 @@
use crate::orientation::Orientation; use crate::orientation::Orientation;
use crate::outline::Contour; use crate::outline::Contour;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
pub struct ContourDilator<'a> { pub struct ContourDilator<'a> {
contour: &'a mut Contour, contour: &'a mut Contour,
@ -33,10 +33,11 @@ impl<'a> ContourDilator<'a> {
pub fn dilate(&mut self) { pub fn dilate(&mut self) {
// Determine orientation. // Determine orientation.
let scale = self.amount * (match self.orientation { let scale = self.amount
Orientation::Ccw => vec2f( 1.0, -1.0), * (match self.orientation {
Orientation::Cw => vec2f(-1.0, 1.0), Orientation::Ccw => vec2f(1.0, -1.0),
}); Orientation::Cw => vec2f(-1.0, 1.0),
});
// Find the starting and previous positions. // Find the starting and previous positions.
let first_position = self.contour.position_of(0); let first_position = self.contour.position_of(0);

View File

@ -10,7 +10,7 @@
//! Special effects that can be applied to layers. //! Special effects that can be applied to layers.
use pathfinder_color::{ColorF, matrix::ColorMatrix}; use pathfinder_color::{matrix::ColorMatrix, ColorF};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2; use pathfinder_simd::default::F32x2;
@ -88,7 +88,7 @@ pub enum PatternFilter {
}, },
/// A color matrix multiplication. /// A color matrix multiplication.
/// ///
/// The matrix is stored in 5 columns of `F32x4`. See the `feColorMatrix` element in the SVG /// The matrix is stored in 5 columns of `F32x4`. See the `feColorMatrix` element in the SVG
/// specification. /// specification.
ColorMatrix(ColorMatrix), ColorMatrix(ColorMatrix),
@ -202,64 +202,64 @@ impl BlendMode {
pub fn occludes_backdrop(self) -> bool { pub fn occludes_backdrop(self) -> bool {
match self { match self {
BlendMode::SrcOver | BlendMode::Clear => true, BlendMode::SrcOver | BlendMode::Clear => true,
BlendMode::DestOver | BlendMode::DestOver
BlendMode::DestOut | | BlendMode::DestOut
BlendMode::SrcAtop | | BlendMode::SrcAtop
BlendMode::Xor | | BlendMode::Xor
BlendMode::Lighter | | BlendMode::Lighter
BlendMode::Lighten | | BlendMode::Lighten
BlendMode::Darken | | BlendMode::Darken
BlendMode::Copy | | BlendMode::Copy
BlendMode::SrcIn | | BlendMode::SrcIn
BlendMode::DestIn | | BlendMode::DestIn
BlendMode::SrcOut | | BlendMode::SrcOut
BlendMode::DestAtop | | BlendMode::DestAtop
BlendMode::Multiply | | BlendMode::Multiply
BlendMode::Screen | | BlendMode::Screen
BlendMode::HardLight | | BlendMode::HardLight
BlendMode::Overlay | | BlendMode::Overlay
BlendMode::ColorDodge | | BlendMode::ColorDodge
BlendMode::ColorBurn | | BlendMode::ColorBurn
BlendMode::SoftLight | | BlendMode::SoftLight
BlendMode::Difference | | BlendMode::Difference
BlendMode::Exclusion | | BlendMode::Exclusion
BlendMode::Hue | | BlendMode::Hue
BlendMode::Saturation | | BlendMode::Saturation
BlendMode::Color | | BlendMode::Color
BlendMode::Luminosity => false, | BlendMode::Luminosity => false,
} }
} }
/// True if this blend mode does not preserve destination areas outside the source. /// True if this blend mode does not preserve destination areas outside the source.
pub fn is_destructive(self) -> bool { pub fn is_destructive(self) -> bool {
match self { match self {
BlendMode::Clear | BlendMode::Clear
BlendMode::Copy | | BlendMode::Copy
BlendMode::SrcIn | | BlendMode::SrcIn
BlendMode::DestIn | | BlendMode::DestIn
BlendMode::SrcOut | | BlendMode::SrcOut
BlendMode::DestAtop => true, | BlendMode::DestAtop => true,
BlendMode::SrcOver | BlendMode::SrcOver
BlendMode::DestOver | | BlendMode::DestOver
BlendMode::DestOut | | BlendMode::DestOut
BlendMode::SrcAtop | | BlendMode::SrcAtop
BlendMode::Xor | | BlendMode::Xor
BlendMode::Lighter | | BlendMode::Lighter
BlendMode::Lighten | | BlendMode::Lighten
BlendMode::Darken | | BlendMode::Darken
BlendMode::Multiply | | BlendMode::Multiply
BlendMode::Screen | | BlendMode::Screen
BlendMode::HardLight | | BlendMode::HardLight
BlendMode::Overlay | | BlendMode::Overlay
BlendMode::ColorDodge | | BlendMode::ColorDodge
BlendMode::ColorBurn | | BlendMode::ColorBurn
BlendMode::SoftLight | | BlendMode::SoftLight
BlendMode::Difference | | BlendMode::Difference
BlendMode::Exclusion | | BlendMode::Exclusion
BlendMode::Hue | | BlendMode::Hue
BlendMode::Saturation | | BlendMode::Saturation
BlendMode::Color | | BlendMode::Color
BlendMode::Luminosity => false, | BlendMode::Luminosity => false,
} }
} }
} }

View File

@ -14,8 +14,8 @@ use crate::util;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::util as geometry_util; use pathfinder_geometry::util as geometry_util;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2; use pathfinder_simd::default::F32x2;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert; use std::convert;
@ -67,7 +67,7 @@ pub enum GradientGeometry {
/// Like `gradientTransform` in SVG. Note that this is the inverse of Cairo's gradient /// Like `gradientTransform` in SVG. Note that this is the inverse of Cairo's gradient
/// transform. /// transform.
transform: Transform2F, transform: Transform2F,
} },
} }
/// What should be rendered outside the color stops. /// What should be rendered outside the color stops.
@ -83,13 +83,20 @@ pub enum GradientWrap {
impl Eq for Gradient {} impl Eq for Gradient {}
impl Hash for Gradient { impl Hash for Gradient {
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self.geometry { match self.geometry {
GradientGeometry::Linear(line) => { GradientGeometry::Linear(line) => {
(0).hash(state); (0).hash(state);
util::hash_line_segment(line, state); util::hash_line_segment(line, state);
} }
GradientGeometry::Radial { line, radii, transform } => { GradientGeometry::Radial {
line,
radii,
transform,
} => {
(1).hash(state); (1).hash(state);
util::hash_line_segment(line, state); util::hash_line_segment(line, state);
util::hash_f32(radii.x(), state); util::hash_f32(radii.x(), state);
@ -109,7 +116,10 @@ impl Hash for Gradient {
impl Eq for ColorStop {} impl Eq for ColorStop {}
impl Hash for ColorStop { impl Hash for ColorStop {
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
unsafe { unsafe {
self.color.hash(state); self.color.hash(state);
let offset = mem::transmute::<f32, u32>(self.offset); let offset = mem::transmute::<f32, u32>(self.offset);
@ -145,10 +155,17 @@ impl Gradient {
/// with two circles, pass a `LineSegment2F`. To start the gradient at the center of the /// with two circles, pass a `LineSegment2F`. To start the gradient at the center of the
/// circle, pass zero for the first radius. /// circle, pass zero for the first radius.
#[inline] #[inline]
pub fn radial<L>(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine { pub fn radial<L>(line: L, radii: F32x2) -> Gradient
where
L: RadialGradientLine,
{
let transform = Transform2F::default(); let transform = Transform2F::default();
Gradient { Gradient {
geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform }, geometry: GradientGeometry::Radial {
line: line.to_line(),
radii,
transform,
},
stops: Vec::new(), stops: Vec::new(),
wrap: GradientWrap::Clamp, wrap: GradientWrap::Clamp,
} }
@ -157,9 +174,16 @@ impl Gradient {
/// Adds a new color stop to the radial gradient. /// Adds a new color stop to the radial gradient.
#[inline] #[inline]
pub fn add(&mut self, stop: ColorStop) { pub fn add(&mut self, stop: ColorStop) {
let index = self.stops.binary_search_by(|other| { let index = self
if other.offset <= stop.offset { Ordering::Less } else { Ordering::Greater } .stops
}).unwrap_or_else(convert::identity); .binary_search_by(|other| {
if other.offset <= stop.offset {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_or_else(convert::identity);
self.stops.insert(index, stop); self.stops.insert(index, stop);
} }
@ -193,10 +217,22 @@ impl Gradient {
t = geometry_util::clamp(t, 0.0, 1.0); t = geometry_util::clamp(t, 0.0, 1.0);
let last_index = self.stops.len() - 1; let last_index = self.stops.len() - 1;
let upper_index = self.stops.binary_search_by(|stop| { let upper_index = self
if stop.offset < t || stop.offset == 0.0 { Ordering::Less } else { Ordering::Greater } .stops
}).unwrap_or_else(convert::identity).min(last_index); .binary_search_by(|stop| {
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index }; if stop.offset < t || stop.offset == 0.0 {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_or_else(convert::identity)
.min(last_index);
let lower_index = if upper_index > 0 {
upper_index - 1
} else {
upper_index
};
let lower_stop = &self.stops[lower_index]; let lower_stop = &self.stops[lower_index];
let upper_stop = &self.stops[upper_index]; let upper_stop = &self.stops[upper_index];
@ -207,7 +243,11 @@ impl Gradient {
} }
let ratio = ((t - lower_stop.offset) / denom).min(1.0); let ratio = ((t - lower_stop.offset) / denom).min(1.0);
lower_stop.color.to_f32().lerp(upper_stop.color.to_f32(), ratio).to_u8() lower_stop
.color
.to_f32()
.lerp(upper_stop.color.to_f32(), ratio)
.to_u8()
} }
/// Returns true if all colors of all stops in this gradient are opaque (alpha is 1.0). /// Returns true if all colors of all stops in this gradient are opaque (alpha is 1.0).
@ -220,7 +260,9 @@ impl Gradient {
/// 0.0). /// 0.0).
#[inline] #[inline]
pub fn is_fully_transparent(&self) -> bool { pub fn is_fully_transparent(&self) -> bool {
self.stops.iter().all(|stop| stop.color.is_fully_transparent()) self.stops
.iter()
.all(|stop| stop.color.is_fully_transparent())
} }
/// Applies the given affine transform to this gradient. /// Applies the given affine transform to this gradient.
@ -234,9 +276,9 @@ impl Gradient {
match self.geometry { match self.geometry {
GradientGeometry::Linear(ref mut line) => *line = new_transform * *line, GradientGeometry::Linear(ref mut line) => *line = new_transform * *line,
GradientGeometry::Radial { ref mut transform, .. } => { GradientGeometry::Radial {
*transform = new_transform * *transform ref mut transform, ..
} } => *transform = new_transform * *transform,
} }
} }
} }
@ -283,9 +325,10 @@ mod test {
} }
// Check that it sorted stably // Check that it sorted stably
assert!(grad.stops.windows(2).all(|w| { assert!(grad
w[0].offset < w[1].offset || w[0].color.r < w[1].color.r .stops
})); .windows(2)
.all(|w| { w[0].offset < w[1].offset || w[0].color.r < w[1].color.r }));
} }
#[test] #[test]
@ -293,7 +336,10 @@ mod test {
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default()); let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
for i in 0..110 { for i in 0..110 {
let zero_width = (i == 0) || (11 <= i && i < 99) || (i == 109); let zero_width = (i == 0) || (11 <= i && i < 99) || (i == 109);
grad.add_color_stop(ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1), (i % 11) as f32 / 10.0); grad.add_color_stop(
ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1),
(i % 11) as f32 / 10.0,
);
} }
for i in 0..11 { for i in 0..11 {

View File

@ -17,10 +17,10 @@ use crate::segment::{Segment, SegmentFlags, SegmentKind};
use crate::util::safe_sqrt; use crate::util::safe_sqrt;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::{Transform2F, Matrix2x2F}; use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::unit_vector::UnitVector; use pathfinder_geometry::unit_vector::UnitVector;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::f32::consts::PI; use std::f32::consts::PI;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem; use std::mem;
@ -90,7 +90,10 @@ impl Outline {
/// Creates a new outline from a list of segments. /// Creates a new outline from a list of segments.
#[inline] #[inline]
pub fn from_segments<I>(segments: I) -> Outline where I: Iterator<Item = Segment> { pub fn from_segments<I>(segments: I) -> Outline
where
I: Iterator<Item = Segment>,
{
let mut outline = Outline::new(); let mut outline = Outline::new();
let mut current_contour = Contour::new(); let mut current_contour = Contour::new();
@ -380,7 +383,7 @@ impl Contour {
contour.push_cubic( contour.push_cubic(
vec2f(p0.x(), p1.y()), vec2f(p0.x(), p1.y()),
vec2f(p1.x(), p0.y()), vec2f(p1.x(), p0.y()),
vec2f(p2.x(), p0.y()) vec2f(p2.x(), p0.y()),
); );
} }
@ -393,7 +396,7 @@ impl Contour {
contour.push_cubic( contour.push_cubic(
vec2f(p1.x(), p0.y()), vec2f(p1.x(), p0.y()),
vec2f(p0.x(), p1.y()), vec2f(p0.x(), p1.y()),
vec2f(p0.x(), p2.y()) vec2f(p0.x(), p2.y()),
); );
} }
@ -406,7 +409,7 @@ impl Contour {
contour.push_cubic( contour.push_cubic(
vec2f(p0.x(), p1.y()), vec2f(p0.x(), p1.y()),
vec2f(p1.x(), p0.y()), vec2f(p1.x(), p0.y()),
vec2f(p2.x(), p0.y()) vec2f(p2.x(), p0.y()),
); );
} }
@ -419,7 +422,7 @@ impl Contour {
contour.push_cubic( contour.push_cubic(
vec2f(p1.x(), p0.y()), vec2f(p1.x(), p0.y()),
vec2f(p0.x(), p1.y()), vec2f(p0.x(), p1.y()),
vec2f(p0.x(), p2.y()) vec2f(p0.x(), p2.y()),
); );
} }
@ -557,10 +560,7 @@ impl Contour {
} }
#[inline] #[inline]
pub(crate) fn push_point(&mut self, pub(crate) fn push_point(&mut self, point: Vector2F, flags: PointFlags, update_bounds: bool) {
point: Vector2F,
flags: PointFlags,
update_bounds: bool) {
debug_assert!(!point.x().is_nan() && !point.y().is_nan()); debug_assert!(!point.x().is_nan() && !point.y().is_nan());
if update_bounds { if update_bounds {
@ -612,26 +612,30 @@ impl Contour {
/// ///
/// * `direction`: Whether the arc should be drawn clockwise or counterclockwise from the +x /// * `direction`: Whether the arc should be drawn clockwise or counterclockwise from the +x
/// axis. /// axis.
pub fn push_arc(&mut self, pub fn push_arc(
transform: &Transform2F, &mut self,
start_angle: f32, transform: &Transform2F,
end_angle: f32, start_angle: f32,
direction: ArcDirection) { end_angle: f32,
direction: ArcDirection,
) {
if end_angle - start_angle >= PI * 2.0 { if end_angle - start_angle >= PI * 2.0 {
self.push_ellipse(transform); self.push_ellipse(transform);
} else { } else {
let start = vec2f(start_angle.cos(), start_angle.sin()); let start = vec2f(start_angle.cos(), start_angle.sin());
let end = vec2f(end_angle.cos(), end_angle.sin()); let end = vec2f(end_angle.cos(), end_angle.sin());
self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction); self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction);
} }
} }
/// Given the endpoints of a unit arc, adds Bézier curves to approximate that arc to the /// Given the endpoints of a unit arc, adds Bézier curves to approximate that arc to the
/// current contour. The given transform is applied to the resulting arc. /// current contour. The given transform is applied to the resulting arc.
pub fn push_arc_from_unit_chord(&mut self, pub fn push_arc_from_unit_chord(
transform: &Transform2F, &mut self,
mut chord: LineSegment2F, transform: &Transform2F,
direction: ArcDirection) { mut chord: LineSegment2F,
direction: ArcDirection,
) {
let mut direction_transform = Transform2F::default(); let mut direction_transform = Transform2F::default();
if direction == ArcDirection::CCW { if direction == ArcDirection::CCW {
chord *= vec2f(1.0, -1.0); chord *= vec2f(1.0, -1.0);
@ -640,15 +644,17 @@ impl Contour {
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to())); let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
for segment_index in 0..4 { for segment_index in 0..4 {
debug!("push_arc_from_unit_chord(): loop segment index {}", segment_index); debug!(
"push_arc_from_unit_chord(): loop segment index {}",
segment_index
);
let mut sweep_vector = end_vector.rev_rotate_by(vector); let mut sweep_vector = end_vector.rev_rotate_by(vector);
let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON;
debug!("... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}", debug!(
end_vector, "... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}",
vector, end_vector, vector, sweep_vector, last
sweep_vector, );
last);
let mut segment; let mut segment;
if !last { if !last {
@ -683,12 +689,14 @@ impl Contour {
/// Draws an ellipse section with radii given by `radius` rotated by `x_axis_rotation` in /// Draws an ellipse section with radii given by `radius` rotated by `x_axis_rotation` in
/// radians to `to` in the given direction. If `large_arc` is true, draws an arc bigger than /// radians to `to` in the given direction. If `large_arc` is true, draws an arc bigger than
/// π radians, otherwise smaller than π radians. /// π radians, otherwise smaller than π radians.
pub fn push_svg_arc(&mut self, pub fn push_svg_arc(
radius: Vector2F, &mut self,
x_axis_rotation: f32, radius: Vector2F,
large_arc: bool, x_axis_rotation: f32,
direction: ArcDirection, large_arc: bool,
to: Vector2F) { direction: ArcDirection,
to: Vector2F,
) {
let r = radius; let r = radius;
let p = to; let p = to;
let last = self.last_position().unwrap_or_default(); let last = self.last_position().unwrap_or_default();
@ -698,7 +706,7 @@ impl Contour {
let r_inv = r.recip(); let r_inv = r.recip();
let sign = match (large_arc, direction) { let sign = match (large_arc, direction) {
(false, ArcDirection::CW) | (true, ArcDirection::CCW) => 1.0, (false, ArcDirection::CW) | (true, ArcDirection::CCW) => 1.0,
(false, ArcDirection::CCW) | (true, ArcDirection::CW) => -1.0 (false, ArcDirection::CCW) | (true, ArcDirection::CW) => -1.0,
}; };
let rot = Matrix2x2F::from_rotation(x_axis_rotation); let rot = Matrix2x2F::from_rotation(x_axis_rotation);
// x' // x'
@ -716,10 +724,14 @@ impl Contour {
let rq2 = r2 * q2.yx(); // (r_x^2 q_y^2, r_y^2 q_x^2) let rq2 = r2 * q2.yx(); // (r_x^2 q_y^2, r_y^2 q_x^2)
let rq2_sum = rq2.x() + rq2.y(); // r_x^2 q_y^2 + r_y^2 q_x^2 let rq2_sum = rq2.x() + rq2.y(); // r_x^2 q_y^2 + r_y^2 q_x^2
// c' // c'
let s = vec2f(1., -1.) * r * (q * r_inv).yx() * safe_sqrt((r2_prod - rq2_sum) / rq2_sum) * sign; let s = vec2f(1., -1.)
* r
* (q * r_inv).yx()
* safe_sqrt((r2_prod - rq2_sum) / rq2_sum)
* sign;
let c = rot * s + (last + p) * 0.5; let c = rot * s + (last + p) * 0.5;
let a = (q - s) * r_inv; let a = (q - s) * r_inv;
let b = -(q + s) * r_inv; let b = -(q + s) * r_inv;
(a, b, c) (a, b, c)
@ -729,10 +741,10 @@ impl Contour {
let b = -a; let b = -a;
(a, b, c) (a, b, c)
}; };
let transform = Transform2F { let transform = Transform2F {
matrix: rot, matrix: rot,
vector: c vector: c,
} * Transform2F::from_scale(r); } * Transform2F::from_scale(r);
let chord = LineSegment2F::new(a, b); let chord = LineSegment2F::new(a, b);
self.push_arc_from_unit_chord(&transform, chord, direction); self.push_arc_from_unit_chord(&transform, chord, direction);
@ -747,17 +759,25 @@ impl Contour {
pub fn push_ellipse(&mut self, transform: &Transform2F) { pub fn push_ellipse(&mut self, transform: &Transform2F) {
let segment = Segment::quarter_circle_arc(); let segment = Segment::quarter_circle_arc();
let mut rotation; let mut rotation;
self.push_segment(&segment.transform(transform), self.push_segment(
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT); &segment.transform(transform),
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, 1.0))); PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT,
self.push_segment(&segment.transform(&(*transform * rotation)), );
PushSegmentFlags::UPDATE_BOUNDS); rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(0.0, 1.0)));
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0))); self.push_segment(
self.push_segment(&segment.transform(&(*transform * rotation)), &segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS,
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, -1.0))); );
self.push_segment(&segment.transform(&(*transform * rotation)), rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0)));
PushSegmentFlags::UPDATE_BOUNDS); self.push_segment(
&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS,
);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(0.0, -1.0)));
self.push_segment(
&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS,
);
} }
/// Returns the segment starting at the point with the given index. /// Returns the segment starting at the point with the given index.
@ -812,7 +832,7 @@ impl Contour {
#[inline] #[inline]
pub fn point_is_endpoint(&self, point_index: u32) -> bool { pub fn point_is_endpoint(&self, point_index: u32) -> bool {
!self.flags[point_index as usize] !self.flags[point_index as usize]
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1) .intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
} }
/// Returns `point_index + addend` modulo the number of points in this contour. /// Returns `point_index + addend` modulo the number of points in this contour.
@ -928,8 +948,10 @@ impl Contour {
impl Debug for Contour { impl Debug for Contour {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT) for (segment_index, segment) in self
.enumerate() { .iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
.enumerate()
{
if segment_index == 0 { if segment_index == 0 {
write!( write!(
formatter, formatter,
@ -1024,10 +1046,11 @@ impl<'a> Iterator for ContourIter<'a> {
fn next(&mut self) -> Option<Segment> { fn next(&mut self) -> Option<Segment> {
let contour = self.contour; let contour = self.contour;
let include_close_segment = self.contour.closed && let include_close_segment =
!self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT); self.contour.closed && !self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
if (self.index == contour.len() && !include_close_segment) || if (self.index == contour.len() && !include_close_segment)
self.index == contour.len() + 1 { || self.index == contour.len() + 1
{
return None; return None;
} }
@ -1050,7 +1073,10 @@ impl<'a> Iterator for ContourIter<'a> {
let point2 = contour.position_of(point2_index); let point2 = contour.position_of(point2_index);
self.index += 1; self.index += 1;
if contour.point_is_endpoint(point2_index) { if contour.point_is_endpoint(point2_index) {
return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1)); return Some(Segment::quadratic(
LineSegment2F::new(point0, point2),
point1,
));
} }
let point3_index = self.index; let point3_index = self.index;

View File

@ -15,7 +15,7 @@ use crate::render_target::RenderTargetId;
use crate::util; use crate::util;
use pathfinder_color::{self as color, ColorU}; use pathfinder_color::{self as color, ColorU};
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -46,7 +46,7 @@ pub enum PatternSource {
id: RenderTargetId, id: RenderTargetId,
/// The device pixel size of the render target. /// The device pixel size of the render target.
size: Vector2I, size: Vector2I,
} },
} }
/// A raster image, in 32-bit RGBA (8 bits per channel), non-premultiplied form. /// A raster image, in 32-bit RGBA (8 bits per channel), non-premultiplied form.
@ -213,7 +213,12 @@ impl Image {
pixels.hash(&mut pixels_hasher); pixels.hash(&mut pixels_hasher);
let pixels_hash = pixels_hasher.finish(); let pixels_hash = pixels_hasher.finish();
Image { size, pixels, pixels_hash, is_opaque } Image {
size,
pixels,
pixels_hash,
is_opaque,
}
} }
/// A convenience function to create a new image with the given image from the `image` crate. /// A convenience function to create a new image with the given image from the `image` crate.
@ -280,7 +285,10 @@ impl Debug for Image {
} }
impl Hash for Image { impl Hash for Image {
fn hash<H>(&self, hasher: &mut H) where H: Hasher { fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
self.size.hash(hasher); self.size.hash(hasher);
self.pixels_hash.hash(hasher); self.pixels_hash.hash(hasher);
self.is_opaque.hash(hasher); self.is_opaque.hash(hasher);
@ -290,7 +298,10 @@ impl Hash for Image {
impl Eq for Pattern {} impl Eq for Pattern {}
impl Hash for Pattern { impl Hash for Pattern {
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.source.hash(state); self.source.hash(state);
util::hash_transform2f(self.transform, state); util::hash_transform2f(self.transform, state);
self.flags.hash(state); self.flags.hash(state);

View File

@ -13,7 +13,7 @@
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util::EPSILON; use pathfinder_geometry::util::EPSILON;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::f32::consts::SQRT_2; use std::f32::consts::SQRT_2;
@ -100,8 +100,12 @@ impl Segment {
return Segment::line(LineSegment2F::new(vec2f(1.0, 0.0), vec2f(1.0, 0.0))); return Segment::line(LineSegment2F::new(vec2f(1.0, 0.0), vec2f(1.0, 0.0)));
} }
let term = F32x4::new(cos_sweep_angle, -cos_sweep_angle, let term = F32x4::new(
cos_sweep_angle, -cos_sweep_angle); cos_sweep_angle,
-cos_sweep_angle,
cos_sweep_angle,
-cos_sweep_angle,
);
let signs = F32x4::new(1.0, -1.0, 1.0, 1.0); let signs = F32x4::new(1.0, -1.0, 1.0, 1.0);
let p3p0 = ((F32x4::splat(1.0) + term) * F32x4::splat(0.5)).sqrt() * signs; let p3p0 = ((F32x4::splat(1.0) + term) * F32x4::splat(0.5)).sqrt() * signs;
let (p0x, p0y) = (p3p0.z(), p3p0.w()); let (p0x, p0y) = (p3p0.z(), p3p0.w());
@ -175,8 +179,9 @@ impl Segment {
let mut new_segment = *self; let mut new_segment = *self;
let p1_2 = self.ctrl.from() + self.ctrl.from(); let p1_2 = self.ctrl.from() + self.ctrl.from();
new_segment.ctrl = LineSegment2F::new(self.baseline.from() + p1_2, new_segment.ctrl =
p1_2 + self.baseline.to()) * (1.0 / 3.0); LineSegment2F::new(self.baseline.from() + p1_2, p1_2 + self.baseline.to())
* (1.0 / 3.0);
new_segment.kind = SegmentKind::Cubic; new_segment.kind = SegmentKind::Cubic;
new_segment new_segment
} }

View File

@ -16,15 +16,15 @@ use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util::EPSILON; use pathfinder_geometry::util::EPSILON;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::f32; use std::f32;
const TOLERANCE: f32 = 0.01; const TOLERANCE: f32 = 0.01;
/// Strokes an outline with a stroke style to produce a new outline. /// Strokes an outline with a stroke style to produce a new outline.
/// ///
/// An example of use: /// An example of use:
/// ///
/// ```no_run /// ```no_run
/// use pathfinder_content::stroke::OutlineStrokeToFill; /// use pathfinder_content::stroke::OutlineStrokeToFill;
/// use pathfinder_content::stroke::StrokeStyle; /// use pathfinder_content::stroke::StrokeStyle;
@ -86,7 +86,11 @@ impl<'a> OutlineStrokeToFill<'a> {
/// given stroke style. /// given stroke style.
#[inline] #[inline]
pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill { pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill {
OutlineStrokeToFill { input, output: Outline::new(), style } OutlineStrokeToFill {
input,
output: Outline::new(),
style,
}
} }
/// Performs the stroke operation. /// Performs the stroke operation.
@ -94,18 +98,22 @@ impl<'a> OutlineStrokeToFill<'a> {
let mut new_contours = vec![]; let mut new_contours = vec![];
for input in &self.input.contours { for input in &self.input.contours {
let closed = input.closed; let closed = input.closed;
let mut stroker = ContourStrokeToFill::new(input, let mut stroker = ContourStrokeToFill::new(
Contour::new(), input,
self.style.line_width * 0.5, Contour::new(),
self.style.line_join); self.style.line_width * 0.5,
self.style.line_join,
);
stroker.offset_forward(); stroker.offset_forward();
if closed { if closed {
self.push_stroked_contour(&mut new_contours, stroker, true); self.push_stroked_contour(&mut new_contours, stroker, true);
stroker = ContourStrokeToFill::new(input, stroker = ContourStrokeToFill::new(
Contour::new(), input,
self.style.line_width * 0.5, Contour::new(),
self.style.line_join); self.style.line_width * 0.5,
self.style.line_join,
);
} else { } else {
self.add_cap(&mut stroker.output); self.add_cap(&mut stroker.output);
} }
@ -119,7 +127,9 @@ impl<'a> OutlineStrokeToFill<'a> {
} }
let mut new_bounds = None; let mut new_bounds = None;
new_contours.iter().for_each(|contour| contour.update_bounds(&mut new_bounds)); new_contours
.iter()
.for_each(|contour| contour.update_bounds(&mut new_bounds));
self.output.contours = new_contours; self.output.contours = new_contours;
self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default()); self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default());
@ -131,18 +141,22 @@ impl<'a> OutlineStrokeToFill<'a> {
self.output self.output
} }
fn push_stroked_contour(&mut self, fn push_stroked_contour(
new_contours: &mut Vec<Contour>, &mut self,
mut stroker: ContourStrokeToFill, new_contours: &mut Vec<Contour>,
closed: bool) { mut stroker: ContourStrokeToFill,
closed: bool,
) {
// Add join if necessary. // Add join if necessary.
if closed && stroker.output.might_need_join(self.style.line_join) { if closed && stroker.output.might_need_join(self.style.line_join) {
let (p1, p0) = (stroker.output.position_of(1), stroker.output.position_of(0)); let (p1, p0) = (stroker.output.position_of(1), stroker.output.position_of(0));
let final_segment = LineSegment2F::new(p1, p0); let final_segment = LineSegment2F::new(p1, p0);
stroker.output.add_join(self.style.line_width * 0.5, stroker.output.add_join(
self.style.line_join, self.style.line_width * 0.5,
stroker.input.position_of(0), self.style.line_join,
final_segment); stroker.input.position_of(0),
final_segment,
);
} }
stroker.output.closed = true; stroker.output.closed = true;
@ -151,7 +165,7 @@ impl<'a> OutlineStrokeToFill<'a> {
fn add_cap(&mut self, contour: &mut Contour) { fn add_cap(&mut self, contour: &mut Contour) {
if self.style.line_cap == LineCap::Butt || contour.len() < 2 { if self.style.line_cap == LineCap::Butt || contour.len() < 2 {
return return;
} }
let width = self.style.line_width; let width = self.style.line_width;
@ -209,14 +223,23 @@ struct ContourStrokeToFill<'a> {
impl<'a> ContourStrokeToFill<'a> { impl<'a> ContourStrokeToFill<'a> {
#[inline] #[inline]
fn new(input: &Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill { fn new(input: &Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill {
ContourStrokeToFill { input, output, radius, join } ContourStrokeToFill {
input,
output,
radius,
join,
}
} }
fn offset_forward(&mut self) { fn offset_forward(&mut self) {
for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() { for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with... // clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; let join = if segment_index == 0 {
LineJoin::Bevel
} else {
self.join
};
segment.offset(-self.radius, join, &mut self.output); segment.offset(-self.radius, join, &mut self.output);
} }
} }
@ -231,7 +254,11 @@ impl<'a> ContourStrokeToFill<'a> {
for (segment_index, segment) in segments.iter().enumerate() { for (segment_index, segment) in segments.iter().enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with... // clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; let join = if segment_index == 0 {
LineJoin::Bevel
} else {
self.join
};
segment.offset(-self.radius, join, &mut self.output); segment.offset(-self.radius, join, &mut self.output);
} }
} }
@ -239,11 +266,13 @@ impl<'a> ContourStrokeToFill<'a> {
trait Offset { trait Offset {
fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour); fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour);
fn add_to_contour(&self, fn add_to_contour(
distance: f32, &self,
join: LineJoin, distance: f32,
join_point: Vector2F, join: LineJoin,
contour: &mut Contour); join_point: Vector2F,
contour: &mut Contour,
);
fn offset_once(&self, distance: f32) -> Self; fn offset_once(&self, distance: f32) -> Self;
fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool; fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool;
} }
@ -270,11 +299,13 @@ impl Offset for Segment {
after.offset(distance, join, contour); after.offset(distance, join, contour);
} }
fn add_to_contour(&self, fn add_to_contour(
distance: f32, &self,
join: LineJoin, distance: f32,
join_point: Vector2F, join: LineJoin,
contour: &mut Contour) { join_point: Vector2F,
contour: &mut Contour,
) {
// Add join if necessary. // Add join if necessary.
if contour.might_need_join(join) { if contour.might_need_join(join) {
let p3 = self.baseline.from(); let p3 = self.baseline.from();
@ -404,11 +435,13 @@ impl Contour {
} }
} }
fn add_join(&mut self, fn add_join(
distance: f32, &mut self,
join: LineJoin, distance: f32,
join_point: Vector2F, join: LineJoin,
next_tangent: LineSegment2F) { join_point: Vector2F,
next_tangent: LineSegment2F,
) {
let (p0, p1) = (self.position_of_last(2), self.position_of_last(1)); let (p0, p1) = (self.position_of_last(2), self.position_of_last(1));
let prev_tangent = LineSegment2F::new(p0, p1); let prev_tangent = LineSegment2F::new(p0, p1);
@ -456,10 +489,14 @@ impl Default for StrokeStyle {
impl Default for LineCap { impl Default for LineCap {
#[inline] #[inline]
fn default() -> LineCap { LineCap::Butt } fn default() -> LineCap {
LineCap::Butt
}
} }
impl Default for LineJoin { impl Default for LineJoin {
#[inline] #[inline]
fn default() -> LineJoin { LineJoin::Miter(10.0) } fn default() -> LineJoin {
LineJoin::Miter(10.0)
}
} }

View File

@ -36,8 +36,12 @@ where
// TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD? // TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD?
let mut segment = self.iter.next()?; let mut segment = self.iter.next()?;
if !segment.is_none() { if !segment.is_none() {
segment.baseline.set_from(self.transform * segment.baseline.from()); segment
segment.baseline.set_to(self.transform * segment.baseline.to()); .baseline
.set_from(self.transform * segment.baseline.from());
segment
.baseline
.set_to(self.transform * segment.baseline.to());
if !segment.is_line() { if !segment.is_line() {
segment.ctrl.set_from(self.transform * segment.ctrl.from()); segment.ctrl.set_from(self.transform * segment.ctrl.from());
if !segment.is_quadratic() { if !segment.is_quadratic() {
@ -83,10 +87,16 @@ where
fn next(&mut self) -> Option<Segment> { fn next(&mut self) -> Option<Segment> {
let mut segment = self.iter.next()?; let mut segment = self.iter.next()?;
if !segment.is_none() { if !segment.is_none() {
segment.baseline.set_from(self.perspective * segment.baseline.from()); segment
segment.baseline.set_to(self.perspective * segment.baseline.to()); .baseline
.set_from(self.perspective * segment.baseline.from());
segment
.baseline
.set_to(self.perspective * segment.baseline.to());
if !segment.is_line() { if !segment.is_line() {
segment.ctrl.set_from(self.perspective * segment.ctrl.from()); segment
.ctrl
.set_from(self.perspective * segment.ctrl.from());
if !segment.is_quadratic() { if !segment.is_quadratic() {
segment.ctrl.set_to(self.perspective * segment.ctrl.to()); segment.ctrl.set_to(self.perspective * segment.ctrl.to());
} }

View File

@ -16,30 +16,45 @@ use pathfinder_simd::default::{F32x2, F32x4};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem; use std::mem;
pub(crate) fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher { pub(crate) fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H)
where
H: Hasher,
{
hash_f32x4(line_segment.0, state); hash_f32x4(line_segment.0, state);
} }
pub(crate) fn hash_transform2f<H>(transform: Transform2F, state: &mut H) where H: Hasher { pub(crate) fn hash_transform2f<H>(transform: Transform2F, state: &mut H)
where
H: Hasher,
{
hash_f32x4(transform.matrix.0, state); hash_f32x4(transform.matrix.0, state);
hash_f32x2(transform.vector.0, state); hash_f32x2(transform.vector.0, state);
} }
pub(crate) fn hash_f32<H>(value: f32, state: &mut H) where H: Hasher { pub(crate) fn hash_f32<H>(value: f32, state: &mut H)
where
H: Hasher,
{
unsafe { unsafe {
let data: u32 = mem::transmute::<f32, u32>(value); let data: u32 = mem::transmute::<f32, u32>(value);
data.hash(state); data.hash(state);
} }
} }
pub(crate) fn hash_f32x2<H>(vector: F32x2, state: &mut H) where H: Hasher { pub(crate) fn hash_f32x2<H>(vector: F32x2, state: &mut H)
where
H: Hasher,
{
unsafe { unsafe {
let data: [u32; 2] = mem::transmute::<F32x2, [u32; 2]>(vector); let data: [u32; 2] = mem::transmute::<F32x2, [u32; 2]>(vector);
data.hash(state); data.hash(state);
} }
} }
pub(crate) fn hash_f32x4<H>(vector: F32x4, state: &mut H) where H: Hasher { pub(crate) fn hash_f32x4<H>(vector: F32x4, state: &mut H)
where
H: Hasher,
{
unsafe { unsafe {
let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(vector); let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(vector);
data.hash(state); data.hash(state);

View File

@ -16,8 +16,8 @@ use jni::{JNIEnv, JavaVM};
use pathfinder_demo::window::{Event, SVGPath, View, Window, WindowSize}; use pathfinder_demo::window::{Event, SVGPath, View, Window, WindowSize};
use pathfinder_demo::DemoApp; use pathfinder_demo::DemoApp;
use pathfinder_demo::Options; use pathfinder_demo::Options;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
use std::cell::RefCell; use std::cell::RefCell;
@ -131,7 +131,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
x: i32, x: i32,
y: i32, y: i32,
) { ) {
EVENT_QUEUE.lock().unwrap().push(Event::MouseDown(vec2i(x, y))) EVENT_QUEUE
.lock()
.unwrap()
.push(Event::MouseDown(vec2i(x, y)))
} }
#[no_mangle] #[no_mangle]
@ -141,7 +144,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
x: i32, x: i32,
y: i32, y: i32,
) { ) {
EVENT_QUEUE.lock().unwrap().push(Event::MouseDragged(vec2i(x, y))) EVENT_QUEUE
.lock()
.unwrap()
.push(Event::MouseDragged(vec2i(x, y)))
} }
#[no_mangle] #[no_mangle]
@ -152,7 +158,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
center_x: i32, center_x: i32,
center_y: i32, center_y: i32,
) { ) {
EVENT_QUEUE.lock().unwrap().push(Event::Zoom(factor, vec2i(center_x, center_y))) EVENT_QUEUE
.lock()
.unwrap()
.push(Event::Zoom(factor, vec2i(center_x, center_y)))
} }
#[no_mangle] #[no_mangle]

View File

@ -11,7 +11,7 @@ pf-gl = []
clap = "2.32" clap = "2.32"
gl = "0.14" gl = "0.14"
rayon = "1.0" rayon = "1.0"
usvg = "0.9" usvg = "0.10"
[dependencies.image] [dependencies.image]
version = "0.23" version = "0.23"

View File

@ -14,10 +14,10 @@
// proper. // proper.
use crate::window::{OcularTransform, View}; use crate::window::{OcularTransform, View};
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::{Perspective, Transform4F}; use pathfinder_geometry::transform3d::{Perspective, Transform4F};
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use std::f32::consts::FRAC_PI_4; use std::f32::consts::FRAC_PI_4;
const NEAR_CLIP_PLANE: f32 = 0.01; const NEAR_CLIP_PLANE: f32 = 0.01;
@ -53,8 +53,8 @@ impl Camera {
} }
fn new_2d(view_box: RectF, viewport_size: Vector2I) -> Camera { fn new_2d(view_box: RectF, viewport_size: Vector2I) -> Camera {
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 * let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
scale_factor_for_view_box(view_box); * scale_factor_for_view_box(view_box);
let origin = viewport_size.to_f32() * 0.5 - view_box.size() * (scale * 0.5); let origin = viewport_size.to_f32() * 0.5 - view_box.size() * (scale * 0.5);
Camera::TwoD(Transform2F::from_scale(scale).translate(origin)) Camera::TwoD(Transform2F::from_scale(scale).translate(origin))
} }
@ -154,9 +154,10 @@ impl CameraTransform3D {
pub fn to_transform(&self) -> Transform4F { pub fn to_transform(&self) -> Transform4F {
let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0); let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0);
Transform4F::from_scale(flip).translate(-self.position) Transform4F::from_scale(flip)
.uniform_scale(2.0 * self.scale) .translate(-self.position)
.rotate(self.yaw, self.pitch, 0.0) .uniform_scale(2.0 * self.scale)
.rotate(self.yaw, self.pitch, 0.0)
} }
} }

View File

@ -35,7 +35,10 @@ impl DemoExecutor {
impl Executor for DemoExecutor { impl Executor for DemoExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T> fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync { where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
if self.sequential_mode { if self.sequential_mode {
SequentialExecutor.build_vector(length, builder) SequentialExecutor.build_vector(length, builder)
} else { } else {

View File

@ -63,19 +63,33 @@ where
) -> GroundVertexArray<D> { ) -> GroundVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&ground_program.program, "Position").unwrap(); let position_attr = device
.get_vertex_attr(&ground_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &position_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
GroundVertexArray { vertex_array } GroundVertexArray { vertex_array }
} }

View File

@ -20,10 +20,10 @@ use crate::camera::Camera;
use crate::concurrent::DemoExecutor; use crate::concurrent::DemoExecutor;
use crate::device::{GroundProgram, GroundVertexArray}; use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction}; use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, DataPath, Window, WindowSize}; use crate::window::{DataPath, Event, Keycode, Window, WindowSize};
use clap::{App, Arg}; use clap::{App, Arg};
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_content::effects::PatternFilter; use pathfinder_content::effects::PatternFilter;
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_content::effects::STEM_DARKENING_FACTORS; use pathfinder_content::effects::STEM_DARKENING_FACTORS;
use pathfinder_content::outline::Outline; use pathfinder_content::outline::Outline;
use pathfinder_content::pattern::Pattern; use pathfinder_content::pattern::Pattern;
@ -32,7 +32,7 @@ use pathfinder_export::{Export, FileFormat};
use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I, Vector4F};
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererLevel}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererLevel};
@ -44,14 +44,14 @@ use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
use pathfinder_svg::SVGScene; use pathfinder_svg::SVGScene;
use pathfinder_ui::{MousePosition, UIEvent}; use pathfinder_ui::{MousePosition, UIEvent};
use pdf::file::{CachedFile, FileOptions};
use pdf_render::{Cache as PdfRenderCache, SceneBackend};
use std::fs::File; use std::fs::File;
use std::io::BufWriter; use std::io::BufWriter;
use std::path::PathBuf; use std::path::PathBuf;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use usvg::{Options as UsvgOptions, Tree as SvgTree}; use usvg::{Options as UsvgOptions, Tree as SvgTree};
use pdf::file::{CachedFile, FileOptions};
use pdf_render::{Cache as PdfRenderCache, SceneBackend};
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use pathfinder_gl::GLDevice as DeviceImpl; use pathfinder_gl::GLDevice as DeviceImpl;
@ -88,11 +88,14 @@ enum Content {
Pdf { Pdf {
file: CachedFile<Vec<u8>>, file: CachedFile<Vec<u8>>,
cache: PdfRenderCache, cache: PdfRenderCache,
page_nr: u32 page_nr: u32,
} },
} }
pub struct DemoApp<W> where W: Window { pub struct DemoApp<W>
where
W: Window,
{
pub window: W, pub window: W,
pub should_exit: bool, pub should_exit: bool,
pub options: Options, pub options: Options,
@ -126,7 +129,10 @@ pub struct DemoApp<W> where W: Window {
ground_vertex_array: GroundVertexArray<DeviceImpl>, ground_vertex_array: GroundVertexArray<DeviceImpl>,
} }
impl<W> DemoApp<W> where W: Window { impl<W> DemoApp<W>
where
W: Window,
{
pub fn new(window: W, window_size: WindowSize, options: Options) -> DemoApp<W> { pub fn new(window: W, window_size: WindowSize, options: Options) -> DemoApp<W> {
let expire_message_event_id = window.create_user_event_id(); let expire_message_event_id = window.create_user_event_id();
@ -172,17 +178,18 @@ impl<W> DemoApp<W> where W: Window {
let renderer = Renderer::new(device, resources, render_mode, render_options); let renderer = Renderer::new(device, resources, render_mode, render_options);
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut scene, let scene_metadata = SceneMetadata::new_clipping_view_box(&mut scene, viewport.size());
viewport.size());
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size()); let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
let scene_proxy = SceneProxy::from_scene(scene, level, executor); let scene_proxy = SceneProxy::from_scene(scene, level, executor);
let ground_program = GroundProgram::new(renderer.device(), resources); let ground_program = GroundProgram::new(renderer.device(), resources);
let ground_vertex_array = GroundVertexArray::new(renderer.device(), let ground_vertex_array = GroundVertexArray::new(
&ground_program, renderer.device(),
&renderer.quad_vertex_positions_buffer(), &ground_program,
&renderer.quad_vertex_indices_buffer()); &renderer.quad_vertex_positions_buffer(),
&renderer.quad_vertex_indices_buffer(),
);
let mut message_epoch = 0; let mut message_epoch = 0;
emit_message::<W>( emit_message::<W>(
@ -260,9 +267,9 @@ impl<W> DemoApp<W> where W: Window {
if modelview_transform.offset(*velocity) { if modelview_transform.offset(*velocity) {
self.dirty = true; self.dirty = true;
} }
let perspective = scene_transform.perspective * let perspective = scene_transform.perspective
scene_transform.modelview_to_eye * * scene_transform.modelview_to_eye
modelview_transform.to_transform(); * modelview_transform.to_transform();
Some(RenderTransform::Perspective(perspective)) Some(RenderTransform::Perspective(perspective))
} }
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)), Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
@ -281,7 +288,7 @@ impl<W> DemoApp<W> where W: Window {
self.scene_proxy.build(build_options); self.scene_proxy.build(build_options);
/* /*
self.render_command_stream = self.render_command_stream =
Some(self.scene_proxy.build_with_stream(build_options, self.renderer.gpu_features())); Some(self.scene_proxy.build_with_stream(build_options, self.renderer.gpu_features()));
*/ */
} }
@ -299,8 +306,8 @@ impl<W> DemoApp<W> where W: Window {
Event::WindowResized(new_size) => { Event::WindowResized(new_size) => {
self.window_size = new_size; self.window_size = new_size;
let viewport = self.window.viewport(self.ui_model.mode.view(0)); let viewport = self.window.viewport(self.ui_model.mode.view(0));
self.scene_proxy.set_view_box(RectF::new(Vector2F::zero(), self.scene_proxy
viewport.size().to_f32())); .set_view_box(RectF::new(Vector2F::zero(), viewport.size().to_f32()));
self.renderer.options_mut().dest = self.renderer.options_mut().dest =
DestFramebuffer::full_window(self.window_size.device_size()); DestFramebuffer::full_window(self.window_size.device_size());
self.renderer.dest_framebuffer_size_changed(); self.renderer.dest_framebuffer_size_changed();
@ -312,7 +319,11 @@ impl<W> DemoApp<W> where W: Window {
} }
Event::MouseMoved(new_position) if self.mouselook_enabled => { Event::MouseMoved(new_position) if self.mouselook_enabled => {
let mouse_position = self.process_mouse_position(new_position); let mouse_position = self.process_mouse_position(new_position);
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera { if let Camera::ThreeD {
ref mut modelview_transform,
..
} = self.camera
{
let rotation = mouse_position.relative.to_f32() * MOUSELOOK_ROTATION_SPEED; let rotation = mouse_position.relative.to_f32() * MOUSELOOK_ROTATION_SPEED;
modelview_transform.yaw += rotation.x(); modelview_transform.yaw += rotation.x();
modelview_transform.pitch += rotation.y(); modelview_transform.pitch += rotation.y();
@ -329,13 +340,18 @@ impl<W> DemoApp<W> where W: Window {
let backing_scale_factor = self.window_size.backing_scale_factor; let backing_scale_factor = self.window_size.backing_scale_factor;
let position = position.to_f32() * backing_scale_factor; let position = position.to_f32() * backing_scale_factor;
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D; let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
*transform = transform.translate(-position) *transform = transform
.scale(scale_delta) .translate(-position)
.translate(position); .scale(scale_delta)
.translate(position);
} }
} }
Event::Look { pitch, yaw } => { Event::Look { pitch, yaw } => {
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera { if let Camera::ThreeD {
ref mut modelview_transform,
..
} = self.camera
{
modelview_transform.pitch += pitch; modelview_transform.pitch += pitch;
modelview_transform.yaw += yaw; modelview_transform.yaw += yaw;
} }
@ -352,21 +368,20 @@ impl<W> DemoApp<W> where W: Window {
*scene_transform = eye_transforms[0]; *scene_transform = eye_transforms[0];
for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) { for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) {
let weight = 1.0 / (index + 1) as f32; let weight = 1.0 / (index + 1) as f32;
scene_transform.perspective.transform = scene_transform.perspective.transform = scene_transform
scene_transform.perspective .perspective
.transform .transform
.lerp(weight, &eye_transform.perspective.transform); .lerp(weight, &eye_transform.perspective.transform);
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye = scene_transform
scene_transform.modelview_to_eye .modelview_to_eye
.lerp(weight, &eye_transform.modelview_to_eye); .lerp(weight, &eye_transform.modelview_to_eye);
} }
// TODO: calculate the eye offset from the eye transforms? // TODO: calculate the eye offset from the eye transforms?
let z_offset = -DEFAULT_EYE_OFFSET * let z_offset =
scene_transform.perspective.transform.c0.x(); -DEFAULT_EYE_OFFSET * scene_transform.perspective.transform.c0.x();
let z_offset = Vector4F::new(0.0, 0.0, z_offset, 1.0); let z_offset = Vector4F::new(0.0, 0.0, z_offset, 1.0);
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye = Transform4F::from_translation(z_offset)
Transform4F::from_translation(z_offset) * * scene_transform.modelview_to_eye;
scene_transform.modelview_to_eye;
} }
} }
Event::KeyDown(Keycode::Alphanumeric(b'w')) => { Event::KeyDown(Keycode::Alphanumeric(b'w')) => {
@ -452,9 +467,11 @@ impl<W> DemoApp<W> where W: Window {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
self.scene_metadata = self.scene_metadata =
SceneMetadata::new_clipping_view_box(&mut scene, viewport_size); SceneMetadata::new_clipping_view_box(&mut scene, viewport_size);
self.camera = Camera::new(self.ui_model.mode, self.camera = Camera::new(
self.scene_metadata.view_box, self.ui_model.mode,
viewport_size); self.scene_metadata.view_box,
viewport_size,
);
self.scene_proxy.replace_scene(scene); self.scene_proxy.replace_scene(scene);
@ -498,19 +515,28 @@ impl<W> DemoApp<W> where W: Window {
.push(*ui_event); .push(*ui_event);
} }
self.renderer.debug_ui_presenter_mut().debug_ui_presenter.ui_presenter.mouse_position = self.renderer
.debug_ui_presenter_mut()
.debug_ui_presenter
.ui_presenter
.mouse_position =
self.last_mouse_position.to_f32() * self.window_size.backing_scale_factor; self.last_mouse_position.to_f32() * self.window_size.backing_scale_factor;
let mut ui_action = UIAction::None; let mut ui_action = UIAction::None;
if self.options.ui == UIVisibility::All { if self.options.ui == UIVisibility::All {
let DebugUIPresenterInfo { device, allocator, debug_ui_presenter } = let DebugUIPresenterInfo {
self.renderer.debug_ui_presenter_mut(); device,
self.ui_presenter.update(device, allocator,
allocator, debug_ui_presenter,
&mut self.window, } = self.renderer.debug_ui_presenter_mut();
debug_ui_presenter, self.ui_presenter.update(
&mut ui_action, device,
&mut self.ui_model); allocator,
&mut self.window,
debug_ui_presenter,
&mut ui_action,
&mut self.ui_model,
);
} }
self.handle_ui_events(frame, &mut ui_action); self.handle_ui_events(frame, &mut ui_action);
@ -524,24 +550,32 @@ impl<W> DemoApp<W> where W: Window {
fn maybe_take_screenshot(&mut self) { fn maybe_take_screenshot(&mut self) {
match self.pending_screenshot_info.take() { match self.pending_screenshot_info.take() {
None => {} None => {}
Some(ScreenshotInfo { kind: ScreenshotType::PNG, path }) => { Some(ScreenshotInfo {
self.take_raster_screenshot(path) kind: ScreenshotType::PNG,
} path,
Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => { }) => self.take_raster_screenshot(path),
Some(ScreenshotInfo {
kind: ScreenshotType::SVG,
path,
}) => {
// FIXME(pcwalton): This won't work on Android. // FIXME(pcwalton): This won't work on Android.
let mut writer = BufWriter::new(File::create(path).unwrap()); let mut writer = BufWriter::new(File::create(path).unwrap());
self.scene_proxy.copy_scene().export(&mut writer, FileFormat::SVG).unwrap(); self.scene_proxy
.copy_scene()
.export(&mut writer, FileFormat::SVG)
.unwrap();
} }
} }
} }
fn handle_ui_events(&mut self, mut frame: Frame, ui_action: &mut UIAction) { fn handle_ui_events(&mut self, mut frame: Frame, ui_action: &mut UIAction) {
frame.ui_events = self.renderer frame.ui_events = self
.debug_ui_presenter_mut() .renderer
.debug_ui_presenter .debug_ui_presenter_mut()
.ui_presenter .debug_ui_presenter
.event_queue .ui_presenter
.drain(); .event_queue
.drain();
self.handle_ui_action(ui_action); self.handle_ui_action(ui_action);
@ -550,9 +584,11 @@ impl<W> DemoApp<W> where W: Window {
// FIXME(pcwalton): This should really be an MVC setup. // FIXME(pcwalton): This should really be an MVC setup.
if self.camera.mode() != self.ui_model.mode { if self.camera.mode() != self.ui_model.mode {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
self.camera = Camera::new(self.ui_model.mode, self.camera = Camera::new(
self.scene_metadata.view_box, self.ui_model.mode,
viewport_size); self.scene_metadata.view_box,
viewport_size,
);
} }
for ui_event in frame.ui_events { for ui_event in frame.ui_events {
@ -614,9 +650,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let old_rotation = transform.rotation(); let old_rotation = transform.rotation();
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform.translate(-center) *transform = transform
.rotate(*theta - old_rotation) .translate(-center)
.translate(center); .rotate(*theta - old_rotation)
.translate(center);
} }
} }
} }
@ -695,7 +732,7 @@ impl Options {
Arg::with_name("high-performance-gpu") Arg::with_name("high-performance-gpu")
.short("g") .short("g")
.long("high-performance-gpu") .long("high-performance-gpu")
.help("Use the high-performance (discrete) GPU, if available") .help("Use the high-performance (discrete) GPU, if available"),
) )
.arg( .arg(
Arg::with_name("level") Arg::with_name("level")
@ -703,7 +740,7 @@ impl Options {
.short("l") .short("l")
.help("Set the renderer feature level as a Direct3D version equivalent") .help("Set the renderer feature level as a Direct3D version equivalent")
.takes_value(true) .takes_value(true)
.possible_values(&["9", "11"]) .possible_values(&["9", "11"]),
) )
.arg( .arg(
Arg::with_name("INPUT") Arg::with_name("INPUT")
@ -764,36 +801,52 @@ pub enum UIVisibility {
} }
impl Content { impl Content {
fn render(&mut self, viewport_size: Vector2I, filter: Option<PatternFilter>) -> (Scene, String) { fn render(
&mut self,
viewport_size: Vector2I,
filter: Option<PatternFilter>,
) -> (Scene, String) {
match *self { match *self {
Content::Svg(ref tree) => { Content::Svg(ref tree) => {
let built_svg = build_svg_tree(&tree, viewport_size, filter); let built_svg = build_svg_tree(&tree, viewport_size, filter);
let message = get_svg_building_message(&built_svg); let message = get_svg_building_message(&built_svg);
(built_svg.scene, message) (built_svg.scene, message)
} }
Content::Pdf { ref file, ref mut cache, page_nr } => { Content::Pdf {
ref file,
ref mut cache,
page_nr,
} => {
let page = file.get_page(page_nr).expect("no such page"); let page = file.get_page(page_nr).expect("no such page");
let mut backend = SceneBackend::new(cache); let mut backend = SceneBackend::new(cache);
pdf_render::render_page(&mut backend, &file.resolver(), &page, Transform2F::default()).unwrap(); pdf_render::render_page(
&mut backend,
&file.resolver(),
&page,
Transform2F::default(),
)
.unwrap();
(backend.finish(), String::new()) (backend.finish(), String::new())
} }
} }
} }
} }
fn load_scene(resource_loader: &dyn ResourceLoader, fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &DataPath) -> Content {
input_path: &DataPath,)
-> Content {
let data = match *input_path { let data = match *input_path {
DataPath::Default => resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(), DataPath::Default => resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
DataPath::Resource(ref name) => resource_loader.slurp(name).unwrap(), DataPath::Resource(ref name) => resource_loader.slurp(name).unwrap(),
DataPath::Path(ref path) => std::fs::read(path).unwrap().into() DataPath::Path(ref path) => std::fs::read(path).unwrap().into(),
}; };
if let Ok(tree) = SvgTree::from_data(&data, &UsvgOptions::default()) { if let Ok(tree) = SvgTree::from_data(&data, &UsvgOptions::default()) {
Content::Svg(tree) Content::Svg(tree)
} else if let Ok(file) = FileOptions::cached().load(data) { } else if let Ok(file) = FileOptions::cached().load(data) {
Content::Pdf { file, cache: PdfRenderCache::new(), page_nr: 0 } Content::Pdf {
file,
cache: PdfRenderCache::new(),
page_nr: 0,
}
} else { } else {
panic!("can't load data"); panic!("can't load data");
} }
@ -801,22 +854,38 @@ fn load_scene(resource_loader: &dyn ResourceLoader,
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final // FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
// composite steps, breaking this approach. // composite steps, breaking this approach.
fn build_svg_tree(tree: &SvgTree, viewport_size: Vector2I, filter: Option<PatternFilter>) -> SVGScene { fn build_svg_tree(
tree: &SvgTree,
viewport_size: Vector2I,
filter: Option<PatternFilter>,
) -> SVGScene {
let mut scene = Scene::new(); let mut scene = Scene::new();
let filter_info = filter.map(|filter| { let filter_info = filter.map(|filter| {
let scale = match filter { let scale = match filter {
PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1), PatternFilter::Text {
defringing_kernel: Some(_),
..
} => vec2i(3, 1),
_ => vec2i(1, 1), _ => vec2i(1, 1),
}; };
let name = "Text".to_owned(); let name = "Text".to_owned();
let render_target_size = viewport_size * scale; let render_target_size = viewport_size * scale;
let render_target = RenderTarget::new(render_target_size, name); let render_target = RenderTarget::new(render_target_size, name);
let render_target_id = scene.push_render_target(render_target); let render_target_id = scene.push_render_target(render_target);
FilterInfo { filter, render_target_id, render_target_size } FilterInfo {
filter,
render_target_id,
render_target_size,
}
}); });
let mut built_svg = SVGScene::from_tree_and_scene(&tree, scene); let mut built_svg = SVGScene::from_tree_and_scene(&tree, scene);
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info { if let Some(FilterInfo {
filter,
render_target_id,
render_target_size,
}) = filter_info
{
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size); let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
pattern.set_filter(Some(filter)); pattern.set_filter(Some(filter));
let paint_id = built_svg.scene.push_paint(&Paint::from_pattern(pattern)); let paint_id = built_svg.scene.push_paint(&Paint::from_pattern(pattern));
@ -879,7 +948,10 @@ struct Frame {
impl Frame { impl Frame {
fn new(transform: RenderTransform, ui_events: Vec<UIEvent>) -> Frame { fn new(transform: RenderTransform, ui_events: Vec<UIEvent>) -> Frame {
Frame { transform, ui_events } Frame {
transform,
ui_events,
}
} }
} }
@ -928,6 +1000,6 @@ fn build_filter(ui_model: &DemoUIModel) -> Option<PatternFilter> {
Some(DEFRINGING_KERNEL_CORE_GRAPHICS) Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else { } else {
None None
} },
}) })
} }

View File

@ -15,11 +15,11 @@ use crate::window::{View, Window};
use crate::{BackgroundColor, DemoApp, UIVisibility}; use crate::{BackgroundColor, DemoApp, UIVisibility};
use image::ColorType; use image::ColorType;
use pathfinder_color::{ColorF, ColorU}; use pathfinder_color::{ColorF, ColorU};
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, Vector4F}; use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::options::RenderTransform; use pathfinder_renderer::options::RenderTransform;
use std::mem; use std::mem;
@ -41,7 +41,10 @@ const GROUND_LINE_COLOR: ColorU = ColorU {
const GRIDLINE_COUNT: i32 = 10; const GRIDLINE_COUNT: i32 = 10;
impl<W> DemoApp<W> where W: Window { impl<W> DemoApp<W>
where
W: Window,
{
pub fn prepare_frame_rendering(&mut self) -> u32 { pub fn prepare_frame_rendering(&mut self) -> u32 {
// Make the context current. // Make the context current.
let view = self.ui_model.mode.view(0); let view = self.ui_model.mode.view(0);
@ -62,9 +65,10 @@ impl<W> DemoApp<W> where W: Window {
let viewport = self.window.viewport(View::Stereo(0)); let viewport = self.window.viewport(View::Stereo(0));
if self.scene_framebuffer.is_none() if self.scene_framebuffer.is_none()
|| self.renderer.device().texture_size( || self.renderer.device().texture_size(
&self.renderer.device().framebuffer_texture(self.scene_framebuffer &self
.as_ref() .renderer
.unwrap()), .device()
.framebuffer_texture(self.scene_framebuffer.as_ref().unwrap()),
) != viewport.size() ) != viewport.size()
{ {
let scene_texture = self let scene_texture = self
@ -120,9 +124,9 @@ impl<W> DemoApp<W> where W: Window {
}, },
..*self.renderer.options() ..*self.renderer.options()
}; };
if let DestFramebuffer::Other(scene_framebuffer) = mem::replace(self.renderer if let DestFramebuffer::Other(scene_framebuffer) =
.options_mut(), mem::replace(self.renderer.options_mut(), new_options).dest
new_options).dest { {
self.scene_framebuffer = Some(scene_framebuffer); self.scene_framebuffer = Some(scene_framebuffer);
} }
} }
@ -165,22 +169,25 @@ impl<W> DemoApp<W> where W: Window {
self.draw_environment(render_scene_index); self.draw_environment(render_scene_index);
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
let scene_texture = self.renderer.device().framebuffer_texture(scene_framebuffer); let scene_texture = self
.renderer
.device()
.framebuffer_texture(scene_framebuffer);
let mut quad_scale = self.scene_metadata.view_box.size().to_4d(); let mut quad_scale = self.scene_metadata.view_box.size().to_4d();
quad_scale.set_z(1.0); quad_scale.set_z(1.0);
let quad_scale_transform = Transform4F::from_scale(quad_scale); let quad_scale_transform = Transform4F::from_scale(quad_scale);
let scene_transform_matrix = scene_transform.perspective * let scene_transform_matrix = scene_transform.perspective
scene_transform.modelview_to_eye * * scene_transform.modelview_to_eye
modelview_transform.to_transform() * * modelview_transform.to_transform()
quad_scale_transform; * quad_scale_transform;
let eye_transform = &eye_transforms[render_scene_index as usize]; let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform.perspective * let eye_transform_matrix = eye_transform.perspective
eye_transform.modelview_to_eye * * eye_transform.modelview_to_eye
modelview_transform.to_transform() * * modelview_transform.to_transform()
quad_scale_transform; * quad_scale_transform;
debug!( debug!(
"eye transform({}).modelview_to_eye={:?}", "eye transform({}).modelview_to_eye={:?}",
@ -220,8 +227,8 @@ impl<W> DemoApp<W> where W: Window {
let base_transform = perspective.transform * Transform4F::from_translation(offset); let base_transform = perspective.transform * Transform4F::from_translation(offset);
// Fill ground. // Fill ground.
let transform = base_transform * let transform = base_transform
Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0)); * Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0));
// Don't clear the first scene after drawing it. // Don't clear the first scene after drawing it.
let clear_color = if render_scene_index == 0 { let clear_color = if render_scene_index == 0 {
@ -230,30 +237,49 @@ impl<W> DemoApp<W> where W: Window {
None None
}; };
self.renderer.device().draw_elements(6, &RenderState { self.renderer.device().draw_elements(
target: &self.renderer.draw_render_target(), 6,
program: &self.ground_program.program, &RenderState {
vertex_array: &self.ground_vertex_array.vertex_array, target: &self.renderer.draw_render_target(),
primitive: Primitive::Triangles, program: &self.ground_program.program,
textures: &[], vertex_array: &self.ground_vertex_array.vertex_array,
images: &[], primitive: Primitive::Triangles,
storage_buffers: &[], textures: &[],
uniforms: &[ images: &[],
(&self.ground_program.transform_uniform, storage_buffers: &[],
UniformData::from_transform_3d(&transform)), uniforms: &[
(&self.ground_program.ground_color_uniform, (
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0)), &self.ground_program.transform_uniform,
(&self.ground_program.gridline_color_uniform, UniformData::from_transform_3d(&transform),
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0)), ),
(&self.ground_program.gridline_count_uniform, UniformData::Int(GRIDLINE_COUNT)), (
], &self.ground_program.ground_color_uniform,
viewport: self.renderer.draw_viewport(), UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0),
options: RenderOptions { ),
depth: Some(DepthState { func: DepthFunc::Less, write: true }), (
clear_ops: ClearOps { color: clear_color, depth: Some(1.0), stencil: Some(0) }, &self.ground_program.gridline_color_uniform,
..RenderOptions::default() UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0),
),
(
&self.ground_program.gridline_count_uniform,
UniformData::Int(GRIDLINE_COUNT),
),
],
viewport: self.renderer.draw_viewport(),
options: RenderOptions {
depth: Some(DepthState {
func: DepthFunc::Less,
write: true,
}),
clear_ops: ClearOps {
color: clear_color,
depth: Some(1.0),
stencil: Some(0),
},
..RenderOptions::default()
},
}, },
}); );
} }
#[allow(deprecated)] #[allow(deprecated)]
@ -271,9 +297,15 @@ impl<W> DemoApp<W> where W: Window {
pub fn take_raster_screenshot(&mut self, path: PathBuf) { pub fn take_raster_screenshot(&mut self, path: PathBuf) {
let drawable_size = self.window_size.device_size(); let drawable_size = self.window_size.device_size();
let viewport = RectI::new(Vector2I::default(), drawable_size); let viewport = RectI::new(Vector2I::default(), drawable_size);
let texture_data_receiver = let texture_data_receiver = self
self.renderer.device().read_pixels(&RenderTarget::Default, viewport); .renderer
let pixels = match self.renderer.device().recv_texture_data(&texture_data_receiver) { .device()
.read_pixels(&RenderTarget::Default, viewport);
let pixels = match self
.renderer
.device()
.recv_texture_data(&texture_data_receiver)
{
TextureData::U8(pixels) => pixels, TextureData::U8(pixels) => pixels,
_ => panic!("Unexpected pixel format for default framebuffer!"), _ => panic!("Unexpected pixel format for default framebuffer!"),
}; };

View File

@ -13,7 +13,7 @@ use crate::window::Window;
use crate::{BackgroundColor, Options}; use crate::{BackgroundColor, Options};
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gpu::allocator::GPUMemoryAllocator; use pathfinder_gpu::allocator::GPUMemoryAllocator;
use pathfinder_gpu::{Device, TextureFormat}; use pathfinder_gpu::{Device, TextureFormat};
use pathfinder_renderer::gpu::debug::DebugUIPresenter; use pathfinder_renderer::gpu::debug::DebugUIPresenter;
@ -41,9 +41,24 @@ const SCREENSHOT_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 2;
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2; const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT; const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
const LIGHT_BG_COLOR: ColorU = ColorU { r: 248, g: 248, b: 248, a: 255, }; const LIGHT_BG_COLOR: ColorU = ColorU {
const DARK_BG_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255, }; r: 248,
const TRANSPARENT_BG_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 0, }; g: 248,
b: 248,
a: 255,
};
const DARK_BG_COLOR: ColorU = ColorU {
r: 32,
g: 32,
b: 32,
a: 255,
};
const TRANSPARENT_BG_COLOR: ColorU = ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
};
static EFFECTS_PNG_NAME: &'static str = "demo-effects"; static EFFECTS_PNG_NAME: &'static str = "demo-effects";
static OPEN_PNG_NAME: &'static str = "demo-open"; static OPEN_PNG_NAME: &'static str = "demo-open";
@ -98,7 +113,10 @@ impl DemoUIModel {
} }
} }
pub struct DemoUIPresenter<D> where D: Device { pub struct DemoUIPresenter<D>
where
D: Device,
{
effects_texture: D::Texture, effects_texture: D::Texture,
open_texture: D::Texture, open_texture: D::Texture,
rotate_texture: D::Texture, rotate_texture: D::Texture,
@ -114,34 +132,29 @@ pub struct DemoUIPresenter<D> where D: Device {
rotate_panel_visible: bool, rotate_panel_visible: bool,
} }
impl<D> DemoUIPresenter<D> where D: Device { impl<D> DemoUIPresenter<D>
where
D: Device,
{
pub fn new(device: &D, resources: &dyn ResourceLoader) -> DemoUIPresenter<D> { pub fn new(device: &D, resources: &dyn ResourceLoader) -> DemoUIPresenter<D> {
device.begin_commands(); device.begin_commands();
let effects_texture = device.create_texture_from_png(resources, let effects_texture =
EFFECTS_PNG_NAME, device.create_texture_from_png(resources, EFFECTS_PNG_NAME, TextureFormat::R8);
TextureFormat::R8); let open_texture =
let open_texture = device.create_texture_from_png(resources, device.create_texture_from_png(resources, OPEN_PNG_NAME, TextureFormat::R8);
OPEN_PNG_NAME, let rotate_texture =
TextureFormat::R8); device.create_texture_from_png(resources, ROTATE_PNG_NAME, TextureFormat::R8);
let rotate_texture = device.create_texture_from_png(resources, let zoom_in_texture =
ROTATE_PNG_NAME, device.create_texture_from_png(resources, ZOOM_IN_PNG_NAME, TextureFormat::R8);
TextureFormat::R8); let zoom_actual_size_texture =
let zoom_in_texture = device.create_texture_from_png(resources, device.create_texture_from_png(resources, ZOOM_ACTUAL_SIZE_PNG_NAME, TextureFormat::R8);
ZOOM_IN_PNG_NAME, let zoom_out_texture =
TextureFormat::R8); device.create_texture_from_png(resources, ZOOM_OUT_PNG_NAME, TextureFormat::R8);
let zoom_actual_size_texture = device.create_texture_from_png(resources, let background_texture =
ZOOM_ACTUAL_SIZE_PNG_NAME, device.create_texture_from_png(resources, BACKGROUND_PNG_NAME, TextureFormat::R8);
TextureFormat::R8); let screenshot_texture =
let zoom_out_texture = device.create_texture_from_png(resources, device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME, TextureFormat::R8);
ZOOM_OUT_PNG_NAME,
TextureFormat::R8);
let background_texture = device.create_texture_from_png(resources,
BACKGROUND_PNG_NAME,
TextureFormat::R8);
let screenshot_texture = device.create_texture_from_png(resources,
SCREENSHOT_PNG_NAME,
TextureFormat::R8);
device.end_commands(); device.end_commands();
@ -162,14 +175,17 @@ impl<D> DemoUIPresenter<D> where D: Device {
} }
} }
pub fn update<W>(&mut self, pub fn update<W>(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
window: &mut W, allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>, window: &mut W,
action: &mut UIAction, debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel) action: &mut UIAction,
where W: Window { model: &mut DemoUIModel,
) where
W: Window,
{
// Draw message text. // Draw message text.
self.draw_message_text(device, allocator, debug_ui_presenter, model); self.draw_message_text(device, allocator, debug_ui_presenter, model);
@ -182,34 +198,50 @@ impl<D> DemoUIPresenter<D> where D: Device {
let button_size = vec2i(BUTTON_WIDTH, BUTTON_HEIGHT); let button_size = vec2i(BUTTON_WIDTH, BUTTON_HEIGHT);
// Draw effects button. // Draw effects button.
if debug_ui_presenter.ui_presenter if debug_ui_presenter.ui_presenter.draw_button(
.draw_button(device, allocator, position, &self.effects_texture) { device,
allocator,
position,
&self.effects_texture,
) {
self.effects_panel_visible = !self.effects_panel_visible; self.effects_panel_visible = !self.effects_panel_visible;
} }
if !self.effects_panel_visible { if !self.effects_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(device, debug_ui_presenter.ui_presenter.draw_tooltip(
allocator, device,
"Effects", allocator,
RectI::new(position, button_size)); "Effects",
RectI::new(position, button_size),
);
} }
position += vec2i(button_size.x() + PADDING, 0); position += vec2i(button_size.x() + PADDING, 0);
// Draw open button. // Draw open button.
if debug_ui_presenter.ui_presenter if debug_ui_presenter.ui_presenter.draw_button(
.draw_button(device, allocator, position, &self.open_texture) { device,
allocator,
position,
&self.open_texture,
) {
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
// the contents of the file. // the contents of the file.
window.present_open_svg_dialog(); window.present_open_svg_dialog();
} }
debug_ui_presenter.ui_presenter.draw_tooltip(device, debug_ui_presenter.ui_presenter.draw_tooltip(
allocator, device,
"Open SVG", allocator,
RectI::new(position, button_size)); "Open SVG",
RectI::new(position, button_size),
);
position += vec2i(BUTTON_WIDTH + PADDING, 0); position += vec2i(BUTTON_WIDTH + PADDING, 0);
// Draw screenshot button. // Draw screenshot button.
if debug_ui_presenter.ui_presenter if debug_ui_presenter.ui_presenter.draw_button(
.draw_button(device, allocator, position, &self.screenshot_texture) { device,
allocator,
position,
&self.screenshot_texture,
) {
self.screenshot_panel_visible = !self.screenshot_panel_visible; self.screenshot_panel_visible = !self.screenshot_panel_visible;
} }
if !self.screenshot_panel_visible { if !self.screenshot_panel_visible {
@ -222,20 +254,24 @@ impl<D> DemoUIPresenter<D> where D: Device {
} }
// Draw screenshot panel, if necessary. // Draw screenshot panel, if necessary.
self.draw_screenshot_panel(device, self.draw_screenshot_panel(
allocator, device,
window, allocator,
debug_ui_presenter, window,
position.x(), debug_ui_presenter,
action); position.x(),
action,
);
position += vec2i(button_size.x() + PADDING, 0); position += vec2i(button_size.x() + PADDING, 0);
// Draw mode switch. // Draw mode switch.
let new_mode = debug_ui_presenter.ui_presenter.draw_text_switch(device, let new_mode = debug_ui_presenter.ui_presenter.draw_text_switch(
allocator, device,
position, allocator,
&["2D", "3D", "VR"], position,
model.mode as u8); &["2D", "3D", "VR"],
model.mode as u8,
);
if new_mode != model.mode as u8 { if new_mode != model.mode as u8 {
model.mode = match new_mode { model.mode = match new_mode {
0 => Mode::TwoD, 0 => Mode::TwoD,
@ -256,10 +292,12 @@ impl<D> DemoUIPresenter<D> where D: Device {
position += vec2i(mode_switch_width + PADDING, 0); position += vec2i(mode_switch_width + PADDING, 0);
// Draw background switch. // Draw background switch.
if debug_ui_presenter.ui_presenter.draw_button(device, if debug_ui_presenter.ui_presenter.draw_button(
allocator, device,
position, allocator,
&self.background_texture) { position,
&self.background_texture,
) {
self.background_panel_visible = !self.background_panel_visible; self.background_panel_visible = !self.background_panel_visible;
} }
if !self.background_panel_visible { if !self.background_panel_visible {
@ -272,12 +310,14 @@ impl<D> DemoUIPresenter<D> where D: Device {
} }
// Draw background panel, if necessary. // Draw background panel, if necessary.
self.draw_background_panel(device, self.draw_background_panel(
allocator, device,
debug_ui_presenter, allocator,
position.x(), debug_ui_presenter,
action, position.x(),
model); action,
model,
);
position += vec2i(button_size.x() + PADDING, 0); position += vec2i(button_size.x() + PADDING, 0);
// Draw effects panel, if necessary. // Draw effects panel, if necessary.
@ -288,49 +328,65 @@ impl<D> DemoUIPresenter<D> where D: Device {
return; return;
} }
if debug_ui_presenter.ui_presenter.draw_button(device, if debug_ui_presenter.ui_presenter.draw_button(
allocator, device,
position, allocator,
&self.rotate_texture) { position,
&self.rotate_texture,
) {
self.rotate_panel_visible = !self.rotate_panel_visible; self.rotate_panel_visible = !self.rotate_panel_visible;
} }
if !self.rotate_panel_visible { if !self.rotate_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(device, debug_ui_presenter.ui_presenter.draw_tooltip(
allocator, device,
"Rotate", allocator,
RectI::new(position, button_size)); "Rotate",
RectI::new(position, button_size),
);
} }
self.draw_rotate_panel(device, allocator, debug_ui_presenter, position.x(), action, model); self.draw_rotate_panel(
device,
allocator,
debug_ui_presenter,
position.x(),
action,
model,
);
position += vec2i(BUTTON_WIDTH + PADDING, 0); position += vec2i(BUTTON_WIDTH + PADDING, 0);
// Draw zoom control. // Draw zoom control.
self.draw_zoom_control(device, allocator, debug_ui_presenter, position, action); self.draw_zoom_control(device, allocator, debug_ui_presenter, position, action);
} }
fn draw_zoom_control(&mut self, fn draw_zoom_control(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
position: Vector2I, debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction) { position: Vector2I,
action: &mut UIAction,
) {
let zoom_segmented_control_width = let zoom_segmented_control_width =
debug_ui_presenter.ui_presenter.measure_segmented_control(3); debug_ui_presenter.ui_presenter.measure_segmented_control(3);
let zoom_segmented_control_rect = let zoom_segmented_control_rect =
RectI::new(position, vec2i(zoom_segmented_control_width, BUTTON_HEIGHT)); RectI::new(position, vec2i(zoom_segmented_control_width, BUTTON_HEIGHT));
debug_ui_presenter.ui_presenter debug_ui_presenter.ui_presenter.draw_tooltip(
.draw_tooltip(device, allocator, "Zoom", zoom_segmented_control_rect); device,
allocator,
"Zoom",
zoom_segmented_control_rect,
);
let zoom_textures = &[ let zoom_textures = &[
&self.zoom_in_texture, &self.zoom_in_texture,
&self.zoom_actual_size_texture, &self.zoom_actual_size_texture,
&self.zoom_out_texture &self.zoom_out_texture,
]; ];
match debug_ui_presenter.ui_presenter.draw_image_segmented_control(device, match debug_ui_presenter
allocator, .ui_presenter
position, .draw_image_segmented_control(device, allocator, position, zoom_textures, None)
zoom_textures, {
None) {
Some(0) => *action = UIAction::ZoomIn, Some(0) => *action = UIAction::ZoomIn,
Some(1) => *action = UIAction::ZoomActualSize, Some(1) => *action = UIAction::ZoomActualSize,
Some(2) => *action = UIAction::ZoomOut, Some(2) => *action = UIAction::ZoomOut,
@ -338,11 +394,13 @@ impl<D> DemoUIPresenter<D> where D: Device {
} }
} }
fn draw_message_text(&mut self, fn draw_message_text(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
model: &mut DemoUIModel) { debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel,
) {
if model.message.is_empty() { if model.message.is_empty() {
return; return;
} }
@ -365,12 +423,14 @@ impl<D> DemoUIPresenter<D> where D: Device {
); );
} }
fn draw_effects_panel(&mut self, fn draw_effects_panel(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
model: &mut DemoUIModel, debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction) { model: &mut DemoUIModel,
action: &mut UIAction,
) {
if !self.effects_panel_visible { if !self.effects_panel_visible {
return; return;
} }
@ -380,44 +440,56 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect( debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device, device,
allocator, allocator,
RectI::new(vec2i(PADDING, effects_panel_y), RectI::new(
vec2i(EFFECTS_PANEL_WIDTH, EFFECTS_PANEL_HEIGHT)), vec2i(PADDING, effects_panel_y),
WINDOW_COLOR); vec2i(EFFECTS_PANEL_WIDTH, EFFECTS_PANEL_HEIGHT),
),
WINDOW_COLOR,
);
self.draw_effects_switch(device, self.draw_effects_switch(
allocator, device,
action, allocator,
debug_ui_presenter, action,
"Gamma Correction", debug_ui_presenter,
0, "Gamma Correction",
effects_panel_y, 0,
&mut model.gamma_correction_effect_enabled); effects_panel_y,
self.draw_effects_switch(device, &mut model.gamma_correction_effect_enabled,
allocator, );
action, self.draw_effects_switch(
debug_ui_presenter, device,
"Stem Darkening", allocator,
1, action,
effects_panel_y, debug_ui_presenter,
&mut model.stem_darkening_effect_enabled); "Stem Darkening",
self.draw_effects_switch(device, 1,
allocator, effects_panel_y,
action, &mut model.stem_darkening_effect_enabled,
debug_ui_presenter, );
"Subpixel AA", self.draw_effects_switch(
2, device,
effects_panel_y, allocator,
&mut model.subpixel_aa_effect_enabled); action,
debug_ui_presenter,
"Subpixel AA",
2,
effects_panel_y,
&mut model.subpixel_aa_effect_enabled,
);
} }
fn draw_screenshot_panel<W>(&mut self, fn draw_screenshot_panel<W>(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
window: &mut W, allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>, window: &mut W,
panel_x: i32, debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction) panel_x: i32,
where W: Window { action: &mut UIAction,
) where
W: Window,
{
if !self.screenshot_panel_visible { if !self.screenshot_panel_visible {
return; return;
} }
@ -428,33 +500,42 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect( debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device, device,
allocator, allocator,
RectI::new(panel_position, vec2i(SCREENSHOT_PANEL_WIDTH, SCREENSHOT_PANEL_HEIGHT)), RectI::new(
panel_position,
vec2i(SCREENSHOT_PANEL_WIDTH, SCREENSHOT_PANEL_HEIGHT),
),
WINDOW_COLOR, WINDOW_COLOR,
); );
self.draw_screenshot_menu_item(device, self.draw_screenshot_menu_item(
allocator, device,
window, allocator,
debug_ui_presenter, window,
ScreenshotType::PNG, debug_ui_presenter,
panel_position, ScreenshotType::PNG,
action); panel_position,
self.draw_screenshot_menu_item(device, action,
allocator, );
window, self.draw_screenshot_menu_item(
debug_ui_presenter, device,
ScreenshotType::SVG, allocator,
panel_position, window,
action); debug_ui_presenter,
ScreenshotType::SVG,
panel_position,
action,
);
} }
fn draw_background_panel(&mut self, fn draw_background_panel(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
panel_x: i32, debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction, panel_x: i32,
model: &mut DemoUIModel) { action: &mut UIAction,
model: &mut DemoUIModel,
) {
if !self.background_panel_visible { if !self.background_panel_visible {
return; return;
} }
@ -465,40 +546,51 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect( debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device, device,
allocator, allocator,
RectI::new(panel_position, vec2i(BACKGROUND_PANEL_WIDTH, BACKGROUND_PANEL_HEIGHT)), RectI::new(
panel_position,
vec2i(BACKGROUND_PANEL_WIDTH, BACKGROUND_PANEL_HEIGHT),
),
WINDOW_COLOR, WINDOW_COLOR,
); );
self.draw_background_menu_item(device, self.draw_background_menu_item(
allocator, device,
debug_ui_presenter, allocator,
BackgroundColor::Light, debug_ui_presenter,
panel_position, BackgroundColor::Light,
action, panel_position,
model); action,
self.draw_background_menu_item(device, model,
allocator, );
debug_ui_presenter, self.draw_background_menu_item(
BackgroundColor::Dark, device,
panel_position, allocator,
action, debug_ui_presenter,
model); BackgroundColor::Dark,
self.draw_background_menu_item(device, panel_position,
allocator, action,
debug_ui_presenter, model,
BackgroundColor::Transparent, );
panel_position, self.draw_background_menu_item(
action, device,
model); allocator,
debug_ui_presenter,
BackgroundColor::Transparent,
panel_position,
action,
model,
);
} }
fn draw_rotate_panel(&mut self, fn draw_rotate_panel(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
rotate_panel_x: i32, debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction, rotate_panel_x: i32,
model: &mut DemoUIModel) { action: &mut UIAction,
model: &mut DemoUIModel,
) {
if !self.rotate_panel_visible { if !self.rotate_panel_visible {
return; return;
} }
@ -511,42 +603,61 @@ impl<D> DemoUIPresenter<D> where D: Device {
device, device,
allocator, allocator,
RectI::new(rotate_panel_origin, rotate_panel_size), RectI::new(rotate_panel_origin, rotate_panel_size),
WINDOW_COLOR); WINDOW_COLOR,
);
let (widget_x, widget_y) = (rotate_panel_x + PADDING, rotate_panel_y + PADDING); let (widget_x, widget_y) = (rotate_panel_x + PADDING, rotate_panel_y + PADDING);
let widget_rect = RectI::new(vec2i(widget_x, widget_y), let widget_rect = RectI::new(
vec2i(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT)); vec2i(widget_x, widget_y),
vec2i(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT),
);
if let Some(position) = debug_ui_presenter if let Some(position) = debug_ui_presenter
.ui_presenter .ui_presenter
.event_queue .event_queue
.handle_mouse_down_or_dragged_in_rect(widget_rect) { .handle_mouse_down_or_dragged_in_rect(widget_rect)
{
model.rotation = position.x(); model.rotation = position.x();
*action = UIAction::Rotate(model.rotation()); *action = UIAction::Rotate(model.rotation());
} }
let slider_track_y = let slider_track_y =
rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 - SLIDER_TRACK_HEIGHT / 2; rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 - SLIDER_TRACK_HEIGHT / 2;
let slider_track_rect = RectI::new(vec2i(widget_x, slider_track_y), let slider_track_rect = RectI::new(
vec2i(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT)); vec2i(widget_x, slider_track_y),
debug_ui_presenter.ui_presenter vec2i(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT),
.draw_rect_outline(device, allocator, slider_track_rect, TEXT_COLOR); );
debug_ui_presenter.ui_presenter.draw_rect_outline(
device,
allocator,
slider_track_rect,
TEXT_COLOR,
);
let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2; let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2;
let slider_knob_rect = RectI::new(vec2i(slider_knob_x, widget_y), let slider_knob_rect = RectI::new(
vec2i(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT)); vec2i(slider_knob_x, widget_y),
debug_ui_presenter.ui_presenter vec2i(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT),
.draw_solid_rect(device, allocator, slider_knob_rect, TEXT_COLOR); );
debug_ui_presenter.ui_presenter.draw_solid_rect(
device,
allocator,
slider_knob_rect,
TEXT_COLOR,
);
} }
fn draw_screenshot_menu_item<W>(&mut self, fn draw_screenshot_menu_item<W>(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
window: &mut W, allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>, window: &mut W,
screenshot_type: ScreenshotType, debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_position: Vector2I, screenshot_type: ScreenshotType,
action: &mut UIAction) panel_position: Vector2I,
where W: Window { action: &mut UIAction,
) where
W: Window,
{
let index = screenshot_type as i32; let index = screenshot_type as i32;
let text = format!("Save as {}...", screenshot_type.as_str()); let text = format!("Save as {}...", screenshot_type.as_str());
@ -554,29 +665,36 @@ impl<D> DemoUIPresenter<D> where D: Device {
let widget_origin = panel_position + vec2i(0, widget_size.y() * index); let widget_origin = panel_position + vec2i(0, widget_size.y() * index);
let widget_rect = RectI::new(widget_origin, widget_size); let widget_rect = RectI::new(widget_origin, widget_size);
if self.draw_menu_item(device, if self.draw_menu_item(
allocator, device,
debug_ui_presenter, allocator,
&text, debug_ui_presenter,
widget_rect, &text,
false) { widget_rect,
false,
) {
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
// the contents of the file. // the contents of the file.
if let Ok(path) = window.run_save_dialog(screenshot_type.extension()) { if let Ok(path) = window.run_save_dialog(screenshot_type.extension()) {
self.screenshot_panel_visible = false; self.screenshot_panel_visible = false;
*action = UIAction::TakeScreenshot(ScreenshotInfo { kind: screenshot_type, path }); *action = UIAction::TakeScreenshot(ScreenshotInfo {
kind: screenshot_type,
path,
});
} }
} }
} }
fn draw_background_menu_item(&mut self, fn draw_background_menu_item(
device: &D, &mut self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
color: BackgroundColor, debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_position: Vector2I, color: BackgroundColor,
action: &mut UIAction, panel_position: Vector2I,
model: &mut DemoUIModel) { action: &mut UIAction,
model: &mut DemoUIModel,
) {
let (text, index) = (color.as_str(), color as i32); let (text, index) = (color.as_str(), color as i32);
let widget_size = vec2i(BACKGROUND_PANEL_WIDTH, BUTTON_HEIGHT); let widget_size = vec2i(BACKGROUND_PANEL_WIDTH, BUTTON_HEIGHT);
@ -584,66 +702,83 @@ impl<D> DemoUIPresenter<D> where D: Device {
let widget_rect = RectI::new(widget_origin, widget_size); let widget_rect = RectI::new(widget_origin, widget_size);
let selected = color == model.background_color; let selected = color == model.background_color;
if self.draw_menu_item(device, if self.draw_menu_item(
allocator, device,
debug_ui_presenter, allocator,
text, debug_ui_presenter,
widget_rect, text,
selected) { widget_rect,
selected,
) {
model.background_color = color; model.background_color = color;
*action = UIAction::ModelChanged; *action = UIAction::ModelChanged;
} }
} }
fn draw_menu_item(&self, fn draw_menu_item(
device: &D, &self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>, allocator: &mut GPUMemoryAllocator<D>,
text: &str, debug_ui_presenter: &mut DebugUIPresenter<D>,
widget_rect: RectI, text: &str,
selected: bool) widget_rect: RectI,
-> bool { selected: bool,
) -> bool {
if selected { if selected {
debug_ui_presenter.ui_presenter debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
.draw_solid_rounded_rect(device, allocator, widget_rect, TEXT_COLOR); device,
allocator,
widget_rect,
TEXT_COLOR,
);
} }
let (text_x, text_y) = (PADDING * 2, BUTTON_TEXT_OFFSET); let (text_x, text_y) = (PADDING * 2, BUTTON_TEXT_OFFSET);
let text_position = widget_rect.origin() + vec2i(text_x, text_y); let text_position = widget_rect.origin() + vec2i(text_x, text_y);
debug_ui_presenter.ui_presenter debug_ui_presenter
.draw_text(device, allocator, text, text_position, selected); .ui_presenter
.draw_text(device, allocator, text, text_position, selected);
debug_ui_presenter.ui_presenter debug_ui_presenter
.event_queue .ui_presenter
.handle_mouse_down_in_rect(widget_rect) .event_queue
.is_some() .handle_mouse_down_in_rect(widget_rect)
.is_some()
} }
fn draw_effects_switch(&self, fn draw_effects_switch(
device: &D, &self,
allocator: &mut GPUMemoryAllocator<D>, device: &D,
action: &mut UIAction, allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>, action: &mut UIAction,
text: &str, debug_ui_presenter: &mut DebugUIPresenter<D>,
index: i32, text: &str,
window_y: i32, index: i32,
value: &mut bool) { window_y: i32,
value: &mut bool,
) {
let text_x = PADDING * 2; let text_x = PADDING * 2;
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index; let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
debug_ui_presenter.ui_presenter debug_ui_presenter.ui_presenter.draw_text(
.draw_text(device, allocator, text, vec2i(text_x, text_y), false); device,
allocator,
text,
vec2i(text_x, text_y),
false,
);
let switch_width = debug_ui_presenter.ui_presenter.measure_segmented_control(2); let switch_width = debug_ui_presenter.ui_presenter.measure_segmented_control(2);
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING); let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index; let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
let switch_position = vec2i(switch_x, switch_y); let switch_position = vec2i(switch_x, switch_y);
let new_value = debug_ui_presenter.ui_presenter let new_value = debug_ui_presenter.ui_presenter.draw_text_switch(
.draw_text_switch(device, device,
allocator, allocator,
switch_position, switch_position,
&["Off", "On"], &["Off", "On"],
*value as u8) != 0; *value as u8,
) != 0;
if new_value != *value { if new_value != *value {
*action = UIAction::EffectsChanged; *action = UIAction::EffectsChanged;

View File

@ -33,7 +33,9 @@ pub trait Window {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_version(&self) -> GLVersion; fn gl_version(&self) -> GLVersion;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_default_framebuffer(&self) -> GLuint { 0 } fn gl_default_framebuffer(&self) -> GLuint {
0
}
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn present(&mut self, device: &mut GLDevice); fn present(&mut self, device: &mut GLDevice);

View File

@ -7,7 +7,7 @@ authors = ["Alan Jeffrey <ajeffrey@mozilla.com>"]
[dependencies] [dependencies]
gl = "0.14" gl = "0.14"
rayon = "1.0" rayon = "1.0"
usvg = "0.9" usvg = "0.10"
egl = "0.2" egl = "0.2"
log = "0.4" log = "0.4"
smallvec = "1.2" smallvec = "1.2"

View File

@ -263,27 +263,51 @@ impl MLResult {
} }
} }
impl Error for MLResult { impl Error for MLResult {}
}
// Functions from the MagicLeap C API // Functions from the MagicLeap C API
#[cfg(not(feature = "mocked"))] #[cfg(not(feature = "mocked"))]
extern "C" { extern "C" {
pub fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult; pub fn MLGraphicsCreateClientGL(
options: *const MLGraphicsOptions,
gl_context: MLHandle,
graphics_client: &mut MLHandle,
) -> MLResult;
pub fn MLGraphicsDestroyClient(graphics_client: *mut MLHandle) -> MLResult; pub fn MLGraphicsDestroyClient(graphics_client: *mut MLHandle) -> MLResult;
pub fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult; pub fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult;
pub fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult; pub fn MLHeadTrackingGetStaticData(
head_tracker: MLHandle,
data: *mut MLHeadTrackingStaticData,
) -> MLResult;
pub fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult; pub fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult;
pub fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult; pub fn MLSnapshotGetTransform(
snapshot: MLSnapshotPtr,
id: *const MLCoordinateFrameUID,
transform: *mut MLTransform,
) -> MLResult;
pub fn MLPerceptionReleaseSnapshot(snapshot: MLSnapshotPtr) -> MLResult; pub fn MLPerceptionReleaseSnapshot(snapshot: MLSnapshotPtr) -> MLResult;
pub fn MLLifecycleSetReadyIndication() -> MLResult; pub fn MLLifecycleSetReadyIndication() -> MLResult;
pub fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult; pub fn MLGraphicsGetClipExtents(
pub fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult; graphics_client: MLHandle,
array: *mut MLGraphicsClipExtentsInfoArray,
) -> MLResult;
pub fn MLGraphicsGetRenderTargets(
graphics_client: MLHandle,
targets: *mut MLGraphicsRenderTargetsInfo,
) -> MLResult;
pub fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> MLResult; pub fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> MLResult;
pub fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult; pub fn MLGraphicsBeginFrame(
graphics_client: MLHandle,
params: *const MLGraphicsFrameParams,
frame_handle: *mut MLHandle,
virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray,
) -> MLResult;
pub fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHandle) -> MLResult; pub fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHandle) -> MLResult;
pub fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult; pub fn MLGraphicsSignalSyncObjectGL(
graphics_client: MLHandle,
sync_object: MLHandle,
) -> MLResult;
pub fn MLGetResultString(result_code: MLResult) -> *const c_char; pub fn MLGetResultString(result_code: MLResult) -> *const c_char;
pub fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool; pub fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool;
pub fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char); pub fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char);

View File

@ -22,31 +22,31 @@ use gl::types::GLuint;
use log::info; use log::info;
use pathfinder_demo::DemoApp; use pathfinder_color::ColorF;
use pathfinder_demo::Options;
use pathfinder_demo::UIVisibility;
use pathfinder_demo::BackgroundColor;
use pathfinder_demo::Mode;
use pathfinder_demo::window::Event; use pathfinder_demo::window::Event;
use pathfinder_demo::window::SVGPath; use pathfinder_demo::window::SVGPath;
use pathfinder_demo::BackgroundColor;
use pathfinder_demo::DemoApp;
use pathfinder_demo::Mode;
use pathfinder_demo::Options;
use pathfinder_demo::UIVisibility;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::vec2i;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::vector::vec2i;
use pathfinder_color::ColorF;
use pathfinder_gl::GLDevice; use pathfinder_gl::GLDevice;
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_gpu::ClearParams; use pathfinder_gpu::ClearParams;
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::executor::SequentialExecutor; use pathfinder_renderer::concurrent::executor::SequentialExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::renderer::DestFramebuffer; use pathfinder_renderer::gpu::renderer::DestFramebuffer;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::RenderOptions; use pathfinder_renderer::options::RenderOptions;
use pathfinder_renderer::options::RenderTransform; use pathfinder_renderer::options::RenderTransform;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader; use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use pathfinder_svg::SVGScene; use pathfinder_svg::SVGScene;
@ -72,10 +72,17 @@ struct ImmersiveApp {
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn magicleap_pathfinder_demo_init(egl_display: EGLDisplay, egl_context: EGLContext) -> *mut c_void { pub extern "C" fn magicleap_pathfinder_demo_init(
unsafe { c_api::MLLoggingLog(c_api::MLLogLevel::Info, egl_display: EGLDisplay,
b"Pathfinder Demo\0".as_ptr() as *const _, egl_context: EGLContext,
b"Initializing\0".as_ptr() as *const _) }; ) -> *mut c_void {
unsafe {
c_api::MLLoggingLog(
c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _,
)
};
let tag = CString::new("Pathfinder Demo").unwrap(); let tag = CString::new("Pathfinder Demo").unwrap();
let level = log::LevelFilter::Warn; let level = log::LevelFilter::Warn;
@ -97,7 +104,11 @@ pub extern "C" fn magicleap_pathfinder_demo_init(egl_display: EGLDisplay, egl_co
info!("Initialized app"); info!("Initialized app");
let (sender, receiver) = flume::unbounded(); let (sender, receiver) = flume::unbounded();
Box::into_raw(Box::new(ImmersiveApp { sender, receiver, demo })) as *mut c_void Box::into_raw(Box::new(ImmersiveApp {
sender,
receiver,
demo,
})) as *mut c_void
} }
#[no_mangle] #[no_mangle]
@ -124,12 +135,17 @@ pub unsafe extern "C" fn magicleap_pathfinder_demo_run(app: *mut c_void) {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn magicleap_pathfinder_demo_load(app: *mut c_void, svg_filename: *const c_char) { pub unsafe extern "C" fn magicleap_pathfinder_demo_load(
app: *mut c_void,
svg_filename: *const c_char,
) {
let app = app as *mut ImmersiveApp; let app = app as *mut ImmersiveApp;
if let Some(app) = app.as_mut() { if let Some(app) = app.as_mut() {
let svg_filename = CStr::from_ptr(svg_filename).to_string_lossy().into_owned(); let svg_filename = CStr::from_ptr(svg_filename).to_string_lossy().into_owned();
info!("Loading {}.", svg_filename); info!("Loading {}.", svg_filename);
let _ = app.sender.send(Event::OpenSVG(SVGPath::Resource(svg_filename))); let _ = app
.sender
.send(Event::OpenSVG(SVGPath::Resource(svg_filename)));
} }
} }
@ -150,9 +166,13 @@ pub struct MagicLeapPathfinderRenderOptions {
#[no_mangle] #[no_mangle]
pub extern "C" fn magicleap_pathfinder_init() -> *mut c_void { pub extern "C" fn magicleap_pathfinder_init() -> *mut c_void {
unsafe { c_api::MLLoggingLog(c_api::MLLogLevel::Info, unsafe {
b"Pathfinder Demo\0".as_ptr() as *const _, c_api::MLLoggingLog(
b"Initializing\0".as_ptr() as *const _) }; c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _,
)
};
let tag = CString::new("Pathfinder Demo").unwrap(); let tag = CString::new("Pathfinder Demo").unwrap();
let level = log::LevelFilter::Info; let level = log::LevelFilter::Info;
@ -175,12 +195,17 @@ pub extern "C" fn magicleap_pathfinder_init() -> *mut c_void {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *const MagicLeapPathfinderRenderOptions) { pub unsafe extern "C" fn magicleap_pathfinder_render(
pf: *mut c_void,
options: *const MagicLeapPathfinderRenderOptions,
) {
let pf = pf as *mut MagicLeapPathfinder; let pf = pf as *mut MagicLeapPathfinder;
if let (Some(pf), Some(options)) = (pf.as_mut(), options.as_ref()) { if let (Some(pf), Some(options)) = (pf.as_mut(), options.as_ref()) {
let resources = &pf.resources; let resources = &pf.resources;
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy().into_owned(); let svg_filename = CStr::from_ptr(options.svg_filename)
.to_string_lossy()
.into_owned();
let svg = pf.svgs.entry(svg_filename).or_insert_with(|| { let svg = pf.svgs.entry(svg_filename).or_insert_with(|| {
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy(); let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy();
let data = resources.slurp(&*svg_filename).unwrap(); let data = resources.slurp(&*svg_filename).unwrap();
@ -191,35 +216,56 @@ pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *
let mut width = 0; let mut width = 0;
let mut height = 0; let mut height = 0;
egl::query_surface(options.display, options.surface, egl::EGL_WIDTH, &mut width); egl::query_surface(options.display, options.surface, egl::EGL_WIDTH, &mut width);
egl::query_surface(options.display, options.surface, egl::EGL_HEIGHT, &mut height); egl::query_surface(
options.display,
options.surface,
egl::EGL_HEIGHT,
&mut height,
);
let size = vec2i(width, height); let size = vec2i(width, height);
let viewport_origin = vec2i(options.viewport[0] as i32, options.viewport[1] as i32); let viewport_origin = vec2i(options.viewport[0] as i32, options.viewport[1] as i32);
let viewport_size = vec2i(options.viewport[2] as i32, options.viewport[3] as i32); let viewport_size = vec2i(options.viewport[2] as i32, options.viewport[3] as i32);
let viewport = RectI::new(viewport_origin, viewport_size); let viewport = RectI::new(viewport_origin, viewport_size);
let bg_color = ColorF(F32x4::new(options.bg_color[0], options.bg_color[1], options.bg_color[2], options.bg_color[3])); let bg_color = ColorF(F32x4::new(
options.bg_color[0],
options.bg_color[1],
options.bg_color[2],
options.bg_color[3],
));
let renderer = pf.renderers.entry((options.display, options.surface)).or_insert_with(|| { let renderer = pf
let mut fbo = 0; .renderers
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fbo); .entry((options.display, options.surface))
let device = GLDevice::new(GLVersion::GLES3, fbo as GLuint); .or_insert_with(|| {
let dest_framebuffer = DestFramebuffer::Default { viewport, window_size: size }; let mut fbo = 0;
Renderer::new(device, resources, dest_framebuffer) gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fbo);
}); let device = GLDevice::new(GLVersion::GLES3, fbo as GLuint);
let dest_framebuffer = DestFramebuffer::Default {
viewport,
window_size: size,
};
Renderer::new(device, resources, dest_framebuffer)
});
renderer.set_main_framebuffer_size(size); renderer.set_main_framebuffer_size(size);
renderer.device.bind_default_framebuffer(viewport); renderer.device.bind_default_framebuffer(viewport);
renderer.device.clear(&ClearParams { color: Some(bg_color), ..ClearParams::default() }); renderer.device.clear(&ClearParams {
color: Some(bg_color),
..ClearParams::default()
});
renderer.disable_depth(); renderer.disable_depth();
svg.scene.set_view_box(viewport.to_f32()); svg.scene.set_view_box(viewport.to_f32());
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 / let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y()); / f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y());
let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5)) let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5))
.post_mul(&Transform2F::from_scale(scale)) .post_mul(&Transform2F::from_scale(scale))
.post_mul(&Transform2F::from_translation(viewport_size.to_f32().scale(0.5))); .post_mul(&Transform2F::from_translation(
viewport_size.to_f32().scale(0.5),
));
let render_options = RenderOptions { let render_options = RenderOptions {
transform: RenderTransform::Transform2D(transform), transform: RenderTransform::Transform2D(transform),

View File

@ -32,9 +32,9 @@ use crate::c_api::ML_RESULT_TIMEOUT;
use crate::c_api::ML_VIRTUAL_CAMERA_COUNT; use crate::c_api::ML_VIRTUAL_CAMERA_COUNT;
use egl; use egl;
use egl::EGL_NO_SURFACE;
use egl::EGLContext; use egl::EGLContext;
use egl::EGLDisplay; use egl::EGLDisplay;
use egl::EGL_NO_SURFACE;
use gl; use gl;
use gl::types::GLuint; use gl::types::GLuint;
@ -53,12 +53,12 @@ use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::util; use pathfinder_geometry::util;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::vec2i; use pathfinder_geometry::vector::vec2i;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader; use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
@ -99,19 +99,20 @@ impl Window for MagicLeapWindow {
self.framebuffer_id self.framebuffer_id
} }
fn adjust_thread_pool_settings(&self, thread_pool_builder: ThreadPoolBuilder) -> ThreadPoolBuilder { fn adjust_thread_pool_settings(
&self,
thread_pool_builder: ThreadPoolBuilder,
) -> ThreadPoolBuilder {
thread_pool_builder.start_handler(|id| unsafe { init_scene_thread(id) }) thread_pool_builder.start_handler(|id| unsafe { init_scene_thread(id) })
} }
fn create_user_event_id (&self) -> u32 { fn create_user_event_id(&self) -> u32 {
0 0
} }
fn push_user_event(_: u32, _: u32) { fn push_user_event(_: u32, _: u32) {}
}
fn present_open_svg_dialog(&mut self) { fn present_open_svg_dialog(&mut self) {}
}
fn run_save_dialog(&self, _: &str) -> Result<PathBuf, ()> { fn run_save_dialog(&self, _: &str) -> Result<PathBuf, ()> {
Err(()) Err(())
@ -125,7 +126,10 @@ impl Window for MagicLeapWindow {
self.begin_frame(); self.begin_frame();
let eye = match view { let eye = match view {
View::Stereo(eye) if (eye as usize) < ML_VIRTUAL_CAMERA_COUNT => eye as usize, View::Stereo(eye) if (eye as usize) < ML_VIRTUAL_CAMERA_COUNT => eye as usize,
_ => { debug!("Asked for unexpected view: {:?}", view); 0 } _ => {
debug!("Asked for unexpected view: {:?}", view);
0
}
}; };
debug!("Making {} current.", eye); debug!("Making {} current.", eye);
let viewport = self.virtual_camera_array.viewport; let viewport = self.virtual_camera_array.viewport;
@ -135,9 +139,26 @@ impl Window for MagicLeapWindow {
let layer_id = virtual_camera.virtual_camera_name as i32; let layer_id = virtual_camera.virtual_camera_name as i32;
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id); gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id);
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, color_id, 0, layer_id); gl::FramebufferTextureLayer(
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, depth_id, 0, layer_id); gl::FRAMEBUFFER,
gl::Viewport(viewport.x as i32, viewport.y as i32, viewport.w as i32, viewport.h as i32); gl::COLOR_ATTACHMENT0,
color_id,
0,
layer_id,
);
gl::FramebufferTextureLayer(
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
depth_id,
0,
layer_id,
);
gl::Viewport(
viewport.x as i32,
viewport.y as i32,
viewport.w as i32,
viewport.h as i32,
);
} }
debug!("Made {} current.", eye); debug!("Made {} current.", eye);
} }
@ -161,7 +182,7 @@ impl MagicLeapWindow {
debug!("Creating MagicLeapWindow"); debug!("Creating MagicLeapWindow");
let mut framebuffer_id = 0; let mut framebuffer_id = 0;
let graphics_options = MLGraphicsOptions::default(); let graphics_options = MLGraphicsOptions::default();
let mut graphics_client = unsafe { mem::zeroed() }; let mut graphics_client = unsafe { mem::zeroed() };
let mut head_tracker = unsafe { mem::zeroed() }; let mut head_tracker = unsafe { mem::zeroed() };
let mut targets = unsafe { mem::zeroed() }; let mut targets = unsafe { mem::zeroed() };
let virtual_camera_array = unsafe { mem::zeroed() }; let virtual_camera_array = unsafe { mem::zeroed() };
@ -175,7 +196,10 @@ impl MagicLeapWindow {
MLHeadTrackingCreate(&mut head_tracker).unwrap(); MLHeadTrackingCreate(&mut head_tracker).unwrap();
MLGraphicsGetRenderTargets(graphics_client, &mut targets).unwrap(); MLGraphicsGetRenderTargets(graphics_client, &mut targets).unwrap();
} }
let (max_width, max_height) = targets.buffers.iter().map(|buffer| buffer.color) let (max_width, max_height) = targets
.buffers
.iter()
.map(|buffer| buffer.color)
.chain(targets.buffers.iter().map(|buffer| buffer.depth)) .chain(targets.buffers.iter().map(|buffer| buffer.depth))
.map(|target| (target.width as i32, target.height as i32)) .map(|target| (target.width as i32, target.height as i32))
.max() .max()
@ -218,7 +242,12 @@ impl MagicLeapWindow {
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id); gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id);
MLGraphicsInitFrameParams(&mut params).unwrap(); MLGraphicsInitFrameParams(&mut params).unwrap();
let mut result = MLGraphicsBeginFrame(self.graphics_client, &params, &mut self.frame_handle, &mut self.virtual_camera_array); let mut result = MLGraphicsBeginFrame(
self.graphics_client,
&params,
&mut self.frame_handle,
&mut self.virtual_camera_array,
);
if result == ML_RESULT_TIMEOUT { if result == ML_RESULT_TIMEOUT {
info!("PF frame timeout"); info!("PF frame timeout");
let mut sleep = Duration::from_millis(1); let mut sleep = Duration::from_millis(1);
@ -227,7 +256,12 @@ impl MagicLeapWindow {
sleep = (sleep * 2).min(max_sleep); sleep = (sleep * 2).min(max_sleep);
info!("PF exponential backoff {}ms", sleep.as_millis()); info!("PF exponential backoff {}ms", sleep.as_millis());
thread::sleep(sleep); thread::sleep(sleep);
result = MLGraphicsBeginFrame(self.graphics_client, &params, &mut self.frame_handle, &mut self.virtual_camera_array); result = MLGraphicsBeginFrame(
self.graphics_client,
&params,
&mut self.frame_handle,
&mut self.virtual_camera_array,
);
} }
info!("PF frame finished timeout"); info!("PF frame finished timeout");
} }
@ -236,21 +270,26 @@ impl MagicLeapWindow {
let virtual_camera_array = &self.virtual_camera_array; let virtual_camera_array = &self.virtual_camera_array;
let initial_camera = self.initial_camera_transform.get_or_insert_with(|| { let initial_camera = self.initial_camera_transform.get_or_insert_with(|| {
let initial_offset = Transform4F::from_translation(0.0, 0.0, 1.0); let initial_offset = Transform4F::from_translation(0.0, 0.0, 1.0);
let mut camera = virtual_camera_array.virtual_cameras[0].transform; let mut camera = virtual_camera_array.virtual_cameras[0].transform;
for i in 1..virtual_camera_array.num_virtual_cameras { for i in 1..virtual_camera_array.num_virtual_cameras {
let next = virtual_camera_array.virtual_cameras[i as usize].transform; let next = virtual_camera_array.virtual_cameras[i as usize].transform;
camera = camera.lerp(next, 1.0 / (i as f32 + 1.0)); camera = camera.lerp(next, 1.0 / (i as f32 + 1.0));
} }
Transform4F::from(camera).post_mul(&initial_offset) Transform4F::from(camera).post_mul(&initial_offset)
}); });
let camera_transforms = (0..virtual_camera_array.num_virtual_cameras) let camera_transforms = (0..virtual_camera_array.num_virtual_cameras)
.map(|i| { .map(|i| {
let camera = &virtual_camera_array.virtual_cameras[i as usize]; let camera = &virtual_camera_array.virtual_cameras[i as usize];
let projection = Transform4F::from(camera.projection); let projection = Transform4F::from(camera.projection);
let size = RectI::from(virtual_camera_array.viewport).size(); let size = RectI::from(virtual_camera_array.viewport).size();
let perspective = Perspective::new(&projection, size); let perspective = Perspective::new(&projection, size);
let modelview_to_eye = Transform4F::from(camera.transform).inverse().post_mul(initial_camera); let modelview_to_eye = Transform4F::from(camera.transform)
OcularTransform { perspective, modelview_to_eye } .inverse()
.post_mul(initial_camera);
OcularTransform {
perspective,
modelview_to_eye,
}
}) })
.collect(); .collect();
self.in_frame = true; self.in_frame = true;
@ -266,7 +305,8 @@ impl MagicLeapWindow {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
for i in 0..self.virtual_camera_array.num_virtual_cameras { for i in 0..self.virtual_camera_array.num_virtual_cameras {
let virtual_camera = &self.virtual_camera_array.virtual_cameras[i as usize]; let virtual_camera = &self.virtual_camera_array.virtual_cameras[i as usize];
MLGraphicsSignalSyncObjectGL(self.graphics_client, virtual_camera.sync_object).unwrap(); MLGraphicsSignalSyncObjectGL(self.graphics_client, virtual_camera.sync_object)
.unwrap();
} }
MLGraphicsEndFrame(self.graphics_client, self.frame_handle).unwrap(); MLGraphicsEndFrame(self.graphics_client, self.frame_handle).unwrap();
} }
@ -358,8 +398,7 @@ impl MLTransform {
impl From<MLTransform> for Transform4F { impl From<MLTransform> for Transform4F {
fn from(mat: MLTransform) -> Self { fn from(mat: MLTransform) -> Self {
Transform4F::from(mat.rotation) Transform4F::from(mat.rotation).pre_mul(&Transform4F::from(mat.position))
.pre_mul(&Transform4F::from(mat.position))
} }
} }
@ -390,10 +429,9 @@ impl From<MLQuaternionf> for Transform4F {
impl From<MLMat4f> for Transform4F { impl From<MLMat4f> for Transform4F {
fn from(mat: MLMat4f) -> Self { fn from(mat: MLMat4f) -> Self {
let a = mat.matrix_colmajor; let a = mat.matrix_colmajor;
Transform4F::row_major(a[0], a[4], a[8], a[12], Transform4F::row_major(
a[1], a[5], a[9], a[13], a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7],
a[2], a[6], a[10], a[14], a[11], a[15],
a[3], a[7], a[11], a[15]) )
} }
} }

View File

@ -28,7 +28,11 @@ use crate::c_api::MLSnapshotPtr;
use crate::c_api::MLTransform; use crate::c_api::MLTransform;
use std::os::raw::c_char; use std::os::raw::c_char;
pub unsafe fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult { pub unsafe fn MLGraphicsCreateClientGL(
options: *const MLGraphicsOptions,
gl_context: MLHandle,
graphics_client: &mut MLHandle,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -40,7 +44,10 @@ pub unsafe fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult {
unimplemented!() unimplemented!()
} }
pub unsafe fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult { pub unsafe fn MLHeadTrackingGetStaticData(
head_tracker: MLHandle,
data: *mut MLHeadTrackingStaticData,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -48,7 +55,11 @@ pub unsafe fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult
unimplemented!() unimplemented!()
} }
pub unsafe fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult { pub unsafe fn MLSnapshotGetTransform(
snapshot: MLSnapshotPtr,
id: *const MLCoordinateFrameUID,
transform: *mut MLTransform,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -60,11 +71,17 @@ pub unsafe fn MLLifecycleSetReadyIndication() -> MLResult {
unimplemented!() unimplemented!()
} }
pub unsafe fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult { pub unsafe fn MLGraphicsGetClipExtents(
graphics_client: MLHandle,
array: *mut MLGraphicsClipExtentsInfoArray,
) -> MLResult {
unimplemented!() unimplemented!()
} }
pub unsafe fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult { pub unsafe fn MLGraphicsGetRenderTargets(
graphics_client: MLHandle,
targets: *mut MLGraphicsRenderTargetsInfo,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -72,7 +89,12 @@ pub unsafe fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> M
unimplemented!() unimplemented!()
} }
pub unsafe fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult { pub unsafe fn MLGraphicsBeginFrame(
graphics_client: MLHandle,
params: *const MLGraphicsFrameParams,
frame_handle: *mut MLHandle,
virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -80,7 +102,10 @@ pub unsafe fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHand
unimplemented!() unimplemented!()
} }
pub unsafe fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult { pub unsafe fn MLGraphicsSignalSyncObjectGL(
graphics_client: MLHandle,
sync_object: MLHandle,
) -> MLResult {
unimplemented!() unimplemented!()
} }
@ -95,4 +120,3 @@ pub unsafe fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool {
pub unsafe fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char) { pub unsafe fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char) {
unimplemented!() unimplemented!()
} }

View File

@ -18,33 +18,33 @@ extern crate objc;
use euclid::default::Size2D; use euclid::default::Size2D;
use nfd::Response; use nfd::Response;
use pathfinder_demo::window::{Event, Keycode, DataPath, View, Window, WindowSize}; use pathfinder_demo::window::{DataPath, Event, Keycode, View, Window, WindowSize};
use pathfinder_demo::{DemoApp, Options}; use pathfinder_demo::{DemoApp, Options};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader; use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use std::cell::Cell; use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex; use std::sync::Mutex;
use surfman::{SurfaceAccess, SurfaceType, declare_surfman}; use surfman::{declare_surfman, SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize;
use winit::{ControlFlow, ElementState, Event as WinitEvent, EventsLoop, EventsLoopProxy}; use winit::{ControlFlow, ElementState, Event as WinitEvent, EventsLoop, EventsLoopProxy};
use winit::{MouseButton, VirtualKeyCode, Window as WinitWindow, WindowBuilder, WindowEvent}; use winit::{MouseButton, VirtualKeyCode, Window as WinitWindow, WindowBuilder, WindowEvent};
use winit::dpi::LogicalSize;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use gl::types::GLuint;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use gl; use gl;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Connection, Context, ContextAttributeFlags, ContextAttributes}; use gl::types::GLuint;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Device, GLVersion as SurfmanGLVersion};
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use io_surface::IOSurfaceRef; use io_surface::IOSurfaceRef;
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use pathfinder_metal::MetalDevice; use pathfinder_metal::MetalDevice;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Connection, Context, ContextAttributeFlags, ContextAttributes};
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Device, GLVersion as SurfmanGLVersion};
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use surfman::{NativeDevice, SystemConnection, SystemDevice, SystemSurface}; use surfman::{NativeDevice, SystemConnection, SystemDevice, SystemSurface};
@ -138,7 +138,10 @@ struct EventQueue {
#[derive(Clone)] #[derive(Clone)]
enum CustomEvent { enum CustomEvent {
User { message_type: u32, message_data: u32 }, User {
message_type: u32,
message_data: u32,
},
OpenData(PathBuf), OpenData(PathBuf),
} }
@ -155,15 +158,17 @@ impl Window for WindowImpl {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_default_framebuffer(&self) -> GLuint { fn gl_default_framebuffer(&self) -> GLuint {
self.device.context_surface_info(&self.context).unwrap().unwrap().framebuffer_object self.device
.context_surface_info(&self.context)
.unwrap()
.unwrap()
.framebuffer_object
} }
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
fn metal_device(&self) -> metal::Device { fn metal_device(&self) -> metal::Device {
// FIXME(pcwalton): Remove once `surfman` upgrades `metal-rs` version. // FIXME(pcwalton): Remove once `surfman` upgrades `metal-rs` version.
unsafe { unsafe { std::mem::transmute(self.metal_device.0.clone()) }
std::mem::transmute(self.metal_device.0.clone())
}
} }
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
@ -172,7 +177,10 @@ impl Window for WindowImpl {
} }
fn viewport(&self, view: View) -> RectI { fn viewport(&self, view: View) -> RectI {
let WindowSize { logical_size, backing_scale_factor } = self.size(); let WindowSize {
logical_size,
backing_scale_factor,
} = self.size();
let mut size = (logical_size.to_f32() * backing_scale_factor).to_i32(); let mut size = (logical_size.to_f32() * backing_scale_factor).to_i32();
let mut x_offset = 0; let mut x_offset = 0;
if let View::Stereo(index) = view { if let View::Stereo(index) = view {
@ -192,17 +200,24 @@ impl Window for WindowImpl {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn present(&mut self, _: &mut GLDevice) { fn present(&mut self, _: &mut GLDevice) {
let mut surface = self.device let mut surface = self
.unbind_surface_from_context(&mut self.context) .device
.unwrap() .unbind_surface_from_context(&mut self.context)
.unwrap(); .unwrap()
self.device.present_surface(&mut self.context, &mut surface).unwrap(); .unwrap();
self.device.bind_surface_to_context(&mut self.context, surface).unwrap(); self.device
.present_surface(&mut self.context, &mut surface)
.unwrap();
self.device
.bind_surface_to_context(&mut self.context, surface)
.unwrap();
} }
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
fn present(&mut self, metal_device: &mut MetalDevice) { fn present(&mut self, metal_device: &mut MetalDevice) {
self.device.present_surface(&mut self.surface).expect("Failed to present surface!"); self.device
.present_surface(&mut self.surface)
.expect("Failed to present surface!");
metal_device.swap_texture(self.device.native_surface(&self.surface).0); metal_device.swap_texture(self.device.native_surface(&self.surface).0);
} }
@ -214,7 +229,9 @@ impl Window for WindowImpl {
if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg,pdf"), None) { if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg,pdf"), None) {
let mut event_queue = EVENT_QUEUE.lock().unwrap(); let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap(); let event_queue = event_queue.as_mut().unwrap();
event_queue.pending_custom_events.push_back(CustomEvent::OpenData(PathBuf::from(path))); event_queue
.pending_custom_events
.push_back(CustomEvent::OpenData(PathBuf::from(path)));
drop(event_queue.event_loop_proxy.wakeup()); drop(event_queue.event_loop_proxy.wakeup());
} }
} }
@ -235,10 +252,12 @@ impl Window for WindowImpl {
fn push_user_event(message_type: u32, message_data: u32) { fn push_user_event(message_type: u32, message_data: u32) {
let mut event_queue = EVENT_QUEUE.lock().unwrap(); let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap(); let event_queue = event_queue.as_mut().unwrap();
event_queue.pending_custom_events.push_back(CustomEvent::User { event_queue
message_type, .pending_custom_events
message_data, .push_back(CustomEvent::User {
}); message_type,
message_data,
});
drop(event_queue.event_loop_proxy.wakeup()); drop(event_queue.event_loop_proxy.wakeup());
} }
} }
@ -249,14 +268,17 @@ impl WindowImpl {
let event_loop = EventsLoop::new(); let event_loop = EventsLoop::new();
let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64); let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Pathfinder Demo") let window = WindowBuilder::new()
.with_dimensions(logical_size) .with_title("Pathfinder Demo")
.build(&event_loop) .with_dimensions(logical_size)
.unwrap(); .build(&event_loop)
.unwrap();
window.show(); window.show();
let connection = Connection::from_winit_window(&window).unwrap(); let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap(); let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = if options.high_performance_gpu { let adapter = if options.high_performance_gpu {
connection.create_hardware_adapter().unwrap() connection.create_hardware_adapter().unwrap()
@ -270,13 +292,18 @@ impl WindowImpl {
version: SurfmanGLVersion::new(3, 0), version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA, flags: ContextAttributeFlags::ALPHA,
}; };
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap(); let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
let surface_type = SurfaceType::Widget { native_widget }; let surface_type = SurfaceType::Widget { native_widget };
let mut context = device.create_context(&context_descriptor).unwrap(); let mut context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&context, SurfaceAccess::GPUOnly, surface_type) let surface = device
.unwrap(); .create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
device.bind_surface_to_context(&mut context, surface).unwrap(); .unwrap();
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap(); device.make_context_current(&context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name)); gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
@ -307,14 +334,17 @@ impl WindowImpl {
let event_loop = EventsLoop::new(); let event_loop = EventsLoop::new();
let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64); let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Pathfinder Demo") let window = WindowBuilder::new()
.with_dimensions(logical_size) .with_title("Pathfinder Demo")
.build(&event_loop) .with_dimensions(logical_size)
.unwrap(); .build(&event_loop)
.unwrap();
window.show(); window.show();
let connection = SystemConnection::from_winit_window(&window).unwrap(); let connection = SystemConnection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap(); let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = if options.high_performance_gpu { let adapter = if options.high_performance_gpu {
connection.create_hardware_adapter().unwrap() connection.create_hardware_adapter().unwrap()
@ -326,7 +356,9 @@ impl WindowImpl {
let native_device = device.native_device(); let native_device = device.native_device();
let surface_type = SurfaceType::Widget { native_widget }; let surface_type = SurfaceType::Widget { native_widget };
let surface = device.create_surface(SurfaceAccess::GPUOnly, surface_type).unwrap(); let surface = device
.create_surface(SurfaceAccess::GPUOnly, surface_type)
.unwrap();
let resource_loader = FilesystemResourceLoader::locate(); let resource_loader = FilesystemResourceLoader::locate();
@ -350,11 +382,16 @@ impl WindowImpl {
} }
} }
fn window(&self) -> &WinitWindow { &self.window } fn window(&self) -> &WinitWindow {
&self.window
}
fn size(&self) -> WindowSize { fn size(&self) -> WindowSize {
let window = self.window(); let window = self.window();
let (monitor, size) = (window.get_current_monitor(), window.get_inner_size().unwrap()); let (monitor, size) = (
window.get_current_monitor(),
window.get_inner_size().unwrap(),
);
WindowSize { WindowSize {
logical_size: vec2i(size.width as i32, size.height as i32), logical_size: vec2i(size.width as i32, size.height as i32),
@ -370,18 +407,13 @@ impl WindowImpl {
let pending_events = &mut self.pending_events; let pending_events = &mut self.pending_events;
self.event_loop.run_forever(|winit_event| { self.event_loop.run_forever(|winit_event| {
//println!("blocking {:?}", winit_event); //println!("blocking {:?}", winit_event);
match convert_winit_event(winit_event, match convert_winit_event(winit_event, window, mouse_position, mouse_down) {
window,
mouse_position,
mouse_down) {
Some(event) => { Some(event) => {
//println!("handled"); //println!("handled");
pending_events.push_back(event); pending_events.push_back(event);
ControlFlow::Break ControlFlow::Break
} }
None => { None => ControlFlow::Continue,
ControlFlow::Continue
}
} }
}); });
} }
@ -397,10 +429,9 @@ impl WindowImpl {
let pending_events = &mut self.pending_events; let pending_events = &mut self.pending_events;
self.event_loop.poll_events(|winit_event| { self.event_loop.poll_events(|winit_event| {
//println!("nonblocking {:?}", winit_event); //println!("nonblocking {:?}", winit_event);
if let Some(event) = convert_winit_event(winit_event, if let Some(event) =
window, convert_winit_event(winit_event, window, mouse_position, mouse_down)
mouse_position, {
mouse_down) {
//println!("handled"); //println!("handled");
pending_events.push_back(event); pending_events.push_back(event);
} }
@ -410,87 +441,93 @@ impl WindowImpl {
} }
} }
fn convert_winit_event(winit_event: WinitEvent, fn convert_winit_event(
window: &WinitWindow, winit_event: WinitEvent,
mouse_position: &mut Vector2I, window: &WinitWindow,
mouse_down: &mut bool) mouse_position: &mut Vector2I,
-> Option<Event> { mouse_down: &mut bool,
) -> Option<Event> {
match winit_event { match winit_event {
WinitEvent::Awakened => { WinitEvent::Awakened => {
let mut event_queue = EVENT_QUEUE.lock().unwrap(); let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap(); let event_queue = event_queue.as_mut().unwrap();
match event_queue.pending_custom_events match event_queue
.pop_front() .pending_custom_events
.expect("`Awakened` with no pending custom event!") { .pop_front()
CustomEvent::OpenData(data_path) => Some(Event::OpenData(DataPath::Path(data_path))), .expect("`Awakened` with no pending custom event!")
CustomEvent::User { message_data, message_type } => { {
Some(Event::User { message_data, message_type }) CustomEvent::OpenData(data_path) => {
Some(Event::OpenData(DataPath::Path(data_path)))
} }
CustomEvent::User {
message_data,
message_type,
} => Some(Event::User {
message_data,
message_type,
}),
} }
} }
WinitEvent::WindowEvent { event: window_event, .. } => { WinitEvent::WindowEvent {
match window_event { event: window_event,
WindowEvent::MouseInput { ..
state: ElementState::Pressed, } => match window_event {
button: MouseButton::Left, WindowEvent::MouseInput {
.. state: ElementState::Pressed,
} => { button: MouseButton::Left,
*mouse_down = true; ..
Some(Event::MouseDown(*mouse_position)) } => {
*mouse_down = true;
Some(Event::MouseDown(*mouse_position))
}
WindowEvent::MouseInput {
state: ElementState::Released,
button: MouseButton::Left,
..
} => {
*mouse_down = false;
None
}
WindowEvent::CursorMoved { position, .. } => {
*mouse_position = vec2i(position.x as i32, position.y as i32);
if *mouse_down {
Some(Event::MouseDragged(*mouse_position))
} else {
Some(Event::MouseMoved(*mouse_position))
} }
WindowEvent::MouseInput { }
state: ElementState::Released, WindowEvent::KeyboardInput { input, .. } => input
button: MouseButton::Left, .virtual_keycode
.. .and_then(|virtual_keycode| match virtual_keycode {
} => { VirtualKeyCode::Escape => Some(Keycode::Escape),
*mouse_down = false; VirtualKeyCode::Tab => Some(Keycode::Tab),
None virtual_keycode => {
} let vk = virtual_keycode as u32;
WindowEvent::CursorMoved { position, .. } => { let vk_a = VirtualKeyCode::A as u32;
*mouse_position = vec2i(position.x as i32, position.y as i32); let vk_z = VirtualKeyCode::Z as u32;
if *mouse_down { if vk >= vk_a && vk <= vk_z {
Some(Event::MouseDragged(*mouse_position)) let character = ((vk - vk_a) + 'A' as u32) as u8;
} else { Some(Keycode::Alphanumeric(character))
Some(Event::MouseMoved(*mouse_position)) } else {
None
}
} }
} })
WindowEvent::KeyboardInput { input, .. } => { .map(|keycode| match input.state {
input.virtual_keycode.and_then(|virtual_keycode| { ElementState::Pressed => Event::KeyDown(keycode),
match virtual_keycode { ElementState::Released => Event::KeyUp(keycode),
VirtualKeyCode::Escape => Some(Keycode::Escape), }),
VirtualKeyCode::Tab => Some(Keycode::Tab), WindowEvent::CloseRequested => Some(Event::Quit),
virtual_keycode => { WindowEvent::Resized(new_size) => {
let vk = virtual_keycode as u32; let logical_size = vec2i(new_size.width as i32, new_size.height as i32);
let vk_a = VirtualKeyCode::A as u32; let backing_scale_factor = window.get_current_monitor().get_hidpi_factor() as f32;
let vk_z = VirtualKeyCode::Z as u32; Some(Event::WindowResized(WindowSize {
if vk >= vk_a && vk <= vk_z { logical_size,
let character = ((vk - vk_a) + 'A' as u32) as u8; backing_scale_factor,
Some(Keycode::Alphanumeric(character)) }))
} else {
None
}
}
}
}).map(|keycode| {
match input.state {
ElementState::Pressed => Event::KeyDown(keycode),
ElementState::Released => Event::KeyUp(keycode),
}
})
}
WindowEvent::CloseRequested => Some(Event::Quit),
WindowEvent::Resized(new_size) => {
let logical_size = vec2i(new_size.width as i32, new_size.height as i32);
let backing_scale_factor =
window.get_current_monitor().get_hidpi_factor() as f32;
Some(Event::WindowResized(WindowSize {
logical_size,
backing_scale_factor,
}))
}
_ => None,
} }
} _ => None,
},
_ => None, _ => None,
} }
} }

View File

@ -11,21 +11,21 @@
//! Demonstrates how to use the Pathfinder canvas API with `glutin`. //! Demonstrates how to use the Pathfinder canvas API with `glutin`.
use glutin::dpi::PhysicalSize; use glutin::dpi::PhysicalSize;
use glutin::{ContextBuilder, GlProfile, GlRequest};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use glutin::event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowBuilder; use glutin::window::WindowBuilder;
use glutin::{ContextBuilder, GlProfile, GlRequest};
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D}; use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D};
use pathfinder_color::ColorF; use pathfinder_color::ColorF;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
fn main() { fn main() {
// Calculate the right logical size of the window. // Calculate the right logical size of the window.
@ -34,14 +34,16 @@ fn main() {
let physical_window_size = PhysicalSize::new(window_size.x() as f64, window_size.y() as f64); let physical_window_size = PhysicalSize::new(window_size.x() as f64, window_size.y() as f64);
// Open a window. // Open a window.
let window_builder = WindowBuilder::new().with_title("Minimal example") let window_builder = WindowBuilder::new()
.with_inner_size(physical_window_size); .with_title("Minimal example")
.with_inner_size(physical_window_size);
// Create an OpenGL 3.x context for Pathfinder to use. // Create an OpenGL 3.x context for Pathfinder to use.
let gl_context = ContextBuilder::new().with_gl(GlRequest::Latest) let gl_context = ContextBuilder::new()
.with_gl_profile(GlProfile::Core) .with_gl(GlRequest::Latest)
.build_windowed(window_builder, &event_loop) .with_gl_profile(GlProfile::Core)
.unwrap(); .build_windowed(window_builder, &event_loop)
.unwrap();
// Load OpenGL, and make the context current. // Load OpenGL, and make the context current.
let gl_context = unsafe { gl_context.make_current().unwrap() }; let gl_context = unsafe { gl_context.make_current().unwrap() };
@ -79,28 +81,38 @@ fn main() {
canvas.stroke_path(path); canvas.stroke_path(path);
// Render the canvas to screen. // Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(), let mut scene = SceneProxy::from_scene(
renderer.mode().level, canvas.into_canvas().into_scene(),
RayonExecutor); renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default()); scene.build_and_render(&mut renderer, BuildOptions::default());
gl_context.swap_buffers().unwrap(); gl_context.swap_buffers().unwrap();
// Wait for a keypress. // Wait for a keypress.
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
match event { match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } |
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::KeyboardInput { event: WindowEvent::CloseRequested,
input: KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Escape), .. }, ..
.. }
}, | Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
},
.. ..
} => { } => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
}, }
_ => { _ => {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
}, }
}; };
}) })
} }

View File

@ -12,8 +12,8 @@ use foreign_types::ForeignTypeRef;
use metal::{CAMetalLayer, CoreAnimationLayerRef}; use metal::{CAMetalLayer, CoreAnimationLayerRef};
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D}; use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D};
use pathfinder_color::ColorF; use pathfinder_color::ColorF;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_metal::MetalDevice; use pathfinder_metal::MetalDevice;
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
@ -34,10 +34,15 @@ fn main() {
// Open a window. // Open a window.
let window_size = vec2i(640, 480); let window_size = vec2i(640, 480);
let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32) let window = video
.opengl() .window(
.build() "Minimal example",
.unwrap(); window_size.x() as u32,
window_size.y() as u32,
)
.opengl()
.build()
.unwrap();
// Create a Metal context. // Create a Metal context.
let canvas = window.into_canvas().present_vsync().build().unwrap(); let canvas = window.into_canvas().present_vsync().build().unwrap();
@ -48,9 +53,7 @@ fn main() {
let drawable = metal_layer.next_drawable().unwrap(); let drawable = metal_layer.next_drawable().unwrap();
// Create a Pathfinder renderer. // Create a Pathfinder renderer.
let device = unsafe { let device = unsafe { MetalDevice::new(metal_device, drawable.clone()) };
MetalDevice::new(metal_device, drawable.clone())
};
let mode = RendererMode::default_for_device(&device); let mode = RendererMode::default_for_device(&device);
let options = RendererOptions { let options = RendererOptions {
dest: DestFramebuffer::full_window(window_size), dest: DestFramebuffer::full_window(window_size),
@ -81,9 +84,11 @@ fn main() {
canvas.stroke_path(path); canvas.stroke_path(path);
// Render the canvas to screen. // Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(), let mut scene = SceneProxy::from_scene(
renderer.mode().level, canvas.into_canvas().into_scene(),
RayonExecutor); renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default()); scene.build_and_render(&mut renderer, BuildOptions::default());
renderer.device().present_drawable(drawable); renderer.device().present_drawable(drawable);
@ -91,7 +96,11 @@ fn main() {
let mut event_pump = sdl_context.event_pump().unwrap(); let mut event_pump = sdl_context.event_pump().unwrap();
loop { loop {
match event_pump.wait_event() { match event_pump.wait_event() {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return, Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => return,
_ => {} _ => {}
} }
} }

View File

@ -20,7 +20,9 @@ use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererO
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader; use pathfinder_resources::embedded::EmbeddedResourceLoader;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion}; use surfman::{
Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion,
};
use surfman::{SurfaceAccess, SurfaceType}; use surfman::{SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
use winit::{ControlFlow, Event, EventsLoop, WindowBuilder, WindowEvent}; use winit::{ControlFlow, Event, EventsLoop, WindowBuilder, WindowEvent};
@ -30,16 +32,19 @@ fn main() {
let mut event_loop = EventsLoop::new(); let mut event_loop = EventsLoop::new();
let window_size = Size2D::new(640, 480); let window_size = Size2D::new(640, 480);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64); let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Minimal example") let window = WindowBuilder::new()
.with_dimensions(logical_size) .with_title("Minimal example")
.build(&event_loop) .with_dimensions(logical_size)
.unwrap(); .build(&event_loop)
.unwrap();
window.show(); window.show();
// Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated // Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated
// GPU. // GPU.
let connection = Connection::from_winit_window(&window).unwrap(); let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap(); let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = connection.create_low_power_adapter().unwrap(); let adapter = connection.create_low_power_adapter().unwrap();
let mut device = connection.create_device(&adapter).unwrap(); let mut device = connection.create_device(&adapter).unwrap();
@ -48,14 +53,19 @@ fn main() {
version: SurfmanGLVersion::new(3, 0), version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA, flags: ContextAttributeFlags::ALPHA,
}; };
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap(); let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
// Make the OpenGL context via `surfman`, and load OpenGL functions. // Make the OpenGL context via `surfman`, and load OpenGL functions.
let surface_type = SurfaceType::Widget { native_widget }; let surface_type = SurfaceType::Widget { native_widget };
let mut context = device.create_context(&context_descriptor).unwrap(); let mut context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&context, SurfaceAccess::GPUOnly, surface_type) let surface = device
.unwrap(); .create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
device.bind_surface_to_context(&mut context, surface).unwrap(); .unwrap();
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap(); device.make_context_current(&context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name)); gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
@ -65,10 +75,11 @@ fn main() {
let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32); let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32);
// Create a Pathfinder GL device. // Create a Pathfinder GL device.
let default_framebuffer = device.context_surface_info(&context) let default_framebuffer = device
.unwrap() .context_surface_info(&context)
.unwrap() .unwrap()
.framebuffer_object; .unwrap()
.framebuffer_object;
let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer); let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer);
// Create a Pathfinder renderer. // Create a Pathfinder renderer.
@ -87,17 +98,27 @@ fn main() {
event_loop.run_forever(|event| { event_loop.run_forever(|event| {
let mut should_render = is_first_render; let mut should_render = is_first_render;
match event { match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } | Event::WindowEvent {
Event::WindowEvent { event: WindowEvent::KeyboardInput { .. }, .. } => return ControlFlow::Break, event: WindowEvent::CloseRequested,
Event::WindowEvent { event: WindowEvent::Refresh, .. } => { ..
}
| Event::WindowEvent {
event: WindowEvent::KeyboardInput { .. },
..
} => return ControlFlow::Break,
Event::WindowEvent {
event: WindowEvent::Refresh,
..
} => {
should_render = true; should_render = true;
} }
_ => {}, _ => {}
} }
if should_render { if should_render {
// Make a canvas. We're going to draw a house. // Make a canvas. We're going to draw a house.
let mut canvas = Canvas::new(framebuffer_size.to_f32()).get_context_2d(font_context.clone()); let mut canvas =
Canvas::new(framebuffer_size.to_f32()).get_context_2d(font_context.clone());
// Set line width. // Set line width.
canvas.set_line_width(10.0); canvas.set_line_width(10.0);
@ -117,15 +138,22 @@ fn main() {
canvas.stroke_path(path); canvas.stroke_path(path);
// Render the canvas to screen. // Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(), let mut scene = SceneProxy::from_scene(
renderer.mode().level, canvas.into_canvas().into_scene(),
RayonExecutor); renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default()); scene.build_and_render(&mut renderer, BuildOptions::default());
// Present the surface. // Present the surface.
let mut surface = device.unbind_surface_from_context(&mut context).unwrap().unwrap(); let mut surface = device
.unbind_surface_from_context(&mut context)
.unwrap()
.unwrap();
device.present_surface(&mut context, &mut surface).unwrap(); device.present_surface(&mut context, &mut surface).unwrap();
device.bind_surface_to_context(&mut context, surface).unwrap(); device
.bind_surface_to_context(&mut context, surface)
.unwrap();
} }
is_first_render = false; is_first_render = false;

View File

@ -11,7 +11,7 @@
use euclid::default::Size2D; use euclid::default::Size2D;
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillStyle, Path2D}; use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillStyle, Path2D};
use pathfinder_color::{ColorF, ColorU}; use pathfinder_color::{ColorF, ColorU};
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
@ -19,9 +19,11 @@ use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererO
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader; use pathfinder_resources::embedded::EmbeddedResourceLoader;
use std::f32::consts::PI;
use std::f32; use std::f32;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion}; use std::f32::consts::PI;
use surfman::{
Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion,
};
use surfman::{SurfaceAccess, SurfaceType}; use surfman::{SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
use winit::{Event, EventsLoop, WindowBuilder, WindowEvent}; use winit::{Event, EventsLoop, WindowBuilder, WindowEvent};
@ -43,16 +45,19 @@ fn main() {
let mut event_loop = EventsLoop::new(); let mut event_loop = EventsLoop::new();
let window_size = Size2D::new(1067, 800); let window_size = Size2D::new(1067, 800);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64); let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Moire example") let window = WindowBuilder::new()
.with_dimensions(logical_size) .with_title("Moire example")
.build(&event_loop) .with_dimensions(logical_size)
.unwrap(); .build(&event_loop)
.unwrap();
window.show(); window.show();
// Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated // Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated
// GPU. // GPU.
let connection = Connection::from_winit_window(&window).unwrap(); let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap(); let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = connection.create_low_power_adapter().unwrap(); let adapter = connection.create_low_power_adapter().unwrap();
let mut device = connection.create_device(&adapter).unwrap(); let mut device = connection.create_device(&adapter).unwrap();
@ -61,14 +66,19 @@ fn main() {
version: SurfmanGLVersion::new(3, 0), version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA, flags: ContextAttributeFlags::ALPHA,
}; };
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap(); let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
// Make the OpenGL context via `surfman`, and load OpenGL functions. // Make the OpenGL context via `surfman`, and load OpenGL functions.
let surface_type = SurfaceType::Widget { native_widget }; let surface_type = SurfaceType::Widget { native_widget };
let mut gl_context = device.create_context(&context_descriptor).unwrap(); let mut gl_context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&gl_context, SurfaceAccess::GPUOnly, surface_type) let surface = device
.unwrap(); .create_surface(&gl_context, SurfaceAccess::GPUOnly, surface_type)
device.bind_surface_to_context(&mut gl_context, surface).unwrap(); .unwrap();
device
.bind_surface_to_context(&mut gl_context, surface)
.unwrap();
device.make_context_current(&gl_context).unwrap(); device.make_context_current(&gl_context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&gl_context, symbol_name)); gl::load_with(|symbol_name| device.get_proc_address(&gl_context, symbol_name));
@ -78,10 +88,11 @@ fn main() {
let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32); let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32);
// Create a Pathfinder GL device. // Create a Pathfinder GL device.
let default_framebuffer = device.context_surface_info(&gl_context) let default_framebuffer = device
.unwrap() .context_surface_info(&gl_context)
.unwrap() .unwrap()
.framebuffer_object; .unwrap()
.framebuffer_object;
let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer); let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer);
// Create our renderers. // Create our renderers.
@ -101,16 +112,27 @@ fn main() {
moire_renderer.render(); moire_renderer.render();
// Present the rendered canvas via `surfman`. // Present the rendered canvas via `surfman`.
let mut surface = device.unbind_surface_from_context(&mut gl_context).unwrap().unwrap(); let mut surface = device
device.present_surface(&mut gl_context, &mut surface).unwrap(); .unbind_surface_from_context(&mut gl_context)
device.bind_surface_to_context(&mut gl_context, surface).unwrap(); .unwrap()
.unwrap();
device
.present_surface(&mut gl_context, &mut surface)
.unwrap();
device
.bind_surface_to_context(&mut gl_context, surface)
.unwrap();
event_loop.poll_events(|event| { event_loop.poll_events(|event| match event {
match event { Event::WindowEvent {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } | event: WindowEvent::CloseRequested,
Event::WindowEvent { event: WindowEvent::KeyboardInput { .. }, .. } => exit = true, ..
_ => {}
} }
| Event::WindowEvent {
event: WindowEvent::KeyboardInput { .. },
..
} => exit = true,
_ => {}
}); });
} }
} }
@ -127,8 +149,11 @@ struct MoireRenderer {
} }
impl MoireRenderer { impl MoireRenderer {
fn new(renderer: Renderer<GLDevice>, window_size: Vector2I, drawable_size: Vector2I) fn new(
-> MoireRenderer { renderer: Renderer<GLDevice>,
window_size: Vector2I,
drawable_size: Vector2I,
) -> MoireRenderer {
let level = renderer.mode().level; let level = renderer.mode().level;
MoireRenderer { MoireRenderer {
renderer, renderer,
@ -159,7 +184,7 @@ impl MoireRenderer {
self.renderer.options_mut().background_color = Some(background_color); self.renderer.options_mut().background_color = Some(background_color);
// Make a canvas. // Make a canvas.
let mut canvas = let mut canvas =
Canvas::new(self.drawable_size.to_f32()).get_context_2d(self.font_context.clone()); Canvas::new(self.drawable_size.to_f32()).get_context_2d(self.font_context.clone());
canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio); canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio);
canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8())); canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8()));
@ -171,7 +196,8 @@ impl MoireRenderer {
// Build and render scene. // Build and render scene.
self.scene.replace_scene(canvas.into_canvas().into_scene()); self.scene.replace_scene(canvas.into_canvas().into_scene());
self.scene.build_and_render(&mut self.renderer, BuildOptions::default()); self.scene
.build_and_render(&mut self.renderer, BuildOptions::default());
self.frame += 1; self.frame += 1;
} }

View File

@ -6,7 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
arrayvec = "0.5" arrayvec = "0.5"
font-kit = "0.6" font-kit = "0.13"
gl = "0.14" gl = "0.14"
[dependencies.euclid] [dependencies.euclid]

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
font-kit = "0.6" font-kit = "0.13"
gl = "0.14" gl = "0.14"
sdl2 = "0.33" sdl2 = "0.33"
sdl2-sys = "0.33" sdl2-sys = "0.33"

View File

@ -18,8 +18,8 @@ use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader; use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use sdl2::event::{Event, WindowEvent}; use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile; use sdl2::video::GLProfile;
@ -36,16 +36,19 @@ fn main() {
let gl_attributes = video.gl_attr(); let gl_attributes = video.gl_attr();
gl_attributes.set_context_profile(GLProfile::Core); gl_attributes.set_context_profile(GLProfile::Core);
gl_attributes.set_context_version(4, 1); gl_attributes.set_context_version(4, 1);
gl_attributes.set_context_flags() gl_attributes.set_context_flags().forward_compatible().set();
.forward_compatible()
.set();
// Open a window. // Open a window.
let window_size = vec2i(640, 480); let window_size = vec2i(640, 480);
let window = video.window("Text example", window_size.x() as u32, window_size.y() as u32) let window = video
.opengl() .window(
.build() "Text example",
.unwrap(); window_size.x() as u32,
window_size.y() as u32,
)
.opengl()
.build()
.unwrap();
// Create the GL context, and make it current. // Create the GL context, and make it current.
let gl_context = window.gl_create_context().unwrap(); let gl_context = window.gl_create_context().unwrap();
@ -75,10 +78,21 @@ fn main() {
let mut fps_buf = String::new(); let mut fps_buf = String::new();
loop { loop {
match event_pump.poll_event() { match event_pump.poll_event() {
Some(Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. }) => return, Some(
Some(Event::Window { win_event: WindowEvent::Exposed, .. }) | None => { Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
},
) => return,
Some(Event::Window {
win_event: WindowEvent::Exposed,
..
})
| None => {
// Make a canvas. // Make a canvas.
let mut canvas = Canvas::new(window_size.to_f32()).get_context_2d(font_context.clone()); let mut canvas =
Canvas::new(window_size.to_f32()).get_context_2d(font_context.clone());
// Draw the text. // Draw the text.
canvas.set_font("Overpass-Regular").unwrap(); canvas.set_font("Overpass-Regular").unwrap();
@ -87,7 +101,9 @@ fn main() {
let elapsed = now.duration_since(last_update); let elapsed = now.duration_since(last_update);
if elapsed >= Duration::from_millis(50) { if elapsed >= Duration::from_millis(50) {
last_update = now; last_update = now;
let fps = (n as f64) * ((Duration::from_secs(1).as_micros() as f64) / (elapsed.as_micros() as f64)); let fps = (n as f64)
* ((Duration::from_secs(1).as_micros() as f64)
/ (elapsed.as_micros() as f64));
fps_buf.clear(); fps_buf.clear();
fps_buf.push_str("FPS: "); fps_buf.push_str("FPS: ");
use std::fmt::Write; use std::fmt::Write;
@ -100,14 +116,16 @@ fn main() {
canvas.stroke_text("Goodbye Pathfinder!", vec2f(608.0, 464.0)); canvas.stroke_text("Goodbye Pathfinder!", vec2f(608.0, 464.0));
// Render the canvas to screen. // Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(), let mut scene = SceneProxy::from_scene(
renderer.mode().level, canvas.into_canvas().into_scene(),
RayonExecutor); renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default()); scene.build_and_render(&mut renderer, BuildOptions::default());
window.gl_swap_window(); window.gl_swap_window();
n += 1; n += 1;
}, }
_ => {} _ => {}
} }
} }

View File

@ -18,8 +18,8 @@ use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader; use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_webgl::WebGlDevice; use pathfinder_webgl::WebGlDevice;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext}; use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
mod utils; mod utils;
@ -32,11 +32,12 @@ pub fn rust_main() {
let canvas = document.get_element_by_id("c").unwrap(); let canvas = document.get_element_by_id("c").unwrap();
let canvas: HtmlCanvasElement = canvas.dyn_into::<HtmlCanvasElement>().unwrap(); let canvas: HtmlCanvasElement = canvas.dyn_into::<HtmlCanvasElement>().unwrap();
let context = canvas.get_context("webgl2") let context = canvas
.unwrap() .get_context("webgl2")
.unwrap() .unwrap()
.dyn_into::<WebGl2RenderingContext>() .unwrap()
.unwrap(); .dyn_into::<WebGl2RenderingContext>()
.unwrap();
// Get the real size of the window, taking HiDPI into account. // Get the real size of the window, taking HiDPI into account.
let framebuffer_size = vec2i(canvas.width() as i32, canvas.height() as i32); let framebuffer_size = vec2i(canvas.width() as i32, canvas.height() as i32);

View File

@ -10,20 +10,20 @@
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::options::{RenderTransform, BuildOptions}; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_resources::ResourceLoader; use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::scene::Scene;
use pathfinder_resources::embedded::EmbeddedResourceLoader; use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_swf::{draw_paths_into_scene, process_swf_tags};
use sdl2::event::Event; use sdl2::event::Event;
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile; use sdl2::video::GLProfile;
use pathfinder_renderer::scene::Scene;
use pathfinder_swf::{draw_paths_into_scene, process_swf_tags};
use std::env; use std::env;
use std::fs::read; use std::fs::read;
@ -35,8 +35,8 @@ fn main() {
match read(path) { match read(path) {
Ok(bytes) => { Ok(bytes) => {
swf_bytes = bytes; swf_bytes = bytes;
}, }
Err(e) => panic!("{}", e) Err(e) => panic!("{}", e),
} }
} else { } else {
// NOTE(jon): This is a version of the ghostscript tiger graphic flattened to a single // NOTE(jon): This is a version of the ghostscript tiger graphic flattened to a single
@ -83,13 +83,21 @@ fn main() {
// Open a window. // Open a window.
let window_size = vec2i(stage.width(), stage.height()); let window_size = vec2i(stage.width(), stage.height());
let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32) let window = video
.window(
"Minimal example",
window_size.x() as u32,
window_size.y() as u32,
)
.opengl() .opengl()
.allow_highdpi() .allow_highdpi()
.build() .build()
.unwrap(); .unwrap();
let pixel_size = vec2i(window.drawable_size().0 as i32, window.drawable_size().1 as i32); let pixel_size = vec2i(
window.drawable_size().0 as i32,
window.drawable_size().1 as i32,
);
let device_pixel_ratio = pixel_size.x() as f32 / window_size.x() as f32; let device_pixel_ratio = pixel_size.x() as f32 / window_size.x() as f32;
// Create the GL context, and make it current. // Create the GL context, and make it current.
@ -109,9 +117,10 @@ fn main() {
// Clear to swf stage background color. // Clear to swf stage background color.
let mut scene = Scene::new(); let mut scene = Scene::new();
scene.set_view_box(RectF::new(Vector2F::zero(), scene.set_view_box(RectF::new(
vec2f(stage.width() as f32, Vector2F::zero(),
stage.height() as f32) * device_pixel_ratio)); vec2f(stage.width() as f32, stage.height() as f32) * device_pixel_ratio,
));
draw_paths_into_scene(&library, &mut scene); draw_paths_into_scene(&library, &mut scene);
// Render the canvas to screen. // Render the canvas to screen.
@ -126,7 +135,11 @@ fn main() {
let mut event_pump = sdl_context.event_pump().unwrap(); let mut event_pump = sdl_context.event_pump().unwrap();
loop { loop {
match event_pump.wait_event() { match event_pump.wait_event() {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return, Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => return,
_ => {} _ => {}
} }
} }

View File

@ -10,7 +10,7 @@
use pathfinder_content::outline::ContourIterFlags; use pathfinder_content::outline::ContourIterFlags;
use pathfinder_content::segment::SegmentKind; use pathfinder_content::segment::SegmentKind;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::scene::{DrawPathId, Scene}; use pathfinder_renderer::scene::{DrawPathId, Scene};
use std::fmt; use std::fmt;
use std::io::{self, Write}; use std::io::{self, Write};
@ -39,7 +39,7 @@ impl Export for Scene {
match format { match format {
FileFormat::SVG => export_svg(self, writer), FileFormat::SVG => export_svg(self, writer),
FileFormat::PDF => export_pdf(self, writer), FileFormat::PDF => export_pdf(self, writer),
FileFormat::PS => export_ps(self, writer) FileFormat::PS => export_ps(self, writer),
} }
} }
} }
@ -63,7 +63,12 @@ fn export_svg<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
if !draw_path.name.is_empty() { if !draw_path.name.is_empty() {
write!(writer, " id=\"{}\"", draw_path.name)?; write!(writer, " id=\"{}\"", draw_path.name)?;
} }
writeln!(writer, " fill=\"{:?}\" d=\"{:?}\" />", paint.base_color(), draw_path.outline)?; writeln!(
writer,
" fill=\"{:?}\" d=\"{:?}\" />",
paint.base_color(),
draw_path.outline
)?;
} }
writeln!(writer, "</svg>")?; writeln!(writer, "</svg>")?;
Ok(()) Ok(())
@ -107,11 +112,11 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let c2 = c * (2.0 / 3.0) + p * (1.0 / 3.0); let c2 = c * (2.0 / 3.0) + p * (1.0 / 3.0);
pdf.cubic_to(c1, c2, p); pdf.cubic_to(c1, c2, p);
} }
SegmentKind::Cubic => { SegmentKind::Cubic => pdf.cubic_to(
pdf.cubic_to(tr(segment.ctrl.from()), tr(segment.ctrl.from()),
tr(segment.ctrl.to()), tr(segment.ctrl.to()),
tr(segment.baseline.to())) tr(segment.baseline.to()),
} ),
} }
} }
@ -136,11 +141,15 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let view_box = scene.view_box(); let view_box = scene.view_box();
writeln!(writer, "%!PS-Adobe-3.0 EPSF-3.0")?; writeln!(writer, "%!PS-Adobe-3.0 EPSF-3.0")?;
writeln!(writer, "%%BoundingBox: {:.0} {:.0}", writeln!(
writer,
"%%BoundingBox: {:.0} {:.0}",
P(view_box.origin()), P(view_box.origin()),
P(view_box.size()), P(view_box.size()),
)?; )?;
writeln!(writer, "%%HiResBoundingBox: {} {}", writeln!(
writer,
"%%HiResBoundingBox: {} {}",
P(view_box.origin()), P(view_box.origin()),
P(view_box.size()), P(view_box.size()),
)?; )?;
@ -176,7 +185,9 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
writeln!(writer, "{} {} {} curveto", P(c1), P(c2), P(p))?; writeln!(writer, "{} {} {} curveto", P(c1), P(c2), P(p))?;
} }
SegmentKind::Cubic => { SegmentKind::Cubic => {
writeln!(writer, "{} {} {} curveto", writeln!(
writer,
"{} {} {} curveto",
P(segment.ctrl.from()), P(segment.ctrl.from()),
P(segment.ctrl.to()), P(segment.ctrl.to()),
P(segment.baseline.to()) P(segment.baseline.to())
@ -202,5 +213,3 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
writeln!(writer, "showpage")?; writeln!(writer, "showpage")?;
Ok(()) Ok(())
} }

View File

@ -18,14 +18,11 @@ use std::io::{self, Write};
struct Counter<T> { struct Counter<T> {
inner: T, inner: T,
count: u64 count: u64,
} }
impl<T> Counter<T> { impl<T> Counter<T> {
pub fn new(inner: T) -> Counter<T> { pub fn new(inner: T) -> Counter<T> {
Counter { Counter { inner, count: 0 }
inner,
count: 0
}
} }
pub fn pos(&self) -> u64 { pub fn pos(&self) -> u64 {
self.count self.count
@ -37,8 +34,8 @@ impl<W: Write> Write for Counter<W> {
Ok(n) => { Ok(n) => {
self.count += n as u64; self.count += n as u64;
Ok(n) Ok(n)
}, }
Err(e) => Err(e) Err(e) => Err(e),
} }
} }
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
@ -95,7 +92,7 @@ impl Pdf {
}, },
], ],
page_size: None, page_size: None,
compression: Some(Compression::Fast) compression: Some(Compression::Fast),
} }
} }
@ -113,11 +110,14 @@ impl Pdf {
#[inline] #[inline]
pub fn set_fill_color(&mut self, color: ColorU) { pub fn set_fill_color(&mut self, color: ColorU) {
let norm = |color| f32::from(color) / 255.0; let norm = |color| f32::from(color) / 255.0;
writeln!(self.page_buffer, "{} {} {} rg", writeln!(
self.page_buffer,
"{} {} {} rg",
norm(color.r), norm(color.r),
norm(color.g), norm(color.g),
norm(color.b) norm(color.b)
).unwrap(); )
.unwrap();
} }
/// Move to a new page in the PDF document /// Move to a new page in the PDF document
@ -134,7 +134,7 @@ impl Pdf {
self.page_size = Some(size); self.page_size = Some(size);
} }
pub fn move_to(&mut self, p: Vector2F) { pub fn move_to(&mut self, p: Vector2F) {
writeln!(self.page_buffer, "{} {} m", p.x(), p.y()).unwrap(); writeln!(self.page_buffer, "{} {} m", p.x(), p.y()).unwrap();
} }
@ -143,7 +143,17 @@ impl Pdf {
} }
pub fn cubic_to(&mut self, c1: Vector2F, c2: Vector2F, p: Vector2F) { pub fn cubic_to(&mut self, c1: Vector2F, c2: Vector2F, p: Vector2F) {
writeln!(self.page_buffer, "{} {} {} {} {} {} c", c1.x(), c1.y(), c2.x(), c2.y(), p.x(), p.y()).unwrap(); writeln!(
self.page_buffer,
"{} {} {} {} {} {} c",
c1.x(),
c1.y(),
c2.x(),
c2.y(),
p.x(),
p.y()
)
.unwrap();
} }
pub fn fill(&mut self) { pub fn fill(&mut self) {
writeln!(self.page_buffer, "f").unwrap(); writeln!(self.page_buffer, "f").unwrap();
@ -156,7 +166,7 @@ impl Pdf {
fn end_page(&mut self) { fn end_page(&mut self) {
let size = match self.page_size.take() { let size = match self.page_size.take() {
Some(size) => size, Some(size) => size,
None => return // no page started None => return, // no page started
}; };
let page_stream = if let Some(level) = self.compression { let page_stream = if let Some(level) = self.compression {
let compressed = deflate::deflate_bytes_zlib_conf(&self.page_buffer, level); let compressed = deflate::deflate_bytes_zlib_conf(&self.page_buffer, level);
@ -185,22 +195,31 @@ impl Pdf {
/Resources <<\n" /Resources <<\n"
.to_vec(); .to_vec();
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) { for (idx, _obj) in self
write!(page_object, "/XObject {} 0 R ", idx+1).unwrap(); .objects
.iter()
.enumerate()
.filter(|&(_, o)| o.is_xobject)
{
write!(page_object, "/XObject {} 0 R ", idx + 1).unwrap();
} }
write!(page_object, write!(
" >>\n \ page_object,
/MediaBox [0 0 {} {}]\n \ " >>\n /MediaBox [0 0 {} {}]\n /Contents {} 0 R\n>>\n",
/Contents {} 0 R\n\ size.x(),
>>\n", size.y(),
size.x(), size.y(), stream_object_id stream_object_id
).unwrap(); )
.unwrap();
self.add_object(page_object, true, false); self.add_object(page_object, true, false);
} }
/// Write the in-memory PDF representation to disk /// Write the in-memory PDF representation to disk
pub fn write_to<W>(&mut self, writer: W) -> io::Result<()> where W: Write { pub fn write_to<W>(&mut self, writer: W) -> io::Result<()>
where
W: Write,
{
let mut out = Counter::new(writer); let mut out = Counter::new(writer);
out.write_all(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n")?; out.write_all(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n")?;
@ -211,7 +230,7 @@ impl Pdf {
// Write out each object // Write out each object
for (idx, obj) in self.objects.iter_mut().enumerate().skip(2) { for (idx, obj) in self.objects.iter_mut().enumerate().skip(2) {
obj.offset = Some(out.pos()); obj.offset = Some(out.pos());
write!(out, "{} 0 obj\n", idx+1)?; write!(out, "{} 0 obj\n", idx + 1)?;
out.write_all(&obj.contents)?; out.write_all(&obj.contents)?;
out.write_all(b"endobj\n")?; out.write_all(b"endobj\n")?;
} }
@ -220,12 +239,18 @@ impl Pdf {
self.objects[1].offset = Some(out.pos()); self.objects[1].offset = Some(out.pos());
out.write_all(b"2 0 obj\n")?; out.write_all(b"2 0 obj\n")?;
out.write_all(b"<< /Type /Pages\n")?; out.write_all(b"<< /Type /Pages\n")?;
write!(out, write!(
out,
"/Count {}\n", "/Count {}\n",
self.objects.iter().filter(|o| o.is_page).count() self.objects.iter().filter(|o| o.is_page).count()
)?; )?;
out.write_all(b"/Kids [")?; out.write_all(b"/Kids [")?;
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) { for (idx, _obj) in self
.objects
.iter()
.enumerate()
.filter(|&(_, obj)| obj.is_page)
{
write!(out, "{} 0 R ", idx + 1)?; write!(out, "{} 0 R ", idx + 1)?;
} }
out.write_all(b"] >>\nendobj\n")?; out.write_all(b"] >>\nendobj\n")?;

View File

@ -12,7 +12,7 @@
use crate::transform2d::Matrix2x2F; use crate::transform2d::Matrix2x2F;
use crate::util; use crate::util;
use crate::vector::{Vector2F, vec2f}; use crate::vector::{vec2f, Vector2F};
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Add, Mul, MulAssign, Sub}; use std::ops::{Add, Mul, MulAssign, Sub};

View File

@ -91,13 +91,19 @@ impl RectF {
pub fn contains_point(self, point: Vector2F) -> bool { pub fn contains_point(self, point: Vector2F) -> bool {
// self.origin <= point && point <= self.lower_right // self.origin <= point && point <= self.lower_right
let point = point.0.to_f32x4(); let point = point.0.to_f32x4();
self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).all_true() self.0
.concat_xy_xy(point)
.packed_le(point.concat_xy_zw(self.0))
.all_true()
} }
#[inline] #[inline]
pub fn contains_rect(self, other: RectF) -> bool { pub fn contains_rect(self, other: RectF) -> bool {
// self.origin <= other.origin && other.lower_right <= self.lower_right // self.origin <= other.origin && other.lower_right <= self.lower_right
self.0.concat_xy_zw(other.0).packed_le(other.0.concat_xy_zw(self.0)).all_true() self.0
.concat_xy_zw(other.0)
.packed_le(other.0.concat_xy_zw(self.0))
.all_true()
} }
#[inline] #[inline]
@ -121,7 +127,10 @@ impl RectF {
#[inline] #[inline]
pub fn intersects(self, other: RectF) -> bool { pub fn intersects(self, other: RectF) -> bool {
// self.origin < other.lower_right && other.origin < self.lower_right // self.origin < other.lower_right && other.origin < self.lower_right
self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() self.0
.concat_xy_xy(other.0)
.packed_lt(other.0.concat_zw_zw(self.0))
.all_true()
} }
#[inline] #[inline]
@ -173,13 +182,19 @@ impl RectF {
} }
#[inline] #[inline]
pub fn dilate<A>(self, amount: A) -> RectF where A: IntoVector2F { pub fn dilate<A>(self, amount: A) -> RectF
where
A: IntoVector2F,
{
let amount = amount.into_vector_2f(); let amount = amount.into_vector_2f();
RectF::from_points(self.origin() - amount, self.lower_right() + amount) RectF::from_points(self.origin() - amount, self.lower_right() + amount)
} }
#[inline] #[inline]
pub fn contract<A>(self, amount: A) -> RectF where A: IntoVector2F { pub fn contract<A>(self, amount: A) -> RectF
where
A: IntoVector2F,
{
let amount = amount.into_vector_2f(); let amount = amount.into_vector_2f();
RectF::from_points(self.origin() + amount, self.lower_right() - amount) RectF::from_points(self.origin() + amount, self.lower_right() - amount)
} }
@ -380,7 +395,10 @@ impl RectI {
#[inline] #[inline]
pub fn intersects(self, other: RectI) -> bool { pub fn intersects(self, other: RectI) -> bool {
// self.origin < other.lower_right && other.origin < self.lower_right // self.origin < other.lower_right && other.origin < self.lower_right
self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() self.0
.concat_xy_xy(other.0)
.packed_lt(other.0.concat_zw_zw(self.0))
.all_true()
} }
#[inline] #[inline]

View File

@ -14,7 +14,7 @@ use crate::line_segment::LineSegment2F;
use crate::rect::RectF; use crate::rect::RectF;
use crate::transform3d::Transform4F; use crate::transform3d::Transform4F;
use crate::unit_vector::UnitVector; use crate::unit_vector::UnitVector;
use crate::vector::{IntoVector2F, Vector2F, vec2f}; use crate::vector::{vec2f, IntoVector2F, Vector2F};
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Mul, MulAssign, Sub}; use std::ops::{Mul, MulAssign, Sub};
@ -31,7 +31,10 @@ impl Default for Matrix2x2F {
impl Matrix2x2F { impl Matrix2x2F {
#[inline] #[inline]
pub fn from_scale<S>(scale: S) -> Matrix2x2F where S: IntoVector2F { pub fn from_scale<S>(scale: S) -> Matrix2x2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f(); let scale = scale.into_vector_2f();
Matrix2x2F(F32x4::new(scale.x(), 0.0, 0.0, scale.y())) Matrix2x2F(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
} }
@ -145,7 +148,10 @@ impl Default for Transform2F {
impl Transform2F { impl Transform2F {
#[inline] #[inline]
pub fn from_scale<S>(scale: S) -> Transform2F where S: IntoVector2F { pub fn from_scale<S>(scale: S) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f(); let scale = scale.into_vector_2f();
Transform2F { Transform2F {
matrix: Matrix2x2F::from_scale(scale), matrix: Matrix2x2F::from_scale(scale),
@ -171,12 +177,21 @@ impl Transform2F {
#[inline] #[inline]
pub fn from_translation(vector: Vector2F) -> Transform2F { pub fn from_translation(vector: Vector2F) -> Transform2F {
Transform2F { matrix: Matrix2x2F::default(), vector } Transform2F {
matrix: Matrix2x2F::default(),
vector,
}
} }
#[inline] #[inline]
pub fn from_scale_rotation_translation<S>(scale: S, theta: f32, translation: Vector2F) pub fn from_scale_rotation_translation<S>(
-> Transform2F where S: IntoVector2F { scale: S,
theta: f32,
translation: Vector2F,
) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f(); let scale = scale.into_vector_2f();
let rotation = Transform2F::from_rotation(theta); let rotation = Transform2F::from_rotation(theta);
let translation = Transform2F::from_translation(translation); let translation = Transform2F::from_translation(translation);
@ -261,7 +276,10 @@ impl Transform2F {
} }
#[inline] #[inline]
pub fn scale<S>(&self, scale: S) -> Transform2F where S: IntoVector2F { pub fn scale<S>(&self, scale: S) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f(); let scale = scale.into_vector_2f();
Transform2F::from_scale(scale) * *self Transform2F::from_scale(scale) * *self
} }
@ -294,7 +312,10 @@ impl Transform2F {
pub fn inverse(&self) -> Transform2F { pub fn inverse(&self) -> Transform2F {
let matrix_inv = self.matrix.inverse(); let matrix_inv = self.matrix.inverse();
let vector_inv = -(matrix_inv * self.vector); let vector_inv = -(matrix_inv * self.vector);
Transform2F { matrix: matrix_inv, vector: vector_inv } Transform2F {
matrix: matrix_inv,
vector: vector_inv,
}
} }
} }
@ -329,7 +350,7 @@ impl Mul<RectF> for Transform2F {
type Output = RectF; type Output = RectF;
#[inline] #[inline]
fn mul(self, rect: RectF) -> RectF { fn mul(self, rect: RectF) -> RectF {
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);

View File

@ -74,7 +74,7 @@ impl Transform4F {
c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0), c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0),
c1: F32x4::new(0.0, scale.y(), 0.0, 0.0), c1: F32x4::new(0.0, scale.y(), 0.0, 0.0),
c2: F32x4::new(0.0, 0.0, scale.z(), 0.0), c2: F32x4::new(0.0, 0.0, scale.z(), 0.0),
c3: F32x4::new(0.0, 0.0, 0.0, 1.0), c3: F32x4::new(0.0, 0.0, 0.0, 1.0),
} }
} }
@ -86,7 +86,10 @@ impl Transform4F {
#[inline] #[inline]
pub fn from_translation(mut translation: Vector4F) -> Transform4F { pub fn from_translation(mut translation: Vector4F) -> Transform4F {
translation.set_w(1.0); translation.set_w(1.0);
Transform4F { c3: translation.0, ..Transform4F::default() } Transform4F {
c3: translation.0,
..Transform4F::default()
}
} }
// TODO(pcwalton): Optimize. // TODO(pcwalton): Optimize.
@ -212,11 +215,24 @@ impl Transform4F {
// TODO(pcwalton): Use SIMD. This needs a matrix transpose: // TODO(pcwalton): Use SIMD. This needs a matrix transpose:
// https://fgiesen.wordpress.com/2013/07/09/simd-transposes-1/ // https://fgiesen.wordpress.com/2013/07/09/simd-transposes-1/
let transform = Transform4F::row_major(s.x(), s.y(), s.z(), 0.0, let transform = Transform4F::row_major(
u.x(), u.y(), u.z(), 0.0, s.x(),
minus_f.x(), minus_f.y(), minus_f.z(), 0.0, s.y(),
0.0, 0.0, 0.0, 1.0) * s.z(),
Transform4F::from_translation((-eye).to_4d()); 0.0,
u.x(),
u.y(),
u.z(),
0.0,
minus_f.x(),
minus_f.y(),
minus_f.z(),
0.0,
0.0,
0.0,
0.0,
1.0,
) * Transform4F::from_translation((-eye).to_4d());
transform transform
} }
@ -415,7 +431,7 @@ impl Mul<RectF> for Perspective {
type Output = RectF; type Output = RectF;
#[inline] #[inline]
fn mul(self, rect: RectF) -> RectF { fn mul(self, rect: RectF) -> RectF {
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
@ -425,8 +441,8 @@ impl Mul<RectF> for Perspective {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::vector::Vector4F;
use crate::transform3d::Transform4F; use crate::transform3d::Transform4F;
use crate::vector::Vector4F;
#[test] #[test]
fn test_post_mul() { fn test_post_mul() {

View File

@ -26,14 +26,20 @@ impl UnitVector {
#[inline] #[inline]
pub fn rotate_by(&self, other: UnitVector) -> UnitVector { pub fn rotate_by(&self, other: UnitVector) -> UnitVector {
let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy();
UnitVector(Vector2F::new(products[0] - products[1], products[2] + products[3])) UnitVector(Vector2F::new(
products[0] - products[1],
products[2] + products[3],
))
} }
/// Angle subtraction formula. /// Angle subtraction formula.
#[inline] #[inline]
pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector { pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector {
let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy();
UnitVector(Vector2F::new(products[0] + products[1], products[2] - products[3])) UnitVector(Vector2F::new(
products[0] + products[1],
products[2] - products[3],
))
} }
/// Half angle formula. /// Half angle formula.
@ -41,7 +47,10 @@ impl UnitVector {
pub fn halve_angle(&self) -> UnitVector { pub fn halve_angle(&self) -> UnitVector {
let x = self.0.x(); let x = self.0.x();
let term = F32x2::new(x, -x); let term = F32x2::new(x, -x);
UnitVector(Vector2F((F32x2::splat(0.5) * (F32x2::splat(1.0) + term)).max(F32x2::default()) UnitVector(Vector2F(
.sqrt())) (F32x2::splat(0.5) * (F32x2::splat(1.0) + term))
.max(F32x2::default())
.sqrt(),
))
} }
} }

View File

@ -36,12 +36,20 @@ impl Vector2F {
#[inline] #[inline]
pub fn to_3d(self) -> Vector3F { pub fn to_3d(self) -> Vector3F {
Vector3F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0))) Vector3F(
self.0
.to_f32x4()
.concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0)),
)
} }
#[inline] #[inline]
pub fn to_4d(self) -> Vector4F { pub fn to_4d(self) -> Vector4F {
Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0))) Vector4F(
self.0
.to_f32x4()
.concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0)),
)
} }
#[inline] #[inline]
@ -294,9 +302,9 @@ impl Neg for Vector2F {
} }
/// Either a scalar or a `Vector2F`. /// Either a scalar or a `Vector2F`.
/// ///
/// Scalars will be automatically splatted (i.e. `x` becomes `vec2f(x, x)`). /// Scalars will be automatically splatted (i.e. `x` becomes `vec2f(x, x)`).
/// ///
/// Be judicious with the use of this trait. Only use it if it will aid readability without the /// Be judicious with the use of this trait. Only use it if it will aid readability without the
/// potential to introduce bugs. /// potential to introduce bugs.
pub trait IntoVector2F { pub trait IntoVector2F {
@ -458,7 +466,10 @@ impl Eq for Vector2I {}
impl Hash for Vector2I { impl Hash for Vector2I {
#[inline] #[inline]
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.x().hash(state); self.x().hash(state);
self.y().hash(state); self.y().hash(state);
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@
//! GPU memory management. //! GPU memory management.
use crate::{BufferData, BufferTarget, BufferUploadMode, Device, TextureFormat}; use crate::{BufferData, BufferTarget, BufferUploadMode, Device, TextureFormat};
use instant::Instant;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use instant::Instant;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::default::Default; use std::default::Default;
@ -31,7 +31,10 @@ const DECAY_TIME: f32 = 0.250;
// This helps avoid stalls. This is admittedly a bit of a hack. // This helps avoid stalls. This is admittedly a bit of a hack.
const REUSE_TIME: f32 = 0.015; const REUSE_TIME: f32 = 0.015;
pub struct GPUMemoryAllocator<D> where D: Device { pub struct GPUMemoryAllocator<D>
where
D: Device,
{
general_buffers_in_use: FxHashMap<GeneralBufferID, BufferAllocation<D>>, general_buffers_in_use: FxHashMap<GeneralBufferID, BufferAllocation<D>>,
index_buffers_in_use: FxHashMap<IndexBufferID, BufferAllocation<D>>, index_buffers_in_use: FxHashMap<IndexBufferID, BufferAllocation<D>>,
textures_in_use: FxHashMap<TextureID, TextureAllocation<D>>, textures_in_use: FxHashMap<TextureID, TextureAllocation<D>>,
@ -45,34 +48,61 @@ pub struct GPUMemoryAllocator<D> where D: Device {
bytes_allocated: u64, bytes_allocated: u64,
} }
struct BufferAllocation<D> where D: Device { struct BufferAllocation<D>
where
D: Device,
{
buffer: D::Buffer, buffer: D::Buffer,
size: u64, size: u64,
tag: BufferTag, tag: BufferTag,
} }
struct TextureAllocation<D> where D: Device { struct TextureAllocation<D>
where
D: Device,
{
texture: D::Texture, texture: D::Texture,
descriptor: TextureDescriptor, descriptor: TextureDescriptor,
tag: TextureTag, tag: TextureTag,
} }
struct FramebufferAllocation<D> where D: Device { struct FramebufferAllocation<D>
where
D: Device,
{
framebuffer: D::Framebuffer, framebuffer: D::Framebuffer,
descriptor: TextureDescriptor, descriptor: TextureDescriptor,
tag: FramebufferTag, tag: FramebufferTag,
} }
struct FreeObject<D> where D: Device { struct FreeObject<D>
where
D: Device,
{
timestamp: Instant, timestamp: Instant,
kind: FreeObjectKind<D>, kind: FreeObjectKind<D>,
} }
enum FreeObjectKind<D> where D: Device { enum FreeObjectKind<D>
GeneralBuffer { id: GeneralBufferID, allocation: BufferAllocation<D> }, where
IndexBuffer { id: IndexBufferID, allocation: BufferAllocation<D> }, D: Device,
Texture { id: TextureID, allocation: TextureAllocation<D> }, {
Framebuffer { id: FramebufferID, allocation: FramebufferAllocation<D> }, GeneralBuffer {
id: GeneralBufferID,
allocation: BufferAllocation<D>,
},
IndexBuffer {
id: IndexBufferID,
allocation: BufferAllocation<D>,
},
Texture {
id: TextureID,
allocation: TextureAllocation<D>,
},
Framebuffer {
id: FramebufferID,
allocation: FramebufferAllocation<D>,
},
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -108,7 +138,10 @@ pub struct TextureTag(pub &'static str);
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct FramebufferTag(pub &'static str); pub struct FramebufferTag(pub &'static str);
impl<D> GPUMemoryAllocator<D> where D: Device { impl<D> GPUMemoryAllocator<D>
where
D: Device,
{
pub fn new() -> GPUMemoryAllocator<D> { pub fn new() -> GPUMemoryAllocator<D> {
GPUMemoryAllocator { GPUMemoryAllocator {
general_buffers_in_use: FxHashMap::default(), general_buffers_in_use: FxHashMap::default(),
@ -125,8 +158,12 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
} }
pub fn allocate_general_buffer<T>(&mut self, device: &D, size: u64, tag: BufferTag) pub fn allocate_general_buffer<T>(
-> GeneralBufferID { &mut self,
device: &D,
size: u64,
tag: BufferTag,
) -> GeneralBufferID {
let mut byte_size = size * mem::size_of::<T>() as u64; let mut byte_size = size * mem::size_of::<T>() as u64;
if byte_size < MAX_BUFFER_SIZE_CLASS { if byte_size < MAX_BUFFER_SIZE_CLASS {
byte_size = byte_size.next_power_of_two(); byte_size = byte_size.next_power_of_two();
@ -139,8 +176,8 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
FreeObject { FreeObject {
ref timestamp, ref timestamp,
kind: FreeObjectKind::GeneralBuffer { ref allocation, .. }, kind: FreeObjectKind::GeneralBuffer { ref allocation, .. },
} if allocation.size == byte_size && } if allocation.size == byte_size
(now - *timestamp).as_secs_f32() >= REUSE_TIME => {} && (now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
_ => continue, _ => continue,
} }
@ -148,9 +185,7 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
Some(FreeObject { Some(FreeObject {
kind: FreeObjectKind::GeneralBuffer { id, allocation }, kind: FreeObjectKind::GeneralBuffer { id, allocation },
.. ..
}) => { }) => (id, allocation),
(id, allocation)
}
_ => unreachable!(), _ => unreachable!(),
}; };
@ -161,29 +196,44 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
let buffer = device.create_buffer(BufferUploadMode::Dynamic); let buffer = device.create_buffer(BufferUploadMode::Dynamic);
device.allocate_buffer::<u8>(&buffer, device.allocate_buffer::<u8>(
BufferData::Uninitialized(byte_size as usize), &buffer,
BufferTarget::Vertex); BufferData::Uninitialized(byte_size as usize),
BufferTarget::Vertex,
);
let id = self.next_general_buffer_id; let id = self.next_general_buffer_id;
self.next_general_buffer_id.0 += 1; self.next_general_buffer_id.0 += 1;
debug!("mapping general buffer: {:?} {} ({}x{}) {:?}", debug!(
id, "mapping general buffer: {:?} {} ({}x{}) {:?}",
byte_size, id,
size, byte_size,
mem::size_of::<T>(), size,
tag); mem::size_of::<T>(),
tag
);
self.general_buffers_in_use.insert(id, BufferAllocation { buffer, size: byte_size, tag }); self.general_buffers_in_use.insert(
id,
BufferAllocation {
buffer,
size: byte_size,
tag,
},
);
self.bytes_allocated += byte_size; self.bytes_allocated += byte_size;
self.bytes_committed += byte_size; self.bytes_committed += byte_size;
id id
} }
pub fn allocate_index_buffer<T>(&mut self, device: &D, size: u64, tag: BufferTag) pub fn allocate_index_buffer<T>(
-> IndexBufferID { &mut self,
device: &D,
size: u64,
tag: BufferTag,
) -> IndexBufferID {
let mut byte_size = size * mem::size_of::<T>() as u64; let mut byte_size = size * mem::size_of::<T>() as u64;
if byte_size < MAX_BUFFER_SIZE_CLASS { if byte_size < MAX_BUFFER_SIZE_CLASS {
byte_size = byte_size.next_power_of_two(); byte_size = byte_size.next_power_of_two();
@ -196,15 +246,16 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
FreeObject { FreeObject {
ref timestamp, ref timestamp,
kind: FreeObjectKind::IndexBuffer { ref allocation, .. }, kind: FreeObjectKind::IndexBuffer { ref allocation, .. },
} if allocation.size == byte_size && } if allocation.size == byte_size
(now - *timestamp).as_secs_f32() >= REUSE_TIME => {} && (now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
_ => continue, _ => continue,
} }
let (id, mut allocation) = match self.free_objects.remove(free_object_index) { let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::IndexBuffer { id, allocation }, .. }) => { Some(FreeObject {
(id, allocation) kind: FreeObjectKind::IndexBuffer { id, allocation },
} ..
}) => (id, allocation),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -215,33 +266,45 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
let buffer = device.create_buffer(BufferUploadMode::Dynamic); let buffer = device.create_buffer(BufferUploadMode::Dynamic);
device.allocate_buffer::<u8>(&buffer, device.allocate_buffer::<u8>(
BufferData::Uninitialized(byte_size as usize), &buffer,
BufferTarget::Index); BufferData::Uninitialized(byte_size as usize),
BufferTarget::Index,
);
let id = self.next_index_buffer_id; let id = self.next_index_buffer_id;
self.next_index_buffer_id.0 += 1; self.next_index_buffer_id.0 += 1;
debug!("mapping index buffer: {:?} {} ({}x{}) {:?}", debug!(
id, "mapping index buffer: {:?} {} ({}x{}) {:?}",
byte_size, id,
size, byte_size,
mem::size_of::<T>(), size,
tag); mem::size_of::<T>(),
tag
);
self.index_buffers_in_use.insert(id, BufferAllocation { buffer, size: byte_size, tag }); self.index_buffers_in_use.insert(
id,
BufferAllocation {
buffer,
size: byte_size,
tag,
},
);
self.bytes_allocated += byte_size; self.bytes_allocated += byte_size;
self.bytes_committed += byte_size; self.bytes_committed += byte_size;
id id
} }
pub fn allocate_texture(&mut self, pub fn allocate_texture(
device: &D, &mut self,
size: Vector2I, device: &D,
format: TextureFormat, size: Vector2I,
tag: TextureTag) format: TextureFormat,
-> TextureID { tag: TextureTag,
) -> TextureID {
let descriptor = TextureDescriptor { let descriptor = TextureDescriptor {
width: size.x() as u32, width: size.x() as u32,
height: size.y() as u32, height: size.y() as u32,
@ -251,15 +314,18 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
for free_object_index in 0..self.free_objects.len() { for free_object_index in 0..self.free_objects.len() {
match self.free_objects[free_object_index] { match self.free_objects[free_object_index] {
FreeObject { kind: FreeObjectKind::Texture { ref allocation, .. }, .. } if FreeObject {
allocation.descriptor == descriptor => {} kind: FreeObjectKind::Texture { ref allocation, .. },
..
} if allocation.descriptor == descriptor => {}
_ => continue, _ => continue,
} }
let (id, mut allocation) = match self.free_objects.remove(free_object_index) { let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::Texture { id, allocation }, .. }) => { Some(FreeObject {
(id, allocation) kind: FreeObjectKind::Texture { id, allocation },
} ..
}) => (id, allocation),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -275,7 +341,14 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let id = self.next_texture_id; let id = self.next_texture_id;
self.next_texture_id.0 += 1; self.next_texture_id.0 += 1;
self.textures_in_use.insert(id, TextureAllocation { texture, descriptor, tag }); self.textures_in_use.insert(
id,
TextureAllocation {
texture,
descriptor,
tag,
},
);
self.bytes_allocated += byte_size; self.bytes_allocated += byte_size;
self.bytes_committed += byte_size; self.bytes_committed += byte_size;
@ -283,12 +356,13 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
id id
} }
pub fn allocate_framebuffer(&mut self, pub fn allocate_framebuffer(
device: &D, &mut self,
size: Vector2I, device: &D,
format: TextureFormat, size: Vector2I,
tag: FramebufferTag) format: TextureFormat,
-> FramebufferID { tag: FramebufferTag,
) -> FramebufferID {
let descriptor = TextureDescriptor { let descriptor = TextureDescriptor {
width: size.x() as u32, width: size.x() as u32,
height: size.y() as u32, height: size.y() as u32,
@ -298,15 +372,16 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
for free_object_index in 0..self.free_objects.len() { for free_object_index in 0..self.free_objects.len() {
match self.free_objects[free_object_index].kind { match self.free_objects[free_object_index].kind {
FreeObjectKind::Framebuffer { ref allocation, .. } if allocation.descriptor == FreeObjectKind::Framebuffer { ref allocation, .. }
descriptor => {} if allocation.descriptor == descriptor => {}
_ => continue, _ => continue,
} }
let (id, mut allocation) = match self.free_objects.remove(free_object_index) { let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::Framebuffer { id, allocation }, .. }) => { Some(FreeObject {
(id, allocation) kind: FreeObjectKind::Framebuffer { id, allocation },
} ..
}) => (id, allocation),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -323,11 +398,14 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let id = self.next_framebuffer_id; let id = self.next_framebuffer_id;
self.next_framebuffer_id.0 += 1; self.next_framebuffer_id.0 += 1;
self.framebuffers_in_use.insert(id, FramebufferAllocation { self.framebuffers_in_use.insert(
framebuffer, id,
descriptor, FramebufferAllocation {
tag, framebuffer,
}); descriptor,
tag,
},
);
self.bytes_allocated += byte_size; self.bytes_allocated += byte_size;
self.bytes_committed += byte_size; self.bytes_committed += byte_size;
@ -339,8 +417,8 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let now = Instant::now(); let now = Instant::now();
loop { loop {
match self.free_objects.front() { match self.free_objects.front() {
Some(FreeObject { timestamp, .. }) if (now - *timestamp).as_secs_f32() >= Some(FreeObject { timestamp, .. })
DECAY_TIME => {} if (now - *timestamp).as_secs_f32() >= DECAY_TIME => {}
_ => break, _ => break,
} }
match self.free_objects.pop_front() { match self.free_objects.pop_front() {
@ -352,15 +430,24 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
debug!("purging general buffer: {}", allocation.size); debug!("purging general buffer: {}", allocation.size);
self.bytes_allocated -= allocation.size; self.bytes_allocated -= allocation.size;
} }
Some(FreeObject { kind: FreeObjectKind::IndexBuffer { allocation, .. }, .. }) => { Some(FreeObject {
kind: FreeObjectKind::IndexBuffer { allocation, .. },
..
}) => {
debug!("purging index buffer: {}", allocation.size); debug!("purging index buffer: {}", allocation.size);
self.bytes_allocated -= allocation.size; self.bytes_allocated -= allocation.size;
} }
Some(FreeObject { kind: FreeObjectKind::Texture { allocation, .. }, .. }) => { Some(FreeObject {
kind: FreeObjectKind::Texture { allocation, .. },
..
}) => {
debug!("purging texture: {:?}", allocation.descriptor); debug!("purging texture: {:?}", allocation.descriptor);
self.bytes_allocated -= allocation.descriptor.byte_size(); self.bytes_allocated -= allocation.descriptor.byte_size();
} }
Some(FreeObject { kind: FreeObjectKind::Framebuffer { allocation, .. }, .. }) => { Some(FreeObject {
kind: FreeObjectKind::Framebuffer { allocation, .. },
..
}) => {
debug!("purging framebuffer: {:?}", allocation.descriptor); debug!("purging framebuffer: {:?}", allocation.descriptor);
self.bytes_allocated -= allocation.descriptor.byte_size(); self.bytes_allocated -= allocation.descriptor.byte_size();
} }
@ -369,9 +456,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
pub fn free_general_buffer(&mut self, id: GeneralBufferID) { pub fn free_general_buffer(&mut self, id: GeneralBufferID) {
let allocation = self.general_buffers_in_use let allocation = self
.remove(&id) .general_buffers_in_use
.expect("Attempted to free unallocated general buffer!"); .remove(&id)
.expect("Attempted to free unallocated general buffer!");
self.bytes_committed -= allocation.size; self.bytes_committed -= allocation.size;
self.free_objects.push_back(FreeObject { self.free_objects.push_back(FreeObject {
timestamp: Instant::now(), timestamp: Instant::now(),
@ -380,9 +468,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
pub fn free_index_buffer(&mut self, id: IndexBufferID) { pub fn free_index_buffer(&mut self, id: IndexBufferID) {
let allocation = self.index_buffers_in_use let allocation = self
.remove(&id) .index_buffers_in_use
.expect("Attempted to free unallocated index buffer!"); .remove(&id)
.expect("Attempted to free unallocated index buffer!");
self.bytes_committed -= allocation.size; self.bytes_committed -= allocation.size;
self.free_objects.push_back(FreeObject { self.free_objects.push_back(FreeObject {
timestamp: Instant::now(), timestamp: Instant::now(),
@ -391,9 +480,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
pub fn free_texture(&mut self, id: TextureID) { pub fn free_texture(&mut self, id: TextureID) {
let allocation = self.textures_in_use let allocation = self
.remove(&id) .textures_in_use
.expect("Attempted to free unallocated texture!"); .remove(&id)
.expect("Attempted to free unallocated texture!");
let byte_size = allocation.descriptor.byte_size(); let byte_size = allocation.descriptor.byte_size();
self.bytes_committed -= byte_size; self.bytes_committed -= byte_size;
self.free_objects.push_back(FreeObject { self.free_objects.push_back(FreeObject {
@ -403,9 +493,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
} }
pub fn free_framebuffer(&mut self, id: FramebufferID) { pub fn free_framebuffer(&mut self, id: FramebufferID) {
let allocation = self.framebuffers_in_use let allocation = self
.remove(&id) .framebuffers_in_use
.expect("Attempted to free unallocated framebuffer!"); .remove(&id)
.expect("Attempted to free unallocated framebuffer!");
let byte_size = allocation.descriptor.byte_size(); let byte_size = allocation.descriptor.byte_size();
self.bytes_committed -= byte_size; self.bytes_committed -= byte_size;
self.free_objects.push_back(FreeObject { self.free_objects.push_back(FreeObject {
@ -450,7 +541,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort(); ids.sort();
for id in ids { for id in ids {
let allocation = &self.general_buffers_in_use[&id]; let allocation = &self.general_buffers_in_use[&id];
println!("id {:?}: {:?} ({:?} B)", id, allocation.tag, allocation.size); println!(
"id {:?}: {:?} ({:?} B)",
id, allocation.tag, allocation.size
);
} }
println!("Index buffers:"); println!("Index buffers:");
@ -458,7 +552,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort(); ids.sort();
for id in ids { for id in ids {
let allocation = &self.index_buffers_in_use[&id]; let allocation = &self.index_buffers_in_use[&id];
println!("id {:?}: {:?} ({:?} B)", id, allocation.tag, allocation.size); println!(
"id {:?}: {:?} ({:?} B)",
id, allocation.tag, allocation.size
);
} }
println!("Textures:"); println!("Textures:");
@ -466,13 +563,15 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort(); ids.sort();
for id in ids { for id in ids {
let allocation = &self.textures_in_use[&id]; let allocation = &self.textures_in_use[&id];
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)", println!(
id, "id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
allocation.tag, id,
allocation.descriptor.width, allocation.tag,
allocation.descriptor.height, allocation.descriptor.width,
allocation.descriptor.format, allocation.descriptor.height,
allocation.descriptor.byte_size()); allocation.descriptor.format,
allocation.descriptor.byte_size()
);
} }
println!("Framebuffers:"); println!("Framebuffers:");
@ -480,13 +579,15 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort(); ids.sort();
for id in ids { for id in ids {
let allocation = &self.framebuffers_in_use[&id]; let allocation = &self.framebuffers_in_use[&id];
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)", println!(
id, "id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
allocation.tag, id,
allocation.descriptor.width, allocation.tag,
allocation.descriptor.height, allocation.descriptor.width,
allocation.descriptor.format, allocation.descriptor.height,
allocation.descriptor.byte_size()); allocation.descriptor.format,
allocation.descriptor.byte_size()
);
} }
} }
} }

View File

@ -22,7 +22,7 @@ use image::ImageFormat;
use pathfinder_color::ColorF; use pathfinder_color::ColorF;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::{F32x2, F32x4, I32x2}; use pathfinder_simd::default::{F32x2, F32x4, I32x2};
use std::ops::Range; use std::ops::Range;
@ -50,64 +50,95 @@ pub trait Device: Sized {
fn device_name(&self) -> String; fn device_name(&self) -> String;
fn feature_level(&self) -> FeatureLevel; fn feature_level(&self) -> FeatureLevel;
fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture; fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture;
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef) fn create_texture_from_data(
-> Self::Texture; &self,
fn create_shader(&self, resources: &dyn ResourceLoader, name: &str, kind: ShaderKind) format: TextureFormat,
-> Self::Shader; size: Vector2I,
fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) data: TextureDataRef,
-> Self::Shader; ) -> Self::Texture;
fn create_shader(
&self,
resources: &dyn ResourceLoader,
name: &str,
kind: ShaderKind,
) -> Self::Shader;
fn create_shader_from_source(
&self,
name: &str,
source: &[u8],
kind: ShaderKind,
) -> Self::Shader;
fn create_vertex_array(&self) -> Self::VertexArray; fn create_vertex_array(&self) -> Self::VertexArray;
fn create_program_from_shaders(&self, fn create_program_from_shaders(
resources: &dyn ResourceLoader, &self,
name: &str, resources: &dyn ResourceLoader,
shaders: ProgramKind<Self::Shader>) name: &str,
-> Self::Program; shaders: ProgramKind<Self::Shader>,
fn set_compute_program_local_size(&self, ) -> Self::Program;
program: &mut Self::Program, fn set_compute_program_local_size(
local_size: ComputeDimensions); &self,
program: &mut Self::Program,
local_size: ComputeDimensions,
);
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option<Self::VertexAttr>; fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option<Self::VertexAttr>;
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform; fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
fn get_texture_parameter(&self, program: &Self::Program, name: &str) -> Self::TextureParameter; fn get_texture_parameter(&self, program: &Self::Program, name: &str) -> Self::TextureParameter;
fn get_image_parameter(&self, program: &Self::Program, name: &str) -> Self::ImageParameter; fn get_image_parameter(&self, program: &Self::Program, name: &str) -> Self::ImageParameter;
fn get_storage_buffer(&self, program: &Self::Program, name: &str, binding: u32) fn get_storage_buffer(
-> Self::StorageBuffer; &self,
fn bind_buffer(&self, program: &Self::Program,
vertex_array: &Self::VertexArray, name: &str,
buffer: &Self::Buffer, binding: u32,
target: BufferTarget); ) -> Self::StorageBuffer;
fn configure_vertex_attr(&self, fn bind_buffer(
vertex_array: &Self::VertexArray, &self,
attr: &Self::VertexAttr, vertex_array: &Self::VertexArray,
descriptor: &VertexAttrDescriptor); buffer: &Self::Buffer,
target: BufferTarget,
);
fn configure_vertex_attr(
&self,
vertex_array: &Self::VertexArray,
attr: &Self::VertexAttr,
descriptor: &VertexAttrDescriptor,
);
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer; fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer; fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer;
fn allocate_buffer<T>(&self, fn allocate_buffer<T>(&self, buffer: &Self::Buffer, data: BufferData<T>, target: BufferTarget);
buffer: &Self::Buffer, fn upload_to_buffer<T>(
data: BufferData<T>, &self,
target: BufferTarget); buffer: &Self::Buffer,
fn upload_to_buffer<T>(&self, position: usize,
buffer: &Self::Buffer, data: &[T],
position: usize, target: BufferTarget,
data: &[T], );
target: BufferTarget);
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture; fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture; fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture;
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat; fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
fn texture_size(&self, texture: &Self::Texture) -> Vector2I; fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags); fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags);
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef); fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI) fn read_pixels(
-> Self::TextureDataReceiver; &self,
fn read_buffer(&self, buffer: &Self::Buffer, target: BufferTarget, range: Range<usize>) target: &RenderTarget<Self>,
-> Self::BufferDataReceiver; viewport: RectI,
) -> Self::TextureDataReceiver;
fn read_buffer(
&self,
buffer: &Self::Buffer,
target: BufferTarget,
range: Range<usize>,
) -> Self::BufferDataReceiver;
fn begin_commands(&self); fn begin_commands(&self);
fn end_commands(&self); fn end_commands(&self);
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>); fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>);
fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>); fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>);
fn draw_elements_instanced(&self, fn draw_elements_instanced(
index_count: u32, &self,
instance_count: u32, index_count: u32,
render_state: &RenderState<Self>); instance_count: u32,
render_state: &RenderState<Self>,
);
fn dispatch_compute(&self, dimensions: ComputeDimensions, state: &ComputeState<Self>); fn dispatch_compute(&self, dimensions: ComputeDimensions, state: &ComputeState<Self>);
fn add_fence(&self) -> Self::Fence; fn add_fence(&self) -> Self::Fence;
fn wait_for_fence(&self, fence: &Self::Fence); fn wait_for_fence(&self, fence: &Self::Fence);
@ -121,11 +152,12 @@ pub trait Device: Sized {
fn try_recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Option<Vec<u8>>; fn try_recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Option<Vec<u8>>;
fn recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Vec<u8>; fn recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Vec<u8>;
fn create_texture_from_png(&self, fn create_texture_from_png(
resources: &dyn ResourceLoader, &self,
name: &str, resources: &dyn ResourceLoader,
format: TextureFormat) name: &str,
-> Self::Texture { format: TextureFormat,
) -> Self::Texture {
let data = resources.slurp(&format!("textures/{}.png", name)).unwrap(); let data = resources.slurp(&format!("textures/{}.png", name)).unwrap();
let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap(); let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap();
match format { match format {
@ -143,11 +175,13 @@ pub trait Device: Sized {
} }
} }
fn upload_png_to_texture(&self, fn upload_png_to_texture(
resources: &dyn ResourceLoader, &self,
name: &str, resources: &dyn ResourceLoader,
texture: &Self::Texture, name: &str,
format: TextureFormat) { texture: &Self::Texture,
format: TextureFormat,
) {
let data = resources.slurp(&format!("textures/{}.png", name)).unwrap(); let data = resources.slurp(&format!("textures/{}.png", name)).unwrap();
let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap(); let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap();
match format { match format {
@ -174,12 +208,10 @@ pub trait Device: Sized {
shader_names: ProgramKind<&str>, shader_names: ProgramKind<&str>,
) -> Self::Program { ) -> Self::Program {
let shaders = match shader_names { let shaders = match shader_names {
ProgramKind::Raster { vertex, fragment } => { ProgramKind::Raster { vertex, fragment } => ProgramKind::Raster {
ProgramKind::Raster { vertex: self.create_shader(resources, vertex, ShaderKind::Vertex),
vertex: self.create_shader(resources, vertex, ShaderKind::Vertex), fragment: self.create_shader(resources, fragment, ShaderKind::Fragment),
fragment: self.create_shader(resources, fragment, ShaderKind::Fragment), },
}
}
ProgramKind::Compute(compute) => { ProgramKind::Compute(compute) => {
ProgramKind::Compute(self.create_shader(resources, compute, ShaderKind::Compute)) ProgramKind::Compute(self.create_shader(resources, compute, ShaderKind::Compute))
} }
@ -188,7 +220,10 @@ pub trait Device: Sized {
} }
fn create_raster_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program { fn create_raster_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program {
let shaders = ProgramKind::Raster { vertex: name, fragment: name }; let shaders = ProgramKind::Raster {
vertex: name,
fragment: name,
};
self.create_program_from_shader_names(resources, name, shaders) self.create_program_from_shader_names(resources, name, shaders)
} }
@ -253,10 +288,7 @@ pub enum ShaderKind {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum ProgramKind<T> { pub enum ProgramKind<T> {
Raster { Raster { vertex: T, fragment: T },
vertex: T,
fragment: T,
},
Compute(T), Compute(T),
} }
@ -287,7 +319,10 @@ pub enum Primitive {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct RenderState<'a, D> where D: Device { pub struct RenderState<'a, D>
where
D: Device,
{
pub target: &'a RenderTarget<'a, D>, pub target: &'a RenderTarget<'a, D>,
pub program: &'a D::Program, pub program: &'a D::Program,
pub vertex_array: &'a D::VertexArray, pub vertex_array: &'a D::VertexArray,
@ -301,7 +336,10 @@ pub struct RenderState<'a, D> where D: Device {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ComputeState<'a, D> where D: Device { pub struct ComputeState<'a, D>
where
D: Device,
{
pub program: &'a D::Program, pub program: &'a D::Program,
pub uniforms: &'a [UniformBinding<'a, D::Uniform>], pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>], pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
@ -332,7 +370,10 @@ pub struct ClearOps {
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum RenderTarget<'a, D> where D: Device { pub enum RenderTarget<'a, D>
where
D: Device,
{
Default, Default,
Framebuffer(&'a D::Framebuffer), Framebuffer(&'a D::Framebuffer),
} }
@ -537,8 +578,11 @@ pub enum ImageAccess {
impl<'a> TextureDataRef<'a> { impl<'a> TextureDataRef<'a> {
#[doc(hidden)] #[doc(hidden)]
pub fn check_and_extract_data_ptr(self, minimum_size: Vector2I, format: TextureFormat) pub fn check_and_extract_data_ptr(
-> *const c_void { self,
minimum_size: Vector2I,
format: TextureFormat,
) -> *const c_void {
let channels = match (format, self) { let channels = match (format, self) {
(TextureFormat::R8, TextureDataRef::U8(_)) => 1, (TextureFormat::R8, TextureDataRef::U8(_)) => 1,
(TextureFormat::RGBA8, TextureDataRef::U8(_)) => 4, (TextureFormat::RGBA8, TextureDataRef::U8(_)) => 4,

View File

@ -307,7 +307,10 @@ pub struct ShapeKeyframeProperty {
impl Lottie { impl Lottie {
#[inline] #[inline]
pub fn from_reader<R>(reader: R) -> Result<Lottie, JSONError> where R: Read { pub fn from_reader<R>(reader: R) -> Result<Lottie, JSONError>
where
R: Read,
{
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
use crate::gpu_data::{TextureLocation, TexturePageId}; use crate::gpu_data::{TextureLocation, TexturePageId};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
const ATLAS_TEXTURE_LENGTH: u32 = 1024; const ATLAS_TEXTURE_LENGTH: u32 = 1024;
@ -64,9 +64,10 @@ impl TextureAllocator {
pub fn allocate(&mut self, requested_size: Vector2I, mode: AllocationMode) -> TextureLocation { pub fn allocate(&mut self, requested_size: Vector2I, mode: AllocationMode) -> TextureLocation {
// If requested, or if the image is too big, use a separate page. // If requested, or if the image is too big, use a separate page.
if mode == AllocationMode::OwnPage || if mode == AllocationMode::OwnPage
requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 || || requested_size.x() > ATLAS_TEXTURE_LENGTH as i32
requested_size.y() > ATLAS_TEXTURE_LENGTH as i32 { || requested_size.y() > ATLAS_TEXTURE_LENGTH as i32
{
return self.allocate_image(requested_size); return self.allocate_image(requested_size);
} }
@ -74,19 +75,17 @@ impl TextureAllocator {
let mut first_free_page_index = self.pages.len(); let mut first_free_page_index = self.pages.len();
for (page_index, page) in self.pages.iter_mut().enumerate() { for (page_index, page) in self.pages.iter_mut().enumerate() {
match *page { match *page {
Some(ref mut page) => { Some(ref mut page) => match page.allocator {
match page.allocator { TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::Image { .. } => {} TexturePageAllocator::Atlas(ref mut allocator) => {
TexturePageAllocator::Atlas(ref mut allocator) => { if let Some(rect) = allocator.allocate(requested_size) {
if let Some(rect) = allocator.allocate(requested_size) { return TextureLocation {
return TextureLocation { page: TexturePageId(page_index as u32),
page: TexturePageId(page_index as u32), rect,
rect };
};
}
} }
} }
} },
None => first_free_page_index = first_free_page_index.min(page_index), None => first_free_page_index = first_free_page_index.min(page_index),
} }
} }
@ -94,7 +93,9 @@ impl TextureAllocator {
// Add a new atlas. // Add a new atlas.
let page = self.get_first_free_page_id(); let page = self.get_first_free_page_id();
let mut allocator = TextureAtlasAllocator::new(); let mut allocator = TextureAtlasAllocator::new();
let rect = allocator.allocate(requested_size).expect("Allocation failed!"); let rect = allocator
.allocate(requested_size)
.expect("Allocation failed!");
while (page.0 as usize) >= self.pages.len() { while (page.0 as usize) >= self.pages.len() {
self.pages.push(None); self.pages.push(None);
} }
@ -130,9 +131,10 @@ impl TextureAllocator {
pub fn free(&mut self, location: TextureLocation) { pub fn free(&mut self, location: TextureLocation) {
//println!("free({:?})", location); //println!("free({:?})", location);
match self.pages[location.page.0 as usize] match self.pages[location.page.0 as usize]
.as_mut() .as_mut()
.expect("Texture page is not allocated!") .expect("Texture page is not allocated!")
.allocator { .allocator
{
TexturePageAllocator::Image { size } => { TexturePageAllocator::Image { size } => {
debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size)); debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size));
} }
@ -151,7 +153,11 @@ impl TextureAllocator {
} }
pub fn page_size(&self, page_id: TexturePageId) -> Vector2I { pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
match self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").allocator { match self.pages[page_id.0 as usize]
.as_ref()
.expect("No such texture page!")
.allocator
{
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
TexturePageAllocator::Image { size, .. } => size, TexturePageAllocator::Image { size, .. } => size,
} }
@ -162,7 +168,10 @@ impl TextureAllocator {
} }
pub fn page_is_new(&self, page_id: TexturePageId) -> bool { pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").is_new self.pages[page_id.0 as usize]
.as_ref()
.expect("No such texture page!")
.is_new
} }
pub fn mark_all_pages_as_allocated(&mut self) { pub fn mark_all_pages_as_allocated(&mut self) {
@ -178,7 +187,10 @@ impl TextureAllocator {
while first_index < self.pages.len() && self.pages[first_index].is_none() { while first_index < self.pages.len() && self.pages[first_index].is_none() {
first_index += 1; first_index += 1;
} }
TexturePageIter { allocator: self, next_index: first_index } TexturePageIter {
allocator: self,
next_index: first_index,
}
} }
} }
@ -190,20 +202,29 @@ impl TextureAtlasAllocator {
#[inline] #[inline]
fn with_length(length: u32) -> TextureAtlasAllocator { fn with_length(length: u32) -> TextureAtlasAllocator {
TextureAtlasAllocator { root: TreeNode::EmptyLeaf, size: length } TextureAtlasAllocator {
root: TreeNode::EmptyLeaf,
size: length,
}
} }
#[inline] #[inline]
fn allocate(&mut self, requested_size: Vector2I) -> Option<RectI> { fn allocate(&mut self, requested_size: Vector2I) -> Option<RectI> {
let requested_length = let requested_length =
(requested_size.x().max(requested_size.y()) as u32).next_power_of_two(); (requested_size.x().max(requested_size.y()) as u32).next_power_of_two();
self.root.allocate(Vector2I::default(), self.size, requested_length) self.root
.allocate(Vector2I::default(), self.size, requested_length)
} }
#[inline] #[inline]
fn free(&mut self, rect: RectI) { fn free(&mut self, rect: RectI) {
let requested_length = rect.width() as u32; let requested_length = rect.width() as u32;
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length) self.root.free(
Vector2I::default(),
self.size,
rect.origin(),
requested_length,
)
} }
#[inline] #[inline]
@ -218,8 +239,12 @@ impl TextureAtlasAllocator {
impl TreeNode { impl TreeNode {
// Invariant: `requested_size` must be a power of two. // Invariant: `requested_size` must be a power of two.
fn allocate(&mut self, this_origin: Vector2I, this_size: u32, requested_size: u32) fn allocate(
-> Option<RectI> { &mut self,
this_origin: Vector2I,
this_size: u32,
requested_size: u32,
) -> Option<RectI> {
if let TreeNode::FullLeaf = *self { if let TreeNode::FullLeaf = *self {
// No room here. // No room here.
return None; return None;
@ -253,19 +278,23 @@ impl TreeNode {
if let Some(origin) = kids[0].allocate(this_origin, kid_size, requested_size) { if let Some(origin) = kids[0].allocate(this_origin, kid_size, requested_size) {
return Some(origin); return Some(origin);
} }
if let Some(origin) = kids[1].allocate(this_origin + vec2i(kid_size as i32, 0), if let Some(origin) = kids[1].allocate(
kid_size, this_origin + vec2i(kid_size as i32, 0),
requested_size) { kid_size,
requested_size,
) {
return Some(origin); return Some(origin);
} }
if let Some(origin) = kids[2].allocate(this_origin + vec2i(0, kid_size as i32), if let Some(origin) = kids[2].allocate(
kid_size, this_origin + vec2i(0, kid_size as i32),
requested_size) { kid_size,
requested_size,
) {
return Some(origin); return Some(origin);
} }
if let Some(origin) = kids[3].allocate(this_origin + kid_size as i32, if let Some(origin) =
kid_size, kids[3].allocate(this_origin + kid_size as i32, kid_size, requested_size)
requested_size) { {
return Some(origin); return Some(origin);
} }
@ -277,11 +306,13 @@ impl TreeNode {
} }
#[allow(dead_code)] #[allow(dead_code)]
fn free(&mut self, fn free(
this_origin: Vector2I, &mut self,
this_size: u32, this_origin: Vector2I,
requested_origin: Vector2I, this_size: u32,
requested_size: u32) { requested_origin: Vector2I,
requested_size: u32,
) {
if this_size <= requested_size { if this_size <= requested_size {
if this_size == requested_size && this_origin == requested_origin { if this_size == requested_size && this_origin == requested_origin {
*self = TreeNode::EmptyLeaf; *self = TreeNode::EmptyLeaf;
@ -324,11 +355,9 @@ impl TreeNode {
fn merge_if_necessary(&mut self) { fn merge_if_necessary(&mut self) {
match *self { match *self {
TreeNode::Parent(ref mut kids) => { TreeNode::Parent(ref mut kids) => {
if kids.iter().all(|kid| { if kids.iter().all(|kid| match **kid {
match **kid { TreeNode::EmptyLeaf => true,
TreeNode::EmptyLeaf => true, _ => false,
_ => false,
}
}) { }) {
*self = TreeNode::EmptyLeaf; *self = TreeNode::EmptyLeaf;
} }
@ -353,8 +382,9 @@ impl<'a> Iterator for TexturePageIter<'a> {
}; };
loop { loop {
self.next_index += 1; self.next_index += 1;
if self.next_index >= self.allocator.pages.len() || if self.next_index >= self.allocator.pages.len()
self.allocator.pages[self.next_index as usize].is_some() { || self.allocator.pages[self.next_index as usize].is_some()
{
break; break;
} }
} }
@ -372,8 +402,9 @@ mod test {
#[test] #[test]
fn test_allocation_and_freeing() { fn test_allocation_and_freeing() {
quickcheck::quickcheck(prop_allocation_and_freeing_work as quickcheck::quickcheck(
fn(u32, Vec<(u32, u32)>) -> bool); prop_allocation_and_freeing_work as fn(u32, Vec<(u32, u32)>) -> bool,
);
fn prop_allocation_and_freeing_work(mut length: u32, mut sizes: Vec<(u32, u32)>) -> bool { fn prop_allocation_and_freeing_work(mut length: u32, mut sizes: Vec<(u32, u32)>) -> bool {
length = u32::next_power_of_two(length).max(1); length = u32::next_power_of_two(length).max(1);

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,9 @@ pub trait Executor {
/// (0..length).into_par_iter().map(builder).collect() /// (0..length).into_par_iter().map(builder).collect()
/// ``` /// ```
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T> fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync; where
T: Send,
F: Fn(usize) -> T + Send + Sync;
} }
/// An executor that simply executes tasks sequentially in the same thread. /// An executor that simply executes tasks sequentially in the same thread.
@ -26,7 +28,10 @@ pub struct SequentialExecutor;
impl Executor for SequentialExecutor { impl Executor for SequentialExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T> fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync { where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
(0..length).into_iter().map(builder).collect() (0..length).into_iter().map(builder).collect()
} }
} }

View File

@ -18,7 +18,10 @@ pub struct RayonExecutor;
impl Executor for RayonExecutor { impl Executor for RayonExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T> fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync { where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
(0..length).into_par_iter().map(builder).collect() (0..length).into_par_iter().map(builder).collect()
} }
} }

View File

@ -41,24 +41,27 @@ pub struct SceneProxy {
impl SceneProxy { impl SceneProxy {
/// Creates a new scene proxy using the given renderer GPU API level and executor to execute /// Creates a new scene proxy using the given renderer GPU API level and executor to execute
/// CPU tasks. /// CPU tasks.
/// ///
/// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you /// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you
/// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a /// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a
/// `SequentialExecutor`. /// `SequentialExecutor`.
pub fn new<E>(renderer_level: RendererLevel, executor: E) -> SceneProxy pub fn new<E>(renderer_level: RendererLevel, executor: E) -> SceneProxy
where E: Executor + Send + 'static { where
E: Executor + Send + 'static,
{
SceneProxy::from_scene(Scene::new(), renderer_level, executor) SceneProxy::from_scene(Scene::new(), renderer_level, executor)
} }
/// Wraps an existing scene in a scene proxy using the given renderer GPU API level an executor /// Wraps an existing scene in a scene proxy using the given renderer GPU API level an executor
/// to execute CPU tasks. /// to execute CPU tasks.
/// ///
/// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you /// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you
/// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a /// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a
/// `SequentialExecutor`. /// `SequentialExecutor`.
pub fn from_scene<E>(scene: Scene, renderer_level: RendererLevel, executor: E) pub fn from_scene<E>(scene: Scene, renderer_level: RendererLevel, executor: E) -> SceneProxy
-> SceneProxy where
where E: Executor + Send + 'static { E: Executor + Send + 'static,
{
let (main_to_worker_sender, main_to_worker_receiver) = let (main_to_worker_sender, main_to_worker_receiver) =
flume::bounded(MAX_MESSAGES_IN_FLIGHT); flume::bounded(MAX_MESSAGES_IN_FLIGHT);
let (worker_to_main_sender, worker_to_main_receiver) = let (worker_to_main_sender, worker_to_main_receiver) =
@ -68,19 +71,26 @@ impl SceneProxy {
})); }));
let sink = SceneSink::new(listener, renderer_level); let sink = SceneSink::new(listener, renderer_level);
thread::spawn(move || scene_thread(scene, executor, sink, main_to_worker_receiver)); thread::spawn(move || scene_thread(scene, executor, sink, main_to_worker_receiver));
SceneProxy { sender: main_to_worker_sender, receiver: worker_to_main_receiver } SceneProxy {
sender: main_to_worker_sender,
receiver: worker_to_main_receiver,
}
} }
/// Replaces the wrapped scene with a new one, discarding the old scene. /// Replaces the wrapped scene with a new one, discarding the old scene.
#[inline] #[inline]
pub fn replace_scene(&self, new_scene: Scene) { pub fn replace_scene(&self, new_scene: Scene) {
self.sender.send(MainToWorkerMsg::ReplaceScene(new_scene)).unwrap(); self.sender
.send(MainToWorkerMsg::ReplaceScene(new_scene))
.unwrap();
} }
/// Sets the view box of the scene, which defines the visible rectangle. /// Sets the view box of the scene, which defines the visible rectangle.
#[inline] #[inline]
pub fn set_view_box(&self, new_view_box: RectF) { pub fn set_view_box(&self, new_view_box: RectF) {
self.sender.send(MainToWorkerMsg::SetViewBox(new_view_box)).unwrap(); self.sender
.send(MainToWorkerMsg::SetViewBox(new_view_box))
.unwrap();
} }
/// Constructs a scene and queues up the commands needed to render it. /// Constructs a scene and queues up the commands needed to render it.
@ -91,7 +101,10 @@ impl SceneProxy {
/// Sends all queued commands to the given renderer to render the wrapped scene. /// Sends all queued commands to the given renderer to render the wrapped scene.
#[inline] #[inline]
pub fn render<D>(&mut self, renderer: &mut Renderer<D>) where D: Device { pub fn render<D>(&mut self, renderer: &mut Renderer<D>)
where
D: Device,
{
renderer.begin_scene(); renderer.begin_scene();
while let Ok(command) = self.receiver.recv() { while let Ok(command) = self.receiver.recv() {
renderer.render_command(&command); renderer.render_command(&command);
@ -114,7 +127,9 @@ impl SceneProxy {
/// ``` /// ```
#[inline] #[inline]
pub fn build_and_render<D>(&mut self, renderer: &mut Renderer<D>, build_options: BuildOptions) pub fn build_and_render<D>(&mut self, renderer: &mut Renderer<D>, build_options: BuildOptions)
where D: Device { where
D: Device,
{
self.build(build_options); self.build(build_options);
self.render(renderer); self.render(renderer);
} }
@ -123,16 +138,21 @@ impl SceneProxy {
#[inline] #[inline]
pub fn copy_scene(&self) -> Scene { pub fn copy_scene(&self) -> Scene {
let (sender, receiver) = flume::bounded(MAX_MESSAGES_IN_FLIGHT); let (sender, receiver) = flume::bounded(MAX_MESSAGES_IN_FLIGHT);
self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap(); self.sender
.send(MainToWorkerMsg::CopyScene(sender))
.unwrap();
receiver.recv().unwrap() receiver.recv().unwrap()
} }
} }
fn scene_thread<E>(mut scene: Scene, fn scene_thread<E>(
executor: E, mut scene: Scene,
mut sink: SceneSink<'static>, executor: E,
main_to_worker_receiver: Receiver<MainToWorkerMsg>) mut sink: SceneSink<'static>,
where E: Executor { main_to_worker_receiver: Receiver<MainToWorkerMsg>,
) where
E: Executor,
{
while let Ok(msg) = main_to_worker_receiver.recv() { while let Ok(msg) = main_to_worker_receiver.recv() {
match msg { match msg {
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene, MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene,

View File

@ -15,25 +15,25 @@ use crate::paint::PaintCompositeOp;
use pathfinder_content::effects::BlendMode; use pathfinder_content::effects::BlendMode;
use pathfinder_gpu::{BlendFactor, BlendState}; use pathfinder_gpu::{BlendFactor, BlendState};
const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1; const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1;
const COMBINER_CTRL_COLOR_COMBINE_DEST_IN: i32 = 0x2; const COMBINER_CTRL_COLOR_COMBINE_DEST_IN: i32 = 0x2;
const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0; const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0;
const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1; const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1;
const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2; const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2;
const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3; const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3;
const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4; const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4;
const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5; const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5;
const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6; const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6;
const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7; const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7;
const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8; const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8;
const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9; const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9;
const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa; const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa;
const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb; const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb;
const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc; const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc;
const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd; const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd;
const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe; const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe;
const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf; const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf;
pub(crate) trait ToBlendState { pub(crate) trait ToBlendState {
fn to_blend_state(self) -> Option<BlendState>; fn to_blend_state(self) -> Option<BlendState>;
@ -42,121 +42,99 @@ pub(crate) trait ToBlendState {
impl ToBlendState for BlendMode { impl ToBlendState for BlendMode {
fn to_blend_state(self) -> Option<BlendState> { fn to_blend_state(self) -> Option<BlendState> {
match self { match self {
BlendMode::Clear => { BlendMode::Clear => Some(BlendState {
Some(BlendState { src_rgb_factor: BlendFactor::Zero,
src_rgb_factor: BlendFactor::Zero, dest_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::Zero, src_alpha_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::Zero, dest_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::Zero, ..BlendState::default()
..BlendState::default() }),
}) BlendMode::SrcOver => Some(BlendState {
} src_rgb_factor: BlendFactor::One,
BlendMode::SrcOver => { dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
Some(BlendState { src_alpha_factor: BlendFactor::One,
src_rgb_factor: BlendFactor::One, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, ..BlendState::default()
src_alpha_factor: BlendFactor::One, }),
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, BlendMode::DestOver => Some(BlendState {
..BlendState::default() src_rgb_factor: BlendFactor::OneMinusDestAlpha,
}) dest_rgb_factor: BlendFactor::One,
} src_alpha_factor: BlendFactor::OneMinusDestAlpha,
BlendMode::DestOver => { dest_alpha_factor: BlendFactor::One,
Some(BlendState { ..BlendState::default()
src_rgb_factor: BlendFactor::OneMinusDestAlpha, }),
dest_rgb_factor: BlendFactor::One, BlendMode::SrcIn => Some(BlendState {
src_alpha_factor: BlendFactor::OneMinusDestAlpha, src_rgb_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::One, dest_rgb_factor: BlendFactor::Zero,
..BlendState::default() src_alpha_factor: BlendFactor::DestAlpha,
}) dest_alpha_factor: BlendFactor::Zero,
} ..BlendState::default()
BlendMode::SrcIn => { }),
Some(BlendState { BlendMode::DestIn => Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha, src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::Zero, dest_rgb_factor: BlendFactor::SrcAlpha,
src_alpha_factor: BlendFactor::DestAlpha, src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::Zero, dest_alpha_factor: BlendFactor::SrcAlpha,
..BlendState::default() ..BlendState::default()
}) }),
} BlendMode::SrcOut => Some(BlendState {
BlendMode::DestIn => { src_rgb_factor: BlendFactor::OneMinusDestAlpha,
Some(BlendState { dest_rgb_factor: BlendFactor::Zero,
src_rgb_factor: BlendFactor::Zero, src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::SrcAlpha, dest_alpha_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::Zero, ..BlendState::default()
dest_alpha_factor: BlendFactor::SrcAlpha, }),
..BlendState::default() BlendMode::DestOut => Some(BlendState {
}) src_rgb_factor: BlendFactor::Zero,
} dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
BlendMode::SrcOut => { src_alpha_factor: BlendFactor::Zero,
Some(BlendState { dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
src_rgb_factor: BlendFactor::OneMinusDestAlpha, ..BlendState::default()
dest_rgb_factor: BlendFactor::Zero, }),
src_alpha_factor: BlendFactor::OneMinusDestAlpha, BlendMode::SrcAtop => Some(BlendState {
dest_alpha_factor: BlendFactor::Zero, src_rgb_factor: BlendFactor::DestAlpha,
..BlendState::default() dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
}) src_alpha_factor: BlendFactor::DestAlpha,
} dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
BlendMode::DestOut => { ..BlendState::default()
Some(BlendState { }),
src_rgb_factor: BlendFactor::Zero, BlendMode::DestAtop => Some(BlendState {
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, src_rgb_factor: BlendFactor::OneMinusDestAlpha,
src_alpha_factor: BlendFactor::Zero, dest_rgb_factor: BlendFactor::SrcAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, src_alpha_factor: BlendFactor::OneMinusDestAlpha,
..BlendState::default() dest_alpha_factor: BlendFactor::SrcAlpha,
}) ..BlendState::default()
} }),
BlendMode::SrcAtop => { BlendMode::Xor => Some(BlendState {
Some(BlendState { src_rgb_factor: BlendFactor::OneMinusDestAlpha,
src_rgb_factor: BlendFactor::DestAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, src_alpha_factor: BlendFactor::OneMinusDestAlpha,
src_alpha_factor: BlendFactor::DestAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, ..BlendState::default()
..BlendState::default() }),
}) BlendMode::Lighter => Some(BlendState {
} src_rgb_factor: BlendFactor::One,
BlendMode::DestAtop => { dest_rgb_factor: BlendFactor::One,
Some(BlendState { src_alpha_factor: BlendFactor::One,
src_rgb_factor: BlendFactor::OneMinusDestAlpha, dest_alpha_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::SrcAlpha, ..BlendState::default()
src_alpha_factor: BlendFactor::OneMinusDestAlpha, }),
dest_alpha_factor: BlendFactor::SrcAlpha, BlendMode::Copy
..BlendState::default() | BlendMode::Darken
}) | BlendMode::Lighten
} | BlendMode::Multiply
BlendMode::Xor => { | BlendMode::Screen
Some(BlendState { | BlendMode::HardLight
src_rgb_factor: BlendFactor::OneMinusDestAlpha, | BlendMode::Overlay
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, | BlendMode::ColorDodge
src_alpha_factor: BlendFactor::OneMinusDestAlpha, | BlendMode::ColorBurn
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, | BlendMode::SoftLight
..BlendState::default() | BlendMode::Difference
}) | BlendMode::Exclusion
} | BlendMode::Hue
BlendMode::Lighter => { | BlendMode::Saturation
Some(BlendState { | BlendMode::Color
src_rgb_factor: BlendFactor::One, | BlendMode::Luminosity => {
dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
})
}
BlendMode::Copy |
BlendMode::Darken |
BlendMode::Lighten |
BlendMode::Multiply |
BlendMode::Screen |
BlendMode::HardLight |
BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::SoftLight |
BlendMode::Difference |
BlendMode::Exclusion |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => {
// Blending is done manually in the shader. // Blending is done manually in the shader.
None None
} }
@ -171,18 +149,18 @@ pub(crate) trait ToCompositeCtrl {
impl ToCompositeCtrl for BlendMode { impl ToCompositeCtrl for BlendMode {
fn to_composite_ctrl(&self) -> i32 { fn to_composite_ctrl(&self) -> i32 {
match *self { match *self {
BlendMode::SrcOver | BlendMode::SrcOver
BlendMode::SrcAtop | | BlendMode::SrcAtop
BlendMode::DestOver | | BlendMode::DestOver
BlendMode::DestOut | | BlendMode::DestOut
BlendMode::Xor | | BlendMode::Xor
BlendMode::Lighter | | BlendMode::Lighter
BlendMode::Clear | | BlendMode::Clear
BlendMode::Copy | | BlendMode::Copy
BlendMode::SrcIn | | BlendMode::SrcIn
BlendMode::SrcOut | | BlendMode::SrcOut
BlendMode::DestIn | | BlendMode::DestIn
BlendMode::DestAtop => COMBINER_CTRL_COMPOSITE_NORMAL, | BlendMode::DestAtop => COMBINER_CTRL_COMPOSITE_NORMAL,
BlendMode::Multiply => COMBINER_CTRL_COMPOSITE_MULTIPLY, BlendMode::Multiply => COMBINER_CTRL_COMPOSITE_MULTIPLY,
BlendMode::Darken => COMBINER_CTRL_COMPOSITE_DARKEN, BlendMode::Darken => COMBINER_CTRL_COMPOSITE_DARKEN,
BlendMode::Lighten => COMBINER_CTRL_COMPOSITE_LIGHTEN, BlendMode::Lighten => COMBINER_CTRL_COMPOSITE_LIGHTEN,
@ -219,33 +197,33 @@ pub trait BlendModeExt {
impl BlendModeExt for BlendMode { impl BlendModeExt for BlendMode {
fn needs_readable_framebuffer(self) -> bool { fn needs_readable_framebuffer(self) -> bool {
match self { match self {
BlendMode::Clear | BlendMode::Clear
BlendMode::SrcOver | | BlendMode::SrcOver
BlendMode::DestOver | | BlendMode::DestOver
BlendMode::SrcIn | | BlendMode::SrcIn
BlendMode::DestIn | | BlendMode::DestIn
BlendMode::SrcOut | | BlendMode::SrcOut
BlendMode::DestOut | | BlendMode::DestOut
BlendMode::SrcAtop | | BlendMode::SrcAtop
BlendMode::DestAtop | | BlendMode::DestAtop
BlendMode::Xor | | BlendMode::Xor
BlendMode::Lighter | | BlendMode::Lighter
BlendMode::Copy => false, | BlendMode::Copy => false,
BlendMode::Lighten | BlendMode::Lighten
BlendMode::Darken | | BlendMode::Darken
BlendMode::Multiply | | BlendMode::Multiply
BlendMode::Screen | | BlendMode::Screen
BlendMode::HardLight | | BlendMode::HardLight
BlendMode::Overlay | | BlendMode::Overlay
BlendMode::ColorDodge | | BlendMode::ColorDodge
BlendMode::ColorBurn | | BlendMode::ColorBurn
BlendMode::SoftLight | | BlendMode::SoftLight
BlendMode::Difference | | BlendMode::Difference
BlendMode::Exclusion | | BlendMode::Exclusion
BlendMode::Hue | | BlendMode::Hue
BlendMode::Saturation | | BlendMode::Saturation
BlendMode::Color | | BlendMode::Color
BlendMode::Luminosity => true, | BlendMode::Luminosity => true,
} }
} }
} }

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
//! A GPU compute-based renderer that uses functionality available in Direct3D 11. //! A GPU compute-based renderer that uses functionality available in Direct3D 11.
//! //!
//! This renderer supports OpenGL at least 4.3, OpenGL ES at least 3.1, and Metal of any version. //! This renderer supports OpenGL at least 4.3, OpenGL ES at least 3.1, and Metal of any version.
pub mod renderer; pub mod renderer;

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,10 @@ pub(crate) const BIN_WORKGROUP_SIZE: u32 = 64;
pub(crate) const PROPAGATE_WORKGROUP_SIZE: u32 = 64; pub(crate) const PROPAGATE_WORKGROUP_SIZE: u32 = 64;
pub(crate) const SORT_WORKGROUP_SIZE: u32 = 64; pub(crate) const SORT_WORKGROUP_SIZE: u32 = 64;
pub(crate) struct ProgramsD3D11<D> where D: Device { pub(crate) struct ProgramsD3D11<D>
where
D: Device,
{
pub(crate) bound_program: BoundProgramD3D11<D>, pub(crate) bound_program: BoundProgramD3D11<D>,
pub(crate) dice_program: DiceProgramD3D11<D>, pub(crate) dice_program: DiceProgramD3D11<D>,
pub(crate) bin_program: BinProgramD3D11<D>, pub(crate) bin_program: BinProgramD3D11<D>,
@ -31,7 +34,10 @@ pub(crate) struct ProgramsD3D11<D> where D: Device {
pub(crate) tile_program: TileProgramD3D11<D>, pub(crate) tile_program: TileProgramD3D11<D>,
} }
impl<D> ProgramsD3D11<D> where D: Device { impl<D> ProgramsD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D11<D> {
ProgramsD3D11 { ProgramsD3D11 {
bound_program: BoundProgramD3D11::new(device, resources), bound_program: BoundProgramD3D11::new(device, resources),
@ -45,7 +51,10 @@ impl<D> ProgramsD3D11<D> where D: Device {
} }
} }
pub(crate) struct PropagateProgramD3D11<D> where D: Device { pub(crate) struct PropagateProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) framebuffer_tile_size_uniform: D::Uniform, pub(crate) framebuffer_tile_size_uniform: D::Uniform,
pub(crate) column_count_uniform: D::Uniform, pub(crate) column_count_uniform: D::Uniform,
@ -60,10 +69,17 @@ pub(crate) struct PropagateProgramD3D11<D> where D: Device {
pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer, pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer,
} }
impl<D> PropagateProgramD3D11<D> where D: Device { impl<D> PropagateProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> PropagateProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> PropagateProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/propagate"); let mut program = device.create_compute_program(resources, "d3d11/propagate");
let local_size = ComputeDimensions { x: PROPAGATE_WORKGROUP_SIZE, y: 1, z: 1 }; let local_size = ComputeDimensions {
x: PROPAGATE_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, local_size); device.set_compute_program_local_size(&mut program, local_size);
let framebuffer_tile_size_uniform = device.get_uniform(&program, "FramebufferTileSize"); let framebuffer_tile_size_uniform = device.get_uniform(&program, "FramebufferTileSize");
@ -95,7 +111,10 @@ impl<D> PropagateProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct FillProgramD3D11<D> where D: Device { pub(crate) struct FillProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) dest_image: D::ImageParameter, pub(crate) dest_image: D::ImageParameter,
pub(crate) area_lut_texture: D::TextureParameter, pub(crate) area_lut_texture: D::TextureParameter,
@ -105,10 +124,17 @@ pub(crate) struct FillProgramD3D11<D> where D: Device {
pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer, pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer,
} }
impl<D> FillProgramD3D11<D> where D: Device { impl<D> FillProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/fill"); let mut program = device.create_compute_program(resources, "d3d11/fill");
let local_size = ComputeDimensions { x: TILE_WIDTH, y: TILE_HEIGHT / 4, z: 1 }; let local_size = ComputeDimensions {
x: TILE_WIDTH,
y: TILE_HEIGHT / 4,
z: 1,
};
device.set_compute_program_local_size(&mut program, local_size); device.set_compute_program_local_size(&mut program, local_size);
let dest_image = device.get_image_parameter(&program, "Dest"); let dest_image = device.get_image_parameter(&program, "Dest");
@ -130,7 +156,10 @@ impl<D> FillProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct TileProgramD3D11<D> where D: Device { pub(crate) struct TileProgramD3D11<D>
where
D: Device,
{
pub(crate) common: TileProgramCommon<D>, pub(crate) common: TileProgramCommon<D>,
pub(crate) load_action_uniform: D::Uniform, pub(crate) load_action_uniform: D::Uniform,
pub(crate) clear_color_uniform: D::Uniform, pub(crate) clear_color_uniform: D::Uniform,
@ -140,11 +169,14 @@ pub(crate) struct TileProgramD3D11<D> where D: Device {
pub(crate) first_tile_map_storage_buffer: D::StorageBuffer, pub(crate) first_tile_map_storage_buffer: D::StorageBuffer,
} }
impl<D> TileProgramD3D11<D> where D: Device { impl<D> TileProgramD3D11<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D11<D> { fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/tile"); let mut program = device.create_compute_program(resources, "d3d11/tile");
device.set_compute_program_local_size(&mut program, device
ComputeDimensions { x: 16, y: 4, z: 1 }); .set_compute_program_local_size(&mut program, ComputeDimensions { x: 16, y: 4, z: 1 });
let load_action_uniform = device.get_uniform(&program, "LoadAction"); let load_action_uniform = device.get_uniform(&program, "LoadAction");
let clear_color_uniform = device.get_uniform(&program, "ClearColor"); let clear_color_uniform = device.get_uniform(&program, "ClearColor");
@ -166,7 +198,10 @@ impl<D> TileProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct BinProgramD3D11<D> where D: Device { pub(crate) struct BinProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) microline_count_uniform: D::Uniform, pub(crate) microline_count_uniform: D::Uniform,
pub(crate) max_fill_count_uniform: D::Uniform, pub(crate) max_fill_count_uniform: D::Uniform,
@ -178,10 +213,17 @@ pub(crate) struct BinProgramD3D11<D> where D: Device {
pub(crate) backdrops_storage_buffer: D::StorageBuffer, pub(crate) backdrops_storage_buffer: D::StorageBuffer,
} }
impl<D> BinProgramD3D11<D> where D: Device { impl<D> BinProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BinProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BinProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/bin"); let mut program = device.create_compute_program(resources, "d3d11/bin");
let dimensions = ComputeDimensions { x: BIN_WORKGROUP_SIZE, y: 1, z: 1 }; let dimensions = ComputeDimensions {
x: BIN_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions); device.set_compute_program_local_size(&mut program, dimensions);
let microline_count_uniform = device.get_uniform(&program, "MicrolineCount"); let microline_count_uniform = device.get_uniform(&program, "MicrolineCount");
@ -209,7 +251,10 @@ impl<D> BinProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct DiceProgramD3D11<D> where D: Device { pub(crate) struct DiceProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) transform_uniform: D::Uniform, pub(crate) transform_uniform: D::Uniform,
pub(crate) translation_uniform: D::Uniform, pub(crate) translation_uniform: D::Uniform,
@ -223,17 +268,24 @@ pub(crate) struct DiceProgramD3D11<D> where D: Device {
pub(crate) microlines_storage_buffer: D::StorageBuffer, pub(crate) microlines_storage_buffer: D::StorageBuffer,
} }
impl<D> DiceProgramD3D11<D> where D: Device { impl<D> DiceProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> DiceProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> DiceProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/dice"); let mut program = device.create_compute_program(resources, "d3d11/dice");
let dimensions = ComputeDimensions { x: DICE_WORKGROUP_SIZE, y: 1, z: 1 }; let dimensions = ComputeDimensions {
x: DICE_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions); device.set_compute_program_local_size(&mut program, dimensions);
let transform_uniform = device.get_uniform(&program, "Transform"); let transform_uniform = device.get_uniform(&program, "Transform");
let translation_uniform = device.get_uniform(&program, "Translation"); let translation_uniform = device.get_uniform(&program, "Translation");
let path_count_uniform = device.get_uniform(&program, "PathCount"); let path_count_uniform = device.get_uniform(&program, "PathCount");
let last_batch_segment_index_uniform = device.get_uniform(&program, let last_batch_segment_index_uniform =
"LastBatchSegmentIndex"); device.get_uniform(&program, "LastBatchSegmentIndex");
let max_microline_count_uniform = device.get_uniform(&program, "MaxMicrolineCount"); let max_microline_count_uniform = device.get_uniform(&program, "MaxMicrolineCount");
let compute_indirect_params_storage_buffer = let compute_indirect_params_storage_buffer =
@ -259,7 +311,10 @@ impl<D> DiceProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct BoundProgramD3D11<D> where D: Device { pub(crate) struct BoundProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) path_count_uniform: D::Uniform, pub(crate) path_count_uniform: D::Uniform,
pub(crate) tile_count_uniform: D::Uniform, pub(crate) tile_count_uniform: D::Uniform,
@ -267,10 +322,17 @@ pub(crate) struct BoundProgramD3D11<D> where D: Device {
pub(crate) tiles_storage_buffer: D::StorageBuffer, pub(crate) tiles_storage_buffer: D::StorageBuffer,
} }
impl<D> BoundProgramD3D11<D> where D: Device { impl<D> BoundProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BoundProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BoundProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/bound"); let mut program = device.create_compute_program(resources, "d3d11/bound");
let dimensions = ComputeDimensions { x: BOUND_WORKGROUP_SIZE, y: 1, z: 1 }; let dimensions = ComputeDimensions {
x: BOUND_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions); device.set_compute_program_local_size(&mut program, dimensions);
let path_count_uniform = device.get_uniform(&program, "PathCount"); let path_count_uniform = device.get_uniform(&program, "PathCount");
@ -289,7 +351,10 @@ impl<D> BoundProgramD3D11<D> where D: Device {
} }
} }
pub(crate) struct SortProgramD3D11<D> where D: Device { pub(crate) struct SortProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) tile_count_uniform: D::Uniform, pub(crate) tile_count_uniform: D::Uniform,
pub(crate) tiles_storage_buffer: D::StorageBuffer, pub(crate) tiles_storage_buffer: D::StorageBuffer,
@ -297,10 +362,17 @@ pub(crate) struct SortProgramD3D11<D> where D: Device {
pub(crate) z_buffer_storage_buffer: D::StorageBuffer, pub(crate) z_buffer_storage_buffer: D::StorageBuffer,
} }
impl<D> SortProgramD3D11<D> where D: Device { impl<D> SortProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> SortProgramD3D11<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> SortProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/sort"); let mut program = device.create_compute_program(resources, "d3d11/sort");
let dimensions = ComputeDimensions { x: SORT_WORKGROUP_SIZE, y: 1, z: 1 }; let dimensions = ComputeDimensions {
x: SORT_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions); device.set_compute_program_local_size(&mut program, dimensions);
let tile_count_uniform = device.get_uniform(&program, "TileCount"); let tile_count_uniform = device.get_uniform(&program, "TileCount");
@ -316,4 +388,4 @@ impl<D> SortProgramD3D11<D> where D: Device {
z_buffer_storage_buffer, z_buffer_storage_buffer,
} }
} }
} }

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
//! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9. //! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9.
//! //!
//! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and //! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and
//! WebGL at least 2.0. //! WebGL at least 2.0.

View File

@ -9,17 +9,17 @@
// except according to those terms. // except according to those terms.
//! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9. //! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9.
//! //!
//! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and //! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and
//! WebGL at least 2.0. //! WebGL at least 2.0.
use crate::gpu::blend::{BlendModeExt, ToBlendState}; use crate::gpu::blend::{BlendModeExt, ToBlendState};
use crate::gpu::perf::TimeCategory;
use crate::gpu::renderer::{FramebufferFlags, MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_WIDTH};
use crate::gpu::renderer::{RendererCore, RendererFlags};
use crate::gpu::d3d9::shaders::{ClipTileCombineVertexArrayD3D9, ClipTileCopyVertexArrayD3D9}; use crate::gpu::d3d9::shaders::{ClipTileCombineVertexArrayD3D9, ClipTileCopyVertexArrayD3D9};
use crate::gpu::d3d9::shaders::{CopyTileVertexArray, FillVertexArrayD3D9}; use crate::gpu::d3d9::shaders::{CopyTileVertexArray, FillVertexArrayD3D9};
use crate::gpu::d3d9::shaders::{ProgramsD3D9, TileVertexArrayD3D9}; use crate::gpu::d3d9::shaders::{ProgramsD3D9, TileVertexArrayD3D9};
use crate::gpu::perf::TimeCategory;
use crate::gpu::renderer::{FramebufferFlags, MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_WIDTH};
use crate::gpu::renderer::{RendererCore, RendererFlags};
use crate::gpu_data::{Clip, DrawTileBatchD3D9, Fill, TileBatchTexture, TileObjectPrimitive}; use crate::gpu_data::{Clip, DrawTileBatchD3D9, Fill, TileBatchTexture, TileObjectPrimitive};
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
@ -28,7 +28,7 @@ use pathfinder_color::ColorF;
use pathfinder_content::effects::BlendMode; use pathfinder_content::effects::BlendMode;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, Vector4F, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I, Vector4F};
use pathfinder_gpu::allocator::{BufferTag, FramebufferID, FramebufferTag, GeneralBufferID}; use pathfinder_gpu::allocator::{BufferTag, FramebufferID, FramebufferTag, GeneralBufferID};
use pathfinder_gpu::allocator::{IndexBufferID, TextureID, TextureTag}; use pathfinder_gpu::allocator::{IndexBufferID, TextureID, TextureTag};
use pathfinder_gpu::{BlendFactor, BlendState, BufferTarget, ClearOps, Device, Primitive}; use pathfinder_gpu::{BlendFactor, BlendState, BufferTarget, ClearOps, Device, Primitive};
@ -40,7 +40,10 @@ use std::u32;
const MAX_FILLS_PER_BATCH: usize = 0x10000; const MAX_FILLS_PER_BATCH: usize = 0x10000;
pub(crate) struct RendererD3D9<D> where D: Device { pub(crate) struct RendererD3D9<D>
where
D: Device,
{
// Basic data // Basic data
programs: ProgramsD3D9<D>, programs: ProgramsD3D9<D>,
quads_vertex_indices_buffer_id: Option<IndexBufferID>, quads_vertex_indices_buffer_id: Option<IndexBufferID>,
@ -54,17 +57,23 @@ pub(crate) struct RendererD3D9<D> where D: Device {
dest_blend_framebuffer_id: FramebufferID, dest_blend_framebuffer_id: FramebufferID,
} }
impl<D> RendererD3D9<D> where D: Device { impl<D> RendererD3D9<D>
pub(crate) fn new(core: &mut RendererCore<D>, resources: &dyn ResourceLoader) where
-> RendererD3D9<D> { D: Device,
{
pub(crate) fn new(
core: &mut RendererCore<D>,
resources: &dyn ResourceLoader,
) -> RendererD3D9<D> {
let programs = ProgramsD3D9::new(&core.device, resources); let programs = ProgramsD3D9::new(&core.device, resources);
let window_size = core.options.dest.window_size(&core.device); let window_size = core.options.dest.window_size(&core.device);
let dest_blend_framebuffer_id = let dest_blend_framebuffer_id = core.allocator.allocate_framebuffer(
core.allocator.allocate_framebuffer(&core.device, &core.device,
window_size, window_size,
TextureFormat::RGBA8, TextureFormat::RGBA8,
FramebufferTag("DestBlendD3D9")); FramebufferTag("DestBlendD3D9"),
);
RendererD3D9 { RendererD3D9 {
programs, programs,
@ -78,43 +87,57 @@ impl<D> RendererD3D9<D> where D: Device {
} }
} }
pub(crate) fn upload_and_draw_tiles(&mut self, pub(crate) fn upload_and_draw_tiles(
core: &mut RendererCore<D>, &mut self,
batch: &DrawTileBatchD3D9) { core: &mut RendererCore<D>,
batch: &DrawTileBatchD3D9,
) {
if !batch.clips.is_empty() { if !batch.clips.is_empty() {
let clip_buffer_info = self.upload_clip_tiles(core, &batch.clips); let clip_buffer_info = self.upload_clip_tiles(core, &batch.clips);
self.clip_tiles(core, &clip_buffer_info); self.clip_tiles(core, &clip_buffer_info);
core.allocator.free_general_buffer(clip_buffer_info.clip_buffer_id); core.allocator
.free_general_buffer(clip_buffer_info.clip_buffer_id);
} }
let tile_buffer = self.upload_tiles(core, &batch.tiles); let tile_buffer = self.upload_tiles(core, &batch.tiles);
let z_buffer_texture_id = self.upload_z_buffer(core, &batch.z_buffer_data); let z_buffer_texture_id = self.upload_z_buffer(core, &batch.z_buffer_data);
self.draw_tiles(core, self.draw_tiles(
batch.tiles.len() as u32, core,
tile_buffer.tile_vertex_buffer_id, batch.tiles.len() as u32,
batch.color_texture, tile_buffer.tile_vertex_buffer_id,
batch.blend_mode, batch.color_texture,
z_buffer_texture_id); batch.blend_mode,
z_buffer_texture_id,
);
core.allocator.free_texture(z_buffer_texture_id); core.allocator.free_texture(z_buffer_texture_id);
core.allocator.free_general_buffer(tile_buffer.tile_vertex_buffer_id); core.allocator
.free_general_buffer(tile_buffer.tile_vertex_buffer_id);
} }
fn upload_tiles(&mut self, core: &mut RendererCore<D>, tiles: &[TileObjectPrimitive]) fn upload_tiles(
-> TileBufferD3D9 { &mut self,
let tile_vertex_buffer_id = core: &mut RendererCore<D>,
core.allocator.allocate_general_buffer::<TileObjectPrimitive>(&core.device, tiles: &[TileObjectPrimitive],
tiles.len() as u64, ) -> TileBufferD3D9 {
BufferTag("TileD3D9")); let tile_vertex_buffer_id = core
.allocator
.allocate_general_buffer::<TileObjectPrimitive>(
&core.device,
tiles.len() as u64,
BufferTag("TileD3D9"),
);
let tile_vertex_buffer = &core.allocator.get_general_buffer(tile_vertex_buffer_id); let tile_vertex_buffer = &core.allocator.get_general_buffer(tile_vertex_buffer_id);
core.device.upload_to_buffer(tile_vertex_buffer, 0, tiles, BufferTarget::Vertex); core.device
.upload_to_buffer(tile_vertex_buffer, 0, tiles, BufferTarget::Vertex);
self.ensure_index_buffer(core, tiles.len()); self.ensure_index_buffer(core, tiles.len());
TileBufferD3D9 { tile_vertex_buffer_id } TileBufferD3D9 {
tile_vertex_buffer_id,
}
} }
fn ensure_index_buffer(&mut self, core: &mut RendererCore<D>, mut length: usize) { fn ensure_index_buffer(&mut self, core: &mut RendererCore<D>, mut length: usize) {
length = length.next_power_of_two(); length = length.next_power_of_two();
if self.quads_vertex_indices_length >= length { if self.quads_vertex_indices_length >= length {
@ -125,24 +148,33 @@ impl<D> RendererD3D9<D> where D: Device {
let mut indices: Vec<u32> = Vec::with_capacity(length * 6); let mut indices: Vec<u32> = Vec::with_capacity(length * 6);
for index in 0..(length as u32) { for index in 0..(length as u32) {
indices.extend_from_slice(&[ indices.extend_from_slice(&[
index * 4 + 0, index * 4 + 1, index * 4 + 2, index * 4 + 0,
index * 4 + 1, index * 4 + 3, index * 4 + 2, index * 4 + 1,
index * 4 + 2,
index * 4 + 1,
index * 4 + 3,
index * 4 + 2,
]); ]);
} }
if let Some(quads_vertex_indices_buffer_id) = self.quads_vertex_indices_buffer_id.take() { if let Some(quads_vertex_indices_buffer_id) = self.quads_vertex_indices_buffer_id.take() {
core.allocator.free_index_buffer(quads_vertex_indices_buffer_id); core.allocator
.free_index_buffer(quads_vertex_indices_buffer_id);
} }
let quads_vertex_indices_buffer_id = let quads_vertex_indices_buffer_id = core.allocator.allocate_index_buffer::<u32>(
core.allocator.allocate_index_buffer::<u32>(&core.device, &core.device,
indices.len() as u64, indices.len() as u64,
BufferTag("QuadsVertexIndicesD3D9")); BufferTag("QuadsVertexIndicesD3D9"),
let quads_vertex_indices_buffer = );
core.allocator.get_index_buffer(quads_vertex_indices_buffer_id); let quads_vertex_indices_buffer = core
core.device.upload_to_buffer(quads_vertex_indices_buffer, .allocator
0, .get_index_buffer(quads_vertex_indices_buffer_id);
&indices, core.device.upload_to_buffer(
BufferTarget::Index); quads_vertex_indices_buffer,
0,
&indices,
BufferTarget::Index,
);
self.quads_vertex_indices_buffer_id = Some(quads_vertex_indices_buffer_id); self.quads_vertex_indices_buffer_id = Some(quads_vertex_indices_buffer_id);
self.quads_vertex_indices_length = length; self.quads_vertex_indices_length = length;
} }
@ -179,221 +211,305 @@ impl<D> RendererD3D9<D> where D: Device {
} }
let fill_storage_info = self.upload_buffered_fills(core); let fill_storage_info = self.upload_buffered_fills(core);
self.draw_fills(core, fill_storage_info.fill_buffer_id, fill_storage_info.fill_count); self.draw_fills(
core.allocator.free_general_buffer(fill_storage_info.fill_buffer_id); core,
fill_storage_info.fill_buffer_id,
fill_storage_info.fill_count,
);
core.allocator
.free_general_buffer(fill_storage_info.fill_buffer_id);
} }
fn upload_buffered_fills(&mut self, core: &mut RendererCore<D>) -> FillBufferInfoD3D9 { fn upload_buffered_fills(&mut self, core: &mut RendererCore<D>) -> FillBufferInfoD3D9 {
let buffered_fills = &mut self.buffered_fills; let buffered_fills = &mut self.buffered_fills;
debug_assert!(!buffered_fills.is_empty()); debug_assert!(!buffered_fills.is_empty());
let fill_buffer_id = core.allocator let fill_buffer_id = core.allocator.allocate_general_buffer::<Fill>(
.allocate_general_buffer::<Fill>(&core.device, &core.device,
MAX_FILLS_PER_BATCH as u64, MAX_FILLS_PER_BATCH as u64,
BufferTag("Fill")); BufferTag("Fill"),
);
let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id); let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id);
debug_assert!(buffered_fills.len() <= u32::MAX as usize); debug_assert!(buffered_fills.len() <= u32::MAX as usize);
core.device.upload_to_buffer(fill_vertex_buffer, 0, &buffered_fills, BufferTarget::Vertex); core.device
.upload_to_buffer(fill_vertex_buffer, 0, &buffered_fills, BufferTarget::Vertex);
let fill_count = buffered_fills.len() as u32; let fill_count = buffered_fills.len() as u32;
buffered_fills.clear(); buffered_fills.clear();
FillBufferInfoD3D9 { fill_buffer_id, fill_count } FillBufferInfoD3D9 {
fill_buffer_id,
fill_count,
}
} }
fn draw_fills(&mut self, fn draw_fills(
core: &mut RendererCore<D>, &mut self,
fill_buffer_id: GeneralBufferID, core: &mut RendererCore<D>,
fill_count: u32) { fill_buffer_id: GeneralBufferID,
fill_count: u32,
) {
let fill_raster_program = &self.programs.fill_program; let fill_raster_program = &self.programs.fill_program;
let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id); let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id);
let quad_vertex_positions_buffer = let quad_vertex_positions_buffer = core
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id); .allocator
let quad_vertex_indices_buffer = core.allocator .get_general_buffer(core.quad_vertex_positions_buffer_id);
.get_index_buffer(core.quad_vertex_indices_buffer_id); let quad_vertex_indices_buffer = core
.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let area_lut_texture = core.allocator.get_texture(core.area_lut_texture_id); let area_lut_texture = core.allocator.get_texture(core.area_lut_texture_id);
let mask_viewport = self.mask_viewport(core); let mask_viewport = self.mask_viewport(core);
let mask_storage = core.mask_storage.as_ref().expect("Where's the mask storage?"); let mask_storage = core
.mask_storage
.as_ref()
.expect("Where's the mask storage?");
let mask_framebuffer_id = mask_storage.framebuffer_id; let mask_framebuffer_id = mask_storage.framebuffer_id;
let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id); let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id);
let fill_vertex_array = FillVertexArrayD3D9::new(&core.device, let fill_vertex_array = FillVertexArrayD3D9::new(
fill_raster_program, &core.device,
fill_vertex_buffer, fill_raster_program,
quad_vertex_positions_buffer, fill_vertex_buffer,
quad_vertex_indices_buffer); quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
let mut clear_color = None; let mut clear_color = None;
if !core.framebuffer_flags.contains(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY) { if !core
.framebuffer_flags
.contains(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY)
{
clear_color = Some(ColorF::default()); clear_color = Some(ColorF::default());
}; };
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device, let timer_query = core
&core.options); .timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
core.device.draw_elements_instanced(6, fill_count, &RenderState { core.device.draw_elements_instanced(
target: &RenderTarget::Framebuffer(mask_framebuffer), 6,
program: &fill_raster_program.program, fill_count,
vertex_array: &fill_vertex_array.vertex_array, &RenderState {
primitive: Primitive::Triangles, target: &RenderTarget::Framebuffer(mask_framebuffer),
textures: &[(&fill_raster_program.area_lut_texture, area_lut_texture)], program: &fill_raster_program.program,
uniforms: &[ vertex_array: &fill_vertex_array.vertex_array,
(&fill_raster_program.framebuffer_size_uniform, primitive: Primitive::Triangles,
UniformData::Vec2(mask_viewport.size().to_f32().0)), textures: &[(&fill_raster_program.area_lut_texture, area_lut_texture)],
(&fill_raster_program.tile_size_uniform, uniforms: &[
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), (
], &fill_raster_program.framebuffer_size_uniform,
images: &[], UniformData::Vec2(mask_viewport.size().to_f32().0),
storage_buffers: &[], ),
viewport: mask_viewport, (
options: RenderOptions { &fill_raster_program.tile_size_uniform,
blend: Some(BlendState { UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32)),
src_rgb_factor: BlendFactor::One, ),
src_alpha_factor: BlendFactor::One, ],
dest_rgb_factor: BlendFactor::One, images: &[],
dest_alpha_factor: BlendFactor::One, storage_buffers: &[],
..BlendState::default() viewport: mask_viewport,
}), options: RenderOptions {
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, blend: Some(BlendState {
..RenderOptions::default() src_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
}),
clear_ops: ClearOps {
color: clear_color,
..ClearOps::default()
},
..RenderOptions::default()
},
}, },
}); );
core.stats.drawcall_count += 1; core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query); core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Fill, timer_query); core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Fill, timer_query);
core.framebuffer_flags.insert(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY); core.framebuffer_flags
.insert(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY);
} }
fn clip_tiles(&mut self, core: &mut RendererCore<D>, clip_buffer_info: &ClipBufferInfo) { fn clip_tiles(&mut self, core: &mut RendererCore<D>, clip_buffer_info: &ClipBufferInfo) {
// Allocate temp mask framebuffer. // Allocate temp mask framebuffer.
let mask_temp_framebuffer_id = let mask_temp_framebuffer_id = core.allocator.allocate_framebuffer(
core.allocator.allocate_framebuffer(&core.device, &core.device,
self.mask_viewport(core).size(), self.mask_viewport(core).size(),
core.mask_texture_format(), core.mask_texture_format(),
FramebufferTag("TempClipMaskD3D9")); FramebufferTag("TempClipMaskD3D9"),
);
let mask_temp_framebuffer = core.allocator.get_framebuffer(mask_temp_framebuffer_id); let mask_temp_framebuffer = core.allocator.get_framebuffer(mask_temp_framebuffer_id);
let mask_storage = core.mask_storage.as_ref().expect("Where's the mask storage?"); let mask_storage = core
.mask_storage
.as_ref()
.expect("Where's the mask storage?");
let mask_framebuffer_id = mask_storage.framebuffer_id; let mask_framebuffer_id = mask_storage.framebuffer_id;
let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id); let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id);
let mask_texture = core.device.framebuffer_texture(mask_framebuffer); let mask_texture = core.device.framebuffer_texture(mask_framebuffer);
let mask_texture_size = core.device.texture_size(&mask_texture); let mask_texture_size = core.device.texture_size(&mask_texture);
let clip_vertex_buffer = core.allocator let clip_vertex_buffer = core
.get_general_buffer(clip_buffer_info.clip_buffer_id); .allocator
let quad_vertex_positions_buffer = .get_general_buffer(clip_buffer_info.clip_buffer_id);
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id); let quad_vertex_positions_buffer = core
let quad_vertex_indices_buffer = core.allocator .allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id); .get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core
.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let tile_clip_copy_vertex_array = let tile_clip_copy_vertex_array = ClipTileCopyVertexArrayD3D9::new(
ClipTileCopyVertexArrayD3D9::new(&core.device, &core.device,
&self.programs.tile_clip_copy_program, &self.programs.tile_clip_copy_program,
clip_vertex_buffer, clip_vertex_buffer,
quad_vertex_positions_buffer, quad_vertex_positions_buffer,
quad_vertex_indices_buffer); quad_vertex_indices_buffer,
let tile_clip_combine_vertex_array = );
ClipTileCombineVertexArrayD3D9::new(&core.device, let tile_clip_combine_vertex_array = ClipTileCombineVertexArrayD3D9::new(
&self.programs.tile_clip_combine_program, &core.device,
clip_vertex_buffer, &self.programs.tile_clip_combine_program,
quad_vertex_positions_buffer, clip_vertex_buffer,
quad_vertex_indices_buffer); quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device, let timer_query = core
&core.options); .timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
// Copy out tiles. // Copy out tiles.
// //
// TODO(pcwalton): Don't do this on GL4. // TODO(pcwalton): Don't do this on GL4.
core.device.draw_elements_instanced(6, clip_buffer_info.clip_count * 2, &RenderState { core.device.draw_elements_instanced(
target: &RenderTarget::Framebuffer(mask_temp_framebuffer), 6,
program: &self.programs.tile_clip_copy_program.program, clip_buffer_info.clip_count * 2,
vertex_array: &tile_clip_copy_vertex_array.vertex_array, &RenderState {
primitive: Primitive::Triangles, target: &RenderTarget::Framebuffer(mask_temp_framebuffer),
textures: &[ program: &self.programs.tile_clip_copy_program.program,
(&self.programs.tile_clip_copy_program.src_texture, vertex_array: &tile_clip_copy_vertex_array.vertex_array,
core.device.framebuffer_texture(mask_framebuffer)), primitive: Primitive::Triangles,
], textures: &[(
images: &[], &self.programs.tile_clip_copy_program.src_texture,
uniforms: &[ core.device.framebuffer_texture(mask_framebuffer),
(&self.programs.tile_clip_copy_program.framebuffer_size_uniform, )],
UniformData::Vec2(mask_texture_size.to_f32().0)), images: &[],
], uniforms: &[(
storage_buffers: &[], &self
viewport: RectI::new(Vector2I::zero(), mask_texture_size), .programs
options: RenderOptions::default(), .tile_clip_copy_program
}); .framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0),
)],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
},
);
core.stats.drawcall_count += 1; core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query); core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Other, timer_query); core.current_timer
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device, .as_mut()
&core.options); .unwrap()
.push_query(TimeCategory::Other, timer_query);
let timer_query = core
.timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
// Combine clip tiles. // Combine clip tiles.
core.device.draw_elements_instanced(6, clip_buffer_info.clip_count, &RenderState { core.device.draw_elements_instanced(
target: &RenderTarget::Framebuffer(mask_framebuffer), 6,
program: &self.programs.tile_clip_combine_program.program, clip_buffer_info.clip_count,
vertex_array: &tile_clip_combine_vertex_array.vertex_array, &RenderState {
primitive: Primitive::Triangles, target: &RenderTarget::Framebuffer(mask_framebuffer),
textures: &[ program: &self.programs.tile_clip_combine_program.program,
(&self.programs.tile_clip_combine_program.src_texture, vertex_array: &tile_clip_combine_vertex_array.vertex_array,
core.device.framebuffer_texture(&mask_temp_framebuffer)), primitive: Primitive::Triangles,
], textures: &[(
images: &[], &self.programs.tile_clip_combine_program.src_texture,
uniforms: &[ core.device.framebuffer_texture(&mask_temp_framebuffer),
(&self.programs.tile_clip_combine_program.framebuffer_size_uniform, )],
UniformData::Vec2(mask_texture_size.to_f32().0)), images: &[],
], uniforms: &[(
storage_buffers: &[], &self
viewport: RectI::new(Vector2I::zero(), mask_texture_size), .programs
options: RenderOptions::default(), .tile_clip_combine_program
}); .framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0),
)],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
},
);
core.stats.drawcall_count += 1; core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query); core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Other, timer_query); core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Other, timer_query);
core.allocator.free_framebuffer(mask_temp_framebuffer_id); core.allocator.free_framebuffer(mask_temp_framebuffer_id);
} }
fn upload_z_buffer(&mut self, core: &mut RendererCore<D>, z_buffer_map: &DenseTileMap<i32>) fn upload_z_buffer(
-> TextureID { &mut self,
let z_buffer_texture_id = core.allocator.allocate_texture(&core.device, core: &mut RendererCore<D>,
z_buffer_map.rect.size(), z_buffer_map: &DenseTileMap<i32>,
TextureFormat::RGBA8, ) -> TextureID {
TextureTag("ZBufferD3D9")); let z_buffer_texture_id = core.allocator.allocate_texture(
&core.device,
z_buffer_map.rect.size(),
TextureFormat::RGBA8,
TextureTag("ZBufferD3D9"),
);
let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id); let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id);
debug_assert_eq!(z_buffer_map.rect.origin(), Vector2I::default()); debug_assert_eq!(z_buffer_map.rect.origin(), Vector2I::default());
let z_data: &[u8] = z_buffer_map.data.as_byte_slice(); let z_data: &[u8] = z_buffer_map.data.as_byte_slice();
core.device.upload_to_texture(z_buffer_texture, core.device.upload_to_texture(
z_buffer_map.rect, z_buffer_texture,
TextureDataRef::U8(&z_data)); z_buffer_map.rect,
TextureDataRef::U8(&z_data),
);
z_buffer_texture_id z_buffer_texture_id
} }
// Uploads clip tiles from CPU to GPU. // Uploads clip tiles from CPU to GPU.
fn upload_clip_tiles(&mut self, core: &mut RendererCore<D>, clips: &[Clip]) -> ClipBufferInfo { fn upload_clip_tiles(&mut self, core: &mut RendererCore<D>, clips: &[Clip]) -> ClipBufferInfo {
let clip_buffer_id = core.allocator.allocate_general_buffer::<Clip>(&core.device, let clip_buffer_id = core.allocator.allocate_general_buffer::<Clip>(
clips.len() as u64, &core.device,
BufferTag("ClipD3D9")); clips.len() as u64,
BufferTag("ClipD3D9"),
);
let clip_buffer = core.allocator.get_general_buffer(clip_buffer_id); let clip_buffer = core.allocator.get_general_buffer(clip_buffer_id);
core.device.upload_to_buffer(clip_buffer, 0, clips, BufferTarget::Vertex); core.device
ClipBufferInfo { clip_buffer_id, clip_count: clips.len() as u32 } .upload_to_buffer(clip_buffer, 0, clips, BufferTarget::Vertex);
ClipBufferInfo {
clip_buffer_id,
clip_count: clips.len() as u32,
}
} }
fn draw_tiles(&mut self, fn draw_tiles(
core: &mut RendererCore<D>, &mut self,
tile_count: u32, core: &mut RendererCore<D>,
tile_vertex_buffer_id: GeneralBufferID, tile_count: u32,
color_texture_0: Option<TileBatchTexture>, tile_vertex_buffer_id: GeneralBufferID,
blend_mode: BlendMode, color_texture_0: Option<TileBatchTexture>,
z_buffer_texture_id: TextureID) { blend_mode: BlendMode,
z_buffer_texture_id: TextureID,
) {
// TODO(pcwalton): Disable blend for solid tiles. // TODO(pcwalton): Disable blend for solid tiles.
if tile_count == 0 { if tile_count == 0 {
@ -410,79 +526,112 @@ impl<D> RendererD3D9<D> where D: Device {
let clear_color = core.clear_color_for_draw_operation(); let clear_color = core.clear_color_for_draw_operation();
let draw_viewport = core.draw_viewport(); let draw_viewport = core.draw_viewport();
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device, let timer_query = core
&core.options); .timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
let tile_raster_program = &self.programs.tile_program; let tile_raster_program = &self.programs.tile_program;
let tile_vertex_buffer = core.allocator.get_general_buffer(tile_vertex_buffer_id); let tile_vertex_buffer = core.allocator.get_general_buffer(tile_vertex_buffer_id);
let quad_vertex_positions_buffer = let quad_vertex_positions_buffer = core
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id); .allocator
let quad_vertex_indices_buffer = core.allocator .get_general_buffer(core.quad_vertex_positions_buffer_id);
.get_index_buffer(core.quad_vertex_indices_buffer_id); let quad_vertex_indices_buffer = core
let dest_blend_framebuffer = core.allocator .allocator
.get_framebuffer(self.dest_blend_framebuffer_id); .get_index_buffer(core.quad_vertex_indices_buffer_id);
let dest_blend_framebuffer = core
.allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
let (mut textures, mut uniforms) = (vec![], vec![]); let (mut textures, mut uniforms) = (vec![], vec![]);
core.set_uniforms_for_drawing_tiles(&tile_raster_program.common, core.set_uniforms_for_drawing_tiles(
&mut textures, &tile_raster_program.common,
&mut uniforms, &mut textures,
color_texture_0); &mut uniforms,
color_texture_0,
);
uniforms.push((&tile_raster_program.transform_uniform, uniforms.push((
UniformData::Mat4(self.tile_transform(core).to_columns()))); &tile_raster_program.transform_uniform,
textures.push((&tile_raster_program.dest_texture, UniformData::Mat4(self.tile_transform(core).to_columns()),
core.device.framebuffer_texture(dest_blend_framebuffer))); ));
textures.push((
&tile_raster_program.dest_texture,
core.device.framebuffer_texture(dest_blend_framebuffer),
));
let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id); let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id);
textures.push((&tile_raster_program.common.z_buffer_texture, z_buffer_texture)); textures.push((
uniforms.push((&tile_raster_program.common.z_buffer_texture_size_uniform, &tile_raster_program.common.z_buffer_texture,
UniformData::IVec2(core.device.texture_size(z_buffer_texture).0))); z_buffer_texture,
));
uniforms.push((
&tile_raster_program.common.z_buffer_texture_size_uniform,
UniformData::IVec2(core.device.texture_size(z_buffer_texture).0),
));
let tile_vertex_array = TileVertexArrayD3D9::new(&core.device, let tile_vertex_array = TileVertexArrayD3D9::new(
&self.programs.tile_program, &core.device,
tile_vertex_buffer, &self.programs.tile_program,
quad_vertex_positions_buffer, tile_vertex_buffer,
quad_vertex_indices_buffer); quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
core.device.draw_elements_instanced(6, tile_count, &RenderState { core.device.draw_elements_instanced(
target: &core.draw_render_target(), 6,
program: &tile_raster_program.common.program, tile_count,
vertex_array: &tile_vertex_array.vertex_array, &RenderState {
primitive: Primitive::Triangles, target: &core.draw_render_target(),
textures: &textures, program: &tile_raster_program.common.program,
images: &[], vertex_array: &tile_vertex_array.vertex_array,
storage_buffers: &[], primitive: Primitive::Triangles,
uniforms: &uniforms, textures: &textures,
viewport: draw_viewport, images: &[],
options: RenderOptions { storage_buffers: &[],
blend: blend_mode.to_blend_state(), uniforms: &uniforms,
stencil: self.stencil_state(core), viewport: draw_viewport,
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, options: RenderOptions {
..RenderOptions::default() blend: blend_mode.to_blend_state(),
stencil: self.stencil_state(core),
clear_ops: ClearOps {
color: clear_color,
..ClearOps::default()
},
..RenderOptions::default()
},
}, },
}); );
core.stats.drawcall_count += 1; core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query); core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Composite, timer_query); core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Composite, timer_query);
core.preserve_draw_framebuffer(); core.preserve_draw_framebuffer();
} }
fn copy_alpha_tiles_to_dest_blend_texture(&mut self, fn copy_alpha_tiles_to_dest_blend_texture(
core: &mut RendererCore<D>, &mut self,
tile_count: u32, core: &mut RendererCore<D>,
vertex_buffer_id: GeneralBufferID) { tile_count: u32,
vertex_buffer_id: GeneralBufferID,
) {
let draw_viewport = core.draw_viewport(); let draw_viewport = core.draw_viewport();
let mut textures = vec![]; let mut textures = vec![];
let mut uniforms = vec![ let mut uniforms = vec![
(&self.programs.tile_copy_program.transform_uniform, (
UniformData::Mat4(self.tile_transform(core).to_columns())), &self.programs.tile_copy_program.transform_uniform,
(&self.programs.tile_copy_program.tile_size_uniform, UniformData::Mat4(self.tile_transform(core).to_columns()),
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), ),
(
&self.programs.tile_copy_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32)),
),
]; ];
let draw_framebuffer = match core.draw_render_target() { let draw_framebuffer = match core.draw_render_target() {
@ -492,41 +641,51 @@ impl<D> RendererD3D9<D> where D: Device {
let draw_texture = core.device.framebuffer_texture(&draw_framebuffer); let draw_texture = core.device.framebuffer_texture(&draw_framebuffer);
textures.push((&self.programs.tile_copy_program.src_texture, draw_texture)); textures.push((&self.programs.tile_copy_program.src_texture, draw_texture));
uniforms.push((&self.programs.tile_copy_program.framebuffer_size_uniform, uniforms.push((
UniformData::Vec2(draw_viewport.size().to_f32().0))); &self.programs.tile_copy_program.framebuffer_size_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0),
));
let quads_vertex_indices_buffer_id = self.quads_vertex_indices_buffer_id let quads_vertex_indices_buffer_id = self
.expect("Where's the quads vertex buffer?"); .quads_vertex_indices_buffer_id
let quads_vertex_indices_buffer = core.allocator .expect("Where's the quads vertex buffer?");
.get_index_buffer(quads_vertex_indices_buffer_id); let quads_vertex_indices_buffer = core
.allocator
.get_index_buffer(quads_vertex_indices_buffer_id);
let vertex_buffer = core.allocator.get_general_buffer(vertex_buffer_id); let vertex_buffer = core.allocator.get_general_buffer(vertex_buffer_id);
let tile_copy_vertex_array = CopyTileVertexArray::new(&core.device, let tile_copy_vertex_array = CopyTileVertexArray::new(
&self.programs.tile_copy_program, &core.device,
vertex_buffer, &self.programs.tile_copy_program,
quads_vertex_indices_buffer); vertex_buffer,
quads_vertex_indices_buffer,
);
let dest_blend_framebuffer = core.allocator let dest_blend_framebuffer = core
.get_framebuffer(self.dest_blend_framebuffer_id); .allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
core.device.draw_elements(tile_count * 6, &RenderState { core.device.draw_elements(
target: &RenderTarget::Framebuffer(dest_blend_framebuffer), tile_count * 6,
program: &self.programs.tile_copy_program.program, &RenderState {
vertex_array: &tile_copy_vertex_array.vertex_array, target: &RenderTarget::Framebuffer(dest_blend_framebuffer),
primitive: Primitive::Triangles, program: &self.programs.tile_copy_program.program,
textures: &textures, vertex_array: &tile_copy_vertex_array.vertex_array,
images: &[], primitive: Primitive::Triangles,
storage_buffers: &[], textures: &textures,
uniforms: &uniforms, images: &[],
viewport: draw_viewport, storage_buffers: &[],
options: RenderOptions { uniforms: &uniforms,
clear_ops: ClearOps { viewport: draw_viewport,
color: Some(ColorF::new(1.0, 0.0, 0.0, 1.0)), options: RenderOptions {
..ClearOps::default() clear_ops: ClearOps {
color: Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
..ClearOps::default()
},
..RenderOptions::default()
}, },
..RenderOptions::default()
}, },
}); );
core.stats.drawcall_count += 1; core.stats.drawcall_count += 1;
} }

View File

@ -10,313 +10,478 @@
//! Shaders and vertex specifications for the Direct3D 9-level renderer. //! Shaders and vertex specifications for the Direct3D 9-level renderer.
use crate::gpu::shaders::{TILE_INSTANCE_SIZE, TileProgramCommon}; use crate::gpu::shaders::{TileProgramCommon, TILE_INSTANCE_SIZE};
use pathfinder_gpu::{BufferTarget, Device, VertexAttrClass, VertexAttrDescriptor, VertexAttrType}; use pathfinder_gpu::{BufferTarget, Device, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
const FILL_INSTANCE_SIZE: usize = 12; const FILL_INSTANCE_SIZE: usize = 12;
const CLIP_TILE_INSTANCE_SIZE: usize = 16; const CLIP_TILE_INSTANCE_SIZE: usize = 16;
pub(crate) struct FillVertexArrayD3D9<D> where D: Device { pub(crate) struct FillVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> FillVertexArrayD3D9<D> where D: Device { impl<D> FillVertexArrayD3D9<D>
pub(crate) fn new(device: &D, where
fill_program: &FillProgramD3D9<D>, D: Device,
vertex_buffer: &D::Buffer, {
quad_vertex_positions_buffer: &D::Buffer, pub(crate) fn new(
quad_vertex_indices_buffer: &D::Buffer) device: &D,
-> FillVertexArrayD3D9<D> { fill_program: &FillProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> FillVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let tess_coord_attr = device.get_vertex_attr(&fill_program.program, "TessCoord").unwrap(); let tess_coord_attr = device
let line_segment_attr = device.get_vertex_attr(&fill_program.program, "LineSegment") .get_vertex_attr(&fill_program.program, "TessCoord")
.unwrap(); .unwrap();
let tile_index_attr = device.get_vertex_attr(&fill_program.program, "TileIndex").unwrap(); let line_segment_attr = device
.get_vertex_attr(&fill_program.program, "LineSegment")
.unwrap();
let tile_index_attr = device
.get_vertex_attr(&fill_program.program, "TileIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &tess_coord_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::U16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &tess_coord_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &line_segment_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 4, &vertex_array,
class: VertexAttrClass::Int, &line_segment_attr,
attr_type: VertexAttrType::U16, &VertexAttrDescriptor {
stride: FILL_INSTANCE_SIZE, size: 4,
offset: 0, class: VertexAttrClass::Int,
divisor: 1, attr_type: VertexAttrType::U16,
buffer_index: 1, stride: FILL_INSTANCE_SIZE,
}); offset: 0,
device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor { divisor: 1,
size: 1, buffer_index: 1,
class: VertexAttrClass::Int, },
attr_type: VertexAttrType::I32, );
stride: FILL_INSTANCE_SIZE, device.configure_vertex_attr(
offset: 8, &vertex_array,
divisor: 1, &tile_index_attr,
buffer_index: 1, &VertexAttrDescriptor {
}); size: 1,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: FILL_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
FillVertexArrayD3D9 { vertex_array } FillVertexArrayD3D9 { vertex_array }
} }
} }
pub(crate) struct TileVertexArrayD3D9<D> where D: Device { pub(crate) struct TileVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> TileVertexArrayD3D9<D> where D: Device { impl<D> TileVertexArrayD3D9<D>
pub(crate) fn new(device: &D, where
tile_program: &TileProgramD3D9<D>, D: Device,
tile_vertex_buffer: &D::Buffer, {
quad_vertex_positions_buffer: &D::Buffer, pub(crate) fn new(
quad_vertex_indices_buffer: &D::Buffer) device: &D,
-> TileVertexArrayD3D9<D> { tile_program: &TileProgramD3D9<D>,
tile_vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> TileVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let tile_offset_attr = let tile_offset_attr = device
device.get_vertex_attr(&tile_program.common.program, "TileOffset").unwrap(); .get_vertex_attr(&tile_program.common.program, "TileOffset")
let tile_origin_attr = .unwrap();
device.get_vertex_attr(&tile_program.common.program, "TileOrigin").unwrap(); let tile_origin_attr = device
let mask_0_tex_coord_attr = .get_vertex_attr(&tile_program.common.program, "TileOrigin")
device.get_vertex_attr(&tile_program.common.program, "MaskTexCoord0").unwrap(); .unwrap();
let ctrl_backdrop_attr = let mask_0_tex_coord_attr = device
device.get_vertex_attr(&tile_program.common.program, "CtrlBackdrop").unwrap(); .get_vertex_attr(&tile_program.common.program, "MaskTexCoord0")
let color_attr = device.get_vertex_attr(&tile_program.common.program, "Color").unwrap(); .unwrap();
let path_index_attr = device.get_vertex_attr(&tile_program.common.program, "PathIndex") let ctrl_backdrop_attr = device
.unwrap(); .get_vertex_attr(&tile_program.common.program, "CtrlBackdrop")
.unwrap();
let color_attr = device
.get_vertex_attr(&tile_program.common.program, "Color")
.unwrap();
let path_index_attr = device
.get_vertex_attr(&tile_program.common.program, "PathIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &tile_offset_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, tile_vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_origin_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 2, &vertex_array,
class: VertexAttrClass::Int, &tile_origin_attr,
attr_type: VertexAttrType::I16, &VertexAttrDescriptor {
stride: TILE_INSTANCE_SIZE, size: 2,
offset: 0, class: VertexAttrClass::Int,
divisor: 1, attr_type: VertexAttrType::I16,
buffer_index: 1, stride: TILE_INSTANCE_SIZE,
}); offset: 0,
device.configure_vertex_attr(&vertex_array, &mask_0_tex_coord_attr, &VertexAttrDescriptor { divisor: 1,
size: 4, buffer_index: 1,
class: VertexAttrClass::Int, },
attr_type: VertexAttrType::U8, );
stride: TILE_INSTANCE_SIZE, device.configure_vertex_attr(
offset: 4, &vertex_array,
divisor: 1, &mask_0_tex_coord_attr,
buffer_index: 1, &VertexAttrDescriptor {
}); size: 4,
device.configure_vertex_attr(&vertex_array, &path_index_attr, &VertexAttrDescriptor { class: VertexAttrClass::Int,
size: 1, attr_type: VertexAttrType::U8,
class: VertexAttrClass::Int, stride: TILE_INSTANCE_SIZE,
attr_type: VertexAttrType::I32, offset: 4,
stride: TILE_INSTANCE_SIZE, divisor: 1,
offset: 8, buffer_index: 1,
divisor: 1, },
buffer_index: 1, );
}); device.configure_vertex_attr(
device.configure_vertex_attr(&vertex_array, &color_attr, &VertexAttrDescriptor { &vertex_array,
size: 1, &path_index_attr,
class: VertexAttrClass::Int, &VertexAttrDescriptor {
attr_type: VertexAttrType::I16, size: 1,
stride: TILE_INSTANCE_SIZE, class: VertexAttrClass::Int,
offset: 12, attr_type: VertexAttrType::I32,
divisor: 1, stride: TILE_INSTANCE_SIZE,
buffer_index: 1, offset: 8,
}); divisor: 1,
device.configure_vertex_attr(&vertex_array, &ctrl_backdrop_attr, &VertexAttrDescriptor { buffer_index: 1,
size: 2, },
class: VertexAttrClass::Int, );
attr_type: VertexAttrType::I8, device.configure_vertex_attr(
stride: TILE_INSTANCE_SIZE, &vertex_array,
offset: 14, &color_attr,
divisor: 1, &VertexAttrDescriptor {
buffer_index: 1, size: 1,
}); class: VertexAttrClass::Int,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&ctrl_backdrop_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I8,
stride: TILE_INSTANCE_SIZE,
offset: 14,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
TileVertexArrayD3D9 { vertex_array } TileVertexArrayD3D9 { vertex_array }
} }
} }
pub(crate) struct ClipTileCopyVertexArrayD3D9<D> where D: Device { pub(crate) struct ClipTileCopyVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> ClipTileCopyVertexArrayD3D9<D> where D: Device { impl<D> ClipTileCopyVertexArrayD3D9<D>
pub(crate) fn new(device: &D, where
clip_tile_copy_program: &ClipTileCopyProgramD3D9<D>, D: Device,
vertex_buffer: &D::Buffer, {
quad_vertex_positions_buffer: &D::Buffer, pub(crate) fn new(
quad_vertex_indices_buffer: &D::Buffer) device: &D,
-> ClipTileCopyVertexArrayD3D9<D> { clip_tile_copy_program: &ClipTileCopyProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClipTileCopyVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let tile_offset_attr = let tile_offset_attr = device
device.get_vertex_attr(&clip_tile_copy_program.program, "TileOffset").unwrap(); .get_vertex_attr(&clip_tile_copy_program.program, "TileOffset")
let tile_index_attr = .unwrap();
device.get_vertex_attr(&clip_tile_copy_program.program, "TileIndex").unwrap(); let tile_index_attr = device
.get_vertex_attr(&clip_tile_copy_program.program, "TileIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &tile_offset_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 1, &vertex_array,
class: VertexAttrClass::Int, &tile_index_attr,
attr_type: VertexAttrType::I32, &VertexAttrDescriptor {
stride: CLIP_TILE_INSTANCE_SIZE / 2, size: 1,
offset: 0, class: VertexAttrClass::Int,
divisor: 1, attr_type: VertexAttrType::I32,
buffer_index: 1, stride: CLIP_TILE_INSTANCE_SIZE / 2,
}); offset: 0,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClipTileCopyVertexArrayD3D9 { vertex_array } ClipTileCopyVertexArrayD3D9 { vertex_array }
} }
} }
pub(crate) struct ClipTileCombineVertexArrayD3D9<D> where D: Device { pub(crate) struct ClipTileCombineVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> ClipTileCombineVertexArrayD3D9<D> where D: Device { impl<D> ClipTileCombineVertexArrayD3D9<D>
pub(crate) fn new(device: &D, where
clip_tile_combine_program: &ClipTileCombineProgramD3D9<D>, D: Device,
vertex_buffer: &D::Buffer, {
quad_vertex_positions_buffer: &D::Buffer, pub(crate) fn new(
quad_vertex_indices_buffer: &D::Buffer) device: &D,
-> ClipTileCombineVertexArrayD3D9<D> { clip_tile_combine_program: &ClipTileCombineProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClipTileCombineVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let tile_offset_attr = let tile_offset_attr = device
device.get_vertex_attr(&clip_tile_combine_program.program, "TileOffset").unwrap(); .get_vertex_attr(&clip_tile_combine_program.program, "TileOffset")
let dest_tile_index_attr = .unwrap();
device.get_vertex_attr(&clip_tile_combine_program.program, "DestTileIndex").unwrap(); let dest_tile_index_attr = device
let dest_backdrop_attr = .get_vertex_attr(&clip_tile_combine_program.program, "DestTileIndex")
device.get_vertex_attr(&clip_tile_combine_program.program, "DestBackdrop").unwrap(); .unwrap();
let src_tile_index_attr = let dest_backdrop_attr = device
device.get_vertex_attr(&clip_tile_combine_program.program, "SrcTileIndex").unwrap(); .get_vertex_attr(&clip_tile_combine_program.program, "DestBackdrop")
let src_backdrop_attr = .unwrap();
device.get_vertex_attr(&clip_tile_combine_program.program, "SrcBackdrop").unwrap(); let src_tile_index_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "SrcTileIndex")
.unwrap();
let src_backdrop_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "SrcBackdrop")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &tile_offset_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &dest_tile_index_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 1, &vertex_array,
class: VertexAttrClass::Int, &dest_tile_index_attr,
attr_type: VertexAttrType::I32, &VertexAttrDescriptor {
stride: CLIP_TILE_INSTANCE_SIZE, size: 1,
offset: 0, class: VertexAttrClass::Int,
divisor: 1, attr_type: VertexAttrType::I32,
buffer_index: 1, stride: CLIP_TILE_INSTANCE_SIZE,
}); offset: 0,
device.configure_vertex_attr(&vertex_array, &dest_backdrop_attr, &VertexAttrDescriptor { divisor: 1,
size: 1, buffer_index: 1,
class: VertexAttrClass::Int, },
attr_type: VertexAttrType::I32, );
stride: CLIP_TILE_INSTANCE_SIZE, device.configure_vertex_attr(
offset: 4, &vertex_array,
divisor: 1, &dest_backdrop_attr,
buffer_index: 1, &VertexAttrDescriptor {
}); size: 1,
device.configure_vertex_attr(&vertex_array, &src_tile_index_attr, &VertexAttrDescriptor { class: VertexAttrClass::Int,
size: 1, attr_type: VertexAttrType::I32,
class: VertexAttrClass::Int, stride: CLIP_TILE_INSTANCE_SIZE,
attr_type: VertexAttrType::I32, offset: 4,
stride: CLIP_TILE_INSTANCE_SIZE, divisor: 1,
offset: 8, buffer_index: 1,
divisor: 1, },
buffer_index: 1, );
}); device.configure_vertex_attr(
device.configure_vertex_attr(&vertex_array, &src_backdrop_attr, &VertexAttrDescriptor { &vertex_array,
size: 1, &src_tile_index_attr,
class: VertexAttrClass::Int, &VertexAttrDescriptor {
attr_type: VertexAttrType::I32, size: 1,
stride: CLIP_TILE_INSTANCE_SIZE, class: VertexAttrClass::Int,
offset: 12, attr_type: VertexAttrType::I32,
divisor: 1, stride: CLIP_TILE_INSTANCE_SIZE,
buffer_index: 1, offset: 8,
}); divisor: 1,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&src_backdrop_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClipTileCombineVertexArrayD3D9 { vertex_array } ClipTileCombineVertexArrayD3D9 { vertex_array }
} }
} }
pub(crate) struct CopyTileVertexArray<D> where D: Device { pub(crate) struct CopyTileVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> CopyTileVertexArray<D> where D: Device { impl<D> CopyTileVertexArray<D>
pub(crate) fn new(device: &D, where
copy_tile_program: &CopyTileProgram<D>, D: Device,
copy_tile_vertex_buffer: &D::Buffer, {
quads_vertex_indices_buffer: &D::Buffer) pub(crate) fn new(
-> CopyTileVertexArray<D> { device: &D,
copy_tile_program: &CopyTileProgram<D>,
copy_tile_vertex_buffer: &D::Buffer,
quads_vertex_indices_buffer: &D::Buffer,
) -> CopyTileVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let tile_position_attr = let tile_position_attr = device
device.get_vertex_attr(&copy_tile_program.program, "TilePosition").unwrap(); .get_vertex_attr(&copy_tile_program.program, "TilePosition")
.unwrap();
device.bind_buffer(&vertex_array, copy_tile_vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, copy_tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 2, &vertex_array,
class: VertexAttrClass::Int, &tile_position_attr,
attr_type: VertexAttrType::I16, &VertexAttrDescriptor {
stride: TILE_INSTANCE_SIZE, size: 2,
offset: 0, class: VertexAttrClass::Int,
divisor: 0, attr_type: VertexAttrType::I16,
buffer_index: 0, stride: TILE_INSTANCE_SIZE,
}); offset: 0,
device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index); divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quads_vertex_indices_buffer,
BufferTarget::Index,
);
CopyTileVertexArray { vertex_array } CopyTileVertexArray { vertex_array }
} }
} }
pub(crate) struct FillProgramD3D9<D> where D: Device { pub(crate) struct FillProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) tile_size_uniform: D::Uniform, pub(crate) tile_size_uniform: D::Uniform,
pub(crate) area_lut_texture: D::TextureParameter, pub(crate) area_lut_texture: D::TextureParameter,
} }
impl<D> FillProgramD3D9<D> where D: Device { impl<D> FillProgramD3D9<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D9<D> { fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/fill"); let program = device.create_raster_program(resources, "d3d9/fill");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
@ -331,54 +496,86 @@ impl<D> FillProgramD3D9<D> where D: Device {
} }
} }
pub(crate) struct TileProgramD3D9<D> where D: Device { pub(crate) struct TileProgramD3D9<D>
where
D: Device,
{
pub(crate) common: TileProgramCommon<D>, pub(crate) common: TileProgramCommon<D>,
pub(crate) dest_texture: D::TextureParameter, pub(crate) dest_texture: D::TextureParameter,
pub(crate) transform_uniform: D::Uniform, pub(crate) transform_uniform: D::Uniform,
} }
impl<D> TileProgramD3D9<D> where D: Device { impl<D> TileProgramD3D9<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D9<D> { fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile"); let program = device.create_raster_program(resources, "d3d9/tile");
let dest_texture = device.get_texture_parameter(&program, "DestTexture"); let dest_texture = device.get_texture_parameter(&program, "DestTexture");
let transform_uniform = device.get_uniform(&program, "Transform"); let transform_uniform = device.get_uniform(&program, "Transform");
let common = TileProgramCommon::new(device, program); let common = TileProgramCommon::new(device, program);
TileProgramD3D9 { common, dest_texture, transform_uniform } TileProgramD3D9 {
common,
dest_texture,
transform_uniform,
}
} }
} }
pub(crate) struct ClipTileCombineProgramD3D9<D> where D: Device { pub(crate) struct ClipTileCombineProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) src_texture: D::TextureParameter, pub(crate) src_texture: D::TextureParameter,
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
} }
impl<D> ClipTileCombineProgramD3D9<D> where D: Device { impl<D> ClipTileCombineProgramD3D9<D>
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) where
-> ClipTileCombineProgramD3D9<D> { D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileCombineProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile_clip_combine"); let program = device.create_raster_program(resources, "d3d9/tile_clip_combine");
let src_texture = device.get_texture_parameter(&program, "Src"); let src_texture = device.get_texture_parameter(&program, "Src");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
ClipTileCombineProgramD3D9 { program, src_texture, framebuffer_size_uniform } ClipTileCombineProgramD3D9 {
program,
src_texture,
framebuffer_size_uniform,
}
} }
} }
pub(crate) struct ClipTileCopyProgramD3D9<D> where D: Device { pub(crate) struct ClipTileCopyProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) src_texture: D::TextureParameter, pub(crate) src_texture: D::TextureParameter,
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
} }
impl<D> ClipTileCopyProgramD3D9<D> where D: Device { impl<D> ClipTileCopyProgramD3D9<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileCopyProgramD3D9<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileCopyProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile_clip_copy"); let program = device.create_raster_program(resources, "d3d9/tile_clip_copy");
let src_texture = device.get_texture_parameter(&program, "Src"); let src_texture = device.get_texture_parameter(&program, "Src");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
ClipTileCopyProgramD3D9 { program, src_texture, framebuffer_size_uniform } ClipTileCopyProgramD3D9 {
program,
src_texture,
framebuffer_size_uniform,
}
} }
} }
pub(crate) struct CopyTileProgram<D> where D: Device { pub(crate) struct CopyTileProgram<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) transform_uniform: D::Uniform, pub(crate) transform_uniform: D::Uniform,
pub(crate) tile_size_uniform: D::Uniform, pub(crate) tile_size_uniform: D::Uniform,
@ -386,7 +583,10 @@ pub(crate) struct CopyTileProgram<D> where D: Device {
pub(crate) src_texture: D::TextureParameter, pub(crate) src_texture: D::TextureParameter,
} }
impl<D> CopyTileProgram<D> where D: Device { impl<D> CopyTileProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> CopyTileProgram<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> CopyTileProgram<D> {
let program = device.create_raster_program(resources, "d3d9/tile_copy"); let program = device.create_raster_program(resources, "d3d9/tile_copy");
let transform_uniform = device.get_uniform(&program, "Transform"); let transform_uniform = device.get_uniform(&program, "Transform");
@ -403,7 +603,10 @@ impl<D> CopyTileProgram<D> where D: Device {
} }
} }
pub(crate) struct ProgramsD3D9<D> where D: Device { pub(crate) struct ProgramsD3D9<D>
where
D: Device,
{
pub(crate) fill_program: FillProgramD3D9<D>, pub(crate) fill_program: FillProgramD3D9<D>,
pub(crate) tile_program: TileProgramD3D9<D>, pub(crate) tile_program: TileProgramD3D9<D>,
pub(crate) tile_clip_copy_program: ClipTileCopyProgramD3D9<D>, pub(crate) tile_clip_copy_program: ClipTileCopyProgramD3D9<D>,
@ -411,7 +614,10 @@ pub(crate) struct ProgramsD3D9<D> where D: Device {
pub(crate) tile_copy_program: CopyTileProgram<D>, pub(crate) tile_copy_program: CopyTileProgram<D>,
} }
impl<D> ProgramsD3D9<D> where D: Device { impl<D> ProgramsD3D9<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D9<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D9<D> {
ProgramsD3D9 { ProgramsD3D9 {
fill_program: FillProgramD3D9::new(device, resources), fill_program: FillProgramD3D9::new(device, resources),

View File

@ -18,11 +18,11 @@
use crate::gpu::options::RendererLevel; use crate::gpu::options::RendererLevel;
use crate::gpu::perf::{RenderStats, RenderTime}; use crate::gpu::perf::{RenderStats, RenderTime};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gpu::Device;
use pathfinder_gpu::allocator::GPUMemoryAllocator; use pathfinder_gpu::allocator::GPUMemoryAllocator;
use pathfinder_gpu::Device;
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
use pathfinder_ui::{FONT_ASCENT, LINE_HEIGHT, PADDING, UIPresenter, WINDOW_COLOR}; use pathfinder_ui::{UIPresenter, FONT_ASCENT, LINE_HEIGHT, PADDING, WINDOW_COLOR};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Add, Div}; use std::ops::{Add, Div};
use std::time::Duration; use std::time::Duration;
@ -40,9 +40,12 @@ const INFO_WINDOW_WIDTH: i32 = 425;
const INFO_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 2 + PADDING + 2; const INFO_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 2 + PADDING + 2;
/// Manages the debug UI. /// Manages the debug UI.
pub struct DebugUIPresenter<D> where D: Device { pub struct DebugUIPresenter<D>
where
D: Device,
{
/// The general UI presenter object. /// The general UI presenter object.
/// ///
/// You can use this to draw your own application-specific debug widgets. /// You can use this to draw your own application-specific debug widgets.
pub ui_presenter: UIPresenter<D>, pub ui_presenter: UIPresenter<D>,
@ -53,12 +56,16 @@ pub struct DebugUIPresenter<D> where D: Device {
renderer_level: RendererLevel, renderer_level: RendererLevel,
} }
impl<D> DebugUIPresenter<D> where D: Device { impl<D> DebugUIPresenter<D>
pub(crate) fn new(device: &D, where
resources: &dyn ResourceLoader, D: Device,
framebuffer_size: Vector2I, {
renderer_level: RendererLevel) pub(crate) fn new(
-> DebugUIPresenter<D> { device: &D,
resources: &dyn ResourceLoader,
framebuffer_size: Vector2I,
renderer_level: RendererLevel,
) -> DebugUIPresenter<D> {
let ui_presenter = UIPresenter::new(device, resources, framebuffer_size); let ui_presenter = UIPresenter::new(device, resources, framebuffer_size);
DebugUIPresenter { DebugUIPresenter {
ui_presenter, ui_presenter,
@ -90,29 +97,35 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size(); let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING; let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new( let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - INFO_WINDOW_WIDTH, vec2i(
bottom - INFO_WINDOW_HEIGHT), framebuffer_size.x() - PADDING - INFO_WINDOW_WIDTH,
bottom - INFO_WINDOW_HEIGHT,
),
vec2i(INFO_WINDOW_WIDTH, INFO_WINDOW_HEIGHT), vec2i(INFO_WINDOW_WIDTH, INFO_WINDOW_HEIGHT),
); );
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR); self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT); let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT);
let level = match self.renderer_level { let level = match self.renderer_level {
RendererLevel::D3D9 => "D3D9", RendererLevel::D3D9 => "D3D9",
RendererLevel::D3D11 => "D3D11", RendererLevel::D3D11 => "D3D11",
}; };
self.ui_presenter.draw_text(device, self.ui_presenter.draw_text(
allocator, device,
&format!("{} ({} level)", self.backend_name, level), allocator,
origin + vec2i(0, LINE_HEIGHT * 0), &format!("{} ({} level)", self.backend_name, level),
false); origin + vec2i(0, LINE_HEIGHT * 0),
self.ui_presenter.draw_text(device, false,
allocator, );
&self.device_name, self.ui_presenter.draw_text(
origin + vec2i(0, LINE_HEIGHT * 1), device,
false); allocator,
&self.device_name,
origin + vec2i(0, LINE_HEIGHT * 1),
false,
);
} }
fn performance_window_size(&self) -> Vector2I { fn performance_window_size(&self) -> Vector2I {
@ -130,16 +143,20 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size(); let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING; let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new( let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH, vec2i(
bottom - framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH,
PADDING - bottom
INFO_WINDOW_HEIGHT - - PADDING
performance_window_height - - INFO_WINDOW_HEIGHT
PADDING - - performance_window_height
STATS_WINDOW_HEIGHT), - PADDING
vec2i(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT)); - STATS_WINDOW_HEIGHT,
),
vec2i(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT),
);
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR); self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let mean_cpu_sample = self.cpu_samples.mean(); let mean_cpu_sample = self.cpu_samples.mean();
let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT); let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT);
@ -179,11 +196,15 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size(); let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING; let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new( let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - performance_window_size.x(), vec2i(
bottom - INFO_WINDOW_HEIGHT - PADDING - performance_window_size.y()), framebuffer_size.x() - PADDING - performance_window_size.x(),
performance_window_size); bottom - INFO_WINDOW_HEIGHT - PADDING - performance_window_size.y(),
),
performance_window_size,
);
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR); self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let mean_cpu_sample = self.cpu_samples.mean(); let mean_cpu_sample = self.cpu_samples.mean();
let mean_gpu_sample = self.gpu_samples.mean(); let mean_gpu_sample = self.gpu_samples.mean();
@ -201,8 +222,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("VRAM Alloc.: {:.1} MB", &format!(
mean_cpu_sample.gpu_bytes_allocated as f64 / (1024.0 * 1024.0)), "VRAM Alloc.: {:.1} MB",
mean_cpu_sample.gpu_bytes_allocated as f64 / (1024.0 * 1024.0)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -210,8 +233,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("VRAM Commit: {:.1} MB", &format!(
mean_cpu_sample.gpu_bytes_committed as f64 / (1024.0 * 1024.0)), "VRAM Commit: {:.1} MB",
mean_cpu_sample.gpu_bytes_committed as f64 / (1024.0 * 1024.0)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -220,7 +245,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("CPU: {:.3} ms", duration_to_ms(mean_cpu_sample.cpu_build_time)), &format!(
"CPU: {:.3} ms",
duration_to_ms(mean_cpu_sample.cpu_build_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -231,7 +259,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("GPU Dice: {:.3} ms", duration_to_ms(mean_gpu_sample.dice_time)), &format!(
"GPU Dice: {:.3} ms",
duration_to_ms(mean_gpu_sample.dice_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -239,7 +270,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("GPU Bin: {:.3} ms", duration_to_ms(mean_gpu_sample.bin_time)), &format!(
"GPU Bin: {:.3} ms",
duration_to_ms(mean_gpu_sample.bin_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -250,7 +284,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("GPU Fill: {:.3} ms", duration_to_ms(mean_gpu_sample.fill_time)), &format!(
"GPU Fill: {:.3} ms",
duration_to_ms(mean_gpu_sample.fill_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -258,7 +295,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("GPU Comp.: {:.3} ms", duration_to_ms(mean_gpu_sample.composite_time)), &format!(
"GPU Comp.: {:.3} ms",
duration_to_ms(mean_gpu_sample.composite_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -266,7 +306,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
&format!("GPU Other: {:.3} ms", duration_to_ms(mean_gpu_sample.other_time)), &format!(
"GPU Other: {:.3} ms",
duration_to_ms(mean_gpu_sample.other_time)
),
origin + vec2i(0, current_y), origin + vec2i(0, current_y),
false, false,
); );
@ -274,18 +317,18 @@ impl<D> DebugUIPresenter<D> where D: Device {
let mut wallclock_time = match self.renderer_level { let mut wallclock_time = match self.renderer_level {
RendererLevel::D3D11 => { RendererLevel::D3D11 => {
duration_to_ms(mean_cpu_sample.cpu_build_time) + duration_to_ms(mean_cpu_sample.cpu_build_time)
duration_to_ms(mean_gpu_sample.fill_time) + duration_to_ms(mean_gpu_sample.fill_time)
}
RendererLevel::D3D9 => {
f64::max(duration_to_ms(mean_cpu_sample.cpu_build_time),
duration_to_ms(mean_gpu_sample.fill_time))
} }
RendererLevel::D3D9 => f64::max(
duration_to_ms(mean_cpu_sample.cpu_build_time),
duration_to_ms(mean_gpu_sample.fill_time),
),
}; };
wallclock_time += duration_to_ms(mean_gpu_sample.composite_time) + wallclock_time += duration_to_ms(mean_gpu_sample.composite_time)
duration_to_ms(mean_gpu_sample.dice_time) + + duration_to_ms(mean_gpu_sample.dice_time)
duration_to_ms(mean_gpu_sample.bin_time) + + duration_to_ms(mean_gpu_sample.bin_time)
duration_to_ms(mean_gpu_sample.other_time); + duration_to_ms(mean_gpu_sample.other_time);
self.ui_presenter.draw_text( self.ui_presenter.draw_text(
device, device,
allocator, allocator,
@ -294,7 +337,6 @@ impl<D> DebugUIPresenter<D> where D: Device {
false, false,
); );
} }
} }
struct SampleBuffer<S> struct SampleBuffer<S>

View File

@ -10,8 +10,8 @@
//! The GPU renderer for Pathfinder 3. //! The GPU renderer for Pathfinder 3.
pub mod d3d9;
pub mod d3d11; pub mod d3d11;
pub mod d3d9;
pub mod debug; pub mod debug;
pub mod options; pub mod options;
pub mod perf; pub mod perf;

View File

@ -22,7 +22,10 @@ pub struct RendererMode {
} }
/// Options that influence rendering that can be changed at runtime. /// Options that influence rendering that can be changed at runtime.
pub struct RendererOptions<D> where D: Device { pub struct RendererOptions<D>
where
D: Device,
{
/// Where the rendering should go: either to the default framebuffer (i.e. screen) or to a /// Where the rendering should go: either to the default framebuffer (i.e. screen) or to a
/// custom framebuffer. /// custom framebuffer.
pub dest: DestFramebuffer<D>, pub dest: DestFramebuffer<D>,
@ -48,12 +51,20 @@ pub enum RendererLevel {
impl RendererMode { impl RendererMode {
/// Creates a new `RendererMode` with a suitable API level for the given GPU device. /// Creates a new `RendererMode` with a suitable API level for the given GPU device.
#[inline] #[inline]
pub fn default_for_device<D>(device: &D) -> RendererMode where D: Device { pub fn default_for_device<D>(device: &D) -> RendererMode
RendererMode { level: RendererLevel::default_for_device(device) } where
D: Device,
{
RendererMode {
level: RendererLevel::default_for_device(device),
}
} }
} }
impl<D> Default for RendererOptions<D> where D: Device { impl<D> Default for RendererOptions<D>
where
D: Device,
{
#[inline] #[inline]
fn default() -> RendererOptions<D> { fn default() -> RendererOptions<D> {
RendererOptions { RendererOptions {
@ -66,7 +77,10 @@ impl<D> Default for RendererOptions<D> where D: Device {
impl RendererLevel { impl RendererLevel {
/// Returns a suitable renderer level for the given device. /// Returns a suitable renderer level for the given device.
pub fn default_for_device<D>(device: &D) -> RendererLevel where D: Device { pub fn default_for_device<D>(device: &D) -> RendererLevel
where
D: Device,
{
match device.feature_level() { match device.feature_level() {
FeatureLevel::D3D10 => RendererLevel::D3D9, FeatureLevel::D3D10 => RendererLevel::D3D9,
FeatureLevel::D3D11 => RendererLevel::D3D11, FeatureLevel::D3D11 => RendererLevel::D3D11,
@ -76,7 +90,10 @@ impl RendererLevel {
/// Where the rendered content should go. /// Where the rendered content should go.
#[derive(Clone)] #[derive(Clone)]
pub enum DestFramebuffer<D> where D: Device { pub enum DestFramebuffer<D>
where
D: Device,
{
/// The rendered content should go to the default framebuffer (e.g. the window in OpenGL). /// The rendered content should go to the default framebuffer (e.g. the window in OpenGL).
Default { Default {
/// The rectangle within the window to draw in, in device pixels. /// The rectangle within the window to draw in, in device pixels.
@ -88,22 +105,34 @@ pub enum DestFramebuffer<D> where D: Device {
Other(D::Framebuffer), Other(D::Framebuffer),
} }
impl<D> Default for DestFramebuffer<D> where D: Device { impl<D> Default for DestFramebuffer<D>
where
D: Device,
{
#[inline] #[inline]
fn default() -> DestFramebuffer<D> { fn default() -> DestFramebuffer<D> {
DestFramebuffer::Default { viewport: RectI::default(), window_size: Vector2I::default() } DestFramebuffer::Default {
viewport: RectI::default(),
window_size: Vector2I::default(),
}
} }
} }
impl<D> DestFramebuffer<D> where D: Device { impl<D> DestFramebuffer<D>
where
D: Device,
{
/// Returns a `DestFramebuffer` object that renders to the entire contents of the default /// Returns a `DestFramebuffer` object that renders to the entire contents of the default
/// framebuffer. /// framebuffer.
/// ///
/// The `window_size` parameter specifies the size of the window in device pixels. /// The `window_size` parameter specifies the size of the window in device pixels.
#[inline] #[inline]
pub fn full_window(window_size: Vector2I) -> DestFramebuffer<D> { pub fn full_window(window_size: Vector2I) -> DestFramebuffer<D> {
let viewport = RectI::new(Vector2I::default(), window_size); let viewport = RectI::new(Vector2I::default(), window_size);
DestFramebuffer::Default { viewport, window_size } DestFramebuffer::Default {
viewport,
window_size,
}
} }
/// Returns the size of the destination buffer, in device pixels. /// Returns the size of the destination buffer, in device pixels.

View File

@ -22,7 +22,7 @@ pub struct RenderStats {
/// The total number of path objects in the scene. /// The total number of path objects in the scene.
pub path_count: usize, pub path_count: usize,
/// The number of fill operations it took to render the scene. /// The number of fill operations it took to render the scene.
/// ///
/// A fill operation is a single edge in a 16x16 device pixel tile. /// A fill operation is a single edge in a 16x16 device pixel tile.
pub fill_count: usize, pub fill_count: usize,
/// The total number of 16x16 device pixel tile masks generated. /// The total number of 16x16 device pixel tile masks generated.
@ -35,7 +35,7 @@ pub struct RenderStats {
/// The number of GPU API draw calls it took to render the scene. /// The number of GPU API draw calls it took to render the scene.
pub drawcall_count: u32, pub drawcall_count: u32,
/// The number of bytes of VRAM Pathfinder has allocated. /// The number of bytes of VRAM Pathfinder has allocated.
/// ///
/// This may be higher than `gpu_bytes_committed` because Pathfinder caches some data for /// This may be higher than `gpu_bytes_committed` because Pathfinder caches some data for
/// faster reuse. /// faster reuse.
pub gpu_bytes_allocated: u64, pub gpu_bytes_allocated: u64,
@ -75,11 +75,17 @@ impl Div<usize> for RenderStats {
} }
} }
pub(crate) struct TimerQueryCache<D> where D: Device { pub(crate) struct TimerQueryCache<D>
where
D: Device,
{
free_queries: Vec<D::TimerQuery>, free_queries: Vec<D::TimerQuery>,
} }
pub(crate) struct PendingTimer<D> where D: Device { pub(crate) struct PendingTimer<D>
where
D: Device,
{
pub(crate) dice_times: Vec<TimerFuture<D>>, pub(crate) dice_times: Vec<TimerFuture<D>>,
pub(crate) bin_times: Vec<TimerFuture<D>>, pub(crate) bin_times: Vec<TimerFuture<D>>,
pub(crate) fill_times: Vec<TimerFuture<D>>, pub(crate) fill_times: Vec<TimerFuture<D>>,
@ -87,7 +93,10 @@ pub(crate) struct PendingTimer<D> where D: Device {
pub(crate) other_times: Vec<TimerFuture<D>>, pub(crate) other_times: Vec<TimerFuture<D>>,
} }
pub(crate) enum TimerFuture<D> where D: Device { pub(crate) enum TimerFuture<D>
where
D: Device,
{
Pending(D::TimerQuery), Pending(D::TimerQuery),
Resolved(Duration), Resolved(Duration),
} }
@ -101,21 +110,31 @@ pub(crate) enum TimeCategory {
Other, Other,
} }
impl<D> TimerQueryCache<D> where D: Device { impl<D> TimerQueryCache<D>
where
D: Device,
{
pub(crate) fn new() -> TimerQueryCache<D> { pub(crate) fn new() -> TimerQueryCache<D> {
TimerQueryCache { free_queries: vec![] } TimerQueryCache {
free_queries: vec![],
}
} }
pub(crate) fn alloc(&mut self, device: &D) -> D::TimerQuery { pub(crate) fn alloc(&mut self, device: &D) -> D::TimerQuery {
self.free_queries.pop().unwrap_or_else(|| device.create_timer_query()) self.free_queries
.pop()
.unwrap_or_else(|| device.create_timer_query())
} }
pub(crate) fn free(&mut self, old_query: D::TimerQuery) { pub(crate) fn free(&mut self, old_query: D::TimerQuery) {
self.free_queries.push(old_query); self.free_queries.push(old_query);
} }
pub(crate) fn start_timing_draw_call(&mut self, device: &D, options: &RendererOptions<D>) pub(crate) fn start_timing_draw_call(
-> Option<D::TimerQuery> { &mut self,
device: &D,
options: &RendererOptions<D>,
) -> Option<D::TimerQuery> {
if !options.show_debug_ui { if !options.show_debug_ui {
return None; return None;
} }
@ -126,7 +145,10 @@ impl<D> TimerQueryCache<D> where D: Device {
} }
} }
impl<D> PendingTimer<D> where D: Device { impl<D> PendingTimer<D>
where
D: Device,
{
pub(crate) fn new() -> PendingTimer<D> { pub(crate) fn new() -> PendingTimer<D> {
PendingTimer { PendingTimer {
dice_times: vec![], dice_times: vec![],
@ -139,10 +161,14 @@ impl<D> PendingTimer<D> where D: Device {
pub(crate) fn poll(&mut self, device: &D) -> Vec<D::TimerQuery> { pub(crate) fn poll(&mut self, device: &D) -> Vec<D::TimerQuery> {
let mut old_queries = vec![]; let mut old_queries = vec![];
for future in self.dice_times.iter_mut().chain(self.bin_times.iter_mut()) for future in self
.chain(self.fill_times.iter_mut()) .dice_times
.chain(self.composite_times.iter_mut()) .iter_mut()
.chain(self.other_times.iter_mut()) { .chain(self.bin_times.iter_mut())
.chain(self.fill_times.iter_mut())
.chain(self.composite_times.iter_mut())
.chain(self.other_times.iter_mut())
{
if let Some(old_query) = future.poll(device) { if let Some(old_query) = future.poll(device) {
old_queries.push(old_query) old_queries.push(old_query)
} }
@ -157,20 +183,28 @@ impl<D> PendingTimer<D> where D: Device {
let composite_time = total_time_of_timer_futures(&self.composite_times); let composite_time = total_time_of_timer_futures(&self.composite_times);
let other_time = total_time_of_timer_futures(&self.other_times); let other_time = total_time_of_timer_futures(&self.other_times);
match (dice_time, bin_time, fill_time, composite_time, other_time) { match (dice_time, bin_time, fill_time, composite_time, other_time) {
(Some(dice_time), (
Some(bin_time), Some(dice_time),
Some(fill_time), Some(bin_time),
Some(composite_time), Some(fill_time),
Some(other_time)) => { Some(composite_time),
Some(RenderTime { dice_time, bin_time, fill_time, composite_time, other_time }) Some(other_time),
} ) => Some(RenderTime {
dice_time,
bin_time,
fill_time,
composite_time,
other_time,
}),
_ => None, _ => None,
} }
} }
pub(crate) fn push_query(&mut self, pub(crate) fn push_query(
time_category: TimeCategory, &mut self,
timer_query: Option<D::TimerQuery>) { time_category: TimeCategory,
timer_query: Option<D::TimerQuery>,
) {
let timer_future = match timer_query { let timer_future = match timer_query {
None => return, None => return,
Some(timer_query) => TimerFuture::new(timer_query), Some(timer_query) => TimerFuture::new(timer_query),
@ -185,7 +219,10 @@ impl<D> PendingTimer<D> where D: Device {
} }
} }
impl<D> TimerFuture<D> where D: Device { impl<D> TimerFuture<D>
where
D: Device,
{
pub(crate) fn new(query: D::TimerQuery) -> TimerFuture<D> { pub(crate) fn new(query: D::TimerQuery) -> TimerFuture<D> {
TimerFuture::Pending(query) TimerFuture::Pending(query)
} }
@ -197,17 +234,18 @@ impl<D> TimerFuture<D> where D: Device {
}; };
match duration { match duration {
None => None, None => None,
Some(duration) => { Some(duration) => match mem::replace(self, TimerFuture::Resolved(duration)) {
match mem::replace(self, TimerFuture::Resolved(duration)) { TimerFuture::Resolved(_) => unreachable!(),
TimerFuture::Resolved(_) => unreachable!(), TimerFuture::Pending(old_query) => Some(old_query),
TimerFuture::Pending(old_query) => Some(old_query), },
}
}
} }
} }
} }
fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration> where D: Device { fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration>
where
D: Device,
{
let mut total = Duration::default(); let mut total = Duration::default();
for future in futures { for future in futures {
match *future { match *future {
@ -222,11 +260,11 @@ fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct RenderTime { pub struct RenderTime {
/// How much GPU time it took to divide all edges in the scene into small lines. /// How much GPU time it took to divide all edges in the scene into small lines.
/// ///
/// This will be zero in the D3D9-level backend, since in that backend dicing is done on CPU. /// This will be zero in the D3D9-level backend, since in that backend dicing is done on CPU.
pub dice_time: Duration, pub dice_time: Duration,
/// How much GPU time it took to assign those diced microlines to tiles. /// How much GPU time it took to assign those diced microlines to tiles.
/// ///
/// This will be zero in the D3D9-level backend, since in that backend binning is done on CPU. /// This will be zero in the D3D9-level backend, since in that backend binning is done on CPU.
pub bin_time: Duration, pub bin_time: Duration,
/// How much GPU time it took to draw fills (i.e. render edges) to masks. /// How much GPU time it took to draw fills (i.e. render edges) to masks.

File diff suppressed because it is too large Load Diff

View File

@ -15,105 +15,173 @@ use pathfinder_resources::ResourceLoader;
// TODO(pcwalton): Replace with `mem::size_of` calls? // TODO(pcwalton): Replace with `mem::size_of` calls?
pub(crate) const TILE_INSTANCE_SIZE: usize = 16; pub(crate) const TILE_INSTANCE_SIZE: usize = 16;
pub(crate) struct BlitVertexArray<D> where D: Device { pub(crate) struct BlitVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> BlitVertexArray<D> where D: Device { impl<D> BlitVertexArray<D>
pub(crate) fn new(device: &D, where
blit_program: &BlitProgram<D>, D: Device,
quad_vertex_positions_buffer: &D::Buffer, {
quad_vertex_indices_buffer: &D::Buffer) pub(crate) fn new(
-> BlitVertexArray<D> { device: &D,
blit_program: &BlitProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> BlitVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&blit_program.program, "Position").unwrap(); let position_attr = device
.get_vertex_attr(&blit_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &position_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
BlitVertexArray { vertex_array } BlitVertexArray { vertex_array }
} }
} }
pub(crate) struct VertexArraysCore<D> where D: Device { pub(crate) struct VertexArraysCore<D>
where
D: Device,
{
pub(crate) blit_vertex_array: BlitVertexArray<D>, pub(crate) blit_vertex_array: BlitVertexArray<D>,
} }
impl<D> VertexArraysCore<D> where D: Device { impl<D> VertexArraysCore<D>
pub(crate) fn new(device: &D, where
programs: &ProgramsCore<D>, D: Device,
quad_vertex_positions_buffer: &D::Buffer, {
quad_vertex_indices_buffer: &D::Buffer) pub(crate) fn new(
-> VertexArraysCore<D> { device: &D,
programs: &ProgramsCore<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> VertexArraysCore<D> {
VertexArraysCore { VertexArraysCore {
blit_vertex_array: BlitVertexArray::new(device, blit_vertex_array: BlitVertexArray::new(
&programs.blit_program, device,
quad_vertex_positions_buffer, &programs.blit_program,
quad_vertex_indices_buffer), quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
),
} }
} }
} }
pub(crate) struct ClearVertexArray<D> where D: Device { pub(crate) struct ClearVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> ClearVertexArray<D> where D: Device { impl<D> ClearVertexArray<D>
pub(crate) fn new(device: &D, where
clear_program: &ClearProgram<D>, D: Device,
quad_vertex_positions_buffer: &D::Buffer, {
quad_vertex_indices_buffer: &D::Buffer) pub(crate) fn new(
-> ClearVertexArray<D> { device: &D,
clear_program: &ClearProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClearVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&clear_program.program, "Position").unwrap(); let position_attr = device
.get_vertex_attr(&clear_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &position_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClearVertexArray { vertex_array } ClearVertexArray { vertex_array }
} }
} }
pub(crate) struct BlitProgram<D> where D: Device { pub(crate) struct BlitProgram<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) dest_rect_uniform: D::Uniform, pub(crate) dest_rect_uniform: D::Uniform,
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) src_texture: D::TextureParameter, pub(crate) src_texture: D::TextureParameter,
} }
impl<D> BlitProgram<D> where D: Device { impl<D> BlitProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BlitProgram<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BlitProgram<D> {
let program = device.create_raster_program(resources, "blit"); let program = device.create_raster_program(resources, "blit");
let dest_rect_uniform = device.get_uniform(&program, "DestRect"); let dest_rect_uniform = device.get_uniform(&program, "DestRect");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let src_texture = device.get_texture_parameter(&program, "Src"); let src_texture = device.get_texture_parameter(&program, "Src");
BlitProgram { program, dest_rect_uniform, framebuffer_size_uniform, src_texture } BlitProgram {
program,
dest_rect_uniform,
framebuffer_size_uniform,
src_texture,
}
} }
} }
pub(crate) struct ProgramsCore<D> where D: Device { pub(crate) struct ProgramsCore<D>
where
D: Device,
{
pub(crate) blit_program: BlitProgram<D>, pub(crate) blit_program: BlitProgram<D>,
} }
impl<D> ProgramsCore<D> where D: Device { impl<D> ProgramsCore<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsCore<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsCore<D> {
ProgramsCore { ProgramsCore {
blit_program: BlitProgram::new(device, resources), blit_program: BlitProgram::new(device, resources),
@ -121,24 +189,38 @@ impl<D> ProgramsCore<D> where D: Device {
} }
} }
pub(crate) struct ClearProgram<D> where D: Device { pub(crate) struct ClearProgram<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) rect_uniform: D::Uniform, pub(crate) rect_uniform: D::Uniform,
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) color_uniform: D::Uniform, pub(crate) color_uniform: D::Uniform,
} }
impl<D> ClearProgram<D> where D: Device { impl<D> ClearProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClearProgram<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClearProgram<D> {
let program = device.create_raster_program(resources, "clear"); let program = device.create_raster_program(resources, "clear");
let rect_uniform = device.get_uniform(&program, "Rect"); let rect_uniform = device.get_uniform(&program, "Rect");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let color_uniform = device.get_uniform(&program, "Color"); let color_uniform = device.get_uniform(&program, "Color");
ClearProgram { program, rect_uniform, framebuffer_size_uniform, color_uniform } ClearProgram {
program,
rect_uniform,
framebuffer_size_uniform,
color_uniform,
}
} }
} }
pub(crate) struct TileProgramCommon<D> where D: Device { pub(crate) struct TileProgramCommon<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) tile_size_uniform: D::Uniform, pub(crate) tile_size_uniform: D::Uniform,
pub(crate) texture_metadata_texture: D::TextureParameter, pub(crate) texture_metadata_texture: D::TextureParameter,
@ -153,7 +235,10 @@ pub(crate) struct TileProgramCommon<D> where D: Device {
pub(crate) framebuffer_size_uniform: D::Uniform, pub(crate) framebuffer_size_uniform: D::Uniform,
} }
impl<D> TileProgramCommon<D> where D: Device { impl<D> TileProgramCommon<D>
where
D: Device,
{
pub(crate) fn new(device: &D, program: D::Program) -> TileProgramCommon<D> { pub(crate) fn new(device: &D, program: D::Program) -> TileProgramCommon<D> {
let tile_size_uniform = device.get_uniform(&program, "TileSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize");
let texture_metadata_texture = device.get_texture_parameter(&program, "TextureMetadata"); let texture_metadata_texture = device.get_texture_parameter(&program, "TextureMetadata");
@ -184,89 +269,142 @@ impl<D> TileProgramCommon<D> where D: Device {
} }
} }
pub(crate) struct StencilProgram<D> where D: Device { pub(crate) struct StencilProgram<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
} }
impl<D> StencilProgram<D> where D: Device { impl<D> StencilProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> StencilProgram<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> StencilProgram<D> {
let program = device.create_raster_program(resources, "stencil"); let program = device.create_raster_program(resources, "stencil");
StencilProgram { program } StencilProgram { program }
} }
} }
pub(crate) struct StencilVertexArray<D> where D: Device { pub(crate) struct StencilVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
pub(crate) vertex_buffer: D::Buffer, pub(crate) vertex_buffer: D::Buffer,
pub(crate) index_buffer: D::Buffer, pub(crate) index_buffer: D::Buffer,
} }
impl<D> StencilVertexArray<D> where D: Device { impl<D> StencilVertexArray<D>
where
D: Device,
{
pub(crate) fn new(device: &D, stencil_program: &StencilProgram<D>) -> StencilVertexArray<D> { pub(crate) fn new(device: &D, stencil_program: &StencilProgram<D>) -> StencilVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let vertex_buffer = device.create_buffer(BufferUploadMode::Static); let vertex_buffer = device.create_buffer(BufferUploadMode::Static);
let index_buffer = device.create_buffer(BufferUploadMode::Static); let index_buffer = device.create_buffer(BufferUploadMode::Static);
let position_attr = device.get_vertex_attr(&stencil_program.program, "Position").unwrap(); let position_attr = device
.get_vertex_attr(&stencil_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { device.configure_vertex_attr(
size: 3, &vertex_array,
class: VertexAttrClass::Float, &position_attr,
attr_type: VertexAttrType::F32, &VertexAttrDescriptor {
stride: 4 * 4, size: 3,
offset: 0, class: VertexAttrClass::Float,
divisor: 0, attr_type: VertexAttrType::F32,
buffer_index: 0, stride: 4 * 4,
}); offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index); device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index);
StencilVertexArray { vertex_array, vertex_buffer, index_buffer } StencilVertexArray {
vertex_array,
vertex_buffer,
index_buffer,
}
} }
} }
pub(crate) struct ReprojectionProgram<D> where D: Device { pub(crate) struct ReprojectionProgram<D>
where
D: Device,
{
pub(crate) program: D::Program, pub(crate) program: D::Program,
pub(crate) old_transform_uniform: D::Uniform, pub(crate) old_transform_uniform: D::Uniform,
pub(crate) new_transform_uniform: D::Uniform, pub(crate) new_transform_uniform: D::Uniform,
pub(crate) texture: D::TextureParameter, pub(crate) texture: D::TextureParameter,
} }
impl<D> ReprojectionProgram<D> where D: Device { impl<D> ReprojectionProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ReprojectionProgram<D> { pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ReprojectionProgram<D> {
let program = device.create_raster_program(resources, "reproject"); let program = device.create_raster_program(resources, "reproject");
let old_transform_uniform = device.get_uniform(&program, "OldTransform"); let old_transform_uniform = device.get_uniform(&program, "OldTransform");
let new_transform_uniform = device.get_uniform(&program, "NewTransform"); let new_transform_uniform = device.get_uniform(&program, "NewTransform");
let texture = device.get_texture_parameter(&program, "Texture"); let texture = device.get_texture_parameter(&program, "Texture");
ReprojectionProgram { program, old_transform_uniform, new_transform_uniform, texture } ReprojectionProgram {
program,
old_transform_uniform,
new_transform_uniform,
texture,
}
} }
} }
pub(crate) struct ReprojectionVertexArray<D> where D: Device { pub(crate) struct ReprojectionVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray, pub(crate) vertex_array: D::VertexArray,
} }
impl<D> ReprojectionVertexArray<D> where D: Device { impl<D> ReprojectionVertexArray<D>
pub(crate) fn new(device: &D, where
reprojection_program: &ReprojectionProgram<D>, D: Device,
quad_vertex_positions_buffer: &D::Buffer, {
quad_vertex_indices_buffer: &D::Buffer) pub(crate) fn new(
-> ReprojectionVertexArray<D> { device: &D,
reprojection_program: &ReprojectionProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ReprojectionVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&reprojection_program.program, "Position") let position_attr = device
.unwrap(); .get_vertex_attr(&reprojection_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { &vertex_array,
size: 2, quad_vertex_positions_buffer,
class: VertexAttrClass::Int, BufferTarget::Vertex,
attr_type: VertexAttrType::I16, );
stride: 4, device.configure_vertex_attr(
offset: 0, &vertex_array,
divisor: 0, &position_attr,
buffer_index: 0, &VertexAttrDescriptor {
}); size: 2,
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ReprojectionVertexArray { vertex_array } ReprojectionVertexArray { vertex_array }
} }

View File

@ -24,15 +24,15 @@ use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gpu::TextureSamplingFlags; use pathfinder_gpu::TextureSamplingFlags;
use std::fmt::{Debug, Formatter, Result as DebugResult}; use std::fmt::{Debug, Formatter, Result as DebugResult};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::u32; use std::u32;
pub const TILE_CTRL_MASK_WINDING: i32 = 0x1; pub const TILE_CTRL_MASK_WINDING: i32 = 0x1;
pub const TILE_CTRL_MASK_EVEN_ODD: i32 = 0x2; pub const TILE_CTRL_MASK_EVEN_ODD: i32 = 0x2;
pub const TILE_CTRL_MASK_0_SHIFT: i32 = 0; pub const TILE_CTRL_MASK_0_SHIFT: i32 = 0;
pub enum RenderCommand { pub enum RenderCommand {
// Starts rendering a frame. // Starts rendering a frame.
@ -51,15 +51,24 @@ pub enum RenderCommand {
}, },
// Allocates a texture page. // Allocates a texture page.
AllocateTexturePage { page_id: TexturePageId, descriptor: TexturePageDescriptor }, AllocateTexturePage {
page_id: TexturePageId,
descriptor: TexturePageDescriptor,
},
// Uploads data to a texture page. // Uploads data to a texture page.
UploadTexelData { texels: Arc<Vec<ColorU>>, location: TextureLocation }, UploadTexelData {
texels: Arc<Vec<ColorU>>,
location: TextureLocation,
},
// Associates a render target with a texture page. // Associates a render target with a texture page.
// //
// TODO(pcwalton): Add a rect to this so we can render to subrects of a page. // TODO(pcwalton): Add a rect to this so we can render to subrects of a page.
DeclareRenderTarget { id: RenderTargetId, location: TextureLocation }, DeclareRenderTarget {
id: RenderTargetId,
location: TextureLocation,
},
// Upload texture metadata. // Upload texture metadata.
UploadTextureMetadata(Vec<TextureMetadataEntry>), UploadTextureMetadata(Vec<TextureMetadataEntry>),
@ -71,7 +80,7 @@ pub enum RenderCommand {
FlushFillsD3D9, FlushFillsD3D9,
/// Upload a scene to GPU. /// Upload a scene to GPU.
/// ///
/// This will only be sent if dicing and binning is done on GPU. /// This will only be sent if dicing and binning is done on GPU.
UploadSceneD3D11 { UploadSceneD3D11 {
draw_segments: SegmentsD3D11, draw_segments: SegmentsD3D11,
@ -95,7 +104,9 @@ pub enum RenderCommand {
DrawTilesD3D11(DrawTileBatchD3D11), DrawTilesD3D11(DrawTileBatchD3D11),
// Presents a rendered frame. // Presents a rendered frame.
Finish { cpu_build_time: Duration }, Finish {
cpu_build_time: Duration,
},
} }
#[derive(Clone, Copy, PartialEq, Debug, Default)] #[derive(Clone, Copy, PartialEq, Debug, Default)]
@ -116,7 +127,7 @@ pub struct TextureLocation {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TileBatchDataD3D11 { pub struct TileBatchDataD3D11 {
/// The ID of this batch. /// The ID of this batch.
/// ///
/// The renderer should not assume that these values are consecutive. /// The renderer should not assume that these values are consecutive.
pub batch_id: TileBatchId, pub batch_id: TileBatchId,
/// The number of paths in this batch. /// The number of paths in this batch.
@ -147,7 +158,7 @@ pub struct PrepareTilesInfoD3D11 {
pub backdrops: Vec<BackdropInfoD3D11>, pub backdrops: Vec<BackdropInfoD3D11>,
/// Mapping from path index to metadata needed to compute propagation on GPU. /// Mapping from path index to metadata needed to compute propagation on GPU.
/// ///
/// This contains indices into the `tiles` vector. /// This contains indices into the `tiles` vector.
pub propagate_metadata: Vec<PropagateMetadataD3D11>, pub propagate_metadata: Vec<PropagateMetadataD3D11>,
@ -184,7 +195,7 @@ pub struct ClippedPathInfo {
pub clipped_path_count: u32, pub clipped_path_count: u32,
/// The maximum number of clipped tiles. /// The maximum number of clipped tiles.
/// ///
/// This is used to allocate vertex buffers. /// This is used to allocate vertex buffers.
pub max_clipped_tile_count: u32, pub max_clipped_tile_count: u32,
@ -193,7 +204,7 @@ pub struct ClippedPathInfo {
} }
/// Together with the `TileBatchId`, uniquely identifies a path on the renderer side. /// Together with the `TileBatchId`, uniquely identifies a path on the renderer side.
/// ///
/// Generally, `PathIndex(!0)` represents no path. /// Generally, `PathIndex(!0)` represents no path.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PathBatchIndex(pub u32); pub struct PathBatchIndex(pub u32);
@ -438,8 +449,10 @@ impl PathBatchIndex {
impl AlphaTileId { impl AlphaTileId {
#[inline] #[inline]
pub fn new(next_alpha_tile_index: &[AtomicUsize; ALPHA_TILE_LEVEL_COUNT], level: usize) pub fn new(
-> AlphaTileId { next_alpha_tile_index: &[AtomicUsize; ALPHA_TILE_LEVEL_COUNT],
level: usize,
) -> AlphaTileId {
let alpha_tile_index = next_alpha_tile_index[level].fetch_add(1, Ordering::Relaxed); let alpha_tile_index = next_alpha_tile_index[level].fetch_add(1, Ordering::Relaxed);
debug_assert!(alpha_tile_index < ALPHA_TILES_PER_LEVEL); debug_assert!(alpha_tile_index < ALPHA_TILES_PER_LEVEL);
AlphaTileId((level * ALPHA_TILES_PER_LEVEL + alpha_tile_index) as u32) AlphaTileId((level * ALPHA_TILES_PER_LEVEL + alpha_tile_index) as u32)
@ -470,11 +483,22 @@ impl Debug for RenderCommand {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult { fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
match *self { match *self {
RenderCommand::Start { .. } => write!(formatter, "Start"), RenderCommand::Start { .. } => write!(formatter, "Start"),
RenderCommand::AllocateTexturePage { page_id, descriptor: _ } => { RenderCommand::AllocateTexturePage {
page_id,
descriptor: _,
} => {
write!(formatter, "AllocateTexturePage({})", page_id.0) write!(formatter, "AllocateTexturePage({})", page_id.0)
} }
RenderCommand::UploadTexelData { ref texels, location } => { RenderCommand::UploadTexelData {
write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location) ref texels,
location,
} => {
write!(
formatter,
"UploadTexelData(x{:?}, {:?})",
texels.len(),
location
)
} }
RenderCommand::DeclareRenderTarget { id, location } => { RenderCommand::DeclareRenderTarget { id, location } => {
write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location) write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location)
@ -486,23 +510,29 @@ impl Debug for RenderCommand {
write!(formatter, "AddFillsD3D9(x{})", fills.len()) write!(formatter, "AddFillsD3D9(x{})", fills.len())
} }
RenderCommand::FlushFillsD3D9 => write!(formatter, "FlushFills"), RenderCommand::FlushFillsD3D9 => write!(formatter, "FlushFills"),
RenderCommand::UploadSceneD3D11 { ref draw_segments, ref clip_segments } => { RenderCommand::UploadSceneD3D11 {
write!(formatter, ref draw_segments,
"UploadSceneD3D11(DP x{}, DI x{}, CP x{}, CI x{})", ref clip_segments,
draw_segments.points.len(), } => {
draw_segments.indices.len(), write!(
clip_segments.points.len(), formatter,
clip_segments.indices.len()) "UploadSceneD3D11(DP x{}, DI x{}, CP x{}, CI x{})",
draw_segments.points.len(),
draw_segments.indices.len(),
clip_segments.points.len(),
clip_segments.indices.len()
)
} }
RenderCommand::PrepareClipTilesD3D11(ref batch) => { RenderCommand::PrepareClipTilesD3D11(ref batch) => {
let clipped_path_count = match batch.clipped_path_info { let clipped_path_count = match batch.clipped_path_info {
None => 0, None => 0,
Some(ref clipped_path_info) => clipped_path_info.clipped_path_count, Some(ref clipped_path_info) => clipped_path_info.clipped_path_count,
}; };
write!(formatter, write!(
"PrepareClipTilesD3D11({:?}, C {})", formatter,
batch.batch_id, "PrepareClipTilesD3D11({:?}, C {})",
clipped_path_count) batch.batch_id, clipped_path_count
)
} }
RenderCommand::PushRenderTarget(render_target_id) => { RenderCommand::PushRenderTarget(render_target_id) => {
write!(formatter, "PushRenderTarget({:?})", render_target_id) write!(formatter, "PushRenderTarget({:?})", render_target_id)
@ -512,13 +542,18 @@ impl Debug for RenderCommand {
write!(formatter, "DrawTilesD3D9(x{:?})", batch.tiles.len()) write!(formatter, "DrawTilesD3D9(x{:?})", batch.tiles.len())
} }
RenderCommand::DrawTilesD3D11(ref batch) => { RenderCommand::DrawTilesD3D11(ref batch) => {
write!(formatter, write!(
"DrawTilesD3D11({:?}, C0 {:?})", formatter,
batch.tile_batch_data.batch_id, "DrawTilesD3D11({:?}, C0 {:?})",
batch.color_texture) batch.tile_batch_data.batch_id, batch.color_texture
)
} }
RenderCommand::Finish { cpu_build_time } => { RenderCommand::Finish { cpu_build_time } => {
write!(formatter, "Finish({} ms)", cpu_build_time.as_secs_f64() * 1000.0) write!(
formatter,
"Finish({} ms)",
cpu_build_time.as_secs_f64() * 1000.0
)
} }
} }
} }

View File

@ -21,7 +21,7 @@ use pathfinder_geometry::vector::{Vector2F, Vector4F};
use pathfinder_content::clip::PolygonClipper3D; use pathfinder_content::clip::PolygonClipper3D;
/// A sink for the render commands that scenes build. /// A sink for the render commands that scenes build.
/// ///
/// In single-threaded operation, this object typically buffers commands into an array and then, /// In single-threaded operation, this object typically buffers commands into an array and then,
/// once scene building is complete, commands are all sent to the output at once. In multithreaded /// once scene building is complete, commands are all sent to the output at once. In multithreaded
/// operation, on the other hand, commands are sent to the renderer on the fly as they're built. /// operation, on the other hand, commands are sent to the renderer on the fly as they're built.
@ -129,9 +129,10 @@ impl RenderTransform {
} }
let inverse_transform = perspective.transform.inverse(); let inverse_transform = perspective.transform.inverse();
let clip_polygon = points.into_iter() let clip_polygon = points
.map(|point| (inverse_transform * point).to_2d()) .into_iter()
.collect(); .map(|point| (inverse_transform * point).to_2d())
.collect();
return PreparedRenderTransform::Perspective { return PreparedRenderTransform::Perspective {
perspective, perspective,
clip_polygon, clip_polygon,
@ -166,17 +167,13 @@ impl PreparedBuildOptions {
pub(crate) fn to_prepare_mode(&self, renderer_level: RendererLevel) -> PrepareMode { pub(crate) fn to_prepare_mode(&self, renderer_level: RendererLevel) -> PrepareMode {
match renderer_level { match renderer_level {
RendererLevel::D3D9 => PrepareMode::CPU, RendererLevel::D3D9 => PrepareMode::CPU,
RendererLevel::D3D11 => { RendererLevel::D3D11 => match self.transform {
match self.transform { PreparedRenderTransform::Perspective { .. } => PrepareMode::TransformCPUBinGPU,
PreparedRenderTransform::Perspective { .. } => PrepareMode::TransformCPUBinGPU, PreparedRenderTransform::None => PrepareMode::GPU {
PreparedRenderTransform::None => { transform: Transform2F::default(),
PrepareMode::GPU { transform: Transform2F::default() } },
} PreparedRenderTransform::Transform2D(transform) => PrepareMode::GPU { transform },
PreparedRenderTransform::Transform2D(transform) => { },
PrepareMode::GPU { transform }
}
}
}
} }
} }
} }

View File

@ -23,7 +23,7 @@ use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_gpu::TextureSamplingFlags; use pathfinder_gpu::TextureSamplingFlags;
use pathfinder_simd::default::{F32x2, F32x4}; use pathfinder_simd::default::{F32x2, F32x4};
use std::f32; use std::f32;
@ -118,7 +118,10 @@ impl Paint {
/// Creates a simple paint from a single base color. /// Creates a simple paint from a single base color.
#[inline] #[inline]
pub fn from_color(color: ColorU) -> Paint { pub fn from_color(color: ColorU) -> Paint {
Paint { base_color: color, overlay: None } Paint {
base_color: color,
overlay: None,
}
} }
/// Creates a paint from a gradient. /// Creates a paint from a gradient.
@ -167,12 +170,10 @@ impl Paint {
match self.overlay { match self.overlay {
None => true, None => true,
Some(ref overlay) => { Some(ref overlay) => match overlay.contents {
match overlay.contents { PaintContents::Gradient(ref gradient) => gradient.is_opaque(),
PaintContents::Gradient(ref gradient) => gradient.is_opaque(), PaintContents::Pattern(ref pattern) => pattern.is_opaque(),
PaintContents::Pattern(ref pattern) => pattern.is_opaque(), },
}
}
} }
} }
@ -186,12 +187,10 @@ impl Paint {
match self.overlay { match self.overlay {
None => true, None => true,
Some(ref overlay) => { Some(ref overlay) => match overlay.contents {
match overlay.contents { PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(),
PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(), PaintContents::Pattern(_) => false,
PaintContents::Pattern(_) => false, },
}
}
} }
} }
@ -251,12 +250,10 @@ impl Paint {
pub fn pattern(&self) -> Option<&Pattern> { pub fn pattern(&self) -> Option<&Pattern> {
match self.overlay { match self.overlay {
None => None, None => None,
Some(ref overlay) => { Some(ref overlay) => match overlay.contents {
match overlay.contents { PaintContents::Pattern(ref pattern) => Some(pattern),
PaintContents::Pattern(ref pattern) => Some(pattern), _ => None,
_ => None, },
}
}
} }
} }
@ -265,12 +262,10 @@ impl Paint {
pub fn pattern_mut(&mut self) -> Option<&mut Pattern> { pub fn pattern_mut(&mut self) -> Option<&mut Pattern> {
match self.overlay { match self.overlay {
None => None, None => None,
Some(ref mut overlay) => { Some(ref mut overlay) => match overlay.contents {
match overlay.contents { PaintContents::Pattern(ref mut pattern) => Some(pattern),
PaintContents::Pattern(ref mut pattern) => Some(pattern), _ => None,
_ => None, },
}
}
} }
} }
@ -279,12 +274,10 @@ impl Paint {
pub fn gradient(&self) -> Option<&Gradient> { pub fn gradient(&self) -> Option<&Gradient> {
match self.overlay { match self.overlay {
None => None, None => None,
Some(ref overlay) => { Some(ref overlay) => match overlay.contents {
match overlay.contents { PaintContents::Gradient(ref gradient) => Some(gradient),
PaintContents::Gradient(ref gradient) => Some(gradient), _ => None,
_ => None, },
}
}
} }
} }
} }
@ -392,13 +385,17 @@ impl Palette {
pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
let id = self.render_targets.len() as u32; let id = self.render_targets.len() as u32;
self.render_targets.push(render_target); self.render_targets.push(render_target);
RenderTargetId { scene: self.scene_id.0, render_target: id } RenderTargetId {
scene: self.scene_id.0,
render_target: id,
}
} }
pub(crate) fn build_paint_info(&mut self, pub(crate) fn build_paint_info(
texture_manager: &mut PaintTextureManager, &mut self,
render_transform: Transform2F) texture_manager: &mut PaintTextureManager,
-> PaintInfo { render_transform: Transform2F,
) -> PaintInfo {
// Assign render target locations. // Assign render target locations.
let mut transient_paint_locations = vec![]; let mut transient_paint_locations = vec![];
let render_target_metadata = let render_target_metadata =
@ -410,9 +407,11 @@ impl Palette {
gradient_tile_builder, gradient_tile_builder,
image_texel_info, image_texel_info,
used_image_hashes, used_image_hashes,
} = self.assign_paint_locations(&render_target_metadata, } = self.assign_paint_locations(
texture_manager, &render_target_metadata,
&mut transient_paint_locations); texture_manager,
&mut transient_paint_locations,
);
// Calculate texture transforms. // Calculate texture transforms.
self.calculate_texture_transforms(&mut paint_metadata, texture_manager, render_transform); self.calculate_texture_transforms(&mut paint_metadata, texture_manager, render_transform);
@ -425,36 +424,45 @@ impl Palette {
self.allocate_textures(&mut render_commands, texture_manager); self.allocate_textures(&mut render_commands, texture_manager);
// Create render commands. // Create render commands.
self.create_render_commands(&mut render_commands, self.create_render_commands(
render_target_metadata, &mut render_commands,
gradient_tile_builder, render_target_metadata,
image_texel_info); gradient_tile_builder,
image_texel_info,
);
// Free transient locations and unused images, now that they're no longer needed. // Free transient locations and unused images, now that they're no longer needed.
self.free_transient_locations(texture_manager, transient_paint_locations); self.free_transient_locations(texture_manager, transient_paint_locations);
self.free_unused_images(texture_manager, used_image_hashes); self.free_unused_images(texture_manager, used_image_hashes);
PaintInfo { render_commands, paint_metadata } PaintInfo {
render_commands,
paint_metadata,
}
} }
fn assign_render_target_locations(&self, fn assign_render_target_locations(
texture_manager: &mut PaintTextureManager, &self,
transient_paint_locations: &mut Vec<TextureLocation>) texture_manager: &mut PaintTextureManager,
-> Vec<RenderTargetMetadata> { transient_paint_locations: &mut Vec<TextureLocation>,
) -> Vec<RenderTargetMetadata> {
let mut render_target_metadata = vec![]; let mut render_target_metadata = vec![];
for render_target in &self.render_targets { for render_target in &self.render_targets {
let location = texture_manager.allocator.allocate_image(render_target.size()); let location = texture_manager
.allocator
.allocate_image(render_target.size());
render_target_metadata.push(RenderTargetMetadata { location }); render_target_metadata.push(RenderTargetMetadata { location });
transient_paint_locations.push(location); transient_paint_locations.push(location);
} }
render_target_metadata render_target_metadata
} }
fn assign_paint_locations(&self, fn assign_paint_locations(
render_target_metadata: &[RenderTargetMetadata], &self,
texture_manager: &mut PaintTextureManager, render_target_metadata: &[RenderTargetMetadata],
transient_paint_locations: &mut Vec<TextureLocation>) texture_manager: &mut PaintTextureManager,
-> PaintLocationsInfo { transient_paint_locations: &mut Vec<TextureLocation>,
) -> PaintLocationsInfo {
let mut paint_metadata = vec![]; let mut paint_metadata = vec![];
let mut gradient_tile_builder = GradientTileBuilder::new(); let mut gradient_tile_builder = GradientTileBuilder::new();
let mut image_texel_info = vec![]; let mut image_texel_info = vec![];
@ -476,10 +484,11 @@ impl Palette {
// FIXME(pcwalton): The gradient size might not be big enough. Detect // FIXME(pcwalton): The gradient size might not be big enough. Detect
// this. // this.
let location = let location = gradient_tile_builder.allocate(
gradient_tile_builder.allocate(allocator, allocator,
transient_paint_locations, transient_paint_locations,
gradient); gradient,
);
Some(PaintColorTextureMetadata { Some(PaintColorTextureMetadata {
location, location,
page_scale: allocator.page_scale(location.page), page_scale: allocator.page_scale(location.page),
@ -496,12 +505,17 @@ impl Palette {
}) })
} }
PaintContents::Pattern(ref pattern) => { PaintContents::Pattern(ref pattern) => {
let border = vec2i(if pattern.repeat_x() { 0 } else { 1 }, let border = vec2i(
if pattern.repeat_y() { 0 } else { 1 }); if pattern.repeat_x() { 0 } else { 1 },
if pattern.repeat_y() { 0 } else { 1 },
);
let location; let location;
match *pattern.source() { match *pattern.source() {
PatternSource::RenderTarget { id: render_target_id, .. } => { PatternSource::RenderTarget {
id: render_target_id,
..
} => {
let index = render_target_id.render_target as usize; let index = render_target_id.render_target as usize;
location = render_target_metadata[index].location; location = render_target_metadata[index].location;
} }
@ -519,9 +533,11 @@ impl Palette {
let allocation_mode = AllocationMode::OwnPage; let allocation_mode = AllocationMode::OwnPage;
location = allocator.allocate( location = allocator.allocate(
image.size() + border * 2, image.size() + border * 2,
allocation_mode); allocation_mode,
texture_manager.cached_images.insert(image_hash, );
location); texture_manager
.cached_images
.insert(image_hash, location);
} }
} }
image_texel_info.push(ImageTexelInfo { image_texel_info.push(ImageTexelInfo {
@ -542,8 +558,10 @@ impl Palette {
sampling_flags.insert(TextureSamplingFlags::REPEAT_V); sampling_flags.insert(TextureSamplingFlags::REPEAT_V);
} }
if !pattern.smoothing_enabled() { if !pattern.smoothing_enabled() {
sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | sampling_flags.insert(
TextureSamplingFlags::NEAREST_MAG); TextureSamplingFlags::NEAREST_MIN
| TextureSamplingFlags::NEAREST_MAG,
);
} }
let filter = match pattern.filter() { let filter = match pattern.filter() {
@ -582,24 +600,28 @@ impl Palette {
} }
} }
fn calculate_texture_transforms(&self, fn calculate_texture_transforms(
paint_metadata: &mut [PaintMetadata], &self,
texture_manager: &mut PaintTextureManager, paint_metadata: &mut [PaintMetadata],
render_transform: Transform2F) { texture_manager: &mut PaintTextureManager,
render_transform: Transform2F,
) {
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) { for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) {
let color_texture_metadata = match metadata.color_texture_metadata { let color_texture_metadata = match metadata.color_texture_metadata {
None => continue, None => continue,
Some(ref mut color_texture_metadata) => color_texture_metadata, Some(ref mut color_texture_metadata) => color_texture_metadata,
}; };
let texture_scale = texture_manager.allocator let texture_scale = texture_manager
.page_scale(color_texture_metadata.location.page); .allocator
.page_scale(color_texture_metadata.location.page);
let texture_rect = color_texture_metadata.location.rect; let texture_rect = color_texture_metadata.location.rect;
color_texture_metadata.transform = match paint.overlay color_texture_metadata.transform = match paint
.as_ref() .overlay
.expect("Why do we have color texture \ .as_ref()
metadata but no overlay?") .expect("Why do we have color texture metadata but no overlay?")
.contents { .contents
{
PaintContents::Gradient(Gradient { PaintContents::Gradient(Gradient {
geometry: GradientGeometry::Linear(gradient_line), geometry: GradientGeometry::Linear(gradient_line),
.. ..
@ -620,16 +642,16 @@ impl Palette {
PatternSource::Image(_) => { PatternSource::Image(_) => {
let texture_origin_uv = let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).origin(); rect_to_uv(texture_rect, texture_scale).origin();
Transform2F::from_scale(texture_scale).translate(texture_origin_uv) * Transform2F::from_scale(texture_scale).translate(texture_origin_uv)
pattern.transform().inverse() * pattern.transform().inverse()
} }
PatternSource::RenderTarget { .. } => { PatternSource::RenderTarget { .. } => {
// FIXME(pcwalton): Only do this in GL, not Metal! // FIXME(pcwalton): Only do this in GL, not Metal!
let texture_origin_uv = let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).lower_left(); rect_to_uv(texture_rect, texture_scale).lower_left();
Transform2F::from_translation(texture_origin_uv) * Transform2F::from_translation(texture_origin_uv)
Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) * * Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0))
pattern.transform().inverse() * pattern.transform().inverse()
} }
} }
} }
@ -638,10 +660,13 @@ impl Palette {
} }
} }
fn create_texture_metadata(&self, paint_metadata: &[PaintMetadata]) fn create_texture_metadata(
-> Vec<TextureMetadataEntry> { &self,
paint_metadata.iter().map(|paint_metadata| { paint_metadata: &[PaintMetadata],
TextureMetadataEntry { ) -> Vec<TextureMetadataEntry> {
paint_metadata
.iter()
.map(|paint_metadata| TextureMetadataEntry {
color_0_transform: match paint_metadata.color_texture_metadata { color_0_transform: match paint_metadata.color_texture_metadata {
None => Transform2F::default(), None => Transform2F::default(),
Some(ref color_texture_metadata) => color_texture_metadata.transform, Some(ref color_texture_metadata) => color_texture_metadata.transform,
@ -654,31 +679,41 @@ impl Palette {
base_color: paint_metadata.base_color, base_color: paint_metadata.base_color,
filter: paint_metadata.filter(), filter: paint_metadata.filter(),
blend_mode: paint_metadata.blend_mode, blend_mode: paint_metadata.blend_mode,
} })
}).collect() .collect()
} }
fn allocate_textures(&self, fn allocate_textures(
render_commands: &mut Vec<RenderCommand>, &self,
texture_manager: &mut PaintTextureManager) { render_commands: &mut Vec<RenderCommand>,
texture_manager: &mut PaintTextureManager,
) {
for page_id in texture_manager.allocator.page_ids() { for page_id in texture_manager.allocator.page_ids() {
let page_size = texture_manager.allocator.page_size(page_id); let page_size = texture_manager.allocator.page_size(page_id);
let descriptor = TexturePageDescriptor { size: page_size }; let descriptor = TexturePageDescriptor { size: page_size };
if texture_manager.allocator.page_is_new(page_id) { if texture_manager.allocator.page_is_new(page_id) {
render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor }); render_commands.push(RenderCommand::AllocateTexturePage {
page_id,
descriptor,
});
} }
} }
texture_manager.allocator.mark_all_pages_as_allocated(); texture_manager.allocator.mark_all_pages_as_allocated();
} }
fn create_render_commands(&self, fn create_render_commands(
render_commands: &mut Vec<RenderCommand>, &self,
render_target_metadata: Vec<RenderTargetMetadata>, render_commands: &mut Vec<RenderCommand>,
gradient_tile_builder: GradientTileBuilder, render_target_metadata: Vec<RenderTargetMetadata>,
image_texel_info: Vec<ImageTexelInfo>) { gradient_tile_builder: GradientTileBuilder,
image_texel_info: Vec<ImageTexelInfo>,
) {
for (index, metadata) in render_target_metadata.iter().enumerate() { for (index, metadata) in render_target_metadata.iter().enumerate() {
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 }; let id = RenderTargetId {
scene: self.scene_id.0,
render_target: index as u32,
};
render_commands.push(RenderCommand::DeclareRenderTarget { render_commands.push(RenderCommand::DeclareRenderTarget {
id, id,
location: metadata.location, location: metadata.location,
@ -693,18 +728,22 @@ impl Palette {
} }
} }
fn free_transient_locations(&self, fn free_transient_locations(
texture_manager: &mut PaintTextureManager, &self,
transient_paint_locations: Vec<TextureLocation>) { texture_manager: &mut PaintTextureManager,
transient_paint_locations: Vec<TextureLocation>,
) {
for location in transient_paint_locations { for location in transient_paint_locations {
texture_manager.allocator.free(location); texture_manager.allocator.free(location);
} }
} }
// Frees images that are cached but not used this frame. // Frees images that are cached but not used this frame.
fn free_unused_images(&self, fn free_unused_images(
texture_manager: &mut PaintTextureManager, &self,
used_image_hashes: HashSet<ImageHash>) { texture_manager: &mut PaintTextureManager,
used_image_hashes: HashSet<ImageHash>,
) {
let cached_images = &mut texture_manager.cached_images; let cached_images = &mut texture_manager.cached_images;
let allocator = &mut texture_manager.allocator; let allocator = &mut texture_manager.allocator;
cached_images.retain(|image_hash, location| { cached_images.retain(|image_hash, location| {
@ -719,9 +758,9 @@ impl Palette {
pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo { pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo {
// Merge render targets. // Merge render targets.
let mut render_target_mapping = HashMap::new(); let mut render_target_mapping = HashMap::new();
for (old_render_target_index, render_target) in palette.render_targets for (old_render_target_index, render_target) in
.into_iter() palette.render_targets.into_iter().enumerate()
.enumerate() { {
let old_render_target_id = RenderTargetId { let old_render_target_id = RenderTargetId {
scene: palette.scene_id.0, scene: palette.scene_id.0,
render_target: old_render_target_index as u32, render_target: old_render_target_index as u32,
@ -736,31 +775,33 @@ impl Palette {
let old_paint_id = PaintId(old_paint_index as u16); let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match *old_paint.overlay() { let new_paint_id = match *old_paint.overlay() {
None => self.push_paint(old_paint), None => self.push_paint(old_paint),
Some(ref overlay) => { Some(ref overlay) => match *overlay.contents() {
match *overlay.contents() { PaintContents::Pattern(ref pattern) => match pattern.source() {
PaintContents::Pattern(ref pattern) => { PatternSource::RenderTarget {
match pattern.source() { id: old_render_target_id,
PatternSource::RenderTarget { id: old_render_target_id, size } => { size,
let mut new_pattern = } => {
Pattern::from_render_target(*old_render_target_id, *size); let mut new_pattern =
new_pattern.set_filter(pattern.filter()); Pattern::from_render_target(*old_render_target_id, *size);
new_pattern.apply_transform(pattern.transform()); new_pattern.set_filter(pattern.filter());
new_pattern.set_repeat_x(pattern.repeat_x()); new_pattern.apply_transform(pattern.transform());
new_pattern.set_repeat_y(pattern.repeat_y()); new_pattern.set_repeat_x(pattern.repeat_x());
new_pattern.set_smoothing_enabled(pattern.smoothing_enabled()); new_pattern.set_repeat_y(pattern.repeat_y());
self.push_paint(&Paint::from_pattern(new_pattern)) new_pattern.set_smoothing_enabled(pattern.smoothing_enabled());
} self.push_paint(&Paint::from_pattern(new_pattern))
_ => self.push_paint(old_paint),
}
} }
_ => self.push_paint(old_paint), _ => self.push_paint(old_paint),
} },
} _ => self.push_paint(old_paint),
},
}; };
paint_mapping.insert(old_paint_id, new_paint_id); paint_mapping.insert(old_paint_id, new_paint_id);
} }
MergedPaletteInfo { render_target_mapping, paint_mapping } MergedPaletteInfo {
render_target_mapping,
paint_mapping,
}
} }
} }
@ -782,25 +823,27 @@ impl PaintMetadata {
pub(crate) fn filter(&self) -> Filter { pub(crate) fn filter(&self) -> Filter {
match self.color_texture_metadata { match self.color_texture_metadata {
None => Filter::None, None => Filter::None,
Some(ref color_metadata) => { Some(ref color_metadata) => match color_metadata.filter {
match color_metadata.filter { PaintFilter::None => Filter::None,
PaintFilter::None => Filter::None, PaintFilter::RadialGradient { line, radii } => {
PaintFilter::RadialGradient { line, radii } => { let uv_rect =
let uv_rect = rect_to_uv(color_metadata.location.rect, rect_to_uv(color_metadata.location.rect, color_metadata.page_scale)
color_metadata.page_scale).contract( .contract(vec2f(0.0, color_metadata.page_scale.y() * 0.5));
vec2f(0.0, color_metadata.page_scale.y() * 0.5)); Filter::RadialGradient {
Filter::RadialGradient { line, radii, uv_origin: uv_rect.origin() } line,
} radii,
PaintFilter::PatternFilter(pattern_filter) => { uv_origin: uv_rect.origin(),
Filter::PatternFilter(pattern_filter)
} }
} }
} PaintFilter::PatternFilter(pattern_filter) => Filter::PatternFilter(pattern_filter),
},
} }
} }
pub(crate) fn tile_batch_texture(&self) -> Option<TileBatchTexture> { pub(crate) fn tile_batch_texture(&self) -> Option<TileBatchTexture> {
self.color_texture_metadata.as_ref().map(PaintColorTextureMetadata::as_tile_batch_texture) self.color_texture_metadata
.as_ref()
.map(PaintColorTextureMetadata::as_tile_batch_texture)
} }
} }
@ -825,13 +868,13 @@ impl GradientTileBuilder {
GradientTileBuilder { tiles: vec![] } GradientTileBuilder { tiles: vec![] }
} }
fn allocate(&mut self, fn allocate(
allocator: &mut TextureAllocator, &mut self,
transient_paint_locations: &mut Vec<TextureLocation>, allocator: &mut TextureAllocator,
gradient: &Gradient) transient_paint_locations: &mut Vec<TextureLocation>,
-> TextureLocation { gradient: &Gradient,
if self.tiles.is_empty() || ) -> TextureLocation {
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH { if self.tiles.is_empty() || self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32); let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
let area = size.x() as usize * size.y() as usize; let area = size.x() as usize * size.y() as usize;
let page_location = allocator.allocate(size, AllocationMode::OwnPage); let page_location = allocator.allocate(size, AllocationMode::OwnPage);
@ -846,8 +889,10 @@ impl GradientTileBuilder {
let data = self.tiles.last_mut().unwrap(); let data = self.tiles.last_mut().unwrap();
let location = TextureLocation { let location = TextureLocation {
page: data.page, page: data.page,
rect: RectI::new(vec2i(0, data.next_index as i32), rect: RectI::new(
vec2i(GRADIENT_TILE_LENGTH as i32, 1)), vec2i(0, data.next_index as i32),
vec2i(GRADIENT_TILE_LENGTH as i32, 1),
),
}; };
data.next_index += 1; data.next_index += 1;

View File

@ -24,7 +24,7 @@ use pathfinder_content::outline::Outline;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2I};
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
use std::mem; use std::mem;
use std::ops::Range; use std::ops::Range;
@ -90,7 +90,9 @@ impl Scene {
let end_path_id = DrawPathId(draw_path_id.0 + 1); let end_path_id = DrawPathId(draw_path_id.0 + 1);
match self.display_list.last_mut() { match self.display_list.last_mut() {
Some(DisplayItem::DrawPaths(ref mut range)) => range.end = end_path_id, Some(DisplayItem::DrawPaths(ref mut range)) => range.end = end_path_id,
_ => self.display_list.push(DisplayItem::DrawPaths(draw_path_id..end_path_id)), _ => self
.display_list
.push(DisplayItem::DrawPaths(draw_path_id..end_path_id)),
} }
self.epoch.next(); self.epoch.next();
@ -111,7 +113,8 @@ impl Scene {
/// top of the stack. /// top of the stack.
pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
let render_target_id = self.palette.push_render_target(render_target); let render_target_id = self.palette.push_render_target(render_target);
self.display_list.push(DisplayItem::PushRenderTarget(render_target_id)); self.display_list
.push(DisplayItem::PushRenderTarget(render_target_id));
self.epoch.next(); self.epoch.next();
render_target_id render_target_id
} }
@ -161,7 +164,8 @@ impl Scene {
match display_item { match display_item {
DisplayItem::PushRenderTarget(old_render_target_id) => { DisplayItem::PushRenderTarget(old_render_target_id) => {
let new_render_target_id = render_target_mapping[&old_render_target_id]; let new_render_target_id = render_target_mapping[&old_render_target_id];
self.display_list.push(DisplayItem::PushRenderTarget(new_render_target_id)); self.display_list
.push(DisplayItem::PushRenderTarget(new_render_target_id));
} }
DisplayItem::PopRenderTarget => { DisplayItem::PopRenderTarget => {
self.display_list.push(DisplayItem::PopRenderTarget); self.display_list.push(DisplayItem::PopRenderTarget);
@ -180,11 +184,13 @@ impl Scene {
} }
#[inline] #[inline]
pub(crate) fn build_paint_info(&mut self, pub(crate) fn build_paint_info(
texture_manager: &mut PaintTextureManager, &mut self,
render_transform: Transform2F) texture_manager: &mut PaintTextureManager,
-> PaintInfo { render_transform: Transform2F,
self.palette.build_paint_info(texture_manager, render_transform) ) -> PaintInfo {
self.palette
.build_paint_info(texture_manager, render_transform)
} }
/// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint /// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint
@ -227,10 +233,11 @@ impl Scene {
} }
#[allow(deprecated)] #[allow(deprecated)]
pub(crate) fn apply_render_options(&self, pub(crate) fn apply_render_options(
original_outline: &Outline, &self,
options: &PreparedBuildOptions) original_outline: &Outline,
-> Outline { options: &PreparedBuildOptions,
) -> Outline {
let mut outline; let mut outline;
match options.transform { match options.transform {
PreparedRenderTransform::Perspective { PreparedRenderTransform::Perspective {
@ -289,11 +296,14 @@ impl Scene {
/// `SequentialExecutor` to prepare commands on a single thread or `RayonExecutor` to prepare /// `SequentialExecutor` to prepare commands on a single thread or `RayonExecutor` to prepare
/// commands in parallel across multiple threads. /// commands in parallel across multiple threads.
#[inline] #[inline]
pub fn build<'a, 'b, E>(&mut self, pub fn build<'a, 'b, E>(
options: BuildOptions, &mut self,
sink: &'b mut SceneSink<'a>, options: BuildOptions,
executor: &E) sink: &'b mut SceneSink<'a>,
where E: Executor { executor: &E,
) where
E: Executor,
{
let prepared_options = options.prepare(self.bounds); let prepared_options = options.prepare(self.bounds);
SceneBuilder::new(self, &prepared_options, sink).build(executor) SceneBuilder::new(self, &prepared_options, sink).build(executor)
} }
@ -334,7 +344,10 @@ impl Scene {
/// Returns the paint with the given ID. /// Returns the paint with the given ID.
#[inline] #[inline]
pub fn get_paint(&self, paint_id: PaintId) -> &Paint { pub fn get_paint(&self, paint_id: PaintId) -> &Paint {
self.palette.paints.get(paint_id.0 as usize).expect("No paint with that ID!") self.palette
.paints
.get(paint_id.0 as usize)
.expect("No paint with that ID!")
} }
/// Returns the globally-unique ID of the scene. /// Returns the globally-unique ID of the scene.
@ -349,12 +362,16 @@ impl Scene {
} }
/// A convenience method to build a scene and accumulate commands into a vector. /// A convenience method to build a scene and accumulate commands into a vector.
pub fn build_into_vector<D, E>(&mut self, pub fn build_into_vector<D, E>(
renderer: &mut Renderer<D>, &mut self,
build_options: BuildOptions, renderer: &mut Renderer<D>,
executor: E) build_options: BuildOptions,
-> Vec<RenderCommand> executor: E,
where D: Device, E: Executor { ) -> Vec<RenderCommand>
where
D: Device,
E: Executor,
{
let commands = Arc::new(Mutex::new(vec![])); let commands = Arc::new(Mutex::new(vec![]));
let commands_for_listener = commands.clone(); let commands_for_listener = commands.clone();
let listener = RenderCommandListener::new(Box::new(move |command| { let listener = RenderCommandListener::new(Box::new(move |command| {
@ -368,11 +385,15 @@ impl Scene {
/// A convenience method to build a scene and send the resulting commands to the given /// A convenience method to build a scene and send the resulting commands to the given
/// renderer. /// renderer.
pub fn build_and_render<D, E>(&mut self, pub fn build_and_render<D, E>(
renderer: &mut Renderer<D>, &mut self,
build_options: BuildOptions, renderer: &mut Renderer<D>,
executor: E) build_options: BuildOptions,
where D: Device, E: Executor + Send { executor: E,
) where
D: Device,
E: Executor + Send,
{
std::thread::scope(move |scope| { std::thread::scope(move |scope| {
let (tx, rx) = flume::bounded(MAX_MESSAGES_IN_FLIGHT); let (tx, rx) = flume::bounded(MAX_MESSAGES_IN_FLIGHT);
@ -380,9 +401,8 @@ impl Scene {
// TODO: avoid this auxiliary thread // TODO: avoid this auxiliary thread
scope.spawn(move || { scope.spawn(move || {
let listener = RenderCommandListener::new(Box::new(move |command| { let listener =
tx.send(command).unwrap() RenderCommandListener::new(Box::new(move |command| tx.send(command).unwrap()));
}));
let mut sink = SceneSink::new(listener, level); let mut sink = SceneSink::new(listener, level);
self.build(build_options, &mut sink, &executor); self.build(build_options, &mut sink, &executor);
}); });
@ -430,9 +450,15 @@ impl SceneEpoch {
#[inline] #[inline]
fn successor(&self) -> SceneEpoch { fn successor(&self) -> SceneEpoch {
if self.lo == u64::MAX { if self.lo == u64::MAX {
SceneEpoch { hi: self.hi + 1, lo: 0 } SceneEpoch {
hi: self.hi + 1,
lo: 0,
}
} else { } else {
SceneEpoch { hi: self.hi, lo: self.lo + 1 } SceneEpoch {
hi: self.hi,
lo: self.lo + 1,
}
} }
} }
@ -445,8 +471,10 @@ impl SceneEpoch {
impl<'a> SceneSink<'a> { impl<'a> SceneSink<'a> {
/// Creates a new scene sink from the given render command listener and level. /// Creates a new scene sink from the given render command listener and level.
#[inline] #[inline]
pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel) pub fn new(
-> SceneSink<'a> { listener: RenderCommandListener<'a>,
renderer_level: RendererLevel,
) -> SceneSink<'a> {
SceneSink { SceneSink {
listener, listener,
renderer_level, renderer_level,
@ -607,7 +635,12 @@ impl ClipPath {
/// has no name. /// has no name.
#[inline] #[inline]
pub fn new(outline: Outline) -> ClipPath { pub fn new(outline: Outline) -> ClipPath {
ClipPath { outline, clip_path: None, fill_rule: FillRule::Winding, name: String::new() } ClipPath {
outline,
clip_path: None,
fill_rule: FillRule::Winding,
name: String::new(),
}
} }
/// Returns the outline of this clip path, which defines its vector commands. /// Returns the outline of this clip path, which defines its vector commands.

View File

@ -9,18 +9,26 @@
// except according to those terms. // except according to those terms.
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{vec2i, Vector2I};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DenseTileMap<T> where T: Clone + Copy { pub struct DenseTileMap<T>
where
T: Clone + Copy,
{
pub data: Vec<T>, pub data: Vec<T>,
pub rect: RectI, pub rect: RectI,
} }
impl<T> DenseTileMap<T> where T: Clone + Copy { impl<T> DenseTileMap<T>
where
T: Clone + Copy,
{
#[inline] #[inline]
pub fn from_builder<F>(mut build: F, rect: RectI) -> DenseTileMap<T> pub fn from_builder<F>(mut build: F, rect: RectI) -> DenseTileMap<T>
where F: FnMut(Vector2I) -> T { where
F: FnMut(Vector2I) -> T,
{
let mut data = Vec::with_capacity(rect.size().x() as usize * rect.size().y() as usize); let mut data = Vec::with_capacity(rect.size().x() as usize * rect.size().y() as usize);
for y in rect.min_y()..rect.max_y() { for y in rect.min_y()..rect.max_y() {
for x in rect.min_x()..rect.max_x() { for x in rect.min_x()..rect.max_x() {
@ -32,7 +40,8 @@ impl<T> DenseTileMap<T> where T: Clone + Copy {
#[inline] #[inline]
pub fn get(&self, coords: Vector2I) -> Option<&T> { pub fn get(&self, coords: Vector2I) -> Option<&T> {
self.coords_to_index(coords).and_then(|index| self.data.get(index)) self.coords_to_index(coords)
.and_then(|index| self.data.get(index))
} }
#[inline] #[inline]

View File

@ -16,14 +16,14 @@ use crate::gpu::options::RendererLevel;
use crate::gpu_data::AlphaTileId; use crate::gpu_data::AlphaTileId;
use crate::options::PrepareMode; use crate::options::PrepareMode;
use crate::scene::{ClipPathId, PathId}; use crate::scene::{ClipPathId, PathId};
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH, TilingPathInfo}; use crate::tiles::{TilingPathInfo, TILE_HEIGHT, TILE_WIDTH};
use pathfinder_content::clip; use pathfinder_content::clip;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{ContourIterFlags, Outline}; use pathfinder_content::outline::{ContourIterFlags, Outline};
use pathfinder_content::segment::Segment; use pathfinder_content::segment::Segment;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_simd::default::{F32x2, U32x2}; use pathfinder_simd::default::{F32x2, U32x2};
use std::f32::NEG_INFINITY; use std::f32::NEG_INFINITY;
@ -37,32 +37,43 @@ pub(crate) struct Tiler<'a, 'b, 'c, 'd> {
} }
impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> { impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>, pub(crate) fn new(
path_id: PathId, scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>,
outline: &'a Outline, path_id: PathId,
fill_rule: FillRule, outline: &'a Outline,
view_box: RectF, fill_rule: FillRule,
prepare_mode: &PrepareMode, view_box: RectF,
clip_path_id: Option<ClipPathId>, prepare_mode: &PrepareMode,
built_clip_paths: &'a [BuiltPath], clip_path_id: Option<ClipPathId>,
path_info: TilingPathInfo) built_clip_paths: &'a [BuiltPath],
-> Tiler<'a, 'b, 'c, 'd> { path_info: TilingPathInfo,
let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF::default()); ) -> Tiler<'a, 'b, 'c, 'd> {
let bounds = outline
.bounds()
.intersection(view_box)
.unwrap_or(RectF::default());
let clip_path = match clip_path_id { let clip_path = match clip_path_id {
Some(clip_path_id) => Some(&built_clip_paths[clip_path_id.0 as usize]), Some(clip_path_id) => Some(&built_clip_paths[clip_path_id.0 as usize]),
_ => None, _ => None,
}; };
let object_builder = ObjectBuilder::new(path_id, let object_builder = ObjectBuilder::new(
bounds, path_id,
view_box, bounds,
fill_rule, view_box,
prepare_mode, fill_rule,
clip_path_id, prepare_mode,
&path_info); clip_path_id,
&path_info,
);
Tiler { scene_builder, object_builder, outline, clip_path } Tiler {
scene_builder,
object_builder,
outline,
clip_path,
}
} }
pub(crate) fn generate_tiles(&mut self) { pub(crate) fn generate_tiles(&mut self) {
@ -93,9 +104,11 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
fn prepare_tiles(&mut self) { fn prepare_tiles(&mut self) {
// Don't do this here if the GPU will do it. // Don't do this here if the GPU will do it.
let (backdrops, tiles, clips) = match self.object_builder.built_path.data { let (backdrops, tiles, clips) = match self.object_builder.built_path.data {
BuiltPathData::CPU(ref mut tiled_data) => { BuiltPathData::CPU(ref mut tiled_data) => (
(&mut tiled_data.backdrops, &mut tiled_data.tiles, &mut tiled_data.clip_tiles) &mut tiled_data.backdrops,
} &mut tiled_data.tiles,
&mut tiled_data.clip_tiles,
),
BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => { BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => {
panic!("We shouldn't be preparing tiles on CPU!") panic!("We shouldn't be preparing tiles on CPU!")
} }
@ -118,30 +131,34 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
}; };
match clip_tiles.get(tile_coords) { match clip_tiles.get(tile_coords) {
Some(clip_tile) => { Some(clip_tile) => {
if clip_tile.alpha_tile_id != AlphaTileId(!0) && if clip_tile.alpha_tile_id != AlphaTileId(!0)
draw_alpha_tile_id != AlphaTileId(!0) { && draw_alpha_tile_id != AlphaTileId(!0)
{
// Hard case: We have an alpha tile and a clip tile with masks. Add a // Hard case: We have an alpha tile and a clip tile with masks. Add a
// job to combine the two masks. Because the mask combining step // job to combine the two masks. Because the mask combining step
// applies the backdrops, zero out the backdrop in the draw tile itself // applies the backdrops, zero out the backdrop in the draw tile itself
// so that we don't double-count it. // so that we don't double-count it.
let clip = clips.as_mut() let clip = clips
.expect("Where are the clips?") .as_mut()
.get_mut(tile_coords) .expect("Where are the clips?")
.unwrap(); .get_mut(tile_coords)
.unwrap();
clip.dest_tile_id = draw_tile.alpha_tile_id; clip.dest_tile_id = draw_tile.alpha_tile_id;
clip.dest_backdrop = draw_tile_backdrop as i32; clip.dest_backdrop = draw_tile_backdrop as i32;
clip.src_tile_id = clip_tile.alpha_tile_id; clip.src_tile_id = clip_tile.alpha_tile_id;
clip.src_backdrop = clip_tile.backdrop as i32; clip.src_backdrop = clip_tile.backdrop as i32;
draw_tile_backdrop = 0; draw_tile_backdrop = 0;
} else if clip_tile.alpha_tile_id != AlphaTileId(!0) && } else if clip_tile.alpha_tile_id != AlphaTileId(!0)
draw_alpha_tile_id == AlphaTileId(!0) && && draw_alpha_tile_id == AlphaTileId(!0)
draw_tile_backdrop != 0 { && draw_tile_backdrop != 0
{
// This is a solid draw tile, but there's a clip applied. Replace it // This is a solid draw tile, but there's a clip applied. Replace it
// with an alpha tile pointing directly to the clip mask. // with an alpha tile pointing directly to the clip mask.
draw_alpha_tile_id = clip_tile.alpha_tile_id; draw_alpha_tile_id = clip_tile.alpha_tile_id;
draw_tile_backdrop = clip_tile.backdrop; draw_tile_backdrop = clip_tile.backdrop;
} else if clip_tile.alpha_tile_id == AlphaTileId(!0) && } else if clip_tile.alpha_tile_id == AlphaTileId(!0)
clip_tile.backdrop == 0 { && clip_tile.backdrop == 0
{
// This is a blank clip tile. Cull the draw tile entirely. // This is a blank clip tile. Cull the draw tile entirely.
draw_alpha_tile_id = AlphaTileId(!0); draw_alpha_tile_id = AlphaTileId(!0);
draw_tile_backdrop = 0; draw_tile_backdrop = 0;
@ -163,17 +180,20 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
} }
} }
fn process_segment(segment: &Segment, fn process_segment(
scene_builder: &SceneBuilder, segment: &Segment,
object_builder: &mut ObjectBuilder) { scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder,
) {
// TODO(pcwalton): Stop degree elevating. // TODO(pcwalton): Stop degree elevating.
if segment.is_quadratic() { if segment.is_quadratic() {
let cubic = segment.to_cubic(); let cubic = segment.to_cubic();
return process_segment(&cubic, scene_builder, object_builder); return process_segment(&cubic, scene_builder, object_builder);
} }
if segment.is_line() || if segment.is_line()
(segment.is_cubic() && segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE)) { || (segment.is_cubic() && segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE))
{
return process_line_segment(segment.baseline, scene_builder, object_builder); return process_line_segment(segment.baseline, scene_builder, object_builder);
} }
@ -188,12 +208,16 @@ fn process_segment(segment: &Segment,
// //
// The algorithm to step through tiles is Amanatides and Woo, "A Fast Voxel Traversal Algorithm for // The algorithm to step through tiles is Amanatides and Woo, "A Fast Voxel Traversal Algorithm for
// Ray Tracing" 1987: http://www.cse.yorku.ca/~amana/research/grid.pdf // Ray Tracing" 1987: http://www.cse.yorku.ca/~amana/research/grid.pdf
fn process_line_segment(line_segment: LineSegment2F, fn process_line_segment(
scene_builder: &SceneBuilder, line_segment: LineSegment2F,
object_builder: &mut ObjectBuilder) { scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder,
) {
let view_box = scene_builder.scene.view_box(); let view_box = scene_builder.scene.view_box();
let clip_box = RectF::from_points(vec2f(view_box.min_x(), NEG_INFINITY), let clip_box = RectF::from_points(
view_box.lower_right()); vec2f(view_box.min_x(), NEG_INFINITY),
view_box.lower_right(),
);
let line_segment = match clip::clip_line_segment_to_rect(line_segment, clip_box) { let line_segment = match clip::clip_line_segment_to_rect(line_segment, clip_box) {
None => return, None => return,
Some(line_segment) => line_segment, Some(line_segment) => line_segment,
@ -202,8 +226,9 @@ fn process_line_segment(line_segment: LineSegment2F,
let tile_size = vec2f(TILE_WIDTH as f32, TILE_HEIGHT as f32); let tile_size = vec2f(TILE_WIDTH as f32, TILE_HEIGHT as f32);
let tile_size_recip = Vector2F::splat(1.0) / tile_size; let tile_size_recip = Vector2F::splat(1.0) / tile_size;
let tile_line_segment = let tile_line_segment = (line_segment.0 * tile_size_recip.0.concat_xy_xy(tile_size_recip.0))
(line_segment.0 * tile_size_recip.0.concat_xy_xy(tile_size_recip.0)).floor().to_i32x4(); .floor()
.to_i32x4();
let from_tile_coords = Vector2I(tile_line_segment.xy()); let from_tile_coords = Vector2I(tile_line_segment.xy());
let to_tile_coords = Vector2I(tile_line_segment.zw()); let to_tile_coords = Vector2I(tile_line_segment.zw());
@ -216,8 +241,9 @@ fn process_line_segment(line_segment: LineSegment2F,
// Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x >= 0 ? 1 : 0, // Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x >= 0 ? 1 : 0,
// vector.y >= 0 ? 1 : 0)) * tile_size`. // vector.y >= 0 ? 1 : 0)) * tile_size`.
let first_tile_crossing = (from_tile_coords + let first_tile_crossing =
Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32() * tile_size; (from_tile_coords + Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32()
* tile_size;
let mut t_max = (first_tile_crossing - line_segment.from()) / vector; let mut t_max = (first_tile_crossing - line_segment.from()) / vector;
let t_delta = (tile_size / vector).abs(); let t_delta = (tile_size / vector).abs();
@ -244,11 +270,19 @@ fn process_line_segment(line_segment: LineSegment2F,
// //
// In that case we just need to step in the positive direction to move to the lower // In that case we just need to step in the positive direction to move to the lower
// right tile. // right tile.
if step.x() > 0 { StepDirection::X } else { StepDirection::Y } if step.x() > 0 {
StepDirection::X
} else {
StepDirection::Y
}
}; };
let next_t = let next_t = (if next_step_direction == StepDirection::X {
(if next_step_direction == StepDirection::X { t_max.x() } else { t_max.y() }).min(1.0); t_max.x()
} else {
t_max.y()
})
.min(1.0);
// If we've reached the end tile, don't step at all. // If we've reached the end tile, don't step at all.
let next_step_direction = if tile_coords == to_tile_coords { let next_step_direction = if tile_coords == to_tile_coords {
@ -264,13 +298,15 @@ fn process_line_segment(line_segment: LineSegment2F,
// Add extra fills if necessary. // Add extra fills if necessary.
if step.y() < 0 && next_step_direction == Some(StepDirection::Y) { if step.y() < 0 && next_step_direction == Some(StepDirection::Y) {
// Leaves through top boundary. // Leaves through top boundary.
let auxiliary_segment = LineSegment2F::new(clipped_line_segment.to(), let auxiliary_segment =
tile_coords.to_f32() * tile_size); LineSegment2F::new(clipped_line_segment.to(), tile_coords.to_f32() * tile_size);
object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords); object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords);
} else if step.y() > 0 && last_step_direction == Some(StepDirection::Y) { } else if step.y() > 0 && last_step_direction == Some(StepDirection::Y) {
// Enters through top boundary. // Enters through top boundary.
let auxiliary_segment = LineSegment2F::new(tile_coords.to_f32() * tile_size, let auxiliary_segment = LineSegment2F::new(
clipped_line_segment.from()); tile_coords.to_f32() * tile_size,
clipped_line_segment.from(),
);
object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords); object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords);
} }

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::gpu_data::{TileObjectPrimitive, TILE_CTRL_MASK_WINDING};
use crate::gpu_data::{TILE_CTRL_MASK_0_SHIFT, TILE_CTRL_MASK_EVEN_ODD}; use crate::gpu_data::{TILE_CTRL_MASK_0_SHIFT, TILE_CTRL_MASK_EVEN_ODD};
use crate::gpu_data::{TILE_CTRL_MASK_WINDING, TileObjectPrimitive};
use crate::paint::PaintId; use crate::paint::PaintId;
use pathfinder_content::effects::BlendMode; use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
@ -62,10 +62,14 @@ impl TilingPathInfo {
} }
pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI { pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI {
(rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32)).round_out().to_i32() (rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32))
.round_out()
.to_i32()
} }
impl TileObjectPrimitive { impl TileObjectPrimitive {
#[inline] #[inline]
pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() } pub fn is_solid(&self) -> bool {
!self.alpha_tile_id.is_valid()
}
} }

View File

@ -19,9 +19,16 @@ fn main() {
let mut dest = File::create(dest_path).unwrap(); let mut dest = File::create(dest_path).unwrap();
let cwd = env::current_dir().unwrap(); let cwd = env::current_dir().unwrap();
writeln!(&mut dest, "// Generated by `pathfinder/resources/build.rs`. Do not edit!\n").unwrap(); writeln!(
writeln!(&mut dest, &mut dest,
"pub static RESOURCES: &'static [(&'static str, &'static [u8])] = &[").unwrap(); "// Generated by `pathfinder/resources/build.rs`. Do not edit!\n"
)
.unwrap();
writeln!(
&mut dest,
"pub static RESOURCES: &'static [(&'static str, &'static [u8])] = &["
)
.unwrap();
let src = BufReader::new(File::open("MANIFEST").unwrap()); let src = BufReader::new(File::open("MANIFEST").unwrap());
for line in src.lines() { for line in src.lines() {
@ -36,10 +43,12 @@ fn main() {
full_path.push(line); full_path.push(line);
let escaped_full_path = full_path.to_str().unwrap().escape_default().to_string(); let escaped_full_path = full_path.to_str().unwrap().escape_default().to_string();
writeln!(&mut dest, writeln!(
" (\"{}\", include_bytes!(\"{}\")),", &mut dest,
escaped_path, " (\"{}\", include_bytes!(\"{}\")),",
escaped_full_path).unwrap(); escaped_path, escaped_full_path
)
.unwrap();
println!("cargo:rerun-if-changed={}", line); println!("cargo:rerun-if-changed={}", line);
} }

View File

@ -26,7 +26,11 @@ impl EmbeddedResourceLoader {
impl ResourceLoader for EmbeddedResourceLoader { impl ResourceLoader for EmbeddedResourceLoader {
fn slurp(&self, virtual_path: &str) -> Result<Vec<u8>, IOError> { fn slurp(&self, virtual_path: &str) -> Result<Vec<u8>, IOError> {
match RESOURCES.iter().filter(|&(path, _)| *path == virtual_path).next() { match RESOURCES
.iter()
.filter(|&(path, _)| *path == virtual_path)
.next()
{
Some((_, data)) => Ok(data.to_vec()), Some((_, data)) => Ok(data.to_vec()),
None => Err(IOError::from(ErrorKind::NotFound)), None => Err(IOError::from(ErrorKind::NotFound)),
} }

View File

@ -11,10 +11,10 @@
//! An abstraction for reading resources. //! An abstraction for reading resources.
//! //!
//! This accomplishes two purposes over just using the filesystem to locate shaders and so forth: //! This accomplishes two purposes over just using the filesystem to locate shaders and so forth:
//! //!
//! 1. Downstream users of Pathfinder shouldn't be burdened with having to install the resources //! 1. Downstream users of Pathfinder shouldn't be burdened with having to install the resources
//! alongside their binary. //! alongside their binary.
//! //!
//! 2. There may not be a traditional filesystem available, as for example is the case on Android. //! 2. There may not be a traditional filesystem available, as for example is the case on Android.
use std::io::Error as IOError; use std::io::Error as IOError;

View File

@ -10,9 +10,9 @@
use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t}; use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t};
use std::arch::aarch64::{uint32x2_t, uint32x4_t}; use std::arch::aarch64::{uint32x2_t, uint32x4_t};
use std::intrinsics::simd::*;
use std::f32; use std::f32;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::intrinsics::simd::*;
use std::mem; use std::mem;
use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Not, Shr, Sub}; use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Not, Shr, Sub};
@ -201,7 +201,6 @@ impl IndexMut<usize> for F32x2 {
} }
} }
impl Add<F32x2> for F32x2 { impl Add<F32x2> for F32x2 {
type Output = F32x2; type Output = F32x2;
#[inline] #[inline]
@ -832,7 +831,6 @@ impl BitOr<U32x2> for U32x2 {
} }
} }
// Four 32-bit unsigned integers // Four 32-bit unsigned integers
#[derive(Clone, Copy)] #[derive(Clone, Copy)]

View File

@ -49,7 +49,10 @@ impl F32x2 {
#[inline] #[inline]
pub fn approx_eq(self, other: F32x2, epsilon: f32) -> bool { pub fn approx_eq(self, other: F32x2, epsilon: f32) -> bool {
(self - other).abs().packed_gt(F32x2::splat(epsilon)).all_false() (self - other)
.abs()
.packed_gt(F32x2::splat(epsilon))
.all_false()
} }
} }
@ -140,7 +143,10 @@ impl F32x4 {
#[inline] #[inline]
pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool { pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool {
(self - other).abs().packed_gt(F32x4::splat(epsilon)).all_false() (self - other)
.abs()
.packed_gt(F32x4::splat(epsilon))
.all_false()
} }
} }

View File

@ -10,7 +10,7 @@
use std::f32; use std::f32;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Shr, Sub, Not}; use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Not, Shr, Sub};
mod swizzle_f32x4; mod swizzle_f32x4;
mod swizzle_i32x4; mod swizzle_i32x4;
@ -485,18 +485,12 @@ impl I32x2 {
#[inline] #[inline]
pub fn min(self, other: I32x2) -> I32x2 { pub fn min(self, other: I32x2) -> I32x2 {
I32x2([ I32x2([self[0].min(other[0]), self[1].min(other[1])])
self[0].min(other[0]),
self[1].min(other[1]),
])
} }
#[inline] #[inline]
pub fn max(self, other: I32x2) -> I32x2 { pub fn max(self, other: I32x2) -> I32x2 {
I32x2([ I32x2([self[0].max(other[0]), self[1].max(other[1])])
self[0].max(other[0]),
self[1].max(other[1]),
])
} }
// Packed comparisons // Packed comparisons
@ -531,7 +525,7 @@ impl I32x2 {
if self[0] < other[0] { !0 } else { 0 }, if self[0] < other[0] { !0 } else { 0 },
if self[1] < other[1] { !0 } else { 0 }, if self[1] < other[1] { !0 } else { 0 },
]) ])
} }
// Conversions // Conversions
@ -715,7 +709,12 @@ impl I32x4 {
/// FIXME(pcwalton): Should they? This will assert on overflow in debug. /// FIXME(pcwalton): Should they? This will assert on overflow in debug.
#[inline] #[inline]
pub fn to_u32x4(self) -> U32x4 { pub fn to_u32x4(self) -> U32x4 {
U32x4([self[0] as u32, self[1] as u32, self[2] as u32, self[3] as u32]) U32x4([
self[0] as u32,
self[1] as u32,
self[2] as u32,
self[3] as u32,
])
} }
} }
@ -777,7 +776,12 @@ impl BitAnd<I32x4> for I32x4 {
type Output = I32x4; type Output = I32x4;
#[inline] #[inline]
fn bitand(self, other: I32x4) -> I32x4 { fn bitand(self, other: I32x4) -> I32x4 {
I32x4([self[0] & other[0], self[1] & other[1], self[2] & other[2], self[3] & other[3]]) I32x4([
self[0] & other[0],
self[1] & other[1],
self[2] & other[2],
self[3] & other[3],
])
} }
} }
@ -785,7 +789,12 @@ impl BitOr<I32x4> for I32x4 {
type Output = I32x4; type Output = I32x4;
#[inline] #[inline]
fn bitor(self, other: I32x4) -> I32x4 { fn bitor(self, other: I32x4) -> I32x4 {
I32x4([self[0] | other[0], self[1] | other[1], self[2] | other[2], self[3] | other[3]]) I32x4([
self[0] | other[0],
self[1] | other[1],
self[2] | other[2],
self[3] | other[3],
])
} }
} }
@ -840,7 +849,6 @@ impl U32x2 {
pub fn to_i32x2(self) -> I32x2 { pub fn to_i32x2(self) -> I32x2 {
I32x2::new(self[0] as i32, self[1] as i32) I32x2::new(self[0] as i32, self[1] as i32)
} }
} }
impl BitAnd<U32x2> for U32x2 { impl BitAnd<U32x2> for U32x2 {
@ -894,7 +902,12 @@ impl U32x4 {
/// FIXME(pcwalton): Should they? This will assert on overflow in debug. /// FIXME(pcwalton): Should they? This will assert on overflow in debug.
#[inline] #[inline]
pub fn to_i32x4(self) -> I32x4 { pub fn to_i32x4(self) -> I32x4 {
I32x4([self[0] as i32, self[1] as i32, self[2] as i32, self[3] as i32]) I32x4([
self[0] as i32,
self[1] as i32,
self[2] as i32,
self[3] as i32,
])
} }
// Basic operations // Basic operations
@ -930,6 +943,11 @@ impl Shr<u32> for U32x4 {
type Output = U32x4; type Output = U32x4;
#[inline] #[inline]
fn shr(self, amount: u32) -> U32x4 { fn shr(self, amount: u32) -> U32x4 {
U32x4([self[0] >> amount, self[1] >> amount, self[2] >> amount, self[3] >> amount]) U32x4([
self[0] >> amount,
self[1] >> amount,
self[2] >> amount,
self[3] >> amount,
])
} }
} }

View File

@ -37,7 +37,10 @@ fn test_f32x4_accessors_and_mutators() {
fn test_f32x4_basic_ops() { fn test_f32x4_basic_ops() {
let a = F32x4::new(1.0, 3.0, 5.0, 7.0); let a = F32x4::new(1.0, 3.0, 5.0, 7.0);
let b = F32x4::new(2.0, 2.0, 6.0, 6.0); let b = F32x4::new(2.0, 2.0, 6.0, 6.0);
assert_eq!(a.approx_recip(), F32x4::new(0.99975586, 0.333313, 0.19995117, 0.14282227)); assert_eq!(
a.approx_recip(),
F32x4::new(0.99975586, 0.333313, 0.19995117, 0.14282227)
);
assert_eq!(a.min(b), F32x4::new(1.0, 2.0, 5.0, 6.0)); assert_eq!(a.min(b), F32x4::new(1.0, 2.0, 5.0, 6.0));
assert_eq!(a.max(b), F32x4::new(2.0, 3.0, 6.0, 7.0)); assert_eq!(a.max(b), F32x4::new(2.0, 3.0, 6.0, 7.0));
let c = F32x4::new(-1.0, 1.3, -20.0, 3.6); let c = F32x4::new(-1.0, 1.3, -20.0, 3.6);

View File

@ -13,14 +13,14 @@ use std::fmt::{self, Debug, Formatter};
use std::mem; use std::mem;
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Index, IndexMut, Mul, Not, Shr, Sub}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Index, IndexMut, Mul, Not, Shr, Sub};
#[cfg(target_pointer_width = "32")]
use std::arch::x86::{__m128, __m128i};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
use std::arch::x86; use std::arch::x86;
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "32")]
use std::arch::x86_64::{__m128, __m128i}; use std::arch::x86::{__m128, __m128i};
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
use std::arch::x86_64 as x86; use std::arch::x86_64 as x86;
#[cfg(target_pointer_width = "64")]
use std::arch::x86_64::{__m128, __m128i};
mod swizzle_f32x4; mod swizzle_f32x4;
mod swizzle_i32x4; mod swizzle_i32x4;
@ -285,20 +285,12 @@ impl F32x4 {
#[inline] #[inline]
pub fn packed_eq(self, other: F32x4) -> U32x4 { pub fn packed_eq(self, other: F32x4) -> U32x4 {
unsafe { unsafe { U32x4(x86::_mm_castps_si128(x86::_mm_cmpeq_ps(self.0, other.0))) }
U32x4(x86::_mm_castps_si128(x86::_mm_cmpeq_ps(
self.0, other.0,
)))
}
} }
#[inline] #[inline]
pub fn packed_gt(self, other: F32x4) -> U32x4 { pub fn packed_gt(self, other: F32x4) -> U32x4 {
unsafe { unsafe { U32x4(x86::_mm_castps_si128(x86::_mm_cmpgt_ps(self.0, other.0))) }
U32x4(x86::_mm_castps_si128(x86::_mm_cmpgt_ps(
self.0, other.0,
)))
}
} }
#[inline] #[inline]

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ keywords = ["pathfinder", "svg", "vector", "graphics", "gpu"]
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
hashbrown = "0.7" hashbrown = "0.7"
usvg = "0.9" usvg = "0.10.0"
[dependencies.pathfinder_color] [dependencies.pathfinder_color]
path = "../color" path = "../color"

View File

@ -25,7 +25,7 @@ use pathfinder_content::transform::Transform2FPathIter;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::Paint; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene}; use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
use pathfinder_simd::default::F32x2; use pathfinder_simd::default::F32x2;
@ -80,7 +80,9 @@ impl SVGScene {
let root = &tree.root(); let root = &tree.root();
match *root.borrow() { match *root.borrow() {
NodeKind::Svg(ref svg) => { NodeKind::Svg(ref svg) => {
built_svg.scene.set_view_box(usvg_rect_to_euclid_rect(&svg.view_box.rect)); built_svg
.scene
.set_view_box(usvg_rect_to_euclid_rect(&svg.view_box.rect));
for kid in root.children() { for kid in root.children() {
built_svg.process_node(&kid, &State::new(), &mut None); built_svg.process_node(&kid, &State::new(), &mut None);
} }
@ -91,24 +93,24 @@ impl SVGScene {
built_svg built_svg
} }
fn process_node(&mut self, fn process_node(&mut self, node: &Node, state: &State, clip_outline: &mut Option<Outline>) {
node: &Node,
state: &State,
clip_outline: &mut Option<Outline>) {
let mut state = (*state).clone(); let mut state = (*state).clone();
let node_transform = usvg_transform_to_transform_2d(&node.transform()); let node_transform = usvg_transform_to_transform_2d(&node.transform());
state.transform = state.transform * node_transform; state.transform = state.transform * node_transform;
match *node.borrow() { match *node.borrow() {
NodeKind::Group(ref group) => { NodeKind::Group(ref group) => {
if group.filter.is_some() { if group.filter.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_FILTER_ATTR); self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_FILTER_ATTR);
} }
if group.mask.is_some() { if group.mask.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MASK_ATTR); self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_MASK_ATTR);
} }
if let Some(ref clip_path_name) = group.clip_path { if let Some(ref clip_path_name) = group.clip_path {
if let Some(clip_outline) = self.clip_paths.get(clip_path_name) { if let Some(clip_outline) = self.clip_paths.get(clip_path_name) {
let transformed_outline = clip_outline.clone().transformed(&state.transform); let transformed_outline =
clip_outline.clone().transformed(&state.transform);
let mut clip_path = ClipPath::new(transformed_outline); let mut clip_path = ClipPath::new(transformed_outline);
clip_path.set_clip_path(state.clip_path); clip_path.set_clip_path(state.clip_path);
clip_path.set_name(format!("ClipPath({})", clip_path_name)); clip_path.set_name(format!("ClipPath({})", clip_path_name));
@ -126,31 +128,38 @@ impl SVGScene {
let path = UsvgPathToSegments::new(path.data.iter().cloned()); let path = UsvgPathToSegments::new(path.data.iter().cloned());
let path = Transform2FPathIter::new(path, &state.transform); let path = Transform2FPathIter::new(path, &state.transform);
if clip_outline.is_some() { if clip_outline.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MULTIPLE_CLIP_PATHS); self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_MULTIPLE_CLIP_PATHS);
} }
*clip_outline = Some(Outline::from_segments(path)); *clip_outline = Some(Outline::from_segments(path));
} }
NodeKind::Path(ref path) if state.path_destination == PathDestination::Draw && NodeKind::Path(ref path)
path.visibility == Visibility::Visible => { if state.path_destination == PathDestination::Draw
&& path.visibility == Visibility::Visible =>
{
if let Some(ref fill) = path.fill { if let Some(ref fill) = path.fill {
let path = UsvgPathToSegments::new(path.data.iter().cloned()); let path = UsvgPathToSegments::new(path.data.iter().cloned());
let outline = Outline::from_segments(path); let outline = Outline::from_segments(path);
let name = format!("Fill({})", node.id()); let name = format!("Fill({})", node.id());
self.push_draw_path(outline, self.push_draw_path(
name, outline,
&state, name,
&fill.paint, &state,
fill.opacity, &fill.paint,
fill.rule); fill.opacity,
fill.rule,
);
} }
if let Some(ref stroke) = path.stroke { if let Some(ref stroke) = path.stroke {
let stroke_style = StrokeStyle { let stroke_style = StrokeStyle {
line_width: f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH), line_width: f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH),
line_cap: LineCap::from_usvg_line_cap(stroke.linecap), line_cap: LineCap::from_usvg_line_cap(stroke.linecap),
line_join: LineJoin::from_usvg_line_join(stroke.linejoin, line_join: LineJoin::from_usvg_line_join(
stroke.miterlimit.value() as f32), stroke.linejoin,
stroke.miterlimit.value() as f32,
),
}; };
let path = UsvgPathToSegments::new(path.data.iter().cloned()); let path = UsvgPathToSegments::new(path.data.iter().cloned());
@ -168,12 +177,14 @@ impl SVGScene {
let outline = stroke_to_fill.into_outline(); let outline = stroke_to_fill.into_outline();
let name = format!("Stroke({})", node.id()); let name = format!("Stroke({})", node.id());
self.push_draw_path(outline, self.push_draw_path(
name, outline,
&state, name,
&stroke.paint, &state,
stroke.opacity, &stroke.paint,
UsvgFillRule::NonZero); stroke.opacity,
UsvgFillRule::NonZero,
);
} }
} }
NodeKind::Path(..) => {} NodeKind::Path(..) => {}
@ -184,7 +195,8 @@ impl SVGScene {
self.process_node(&kid, &state, &mut clip_outline); self.process_node(&kid, &state, &mut clip_outline);
} }
self.clip_paths.insert(node.id().to_owned(), clip_outline.unwrap()); self.clip_paths
.insert(node.id().to_owned(), clip_outline.unwrap());
} }
NodeKind::Defs => { NodeKind::Defs => {
// FIXME(pcwalton): This is wrong. // FIXME(pcwalton): This is wrong.
@ -195,20 +207,24 @@ impl SVGScene {
} }
NodeKind::LinearGradient(ref svg_linear_gradient) => { NodeKind::LinearGradient(ref svg_linear_gradient) => {
let from = vec2f(svg_linear_gradient.x1 as f32, svg_linear_gradient.y1 as f32); let from = vec2f(svg_linear_gradient.x1 as f32, svg_linear_gradient.y1 as f32);
let to = vec2f(svg_linear_gradient.x2 as f32, svg_linear_gradient.y2 as f32); let to = vec2f(svg_linear_gradient.x2 as f32, svg_linear_gradient.y2 as f32);
let gradient = Gradient::linear_from_points(from, to); let gradient = Gradient::linear_from_points(from, to);
self.add_gradient(gradient, self.add_gradient(
svg_linear_gradient.id.clone(), gradient,
&svg_linear_gradient.base) svg_linear_gradient.id.clone(),
&svg_linear_gradient.base,
)
} }
NodeKind::RadialGradient(ref svg_radial_gradient) => { NodeKind::RadialGradient(ref svg_radial_gradient) => {
let from = vec2f(svg_radial_gradient.fx as f32, svg_radial_gradient.fy as f32); let from = vec2f(svg_radial_gradient.fx as f32, svg_radial_gradient.fy as f32);
let to = vec2f(svg_radial_gradient.cx as f32, svg_radial_gradient.cy as f32); let to = vec2f(svg_radial_gradient.cx as f32, svg_radial_gradient.cy as f32);
let radii = F32x2::new(0.0, svg_radial_gradient.r.value() as f32); let radii = F32x2::new(0.0, svg_radial_gradient.r.value() as f32);
let gradient = Gradient::radial(LineSegment2F::new(from, to), radii); let gradient = Gradient::radial(LineSegment2F::new(from, to), radii);
self.add_gradient(gradient, self.add_gradient(
svg_radial_gradient.id.clone(), gradient,
&svg_radial_gradient.base) svg_radial_gradient.id.clone(),
&svg_radial_gradient.base,
)
} }
NodeKind::Filter(..) => { NodeKind::Filter(..) => {
self.result_flags self.result_flags
@ -230,10 +246,12 @@ impl SVGScene {
} }
} }
fn add_gradient(&mut self, fn add_gradient(
mut gradient: Gradient, &mut self,
id: String, mut gradient: Gradient,
usvg_base_gradient: &BaseGradient) { id: String,
usvg_base_gradient: &BaseGradient,
) {
for stop in &usvg_base_gradient.stops { for stop in &usvg_base_gradient.stops {
let mut stop = ColorStop::from_usvg_stop(stop); let mut stop = ColorStop::from_usvg_stop(stop);
if usvg_base_gradient.spread_method == SpreadMethod::Reflect { if usvg_base_gradient.spread_method == SpreadMethod::Reflect {
@ -261,22 +279,32 @@ impl SVGScene {
let transform = usvg_transform_to_transform_2d(&usvg_base_gradient.transform); let transform = usvg_transform_to_transform_2d(&usvg_base_gradient.transform);
// TODO(pcwalton): What should we do with `gradientUnits`? // TODO(pcwalton): What should we do with `gradientUnits`?
self.gradients.insert(id, GradientInfo { gradient, transform }); self.gradients.insert(
id,
GradientInfo {
gradient,
transform,
},
);
} }
fn push_draw_path(&mut self, fn push_draw_path(
mut outline: Outline, &mut self,
name: String, mut outline: Outline,
state: &State, name: String,
paint: &UsvgPaint, state: &State,
opacity: Opacity, paint: &UsvgPaint,
fill_rule: UsvgFillRule) { opacity: Opacity,
fill_rule: UsvgFillRule,
) {
outline.transform(&state.transform); outline.transform(&state.transform);
let paint = Paint::from_svg_paint(paint, let paint = Paint::from_svg_paint(
&state.transform, paint,
opacity, &state.transform,
&self.gradients, opacity,
&mut self.result_flags); &self.gradients,
&mut self.result_flags,
);
let style = self.scene.push_paint(&paint); let style = self.scene.push_paint(&paint);
let fill_rule = FillRule::from_usvg_fill_rule(fill_rule); let fill_rule = FillRule::from_usvg_fill_rule(fill_rule);
let mut path = DrawPath::new(outline, style); let mut path = DrawPath::new(outline, style);
@ -323,22 +351,24 @@ impl Display for BuildResultFlags {
} }
trait PaintExt { trait PaintExt {
fn from_svg_paint(svg_paint: &UsvgPaint, fn from_svg_paint(
transform: &Transform2F, svg_paint: &UsvgPaint,
opacity: Opacity, transform: &Transform2F,
gradients: &HashMap<String, GradientInfo>, opacity: Opacity,
result_flags: &mut BuildResultFlags) gradients: &HashMap<String, GradientInfo>,
-> Self; result_flags: &mut BuildResultFlags,
) -> Self;
} }
impl PaintExt for Paint { impl PaintExt for Paint {
#[inline] #[inline]
fn from_svg_paint(svg_paint: &UsvgPaint, fn from_svg_paint(
transform: &Transform2F, svg_paint: &UsvgPaint,
opacity: Opacity, transform: &Transform2F,
gradients: &HashMap<String, GradientInfo>, opacity: Opacity,
result_flags: &mut BuildResultFlags) gradients: &HashMap<String, GradientInfo>,
-> Paint { result_flags: &mut BuildResultFlags,
) -> Paint {
let mut paint; let mut paint;
match *svg_paint { match *svg_paint {
UsvgPaint::Color(color) => paint = Paint::from_color(ColorU::from_svg_color(color)), UsvgPaint::Color(color) => paint = Paint::from_color(ColorU::from_svg_color(color)),
@ -366,13 +396,21 @@ impl PaintExt for Paint {
} }
fn usvg_rect_to_euclid_rect(rect: &UsvgRect) -> RectF { fn usvg_rect_to_euclid_rect(rect: &UsvgRect) -> RectF {
RectF::new(vec2f(rect.x() as f32, rect.y() as f32), RectF::new(
vec2f(rect.width() as f32, rect.height() as f32)) vec2f(rect.x() as f32, rect.y() as f32),
vec2f(rect.width() as f32, rect.height() as f32),
)
} }
fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F { fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F {
Transform2F::row_major(transform.a as f32, transform.c as f32, transform.e as f32, Transform2F::row_major(
transform.b as f32, transform.d as f32, transform.f as f32) transform.a as f32,
transform.c as f32,
transform.e as f32,
transform.b as f32,
transform.d as f32,
transform.f as f32,
)
} }
struct UsvgPathToSegments<I> struct UsvgPathToSegments<I>
@ -435,8 +473,10 @@ where
let ctrl0 = vec2f(x1 as f32, y1 as f32); let ctrl0 = vec2f(x1 as f32, y1 as f32);
let ctrl1 = vec2f(x2 as f32, y2 as f32); let ctrl1 = vec2f(x2 as f32, y2 as f32);
let to = vec2f(x as f32, y as f32); let to = vec2f(x as f32, y as f32);
let mut segment = Segment::cubic(LineSegment2F::new(self.last_subpath_point, to), let mut segment = Segment::cubic(
LineSegment2F::new(ctrl0, ctrl1)); LineSegment2F::new(self.last_subpath_point, to),
LineSegment2F::new(ctrl0, ctrl1),
);
if self.just_moved { if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH); segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
} }
@ -465,7 +505,12 @@ trait ColorUExt {
impl ColorUExt for ColorU { impl ColorUExt for ColorU {
#[inline] #[inline]
fn from_svg_color(svg_color: SvgColor) -> ColorU { fn from_svg_color(svg_color: SvgColor) -> ColorU {
ColorU { r: svg_color.red, g: svg_color.green, b: svg_color.blue, a: !0 } ColorU {
r: svg_color.red,
g: svg_color.green,
b: svg_color.blue,
a: !0,
}
} }
} }

View File

@ -8,16 +8,16 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::ops::Add;
use pathfinder_color::{ColorF, ColorU}; use pathfinder_color::{ColorF, ColorU};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{Outline, Contour}; use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::vector::vec2f; use pathfinder_geometry::vector::vec2f;
use pathfinder_renderer::scene::{DrawPath, Scene}; use pathfinder_renderer::scene::{DrawPath, Scene};
use std::ops::Add;
use swf_types::tags::SetBackgroundColor; use swf_types::tags::SetBackgroundColor;
use swf_types::{Tag, SRgb8, Movie}; use swf_types::{Movie, SRgb8, Tag};
use crate::shapes::{GraphicLayers, PaintOrLine}; use crate::shapes::{GraphicLayers, PaintOrLine};
@ -51,7 +51,7 @@ impl Add for Twips {
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
struct Point2<T> { struct Point2<T> {
x: T, x: T,
y: T y: T,
} }
impl Point2<Twips> { impl Point2<Twips> {
@ -66,7 +66,10 @@ impl Point2<Twips> {
impl Add for Point2<Twips> { impl Add for Point2<Twips> {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self { fn add(self, rhs: Self) -> Self {
Point2 { x: self.x + rhs.x, y: self.y + rhs.y } Point2 {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
} }
} }
@ -98,11 +101,11 @@ impl Stage {
g: self.background_color.g, g: self.background_color.g,
b: self.background_color.b, b: self.background_color.b,
a: 255, a: 255,
}.to_f32() }
.to_f32()
} }
} }
pub struct SymbolLibrary(Vec<Symbol>); pub struct SymbolLibrary(Vec<Symbol>);
impl SymbolLibrary { impl SymbolLibrary {
@ -126,7 +129,7 @@ pub fn process_swf_tags(movie: &Movie) -> (SymbolLibrary, Stage) {
background_color: SRgb8 { background_color: SRgb8 {
r: 255, r: 255,
g: 255, g: 255,
b: 255 b: 255,
}, },
width: stage_width.as_f32() as i32, width: stage_width.as_f32() as i32,
height: stage_height.as_f32() as i32, height: stage_height.as_f32() as i32,
@ -136,14 +139,14 @@ pub fn process_swf_tags(movie: &Movie) -> (SymbolLibrary, Stage) {
match tag { match tag {
Tag::SetBackgroundColor(SetBackgroundColor { color }) => { Tag::SetBackgroundColor(SetBackgroundColor { color }) => {
stage.background_color = *color; stage.background_color = *color;
}, }
Tag::DefineShape(shape) => { Tag::DefineShape(shape) => {
symbol_library.add_symbol(Symbol::Graphic(shapes::decode_shape(shape))); symbol_library.add_symbol(Symbol::Graphic(shapes::decode_shape(shape)));
// We will assume that symbol ids just go up, and are 1 based. // We will assume that symbol ids just go up, and are 1 based.
let symbol_id: SymbolId = shape.id; let symbol_id: SymbolId = shape.id;
debug_assert!(symbol_id as usize == symbol_library.0.len()); debug_assert!(symbol_id as usize == symbol_library.0.len());
} }
_ => () _ => (),
} }
} }
(symbol_library, stage) (symbol_library, stage)
@ -166,12 +169,15 @@ pub fn draw_paths_into_scene(library: &SymbolLibrary, scene: &mut Scene) {
let Point2 { x, y } = segment.to.as_f32(); let Point2 { x, y } = segment.to.as_f32();
match segment.ctrl { match segment.ctrl {
Some(ctrl) => { Some(ctrl) => {
let Point2 { x: ctrl_x, y: ctrl_y } = ctrl.as_f32(); let Point2 {
x: ctrl_x,
y: ctrl_y,
} = ctrl.as_f32();
contour.push_quadratic(vec2f(ctrl_x, ctrl_y), vec2f(x, y)); contour.push_quadratic(vec2f(ctrl_x, ctrl_y), vec2f(x, y));
} }
None => { None => {
contour.push_endpoint(vec2f(x, y)); contour.push_endpoint(vec2f(x, y));
}, }
} }
} }
if shape.is_closed() { if shape.is_closed() {
@ -183,11 +189,14 @@ pub fn draw_paths_into_scene(library: &SymbolLibrary, scene: &mut Scene) {
} }
if let PaintOrLine::Line(line) = style_layer.kind() { if let PaintOrLine::Line(line) = style_layer.kind() {
let mut stroke_to_fill = OutlineStrokeToFill::new(&path, StrokeStyle { let mut stroke_to_fill = OutlineStrokeToFill::new(
line_width: line.width.as_f32(), &path,
line_cap: line.cap, StrokeStyle {
line_join: line.join, line_width: line.width.as_f32(),
}); line_cap: line.cap,
line_join: line.join,
},
);
stroke_to_fill.offset(); stroke_to_fill.offset();
path = stroke_to_fill.into_outline(); path = stroke_to_fill.into_outline();
} }

View File

@ -8,16 +8,16 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::{Twips, Point2}; use crate::{Point2, Twips};
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_content::stroke::{LineJoin, LineCap}; use pathfinder_content::stroke::{LineCap, LineJoin};
use pathfinder_renderer::paint::Paint; use pathfinder_renderer::paint::Paint;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::mem; use std::mem;
use swf_types::tags::DefineShape; use swf_types::tags::DefineShape;
use swf_types::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
use swf_types::{fill_styles, join_styles, shape_records}; use swf_types::{fill_styles, join_styles, shape_records};
use swf_types::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub(crate) struct LineSegment { pub(crate) struct LineSegment {
@ -44,7 +44,7 @@ impl LineDirection {
fn reverse(&mut self) { fn reverse(&mut self) {
*self = match self { *self = match self {
LineDirection::Right => LineDirection::Left, LineDirection::Right => LineDirection::Left,
LineDirection::Left => LineDirection::Right LineDirection::Left => LineDirection::Right,
}; };
} }
} }
@ -194,19 +194,16 @@ impl StyleLayer {
if self.is_fill() { if self.is_fill() {
// I think sorting is only necessary when we want to have closed shapes, // I think sorting is only necessary when we want to have closed shapes,
// lines don't really need this? // lines don't really need this?
self.shapes.sort_unstable_by(|a, b| { self.shapes
match (a.is_closed(), b.is_closed()) { .sort_unstable_by(|a, b| match (a.is_closed(), b.is_closed()) {
(true, true) | (false, false) => Ordering::Equal, (true, true) | (false, false) => Ordering::Equal,
(true, false) => Ordering::Less, (true, false) => Ordering::Less,
(false, true) => Ordering::Greater, (false, true) => Ordering::Greater,
} });
});
} }
// A cursor at the index of the first unclosed shape, if any. // A cursor at the index of the first unclosed shape, if any.
let first_open_index = self.shapes let first_open_index = self.shapes.iter().position(|frag| !frag.is_closed());
.iter()
.position(|frag| !frag.is_closed());
if let Some(first_open_index) = first_open_index { if let Some(first_open_index) = first_open_index {
if self.shapes.len() - first_open_index >= 2 { if self.shapes.len() - first_open_index >= 2 {
@ -235,78 +232,75 @@ impl StyleLayer {
} }
} }
fn get_new_styles<'a>( fn get_new_styles<'a>(
fills: &'a Vec<FillStyle>, fills: &'a Vec<FillStyle>,
lines: &'a Vec<LineStyle> lines: &'a Vec<LineStyle>,
) -> impl Iterator<Item=PaintOrLine> + 'a { ) -> impl Iterator<Item = PaintOrLine> + 'a {
// This enforces the order that fills and line groupings are added in. // This enforces the order that fills and line groupings are added in.
// Fills always come first. // Fills always come first.
fills.iter().filter_map(|fill_style| { fills
match fill_style { .iter()
FillStyle::Solid( .filter_map(|fill_style| match fill_style {
fill_styles::Solid { FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 { color: StraightSRgba8 { r, g, b, a },
r, }) => Some(PaintOrLine::Paint(Paint::from_color(ColorU {
g, r: *r,
b, g: *g,
a b: *b,
} a: *a,
} }))),
) => { _ => unimplemented!("Unimplemented fill style"),
Some(PaintOrLine::Paint(Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a })))
}
_ => unimplemented!("Unimplemented fill style")
}
}).chain(
lines.iter().filter_map(|LineStyle {
width,
fill,
join,
start_cap,
end_cap: _,
/*
TODO(jon): Handle these cases?
pub no_h_scale: bool,
pub no_v_scale: bool,
pub no_close: bool,
pub pixel_hinting: bool,
*/
..
}| {
if let FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 {
r,
g,
b,
a
}
}) = fill {
// NOTE: PathFinder doesn't support different cap styles for start and end of
// strokes, so lets assume that they're always the same for the inputs we care about.
// Alternately, we split a line in two with a diff cap style for each.
// assert_eq!(start_cap, end_cap);
Some(PaintOrLine::Line(SwfLineStyle {
width: Twips(*width as i32),
color: Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a }),
join: match join {
JoinStyle::Bevel => LineJoin::Bevel,
JoinStyle::Round => LineJoin::Round,
JoinStyle::Miter(join_styles::Miter { limit }) => {
LineJoin::Miter(*limit as f32)
},
},
cap: match start_cap {
CapStyle::None => LineCap::Butt,
CapStyle::Square => LineCap::Square,
CapStyle::Round => LineCap::Round,
},
}))
} else {
unimplemented!("unimplemented line fill style");
}
}) })
) .chain(lines.iter().filter_map(
|LineStyle {
width,
fill,
join,
start_cap,
end_cap: _,
/*
TODO(jon): Handle these cases?
pub no_h_scale: bool,
pub no_v_scale: bool,
pub no_close: bool,
pub pixel_hinting: bool,
*/
..
}| {
if let FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 { r, g, b, a },
}) = fill
{
// NOTE: PathFinder doesn't support different cap styles for start and end of
// strokes, so lets assume that they're always the same for the inputs we care about.
// Alternately, we split a line in two with a diff cap style for each.
// assert_eq!(start_cap, end_cap);
Some(PaintOrLine::Line(SwfLineStyle {
width: Twips(*width as i32),
color: Paint::from_color(ColorU {
r: *r,
g: *g,
b: *b,
a: *a,
}),
join: match join {
JoinStyle::Bevel => LineJoin::Bevel,
JoinStyle::Round => LineJoin::Round,
JoinStyle::Miter(join_styles::Miter { limit }) => {
LineJoin::Miter(*limit as f32)
}
},
cap: match start_cap {
CapStyle::None => LineCap::Butt,
CapStyle::Square => LineCap::Square,
CapStyle::Round => LineCap::Round,
},
}))
} else {
unimplemented!("unimplemented line fill style");
}
},
))
} }
pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers { pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
@ -339,15 +333,13 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
for record in &shape.records { for record in &shape.records {
match record { match record {
ShapeRecord::StyleChange( ShapeRecord::StyleChange(shape_records::StyleChange {
shape_records::StyleChange { move_to,
move_to, new_styles,
new_styles, line_style,
line_style, left_fill,
left_fill, right_fill,
right_fill, }) => {
}
) => {
// Start a whole new style grouping. // Start a whole new style grouping.
if let Some(new_style) = new_styles { if let Some(new_style) = new_styles {
// Consolidate current style grouping and begin a new one. // Consolidate current style grouping and begin a new one.
@ -406,7 +398,10 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
// Move to, start new shape fragments with the current styles. // Move to, start new shape fragments with the current styles.
if let Some(Vector2D { x, y }) = move_to { if let Some(Vector2D { x, y }) = move_to {
let to: Point2<Twips> = Point2 { x: Twips(*x), y: Twips(*y) }; let to: Point2<Twips> = Point2 {
x: Twips(*x),
y: Twips(*y),
};
prev_pos = Some(to); prev_pos = Some(to);
// If we didn't start a new shape for the current fill due to a fill // If we didn't start a new shape for the current fill due to a fill
@ -433,34 +428,27 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
.push_new_shape(LineDirection::Right); .push_new_shape(LineDirection::Right);
} }
} }
}, }
ShapeRecord::Edge( ShapeRecord::Edge(shape_records::Edge {
shape_records::Edge { delta,
delta, control_delta,
control_delta, }) => {
}
) => {
let from = prev_pos.unwrap(); let from = prev_pos.unwrap();
let to = Point2 { let to = Point2 {
x: from.x + Twips(delta.x), x: from.x + Twips(delta.x),
y: from.y + Twips(delta.y) y: from.y + Twips(delta.y),
}; };
prev_pos = Some(to); prev_pos = Some(to);
let new_segment = LineSegment { let new_segment = LineSegment {
from, from,
to, to,
ctrl: control_delta.map(|Vector2D { x, y }| { ctrl: control_delta.map(|Vector2D { x, y }| Point2 {
Point2 { x: from.x + Twips(x),
x: from.x + Twips(x), y: from.y + Twips(y),
y: from.y + Twips(y),
}
}), }),
}; };
if some_fill_set && !both_fills_same { if some_fill_set && !both_fills_same {
for fill_id in [ for fill_id in [current_right_fill, current_left_fill].iter() {
current_right_fill,
current_left_fill
].iter() {
if let Some(fill_id) = fill_id { if let Some(fill_id) = fill_id {
graphic graphic
.with_fill_style_mut(*fill_id) .with_fill_style_mut(*fill_id)
@ -472,8 +460,10 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
} else if both_fills_set_and_same { } else if both_fills_set_and_same {
for (fill_id, direction) in [ for (fill_id, direction) in [
(current_right_fill, LineDirection::Right), (current_right_fill, LineDirection::Right),
(current_left_fill, LineDirection::Left) (current_left_fill, LineDirection::Left),
].iter() { ]
.iter()
{
// NOTE: If both left and right fill are set the same, // NOTE: If both left and right fill are set the same,
// then we don't record the edge as part of the current shape; // then we don't record the edge as part of the current shape;
// it's will just be an internal stroke inside an otherwise solid // it's will just be an internal stroke inside an otherwise solid
@ -508,7 +498,7 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
fn find_matches( fn find_matches(
mut first_open_index: usize, mut first_open_index: usize,
shapes: &mut Vec<Shape>, shapes: &mut Vec<Shape>,
reverse: bool reverse: bool,
) -> Option<Vec<Shape>> { ) -> Option<Vec<Shape>> {
let mut dropped_pieces = None; let mut dropped_pieces = None;
while first_open_index < shapes.len() { while first_open_index < shapes.len() {
@ -563,7 +553,11 @@ pub(crate) struct GraphicLayers {
impl GraphicLayers { impl GraphicLayers {
fn new() -> GraphicLayers { fn new() -> GraphicLayers {
GraphicLayers { style_layers: Vec::new(), stroke_layer_offset: None, base_layer_offset: 0 } GraphicLayers {
style_layers: Vec::new(),
stroke_layer_offset: None,
base_layer_offset: 0,
}
} }
fn begin_style_group(&mut self) { fn begin_style_group(&mut self) {
@ -572,22 +566,30 @@ impl GraphicLayers {
} }
fn begin_fill_style(&mut self, fill: Paint) { fn begin_fill_style(&mut self, fill: Paint) {
self.style_layers.push(StyleLayer { fill: PaintOrLine::Paint(fill), shapes: Vec::new() }) self.style_layers.push(StyleLayer {
fill: PaintOrLine::Paint(fill),
shapes: Vec::new(),
})
} }
fn begin_line_style(&mut self, line: SwfLineStyle) { fn begin_line_style(&mut self, line: SwfLineStyle) {
if self.stroke_layer_offset.is_none() { if self.stroke_layer_offset.is_none() {
self.stroke_layer_offset = Some(self.style_layers.len()); self.stroke_layer_offset = Some(self.style_layers.len());
} }
self.style_layers.push(StyleLayer { fill: PaintOrLine::Line(line), shapes: Vec::new() }) self.style_layers.push(StyleLayer {
fill: PaintOrLine::Line(line),
shapes: Vec::new(),
})
} }
fn with_fill_style_mut(&mut self, fill_id: usize) -> Option<&mut StyleLayer> { fn with_fill_style_mut(&mut self, fill_id: usize) -> Option<&mut StyleLayer> {
self.style_layers.get_mut(self.base_layer_offset + fill_id - 1) self.style_layers
.get_mut(self.base_layer_offset + fill_id - 1)
} }
fn with_line_style_mut(&mut self, line_id: usize) -> Option<&mut StyleLayer> { fn with_line_style_mut(&mut self, line_id: usize) -> Option<&mut StyleLayer> {
self.style_layers.get_mut((self.stroke_layer_offset.unwrap() + line_id) - 1) self.style_layers
.get_mut((self.stroke_layer_offset.unwrap() + line_id) - 1)
} }
pub(crate) fn layers(&self) -> &Vec<StyleLayer> { pub(crate) fn layers(&self) -> &Vec<StyleLayer> {
@ -606,4 +608,3 @@ impl GraphicLayers {
} }
} }
} }

View File

@ -9,7 +9,7 @@ repository = "https://github.com/servo/pathfinder"
homepage = "https://github.com/servo/pathfinder" homepage = "https://github.com/servo/pathfinder"
[dependencies] [dependencies]
font-kit = "0.6" font-kit = "0.13"
[dependencies.pathfinder_content] [dependencies.pathfinder_content]
path = "../content" path = "../content"

View File

@ -19,7 +19,7 @@ use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f}; use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::PaintId; use pathfinder_renderer::paint::PaintId;
use pathfinder_renderer::scene::{ClipPathId, DrawPath, Scene}; use pathfinder_renderer::scene::{ClipPathId, DrawPath, Scene};
use skribo::{FontCollection, Layout, TextStyle}; use skribo::{FontCollection, Layout, TextStyle};
@ -28,12 +28,18 @@ use std::mem;
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct FontContext<F> where F: Loader { pub struct FontContext<F>
font_info: HashMap<String, FontInfo<F>>, where
F: Loader,
{
font_info: HashMap<String, FontInfo<F>>,
} }
#[derive(Clone)] #[derive(Clone)]
struct FontInfo<F> where F: Loader { struct FontInfo<F>
where
F: Loader,
{
font: F, font: F,
metrics: Metrics, metrics: Metrics,
outline_cache: HashMap<GlyphId, Outline>, outline_cache: HashMap<GlyphId, Outline>,
@ -63,7 +69,10 @@ impl Default for FontRenderOptions {
} }
} }
enum FontInfoRefMut<'a, F> where F: Loader { enum FontInfoRefMut<'a, F>
where
F: Loader,
{
Ref(&'a mut FontInfo<F>), Ref(&'a mut FontInfo<F>),
Owned(FontInfo<F>), Owned(FontInfo<F>),
} }
@ -71,26 +80,33 @@ enum FontInfoRefMut<'a, F> where F: Loader {
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)] #[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
pub struct GlyphId(pub u32); pub struct GlyphId(pub u32);
impl<F> FontContext<F> where F: Loader { impl<F> FontContext<F>
where
F: Loader,
{
#[inline] #[inline]
pub fn new() -> FontContext<F> { pub fn new() -> FontContext<F> {
FontContext { font_info: HashMap::new() } FontContext {
font_info: HashMap::new(),
}
} }
fn push_glyph(&mut self, fn push_glyph(
scene: &mut Scene, &mut self,
font: &F, scene: &mut Scene,
font_key: Option<&str>, font: &F,
glyph_id: GlyphId, font_key: Option<&str>,
glyph_offset: Vector2F, glyph_id: GlyphId,
font_size: f32, glyph_offset: Vector2F,
render_options: &FontRenderOptions) font_size: f32,
-> Result<(), GlyphLoadingError> { render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
// Insert the font into the cache if needed. // Insert the font into the cache if needed.
let mut font_info = match font_key { let mut font_info = match font_key {
Some(font_key) => { Some(font_key) => {
if !self.font_info.contains_key(&*font_key) { if !self.font_info.contains_key(&*font_key) {
self.font_info.insert(font_key.to_owned(), FontInfo::new((*font).clone())); self.font_info
.insert(font_key.to_owned(), FontInfo::new((*font).clone()));
} }
FontInfoRefMut::Ref(self.font_info.get_mut(&*font_key).unwrap()) FontInfoRefMut::Ref(self.font_info.get_mut(&*font_key).unwrap())
} }
@ -115,8 +131,8 @@ impl<F> FontContext<F> where F: Loader {
let metrics = &font_info.metrics; let metrics = &font_info.metrics;
let font_scale = font_size / metrics.units_per_em as f32; let font_scale = font_size / metrics.units_per_em as f32;
let render_transform = render_options.transform * let render_transform = render_options.transform
Transform2F::from_scale(vec2f(font_scale, -font_scale)).translate(glyph_offset); * Transform2F::from_scale(vec2f(font_scale, -font_scale)).translate(glyph_offset);
let mut outline = match cached_outline { let mut outline = match cached_outline {
Some(mut cached_outline) => { Some(mut cached_outline) => {
@ -131,7 +147,11 @@ impl<F> FontContext<F> where F: Loader {
render_transform render_transform
}; };
let mut outline_builder = OutlinePathBuilder::new(&transform); let mut outline_builder = OutlinePathBuilder::new(&transform);
font.outline(glyph_id.0, render_options.hinting_options, &mut outline_builder)?; font.outline(
glyph_id.0,
render_options.hinting_options,
&mut outline_builder,
)?;
let mut outline = outline_builder.build(); let mut outline = outline_builder.build();
if can_cache_outline { if can_cache_outline {
font_info.outline_cache.insert(glyph_id, outline.clone()); font_info.outline_cache.insert(glyph_id, outline.clone());
@ -159,22 +179,25 @@ impl<F> FontContext<F> where F: Loader {
/// Attempts to look up a font in the font cache. /// Attempts to look up a font in the font cache.
#[inline] #[inline]
pub fn get_cached_font(&self, postscript_name: &str) -> Option<&F> { pub fn get_cached_font(&self, postscript_name: &str) -> Option<&F> {
self.font_info.get(postscript_name).map(|font_info| &font_info.font) self.font_info
.get(postscript_name)
.map(|font_info| &font_info.font)
} }
} }
impl FontContext<DefaultLoader> { impl FontContext<DefaultLoader> {
pub fn push_layout(&mut self, pub fn push_layout(
scene: &mut Scene, &mut self,
layout: &Layout, scene: &mut Scene,
style: &TextStyle, layout: &Layout,
render_options: &FontRenderOptions) style: &TextStyle,
-> Result<(), GlyphLoadingError> { render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
let mut cached_font_key: Option<CachedFontKey<DefaultLoader>> = None; let mut cached_font_key: Option<CachedFontKey<DefaultLoader>> = None;
for glyph in &layout.glyphs { for glyph in &layout.glyphs {
match cached_font_key { match cached_font_key {
Some(ref cached_font_key) if Arc::ptr_eq(&cached_font_key.font, Some(ref cached_font_key)
&glyph.font.font) => {} if Arc::ptr_eq(&cached_font_key.font, &glyph.font.font) => {}
_ => { _ => {
cached_font_key = Some(CachedFontKey { cached_font_key = Some(CachedFontKey {
font: glyph.font.font.clone(), font: glyph.font.font.clone(),
@ -183,43 +206,59 @@ impl FontContext<DefaultLoader> {
} }
} }
let cached_font_key = cached_font_key.as_ref().unwrap(); let cached_font_key = cached_font_key.as_ref().unwrap();
self.push_glyph(scene, self.push_glyph(
&*cached_font_key.font, scene,
cached_font_key.key.as_ref().map(|key| &**key), &*cached_font_key.font,
GlyphId(glyph.glyph_id), cached_font_key.key.as_ref().map(|key| &**key),
glyph.offset, GlyphId(glyph.glyph_id),
style.size, glyph.offset,
&render_options)?; style.size,
&render_options,
)?;
} }
Ok(()) Ok(())
} }
#[inline] #[inline]
pub fn push_text(&mut self, pub fn push_text(
scene: &mut Scene, &mut self,
text: &str, scene: &mut Scene,
style: &TextStyle, text: &str,
collection: &FontCollection, style: &TextStyle,
render_options: &FontRenderOptions) collection: &FontCollection,
-> Result<(), GlyphLoadingError> { render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
let layout = skribo::layout(style, collection, text); let layout = skribo::layout(style, collection, text);
self.push_layout(scene, &layout, style, render_options) self.push_layout(scene, &layout, style, render_options)
} }
} }
struct CachedFontKey<F> where F: Loader { struct CachedFontKey<F>
where
F: Loader,
{
font: Arc<F>, font: Arc<F>,
key: Option<String>, key: Option<String>,
} }
impl<F> FontInfo<F> where F: Loader { impl<F> FontInfo<F>
where
F: Loader,
{
fn new(font: F) -> FontInfo<F> { fn new(font: F) -> FontInfo<F> {
let metrics = font.metrics(); let metrics = font.metrics();
FontInfo { font, metrics, outline_cache: HashMap::new() } FontInfo {
font,
metrics,
outline_cache: HashMap::new(),
}
} }
} }
impl<'a, F> FontInfoRefMut<'a, F> where F: Loader { impl<'a, F> FontInfoRefMut<'a, F>
where
F: Loader,
{
fn get_mut(&mut self) -> &mut FontInfo<F> { fn get_mut(&mut self) -> &mut FontInfo<F> {
match *self { match *self {
FontInfoRefMut::Ref(ref mut reference) => &mut **reference, FontInfoRefMut::Ref(ref mut reference) => &mut **reference,
@ -251,7 +290,8 @@ impl OutlinePathBuilder {
fn flush_current_contour(&mut self) { fn flush_current_contour(&mut self) {
if !self.current_contour.is_empty() { if !self.current_contour.is_empty() {
self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new())); self.outline
.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
} }
} }
@ -272,13 +312,16 @@ impl OutlineSink for OutlinePathBuilder {
} }
fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) { fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
self.current_contour.push_quadratic(self.transform * ctrl, self.transform * to); self.current_contour
.push_quadratic(self.transform * ctrl, self.transform * to);
} }
fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) { fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
self.current_contour.push_cubic(self.transform * ctrl.from(), self.current_contour.push_cubic(
self.transform * ctrl.to(), self.transform * ctrl.from(),
self.transform * to); self.transform * ctrl.to(),
self.transform * to,
);
} }
fn close(&mut self) { fn close(&mut self) {

File diff suppressed because it is too large Load Diff

View File

@ -69,17 +69,20 @@ fn main() {
.version("0.1") .version("0.1")
.author("The Pathfinder Project Developers") .author("The Pathfinder Project Developers")
.about("Generates area lookup tables for use with Pathfinder") .about("Generates area lookup tables for use with Pathfinder")
.arg(Arg::with_name("OUTPUT-PATH").help("The `.png` image to produce") .arg(
.required(true) Arg::with_name("OUTPUT-PATH")
.index(1)); .help("The `.png` image to produce")
.required(true)
.index(1),
);
let matches = app.get_matches(); let matches = app.get_matches();
let image = ImageBuffer::from_fn(WIDTH, HEIGHT, |u, v| { let image = ImageBuffer::from_fn(WIDTH, HEIGHT, |u, v| {
if u == 0 { if u == 0 {
return Rgba([255, 255, 255, 255]) return Rgba([255, 255, 255, 255]);
} }
if u == WIDTH - 1 { if u == WIDTH - 1 {
return Rgba([0, 0, 0, 0]) return Rgba([0, 0, 0, 0]);
} }
let y = ((u as f32) - (WIDTH / 2) as f32) / 16.0; let y = ((u as f32) - (WIDTH / 2) as f32) / 16.0;

View File

@ -9,4 +9,4 @@ edition = "2018"
[dependencies] [dependencies]
pathfinder_export = { path = "../../export" } pathfinder_export = { path = "../../export" }
pathfinder_svg = { path = "../../svg" } pathfinder_svg = { path = "../../svg" }
usvg = "0.9" usvg = "0.10"

View File

@ -1,10 +1,10 @@
use std::fs::File;
use std::io::{Read, BufWriter};
use std::error::Error;
use std::path::PathBuf;
use pathfinder_svg::SVGScene;
use pathfinder_export::{Export, FileFormat}; use pathfinder_export::{Export, FileFormat};
use usvg::{Tree, Options}; use pathfinder_svg::SVGScene;
use std::error::Error;
use std::fs::File;
use std::io::{BufWriter, Read};
use std::path::PathBuf;
use usvg::{Options, Tree};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args_os().skip(1); let mut args = std::env::args_os().skip(1);
@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let format = match output.extension().and_then(|s| s.to_str()) { let format = match output.extension().and_then(|s| s.to_str()) {
Some("pdf") => FileFormat::PDF, Some("pdf") => FileFormat::PDF,
Some("ps") => FileFormat::PS, Some("ps") => FileFormat::PS,
_ => return Err("output filename must have .ps or .pdf extension".into()) _ => return Err("output filename must have .ps or .pdf extension".into()),
}; };
scene.export(&mut writer, format).unwrap(); scene.export(&mut writer, format).unwrap();
Ok(()) Ok(())

View File

@ -62,7 +62,7 @@ impl LuminanceColorSpace {
if luma <= 0.0031308 { if luma <= 0.0031308 {
luma * 12.92 luma * 12.92
} else { } else {
1.055 * luma.powf(1./2.4) - 0.055 1.055 * luma.powf(1. / 2.4) - 0.055
} }
} }
} }
@ -70,7 +70,7 @@ impl LuminanceColorSpace {
} }
//TODO: tests //TODO: tests
fn round_to_u8(x : f32) -> u8 { fn round_to_u8(x: f32) -> u8 {
let v = (x + 0.5).floor() as i32; let v = (x + 0.5).floor() as i32;
assert!(0 <= v && v < 0x100); assert!(0 <= v && v < 0x100);
v as u8 v as u8
@ -134,12 +134,7 @@ impl ColorLut for ColorU {
// Quantize to the smallest value that yields the same table index. // Quantize to the smallest value that yields the same table index.
fn quantized_floor(&self) -> ColorU { fn quantized_floor(&self) -> ColorU {
ColorU::new( ColorU::new(self.r & LUM_MASK, self.g & LUM_MASK, self.b & LUM_MASK, 255)
self.r & LUM_MASK,
self.g & LUM_MASK,
self.b & LUM_MASK,
255,
)
} }
// Quantize to the largest value that yields the same table index. // Quantize to the largest value that yields the same table index.
@ -176,10 +171,13 @@ fn apply_contrast(srca: f32, contrast: f32) -> f32 {
// The approach here is not necessarily the one with the lowest error // The approach here is not necessarily the one with the lowest error
// See https://bel.fi/alankila/lcd/alpcor.html for a similar kind of thing // See https://bel.fi/alankila/lcd/alpcor.html for a similar kind of thing
// that just search for the adjusted alpha value // that just search for the adjusted alpha value
pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32, pub fn build_gamma_correcting_lut(
src_space: LuminanceColorSpace, table: &mut [u8; 256],
dst_convert: LuminanceColorSpace) { src: u8,
contrast: f32,
src_space: LuminanceColorSpace,
dst_convert: LuminanceColorSpace,
) {
let src = src as f32 / 255.0; let src = src as f32 / 255.0;
let lin_src = src_space.to_luma(src); let lin_src = src_space.to_luma(src);
// Guess at the dst. The perceptual inverse provides smaller visual // Guess at the dst. The perceptual inverse provides smaller visual
@ -195,7 +193,7 @@ pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32,
// Remove discontinuity and instability when src is close to dst. // Remove discontinuity and instability when src is close to dst.
// The value 1/256 is arbitrary and appears to contain the instability. // The value 1/256 is arbitrary and appears to contain the instability.
if (src - dst).abs() < (1.0 / 256.0) { if (src - dst).abs() < (1.0 / 256.0) {
let mut ii : f32 = 0.0; let mut ii: f32 = 0.0;
for v in table.iter_mut() { for v in table.iter_mut() {
let raw_srca = ii / 255.0; let raw_srca = ii / 255.0;
let srca = apply_contrast(raw_srca, adjusted_contrast); let srca = apply_contrast(raw_srca, adjusted_contrast);
@ -205,7 +203,7 @@ pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32,
} }
} else { } else {
// Avoid slow int to float conversion. // Avoid slow int to float conversion.
let mut ii : f32 = 0.0; let mut ii: f32 = 0.0;
for v in table.iter_mut() { for v in table.iter_mut() {
// 'raw_srca += 1.0f / 255.0f' and even // 'raw_srca += 1.0f / 255.0f' and even
// 'raw_srca = i * (1.0f / 255.0f)' can add up to more than 1.0f. // 'raw_srca = i * (1.0f / 255.0f)' can add up to more than 1.0f.
@ -247,11 +245,13 @@ impl GammaLut {
for (i, entry) in self.tables.iter_mut().enumerate() { for (i, entry) in self.tables.iter_mut().enumerate() {
let luminance = scale255(LUM_BITS, i as u8); let luminance = scale255(LUM_BITS, i as u8);
build_gamma_correcting_lut(entry, build_gamma_correcting_lut(
luminance, entry,
contrast, luminance,
paint_color_space, contrast,
device_color_space); paint_color_space,
device_color_space,
);
} }
} }
@ -280,7 +280,11 @@ impl GammaLut {
let table_b = self.get_table(color.b); let table_b = self.get_table(color.b);
for pixel in pixels.chunks_mut(4) { for pixel in pixels.chunks_mut(4) {
let (b, g, r) = (table_b[pixel[0] as usize], table_g[pixel[1] as usize], table_r[pixel[2] as usize]); let (b, g, r) = (
table_b[pixel[0] as usize],
table_g[pixel[1] as usize],
table_r[pixel[2] as usize],
);
pixel[0] = b; pixel[0] = b;
pixel[1] = g; pixel[1] = g;
pixel[2] = r; pixel[2] = r;
@ -301,7 +305,6 @@ impl GammaLut {
pixel[3] = alpha; pixel[3] = alpha;
} }
} }
} // end impl GammaLut } // end impl GammaLut
#[cfg(test)] #[cfg(test)]
@ -309,16 +312,19 @@ mod tests {
use super::*; use super::*;
fn over(dst: u32, src: u32, alpha: u32) -> u32 { fn over(dst: u32, src: u32, alpha: u32) -> u32 {
(src * alpha + dst * (255 - alpha))/255 (src * alpha + dst * (255 - alpha)) / 255
} }
fn overf(dst: f32, src: f32, alpha: f32) -> f32 { fn overf(dst: f32, src: f32, alpha: f32) -> f32 {
((src * alpha + dst * (255. - alpha))/255.) as f32 ((src * alpha + dst * (255. - alpha)) / 255.) as f32
} }
fn absdiff(a: u32, b: u32) -> u32 { fn absdiff(a: u32, b: u32) -> u32 {
if a < b { b - a } else { a - b } if a < b {
b - a
} else {
a - b
}
} }
#[test] #[test]
@ -326,7 +332,7 @@ mod tests {
let mut table = [0u8; 256]; let mut table = [0u8; 256];
let g = 2.0; let g = 2.0;
let space = LuminanceColorSpace::Gamma(g); let space = LuminanceColorSpace::Gamma(g);
let mut src : u32 = 131; let mut src: u32 = 131;
while src < 256 { while src < 256 {
build_gamma_correcting_lut(&mut table, src as u8, 0., space, space); build_gamma_correcting_lut(&mut table, src as u8, 0., space, space);
let mut max_diff = 0; let mut max_diff = 0;
@ -338,7 +344,8 @@ mod tests {
let lin_src = (src as f32 / 255.).powf(g) * 255.; let lin_src = (src as f32 / 255.).powf(g) * 255.;
let preblend_result = over(dst, src, preblend as u32); let preblend_result = over(dst, src, preblend as u32);
let true_result = ((overf(lin_dst, lin_src, alpha as f32) / 255.).powf(1. / g) * 255.) as u32; let true_result =
((overf(lin_dst, lin_src, alpha as f32) / 255.).powf(1. / g) * 255.) as u32;
let diff = absdiff(preblend_result, true_result); let diff = absdiff(preblend_result, true_result);
//println!("{} -- {} {} = {}", alpha, preblend_result, true_result, diff); //println!("{} -- {} {} = {}", alpha, preblend_result, true_result, diff);
max_diff = max(max_diff, diff); max_diff = max(max_diff, diff);
@ -347,7 +354,6 @@ mod tests {
//println!("{} {} max {}", src, dst, max_diff); //println!("{} {} max {}", src, dst, max_diff);
assert!(max_diff <= 33); assert!(max_diff <= 33);
dst += 1; dst += 1;
} }
src += 1; src += 1;
} }

View File

@ -42,9 +42,12 @@ pub fn main() {
.version("0.1") .version("0.1")
.author("The Pathfinder Project Developers") .author("The Pathfinder Project Developers")
.about("Generates gamma lookup tables for use with Pathfinder") .about("Generates gamma lookup tables for use with Pathfinder")
.arg(Arg::with_name("OUTPUT-PATH").help("The `.png` image to produce") .arg(
.required(true) Arg::with_name("OUTPUT-PATH")
.index(1)); .help("The `.png` image to produce")
.required(true)
.index(1),
);
let matches = app.get_matches(); let matches = app.get_matches();
let gamma_lut = GammaLut::new(CONTRAST, GAMMA, GAMMA); let gamma_lut = GammaLut::new(CONTRAST, GAMMA, GAMMA);

View File

@ -5,4 +5,4 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
usvg = "0.9" usvg = "0.10"

View File

@ -49,9 +49,18 @@ fn process_node(node: &Node) {
match segment { match segment {
PathSegment::MoveTo { x, y } => println!(" path.moveTo({}, {});", x, y), PathSegment::MoveTo { x, y } => println!(" path.moveTo({}, {});", x, y),
PathSegment::LineTo { x, y } => println!(" path.lineTo({}, {});", x, y), PathSegment::LineTo { x, y } => println!(" path.lineTo({}, {});", x, y),
PathSegment::CurveTo { x1, y1, x2, y2, x, y } => { PathSegment::CurveTo {
println!(" path.cubicTo({}, {}, {}, {}, {}, {});", x1,
x1, y1, x2, y2, x, y); y1,
x2,
y2,
x,
y,
} => {
println!(
" path.cubicTo({}, {}, {}, {}, {}, {});",
x1, y1, x2, y2, x, y
);
} }
PathSegment::ClosePath => println!(" path.close();"), PathSegment::ClosePath => println!(" path.close();"),
} }
@ -78,10 +87,12 @@ fn process_node(node: &Node) {
fn set_color(paint: &Paint) { fn set_color(paint: &Paint) {
if let Paint::Color(color) = *paint { if let Paint::Color(color) = *paint {
println!(" paint.setColor(0x{:x});", println!(
((color.red as u32) << 16) | " paint.setColor(0x{:x});",
((color.green as u32) << 8) | ((color.red as u32) << 16)
((color.blue as u32) << 0) | | ((color.green as u32) << 8)
(0xff << 24)); | ((color.blue as u32) << 0)
| (0xff << 24)
);
} }
} }

View File

@ -23,8 +23,8 @@ use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_webgl::WebGlDevice; use pathfinder_webgl::WebGlDevice;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext}; use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
#[wasm_bindgen] #[wasm_bindgen]
@ -45,11 +45,12 @@ struct WebCanvasState {
#[wasm_bindgen(js_name = "createContext")] #[wasm_bindgen(js_name = "createContext")]
pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContext2D { pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContext2D {
let context = html_canvas.get_context("webgl2") let context = html_canvas
.unwrap() .get_context("webgl2")
.unwrap() .unwrap()
.dyn_into::<WebGl2RenderingContext>() .unwrap()
.unwrap(); .dyn_into::<WebGl2RenderingContext>()
.unwrap();
// Get the real size of the window, taking HiDPI into account. // Get the real size of the window, taking HiDPI into account.
let framebuffer_size = vec2i(html_canvas.width() as i32, html_canvas.height() as i32); let framebuffer_size = vec2i(html_canvas.width() as i32, html_canvas.height() as i32);
@ -89,15 +90,21 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "pfFlush")] #[wasm_bindgen(js_name = "pfFlush")]
pub fn pf_flush(&mut self) { pub fn pf_flush(&mut self) {
// Update framebuffer size. // Update framebuffer size.
let framebuffer_size = vec2i(self.html_canvas.width() as i32, let framebuffer_size = vec2i(
self.html_canvas.height() as i32); self.html_canvas.width() as i32,
self.html_canvas.height() as i32,
);
self.renderer.options_mut().dest = DestFramebuffer::full_window(framebuffer_size); self.renderer.options_mut().dest = DestFramebuffer::full_window(framebuffer_size);
self.renderer.options_mut().background_color = None; self.renderer.options_mut().background_color = None;
self.renderer.dest_framebuffer_size_changed(); self.renderer.dest_framebuffer_size_changed();
// TODO(pcwalton): This is inefficient! // TODO(pcwalton): This is inefficient!
let mut scene = (*self.context.canvas_mut().scene()).clone(); let mut scene = (*self.context.canvas_mut().scene()).clone();
scene.build_and_render(&mut self.renderer, BuildOptions::default(), SequentialExecutor); scene.build_and_render(
&mut self.renderer,
BuildOptions::default(),
SequentialExecutor,
);
self.context.canvas_mut().set_size(framebuffer_size); self.context.canvas_mut().set_size(framebuffer_size);
} }
@ -116,17 +123,20 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "clearRect")] #[wasm_bindgen(js_name = "clearRect")]
pub fn clear_rect(&mut self, x: f32, y: f32, width: f32, height: f32) { pub fn clear_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.clear_rect(RectF::new(vec2f(x, y), vec2f(width, height))); self.context
.clear_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
} }
#[wasm_bindgen(js_name = "fillRect")] #[wasm_bindgen(js_name = "fillRect")]
pub fn fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32) { pub fn fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height))); self.context
.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
} }
#[wasm_bindgen(js_name = "strokeRect")] #[wasm_bindgen(js_name = "strokeRect")]
pub fn stroke_rect(&mut self, x: f32, y: f32, width: f32, height: f32) { pub fn stroke_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.stroke_rect(RectF::new(vec2f(x, y), vec2f(width, height))); self.context
.stroke_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
} }
// TODO(pcwalton): Drawing text // TODO(pcwalton): Drawing text
@ -219,12 +229,14 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "bezierCurveTo")] #[wasm_bindgen(js_name = "bezierCurveTo")]
pub fn bezier_curve_to(&mut self, cp1x: f32, cp1y: f32, cp2x: f32, cp2y: f32, x: f32, y: f32) { pub fn bezier_curve_to(&mut self, cp1x: f32, cp1y: f32, cp2x: f32, cp2y: f32, x: f32, y: f32) {
self.default_path.bezier_curve_to(vec2f(cp1x, cp1y), vec2f(cp2x, cp2y), vec2f(x, y)) self.default_path
.bezier_curve_to(vec2f(cp1x, cp1y), vec2f(cp2x, cp2y), vec2f(x, y))
} }
#[wasm_bindgen(js_name = "quadraticCurveTo")] #[wasm_bindgen(js_name = "quadraticCurveTo")]
pub fn quadratic_curve_to(&mut self, cpx: f32, cpy: f32, x: f32, y: f32) { pub fn quadratic_curve_to(&mut self, cpx: f32, cpy: f32, x: f32, y: f32) {
self.default_path.quadratic_curve_to(vec2f(cpx, cpy), vec2f(x, y)) self.default_path
.quadratic_curve_to(vec2f(cpx, cpy), vec2f(x, y))
} }
#[wasm_bindgen(js_name = "closePath")] #[wasm_bindgen(js_name = "closePath")]
@ -260,9 +272,11 @@ fn parse_fill_or_stroke_style(string: &str) -> Option<FillStyle> {
Err(_) => return None, Err(_) => return None,
Ok(css_color) => css_color, Ok(css_color) => css_color,
}; };
let color = ColorU::new(css_color.r, let color = ColorU::new(
css_color.g, css_color.r,
css_color.b, css_color.g,
(css_color.a * 255.0).round() as u8); css_color.b,
(css_color.a * 255.0).round() as u8,
);
Some(FillStyle::Color(color)) Some(FillStyle::Color(color))
} }

View File

@ -13,7 +13,7 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use js_sys::{Uint8Array, Uint16Array, Float32Array, Object}; use js_sys::{Float32Array, Object, Uint16Array, Uint8Array};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps}; use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
@ -186,12 +186,16 @@ impl WebGlDevice {
self.clear(&render_state.options.clear_ops); self.clear(&render_state.options.clear_ops);
} }
self.context.use_program(Some(&render_state.program.gl_program)); self.context
self.context.bind_vertex_array(Some(&render_state.vertex_array.gl_vertex_array)); .use_program(Some(&render_state.program.gl_program));
self.context
.bind_vertex_array(Some(&render_state.vertex_array.gl_vertex_array));
self.bind_textures_and_images(&render_state.program, self.bind_textures_and_images(
&render_state.textures, &render_state.program,
&render_state.images); &render_state.textures,
&render_state.images,
);
for (uniform, data) in render_state.uniforms { for (uniform, data) in render_state.uniforms {
self.set_uniform(uniform, data); self.set_uniform(uniform, data);
@ -200,17 +204,19 @@ impl WebGlDevice {
} }
fn bind_textures_and_images( fn bind_textures_and_images(
&self, &self,
program: &WebGlProgram, program: &WebGlProgram,
texture_bindings: &[TextureBinding<WebGlTextureParameter, WebGlTexture>], texture_bindings: &[TextureBinding<WebGlTextureParameter, WebGlTexture>],
_: &[ImageBinding<(), WebGlTexture>]) { _: &[ImageBinding<(), WebGlTexture>],
) {
for &(texture_parameter, texture) in texture_bindings { for &(texture_parameter, texture) in texture_bindings {
self.bind_texture(texture, texture_parameter.texture_unit); self.bind_texture(texture, texture_parameter.texture_unit);
} }
let parameters = program.parameters.borrow(); let parameters = program.parameters.borrow();
for (texture_unit, uniform) in parameters.textures.iter().enumerate() { for (texture_unit, uniform) in parameters.textures.iter().enumerate() {
self.context.uniform1i(uniform.location.as_ref(), texture_unit as i32); self.context
.uniform1i(uniform.location.as_ref(), texture_unit as i32);
self.ck(); self.ck();
} }
} }
@ -486,9 +492,7 @@ impl Device for WebGlDevice {
size: Vector2I, size: Vector2I,
data_ref: TextureDataRef, data_ref: TextureDataRef,
) -> WebGlTexture { ) -> WebGlTexture {
let data = unsafe { let data = unsafe { check_and_extract_data(data_ref, size, format) };
check_and_extract_data(data_ref, size, format)
};
let texture = self.context.create_texture().unwrap(); let texture = self.context.create_texture().unwrap();
let texture = WebGlTexture { let texture = WebGlTexture {
@ -568,7 +572,10 @@ impl Device for WebGlDevice {
.create_program() .create_program()
.expect("unable to create program object"); .expect("unable to create program object");
match shaders { match shaders {
ProgramKind::Raster { ref vertex, ref fragment } => { ProgramKind::Raster {
ref vertex,
ref fragment,
} => {
self.context.attach_shader(&gl_program, &vertex.gl_shader); self.context.attach_shader(&gl_program, &vertex.gl_shader);
self.context.attach_shader(&gl_program, &fragment.gl_shader); self.context.attach_shader(&gl_program, &fragment.gl_shader);
} }
@ -622,7 +629,9 @@ impl Device for WebGlDevice {
fn get_uniform(&self, program: &WebGlProgram, name: &str) -> WebGlUniform { fn get_uniform(&self, program: &WebGlProgram, name: &str) -> WebGlUniform {
let name = format!("u{}", name); let name = format!("u{}", name);
let location = self.context.get_uniform_location(&program.gl_program, &name); let location = self
.context
.get_uniform_location(&program.gl_program, &name);
self.ck(); self.ck();
WebGlUniform { location: location } WebGlUniform { location: location }
} }
@ -638,7 +647,10 @@ impl Device for WebGlDevice {
index index
} }
}; };
WebGlTextureParameter { uniform, texture_unit: index as u32 } WebGlTextureParameter {
uniform,
texture_unit: index as u32,
}
} }
fn get_image_parameter(&self, _: &WebGlProgram, _: &str) { fn get_image_parameter(&self, _: &WebGlProgram, _: &str) {
@ -744,12 +756,7 @@ impl Device for WebGlDevice {
} }
} }
fn allocate_buffer<T>( fn allocate_buffer<T>(&self, buffer: &WebGlBuffer, data: BufferData<T>, target: BufferTarget) {
&self,
buffer: &WebGlBuffer,
data: BufferData<T>,
target: BufferTarget,
) {
let target = match target { let target = match target {
BufferTarget::Vertex => WebGl::ARRAY_BUFFER, BufferTarget::Vertex => WebGl::ARRAY_BUFFER,
BufferTarget::Index => WebGl::ELEMENT_ARRAY_BUFFER, BufferTarget::Index => WebGl::ELEMENT_ARRAY_BUFFER,
@ -770,16 +777,22 @@ impl Device for WebGlDevice {
} }
} }
fn upload_to_buffer<T>(&self, fn upload_to_buffer<T>(
buffer: &Self::Buffer, &self,
position: usize, buffer: &Self::Buffer,
data: &[T], position: usize,
target: BufferTarget) { data: &[T],
target: BufferTarget,
) {
let target = target.to_gl_target(); let target = target.to_gl_target();
self.context.bind_buffer(target, Some(&buffer.buffer)); self.ck(); self.context.bind_buffer(target, Some(&buffer.buffer));
self.context.buffer_sub_data_with_i32_and_u8_array(target, self.ck();
position as i32, self.context.buffer_sub_data_with_i32_and_u8_array(
slice_to_u8(data)); self.ck(); target,
position as i32,
slice_to_u8(data),
);
self.ck();
} }
#[inline] #[inline]
@ -794,44 +807,46 @@ impl Device for WebGlDevice {
fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags) { fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags) {
self.bind_texture(texture, 0); self.bind_texture(texture, 0);
self.context self.context.tex_parameteri(
.tex_parameteri(WebGl::TEXTURE_2D, WebGl::TEXTURE_2D,
WebGl::TEXTURE_MIN_FILTER, WebGl::TEXTURE_MIN_FILTER,
if flags.contains(TextureSamplingFlags::NEAREST_MIN) { if flags.contains(TextureSamplingFlags::NEAREST_MIN) {
WebGl::NEAREST as i32 WebGl::NEAREST as i32
} else { } else {
WebGl::LINEAR as i32 WebGl::LINEAR as i32
}); },
self.context );
.tex_parameteri(WebGl::TEXTURE_2D, self.context.tex_parameteri(
WebGl::TEXTURE_MAG_FILTER, WebGl::TEXTURE_2D,
if flags.contains(TextureSamplingFlags::NEAREST_MAG) { WebGl::TEXTURE_MAG_FILTER,
WebGl::NEAREST as i32 if flags.contains(TextureSamplingFlags::NEAREST_MAG) {
} else { WebGl::NEAREST as i32
WebGl::LINEAR as i32 } else {
}); WebGl::LINEAR as i32
self.context },
.tex_parameteri(WebGl::TEXTURE_2D, );
WebGl::TEXTURE_WRAP_S, self.context.tex_parameteri(
if flags.contains(TextureSamplingFlags::REPEAT_U) { WebGl::TEXTURE_2D,
WebGl::REPEAT as i32 WebGl::TEXTURE_WRAP_S,
} else { if flags.contains(TextureSamplingFlags::REPEAT_U) {
WebGl::CLAMP_TO_EDGE as i32 WebGl::REPEAT as i32
}); } else {
self.context WebGl::CLAMP_TO_EDGE as i32
.tex_parameteri(WebGl::TEXTURE_2D, },
WebGl::TEXTURE_WRAP_T, );
if flags.contains(TextureSamplingFlags::REPEAT_V) { self.context.tex_parameteri(
WebGl::REPEAT as i32 WebGl::TEXTURE_2D,
} else { WebGl::TEXTURE_WRAP_T,
WebGl::CLAMP_TO_EDGE as i32 if flags.contains(TextureSamplingFlags::REPEAT_V) {
}); WebGl::REPEAT as i32
} else {
WebGl::CLAMP_TO_EDGE as i32
},
);
} }
fn upload_to_texture(&self, texture: &WebGlTexture, rect: RectI, data_ref: TextureDataRef) { fn upload_to_texture(&self, texture: &WebGlTexture, rect: RectI, data_ref: TextureDataRef) {
let data = unsafe { let data = unsafe { check_and_extract_data(data_ref, rect.size(), texture.format) };
check_and_extract_data(data_ref, rect.size(), texture.format)
};
assert!(rect.size().x() >= 0); assert!(rect.size().x() >= 0);
assert!(rect.size().y() >= 0); assert!(rect.size().y() >= 0);
assert!(rect.max_x() <= texture.size.x()); assert!(rect.max_x() <= texture.size.x());