diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index 02da9f4310af..1d9340a50177 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc the need to run the cargo update command in js/src as well. Hopefully this will be resolved soon. -Latest Commit: 101c69db1a989fe89c308dabd53cf50aedfe4a96 +Latest Commit: 1007a65c6dd1fdfb8b39d57d7faff3cae7b32e0c diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 3a2422c00860..6b56d73df6f7 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "webrender" -version = "0.48.0" +version = "0.49.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" build = "build.rs" [features] -default = ["freetype-lib", "webgl"] +default = ["freetype-lib"] freetype-lib = ["freetype/servo-freetype-sys"] profiler = ["thread_profiler/thread_profiler"] -webgl = ["offscreen_gl_context", "webrender_api/webgl"] [dependencies] app_units = "0.5" @@ -19,11 +18,10 @@ bit-set = "0.4" byteorder = "1.0" euclid = "0.15.1" fxhash = "0.2.1" -gleam = "0.4.7" +gleam = "0.4.8" lazy_static = "0.2" log = "0.3" num-traits = "0.1.32" -offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true} time = "0.1" rayon = "0.8" webrender_api = {path = "../webrender_api"} diff --git a/gfx/webrender/benches/coalesce.rs b/gfx/webrender/benches/coalesce.rs deleted file mode 100644 index 69c02e28e1d8..000000000000 --- a/gfx/webrender/benches/coalesce.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![feature(test)] - -extern crate rand; -extern crate test; -extern crate webrender; - -use rand::Rng; -use test::Bencher; -use webrender::TexturePage; -use webrender::api::{DeviceUintSize as Size}; - -#[bench] -fn bench_coalesce(b: &mut Bencher) { - let mut rng = rand::thread_rng(); - let mut page = TexturePage::new_dummy(Size::new(10000, 10000)); - let mut test_page = TexturePage::new_dummy(Size::new(10000, 10000)); - while page.allocate(&Size::new(rng.gen_range(1, 100), rng.gen_range(1, 100))).is_some() {} - b.iter(|| { - test_page.fill_from(&page); - test_page.coalesce(); - }); -} diff --git a/gfx/webrender/examples/common/boilerplate.rs b/gfx/webrender/examples/common/boilerplate.rs index 459565620d6c..69ef4c9c6d7f 100644 --- a/gfx/webrender/examples/common/boilerplate.rs +++ b/gfx/webrender/examples/common/boilerplate.rs @@ -9,6 +9,7 @@ use std::path::PathBuf; use webrender; use webrender::api::*; use webrender::renderer::{PROFILER_DBG, RENDER_TARGET_DBG, TEXTURE_CACHE_DBG}; +use webrender::renderer::ExternalImageHandler; struct Notifier { window_proxy: glutin::WindowProxy, @@ -64,6 +65,9 @@ pub trait Example { event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool; + fn get_external_image_handler(&self) -> Option> { + None + } } pub fn main_wrapper(example: &mut Example, @@ -116,6 +120,10 @@ pub fn main_wrapper(example: &mut Example, let notifier = Box::new(Notifier::new(window.create_window_proxy())); renderer.set_render_notifier(notifier); + if let Some(external_image_handler) = example.get_external_image_handler() { + renderer.set_external_image_handler(external_image_handler); + } + let epoch = Epoch(0); let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0); diff --git a/gfx/webrender/examples/texture_cache_stress.rs b/gfx/webrender/examples/texture_cache_stress.rs new file mode 100644 index 000000000000..e15bdfb56d60 --- /dev/null +++ b/gfx/webrender/examples/texture_cache_stress.rs @@ -0,0 +1,287 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate gleam; +extern crate glutin; +extern crate webrender; + +#[path="common/boilerplate.rs"] +mod boilerplate; + +use boilerplate::{Example, HandyDandyRectBuilder}; +use std::mem; +use webrender::api::*; +use webrender::renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler}; + +struct ImageGenerator { + patterns: [[u8; 3]; 6], + next_pattern: usize, + current_image: Vec, +} + +impl ImageGenerator { + fn new() -> ImageGenerator { + ImageGenerator { + next_pattern: 0, + patterns: [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], + [1, 1, 0], + [0, 1, 1], + [1, 0, 1], + ], + current_image: Vec::new(), + } + } + + fn generate_image(&mut self, size: u32) { + let pattern = &self.patterns[self.next_pattern]; + self.current_image.clear(); + for y in 0..size { + for x in 0..size { + let lum = 255 * (1 - (((x & 8) == 0) ^ ((y & 8) == 0)) as u8); + self.current_image.extend_from_slice(&[lum * pattern[0], + lum * pattern[1], + lum * pattern[2], + 0xff]); + } + } + + self.next_pattern = (self.next_pattern + 1) % self.patterns.len(); + } + + fn take(&mut self) -> Vec { + mem::replace(&mut self.current_image, Vec::new()) + } +} + +impl ExternalImageHandler for ImageGenerator { + fn lock(&mut self, _key: ExternalImageId, channel_index: u8) -> ExternalImage { + self.generate_image(channel_index as u32); + ExternalImage { + u0: 0.0, + v0: 0.0, + u1: 1.0, + v1: 1.0, + source: ExternalImageSource::RawData(&self.current_image) + } + } + fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) { + } +} + +struct App { + stress_keys: Vec, + image_key: Option, + image_generator: ImageGenerator, + swap_keys: Vec, + swap_index: usize, +} + +impl Example for App { + fn render(&mut self, + api: &RenderApi, + builder: &mut DisplayListBuilder, + resources: &mut ResourceUpdates, + _layout_size: LayoutSize, + _pipeline_id: PipelineId, + _document_id: DocumentId) { + let bounds = (0,0).to(512, 512); + builder.push_stacking_context(ScrollPolicy::Scrollable, + bounds, + None, + TransformStyle::Flat, + None, + MixBlendMode::Normal, + Vec::new()); + + let x0 = 50.0; + let y0 = 50.0; + let image_size = LayoutSize::new(4.0, 4.0); + + if self.swap_keys.is_empty() { + let key0 = api.generate_image_key(); + let key1 = api.generate_image_key(); + + self.image_generator.generate_image(128); + resources.add_image( + key0, + ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true), + ImageData::new(self.image_generator.take()), + None, + ); + + self.image_generator.generate_image(128); + resources.add_image( + key1, + ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true), + ImageData::new(self.image_generator.take()), + None, + ); + + self.swap_keys.push(key0); + self.swap_keys.push(key1); + } + + for (i, key) in self.stress_keys.iter().enumerate() { + let x = (i % 128) as f32; + let y = (i / 128) as f32; + + builder.push_image( + LayoutRect::new(LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y), image_size), + Some(LocalClip::from(bounds)), + image_size, + LayoutSize::zero(), + ImageRendering::Auto, + *key + ); + } + + if let Some(image_key) = self.image_key { + let image_size = LayoutSize::new(100.0, 100.0); + + builder.push_image( + LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size), + Some(LocalClip::from(bounds)), + image_size, + LayoutSize::zero(), + ImageRendering::Auto, + image_key + ); + } + + let swap_key = self.swap_keys[self.swap_index]; + let image_size = LayoutSize::new(64.0, 64.0); + + builder.push_image( + LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size), + Some(LocalClip::from(bounds)), + image_size, + LayoutSize::zero(), + ImageRendering::Auto, + swap_key + ); + self.swap_index = 1 - self.swap_index; + + builder.pop_stacking_context(); + } + + fn on_event(&mut self, + event: glutin::Event, + api: &RenderApi, + _document_id: DocumentId) -> bool { + match event { + glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => { + let mut updates = ResourceUpdates::new(); + + match key { + glutin::VirtualKeyCode::S => { + self.stress_keys.clear(); + + for _ in 0..16 { + for _ in 0..16 { + let size = 4; + + let image_key = api.generate_image_key(); + + self.image_generator.generate_image(size); + + updates.add_image( + image_key, + ImageDescriptor::new(size, size, ImageFormat::BGRA8, true), + ImageData::new(self.image_generator.take()), + None, + ); + + self.stress_keys.push(image_key); + } + } + } + glutin::VirtualKeyCode::D => { + if let Some(image_key) = self.image_key.take() { + updates.delete_image(image_key); + } + } + glutin::VirtualKeyCode::U => { + if let Some(image_key) = self.image_key { + let size = 128; + self.image_generator.generate_image(size); + + updates.update_image( + image_key, + ImageDescriptor::new(size, size, ImageFormat::BGRA8, true), + ImageData::new(self.image_generator.take()), + None, + ); + } + } + glutin::VirtualKeyCode::E => { + if let Some(image_key) = self.image_key.take() { + updates.delete_image(image_key); + } + + let size = 32; + let image_key = api.generate_image_key(); + + let image_data = ExternalImageData { + id: ExternalImageId(0), + channel_index: size as u8, + image_type: ExternalImageType::ExternalBuffer, + }; + + updates.add_image( + image_key, + ImageDescriptor::new(size, size, ImageFormat::BGRA8, true), + ImageData::External(image_data), + None, + ); + + self.image_key = Some(image_key); + } + glutin::VirtualKeyCode::R => { + if let Some(image_key) = self.image_key.take() { + updates.delete_image(image_key); + } + + let image_key = api.generate_image_key(); + let size = 32; + self.image_generator.generate_image(size); + + updates.add_image( + image_key, + ImageDescriptor::new(size, size, ImageFormat::BGRA8, true), + ImageData::new(self.image_generator.take()), + None, + ); + + self.image_key = Some(image_key); + } + _ => {} + } + + api.update_resources(updates); + return true; + } + _ => {} + } + + false + } + + fn get_external_image_handler(&self) -> Option> { + Some(Box::new(ImageGenerator::new())) + } +} + +fn main() { + let mut app = App { + image_key: None, + stress_keys: Vec::new(), + image_generator: ImageGenerator::new(), + swap_keys: Vec::new(), + swap_index: 0, + }; + boilerplate::main_wrapper(&mut app, None); +} diff --git a/gfx/webrender/res/clip_shared.glsl b/gfx/webrender/res/clip_shared.glsl index 59de7bb87df2..d773d5838d10 100644 --- a/gfx/webrender/res/clip_shared.glsl +++ b/gfx/webrender/res/clip_shared.glsl @@ -13,16 +13,15 @@ in int aClipRenderTaskIndex; in int aClipLayerIndex; -in int aClipDataIndex; -in int aClipSegmentIndex; -in int aClipResourceAddress; +in int aClipSegment; +in ivec4 aClipDataResourceAddress; struct CacheClipInstance { int render_task_index; int layer_index; - int data_index; - int segment_index; - int resource_address; + int segment; + ivec2 clip_data_address; + ivec2 resource_address; }; CacheClipInstance fetch_clip_item(int index) { @@ -30,9 +29,9 @@ CacheClipInstance fetch_clip_item(int index) { cci.render_task_index = aClipRenderTaskIndex; cci.layer_index = aClipLayerIndex; - cci.data_index = aClipDataIndex; - cci.segment_index = aClipSegmentIndex; - cci.resource_address = aClipResourceAddress; + cci.segment = aClipSegment; + cci.clip_data_address = aClipDataResourceAddress.xy; + cci.resource_address = aClipDataResourceAddress.zw; return cci; } @@ -48,7 +47,7 @@ struct ClipVertexInfo { ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, Layer layer, ClipArea area, - int segment_index) { + int segment) { RectWithSize clipped_local_rect = intersect_rect(local_clip_rect, layer.local_clip_rect); @@ -59,7 +58,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, vec2 inner_p1 = area.inner_rect.zw; vec2 p0, p1; - switch (segment_index) { + switch (segment) { case SEGMENT_ALL: p0 = outer_p0; p1 = outer_p1; diff --git a/gfx/webrender/res/cs_clip_border.vs.glsl b/gfx/webrender/res/cs_clip_border.vs.glsl index f9f75b4637d7..619ab427181f 100644 --- a/gfx/webrender/res/cs_clip_border.vs.glsl +++ b/gfx/webrender/res/cs_clip_border.vs.glsl @@ -21,8 +21,8 @@ struct BorderCorner { int clip_mode; }; -BorderCorner fetch_border_corner(int index) { - vec4 data[2] = fetch_from_resource_cache_2(index); +BorderCorner fetch_border_corner(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); return BorderCorner(RectWithSize(data[0].xy, data[0].zw), data[1].xy, int(data[1].z), @@ -35,8 +35,8 @@ struct BorderClipDash { vec4 point_tangent_1; }; -BorderClipDash fetch_border_clip_dash(int index) { - vec4 data[2] = fetch_from_resource_cache_2(index); +BorderClipDash fetch_border_clip_dash(ivec2 address, int segment) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address + ivec2(2 + 2 * (segment - 1), 0)); return BorderClipDash(data[0], data[1]); } @@ -45,8 +45,8 @@ struct BorderClipDot { vec3 center_radius; }; -BorderClipDot fetch_border_clip_dot(int index) { - vec4 data = fetch_from_resource_cache_1(index); +BorderClipDot fetch_border_clip_dot(ivec2 address, int segment) { + vec4 data = fetch_from_resource_cache_1_direct(address + ivec2(2 + (segment - 1), 0)); return BorderClipDot(data.xyz); } @@ -56,10 +56,10 @@ void main(void) { Layer layer = fetch_layer(cci.layer_index); // Fetch the header information for this corner clip. - BorderCorner corner = fetch_border_corner(cci.data_index); + BorderCorner corner = fetch_border_corner(cci.clip_data_address); vClipCenter = corner.clip_center; - if (cci.segment_index == 0) { + if (cci.segment == 0) { // The first segment is used to zero out the border corner. vAlphaMask = vec2(0.0); vDotParams = vec3(0.0); @@ -85,7 +85,7 @@ void main(void) { switch (corner.clip_mode) { case CLIP_MODE_DASH: { // Fetch the information about this particular dash. - BorderClipDash dash = fetch_border_clip_dash(cci.data_index + 2 + 2 * (cci.segment_index - 1)); + BorderClipDash dash = fetch_border_clip_dash(cci.clip_data_address, cci.segment); vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy; vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy; vDotParams = vec3(0.0); @@ -93,7 +93,7 @@ void main(void) { break; } case CLIP_MODE_DOT: { - BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + 2 + (cci.segment_index - 1)); + BorderClipDot cdot = fetch_border_clip_dot(cci.clip_data_address, cci.segment); vPoint_Tangent0 = vec4(1.0); vPoint_Tangent1 = vec4(1.0); vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z); diff --git a/gfx/webrender/res/cs_clip_image.fs.glsl b/gfx/webrender/res/cs_clip_image.fs.glsl index 20d2bc3e9dd9..54768997f621 100644 --- a/gfx/webrender/res/cs_clip_image.fs.glsl +++ b/gfx/webrender/res/cs_clip_image.fs.glsl @@ -11,7 +11,7 @@ void main(void) { clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)); vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy, vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw); - float clip_alpha = texture(sColor0, source_uv).r; //careful: texture has type A8 + float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8 oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0); } diff --git a/gfx/webrender/res/cs_clip_image.glsl b/gfx/webrender/res/cs_clip_image.glsl index 0960a724bd8c..8a8f1e3cdd2f 100644 --- a/gfx/webrender/res/cs_clip_image.glsl +++ b/gfx/webrender/res/cs_clip_image.glsl @@ -7,3 +7,4 @@ varying vec3 vPos; flat varying vec4 vClipMaskUvRect; flat varying vec4 vClipMaskUvInnerRect; +flat varying float vLayer; diff --git a/gfx/webrender/res/cs_clip_image.vs.glsl b/gfx/webrender/res/cs_clip_image.vs.glsl index d992a7a6dd80..5027b3eacd2c 100644 --- a/gfx/webrender/res/cs_clip_image.vs.glsl +++ b/gfx/webrender/res/cs_clip_image.vs.glsl @@ -7,8 +7,8 @@ struct ImageMaskData { RectWithSize local_rect; }; -ImageMaskData fetch_mask_data(int index) { - vec4 data = fetch_from_resource_cache_1(index); +ImageMaskData fetch_mask_data(ivec2 address) { + vec4 data = fetch_from_resource_cache_1_direct(address); return ImageMaskData(RectWithSize(data.xy, data.zw)); } @@ -16,16 +16,17 @@ void main(void) { CacheClipInstance cci = fetch_clip_item(gl_InstanceID); ClipArea area = fetch_clip_area(cci.render_task_index); Layer layer = fetch_layer(cci.layer_index); - ImageMaskData mask = fetch_mask_data(cci.data_index); + ImageMaskData mask = fetch_mask_data(cci.clip_data_address); RectWithSize local_rect = mask.local_rect; - ImageResource res = fetch_image_resource(cci.resource_address); + ImageResource res = fetch_image_resource_direct(cci.resource_address); ClipVertexInfo vi = write_clip_tile_vertex(local_rect, layer, area, - cci.segment_index); + cci.segment); vPos = vi.local_pos; + vLayer = res.layer; vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0); vec2 texture_size = vec2(textureSize(sColor0, 0)); diff --git a/gfx/webrender/res/cs_clip_rectangle.vs.glsl b/gfx/webrender/res/cs_clip_rectangle.vs.glsl index 58f1bc37c7fa..24a21a21af09 100644 --- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl +++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl @@ -8,8 +8,8 @@ struct ClipRect { vec4 mode; }; -ClipRect fetch_clip_rect(int index) { - vec4 data[2] = fetch_from_resource_cache_2(index); +ClipRect fetch_clip_rect(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); } @@ -18,8 +18,9 @@ struct ClipCorner { vec4 outer_inner_radius; }; -ClipCorner fetch_clip_corner(int index) { - vec4 data[2] = fetch_from_resource_cache_2(index); +ClipCorner fetch_clip_corner(ivec2 address, int index) { + address += ivec2(2 + 2 * index, 0); + vec4 data[2] = fetch_from_resource_cache_2_direct(address); return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); } @@ -31,14 +32,14 @@ struct ClipData { ClipCorner bottom_right; }; -ClipData fetch_clip(int index) { +ClipData fetch_clip(ivec2 address) { ClipData clip; - clip.rect = fetch_clip_rect(index + 0); - clip.top_left = fetch_clip_corner(index + 2); - clip.top_right = fetch_clip_corner(index + 4); - clip.bottom_left = fetch_clip_corner(index + 6); - clip.bottom_right = fetch_clip_corner(index + 8); + clip.rect = fetch_clip_rect(address); + clip.top_left = fetch_clip_corner(address, 0); + clip.top_right = fetch_clip_corner(address, 1); + clip.bottom_left = fetch_clip_corner(address, 2); + clip.bottom_right = fetch_clip_corner(address, 3); return clip; } @@ -47,13 +48,13 @@ void main(void) { CacheClipInstance cci = fetch_clip_item(gl_InstanceID); ClipArea area = fetch_clip_area(cci.render_task_index); Layer layer = fetch_layer(cci.layer_index); - ClipData clip = fetch_clip(cci.data_index); + ClipData clip = fetch_clip(cci.clip_data_address); RectWithSize local_rect = clip.rect.rect; ClipVertexInfo vi = write_clip_tile_vertex(local_rect, layer, area, - cci.segment_index); + cci.segment); vPos = vi.local_pos; vClipMode = clip.rect.mode.x; diff --git a/gfx/webrender/res/cs_text_run.glsl b/gfx/webrender/res/cs_text_run.glsl index 39ed226b2646..5dcde8b661e0 100644 --- a/gfx/webrender/res/cs_text_run.glsl +++ b/gfx/webrender/res/cs_text_run.glsl @@ -3,5 +3,5 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -varying vec2 vUv; +varying vec3 vUv; flat varying vec4 vColor; diff --git a/gfx/webrender/res/cs_text_run.vs.glsl b/gfx/webrender/res/cs_text_run.vs.glsl index ab43c8c4e896..b018fdf4d036 100644 --- a/gfx/webrender/res/cs_text_run.vs.glsl +++ b/gfx/webrender/res/cs_text_run.vs.glsl @@ -47,7 +47,7 @@ void main(void) { local_rect.xy + local_rect.zw, aPosition.xy); - vUv = mix(st0, st1, aPosition.xy); + vUv = vec3(mix(st0, st1, aPosition.xy), res.layer); vColor = shadow.color; gl_Position = uTransform * vec4(pos, 0.0, 1.0); diff --git a/gfx/webrender/res/debug_font.fs.glsl b/gfx/webrender/res/debug_font.fs.glsl index 46acfb5b0e91..e77ab29bed6d 100644 --- a/gfx/webrender/res/debug_font.fs.glsl +++ b/gfx/webrender/res/debug_font.fs.glsl @@ -7,10 +7,6 @@ varying vec4 vColor; void main(void) { -#ifdef SERVO_ES2 - float alpha = texture(sColor0, vColorTexCoord.xy).a; -#else - float alpha = texture(sColor0, vColorTexCoord.xy).r; -#endif + float alpha = texture(sColor0, vec3(vColorTexCoord.xy, 0.0)).r; oFragColor = vec4(vColor.xyz, vColor.w * alpha); } diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index 90f89dc573a7..dd35f960b851 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -3,25 +3,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#if defined(GL_ES) - #if GL_ES == 1 - #ifdef GL_FRAGMENT_PRECISION_HIGH - precision highp sampler2DArray; - #else - precision mediump sampler2DArray; - #endif - - // Sampler default precision is lowp on mobile GPUs. - // This causes RGBA32F texture data to be clamped to 16 bit floats on some GPUs (e.g. Mali-T880). - // Define highp precision macro to allow lossless FLOAT texture sampling. - #define HIGHP_SAMPLER_FLOAT highp - #else - #define HIGHP_SAMPLER_FLOAT - #endif -#else - #define HIGHP_SAMPLER_FLOAT -#endif - #define PST_TOP_LEFT 0 #define PST_TOP 1 #define PST_TOP_RIGHT 2 @@ -129,6 +110,13 @@ ivec2 get_resource_cache_uv(int address) { uniform HIGHP_SAMPLER_FLOAT sampler2D sResourceCache; +vec4[2] fetch_from_resource_cache_2_direct(ivec2 address) { + return vec4[2]( + texelFetchOffset(sResourceCache, address, 0, ivec2(0, 0)), + texelFetchOffset(sResourceCache, address, 0, ivec2(1, 0)) + ); +} + vec4[2] fetch_from_resource_cache_2(int address) { ivec2 uv = get_resource_cache_uv(address); return vec4[2]( @@ -193,6 +181,10 @@ vec4[4] fetch_from_resource_cache_4(int address) { ); } +vec4 fetch_from_resource_cache_1_direct(ivec2 address) { + return texelFetch(sResourceCache, address, 0); +} + vec4 fetch_from_resource_cache_1(int address) { ivec2 uv = get_resource_cache_uv(address); return texelFetch(sResourceCache, uv, 0); @@ -771,21 +763,28 @@ TransformVertexInfo write_transform_vertex(RectWithSize instance_rect, struct GlyphResource { vec4 uv_rect; + float layer; vec2 offset; }; GlyphResource fetch_glyph_resource(int address) { vec4 data[2] = fetch_from_resource_cache_2(address); - return GlyphResource(data[0], data[1].xy); + return GlyphResource(data[0], data[1].x, data[1].yz); } struct ImageResource { vec4 uv_rect; + float layer; }; ImageResource fetch_image_resource(int address) { - vec4 data = fetch_from_resource_cache_1(address); - return ImageResource(data); + vec4 data[2] = fetch_from_resource_cache_2(address); + return ImageResource(data[0], data[1].x); +} + +ImageResource fetch_image_resource_direct(ivec2 address) { + vec4 data[2] = fetch_from_resource_cache_2_direct(address); + return ImageResource(data[0], data[1].x); } struct Rectangle { diff --git a/gfx/webrender/res/ps_cache_image.fs.glsl b/gfx/webrender/res/ps_cache_image.fs.glsl index 7a70bc4a6f9c..566039fa37b9 100644 --- a/gfx/webrender/res/ps_cache_image.fs.glsl +++ b/gfx/webrender/res/ps_cache_image.fs.glsl @@ -5,5 +5,5 @@ void main(void) { vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw); - oFragColor = texture(sCacheRGBA8, vec3(uv, vUv.z)); + oFragColor = texture(sColor0, vec3(uv, vUv.z)); } diff --git a/gfx/webrender/res/ps_image.fs.glsl b/gfx/webrender/res/ps_image.fs.glsl index db45514ab0c9..d31a2bf41ade 100644 --- a/gfx/webrender/res/ps_image.fs.glsl +++ b/gfx/webrender/res/ps_image.fs.glsl @@ -28,5 +28,5 @@ void main(void) { alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize)))); - oFragColor = vec4(alpha) * TEX_SAMPLE(sColor0, st); + oFragColor = vec4(alpha) * TEX_SAMPLE(sColor0, vec3(st, vLayer)); } diff --git a/gfx/webrender/res/ps_image.glsl b/gfx/webrender/res/ps_image.glsl index 85872f6c4b66..93138dd09db9 100644 --- a/gfx/webrender/res/ps_image.glsl +++ b/gfx/webrender/res/ps_image.glsl @@ -9,6 +9,7 @@ flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas flat varying vec2 vTextureSize; // Size of the image in the texture atlas. flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image. flat varying vec4 vStRect; // Rectangle of valid texture rect. +flat varying float vLayer; #ifdef WR_FEATURE_TRANSFORM varying vec3 vLocalPos; diff --git a/gfx/webrender/res/ps_image.vs.glsl b/gfx/webrender/res/ps_image.vs.glsl index 6fb33a5937e9..bf9e15d107b2 100644 --- a/gfx/webrender/res/ps_image.vs.glsl +++ b/gfx/webrender/res/ps_image.vs.glsl @@ -50,6 +50,7 @@ void main(void) { vec2 st0 = uv0 / texture_size_normalization_factor; vec2 st1 = uv1 / texture_size_normalization_factor; + vLayer = res.layer; vTextureSize = st1 - st0; vTextureOffset = st0; vTileSpacing = image.stretch_size_and_tile_spacing.zw; diff --git a/gfx/webrender/res/ps_text_run.fs.glsl b/gfx/webrender/res/ps_text_run.fs.glsl index b21ac22949ea..55ccdccfaa7e 100644 --- a/gfx/webrender/res/ps_text_run.fs.glsl +++ b/gfx/webrender/res/ps_text_run.fs.glsl @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ void main(void) { - vec2 tc = clamp(vUv, vUvBorder.xy, vUvBorder.zw); + vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z); #ifdef WR_FEATURE_SUBPIXEL_AA //note: the blend mode is not compatible with clipping oFragColor = texture(sColor0, tc); diff --git a/gfx/webrender/res/ps_text_run.glsl b/gfx/webrender/res/ps_text_run.glsl index a5915da61f26..25f632bd50ba 100644 --- a/gfx/webrender/res/ps_text_run.glsl +++ b/gfx/webrender/res/ps_text_run.glsl @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ flat varying vec4 vColor; -varying vec2 vUv; +varying vec3 vUv; flat varying vec4 vUvBorder; #ifdef WR_FEATURE_TRANSFORM diff --git a/gfx/webrender/res/ps_text_run.vs.glsl b/gfx/webrender/res/ps_text_run.vs.glsl index 8e874e3a961a..12ecf1b68420 100644 --- a/gfx/webrender/res/ps_text_run.vs.glsl +++ b/gfx/webrender/res/ps_text_run.vs.glsl @@ -48,6 +48,6 @@ void main(void) { vec2 st1 = res.uv_rect.zw / texture_size; vColor = text.color; - vUv = mix(st0, st1, f); + vUv = vec3(mix(st0, st1, f), res.layer); vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } diff --git a/gfx/webrender/res/ps_yuv_image.fs.glsl b/gfx/webrender/res/ps_yuv_image.fs.glsl index 3698841d8603..81b134e2c2f6 100644 --- a/gfx/webrender/res/ps_yuv_image.fs.glsl +++ b/gfx/webrender/res/ps_yuv_image.fs.glsl @@ -73,17 +73,17 @@ void main(void) { // "The Y, Cb and Cr color channels within the 422 data are mapped into // the existing green, blue and red color channels." // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt - yuv_value = TEX_SAMPLE(sColor0, st_y).gbr; + yuv_value = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).gbr; #elif defined(WR_FEATURE_NV12) - yuv_value.x = TEX_SAMPLE(sColor0, st_y).r; - yuv_value.yz = TEX_SAMPLE(sColor1, st_u).rg; + yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r; + yuv_value.yz = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).rg; #else // The yuv_planar format should have this third texture coordinate. vec2 st_v = vTextureOffsetV + uv_offset; - yuv_value.x = TEX_SAMPLE(sColor0, st_y).r; - yuv_value.y = TEX_SAMPLE(sColor1, st_u).r; - yuv_value.z = TEX_SAMPLE(sColor2, st_v).r; + yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r; + yuv_value.y = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).r; + yuv_value.z = TEX_SAMPLE(sColor2, vec3(st_v, vLayers.z)).r; #endif // See the YuvColorMatrix definition for an explanation of where the constants come from. diff --git a/gfx/webrender/res/ps_yuv_image.glsl b/gfx/webrender/res/ps_yuv_image.glsl index 05bb9a0ff5c9..9280c4014eda 100644 --- a/gfx/webrender/res/ps_yuv_image.glsl +++ b/gfx/webrender/res/ps_yuv_image.glsl @@ -13,6 +13,7 @@ flat varying vec2 vTextureSizeUv; // Size of the u and v planes in the texture flat varying vec2 vStretchSize; flat varying vec2 vHalfTexelY; // Normalized length of the half of a Y texel. flat varying vec2 vHalfTexelUv; // Normalized length of the half of u and v texels. +flat varying vec3 vLayers; #ifdef WR_FEATURE_TRANSFORM varying vec3 vLocalPos; diff --git a/gfx/webrender/res/ps_yuv_image.vs.glsl b/gfx/webrender/res/ps_yuv_image.vs.glsl index 05fc50d7d821..65df2b64f4ca 100644 --- a/gfx/webrender/res/ps_yuv_image.vs.glsl +++ b/gfx/webrender/res/ps_yuv_image.vs.glsl @@ -26,10 +26,14 @@ void main(void) { write_clip(vi.screen_pos, prim.clip_area); ImageResource y_rect = fetch_image_resource(prim.user_data0); + vLayers = vec3(y_rect.layer, 0.0, 0.0); + #ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR // only 1 channel ImageResource u_rect = fetch_image_resource(prim.user_data1); + vLayers.y = u_rect.layer; #ifndef WR_FEATURE_NV12 // 2 channel ImageResource v_rect = fetch_image_resource(prim.user_data2); + vLayers.z = v_rect.layer; #endif #endif diff --git a/gfx/webrender/res/shared.glsl b/gfx/webrender/res/shared.glsl index b739b2697b5c..9d33175552d3 100644 --- a/gfx/webrender/res/shared.glsl +++ b/gfx/webrender/res/shared.glsl @@ -14,8 +14,8 @@ // The textureLod() doesn't support sampler2DRect for WR_FEATURE_TEXTURE_RECT, too. // // Use texture() instead. -#if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_RECT) -#define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord) +#if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_RECT) || defined(WR_FEATURE_TEXTURE_2D) +#define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord.xy) #else // In normal case, we use textureLod(). We haven't used the lod yet. So, we always pass 0.0 now. #define TEX_SAMPLE(sampler, tex_coord) textureLod(sampler, tex_coord, 0.0) @@ -52,7 +52,30 @@ //====================================================================================== // Shared shader uniforms //====================================================================================== -#ifdef WR_FEATURE_TEXTURE_RECT +#if defined(GL_ES) + #if GL_ES == 1 + #ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp sampler2DArray; + #else + precision mediump sampler2DArray; + #endif + + // Sampler default precision is lowp on mobile GPUs. + // This causes RGBA32F texture data to be clamped to 16 bit floats on some GPUs (e.g. Mali-T880). + // Define highp precision macro to allow lossless FLOAT texture sampling. + #define HIGHP_SAMPLER_FLOAT highp + #else + #define HIGHP_SAMPLER_FLOAT + #endif +#else + #define HIGHP_SAMPLER_FLOAT +#endif + +#ifdef WR_FEATURE_TEXTURE_2D +uniform sampler2D sColor0; +uniform sampler2D sColor1; +uniform sampler2D sColor2; +#elif defined WR_FEATURE_TEXTURE_RECT uniform sampler2DRect sColor0; uniform sampler2DRect sColor1; uniform sampler2DRect sColor2; @@ -61,9 +84,9 @@ uniform samplerExternalOES sColor0; uniform samplerExternalOES sColor1; uniform samplerExternalOES sColor2; #else -uniform sampler2D sColor0; -uniform sampler2D sColor1; -uniform sampler2D sColor2; +uniform sampler2DArray sColor0; +uniform sampler2DArray sColor1; +uniform sampler2DArray sColor2; #endif #ifdef WR_FEATURE_DITHERING diff --git a/gfx/webrender/src/clip_scroll_node.rs b/gfx/webrender/src/clip_scroll_node.rs index 83763c50f785..e7bec1b1559e 100644 --- a/gfx/webrender/src/clip_scroll_node.rs +++ b/gfx/webrender/src/clip_scroll_node.rs @@ -4,7 +4,9 @@ use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize}; use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId}; -use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, WorldPoint}; +use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo}; +use api::WorldPoint; +use clip_scroll_tree::TransformUpdateState; use geometry::ray_intersects_rect; use mask_cache::{ClipRegion, ClipSource, MaskCacheInfo}; use spring::{DAMPING, STIFFNESS, Spring}; @@ -61,15 +63,21 @@ impl ClipInfo { #[derive(Clone, Debug)] pub enum NodeType { - /// Transform for this layer, relative to parent reference frame. A reference - /// frame establishes a new coordinate space in the tree. - ReferenceFrame(LayerToScrollTransform), + /// A reference frame establishes a new coordinate space in the tree. + ReferenceFrame(ReferenceFrameInfo), /// Other nodes just do clipping, but no transformation. Clip(ClipInfo), - /// Other nodes just do clipping, but no transformation. + /// Transforms it's content, but doesn't clip it. Can also be adjusted + /// by scroll events or setting scroll offsets. ScrollFrame(ScrollingState), + + /// A special kind of node that adjusts its position based on the position + /// of its parent node and a given set of sticky positioning constraints. + /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here: + /// https://www.w3.org/TR/css-position-3/#sticky-pos + StickyFrame(StickyFrameInfo), } /// Contains information common among all types of ClipScrollTree nodes. @@ -159,9 +167,15 @@ impl ClipScrollNode { pub fn new_reference_frame(parent_id: Option, local_viewport_rect: &LayerRect, content_size: LayerSize, - local_transform: &LayerToScrollTransform, + transform: &LayerToScrollTransform, + origin_in_parent_reference_frame: LayerVector2D, pipeline_id: PipelineId) -> ClipScrollNode { + let info = ReferenceFrameInfo { + transform: *transform, + origin_in_parent_reference_frame, + }; + ClipScrollNode { content_size, local_viewport_rect: *local_viewport_rect, @@ -173,26 +187,45 @@ impl ClipScrollNode { parent: parent_id, children: Vec::new(), pipeline_id, - node_type: NodeType::ReferenceFrame(*local_transform), + node_type: NodeType::ReferenceFrame(info), } } + pub fn new_sticky_frame(parent_id: ClipId, + frame_rect: LayerRect, + sticky_frame_info: StickyFrameInfo, + pipeline_id: PipelineId) + -> ClipScrollNode { + ClipScrollNode { + content_size: frame_rect.size, + local_viewport_rect: frame_rect, + local_clip_rect: frame_rect, + combined_local_viewport_rect: LayerRect::zero(), + world_viewport_transform: LayerToWorldTransform::identity(), + world_content_transform: LayerToWorldTransform::identity(), + reference_frame_relative_scroll_offset: LayerVector2D::zero(), + parent: Some(parent_id), + children: Vec::new(), + pipeline_id, + node_type: NodeType::StickyFrame(sticky_frame_info), + } + } + + pub fn add_child(&mut self, child: ClipId) { self.children.push(child); } pub fn apply_old_scrolling_state(&mut self, new_scrolling: &ScrollingState) { match self.node_type { - NodeType::ReferenceFrame(_) | NodeType::Clip(_) => { - if new_scrolling.offset != LayerVector2D::zero() { - warn!("Tried to scroll a non-scroll node."); - } - } NodeType::ScrollFrame(ref mut scrolling) => { let scroll_sensitivity = scrolling.scroll_sensitivity; *scrolling = *new_scrolling; scrolling.scroll_sensitivity = scroll_sensitivity; } + _ if new_scrolling.offset != LayerVector2D::zero() => + warn!("Tried to scroll a non-scroll node."), + _ => {} } } @@ -201,11 +234,11 @@ impl ClipScrollNode { let scrollable_width = self.scrollable_width(); let scrolling = match self.node_type { - NodeType::ReferenceFrame(_) | NodeType::Clip(_) => { + NodeType::ScrollFrame(ref mut scrolling) => scrolling, + _ => { warn!("Tried to scroll a non-scroll node."); return false; } - NodeType::ScrollFrame(ref mut scrolling) => scrolling, }; let new_offset = match clamp { @@ -231,41 +264,53 @@ impl ClipScrollNode { true } - pub fn update_transform(&mut self, - parent_reference_frame_transform: &LayerToWorldTransform, - parent_combined_viewport_rect: &LayerRect, - parent_scroll_offset: LayerVector2D, - parent_accumulated_scroll_offset: LayerVector2D) { + pub fn update_transform(&mut self, state: &TransformUpdateState) { + let scrolled_parent_combined_clip = state.parent_combined_viewport_rect + .translate(&-state.parent_scroll_offset); - let scrolled_parent_combined_clip = parent_combined_viewport_rect - .translate(&-parent_scroll_offset); - - let (local_transform, combined_clip, reference_frame_scroll_offset) = match self.node_type { - NodeType::ReferenceFrame(transform) => { - let combined_clip = transform.with_destination::() - .inverse_rect_footprint(&scrolled_parent_combined_clip); - (transform, combined_clip, LayerVector2D::zero()) + let (local_transform, accumulated_scroll_offset) = match self.node_type { + NodeType::ReferenceFrame(ref info) => { + self.combined_local_viewport_rect = + info.transform.with_destination::() + .inverse_rect_footprint(&scrolled_parent_combined_clip); + self.reference_frame_relative_scroll_offset = LayerVector2D::zero(); + (info.transform, state.parent_accumulated_scroll_offset) } NodeType::Clip(_) | NodeType::ScrollFrame(_) => { // Move the parent's viewport into the local space (of the node origin) // and intersect with the local clip rectangle to get the local viewport. - let combined_clip = scrolled_parent_combined_clip.intersection(&self.local_clip_rect) - .unwrap_or(LayerRect::zero()); - (LayerToScrollTransform::identity(), combined_clip, parent_accumulated_scroll_offset) + self.combined_local_viewport_rect = + scrolled_parent_combined_clip.intersection(&self.local_clip_rect) + .unwrap_or(LayerRect::zero()); + self.reference_frame_relative_scroll_offset = + state.parent_accumulated_scroll_offset; + (LayerToScrollTransform::identity(), self.reference_frame_relative_scroll_offset) + } + NodeType::StickyFrame(sticky_frame_info) => { + let sticky_offset = + self.calculate_sticky_offset(&self.local_viewport_rect, + &sticky_frame_info, + &state.nearest_scrolling_ancestor_offset, + &state.nearest_scrolling_ancestor_viewport); + + self.combined_local_viewport_rect = + scrolled_parent_combined_clip.translate(&-sticky_offset) + .intersection(&self.local_clip_rect) + .unwrap_or(LayerRect::zero()); + self.reference_frame_relative_scroll_offset = + state.parent_accumulated_scroll_offset + sticky_offset; + (LayerToScrollTransform::identity(), self.reference_frame_relative_scroll_offset) } }; - self.combined_local_viewport_rect = combined_clip; - self.reference_frame_relative_scroll_offset = reference_frame_scroll_offset; - // The transformation for this viewport in world coordinates is the transformation for // our parent reference frame, plus any accumulated scrolling offsets from nodes // between our reference frame and this node. For reference frames, we also include // whatever local transformation this reference frame provides. This can be combined // with the local_viewport_rect to get its position in world space. self.world_viewport_transform = - parent_reference_frame_transform - .pre_translate(parent_accumulated_scroll_offset.to_3d()) + state.parent_reference_frame_transform + .pre_translate(accumulated_scroll_offset.to_3d()) .pre_mul(&local_transform.with_destination::()); // The transformation for any content inside of us is the viewport transformation, plus @@ -275,6 +320,44 @@ impl ClipScrollNode { self.world_viewport_transform.pre_translate(scroll_offset.to_3d()); } + fn calculate_sticky_offset(&self, + sticky_rect: &LayerRect, + sticky_frame_info: &StickyFrameInfo, + viewport_scroll_offset: &LayerVector2D, + viewport_rect: &LayerRect) + -> LayerVector2D { + let sticky_rect = sticky_rect.translate(viewport_scroll_offset); + let mut sticky_offset = LayerVector2D::zero(); + + if let Some(info) = sticky_frame_info.top { + sticky_offset.y = viewport_rect.min_y() + info.margin - sticky_rect.min_y(); + sticky_offset.y = sticky_offset.y.max(0.0).min(info.max_offset); + } + + if sticky_offset.y == 0.0 { + if let Some(info) = sticky_frame_info.bottom { + sticky_offset.y = (viewport_rect.max_y() - info.margin) - + (sticky_offset.y + sticky_rect.min_y() + sticky_rect.size.height); + sticky_offset.y = sticky_offset.y.min(0.0).max(info.max_offset); + } + } + + if let Some(info) = sticky_frame_info.left { + sticky_offset.x = viewport_rect.min_x() + info.margin - sticky_rect.min_x(); + sticky_offset.x = sticky_offset.x.max(0.0).min(info.max_offset); + } + + if sticky_offset.x == 0.0 { + if let Some(info) = sticky_frame_info.right { + sticky_offset.x = (viewport_rect.max_x() - info.margin) - + (sticky_offset.x + sticky_rect.min_x() + sticky_rect.size.width); + sticky_offset.x = sticky_offset.x.min(0.0).max(info.max_offset); + } + } + + sticky_offset + } + pub fn scrollable_height(&self) -> f32 { self.content_size.height - self.local_viewport_rect.size.height } @@ -288,8 +371,8 @@ impl ClipScrollNode { let scrollable_height = self.scrollable_height(); let scrolling = match self.node_type { - NodeType::ReferenceFrame(_) | NodeType::Clip(_) => return false, NodeType::ScrollFrame(ref mut scrolling) => scrolling, + _ => return false, }; if scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) { @@ -470,3 +553,15 @@ impl ScrollingState { } } +/// Contains information about reference frames. +#[derive(Copy, Clone, Debug)] +pub struct ReferenceFrameInfo { + /// The transformation that establishes this reference frame, relative to the parent + /// reference frame. The origin of the reference frame is included in the transformation. + pub transform: LayerToScrollTransform, + + /// The original, not including the transform and relative to the parent reference frame, + /// origin of this reference frame. This is already rolled into the `transform' property, but + /// we also store it here to properly transform the viewport for sticky positioning. + pub origin_in_parent_reference_frame: LayerVector2D, +} diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index 8bd29d0539cb..d7feb17c52d5 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -5,9 +5,9 @@ use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState}; use internal_types::{FastHashSet, FastHashMap}; use print_tree::PrintTree; -use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform}; -use api::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase}; -use api::{LayerVector2D, ScrollLayerState, ScrollLocation, WorldPoint}; +use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform, LayerToWorldTransform}; +use api::{LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState}; +use api::{ScrollLocation, StickyFrameInfo, WorldPoint}; pub type ScrollStates = FastHashMap; @@ -37,6 +37,15 @@ pub struct ClipScrollTree { pub pipelines_to_discard: FastHashSet, } +pub struct TransformUpdateState { + pub parent_reference_frame_transform: LayerToWorldTransform, + pub parent_combined_viewport_rect: LayerRect, + pub parent_scroll_offset: LayerVector2D, + pub parent_accumulated_scroll_offset: LayerVector2D, + pub nearest_scrolling_ancestor_offset: LayerVector2D, + pub nearest_scrolling_ancestor_viewport: LayerRect, +} + impl ClipScrollTree { pub fn new() -> ClipScrollTree { let dummy_pipeline = PipelineId::dummy(); @@ -226,68 +235,62 @@ impl ClipScrollTree { let root_reference_frame_id = self.root_reference_frame_id(); let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect; - self.update_node_transform(root_reference_frame_id, - &LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0), - &root_viewport, - LayerVector2D::zero(), - LayerVector2D::zero()); + let state = TransformUpdateState { + parent_reference_frame_transform: + LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0), + parent_combined_viewport_rect: root_viewport, + parent_scroll_offset: LayerVector2D::zero(), + parent_accumulated_scroll_offset: LayerVector2D::zero(), + nearest_scrolling_ancestor_offset: LayerVector2D::zero(), + nearest_scrolling_ancestor_viewport: LayerRect::zero(), + }; + self.update_node_transform(root_reference_frame_id, &state); } - fn update_node_transform(&mut self, - layer_id: ClipId, - parent_reference_frame_transform: &LayerToWorldTransform, - parent_viewport_rect: &LayerRect, - parent_scroll_offset: LayerVector2D, - parent_accumulated_scroll_offset: LayerVector2D) { + fn update_node_transform(&mut self, layer_id: ClipId, state: &TransformUpdateState) { // TODO(gw): This is an ugly borrow check workaround to clone these. // Restructure this to avoid the clones! - let (reference_frame_transform, - combined_local_viewport_rect, - scroll_offset, - accumulated_scroll_offset, - node_children) = { - + let (state, node_children) = { let mut node = match self.nodes.get_mut(&layer_id) { Some(node) => node, None => return, }; - node.update_transform(parent_reference_frame_transform, - parent_viewport_rect, - parent_scroll_offset, - parent_accumulated_scroll_offset); + node.update_transform(&state); // The transformation we are passing is the transformation of the parent // reference frame and the offset is the accumulated offset of all the nodes // between us and the parent reference frame. If we are a reference frame, // we need to reset both these values. - let (reference_frame_transform, scroll_offset, accumulated_scroll_offset) = match node.node_type { - NodeType::ReferenceFrame(..) => - (node.world_viewport_transform, - LayerVector2D::zero(), - LayerVector2D::zero()), - NodeType::Clip(..) => - (*parent_reference_frame_transform, - LayerVector2D::zero(), - parent_accumulated_scroll_offset), - NodeType::ScrollFrame(ref scrolling) => - (*parent_reference_frame_transform, - scrolling.offset, - scrolling.offset + parent_accumulated_scroll_offset), + let state = match node.node_type { + NodeType::ReferenceFrame(ref info) => TransformUpdateState { + parent_reference_frame_transform: node.world_viewport_transform, + parent_combined_viewport_rect: node.combined_local_viewport_rect, + parent_scroll_offset: LayerVector2D::zero(), + parent_accumulated_scroll_offset: LayerVector2D::zero(), + nearest_scrolling_ancestor_viewport: + state.nearest_scrolling_ancestor_viewport.translate(&info.origin_in_parent_reference_frame), + ..*state + }, + NodeType::Clip(..) | NodeType::StickyFrame(..) => TransformUpdateState { + parent_combined_viewport_rect: node.combined_local_viewport_rect, + parent_scroll_offset: LayerVector2D::zero(), + ..*state + }, + NodeType::ScrollFrame(ref scrolling) => TransformUpdateState { + parent_combined_viewport_rect: node.combined_local_viewport_rect, + parent_scroll_offset: scrolling.offset, + parent_accumulated_scroll_offset: scrolling.offset + state.parent_accumulated_scroll_offset, + nearest_scrolling_ancestor_offset: scrolling.offset, + nearest_scrolling_ancestor_viewport: node.local_viewport_rect, + ..*state + }, }; - (reference_frame_transform, - node.combined_local_viewport_rect, - scroll_offset, - accumulated_scroll_offset, - node.children.clone()) + (state, node.children.clone()) }; for child_layer_id in node_children { - self.update_node_transform(child_layer_id, - &reference_frame_transform, - &combined_local_viewport_rect, - scroll_offset, - accumulated_scroll_offset); + self.update_node_transform(child_layer_id, &state); } } @@ -320,6 +323,7 @@ impl ClipScrollTree { pub fn add_reference_frame(&mut self, rect: &LayerRect, transform: &LayerToScrollTransform, + origin_in_parent_reference_frame: LayerVector2D, pipeline_id: PipelineId, parent_id: Option) -> ClipId { @@ -328,11 +332,24 @@ impl ClipScrollTree { rect, rect.size, transform, + origin_in_parent_reference_frame, pipeline_id); self.add_node(node, reference_frame_id); reference_frame_id } + pub fn add_sticky_frame(&mut self, + id: ClipId, + parent_id: ClipId, + frame_rect: LayerRect, + sticky_frame_info: StickyFrameInfo) { + let node = ClipScrollNode::new_sticky_frame(parent_id, + frame_rect, + sticky_frame_info, + id.pipeline_id()); + self.add_node(node, id); + } + pub fn add_node(&mut self, node: ClipScrollNode, id: ClipId) { // When the parent node is None this means we are adding the root. match node.parent { @@ -368,13 +385,17 @@ impl ClipScrollTree { } pt.end_level(); } - NodeType::ReferenceFrame(ref transform) => { - pt.new_level(format!("ReferenceFrame {:?}", transform)); + NodeType::ReferenceFrame(ref info) => { + pt.new_level(format!("ReferenceFrame {:?}", info.transform)); } NodeType::ScrollFrame(scrolling_info) => { pt.new_level(format!("ScrollFrame")); pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset)); } + NodeType::StickyFrame(sticky_frame_info) => { + pt.new_level(format!("StickyFrame")); + pt.add_item(format!("sticky info: {:?}", sticky_frame_info)); + } } pt.add_item(format!("content_size: {:?}", node.content_size)); diff --git a/gfx/webrender/src/debug_render.rs b/gfx/webrender/src/debug_render.rs index 9b4b33e8b047..7b0d539174a1 100644 --- a/gfx/webrender/src/debug_render.rs +++ b/gfx/webrender/src/debug_render.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use debug_font_data; -use device::{Device, GpuMarker, Program, VAOId, TextureId, VertexDescriptor}; +use device::{Device, GpuMarker, Program, VAO, TextureId, VertexDescriptor}; use device::{TextureFilter, VertexAttribute, VertexUsageHint, VertexAttributeKind, TextureTarget}; use euclid::{Transform3D, Point2D, Size2D, Rect}; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler}; @@ -70,14 +70,14 @@ pub struct DebugRenderer { font_vertices: Vec, font_indices: Vec, font_program: Program, - font_vao: VAOId, + font_vao: VAO, font_texture_id: TextureId, tri_vertices: Vec, tri_indices: Vec, - tri_vao: VAOId, + tri_vao: VAO, line_vertices: Vec, - line_vao: VAOId, + line_vao: VAO, color_program: Program, } @@ -90,13 +90,14 @@ impl DebugRenderer { let line_vao = device.create_vao(&DESC_COLOR, 32); let tri_vao = device.create_vao(&DESC_COLOR, 32); - let font_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0]; + let font_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0]; device.init_texture(font_texture_id, debug_font_data::BMP_WIDTH, debug_font_data::BMP_HEIGHT, ImageFormat::A8, TextureFilter::Linear, RenderTargetMode::None, + 1, Some(&debug_font_data::FONT_BITMAP)); DebugRenderer { @@ -114,9 +115,12 @@ impl DebugRenderer { } } - pub fn deinit(&mut self, device: &mut Device) { - device.delete_program(&mut self.font_program); - device.delete_program(&mut self.color_program); + pub fn deinit(self, device: &mut Device) { + device.delete_program(self.font_program); + device.delete_program(self.color_program); + device.delete_vao(self.tri_vao); + device.delete_vao(self.line_vao); + device.delete_vao(self.font_vao); } pub fn line_height(&self) -> f32 { @@ -232,11 +236,11 @@ impl DebugRenderer { if !self.tri_vertices.is_empty() { device.bind_program(&self.color_program); device.set_uniforms(&self.color_program, &projection); - device.bind_vao(self.tri_vao); - device.update_vao_indices(self.tri_vao, + device.bind_vao(&self.tri_vao); + device.update_vao_indices(&self.tri_vao, &self.tri_indices, VertexUsageHint::Dynamic); - device.update_vao_main_vertices(self.tri_vao, + device.update_vao_main_vertices(&self.tri_vao, &self.tri_vertices, VertexUsageHint::Dynamic); device.draw_triangles_u32(0, self.tri_indices.len() as i32); @@ -246,8 +250,8 @@ impl DebugRenderer { if !self.line_vertices.is_empty() { device.bind_program(&self.color_program); device.set_uniforms(&self.color_program, &projection); - device.bind_vao(self.line_vao); - device.update_vao_main_vertices(self.line_vao, + device.bind_vao(&self.line_vao); + device.update_vao_main_vertices(&self.line_vao, &self.line_vertices, VertexUsageHint::Dynamic); device.draw_nonindexed_lines(0, self.line_vertices.len() as i32); @@ -258,11 +262,11 @@ impl DebugRenderer { device.bind_program(&self.font_program); device.set_uniforms(&self.font_program, &projection); device.bind_texture(TextureSampler::Color0, self.font_texture_id); - device.bind_vao(self.font_vao); - device.update_vao_indices(self.font_vao, + device.bind_vao(&self.font_vao); + device.update_vao_indices(&self.font_vao, &self.font_indices, VertexUsageHint::Dynamic); - device.update_vao_main_vertices(self.font_vao, + device.update_vao_main_vertices(&self.font_vao, &self.font_vertices, VertexUsageHint::Dynamic); device.draw_triangles_u32(0, self.font_indices.len() as i32); diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs index 66262679d417..c8f5f75ea130 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device.rs @@ -5,7 +5,6 @@ use euclid::Transform3D; use gleam::gl; use internal_types::{RenderTargetMode, TextureSampler, DEFAULT_TEXTURE, FastHashMap}; -//use notify::{self, Watcher}; use super::shader_source; use std::fs::File; use std::io::Read; @@ -15,7 +14,6 @@ use std::ops::Add; use std::path::PathBuf; use std::ptr; use std::rc::Rc; -//use std::sync::mpsc::{channel, Sender}; use std::thread; use api::{ColorF, ImageFormat}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize}; @@ -89,6 +87,7 @@ pub enum VertexAttributeKind { F32, U8Norm, I32, + U16, } #[derive(Debug)] @@ -159,6 +158,7 @@ impl VertexAttributeKind { VertexAttributeKind::F32 => 4, VertexAttributeKind::U8Norm => 1, VertexAttributeKind::I32 => 4, + VertexAttributeKind::U16 => 2, } } } @@ -201,6 +201,13 @@ impl VertexAttribute { stride, offset); } + VertexAttributeKind::U16 => { + gl.vertex_attrib_i_pointer(attr_index, + self.count as gl::GLint, + gl::UNSIGNED_SHORT, + stride, + offset); + } } } } @@ -296,6 +303,7 @@ impl FBOId { struct Texture { gl: Rc, id: gl::GLuint, + layer_count: i32, format: ImageFormat, width: u32, height: u32, @@ -361,36 +369,22 @@ impl Program { impl Drop for Program { fn drop(&mut self) { - debug_assert!(thread::panicking() || self.id == 0); + debug_assert!(thread::panicking() || self.id == 0, "renderer::deinit not called"); } } -struct VAO { - gl: Rc, +pub struct VAO { id: gl::GLuint, ibo_id: IBOId, main_vbo_id: VBOId, instance_vbo_id: VBOId, instance_stride: gl::GLint, - owns_indices: bool, - owns_vertices: bool, - owns_instances: bool, + owns_vertices_and_indices: bool, } impl Drop for VAO { fn drop(&mut self) { - self.gl.delete_vertex_arrays(&[self.id]); - - if self.owns_indices { - // todo(gw): maybe make these their own type with hashmap? - self.gl.delete_buffers(&[self.ibo_id.0]); - } - if self.owns_vertices { - self.gl.delete_buffers(&[self.main_vbo_id.0]); - } - if self.owns_instances { - self.gl.delete_buffers(&[self.instance_vbo_id.0]) - } + debug_assert!(thread::panicking() || self.id == 0, "renderer::deinit not called"); } } @@ -400,9 +394,6 @@ pub struct TextureId { target: gl::GLuint, } -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub struct VAOId(gl::GLuint); - #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] pub struct FBOId(gl::GLuint); @@ -690,75 +681,6 @@ impl UniformLocation { } } -// TODO(gw): Fix up notify cargo deps and re-enable this! -/* -enum FileWatcherCmd { - AddWatch(PathBuf), - Exit, -} - -struct FileWatcherThread { - api_tx: Sender, -} - -impl FileWatcherThread { - fn new(handler: Box) -> FileWatcherThread { - let (api_tx, api_rx) = channel(); - - thread::spawn(move || { - - let (watch_tx, watch_rx) = channel(); - - enum Request { - Watcher(notify::Event), - Command(FileWatcherCmd), - } - - let mut file_watcher: notify::RecommendedWatcher = notify::Watcher::new(watch_tx).unwrap(); - - loop { - let request = { - let receiver_from_api = &api_rx; - let receiver_from_watcher = &watch_rx; - select! { - msg = receiver_from_api.recv() => Request::Command(msg.unwrap()), - msg = receiver_from_watcher.recv() => Request::Watcher(msg.unwrap()) - } - }; - - match request { - Request::Watcher(event) => { - handler.file_changed(event.path.unwrap()); - } - Request::Command(cmd) => { - match cmd { - FileWatcherCmd::AddWatch(path) => { - file_watcher.watch(path).ok(); - } - FileWatcherCmd::Exit => { - break; - } - } - } - } - } - }); - - FileWatcherThread { - api_tx, - } - } - - fn exit(&self) { - self.api_tx.send(FileWatcherCmd::Exit).ok(); - } - - fn add_watch(&self, path: PathBuf) { - self.api_tx.send(FileWatcherCmd::AddWatch(path)).ok(); - } -} -*/ - pub struct Capabilities { pub supports_multisampling: bool, } @@ -774,7 +696,7 @@ pub struct Device { // device state bound_textures: [TextureId; 16], bound_program: gl::GLuint, - bound_vao: VAOId, + bound_vao: gl::GLuint, bound_pbo: PBOId, bound_read_fbo: FBOId, bound_draw_fbo: FBOId, @@ -791,15 +713,9 @@ pub struct Device { // resources resource_override_path: Option, textures: FastHashMap, - vaos: FastHashMap, // misc. shader_preamble: String, - //file_watcher: FileWatcherThread, - - // Used on android only - #[allow(dead_code)] - next_vao_id: gl::GLuint, max_texture_size: u32, @@ -812,11 +728,7 @@ impl Device { pub fn new(gl: Rc, resource_override_path: Option, _file_changed_handler: Box) -> Device { - //let file_watcher = FileWatcherThread::new(file_changed_handler); - let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path); - //file_watcher.add_watch(resource_path); - let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32; Device { @@ -833,7 +745,7 @@ impl Device { bound_textures: [ TextureId::invalid(); 16 ], bound_program: 0, - bound_vao: VAOId(0), + bound_vao: 0, bound_pbo: PBOId(0), bound_read_fbo: FBOId(0), bound_draw_fbo: FBOId(0), @@ -841,13 +753,9 @@ impl Device { default_draw_fbo: 0, textures: FastHashMap::default(), - vaos: FastHashMap::default(), shader_preamble, - next_vao_id: 1, - //file_watcher: file_watcher, - max_texture_size, frame_id: FrameId(0), } @@ -924,8 +832,8 @@ impl Device { self.gl.use_program(0); // Vertex state - self.bound_vao = VAOId(0); - self.clear_vertex_array(); + self.bound_vao = 0; + self.gl.bind_vertex_array(0); // FBO state self.bound_read_fbo = FBOId(self.default_read_fbo); @@ -1014,6 +922,7 @@ impl Device { id, width: 0, height: 0, + layer_count: 0, format: ImageFormat::Invalid, filter: TextureFilter::Nearest, mode: RenderTargetMode::None, @@ -1030,6 +939,11 @@ impl Device { texture_ids } + pub fn get_texture_layer_count(&self, texture_id: TextureId) -> i32 { + let texture = &self.textures[&texture_id]; + texture.layer_count + } + pub fn get_texture_dimensions(&self, texture_id: TextureId) -> DeviceUintSize { let texture = &self.textures[&texture_id]; DeviceUintSize::new(texture.width, texture.height) @@ -1052,24 +966,6 @@ impl Device { self.gl.tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); } - fn upload_texture_image(&mut self, - target: gl::GLuint, - width: u32, - height: u32, - internal_format: u32, - format: u32, - type_: u32, - pixels: Option<&[u8]>) { - self.gl.tex_image_2d(target, - 0, - internal_format as gl::GLint, - width as gl::GLint, height as gl::GLint, - 0, - format, - type_, - pixels); - } - pub fn init_texture(&mut self, texture_id: TextureId, width: u32, @@ -1077,6 +973,7 @@ impl Device { format: ImageFormat, filter: TextureFilter, mode: RenderTargetMode, + layer_count: i32, pixels: Option<&[u8]>) { debug_assert!(self.inside_frame); @@ -1088,6 +985,7 @@ impl Device { texture.width = width; texture.height = height; texture.filter = filter; + texture.layer_count = layer_count; texture.mode = mode; } @@ -1095,22 +993,10 @@ impl Device { let type_ = gl_type_for_texture_format(format); match mode { - RenderTargetMode::SimpleRenderTarget => { + RenderTargetMode::RenderTarget => { self.bind_texture(DEFAULT_TEXTURE, texture_id); self.set_texture_parameters(texture_id.target, filter); - self.upload_texture_image(texture_id.target, - width, - height, - internal_format as u32, - gl_format, - type_, - None); - self.update_texture_storage(texture_id, None, resized); - } - RenderTargetMode::LayerRenderTarget(layer_count) => { - self.bind_texture(DEFAULT_TEXTURE, texture_id); - self.set_texture_parameters(texture_id.target, filter); - self.update_texture_storage(texture_id, Some(layer_count), resized); + self.update_texture_storage(texture_id, layer_count, resized); } RenderTargetMode::None => { self.bind_texture(DEFAULT_TEXTURE, texture_id); @@ -1124,13 +1010,34 @@ impl Device { } else { pixels }; - self.upload_texture_image(texture_id.target, - width, - height, - internal_format as u32, - gl_format, - type_, - actual_pixels); + + match texture_id.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.tex_image_3d(gl::TEXTURE_2D_ARRAY, + 0, + internal_format as gl::GLint, + width as gl::GLint, + height as gl::GLint, + layer_count, + 0, + gl_format, + type_, + actual_pixels); + } + gl::TEXTURE_2D | + gl::TEXTURE_RECTANGLE | + gl::TEXTURE_EXTERNAL_OES => { + self.gl.tex_image_2d(texture_id.target, + 0, + internal_format as gl::GLint, + width as gl::GLint, height as gl::GLint, + 0, + gl_format, + type_, + actual_pixels); + } + _ => panic!("BUG: Unexpected texture target!"), + } } } } @@ -1143,92 +1050,70 @@ impl Device { /// FBOs as required. pub fn update_texture_storage(&mut self, texture_id: TextureId, - layer_count: Option, + layer_count: i32, resized: bool) { let texture = self.textures.get_mut(&texture_id).unwrap(); - match layer_count { - Some(layer_count) => { - assert!(layer_count > 0); - assert_eq!(texture_id.target, gl::TEXTURE_2D_ARRAY); + assert!(layer_count > 0); + assert_eq!(texture_id.target, gl::TEXTURE_2D_ARRAY); - let current_layer_count = texture.fbo_ids.len() as i32; - // If the texture is already the required size skip. - if current_layer_count == layer_count && !resized { - return; - } + let current_layer_count = texture.fbo_ids.len() as i32; + // If the texture is already the required size skip. + if current_layer_count == layer_count && !resized { + return; + } - let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format); - let type_ = gl_type_for_texture_format(texture.format); + let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format); + let type_ = gl_type_for_texture_format(texture.format); - self.gl.tex_image_3d(texture_id.target, - 0, - internal_format as gl::GLint, - texture.width as gl::GLint, - texture.height as gl::GLint, - layer_count, - 0, - gl_format, - type_, - None); + self.gl.tex_image_3d(texture_id.target, + 0, + internal_format as gl::GLint, + texture.width as gl::GLint, + texture.height as gl::GLint, + layer_count, + 0, + gl_format, + type_, + None); - let needed_layer_count = layer_count - current_layer_count; - if needed_layer_count > 0 { - // Create more framebuffers to fill the gap - let new_fbos = self.gl.gen_framebuffers(needed_layer_count); - texture.fbo_ids.extend(new_fbos.into_iter().map(|id| FBOId(id))); - } else if needed_layer_count < 0 { - // Remove extra framebuffers - for old in texture.fbo_ids.drain(layer_count as usize ..) { - self.gl.delete_framebuffers(&[old.0]); - } - } - - let depth_rb = if let Some(rbo) = texture.depth_rb { - rbo.0 - } else { - let renderbuffer_ids = self.gl.gen_renderbuffers(1); - let depth_rb = renderbuffer_ids[0]; - texture.depth_rb = Some(RBOId(depth_rb)); - depth_rb - }; - self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); - self.gl.renderbuffer_storage(gl::RENDERBUFFER, - gl::DEPTH_COMPONENT24, - texture.width as gl::GLsizei, - texture.height as gl::GLsizei); - - for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() { - self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0); - self.gl.framebuffer_texture_layer(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture_id.name, - 0, - fbo_index as gl::GLint); - self.gl.framebuffer_renderbuffer(gl::FRAMEBUFFER, - gl::DEPTH_ATTACHMENT, - gl::RENDERBUFFER, - depth_rb); - } + let needed_layer_count = layer_count - current_layer_count; + if needed_layer_count > 0 { + // Create more framebuffers to fill the gap + let new_fbos = self.gl.gen_framebuffers(needed_layer_count); + texture.fbo_ids.extend(new_fbos.into_iter().map(|id| FBOId(id))); + } else if needed_layer_count < 0 { + // Remove extra framebuffers + for old in texture.fbo_ids.drain(layer_count as usize ..) { + self.gl.delete_framebuffers(&[old.0]); } - None => { - if texture.fbo_ids.is_empty() { - assert!(texture_id.target != gl::TEXTURE_2D_ARRAY); + } - let new_fbo = self.gl.gen_framebuffers(1)[0]; - self.gl.bind_framebuffer(gl::FRAMEBUFFER, new_fbo); + let depth_rb = if let Some(rbo) = texture.depth_rb { + rbo.0 + } else { + let renderbuffer_ids = self.gl.gen_renderbuffers(1); + let depth_rb = renderbuffer_ids[0]; + texture.depth_rb = Some(RBOId(depth_rb)); + depth_rb + }; + self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); + self.gl.renderbuffer_storage(gl::RENDERBUFFER, + gl::DEPTH_COMPONENT24, + texture.width as gl::GLsizei, + texture.height as gl::GLsizei); - self.gl.framebuffer_texture_2d(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture_id.target, - texture_id.name, - 0); - - texture.fbo_ids.push(FBOId(new_fbo)); - } else { - assert_eq!(texture.fbo_ids.len(), 1); - } - } + for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() { + self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0); + self.gl.framebuffer_texture_layer(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture_id.name, + 0, + fbo_index as gl::GLint); + self.gl.framebuffer_renderbuffer(gl::FRAMEBUFFER, + gl::DEPTH_ATTACHMENT, + gl::RENDERBUFFER, + depth_rb); } // TODO(gw): Hack! Modify the code above to use the normal binding interfaces the device exposes. @@ -1263,52 +1148,6 @@ impl Device { gl::LINEAR); } - pub fn resize_texture(&mut self, - texture_id: TextureId, - new_width: u32, - new_height: u32, - format: ImageFormat, - filter: TextureFilter, - mode: RenderTargetMode) { - debug_assert!(self.inside_frame); - - let old_size = self.get_texture_dimensions(texture_id); - - let temp_texture_id = self.create_texture_ids(1, TextureTarget::Default)[0]; - self.init_texture(temp_texture_id, old_size.width, old_size.height, format, filter, mode, None); - self.update_texture_storage(temp_texture_id, None, true); - - self.bind_read_target(Some((texture_id, 0))); - self.bind_texture(DEFAULT_TEXTURE, temp_texture_id); - - self.gl.copy_tex_sub_image_2d(temp_texture_id.target, - 0, - 0, - 0, - 0, - 0, - old_size.width as i32, - old_size.height as i32); - - self.deinit_texture(texture_id); - self.init_texture(texture_id, new_width, new_height, format, filter, mode, None); - self.update_texture_storage(texture_id, None, true); - self.bind_read_target(Some((temp_texture_id, 0))); - self.bind_texture(DEFAULT_TEXTURE, texture_id); - - self.gl.copy_tex_sub_image_2d(texture_id.target, - 0, - 0, - 0, - 0, - 0, - old_size.width as i32, - old_size.height as i32); - - self.bind_read_target(None); - self.deinit_texture(temp_texture_id); - } - pub fn deinit_texture(&mut self, texture_id: TextureId) { debug_assert!(self.inside_frame); @@ -1318,15 +1157,32 @@ impl Device { let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format); let type_ = gl_type_for_texture_format(texture.format); - self.gl.tex_image_2d(texture_id.target, - 0, - internal_format, - 0, - 0, - 0, - gl_format, - type_, - None); + match texture_id.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.tex_image_3d(gl::TEXTURE_2D_ARRAY, + 0, + internal_format as gl::GLint, + 0, + 0, + 0, + 0, + gl_format, + type_, + None); + } + _ => { + self.gl.tex_image_2d(texture_id.target, + 0, + internal_format, + 0, + 0, + 0, + gl_format, + type_, + None); + } + } + if let Some(RBOId(depth_rb)) = texture.depth_rb.take() { self.gl.delete_renderbuffers(&[depth_rb]); @@ -1340,6 +1196,7 @@ impl Device { texture.format = ImageFormat::Invalid; texture.width = 0; texture.height = 0; + texture.layer_count = 0; } pub fn create_program(&mut self, @@ -1352,7 +1209,7 @@ impl Device { descriptor) } - pub fn delete_program(&mut self, program: &mut Program) { + pub fn delete_program(&mut self, mut program: Program) { self.gl.delete_program(program.id); program.id = 0; } @@ -1507,43 +1364,6 @@ impl Device { Ok(()) } -/* - pub fn refresh_shader(&mut self, path: PathBuf) { - let mut vs_preamble_path = self.resource_path.clone(); - vs_preamble_path.push(VERTEX_SHADER_PREAMBLE); - - let mut fs_preamble_path = self.resource_path.clone(); - fs_preamble_path.push(FRAGMENT_SHADER_PREAMBLE); - - let mut refresh_all = false; - - if path == vs_preamble_path { - let mut f = File::open(&vs_preamble_path).unwrap(); - self.vertex_shader_preamble = String::new(); - f.read_to_string(&mut self.vertex_shader_preamble).unwrap(); - refresh_all = true; - } - - if path == fs_preamble_path { - let mut f = File::open(&fs_preamble_path).unwrap(); - self.fragment_shader_preamble = String::new(); - f.read_to_string(&mut self.fragment_shader_preamble).unwrap(); - refresh_all = true; - } - - let mut programs_to_update = Vec::new(); - - for (program_id, program) in &mut self.programs { - if refresh_all || program.vs_path == path || program.fs_path == path { - programs_to_update.push(*program_id) - } - } - - for program_id in programs_to_update { - self.load_program(program_id, false); - } - }*/ - pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation { UniformLocation(self.gl.get_uniform_location(program.id, name)) } @@ -1610,48 +1430,17 @@ impl Device { y0: u32, width: u32, height: u32, + layer_index: i32, + stride: Option, offset: usize) { debug_assert!(self.inside_frame); - debug_assert_eq!(self.textures.get(&texture_id).unwrap().format, ImageFormat::RGBAF32); - self.bind_texture(DEFAULT_TEXTURE, texture_id); - - self.gl.tex_sub_image_2d_pbo(texture_id.target, - 0, - x0 as gl::GLint, - y0 as gl::GLint, - width as gl::GLint, - height as gl::GLint, - gl::RGBA, - gl::FLOAT, - offset); - } - - pub fn update_texture(&mut self, - texture_id: TextureId, - x0: u32, - y0: u32, - width: u32, - height: u32, - stride: Option, - data: &[u8]) { - debug_assert!(self.inside_frame); - - let mut expanded_data = Vec::new(); - - let (gl_format, bpp, data, data_type) = match self.textures.get(&texture_id).unwrap().format { - ImageFormat::A8 => { - if cfg!(any(target_arch="arm", target_arch="aarch64")) { - expanded_data.extend(data.iter().flat_map(|byte| repeat(*byte).take(4))); - (get_gl_format_bgra(self.gl()), 4, expanded_data.as_slice(), gl::UNSIGNED_BYTE) - } else { - (GL_FORMAT_A, 1, data, gl::UNSIGNED_BYTE) - } - } - ImageFormat::RGB8 => (gl::RGB, 3, data, gl::UNSIGNED_BYTE), - ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl()), 4, data, gl::UNSIGNED_BYTE), - ImageFormat::RG8 => (gl::RG, 2, data, gl::UNSIGNED_BYTE), - ImageFormat::RGBAF32 => (gl::RGBA, 16, data, gl::FLOAT), + let (gl_format, bpp, data_type) = match self.textures.get(&texture_id).unwrap().format { + ImageFormat::A8 => (GL_FORMAT_A, 1, gl::UNSIGNED_BYTE), + ImageFormat::RGB8 => (gl::RGB, 3, gl::UNSIGNED_BYTE), + ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl()), 4, gl::UNSIGNED_BYTE), + ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE), + ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT), ImageFormat::Invalid => unreachable!(), }; @@ -1660,26 +1449,41 @@ impl Device { None => width, }; - // Take the stride into account for all rows, except the last one. - let len = bpp * row_length * (height - 1) - + width * bpp; - let data = &data[0..len as usize]; - if let Some(..) = stride { self.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, row_length as gl::GLint); } self.bind_texture(DEFAULT_TEXTURE, texture_id); - self.gl.tex_sub_image_2d(texture_id.target, - 0, - x0 as gl::GLint, - y0 as gl::GLint, - width as gl::GLint, - height as gl::GLint, - gl_format, - data_type, - data); + match texture_id.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.tex_sub_image_3d_pbo(texture_id.target, + 0, + x0 as gl::GLint, + y0 as gl::GLint, + layer_index, + width as gl::GLint, + height as gl::GLint, + 1, + gl_format, + data_type, + offset); + } + gl::TEXTURE_2D | + gl::TEXTURE_RECTANGLE | + gl::TEXTURE_EXTERNAL_OES => { + self.gl.tex_sub_image_2d_pbo(texture_id.target, + 0, + x0 as gl::GLint, + y0 as gl::GLint, + width as gl::GLint, + height as gl::GLint, + gl_format, + data_type, + offset); + } + _ => panic!("BUG: Unexpected texture target!"), + } // Reset row length to 0, otherwise the stride would apply to all texture uploads. if let Some(..) = stride { @@ -1687,19 +1491,12 @@ impl Device { } } - fn clear_vertex_array(&mut self) { - debug_assert!(self.inside_frame); - self.gl.bind_vertex_array(0); - } - - pub fn bind_vao(&mut self, vao_id: VAOId) { + pub fn bind_vao(&mut self, vao: &VAO) { debug_assert!(self.inside_frame); - if self.bound_vao != vao_id { - self.bound_vao = vao_id; - - let VAOId(id) = vao_id; - self.gl.bind_vertex_array(id); + if self.bound_vao != vao.id { + self.bound_vao = vao.id; + self.gl.bind_vertex_array(vao.id); } } @@ -1709,14 +1506,11 @@ impl Device { instance_vbo_id: VBOId, ibo_id: IBOId, instance_stride: gl::GLint, - owns_vertices: bool, - owns_instances: bool, - owns_indices: bool) - -> VAOId { + owns_vertices_and_indices: bool) + -> VAO { debug_assert!(self.inside_frame); - let vao_ids = self.gl.gen_vertex_arrays(1); - let vao_id = vao_ids[0]; + let vao_id = self.gl.gen_vertex_arrays(1)[0]; self.gl.bind_vertex_array(vao_id); @@ -1724,30 +1518,22 @@ impl Device { ibo_id.bind(self.gl()); // force it to be a part of VAO let vao = VAO { - gl: Rc::clone(&self.gl), id: vao_id, ibo_id, main_vbo_id, instance_vbo_id, instance_stride, - owns_indices, - owns_vertices, - owns_instances, + owns_vertices_and_indices, }; self.gl.bind_vertex_array(0); - let vao_id = VAOId(vao_id); - - debug_assert!(!self.vaos.contains_key(&vao_id)); - self.vaos.insert(vao_id, vao); - - vao_id + vao } pub fn create_vao(&mut self, descriptor: &VertexDescriptor, - inst_stride: gl::GLint) -> VAOId { + inst_stride: gl::GLint) -> VAO { debug_assert!(self.inside_frame); let buffer_ids = self.gl.gen_buffers(3); @@ -1760,55 +1546,55 @@ impl Device { intance_vbo_id, ibo_id, inst_stride, - true, - true, true) } + pub fn delete_vao(&mut self, mut vao: VAO) { + self.gl.delete_vertex_arrays(&[vao.id]); + vao.id = 0; + + if vao.owns_vertices_and_indices { + self.gl.delete_buffers(&[vao.ibo_id.0]); + self.gl.delete_buffers(&[vao.main_vbo_id.0]); + } + + self.gl.delete_buffers(&[vao.instance_vbo_id.0]) + } + pub fn create_vao_with_new_instances(&mut self, descriptor: &VertexDescriptor, inst_stride: gl::GLint, - base_vao: VAOId) -> VAOId { + base_vao: &VAO) -> VAO { debug_assert!(self.inside_frame); let buffer_ids = self.gl.gen_buffers(1); let intance_vbo_id = VBOId(buffer_ids[0]); - let (main_vbo_id, ibo_id) = { - let vao = self.vaos.get(&base_vao).unwrap(); - (vao.main_vbo_id, vao.ibo_id) - }; self.create_vao_with_vbos(descriptor, - main_vbo_id, + base_vao.main_vbo_id, intance_vbo_id, - ibo_id, + base_vao.ibo_id, inst_stride, - false, - true, false) } pub fn update_vao_main_vertices(&mut self, - vao_id: VAOId, + vao: &VAO, vertices: &[V], usage_hint: VertexUsageHint) { debug_assert!(self.inside_frame); - - let vao = self.vaos.get(&vao_id).unwrap(); - debug_assert_eq!(self.bound_vao, vao_id); + debug_assert_eq!(self.bound_vao, vao.id); vao.main_vbo_id.bind(self.gl()); gl::buffer_data(self.gl(), gl::ARRAY_BUFFER, vertices, usage_hint.to_gl()); } pub fn update_vao_instances(&mut self, - vao_id: VAOId, + vao: &VAO, instances: &[V], usage_hint: VertexUsageHint) { debug_assert!(self.inside_frame); - - let vao = self.vaos.get(&vao_id).unwrap(); - debug_assert_eq!(self.bound_vao, vao_id); + debug_assert_eq!(self.bound_vao, vao.id); debug_assert_eq!(vao.instance_stride as usize, mem::size_of::()); vao.instance_vbo_id.bind(self.gl()); @@ -1816,13 +1602,11 @@ impl Device { } pub fn update_vao_indices(&mut self, - vao_id: VAOId, + vao: &VAO, indices: &[I], usage_hint: VertexUsageHint) { debug_assert!(self.inside_frame); - - let vao = self.vaos.get(&vao_id).unwrap(); - debug_assert_eq!(self.bound_vao, vao_id); + debug_assert_eq!(self.bound_vao, vao.id); vao.ibo_id.bind(self.gl()); gl::buffer_data(self.gl(), gl::ELEMENT_ARRAY_BUFFER, indices, usage_hint.to_gl()); @@ -1991,12 +1775,6 @@ impl Device { } } -impl Drop for Device { - fn drop(&mut self) { - //self.file_watcher.exit(); - } -} - /// return (gl_internal_format, gl_format) fn gl_texture_formats_for_image_format(gl: &gl::Gl, format: ImageFormat) -> (gl::GLint, gl::GLuint) { match format { diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index 98a3647e871c..5f8fcb3f4e21 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -21,7 +21,7 @@ use scene::{Scene, SceneProperties}; use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags}; use util::{ComplexClipRegionHelpers, subtract_rect}; -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] pub struct FrameId(pub u32); static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 }; @@ -410,11 +410,9 @@ impl Frame { let transform = context.scene.properties.resolve_layout_transform(transform); let perspective = stacking_context.perspective.unwrap_or_else(LayoutTransform::identity); + let origin = reference_frame_relative_offset + bounds.origin.to_vector(); let transform = - LayerToScrollTransform::create_translation(reference_frame_relative_offset.x, - reference_frame_relative_offset.y, - 0.0) - .pre_translate(bounds.origin.to_vector().to_3d()) + LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0) .pre_mul(&transform) .pre_mul(&perspective); @@ -424,6 +422,7 @@ impl Frame { pipeline_id, &reference_frame_bounds, &transform, + origin, &mut self.clip_scroll_tree); context.replacements.push((context_scroll_node_id, clip_id)); reference_frame_relative_offset = LayerVector2D::zero(); @@ -485,16 +484,14 @@ impl Frame { self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch); let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size); - let transform = LayerToScrollTransform::create_translation( - reference_frame_relative_offset.x + bounds.origin.x, - reference_frame_relative_offset.y + bounds.origin.y, - 0.0); - + let origin = reference_frame_relative_offset + bounds.origin.to_vector(); + let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0); let iframe_reference_frame_id = context.builder.push_reference_frame(Some(clip_id), pipeline_id, &iframe_rect, &transform, + origin, &mut self.clip_scroll_tree); context.builder.add_scroll_frame( @@ -528,12 +525,6 @@ impl Frame { let item_rect_with_offset = item.rect().translate(&reference_frame_relative_offset); let clip_with_offset = item.local_clip_with_offset(&reference_frame_relative_offset); match *item.item() { - SpecificDisplayItem::WebGL(ref info) => { - context.builder.add_webgl_rectangle(clip_and_scroll, - item_rect_with_offset, - &clip_with_offset, - info.context_id); - } SpecificDisplayItem::Image(ref info) => { if let Some(tiling) = context.tiled_image_map.get(&info.image_key) { // The image resource is tiled. We have to generate an image primitive @@ -693,8 +684,9 @@ impl Frame { clip_region.origin += reference_frame_relative_offset; // Just use clip rectangle as the frame rect for this scroll frame. - // This is only interesting when calculating scroll extents for the - // ClipScrollNode::scroll(..) API + // This is useful when calculating scroll extents for the + // ClipScrollNode::scroll(..) API as well as for properly setting sticky + // positioning offsets. let frame_rect = item.local_clip() .clip_rect() .translate(&reference_frame_relative_offset); @@ -708,6 +700,16 @@ impl Frame { clip_region, info.scroll_sensitivity); } + SpecificDisplayItem::StickyFrame(ref info) => { + let frame_rect = item.rect().translate(&reference_frame_relative_offset); + let new_clip_id = context.convert_new_id_to_nested(&info.id); + self.clip_scroll_tree.add_sticky_frame( + new_clip_id, + clip_and_scroll.scroll_node_id, /* parent id */ + frame_rect, + info.sticky_frame_info); + + } SpecificDisplayItem::PushNestedDisplayList => { // Using the clip and scroll already processed for nesting here // means that in the case of multiple nested display lists, we diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 8272c0712746..51baa5eccd7e 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -5,10 +5,10 @@ use api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize}; use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; -use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, SubpixelDirection}; +use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize}; use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle}; -use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, TextShadow, TileOffset}; -use api::{TransformStyle, WebGLContextId, WorldPixel, YuvColorSpace, YuvData}; +use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow}; +use api::{TileOffset, TransformStyle, WorldPixel, YuvColorSpace, YuvData}; use app_units::Au; use frame::FrameId; use gpu_cache::GpuCache; @@ -16,7 +16,7 @@ use internal_types::{FastHashMap, HardwareCompositeOp}; use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; -use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex}; +use prim_store::{PrimitiveContainer, PrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, TextRunMode}; use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextShadowPrimitiveCpu}; use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu}; @@ -103,7 +103,6 @@ pub struct FrameBuilderConfig { pub enable_scrollbars: bool, pub default_font_render_mode: FontRenderMode, pub debug: bool, - pub cache_expiry_frames: u32, } pub struct FrameBuilder { @@ -323,9 +322,14 @@ impl FrameBuilder { pipeline_id: PipelineId, rect: &LayerRect, transform: &LayerToScrollTransform, + origin_in_parent_reference_frame: LayerVector2D, clip_scroll_tree: &mut ClipScrollTree) -> ClipId { - let new_id = clip_scroll_tree.add_reference_frame(rect, transform, pipeline_id, parent_id); + let new_id = clip_scroll_tree.add_reference_frame(rect, + transform, + origin_in_parent_reference_frame, + pipeline_id, + parent_id); self.reference_frame_stack.push(new_id); new_id } @@ -353,10 +357,10 @@ impl FrameBuilder { let root_id = clip_scroll_tree.root_reference_frame_id(); if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) { - if let NodeType::ReferenceFrame(ref mut transform) = root_node.node_type { - *transform = LayerToScrollTransform::create_translation(viewport_offset.x, - viewport_offset.y, - 0.0); + if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type { + info.transform = LayerToScrollTransform::create_translation(viewport_offset.x, + viewport_offset.y, + 0.0); } root_node.local_clip_rect = viewport_clip; } @@ -375,7 +379,12 @@ impl FrameBuilder { -> ClipId { let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size); let identity = &LayerToScrollTransform::identity(); - self.push_reference_frame(None, pipeline_id, &viewport_rect, identity, clip_scroll_tree); + self.push_reference_frame(None, + pipeline_id, + &viewport_rect, + identity, + LayerVector2D::zero(), + clip_scroll_tree); let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id); clip_scroll_tree.topmost_scrolling_node_id = topmost_scrolling_node_id; @@ -1196,24 +1205,6 @@ impl FrameBuilder { } } - pub fn add_webgl_rectangle(&mut self, - clip_and_scroll: ClipAndScrollInfo, - rect: LayerRect, - local_clip: &LocalClip, - context_id: WebGLContextId) { - let prim_cpu = ImagePrimitiveCpu { - kind: ImagePrimitiveKind::WebGL(context_id), - gpu_blocks: [ [rect.size.width, rect.size.height, 0.0, 0.0].into(), - TexelRect::invalid().into() ], - }; - - self.add_primitive(clip_and_scroll, - &rect, - local_clip, - &[], - PrimitiveContainer::Image(prim_cpu)); - } - pub fn add_image(&mut self, clip_and_scroll: ClipAndScrollInfo, rect: LayerRect, @@ -1227,10 +1218,10 @@ impl FrameBuilder { let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into(); let prim_cpu = ImagePrimitiveCpu { - kind: ImagePrimitiveKind::Image(image_key, - image_rendering, - tile, - *tile_spacing), + image_key, + image_rendering, + tile_offset: tile, + tile_spacing: *tile_spacing, gpu_blocks: [ [ stretch_size.width, stretch_size.height, tile_spacing.width, @@ -1773,7 +1764,10 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { if let Some(mask) = 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, None); + self.resource_cache.request_image(mask.image, + ImageRendering::Auto, + None, + self.gpu_cache); } } } @@ -1879,9 +1873,9 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { current_id = node.parent; let clip = match node.node_type { - NodeType::ReferenceFrame(transform) => { + NodeType::ReferenceFrame(ref info) => { // if the transform is non-aligned, bake the next LCCR into the clip mask - next_node_needs_region_mask |= !transform.preserves_2d_axis_alignment(); + next_node_needs_region_mask |= !info.transform.preserves_2d_axis_alignment(); continue }, NodeType::Clip(ref clip) if clip.mask_cache_info.is_masking() => clip, diff --git a/gfx/webrender/src/freelist.rs b/gfx/webrender/src/freelist.rs index cd026adfa750..d43d7145a709 100644 --- a/gfx/webrender/src/freelist.rs +++ b/gfx/webrender/src/freelist.rs @@ -3,40 +3,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::marker::PhantomData; -use std::mem; - -// TODO(gw): Add a weak free list handle. This is like a strong -// free list handle below, but will contain an epoch -// field. Weak handles will use a get_opt style API -// which returns an Option instead of T. // TODO(gw): Add an occupied list head, for fast // iteration of the occupied list to implement // retain() style functionality. +#[derive(Debug, Copy, Clone, PartialEq)] +struct Epoch(u32); + #[derive(Debug)] pub struct FreeListHandle { index: u32, _marker: PhantomData, } -enum SlotValue { - Free, - Occupied(T), -} - -impl SlotValue { - fn take(&mut self) -> T { - match mem::replace(self, SlotValue::Free) { - SlotValue::Free => unreachable!(), - SlotValue::Occupied(data) => data, - } - } +#[derive(Debug)] +pub struct WeakFreeListHandle { + index: u32, + epoch: Epoch, + _marker: PhantomData, } struct Slot { next: Option, - value: SlotValue, + epoch: Epoch, + value: Option, } pub struct FreeList { @@ -44,6 +35,11 @@ pub struct FreeList { free_list_head: Option, } +pub enum UpsertResult { + Updated(T), + Inserted(FreeListHandle), +} + impl FreeList { pub fn new() -> FreeList { FreeList { @@ -52,17 +48,63 @@ impl FreeList { } } + #[allow(dead_code)] pub fn get(&self, id: &FreeListHandle) -> &T { - match self.slots[id.index as usize].value { - SlotValue::Free => unreachable!(), - SlotValue::Occupied(ref data) => data, + self.slots[id.index as usize] + .value + .as_ref() + .unwrap() + } + + #[allow(dead_code)] + pub fn get_mut(&mut self, id: &FreeListHandle) -> &mut T { + self.slots[id.index as usize] + .value + .as_mut() + .unwrap() + } + + pub fn get_opt(&self, id: &WeakFreeListHandle) -> Option<&T> { + let slot = &self.slots[id.index as usize]; + if slot.epoch == id.epoch { + slot.value.as_ref() + } else { + None } } - pub fn get_mut(&mut self, id: &FreeListHandle) -> &mut T { - match self.slots[id.index as usize].value { - SlotValue::Free => unreachable!(), - SlotValue::Occupied(ref mut data) => data, + pub fn get_opt_mut(&mut self, id: &WeakFreeListHandle) -> Option<&mut T> { + let slot = &mut self.slots[id.index as usize]; + if slot.epoch == id.epoch { + slot.value.as_mut() + } else { + None + } + } + + pub fn create_weak_handle(&self, id: &FreeListHandle) -> WeakFreeListHandle { + let slot = &self.slots[id.index as usize]; + WeakFreeListHandle { + index: id.index, + epoch: slot.epoch, + _marker: PhantomData, + } + } + + // Perform a database style UPSERT operation. If the provided + // handle is a valid entry, update the value and return the + // previous data. If the provided handle is invalid, then + // insert the data into a new slot and return the new handle. + pub fn upsert(&mut self, + id: &WeakFreeListHandle, + data: T) -> UpsertResult { + if self.slots[id.index as usize].epoch == id.epoch { + let slot = &mut self.slots[id.index as usize]; + let result = UpsertResult::Updated(slot.value.take().unwrap()); + slot.value = Some(data); + result + } else { + UpsertResult::Inserted(self.insert(data)) } } @@ -74,7 +116,7 @@ impl FreeList { // Remove from free list. self.free_list_head = slot.next; slot.next = None; - slot.value = SlotValue::Occupied(item); + slot.value = Some(item); FreeListHandle { index: free_index, @@ -86,7 +128,8 @@ impl FreeList { self.slots.push(Slot { next: None, - value: SlotValue::Occupied(item), + epoch: Epoch(0), + value: Some(item), }); FreeListHandle { @@ -100,7 +143,8 @@ impl FreeList { pub fn free(&mut self, id: FreeListHandle) -> T { let slot = &mut self.slots[id.index as usize]; slot.next = self.free_list_head; + slot.epoch = Epoch(slot.epoch.0 + 1); self.free_list_head = Some(id.index); - slot.value.take() + slot.value.take().unwrap() } } diff --git a/gfx/webrender/src/glyph_cache.rs b/gfx/webrender/src/glyph_cache.rs index 218375c53654..59ea9bbe89a3 100644 --- a/gfx/webrender/src/glyph_cache.rs +++ b/gfx/webrender/src/glyph_cache.rs @@ -2,47 +2,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{FontInstanceKey, GlyphKey}; -use frame::FrameId; -use gpu_cache::GpuCache; +use api::{DevicePoint, DeviceUintSize, FontInstance, GlyphKey}; use internal_types::FastHashMap; -use resource_cache::{Resource, ResourceClassCache}; -use texture_cache::{TextureCache, TextureCacheItemId}; +use resource_cache::ResourceClassCache; +use std::sync::Arc; +use texture_cache::TextureCacheHandle; pub struct CachedGlyphInfo { - pub texture_cache_id: Option, - pub last_access: FrameId, + pub texture_cache_handle: TextureCacheHandle, + pub glyph_bytes: Arc>, + pub size: DeviceUintSize, + pub offset: DevicePoint, } -impl Resource for CachedGlyphInfo { - fn free(self, texture_cache: &mut TextureCache) { - if let Some(id) = self.texture_cache_id { - texture_cache.free(id); - } - } - fn get_last_access_time(&self) -> FrameId { - self.last_access - } - fn set_last_access_time(&mut self, frame_id: FrameId) { - self.last_access = frame_id; - } - fn add_to_gpu_cache(&self, - texture_cache: &mut TextureCache, - gpu_cache: &mut GpuCache) { - if let Some(texture_cache_id) = self.texture_cache_id.as_ref() { - let item = texture_cache.get_mut(texture_cache_id); - if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) { - request.push(item.uv_rect); - request.push([item.user_data[0], item.user_data[1], 0.0, 0.0]); - } - } - } -} - -pub type GlyphKeyCache = ResourceClassCache; +pub type GlyphKeyCache = ResourceClassCache>; pub struct GlyphCache { - pub glyph_key_caches: FastHashMap, + pub glyph_key_caches: FastHashMap, } impl GlyphCache { @@ -53,53 +29,30 @@ impl GlyphCache { } pub fn get_glyph_key_cache_for_font_mut(&mut self, - font: FontInstanceKey) -> &mut GlyphKeyCache { + font: FontInstance) -> &mut GlyphKeyCache { self.glyph_key_caches .entry(font) .or_insert(ResourceClassCache::new()) } pub fn get_glyph_key_cache_for_font(&self, - font: &FontInstanceKey) -> &GlyphKeyCache { + font: &FontInstance) -> &GlyphKeyCache { self.glyph_key_caches .get(font) .expect("BUG: Unable to find glyph key cache!") } - pub fn update(&mut self, - texture_cache: &mut TextureCache, - gpu_cache: &mut GpuCache, - current_frame_id: FrameId, - expiry_frame_id: FrameId) { - let mut caches_to_remove = Vec::new(); - - for (font, glyph_key_cache) in &mut self.glyph_key_caches { - glyph_key_cache.update(texture_cache, - gpu_cache, - current_frame_id, - expiry_frame_id); - - if glyph_key_cache.is_empty() { - caches_to_remove.push(font.clone()); - } - } - - for key in caches_to_remove { - self.glyph_key_caches.remove(&key).unwrap(); - } - } - - pub fn clear(&mut self, texture_cache: &mut TextureCache) { + pub fn clear(&mut self) { for (_, glyph_key_cache) in &mut self.glyph_key_caches { - glyph_key_cache.clear(texture_cache) + glyph_key_cache.clear() } // We use this in on_memory_pressure where retaining memory allocations // isn't desirable, so we completely remove the hash map instead of clearing it. self.glyph_key_caches = FastHashMap::default(); } - pub fn clear_fonts(&mut self, texture_cache: &mut TextureCache, key_fun: F) - where for<'r> F: Fn(&'r &FontInstanceKey) -> bool + pub fn clear_fonts(&mut self, key_fun: F) + where for<'r> F: Fn(&'r &FontInstance) -> bool { let caches_to_destroy = self.glyph_key_caches.keys() .filter(&key_fun) @@ -107,7 +60,7 @@ impl GlyphCache { .collect::>(); for key in caches_to_destroy { let mut cache = self.glyph_key_caches.remove(&key).unwrap(); - cache.clear(texture_cache); + cache.clear(); } } } diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs index 2c468a427546..5cb73579b33d 100644 --- a/gfx/webrender/src/glyph_rasterizer.rs +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -5,8 +5,8 @@ #[cfg(test)] use app_units::Au; use device::TextureFilter; -use frame::FrameId; use glyph_cache::{CachedGlyphInfo, GlyphCache}; +use gpu_cache::GpuCache; use internal_types::FastHashSet; use platform::font::{FontContext, RasterizedGlyph}; use profiler::TextureCacheProfileCounters; @@ -16,10 +16,10 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::collections::hash_map::Entry; use std::mem; -use texture_cache::TextureCache; +use texture_cache::{TextureCache, TextureCacheHandle}; #[cfg(test)] use api::{ColorF, LayoutPoint, FontRenderMode, IdNamespace, SubpixelDirection}; -use api::{FontInstanceKey}; +use api::{DevicePoint, DeviceUintSize, FontInstance}; use api::{FontKey, FontTemplate}; use api::{ImageData, ImageDescriptor, ImageFormat}; use api::{GlyphKey, GlyphDimensions}; @@ -145,9 +145,10 @@ impl GlyphRasterizer { pub fn request_glyphs( &mut self, glyph_cache: &mut GlyphCache, - current_frame_id: FrameId, - font: FontInstanceKey, - glyph_keys: &[GlyphKey]) { + font: FontInstance, + glyph_keys: &[GlyphKey], + texture_cache: &mut TextureCache, + gpu_cache: &mut GpuCache) { assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key)); let mut glyphs = Vec::new(); @@ -155,8 +156,31 @@ impl GlyphRasterizer { // select glyphs that have not been requested yet. for key in glyph_keys { - match glyph_key_cache.entry(key.clone(), current_frame_id) { - Entry::Occupied(..) => {} + match glyph_key_cache.entry(key.clone()) { + Entry::Occupied(mut entry) => { + if let Some(ref mut glyph_info) = *entry.get_mut() { + if texture_cache.request(&mut glyph_info.texture_cache_handle, gpu_cache) { + // This case gets hit when we have already rasterized + // the glyph and stored it in CPU memory, the the glyph + // has been evicted from the texture cache. In which case + // we need to re-upload it to the GPU. + texture_cache.update(&mut glyph_info.texture_cache_handle, + ImageDescriptor { + width: glyph_info.size.width, + height: glyph_info.size.height, + stride: None, + format: ImageFormat::BGRA8, + is_opaque: false, + offset: 0, + }, + TextureFilter::Linear, + ImageData::Raw(glyph_info.glyph_bytes.clone()), + [glyph_info.offset.x, glyph_info.offset.y], + None, + gpu_cache); + } + } + } Entry::Vacant(..) => { let request = GlyphRequest::new(&font, key); if self.pending_glyphs.insert(request.clone()) { @@ -198,7 +222,7 @@ impl GlyphRasterizer { } pub fn get_glyph_dimensions(&mut self, - font: &FontInstanceKey, + font: &FontInstance, glyph_key: &GlyphKey) -> Option { self.font_contexts.lock_shared_context().get_glyph_dimensions(font, glyph_key) } @@ -209,10 +233,10 @@ impl GlyphRasterizer { pub fn resolve_glyphs( &mut self, - current_frame_id: FrameId, glyph_cache: &mut GlyphCache, texture_cache: &mut TextureCache, - texture_cache_profile: &mut TextureCacheProfileCounters, + gpu_cache: &mut GpuCache, + _texture_cache_profile: &mut TextureCacheProfileCounters, ) { let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len()); @@ -241,10 +265,14 @@ impl GlyphRasterizer { // Update the caches. for job in rasterized_glyphs { - let image_id = job.result.and_then( + let glyph_info = job.result.and_then( |glyph| if glyph.width > 0 && glyph.height > 0 { assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0)); - let image_id = texture_cache.insert( + let glyph_bytes = Arc::new(glyph.bytes); + let mut texture_cache_handle = TextureCacheHandle::new(); + texture_cache.request(&mut texture_cache_handle, gpu_cache); + texture_cache.update( + &mut texture_cache_handle, ImageDescriptor { width: glyph.width, height: glyph.height, @@ -254,11 +282,17 @@ impl GlyphRasterizer { offset: 0, }, TextureFilter::Linear, - ImageData::Raw(Arc::new(glyph.bytes)), + ImageData::Raw(glyph_bytes.clone()), [glyph.left, glyph.top], - texture_cache_profile, + None, + gpu_cache, ); - Some(image_id) + Some(CachedGlyphInfo { + texture_cache_handle, + glyph_bytes, + size: DeviceUintSize::new(glyph.width, glyph.height), + offset: DevicePoint::new(glyph.left, glyph.top), + }) } else { None } @@ -266,10 +300,7 @@ impl GlyphRasterizer { let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font); - glyph_key_cache.insert(job.request.key, CachedGlyphInfo { - texture_cache_id: image_id, - last_access: current_frame_id, - }); + glyph_key_cache.insert(job.request.key, glyph_info); } // Now that we are done with the critical path (rendering the glyphs), @@ -308,11 +339,11 @@ impl FontContext { #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] pub struct GlyphRequest { pub key: GlyphKey, - pub font: FontInstanceKey, + pub font: FontInstance, } impl GlyphRequest { - pub fn new(font: &FontInstanceKey, key: &GlyphKey) -> Self { + pub fn new(font: &FontInstance, key: &GlyphKey) -> Self { GlyphRequest { key: key.clone(), font: font.clone(), @@ -337,6 +368,8 @@ fn raterize_200_glyphs() { let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap()); let mut glyph_rasterizer = GlyphRasterizer::new(workers); let mut glyph_cache = GlyphCache::new(); + let mut gpu_cache = GpuCache::new(); + let mut texture_cache = TextureCache::new(2048); let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file"); let mut font_data = vec![]; @@ -345,9 +378,7 @@ fn raterize_200_glyphs() { let font_key = FontKey::new(IdNamespace(0), 0); glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0)); - let frame_id = FrameId(1); - - let font = FontInstanceKey { + let font = FontInstance { font_key, color: ColorF::new(0.0, 0.0, 0.0, 1.0).into(), size: Au::from_px(32), @@ -364,18 +395,19 @@ fn raterize_200_glyphs() { for i in 0..4 { glyph_rasterizer.request_glyphs( &mut glyph_cache, - frame_id, font.clone(), &glyph_keys[(50 * i)..(50 * (i + 1))], + &mut texture_cache, + &mut gpu_cache ); } glyph_rasterizer.delete_font(font_key); glyph_rasterizer.resolve_glyphs( - frame_id, &mut glyph_cache, &mut TextureCache::new(4096), + &mut gpu_cache, &mut TextureCacheProfileCounters::new(), ); } diff --git a/gfx/webrender/src/gpu_cache.rs b/gfx/webrender/src/gpu_cache.rs index 405fc362a33f..a23de7cfceb7 100644 --- a/gfx/webrender/src/gpu_cache.rs +++ b/gfx/webrender/src/gpu_cache.rs @@ -28,7 +28,8 @@ use device::FrameId; use internal_types::UvRect; use profiler::GpuCacheProfileCounters; use renderer::MAX_VERTEX_TEXTURE_WIDTH; -use std::{mem, u32}; +use std::{mem, u16, u32}; +use std::ops::Add; use api::{ColorF, LayerRect}; pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512; @@ -140,6 +141,24 @@ impl GpuCacheAddress { v: v as u16, } } + + pub fn invalid() -> GpuCacheAddress { + GpuCacheAddress { + u: u16::MAX, + v: u16::MAX, + } + } +} + +impl Add for GpuCacheAddress { + type Output = GpuCacheAddress; + + fn add(self, other: usize) -> GpuCacheAddress { + GpuCacheAddress { + u: self.u + other as u16, + v: self.v, + } + } } // An entry in a free-list of blocks in the GPU cache. diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index 0dd36f768936..1a2a04cc8358 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -15,7 +15,7 @@ use tiling; use renderer::BlendMode; use api::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch}; use api::{ExternalImageData, ExternalImageId}; -use api::{ImageData, ImageFormat, PipelineId}; +use api::{ImageFormat, PipelineId}; pub type FastHashMap = HashMap>; pub type FastHashSet = HashSet>; @@ -43,10 +43,8 @@ pub enum SourceTexture { Invalid, TextureCache(CacheTextureId), External(ExternalImageData), - #[cfg_attr(not(feature = "webgl"), allow(dead_code))] - /// This is actually a gl::GLuint, with the shared texture id between the - /// main context and the WebGL context. - WebGL(u32), + CacheA8, + CacheRGBA8, } pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; @@ -91,6 +89,16 @@ impl BatchTextures { colors: [SourceTexture::Invalid; 3], } } + + pub fn render_target_cache() -> Self { + BatchTextures { + colors: [ + SourceTexture::CacheRGBA8, + SourceTexture::Invalid, + SourceTexture::Invalid, + ] + } + } } // In some places we need to temporarily bind a texture to any slot. @@ -99,42 +107,31 @@ pub const DEFAULT_TEXTURE: TextureSampler = TextureSampler::Color0; #[derive(Copy, Clone, Debug, PartialEq)] pub enum RenderTargetMode { None, - SimpleRenderTarget, - LayerRenderTarget(i32), // Number of texture layers + RenderTarget, +} + +#[derive(Debug)] +pub enum TextureUpdateSource { + External { id: ExternalImageId, channel_index: u8 }, + Bytes { data: Arc> }, } #[derive(Debug)] pub enum TextureUpdateOp { Create { - width: u32, - height: u32, - format: ImageFormat, - filter: TextureFilter, - mode: RenderTargetMode, - data: Option, - }, - Update { - page_pos_x: u32, // the texture page position which we want to upload - page_pos_y: u32, - width: u32, - height: u32, - data: Arc>, - stride: Option, - offset: u32, - }, - UpdateForExternalBuffer { - rect: DeviceUintRect, - id: ExternalImageId, - channel_index: u8, - stride: Option, - offset: u32, - }, - Grow { width: u32, height: u32, format: ImageFormat, filter: TextureFilter, mode: RenderTargetMode, + layer_count: i32, + }, + Update { + rect: DeviceUintRect, + stride: Option, + offset: u32, + layer_index: i32, + source: TextureUpdateSource, }, Free, } diff --git a/gfx/webrender/src/lib.rs b/gfx/webrender/src/lib.rs index 0464096d47cb..809df04df323 100644 --- a/gfx/webrender/src/lib.rs +++ b/gfx/webrender/src/lib.rs @@ -80,16 +80,6 @@ mod texture_cache; mod tiling; mod util; -#[doc(hidden)] // for benchmarks -pub use texture_cache::TexturePage; - -#[cfg(feature = "webgl")] -mod webgl_types; - -#[cfg(not(feature = "webgl"))] -#[path = "webgl_stubs.rs"] -mod webgl_types; - mod shader_source { include!(concat!(env!("OUT_DIR"), "/shaders.rs")); } @@ -140,8 +130,6 @@ extern crate num_traits; //extern crate notify; extern crate time; pub extern crate webrender_api; -#[cfg(feature = "webgl")] -extern crate offscreen_gl_context; extern crate byteorder; extern crate rayon; extern crate plane_split; diff --git a/gfx/webrender/src/mask_cache.rs b/gfx/webrender/src/mask_cache.rs index d2256f88d736..8561fbaa2ffa 100644 --- a/gfx/webrender/src/mask_cache.rs +++ b/gfx/webrender/src/mask_cache.rs @@ -120,6 +120,10 @@ impl ClipAddressRange { } } + pub fn is_empty(&self) -> bool { + self.item_count == 0 + } + pub fn get_count(&self) -> usize { self.item_count } diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs index aff4576b24ba..4d5da5884a45 100644 --- a/gfx/webrender/src/platform/macos/font.rs +++ b/gfx/webrender/src/platform/macos/font.rs @@ -17,7 +17,7 @@ use internal_types::FastHashMap; use std::collections::hash_map::Entry; use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions}; use api::{GlyphKey}; -use api::{FontInstanceKey, NativeFontHandle}; +use api::{FontInstance, NativeFontHandle}; use gamma_lut::{GammaLut, Color as ColorLut}; use std::ptr; use std::sync::Arc; @@ -238,7 +238,7 @@ impl FontContext { } pub fn get_glyph_dimensions(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { self.get_ct_font(font.font_key, font.size).and_then(|ref ct_font| { let glyph = key.index as CGGlyph; @@ -298,7 +298,7 @@ impl FontContext { } pub fn rasterize_glyph(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs index 95558ece13ee..693c85f54314 100644 --- a/gfx/webrender/src/platform/unix/font.rs +++ b/gfx/webrender/src/platform/unix/font.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{FontInstanceKey, FontKey, FontRenderMode, GlyphDimensions}; +use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions}; use api::{NativeFontHandle, SubpixelDirection}; use api::{GlyphKey}; use internal_types::FastHashMap; @@ -123,7 +123,7 @@ impl FontContext { } fn load_glyph(&self, - font: &FontInstanceKey, + font: &FontInstance, glyph: &GlyphKey) -> Option { debug_assert!(self.faces.contains_key(&font.font_key)); @@ -166,7 +166,7 @@ impl FontContext { // Get the bounding box for a glyph, accounting for sub-pixel positioning. fn get_bounding_box(&self, slot: FT_GlyphSlot, - font: &FontInstanceKey, + font: &FontInstance, glyph: &GlyphKey) -> FT_BBox { let mut cbox: FT_BBox = unsafe { mem::uninitialized() }; @@ -213,7 +213,7 @@ impl FontContext { fn get_glyph_dimensions_impl(&self, slot: FT_GlyphSlot, - font: &FontInstanceKey, + font: &FontInstance, glyph: &GlyphKey) -> Option { let metrics = unsafe { &(*slot).metrics }; @@ -249,7 +249,7 @@ impl FontContext { } pub fn get_glyph_dimensions(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { let slot = self.load_glyph(font, key); slot.and_then(|slot| { @@ -258,7 +258,7 @@ impl FontContext { } pub fn rasterize_glyph(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index aec738865adc..d84bca9eaf52 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{FontKey, FontRenderMode, GlyphDimensions}; -use api::{FontInstanceKey, GlyphKey, GlyphOptions, SubpixelDirection}; +use api::{FontInstance, GlyphKey, GlyphOptions, SubpixelDirection}; use gamma_lut::{GammaLut, Color as ColorLut}; use internal_types::FastHashMap; @@ -155,7 +155,7 @@ impl FontContext { } fn create_glyph_analysis(&self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> dwrote::GlyphRunAnalysis { let face = self.fonts.get(&font.font_key).unwrap(); @@ -202,7 +202,7 @@ impl FontContext { // TODO: Pipe GlyphOptions into glyph_dimensions too pub fn get_glyph_dimensions(&self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { // Probably have to default to something else here. @@ -283,7 +283,7 @@ impl FontContext { } pub fn rasterize_glyph(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { let analysis = self.create_glyph_analysis(font, key); diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index b6d28249720c..0d07960ea16f 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -5,8 +5,8 @@ use api::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint}; use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop}; use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, TextShadow}; -use api::{GlyphKey, LayerToWorldTransform, TileOffset, WebGLContextId, YuvColorSpace, YuvFormat}; -use api::{device_length, FontInstanceKey, LayerVector2D, LineOrientation, LineStyle, SubpixelDirection}; +use api::{GlyphKey, LayerToWorldTransform, TileOffset, YuvColorSpace, YuvFormat}; +use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle, SubpixelDirection}; use app_units::Au; use border::BorderCornerInstance; use euclid::{Size2D}; @@ -199,15 +199,12 @@ impl ToGpuBlocks for LinePrimitive { } } -#[derive(Debug)] -pub enum ImagePrimitiveKind { - Image(ImageKey, ImageRendering, Option, LayerSize), - WebGL(WebGLContextId), -} - #[derive(Debug)] pub struct ImagePrimitiveCpu { - pub kind: ImagePrimitiveKind, + pub image_key: ImageKey, + pub image_rendering: ImageRendering, + pub tile_offset: Option, + pub tile_spacing: LayerSize, // TODO(gw): Build on demand pub gpu_blocks: [GpuBlockData; 2], } @@ -538,19 +535,20 @@ impl TextRunPrimitiveCpu { resource_cache: &mut ResourceCache, device_pixel_ratio: f32, display_list: &BuiltDisplayList, - run_mode: TextRunMode) { + run_mode: TextRunMode, + gpu_cache: &mut GpuCache) { let font_size_dp = self.logical_font_size.scale_by(device_pixel_ratio); let render_mode = match run_mode { TextRunMode::Normal => self.normal_render_mode, TextRunMode::Shadow => self.shadow_render_mode, }; - let font = FontInstanceKey::new(self.font_key, - font_size_dp, - self.color, - render_mode, - self.glyph_options, - self.subpx_dir); + let font = FontInstance::new(self.font_key, + font_size_dp, + self.color, + render_mode, + self.glyph_options, + self.subpx_dir); // Cache the glyph positions, if not in the cache already. // TODO(gw): In the future, remove `glyph_instances` @@ -590,7 +588,7 @@ impl TextRunPrimitiveCpu { } } - resource_cache.request_glyphs(font, &self.glyph_keys); + resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache); } fn write_gpu_blocks(&self, @@ -1130,7 +1128,10 @@ impl PrimitiveStore { for clip in &metadata.clips { if let ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, ..) = *clip { - resource_cache.request_image(mask.image, ImageRendering::Auto, None); + resource_cache.request_image(mask.image, + ImageRendering::Auto, + None, + gpu_cache); } } } @@ -1177,25 +1178,25 @@ impl PrimitiveStore { text.prepare_for_render(resource_cache, device_pixel_ratio, display_list, - text_run_mode); + text_run_mode, + gpu_cache); } PrimitiveKind::Image => { let image_cpu = &mut self.cpu_images[cpu_prim_index.0]; - match image_cpu.kind { - ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, tile_spacing) => { - resource_cache.request_image(image_key, image_rendering, tile_offset); + resource_cache.request_image(image_cpu.image_key, + image_cpu.image_rendering, + image_cpu.tile_offset, + gpu_cache); - // 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.opacity.is_opaque = image_properties.descriptor.is_opaque && - tile_spacing.width == 0.0 && - tile_spacing.height == 0.0; - } - ImagePrimitiveKind::WebGL(..) => {} + // 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. + if let Some(image_properties) = resource_cache.get_image_properties(image_cpu.image_key) { + metadata.opacity.is_opaque = image_properties.descriptor.is_opaque && + image_cpu.tile_spacing.width == 0.0 && + image_cpu.tile_spacing.height == 0.0; } } PrimitiveKind::YuvImage => { @@ -1204,7 +1205,10 @@ impl PrimitiveStore { let channel_num = image_cpu.format.get_plane_num(); debug_assert!(channel_num <= 3); for channel in 0..channel_num { - resource_cache.request_image(image_cpu.yuv_key[channel], image_cpu.image_rendering, None); + resource_cache.request_image(image_cpu.yuv_key[channel], + image_cpu.image_rendering, + None, + gpu_cache); } } PrimitiveKind::AlignedGradient | diff --git a/gfx/webrender/src/record.rs b/gfx/webrender/src/record.rs index 5c6c7b996537..fe03d823ca69 100644 --- a/gfx/webrender/src/record.rs +++ b/gfx/webrender/src/record.rs @@ -67,8 +67,7 @@ pub fn should_record_msg(msg: &ApiMsg) -> bool { ApiMsg::UpdateResources(..) | ApiMsg::AddDocument{..} | ApiMsg::UpdateDocument(..) | - ApiMsg::DeleteDocument(..) | - ApiMsg::WebGLCommand(..) => + ApiMsg::DeleteDocument(..) => true, _ => false } diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 2c1169becc98..0ee04a63a87f 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -5,7 +5,7 @@ use frame::Frame; use frame_builder::FrameBuilderConfig; use gpu_cache::GpuCache; -use internal_types::{FastHashMap, SourceTexture, ResultMsg, RendererFrame}; +use internal_types::{FastHashMap, ResultMsg, RendererFrame}; use profiler::{BackendProfileCounters, ResourceProfileCounters}; use record::ApiRecordingReceiver; use resource_cache::ResourceCache; @@ -17,19 +17,11 @@ use texture_cache::TextureCache; use time::precise_time_ns; use thread_profiler::register_thread_with_profiler; use rayon::ThreadPool; -use webgl_types::{GLContextHandleWrapper, GLContextWrapper}; use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods}; use api::channel::{PayloadSender, PayloadSenderHelperMethods}; use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg}; -use api::{IdNamespace, LayerPoint, RenderDispatcher, RenderNotifier}; -use api::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId}; - -#[cfg(feature = "webgl")] -use offscreen_gl_context::GLContextDispatcher; - -#[cfg(not(feature = "webgl"))] -use webgl_types::GLContextDispatcher; +use api::{IdNamespace, LayerPoint, RenderNotifier}; struct Document { scene: Scene, @@ -102,59 +94,6 @@ impl Document { } } -struct WebGL { - last_id: WebGLContextId, - contexts: FastHashMap, - active_id: Option, -} - -impl WebGL { - fn new() -> Self { - WebGL { - last_id: WebGLContextId(0), - contexts: FastHashMap::default(), - active_id: None, - } - } - - fn register(&mut self, context: GLContextWrapper) -> WebGLContextId { - // Creating a new GLContext may make the current bound context_id dirty. - // Clear it to ensure that make_current() is called in subsequent commands. - self.active_id = None; - self.last_id.0 += 1; - self.contexts.insert(self.last_id, context); - self.last_id - } - - fn activate(&mut self, id: WebGLContextId) -> &mut GLContextWrapper { - let ctx = self.contexts.get_mut(&id).unwrap(); - if Some(id) != self.active_id { - ctx.make_current(); - self.active_id = Some(id); - } - ctx - } - - fn flush(&mut self) { - if let Some(id) = self.active_id.take() { - self.contexts[&id].unbind(); - } - - // When running in OSMesa mode with texture sharing, - // a flush is required on any GL contexts to ensure - // that read-back from the shared texture returns - // valid data! This should be fine to have run on all - // implementations - a single flush for each webgl - // context at the start of a render frame should - // incur minimal cost. - for (_, context) in &self.contexts { - context.make_current(); - context.apply_command(WebGLCommand::Flush); - context.unbind(); - } - } -} - enum DocumentOp { Nop, Built, @@ -184,12 +123,7 @@ pub struct RenderBackend { documents: FastHashMap, notifier: Arc>>>, - webrender_context_handle: Option, recorder: Option>, - main_thread_dispatcher: Arc>>>, - - vr_compositor_handler: Arc>>>, - webgl: WebGL, enable_render_on_scroll: bool, } @@ -204,19 +138,15 @@ impl RenderBackend { texture_cache: TextureCache, workers: Arc, notifier: Arc>>>, - webrender_context_handle: Option, frame_config: FrameBuilderConfig, recorder: Option>, - main_thread_dispatcher: Arc>>>, blob_image_renderer: Option>, - vr_compositor_handler: Arc>>>, enable_render_on_scroll: bool, ) -> RenderBackend { let resource_cache = ResourceCache::new(texture_cache, workers, - blob_image_renderer, - frame_config.cache_expiry_frames); + blob_image_renderer); register_thread_with_profiler("Backend".to_string()); @@ -233,12 +163,7 @@ impl RenderBackend { documents: FastHashMap::default(), next_namespace_id: IdNamespace(1), notifier, - webrender_context_handle, recorder, - main_thread_dispatcher, - - vr_compositor_handler, - webgl: WebGL::new(), enable_render_on_scroll, } @@ -308,7 +233,6 @@ impl RenderBackend { let display_list_received_time = precise_time_ns(); { - self.webgl.flush(); let _timer = profile_counters.total_time.timer(); doc.scene.set_display_list( pipeline_id, @@ -344,7 +268,6 @@ impl RenderBackend { doc.scene.set_root_pipeline_id(pipeline_id); if doc.scene.display_lists.get(&pipeline_id).is_some() { - self.webgl.flush(); let _timer = profile_counters.total_time.timer(); doc.build_scene(&mut self.resource_cache, self.hidpi_factor); DocumentOp::Built @@ -415,7 +338,6 @@ impl RenderBackend { // animated properties to not require a full // rebuild of the frame! if let Some(property_bindings) = property_bindings { - self.webgl.flush(); doc.scene.properties.set_properties(property_bindings); doc.build_scene(&mut self.resource_cache, self.hidpi_factor); } @@ -511,63 +433,6 @@ impl RenderBackend { ApiMsg::DeleteDocument(document_id) => { self.documents.remove(&document_id); } - ApiMsg::RequestWebGLContext(size, attributes, tx) => { - if let Some(ref wrapper) = self.webrender_context_handle { - let dispatcher: Option> = if cfg!(target_os = "windows") { - Some(Box::new(WebRenderGLDispatcher { - dispatcher: Arc::clone(&self.main_thread_dispatcher) - })) - } else { - None - }; - - let result = wrapper.new_context(size, attributes, dispatcher); - - match result { - Ok(ctx) => { - let (real_size, texture_id, limits) = ctx.get_info(); - let id = self.webgl.register(ctx); - - self.resource_cache - .add_webgl_texture(id, SourceTexture::WebGL(texture_id), - real_size); - - tx.send(Ok((id, limits))).unwrap(); - }, - Err(msg) => { - tx.send(Err(msg.to_owned())).unwrap(); - } - } - } else { - tx.send(Err("Not implemented yet".to_owned())).unwrap(); - } - } - ApiMsg::ResizeWebGLContext(context_id, size) => { - let ctx = self.webgl.activate(context_id); - match ctx.resize(&size) { - Ok(_) => { - // Update webgl texture size. Texture id may change too. - let (real_size, texture_id, _) = ctx.get_info(); - self.resource_cache - .update_webgl_texture(context_id, SourceTexture::WebGL(texture_id), - real_size); - }, - Err(msg) => { - error!("Error resizing WebGLContext: {}", msg); - } - } - } - ApiMsg::WebGLCommand(context_id, command) => { - // TODO: Buffer the commands and only apply them here if they need to - // be synchronous. - let ctx = self.webgl.activate(context_id); - ctx.apply_command(command); - }, - - ApiMsg::VRCompositorCommand(context_id, command) => { - self.webgl.activate(context_id); - self.handle_vr_compositor_command(context_id, command); - } ApiMsg::ExternalEvent(evt) => { let notifier = self.notifier.lock(); notifier.unwrap() @@ -640,32 +505,4 @@ impl RenderBackend { let mut notifier = self.notifier.lock(); notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed); } - - fn handle_vr_compositor_command(&mut self, ctx_id: WebGLContextId, cmd: VRCompositorCommand) { - let texture = match cmd { - VRCompositorCommand::SubmitFrame(..) => { - match self.resource_cache.get_webgl_texture(&ctx_id).id { - SourceTexture::WebGL(texture_id) => { - let size = self.resource_cache.get_webgl_texture_size(&ctx_id); - Some((texture_id, size)) - }, - _=> None - } - }, - _ => None - }; - let mut handler = self.vr_compositor_handler.lock(); - handler.as_mut().unwrap().as_mut().unwrap().handle(cmd, texture); - } -} - -struct WebRenderGLDispatcher { - dispatcher: Arc>>> -} - -impl GLContextDispatcher for WebRenderGLDispatcher { - fn dispatch(&self, f: Box) { - let mut dispatcher = self.dispatcher.lock(); - dispatcher.as_mut().unwrap().as_mut().unwrap().dispatch(f); - } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 777440d37fba..40ea5bd5f8b2 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -12,14 +12,14 @@ use debug_colors; use debug_render::DebugRenderer; use device::{DepthFunction, Device, FrameId, Program, TextureId, VertexDescriptor, GpuMarker, GpuProfiler, PBOId}; -use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError}; +use device::{GpuSample, TextureFilter, VAO, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError}; use device::{get_gl_format_bgra, VertexAttribute, VertexAttributeKind}; use euclid::{Transform3D, rect}; use frame_builder::FrameBuilderConfig; use gleam::gl; use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use internal_types::{FastHashMap, CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp}; -use internal_types::{TextureUpdateList, RenderTargetMode}; +use internal_types::{TextureUpdateList, RenderTargetMode, TextureUpdateSource}; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture}; use internal_types::{BatchTextures, TextureSampler}; use profiler::{Profiler, BackendProfileCounters}; @@ -46,12 +46,10 @@ use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRende use time::precise_time_ns; use thread_profiler::{register_thread_with_profiler, write_profile}; use util::TransformedRectKind; -use webgl_types::GLContextHandleWrapper; -use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier, RenderDispatcher}; -use api::{ExternalImageId, ExternalImageType, ImageData, ImageFormat}; +use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier}; +use api::{ExternalImageId, ExternalImageType, ImageFormat}; use api::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize}; use api::{BlobImageRenderer, channel, FontRenderMode}; -use api::VRCompositorHandler; use api::{YuvColorSpace, YuvFormat}; use api::{YUV_COLOR_SPACES, YUV_FORMATS}; @@ -125,12 +123,17 @@ const DESC_CLIP: VertexDescriptor = VertexDescriptor { instance_attributes: &[ VertexAttribute { name: "aClipRenderTaskIndex", count: 1, kind: VertexAttributeKind::I32 }, VertexAttribute { name: "aClipLayerIndex", count: 1, kind: VertexAttributeKind::I32 }, - VertexAttribute { name: "aClipDataIndex", count: 1, kind: VertexAttributeKind::I32 }, - VertexAttribute { name: "aClipSegmentIndex", count: 1, kind: VertexAttributeKind::I32 }, - VertexAttribute { name: "aClipResourceAddress", count: 1, kind: VertexAttributeKind::I32 }, + VertexAttribute { name: "aClipSegment", count: 1, kind: VertexAttributeKind::I32 }, + VertexAttribute { name: "aClipDataResourceAddress", count: 4, kind: VertexAttributeKind::U16 }, ] }; +enum VertexArrayKind { + Primitive, + Blur, + Clip, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum VertexFormat { PrimitiveInstances, @@ -155,18 +158,21 @@ pub enum ImageBufferKind { Texture2D = 0, TextureRect = 1, TextureExternal = 2, + Texture2DArray = 3, } -pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 3] = [ +pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [ ImageBufferKind::Texture2D, ImageBufferKind::TextureRect, - ImageBufferKind::TextureExternal + ImageBufferKind::TextureExternal, + ImageBufferKind::Texture2DArray, ]; impl ImageBufferKind { pub fn get_feature_string(&self) -> &'static str { match *self { - ImageBufferKind::Texture2D => "", + ImageBufferKind::Texture2D => "TEXTURE_2D", + ImageBufferKind::Texture2DArray => "", ImageBufferKind::TextureRect => "TEXTURE_RECT", ImageBufferKind::TextureExternal => "TEXTURE_EXTERNAL", } @@ -177,6 +183,7 @@ impl ImageBufferKind { gl::GlType::Gles => { match *self { ImageBufferKind::Texture2D => true, + ImageBufferKind::Texture2DArray => true, ImageBufferKind::TextureRect => true, ImageBufferKind::TextureExternal => true, } @@ -184,6 +191,7 @@ impl ImageBufferKind { gl::GlType::Gl => { match *self { ImageBufferKind::Texture2D => true, + ImageBufferKind::Texture2DArray => true, ImageBufferKind::TextureRect => true, ImageBufferKind::TextureExternal => false, } @@ -239,6 +247,88 @@ impl CpuProfile { } } +struct SourceTextureResolver { + /// A vector for fast resolves of texture cache IDs to + /// native texture IDs. This maps to a free-list managed + /// by the backend thread / texture cache. We free the + /// texture memory associated with a TextureId when its + /// texture cache ID is freed by the texture cache, but + /// reuse the TextureId when the texture caches's free + /// list reuses the texture cache ID. This saves having to + /// use a hashmap, and allows a flat vector for performance. + cache_texture_id_map: Vec, + + /// Map of external image IDs to native textures. + external_images: FastHashMap<(ExternalImageId, u8), TextureId>, + + /// A special 1x1 dummy cache texture used for shaders that expect to work + /// with the cache but are actually running in the first pass + /// when no target is yet provided as a cache texture input. + dummy_cache_texture_id: TextureId, + + /// The current cache textures. + cache_rgba8_texture: Option, + cache_a8_texture: Option, +} + +impl SourceTextureResolver { + fn new(device: &mut Device) -> SourceTextureResolver { + let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0]; + device.init_texture(dummy_cache_texture_id, + 1, + 1, + ImageFormat::BGRA8, + TextureFilter::Linear, + RenderTargetMode::RenderTarget, + 1, + None); + + SourceTextureResolver { + cache_texture_id_map: Vec::new(), + external_images: FastHashMap::default(), + dummy_cache_texture_id, + cache_a8_texture: None, + cache_rgba8_texture: None, + } + } + + fn deinit(self, device: &mut Device) { + device.deinit_texture(self.dummy_cache_texture_id); + } + + fn set_cache_textures(&mut self, + a8_texture: Option, + rgba8_texture: Option) { + self.cache_a8_texture = a8_texture; + self.cache_rgba8_texture = rgba8_texture; + } + + // Get the real (OpenGL) texture ID for a given source texture. + // For a texture cache texture, the IDs are stored in a vector + // map for fast access. + fn resolve(&self, texture_id: &SourceTexture) -> TextureId { + match *texture_id { + SourceTexture::Invalid => { + TextureId::invalid() + } + SourceTexture::CacheA8 => { + self.cache_a8_texture.unwrap_or(self.dummy_cache_texture_id) + } + SourceTexture::CacheRGBA8 => { + self.cache_rgba8_texture.unwrap_or(self.dummy_cache_texture_id) + } + SourceTexture::External(external_image) => { + *self.external_images + .get(&(external_image.id, external_image.channel_index)) + .expect("BUG: External image should be resolved by now!") + } + SourceTexture::TextureCache(index) => { + self.cache_texture_id_map[index.0] + } + } + } +} + #[derive(Debug, Copy, Clone, PartialEq)] pub enum BlendMode { None, @@ -324,6 +414,7 @@ impl CacheTexture { ImageFormat::RGBAF32, TextureFilter::Nearest, RenderTargetMode::None, + 1, None); // Copy the current texture into the newly resized texture. @@ -361,6 +452,8 @@ impl CacheTexture { row_index as u32, MAX_VERTEX_TEXTURE_WIDTH as u32, 1, + 0, + None, 0); // Orphan the PBO. This is the recommended way to hint to the @@ -456,6 +549,7 @@ impl GpuDataTexture { L::image_format(), L::texture_filter(), RenderTargetMode::None, + 1, Some(unsafe { mem::transmute(data.as_slice()) } )); } } @@ -549,8 +643,8 @@ impl LazilyCompiledShader { Ok(self.program.as_ref().unwrap()) } - fn deinit(&mut self, device: &mut Device) { - if let &mut Some(ref mut program) = &mut self.program { + fn deinit(self, device: &mut Device) { + if let Some(program) = self.program { device.delete_program(program); } } @@ -614,7 +708,7 @@ impl PrimitiveShader { } } - fn deinit(&mut self, device: &mut Device) { + fn deinit(self, device: &mut Device) { self.simple.deinit(device); self.transform.deinit(device); } @@ -758,9 +852,9 @@ pub struct Renderer { alpha_render_targets: Vec, gpu_profile: GpuProfiler, - prim_vao_id: VAOId, - blur_vao_id: VAOId, - clip_vao_id: VAOId, + prim_vao: VAO, + blur_vao: VAO, + clip_vao: VAO, gdt_index: usize, gpu_data_textures: [GpuDataTextures; GPU_DATA_TEXTURE_POOL], @@ -768,24 +862,12 @@ pub struct Renderer { gpu_cache_texture: CacheTexture, pipeline_epoch_map: FastHashMap, - /// Used to dispatch functions to the main thread's event loop. - /// Required to allow GLContext sharing in some implementations like WGL. - main_thread_dispatcher: Arc>>>, - /// A vector for fast resolves of texture cache IDs to - /// native texture IDs. This maps to a free-list managed - /// by the backend thread / texture cache. We free the - /// texture memory associated with a TextureId when its - /// texture cache ID is freed by the texture cache, but - /// reuse the TextureId when the texture caches's free - /// list reuses the texture cache ID. This saves having to - /// use a hashmap, and allows a flat vector for performance. - cache_texture_id_map: Vec, + // Manages and resolves source textures IDs to real texture IDs. + texture_resolver: SourceTextureResolver, - /// A special 1x1 dummy cache texture used for shaders that expect to work - /// with the cache but are actually running in the first pass - /// when no target is yet provided as a cache texture input. - dummy_cache_texture_id: TextureId, + // A PBO used to do asynchronous texture cache uploads. + texture_cache_upload_pbo: PBOId, dither_matrix_texture_id: Option, @@ -793,13 +875,6 @@ pub struct Renderer { /// application to provide external buffers for image data. external_image_handler: Option>, - /// Map of external image IDs to native textures. - external_images: FastHashMap<(ExternalImageId, u8), TextureId>, - - // Optional trait object that handles WebVR commands. - // Some WebVR commands such as SubmitFrame must be synced with the WebGL render thread. - vr_compositor_handler: Arc>>>, - /// List of profile results from previous frames. Can be retrieved /// via get_frame_profiles(). cpu_profiles: VecDeque, @@ -1136,15 +1211,6 @@ impl Renderer { let backend_profile_counters = BackendProfileCounters::new(); - let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0]; - device.init_texture(dummy_cache_texture_id, - 1, - 1, - ImageFormat::BGRA8, - TextureFilter::Linear, - RenderTargetMode::LayerRenderTarget(1), - None); - let dither_matrix_texture_id = if options.enable_dithering { let dither_matrix: [u8; 64] = [ 00, 48, 12, 60, 03, 51, 15, 63, @@ -1164,6 +1230,7 @@ impl Renderer { ImageFormat::A8, TextureFilter::Nearest, RenderTargetMode::None, + 1, Some(&dither_matrix)); Some(id) @@ -1202,29 +1269,30 @@ impl Renderer { }, ]; - let prim_vao_id = device.create_vao(&DESC_PRIM_INSTANCES, mem::size_of::() as i32); - device.bind_vao(prim_vao_id); - device.update_vao_indices(prim_vao_id, &quad_indices, VertexUsageHint::Static); - device.update_vao_main_vertices(prim_vao_id, &quad_vertices, VertexUsageHint::Static); + let prim_vao = device.create_vao(&DESC_PRIM_INSTANCES, + mem::size_of::() as i32); + device.bind_vao(&prim_vao); + device.update_vao_indices(&prim_vao, + &quad_indices, + VertexUsageHint::Static); + device.update_vao_main_vertices(&prim_vao, + &quad_vertices, + VertexUsageHint::Static); - let blur_vao_id = device.create_vao_with_new_instances(&DESC_BLUR, mem::size_of::() as i32, prim_vao_id); - let clip_vao_id = device.create_vao_with_new_instances(&DESC_CLIP, mem::size_of::() as i32, prim_vao_id); + let blur_vao = device.create_vao_with_new_instances(&DESC_BLUR, + mem::size_of::() as i32, + &prim_vao); + let clip_vao = device.create_vao_with_new_instances(&DESC_CLIP, + mem::size_of::() as i32, + &prim_vao); + + let texture_cache_upload_pbo = device.create_pbo(); + + let texture_resolver = SourceTextureResolver::new(&mut device); device.end_frame(); - let main_thread_dispatcher = Arc::new(Mutex::new(None)); let backend_notifier = Arc::clone(¬ifier); - let backend_main_thread_dispatcher = Arc::clone(&main_thread_dispatcher); - - let vr_compositor = Arc::new(Mutex::new(None)); - let backend_vr_compositor = Arc::clone(&vr_compositor); - - // We need a reference to the webrender context from the render backend in order to share - // texture ids - let context_handle = match options.renderer_kind { - RendererKind::Native => GLContextHandleWrapper::current_native_handle(), - RendererKind::OSMesa => GLContextHandleWrapper::current_osmesa_handle(), - }; let default_font_render_mode = match (options.enable_aa, options.enable_subpixel_aa) { (true, true) => FontRenderMode::Subpixel, @@ -1236,7 +1304,6 @@ impl Renderer { enable_scrollbars: options.enable_scrollbars, default_font_render_mode, debug: options.debug, - cache_expiry_frames: options.cache_expiry_frames, }; let device_pixel_ratio = options.device_pixel_ratio; @@ -1261,12 +1328,9 @@ impl Renderer { texture_cache, workers, backend_notifier, - context_handle, config, recorder, - backend_main_thread_dispatcher, blob_image_renderer, - backend_vr_compositor, enable_render_on_scroll); backend.run(backend_profile_counters); })}; @@ -1323,22 +1387,19 @@ impl Renderer { color_render_targets: Vec::new(), alpha_render_targets: Vec::new(), gpu_profile, - prim_vao_id, - blur_vao_id, - clip_vao_id, + prim_vao, + blur_vao, + clip_vao, gdt_index: 0, gpu_data_textures, pipeline_epoch_map: FastHashMap::default(), - main_thread_dispatcher, - cache_texture_id_map: Vec::new(), - dummy_cache_texture_id, dither_matrix_texture_id, external_image_handler: None, - external_images: FastHashMap::default(), - vr_compositor_handler: vr_compositor, cpu_profiles: VecDeque::new(), gpu_profiles: VecDeque::new(), gpu_cache_texture, + texture_cache_upload_pbo, + texture_resolver, }; let sender = RenderApiSender::new(api_tx, payload_tx); @@ -1370,23 +1431,6 @@ impl Renderer { *notifier_arc = Some(notifier); } - /// Sets the new main thread dispatcher. - /// - /// Allows to dispatch functions to the main thread's event loop. - pub fn set_main_thread_dispatcher(&self, dispatcher: Box) { - let mut dispatcher_arc = self.main_thread_dispatcher.lock().unwrap(); - *dispatcher_arc = Some(dispatcher); - } - - /// Sets the VRCompositorHandler. - /// - /// It's used to handle WebVR render commands. - /// Some WebVR commands such as Vsync and SubmitFrame must be called in the WebGL render thread. - pub fn set_vr_compositor_handler(&self, creator: Box) { - let mut handler_arc = self.vr_compositor_handler.lock().unwrap(); - *handler_arc = Some(creator); - } - /// Returns the Epoch of the current frame in a pipeline. pub fn current_epoch(&self, pipeline_id: PipelineId) -> Option { self.pipeline_epoch_map.get(&pipeline_id).cloned() @@ -1445,27 +1489,6 @@ impl Renderer { } } - // Get the real (OpenGL) texture ID for a given source texture. - // For a texture cache texture, the IDs are stored in a vector - // map for fast access. For WebGL textures, the native texture ID - // is stored inline. When we add support for external textures, - // we will add a callback here that is able to ask the caller - // for the image data. - fn resolve_source_texture(&mut self, texture_id: &SourceTexture) -> TextureId { - match *texture_id { - SourceTexture::Invalid => TextureId::invalid(), - SourceTexture::WebGL(id) => TextureId::new(id, TextureTarget::Default), - SourceTexture::External(external_image) => { - *self.external_images - .get(&(external_image.id, external_image.channel_index)) - .expect("BUG: External image should be resolved by now!") - } - SourceTexture::TextureCache(index) => { - self.cache_texture_id_map[index.0] - } - } - } - /// Set a callback for handling external images. pub fn set_external_image_handler(&mut self, handler: Box) { self.external_image_handler = Some(handler); @@ -1607,127 +1630,84 @@ impl Renderer { fn update_texture_cache(&mut self) { let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update"); let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]); + for update_list in pending_texture_updates.drain(..) { for update in update_list.updates { match update.op { - TextureUpdateOp::Create { width, height, format, filter, mode, data } => { + TextureUpdateOp::Create { width, height, layer_count, format, filter, mode } => { let CacheTextureId(cache_texture_index) = update.id; - if self.cache_texture_id_map.len() == cache_texture_index { + if self.texture_resolver.cache_texture_id_map.len() == cache_texture_index { // Create a new native texture, as requested by the texture cache. let texture_id = self.device - .create_texture_ids(1, TextureTarget::Default)[0]; - self.cache_texture_id_map.push(texture_id); + .create_texture_ids(1, TextureTarget::Array)[0]; + self.texture_resolver.cache_texture_id_map.push(texture_id); } - let texture_id = self.cache_texture_id_map[cache_texture_index]; + let texture_id = self.texture_resolver.cache_texture_id_map[cache_texture_index]; - if let Some(image) = data { - match image { - ImageData::Raw(raw) => { - self.device.init_texture(texture_id, - width, - height, - format, - filter, - mode, - Some(raw.as_slice())); - } - ImageData::External(ext_image) => { - match ext_image.image_type { - ExternalImageType::ExternalBuffer => { - let handler = self.external_image_handler - .as_mut() - .expect("Found external image, but no handler set!"); + // Ensure no PBO is bound when creating the texture storage, + // or GL will attempt to read data from there. + self.device.bind_pbo(None); + self.device.init_texture(texture_id, + width, + height, + format, + filter, + mode, + layer_count, + None); + } + TextureUpdateOp::Update { rect, source, stride, layer_index, offset } => { + let texture_id = self.texture_resolver.cache_texture_id_map[update.id.0]; - match handler.lock(ext_image.id, ext_image.channel_index).source { - ExternalImageSource::RawData(raw) => { - self.device.init_texture(texture_id, - width, - height, - format, - filter, - mode, - Some(raw)); - } - _ => panic!("No external buffer found"), - }; - handler.unlock(ext_image.id, ext_image.channel_index); - } - ExternalImageType::Texture2DHandle | - ExternalImageType::TextureRectHandle | - ExternalImageType::TextureExternalHandle => { - panic!("External texture handle should not use TextureUpdateOp::Create."); - } + // Bind a PBO to do the texture upload. + // Updating the texture via PBO avoids CPU-side driver stalls. + self.device.bind_pbo(Some(self.texture_cache_upload_pbo)); + + match source { + TextureUpdateSource::Bytes { data } => { + self.device.update_pbo_data(&data[offset as usize..]); + } + TextureUpdateSource::External { id, channel_index } => { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + match handler.lock(id, channel_index).source { + ExternalImageSource::RawData(data) => { + self.device.update_pbo_data(&data[offset as usize..]); } - } - _ => { - panic!("No suitable image buffer for TextureUpdateOp::Create."); - } + _ => panic!("No external buffer found"), + }; + handler.unlock(id, channel_index); } - } else { - self.device.init_texture(texture_id, - width, - height, - format, - filter, - mode, - None); } - } - TextureUpdateOp::Grow { width, height, format, filter, mode } => { - let texture_id = self.cache_texture_id_map[update.id.0]; - self.device.resize_texture(texture_id, - width, - height, - format, - filter, - mode); - } - 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[offset as usize..]); - } - TextureUpdateOp::UpdateForExternalBuffer { rect, id, channel_index, stride, offset } => { - let handler = self.external_image_handler - .as_mut() - .expect("Found external image, but no handler set!"); - let device = &mut self.device; - let cached_id = self.cache_texture_id_map[update.id.0]; - match handler.lock(id, channel_index).source { - ExternalImageSource::RawData(data) => { - device.update_texture(cached_id, - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - stride, - &data[offset as usize..]); - } - _ => panic!("No external buffer found"), - }; - handler.unlock(id, channel_index); + self.device.update_texture_from_pbo(texture_id, + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + layer_index, + stride, + 0); } TextureUpdateOp::Free => { - let texture_id = self.cache_texture_id_map[update.id.0]; + let texture_id = self.texture_resolver.cache_texture_id_map[update.id.0]; self.device.deinit_texture(texture_id); } } } } + + // Ensure that other texture updates won't read from this PBO. + self.device.bind_pbo(None); } fn draw_instanced_batch(&mut self, data: &[T], - vao: VAOId, + vertex_array_kind: VertexArrayKind, textures: &BatchTextures) { - self.device.bind_vao(vao); - for i in 0..textures.colors.len() { - let texture_id = self.resolve_source_texture(&textures.colors[i]); + let texture_id = self.texture_resolver.resolve(&textures.colors[i]); self.device.bind_texture(TextureSampler::color(i), texture_id); } @@ -1736,6 +1716,14 @@ impl Renderer { self.device.bind_texture(TextureSampler::Dither, id); } + let vao = match vertex_array_kind { + VertexArrayKind::Primitive => &self.prim_vao, + VertexArrayKind::Clip => &self.clip_vao, + VertexArrayKind::Blur => &self.blur_vao, + }; + + self.device.bind_vao(vao); + if self.enable_batcher { self.device.update_vao_instances(vao, data, VertexUsageHint::Stream); self.device.draw_indexed_triangles_instanced_u16(6, data.len() as i32); @@ -1755,7 +1743,6 @@ impl Renderer { batch: &PrimitiveBatch, projection: &Transform3D, render_task_data: &[RenderTaskData], - cache_texture: TextureId, render_target: Option<(TextureId, i32)>, target_dimensions: DeviceUintSize) { let transform_kind = batch.key.flags.transform_kind(); @@ -1863,6 +1850,7 @@ impl Renderer { // they may overlap and affect each other. debug_assert!(batch.instances.len() == 1); let instance = CompositePrimitiveInstance::from(&batch.instances[0]); + let cache_texture = self.texture_resolver.resolve(&SourceTexture::CacheRGBA8); // TODO(gw): This code branch is all a bit hacky. We rely // on pulling specific values from the render target data @@ -1917,9 +1905,8 @@ impl Renderer { } let _gm = self.gpu_profile.add_marker(marker); - let vao = self.prim_vao_id; self.draw_instanced_batch(&batch.instances, - vao, + VertexArrayKind::Primitive, &batch.key.textures); } @@ -1927,7 +1914,6 @@ impl Renderer { render_target: Option<(TextureId, i32)>, target: &ColorRenderTarget, target_size: DeviceUintSize, - color_cache_texture: TextureId, clear_color: Option<[f32; 4]>, render_task_data: &[RenderTaskData], projection: &Transform3D) { @@ -1965,20 +1951,19 @@ impl Renderer { // blur radii with fixed weights. if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR); - let vao = self.blur_vao_id; self.device.set_blend(false); self.cs_blur.bind(&mut self.device, projection); if !target.vertical_blurs.is_empty() { self.draw_instanced_batch(&target.vertical_blurs, - vao, + VertexArrayKind::Blur, &BatchTextures::no_texture()); } if !target.horizontal_blurs.is_empty() { self.draw_instanced_batch(&target.horizontal_blurs, - vao, + VertexArrayKind::Blur, &BatchTextures::no_texture()); } } @@ -1987,10 +1972,9 @@ impl Renderer { if !target.box_shadow_cache_prims.is_empty() { self.device.set_blend(false); let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); - let vao = self.prim_vao_id; self.cs_box_shadow.bind(&mut self.device, projection); self.draw_instanced_batch(&target.box_shadow_cache_prims, - vao, + VertexArrayKind::Primitive, &BatchTextures::no_texture()); } @@ -2005,10 +1989,9 @@ impl Renderer { self.device.set_blend_mode_alpha(); let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN); - let vao = self.prim_vao_id; self.cs_text_run.bind(&mut self.device, projection); self.draw_instanced_batch(&target.text_run_cache_prims, - vao, + VertexArrayKind::Primitive, &target.text_run_textures); } if !target.line_cache_prims.is_empty() { @@ -2018,10 +2001,9 @@ impl Renderer { self.device.set_blend_mode_alpha(); let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE); - let vao = self.prim_vao_id; self.cs_line.bind(&mut self.device, projection); self.draw_instanced_batch(&target.line_cache_prims, - vao, + VertexArrayKind::Primitive, &BatchTextures::no_texture()); } @@ -2045,7 +2027,6 @@ impl Renderer { self.submit_batch(batch, &projection, render_task_data, - color_cache_texture, render_target, target_size); } @@ -2077,7 +2058,6 @@ impl Renderer { self.submit_batch(batch, &projection, render_task_data, - color_cache_texture, render_target, target_size); } @@ -2112,7 +2092,6 @@ impl Renderer { // Draw the clip items into the tiled alpha mask. { let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP); - let vao = self.clip_vao_id; // If we have border corner clips, the first step is to clear out the // area in the clip mask. This allows drawing multiple invididual clip @@ -2122,7 +2101,7 @@ impl Renderer { self.device.set_blend(false); self.cs_clip_border.bind(&mut self.device, projection); self.draw_instanced_batch(&target.clip_batcher.border_clears, - vao, + VertexArrayKind::Clip, &BatchTextures::no_texture()); } @@ -2137,7 +2116,7 @@ impl Renderer { self.device.set_blend_mode_max(); self.cs_clip_border.bind(&mut self.device, projection); self.draw_instanced_batch(&target.clip_batcher.borders, - vao, + VertexArrayKind::Clip, &BatchTextures::no_texture()); } @@ -2150,7 +2129,7 @@ impl Renderer { let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip rectangles"); self.cs_clip_rectangle.bind(&mut self.device, projection); self.draw_instanced_batch(&target.clip_batcher.rectangles, - vao, + VertexArrayKind::Clip, &BatchTextures::no_texture()); } // draw image masks @@ -2165,7 +2144,7 @@ impl Renderer { }; self.cs_clip_image.bind(&mut self.device, projection); self.draw_instanced_batch(items, - vao, + VertexArrayKind::Clip, &textures); } } @@ -2189,6 +2168,7 @@ impl Renderer { let image = handler.lock(ext_image.id, ext_image.channel_index); let texture_target = match ext_image.image_type { ExternalImageType::Texture2DHandle => TextureTarget::Default, + ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, ExternalImageType::TextureRectHandle => TextureTarget::Rect, ExternalImageType::TextureExternalHandle => TextureTarget::External, ExternalImageType::ExternalBuffer => { @@ -2202,26 +2182,29 @@ impl Renderer { _ => panic!("No native texture found."), }; - self.external_images.insert((ext_image.id, ext_image.channel_index), texture_id); + self.texture_resolver + .external_images + .insert((ext_image.id, ext_image.channel_index), texture_id); let update = GpuCacheUpdate::Copy { block_index: 0, block_count: 1, address: deferred_resolve.address, }; - let blocks = [ [image.u0, image.v0, image.u1, image.v1].into() ]; + + let blocks = [ [image.u0, image.v0, image.u1, image.v1].into(), [0.0; 4].into() ]; self.gpu_cache_texture.apply_patch(&update, &blocks); } } } fn unlock_external_images(&mut self) { - if !self.external_images.is_empty() { + if !self.texture_resolver.external_images.is_empty() { let handler = self.external_image_handler .as_mut() .expect("Found external image, but no handler set!"); - for (ext_data, _) in self.external_images.drain() { + for (ext_data, _) in self.texture_resolver.external_images.drain() { handler.unlock(ext_data.0, ext_data.1); } } @@ -2264,7 +2247,8 @@ impl Renderer { frame.cache_size.height as u32, ImageFormat::BGRA8, TextureFilter::Linear, - RenderTargetMode::LayerRenderTarget(target_count as i32), + RenderTargetMode::RenderTarget, + target_count as i32, None); } if let Some(texture_id) = pass.alpha_texture_id { @@ -2274,7 +2258,8 @@ impl Renderer { frame.cache_size.height as u32, ImageFormat::A8, TextureFilter::Nearest, - RenderTargetMode::LayerRenderTarget(target_count as i32), + RenderTargetMode::RenderTarget, + target_count as i32, None); } } @@ -2285,6 +2270,7 @@ impl Renderer { // number of driver stalls. self.gpu_data_textures[self.gdt_index].init_frame(&mut self.device, frame); self.gdt_index = (self.gdt_index + 1) % GPU_DATA_TEXTURE_POOL; + self.texture_resolver.set_cache_textures(None, None); } fn draw_tile_frame(&mut self, @@ -2307,9 +2293,6 @@ impl Renderer { } else { self.start_frame(frame); - let mut src_color_id = self.dummy_cache_texture_id; - let mut src_alpha_id = self.dummy_cache_texture_id; - for pass in &mut frame.passes { let size; let clear_color; @@ -2341,8 +2324,10 @@ impl Renderer { ORTHO_FAR_PLANE); } - self.device.bind_texture(TextureSampler::CacheA8, src_alpha_id); - self.device.bind_texture(TextureSampler::CacheRGBA8, src_color_id); + let cache_a8_texture = self.texture_resolver.resolve(&SourceTexture::CacheA8); + let cache_rgba8_texture = self.texture_resolver.resolve(&SourceTexture::CacheRGBA8); + self.device.bind_texture(TextureSampler::CacheA8, cache_a8_texture); + self.device.bind_texture(TextureSampler::CacheRGBA8, cache_rgba8_texture); for (target_index, target) in pass.alpha_targets.targets.iter().enumerate() { self.draw_alpha_target((pass.alpha_texture_id.unwrap(), target_index as i32), @@ -2358,15 +2343,13 @@ impl Renderer { self.draw_color_target(render_target, target, *size, - src_color_id, clear_color, &frame.render_task_data, &projection); } - src_color_id = pass.color_texture_id.unwrap_or(self.dummy_cache_texture_id); - src_alpha_id = pass.alpha_texture_id.unwrap_or(self.dummy_cache_texture_id); + self.texture_resolver.set_cache_textures(pass.alpha_texture_id, pass.color_texture_id); // Return the texture IDs to the pool for next frame. if let Some(texture_id) = pass.color_texture_id.take() { @@ -2443,25 +2426,37 @@ impl Renderer { let mut spacing = 16; let mut size = 512; let fb_width = framebuffer_size.width as i32; - let num_textures = self.cache_texture_id_map.len() as i32; + let num_layers: i32 = self.texture_resolver + .cache_texture_id_map + .iter() + .map(|id| { + self.device.get_texture_layer_count(*id) + }) + .sum(); - if num_textures * (size + spacing) > fb_width { - let factor = fb_width as f32 / (num_textures * (size + spacing)) as f32; + if num_layers * (size + spacing) > fb_width { + let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32; size = (size as f32 * factor) as i32; spacing = (spacing as f32 * factor) as i32; } - for (i, texture_id) in self.cache_texture_id_map.iter().enumerate() { - let x = fb_width - (spacing + size) * (i as i32 + 1); + let mut i = 0; + for texture_id in &self.texture_resolver.cache_texture_id_map { let y = spacing + if self.debug_flags.contains(RENDER_TARGET_DBG) { 528 } else { 0 }; - // If we have more targets than fit on one row in screen, just early exit. - if x > fb_width { - return; - } + let layer_count = self.device.get_texture_layer_count(*texture_id); + for layer_index in 0..layer_count { + let x = fb_width - (spacing + size) * (i as i32 + 1); - let dest_rect = rect(x, y, size, size); - self.device.blit_render_target(Some((*texture_id, 0)), None, dest_rect); + // If we have more targets than fit on one row in screen, just early exit. + if x > fb_width { + return; + } + + let dest_rect = rect(x, y, size, size); + self.device.blit_render_target(Some((*texture_id, layer_index)), None, dest_rect); + i += 1; + } } } @@ -2494,7 +2489,10 @@ impl Renderer { pub fn deinit(mut self) { //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame self.device.begin_frame(1.0); - self.device.deinit_texture(self.dummy_cache_texture_id); + self.texture_resolver.deinit(&mut self.device); + self.device.delete_vao(self.prim_vao); + self.device.delete_vao(self.clip_vao); + self.device.delete_vao(self.blur_vao); self.debug.deinit(&mut self.device); self.cs_box_shadow.deinit(&mut self.device); self.cs_text_run.deinit(&mut self.device); @@ -2507,13 +2505,13 @@ impl Renderer { self.ps_rectangle_clip.deinit(&mut self.device); self.ps_text_run.deinit(&mut self.device); self.ps_text_run_subpixel.deinit(&mut self.device); - for shader in &mut self.ps_image { - if let &mut Some(ref mut shader) = shader { + for shader in self.ps_image { + if let Some(shader) = shader { shader.deinit(&mut self.device); } } - for shader in &mut self.ps_yuv_image { - if let &mut Some(ref mut shader) = shader { + for shader in self.ps_yuv_image { + if let Some(shader) = shader { shader.deinit(&mut self.device); } } @@ -2587,7 +2585,6 @@ pub struct RendererOptions { pub enable_clear_scissor: bool, pub enable_batcher: bool, pub max_texture_size: Option, - pub cache_expiry_frames: u32, pub workers: Option>, pub blob_image_renderer: Option>, pub recorder: Option>, @@ -2614,7 +2611,6 @@ impl Default for RendererOptions { enable_clear_scissor: true, enable_batcher: true, max_texture_size: None, - cache_expiry_frames: 600, // roughly, 10 seconds workers: None, blob_image_renderer: None, recorder: None, diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index 4dc975faf980..ef7b84ae26d6 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -8,21 +8,20 @@ use glyph_cache::GlyphCache; use gpu_cache::{GpuCache, GpuCacheHandle}; use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}; use profiler::{ResourceProfileCounters, TextureCacheProfileCounters}; -use std::cmp; use std::collections::hash_map::Entry::{self, Occupied, Vacant}; use std::fmt::Debug; use std::hash::Hash; use std::mem; use std::sync::Arc; -use texture_cache::{TextureCache, TextureCacheItemId}; +use texture_cache::{TextureCache, TextureCacheHandle}; use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest}; use api::{BlobImageResources, BlobImageData, ResourceUpdates, ResourceUpdate, AddFont}; -use api::{DevicePoint, DeviceIntSize, DeviceUintRect, DeviceUintSize}; -use api::{Epoch, FontInstanceKey, FontKey, FontTemplate}; +use api::{DevicePoint, DeviceUintRect, DeviceUintSize}; +use api::{Epoch, FontInstance, FontKey, FontTemplate}; use api::{GlyphDimensions, GlyphKey, IdNamespace}; use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering}; use api::{TileOffset, TileSize}; -use api::{ExternalImageData, ExternalImageType, WebGLContextId}; +use api::{ExternalImageData, ExternalImageType}; use rayon::ThreadPool; use glyph_rasterizer::{GlyphRasterizer, GlyphRequest}; @@ -102,85 +101,44 @@ impl ImageTemplates { } struct CachedImageInfo { - texture_cache_id: TextureCacheItemId, + texture_cache_handle: TextureCacheHandle, epoch: Epoch, - last_access: FrameId, } pub struct ResourceClassCache { resources: FastHashMap, } -impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resource { +impl ResourceClassCache where K: Clone + Hash + Eq + Debug { pub fn new() -> ResourceClassCache { ResourceClassCache { resources: FastHashMap::default(), } } - fn get(&self, key: &K, frame: FrameId) -> &V { - let resource = self.resources - .get(key) - .expect("Didn't find a cached resource with that ID!"); - - // This assert catches cases in which we accidentally request a resource that we forgot to - // mark as needed this frame. - debug_assert_eq!(frame, resource.get_last_access_time()); - - resource + fn get(&self, key: &K) -> &V { + self.resources + .get(key) + .expect("Didn't find a cached resource with that ID!") } pub fn insert(&mut self, key: K, value: V) { self.resources.insert(key, value); } - pub fn entry(&mut self, key: K, frame: FrameId) -> Entry { - let mut entry = self.resources.entry(key); - match entry { - Occupied(ref mut entry) => { - entry.get_mut().set_last_access_time(frame); - } - Vacant(..) => {} - } - entry + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.resources.get_mut(key) } - pub fn is_empty(&self) -> bool { - self.resources.is_empty() + pub fn entry(&mut self, key: K) -> Entry { + self.resources.entry(key) } - pub fn update(&mut self, - texture_cache: &mut TextureCache, - gpu_cache: &mut GpuCache, - current_frame_id: FrameId, - expiry_frame_id: FrameId) { - let mut resources_to_destroy = Vec::new(); - - for (key, resource) in &self.resources { - let last_access = resource.get_last_access_time(); - if last_access < expiry_frame_id { - resources_to_destroy.push(key.clone()); - } else if last_access == current_frame_id { - resource.add_to_gpu_cache(texture_cache, gpu_cache); - } - } - - for key in resources_to_destroy { - let resource = - self.resources - .remove(&key) - .expect("Resource was in `last_access_times` but not in `resources`!"); - resource.free(texture_cache); - } + pub fn clear(&mut self) { + self.resources.clear(); } - pub fn clear(&mut self, texture_cache: &mut TextureCache) { - for (_, resource) in self.resources.drain() { - resource.free(texture_cache); - } - } - - fn clear_keys(&mut self, texture_cache: &mut TextureCache, key_fun: F) + fn clear_keys(&mut self, key_fun: F) where for<'r> F: Fn(&'r &K) -> bool { let resources_to_destroy = self.resources.keys() @@ -188,8 +146,7 @@ impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resourc .cloned() .collect::>(); for key in resources_to_destroy { - let resource = self.resources.remove(&key).unwrap(); - resource.free(texture_cache); + self.resources.remove(&key).unwrap(); } } } @@ -211,11 +168,6 @@ impl Into for ImageRequest { } } -pub struct WebGLTexture { - pub id: SourceTexture, - pub size: DeviceIntSize, -} - struct Resources { font_templates: FastHashMap, image_templates: ImageTemplates, @@ -234,9 +186,6 @@ pub struct ResourceCache { cached_glyphs: GlyphCache, cached_images: ResourceClassCache, - // TODO(pcwalton): Figure out the lifecycle of these. - webgl_textures: FastHashMap, - resources: Resources, state: State, current_frame_id: FrameId, @@ -253,19 +202,15 @@ pub struct ResourceCache { pending_image_requests: FastHashSet, blob_image_renderer: Option>, - - cache_expiry_frames: u32, } impl ResourceCache { pub fn new(texture_cache: TextureCache, workers: Arc, - blob_image_renderer: Option>, - cache_expiry_frames: u32) -> ResourceCache { + blob_image_renderer: Option>) -> ResourceCache { ResourceCache { cached_glyphs: GlyphCache::new(), cached_images: ResourceClassCache::new(), - webgl_textures: FastHashMap::default(), resources: Resources { font_templates: FastHashMap::default(), image_templates: ImageTemplates::new(), @@ -277,7 +222,6 @@ impl ResourceCache { pending_image_requests: FastHashSet::default(), glyph_rasterizer: GlyphRasterizer::new(workers), blob_image_renderer, - cache_expiry_frames, } } @@ -426,7 +370,7 @@ impl ResourceCache { pub fn delete_image_template(&mut self, image_key: ImageKey) { let value = self.resources.image_templates.remove(image_key); - self.cached_images.clear_keys(&mut self.texture_cache, |request| request.key == image_key); + self.cached_images.clear_keys(|request| request.key == image_key); match value { Some(image) => { @@ -440,25 +384,11 @@ impl ResourceCache { } } - pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) { - self.webgl_textures.insert(id, WebGLTexture { - id: texture_id, - size, - }); - } - - pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) { - let webgl_texture = self.webgl_textures.get_mut(&id).unwrap(); - - // Update new texture id and size - webgl_texture.id = texture_id; - webgl_texture.size = size; - } - pub fn request_image(&mut self, key: ImageKey, rendering: ImageRendering, - tile: Option) { + tile: Option, + gpu_cache: &mut GpuCache) { debug_assert_eq!(self.state, State::AddResources); let request = ImageRequest { @@ -467,74 +397,92 @@ impl ResourceCache { tile, }; - let template = self.resources.image_templates.get(key).unwrap(); - - // Images that don't use the texture cache can early out. - if !template.data.uses_texture_cache() { - return; - } - - // If this image exists in the texture cache, *and* the epoch - // in the cache matches that of the template, then it is - // valid to use as-is. - match self.cached_images.entry(request, self.current_frame_id) { - Occupied(entry) => { - let cached_image = entry.get(); - if cached_image.epoch == template.epoch { + match self.resources.image_templates.get(key) { + Some(template) => { + // Images that don't use the texture cache can early out. + if !template.data.uses_texture_cache() { return; } - } - Vacant(..) => {} - } - // We can start a worker thread rasterizing right now, if: - // - The image is a blob. - // - The blob hasn't already been requested this frame. - if self.pending_image_requests.insert(request) { - if template.data.is_blob() { - if let Some(ref mut renderer) = self.blob_image_renderer { - let (offset, w, h) = match template.tiling { - Some(tile_size) => { - let tile_offset = request.tile.unwrap(); - let (w, h) = compute_tile_size(&template.descriptor, tile_size, tile_offset); - let offset = DevicePoint::new( - tile_offset.x as f32 * tile_size as f32, - tile_offset.y as f32 * tile_size as f32, - ); + // If this image exists in the texture cache, *and* the epoch + // in the cache matches that of the template, then it is + // valid to use as-is. + let (entry, needs_update) = match self.cached_images.entry(request) { + Occupied(entry) => { + let needs_update = entry.get().epoch != template.epoch; + (entry.into_mut(), needs_update) + } + Vacant(entry) => { + (entry.insert(CachedImageInfo { + epoch: template.epoch, + texture_cache_handle: TextureCacheHandle::new(), + }), true) + } + }; - (offset, w, h) - } - None => { - (DevicePoint::zero(), template.descriptor.width, template.descriptor.height) - } - }; + let needs_upload = self.texture_cache + .request(&mut entry.texture_cache_handle, + gpu_cache); - renderer.request( - &self.resources, - request.into(), - &BlobImageDescriptor { - width: w, - height: h, - offset, - format: template.descriptor.format, - }, - template.dirty_rect, - ); + if !needs_upload && !needs_update { + return; } + + // We can start a worker thread rasterizing right now, if: + // - The image is a blob. + // - The blob hasn't already been requested this frame. + if self.pending_image_requests.insert(request) { + if template.data.is_blob() { + if let Some(ref mut renderer) = self.blob_image_renderer { + let (offset, w, h) = match template.tiling { + Some(tile_size) => { + let tile_offset = request.tile.unwrap(); + let (w, h) = compute_tile_size(&template.descriptor, tile_size, tile_offset); + let offset = DevicePoint::new( + tile_offset.x as f32 * tile_size as f32, + tile_offset.y as f32 * tile_size as f32, + ); + + (offset, w, h) + } + None => { + (DevicePoint::zero(), template.descriptor.width, template.descriptor.height) + } + }; + + renderer.request( + &self.resources, + request.into(), + &BlobImageDescriptor { + width: w, + height: h, + offset, + format: template.descriptor.format, + }, + template.dirty_rect, + ); + } + } + } + } + None => { + warn!("ERROR: Trying to render deleted / non-existent key {:?}", key); } } } pub fn request_glyphs(&mut self, - font: FontInstanceKey, - glyph_keys: &[GlyphKey]) { + font: FontInstance, + glyph_keys: &[GlyphKey], + gpu_cache: &mut GpuCache) { debug_assert_eq!(self.state, State::AddResources); self.glyph_rasterizer.request_glyphs( &mut self.cached_glyphs, - self.current_frame_id, font, glyph_keys, + &mut self.texture_cache, + gpu_cache, ); } @@ -543,7 +491,7 @@ impl ResourceCache { } pub fn get_glyphs(&self, - font: FontInstanceKey, + font: FontInstance, glyph_keys: &[GlyphKey], mut f: F) -> SourceTexture where F: FnMut(usize, &GpuCacheHandle) { debug_assert_eq!(self.state, State::QueryResources); @@ -552,10 +500,8 @@ impl ResourceCache { let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font); for (loop_index, key) in glyph_keys.iter().enumerate() { - let glyph = glyph_key_cache.get(key, self.current_frame_id); - let cache_item = glyph.texture_cache_id - .as_ref() - .map(|image_id| self.texture_cache.get(image_id)); + let glyph = glyph_key_cache.get(key); + let cache_item = glyph.as_ref().map(|info| self.texture_cache.get(&info.texture_cache_handle)); if let Some(cache_item) = cache_item { f(loop_index, &cache_item.uv_rect_handle); debug_assert!(texture_id == None || @@ -564,11 +510,11 @@ impl ResourceCache { } } - texture_id.map_or(SourceTexture::Invalid, SourceTexture::TextureCache) + texture_id.unwrap_or(SourceTexture::Invalid) } pub fn get_glyph_dimensions(&mut self, - font: &FontInstanceKey, + font: &FontInstance, key: &GlyphKey) -> Option { let key = GlyphRequest::new(font, key); @@ -595,38 +541,37 @@ impl ResourceCache { rendering: image_rendering, tile, }; - let image_info = &self.cached_images.get(&key, self.current_frame_id); - let item = self.texture_cache.get(&image_info.texture_cache_id); - CacheItem { - texture_id: SourceTexture::TextureCache(item.texture_id), - uv_rect_handle: item.uv_rect_handle, - } + let image_info = &self.cached_images.get(&key); + self.texture_cache.get(&image_info.texture_cache_handle) } - pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties { - let image_template = &self.resources.image_templates.get(image_key).unwrap(); + pub fn get_image_properties(&self, image_key: ImageKey) -> Option { + let image_template = &self.resources.image_templates.get(image_key); - let external_image = match image_template.data { - ImageData::External(ext_image) => { - match ext_image.image_type { - ExternalImageType::Texture2DHandle | - ExternalImageType::TextureRectHandle | - ExternalImageType::TextureExternalHandle => { - Some(ext_image) - }, - // external buffer uses resource_cache. - ExternalImageType::ExternalBuffer => None, - } - }, - // raw and blob image are all using resource_cache. - ImageData::Raw(..) | ImageData::Blob(..) => None, - }; + image_template.map(|image_template| { + let external_image = match image_template.data { + ImageData::External(ext_image) => { + match ext_image.image_type { + ExternalImageType::Texture2DHandle | + ExternalImageType::Texture2DArrayHandle | + ExternalImageType::TextureRectHandle | + ExternalImageType::TextureExternalHandle => { + Some(ext_image) + }, + // external buffer uses resource_cache. + ExternalImageType::ExternalBuffer => None, + } + }, + // raw and blob image are all using resource_cache. + ImageData::Raw(..) | ImageData::Blob(..) => None, + }; - ImageProperties { - descriptor: image_template.descriptor, - external_image, - tiling: image_template.tiling, - } + ImageProperties { + descriptor: image_template.descriptor, + external_image, + tiling: image_template.tiling, + } + }) } pub fn get_tiled_image_map(&self) -> TiledImageMap { @@ -639,17 +584,10 @@ impl ResourceCache { ).collect() } - pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> &WebGLTexture { - &self.webgl_textures[context_id] - } - - pub fn get_webgl_texture_size(&self, context_id: &WebGLContextId) -> DeviceIntSize { - self.webgl_textures[context_id].size - } - pub fn begin_frame(&mut self, frame_id: FrameId) { debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; + self.texture_cache.begin_frame(frame_id); self.current_frame_id = frame_id; } @@ -662,29 +600,20 @@ impl ResourceCache { self.state = State::QueryResources; self.glyph_rasterizer.resolve_glyphs( - self.current_frame_id, &mut self.cached_glyphs, &mut self.texture_cache, + gpu_cache, texture_cache_profile, ); // Apply any updates of new / updated images (incl. blobs) to the texture cache. - self.update_texture_cache(texture_cache_profile); - - // Expire any resources that haven't been used for `cache_expiry_frames`. - let num_frames_back = self.cache_expiry_frames; - let expiry_frame = FrameId(cmp::max(num_frames_back, self.current_frame_id.0) - num_frames_back); - self.cached_images.update(&mut self.texture_cache, - gpu_cache, - self.current_frame_id, - expiry_frame); - self.cached_glyphs.update(&mut self.texture_cache, - gpu_cache, - self.current_frame_id, - expiry_frame); + self.update_texture_cache(gpu_cache, texture_cache_profile); + self.texture_cache.end_frame(); } - fn update_texture_cache(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { + fn update_texture_cache(&mut self, + gpu_cache: &mut GpuCache, + _texture_cache_profile: &mut TextureCacheProfileCounters) { for request in self.pending_image_requests.drain() { let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); debug_assert!(image_template.data.uses_texture_cache()); @@ -757,38 +686,15 @@ impl ResourceCache { image_template.descriptor.clone() }; - match self.cached_images.entry(request, self.current_frame_id) { - Occupied(mut entry) => { - let entry = entry.get_mut(); - - // We should only get to this code path if the image - // definitely needs to be updated. - debug_assert!(entry.epoch != image_template.epoch); - self.texture_cache.update(&entry.texture_cache_id, - descriptor, - filter, - image_data, - image_template.dirty_rect); - - // Update the cached epoch - debug_assert_eq!(self.current_frame_id, entry.last_access); - entry.epoch = image_template.epoch; - image_template.dirty_rect = None; - } - Vacant(entry) => { - let image_id = self.texture_cache.insert(descriptor, - filter, - image_data, - [0.0; 2], - texture_cache_profile); - - entry.insert(CachedImageInfo { - texture_cache_id: image_id, - epoch: image_template.epoch, - last_access: self.current_frame_id, - }); - } - }; + let entry = self.cached_images.get_mut(&request).unwrap(); + self.texture_cache.update(&mut entry.texture_cache_handle, + descriptor, + filter, + image_data, + [0.0; 2], + image_template.dirty_rect, + gpu_cache); + image_template.dirty_rect = None; } } @@ -806,8 +712,8 @@ impl ResourceCache { // The advantage of clearing the cache completely is that it gets rid of any // remaining fragmentation that could have persisted if we kept around the most // recently used resources. - self.cached_images.clear(&mut self.texture_cache); - self.cached_glyphs.clear(&mut self.texture_cache); + self.cached_images.clear(); + self.cached_glyphs.clear(); } pub fn clear_namespace(&mut self, namespace: IdNamespace) { @@ -828,37 +734,8 @@ impl ResourceCache { self.resources.font_templates.remove(key); } - self.cached_images.clear_keys(&mut self.texture_cache, |request| request.key.0 == namespace); - self.cached_glyphs.clear_fonts(&mut self.texture_cache, |font| font.font_key.0 == namespace); - } -} - -pub trait Resource { - fn free(self, texture_cache: &mut TextureCache); - fn get_last_access_time(&self) -> FrameId; - fn set_last_access_time(&mut self, frame_id: FrameId); - fn add_to_gpu_cache(&self, - texture_cache: &mut TextureCache, - gpu_cache: &mut GpuCache); -} - -impl Resource for CachedImageInfo { - fn free(self, texture_cache: &mut TextureCache) { - texture_cache.free(self.texture_cache_id); - } - fn get_last_access_time(&self) -> FrameId { - self.last_access - } - fn set_last_access_time(&mut self, frame_id: FrameId) { - self.last_access = frame_id; - } - fn add_to_gpu_cache(&self, - texture_cache: &mut TextureCache, - gpu_cache: &mut GpuCache) { - let item = texture_cache.get_mut(&self.texture_cache_id); - if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) { - request.push(item.uv_rect); - } + self.cached_images.clear_keys(|request| request.key.0 == namespace); + self.cached_glyphs.clear_fonts(|font| font.font_key.0 == namespace); } } diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs index d664bf33ab2a..4a12a4c535d1 100644 --- a/gfx/webrender/src/texture_cache.rs +++ b/gfx/webrender/src/texture_cache.rs @@ -3,514 +3,33 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use device::TextureFilter; -use freelist::{FreeList, FreeListHandle}; -use gpu_cache::GpuCacheHandle; -use internal_types::{FastHashMap, TextureUpdate, TextureUpdateOp, UvRect}; -use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList}; -use profiler::TextureCacheProfileCounters; +use frame::FrameId; +use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; +use gpu_cache::{GpuCache, GpuCacheHandle}; +use internal_types::{SourceTexture, TextureUpdate, TextureUpdateOp}; +use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList, TextureUpdateSource}; +use resource_cache::CacheItem; use std::cmp; -use std::collections::hash_map::Entry; use std::mem; -use std::slice::Iter; -use time; -use util; use api::{ExternalImageType, ImageData, ImageFormat}; use api::{DeviceUintRect, DeviceUintSize, DeviceUintPoint}; -use api::{DevicePoint, ImageDescriptor}; +use api::{ImageDescriptor}; -/// The number of bytes we're allowed to use for a texture. -const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256; // 256MB +// The fixed number of layers for the shared texture cache. +// There is one array texture per image format, allocated lazily. +const TEXTURE_ARRAY_LAYERS: i32 = 2; -/// The number of RGBA pixels we're allowed to use for a texture. -const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4; +// The dimensions of each layer in the texture cache. +const TEXTURE_LAYER_DIMENSIONS: u32 = 2048; -/// The desired initial size of each texture, in pixels. -const INITIAL_TEXTURE_SIZE: u32 = 1024; +// The size of each region (page) in a texture layer. +const TEXTURE_REGION_DIMENSIONS: u32 = 512; -/// The square root of the number of RGBA pixels we're allowed to use for a texture, rounded down. -/// to the next power of two. -const SQRT_MAX_RGBA_PIXELS_PER_TEXTURE: u32 = 8192; - -/// The minimum number of pixels on each side that we require for rects to be classified as -/// "medium" within the free list. -const MINIMUM_MEDIUM_RECT_SIZE: u32 = 16; - -/// The minimum number of pixels on each side that we require for rects to be classified as -/// "large" within the free list. -const MINIMUM_LARGE_RECT_SIZE: u32 = 32; - -/// The amount of time in milliseconds we give ourselves to coalesce rects before giving up. -const COALESCING_TIMEOUT: u64 = 100; - -/// The number of items that we process in the coalescing work list before checking whether we hit -/// the timeout. -const COALESCING_TIMEOUT_CHECKING_INTERVAL: usize = 256; - -pub type TextureCacheItemId = FreeListHandle; - -enum CoalescingStatus { - Changed, - Unchanged, - Timeout, -} - -/// A texture allocator using the guillotine algorithm with the rectangle merge improvement. See -/// sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two- -/// Dimensional Rectangle Bin Packing": -/// -/// http://clb.demon.fi/files/RectangleBinPack.pdf -/// -/// This approach was chosen because of its simplicity, good performance, and easy support for -/// dynamic texture deallocation. -pub struct TexturePage { - texture_id: CacheTextureId, - texture_size: DeviceUintSize, - free_list: FreeRectList, - coalesce_vec: Vec, - allocations: u32, - dirty: bool, -} - -impl TexturePage { - fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage { - let mut page = TexturePage { - texture_id, - texture_size, - free_list: FreeRectList::new(), - coalesce_vec: Vec::new(), - allocations: 0, - dirty: false, - }; - page.clear(); - page - } - - fn find_index_of_best_rect_in_bin(&self, bin: FreeListBin, requested_dimensions: &DeviceUintSize) - -> Option { - let mut smallest_index_and_area = None; - for (candidate_index, candidate_rect) in self.free_list.iter(bin).enumerate() { - if !requested_dimensions.fits_inside(&candidate_rect.size) { - continue - } - - let candidate_area = candidate_rect.size.width * candidate_rect.size.height; - smallest_index_and_area = Some((candidate_index, candidate_area)); - break - } - - smallest_index_and_area.map(|(index, _)| FreeListIndex(bin, index)) - } - - /// Find a suitable rect in the free list. We choose the smallest such rect - /// in terms of area (Best-Area-Fit, BAF). - fn find_index_of_best_rect(&self, requested_dimensions: &DeviceUintSize) - -> Option { - let bin = FreeListBin::for_size(requested_dimensions); - for &target_bin in &[FreeListBin::Small, FreeListBin::Medium, FreeListBin::Large] { - if bin <= target_bin { - if let Some(index) = self.find_index_of_best_rect_in_bin(target_bin, - requested_dimensions) { - return Some(index); - } - } - } - None - } - - fn can_allocate(&self, requested_dimensions: &DeviceUintSize) -> bool { - self.find_index_of_best_rect(requested_dimensions).is_some() - } - - pub fn allocate(&mut self, - requested_dimensions: &DeviceUintSize) -> Option { - if requested_dimensions.width == 0 || requested_dimensions.height == 0 { - return Some(DeviceUintPoint::new(0, 0)) - } - let index = match self.find_index_of_best_rect(requested_dimensions) { - None => return None, - Some(index) => index, - }; - - // Remove the rect from the free list and decide how to guillotine it. We choose the split - // that results in the single largest area (Min Area Split Rule, MINAS). - let chosen_rect = self.free_list.remove(index); - let candidate_free_rect_to_right = - DeviceUintRect::new( - DeviceUintPoint::new(chosen_rect.origin.x + requested_dimensions.width, chosen_rect.origin.y), - DeviceUintSize::new(chosen_rect.size.width - requested_dimensions.width, requested_dimensions.height)); - let candidate_free_rect_to_bottom = - DeviceUintRect::new( - DeviceUintPoint::new(chosen_rect.origin.x, chosen_rect.origin.y + requested_dimensions.height), - DeviceUintSize::new(requested_dimensions.width, chosen_rect.size.height - requested_dimensions.height)); - let candidate_free_rect_to_right_area = candidate_free_rect_to_right.size.width * - candidate_free_rect_to_right.size.height; - let candidate_free_rect_to_bottom_area = candidate_free_rect_to_bottom.size.width * - candidate_free_rect_to_bottom.size.height; - - // Guillotine the rectangle. - let new_free_rect_to_right; - let new_free_rect_to_bottom; - if candidate_free_rect_to_right_area > candidate_free_rect_to_bottom_area { - new_free_rect_to_right = DeviceUintRect::new( - candidate_free_rect_to_right.origin, - DeviceUintSize::new(candidate_free_rect_to_right.size.width, - chosen_rect.size.height)); - new_free_rect_to_bottom = candidate_free_rect_to_bottom - } else { - new_free_rect_to_right = candidate_free_rect_to_right; - new_free_rect_to_bottom = - DeviceUintRect::new(candidate_free_rect_to_bottom.origin, - DeviceUintSize::new(chosen_rect.size.width, - candidate_free_rect_to_bottom.size.height)) - } - - // Add the guillotined rects back to the free list. If any changes were made, we're now - // dirty since coalescing might be able to defragment. - if !util::rect_is_empty(&new_free_rect_to_right) { - self.free_list.push(&new_free_rect_to_right); - self.dirty = true - } - if !util::rect_is_empty(&new_free_rect_to_bottom) { - self.free_list.push(&new_free_rect_to_bottom); - self.dirty = true - } - - // Bump the allocation counter. - self.allocations += 1; - - // Return the result. - Some(chosen_rect.origin) - } - - fn coalesce_impl(rects: &mut [DeviceUintRect], deadline: u64, fun_key: F, fun_union: U) - -> CoalescingStatus where - F: Fn(&DeviceUintRect) -> (u32, u32), - U: Fn(&mut DeviceUintRect, &mut DeviceUintRect) -> usize, - { - let mut num_changed = 0; - rects.sort_by_key(&fun_key); - - for work_index in 0..rects.len() { - if work_index % COALESCING_TIMEOUT_CHECKING_INTERVAL == 0 && - time::precise_time_ns() >= deadline { - return CoalescingStatus::Timeout - } - - let (left, candidates) = rects.split_at_mut(work_index + 1); - let mut item = left.last_mut().unwrap(); - if util::rect_is_empty(item) { - continue - } - - let key = fun_key(item); - for candidate in candidates.iter_mut() - .take_while(|r| key == fun_key(r)) { - num_changed += fun_union(item, candidate); - } - } - - if num_changed > 0 { - CoalescingStatus::Changed - } else { - CoalescingStatus::Unchanged - } - } - - /// Combine rects that have the same width and are adjacent. - fn coalesce_horisontal(rects: &mut [DeviceUintRect], deadline: u64) -> CoalescingStatus { - Self::coalesce_impl(rects, deadline, - |item| (item.size.width, item.origin.x), - |item, candidate| { - if item.origin.y == candidate.max_y() || item.max_y() == candidate.origin.y { - *item = item.union(candidate); - candidate.size.width = 0; - 1 - } else { 0 } - }) - } - - /// Combine rects that have the same height and are adjacent. - fn coalesce_vertical(rects: &mut [DeviceUintRect], deadline: u64) -> CoalescingStatus { - Self::coalesce_impl(rects, deadline, - |item| (item.size.height, item.origin.y), - |item, candidate| { - if item.origin.x == candidate.max_x() || item.max_x() == candidate.origin.x { - *item = item.union(candidate); - candidate.size.height = 0; - 1 - } else { 0 } - }) - } - - pub fn coalesce(&mut self) -> bool { - if !self.dirty { - return false - } - - // Iterate to a fixed point or until a timeout is reached. - let deadline = time::precise_time_ns() + COALESCING_TIMEOUT; - self.free_list.copy_to_vec(&mut self.coalesce_vec); - let mut changed = false; - - //Note: we might want to consider try to use the last sorted order first - // but the elements get shuffled around a bit anyway during the bin placement - - match Self::coalesce_horisontal(&mut self.coalesce_vec, deadline) { - CoalescingStatus::Changed => changed = true, - CoalescingStatus::Unchanged => (), - CoalescingStatus::Timeout => { - self.free_list.init_from_slice(&self.coalesce_vec); - return true - } - } - - match Self::coalesce_vertical(&mut self.coalesce_vec, deadline) { - CoalescingStatus::Changed => changed = true, - CoalescingStatus::Unchanged => (), - CoalescingStatus::Timeout => { - self.free_list.init_from_slice(&self.coalesce_vec); - return true - } - } - - if changed { - self.free_list.init_from_slice(&self.coalesce_vec); - } - self.dirty = changed; - changed - } - - fn clear(&mut self) { - self.free_list = FreeRectList::new(); - self.free_list.push(&DeviceUintRect::new( - DeviceUintPoint::zero(), - self.texture_size)); - self.allocations = 0; - self.dirty = false; - } - - fn free(&mut self, rect: &DeviceUintRect) { - if util::rect_is_empty(rect) { - return - } - debug_assert!(self.allocations > 0); - self.allocations -= 1; - if self.allocations == 0 { - self.clear(); - return - } - - self.free_list.push(rect); - self.dirty = true - } - - fn grow(&mut self, new_texture_size: DeviceUintSize) { - assert!(new_texture_size.width >= self.texture_size.width); - assert!(new_texture_size.height >= self.texture_size.height); - - let new_rects = [ - DeviceUintRect::new(DeviceUintPoint::new(self.texture_size.width, 0), - DeviceUintSize::new(new_texture_size.width - self.texture_size.width, - new_texture_size.height)), - - DeviceUintRect::new(DeviceUintPoint::new(0, self.texture_size.height), - DeviceUintSize::new(self.texture_size.width, - new_texture_size.height - self.texture_size.height)), - ]; - - for rect in &new_rects { - if rect.size.width > 0 && rect.size.height > 0 { - self.free_list.push(rect); - } - } - - self.texture_size = new_texture_size - } - - fn can_grow(&self, max_size: u32) -> bool { - self.texture_size.width < max_size || self.texture_size.height < max_size - } -} - -// testing functionality -impl TexturePage { - #[doc(hidden)] - pub fn new_dummy(size: DeviceUintSize) -> TexturePage { - Self::new(CacheTextureId(0), size) - } - - #[doc(hidden)] - pub fn fill_from(&mut self, other: &TexturePage) { - self.dirty = true; - self.free_list.small.clear(); - self.free_list.small.extend_from_slice(&other.free_list.small); - self.free_list.medium.clear(); - self.free_list.medium.extend_from_slice(&other.free_list.medium); - self.free_list.large.clear(); - self.free_list.large.extend_from_slice(&other.free_list.large); - } -} - -/// A binning free list. Binning is important to avoid sifting through lots of small strips when -/// allocating many texture items. -struct FreeRectList { - small: Vec, - medium: Vec, - large: Vec, -} - -impl FreeRectList { - fn new() -> FreeRectList { - FreeRectList { - small: vec![], - medium: vec![], - large: vec![], - } - } - - fn init_from_slice(&mut self, rects: &[DeviceUintRect]) { - self.small.clear(); - self.medium.clear(); - self.large.clear(); - for rect in rects { - if !util::rect_is_empty(rect) { - self.push(rect) - } - } - } - - fn push(&mut self, rect: &DeviceUintRect) { - match FreeListBin::for_size(&rect.size) { - FreeListBin::Small => self.small.push(*rect), - FreeListBin::Medium => self.medium.push(*rect), - FreeListBin::Large => self.large.push(*rect), - } - } - - fn remove(&mut self, index: FreeListIndex) -> DeviceUintRect { - match index.0 { - FreeListBin::Small => self.small.swap_remove(index.1), - FreeListBin::Medium => self.medium.swap_remove(index.1), - FreeListBin::Large => self.large.swap_remove(index.1), - } - } - - fn iter(&self, bin: FreeListBin) -> Iter { - match bin { - FreeListBin::Small => self.small.iter(), - FreeListBin::Medium => self.medium.iter(), - FreeListBin::Large => self.large.iter(), - } - } - - fn copy_to_vec(&self, rects: &mut Vec) { - rects.clear(); - rects.extend_from_slice(&self.small); - rects.extend_from_slice(&self.medium); - rects.extend_from_slice(&self.large); - } -} - -#[derive(Debug, Clone, Copy)] -struct FreeListIndex(FreeListBin, usize); - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -enum FreeListBin { - Small, - Medium, - Large, -} - -impl FreeListBin { - fn for_size(size: &DeviceUintSize) -> FreeListBin { - if size.width >= MINIMUM_LARGE_RECT_SIZE && size.height >= MINIMUM_LARGE_RECT_SIZE { - FreeListBin::Large - } else if size.width >= MINIMUM_MEDIUM_RECT_SIZE && - size.height >= MINIMUM_MEDIUM_RECT_SIZE { - FreeListBin::Medium - } else { - debug_assert!(size.width > 0 && size.height > 0); - FreeListBin::Small - } - } -} - -#[derive(Debug, Clone)] -pub struct TextureCacheItem { - // Identifies the texture and array slice - pub texture_id: CacheTextureId, - - // The texture coordinates for this item - pub uv_rect: UvRect, - - // The size of the allocated rectangle. - pub allocated_rect: DeviceUintRect, - - // Handle to the location of the UV rect for this item in GPU cache. - pub uv_rect_handle: GpuCacheHandle, - - pub format: ImageFormat, - - // Some arbitrary data associated with this item. - // In the case of glyphs, it is the top / left offset - // from the rasterized glyph. - pub user_data: [f32; 2], -} - -impl TextureCacheItem { - fn new(texture_id: CacheTextureId, - rect: DeviceUintRect, - format: ImageFormat, - user_data: [f32; 2]) - -> TextureCacheItem { - TextureCacheItem { - texture_id, - uv_rect: UvRect { - uv0: DevicePoint::new(rect.origin.x as f32, - rect.origin.y as f32), - uv1: DevicePoint::new((rect.origin.x + rect.size.width) as f32, - (rect.origin.y + rect.size.height) as f32), - }, - allocated_rect: rect, - uv_rect_handle: GpuCacheHandle::new(), - format, - user_data, - } - } -} - -struct TextureCacheArena { - pages_a8: Vec, - pages_rgb8: Vec, - pages_rgba8: Vec, - pages_rg8: Vec, -} - -impl TextureCacheArena { - fn new() -> TextureCacheArena { - TextureCacheArena { - pages_a8: Vec::new(), - pages_rgb8: Vec::new(), - pages_rgba8: Vec::new(), - pages_rg8: Vec::new(), - } - } - - fn texture_page_for_id(&mut self, id: CacheTextureId) -> Option<&mut TexturePage> { - for page in self.pages_a8.iter_mut().chain(self.pages_rgb8.iter_mut()) - .chain(self.pages_rgba8.iter_mut()) - .chain(self.pages_rg8.iter_mut()) { - if page.texture_id == id { - return Some(page) - } - } - None - } -} - -pub struct CacheTextureIdList { +// Maintains a simple freelist of texture IDs that are mapped +// to real API-specific texture IDs in the renderer. +struct CacheTextureIdList { + free_list: Vec, next_id: usize, - free_list: Vec, } impl CacheTextureIdList { @@ -524,55 +43,219 @@ impl CacheTextureIdList { fn allocate(&mut self) -> CacheTextureId { // If nothing on the free list of texture IDs, // allocate a new one. - if self.free_list.is_empty() { - self.free_list.push(self.next_id); - self.next_id += 1; + match self.free_list.pop() { + Some(id) => { + id + } + None => { + let id = CacheTextureId(self.next_id); + self.next_id += 1; + id + } } - - let id = self.free_list.pop().unwrap(); - CacheTextureId(id) } fn free(&mut self, id: CacheTextureId) { - self.free_list.push(id.0); + self.free_list.push(id); + } +} + +// Items in the texture cache can either be standalone textures, +// or a sub-rect inside the shared cache. +#[derive(Debug)] +enum EntryKind { + Standalone, + Cache { + // Origin within the texture layer where this item exists. + origin: DeviceUintPoint, + // The layer index of the texture array. + layer_index: u16, + // The region that this entry belongs to in the layer. + region_index: u16, + } +} + +// Stores information related to a single entry in the texture +// cache. This is stored for each item whether it's in the shared +// cache or a standalone texture. +#[derive(Debug)] +struct CacheEntry { + // Size the requested item, in device pixels. + size: DeviceUintSize, + // Details specific to standalone or shared items. + kind: EntryKind, + // Arbitrary user data associated with this item. + user_data: [f32; 2], + // The last frame this item was requested for rendering. + last_access: FrameId, + // Handle to the resource rect in the GPU cache. + uv_rect_handle: GpuCacheHandle, + // Image format of the item. + format: ImageFormat, + // The actual device texture ID this is part of. + texture_id: CacheTextureId, +} + +impl CacheEntry { + // Create a new entry for a standalone texture. + fn new_standalone(texture_id: CacheTextureId, + size: DeviceUintSize, + format: ImageFormat, + user_data: [f32; 2], + last_access: FrameId) -> CacheEntry { + CacheEntry { + size, + user_data, + last_access, + kind: EntryKind::Standalone, + texture_id, + format, + uv_rect_handle: GpuCacheHandle::new(), + } + } + + // Update the GPU cache for this texture cache entry. + // This ensures that the UV rect, and texture layer index + // are up to date in the GPU cache for vertex shaders + // to fetch from. + fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) { + if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) { + let (origin, layer_index) = match self.kind { + EntryKind::Standalone { .. } => { + (DeviceUintPoint::zero(), 0.0) + } + EntryKind::Cache { origin, layer_index, .. } => { + (origin, layer_index as f32) + } + }; + request.push([origin.x as f32, + origin.y as f32, + (origin.x + self.size.width) as f32, + (origin.y + self.size.height) as f32]); + request.push([layer_index, + self.user_data[0], + self.user_data[1], + 0.0]); + } + } +} + +type WeakCacheEntryHandle = WeakFreeListHandle; + +// A texture cache handle is a weak reference to a cache entry. +// If the handle has not been inserted into the cache yet, the +// value will be None. Even when the value is Some(), the location +// may not actually be valid if it has been evicted by the cache. +// In this case, the cache handle needs to re-upload this item +// to the texture cache (see request() below). +#[derive(Debug)] +pub struct TextureCacheHandle { + entry: Option, +} + +impl TextureCacheHandle { + pub fn new() -> TextureCacheHandle { + TextureCacheHandle { + entry: None, + } } } pub struct TextureCache { - cache_id_list: CacheTextureIdList, - free_texture_levels: FastHashMap>, - items: FreeList, - arena: TextureCacheArena, - pending_updates: TextureUpdateList, + // A lazily allocated, fixed size, texture array for + // each format the texture cache supports. + // TODO(gw): Do we actually need RG8 and RGB8 or + // are they only used by external textures? + array_a8: TextureArray, + array_rgba8: TextureArray, + array_rg8: TextureArray, + array_rgb8: TextureArray, + + // Maximum texture size supported by hardware. max_texture_size: u32, -} -#[derive(PartialEq, Eq, Debug)] -pub enum AllocationKind { - TexturePage, - Standalone, -} + // A list of texture IDs that represent native + // texture handles. This indirection allows the texture + // cache to create / destroy / reuse texture handles + // without knowing anything about the device code. + cache_textures: CacheTextureIdList, -#[derive(Debug)] -pub struct AllocationResult { - new_image_id: Option, - kind: AllocationKind, - item: TextureCacheItem, + // A list of updates that need to be applied to the + // texture cache in the rendering thread this frame. + pending_updates: TextureUpdateList, + + // The current frame ID. Used for cache eviction policies. + frame_id: FrameId, + + // Maintains the list of all current items in + // the texture cache. + entries: FreeList, + + // A list of the strong handles of items that were + // allocated in the standalone texture pool. Used + // for evicting old standalone textures. + standalone_entry_handles: Vec>, + + // A list of the strong handles of items that were + // allocated in the shared texture cache. Used + // for evicting old cache items. + shared_entry_handles: Vec>, } impl TextureCache { - pub fn new(mut max_texture_size: u32) -> TextureCache { - if max_texture_size * max_texture_size > MAX_RGBA_PIXELS_PER_TEXTURE { - max_texture_size = SQRT_MAX_RGBA_PIXELS_PER_TEXTURE; - } - + pub fn new(max_texture_size: u32) -> TextureCache { TextureCache { - cache_id_list: CacheTextureIdList::new(), - free_texture_levels: FastHashMap::default(), - items: FreeList::new(), - pending_updates: TextureUpdateList::new(), - arena: TextureCacheArena::new(), max_texture_size, + array_a8: TextureArray::new(ImageFormat::A8), + array_rgba8: TextureArray::new(ImageFormat::BGRA8), + array_rg8: TextureArray::new(ImageFormat::RG8), + array_rgb8: TextureArray::new(ImageFormat::RGB8), + cache_textures: CacheTextureIdList::new(), + pending_updates: TextureUpdateList::new(), + frame_id: FrameId(0), + entries: FreeList::new(), + standalone_entry_handles: Vec::new(), + shared_entry_handles: Vec::new(), + } + } + + pub fn begin_frame(&mut self, frame_id: FrameId) { + self.frame_id = frame_id; + } + + pub fn end_frame(&mut self) { + self.expire_old_standalone_entries(); + } + + // Request an item in the texture cache. All images that will + // be used on a frame *must* have request() called on their + // handle, to update the last used timestamp and ensure + // that resources are not flushed from the cache too early. + // + // Returns true if the image needs to be uploaded to the + // texture cache (either never uploaded, or has been + // evicted on a previous frame). + pub fn request(&mut self, + handle: &mut TextureCacheHandle, + gpu_cache: &mut GpuCache) -> bool { + match handle.entry { + Some(ref handle) => { + match self.entries.get_opt_mut(handle) { + // If an image is requested that is already in the cache, + // refresh the GPU cache data associated with this item. + Some(entry) => { + entry.last_access = self.frame_id; + entry.update_gpu_cache(gpu_cache); + false + } + None => { + true + } + } + } + None => { + true + } } } @@ -584,38 +267,303 @@ impl TextureCache { mem::replace(&mut self.pending_updates, TextureUpdateList::new()) } - pub fn allocate( + // Update the data stored by a given texture cache handle. + pub fn update( &mut self, - requested_width: u32, - requested_height: u32, - format: ImageFormat, + handle: &mut TextureCacheHandle, + descriptor: ImageDescriptor, filter: TextureFilter, + data: ImageData, user_data: [f32; 2], - profile: &mut TextureCacheProfileCounters - ) -> AllocationResult { - self.allocate_impl( - requested_width, - requested_height, - format, - filter, - user_data, - profile, - None, - ) + mut dirty_rect: Option, + gpu_cache: &mut GpuCache) { + + // Determine if we need to allocate texture cache memory + // for this item. We need to reallocate if any of the following + // is true: + // - Never been in the cache + // - Has been in the cache but was evicted. + // - Exists in the cache but dimensions / format have changed. + let realloc = match handle.entry { + Some(ref handle) => { + match self.entries.get_opt(handle) { + Some(entry) => { + entry.size.width != descriptor.width || + entry.size.height != descriptor.height || + entry.format != descriptor.format + } + None => { + // Was previously allocated but has been evicted. + true + } + } + } + None => { + // This handle has not been allocated yet. + true + } + }; + + if realloc { + self.allocate(handle, + descriptor, + filter, + user_data); + + // If we reallocated, we need to upload the whole item again. + dirty_rect = None; + } + + let entry = self.entries + .get_opt_mut(handle.entry.as_ref().unwrap()) + .expect("BUG: handle must be valid now"); + + // Invalidate the contents of the resource rect in the GPU cache. + // This ensures that the update_gpu_cache below will add + // the new information to the GPU cache. + gpu_cache.invalidate(&entry.uv_rect_handle); + + // Upload the resource rect and texture array layer. + entry.update_gpu_cache(gpu_cache); + + // Create an update command, which the render thread processes + // to upload the new image data into the correct location + // in GPU memory. + let (layer_index, origin) = match entry.kind { + EntryKind::Standalone { .. } => { + (0, DeviceUintPoint::zero()) + } + EntryKind::Cache { layer_index, origin, .. } => { + (layer_index, origin) + } + }; + + let op = TextureUpdate::new_update(data, + &descriptor, + origin, + entry.size, + entry.texture_id, + layer_index as i32, + dirty_rect); + self.pending_updates.push(op); } - // If existing_item_id is None, create a new id, otherwise reuse it. - fn allocate_impl( - &mut self, - requested_width: u32, - requested_height: u32, - format: ImageFormat, - filter: TextureFilter, - user_data: [f32; 2], - profile: &mut TextureCacheProfileCounters, - existing_item_id: Option<&TextureCacheItemId> - ) -> AllocationResult { - let requested_size = DeviceUintSize::new(requested_width, requested_height); + // Get a specific region by index from a shared texture array. + fn get_region_mut(&mut self, format: ImageFormat, region_index: u16) -> &mut TextureRegion { + let texture_array = match format { + ImageFormat::A8 => &mut self.array_a8, + ImageFormat::BGRA8 => &mut self.array_rgba8, + ImageFormat::RGB8 => &mut self.array_rgb8, + ImageFormat::RG8 => &mut self.array_rg8, + ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(), + }; + + &mut texture_array.regions[region_index as usize] + } + + // Retrieve the details of an item in the cache. This is used + // during batch creation to provide the resource rect address + // to the shaders and texture ID to the batching logic. + // This function will asssert in debug modes if the caller + // tries to get a handle that was not requested this frame. + pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem { + match handle.entry { + Some(ref handle) => { + let entry = self.entries + .get_opt(handle) + .expect("BUG: was dropped from cache or not updated!"); + debug_assert_eq!(entry.last_access, self.frame_id); + CacheItem { + uv_rect_handle: entry.uv_rect_handle, + texture_id: SourceTexture::TextureCache(entry.texture_id), + } + } + None => { + panic!("BUG: handle not requested earlier in frame") + } + } + } + + // Expire old standalone textures. + fn expire_old_standalone_entries(&mut self) { + let mut eviction_candidates = Vec::new(); + let mut retained_entries = Vec::new(); + + // Build a list of eviction candidates (which are + // anything not used this frame). + for handle in self.standalone_entry_handles.drain(..) { + let entry = self.entries.get(&handle); + if entry.last_access == self.frame_id { + retained_entries.push(handle); + } else { + eviction_candidates.push(handle); + } + } + + // Sort by access time so we remove the oldest ones first. + eviction_candidates.sort_by_key(|handle| { + let entry = self.entries.get(handle); + entry.last_access + }); + + // We only allow an arbitrary number of unused + // standalone textures to remain in GPU memory. + // TODO(gw): We should make this a better heuristic, + // for example based on total memory size. + if eviction_candidates.len() > 32 { + let entries_to_keep = eviction_candidates.split_off(32); + retained_entries.extend(entries_to_keep); + } + + // Free the selected items + for handle in eviction_candidates { + let entry = self.entries.free(handle); + self.free(entry); + } + + // Keep a record of the remaining handles for next frame. + self.standalone_entry_handles = retained_entries; + } + + // Expire old shared items. Pass in the allocation size + // that is being requested, so we know when we've evicted + // enough items to guarantee we can fit this allocation in + // the cache. + fn expire_old_shared_entries(&mut self, required_alloc: &ImageDescriptor) { + let mut eviction_candidates = Vec::new(); + let mut retained_entries = Vec::new(); + + // Build a list of eviction candidates (which are + // anything not used this frame). + for handle in self.shared_entry_handles.drain(..) { + let entry = self.entries.get(&handle); + if entry.last_access == self.frame_id { + retained_entries.push(handle); + } else { + eviction_candidates.push(handle); + } + } + + // Sort by access time so we remove the oldest ones first. + eviction_candidates.sort_by_key(|handle| { + let entry = self.entries.get(handle); + entry.last_access + }); + + // Doing an eviction is quite expensive, so we don't want to + // do it all the time. To avoid this, try and evict a + // significant number of items each cycle. However, we don't + // want to evict everything we can, since that will result in + // more items being uploaded than necessary. + // Instead, we say we will keep evicting until both of these + // consitions are met: + // - We have evicted some arbitrary number of items (512 currently). + // AND + // - We have freed an item that will definitely allow us to + // fit the currently requested allocation. + let needed_slab_size = SlabSize::new(required_alloc.width, + required_alloc.height).get_size(); + let mut found_matching_slab = false; + let mut freed_complete_page = false; + let mut evicted_items = 0; + + for handle in eviction_candidates { + if evicted_items > 512 && + (found_matching_slab || freed_complete_page) { + retained_entries.push(handle); + } else { + let entry = self.entries.free(handle); + if let Some(region) = self.free(entry) { + found_matching_slab |= region.slab_size == needed_slab_size; + freed_complete_page |= region.is_empty(); + } + evicted_items += 1; + } + } + + // Keep a record of the remaining handles for next eviction cycle. + self.shared_entry_handles = retained_entries; + } + + // Free a cache entry from the standalone list or shared cache. + fn free(&mut self, entry: CacheEntry) -> Option<&TextureRegion> { + match entry.kind { + EntryKind::Standalone { .. } => { + // This is a standalone texture allocation. Just push it back onto the free + // list. + self.pending_updates.push(TextureUpdate { + id: entry.texture_id, + op: TextureUpdateOp::Free, + }); + self.cache_textures.free(entry.texture_id); + None + } + EntryKind::Cache { origin, region_index, .. } => { + // Free the block in the given region. + let region = self.get_region_mut(entry.format, region_index); + region.free(origin); + Some(region) + } + } + } + + // Attempt to allocate a block from the shared cache. + fn allocate_from_shared_cache(&mut self, + descriptor: &ImageDescriptor, + user_data: [f32; 2]) -> Option { + // Work out which cache it goes in, based on format. + let texture_array = match descriptor.format { + ImageFormat::A8 => &mut self.array_a8, + ImageFormat::BGRA8 => &mut self.array_rgba8, + ImageFormat::RGB8 => &mut self.array_rgb8, + ImageFormat::RG8 => &mut self.array_rg8, + ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(), + }; + + // Lazy initialize this texture array if required. + if texture_array.texture_id.is_none() { + let texture_id = self.cache_textures.allocate(); + + let update_op = TextureUpdate { + id: texture_id, + op: TextureUpdateOp::Create { + width: TEXTURE_LAYER_DIMENSIONS, + height: TEXTURE_LAYER_DIMENSIONS, + format: descriptor.format, + filter: TextureFilter::Linear, + layer_count: TEXTURE_ARRAY_LAYERS, + mode: RenderTargetMode::RenderTarget, // todo: !!!! remove me!? + } + }; + self.pending_updates.push(update_op); + + texture_array.texture_id = Some(texture_id); + } + + // Do the allocation. This can fail and return None + // if there are no free slots or regions available. + texture_array.alloc(descriptor.width, + descriptor.height, + user_data, + self.frame_id) + } + + // Allocate storage for a given image. This attempts to allocate + // from the shared cache, but falls back to standalone texture + // if the image is too large, or the cache is full. + fn allocate(&mut self, + handle: &mut TextureCacheHandle, + descriptor: ImageDescriptor, + filter: TextureFilter, + user_data: [f32; 2]) { + assert!(descriptor.width > 0 && descriptor.height > 0); + + // Work out if this image qualifies to go in the shared (batching) cache. + let mut allowed_in_shared_cache = true; + let mut allocated_in_shared_cache = true; + let mut new_cache_entry = None; + let size = DeviceUintSize::new(descriptor.width, descriptor.height); + let frame_id = self.frame_id; // TODO(gw): For now, anything that requests nearest filtering // just fails to allocate in a texture page, and gets a standalone @@ -623,457 +571,430 @@ impl TextureCache { // but is probably rare enough that it can be fixed up later (it's also // fairly trivial to implement, just tedious). if filter == TextureFilter::Nearest { - // Fall back to standalone texture allocation. - let texture_id = self.cache_id_list.allocate(); + allowed_in_shared_cache = false; + } - let update_op = TextureUpdate { - id: texture_id, - op: texture_create_op(DeviceUintSize::new(requested_width, - requested_height), - format, - RenderTargetMode::None, - filter), - }; - self.pending_updates.push(update_op); + // Anything larger than 512 goes in a standalone texture. + // TODO(gw): If we find pages that suffer from batch breaks in this + // case, add support for storing these in a standalone + // texture array. + if descriptor.width > 512 || descriptor.height > 512 { + allowed_in_shared_cache = false; + } - let cache_item = TextureCacheItem::new( - texture_id, - DeviceUintRect::new(DeviceUintPoint::zero(), requested_size), - format, - user_data - ); + // If it's allowed in the cache, see if there is a spot for it. + if allowed_in_shared_cache { + new_cache_entry = self.allocate_from_shared_cache(&descriptor, user_data); - let new_image_id = match existing_item_id { - Some(id) => { - *self.items.get_mut(id) = cache_item.clone(); - None - } - None => { - let id = self.items.insert(cache_item.clone()); - Some(id) - } - }; + // If we failed to allocate in the shared cache, run an + // eviction cycle, and then try to allocate again. + if new_cache_entry.is_none() { + self.expire_old_shared_entries(&descriptor); - return AllocationResult { - item: cache_item, - kind: AllocationKind::Standalone, - new_image_id, + new_cache_entry = self.allocate_from_shared_cache(&descriptor, user_data); } } - let mode = RenderTargetMode::SimpleRenderTarget; - let (page_list, page_profile) = match format { - ImageFormat::A8 => (&mut self.arena.pages_a8, &mut profile.pages_a8), - ImageFormat::BGRA8 => (&mut self.arena.pages_rgba8, &mut profile.pages_rgba8), - ImageFormat::RGB8 => (&mut self.arena.pages_rgb8, &mut profile.pages_rgb8), - ImageFormat::RG8 => (&mut self.arena.pages_rg8, &mut profile.pages_rg8), - ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(), + // If not allowed in the cache, or if the shared cache is full, then it + // will just have to be in a unique texture. This hurts batching but should + // only occur on a small number of images (or pathological test cases!). + if new_cache_entry.is_none() { + let texture_id = self.cache_textures.allocate(); + + // Create an update operation to allocate device storage + // of the right size / format. + let update_op = TextureUpdate { + id: texture_id, + op: TextureUpdateOp::Create { + width: descriptor.width, + height: descriptor.height, + format: descriptor.format, + filter, + mode: RenderTargetMode::RenderTarget, + layer_count: 1, + } + }; + self.pending_updates.push(update_op); + + new_cache_entry = Some(CacheEntry::new_standalone(texture_id, + size, + descriptor.format, + user_data, + frame_id)); + + allocated_in_shared_cache = false; + } + + let new_cache_entry = new_cache_entry.expect("BUG: must have allocated by now"); + + // We need to update the texture cache handle now, so that it + // points to the correct location. + let new_entry_handle = match handle.entry { + Some(ref existing_entry) => { + // If the handle already exists, there's two possibilities: + // 1) It points to a valid entry in the freelist. + // 2) It points to a stale entry in the freelist (i.e. has been evicted). + // + // For (1) we want to replace the cache entry with our + // newly updated location. We also need to ensure that + // the storage (region or standalone) associated with the + // previous entry here gets freed. + // + // For (2) we need to add the data to a new location + // in the freelist. + // + // This is managed with a database style upsert operation. + match self.entries.upsert(existing_entry, new_cache_entry) { + UpsertResult::Updated(old_entry) => { + self.free(old_entry); + None + } + UpsertResult::Inserted(new_handle) => { + Some(new_handle) + } + } + } + None => { + // This handle has never been allocated, so just + // insert a new cache entry. + Some(self.entries.insert(new_cache_entry)) + } }; - - // TODO(gw): Handle this sensibly (support failing to render items that can't fit?) - assert!( - requested_size.width <= self.max_texture_size, - "Width {:?} > max texture size (format: {:?}).", - requested_size.width, format - ); - assert!( - requested_size.height <= self.max_texture_size, - "Height {:?} > max texture size (format: {:?}).", - requested_size.height, format - ); - - let mut page_id = None; //using ID here to please the borrow checker - for (i, page) in page_list.iter_mut().enumerate() { - if page.can_allocate(&requested_size) { - page_id = Some(i); - break; + // If the cache entry is new, update it in the cache handle. + if let Some(new_entry_handle) = new_entry_handle { + handle.entry = Some(self.entries.create_weak_handle(&new_entry_handle)); + // Store the strong handle in the list that we scan for + // cache evictions. + if allocated_in_shared_cache { + self.shared_entry_handles.push(new_entry_handle); + } else { + self.standalone_entry_handles.push(new_entry_handle); } - // try to coalesce it - if page.coalesce() && page.can_allocate(&requested_size) { - page_id = Some(i); - break; + } + } +} + +// A list of the block sizes that a region can be initialized with. +#[derive(Copy, Clone, PartialEq)] +enum SlabSize { + Size16x16, + Size32x32, + Size64x64, + Size128x128, + Size256x256, + Size512x512, +} + +impl SlabSize { + fn new(width: u32, height: u32) -> SlabSize { + // TODO(gw): Consider supporting non-square + // allocator sizes here. + let max_dim = cmp::max(width, height); + + match max_dim { + 0 => unreachable!(), + 1 ... 16 => SlabSize::Size16x16, + 17 ... 32 => SlabSize::Size32x32, + 33 ... 64 => SlabSize::Size64x64, + 65 ... 128 => SlabSize::Size128x128, + 129 ... 256 => SlabSize::Size256x256, + 257 ... 512 => SlabSize::Size512x512, + _ => panic!("Invalid dimensions for cache!"), + } + } + + fn get_size(&self) -> u32 { + match *self { + SlabSize::Size16x16 => 16, + SlabSize::Size32x32 => 32, + SlabSize::Size64x64 => 64, + SlabSize::Size128x128 => 128, + SlabSize::Size256x256 => 256, + SlabSize::Size512x512 => 512, + } + } +} + +// The x/y location within a texture region of an allocation. +struct TextureLocation { + x: u8, + y: u8, +} + +impl TextureLocation { + fn new(x: u32, y: u32) -> TextureLocation { + debug_assert!(x < 256 && y < 256); + TextureLocation { + x: x as u8, + y: y as u8, + } + } +} + +// A region is a sub-rect of a texture array layer. +// All allocations within a region are of the same size. +struct TextureRegion { + layer_index: i32, + region_size: u32, + slab_size: u32, + free_slots: Vec, + slots_per_axis: u32, + total_slot_count: usize, + origin: DeviceUintPoint, +} + +impl TextureRegion { + fn new(region_size: u32, + layer_index: i32, + origin: DeviceUintPoint) -> TextureRegion { + TextureRegion { + layer_index, + region_size, + slab_size: 0, + free_slots: Vec::new(), + slots_per_axis: 0, + total_slot_count: 0, + origin, + } + } + + // Initialize a region to be an allocator for a specific slab size. + fn init(&mut self, slab_size: SlabSize) { + debug_assert!(self.slab_size == 0); + debug_assert!(self.free_slots.is_empty()); + + self.slab_size = slab_size.get_size(); + self.slots_per_axis = self.region_size / self.slab_size; + + // Add each block to a freelist. + for y in 0..self.slots_per_axis { + for x in 0..self.slots_per_axis { + self.free_slots.push(TextureLocation::new(x, y)); } - if page.can_grow(self.max_texture_size) { - // try to grow it - let new_width = cmp::min(page.texture_size.width * 2, self.max_texture_size); - let new_height = cmp::min(page.texture_size.height * 2, self.max_texture_size); - let texture_size = DeviceUintSize::new(new_width, new_height); - self.pending_updates.push(TextureUpdate { - id: page.texture_id, - op: texture_grow_op(texture_size, format, mode), - }); + } - let extra_texels = new_width * new_height - page.texture_size.width * page.texture_size.height; - let extra_bytes = extra_texels * format.bytes_per_pixel().unwrap_or(0); - page_profile.inc(extra_bytes as usize); + self.total_slot_count = self.free_slots.len(); + } - page.grow(texture_size); + // Deinit a region, allowing it to become a region with + // a different allocator size. + fn deinit(&mut self) { + self.slab_size = 0; + self.free_slots.clear(); + self.slots_per_axis = 0; + self.total_slot_count = 0; + } - if page.can_allocate(&requested_size) { - page_id = Some(i); + fn is_empty(&self) -> bool { + self.slab_size == 0 + } + + // Attempt to allocate a fixed size block from this region. + fn alloc(&mut self) -> Option { + self.free_slots + .pop() + .map(|location| { + DeviceUintPoint::new(self.origin.x + self.slab_size * location.x as u32, + self.origin.y + self.slab_size * location.y as u32) + }) + } + + // Free a block in this region. + fn free(&mut self, point: DeviceUintPoint) { + let x = (point.x - self.origin.x) / self.slab_size; + let y = (point.y - self.origin.y) / self.slab_size; + self.free_slots.push(TextureLocation::new(x, y)); + + // If this region is completely unused, deinit it + // so that it can become a different slab size + // as required. + if self.free_slots.len() == self.total_slot_count { + self.deinit(); + } + } +} + +// A texture array contains a number of texture layers, where +// each layer contains one or more regions that can act +// as slab allocators. +struct TextureArray { + format: ImageFormat, + is_allocated: bool, + regions: Vec, + texture_id: Option, +} + +impl TextureArray { + fn new(format: ImageFormat) -> TextureArray { + TextureArray { + format, + is_allocated: false, + regions: Vec::new(), + texture_id: None, + } + } + + // Allocate space in this texture array. + fn alloc(&mut self, + width: u32, + height: u32, + user_data: [f32; 2], + frame_id: FrameId) -> Option { + // Lazily allocate the regions if not already created. + // This means that very rarely used image formats can be + // added but won't allocate a cache if never used. + if !self.is_allocated { + debug_assert!(TEXTURE_LAYER_DIMENSIONS % TEXTURE_REGION_DIMENSIONS == 0); + let regions_per_axis = TEXTURE_LAYER_DIMENSIONS / TEXTURE_REGION_DIMENSIONS; + for layer_index in 0..TEXTURE_ARRAY_LAYERS { + for y in 0..regions_per_axis { + for x in 0..regions_per_axis { + let origin = DeviceUintPoint::new(x * TEXTURE_REGION_DIMENSIONS, + y * TEXTURE_REGION_DIMENSIONS); + let region = TextureRegion::new(TEXTURE_REGION_DIMENSIONS, + layer_index, + origin); + self.regions.push(region); + } + } + } + self.is_allocated = true; + } + + // Quantize the size of the allocation to select a region to + // allocate from. + let slab_size = SlabSize::new(width, height); + let slab_size_dim = slab_size.get_size(); + + // TODO(gw): For simplicity, the initial implementation just + // has a single vec<> of regions. We could easily + // make this more efficient by storing a list of + // regions for each slab size specifically... + + // Keep track of the location of an empty region, + // in case we need to select a new empty region + // after the loop. + let mut empty_region_index = None; + let mut entry_kind = None; + + // Run through the existing regions of this size, and see if + // we can find a free block in any of them. + for (i, region) in self.regions.iter_mut().enumerate() { + if region.slab_size == 0 { + empty_region_index = Some(i); + } else if region.slab_size == slab_size_dim { + if let Some(location) = region.alloc() { + entry_kind = Some(EntryKind::Cache { + layer_index: region.layer_index as u16, + region_index: i as u16, + origin: location, + }); break; } } } - let mut page = match page_id { - Some(index) => &mut page_list[index], - None => { - let init_texture_size = initial_texture_size(self.max_texture_size); - let texture_size = DeviceUintSize::new(cmp::max(requested_width, init_texture_size.width), - cmp::max(requested_height, init_texture_size.height)); - let extra_bytes = texture_size.width * texture_size.height * format.bytes_per_pixel().unwrap_or(0); - page_profile.inc(extra_bytes as usize); - - let free_texture_levels_entry = self.free_texture_levels.entry(format); - let mut free_texture_levels = match free_texture_levels_entry { - Entry::Vacant(entry) => entry.insert(Vec::new()), - Entry::Occupied(entry) => entry.into_mut(), - }; - if free_texture_levels.is_empty() { - let texture_id = self.cache_id_list.allocate(); - - let update_op = TextureUpdate { - id: texture_id, - op: texture_create_op(texture_size, - format, - mode, - filter), - }; - self.pending_updates.push(update_op); - - free_texture_levels.push(FreeTextureLevel { - texture_id, - }); - } - let free_texture_level = free_texture_levels.pop().unwrap(); - let texture_id = free_texture_level.texture_id; - - let page = TexturePage::new(texture_id, texture_size); - page_list.push(page); - page_list.last_mut().unwrap() - }, - }; - - let location = page.allocate(&requested_size) - .expect("All the checks have passed till now, there is no way back."); - let cache_item = TextureCacheItem::new( - page.texture_id, - DeviceUintRect::new(location, requested_size), - format, - user_data - ); - - let new_image_id = match existing_item_id { - Some(id) => { - *self.items.get_mut(id) = cache_item.clone(); - None + // Find a region of the right size and try to allocate from it. + if entry_kind.is_none() { + if let Some(empty_region_index) = empty_region_index { + let region = &mut self.regions[empty_region_index]; + region.init(slab_size); + entry_kind = region.alloc().map(|location| { + EntryKind::Cache { + layer_index: region.layer_index as u16, + region_index: empty_region_index as u16, + origin: location, + } + }); } - None => { - let id = self.items.insert(cache_item.clone()); - Some(id) - } - }; - - AllocationResult { - item: cache_item, - kind: AllocationKind::TexturePage, - new_image_id, } + + entry_kind.map(|kind| { + CacheEntry { + size: DeviceUintSize::new(width, height), + user_data, + last_access: frame_id, + kind, + uv_rect_handle: GpuCacheHandle::new(), + format: self.format, + texture_id: self.texture_id.unwrap(), + } + }) } +} - pub fn update( - &mut self, - image_id: &TextureCacheItemId, - descriptor: ImageDescriptor, - filter: TextureFilter, - data: ImageData, - mut dirty_rect: Option, - ) { - let mut existing_item = self.items.get(image_id).clone(); - - if existing_item.allocated_rect.size.width != descriptor.width || - existing_item.allocated_rect.size.height != descriptor.height || - existing_item.format != descriptor.format { - - self.free_item_rect(existing_item.clone()); - - self.allocate_impl( - descriptor.width, - descriptor.height, - descriptor.format, - filter, - existing_item.user_data, - &mut TextureCacheProfileCounters::new(), - Some(image_id), - ); - - // Fetch the item again because the rect most likely changed during reallocation. - existing_item = self.items.get(image_id).clone(); - // If we reallocated, we need to upload the whole item again. - dirty_rect = None; - } - - let op = match data { +impl TextureUpdate { + // Constructs a TextureUpdate operation to be passed to the + // rendering thread in order to do an upload to the right + // location in the texture cache. + fn new_update(data: ImageData, + descriptor: &ImageDescriptor, + origin: DeviceUintPoint, + size: DeviceUintSize, + texture_id: CacheTextureId, + layer_index: i32, + dirty_rect: Option) -> TextureUpdate { + let data_src = match data { + ImageData::Blob(..) => { + panic!("The vector image should have been rasterized."); + } ImageData::External(ext_image) => { match ext_image.image_type { ExternalImageType::Texture2DHandle | + ExternalImageType::Texture2DArrayHandle | ExternalImageType::TextureRectHandle | ExternalImageType::TextureExternalHandle => { panic!("External texture handle should not go through texture_cache."); } ExternalImageType::ExternalBuffer => { - TextureUpdateOp::UpdateForExternalBuffer { - rect: existing_item.allocated_rect, + TextureUpdateSource::External { id: ext_image.id, channel_index: ext_image.channel_index, - stride: descriptor.stride, - offset: descriptor.offset, } } } } - ImageData::Blob(..) => { - panic!("The vector image should have been rasterized into a raw image."); - } ImageData::Raw(bytes) => { - match dirty_rect { - Some(dirty) => { - let stride = descriptor.compute_stride(); - let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x; - TextureUpdateOp::Update { - page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x, - page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y, - width: dirty.size.width, - height: dirty.size.height, - data: bytes, - stride: Some(stride), - offset, - } - } - None => { - TextureUpdateOp::Update { - page_pos_x: existing_item.allocated_rect.origin.x, - page_pos_y: existing_item.allocated_rect.origin.y, - width: descriptor.width, - height: descriptor.height, - data: bytes, - stride: descriptor.stride, - offset: descriptor.offset, - } - } + let finish = descriptor.offset + + descriptor.width * descriptor.format.bytes_per_pixel().unwrap_or(0) + + (descriptor.height-1) * descriptor.compute_stride(); + assert!(bytes.len() >= finish as usize); + + TextureUpdateSource::Bytes { + data: bytes } } }; - let update_op = TextureUpdate { - id: existing_item.texture_id, - op, - }; - - self.pending_updates.push(update_op); - } - - pub fn insert(&mut self, - descriptor: ImageDescriptor, - filter: TextureFilter, - data: ImageData, - user_data: [f32; 2], - profile: &mut TextureCacheProfileCounters) -> TextureCacheItemId { - if let ImageData::Blob(..) = data { - panic!("must rasterize the vector image before adding to the cache"); - } - - let width = descriptor.width; - let height = descriptor.height; - let format = descriptor.format; - let stride = descriptor.stride; - - if let ImageData::Raw(ref vec) = data { - let finish = descriptor.offset + - width * format.bytes_per_pixel().unwrap_or(0) + - (height-1) * descriptor.compute_stride(); - assert!(vec.len() >= finish as usize); - } - - let result = self.allocate(width, - height, - format, - filter, - user_data, - profile); - - match result.kind { - AllocationKind::TexturePage => { - match data { - ImageData::External(ext_image) => { - match ext_image.image_type { - ExternalImageType::Texture2DHandle | - ExternalImageType::TextureRectHandle | - ExternalImageType::TextureExternalHandle => { - panic!("External texture handle should not go through texture_cache."); - } - ExternalImageType::ExternalBuffer => { - let update_op = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::UpdateForExternalBuffer { - rect: result.item.allocated_rect, - id: ext_image.id, - channel_index: ext_image.channel_index, - stride, - offset: descriptor.offset, - }, - }; - - self.pending_updates.push(update_op); - } - } - } - ImageData::Blob(..) => { - panic!("The vector image should have been rasterized."); - } - ImageData::Raw(bytes) => { - let update_op = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update { - page_pos_x: result.item.allocated_rect.origin.x, - page_pos_y: result.item.allocated_rect.origin.y, - width: result.item.allocated_rect.size.width, - height: result.item.allocated_rect.size.height, - data: bytes, - stride, - offset: descriptor.offset, - }, - }; - - self.pending_updates.push(update_op); - } + let update_op = match dirty_rect { + Some(dirty) => { + let stride = descriptor.compute_stride(); + let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x; + let origin = DeviceUintPoint::new(origin.x + dirty.origin.x, + origin.y + dirty.origin.y); + TextureUpdateOp::Update { + rect: DeviceUintRect::new(origin, dirty.size), + source: data_src, + stride: Some(stride), + offset, + layer_index, } } - AllocationKind::Standalone => { - match data { - ImageData::External(ext_image) => { - match ext_image.image_type { - ExternalImageType::Texture2DHandle | - ExternalImageType::TextureRectHandle | - ExternalImageType::TextureExternalHandle => { - panic!("External texture handle should not go through texture_cache."); - } - ExternalImageType::ExternalBuffer => { - let update_op = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Create { - width, - height, - format, - filter, - mode: RenderTargetMode::None, - data: Some(data), - }, - }; - - self.pending_updates.push(update_op); - } - } - } - _ => { - let update_op = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Create { - width, - height, - format, - filter, - mode: RenderTargetMode::None, - data: Some(data), - }, - }; - - self.pending_updates.push(update_op); - } - } - } - } - - result.new_image_id.unwrap() - } - - pub fn get(&self, id: &TextureCacheItemId) -> &TextureCacheItem { - self.items.get(id) - } - - pub fn get_mut(&mut self, id: &TextureCacheItemId) -> &mut TextureCacheItem { - self.items.get_mut(id) - } - - pub fn free(&mut self, id: TextureCacheItemId) { - let item = self.items.free(id); - self.free_item_rect(item); - } - - fn free_item_rect(&mut self, item: TextureCacheItem) { - match self.arena.texture_page_for_id(item.texture_id) { - Some(texture_page) => texture_page.free(&item.allocated_rect), None => { - // This is a standalone texture allocation. Just push it back onto the free - // list. - self.pending_updates.push(TextureUpdate { - id: item.texture_id, - op: TextureUpdateOp::Free, - }); - self.cache_id_list.free(item.texture_id); + TextureUpdateOp::Update { + rect: DeviceUintRect::new(origin, size), + source: data_src, + stride: descriptor.stride, + offset: descriptor.offset, + layer_index, + } } + }; + + TextureUpdate { + id: texture_id, + op: update_op, } } } - -fn texture_create_op(texture_size: DeviceUintSize, - format: ImageFormat, - mode: RenderTargetMode, - filter: TextureFilter) - -> TextureUpdateOp { - TextureUpdateOp::Create { - width: texture_size.width, - height: texture_size.height, - format, - filter, - mode, - data: None, - } -} - -fn texture_grow_op(texture_size: DeviceUintSize, - format: ImageFormat, - mode: RenderTargetMode) - -> TextureUpdateOp { - TextureUpdateOp::Grow { - width: texture_size.width, - height: texture_size.height, - format, - filter: TextureFilter::Linear, - mode, - } -} - -trait FitsInside { - fn fits_inside(&self, other: &Self) -> bool; -} - -impl FitsInside for DeviceUintSize { - fn fits_inside(&self, other: &DeviceUintSize) -> bool { - self.width <= other.width && self.height <= other.height - } -} - -/// FIXME(pcwalton): Would probably be more efficient as a bit vector. -#[derive(Clone, Copy)] -pub struct FreeTextureLevel { - texture_id: CacheTextureId, -} - -/// Returns the number of pixels on a side we start out with for our texture atlases. -fn initial_texture_size(max_texture_size: u32) -> DeviceUintSize { - let initial_size = cmp::min(max_texture_size, INITIAL_TEXTURE_SIZE); - DeviceUintSize::new(initial_size, initial_size) -} diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index 2f07e2bc1a44..9b2baf6b4e71 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -4,11 +4,11 @@ use border::{BorderCornerInstance, BorderCornerSide}; use device::TextureId; -use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheUpdateList}; +use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList}; use internal_types::BatchTextures; use internal_types::{FastHashMap, SourceTexture}; use mask_cache::MaskCacheInfo; -use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, ImagePrimitiveKind, PrimitiveCacheKey}; +use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, PrimitiveCacheKey}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; use profiler::FrameProfileCounters; use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData}; @@ -21,7 +21,7 @@ use std::{f32, i32, mem, usize}; use texture_allocator::GuillotineAllocator; use util::{TransformedRect, TransformedRectKind}; use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey}; -use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstanceKey}; +use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstance}; use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect}; use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle}; use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D}; @@ -431,30 +431,22 @@ impl AlphaRenderItem { PrimitiveKind::Image => { let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0]; - let (color_texture_id, uv_address) = match image_cpu.kind { - ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, _) => { - resolve_image(image_key, - image_rendering, - tile_offset, - ctx.resource_cache, - gpu_cache, - deferred_resolves) - } - ImagePrimitiveKind::WebGL(context_id) => { - let webgl_texture = ctx.resource_cache.get_webgl_texture(&context_id); - let uv_rect = [ 0.0, - webgl_texture.size.height as f32, - webgl_texture.size.width as f32, - 0.0]; - let cache_handle = gpu_cache.push_per_frame_blocks(&[uv_rect.into()]); - (webgl_texture.id, cache_handle) - } - }; + let (color_texture_id, uv_address) = resolve_image(image_cpu.image_key, + image_cpu.image_rendering, + image_cpu.tile_offset, + ctx.resource_cache, + gpu_cache, + deferred_resolves); + + if color_texture_id == SourceTexture::Invalid { + return; + } let batch_kind = match color_texture_id { SourceTexture::External(ext_image) => { match ext_image.image_type { ExternalImageType::Texture2DHandle => AlphaBatchKind::Image(ImageBufferKind::Texture2D), + ExternalImageType::Texture2DArrayHandle => AlphaBatchKind::Image(ImageBufferKind::Texture2DArray), ExternalImageType::TextureRectHandle => AlphaBatchKind::Image(ImageBufferKind::TextureRect), ExternalImageType::TextureExternalHandle => AlphaBatchKind::Image(ImageBufferKind::TextureExternal), ExternalImageType::ExternalBuffer => { @@ -465,7 +457,7 @@ impl AlphaRenderItem { } } _ => { - AlphaBatchKind::Image(ImageBufferKind::Texture2D) + AlphaBatchKind::Image(ImageBufferKind::Texture2DArray) } }; @@ -484,12 +476,12 @@ impl AlphaRenderItem { // TODO(gw): avoid / recycle this allocation in the future. let mut instances = Vec::new(); - let font = FontInstanceKey::new(text_cpu.font_key, - font_size_dp, - text_cpu.color, - text_cpu.normal_render_mode, - text_cpu.glyph_options, - text_cpu.subpx_dir); + let font = FontInstance::new(text_cpu.font_key, + font_size_dp, + text_cpu.color, + text_cpu.normal_render_mode, + text_cpu.glyph_options, + text_cpu.subpx_dir); let texture_id = ctx.resource_cache.get_glyphs(font, &text_cpu.glyph_keys, @@ -513,7 +505,8 @@ impl AlphaRenderItem { let cache_task_id = prim_metadata.render_task.as_ref().expect("no render task!").id; let cache_task_index = render_tasks.get_task_index(&cache_task_id, child_pass_index); - let key = AlphaBatchKey::new(AlphaBatchKind::CacheImage, flags, blend_mode, no_textures); + let textures = BatchTextures::render_target_cache(); + let key = AlphaBatchKey::new(AlphaBatchKind::CacheImage, flags, blend_mode, textures); let batch = batch_list.get_suitable_batch(&key, item_bounding_rect); batch.add_instance(base_instance.build(0, cache_task_index.0 as i32, 0)); } @@ -552,6 +545,11 @@ impl AlphaRenderItem { ctx.resource_cache, gpu_cache, deferred_resolves); + + if texture == SourceTexture::Invalid { + return; + } + textures.colors[channel] = texture; uv_rect_addresses[channel] = address.as_int(gpu_cache); } @@ -561,6 +559,7 @@ impl AlphaRenderItem { SourceTexture::External(ext_image) => { match ext_image.image_type { ExternalImageType::Texture2DHandle => ImageBufferKind::Texture2D, + ExternalImageType::Texture2DArrayHandle => ImageBufferKind::Texture2DArray, ExternalImageType::TextureRectHandle => ImageBufferKind::TextureRect, ExternalImageType::TextureExternalHandle => ImageBufferKind::TextureExternal, ExternalImageType::ExternalBuffer => { @@ -571,7 +570,7 @@ impl AlphaRenderItem { } } _ => { - ImageBufferKind::Texture2D + ImageBufferKind::Texture2DArray } } }; @@ -702,82 +701,88 @@ impl ClipBatcher { for &(packed_layer_index, ref info) in clips.iter() { let instance = CacheClipInstance { - task_id: task_index.0 as i32, + render_task_index: task_index.0 as i32, layer_index: packed_layer_index.0 as i32, - address: 0, segment: 0, - resource_address: 0, + clip_data_address: GpuCacheAddress::invalid(), + resource_address: GpuCacheAddress::invalid(), }; - for clip_index in 0 .. info.complex_clip_range.get_count() { - let gpu_address = info.complex_clip_range.location.as_int(gpu_cache) + - (CLIP_DATA_GPU_BLOCKS * clip_index) as i32; - match geometry_kind { - MaskGeometryKind::Default => { - self.rectangles.push(CacheClipInstance { - address: gpu_address, - segment: MaskSegment::All as i32, - ..instance - }); - } - MaskGeometryKind::CornersOnly => { - self.rectangles.extend_from_slice(&[ - CacheClipInstance { - address: gpu_address, - segment: MaskSegment::TopLeftCorner as i32, + if !info.complex_clip_range.is_empty() { + let base_gpu_address = gpu_cache.get_address(&info.complex_clip_range.location); + + for clip_index in 0 .. info.complex_clip_range.get_count() { + let gpu_address = base_gpu_address + CLIP_DATA_GPU_BLOCKS * clip_index; + match geometry_kind { + MaskGeometryKind::Default => { + self.rectangles.push(CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::All as i32, ..instance - }, - CacheClipInstance { - address: gpu_address, - segment: MaskSegment::TopRightCorner as i32, - ..instance - }, - CacheClipInstance { - address: gpu_address, - segment: MaskSegment::BottomLeftCorner as i32, - ..instance - }, - CacheClipInstance { - address: gpu_address, - segment: MaskSegment::BottomRightCorner as i32, - ..instance - }, - ]); + }); + } + MaskGeometryKind::CornersOnly => { + self.rectangles.extend_from_slice(&[ + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::TopLeftCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::TopRightCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::BottomLeftCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::BottomRightCorner as i32, + ..instance + }, + ]); + } } } } - for clip_index in 0 .. info.layer_clip_range.get_count() { - let gpu_address = info.layer_clip_range.location.as_int(gpu_cache) + - (CLIP_DATA_GPU_BLOCKS * clip_index) as i32; - self.rectangles.push(CacheClipInstance { - address: gpu_address, - segment: MaskSegment::All as i32, - ..instance - }); + if !info.layer_clip_range.is_empty() { + let base_gpu_address = gpu_cache.get_address(&info.layer_clip_range.location); + + for clip_index in 0 .. info.layer_clip_range.get_count() { + let gpu_address = base_gpu_address + CLIP_DATA_GPU_BLOCKS * clip_index; + self.rectangles.push(CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::All as i32, + ..instance + }); + } } - if let Some((ref mask, gpu_location)) = info.image { + if let Some((ref mask, ref gpu_location)) = info.image { 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 { - address: gpu_location.as_int(gpu_cache), - resource_address: cache_item.uv_rect_handle.as_int(gpu_cache), + clip_data_address: gpu_cache.get_address(gpu_location), + resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), ..instance }) } - for &(ref source, gpu_location) in &info.border_corners { - let gpu_address = gpu_location.as_int(gpu_cache); + for &(ref source, ref gpu_location) in &info.border_corners { + let gpu_address = gpu_cache.get_address(gpu_location); self.border_clears.push(CacheClipInstance { - address: gpu_address, + clip_data_address: gpu_address, segment: 0, ..instance }); for clip_index in 0..source.actual_clip_count { self.borders.push(CacheClipInstance { - address: gpu_address, + clip_data_address: gpu_address, segment: 1 + clip_index as i32, ..instance }) @@ -1058,12 +1063,12 @@ impl RenderTarget for ColorRenderTarget { let text = &ctx.prim_store.cpu_text_runs[sub_metadata.cpu_prim_index.0]; let font_size_dp = text.logical_font_size.scale_by(ctx.device_pixel_ratio); - let font = FontInstanceKey::new(text.font_key, - font_size_dp, - text.color, - text.shadow_render_mode, - text.glyph_options, - text.subpx_dir); + let font = FontInstance::new(text.font_key, + font_size_dp, + text.color, + text.shadow_render_mode, + text.glyph_options, + text.subpx_dir); let texture_id = ctx.resource_cache.get_glyphs(font, &text.glyph_keys, @@ -1375,12 +1380,13 @@ pub struct BlurCommand { /// Could be an image or a rectangle, which defines the /// way `address` is treated. #[derive(Clone, Copy, Debug)] +#[repr(C)] pub struct CacheClipInstance { - task_id: i32, + render_task_index: i32, layer_index: i32, - address: i32, segment: i32, - resource_address: i32, + clip_data_address: GpuCacheAddress, + resource_address: GpuCacheAddress, } // 32 bytes per instance should be enough for anyone! @@ -1721,30 +1727,33 @@ fn resolve_image(image_key: ImageKey, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec) -> (SourceTexture, GpuCacheHandle) { - let image_properties = resource_cache.get_image_properties(image_key); + match resource_cache.get_image_properties(image_key) { + Some(image_properties) => { + // Check if an external image that needs to be resolved + // by the render thread. + match image_properties.external_image { + Some(external_image) => { + // This is an external texture - we will add it to + // the deferred resolves list to be patched by + // the render thread... + let cache_handle = gpu_cache.push_deferred_per_frame_blocks(1); + deferred_resolves.push(DeferredResolve { + image_properties, + address: gpu_cache.get_address(&cache_handle), + }); - // Check if an external image that needs to be resolved - // by the render thread. - match image_properties.external_image { - Some(external_image) => { - // This is an external texture - we will add it to - // the deferred resolves list to be patched by - // the render thread... - let cache_handle = gpu_cache.push_deferred_per_frame_blocks(1); - deferred_resolves.push(DeferredResolve { - image_properties, - address: gpu_cache.get_address(&cache_handle), - }); + (SourceTexture::External(external_image), cache_handle) + } + None => { + let cache_item = resource_cache.get_cached_image(image_key, + image_rendering, + tile_offset); - (SourceTexture::External(external_image), cache_handle) - } - None => { - let cache_item = resource_cache.get_cached_image(image_key, - image_rendering, - tile_offset); - - (cache_item.texture_id, cache_item.uv_rect_handle) + (cache_item.texture_id, cache_item.uv_rect_handle) + } + } } + None => (SourceTexture::Invalid, GpuCacheHandle::new()), } } diff --git a/gfx/webrender/src/webgl_stubs.rs b/gfx/webrender/src/webgl_stubs.rs deleted file mode 100644 index 467497921ac4..000000000000 --- a/gfx/webrender/src/webgl_stubs.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//! Stubs for the types contained in webgl_types.rs -//! -//! The API surface provided here should be roughly the same to the one provided -//! in webgl_types, modulo completely compiled-out stuff. - -use api::DeviceIntSize; -use api::{GLContextAttributes, GLLimits}; -use api::WebGLCommand; - -pub struct GLContextHandleWrapper; - -impl GLContextHandleWrapper { - pub fn new_context(&self, - _: DeviceIntSize, - _: GLContextAttributes, - _: Option>) -> Result { - unreachable!() - } - - pub fn current_native_handle() -> Option { - None - } - - pub fn current_osmesa_handle() -> Option { - None - } -} - -pub struct GLContextWrapper; - -impl GLContextWrapper { - pub fn make_current(&self) { - unreachable!() - } - - pub fn unbind(&self) { - unreachable!() - } - - pub fn apply_command(&self, _: WebGLCommand) { - unreachable!() - } - - pub fn get_info(&self) -> (DeviceIntSize, u32, GLLimits) { - unreachable!() - } - - pub fn resize(&mut self, _: &DeviceIntSize) -> Result<(), &'static str> { - unreachable!() - } -} - -pub trait GLContextDispatcher { - fn dispatch(&self, Box); -} diff --git a/gfx/webrender/src/webgl_types.rs b/gfx/webrender/src/webgl_types.rs deleted file mode 100644 index 97a0f6a0f54e..000000000000 --- a/gfx/webrender/src/webgl_types.rs +++ /dev/null @@ -1,130 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//! A set of WebGL-related types, in their own module so it's easy to -//! compile it off. - -use gleam::gl; -use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle}; -use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextDispatcher}; -use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle}; -use offscreen_gl_context::{ColorAttachmentType, GLContextAttributes, GLLimits}; -use api::{WebGLCommand, DeviceIntSize}; - -pub enum GLContextHandleWrapper { - Native(NativeGLContextHandle), - OSMesa(OSMesaContextHandle), -} - -impl GLContextHandleWrapper { - pub fn current_native_handle() -> Option { - NativeGLContext::current_handle().map(GLContextHandleWrapper::Native) - } - - pub fn current_osmesa_handle() -> Option { - OSMesaContext::current_handle().map(GLContextHandleWrapper::OSMesa) - } - - pub fn new_context(&self, - size: DeviceIntSize, - attributes: GLContextAttributes, - dispatcher: Option>) -> Result { - match *self { - GLContextHandleWrapper::Native(ref handle) => { - let ctx = GLContext::::new_shared_with_dispatcher(size.to_untyped(), - attributes, - ColorAttachmentType::Texture, - gl::GlType::default(), - Some(handle), - dispatcher); - ctx.map(GLContextWrapper::Native) - } - GLContextHandleWrapper::OSMesa(ref handle) => { - let ctx = GLContext::::new_shared_with_dispatcher(size.to_untyped(), - attributes, - ColorAttachmentType::Texture, - gl::GlType::default(), - Some(handle), - dispatcher); - ctx.map(GLContextWrapper::OSMesa) - } - } - } -} - -pub enum GLContextWrapper { - Native(GLContext), - OSMesa(GLContext), -} - -impl GLContextWrapper { - pub fn make_current(&self) { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.make_current().unwrap(); - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.make_current().unwrap(); - } - } - } - - pub fn unbind(&self) { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.unbind().unwrap(); - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.unbind().unwrap(); - } - } - } - - pub fn apply_command(&self, cmd: WebGLCommand) { - match *self { - GLContextWrapper::Native(ref ctx) => { - cmd.apply(ctx); - } - GLContextWrapper::OSMesa(ref ctx) => { - cmd.apply(ctx); - } - } - } - - pub fn get_info(&self) -> (DeviceIntSize, u32, GLLimits) { - match *self { - GLContextWrapper::Native(ref ctx) => { - let (real_size, texture_id) = { - let draw_buffer = ctx.borrow_draw_buffer().unwrap(); - (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap()) - }; - - let limits = ctx.borrow_limits().clone(); - - (DeviceIntSize::from_untyped(&real_size), texture_id, limits) - } - GLContextWrapper::OSMesa(ref ctx) => { - let (real_size, texture_id) = { - let draw_buffer = ctx.borrow_draw_buffer().unwrap(); - (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap()) - }; - - let limits = ctx.borrow_limits().clone(); - - (DeviceIntSize::from_untyped(&real_size), texture_id, limits) - } - } - } - - pub fn resize(&mut self, size: &DeviceIntSize) -> Result<(), &'static str> { - match *self { - GLContextWrapper::Native(ref mut ctx) => { - ctx.resize(size.to_untyped()) - } - GLContextWrapper::OSMesa(ref mut ctx) => { - ctx.resize(size.to_untyped()) - } - } - } -} diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index d68a81393db5..42b2cd2aa98f 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_api" -version = "0.48.0" +version = "0.49.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" @@ -8,7 +8,6 @@ repository = "https://github.com/servo/webrender" [features] nightly = ["euclid/unstable", "serde/unstable"] ipc = ["ipc-channel"] -webgl = ["offscreen_gl_context"] [dependencies] app_units = "0.5" @@ -16,10 +15,8 @@ bincode = "0.8" byteorder = "1.0" euclid = "0.15" fxhash = "0.2.1" -gleam = "0.4.5" heapsize = ">= 0.3.6, < 0.5" ipc-channel = {version = "0.8", optional = true} -offscreen_gl_context = {version = "0.11", features = ["serde"], optional = true} serde = { version = "1.0", features = ["rc", "derive"] } time = "0.1" diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 747dc9b0f45a..6f1589235a50 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -3,17 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender}; -#[cfg(feature = "webgl")] -use offscreen_gl_context::{GLContextAttributes, GLLimits}; use std::cell::Cell; use std::fmt; use std::marker::PhantomData; -use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceIntSize}; +use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint}; use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey}; use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform}; -use {FontInstanceKey, NativeFontHandle, WorldPoint}; -#[cfg(feature = "webgl")] -use {WebGLCommand, WebGLContextId}; +use {FontInstance, NativeFontHandle, WorldPoint}; pub type TileSize = u16; @@ -148,7 +144,7 @@ pub enum ApiMsg { /// Add/remove/update images and fonts. UpdateResources(ResourceUpdates), /// Gets the glyph dimensions - GetGlyphDimensions(FontInstanceKey, Vec, MsgSender>>), + GetGlyphDimensions(FontInstance, Vec, MsgSender>>), /// Gets the glyph indices from a string GetGlyphIndices(FontKey, String, MsgSender>>), /// Adds a new document namespace. @@ -159,11 +155,6 @@ pub enum ApiMsg { UpdateDocument(DocumentId, DocumentMsg), /// Deletes an existing document. DeleteDocument(DocumentId), - RequestWebGLContext(DeviceIntSize, GLContextAttributes, MsgSender>), - ResizeWebGLContext(WebGLContextId, DeviceIntSize), - WebGLCommand(WebGLContextId, WebGLCommand), - // WebVR commands that must be called in the WebGL render thread. - VRCompositorCommand(WebGLContextId, VRCompositorCommand), /// An opaque handle that must be passed to the render notifier. It is used by Gecko /// to forward gecko-specific messages to the render thread preserving the ordering /// within the other messages. @@ -185,10 +176,6 @@ impl fmt::Debug for ApiMsg { ApiMsg::AddDocument(..) => "ApiMsg::AddDocument", ApiMsg::UpdateDocument(..) => "ApiMsg::UpdateDocument", ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument", - ApiMsg::RequestWebGLContext(..) => "ApiMsg::RequestWebGLContext", - ApiMsg::ResizeWebGLContext(..) => "ApiMsg::ResizeWebGLContext", - ApiMsg::WebGLCommand(..) => "ApiMsg::WebGLCommand", - ApiMsg::VRCompositorCommand(..) => "ApiMsg::VRCompositorCommand", ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent", ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace", ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure", @@ -201,24 +188,6 @@ impl fmt::Debug for ApiMsg { #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Epoch(pub u32); -#[cfg(not(feature = "webgl"))] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct WebGLContextId(pub usize); - -#[cfg(not(feature = "webgl"))] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GLContextAttributes([u8; 0]); - -#[cfg(not(feature = "webgl"))] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GLLimits([u8; 0]); - -#[cfg(not(feature = "webgl"))] -#[derive(Clone, Deserialize, Serialize)] -pub enum WebGLCommand { - Flush, -} - #[repr(C)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] pub struct IdNamespace(pub u32); @@ -343,7 +312,7 @@ impl RenderApi { /// 'empty' textures (height or width = 0) /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None. pub fn get_glyph_dimensions(&self, - font: FontInstanceKey, + font: FontInstance, glyph_keys: Vec) -> Vec> { let (tx, rx) = channel::msg_channel().unwrap(); @@ -371,32 +340,12 @@ impl RenderApi { /// Adds an image identified by the `ImageKey`. pub fn update_resources(&self, resources: ResourceUpdates) { + if resources.updates.is_empty() { + return; + } self.api_sender.send(ApiMsg::UpdateResources(resources)).unwrap(); } - pub fn request_webgl_context(&self, size: &DeviceIntSize, attributes: GLContextAttributes) - -> Result<(WebGLContextId, GLLimits), String> { - let (tx, rx) = channel::msg_channel().unwrap(); - let msg = ApiMsg::RequestWebGLContext(*size, attributes, tx); - self.api_sender.send(msg).unwrap(); - rx.recv().unwrap() - } - - pub fn resize_webgl_context(&self, context_id: WebGLContextId, size: &DeviceIntSize) { - let msg = ApiMsg::ResizeWebGLContext(context_id, *size); - self.api_sender.send(msg).unwrap(); - } - - pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) { - let msg = ApiMsg::WebGLCommand(context_id, command); - self.api_sender.send(msg).unwrap(); - } - - pub fn send_vr_compositor_command(&self, context_id: WebGLContextId, command: VRCompositorCommand) { - let msg = ApiMsg::VRCompositorCommand(context_id, command); - self.api_sender.send(msg).unwrap(); - } - pub fn send_external_event(&self, evt: ExternalEvent) { let msg = ApiMsg::ExternalEvent(evt); self.api_sender.send(msg).unwrap(); @@ -705,31 +654,9 @@ pub struct DynamicProperties { pub floats: Vec>, } -pub type VRCompositorId = u64; - -// WebVR commands that must be called in the WebGL render thread. -#[derive(Clone, Deserialize, Serialize)] -pub enum VRCompositorCommand { - Create(VRCompositorId), - SyncPoses(VRCompositorId, f64, f64, MsgSender,()>>), - SubmitFrame(VRCompositorId, [f32; 4], [f32; 4]), - Release(VRCompositorId) -} - -// Trait object that handles WebVR commands. -// Receives the texture id and size associated to the WebGLContext. -pub trait VRCompositorHandler: Send { - fn handle(&mut self, command: VRCompositorCommand, texture: Option<(u32, DeviceIntSize)>); -} - pub trait RenderNotifier: Send { fn new_frame_ready(&mut self); fn new_scroll_frame_ready(&mut self, composite_needed: bool); fn external_event(&mut self, _evt: ExternalEvent) { unimplemented!() } fn shut_down(&mut self) {} } - -/// Trait to allow dispatching functions to a specific thread or event loop. -pub trait RenderDispatcher: Send { - fn dispatch(&self, Box); -} diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index d31265773803..61be7b101b1a 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -3,9 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; -use euclid::SideOffsets2D; +use euclid::{SideOffsets2D, TypedSideOffsets2D}; use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; -use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding, WebGLContextId}; +use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding}; // NOTE: some of these structs have an "IMPLICIT" comment. // This indicates that the BuiltDisplayList will have serialized @@ -50,12 +50,12 @@ pub struct DisplayItem { pub enum SpecificDisplayItem { Clip(ClipDisplayItem), ScrollFrame(ScrollFrameDisplayItem), + StickyFrame(StickyFrameDisplayItem), Rectangle(RectangleDisplayItem), Line(LineDisplayItem), Text(TextDisplayItem), Image(ImageDisplayItem), YuvImage(YuvImageDisplayItem), - WebGL(WebGLDisplayItem), Border(BorderDisplayItem), BoxShadow(BoxShadowDisplayItem), Gradient(GradientDisplayItem), @@ -77,6 +77,20 @@ pub struct ClipDisplayItem { pub image_mask: Option, } +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct StickyFrameDisplayItem { + pub id: ClipId, + pub sticky_frame_info: StickyFrameInfo, +} + +pub type StickyFrameInfo = TypedSideOffsets2D, LayoutPoint>; + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct StickySideConstraint { + pub margin: f32, + pub max_offset: f32, +} + #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub enum ScrollSensitivity { ScriptAndInputEvents, @@ -131,11 +145,6 @@ pub struct TextDisplayItem { pub glyph_options: Option, } // IMPLICIT: glyphs: Vec -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub struct WebGLDisplayItem { - pub context_id: WebGLContextId, -} - #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct NormalBorder { pub left: BorderSide, @@ -663,11 +672,12 @@ macro_rules! define_empty_heap_size_of { } } -define_empty_heap_size_of!(ClipId); -define_empty_heap_size_of!(RepeatMode); -define_empty_heap_size_of!(ImageKey); -define_empty_heap_size_of!(MixBlendMode); -define_empty_heap_size_of!(TransformStyle); -define_empty_heap_size_of!(LocalClip); -define_empty_heap_size_of!(ScrollSensitivity); define_empty_heap_size_of!(ClipAndScrollInfo); +define_empty_heap_size_of!(ClipId); +define_empty_heap_size_of!(ImageKey); +define_empty_heap_size_of!(LocalClip); +define_empty_heap_size_of!(MixBlendMode); +define_empty_heap_size_of!(RepeatMode); +define_empty_heap_size_of!(ScrollSensitivity); +define_empty_heap_size_of!(StickySideConstraint); +define_empty_heap_size_of!(TransformStyle); diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index 528abbfec798..4f7cb43b6bcb 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -9,15 +9,15 @@ use serde::ser::{SerializeSeq, SerializeMap}; use time::precise_time_ns; use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem}; use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem}; -use {ExtendMode, FilterOp, FontKey, GlyphIndex, GlyphInstance, GlyphOptions, Gradient}; -use {GradientDisplayItem, GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask}; -use {ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; -use {LineDisplayItem, LineOrientation, LineStyle, LocalClip, MixBlendMode, PipelineId}; -use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem}; -use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity}; -use {SpecificDisplayItem, StackingContext, TextDisplayItem, TextShadow, TransformStyle}; -use {WebGLContextId, WebGLDisplayItem, YuvColorSpace, YuvData, YuvImageDisplayItem}; -use {FastHashMap, FastHashSet}; +use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontKey, GlyphIndex, GlyphInstance}; +use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem}; +use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayoutPoint, LayoutRect, LayoutSize}; +use {LayoutTransform, LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, LocalClip}; +use {MixBlendMode, PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient}; +use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy}; +use {ScrollSensitivity, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem}; +use {StickyFrameInfo, TextDisplayItem, TextShadow, TransformStyle}; +use {YuvColorSpace, YuvData, YuvImageDisplayItem}; use std::marker::PhantomData; #[repr(C)] @@ -597,16 +597,6 @@ impl DisplayListBuilder { self.push_item(item, rect, local_clip); } - pub fn push_webgl_canvas(&mut self, - rect: LayoutRect, - local_clip: Option, - context_id: WebGLContextId) { - let item = SpecificDisplayItem::WebGL(WebGLDisplayItem { - context_id, - }); - self.push_item(item, rect, local_clip); - } - pub fn push_text(&mut self, rect: LayoutRect, local_clip: Option, @@ -968,6 +958,21 @@ impl DisplayListBuilder { id } + pub fn define_sticky_frame(&mut self, + id: Option, + frame_rect: LayoutRect, + sticky_frame_info: StickyFrameInfo) + -> ClipId { + let id = self.generate_clip_id(id); + let item = SpecificDisplayItem::StickyFrame(StickyFrameDisplayItem { + id, + sticky_frame_info, + }); + + self.push_item(item, frame_rect, None); + id + } + pub fn push_clip_id(&mut self, id: ClipId) { self.clip_stack.push(ClipAndScrollInfo::simple(id)); } diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 5a2089a67942..41cb6ed67355 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -145,7 +145,7 @@ pub struct GlyphOptions { } #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] -pub struct FontInstanceKey { +pub struct FontInstance { pub font_key: FontKey, // The font size is in *device* pixels, not logical pixels. // It is stored as an Au since we need sub-pixel sizes, but @@ -159,13 +159,13 @@ pub struct FontInstanceKey { pub subpx_dir: SubpixelDirection, } -impl FontInstanceKey { +impl FontInstance { pub fn new(font_key: FontKey, size: Au, mut color: ColorF, render_mode: FontRenderMode, glyph_options: Option, - subpx_dir: SubpixelDirection) -> FontInstanceKey { + subpx_dir: SubpixelDirection) -> FontInstance { // In alpha/mono mode, the color of the font is irrelevant. // Forcing it to black in those cases saves rasterizing glyphs // of different colors when not needed. @@ -173,7 +173,7 @@ impl FontInstanceKey { color = ColorF::new(0.0, 0.0, 0.0, 1.0); } - FontInstanceKey { + FontInstance { font_key, size, color: color.into(), diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index 39a85be3caee..9b5fd755aeda 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -33,6 +33,7 @@ pub struct ExternalImageId(pub u64); #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum ExternalImageType { Texture2DHandle, // gl TEXTURE_2D handle + Texture2DArrayHandle, // gl TEXTURE_2D_ARRAY handle TextureRectHandle, // gl TEXTURE_RECT handle TextureExternalHandle, // gl TEXTURE_EXTERNAL handle ExternalBuffer, @@ -131,6 +132,7 @@ impl ImageData { &ImageData::External(ext_data) => { match ext_data.image_type { ExternalImageType::Texture2DHandle => false, + ExternalImageType::Texture2DArrayHandle => false, ExternalImageType::TextureRectHandle => false, ExternalImageType::TextureExternalHandle => false, ExternalImageType::ExternalBuffer => true, diff --git a/gfx/webrender_api/src/lib.rs b/gfx/webrender_api/src/lib.rs index 6e9d3568f29e..461f06501923 100644 --- a/gfx/webrender_api/src/lib.rs +++ b/gfx/webrender_api/src/lib.rs @@ -12,13 +12,10 @@ extern crate byteorder; extern crate core; extern crate euclid; extern crate fxhash; -extern crate gleam; #[macro_use] extern crate heapsize; #[cfg(feature = "ipc")] extern crate ipc_channel; -#[cfg(feature = "webgl")] -extern crate offscreen_gl_context; #[macro_use] extern crate serde; extern crate time; @@ -40,8 +37,6 @@ mod display_item; mod display_list; mod font; mod image; -#[cfg(feature = "webgl")] -mod webgl; pub use api::*; pub use color::*; @@ -50,8 +45,6 @@ pub use display_list::*; pub use font::*; pub use image::*; pub use units::*; -#[cfg(feature = "webgl")] -pub use webgl::*; use std::hash::BuildHasherDefault; use std::collections::{HashMap, HashSet}; diff --git a/gfx/webrender_api/src/webgl.rs b/gfx/webrender_api/src/webgl.rs deleted file mode 100644 index 3a6b734adea9..000000000000 --- a/gfx/webrender_api/src/webgl.rs +++ /dev/null @@ -1,1043 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use channel::MsgSender; -use gleam::gl; - -#[cfg(feature = "nightly")] -use core::nonzero::NonZero; - -use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextAttributes}; -use std::fmt; - -#[derive(Clone, Deserialize, Serialize)] -pub enum WebGLCommand { - GetContextAttributes(MsgSender), - ActiveTexture(u32), - BlendColor(f32, f32, f32, f32), - BlendEquation(u32), - BlendEquationSeparate(u32, u32), - BlendFunc(u32, u32), - BlendFuncSeparate(u32, u32, u32, u32), - AttachShader(WebGLProgramId, WebGLShaderId), - DetachShader(WebGLProgramId, WebGLShaderId), - BindAttribLocation(WebGLProgramId, u32, String), - BufferData(u32, Vec, u32), - BufferSubData(u32, isize, Vec), - Clear(u32), - ClearColor(f32, f32, f32, f32), - ClearDepth(f64), - ClearStencil(i32), - ColorMask(bool, bool, bool, bool), - CullFace(u32), - FrontFace(u32), - DepthFunc(u32), - DepthMask(bool), - DepthRange(f64, f64), - Enable(u32), - Disable(u32), - CompileShader(WebGLShaderId, String), - CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32), - CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32), - CreateBuffer(MsgSender>), - CreateFramebuffer(MsgSender>), - CreateRenderbuffer(MsgSender>), - CreateTexture(MsgSender>), - CreateProgram(MsgSender>), - CreateShader(u32, MsgSender>), - DeleteBuffer(WebGLBufferId), - DeleteFramebuffer(WebGLFramebufferId), - DeleteRenderbuffer(WebGLRenderbufferId), - DeleteTexture(WebGLTextureId), - DeleteProgram(WebGLProgramId), - DeleteShader(WebGLShaderId), - BindBuffer(u32, Option), - BindFramebuffer(u32, WebGLFramebufferBindingRequest), - BindRenderbuffer(u32, Option), - BindTexture(u32, Option), - DisableVertexAttribArray(u32), - DrawArrays(u32, i32, i32), - DrawElements(u32, i32, u32, i64), - EnableVertexAttribArray(u32), - FramebufferRenderbuffer(u32, u32, u32, Option), - FramebufferTexture2D(u32, u32, u32, Option, i32), - GetBufferParameter(u32, u32, MsgSender>), - GetExtensions(MsgSender), - GetParameter(u32, MsgSender>), - GetProgramParameter(WebGLProgramId, u32, MsgSender>), - GetShaderParameter(WebGLShaderId, u32, MsgSender>), - GetShaderPrecisionFormat(u32, u32, MsgSender>), - GetActiveAttrib(WebGLProgramId, u32, MsgSender>), - GetActiveUniform(WebGLProgramId, u32, MsgSender>), - GetAttribLocation(WebGLProgramId, String, MsgSender>), - GetUniformLocation(WebGLProgramId, String, MsgSender>), - GetVertexAttrib(u32, u32, MsgSender>), - GetVertexAttribOffset(u32, u32, MsgSender>), - GetShaderInfoLog(WebGLShaderId, MsgSender), - GetProgramInfoLog(WebGLProgramId, MsgSender), - PolygonOffset(f32, f32), - RenderbufferStorage(u32, u32, i32, i32), - ReadPixels(i32, i32, i32, i32, u32, u32, MsgSender>), - SampleCoverage(f32, bool), - Scissor(i32, i32, i32, i32), - StencilFunc(u32, i32, u32), - StencilFuncSeparate(u32, u32, i32, u32), - StencilMask(u32), - StencilMaskSeparate(u32, u32), - StencilOp(u32, u32, u32), - StencilOpSeparate(u32, u32, u32, u32), - Hint(u32, u32), - IsEnabled(u32, MsgSender), - LineWidth(f32), - PixelStorei(u32, i32), - LinkProgram(WebGLProgramId), - Uniform1f(i32, f32), - Uniform1fv(i32, Vec), - Uniform1i(i32, i32), - Uniform1iv(i32, Vec), - Uniform2f(i32, f32, f32), - Uniform2fv(i32, Vec), - Uniform2i(i32, i32, i32), - Uniform2iv(i32, Vec), - Uniform3f(i32, f32, f32, f32), - Uniform3fv(i32, Vec), - Uniform3i(i32, i32, i32, i32), - Uniform3iv(i32, Vec), - Uniform4f(i32, f32, f32, f32, f32), - Uniform4fv(i32, Vec), - Uniform4i(i32, i32, i32, i32, i32), - Uniform4iv(i32, Vec), - UniformMatrix2fv(i32, bool, Vec), - UniformMatrix3fv(i32, bool, Vec), - UniformMatrix4fv(i32, bool, Vec), - UseProgram(WebGLProgramId), - ValidateProgram(WebGLProgramId), - VertexAttrib(u32, f32, f32, f32, f32), - VertexAttribPointer(u32, i32, u32, bool, i32, u32), - VertexAttribPointer2f(u32, i32, bool, i32, u32), - Viewport(i32, i32, i32, i32), - TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec), - TexParameteri(u32, u32, i32), - TexParameterf(u32, u32, f32), - TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec), - DrawingBufferWidth(MsgSender), - DrawingBufferHeight(MsgSender), - Finish(MsgSender<()>), - Flush, - GenerateMipmap(u32), - CreateVertexArray(MsgSender>), - DeleteVertexArray(WebGLVertexArrayId), - BindVertexArray(Option), -} - -#[cfg(feature = "nightly")] -macro_rules! define_resource_id_struct { - ($name:ident) => { - #[derive(Clone, Copy, Eq, Hash, PartialEq)] - pub struct $name(NonZero); - - impl $name { - #[inline] - unsafe fn new(id: u32) -> Self { - $name(NonZero::new(id)) - } - - #[inline] - fn get(self) -> u32 { - *self.0 - } - } - - }; -} - -#[cfg(not(feature = "nightly"))] -macro_rules! define_resource_id_struct { - ($name:ident) => { - #[derive(Clone, Copy, Eq, Hash, PartialEq)] - pub struct $name(u32); - - impl $name { - #[inline] - unsafe fn new(id: u32) -> Self { - $name(id) - } - - #[inline] - fn get(self) -> u32 { - self.0 - } - } - }; -} - -macro_rules! define_resource_id { - ($name:ident) => { - define_resource_id_struct!($name); - - impl<'de> ::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result - where D: ::serde::Deserializer<'de> - { - let id = try!(u32::deserialize(deserializer)); - if id == 0 { - Err(::serde::de::Error::custom("expected a non-zero value")) - } else { - Ok(unsafe { $name::new(id) }) - } - } - } - - impl ::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where S: ::serde::Serializer - { - self.get().serialize(serializer) - } - } - - impl ::std::fmt::Debug for $name { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) - -> Result<(), ::std::fmt::Error> { - fmt.debug_tuple(stringify!($name)) - .field(&self.get()) - .finish() - } - } - - impl ::std::fmt::Display for $name { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter) - -> Result<(), ::std::fmt::Error> { - write!(fmt, "{}", self.get()) - } - } - - impl ::heapsize::HeapSizeOf for $name { - fn heap_size_of_children(&self) -> usize { 0 } - } - } -} - -define_resource_id!(WebGLBufferId); -define_resource_id!(WebGLFramebufferId); -define_resource_id!(WebGLRenderbufferId); -define_resource_id!(WebGLTextureId); -define_resource_id!(WebGLProgramId); -define_resource_id!(WebGLShaderId); -define_resource_id!(WebGLVertexArrayId); - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct WebGLContextId(pub usize); - -impl ::heapsize::HeapSizeOf for WebGLContextId { - fn heap_size_of_children(&self) -> usize { 0 } -} - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub enum WebGLError { - InvalidEnum, - InvalidFramebufferOperation, - InvalidOperation, - InvalidValue, - OutOfMemory, - ContextLost, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum WebGLFramebufferBindingRequest { - Explicit(WebGLFramebufferId), - Default, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum WebGLParameter { - Int(i32), - Bool(bool), - String(String), - Float(f32), - FloatArray(Vec), - Invalid, -} - -pub type WebGLResult = Result; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum WebGLShaderParameter { - Int(i32), - Bool(bool), - Invalid, -} - -impl fmt::Debug for WebGLCommand { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use WebGLCommand::*; - let name = match *self { - GetContextAttributes(..) => "GetContextAttributes", - ActiveTexture(..) => "ActiveTexture", - BlendColor(..) => "BlendColor", - BlendEquation(..) => "BlendEquation", - BlendEquationSeparate(..) => "BlendEquationSeparate", - BlendFunc(..) => "BlendFunc", - BlendFuncSeparate(..) => "BlendFuncSeparate", - AttachShader(..) => "AttachShader", - DetachShader(..) => "DetachShader", - BindAttribLocation(..) => "BindAttribLocation", - BufferData(..) => "BufferData", - BufferSubData(..) => "BufferSubData", - Clear(..) => "Clear", - ClearColor(..) => "ClearColor", - ClearDepth(..) => "ClearDepth", - ClearStencil(..) => "ClearStencil", - ColorMask(..) => "ColorMask", - CopyTexImage2D(..) => "CopyTexImage2D", - CopyTexSubImage2D(..) => "CopyTexSubImage2D", - CullFace(..) => "CullFace", - FrontFace(..) => "FrontFace", - DepthFunc(..) => "DepthFunc", - DepthMask(..) => "DepthMask", - DepthRange(..) => "DepthRange", - Enable(..) => "Enable", - Disable(..) => "Disable", - CompileShader(..) => "CompileShader", - CreateBuffer(..) => "CreateBuffer", - CreateFramebuffer(..) => "CreateFramebuffer", - CreateRenderbuffer(..) => "CreateRenderbuffer", - CreateTexture(..) => "CreateTexture", - CreateProgram(..) => "CreateProgram", - CreateShader(..) => "CreateShader", - DeleteBuffer(..) => "DeleteBuffer", - DeleteFramebuffer(..) => "DeleteFramebuffer", - DeleteRenderbuffer(..) => "DeleteRenderBuffer", - DeleteTexture(..) => "DeleteTexture", - DeleteProgram(..) => "DeleteProgram", - DeleteShader(..) => "DeleteShader", - BindBuffer(..) => "BindBuffer", - BindFramebuffer(..) => "BindFramebuffer", - BindRenderbuffer(..) => "BindRenderbuffer", - BindTexture(..) => "BindTexture", - DisableVertexAttribArray(..) => "DisableVertexAttribArray", - DrawArrays(..) => "DrawArrays", - DrawElements(..) => "DrawElements", - EnableVertexAttribArray(..) => "EnableVertexAttribArray", - FramebufferRenderbuffer(..) => "FramebufferRenderbuffer", - FramebufferTexture2D(..) => "FramebufferTexture2D", - GetBufferParameter(..) => "GetBufferParameter", - GetExtensions(..) => "GetExtensions", - GetParameter(..) => "GetParameter", - GetProgramParameter(..) => "GetProgramParameter", - GetShaderParameter(..) => "GetShaderParameter", - GetShaderPrecisionFormat(..) => "GetShaderPrecisionFormat", - GetActiveAttrib(..) => "GetActiveAttrib", - GetActiveUniform(..) => "GetActiveUniform", - GetAttribLocation(..) => "GetAttribLocation", - GetUniformLocation(..) => "GetUniformLocation", - GetShaderInfoLog(..) => "GetShaderInfoLog", - GetProgramInfoLog(..) => "GetProgramInfoLog", - GetVertexAttrib(..) => "GetVertexAttrib", - GetVertexAttribOffset(..) => "GetVertexAttribOffset", - PolygonOffset(..) => "PolygonOffset", - ReadPixels(..) => "ReadPixels", - RenderbufferStorage(..) => "RenderbufferStorage", - SampleCoverage(..) => "SampleCoverage", - Scissor(..) => "Scissor", - StencilFunc(..) => "StencilFunc", - StencilFuncSeparate(..) => "StencilFuncSeparate", - StencilMask(..) => "StencilMask", - StencilMaskSeparate(..) => "StencilMaskSeparate", - StencilOp(..) => "StencilOp", - StencilOpSeparate(..) => "StencilOpSeparate", - Hint(..) => "Hint", - IsEnabled(..) => "IsEnabled", - LineWidth(..) => "LineWidth", - PixelStorei(..) => "PixelStorei", - LinkProgram(..) => "LinkProgram", - Uniform1f(..) => "Uniform1f", - Uniform1fv(..) => "Uniform1fv", - Uniform1i(..) => "Uniform1i", - Uniform1iv(..) => "Uniform1iv", - Uniform2f(..) => "Uniform2f", - Uniform2fv(..) => "Uniform2fv", - Uniform2i(..) => "Uniform2i", - Uniform2iv(..) => "Uniform2iv", - Uniform3f(..) => "Uniform3f", - Uniform3fv(..) => "Uniform3fv", - Uniform3i(..) => "Uniform3i", - Uniform3iv(..) => "Uniform3iv", - Uniform4f(..) => "Uniform4f", - Uniform4fv(..) => "Uniform4fv", - Uniform4i(..) => "Uniform4i", - Uniform4iv(..) => "Uniform4iv", - UniformMatrix2fv(..) => "UniformMatrix2fv", - UniformMatrix3fv(..) => "UniformMatrix3fv", - UniformMatrix4fv(..) => "UniformMatrix4fv", - UseProgram(..) => "UseProgram", - ValidateProgram(..) => "ValidateProgram", - VertexAttrib(..) => "VertexAttrib", - VertexAttribPointer2f(..) => "VertexAttribPointer2f", - VertexAttribPointer(..) => "VertexAttribPointer", - Viewport(..) => "Viewport", - TexImage2D(..) => "TexImage2D", - TexParameteri(..) => "TexParameteri", - TexParameterf(..) => "TexParameterf", - TexSubImage2D(..) => "TexSubImage2D", - DrawingBufferWidth(..) => "DrawingBufferWidth", - DrawingBufferHeight(..) => "DrawingBufferHeight", - Finish(..) => "Finish", - Flush => "Flush", - GenerateMipmap(..) => "GenerateMipmap", - CreateVertexArray(..) => "CreateVertexArray", - DeleteVertexArray(..) => "DeleteVertexArray", - BindVertexArray(..) => "BindVertexArray" - }; - - write!(f, "CanvasWebGLMsg::{}(..)", name) - } -} - -impl WebGLCommand { - /// NOTE: This method consumes the command - pub fn apply(self, ctx: &GLContext) { - match self { - WebGLCommand::GetContextAttributes(sender) => - sender.send(*ctx.borrow_attributes()).unwrap(), - WebGLCommand::ActiveTexture(target) => - ctx.gl().active_texture(target), - WebGLCommand::AttachShader(program_id, shader_id) => - ctx.gl().attach_shader(program_id.get(), shader_id.get()), - WebGLCommand::DetachShader(program_id, shader_id) => - ctx.gl().detach_shader(program_id.get(), shader_id.get()), - WebGLCommand::BindAttribLocation(program_id, index, name) => - ctx.gl().bind_attrib_location(program_id.get(), index, &name), - WebGLCommand::BlendColor(r, g, b, a) => - ctx.gl().blend_color(r, g, b, a), - WebGLCommand::BlendEquation(mode) => - ctx.gl().blend_equation(mode), - WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => - ctx.gl().blend_equation_separate(mode_rgb, mode_alpha), - WebGLCommand::BlendFunc(src, dest) => - ctx.gl().blend_func(src, dest), - WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => - ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha), - WebGLCommand::BufferData(buffer_type, data, usage) => - gl::buffer_data(ctx.gl(), buffer_type, &data, usage), - WebGLCommand::BufferSubData(buffer_type, offset, data) => - gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &data), - WebGLCommand::Clear(mask) => - ctx.gl().clear(mask), - WebGLCommand::ClearColor(r, g, b, a) => - ctx.gl().clear_color(r, g, b, a), - WebGLCommand::ClearDepth(depth) => - ctx.gl().clear_depth(depth), - WebGLCommand::ClearStencil(stencil) => - ctx.gl().clear_stencil(stencil), - WebGLCommand::ColorMask(r, g, b, a) => - ctx.gl().color_mask(r, g, b, a), - WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border) => - ctx.gl().copy_tex_image_2d(target, level, internal_format, x, y, width, height, border), - WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) => - ctx.gl().copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height), - WebGLCommand::CullFace(mode) => - ctx.gl().cull_face(mode), - WebGLCommand::DepthFunc(func) => - ctx.gl().depth_func(func), - WebGLCommand::DepthMask(flag) => - ctx.gl().depth_mask(flag), - WebGLCommand::DepthRange(near, far) => - ctx.gl().depth_range(near, far), - WebGLCommand::Disable(cap) => - ctx.gl().disable(cap), - WebGLCommand::Enable(cap) => - ctx.gl().enable(cap), - WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => - ctx.gl().framebuffer_renderbuffer(target, attachment, renderbuffertarget, rb.map_or(0, WebGLRenderbufferId::get)), - WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => - ctx.gl().framebuffer_texture_2d(target, attachment, textarget, texture.map_or(0, WebGLTextureId::get), level), - WebGLCommand::FrontFace(mode) => - ctx.gl().front_face(mode), - WebGLCommand::DisableVertexAttribArray(attrib_id) => - ctx.gl().disable_vertex_attrib_array(attrib_id), - WebGLCommand::DrawArrays(mode, first, count) => - ctx.gl().draw_arrays(mode, first, count), - WebGLCommand::DrawElements(mode, count, type_, offset) => - ctx.gl().draw_elements(mode, count, type_, offset as u32), - WebGLCommand::EnableVertexAttribArray(attrib_id) => - ctx.gl().enable_vertex_attrib_array(attrib_id), - WebGLCommand::Hint(name, val) => - ctx.gl().hint(name, val), - WebGLCommand::IsEnabled(cap, chan) => - chan.send(ctx.gl().is_enabled(cap) != 0).unwrap(), - WebGLCommand::LineWidth(width) => - ctx.gl().line_width(width), - WebGLCommand::PixelStorei(name, val) => - ctx.gl().pixel_store_i(name, val), - WebGLCommand::PolygonOffset(factor, units) => - ctx.gl().polygon_offset(factor, units), - WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, chan) => - Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan), - WebGLCommand::RenderbufferStorage(target, format, width, height) => - ctx.gl().renderbuffer_storage(target, format, width, height), - WebGLCommand::SampleCoverage(value, invert) => - ctx.gl().sample_coverage(value, invert), - WebGLCommand::Scissor(x, y, width, height) => - ctx.gl().scissor(x, y, width, height), - WebGLCommand::StencilFunc(func, ref_, mask) => - ctx.gl().stencil_func(func, ref_, mask), - WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => - ctx.gl().stencil_func_separate(face, func, ref_, mask), - WebGLCommand::StencilMask(mask) => - ctx.gl().stencil_mask(mask), - WebGLCommand::StencilMaskSeparate(face, mask) => - ctx.gl().stencil_mask_separate(face, mask), - WebGLCommand::StencilOp(fail, zfail, zpass) => - ctx.gl().stencil_op(fail, zfail, zpass), - WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => - ctx.gl().stencil_op_separate(face, fail, zfail, zpass), - WebGLCommand::GetActiveAttrib(program_id, index, chan) => - Self::active_attrib(ctx.gl(), program_id, index, chan), - WebGLCommand::GetActiveUniform(program_id, index, chan) => - Self::active_uniform(ctx.gl(), program_id, index, chan), - WebGLCommand::GetAttribLocation(program_id, name, chan) => - Self::attrib_location(ctx.gl(), program_id, name, chan), - WebGLCommand::GetVertexAttrib(index, pname, chan) => - Self::vertex_attrib(ctx.gl(), index, pname, chan), - WebGLCommand::GetVertexAttribOffset(index, pname, chan) => - Self::vertex_attrib_offset(ctx.gl(), index, pname, chan), - WebGLCommand::GetBufferParameter(target, param_id, chan) => - Self::buffer_parameter(ctx.gl(), target, param_id, chan), - WebGLCommand::GetParameter(param_id, chan) => - Self::parameter(ctx.gl(), param_id, chan), - WebGLCommand::GetProgramParameter(program_id, param_id, chan) => - Self::program_parameter(ctx.gl(), program_id, param_id, chan), - WebGLCommand::GetShaderParameter(shader_id, param_id, chan) => - Self::shader_parameter(ctx.gl(), shader_id, param_id, chan), - WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, chan) => - Self::shader_precision_format(ctx.gl(), shader_type, precision_type, chan), - WebGLCommand::GetExtensions(chan) => - Self::get_extensions(ctx.gl(), chan), - WebGLCommand::GetUniformLocation(program_id, name, chan) => - Self::uniform_location(ctx.gl(), program_id, name, chan), - WebGLCommand::GetShaderInfoLog(shader_id, chan) => - Self::shader_info_log(ctx.gl(), shader_id, chan), - WebGLCommand::GetProgramInfoLog(program_id, chan) => - Self::program_info_log(ctx.gl(), program_id, chan), - WebGLCommand::CompileShader(shader_id, source) => - Self::compile_shader(ctx.gl(), shader_id, source), - WebGLCommand::CreateBuffer(chan) => - Self::create_buffer(ctx.gl(), chan), - WebGLCommand::CreateFramebuffer(chan) => - Self::create_framebuffer(ctx.gl(), chan), - WebGLCommand::CreateRenderbuffer(chan) => - Self::create_renderbuffer(ctx.gl(), chan), - WebGLCommand::CreateTexture(chan) => - Self::create_texture(ctx.gl(), chan), - WebGLCommand::CreateProgram(chan) => - Self::create_program(ctx.gl(), chan), - WebGLCommand::CreateShader(shader_type, chan) => - Self::create_shader(ctx.gl(), shader_type, chan), - WebGLCommand::DeleteBuffer(id) => - ctx.gl().delete_buffers(&[id.get()]), - WebGLCommand::DeleteFramebuffer(id) => - ctx.gl().delete_framebuffers(&[id.get()]), - WebGLCommand::DeleteRenderbuffer(id) => - ctx.gl().delete_renderbuffers(&[id.get()]), - WebGLCommand::DeleteTexture(id) => - ctx.gl().delete_textures(&[id.get()]), - WebGLCommand::DeleteProgram(id) => - ctx.gl().delete_program(id.get()), - WebGLCommand::DeleteShader(id) => - ctx.gl().delete_shader(id.get()), - WebGLCommand::BindBuffer(target, id) => - ctx.gl().bind_buffer(target, id.map_or(0, WebGLBufferId::get)), - WebGLCommand::BindFramebuffer(target, request) => - Self::bind_framebuffer(ctx.gl(), target, request, ctx), - WebGLCommand::BindRenderbuffer(target, id) => - ctx.gl().bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)), - WebGLCommand::BindTexture(target, id) => - ctx.gl().bind_texture(target, id.map_or(0, WebGLTextureId::get)), - WebGLCommand::LinkProgram(program_id) => - ctx.gl().link_program(program_id.get()), - WebGLCommand::Uniform1f(uniform_id, v) => - ctx.gl().uniform_1f(uniform_id, v), - WebGLCommand::Uniform1fv(uniform_id, v) => - ctx.gl().uniform_1fv(uniform_id, &v), - WebGLCommand::Uniform1i(uniform_id, v) => - ctx.gl().uniform_1i(uniform_id, v), - WebGLCommand::Uniform1iv(uniform_id, v) => - ctx.gl().uniform_1iv(uniform_id, &v), - WebGLCommand::Uniform2f(uniform_id, x, y) => - ctx.gl().uniform_2f(uniform_id, x, y), - WebGLCommand::Uniform2fv(uniform_id, v) => - ctx.gl().uniform_2fv(uniform_id, &v), - WebGLCommand::Uniform2i(uniform_id, x, y) => - ctx.gl().uniform_2i(uniform_id, x, y), - WebGLCommand::Uniform2iv(uniform_id, v) => - ctx.gl().uniform_2iv(uniform_id, &v), - WebGLCommand::Uniform3f(uniform_id, x, y, z) => - ctx.gl().uniform_3f(uniform_id, x, y, z), - WebGLCommand::Uniform3fv(uniform_id, v) => - ctx.gl().uniform_3fv(uniform_id, &v), - WebGLCommand::Uniform3i(uniform_id, x, y, z) => - ctx.gl().uniform_3i(uniform_id, x, y, z), - WebGLCommand::Uniform3iv(uniform_id, v) => - ctx.gl().uniform_3iv(uniform_id, &v), - WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => - ctx.gl().uniform_4f(uniform_id, x, y, z, w), - WebGLCommand::Uniform4fv(uniform_id, v) => - ctx.gl().uniform_4fv(uniform_id, &v), - WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => - ctx.gl().uniform_4i(uniform_id, x, y, z, w), - WebGLCommand::Uniform4iv(uniform_id, v) => - ctx.gl().uniform_4iv(uniform_id, &v), - WebGLCommand::UniformMatrix2fv(uniform_id, transpose, v) => - ctx.gl().uniform_matrix_2fv(uniform_id, transpose, &v), - WebGLCommand::UniformMatrix3fv(uniform_id, transpose, v) => - ctx.gl().uniform_matrix_3fv(uniform_id, transpose, &v), - WebGLCommand::UniformMatrix4fv(uniform_id, transpose, v) => - ctx.gl().uniform_matrix_4fv(uniform_id, transpose, &v), - WebGLCommand::UseProgram(program_id) => - ctx.gl().use_program(program_id.get()), - WebGLCommand::ValidateProgram(program_id) => - ctx.gl().validate_program(program_id.get()), - WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => - ctx.gl().vertex_attrib_4f(attrib_id, x, y, z, w), - WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => - ctx.gl().vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset), - WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset) => - ctx.gl().vertex_attrib_pointer(attrib_id, size, data_type, normalized, stride, offset), - WebGLCommand::Viewport(x, y, width, height) => - ctx.gl().viewport(x, y, width, height), - WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, data) => - ctx.gl().tex_image_2d(target, level, internal, width, height, /*border*/0, format, data_type, Some(&data)), - WebGLCommand::TexParameteri(target, name, value) => - ctx.gl().tex_parameter_i(target, name, value), - WebGLCommand::TexParameterf(target, name, value) => - ctx.gl().tex_parameter_f(target, name, value), - WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, data) => - ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, &data), - WebGLCommand::DrawingBufferWidth(sender) => - sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(), - WebGLCommand::DrawingBufferHeight(sender) => - sender.send(ctx.borrow_draw_buffer().unwrap().size().height).unwrap(), - WebGLCommand::Finish(sender) => - Self::finish(ctx.gl(), sender), - WebGLCommand::Flush => - ctx.gl().flush(), - WebGLCommand::GenerateMipmap(target) => - ctx.gl().generate_mipmap(target), - WebGLCommand::CreateVertexArray(chan) => - Self::create_vertex_array(ctx.gl(), chan), - WebGLCommand::DeleteVertexArray(id) => - ctx.gl().delete_vertex_arrays(&[id.get()]), - WebGLCommand::BindVertexArray(id) => - ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)), - } - - // FIXME: Use debug_assertions once tests are run with them - let error = ctx.gl().get_error(); - assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error); - } - - fn read_pixels(gl: &gl::Gl, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32, - chan: MsgSender>) { - let result = gl.read_pixels(x, y, width, height, format, pixel_type); - chan.send(result).unwrap() - } - - fn active_attrib(gl: &gl::Gl, - program_id: WebGLProgramId, - index: u32, - chan: MsgSender>) { - let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_ATTRIBUTES) as u32 { - Err(WebGLError::InvalidValue) - } else { - Ok(gl.get_active_attrib(program_id.get(), index)) - }; - chan.send(result).unwrap(); - } - - fn active_uniform(gl: &gl::Gl, - program_id: WebGLProgramId, - index: u32, - chan: MsgSender>) { - let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_UNIFORMS) as u32 { - Err(WebGLError::InvalidValue) - } else { - Ok(gl.get_active_uniform(program_id.get(), index)) - }; - chan.send(result).unwrap(); - } - - fn attrib_location(gl: &gl::Gl, - program_id: WebGLProgramId, - name: String, - chan: MsgSender> ) { - let attrib_location = gl.get_attrib_location(program_id.get(), &name); - - let attrib_location = if attrib_location == -1 { - None - } else { - Some(attrib_location) - }; - - chan.send(attrib_location).unwrap(); - } - - fn parameter(gl: &gl::Gl, - param_id: u32, - chan: MsgSender>) { - let result = match param_id { - gl::ACTIVE_TEXTURE | - gl::ALPHA_BITS | - gl::BLEND_DST_ALPHA | - gl::BLEND_DST_RGB | - gl::BLEND_EQUATION_ALPHA | - gl::BLEND_EQUATION_RGB | - gl::BLEND_SRC_ALPHA | - gl::BLEND_SRC_RGB | - gl::BLUE_BITS | - gl::CULL_FACE_MODE | - gl::DEPTH_BITS | - gl::DEPTH_FUNC | - gl::FRONT_FACE | - //gl::GENERATE_MIPMAP_HINT | - gl::GREEN_BITS | - //gl::IMPLEMENTATION_COLOR_READ_FORMAT | - //gl::IMPLEMENTATION_COLOR_READ_TYPE | - gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS | - gl::MAX_CUBE_MAP_TEXTURE_SIZE | - //gl::MAX_FRAGMENT_UNIFORM_VECTORS | - gl::MAX_RENDERBUFFER_SIZE | - gl::MAX_TEXTURE_IMAGE_UNITS | - gl::MAX_TEXTURE_SIZE | - //gl::MAX_VARYING_VECTORS | - gl::MAX_VERTEX_ATTRIBS | - gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS | - //gl::MAX_VERTEX_UNIFORM_VECTORS | - gl::PACK_ALIGNMENT | - gl::RED_BITS | - gl::SAMPLE_BUFFERS | - gl::SAMPLES | - gl::STENCIL_BACK_FAIL | - gl::STENCIL_BACK_FUNC | - gl::STENCIL_BACK_PASS_DEPTH_FAIL | - gl::STENCIL_BACK_PASS_DEPTH_PASS | - gl::STENCIL_BACK_REF | - gl::STENCIL_BACK_VALUE_MASK | - gl::STENCIL_BACK_WRITEMASK | - gl::STENCIL_BITS | - gl::STENCIL_CLEAR_VALUE | - gl::STENCIL_FAIL | - gl::STENCIL_FUNC | - gl::STENCIL_PASS_DEPTH_FAIL | - gl::STENCIL_PASS_DEPTH_PASS | - gl::STENCIL_REF | - gl::STENCIL_VALUE_MASK | - gl::STENCIL_WRITEMASK | - gl::SUBPIXEL_BITS | - gl::UNPACK_ALIGNMENT => - //gl::UNPACK_COLORSPACE_CONVERSION_WEBGL => - Ok(WebGLParameter::Int(gl.get_integer_v(param_id))), - - gl::BLEND | - gl::CULL_FACE | - gl::DEPTH_TEST | - gl::DEPTH_WRITEMASK | - gl::DITHER | - gl::POLYGON_OFFSET_FILL | - gl::SAMPLE_COVERAGE_INVERT | - gl::STENCIL_TEST => - //gl::UNPACK_FLIP_Y_WEBGL | - //gl::UNPACK_PREMULTIPLY_ALPHA_WEBGL => - Ok(WebGLParameter::Bool(gl.get_boolean_v(param_id) != 0)), - - gl::DEPTH_CLEAR_VALUE | - gl::LINE_WIDTH | - gl::POLYGON_OFFSET_FACTOR | - gl::POLYGON_OFFSET_UNITS | - gl::SAMPLE_COVERAGE_VALUE => - Ok(WebGLParameter::Float(gl.get_float_v(param_id))), - - gl::VERSION => Ok(WebGLParameter::String("WebGL 1.0".to_owned())), - gl::RENDERER | - gl::VENDOR => Ok(WebGLParameter::String("Mozilla/Servo".to_owned())), - gl::SHADING_LANGUAGE_VERSION => Ok(WebGLParameter::String("WebGL GLSL ES 1.0".to_owned())), - - // TODO(zbarsky, emilio): Implement support for the following valid parameters - // Float32Array - gl::ALIASED_LINE_WIDTH_RANGE | - //gl::ALIASED_POINT_SIZE_RANGE | - //gl::BLEND_COLOR | - gl::COLOR_CLEAR_VALUE | - gl::DEPTH_RANGE | - - // WebGLBuffer - gl::ARRAY_BUFFER_BINDING | - gl::ELEMENT_ARRAY_BUFFER_BINDING | - - // WebGLFrameBuffer - gl::FRAMEBUFFER_BINDING | - - // WebGLRenderBuffer - gl::RENDERBUFFER_BINDING | - - // WebGLProgram - gl::CURRENT_PROGRAM | - - // WebGLTexture - gl::TEXTURE_BINDING_2D | - gl::TEXTURE_BINDING_CUBE_MAP | - - // sequence - gl::COLOR_WRITEMASK | - - // Uint32Array - gl::COMPRESSED_TEXTURE_FORMATS | - - // Int32Array - gl::MAX_VIEWPORT_DIMS | - gl::SCISSOR_BOX | - gl::VIEWPORT => Err(WebGLError::InvalidEnum), - - // Invalid parameters - _ => Err(WebGLError::InvalidEnum) - }; - - chan.send(result).unwrap(); - } - - fn finish(gl: &gl::Gl, chan: MsgSender<()>) { - gl.finish(); - chan.send(()).unwrap(); - } - - fn vertex_attrib(gl: &gl::Gl, - index: u32, - pname: u32, - chan: MsgSender>) { - let result = if index >= gl.get_integer_v(gl::MAX_VERTEX_ATTRIBS) as u32 { - Err(WebGLError::InvalidValue) - } else { - match pname { - gl::VERTEX_ATTRIB_ARRAY_ENABLED | - gl::VERTEX_ATTRIB_ARRAY_NORMALIZED => - Ok(WebGLParameter::Bool(gl.get_vertex_attrib_iv(index, pname) != 0)), - gl::VERTEX_ATTRIB_ARRAY_SIZE | - gl::VERTEX_ATTRIB_ARRAY_STRIDE | - gl::VERTEX_ATTRIB_ARRAY_TYPE => - Ok(WebGLParameter::Int(gl.get_vertex_attrib_iv(index, pname))), - gl::CURRENT_VERTEX_ATTRIB => - Ok(WebGLParameter::FloatArray(gl.get_vertex_attrib_fv(index, pname))), - // gl::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING should return WebGLBuffer - _ => Err(WebGLError::InvalidEnum), - } - }; - - chan.send(result).unwrap(); - } - - fn vertex_attrib_offset(gl: &gl::Gl, - index: u32, - pname: u32, - chan: MsgSender>) { - let result = match pname { - gl::VERTEX_ATTRIB_ARRAY_POINTER => Ok(gl.get_vertex_attrib_pointer_v(index, pname)), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn buffer_parameter(gl: &gl::Gl, - target: u32, - param_id: u32, - chan: MsgSender>) { - let result = match param_id { - gl::BUFFER_SIZE | - gl::BUFFER_USAGE => - Ok(WebGLParameter::Int(gl.get_buffer_parameter_iv(target, param_id))), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn program_parameter(gl: &gl::Gl, - program_id: WebGLProgramId, - param_id: u32, - chan: MsgSender>) { - let result = match param_id { - gl::DELETE_STATUS | - gl::LINK_STATUS | - gl::VALIDATE_STATUS => - Ok(WebGLParameter::Bool(gl.get_program_iv(program_id.get(), param_id) != 0)), - gl::ATTACHED_SHADERS | - gl::ACTIVE_ATTRIBUTES | - gl::ACTIVE_UNIFORMS => - Ok(WebGLParameter::Int(gl.get_program_iv(program_id.get(), param_id))), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn shader_parameter(gl: &gl::Gl, - shader_id: WebGLShaderId, - param_id: u32, - chan: MsgSender>) { - let result = match param_id { - gl::SHADER_TYPE => - Ok(WebGLParameter::Int(gl.get_shader_iv(shader_id.get(), param_id))), - gl::DELETE_STATUS | - gl::COMPILE_STATUS => - Ok(WebGLParameter::Bool(gl.get_shader_iv(shader_id.get(), param_id) != 0)), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn shader_precision_format(gl: &gl::Gl, - shader_type: u32, - precision_type: u32, - chan: MsgSender>) { - - let result = match precision_type { - gl::LOW_FLOAT | - gl::MEDIUM_FLOAT | - gl::HIGH_FLOAT | - gl::LOW_INT | - gl::MEDIUM_INT | - gl::HIGH_INT => { - Ok(gl.get_shader_precision_format(shader_type, precision_type)) - }, - _=> { - Err(WebGLError::InvalidEnum) - } - }; - - chan.send(result).unwrap(); - } - - fn get_extensions(gl: &gl::Gl, chan: MsgSender) { - chan.send(gl.get_string(gl::EXTENSIONS)).unwrap(); - } - - fn uniform_location(gl: &gl::Gl, - program_id: WebGLProgramId, - name: String, - chan: MsgSender>) { - let location = gl.get_uniform_location(program_id.get(), &name); - let location = if location == -1 { - None - } else { - Some(location) - }; - - chan.send(location).unwrap(); - } - - - fn shader_info_log(gl: &gl::Gl, shader_id: WebGLShaderId, chan: MsgSender) { - let log = gl.get_shader_info_log(shader_id.get()); - chan.send(log).unwrap(); - } - - fn program_info_log(gl: &gl::Gl, program_id: WebGLProgramId, chan: MsgSender) { - let log = gl.get_program_info_log(program_id.get()); - chan.send(log).unwrap(); - } - - fn create_buffer(gl: &gl::Gl, chan: MsgSender>) { - let buffer = gl.gen_buffers(1)[0]; - let buffer = if buffer == 0 { - None - } else { - Some(unsafe { WebGLBufferId::new(buffer) }) - }; - chan.send(buffer).unwrap(); - } - - fn create_framebuffer(gl: &gl::Gl, chan: MsgSender>) { - let framebuffer = gl.gen_framebuffers(1)[0]; - let framebuffer = if framebuffer == 0 { - None - } else { - Some(unsafe { WebGLFramebufferId::new(framebuffer) }) - }; - chan.send(framebuffer).unwrap(); - } - - - fn create_renderbuffer(gl: &gl::Gl, chan: MsgSender>) { - let renderbuffer = gl.gen_renderbuffers(1)[0]; - let renderbuffer = if renderbuffer == 0 { - None - } else { - Some(unsafe { WebGLRenderbufferId::new(renderbuffer) }) - }; - chan.send(renderbuffer).unwrap(); - } - - fn create_texture(gl: &gl::Gl, chan: MsgSender>) { - let texture = gl.gen_textures(1)[0]; - let texture = if texture == 0 { - None - } else { - Some(unsafe { WebGLTextureId::new(texture) }) - }; - chan.send(texture).unwrap(); - } - - - fn create_program(gl: &gl::Gl, chan: MsgSender>) { - let program = gl.create_program(); - let program = if program == 0 { - None - } else { - Some(unsafe { WebGLProgramId::new(program) }) - }; - chan.send(program).unwrap(); - } - - fn create_shader(gl: &gl::Gl, shader_type: u32, chan: MsgSender>) { - let shader = gl.create_shader(shader_type); - let shader = if shader == 0 { - None - } else { - Some(unsafe { WebGLShaderId::new(shader) }) - }; - chan.send(shader).unwrap(); - } - - fn create_vertex_array(gl: &gl::Gl, chan: MsgSender>) { - let vao = gl.gen_vertex_arrays(1)[0]; - let vao = if vao == 0 { - None - } else { - Some(unsafe { WebGLVertexArrayId::new(vao) }) - }; - chan.send(vao).unwrap(); - } - - #[inline] - fn bind_framebuffer(gl: &gl::Gl, - target: u32, - request: WebGLFramebufferBindingRequest, - ctx: &GLContext) { - let id = match request { - WebGLFramebufferBindingRequest::Explicit(id) => id.get(), - WebGLFramebufferBindingRequest::Default => - ctx.borrow_draw_buffer().unwrap().get_framebuffer(), - }; - - gl.bind_framebuffer(target, id); - } - - - #[inline] - fn compile_shader(gl: &gl::Gl, shader_id: WebGLShaderId, source: String) { - gl.shader_source(shader_id.get(), &[source.as_bytes()]); - gl.compile_shader(shader_id.get()); - } -} diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml index 3986f7c8f66b..87a95ae2024d 100644 --- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"] license = "MPL-2.0" [dependencies] -webrender_api = {path = "../webrender_api", version = "0.48.0"} +webrender_api = {path = "../webrender_api", version = "0.49.0"} rayon = "0.8" thread_profiler = "0.1.1" euclid = "0.15" @@ -14,5 +14,5 @@ gleam = "0.4" [dependencies.webrender] path = "../webrender" -version = "0.48.0" +version = "0.49.0" default-features = false