Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiled image support #897

Merged
merged 6 commits into from Feb 24, 2017
Next

Implement image tiling.

  • Loading branch information
nical committed Feb 23, 2017
commit 9b6b40c22cbd72a3f3fc05e663c662b7c5d9fade
@@ -111,6 +111,7 @@ fn main() {
height: 100,
stride: None,
is_opaque: true,
offset: 0,
},
ImageData::new_blob_image(Vec::new()),
);
@@ -154,6 +155,7 @@ fn main() {
stride: None,
format: ImageFormat::A8,
is_opaque: true,
offset: 0,
},
ImageData::new(vec![0, 80, 180, 255])
);
@@ -1631,7 +1631,12 @@ impl Device {
None => width,
};

assert!(data.len() as u32 == bpp * row_length * height);
// Take the stride into account for all rows, except the last one.
let len = bpp * row_length * (height - 1)
+ width * bpp;

assert!(data.len() as u32 >= len);
let data = &data[0..len as usize];

if let Some(..) = stride {
gl::pixel_store_i(gl::UNPACK_ROW_LENGTH, row_length as gl::GLint);

Large diffs are not rendered by default.

@@ -28,7 +28,7 @@ use util::{self, pack_as_float, rect_from_points_f, subtract_rect, TransformedRe
use util::{RectHelpers, TransformedRectKind};
use webrender_traits::{as_scroll_parent_rect, BorderDetails, BorderDisplayItem, BorderSide, BorderStyle};
use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, device_length, DeviceIntPoint};
use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey};
use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey, TileOffset};
use webrender_traits::{FontRenderMode, GlyphOptions, ImageKey, ImageRendering, ItemRange};
use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
use webrender_traits::{RepeatMode, ScrollLayerId, ScrollLayerPixel, WebGLContextId, YuvColorSpace};
@@ -417,7 +417,8 @@ impl FrameBuilder {
&segment.tile_spacing,
Some(segment.sub_rect),
border.image_key,
ImageRendering::Auto);
ImageRendering::Auto,
None);
}
}
BorderDetails::Normal(ref border) => {
@@ -833,10 +834,12 @@ impl FrameBuilder {
tile_spacing: &LayerSize,
sub_rect: Option<TexelRect>,
image_key: ImageKey,
image_rendering: ImageRendering) {
image_rendering: ImageRendering,
tile: Option<TileOffset>) {
let prim_cpu = ImagePrimitiveCpu {
kind: ImagePrimitiveKind::Image(image_key,
image_rendering,
tile,
*tile_spacing),
color_texture_id: SourceTexture::Invalid,
resource_address: GpuStoreAddress(0),
@@ -1361,7 +1364,7 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {

if let Some(mask) = scroll_layer.clip_source.image_mask() {
// We don't add the image mask for resolution, because layer masks are resolved later.
self.resource_cache.request_image(mask.image, ImageRendering::Auto);
self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
}
}

@@ -387,6 +387,7 @@ pub enum TextureUpdateOp {
height: u32,
data: Arc<Vec<u8>>,
stride: Option<u32>,
offset: u32,
},
UpdateForExternalBuffer {
rect: DeviceUintRect,
@@ -20,7 +20,7 @@ use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
use webrender_traits::{DeviceRect, DevicePoint, DeviceSize};
use webrender_traits::{LayerRect, LayerSize, LayerPoint};
use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
use webrender_traits::{ExtendMode, GradientStop};
use webrender_traits::{ExtendMode, GradientStop, TileOffset};

pub const CLIP_DATA_GPU_SIZE: usize = 5;
pub const MASK_DATA_GPU_SIZE: usize = 1;
@@ -136,7 +136,7 @@ pub struct RectanglePrimitive {

#[derive(Debug)]
pub enum ImagePrimitiveKind {
Image(ImageKey, ImageRendering, LayerSize),
Image(ImageKey, ImageRendering, Option<TileOffset>, LayerSize),
WebGL(WebGLContextId),
}

@@ -838,7 +838,7 @@ impl PrimitiveStore {
clip_info: &MaskCacheInfo,
resource_cache: &ResourceCache) {
if let Some((ref mask, gpu_address)) = clip_info.image {
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto);
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
let mask_data = gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
mask_data[0] = GpuBlock32::from(ImageMaskData {
uv_rect: DeviceRect::new(cache_item.uv0,
@@ -899,7 +899,7 @@ impl PrimitiveStore {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];

let (texture_id, cache_item) = match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, _) => {
ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, _) => {
// Check if an external image that needs to be resolved
// by the render thread.
let image_properties = resource_cache.get_image_properties(image_key);
@@ -917,7 +917,7 @@ impl PrimitiveStore {
(SourceTexture::External(external_id), None)
}
None => {
let cache_item = resource_cache.get_cached_image(image_key, image_rendering);
let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
(cache_item.texture_id, Some(cache_item))
}
}
@@ -952,21 +952,21 @@ impl PrimitiveStore {
};

if image_cpu.y_texture_id == SourceTexture::Invalid {
let y_cache_item = resource_cache.get_cached_image(image_cpu.y_key, ImageRendering::Auto);
let y_cache_item = resource_cache.get_cached_image(image_cpu.y_key, ImageRendering::Auto, None);
image_cpu.y_texture_id = y_cache_item.texture_id;
image_gpu.y_uv0 = y_cache_item.uv0;
image_gpu.y_uv1 = y_cache_item.uv1;
}

if image_cpu.u_texture_id == SourceTexture::Invalid {
let u_cache_item = resource_cache.get_cached_image(image_cpu.u_key, ImageRendering::Auto);
let u_cache_item = resource_cache.get_cached_image(image_cpu.u_key, ImageRendering::Auto, None);
image_cpu.u_texture_id = u_cache_item.texture_id;
image_gpu.u_uv0 = u_cache_item.uv0;
image_gpu.u_uv1 = u_cache_item.uv1;
}

if image_cpu.v_texture_id == SourceTexture::Invalid {
let v_cache_item = resource_cache.get_cached_image(image_cpu.v_key, ImageRendering::Auto);
let v_cache_item = resource_cache.get_cached_image(image_cpu.v_key, ImageRendering::Auto, None);
image_cpu.v_texture_id = v_cache_item.texture_id;
image_gpu.v_uv0 = v_cache_item.uv0;
image_gpu.v_uv1 = v_cache_item.uv1;
@@ -1044,7 +1044,7 @@ impl PrimitiveStore {
device_pixel_ratio,
auxiliary_lists);
if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() {
resource_cache.request_image(mask.image, ImageRendering::Auto);
resource_cache.request_image(mask.image, ImageRendering::Auto, None);
prim_needs_resolve = true;
}
}
@@ -1169,8 +1169,8 @@ impl PrimitiveStore {

prim_needs_resolve = true;
match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => {
resource_cache.request_image(image_key, image_rendering);
ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, tile_spacing) => {
resource_cache.request_image(image_key, image_rendering, tile_offset);

// TODO(gw): This doesn't actually need to be calculated each frame.
// It's cheap enough that it's not worth introducing a cache for images
@@ -1188,9 +1188,9 @@ impl PrimitiveStore {
let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
prim_needs_resolve = true;

resource_cache.request_image(image_cpu.y_key, ImageRendering::Auto);
resource_cache.request_image(image_cpu.u_key, ImageRendering::Auto);
resource_cache.request_image(image_cpu.v_key, ImageRendering::Auto);
resource_cache.request_image(image_cpu.y_key, ImageRendering::Auto, None);
resource_cache.request_image(image_cpu.u_key, ImageRendering::Auto, None);
resource_cache.request_image(image_cpu.v_key, ImageRendering::Auto, None);

// TODO(nical): Currently assuming no tile_spacing for yuv images.
metadata.is_opaque = true;
@@ -407,7 +407,7 @@ impl RenderBackend {
webgl_context.unbind();
}

self.frame.create(&self.scene);
self.frame.create(&self.scene, &mut self.resource_cache);
}

fn render(&mut self) -> RendererFrame {
@@ -702,6 +702,7 @@ impl Renderer {
width: 2,
height: 2,
stride: None,
offset: 0,
format: ImageFormat::RGBA8,
is_opaque: false,
},
@@ -714,6 +715,7 @@ impl Renderer {
width: 2,
height: 2,
stride: None,
offset: 0,
format: ImageFormat::A8,
is_opaque: false,
},
@@ -1123,13 +1125,13 @@ impl Renderer {
filter,
mode);
}
TextureUpdateOp::Update { page_pos_x, page_pos_y, width, height, data, stride } => {
TextureUpdateOp::Update { page_pos_x, page_pos_y, width, height, data, stride, offset } => {
let texture_id = self.cache_texture_id_map[update.id.0];
self.device.update_texture(texture_id,
page_pos_x,
page_pos_y,
width, height, stride,
data.as_slice());
&data[offset as usize..]);
}
TextureUpdateOp::UpdateForExternalBuffer { rect, id, stride } => {
let handler = self.external_image_handler
@@ -23,7 +23,7 @@ use thread_profiler::register_thread_with_profiler;
use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
use webrender_traits::{DevicePoint, DeviceIntSize, ImageDescriptor, ColorF};
use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance};
use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance, TileOffset};
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError};
use threadpool::ThreadPool;
use euclid::Point2D;
@@ -92,6 +92,7 @@ impl RenderedGlyphKey {
pub struct ImageProperties {
pub descriptor: ImageDescriptor,
pub external_id: Option<ExternalImageId>,
pub tiling: Option<u16>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
@@ -105,6 +106,7 @@ struct ImageResource {
data: ImageData,
descriptor: ImageDescriptor,
epoch: Epoch,
tiling: Option<u16>,
}

struct CachedImageInfo {
@@ -177,6 +179,7 @@ impl<K,V> ResourceClassCache<K,V> where K: Clone + Hash + Eq + Debug, V: Resourc
struct ImageRequest {
key: ImageKey,
rendering: ImageRendering,
tile: Option<TileOffset>,
}

struct GlyphRasterJob {
@@ -270,6 +273,7 @@ impl ResourceCache {
descriptor: descriptor,
data: data,
epoch: Epoch(0),
tiling: None,
};

self.image_templates.insert(image_key, resource);
@@ -301,6 +305,7 @@ impl ResourceCache {
descriptor: descriptor,
data: ImageData::new(bytes),
epoch: next_epoch,
tiling: None,
};

self.image_templates.insert(image_key, resource);
@@ -339,11 +344,16 @@ impl ResourceCache {
webgl_texture.size = size;
}

pub fn request_image(&mut self, key: ImageKey, rendering: ImageRendering) {
pub fn request_image(&mut self,
key: ImageKey,
rendering: ImageRendering,
tile: Option<TileOffset>) {

debug_assert!(self.state == State::AddResources);
let request = ImageRequest {
key: key,
rendering: rendering,
tile: tile,
};

let template = self.image_templates.get(&key).unwrap();
@@ -472,11 +482,13 @@ impl ResourceCache {
#[inline]
pub fn get_cached_image(&self,
image_key: ImageKey,
image_rendering: ImageRendering) -> CacheItem {
image_rendering: ImageRendering,
tile: Option<TileOffset>) -> CacheItem {
debug_assert!(self.state == State::QueryResources);
let key = ImageRequest {
key: image_key,
rendering: image_rendering,
tile: tile,
};
let image_info = &self.cached_images.get(&key, self.current_frame_id);
let item = self.texture_cache.get(image_info.texture_cache_id);
@@ -501,6 +513,7 @@ impl ResourceCache {
ImageProperties {
descriptor: image_template.descriptor,
external_id: external_id,
tiling: image_template.tiling,
}
}

@@ -563,6 +576,7 @@ impl ResourceCache {
stride: None,
format: ImageFormat::RGBA8,
is_opaque: false,
offset: 0,
},
TextureFilter::Linear,
ImageData::Raw(Arc::new(glyph.bytes)));
@@ -650,10 +664,48 @@ impl ResourceCache {
ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
};

self.texture_cache.insert(image_id,
image_template.descriptor,
filter,
image_data);
if let Some(tile) = request.tile {
let tile_size = image_template.tiling.unwrap() as u32;
let image_descriptor = image_template.descriptor.clone();
let stride = image_descriptor.compute_stride();
let bpp = image_descriptor.format.bytes_per_pixel().unwrap();

// Storage for the tiles on the right and bottom edges is shrunk to
// fit the image data (See decompose_tiled_image in frame.rs).
let actual_width = if (tile.x as u32) < image_descriptor.width / tile_size {
tile_size
} else {
image_descriptor.width % tile_size
};

let actual_height = if (tile.y as u32) < image_descriptor.height / tile_size {
tile_size
} else {
image_descriptor.height % tile_size
};

let offset = image_descriptor.offset + tile.y as u32 * tile_size * stride
+ tile.x as u32 * tile_size * bpp;

let tile_descriptor = ImageDescriptor {
width: actual_width,
height: actual_height,
stride: Some(stride),
offset: offset,
format: image_descriptor.format,
is_opaque: image_descriptor.is_opaque,
};

self.texture_cache.insert(image_id,
tile_descriptor,
filter,
image_data);
} else {
self.texture_cache.insert(image_id,
image_template.descriptor,
filter,
image_data);
}

entry.insert(CachedImageInfo {
texture_cache_id: image_id,
@@ -731,6 +731,7 @@ impl TextureCache {
height: descriptor.height,
data: bytes,
stride: descriptor.stride,
offset: descriptor.offset,
}
}
};
@@ -782,6 +783,7 @@ impl TextureCache {
height: result.item.allocated_rect.size.height,
data: bytes,
stride: stride,
offset: descriptor.offset,
},
};

@@ -832,7 +832,7 @@ impl ClipBatcher {
}

if let Some((ref mask, address)) = info.image {
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto);
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
self.images.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(CacheClipInstance {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.