From f417f584b2393410b9f3b419c19a3bc7ba0bee0a Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 8 Nov 2016 16:57:18 +0100 Subject: [PATCH 1/2] External textures work. --- Cargo.lock | 1 + webrender/src/frame.rs | 9 ++++ webrender/src/internal_types.rs | 41 +++++++++++++++--- webrender/src/prim_store.rs | 75 +++++++++++++++++++-------------- webrender/src/renderer.rs | 18 ++++++-- webrender/src/tiling.rs | 59 ++++++++++++++++++++------ webrender_traits/src/types.rs | 26 ++++++++++++ 7 files changed, 176 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bae9bde2e0..22abb26420 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,6 +770,7 @@ dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 24f25a2925..b42f01ead9 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -595,6 +595,15 @@ impl Frame { info.image_key, info.image_rendering); } + SpecificDisplayItem::ExternalImage(ref info) => { + builder.add_external_image(item.rect, + &item.clip, + &info.stretch_size, + &info.tile_spacing, + info.image_key, + info.uv_rect, + info.image_rendering); + } SpecificDisplayItem::Text(ref text_info) => { builder.add_text(item.rect, &item.clip, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 1a606d535a..7fe06c56a3 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; use std::sync::Arc; use tiling; use webrender_traits::{Epoch, ColorF, PipelineId}; -use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem}; +use webrender_traits::{ExternalImageKey, ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem}; use webrender_traits::{ScrollLayerId, WebGLCommand}; pub enum GLContextHandleWrapper { @@ -193,19 +193,48 @@ impl TextureSampler { } } +/// A reference to a texture, either an id assigned by the render backend or an +/// indirect key resolved to an id later by the renderer. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SourceTexture { + Id(TextureId), + External(ExternalImageKey), + // TODO(nical): should this have a None variant to better separate the cases + // where the batch does not use all its texture slots and cases where a slot + // will be used but the texture hasn't been assigned yet? +} + +impl SourceTexture { + pub fn invalid() -> SourceTexture { SourceTexture::Id(TextureId::invalid()) } + + pub fn is_valid(&self) -> bool { + match *self { + SourceTexture::Id(id) => { id.is_valid() } + SourceTexture::External(_) => { true } + } + } + + pub fn is_external(&self) -> bool { + match *self { + SourceTexture::External(_) => { true } + SourceTexture::Id(_) => { false } + } + } +} + /// Optional textures that can be used as a source in the shaders. -/// Textures that are not used by the batch are equal to TextureId::invalid(). +/// Textures that are not used by the batch are equal to SourceTexture::invalid(). #[derive(Copy, Clone, Debug)] pub struct BatchTextures { - pub colors: [TextureId; 3], - pub mask: TextureId, + pub colors: [SourceTexture; 3], + pub mask: SourceTexture, } impl BatchTextures { pub fn no_texture() -> Self { BatchTextures { - colors: [TextureId::invalid(); 3], - mask: TextureId::invalid(), + colors: [SourceTexture::invalid(); 3], + mask: SourceTexture::invalid(), } } } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index f05eef539c..a00611ace7 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -6,7 +6,7 @@ use app_units::Au; use device::TextureId; use euclid::{Point2D, Matrix4D, Rect, Size2D}; use gpu_store::{GpuStore, GpuStoreAddress}; -use internal_types::{device_pixel, DeviceRect, DeviceSize}; +use internal_types::{SourceTexture, device_pixel, DeviceRect, DeviceSize}; use resource_cache::ResourceCache; use std::mem; use std::usize; @@ -90,13 +90,14 @@ pub struct RectanglePrimitive { #[derive(Debug)] pub enum ImagePrimitiveKind { Image(ImageKey, ImageRendering, Size2D), + ExternalImage(ImageRendering, Size2D), WebGL(WebGLContextId), } #[derive(Debug)] pub struct ImagePrimitiveCpu { pub kind: ImagePrimitiveKind, - pub color_texture_id: TextureId, + pub color_texture_id: SourceTexture, } #[derive(Debug, Clone)] @@ -610,22 +611,31 @@ impl PrimitiveStore { } PrimitiveKind::Image => { let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0]; - let image_gpu: &mut ImagePrimitiveGpu = unsafe { - mem::transmute(self.gpu_data32.get_mut(metadata.gpu_prim_index)) - }; - - let cache_item = match image_cpu.kind { - ImagePrimitiveKind::Image(image_key, image_rendering, _) => { - resource_cache.get_image(image_key, image_rendering) - } - ImagePrimitiveKind::WebGL(context_id) => { - resource_cache.get_webgl_texture(&context_id) - } - }; + // If the image primitive uses externally allocated textures, it + // is already resolved and doesn't not use the resource cache. + // TODO(nical): choose whether to use the source texture or the kind + // to differentiate between external and regular images, and stick to + // it more concistently. + if !image_cpu.color_texture_id.is_external() { + let image_gpu: &mut ImagePrimitiveGpu = unsafe { + mem::transmute(self.gpu_data32.get_mut(metadata.gpu_prim_index)) + }; + let cache_item = match image_cpu.kind { + ImagePrimitiveKind::Image(image_key, image_rendering, _) => { + resource_cache.get_image(image_key, image_rendering) + } + ImagePrimitiveKind::WebGL(context_id) => { + resource_cache.get_webgl_texture(&context_id) + } + ImagePrimitiveKind::ExternalImage(..) => { + panic!("image_cpu.color_texture_id should have been external"); + } + }; - image_cpu.color_texture_id = cache_item.texture_id; - image_gpu.uv0 = cache_item.uv0; - image_gpu.uv1 = cache_item.uv1; + image_cpu.color_texture_id = SourceTexture::Id(cache_item.texture_id); + image_gpu.uv0 = cache_item.uv0; + image_gpu.uv1 = cache_item.uv1; + } } } } @@ -825,21 +835,24 @@ impl PrimitiveStore { PrimitiveKind::Image => { let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0]; - prim_needs_resolve = true; - match image_cpu.kind { - ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => { - resource_cache.request_image(image_key, image_rendering); - - // 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 - // right now, but if we introduce a cache for images for some other - // reason then we might as well cache this with it. - let image_properties = resource_cache.get_image_properties(image_key); - metadata.is_opaque = image_properties.is_opaque && - tile_spacing.width == 0.0 && - tile_spacing.height == 0.0; + if !image_cpu.color_texture_id.is_external() { + prim_needs_resolve = true; + match image_cpu.kind { + ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => { + resource_cache.request_image(image_key, image_rendering); + + // 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 + // right now, but if we introduce a cache for images for some other + // reason then we might as well cache this with it. + let image_properties = resource_cache.get_image_properties(image_key); + metadata.is_opaque = image_properties.is_opaque && + tile_spacing.width == 0.0 && + tile_spacing.height == 0.0; + } + ImagePrimitiveKind::ExternalImage(..) => {} + ImagePrimitiveKind::WebGL(..) => {} } - ImagePrimitiveKind::WebGL(..) => {} } } PrimitiveKind::Gradient => { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 2c70f4d544..133caaf5b7 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -18,7 +18,7 @@ use fnv::FnvHasher; use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp}; use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode}; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePoint}; -use internal_types::{BatchTextures, TextureSampler, GLContextHandleWrapper}; +use internal_types::{SourceTexture, BatchTextures, TextureSampler, GLContextHandleWrapper}; use ipc_channel::ipc; use profiler::{Profiler, BackendProfileCounters}; use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters}; @@ -951,9 +951,11 @@ impl Renderer { self.device.bind_vao(self.quad_vao_id); for i in 0..textures.colors.len() { - self.device.bind_texture(TextureSampler::color(i), textures.colors[i]); + let color_id = self.resolve_texture_id(textures.colors[i]); + self.device.bind_texture(TextureSampler::color(i), color_id); } - self.device.bind_texture(TextureSampler::Mask, textures.mask); + let mask_id = self.resolve_texture_id(textures.mask); + self.device.bind_texture(TextureSampler::Mask, mask_id); for chunk in ubo_data.chunks(max_prim_items) { let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA); @@ -967,6 +969,16 @@ impl Renderer { } } + fn resolve_texture_id(&self, texture: SourceTexture) -> TextureId { + match texture { + SourceTexture::Id(id) => { id } + SourceTexture::External(_key) => { + //TODO[nical] + unimplemented!(); + } + } + } + fn draw_target(&mut self, render_target: Option<(TextureId, i32)>, target: &RenderTarget, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index a44554712f..19853010bf 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -11,7 +11,7 @@ use frame::FrameId; use gpu_store::GpuStoreAddress; use internal_types::{DeviceRect, DevicePoint, DeviceSize, DeviceLength, device_pixel, CompositionOp}; use internal_types::{ANGLE_FLOAT_TO_FIXED, LowLevelFilterOp}; -use internal_types::{BatchTextures}; +use internal_types::{SourceTexture, BatchTextures}; use layer::Layer; use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer}; use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu}; @@ -34,7 +34,7 @@ use std::usize; use texture_cache::TexturePage; use util::{self, rect_from_points, MatrixHelpers, rect_from_points_f}; use util::{TransformedRect, TransformedRectKind, subtract_rect, pack_as_float}; -use webrender_traits::{ColorF, FontKey, ImageKey, ImageRendering, MixBlendMode}; +use webrender_traits::{ColorF, FontKey, ImageKey, ExternalImageKey, ImageRendering, MixBlendMode}; use webrender_traits::{BorderDisplayItem, BorderSide, BorderStyle}; use webrender_traits::{AuxiliaryLists, ItemRange, BoxShadowClipMode, ClipRegion}; use webrender_traits::{PipelineId, ScrollLayerId, WebGLContextId, FontRenderMode}; @@ -50,7 +50,7 @@ pub type AuxiliaryListsMap = HashMap AlphaBatchKind; - fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [TextureId; 3]; + fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3]; fn get_blend_mode(&self, needs_blending: bool, metadata: &PrimitiveMetadata) -> BlendMode; fn prim_affects_tile(&self, prim_index: PrimitiveIndex, @@ -100,8 +100,8 @@ impl AlphaBatchHelpers for PrimitiveStore { batch_kind } - fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [TextureId; 3] { - let invalid = TextureId::invalid(); + fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3] { + let invalid = SourceTexture::invalid(); match metadata.prim_kind { PrimitiveKind::Border | PrimitiveKind::BoxShadow | @@ -113,7 +113,7 @@ impl AlphaBatchHelpers for PrimitiveStore { } PrimitiveKind::TextRun => { let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0]; - [text_run_cpu.color_texture_id, invalid, invalid] + [SourceTexture::Id(text_run_cpu.color_texture_id), invalid, invalid] } // TODO(nical): YuvImage will return 3 textures. } @@ -494,7 +494,7 @@ impl AlphaBatcher { let textures = BatchTextures { colors: ctx.prim_store.get_color_textures(prim_metadata), - mask: prim_metadata.mask_texture_id, + mask: SourceTexture::Id(prim_metadata.mask_texture_id), }; batch_key = AlphaBatchKey::primitive(batch_kind, @@ -673,11 +673,11 @@ impl RenderTarget { // we switch the texture atlas to use texture layers! let textures = BatchTextures { colors: ctx.prim_store.get_color_textures(prim_metadata), - mask: prim_metadata.mask_texture_id, + mask: SourceTexture::Id(prim_metadata.mask_texture_id), }; - debug_assert!(textures.colors[0] != TextureId::invalid()); - debug_assert!(self.text_run_textures.colors[0] == TextureId::invalid() || + debug_assert!(textures.colors[0] != SourceTexture::invalid()); + debug_assert!(self.text_run_textures.colors[0] == SourceTexture::invalid() || self.text_run_textures.colors[0] == textures.colors[0]); self.text_run_textures = textures; @@ -1117,7 +1117,7 @@ pub enum BlurDirection { } #[inline] -fn textures_compatible(t1: TextureId, t2: TextureId) -> bool { +fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool { !t1.is_valid() || !t2.is_valid() || t1 == t2 } @@ -1995,7 +1995,7 @@ impl FrameBuilder { context_id: WebGLContextId) { let prim_cpu = ImagePrimitiveCpu { kind: ImagePrimitiveKind::WebGL(context_id), - color_texture_id: TextureId::invalid(), + color_texture_id: SourceTexture::invalid(), }; let prim_gpu = ImagePrimitiveGpu { @@ -2021,7 +2021,7 @@ impl FrameBuilder { kind: ImagePrimitiveKind::Image(image_key, image_rendering, *tile_spacing), - color_texture_id: TextureId::invalid(), + color_texture_id: SourceTexture::invalid(), }; let prim_gpu = ImagePrimitiveGpu { @@ -2036,6 +2036,39 @@ impl FrameBuilder { PrimitiveContainer::Image(prim_cpu, prim_gpu)); } + pub fn add_external_image(&mut self, + rect: Rect, + clip_region: &ClipRegion, + stretch_size: &Size2D, + tile_spacing: &Size2D, + key: ExternalImageKey, + uv_rect: Rect, + image_rendering: ImageRendering) { + // The external image primitve works like a regular image where + // most of the information is already resolved, except the texture + // which is resolved later in the renderer instead of using the resource + // cache. + + let prim_cpu = ImagePrimitiveCpu { + kind: ImagePrimitiveKind::ExternalImage(image_rendering, + *tile_spacing), + color_texture_id: SourceTexture::External(key), + }; + + // TODO(nical): check that we don't need to invert y or whatnot, and look into + // where we are supposed to compute the tiled texture coordinates. + let prim_gpu = ImagePrimitiveGpu { + uv0: uv_rect.origin, + uv1: uv_rect.bottom_right(), + stretch_size: *stretch_size, + tile_spacing: *tile_spacing, + }; + + self.add_primitive(&rect, + clip_region, + PrimitiveContainer::Image(prim_cpu, prim_gpu)); + } + /// Compute the contribution (bounding rectangles, and resources) of layers and their /// primitives in screen space. fn cull_layers(&mut self, diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index 1c330982fc..01176a019e 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -319,6 +319,28 @@ pub struct ImageDisplayItem { pub image_rendering: ImageRendering, } +// TODO(nical): The only differences with the image item is that we provide a +// different type of key but more importantly we provide the uv rectangle. +// It would look nicer if the ImageDisplayItem was doing both external and +// regular images, but it may not make sense to have an uv_rect specified for +// regular images. +// The reason I think we should have uv_rect for external images is that I would +// like gecko to pack several of the externally rendered items (for example all +// native widgets like buttons, check boxes and the like) in the same texture, and +// in some cases we just don't have a choice because the GPU will round up the +// texture size to 32 if the image is smaller. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct ExternalImageDisplayItem { + pub image_key: ExternalImageKey, + pub stretch_size: Size2D, + pub tile_spacing: Size2D, + // TODO(nical) Decide what unit to use! + // Should it be in texels or [0..1] texture space? + // I'd say in texels... + pub uv_rect: Rect, + pub image_rendering: ImageRendering, +} + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageFormat { Invalid, @@ -331,6 +353,9 @@ pub enum ImageFormat { #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ImageKey(u32, u32); +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ExternalImageKey(u32, u32); + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageRendering { Auto, @@ -444,6 +469,7 @@ pub enum SpecificDisplayItem { Rectangle(RectangleDisplayItem), Text(TextDisplayItem), Image(ImageDisplayItem), + ExternalImage(ExternalImageDisplayItem), WebGL(WebGLDisplayItem), Border(BorderDisplayItem), BoxShadow(BoxShadowDisplayItem), From 35cfe1b4c8bad0db42cb95bc43e6dcbf47d69136 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 10 Nov 2016 18:04:24 +0100 Subject: [PATCH 2/2] Resolve external image UVs in the renderer instead of the render backend. --- webrender/src/frame.rs | 9 ------ webrender/src/internal_types.rs | 7 +++++ webrender/src/prim_store.rs | 22 ++++++------- webrender/src/renderer.rs | 17 ++++++++++ webrender/src/tiling.rs | 47 ++++++++-------------------- webrender_traits/src/api.rs | 13 ++++++-- webrender_traits/src/display_item.rs | 20 ++++++++++-- webrender_traits/src/types.rs | 29 +++-------------- 8 files changed, 78 insertions(+), 86 deletions(-) diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index b42f01ead9..24f25a2925 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -595,15 +595,6 @@ impl Frame { info.image_key, info.image_rendering); } - SpecificDisplayItem::ExternalImage(ref info) => { - builder.add_external_image(item.rect, - &item.clip, - &info.stretch_size, - &info.tile_spacing, - info.image_key, - info.uv_rect, - info.image_rendering); - } SpecificDisplayItem::Text(ref text_info) => { builder.add_text(item.rect, &item.clip, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 7fe06c56a3..f462d97273 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -220,6 +220,13 @@ impl SourceTexture { SourceTexture::Id(_) => { false } } } + + pub fn to_external(&self) -> Option { + match *self { + SourceTexture::External(key) => Some(key), + SourceTexture::Id(_) => None, + } + } } /// Optional textures that can be used as a source in the shaders. diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index a00611ace7..7c90702429 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -13,7 +13,7 @@ use std::usize; use texture_cache::TextureCacheItem; use tiling::RenderTask; use util::TransformedRect; -use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering}; +use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ExternalImageKey, ImageRendering}; use webrender_traits::{FontRenderMode, WebGLContextId}; use webrender_traits::{ClipRegion, FontKey, ItemRange, ComplexClipRegion, GlyphKey}; @@ -90,7 +90,6 @@ pub struct RectanglePrimitive { #[derive(Debug)] pub enum ImagePrimitiveKind { Image(ImageKey, ImageRendering, Size2D), - ExternalImage(ImageRendering, Size2D), WebGL(WebGLContextId), } @@ -347,6 +346,9 @@ pub struct PrimitiveStore { pub cpu_gradients: Vec, pub cpu_metadata: Vec, pub cpu_borders: Vec, + // the indices witihin gpu_data_32 of the image primitives to be resolved by + // the renderer instead of the render backend. + pub deferred_image_primitives: Vec<(ExternalImageKey, GpuStoreAddress)>, // Gets uploaded directly to GPU via vertex texture pub gpu_geometry: GpuStore, @@ -376,6 +378,7 @@ impl PrimitiveStore { gpu_data128: GpuStore::new(), device_pixel_ratio: device_pixel_ratio, prims_to_resolve: Vec::new(), + deferred_image_primitives: Vec::new(), } } @@ -611,12 +614,11 @@ impl PrimitiveStore { } PrimitiveKind::Image => { let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0]; - // If the image primitive uses externally allocated textures, it - // is already resolved and doesn't not use the resource cache. - // TODO(nical): choose whether to use the source texture or the kind - // to differentiate between external and regular images, and stick to - // it more concistently. - if !image_cpu.color_texture_id.is_external() { + if let Some(key) = image_cpu.color_texture_id.to_external() { + // If the image primitive uses externally allocated textures, + // we resolve it on the renderer. + self.deferred_image_primitives.push((key, metadata.gpu_prim_index)); + } else { let image_gpu: &mut ImagePrimitiveGpu = unsafe { mem::transmute(self.gpu_data32.get_mut(metadata.gpu_prim_index)) }; @@ -627,9 +629,6 @@ impl PrimitiveStore { ImagePrimitiveKind::WebGL(context_id) => { resource_cache.get_webgl_texture(&context_id) } - ImagePrimitiveKind::ExternalImage(..) => { - panic!("image_cpu.color_texture_id should have been external"); - } }; image_cpu.color_texture_id = SourceTexture::Id(cache_item.texture_id); @@ -850,7 +849,6 @@ impl PrimitiveStore { tile_spacing.width == 0.0 && tile_spacing.height == 0.0; } - ImagePrimitiveKind::ExternalImage(..) => {} ImagePrimitiveKind::WebGL(..) => {} } } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 133caaf5b7..3456305da8 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -20,6 +20,7 @@ use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, Rend use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePoint}; use internal_types::{SourceTexture, BatchTextures, TextureSampler, GLContextHandleWrapper}; use ipc_channel::ipc; +use prim_store::{ImagePrimitiveGpu}; use profiler::{Profiler, BackendProfileCounters}; use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters}; use render_backend::RenderBackend; @@ -979,6 +980,20 @@ impl Renderer { } } + fn resolve_external_image_data(&self, frame: &mut Frame) { + for &(key, prim_index) in &frame.deferred_image_primitives[..] { + let image_gpu: &mut ImagePrimitiveGpu = unsafe { + mem::transmute(frame.gpu_data32.get_mut(prim_index.0 as usize)) + }; + + // TODO: fetch the external UV and and texture id using a callback API + // or some such, and patch up the image_gpu. + + // image_gpu.uv0 = + // image_gpu.uv1 = + } + } + fn draw_target(&mut self, render_target: Option<(TextureId, i32)>, target: &RenderTarget, @@ -1297,6 +1312,8 @@ impl Renderer { None); } + self.resolve_external_image_data(frame); + self.layer_texture.init(&mut self.device, &mut frame.layer_texture_data); self.render_task_texture.init(&mut self.device, &mut frame.render_task_data); self.data16_texture.init(&mut self.device, &mut frame.gpu_data16); diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 19853010bf..ac10c7dab8 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -1419,6 +1419,7 @@ pub struct Frame { pub gpu_data64: Vec, pub gpu_data128: Vec, pub gpu_geometry: Vec, + pub deferred_image_primitives: Vec<(ExternalImageKey, GpuStoreAddress)>, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -2017,11 +2018,21 @@ impl FrameBuilder { tile_spacing: &Size2D, image_key: ImageKey, image_rendering: ImageRendering) { + + // The external image primitve works like a regular image except that + // its information is resolved later on the render thread rather than + // using the resource cache. + let texture_id = if image_key.is_external() { + SourceTexture::External(image_key) + } else { + SourceTexture::invalid() + }; + let prim_cpu = ImagePrimitiveCpu { kind: ImagePrimitiveKind::Image(image_key, image_rendering, *tile_spacing), - color_texture_id: SourceTexture::invalid(), + color_texture_id: texture_id, }; let prim_gpu = ImagePrimitiveGpu { @@ -2036,39 +2047,6 @@ impl FrameBuilder { PrimitiveContainer::Image(prim_cpu, prim_gpu)); } - pub fn add_external_image(&mut self, - rect: Rect, - clip_region: &ClipRegion, - stretch_size: &Size2D, - tile_spacing: &Size2D, - key: ExternalImageKey, - uv_rect: Rect, - image_rendering: ImageRendering) { - // The external image primitve works like a regular image where - // most of the information is already resolved, except the texture - // which is resolved later in the renderer instead of using the resource - // cache. - - let prim_cpu = ImagePrimitiveCpu { - kind: ImagePrimitiveKind::ExternalImage(image_rendering, - *tile_spacing), - color_texture_id: SourceTexture::External(key), - }; - - // TODO(nical): check that we don't need to invert y or whatnot, and look into - // where we are supposed to compute the tiled texture coordinates. - let prim_gpu = ImagePrimitiveGpu { - uv0: uv_rect.origin, - uv1: uv_rect.bottom_right(), - stretch_size: *stretch_size, - tile_spacing: *tile_spacing, - }; - - self.add_primitive(&rect, - clip_region, - PrimitiveContainer::Image(prim_cpu, prim_gpu)); - } - /// Compute the contribution (bounding rectangles, and resources) of layers and their /// primitives in screen space. fn cull_layers(&mut self, @@ -2491,6 +2469,7 @@ impl FrameBuilder { gpu_data64: self.prim_store.gpu_data64.build(), gpu_data128: self.prim_store.gpu_data128.build(), gpu_geometry: self.prim_store.gpu_geometry.build(), + deferred_image_primitives: mem::replace(&mut self.prim_store.deferred_image_primitives, Vec::new()), } } } diff --git a/webrender_traits/src/api.rs b/webrender_traits/src/api.rs index 306f79c328..2ee493027b 100644 --- a/webrender_traits/src/api.rs +++ b/webrender_traits/src/api.rs @@ -8,7 +8,7 @@ use ipc_channel::ipc::{self, IpcBytesSender, IpcSender}; use offscreen_gl_context::{GLContextAttributes, GLLimits}; use std::cell::Cell; use {ApiMsg, AuxiliaryLists, BuiltDisplayList, ColorF, DisplayListId, Epoch}; -use {FontKey, IdNamespace, ImageFormat, ImageKey, NativeFontHandle, PipelineId}; +use {FontKey, IdNamespace, ImageFormat, ImageKey, ExternalImageKey, NativeFontHandle, PipelineId}; use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState}; use {StackingContext, StackingContextId, WebGLContextId, WebGLCommand}; use {GlyphKey, GlyphDimensions}; @@ -87,6 +87,12 @@ impl RenderApi { ImageKey::new(new_id.0, new_id.1) } + /// Creates an `ExternalImageKey`. + pub fn alloc_external_image(&self) -> ExternalImageKey { + let new_id = self.next_unique_id(); + ImageKey::new_external(new_id.0, new_id.1) + } + /// Adds an image and returns the corresponding `ImageKey`. pub fn add_image(&self, width: u32, @@ -94,8 +100,7 @@ impl RenderApi { stride: Option, format: ImageFormat, bytes: Vec) -> ImageKey { - let new_id = self.next_unique_id(); - let key = ImageKey::new(new_id.0, new_id.1); + let key = self.alloc_image(); let msg = ApiMsg::AddImage(key, width, height, stride, format, bytes); self.api_sender.send(msg).unwrap(); key @@ -111,12 +116,14 @@ impl RenderApi { height: u32, format: ImageFormat, bytes: Vec) { + assert!(!key.is_external()); let msg = ApiMsg::UpdateImage(key, width, height, format, bytes); self.api_sender.send(msg).unwrap(); } /// Deletes the specific image. pub fn delete_image(&self, key: ImageKey) { + assert!(!key.is_external()); let msg = ApiMsg::DeleteImage(key); self.api_sender.send(msg).unwrap(); } diff --git a/webrender_traits/src/display_item.rs b/webrender_traits/src/display_item.rs index e0873fa60f..fa2a53fdea 100644 --- a/webrender_traits/src/display_item.rs +++ b/webrender_traits/src/display_item.rs @@ -5,7 +5,7 @@ use display_list::AuxiliaryListsBuilder; use euclid::{Rect, Size2D}; use {BorderRadius, BorderDisplayItem, ClipRegion, ColorF, ComplexClipRegion}; -use {FontKey, ImageKey, PipelineId, ScrollLayerId, ScrollLayerInfo, ServoScrollRootId}; +use {FontKey, ImageKey, ExternalImageKey, PipelineId, ScrollLayerId, ScrollLayerInfo, ServoScrollRootId}; use {ImageMask, ItemRange}; impl BorderDisplayItem { @@ -107,9 +107,23 @@ impl FontKey { } } +// hijack the last highest bit of the namespace to store whether or not the key +// is external. +const IMAGE_KEY_EXTERNAL_BIT: u32 = 0x80000000; + impl ImageKey { - pub fn new(key0: u32, key1: u32) -> ImageKey { - ImageKey(key0, key1) + pub fn new(namespace: u32, key: u32) -> ImageKey { + assert!(namespace & IMAGE_KEY_EXTERNAL_BIT == 0); + ImageKey(namespace, key) + } + + pub fn new_external(namespace: u32, key: u32) -> ExternalImageKey { + assert!(namespace & IMAGE_KEY_EXTERNAL_BIT == 0); + ImageKey(namespace | IMAGE_KEY_EXTERNAL_BIT, key) + } + + pub fn is_external(&self) -> bool { + self.0 & IMAGE_KEY_EXTERNAL_BIT != 0 } } diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index 01176a019e..9a3593ae0c 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -319,28 +319,6 @@ pub struct ImageDisplayItem { pub image_rendering: ImageRendering, } -// TODO(nical): The only differences with the image item is that we provide a -// different type of key but more importantly we provide the uv rectangle. -// It would look nicer if the ImageDisplayItem was doing both external and -// regular images, but it may not make sense to have an uv_rect specified for -// regular images. -// The reason I think we should have uv_rect for external images is that I would -// like gecko to pack several of the externally rendered items (for example all -// native widgets like buttons, check boxes and the like) in the same texture, and -// in some cases we just don't have a choice because the GPU will round up the -// texture size to 32 if the image is smaller. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub struct ExternalImageDisplayItem { - pub image_key: ExternalImageKey, - pub stretch_size: Size2D, - pub tile_spacing: Size2D, - // TODO(nical) Decide what unit to use! - // Should it be in texels or [0..1] texture space? - // I'd say in texels... - pub uv_rect: Rect, - pub image_rendering: ImageRendering, -} - #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageFormat { Invalid, @@ -353,8 +331,10 @@ pub enum ImageFormat { #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ImageKey(u32, u32); -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct ExternalImageKey(u32, u32); +// TODO(nical) in order to not split the API, ExternalImageKey is just an alias +// for ImageKey. The downside is that in places where we expect only one of the +// two types, we have to check at runtime. +pub type ExternalImageKey = ImageKey; #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageRendering { @@ -469,7 +449,6 @@ pub enum SpecificDisplayItem { Rectangle(RectangleDisplayItem), Text(TextDisplayItem), Image(ImageDisplayItem), - ExternalImage(ExternalImageDisplayItem), WebGL(WebGLDisplayItem), Border(BorderDisplayItem), BoxShadow(BoxShadowDisplayItem),