Implement pattern repeating and image smoothing control

This commit is contained in:
Patrick Walton 2020-02-26 14:56:05 -08:00
parent 77b3555828
commit d1c7da8bd2
14 changed files with 283 additions and 106 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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,
}

View File

@ -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 }
}
}

View File

@ -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)

View File

@ -5,6 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[dependencies]
bitflags = "1.0"
half = "1.4"
[dependencies.image]

View File

@ -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)

View File

@ -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 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);
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(
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);

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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)));

View File

@ -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,

View File

@ -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"),
}

View File

@ -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(),
});
}
@ -301,11 +331,11 @@ impl Palette {
}
Paint::Gradient(ref gradient) => {
self.render_gradient(gradient,
metadata.tex_rect,
&metadata.tex_transform,
texels,
page_size,
page_scale);
metadata.tex_rect,
&metadata.tex_transform,
texels,
page_size,
page_scale);
}
Paint::Pattern(ref pattern) => {
match pattern.source {
@ -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,
});
}

View File

@ -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,
}
}