Stop copying images every frame on CPU.
We can avoid reuploading them to the GPU every frame as well, but this is a bigger, and easier, win for now.
This commit is contained in:
parent
bf6890b341
commit
3d71703950
|
@ -18,6 +18,7 @@ use pathfinder_geometry::vector::{Vector2I, vec2i};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(feature = "pf-image")]
|
#[cfg(feature = "pf-image")]
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
|
@ -42,7 +43,7 @@ pub enum PatternSource {
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
size: Vector2I,
|
size: Vector2I,
|
||||||
pixels: Vec<ColorU>,
|
pixels: Arc<Vec<ColorU>>,
|
||||||
pixels_hash: u64,
|
pixels_hash: u64,
|
||||||
is_opaque: bool,
|
is_opaque: bool,
|
||||||
}
|
}
|
||||||
|
@ -64,7 +65,7 @@ impl Pattern {
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(size: Vector2I, pixels: Vec<ColorU>) -> Image {
|
pub fn new(size: Vector2I, pixels: Arc<Vec<ColorU>>) -> Image {
|
||||||
assert_eq!(size.x() as usize * size.y() as usize, pixels.len());
|
assert_eq!(size.x() as usize * size.y() as usize, pixels.len());
|
||||||
let is_opaque = pixels.iter().all(|pixel| pixel.is_opaque());
|
let is_opaque = pixels.iter().all(|pixel| pixel.is_opaque());
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ impl Image {
|
||||||
pub fn from_image_buffer(image_buffer: RgbaImage) -> Image {
|
pub fn from_image_buffer(image_buffer: RgbaImage) -> Image {
|
||||||
let (width, height) = image_buffer.dimensions();
|
let (width, height) = image_buffer.dimensions();
|
||||||
let pixels = color::u8_vec_to_color_vec(image_buffer.into_raw());
|
let pixels = color::u8_vec_to_color_vec(image_buffer.into_raw());
|
||||||
Image::new(vec2i(width as i32, height as i32), pixels)
|
Image::new(vec2i(width as i32, height as i32), Arc::new(pixels))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -88,7 +89,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixels(&self) -> &[ColorU] {
|
pub fn pixels(&self) -> &Arc<Vec<ColorU>> {
|
||||||
&self.pixels
|
&self.pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ use pathfinder_geometry::rect::RectI;
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
use pathfinder_gpu::TextureSamplingFlags;
|
use pathfinder_gpu::TextureSamplingFlags;
|
||||||
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub enum RenderCommand {
|
pub enum RenderCommand {
|
||||||
|
@ -42,7 +43,7 @@ pub enum RenderCommand {
|
||||||
AllocateTexturePages(Vec<TexturePageDescriptor>),
|
AllocateTexturePages(Vec<TexturePageDescriptor>),
|
||||||
|
|
||||||
// Uploads data to a texture page.
|
// Uploads data to a texture page.
|
||||||
UploadTexelData { texels: Vec<ColorU>, location: TextureLocation },
|
UploadTexelData { texels: Arc<Vec<ColorU>>, location: TextureLocation },
|
||||||
|
|
||||||
// Associates a render target with a texture page.
|
// Associates a render target with a texture page.
|
||||||
//
|
//
|
||||||
|
|
|
@ -16,7 +16,7 @@ use hashbrown::HashMap;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::effects::{Effects, Filter};
|
use pathfinder_content::effects::{Effects, Filter};
|
||||||
use pathfinder_content::gradient::Gradient;
|
use pathfinder_content::gradient::Gradient;
|
||||||
use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource};
|
use pathfinder_content::pattern::{Pattern, PatternFlags, PatternSource};
|
||||||
use pathfinder_content::render_target::RenderTargetId;
|
use pathfinder_content::render_target::RenderTargetId;
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
|
@ -26,6 +26,7 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
|
||||||
use pathfinder_gpu::TextureSamplingFlags;
|
use pathfinder_gpu::TextureSamplingFlags;
|
||||||
use pathfinder_simd::default::{F32x2, F32x4};
|
use pathfinder_simd::default::{F32x2, F32x4};
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
// The size of a gradient tile.
|
// The size of a gradient tile.
|
||||||
//
|
//
|
||||||
|
@ -216,17 +217,18 @@ impl Palette {
|
||||||
let opacity_tile_builder = OpacityTileBuilder::new(&mut allocator);
|
let opacity_tile_builder = OpacityTileBuilder::new(&mut allocator);
|
||||||
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
||||||
let mut gradient_tile_builder = GradientTileBuilder::new();
|
let mut gradient_tile_builder = GradientTileBuilder::new();
|
||||||
|
let mut image_texel_info = vec![];
|
||||||
for paint in &self.paints {
|
for paint in &self.paints {
|
||||||
let (texture_location, mut sampling_flags, radial_gradient);
|
let (texture_location, mut sampling_flags, radial_gradient);
|
||||||
match paint {
|
match paint {
|
||||||
Paint::Color(_) => {
|
Paint::Color(color) => {
|
||||||
texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
texture_location = solid_color_tile_builder.allocate(&mut allocator, *color);
|
||||||
sampling_flags = TextureSamplingFlags::empty();
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
radial_gradient = None;
|
radial_gradient = None;
|
||||||
}
|
}
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
|
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
|
||||||
texture_location = gradient_tile_builder.allocate(&mut allocator);
|
texture_location = gradient_tile_builder.allocate(&mut allocator, gradient);
|
||||||
sampling_flags = TextureSamplingFlags::empty();
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
radial_gradient = gradient.radii().map(|radii| {
|
radial_gradient = gradient.radii().map(|radii| {
|
||||||
RadialGradientMetadata { line: gradient.line(), radii }
|
RadialGradientMetadata { line: gradient.line(), radii }
|
||||||
|
@ -243,6 +245,10 @@ impl Palette {
|
||||||
// inside the atlas in some cases.
|
// inside the atlas in some cases.
|
||||||
let allocation_mode = AllocationMode::OwnPage;
|
let allocation_mode = AllocationMode::OwnPage;
|
||||||
texture_location = allocator.allocate(image.size(), allocation_mode);
|
texture_location = allocator.allocate(image.size(), allocation_mode);
|
||||||
|
image_texel_info.push(ImageTexelInfo {
|
||||||
|
location: texture_location,
|
||||||
|
texels: (*image.pixels()).clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,39 +333,9 @@ impl Palette {
|
||||||
texture_page_descriptors.push(TexturePageDescriptor { size: page_size });
|
texture_page_descriptors.push(TexturePageDescriptor { size: page_size });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate the texels.
|
// Gather opacity tile metadata.
|
||||||
//
|
let opacity_tile_page = opacity_tile_builder.tile_location.page;
|
||||||
// TODO(pcwalton): This is slow. Do more on GPU.
|
let opacity_tile_transform = opacity_tile_builder.tile_transform(&allocator);
|
||||||
let mut page_texels: Vec<_> =
|
|
||||||
texture_page_descriptors.iter()
|
|
||||||
.map(|descriptor| Texels::new(descriptor.size))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Draw to texels.
|
|
||||||
//
|
|
||||||
// TODO(pcwalton): Do more of this on GPU.
|
|
||||||
opacity_tile_builder.render(&mut page_texels);
|
|
||||||
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter()) {
|
|
||||||
let texture_page = metadata.location.page;
|
|
||||||
let texels = &mut page_texels[texture_page.0 as usize];
|
|
||||||
|
|
||||||
match paint {
|
|
||||||
Paint::Color(color) => {
|
|
||||||
texels.put_texel(metadata.location.rect.origin(), *color);
|
|
||||||
}
|
|
||||||
Paint::Gradient(ref gradient) => {
|
|
||||||
self.render_gradient(gradient, metadata.location.rect, texels);
|
|
||||||
}
|
|
||||||
Paint::Pattern(ref pattern) => {
|
|
||||||
match pattern.source {
|
|
||||||
PatternSource::RenderTarget(_) => {}
|
|
||||||
PatternSource::Image(ref image) => {
|
|
||||||
self.render_image(image, metadata.location.rect, texels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create render commands.
|
// Create render commands.
|
||||||
let mut render_commands = vec![
|
let mut render_commands = vec![
|
||||||
|
@ -372,47 +348,22 @@ impl Palette {
|
||||||
location: metadata.location,
|
location: metadata.location,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (page_index, texels) in page_texels.into_iter().enumerate() {
|
solid_color_tile_builder.create_render_commands(&mut render_commands);
|
||||||
if let Some(texel_data) = texels.data {
|
gradient_tile_builder.create_render_commands(&mut render_commands);
|
||||||
let page_id = TexturePageId(page_index as u32);
|
opacity_tile_builder.create_render_commands(&mut render_commands);
|
||||||
let page_size = allocator.page_size(page_id);
|
for image_texel_info in image_texel_info {
|
||||||
let rect = RectI::new(Vector2I::default(), page_size);
|
|
||||||
render_commands.push(RenderCommand::UploadTexelData {
|
render_commands.push(RenderCommand::UploadTexelData {
|
||||||
texels: texel_data,
|
texels: image_texel_info.texels,
|
||||||
location: TextureLocation { page: page_id, rect },
|
location: image_texel_info.location,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
PaintInfo {
|
PaintInfo {
|
||||||
render_commands,
|
render_commands,
|
||||||
paint_metadata,
|
paint_metadata,
|
||||||
render_target_metadata,
|
render_target_metadata,
|
||||||
opacity_tile_page: opacity_tile_builder.tile_location.page,
|
opacity_tile_page,
|
||||||
opacity_tile_transform: opacity_tile_builder.tile_transform(&allocator),
|
opacity_tile_transform,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pcwalton): This is slow. Do on GPU instead.
|
|
||||||
fn render_gradient(&self, gradient: &Gradient, tex_rect: RectI, texels: &mut Texels) {
|
|
||||||
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
|
||||||
// TODO(pcwalton): Optimize this:
|
|
||||||
// 1. Calculate ∇t up front and use differencing in the inner loop.
|
|
||||||
// 2. Go four pixels at a time with SIMD.
|
|
||||||
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
|
|
||||||
let point = tex_rect.origin() + vec2i(x, 0);
|
|
||||||
let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32;
|
|
||||||
texels.put_texel(point, gradient.sample(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_image(&self, image: &Image, tex_rect: RectI, texels: &mut Texels) {
|
|
||||||
let image_size = image.size();
|
|
||||||
for y in 0..image_size.y() {
|
|
||||||
let dest_origin = tex_rect.origin() + vec2i(0, y);
|
|
||||||
let src_start_index = y as usize * image_size.x() as usize;
|
|
||||||
let src_end_index = src_start_index + image_size.x() as usize;
|
|
||||||
texels.blit_scanline(dest_origin, &image.pixels()[src_start_index..src_end_index]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,39 +393,6 @@ impl PaintMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Texels {
|
|
||||||
data: Option<Vec<ColorU>>,
|
|
||||||
size: Vector2I,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texels {
|
|
||||||
fn new(size: Vector2I) -> Texels {
|
|
||||||
Texels { data: None, size }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn texel_index(&self, position: Vector2I) -> usize {
|
|
||||||
position.y() as usize * self.size.x() as usize + position.x() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate_texels_if_necessary(&mut self) {
|
|
||||||
if self.data.is_none() {
|
|
||||||
let area = self.size.x() as usize * self.size.y() as usize;
|
|
||||||
self.data = Some(vec![ColorU::transparent_black(); area]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blit_scanline(&mut self, dest_origin: Vector2I, src: &[ColorU]) {
|
|
||||||
self.allocate_texels_if_necessary();
|
|
||||||
let start_index = self.texel_index(dest_origin);
|
|
||||||
let end_index = start_index + src.len();
|
|
||||||
self.data.as_mut().unwrap()[start_index..end_index].copy_from_slice(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put_texel(&mut self, position: Vector2I, color: ColorU) {
|
|
||||||
self.blit_scanline(position, &[color])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
||||||
rect.to_f32().scale_xy(texture_scale)
|
rect.to_f32().scale_xy(texture_scale)
|
||||||
}
|
}
|
||||||
|
@ -496,15 +414,12 @@ impl OpacityTileBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, page_texels: &mut [Texels]) {
|
fn create_texels(&self) -> Vec<ColorU> {
|
||||||
let texels = &mut page_texels[self.tile_location.page.0 as usize];
|
let mut texels = Vec::with_capacity(256);
|
||||||
for y in 0..16 {
|
for alpha in 0..=255 {
|
||||||
for x in 0..16 {
|
texels.push(ColorU::new(255, 255, 255, alpha));
|
||||||
let color = ColorU::new(0xff, 0xff, 0xff, y * 16 + x);
|
|
||||||
let coords = self.tile_location.rect.origin() + vec2i(x as i32, y as i32);
|
|
||||||
texels.put_texel(coords, color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
texels
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tile_transform(&self, allocator: &TextureAllocator) -> Transform2F {
|
fn tile_transform(&self, allocator: &TextureAllocator) -> Transform2F {
|
||||||
|
@ -513,93 +428,136 @@ impl OpacityTileBuilder {
|
||||||
let vector = rect_to_uv(self.tile_location.rect, texture_scale).origin();
|
let vector = rect_to_uv(self.tile_location.rect, texture_scale).origin();
|
||||||
Transform2F { matrix, vector }
|
Transform2F { matrix, vector }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_render_commands(self, render_commands: &mut Vec<RenderCommand>) {
|
||||||
|
render_commands.push(RenderCommand::UploadTexelData {
|
||||||
|
texels: Arc::new(self.create_texels()),
|
||||||
|
location: self.tile_location,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solid color allocation
|
// Solid color allocation
|
||||||
|
|
||||||
struct SolidColorTileBuilder(Option<SolidColorTileBuilderData>);
|
struct SolidColorTileBuilder {
|
||||||
|
tiles: Vec<SolidColorTile>,
|
||||||
|
}
|
||||||
|
|
||||||
struct SolidColorTileBuilderData {
|
struct SolidColorTile {
|
||||||
tile_location: TextureLocation,
|
texels: Vec<ColorU>,
|
||||||
|
location: TextureLocation,
|
||||||
next_index: u32,
|
next_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SolidColorTileBuilder {
|
impl SolidColorTileBuilder {
|
||||||
fn new() -> SolidColorTileBuilder {
|
fn new() -> SolidColorTileBuilder {
|
||||||
SolidColorTileBuilder(None)
|
SolidColorTileBuilder { tiles: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate(&mut self, allocator: &mut TextureAllocator) -> TextureLocation {
|
fn allocate(&mut self, allocator: &mut TextureAllocator, color: ColorU) -> TextureLocation {
|
||||||
if self.0.is_none() {
|
if self.tiles.is_empty() ||
|
||||||
// TODO(pcwalton): Handle allocation failure gracefully!
|
self.tiles.last().unwrap().next_index == MAX_SOLID_COLORS_PER_TILE {
|
||||||
self.0 = Some(SolidColorTileBuilderData {
|
let area = SOLID_COLOR_TILE_LENGTH as usize * SOLID_COLOR_TILE_LENGTH as usize;
|
||||||
tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32),
|
self.tiles.push(SolidColorTile {
|
||||||
|
texels: vec![ColorU::black(); area],
|
||||||
|
location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32),
|
||||||
AllocationMode::Atlas),
|
AllocationMode::Atlas),
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (location, tile_full);
|
let mut data = self.tiles.last_mut().unwrap();
|
||||||
{
|
|
||||||
let mut data = self.0.as_mut().unwrap();
|
|
||||||
let subtile_origin = vec2i((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32,
|
let subtile_origin = vec2i((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32,
|
||||||
(data.next_index / SOLID_COLOR_TILE_LENGTH) as i32);
|
(data.next_index / SOLID_COLOR_TILE_LENGTH) as i32);
|
||||||
location = TextureLocation {
|
|
||||||
page: data.tile_location.page,
|
|
||||||
rect: RectI::new(data.tile_location.rect.origin() + subtile_origin,
|
|
||||||
Vector2I::splat(1)),
|
|
||||||
};
|
|
||||||
data.next_index += 1;
|
data.next_index += 1;
|
||||||
tile_full = data.next_index == MAX_SOLID_COLORS_PER_TILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if tile_full {
|
let location = TextureLocation {
|
||||||
self.0 = None;
|
page: data.location.page,
|
||||||
}
|
rect: RectI::new(data.location.rect.origin() + subtile_origin, vec2i(1, 1)),
|
||||||
|
};
|
||||||
|
|
||||||
|
data.texels[subtile_origin.y() as usize * SOLID_COLOR_TILE_LENGTH as usize +
|
||||||
|
subtile_origin.x() as usize] = color;
|
||||||
|
|
||||||
location
|
location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_render_commands(self, render_commands: &mut Vec<RenderCommand>) {
|
||||||
|
for tile in self.tiles {
|
||||||
|
render_commands.push(RenderCommand::UploadTexelData {
|
||||||
|
texels: Arc::new(tile.texels),
|
||||||
|
location: tile.location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gradient allocation
|
// Gradient allocation
|
||||||
|
|
||||||
struct GradientTileBuilder(Option<GradientTileBuilderData>);
|
struct GradientTileBuilder {
|
||||||
|
tiles: Vec<GradientTile>,
|
||||||
|
}
|
||||||
|
|
||||||
struct GradientTileBuilderData {
|
struct GradientTile {
|
||||||
|
texels: Vec<ColorU>,
|
||||||
page: TexturePageId,
|
page: TexturePageId,
|
||||||
next_index: u32,
|
next_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GradientTileBuilder {
|
impl GradientTileBuilder {
|
||||||
fn new() -> GradientTileBuilder {
|
fn new() -> GradientTileBuilder {
|
||||||
GradientTileBuilder(None)
|
GradientTileBuilder { tiles: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate(&mut self, allocator: &mut TextureAllocator) -> TextureLocation {
|
fn allocate(&mut self, allocator: &mut TextureAllocator, gradient: &Gradient)
|
||||||
if self.0.is_none() {
|
-> TextureLocation {
|
||||||
|
if self.tiles.is_empty() ||
|
||||||
|
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
|
||||||
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
||||||
self.0 = Some(GradientTileBuilderData {
|
let area = size.x() as usize * size.y() as usize;
|
||||||
|
self.tiles.push(GradientTile {
|
||||||
|
texels: vec![ColorU::black(); area],
|
||||||
page: allocator.allocate(size, AllocationMode::OwnPage).page,
|
page: allocator.allocate(size, AllocationMode::OwnPage).page,
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let (location, tile_full);
|
let mut data = self.tiles.last_mut().unwrap();
|
||||||
{
|
let location = TextureLocation {
|
||||||
let mut data = self.0.as_mut().unwrap();
|
|
||||||
location = TextureLocation {
|
|
||||||
page: data.page,
|
page: data.page,
|
||||||
rect: RectI::new(vec2i(0, data.next_index as i32),
|
rect: RectI::new(vec2i(0, data.next_index as i32),
|
||||||
vec2i(GRADIENT_TILE_LENGTH as i32, 1)),
|
vec2i(GRADIENT_TILE_LENGTH as i32, 1)),
|
||||||
};
|
};
|
||||||
data.next_index += 1;
|
data.next_index += 1;
|
||||||
tile_full = data.next_index == GRADIENT_TILE_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
if tile_full {
|
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
||||||
self.0 = None;
|
// TODO(pcwalton): Optimize this:
|
||||||
|
// 1. Calculate ∇t up front and use differencing in the inner loop.
|
||||||
|
// 2. Go four pixels at a time with SIMD.
|
||||||
|
let first_address = location.rect.origin_y() as usize * GRADIENT_TILE_LENGTH as usize;
|
||||||
|
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
|
||||||
|
let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32;
|
||||||
|
data.texels[first_address + x as usize] = gradient.sample(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
location
|
location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_render_commands(self, render_commands: &mut Vec<RenderCommand>) {
|
||||||
|
for tile in self.tiles {
|
||||||
|
render_commands.push(RenderCommand::UploadTexelData {
|
||||||
|
texels: Arc::new(tile.texels),
|
||||||
|
location: TextureLocation {
|
||||||
|
rect: RectI::new(Vector2I::zero(),
|
||||||
|
Vector2I::splat(GRADIENT_TILE_LENGTH as i32)),
|
||||||
|
page: tile.page,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageTexelInfo {
|
||||||
|
location: TextureLocation,
|
||||||
|
texels: Arc<Vec<ColorU>>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue