From afb0f062feda095a3c9c02ae3e97df40ba992901 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 17 Nov 2016 04:59:56 +1000 Subject: [PATCH] Add an API for providing external images via native texture handles. This allows applications to provide images without supplying the image bytes up front. This can be useful if the application doesn't have the bytes available immediately (e.g. video decoding) or wants to manage the buffer allocation and reduce memory copies. --- sample/src/main.rs | 4 +- webrender/res/cs_text_run.vs.glsl | 7 +- webrender/res/prim_shared.glsl | 32 +++++-- webrender/res/ps_image.vs.glsl | 5 +- webrender/res/ps_text_run.vs.glsl | 8 +- webrender/src/device.rs | 5 + webrender/src/internal_types.rs | 7 +- webrender/src/lib.rs | 1 + webrender/src/prim_store.rs | 154 ++++++++++++++++++++---------- webrender/src/render_backend.rs | 10 +- webrender/src/renderer.rs | 104 +++++++++++++++++++- webrender/src/resource_cache.rs | 122 +++++++++++++---------- webrender/src/tiling.rs | 35 +++++-- webrender_traits/src/api.rs | 8 +- webrender_traits/src/types.rs | 14 ++- 15 files changed, 376 insertions(+), 140 deletions(-) diff --git a/sample/src/main.rs b/sample/src/main.rs index 914c9b7df2..8a70a53f9f 100644 --- a/sample/src/main.rs +++ b/sample/src/main.rs @@ -9,7 +9,7 @@ use gleam::gl; use std::path::PathBuf; use std::ffi::CStr; use webrender_traits::{AuxiliaryListsBuilder, ColorF, Epoch, GlyphInstance}; -use webrender_traits::{ImageFormat, PipelineId, RendererKind}; +use webrender_traits::{ImageData, ImageFormat, PipelineId, RendererKind}; use std::fs::File; use std::io::Read; use std::env; @@ -135,7 +135,7 @@ fn main() { let clip_region = { let mask = webrender_traits::ImageMask { - image: api.add_image(2, 2, None, ImageFormat::A8, vec![0,80, 180, 255]), + image: api.add_image(2, 2, None, ImageFormat::A8, ImageData::Raw(vec![0,80, 180, 255])), rect: Rect::new(Point2D::new(75.0, 75.0), Size2D::new(100.0, 100.0)), repeat: false, }; diff --git a/webrender/res/cs_text_run.vs.glsl b/webrender/res/cs_text_run.vs.glsl index 3ac5314bce..46703a0d09 100644 --- a/webrender/res/cs_text_run.vs.glsl +++ b/webrender/res/cs_text_run.vs.glsl @@ -13,17 +13,18 @@ void main(void) { TextRun text = fetch_text_run(cpi.specific_prim_index); Glyph glyph = fetch_glyph(cpi.sub_index); PrimitiveGeometry pg = fetch_prim_geometry(cpi.global_prim_index); + ResourceRect res = fetch_resource_rect(cpi.user_data.x); // Glyphs size is already in device-pixels. // The render task origin is in device-pixels. Offset that by // the glyph offset, relative to its primitive bounding rect. - vec2 size = glyph.uv_rect.zw - glyph.uv_rect.xy; + vec2 size = res.uv_rect.zw - res.uv_rect.xy; vec2 origin = task.data0.xy + uDevicePixelRatio * (glyph.offset.xy - pg.local_rect.xy); vec4 local_rect = vec4(origin, size); vec2 texture_size = vec2(textureSize(sColor0, 0)); - vec2 st0 = glyph.uv_rect.xy / texture_size; - vec2 st1 = glyph.uv_rect.zw / texture_size; + vec2 st0 = res.uv_rect.xy / texture_size; + vec2 st1 = res.uv_rect.zw / texture_size; vec2 pos = mix(local_rect.xy, local_rect.xy + local_rect.zw, diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 060216e8d9..0bbcffad99 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -57,6 +57,7 @@ uniform sampler2D sData16; uniform sampler2D sData32; uniform sampler2D sData64; uniform sampler2D sData128; +uniform sampler2D sResourceRects; ivec2 get_fetch_uv(int index, int vecs_per_item) { int items_per_row = WR_MAX_VERTEX_TEXTURE_WIDTH / vecs_per_item; @@ -208,16 +209,14 @@ GradientStop fetch_gradient_stop(int index) { struct Glyph { vec4 offset; - vec4 uv_rect; }; Glyph fetch_glyph(int index) { Glyph glyph; - ivec2 uv = get_fetch_uv_2(index); + ivec2 uv = get_fetch_uv_1(index); - glyph.offset = texelFetchOffset(sData32, uv, 0, ivec2(0, 0)); - glyph.uv_rect = texelFetchOffset(sData32, uv, 0, ivec2(1, 0)); + glyph.offset = texelFetchOffset(sData16, uv, 0, ivec2(0, 0)); return glyph; } @@ -324,19 +323,22 @@ struct CachePrimitiveInstance { int specific_prim_index; int render_task_index; int sub_index; + ivec4 user_data; }; CachePrimitiveInstance fetch_cache_instance(int index) { CachePrimitiveInstance cpi; - int offset = index * 1; + int offset = index * 2; ivec4 data0 = int_data[offset + 0]; + ivec4 data1 = int_data[offset + 1]; cpi.global_prim_index = data0.x; cpi.specific_prim_index = data0.y; cpi.render_task_index = data0.z; cpi.sub_index = data0.w; + cpi.user_data = data1; return cpi; } @@ -536,6 +538,20 @@ TransformVertexInfo write_transform_vertex(vec4 instance_rect, #endif //WR_FEATURE_TRANSFORM +struct ResourceRect { + vec4 uv_rect; +}; + +ResourceRect fetch_resource_rect(int index) { + ResourceRect rect; + + ivec2 uv = get_fetch_uv_1(index); + + rect.uv_rect = texelFetchOffset(sResourceRects, uv, 0, ivec2(0, 0)); + + return rect; +} + struct Rectangle { vec4 color; }; @@ -565,7 +581,6 @@ TextRun fetch_text_run(int index) { } struct Image { - vec4 st_rect; // Location of the image texture in the texture atlas. vec4 stretch_size_and_tile_spacing; // Size of the actual image and amount of space between // tiled instances of this image. }; @@ -573,10 +588,9 @@ struct Image { Image fetch_image(int index) { Image image; - ivec2 uv = get_fetch_uv_2(index); + ivec2 uv = get_fetch_uv_1(index); - image.st_rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0)); - image.stretch_size_and_tile_spacing = texelFetchOffset(sData32, uv, 0, ivec2(1, 0)); + image.stretch_size_and_tile_spacing = texelFetchOffset(sData16, uv, 0, ivec2(0, 0)); return image; } diff --git a/webrender/res/ps_image.vs.glsl b/webrender/res/ps_image.vs.glsl index 2bcd0b4953..ae22ddd7e8 100644 --- a/webrender/res/ps_image.vs.glsl +++ b/webrender/res/ps_image.vs.glsl @@ -6,6 +6,7 @@ void main(void) { Primitive prim = load_primitive(gl_InstanceID); Image image = fetch_image(prim.prim_index); + ResourceRect res = fetch_resource_rect(prim.user_data.x); #ifdef WR_FEATURE_TRANSFORM TransformVertexInfo vi = write_transform_vertex(prim.local_rect, @@ -26,8 +27,8 @@ void main(void) { // vUv will contain how many times this image has wrapped around the image size. vec2 texture_size = vec2(textureSize(sColor0, 0)); - vec2 st0 = image.st_rect.xy / texture_size; - vec2 st1 = image.st_rect.zw / texture_size; + vec2 st0 = res.uv_rect.xy / texture_size; + vec2 st1 = res.uv_rect.zw / texture_size; vTextureSize = st1 - st0; vTextureOffset = st0; diff --git a/webrender/res/ps_text_run.vs.glsl b/webrender/res/ps_text_run.vs.glsl index 4102b50744..0fb5dd8f26 100644 --- a/webrender/res/ps_text_run.vs.glsl +++ b/webrender/res/ps_text_run.vs.glsl @@ -7,7 +7,9 @@ void main(void) { Primitive prim = load_primitive(gl_InstanceID); TextRun text = fetch_text_run(prim.prim_index); Glyph glyph = fetch_glyph(prim.sub_index); - vec4 local_rect = vec4(glyph.offset.xy, (glyph.uv_rect.zw - glyph.uv_rect.xy) / uDevicePixelRatio); + ResourceRect res = fetch_resource_rect(prim.user_data.x); + + vec4 local_rect = vec4(glyph.offset.xy, (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio); #ifdef WR_FEATURE_TRANSFORM TransformVertexInfo vi = write_transform_vertex(local_rect, @@ -28,8 +30,8 @@ void main(void) { write_clip(vi.global_clamped_pos, prim.clip_area); vec2 texture_size = vec2(textureSize(sColor0, 0)); - vec2 st0 = glyph.uv_rect.xy / texture_size; - vec2 st1 = glyph.uv_rect.zw / texture_size; + vec2 st0 = res.uv_rect.xy / texture_size; + vec2 st1 = res.uv_rect.zw / texture_size; vColor = text.color; vUv = mix(st0, st1, f); diff --git a/webrender/src/device.rs b/webrender/src/device.rs index c7a2e6d3cc..e5e4fb310d 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -1366,6 +1366,11 @@ impl Device { if u_data128 != -1 { gl::uniform_1i(u_data128, TextureSampler::Data128 as i32); } + + let u_resource_rects = gl::get_uniform_location(program.id, "sResourceRects"); + if u_resource_rects != -1 { + gl::uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32); + } } } } diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 59cb756c11..9bb569b4c7 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use tiling; use webrender_traits::{Epoch, ColorF, PipelineId}; use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle}; -use webrender_traits::{ScrollLayerId, WebGLCommand}; +use webrender_traits::{ExternalImageId, ScrollLayerId, WebGLCommand}; // An ID for a texture that is owned by the // texture cache module. This can include atlases @@ -45,9 +45,7 @@ pub enum SourceTexture { Invalid, TextureCache(CacheTextureId), WebGL(u32), // Is actually a gl::GLuint - - // TODO(gw): Implement external image support via callback - //External(i32), + External(ExternalImageId), } pub enum GLContextHandleWrapper { @@ -203,6 +201,7 @@ pub enum TextureSampler { Layers, RenderTasks, Geometry, + ResourceRects, } impl TextureSampler { diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 202c8305f0..d4612e01c0 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -121,4 +121,5 @@ extern crate offscreen_gl_context; extern crate byteorder; extern crate rayon; +pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler}; pub use renderer::{Renderer, RendererOptions}; diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index c4aa89285f..46e4c7c068 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -7,7 +7,7 @@ use euclid::{Point2D, Matrix4D, Rect, Size2D}; use gpu_store::{GpuStore, GpuStoreAddress}; use internal_types::{device_pixel, DeviceRect, DeviceSize, SourceTexture}; use mask_cache::{MaskCacheInfo, MaskCacheKey}; -use resource_cache::ResourceCache; +use resource_cache::{ImageProperties, ResourceCache}; use std::mem; use std::usize; use tiling::RenderTask; @@ -19,6 +19,39 @@ use webrender_traits::{FontKey, FontRenderMode, WebGLContextId}; pub const CLIP_DATA_GPU_SIZE: usize = 5; pub const MASK_DATA_GPU_SIZE: usize = 1; +/// Stores two coordinates in texel space. The coordinates +/// are stored in texel coordinates because the texture atlas +/// may grow. Storing them as texel coords and normalizing +/// the UVs in the vertex shader means nothing needs to be +/// updated on the CPU when the texture size changes. +#[derive(Clone)] +pub struct TexelRect { + pub uv0: Point2D, + pub uv1: Point2D, +} + +impl Default for TexelRect { + fn default() -> TexelRect { + TexelRect { + uv0: Point2D::zero(), + uv1: Point2D::zero(), + } + } +} + +/// For external images, it's not possible to know the +/// UV coords of the image (or the image data itself) +/// until the render thread receives the frame and issues +/// callbacks to the client application. For external +/// images that are visible, a DeferredResolve is created +/// that is stored in the frame. This allows the render +/// thread to iterate this list and update any changed +/// texture data and update the UV rect. +pub struct DeferredResolve { + pub resource_address: GpuStoreAddress, + pub image_properties: ImageProperties, +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct SpecificPrimitiveIndex(pub usize); @@ -98,12 +131,11 @@ pub enum ImagePrimitiveKind { pub struct ImagePrimitiveCpu { pub kind: ImagePrimitiveKind, pub color_texture_id: SourceTexture, + pub resource_address: GpuStoreAddress, } #[derive(Debug, Clone)] pub struct ImagePrimitiveGpu { - pub uv0: Point2D, - pub uv1: Point2D, pub stretch_size: Size2D, pub tile_spacing: Size2D, } @@ -193,14 +225,13 @@ pub struct TextRunPrimitiveCpu { pub color_texture_id: SourceTexture, pub color: ColorF, pub render_mode: FontRenderMode, + pub resource_address: GpuStoreAddress, } #[derive(Debug, Clone)] struct GlyphPrimitive { offset: Point2D, padding: Point2D, - uv0: Point2D, - uv1: Point2D, } #[derive(Debug, Clone)] @@ -346,6 +377,9 @@ pub struct PrimitiveStore { pub gpu_data64: GpuStore, pub gpu_data128: GpuStore, + // Resolved resource rects. + pub gpu_resource_rects: GpuStore, + // General device_pixel_ratio: f32, prims_to_resolve: Vec, @@ -365,6 +399,7 @@ impl PrimitiveStore { gpu_data32: GpuStore::new(), gpu_data64: GpuStore::new(), gpu_data128: GpuStore::new(), + gpu_resource_rects: GpuStore::new(), device_pixel_ratio: device_pixel_ratio, prims_to_resolve: Vec::new(), } @@ -406,9 +441,10 @@ impl PrimitiveStore { metadata } - PrimitiveContainer::TextRun(text_cpu, text_gpu) => { + PrimitiveContainer::TextRun(mut text_cpu, text_gpu) => { let gpu_address = self.gpu_data16.push(text_gpu); - let gpu_glyphs_address = self.gpu_data32.alloc(text_cpu.glyph_range.length); + let gpu_glyphs_address = self.gpu_data16.alloc(text_cpu.glyph_range.length); + text_cpu.resource_address = self.gpu_resource_rects.alloc(text_cpu.glyph_range.length); let metadata = PrimitiveMetadata { is_opaque: false, @@ -425,8 +461,10 @@ impl PrimitiveStore { self.cpu_text_runs.push(text_cpu); metadata } - PrimitiveContainer::Image(image_cpu, image_gpu) => { - let gpu_address = self.gpu_data32.push(image_gpu); + PrimitiveContainer::Image(mut image_cpu, image_gpu) => { + image_cpu.resource_address = self.gpu_resource_rects.alloc(1); + + let gpu_address = self.gpu_data16.push(image_gpu); let metadata = PrimitiveMetadata { is_opaque: false, @@ -540,12 +578,14 @@ impl PrimitiveStore { PrimitiveIndex(prim_index) } - pub fn resolve_primitives(&mut self, resource_cache: &ResourceCache) { + pub fn resolve_primitives(&mut self, resource_cache: &ResourceCache) -> Vec { + let mut deferred_resolves = Vec::new(); + for prim_index in self.prims_to_resolve.drain(..) { let metadata = &mut self.cpu_metadata[prim_index.0]; if let Some(MaskCacheInfo{ key: MaskCacheKey { image: Some(gpu_address), .. }, image: Some(ref mask), .. }) = metadata.clip_cache_info { - let cache_item = resource_cache.get_image(mask.image, ImageRendering::Auto); + let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); let mask_data = self.gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE); mask_data[0] = GpuBlock32::from(ImageMaskData { uv_rect: Rect::new(cache_item.uv0, @@ -563,44 +603,64 @@ impl PrimitiveStore { PrimitiveKind::TextRun => { let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0]; - let dest_glyphs = self.gpu_data32.get_slice_mut(metadata.gpu_data_address, - text.glyph_range.length); + let dest_rects = self.gpu_resource_rects.get_slice_mut(text.resource_address, + text.glyph_range.length); let texture_id = resource_cache.get_glyphs(text.font_key, text.font_size, &text.glyph_indices, text.render_mode, |index, uv0, uv1| { - let dest_glyph = &mut dest_glyphs[index]; - let dest: &mut GlyphPrimitive = unsafe { - mem::transmute(dest_glyph) - }; - dest.uv0 = uv0; - dest.uv1 = uv1; + let dest_rect = &mut dest_rects[index]; + dest_rect.uv0 = uv0; + dest_rect.uv1 = uv1; }); text.color_texture_id = texture_id; } 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 { + let (texture_id, cache_item) = match image_cpu.kind { ImagePrimitiveKind::Image(image_key, image_rendering, _) => { - resource_cache.get_image(image_key, image_rendering) + // Check if an external image that needs to be resolved + // by the render thread. + let image_properties = resource_cache.get_image_properties(image_key); + + match image_properties.external_id { + Some(external_id) => { + // This is an external texture - we will add it to + // the deferred resolves list to be patched by + // the render thread... + deferred_resolves.push(DeferredResolve { + resource_address: image_cpu.resource_address, + image_properties: image_properties, + }); + + (SourceTexture::External(external_id), None) + } + None => { + let cache_item = resource_cache.get_cached_image(image_key, image_rendering); + (cache_item.texture_id, Some(cache_item)) + } + } } ImagePrimitiveKind::WebGL(context_id) => { - resource_cache.get_webgl_texture(&context_id) + let cache_item = resource_cache.get_webgl_texture(&context_id); + (cache_item.texture_id, Some(cache_item)) } }; - image_cpu.color_texture_id = cache_item.texture_id; - image_gpu.uv0 = cache_item.uv0; - image_gpu.uv1 = cache_item.uv1; + if let Some(cache_item) = cache_item { + let resource_rect = self.gpu_resource_rects.get_mut(image_cpu.resource_address); + resource_rect.uv0 = cache_item.uv0; + resource_rect.uv1 = cache_item.uv1; + } + image_cpu.color_texture_id = texture_id; } } } + + deferred_resolves } pub fn get_bounding_rect(&self, index: PrimitiveIndex) -> &Option { @@ -695,7 +755,7 @@ impl PrimitiveStore { debug_assert!(metadata.gpu_data_count == text.glyph_range.length as i32); debug_assert!(text.glyph_indices.is_empty()); let src_glyphs = auxiliary_lists.glyph_instances(&text.glyph_range); - let dest_glyphs = self.gpu_data32.get_slice_mut(metadata.gpu_data_address, + let dest_glyphs = self.gpu_data16.get_slice_mut(metadata.gpu_data_address, text.glyph_range.length); let mut glyph_key = GlyphKey::new(text.font_key, text.font_size, @@ -724,9 +784,7 @@ impl PrimitiveStore { Size2D::new(width, height)); local_rect = local_rect.union(&local_glyph_rect); - dest_glyphs[actual_glyph_count] = GpuBlock32::from(GlyphPrimitive { - uv0: Point2D::zero(), - uv1: Point2D::zero(), + dest_glyphs[actual_glyph_count] = GpuBlock16::from(GlyphPrimitive { padding: Point2D::zero(), offset: local_glyph_rect.origin, }); @@ -866,6 +924,22 @@ impl From for GpuBlock16 { } } +impl From for GpuBlock16 { + fn from(data: ImagePrimitiveGpu) -> GpuBlock16 { + unsafe { + mem::transmute::(data) + } + } +} + +impl From for GpuBlock16 { + fn from(data: GlyphPrimitive) -> GpuBlock16 { + unsafe { + mem::transmute::(data) + } + } +} + #[derive(Clone)] pub struct GpuBlock32 { data: [f32; 8], @@ -895,22 +969,6 @@ impl From for GpuBlock32 { } } -impl From for GpuBlock32 { - fn from(data: GlyphPrimitive) -> GpuBlock32 { - unsafe { - mem::transmute::(data) - } - } -} - -impl From for GpuBlock32 { - fn from(data: ImagePrimitiveGpu) -> GpuBlock32 { - unsafe { - mem::transmute::(data) - } - } -} - impl From for GpuBlock32 { fn from(data: ClipRect) -> GpuBlock32 { unsafe { diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 56fa1ce8b2..475602e880 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -16,7 +16,7 @@ use std::io::{Cursor, Read}; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; use texture_cache::TextureCache; -use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace}; +use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData}; use webrender_traits::{FlushNotifier, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId}; use record; use tiling::FrameBuilderConfig; @@ -124,14 +124,16 @@ impl RenderBackend { }; tx.send(glyph_dimensions).unwrap(); } - ApiMsg::AddImage(id, width, height, stride, format, bytes) => { - profile_counters.image_templates.inc(bytes.len()); + ApiMsg::AddImage(id, width, height, stride, format, data) => { + if let ImageData::Raw(ref bytes) = data { + profile_counters.image_templates.inc(bytes.len()); + } self.resource_cache.add_image_template(id, width, height, stride, format, - bytes); + data); } ApiMsg::Flush => { let mut flush_notifier = self.flush_notifier.lock(); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 35add78802..68a596173a 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -13,7 +13,7 @@ use debug_colors; use debug_render::DebugRenderer; use device::{Device, ProgramId, TextureId, VertexFormat, GpuProfiler}; use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget}; -use euclid::{Matrix4D, Size2D}; +use euclid::{Matrix4D, Point2D, Size2D}; use fnv::FnvHasher; use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp}; use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode}; @@ -38,7 +38,7 @@ use tiling::{RenderTarget, ClearTile}; use time::precise_time_ns; use util::TransformedRectKind; use webrender_traits::{ColorF, Epoch, FlushNotifier, PipelineId, RenderNotifier, RenderDispatcher}; -use webrender_traits::{ImageFormat, RenderApiSender, RendererKind}; +use webrender_traits::{ExternalImageId, ImageFormat, RenderApiSender, RendererKind}; pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; @@ -388,6 +388,8 @@ pub struct Renderer { data32_texture: VertexDataTexture, data64_texture: VertexDataTexture, data128_texture: VertexDataTexture, + resource_rects_texture: VertexDataTexture, + pipeline_epoch_map: HashMap>, /// Used to dispatch functions to the main thread's event loop. /// Required to allow GLContext sharing in some implementations like WGL. @@ -400,6 +402,13 @@ pub struct Renderer { /// freed by the backend thread. This saves having to /// use a hashmap, and allows a flat vector for performance. cache_texture_id_map: Vec>, + + /// Optional trait object that allows the client + /// application to provide external buffers for image data. + external_image_handler: Option>, + + /// Map of external image IDs to native textures. + external_images: HashMap>, } impl Renderer { @@ -606,6 +615,7 @@ impl Renderer { let data32_texture = VertexDataTexture::new(&mut device); let data64_texture = VertexDataTexture::new(&mut device); let data128_texture = VertexDataTexture::new(&mut device); + let resource_rects_texture = VertexDataTexture::new(&mut device); let x0 = 0.0; let y0 = 0.0; @@ -721,9 +731,12 @@ impl Renderer { data32_texture: data32_texture, data64_texture: data64_texture, data128_texture: data128_texture, + resource_rects_texture: resource_rects_texture, pipeline_epoch_map: HashMap::with_hasher(Default::default()), main_thread_dispatcher: main_thread_dispatcher, cache_texture_id_map: Vec::new(), + external_image_handler: None, + external_images: HashMap::with_hasher(Default::default()), }; let sender = RenderApiSender::new(api_tx, payload_tx); @@ -800,6 +813,11 @@ impl Renderer { match texture_id { &SourceTexture::Invalid => TextureId::invalid(), &SourceTexture::WebGL(id) => TextureId::new(id), + &SourceTexture::External(ref key) => { + *self.external_images + .get(key) + .expect("BUG: External image should be resolved by now!") + } &SourceTexture::TextureCache(index) => { self.cache_texture_id_map[index.0] .expect("BUG: Texture should exist in texture cache map!") @@ -807,6 +825,11 @@ impl Renderer { } } + /// Set a callback for handling external images. + pub fn set_external_image_handler(&mut self, handler: Box) { + self.external_image_handler = Some(handler); + } + /// Renders the current frame. /// /// A Frame is supplied by calling [set_root_stacking_context()][newframe]. @@ -1321,9 +1344,52 @@ impl Renderer { self.device.set_blend(false); } + fn update_deferred_resolves(&mut self, frame: &mut Frame) { + // The first thing we do is run through any pending deferred + // resolves, and use a callback to get the UV rect for this + // custom item. Then we patch the resource_rects structure + // here before it's uploaded to the GPU. + if !frame.deferred_resolves.is_empty() { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + + for deferred_resolve in &frame.deferred_resolves { + let props = &deferred_resolve.image_properties; + let external_id = props.external_id + .expect("BUG: Deferred resolves must be external images!"); + let image = handler.get(external_id); + + let texture_id = match image.source { + ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id), + }; + + self.external_images.insert(external_id, texture_id); + let resource_rect_index = deferred_resolve.resource_address.0 as usize; + let resource_rect = &mut frame.gpu_resource_rects[resource_rect_index]; + resource_rect.uv0 = Point2D::new(image.u0, image.v0); + resource_rect.uv1 = Point2D::new(image.u1, image.v1); + } + } + } + + fn release_external_textures(&mut self) { + if !self.external_images.is_empty() { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + + for (external_id, _) in self.external_images.drain() { + handler.release(external_id); + } + } + } + fn draw_tile_frame(&mut self, frame: &mut Frame, framebuffer_size: &Size2D) { + self.update_deferred_resolves(frame); + // Some tests use a restricted viewport smaller than the main screen size. // Ensure we clear the framebuffer in these tests. // TODO(gw): Find a better solution for this? @@ -1382,6 +1448,7 @@ impl Renderer { self.data64_texture.init(&mut self.device, &mut frame.gpu_data64); self.data128_texture.init(&mut self.device, &mut frame.gpu_data128); self.prim_geom_texture.init(&mut self.device, &mut frame.gpu_geometry); + self.resource_rects_texture.init(&mut self.device, &mut frame.gpu_resource_rects); self.device.bind_texture(TextureSampler::Layers, self.layer_texture.id); self.device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id); @@ -1390,6 +1457,7 @@ impl Renderer { self.device.bind_texture(TextureSampler::Data32, self.data32_texture.id); self.device.bind_texture(TextureSampler::Data64, self.data64_texture.id); self.device.bind_texture(TextureSampler::Data128, self.data128_texture.id); + self.device.bind_texture(TextureSampler::ResourceRects, self.resource_rects_texture.id); let mut src_id = None; @@ -1432,6 +1500,8 @@ impl Renderer { max_prim_items, &projection); } + + self.release_external_textures(); } pub fn debug_renderer<'a>(&'a mut self) -> &'a mut DebugRenderer { @@ -1447,6 +1517,36 @@ impl Renderer { } } +pub enum ExternalImageSource { + // TODO(gw): Work out the API for raw buffers. + //RawData(*const u8, usize), + NativeTexture(u32), // Is a gl::GLuint texture handle +} + +/// The data that an external client should provide about +/// an external image. The timestamp is used to test if +/// the renderer should upload new texture data this +/// frame. For instance, if providing video frames, the +/// application could call wr.render() whenever a new +/// video frame is ready. If the callback increments +/// the returned timestamp for a given image, the renderer +/// will know to re-upload the image data to the GPU. +/// Note that the UV coords are supplied in texel-space! +pub struct ExternalImage { + pub u0: f32, + pub v0: f32, + pub u1: f32, + pub v1: f32, + pub source: ExternalImageSource, +} + +/// Interface that an application can implement +/// to support providing external image buffers. +pub trait ExternalImageHandler { + fn get(&mut self, key: ExternalImageId) -> ExternalImage; + fn release(&mut self, key: ExternalImageId); +} + #[derive(Clone, Debug)] pub struct RendererOptions { pub device_pixel_ratio: f32, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 2de2bf5b2a..0e7a56eff4 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -18,7 +18,8 @@ use std::hash::BuildHasherDefault; use std::hash::Hash; use texture_cache::{TextureCache, TextureCacheItemId}; use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering}; -use webrender_traits::{FontRenderMode, GlyphDimensions, WebGLContextId}; +use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId}; +use webrender_traits::ExternalImageId; thread_local!(pub static FONT_CONTEXT: RefCell = RefCell::new(FontContext::new())); @@ -58,6 +59,10 @@ impl RenderedGlyphKey { pub struct ImageProperties { pub format: ImageFormat, pub is_opaque: bool, + pub external_id: Option, + pub width: u32, + pub height: u32, + pub stride: Option, } #[derive(Debug, Copy, Clone, PartialEq)] @@ -68,7 +73,7 @@ enum State { } struct ImageResource { - bytes: Vec, + data: ImageData, width: u32, height: u32, stride: Option, @@ -218,14 +223,18 @@ impl ResourceCache { height: u32, stride: Option, format: ImageFormat, - bytes: Vec) { + data: ImageData) { + let is_opaque = match data { + ImageData::Raw(ref bytes) => is_image_opaque(format, bytes), + ImageData::External(..) => false, // TODO: Allow providing this through API. + }; let resource = ImageResource { - is_opaque: is_image_opaque(format, &bytes), + is_opaque: is_opaque, width: width, height: height, stride: stride, format: format, - bytes: bytes, + data: data, epoch: Epoch(0), }; @@ -254,7 +263,7 @@ impl ResourceCache { height: height, stride: None, format: format, - bytes: bytes, + data: ImageData::Raw(bytes), epoch: next_epoch, }; @@ -397,10 +406,11 @@ impl ResourceCache { } #[inline] - pub fn get_image(&self, - image_key: ImageKey, - image_rendering: ImageRendering) -> CacheItem { + pub fn get_cached_image(&self, + image_key: ImageKey, + image_rendering: ImageRendering) -> CacheItem { debug_assert!(self.state == State::QueryResources); + let image_info = &self.cached_images.get(&(image_key, image_rendering), self.current_frame_id); let item = self.texture_cache.get(image_info.texture_cache_id); @@ -416,9 +426,18 @@ impl ResourceCache { pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties { let image_template = &self.image_templates[&image_key]; + let external_id = match image_template.data { + ImageData::External(id) => Some(id), + ImageData::Raw(..) => None, + }; + ImageProperties { format: image_template.format, is_opaque: image_template.is_opaque, + external_id: external_id, + width: image_template.width, + height: image_template.height, + stride: image_template.stride, } } @@ -453,47 +472,54 @@ impl ResourceCache { let cached_images = &mut self.cached_images; let image_template = &self.image_templates[&key]; - match cached_images.entry((key, rendering), self.current_frame_id) { - Occupied(entry) => { - let image_id = entry.get().texture_cache_id; - - if entry.get().epoch != image_template.epoch { - // TODO: Can we avoid the clone of the bytes here? - self.texture_cache.update(image_id, - image_template.width, - image_template.height, - image_template.stride, - image_template.format, - image_template.bytes.clone()); - - // Update the cached epoch - *entry.into_mut() = CachedImageInfo { - texture_cache_id: image_id, - epoch: image_template.epoch, - }; + match image_template.data { + ImageData::Raw(ref bytes) => { + match cached_images.entry((key, rendering), self.current_frame_id) { + Occupied(entry) => { + let image_id = entry.get().texture_cache_id; + + if entry.get().epoch != image_template.epoch { + // TODO: Can we avoid the clone of the bytes here? + self.texture_cache.update(image_id, + image_template.width, + image_template.height, + image_template.stride, + image_template.format, + bytes.clone()); + + // Update the cached epoch + *entry.into_mut() = CachedImageInfo { + texture_cache_id: image_id, + epoch: image_template.epoch, + }; + } + } + Vacant(entry) => { + let image_id = self.texture_cache.new_item_id(); + + let filter = match rendering { + ImageRendering::Pixelated => TextureFilter::Nearest, + ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear, + }; + + // TODO: Can we avoid the clone of the bytes here? + self.texture_cache.insert(image_id, + image_template.width, + image_template.height, + image_template.stride, + image_template.format, + filter, + bytes.clone()); + + entry.insert(CachedImageInfo { + texture_cache_id: image_id, + epoch: image_template.epoch, + }); + } } } - Vacant(entry) => { - let image_id = self.texture_cache.new_item_id(); - - let filter = match rendering { - ImageRendering::Pixelated => TextureFilter::Nearest, - ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear, - }; - - // TODO: Can we avoid the clone of the bytes here? - self.texture_cache.insert(image_id, - image_template.width, - image_template.height, - image_template.stride, - image_template.format, - filter, - image_template.bytes.clone()); - - entry.insert(CachedImageInfo { - texture_cache_id: image_id, - epoch: image_template.epoch, - }); + ImageData::External(..) => { + // External images don't get added to the texture cache! } } } diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 2b94f1ca22..bf93d6ca47 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -16,8 +16,8 @@ use mask_cache::{MaskCacheKey, MaskCacheInfo}; use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer}; use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu}; use prim_store::{ImagePrimitiveCpu, ImagePrimitiveGpu, ImagePrimitiveKind}; -use prim_store::{PrimitiveKind, PrimitiveIndex, PrimitiveMetadata}; -use prim_store::{CLIP_DATA_GPU_SIZE, PrimitiveClipSource}; +use prim_store::{PrimitiveKind, PrimitiveIndex, PrimitiveMetadata, TexelRect}; +use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, PrimitiveClipSource}; use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, GradientType}; use prim_store::{PrimitiveCacheKey, TextRunPrimitiveGpu, TextRunPrimitiveCpu}; use prim_store::{PrimitiveStore, GpuBlock16, GpuBlock32, GpuBlock64, GpuBlock128}; @@ -211,6 +211,8 @@ impl AlphaBatchHelpers for PrimitiveStore { }); } &mut PrimitiveBatchData::TextRun(ref mut data) => { + let text_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0]; + for glyph_index in 0..metadata.gpu_data_count { data.push(PrimitiveInstance { task_index: task_index, @@ -219,11 +221,13 @@ impl AlphaBatchHelpers for PrimitiveStore { global_prim_id: global_prim_id, prim_address: prim_address, sub_index: metadata.gpu_data_address.0 + glyph_index, - user_data: [ 0, 0 ], + user_data: [ text_cpu.resource_address.0 + glyph_index, 0 ], }); } } &mut PrimitiveBatchData::Image(ref mut data) => { + let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0]; + data.push(PrimitiveInstance { task_index: task_index, clip_task_index: clip_task_index, @@ -231,7 +235,7 @@ impl AlphaBatchHelpers for PrimitiveStore { global_prim_id: global_prim_id, prim_address: prim_address, sub_index: 0, - user_data: [ 0, 0 ], + user_data: [ image_cpu.resource_address.0, 0 ], }); } &mut PrimitiveBatchData::Borders(ref mut data) => { @@ -623,7 +627,7 @@ impl ClipBatcher { } })); if let (Some(address), Some(mask_key)) = (key.image, task_info.image) { - let cache_item = resource_cache.get_image(mask_key, ImageRendering::Auto); + let cache_item = resource_cache.get_cached_image(mask_key, ImageRendering::Auto); self.images.entry(cache_item.texture_id) .or_insert(Vec::new()) .push(CacheClipInstance { @@ -744,6 +748,7 @@ impl RenderTarget { global_prim_id: prim_index.0 as i32, prim_address: prim_metadata.gpu_prim_index, sub_index: 0, + user_data: [0; 4], }); } PrimitiveKind::TextRun => { @@ -769,6 +774,7 @@ impl RenderTarget { global_prim_id: prim_index.0 as i32, prim_address: prim_metadata.gpu_prim_index, sub_index: prim_metadata.gpu_data_address.0 + glyph_index, + user_data: [ text.resource_address.0 + glyph_index, 0, 0, 0 ], }); } } @@ -1259,6 +1265,7 @@ pub struct CachePrimitiveInstance { prim_address: GpuStoreAddress, task_id: i32, sub_index: i32, + user_data: [i32; 4], } /// A clipping primitive drawn into the clipping mask. @@ -1552,6 +1559,13 @@ pub struct Frame { pub gpu_data64: Vec, pub gpu_data128: Vec, pub gpu_geometry: Vec, + pub gpu_resource_rects: Vec, + + // List of textures that we don't know about yet + // from the backend thread. The render thread + // will use a callback to resolve these and + // patch the data structures. + pub deferred_resolves: Vec, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -2065,6 +2079,7 @@ impl FrameBuilder { color_texture_id: SourceTexture::Invalid, color: *color, render_mode: render_mode, + resource_address: GpuStoreAddress(0), }; let prim_gpu = TextRunPrimitiveGpu { @@ -2149,11 +2164,10 @@ impl FrameBuilder { let prim_cpu = ImagePrimitiveCpu { kind: ImagePrimitiveKind::WebGL(context_id), color_texture_id: SourceTexture::Invalid, + resource_address: GpuStoreAddress(0), }; let prim_gpu = ImagePrimitiveGpu { - uv0: Point2D::zero(), - uv1: Point2D::zero(), stretch_size: rect.size, tile_spacing: Size2D::zero(), }; @@ -2175,11 +2189,10 @@ impl FrameBuilder { image_rendering, *tile_spacing), color_texture_id: SourceTexture::Invalid, + resource_address: GpuStoreAddress(0), }; let prim_gpu = ImagePrimitiveGpu { - uv0: Point2D::zero(), - uv1: Point2D::zero(), stretch_size: *stretch_size, tile_spacing: *tile_spacing, }; @@ -2562,7 +2575,7 @@ impl FrameBuilder { resource_cache.block_until_all_resources_added(); - self.prim_store.resolve_primitives(resource_cache); + let deferred_resolves = self.prim_store.resolve_primitives(resource_cache); let mut passes = Vec::new(); @@ -2609,6 +2622,8 @@ 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(), + gpu_resource_rects: self.prim_store.gpu_resource_rects.build(), + deferred_resolves: deferred_resolves, } } } diff --git a/webrender_traits/src/api.rs b/webrender_traits/src/api.rs index 134169637e..e1affd00dd 100644 --- a/webrender_traits/src/api.rs +++ b/webrender_traits/src/api.rs @@ -9,8 +9,8 @@ use offscreen_gl_context::{GLContextAttributes, GLLimits}; use std::cell::Cell; use {ApiMsg, AuxiliaryLists, BuiltDisplayList, ColorF, Epoch}; use {FontKey, IdNamespace, ImageFormat, ImageKey, NativeFontHandle, PipelineId}; -use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState, WebGLContextId, WebGLCommand}; -use {GlyphKey, GlyphDimensions}; +use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState}; +use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand}; impl RenderApiSender { pub fn new(api_sender: IpcSender, @@ -92,10 +92,10 @@ impl RenderApi { height: u32, stride: Option, format: ImageFormat, - bytes: Vec) -> ImageKey { + data: ImageData) -> ImageKey { let new_id = self.next_unique_id(); let key = ImageKey::new(new_id.0, new_id.1); - let msg = ApiMsg::AddImage(key, width, height, stride, format, bytes); + let msg = ApiMsg::AddImage(key, width, height, stride, format, data); self.api_sender.send(msg).unwrap(); key } diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index 61248d33aa..834d5d3b8b 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -28,7 +28,7 @@ pub enum ApiMsg { /// Gets the glyph dimensions GetGlyphDimensions(Vec, IpcSender>>), /// Adds an image from the resource cache. - AddImage(ImageKey, u32, u32, Option, ImageFormat, Vec), + AddImage(ImageKey, u32, u32, Option, ImageFormat, ImageData), /// Updates the the resource cache with the new image data. UpdateImage(ImageKey, u32, u32, ImageFormat, Vec), /// Drops an image from the resource cache. @@ -316,6 +316,18 @@ pub enum ImageFormat { RGBAF32, } +/// An arbitrary identifier for an external image provided by the +/// application. It must be a unique identifier for each external +/// image. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct ExternalImageId(pub u64); + +#[derive(Serialize, Deserialize)] +pub enum ImageData { + Raw(Vec), + External(ExternalImageId), +} + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ImageKey(u32, u32);