Implement pattern repeating and image smoothing control
This commit is contained in:
parent
77b3555828
commit
d1c7da8bd2
|
@ -1707,6 +1707,7 @@ dependencies = [
|
|||
name = "pathfinder_gpu"
|
||||
version = "0.1.0"
|
||||
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)",
|
||||
"image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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::gradient::Gradient;
|
||||
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::{OutlineStrokeToFill, StrokeStyle};
|
||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||
|
@ -326,6 +326,28 @@ impl CanvasRenderingContext2D {
|
|||
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
|
||||
|
||||
#[inline]
|
||||
|
@ -357,6 +379,8 @@ struct State {
|
|||
shadow_paint: Paint,
|
||||
shadow_offset: Vector2F,
|
||||
text_align: TextAlign,
|
||||
image_smoothing_enabled: bool,
|
||||
image_smoothing_quality: ImageSmoothingQuality,
|
||||
global_alpha: f32,
|
||||
global_composite_operation: CompositeOperation,
|
||||
clip_path: Option<ClipPathId>,
|
||||
|
@ -379,6 +403,8 @@ impl State {
|
|||
shadow_paint: Paint::transparent_black(),
|
||||
shadow_offset: Vector2F::default(),
|
||||
text_align: TextAlign::Left,
|
||||
image_smoothing_enabled: true,
|
||||
image_smoothing_quality: ImageSmoothingQuality::Low,
|
||||
global_alpha: 1.0,
|
||||
global_composite_operation: CompositeOperation::SourceOver,
|
||||
clip_path: None,
|
||||
|
@ -389,9 +415,18 @@ impl State {
|
|||
if self.transform.is_identity() {
|
||||
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();
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
pub struct Pattern {
|
||||
pub source: PatternSource,
|
||||
pub repeat: Repeat,
|
||||
pub flags: PatternFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -44,16 +44,17 @@ pub struct Image {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct Repeat: u8 {
|
||||
const X = 0x01;
|
||||
const Y = 0x02;
|
||||
pub struct PatternFlags: u8 {
|
||||
const REPEAT_X = 0x01;
|
||||
const REPEAT_Y = 0x02;
|
||||
const NO_SMOOTHING = 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
#[inline]
|
||||
pub fn new(source: PatternSource, repeat: Repeat) -> Pattern {
|
||||
Pattern { source, repeat }
|
||||
pub fn new(source: PatternSource, flags: PatternFlags) -> Pattern {
|
||||
Pattern { source, flags }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ use pathfinder_gpu::resources::ResourceLoader;
|
|||
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
|
||||
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderOptions, RenderState, RenderTarget};
|
||||
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 std::ffi::CString;
|
||||
use std::mem;
|
||||
|
@ -48,20 +49,6 @@ impl GLDevice {
|
|||
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>) {
|
||||
self.bind_render_target(render_state.target);
|
||||
|
||||
|
@ -236,7 +223,7 @@ impl Device for GLDevice {
|
|||
ptr::null()); ck();
|
||||
}
|
||||
|
||||
self.set_texture_parameters(&texture);
|
||||
self.set_texture_sampling_mode(&texture, TextureSamplingFlags::empty());
|
||||
texture
|
||||
}
|
||||
|
||||
|
@ -258,7 +245,7 @@ impl Device for GLDevice {
|
|||
data_ptr)
|
||||
}
|
||||
|
||||
self.set_texture_parameters(&texture);
|
||||
self.set_texture_sampling_mode(&texture, TextureSamplingFlags::empty());
|
||||
texture
|
||||
}
|
||||
|
||||
|
@ -473,6 +460,40 @@ impl Device for GLDevice {
|
|||
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) {
|
||||
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)
|
||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
half = "1.4"
|
||||
|
||||
[dependencies.image]
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
//! Minimal abstractions over GPU device capabilities.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
use crate::resources::ResourceLoader;
|
||||
use half::f16;
|
||||
use image::ImageFormat;
|
||||
|
@ -73,6 +76,7 @@ pub trait Device: Sized {
|
|||
fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture;
|
||||
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
|
||||
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 read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)
|
||||
-> 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> {
|
||||
#[doc(hidden)]
|
||||
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::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, DepthFunc};
|
||||
use pathfinder_gpu::{Device, Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc};
|
||||
use pathfinder_gpu::{TextureData, TextureDataRef, TextureFormat, UniformData, VertexAttrClass};
|
||||
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
||||
use pathfinder_gpu::{TextureData, TextureDataRef, TextureFormat, TextureSamplingFlags};
|
||||
use pathfinder_gpu::{UniformData, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
||||
use pathfinder_simd::default::{F32x2, F32x4};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
|
@ -66,7 +66,7 @@ pub struct MetalDevice {
|
|||
main_depth_stencil_texture: Texture,
|
||||
command_queue: CommandQueue,
|
||||
command_buffers: RefCell<Vec<CommandBuffer>>,
|
||||
sampler: SamplerState,
|
||||
samplers: Vec<SamplerState>,
|
||||
shared_event: SharedEvent,
|
||||
shared_event_listener: SharedEventListener,
|
||||
next_timer_query_event_value: Cell<u64>,
|
||||
|
@ -90,14 +90,37 @@ impl MetalDevice {
|
|||
let drawable = layer.next_drawable().unwrap().retain();
|
||||
let command_queue = device.new_command_queue();
|
||||
|
||||
let samplers = (0..16).map(|sampling_flags_value| {
|
||||
let sampling_flags = TextureSamplingFlags::from_bits(sampling_flags_value).unwrap();
|
||||
let sampler_descriptor = SamplerDescriptor::new();
|
||||
sampler_descriptor.set_support_argument_buffers(true);
|
||||
sampler_descriptor.set_normalized_coordinates(true);
|
||||
sampler_descriptor.set_min_filter(MTLSamplerMinMagFilter::Linear);
|
||||
sampler_descriptor.set_mag_filter(MTLSamplerMinMagFilter::Linear);
|
||||
sampler_descriptor.set_address_mode_s(MTLSamplerAddressMode::ClampToEdge);
|
||||
sampler_descriptor.set_address_mode_t(MTLSamplerAddressMode::ClampToEdge);
|
||||
let sampler = device.new_sampler(&sampler_descriptor);
|
||||
sampler_descriptor.set_min_filter(
|
||||
if sampling_flags.contains(TextureSamplingFlags::NEAREST_MIN) {
|
||||
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 framebuffer_size = Vector2I::new(main_color_texture.width() as i32,
|
||||
|
@ -113,7 +136,7 @@ impl MetalDevice {
|
|||
main_depth_stencil_texture,
|
||||
command_queue,
|
||||
command_buffers: RefCell::new(vec![]),
|
||||
sampler,
|
||||
samplers,
|
||||
shared_event,
|
||||
shared_event_listener: SharedEventListener::new(),
|
||||
next_timer_query_event_value: Cell::new(1),
|
||||
|
@ -145,6 +168,7 @@ enum ShaderUniforms {
|
|||
|
||||
pub struct MetalTexture {
|
||||
texture: Texture,
|
||||
sampling_flags: Cell<TextureSamplingFlags>,
|
||||
dirty: Cell<bool>,
|
||||
}
|
||||
|
||||
|
@ -229,7 +253,11 @@ impl Device for MetalDevice {
|
|||
descriptor.set_height(size.y() as u64);
|
||||
descriptor.set_storage_mode(MTLStorageMode::Managed);
|
||||
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)
|
||||
|
@ -461,6 +489,10 @@ impl Device for MetalDevice {
|
|||
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) {
|
||||
let texture_size = self.texture_size(texture);
|
||||
assert!(rect.size().x() >= 0);
|
||||
|
@ -987,7 +1019,8 @@ impl MetalDevice {
|
|||
argument_encoder.set_texture(&texture.texture, argument_index.main);
|
||||
let mut resource_usage = MTLResourceUsage::Read;
|
||||
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;
|
||||
}
|
||||
render_command_encoder.use_resource(&texture.texture, resource_usage);
|
||||
|
|
|
@ -53,15 +53,22 @@ enum TreeNode {
|
|||
Parent([Box<TreeNode>; 4]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum AllocationMode {
|
||||
Atlas,
|
||||
OwnPage,
|
||||
}
|
||||
|
||||
impl TextureAllocator {
|
||||
#[inline]
|
||||
pub fn new() -> TextureAllocator {
|
||||
TextureAllocator { pages: vec![] }
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, requested_size: Vector2I) -> TextureLocation {
|
||||
// If too big, the image gets its own page.
|
||||
if requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 ||
|
||||
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 mode == AllocationMode::OwnPage ||
|
||||
requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 ||
|
||||
requested_size.y() > ATLAS_TEXTURE_LENGTH as i32 {
|
||||
return self.allocate_image(requested_size);
|
||||
}
|
||||
|
@ -105,7 +112,7 @@ impl TextureAllocator {
|
|||
pub fn page_size(&self, page_index: PaintPageId) -> Vector2I {
|
||||
match self.pages[page_index.0 as usize] {
|
||||
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
||||
TexturePageAllocator::Image { size } |
|
||||
TexturePageAllocator::Image { size, .. } |
|
||||
TexturePageAllocator::RenderTarget { size, .. } => size,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
|
||||
use crate::concurrent::executor::Executor;
|
||||
use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS};
|
||||
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
|
||||
use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileBatch, TileObjectPrimitive};
|
||||
use crate::gpu_data::{AlphaTile, AlphaTileBatch, AlphaTileVertex, FillBatchPrimitive, MaskTile};
|
||||
use crate::gpu_data::{MaskTileVertex, PaintPageId, RenderCommand};
|
||||
use crate::gpu_data::{SolidTileBatch, TileObjectPrimitive};
|
||||
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
||||
use crate::paint::{PaintInfo, PaintMetadata};
|
||||
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::rect::{RectF, RectI};
|
||||
use pathfinder_geometry::util;
|
||||
use pathfinder_gpu::TextureSamplingFlags;
|
||||
use pathfinder_simd::default::{F32x4, I32x4};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
|
@ -53,6 +55,7 @@ pub(crate) struct ObjectBuilder {
|
|||
struct BuiltDrawPath {
|
||||
path: BuiltPath,
|
||||
blend_mode: BlendMode,
|
||||
sampling_flags: TextureSamplingFlags,
|
||||
paint_page: PaintPageId,
|
||||
}
|
||||
|
||||
|
@ -192,6 +195,7 @@ impl<'a> SceneBuilder<'a> {
|
|||
path: tiler.object_builder.built_path,
|
||||
blend_mode: path_object.blend_mode(),
|
||||
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
|
||||
// breaks in some cases…
|
||||
match culled_tiles.display_list.last() {
|
||||
Some(&CulledDisplayItem::DrawAlphaTiles {
|
||||
Some(&CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||
tiles: _,
|
||||
paint_page,
|
||||
blend_mode
|
||||
}) if paint_page == built_draw_path.paint_page &&
|
||||
blend_mode,
|
||||
sampling_flags
|
||||
})) if paint_page == built_draw_path.paint_page &&
|
||||
blend_mode == built_draw_path.blend_mode &&
|
||||
sampling_flags == built_draw_path.sampling_flags &&
|
||||
!BlendModeProgram::from_blend_mode(
|
||||
blend_mode).needs_readable_framebuffer() => {}
|
||||
_ => {
|
||||
culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles {
|
||||
culled_tiles.display_list
|
||||
.push(CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||
tiles: vec![],
|
||||
paint_page: built_draw_path.paint_page,
|
||||
blend_mode: built_draw_path.blend_mode,
|
||||
})
|
||||
sampling_flags: built_draw_path.sampling_flags,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the destination alpha tiles buffer.
|
||||
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
|
||||
CulledDisplayItem::DrawAlphaTiles {
|
||||
CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
|
||||
tiles: ref mut culled_alpha_tiles,
|
||||
..
|
||||
} => culled_alpha_tiles,
|
||||
}) => culled_alpha_tiles,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
@ -358,12 +366,8 @@ impl<'a> SceneBuilder<'a> {
|
|||
CulledDisplayItem::DrawSolidTiles(batch) => {
|
||||
self.listener.send(RenderCommand::DrawSolidTiles(batch))
|
||||
}
|
||||
CulledDisplayItem::DrawAlphaTiles { tiles, paint_page, blend_mode } => {
|
||||
self.listener.send(RenderCommand::DrawAlphaTiles {
|
||||
tiles,
|
||||
paint_page,
|
||||
blend_mode,
|
||||
})
|
||||
CulledDisplayItem::DrawAlphaTiles(batch) => {
|
||||
self.listener.send(RenderCommand::DrawAlphaTiles(batch))
|
||||
}
|
||||
CulledDisplayItem::DrawRenderTarget { render_target, effects } => {
|
||||
self.listener.send(RenderCommand::DrawRenderTarget { render_target, effects })
|
||||
|
@ -444,7 +448,7 @@ struct CulledTiles {
|
|||
|
||||
enum CulledDisplayItem {
|
||||
DrawSolidTiles(SolidTileBatch),
|
||||
DrawAlphaTiles { tiles: Vec<AlphaTile>, paint_page: PaintPageId, blend_mode: BlendMode },
|
||||
DrawAlphaTiles(AlphaTileBatch),
|
||||
DrawRenderTarget { render_target: RenderTargetId, effects: Effects },
|
||||
PushRenderTarget(RenderTargetId),
|
||||
PopRenderTarget,
|
||||
|
@ -733,14 +737,13 @@ impl AlphaTileVertex {
|
|||
paint_metadata: &PaintMetadata)
|
||||
-> AlphaTileVertex {
|
||||
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);
|
||||
|
||||
AlphaTileVertex {
|
||||
tile_x: tile_position.x() as i16,
|
||||
tile_y: tile_position.y() as i16,
|
||||
color_u: color_uv.x() as u16,
|
||||
color_v: color_uv.y() as u16,
|
||||
color_u: color_uv.x(),
|
||||
color_v: color_uv.y(),
|
||||
mask_u: mask_uv.x() as u16,
|
||||
mask_v: mask_uv.y() as u16,
|
||||
object_index,
|
||||
|
|
|
@ -33,7 +33,7 @@ use pathfinder_gpu::resources::ResourceLoader;
|
|||
use pathfinder_gpu::{BlendFactor, BlendOp, BlendState, BufferData, BufferTarget, BufferUploadMode};
|
||||
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
|
||||
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 std::cmp;
|
||||
use std::collections::VecDeque;
|
||||
|
@ -418,13 +418,16 @@ where
|
|||
let count = batch.vertices.len() / 4;
|
||||
self.stats.solid_tile_count += count;
|
||||
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 } => {
|
||||
let count = alpha_tiles.len();
|
||||
RenderCommand::DrawAlphaTiles(ref batch) => {
|
||||
let count = batch.tiles.len();
|
||||
self.stats.alpha_tile_count += count;
|
||||
self.upload_alpha_tiles(alpha_tiles);
|
||||
self.draw_alpha_tiles(count as u32, paint_page, blend_mode);
|
||||
self.upload_alpha_tiles(&batch.tiles);
|
||||
self.draw_alpha_tiles(count as u32,
|
||||
batch.paint_page,
|
||||
batch.sampling_flags,
|
||||
batch.blend_mode)
|
||||
}
|
||||
RenderCommand::Finish { .. } => {}
|
||||
}
|
||||
|
@ -764,6 +767,7 @@ where
|
|||
fn draw_alpha_tiles(&mut self,
|
||||
tile_count: u32,
|
||||
paint_page: PaintPageId,
|
||||
sampling_flags: TextureSamplingFlags,
|
||||
blend_mode: BlendMode) {
|
||||
let blend_mode_program = BlendModeProgram::from_blend_mode(blend_mode);
|
||||
if blend_mode_program.needs_readable_framebuffer() {
|
||||
|
@ -824,6 +828,8 @@ where
|
|||
_ => self.paint_texture(paint_page),
|
||||
};
|
||||
|
||||
self.device.set_texture_sampling_mode(paint_texture, sampling_flags);
|
||||
|
||||
textures.push(paint_texture);
|
||||
uniforms.push((&self.alpha_tile_program.paint_texture_uniform,
|
||||
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 mut textures = vec![];
|
||||
|
@ -995,6 +1004,7 @@ where
|
|||
];
|
||||
|
||||
let paint_texture = self.paint_texture(paint_page);
|
||||
self.device.set_texture_sampling_mode(paint_texture, sampling_flags);
|
||||
textures.push(paint_texture);
|
||||
uniforms.push((&self.solid_tile_program.paint_texture_uniform,
|
||||
UniformData::TextureUnit(0)));
|
||||
|
|
|
@ -16,8 +16,8 @@ use pathfinder_gpu::resources::ResourceLoader;
|
|||
|
||||
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
||||
const FILL_INSTANCE_SIZE: usize = 8;
|
||||
const SOLID_TILE_VERTEX_SIZE: usize = 12;
|
||||
const ALPHA_TILE_VERTEX_SIZE: usize = 16;
|
||||
const SOLID_TILE_VERTEX_SIZE: usize = 16;
|
||||
const ALPHA_TILE_VERTEX_SIZE: usize = 20;
|
||||
const MASK_TILE_VERTEX_SIZE: usize = 12;
|
||||
|
||||
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 {
|
||||
size: 2,
|
||||
class: VertexAttrClass::FloatNorm,
|
||||
attr_type: VertexAttrType::U16,
|
||||
class: VertexAttrClass::Float,
|
||||
attr_type: VertexAttrType::F32,
|
||||
stride: ALPHA_TILE_VERTEX_SIZE,
|
||||
offset: 8,
|
||||
divisor: 0,
|
||||
|
@ -228,7 +228,7 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
|
|||
class: VertexAttrClass::FloatNorm,
|
||||
attr_type: VertexAttrType::U8,
|
||||
stride: ALPHA_TILE_VERTEX_SIZE,
|
||||
offset: 14,
|
||||
offset: 18,
|
||||
divisor: 0,
|
||||
buffer_index: 0,
|
||||
});
|
||||
|
@ -278,8 +278,8 @@ where
|
|||
&color_tex_coord_attr,
|
||||
&VertexAttrDescriptor {
|
||||
size: 2,
|
||||
class: VertexAttrClass::FloatNorm,
|
||||
attr_type: VertexAttrType::U16,
|
||||
class: VertexAttrClass::Float,
|
||||
attr_type: VertexAttrType::F32,
|
||||
stride: SOLID_TILE_VERTEX_SIZE,
|
||||
offset: 4,
|
||||
divisor: 0,
|
||||
|
|
|
@ -17,6 +17,7 @@ use pathfinder_content::fill::FillRule;
|
|||
use pathfinder_content::pattern::RenderTargetId;
|
||||
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
|
||||
use pathfinder_geometry::vector::Vector2I;
|
||||
use pathfinder_gpu::TextureSamplingFlags;
|
||||
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -56,7 +57,7 @@ pub enum RenderCommand {
|
|||
PopRenderTarget,
|
||||
|
||||
// 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.
|
||||
DrawSolidTiles(SolidTileBatch),
|
||||
|
@ -92,10 +93,19 @@ pub enum PaintPageContents {
|
|||
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)]
|
||||
pub struct SolidTileBatch {
|
||||
pub vertices: Vec<SolidTileVertex>,
|
||||
pub paint_page: PaintPageId,
|
||||
pub sampling_flags: TextureSamplingFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -128,8 +138,8 @@ pub struct FillBatchPrimitive {
|
|||
pub struct SolidTileVertex {
|
||||
pub tile_x: i16,
|
||||
pub tile_y: i16,
|
||||
pub color_u: u16,
|
||||
pub color_v: u16,
|
||||
pub color_u: f32,
|
||||
pub color_v: f32,
|
||||
pub object_index: u16,
|
||||
pub pad: u16,
|
||||
}
|
||||
|
@ -170,8 +180,8 @@ pub struct AlphaTileVertex {
|
|||
pub tile_y: i16,
|
||||
pub mask_u: u16,
|
||||
pub mask_v: u16,
|
||||
pub color_u: u16,
|
||||
pub color_v: u16,
|
||||
pub color_u: f32,
|
||||
pub color_v: f32,
|
||||
pub object_index: u16,
|
||||
pub opacity: u8,
|
||||
pub pad: u8,
|
||||
|
@ -196,18 +206,20 @@ impl Debug for RenderCommand {
|
|||
RenderCommand::DrawRenderTarget { render_target, .. } => {
|
||||
write!(formatter, "DrawRenderTarget({:?})", render_target)
|
||||
}
|
||||
RenderCommand::DrawAlphaTiles { ref tiles, paint_page, blend_mode } => {
|
||||
RenderCommand::DrawAlphaTiles(ref batch) => {
|
||||
write!(formatter,
|
||||
"DrawAlphaTiles(x{}, {:?}, {:?})",
|
||||
tiles.len(),
|
||||
paint_page,
|
||||
blend_mode)
|
||||
"DrawAlphaTiles(x{}, {:?}, {:?}, {:?})",
|
||||
batch.tiles.len(),
|
||||
batch.paint_page,
|
||||
batch.blend_mode,
|
||||
batch.sampling_flags)
|
||||
}
|
||||
RenderCommand::DrawSolidTiles(ref batch) => {
|
||||
write!(formatter,
|
||||
"DrawSolidTiles(x{}, {:?})",
|
||||
"DrawSolidTiles(x{}, {:?}, {:?})",
|
||||
batch.vertices.len(),
|
||||
batch.paint_page)
|
||||
batch.paint_page,
|
||||
batch.sampling_flags)
|
||||
}
|
||||
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
||||
}
|
||||
|
|
|
@ -8,18 +8,19 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// 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::scene::RenderTarget;
|
||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||
use hashbrown::HashMap;
|
||||
use pathfinder_color::ColorU;
|
||||
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::transform2d::{Matrix2x2F, Transform2F};
|
||||
use pathfinder_geometry::util;
|
||||
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
||||
use pathfinder_gpu::TextureSamplingFlags;
|
||||
use pathfinder_simd::default::F32x4;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
|
@ -168,6 +169,8 @@ pub struct PaintMetadata {
|
|||
pub tex_rect: RectI,
|
||||
/// The transform to apply to screen coordinates to translate them into UVs.
|
||||
pub tex_transform: Transform2F,
|
||||
/// The sampling mode for the texture.
|
||||
pub sampling_flags: TextureSamplingFlags,
|
||||
/// True if this paint is fully opaque.
|
||||
pub is_opaque: bool,
|
||||
}
|
||||
|
@ -206,24 +209,50 @@ impl Palette {
|
|||
// Assign paint locations.
|
||||
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
||||
for paint in &self.paints {
|
||||
let tex_location = match paint {
|
||||
Paint::Color(_) => solid_color_tile_builder.allocate(&mut allocator),
|
||||
let (tex_location, mut sampling_flags);
|
||||
match paint {
|
||||
Paint::Color(_) => {
|
||||
tex_location = solid_color_tile_builder.allocate(&mut allocator);
|
||||
sampling_flags = TextureSamplingFlags::empty();
|
||||
}
|
||||
Paint::Gradient(_) => {
|
||||
// TODO(pcwalton): Optimize this:
|
||||
// 1. Use repeating/clamp on the sides.
|
||||
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
||||
// 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) => {
|
||||
match pattern.source {
|
||||
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) => {
|
||||
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_rect: tex_location.rect,
|
||||
tex_transform: Transform2F::default(),
|
||||
sampling_flags,
|
||||
is_opaque: paint.is_opaque(),
|
||||
});
|
||||
}
|
||||
|
@ -440,7 +470,8 @@ impl SolidColorTileBuilder {
|
|||
if self.0.is_none() {
|
||||
// TODO(pcwalton): Handle allocation failure gracefully!
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -69,11 +69,13 @@ impl ZBuffer {
|
|||
|
||||
// Create a batch if necessary.
|
||||
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.
|
||||
solid_tiles.batches.push(SolidTileBatch {
|
||||
paint_page: paint_metadata.tex_page,
|
||||
sampling_flags: paint_metadata.sampling_flags,
|
||||
vertices: vec![],
|
||||
});
|
||||
}
|
||||
|
@ -101,13 +103,13 @@ impl ZBuffer {
|
|||
impl SolidTileVertex {
|
||||
fn new(tile_position: Vector2I, object_index: u16, paint_metadata: &PaintMetadata)
|
||||
-> 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 {
|
||||
tile_x: tile_position.x() as i16,
|
||||
tile_y: tile_position.y() as i16,
|
||||
object_index: object_index,
|
||||
color_u: color_uv.x() as u16,
|
||||
color_v: color_uv.y() as u16,
|
||||
color_u: color_uv.x(),
|
||||
color_v: color_uv.y(),
|
||||
pad: 0,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue