Implement pattern repeating and image smoothing control
This commit is contained in:
parent
77b3555828
commit
d1c7da8bd2
|
@ -1707,6 +1707,7 @@ dependencies = [
|
||||||
name = "pathfinder_gpu"
|
name = "pathfinder_gpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"half 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"half 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_color 0.1.0",
|
"pathfinder_color 0.1.0",
|
||||||
|
|
|
@ -16,7 +16,7 @@ use pathfinder_content::effects::{BlendMode, CompositeOp, Effects, Filter};
|
||||||
use pathfinder_content::fill::FillRule;
|
use pathfinder_content::fill::FillRule;
|
||||||
use pathfinder_content::gradient::Gradient;
|
use pathfinder_content::gradient::Gradient;
|
||||||
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
||||||
use pathfinder_content::pattern::{Pattern, RenderTargetId};
|
use pathfinder_content::pattern::{Pattern, PatternFlags, RenderTargetId};
|
||||||
use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin};
|
use pathfinder_content::stroke::{LineCap, 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;
|
||||||
|
@ -326,6 +326,28 @@ impl CanvasRenderingContext2D {
|
||||||
self.current_state.global_composite_operation = new_composite_operation;
|
self.current_state.global_composite_operation = new_composite_operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image smoothing
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn image_smoothing_enabled(&self) -> bool {
|
||||||
|
self.current_state.image_smoothing_enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_image_smoothing_enabled(&mut self, enabled: bool) {
|
||||||
|
self.current_state.image_smoothing_enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn image_smoothing_quality(&self) -> ImageSmoothingQuality {
|
||||||
|
self.current_state.image_smoothing_quality
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_image_smoothing_quality(&mut self, new_quality: ImageSmoothingQuality) {
|
||||||
|
self.current_state.image_smoothing_quality = new_quality
|
||||||
|
}
|
||||||
|
|
||||||
// The canvas state
|
// The canvas state
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -357,6 +379,8 @@ struct State {
|
||||||
shadow_paint: Paint,
|
shadow_paint: Paint,
|
||||||
shadow_offset: Vector2F,
|
shadow_offset: Vector2F,
|
||||||
text_align: TextAlign,
|
text_align: TextAlign,
|
||||||
|
image_smoothing_enabled: bool,
|
||||||
|
image_smoothing_quality: ImageSmoothingQuality,
|
||||||
global_alpha: f32,
|
global_alpha: f32,
|
||||||
global_composite_operation: CompositeOperation,
|
global_composite_operation: CompositeOperation,
|
||||||
clip_path: Option<ClipPathId>,
|
clip_path: Option<ClipPathId>,
|
||||||
|
@ -379,6 +403,8 @@ impl State {
|
||||||
shadow_paint: Paint::transparent_black(),
|
shadow_paint: Paint::transparent_black(),
|
||||||
shadow_offset: Vector2F::default(),
|
shadow_offset: Vector2F::default(),
|
||||||
text_align: TextAlign::Left,
|
text_align: TextAlign::Left,
|
||||||
|
image_smoothing_enabled: true,
|
||||||
|
image_smoothing_quality: ImageSmoothingQuality::Low,
|
||||||
global_alpha: 1.0,
|
global_alpha: 1.0,
|
||||||
global_composite_operation: CompositeOperation::SourceOver,
|
global_composite_operation: CompositeOperation::SourceOver,
|
||||||
clip_path: None,
|
clip_path: None,
|
||||||
|
@ -389,9 +415,18 @@ impl State {
|
||||||
if self.transform.is_identity() {
|
if self.transform.is_identity() {
|
||||||
return Cow::Borrowed(paint);
|
return Cow::Borrowed(paint);
|
||||||
}
|
}
|
||||||
|
if let Paint::Pattern(ref pattern) = *paint {
|
||||||
|
if !self.image_smoothing_enabled ==
|
||||||
|
pattern.flags.contains(PatternFlags::NO_SMOOTHING) {
|
||||||
|
return Cow::Borrowed(paint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut paint = (*paint).clone();
|
let mut paint = (*paint).clone();
|
||||||
paint.apply_transform(&self.transform);
|
paint.apply_transform(&self.transform);
|
||||||
|
if let Paint::Pattern(ref mut pattern) = paint {
|
||||||
|
pattern.flags.set(PatternFlags::NO_SMOOTHING, !self.image_smoothing_enabled);
|
||||||
|
}
|
||||||
Cow::Owned(paint)
|
Cow::Owned(paint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,3 +679,10 @@ impl CompositeOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ImageSmoothingQuality {
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use image::RgbaImage;
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct Pattern {
|
pub struct Pattern {
|
||||||
pub source: PatternSource,
|
pub source: PatternSource,
|
||||||
pub repeat: Repeat,
|
pub flags: PatternFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
@ -44,16 +44,17 @@ pub struct Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct Repeat: u8 {
|
pub struct PatternFlags: u8 {
|
||||||
const X = 0x01;
|
const REPEAT_X = 0x01;
|
||||||
const Y = 0x02;
|
const REPEAT_Y = 0x02;
|
||||||
|
const NO_SMOOTHING = 0x04;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pattern {
|
impl Pattern {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(source: PatternSource, repeat: Repeat) -> Pattern {
|
pub fn new(source: PatternSource, flags: PatternFlags) -> Pattern {
|
||||||
Pattern { source, repeat }
|
Pattern { source, flags }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@ use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
|
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
|
||||||
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderOptions, RenderState, RenderTarget};
|
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderOptions, RenderState, RenderTarget};
|
||||||
use pathfinder_gpu::{ShaderKind, StencilFunc, TextureData, TextureDataRef, TextureFormat};
|
use pathfinder_gpu::{ShaderKind, StencilFunc, TextureData, TextureDataRef, TextureFormat};
|
||||||
use pathfinder_gpu::{UniformData, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
use pathfinder_gpu::{TextureSamplingFlags, UniformData, VertexAttrClass};
|
||||||
|
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -48,20 +49,6 @@ impl GLDevice {
|
||||||
self.default_framebuffer = framebuffer;
|
self.default_framebuffer = framebuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_texture_parameters(&self, texture: &GLTexture) {
|
|
||||||
self.bind_texture(texture, 0);
|
|
||||||
unsafe {
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); ck();
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); ck();
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D,
|
|
||||||
gl::TEXTURE_WRAP_S,
|
|
||||||
gl::CLAMP_TO_EDGE as GLint); ck();
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D,
|
|
||||||
gl::TEXTURE_WRAP_T,
|
|
||||||
gl::CLAMP_TO_EDGE as GLint); ck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_render_state(&self, render_state: &RenderState<GLDevice>) {
|
fn set_render_state(&self, render_state: &RenderState<GLDevice>) {
|
||||||
self.bind_render_target(render_state.target);
|
self.bind_render_target(render_state.target);
|
||||||
|
|
||||||
|
@ -236,7 +223,7 @@ impl Device for GLDevice {
|
||||||
ptr::null()); ck();
|
ptr::null()); ck();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_texture_parameters(&texture);
|
self.set_texture_sampling_mode(&texture, TextureSamplingFlags::empty());
|
||||||
texture
|
texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +245,7 @@ impl Device for GLDevice {
|
||||||
data_ptr)
|
data_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_texture_parameters(&texture);
|
self.set_texture_sampling_mode(&texture, TextureSamplingFlags::empty());
|
||||||
texture
|
texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,6 +460,40 @@ impl Device for GLDevice {
|
||||||
texture.size
|
texture.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags) {
|
||||||
|
self.bind_texture(texture, 0);
|
||||||
|
unsafe {
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D,
|
||||||
|
gl::TEXTURE_MIN_FILTER,
|
||||||
|
if flags.contains(TextureSamplingFlags::NEAREST_MIN) {
|
||||||
|
gl::NEAREST as GLint
|
||||||
|
} else {
|
||||||
|
gl::LINEAR as GLint
|
||||||
|
}); ck();
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D,
|
||||||
|
gl::TEXTURE_MAG_FILTER,
|
||||||
|
if flags.contains(TextureSamplingFlags::NEAREST_MAG) {
|
||||||
|
gl::NEAREST as GLint
|
||||||
|
} else {
|
||||||
|
gl::LINEAR as GLint
|
||||||
|
}); ck();
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D,
|
||||||
|
gl::TEXTURE_WRAP_S,
|
||||||
|
if flags.contains(TextureSamplingFlags::REPEAT_U) {
|
||||||
|
gl::REPEAT as GLint
|
||||||
|
} else {
|
||||||
|
gl::CLAMP_TO_EDGE as GLint
|
||||||
|
}); ck();
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D,
|
||||||
|
gl::TEXTURE_WRAP_T,
|
||||||
|
if flags.contains(TextureSamplingFlags::REPEAT_V) {
|
||||||
|
gl::REPEAT as GLint
|
||||||
|
} else {
|
||||||
|
gl::CLAMP_TO_EDGE as GLint
|
||||||
|
}); ck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef) {
|
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef) {
|
||||||
let data_ptr = data.check_and_extract_data_ptr(rect.size(), texture.format);
|
let data_ptr = data.check_and_extract_data_ptr(rect.size(), texture.format);
|
||||||
|
|
||||||
|
@ -506,7 +527,7 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_texture_parameters(texture);
|
self.set_texture_sampling_mode(texture, TextureSamplingFlags::empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_pixels(&self, render_target: &RenderTarget<GLDevice>, viewport: RectI)
|
fn read_pixels(&self, render_target: &RenderTarget<GLDevice>, viewport: RectI)
|
||||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitflags = "1.0"
|
||||||
half = "1.4"
|
half = "1.4"
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
//! Minimal abstractions over GPU device capabilities.
|
//! Minimal abstractions over GPU device capabilities.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate bitflags;
|
||||||
|
|
||||||
use crate::resources::ResourceLoader;
|
use crate::resources::ResourceLoader;
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use image::ImageFormat;
|
use image::ImageFormat;
|
||||||
|
@ -73,6 +76,7 @@ pub trait Device: Sized {
|
||||||
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 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, target: &RenderTarget<Self>, viewport: RectI)
|
||||||
-> Self::TextureDataReceiver;
|
-> Self::TextureDataReceiver;
|
||||||
|
@ -395,6 +399,15 @@ impl Default for BlendState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct TextureSamplingFlags: u8 {
|
||||||
|
const REPEAT_U = 0x01;
|
||||||
|
const REPEAT_V = 0x02;
|
||||||
|
const NEAREST_MIN = 0x04;
|
||||||
|
const NEAREST_MAG = 0x08;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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(self, minimum_size: Vector2I, format: TextureFormat)
|
||||||
|
|
|
@ -46,8 +46,8 @@ use pathfinder_geometry::vector::Vector2I;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, DepthFunc};
|
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, DepthFunc};
|
||||||
use pathfinder_gpu::{Device, Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc};
|
use pathfinder_gpu::{Device, Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc};
|
||||||
use pathfinder_gpu::{TextureData, TextureDataRef, TextureFormat, UniformData, VertexAttrClass};
|
use pathfinder_gpu::{TextureData, TextureDataRef, TextureFormat, TextureSamplingFlags};
|
||||||
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
use pathfinder_gpu::{UniformData, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
||||||
use pathfinder_simd::default::{F32x2, F32x4};
|
use pathfinder_simd::default::{F32x2, F32x4};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -66,7 +66,7 @@ pub struct MetalDevice {
|
||||||
main_depth_stencil_texture: Texture,
|
main_depth_stencil_texture: Texture,
|
||||||
command_queue: CommandQueue,
|
command_queue: CommandQueue,
|
||||||
command_buffers: RefCell<Vec<CommandBuffer>>,
|
command_buffers: RefCell<Vec<CommandBuffer>>,
|
||||||
sampler: SamplerState,
|
samplers: Vec<SamplerState>,
|
||||||
shared_event: SharedEvent,
|
shared_event: SharedEvent,
|
||||||
shared_event_listener: SharedEventListener,
|
shared_event_listener: SharedEventListener,
|
||||||
next_timer_query_event_value: Cell<u64>,
|
next_timer_query_event_value: Cell<u64>,
|
||||||
|
@ -90,14 +90,37 @@ impl MetalDevice {
|
||||||
let drawable = layer.next_drawable().unwrap().retain();
|
let drawable = layer.next_drawable().unwrap().retain();
|
||||||
let command_queue = device.new_command_queue();
|
let command_queue = device.new_command_queue();
|
||||||
|
|
||||||
let sampler_descriptor = SamplerDescriptor::new();
|
let samplers = (0..16).map(|sampling_flags_value| {
|
||||||
sampler_descriptor.set_support_argument_buffers(true);
|
let sampling_flags = TextureSamplingFlags::from_bits(sampling_flags_value).unwrap();
|
||||||
sampler_descriptor.set_normalized_coordinates(true);
|
let sampler_descriptor = SamplerDescriptor::new();
|
||||||
sampler_descriptor.set_min_filter(MTLSamplerMinMagFilter::Linear);
|
sampler_descriptor.set_support_argument_buffers(true);
|
||||||
sampler_descriptor.set_mag_filter(MTLSamplerMinMagFilter::Linear);
|
sampler_descriptor.set_normalized_coordinates(true);
|
||||||
sampler_descriptor.set_address_mode_s(MTLSamplerAddressMode::ClampToEdge);
|
sampler_descriptor.set_min_filter(
|
||||||
sampler_descriptor.set_address_mode_t(MTLSamplerAddressMode::ClampToEdge);
|
if sampling_flags.contains(TextureSamplingFlags::NEAREST_MIN) {
|
||||||
let sampler = device.new_sampler(&sampler_descriptor);
|
MTLSamplerMinMagFilter::Nearest
|
||||||
|
} else {
|
||||||
|
MTLSamplerMinMagFilter::Linear
|
||||||
|
});
|
||||||
|
sampler_descriptor.set_mag_filter(
|
||||||
|
if sampling_flags.contains(TextureSamplingFlags::NEAREST_MAG) {
|
||||||
|
MTLSamplerMinMagFilter::Nearest
|
||||||
|
} else {
|
||||||
|
MTLSamplerMinMagFilter::Linear
|
||||||
|
});
|
||||||
|
sampler_descriptor.set_address_mode_s(
|
||||||
|
if sampling_flags.contains(TextureSamplingFlags::REPEAT_U) {
|
||||||
|
MTLSamplerAddressMode::Repeat
|
||||||
|
} else {
|
||||||
|
MTLSamplerAddressMode::ClampToEdge
|
||||||
|
});
|
||||||
|
sampler_descriptor.set_address_mode_t(
|
||||||
|
if sampling_flags.contains(TextureSamplingFlags::REPEAT_V) {
|
||||||
|
MTLSamplerAddressMode::Repeat
|
||||||
|
} else {
|
||||||
|
MTLSamplerAddressMode::ClampToEdge
|
||||||
|
});
|
||||||
|
device.new_sampler(&sampler_descriptor)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
let main_color_texture = drawable.texture();
|
let main_color_texture = drawable.texture();
|
||||||
let framebuffer_size = Vector2I::new(main_color_texture.width() as i32,
|
let framebuffer_size = Vector2I::new(main_color_texture.width() as i32,
|
||||||
|
@ -113,7 +136,7 @@ impl MetalDevice {
|
||||||
main_depth_stencil_texture,
|
main_depth_stencil_texture,
|
||||||
command_queue,
|
command_queue,
|
||||||
command_buffers: RefCell::new(vec![]),
|
command_buffers: RefCell::new(vec![]),
|
||||||
sampler,
|
samplers,
|
||||||
shared_event,
|
shared_event,
|
||||||
shared_event_listener: SharedEventListener::new(),
|
shared_event_listener: SharedEventListener::new(),
|
||||||
next_timer_query_event_value: Cell::new(1),
|
next_timer_query_event_value: Cell::new(1),
|
||||||
|
@ -145,6 +168,7 @@ enum ShaderUniforms {
|
||||||
|
|
||||||
pub struct MetalTexture {
|
pub struct MetalTexture {
|
||||||
texture: Texture,
|
texture: Texture,
|
||||||
|
sampling_flags: Cell<TextureSamplingFlags>,
|
||||||
dirty: Cell<bool>,
|
dirty: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +253,11 @@ impl Device for MetalDevice {
|
||||||
descriptor.set_height(size.y() as u64);
|
descriptor.set_height(size.y() as u64);
|
||||||
descriptor.set_storage_mode(MTLStorageMode::Managed);
|
descriptor.set_storage_mode(MTLStorageMode::Managed);
|
||||||
descriptor.set_usage(MTLTextureUsage::Unknown);
|
descriptor.set_usage(MTLTextureUsage::Unknown);
|
||||||
MetalTexture { texture: self.device.new_texture(&descriptor), dirty: Cell::new(false) }
|
MetalTexture {
|
||||||
|
texture: self.device.new_texture(&descriptor),
|
||||||
|
sampling_flags: Cell::new(TextureSamplingFlags::empty()),
|
||||||
|
dirty: Cell::new(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
|
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
|
||||||
|
@ -461,6 +489,10 @@ impl Device for MetalDevice {
|
||||||
Vector2I::new(texture.texture.width() as i32, texture.texture.height() as i32)
|
Vector2I::new(texture.texture.width() as i32, texture.texture.height() as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_texture_sampling_mode(&self, texture: &MetalTexture, flags: TextureSamplingFlags) {
|
||||||
|
texture.sampling_flags.set(flags)
|
||||||
|
}
|
||||||
|
|
||||||
fn upload_to_texture(&self, texture: &MetalTexture, rect: RectI, data: TextureDataRef) {
|
fn upload_to_texture(&self, texture: &MetalTexture, rect: RectI, data: TextureDataRef) {
|
||||||
let texture_size = self.texture_size(texture);
|
let texture_size = self.texture_size(texture);
|
||||||
assert!(rect.size().x() >= 0);
|
assert!(rect.size().x() >= 0);
|
||||||
|
@ -987,7 +1019,8 @@ impl MetalDevice {
|
||||||
argument_encoder.set_texture(&texture.texture, argument_index.main);
|
argument_encoder.set_texture(&texture.texture, argument_index.main);
|
||||||
let mut resource_usage = MTLResourceUsage::Read;
|
let mut resource_usage = MTLResourceUsage::Read;
|
||||||
if let Some(sampler_index) = argument_index.sampler {
|
if let Some(sampler_index) = argument_index.sampler {
|
||||||
argument_encoder.set_sampler_state(&self.sampler, sampler_index);
|
let sampler = &self.samplers[texture.sampling_flags.get().bits() as usize];
|
||||||
|
argument_encoder.set_sampler_state(sampler, sampler_index);
|
||||||
resource_usage |= MTLResourceUsage::Sample;
|
resource_usage |= MTLResourceUsage::Sample;
|
||||||
}
|
}
|
||||||
render_command_encoder.use_resource(&texture.texture, resource_usage);
|
render_command_encoder.use_resource(&texture.texture, resource_usage);
|
||||||
|
|
|
@ -53,15 +53,22 @@ enum TreeNode {
|
||||||
Parent([Box<TreeNode>; 4]),
|
Parent([Box<TreeNode>; 4]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub enum AllocationMode {
|
||||||
|
Atlas,
|
||||||
|
OwnPage,
|
||||||
|
}
|
||||||
|
|
||||||
impl TextureAllocator {
|
impl TextureAllocator {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> TextureAllocator {
|
pub fn new() -> TextureAllocator {
|
||||||
TextureAllocator { pages: vec![] }
|
TextureAllocator { pages: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(&mut self, requested_size: Vector2I) -> TextureLocation {
|
pub fn allocate(&mut self, requested_size: Vector2I, mode: AllocationMode) -> TextureLocation {
|
||||||
// If too big, the image gets its own page.
|
// If requested, or if the image is too big, use a separate page.
|
||||||
if requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 ||
|
if mode == AllocationMode::OwnPage ||
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +112,7 @@ impl TextureAllocator {
|
||||||
pub fn page_size(&self, page_index: PaintPageId) -> Vector2I {
|
pub fn page_size(&self, page_index: PaintPageId) -> Vector2I {
|
||||||
match self.pages[page_index.0 as usize] {
|
match self.pages[page_index.0 as usize] {
|
||||||
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
||||||
TexturePageAllocator::Image { size } |
|
TexturePageAllocator::Image { size, .. } |
|
||||||
TexturePageAllocator::RenderTarget { size, .. } => size,
|
TexturePageAllocator::RenderTarget { size, .. } => size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,9 @@
|
||||||
|
|
||||||
use crate::concurrent::executor::Executor;
|
use crate::concurrent::executor::Executor;
|
||||||
use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS};
|
use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS};
|
||||||
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
|
use crate::gpu_data::{AlphaTile, AlphaTileBatch, AlphaTileVertex, FillBatchPrimitive, MaskTile};
|
||||||
use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileBatch, TileObjectPrimitive};
|
use crate::gpu_data::{MaskTileVertex, PaintPageId, RenderCommand};
|
||||||
|
use crate::gpu_data::{SolidTileBatch, TileObjectPrimitive};
|
||||||
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
||||||
use crate::paint::{PaintInfo, PaintMetadata};
|
use crate::paint::{PaintInfo, PaintMetadata};
|
||||||
use crate::scene::{DisplayItem, Scene};
|
use crate::scene::{DisplayItem, Scene};
|
||||||
|
@ -27,6 +28,7 @@ use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmen
|
||||||
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
|
use pathfinder_gpu::TextureSamplingFlags;
|
||||||
use pathfinder_simd::default::{F32x4, I32x4};
|
use pathfinder_simd::default::{F32x4, I32x4};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
@ -53,6 +55,7 @@ pub(crate) struct ObjectBuilder {
|
||||||
struct BuiltDrawPath {
|
struct BuiltDrawPath {
|
||||||
path: BuiltPath,
|
path: BuiltPath,
|
||||||
blend_mode: BlendMode,
|
blend_mode: BlendMode,
|
||||||
|
sampling_flags: TextureSamplingFlags,
|
||||||
paint_page: PaintPageId,
|
paint_page: PaintPageId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,6 +195,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
path: tiler.object_builder.built_path,
|
path: tiler.object_builder.built_path,
|
||||||
blend_mode: path_object.blend_mode(),
|
blend_mode: path_object.blend_mode(),
|
||||||
paint_page: paint_metadata.tex_page,
|
paint_page: paint_metadata.tex_page,
|
||||||
|
sampling_flags: paint_metadata.sampling_flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,29 +269,33 @@ impl<'a> SceneBuilder<'a> {
|
||||||
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
|
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
|
||||||
// breaks in some cases…
|
// breaks in some cases…
|
||||||
match culled_tiles.display_list.last() {
|
match culled_tiles.display_list.last() {
|
||||||
Some(&CulledDisplayItem::DrawAlphaTiles {
|
Some(&CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||||
tiles: _,
|
tiles: _,
|
||||||
paint_page,
|
paint_page,
|
||||||
blend_mode
|
blend_mode,
|
||||||
}) if paint_page == built_draw_path.paint_page &&
|
sampling_flags
|
||||||
|
})) if paint_page == built_draw_path.paint_page &&
|
||||||
blend_mode == built_draw_path.blend_mode &&
|
blend_mode == built_draw_path.blend_mode &&
|
||||||
|
sampling_flags == built_draw_path.sampling_flags &&
|
||||||
!BlendModeProgram::from_blend_mode(
|
!BlendModeProgram::from_blend_mode(
|
||||||
blend_mode).needs_readable_framebuffer() => {}
|
blend_mode).needs_readable_framebuffer() => {}
|
||||||
_ => {
|
_ => {
|
||||||
culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles {
|
culled_tiles.display_list
|
||||||
|
.push(CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||||
tiles: vec![],
|
tiles: vec![],
|
||||||
paint_page: built_draw_path.paint_page,
|
paint_page: built_draw_path.paint_page,
|
||||||
blend_mode: built_draw_path.blend_mode,
|
blend_mode: built_draw_path.blend_mode,
|
||||||
})
|
sampling_flags: built_draw_path.sampling_flags,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the destination alpha tiles buffer.
|
// Fetch the destination alpha tiles buffer.
|
||||||
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
|
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
|
||||||
CulledDisplayItem::DrawAlphaTiles {
|
CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||||
tiles: ref mut culled_alpha_tiles,
|
tiles: ref mut culled_alpha_tiles,
|
||||||
..
|
..
|
||||||
} => culled_alpha_tiles,
|
}) => culled_alpha_tiles,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -358,12 +366,8 @@ impl<'a> SceneBuilder<'a> {
|
||||||
CulledDisplayItem::DrawSolidTiles(batch) => {
|
CulledDisplayItem::DrawSolidTiles(batch) => {
|
||||||
self.listener.send(RenderCommand::DrawSolidTiles(batch))
|
self.listener.send(RenderCommand::DrawSolidTiles(batch))
|
||||||
}
|
}
|
||||||
CulledDisplayItem::DrawAlphaTiles { tiles, paint_page, blend_mode } => {
|
CulledDisplayItem::DrawAlphaTiles(batch) => {
|
||||||
self.listener.send(RenderCommand::DrawAlphaTiles {
|
self.listener.send(RenderCommand::DrawAlphaTiles(batch))
|
||||||
tiles,
|
|
||||||
paint_page,
|
|
||||||
blend_mode,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
CulledDisplayItem::DrawRenderTarget { render_target, effects } => {
|
CulledDisplayItem::DrawRenderTarget { render_target, effects } => {
|
||||||
self.listener.send(RenderCommand::DrawRenderTarget { render_target, effects })
|
self.listener.send(RenderCommand::DrawRenderTarget { render_target, effects })
|
||||||
|
@ -444,7 +448,7 @@ struct CulledTiles {
|
||||||
|
|
||||||
enum CulledDisplayItem {
|
enum CulledDisplayItem {
|
||||||
DrawSolidTiles(SolidTileBatch),
|
DrawSolidTiles(SolidTileBatch),
|
||||||
DrawAlphaTiles { tiles: Vec<AlphaTile>, paint_page: PaintPageId, blend_mode: BlendMode },
|
DrawAlphaTiles(AlphaTileBatch),
|
||||||
DrawRenderTarget { render_target: RenderTargetId, effects: Effects },
|
DrawRenderTarget { render_target: RenderTargetId, effects: Effects },
|
||||||
PushRenderTarget(RenderTargetId),
|
PushRenderTarget(RenderTargetId),
|
||||||
PopRenderTarget,
|
PopRenderTarget,
|
||||||
|
@ -733,14 +737,13 @@ impl AlphaTileVertex {
|
||||||
paint_metadata: &PaintMetadata)
|
paint_metadata: &PaintMetadata)
|
||||||
-> AlphaTileVertex {
|
-> AlphaTileVertex {
|
||||||
let tile_position = tile_origin + tile_offset;
|
let tile_position = tile_origin + tile_offset;
|
||||||
let color_uv = paint_metadata.calculate_tex_coords(tile_position).scale(65535.0).to_i32();
|
let color_uv = paint_metadata.calculate_tex_coords(tile_position);
|
||||||
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
|
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
|
||||||
|
|
||||||
AlphaTileVertex {
|
AlphaTileVertex {
|
||||||
tile_x: tile_position.x() as i16,
|
tile_x: tile_position.x() as i16,
|
||||||
tile_y: tile_position.y() as i16,
|
tile_y: tile_position.y() as i16,
|
||||||
color_u: color_uv.x() as u16,
|
color_u: color_uv.x(),
|
||||||
color_v: color_uv.y() as u16,
|
color_v: color_uv.y(),
|
||||||
mask_u: mask_uv.x() as u16,
|
mask_u: mask_uv.x() as u16,
|
||||||
mask_v: mask_uv.y() as u16,
|
mask_v: mask_uv.y() as u16,
|
||||||
object_index,
|
object_index,
|
||||||
|
|
|
@ -33,7 +33,7 @@ use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendFactor, BlendOp, BlendState, BufferData, BufferTarget, BufferUploadMode};
|
use pathfinder_gpu::{BlendFactor, BlendOp, BlendState, BufferData, BufferTarget, BufferUploadMode};
|
||||||
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
|
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
|
||||||
use pathfinder_gpu::{RenderState, RenderTarget, StencilFunc, StencilState, TextureDataRef};
|
use pathfinder_gpu::{RenderState, RenderTarget, StencilFunc, StencilState, TextureDataRef};
|
||||||
use pathfinder_gpu::{TextureFormat, UniformData};
|
use pathfinder_gpu::{TextureFormat, TextureSamplingFlags, UniformData};
|
||||||
use pathfinder_simd::default::{F32x2, F32x4};
|
use pathfinder_simd::default::{F32x2, F32x4};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -418,13 +418,16 @@ where
|
||||||
let count = batch.vertices.len() / 4;
|
let count = batch.vertices.len() / 4;
|
||||||
self.stats.solid_tile_count += count;
|
self.stats.solid_tile_count += count;
|
||||||
self.upload_solid_tiles(&batch.vertices);
|
self.upload_solid_tiles(&batch.vertices);
|
||||||
self.draw_solid_tiles(count as u32, batch.paint_page);
|
self.draw_solid_tiles(count as u32, batch.paint_page, batch.sampling_flags);
|
||||||
}
|
}
|
||||||
RenderCommand::DrawAlphaTiles { tiles: ref alpha_tiles, paint_page, blend_mode } => {
|
RenderCommand::DrawAlphaTiles(ref batch) => {
|
||||||
let count = alpha_tiles.len();
|
let count = batch.tiles.len();
|
||||||
self.stats.alpha_tile_count += count;
|
self.stats.alpha_tile_count += count;
|
||||||
self.upload_alpha_tiles(alpha_tiles);
|
self.upload_alpha_tiles(&batch.tiles);
|
||||||
self.draw_alpha_tiles(count as u32, paint_page, blend_mode);
|
self.draw_alpha_tiles(count as u32,
|
||||||
|
batch.paint_page,
|
||||||
|
batch.sampling_flags,
|
||||||
|
batch.blend_mode)
|
||||||
}
|
}
|
||||||
RenderCommand::Finish { .. } => {}
|
RenderCommand::Finish { .. } => {}
|
||||||
}
|
}
|
||||||
|
@ -764,6 +767,7 @@ where
|
||||||
fn draw_alpha_tiles(&mut self,
|
fn draw_alpha_tiles(&mut self,
|
||||||
tile_count: u32,
|
tile_count: u32,
|
||||||
paint_page: PaintPageId,
|
paint_page: PaintPageId,
|
||||||
|
sampling_flags: TextureSamplingFlags,
|
||||||
blend_mode: BlendMode) {
|
blend_mode: BlendMode) {
|
||||||
let blend_mode_program = BlendModeProgram::from_blend_mode(blend_mode);
|
let blend_mode_program = BlendModeProgram::from_blend_mode(blend_mode);
|
||||||
if blend_mode_program.needs_readable_framebuffer() {
|
if blend_mode_program.needs_readable_framebuffer() {
|
||||||
|
@ -824,6 +828,8 @@ where
|
||||||
_ => self.paint_texture(paint_page),
|
_ => self.paint_texture(paint_page),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.device.set_texture_sampling_mode(paint_texture, sampling_flags);
|
||||||
|
|
||||||
textures.push(paint_texture);
|
textures.push(paint_texture);
|
||||||
uniforms.push((&self.alpha_tile_program.paint_texture_uniform,
|
uniforms.push((&self.alpha_tile_program.paint_texture_uniform,
|
||||||
UniformData::TextureUnit(1)));
|
UniformData::TextureUnit(1)));
|
||||||
|
@ -983,7 +989,10 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_solid_tiles(&mut self, tile_count: u32, paint_page: PaintPageId) {
|
fn draw_solid_tiles(&mut self,
|
||||||
|
tile_count: u32,
|
||||||
|
paint_page: PaintPageId,
|
||||||
|
sampling_flags: TextureSamplingFlags) {
|
||||||
let clear_color = self.clear_color_for_draw_operation();
|
let clear_color = self.clear_color_for_draw_operation();
|
||||||
|
|
||||||
let mut textures = vec![];
|
let mut textures = vec![];
|
||||||
|
@ -995,6 +1004,7 @@ where
|
||||||
];
|
];
|
||||||
|
|
||||||
let paint_texture = self.paint_texture(paint_page);
|
let paint_texture = self.paint_texture(paint_page);
|
||||||
|
self.device.set_texture_sampling_mode(paint_texture, sampling_flags);
|
||||||
textures.push(paint_texture);
|
textures.push(paint_texture);
|
||||||
uniforms.push((&self.solid_tile_program.paint_texture_uniform,
|
uniforms.push((&self.solid_tile_program.paint_texture_uniform,
|
||||||
UniformData::TextureUnit(0)));
|
UniformData::TextureUnit(0)));
|
||||||
|
|
|
@ -16,8 +16,8 @@ use pathfinder_gpu::resources::ResourceLoader;
|
||||||
|
|
||||||
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
||||||
const FILL_INSTANCE_SIZE: usize = 8;
|
const FILL_INSTANCE_SIZE: usize = 8;
|
||||||
const SOLID_TILE_VERTEX_SIZE: usize = 12;
|
const SOLID_TILE_VERTEX_SIZE: usize = 16;
|
||||||
const ALPHA_TILE_VERTEX_SIZE: usize = 16;
|
const ALPHA_TILE_VERTEX_SIZE: usize = 20;
|
||||||
const MASK_TILE_VERTEX_SIZE: usize = 12;
|
const MASK_TILE_VERTEX_SIZE: usize = 12;
|
||||||
|
|
||||||
pub const MAX_FILLS_PER_BATCH: usize = 0x4000;
|
pub const MAX_FILLS_PER_BATCH: usize = 0x4000;
|
||||||
|
@ -216,8 +216,8 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
|
||||||
});
|
});
|
||||||
device.configure_vertex_attr(&vertex_array, &color_tex_coord_attr, &VertexAttrDescriptor {
|
device.configure_vertex_attr(&vertex_array, &color_tex_coord_attr, &VertexAttrDescriptor {
|
||||||
size: 2,
|
size: 2,
|
||||||
class: VertexAttrClass::FloatNorm,
|
class: VertexAttrClass::Float,
|
||||||
attr_type: VertexAttrType::U16,
|
attr_type: VertexAttrType::F32,
|
||||||
stride: ALPHA_TILE_VERTEX_SIZE,
|
stride: ALPHA_TILE_VERTEX_SIZE,
|
||||||
offset: 8,
|
offset: 8,
|
||||||
divisor: 0,
|
divisor: 0,
|
||||||
|
@ -228,7 +228,7 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
|
||||||
class: VertexAttrClass::FloatNorm,
|
class: VertexAttrClass::FloatNorm,
|
||||||
attr_type: VertexAttrType::U8,
|
attr_type: VertexAttrType::U8,
|
||||||
stride: ALPHA_TILE_VERTEX_SIZE,
|
stride: ALPHA_TILE_VERTEX_SIZE,
|
||||||
offset: 14,
|
offset: 18,
|
||||||
divisor: 0,
|
divisor: 0,
|
||||||
buffer_index: 0,
|
buffer_index: 0,
|
||||||
});
|
});
|
||||||
|
@ -278,8 +278,8 @@ where
|
||||||
&color_tex_coord_attr,
|
&color_tex_coord_attr,
|
||||||
&VertexAttrDescriptor {
|
&VertexAttrDescriptor {
|
||||||
size: 2,
|
size: 2,
|
||||||
class: VertexAttrClass::FloatNorm,
|
class: VertexAttrClass::Float,
|
||||||
attr_type: VertexAttrType::U16,
|
attr_type: VertexAttrType::F32,
|
||||||
stride: SOLID_TILE_VERTEX_SIZE,
|
stride: SOLID_TILE_VERTEX_SIZE,
|
||||||
offset: 4,
|
offset: 4,
|
||||||
divisor: 0,
|
divisor: 0,
|
||||||
|
|
|
@ -17,6 +17,7 @@ use pathfinder_content::fill::FillRule;
|
||||||
use pathfinder_content::pattern::RenderTargetId;
|
use pathfinder_content::pattern::RenderTargetId;
|
||||||
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
|
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
|
use pathfinder_gpu::TextureSamplingFlags;
|
||||||
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ pub enum RenderCommand {
|
||||||
PopRenderTarget,
|
PopRenderTarget,
|
||||||
|
|
||||||
// Draws a batch of alpha tiles to the render target on top of the stack.
|
// Draws a batch of alpha tiles to the render target on top of the stack.
|
||||||
DrawAlphaTiles { tiles: Vec<AlphaTile>, paint_page: PaintPageId, blend_mode: BlendMode },
|
DrawAlphaTiles(AlphaTileBatch),
|
||||||
|
|
||||||
// Draws a batch of solid tiles to the render target on top of the stack.
|
// Draws a batch of solid tiles to the render target on top of the stack.
|
||||||
DrawSolidTiles(SolidTileBatch),
|
DrawSolidTiles(SolidTileBatch),
|
||||||
|
@ -92,10 +93,19 @@ pub enum PaintPageContents {
|
||||||
RenderTarget(RenderTargetId),
|
RenderTarget(RenderTargetId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AlphaTileBatch {
|
||||||
|
pub tiles: Vec<AlphaTile>,
|
||||||
|
pub paint_page: PaintPageId,
|
||||||
|
pub blend_mode: BlendMode,
|
||||||
|
pub sampling_flags: TextureSamplingFlags,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SolidTileBatch {
|
pub struct SolidTileBatch {
|
||||||
pub vertices: Vec<SolidTileVertex>,
|
pub vertices: Vec<SolidTileVertex>,
|
||||||
pub paint_page: PaintPageId,
|
pub paint_page: PaintPageId,
|
||||||
|
pub sampling_flags: TextureSamplingFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -128,8 +138,8 @@ pub struct FillBatchPrimitive {
|
||||||
pub struct SolidTileVertex {
|
pub struct SolidTileVertex {
|
||||||
pub tile_x: i16,
|
pub tile_x: i16,
|
||||||
pub tile_y: i16,
|
pub tile_y: i16,
|
||||||
pub color_u: u16,
|
pub color_u: f32,
|
||||||
pub color_v: u16,
|
pub color_v: f32,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
pub pad: u16,
|
pub pad: u16,
|
||||||
}
|
}
|
||||||
|
@ -170,8 +180,8 @@ pub struct AlphaTileVertex {
|
||||||
pub tile_y: i16,
|
pub tile_y: i16,
|
||||||
pub mask_u: u16,
|
pub mask_u: u16,
|
||||||
pub mask_v: u16,
|
pub mask_v: u16,
|
||||||
pub color_u: u16,
|
pub color_u: f32,
|
||||||
pub color_v: u16,
|
pub color_v: f32,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
pub opacity: u8,
|
pub opacity: u8,
|
||||||
pub pad: u8,
|
pub pad: u8,
|
||||||
|
@ -196,18 +206,20 @@ impl Debug for RenderCommand {
|
||||||
RenderCommand::DrawRenderTarget { render_target, .. } => {
|
RenderCommand::DrawRenderTarget { render_target, .. } => {
|
||||||
write!(formatter, "DrawRenderTarget({:?})", render_target)
|
write!(formatter, "DrawRenderTarget({:?})", render_target)
|
||||||
}
|
}
|
||||||
RenderCommand::DrawAlphaTiles { ref tiles, paint_page, blend_mode } => {
|
RenderCommand::DrawAlphaTiles(ref batch) => {
|
||||||
write!(formatter,
|
write!(formatter,
|
||||||
"DrawAlphaTiles(x{}, {:?}, {:?})",
|
"DrawAlphaTiles(x{}, {:?}, {:?}, {:?})",
|
||||||
tiles.len(),
|
batch.tiles.len(),
|
||||||
paint_page,
|
batch.paint_page,
|
||||||
blend_mode)
|
batch.blend_mode,
|
||||||
|
batch.sampling_flags)
|
||||||
}
|
}
|
||||||
RenderCommand::DrawSolidTiles(ref batch) => {
|
RenderCommand::DrawSolidTiles(ref batch) => {
|
||||||
write!(formatter,
|
write!(formatter,
|
||||||
"DrawSolidTiles(x{}, {:?})",
|
"DrawSolidTiles(x{}, {:?}, {:?})",
|
||||||
batch.vertices.len(),
|
batch.vertices.len(),
|
||||||
batch.paint_page)
|
batch.paint_page,
|
||||||
|
batch.sampling_flags)
|
||||||
}
|
}
|
||||||
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,19 @@
|
||||||
// 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::allocator::{TextureAllocator, TextureLocation};
|
use crate::allocator::{AllocationMode, TextureAllocator, TextureLocation};
|
||||||
use crate::gpu_data::{PaintData, PaintPageContents, PaintPageData, PaintPageId};
|
use crate::gpu_data::{PaintData, PaintPageContents, PaintPageData, PaintPageId};
|
||||||
use crate::scene::RenderTarget;
|
use crate::scene::RenderTarget;
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::gradient::{Gradient, GradientGeometry};
|
use pathfinder_content::gradient::{Gradient, GradientGeometry};
|
||||||
use pathfinder_content::pattern::{Image, Pattern, PatternSource, RenderTargetId};
|
use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource, RenderTargetId};
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
|
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
||||||
|
use pathfinder_gpu::TextureSamplingFlags;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
|
@ -168,6 +169,8 @@ pub struct PaintMetadata {
|
||||||
pub tex_rect: RectI,
|
pub tex_rect: RectI,
|
||||||
/// The transform to apply to screen coordinates to translate them into UVs.
|
/// The transform to apply to screen coordinates to translate them into UVs.
|
||||||
pub tex_transform: Transform2F,
|
pub tex_transform: Transform2F,
|
||||||
|
/// The sampling mode for the texture.
|
||||||
|
pub sampling_flags: TextureSamplingFlags,
|
||||||
/// True if this paint is fully opaque.
|
/// True if this paint is fully opaque.
|
||||||
pub is_opaque: bool,
|
pub is_opaque: bool,
|
||||||
}
|
}
|
||||||
|
@ -206,24 +209,50 @@ impl Palette {
|
||||||
// Assign paint locations.
|
// Assign paint locations.
|
||||||
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
||||||
for paint in &self.paints {
|
for paint in &self.paints {
|
||||||
let tex_location = match paint {
|
let (tex_location, mut sampling_flags);
|
||||||
Paint::Color(_) => solid_color_tile_builder.allocate(&mut allocator),
|
match paint {
|
||||||
|
Paint::Color(_) => {
|
||||||
|
tex_location = solid_color_tile_builder.allocate(&mut allocator);
|
||||||
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
|
}
|
||||||
Paint::Gradient(_) => {
|
Paint::Gradient(_) => {
|
||||||
// TODO(pcwalton): Optimize this:
|
// TODO(pcwalton): Optimize this:
|
||||||
// 1. Use repeating/clamp on the sides.
|
// 1. Use repeating/clamp on the sides.
|
||||||
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
||||||
// retaining quality.
|
// retaining quality.
|
||||||
allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32))
|
tex_location = allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32),
|
||||||
|
AllocationMode::Atlas);
|
||||||
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
}
|
}
|
||||||
Paint::Pattern(ref pattern) => {
|
Paint::Pattern(ref pattern) => {
|
||||||
match pattern.source {
|
match pattern.source {
|
||||||
PatternSource::RenderTarget(render_target_id) => {
|
PatternSource::RenderTarget(render_target_id) => {
|
||||||
render_target_locations[render_target_id.0 as usize]
|
tex_location = render_target_locations[render_target_id.0 as usize];
|
||||||
}
|
}
|
||||||
PatternSource::Image(ref image) => {
|
PatternSource::Image(ref image) => {
|
||||||
allocator.allocate(image.size())
|
// TODO(pcwalton): We should be able to use tile cleverness to repeat
|
||||||
|
// inside the atlas in some cases.
|
||||||
|
let allocation_mode = if pattern.flags == PatternFlags::empty() {
|
||||||
|
AllocationMode::Atlas
|
||||||
|
} else {
|
||||||
|
AllocationMode::OwnPage
|
||||||
|
};
|
||||||
|
|
||||||
|
tex_location = allocator.allocate(image.size(), allocation_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
|
if pattern.flags.contains(PatternFlags::REPEAT_X) {
|
||||||
|
sampling_flags.insert(TextureSamplingFlags::REPEAT_U);
|
||||||
|
}
|
||||||
|
if pattern.flags.contains(PatternFlags::REPEAT_Y) {
|
||||||
|
sampling_flags.insert(TextureSamplingFlags::REPEAT_V);
|
||||||
|
}
|
||||||
|
if pattern.flags.contains(PatternFlags::NO_SMOOTHING) {
|
||||||
|
sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN |
|
||||||
|
TextureSamplingFlags::NEAREST_MAG);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -231,6 +260,7 @@ impl Palette {
|
||||||
tex_page: tex_location.page,
|
tex_page: tex_location.page,
|
||||||
tex_rect: tex_location.rect,
|
tex_rect: tex_location.rect,
|
||||||
tex_transform: Transform2F::default(),
|
tex_transform: Transform2F::default(),
|
||||||
|
sampling_flags,
|
||||||
is_opaque: paint.is_opaque(),
|
is_opaque: paint.is_opaque(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -301,11 +331,11 @@ impl Palette {
|
||||||
}
|
}
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
self.render_gradient(gradient,
|
self.render_gradient(gradient,
|
||||||
metadata.tex_rect,
|
metadata.tex_rect,
|
||||||
&metadata.tex_transform,
|
&metadata.tex_transform,
|
||||||
texels,
|
texels,
|
||||||
page_size,
|
page_size,
|
||||||
page_scale);
|
page_scale);
|
||||||
}
|
}
|
||||||
Paint::Pattern(ref pattern) => {
|
Paint::Pattern(ref pattern) => {
|
||||||
match pattern.source {
|
match pattern.source {
|
||||||
|
@ -440,7 +470,8 @@ impl SolidColorTileBuilder {
|
||||||
if self.0.is_none() {
|
if self.0.is_none() {
|
||||||
// TODO(pcwalton): Handle allocation failure gracefully!
|
// TODO(pcwalton): Handle allocation failure gracefully!
|
||||||
self.0 = Some(SolidColorTileBuilderData {
|
self.0 = Some(SolidColorTileBuilderData {
|
||||||
tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32)),
|
tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32),
|
||||||
|
AllocationMode::Atlas),
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,11 +69,13 @@ impl ZBuffer {
|
||||||
|
|
||||||
// Create a batch if necessary.
|
// Create a batch if necessary.
|
||||||
match solid_tiles.batches.last() {
|
match solid_tiles.batches.last() {
|
||||||
Some(ref batch) if batch.paint_page == paint_metadata.tex_page => {}
|
Some(ref batch) if batch.paint_page == paint_metadata.tex_page &&
|
||||||
|
batch.sampling_flags == paint_metadata.sampling_flags => {}
|
||||||
_ => {
|
_ => {
|
||||||
// Batch break.
|
// Batch break.
|
||||||
solid_tiles.batches.push(SolidTileBatch {
|
solid_tiles.batches.push(SolidTileBatch {
|
||||||
paint_page: paint_metadata.tex_page,
|
paint_page: paint_metadata.tex_page,
|
||||||
|
sampling_flags: paint_metadata.sampling_flags,
|
||||||
vertices: vec![],
|
vertices: vec![],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -101,13 +103,13 @@ impl ZBuffer {
|
||||||
impl SolidTileVertex {
|
impl SolidTileVertex {
|
||||||
fn new(tile_position: Vector2I, object_index: u16, paint_metadata: &PaintMetadata)
|
fn new(tile_position: Vector2I, object_index: u16, paint_metadata: &PaintMetadata)
|
||||||
-> SolidTileVertex {
|
-> SolidTileVertex {
|
||||||
let color_uv = paint_metadata.calculate_tex_coords(tile_position).scale(65535.0).to_i32();
|
let color_uv = paint_metadata.calculate_tex_coords(tile_position);
|
||||||
SolidTileVertex {
|
SolidTileVertex {
|
||||||
tile_x: tile_position.x() as i16,
|
tile_x: tile_position.x() as i16,
|
||||||
tile_y: tile_position.y() as i16,
|
tile_y: tile_position.y() as i16,
|
||||||
object_index: object_index,
|
object_index: object_index,
|
||||||
color_u: color_uv.x() as u16,
|
color_u: color_uv.x(),
|
||||||
color_v: color_uv.y() as u16,
|
color_v: color_uv.y(),
|
||||||
pad: 0,
|
pad: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue