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/internal_types.rs b/webrender/src/internal_types.rs index 1a606d535a..f462d97273 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,55 @@ 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 } + } + } + + 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. -/// 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..7c90702429 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -6,14 +6,14 @@ 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; 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}; @@ -96,7 +96,7 @@ pub enum ImagePrimitiveKind { #[derive(Debug)] pub struct ImagePrimitiveCpu { pub kind: ImagePrimitiveKind, - pub color_texture_id: TextureId, + pub color_texture_id: SourceTexture, } #[derive(Debug, Clone)] @@ -346,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, @@ -375,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(), } } @@ -610,22 +614,27 @@ 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 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)) + }; + 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) + } + }; - 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 +834,23 @@ 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::WebGL(..) => {} } - ImagePrimitiveKind::WebGL(..) => {} } } PrimitiveKind::Gradient => { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 2c70f4d544..3456305da8 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -18,8 +18,9 @@ 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 prim_store::{ImagePrimitiveGpu}; use profiler::{Profiler, BackendProfileCounters}; use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters}; use render_backend::RenderBackend; @@ -951,9 +952,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 +970,30 @@ impl Renderer { } } + fn resolve_texture_id(&self, texture: SourceTexture) -> TextureId { + match texture { + SourceTexture::Id(id) => { id } + SourceTexture::External(_key) => { + //TODO[nical] + unimplemented!(); + } + } + } + + 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, @@ -1285,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 a44554712f..ac10c7dab8 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 } @@ -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)] @@ -1995,7 +1996,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 { @@ -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: TextureId::invalid(), + color_texture_id: texture_id, }; let prim_gpu = ImagePrimitiveGpu { @@ -2458,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 1c330982fc..9a3593ae0c 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -331,6 +331,11 @@ pub enum ImageFormat { #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ImageKey(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 { Auto,