diff --git a/Cargo.lock b/Cargo.lock index 8cfb52b789..f4354e67cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ name = "cgl" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -371,7 +371,7 @@ dependencies = [ [[package]] name = "gleam" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1021,12 +1021,13 @@ dependencies = [ "euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "ron 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1084,7 +1085,7 @@ dependencies = [ "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-loader 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1203,7 +1204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" "checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d" "checksum gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5c19cde55637681450c92f7a05ea16c78e2b6d0587e601ec1ebdab6960854b" -"checksum gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "4f756699879522bc654ecc44ad42ad14c59803c2dacfa5a67a7fc27257a8b4e9" +"checksum gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "959c818d9bbe9f7b7db55dce0bc44673c4da4f4ee122536c40550f984c3b8017" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1576ffa01849c91b484b95c01d54dddc242b4d50923eaa2d4d74a58c4b9e8fd" diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index d28c607710..c4f86cf93b 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -19,7 +19,7 @@ bincode = "0.9" byteorder = "1.0" euclid = "0.16" fxhash = "0.2.1" -gleam = "0.4.19" +gleam = "0.4.20" lazy_static = "1" log = "0.3" num-traits = "0.1.32" @@ -29,6 +29,7 @@ webrender_api = {path = "../webrender_api"} bitflags = "1.0" thread_profiler = "0.1.1" plane-split = "0.7" +png = { optional = true, version = "0.11" } smallvec = "0.6" ws = { optional = true, version = "0.7.3" } serde_json = { optional = true, version = "1.0" } diff --git a/webrender/examples/frame_output.rs b/webrender/examples/frame_output.rs index 1837ed4bca..800f4ea76c 100644 --- a/webrender/examples/frame_output.rs +++ b/webrender/examples/frame_output.rs @@ -51,10 +51,7 @@ impl webrender::OutputImageHandler for OutputHandler { impl webrender::ExternalImageHandler for ExternalHandler { fn lock(&mut self, _key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage { webrender::ExternalImage { - u0: 0.0, - v0: 0.0, - u1: 1.0, - v1: 1.0, + uv: TexelRect::new(0.0, 0.0, 1.0, 1.0), source: webrender::ExternalImageSource::NativeTexture(self.texture_id), } } @@ -77,7 +74,7 @@ impl App { ImageData::External(ExternalImageData { id: ExternalImageId(0), channel_index: 0, - image_type: ExternalImageType::Texture2DHandle, + image_type: ExternalImageType::TextureHandle(TextureTarget::Default), }), None, ); diff --git a/webrender/examples/texture_cache_stress.rs b/webrender/examples/texture_cache_stress.rs index 8813c54dfc..c6d842cf97 100644 --- a/webrender/examples/texture_cache_stress.rs +++ b/webrender/examples/texture_cache_stress.rs @@ -21,7 +21,7 @@ struct ImageGenerator { } impl ImageGenerator { - fn new() -> ImageGenerator { + fn new() -> Self { ImageGenerator { next_pattern: 0, patterns: [ @@ -63,10 +63,7 @@ impl webrender::ExternalImageHandler for ImageGenerator { fn lock(&mut self, _key: ExternalImageId, channel_index: u8) -> webrender::ExternalImage { self.generate_image(channel_index as u32); webrender::ExternalImage { - u0: 0.0, - v0: 0.0, - u1: 1.0, - v1: 1.0, + uv: TexelRect::new(0.0, 0.0, 1.0, 1.0), source: webrender::ExternalImageSource::RawData(&self.current_image), } } @@ -245,7 +242,7 @@ impl Example for App { let image_data = ExternalImageData { id: ExternalImageId(0), channel_index: size as u8, - image_type: ExternalImageType::ExternalBuffer, + image_type: ExternalImageType::Buffer, }; updates.add_image( @@ -290,7 +287,7 @@ impl Example for App { fn get_image_handlers( &mut self, _gl: &gl::Gl, - ) -> (Option>, + ) -> (Option>, Option>) { (Some(Box::new(ImageGenerator::new())), None) } diff --git a/webrender/res/brush_picture.glsl b/webrender/res/brush_picture.glsl index 361276a2b5..ab3d526248 100644 --- a/webrender/res/brush_picture.glsl +++ b/webrender/res/brush_picture.glsl @@ -63,13 +63,13 @@ void brush_vs( vec2 uv1 = uv0 + blur_task.common_data.task_rect.size; #else Picture pic = fetch_picture(prim_address); - ImageResource uv_rect = fetch_image_resource(user_data.x); + ImageResource res = fetch_image_resource(user_data.x); vec2 texture_size = vec2(textureSize(sColor1, 0).xy); vColor = pic.color; - vec2 uv0 = uv_rect.uv_rect.xy; - vec2 uv1 = uv_rect.uv_rect.zw; - vec2 src_size = (uv1 - uv0) * uv_rect.user_data.x; - vUv.z = uv_rect.layer; + vec2 uv0 = res.uv_rect.p0; + vec2 uv1 = res.uv_rect.p1; + vec2 src_size = (uv1 - uv0) * res.user_data.x; + vUv.z = res.layer; #endif // TODO(gw): In the future we'll probably draw these as segments diff --git a/webrender/res/cs_clip_image.glsl b/webrender/res/cs_clip_image.glsl index d8d703f16c..936aad5641 100644 --- a/webrender/res/cs_clip_image.glsl +++ b/webrender/res/cs_clip_image.glsl @@ -38,9 +38,9 @@ void main(void) { vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0); vec2 texture_size = vec2(textureSize(sColor0, 0)); - vClipMaskUvRect = vec4(res.uv_rect.xy, res.uv_rect.zw - res.uv_rect.xy) / texture_size.xyxy; + vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy; // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside - vec4 inner_rect = vec4(res.uv_rect.xy, res.uv_rect.zw); + vec4 inner_rect = vec4(res.uv_rect.p0, res.uv_rect.p1); vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } #endif diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 0191ef06cf..15952b93ab 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -695,7 +695,7 @@ GlyphResource fetch_glyph_resource(int address) { } struct ImageResource { - vec4 uv_rect; + RectWithEndpoint uv_rect; float layer; vec3 user_data; }; @@ -703,12 +703,14 @@ struct ImageResource { ImageResource fetch_image_resource(int address) { //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT` vec4 data[2] = fetch_from_resource_cache_2(address); - return ImageResource(data[0], data[1].x, data[1].yzw); + RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw); + return ImageResource(uv_rect, data[1].x, data[1].yzw); } ImageResource fetch_image_resource_direct(ivec2 address) { vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return ImageResource(data[0], data[1].x, data[1].yzw); + RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw); + return ImageResource(uv_rect, data[1].x, data[1].yzw); } struct TextRun { diff --git a/webrender/res/ps_image.glsl b/webrender/res/ps_image.glsl index fe36b9aaf1..0217d01708 100644 --- a/webrender/res/ps_image.glsl +++ b/webrender/res/ps_image.glsl @@ -53,11 +53,11 @@ void main(void) { vec2 uv0, uv1; if (image.sub_rect.x < 0.0) { - uv0 = res.uv_rect.xy; - uv1 = res.uv_rect.zw; + uv0 = res.uv_rect.p0; + uv1 = res.uv_rect.p1; } else { - uv0 = res.uv_rect.xy + image.sub_rect.xy; - uv1 = res.uv_rect.xy + image.sub_rect.zw; + uv0 = res.uv_rect.p0 + image.sub_rect.xy; + uv1 = res.uv_rect.p0 + image.sub_rect.zw; } // vUv will contain how many times this image has wrapped around the image size. diff --git a/webrender/res/ps_yuv_image.glsl b/webrender/res/ps_yuv_image.glsl index e8a51016e6..093a55083b 100644 --- a/webrender/res/ps_yuv_image.glsl +++ b/webrender/res/ps_yuv_image.glsl @@ -70,8 +70,8 @@ void main(void) { #else vec2 y_texture_size_normalization_factor = vec2(textureSize(sColor0, 0)); #endif - vec2 y_st0 = y_rect.uv_rect.xy / y_texture_size_normalization_factor; - vec2 y_st1 = y_rect.uv_rect.zw / y_texture_size_normalization_factor; + vec2 y_st0 = y_rect.uv_rect.p0 / y_texture_size_normalization_factor; + vec2 y_st1 = y_rect.uv_rect.p1 / y_texture_size_normalization_factor; vTextureSizeY = y_st1 - y_st0; vTextureOffsetY = y_st0; @@ -83,11 +83,11 @@ void main(void) { #else vec2 uv_texture_size_normalization_factor = vec2(textureSize(sColor1, 0)); #endif - vec2 u_st0 = u_rect.uv_rect.xy / uv_texture_size_normalization_factor; - vec2 u_st1 = u_rect.uv_rect.zw / uv_texture_size_normalization_factor; + vec2 u_st0 = u_rect.uv_rect.p0 / uv_texture_size_normalization_factor; + vec2 u_st1 = u_rect.uv_rect.p1 / uv_texture_size_normalization_factor; #ifndef WR_FEATURE_NV12 - vec2 v_st0 = v_rect.uv_rect.xy / uv_texture_size_normalization_factor; + vec2 v_st0 = v_rect.uv_rect.p0 / uv_texture_size_normalization_factor; #endif vTextureSizeUv = u_st1 - u_st0; diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index fa58e3319d..0178b1a4fa 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -547,6 +547,24 @@ impl AlphaBatcher { } } + fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind { + match texture { + SourceTexture::External(ext_image) => { + match ext_image.image_type { + ExternalImageType::TextureHandle(target) => { + target.into() + } + ExternalImageType::Buffer => { + // The ExternalImageType::Buffer should be handled by resource_cache. + // It should go through the non-external case. + panic!("Unexpected non-texture handle type"); + } + } + } + _ => ImageBufferKind::Texture2DArray, + } + } + // Adds a primitive to a batch. // It can recursively call itself in some situations, for // example if it encounters a picture where the items @@ -691,45 +709,17 @@ impl AlphaBatcher { return; } - let batch_kind = match color_texture_id { - SourceTexture::External(ext_image) => { - match ext_image.image_type { - ExternalImageType::Texture2DHandle => { - TransformBatchKind::Image(ImageBufferKind::Texture2D) - } - ExternalImageType::Texture2DArrayHandle => { - TransformBatchKind::Image(ImageBufferKind::Texture2DArray) - } - ExternalImageType::TextureRectHandle => { - TransformBatchKind::Image(ImageBufferKind::TextureRect) - } - ExternalImageType::TextureExternalHandle => { - TransformBatchKind::Image(ImageBufferKind::TextureExternal) - } - ExternalImageType::ExternalBuffer => { - // The ExternalImageType::ExternalBuffer should be handled by resource_cache. - // It should go through the non-external case. - panic!( - "Non-texture handle type should be handled in other way" - ); - } - } - } - _ => TransformBatchKind::Image(ImageBufferKind::Texture2DArray), - }; - - let textures = BatchTextures { - colors: [ - color_texture_id, - SourceTexture::Invalid, - SourceTexture::Invalid, - ], - }; - + let batch_kind = TransformBatchKind::Image(Self::get_buffer_kind(color_texture_id)); let key = BatchKey::new( BatchKind::Transformable(transform_kind, batch_kind), blend_mode, - textures, + BatchTextures { + colors: [ + color_texture_id, + SourceTexture::Invalid, + SourceTexture::Invalid, + ], + }, ); let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect); batch.push(base_instance.build(uv_address.as_int(gpu_cache), 0, 0)); @@ -1182,39 +1172,12 @@ impl AlphaBatcher { uv_rect_addresses[channel] = address.as_int(gpu_cache); } - let get_buffer_kind = |texture: SourceTexture| { - match texture { - 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 => { - // The ExternalImageType::ExternalBuffer should be handled by resource_cache. - // It should go through the non-external case. - panic!("Unexpected non-texture handle type"); - } - } - } - _ => ImageBufferKind::Texture2DArray, - } - }; - // All yuv textures should be the same type. - let buffer_kind = get_buffer_kind(textures.colors[0]); + let buffer_kind = Self::get_buffer_kind(textures.colors[0]); assert!( textures.colors[1 .. image_yuv_cpu.format.get_plane_num()] .iter() - .all(|&tid| buffer_kind == get_buffer_kind(tid)) + .all(|&tid| buffer_kind == Self::get_buffer_kind(tid)) ); let kind = BatchKind::Transformable( diff --git a/webrender/src/border.rs b/webrender/src/border.rs index c641b538aa..677b76b0b0 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -3,14 +3,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF}; -use api::{LayerPoint, LayerRect}; -use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode}; +use api::{LayerPoint, LayerRect, LayerPrimitiveInfo, LayerSize}; +use api::{NormalBorder, RepeatMode, TexelRect}; use clip::ClipSource; use ellipse::Ellipse; use frame_builder::FrameBuilder; use gpu_cache::GpuDataRequest; use prim_store::{BorderPrimitiveCpu, BrushSegment, BrushSegmentDescriptor}; -use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer, TexelRect}; +use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer}; use util::{lerp, pack_as_float}; #[repr(u8)] diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index cedbe6b040..5a235a2a53 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -6,7 +6,9 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use api::{CaptureBits, ExternalImageData, ImageDescriptor}; +use api::{CaptureBits, ExternalImageData, ExternalImageId, ImageDescriptor, TexelRect}; +#[cfg(feature = "png")] +use device::ReadPixelsFormat; use ron::{de, ser}; use serde::{Deserialize, Serialize}; @@ -58,6 +60,39 @@ impl CaptureConfig { Some(de::from_str(&string) .unwrap()) } + + #[cfg(feature = "png")] + pub fn save_png( + path: PathBuf, size: (u32, u32), format: ReadPixelsFormat, data: &[u8], + ) { + use api::ImageFormat; + use png::{BitDepth, ColorType, Encoder, HasParameters}; + use std::io::BufWriter; + + let color_type = match format { + ReadPixelsFormat::Rgba8 => ColorType::RGBA, + ReadPixelsFormat::Standard(ImageFormat::BGRA8) => { + warn!("Unable to swizzle PNG of BGRA8 type"); + ColorType::RGBA + }, + ReadPixelsFormat::Standard(ImageFormat::R8) => ColorType::Grayscale, + ReadPixelsFormat::Standard(ImageFormat::RG8) => ColorType::GrayscaleAlpha, + ReadPixelsFormat::Standard(fm) => { + error!("Unable to save PNG of {:?}", fm); + return; + } + }; + let w = BufWriter::new(File::create(path).unwrap()); + let mut enc = Encoder::new(w, size.0, size.1); + enc + .set(color_type) + .set(BitDepth::Eight); + enc + .write_header() + .unwrap() + .write_image_data(&data) + .unwrap(); + } } #[derive(Deserialize, Serialize)] @@ -66,3 +101,15 @@ pub struct ExternalCaptureImage { pub descriptor: ImageDescriptor, pub external: ExternalImageData, } + +#[derive(Deserialize, Serialize)] +pub struct PlainExternalImage { + /// Path to the RON file describing the texel data. + pub data: String, + /// Public ID of the external image. + pub id: ExternalImageId, + /// Channel index of an external image. + pub channel_index: u8, + /// UV sub-rectangle of the image. + pub uv: TexelRect, +} diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index 651bcf3654..8d23f01e38 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -2,10 +2,10 @@ * 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::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat}; +use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat, TextureTarget}; use debug_font_data; use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO}; -use device::{TextureFilter, TextureTarget, VertexAttribute, VertexAttributeKind, VertexUsageHint}; +use device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint}; use euclid::{Point2D, Rect, Size2D, Transform3D}; use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE}; use std::f32; diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 3046583f3a..883521885e 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -5,6 +5,7 @@ use super::shader_source; use api::{ColorF, ImageDescriptor, ImageFormat}; use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize}; +use api::TextureTarget; use euclid::Transform3D; use gleam::gl; use internal_types::{FastHashMap, RenderTargetInfo}; @@ -26,7 +27,7 @@ use std::thread; pub struct FrameId(usize); impl FrameId { - pub fn new(value: usize) -> FrameId { + pub fn new(value: usize) -> Self { FrameId(value) } } @@ -62,25 +63,6 @@ pub enum DepthFunction { LessEqual = gl::LEQUAL, } -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum TextureTarget { - Default, - Array, - Rect, - External, -} - -impl TextureTarget { - pub fn to_gl_target(&self) -> gl::GLuint { - match *self { - TextureTarget::Default => gl::TEXTURE_2D, - TextureTarget::Array => gl::TEXTURE_2D_ARRAY, - TextureTarget::Rect => gl::TEXTURE_RECTANGLE, - TextureTarget::External => gl::TEXTURE_EXTERNAL_OES, - } - } -} - #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum TextureFilter { @@ -130,6 +112,15 @@ pub enum ReadPixelsFormat { Rgba8, } +pub fn get_gl_target(target: TextureTarget) -> gl::GLuint { + match target { + TextureTarget::Default => gl::TEXTURE_2D, + TextureTarget::Array => gl::TEXTURE_2D_ARRAY, + TextureTarget::Rect => gl::TEXTURE_RECTANGLE, + TextureTarget::External => gl::TEXTURE_EXTERNAL_OES, + } +} + pub fn get_gl_format_bgra(gl: &gl::Gl) -> gl::GLuint { match gl.get_type() { gl::GlType::Gl => GL_FORMAT_BGRA_GL, @@ -423,9 +414,14 @@ impl ExternalTexture { pub fn new(id: u32, target: TextureTarget) -> Self { ExternalTexture { id, - target: target.to_gl_target(), + target: get_gl_target(target), } } + + #[cfg(feature = "capture")] + pub fn internal_id(&self) -> gl::GLuint { + self.id + } } pub struct Texture { @@ -930,7 +926,7 @@ impl Device { ) -> Texture { Texture { id: self.gl.gen_textures(1)[0], - target: target.to_gl_target(), + target: get_gl_target(target), width: 0, height: 0, layer_count: 0, @@ -1511,7 +1507,7 @@ impl Device { ) } - /// Read rectangle of RGBA8 or BGRA8 pixels into the specified output slice. + /// Read rectangle of pixels into the specified output slice. pub fn read_pixels_into( &mut self, rect: DeviceUintRect, @@ -1545,6 +1541,24 @@ impl Device { ); } + /// Get texels of a texture into the specified output slice. + pub fn get_tex_image_into( + &mut self, + texture: &Texture, + format: ImageFormat, + output: &mut [u8], + ) { + self.bind_texture(DEFAULT_TEXTURE, texture); + let desc = gl_describe_format(self.gl(), format); + self.gl.get_tex_image_into_buffer( + texture.target, + 0, + desc.external, + desc.pixel_type, + output, + ); + } + /// Attaches the provided texture to the current Read FBO binding. fn attach_read_texture_raw( &mut self, texture_id: gl::GLuint, target: gl::GLuint, layer_id: i32 @@ -1575,7 +1589,7 @@ impl Device { pub fn attach_read_texture_external( &mut self, texture_id: gl::GLuint, target: TextureTarget, layer_id: i32 ) { - self.attach_read_texture_raw(texture_id, target.to_gl_target(), layer_id) + self.attach_read_texture_raw(texture_id, get_gl_target(target), layer_id) } pub fn attach_read_texture(&mut self, texture: &Texture, layer_id: i32) { diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 481f54a9b6..014b28e44f 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -419,8 +419,8 @@ impl<'a> FlattenContext<'a> { self.builder.add_image( clip_and_scroll, &prim_info, - &info.stretch_size, - &info.tile_spacing, + info.stretch_size, + info.tile_spacing, None, info.image_key, info.image_rendering, @@ -934,8 +934,8 @@ impl<'a> FlattenContext<'a> { self.builder.add_image( clip_and_scroll, &prim_info, - &stretched_size, - &info.tile_spacing, + stretched_size, + info.tile_spacing, None, info.image_key, info.image_rendering, @@ -1101,6 +1101,8 @@ impl FrameContext { RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame) } + //TODO: this can probably be simplified if `build()` is called directly by RB. + // The only things it needs from the frame context is the CST and frame ID. pub fn build_rendered_document( &mut self, frame_builder: &mut FrameBuilder, diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index f48771095c..09fd4b87d5 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -9,8 +9,8 @@ use api::{GradientStop, HitTestFlags, HitTestItem, HitTestResult, ImageKey, Imag use api::{ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize}; use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation}; use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode}; -use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle, WorldPoint, YuvColorSpace}; -use api::YuvData; +use api::{ScrollSensitivity, Shadow, TexelRect, TileOffset, TransformStyle, WorldPoint}; +use api::{YuvColorSpace, YuvData}; use app_units::Au; use border::ImageBorderSegment; use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains}; @@ -23,7 +23,7 @@ use gpu_cache::GpuCache; use gpu_types::{ClipScrollNodeData, PictureType}; use internal_types::{FastHashMap, FastHashSet, RenderPassIndex}; use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface}; -use prim_store::{BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu}; +use prim_store::{BrushKind, BrushPrimitive, YuvImagePrimitiveCpu}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, PrimitiveKind}; use prim_store::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu}; @@ -1094,8 +1094,8 @@ impl FrameBuilder { self.add_image( clip_and_scroll, &info, - &segment.stretch_size, - &segment.tile_spacing, + segment.stretch_size, + segment.tile_spacing, Some(segment.sub_rect), border.image_key, ImageRendering::Auto, @@ -1422,25 +1422,21 @@ impl FrameBuilder { &mut self, clip_and_scroll: ClipAndScrollInfo, info: &LayerPrimitiveInfo, - stretch_size: &LayerSize, - tile_spacing: &LayerSize, + stretch_size: LayerSize, + mut tile_spacing: LayerSize, sub_rect: Option, image_key: ImageKey, image_rendering: ImageRendering, alpha_type: AlphaType, tile: Option, ) { - let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into(); - // If the tile spacing is the same as the rect size, // then it is effectively zero. We use this later on // in prim_store to detect if an image can be considered // opaque. - let tile_spacing = if *tile_spacing == info.rect.size { - LayerSize::zero() - } else { - *tile_spacing - }; + if tile_spacing == info.rect.size { + tile_spacing = LayerSize::zero(); + } let prim_cpu = ImagePrimitiveCpu { image_key, @@ -1448,15 +1444,8 @@ impl FrameBuilder { tile_offset: tile, tile_spacing, alpha_type, - gpu_blocks: [ - [ - stretch_size.width, - stretch_size.height, - tile_spacing.width, - tile_spacing.height, - ].into(), - sub_rect_block, - ], + stretch_size, + texel_rect: sub_rect.unwrap_or(TexelRect::invalid()), }; self.add_primitive( @@ -1477,9 +1466,9 @@ impl FrameBuilder { ) { let format = yuv_data.get_format(); let yuv_key = match yuv_data { - YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::dummy()], + YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY], YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2], - YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::dummy(), ImageKey::dummy()], + YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY], }; let prim_cpu = YuvImagePrimitiveCpu { diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index d59487d597..ae70b63aa0 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -24,9 +24,9 @@ //! address in the GPU cache of a given resource slot //! for this frame. -use api::{LayerRect, PremultipliedColorF}; +use api::{PremultipliedColorF, TexelRect}; use device::FrameId; -use internal_types::UvRect; +use euclid::TypedRect; use profiler::GpuCacheProfileCounters; use renderer::MAX_VERTEX_TEXTURE_WIDTH; use std::{mem, u16, u32}; @@ -58,51 +58,50 @@ struct CacheLocation { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GpuBlockData { - pub data: [f32; 4], + data: [f32; 4], } impl GpuBlockData { - pub fn empty() -> Self { - GpuBlockData { data: [0.0; 4] } - } + pub const EMPTY: Self = GpuBlockData { data: [0.0; 4] }; } /// Conversion helpers for GpuBlockData -impl Into for PremultipliedColorF { - fn into(self) -> GpuBlockData { +impl From for GpuBlockData { + fn from(c: PremultipliedColorF) -> Self { GpuBlockData { - data: [self.r, self.g, self.b, self.a], + data: [c.r, c.g, c.b, c.a], } } } -impl Into for [f32; 4] { - fn into(self) -> GpuBlockData { - GpuBlockData { data: self } +impl From<[f32; 4]> for GpuBlockData { + fn from(data: [f32; 4]) -> Self { + GpuBlockData { data } } } -impl Into for LayerRect { - fn into(self) -> GpuBlockData { +impl

From> for GpuBlockData { + fn from(r: TypedRect) -> Self { GpuBlockData { data: [ - self.origin.x, - self.origin.y, - self.size.width, - self.size.height, + r.origin.x, + r.origin.y, + r.size.width, + r.size.height, ], } } } -impl Into for UvRect { - fn into(self) -> GpuBlockData { +impl From for GpuBlockData { + fn from(tr: TexelRect) -> Self { GpuBlockData { - data: [self.uv0.x, self.uv0.y, self.uv1.x, self.uv1.y], + data: [tr.uv0.x, tr.uv0.y, tr.uv1.x, tr.uv1.y], } } } + // Any data type that can be stored in the GPU cache should // implement this trait. pub trait ToGpuBlocks { @@ -222,6 +221,7 @@ pub enum GpuCacheUpdate { }, } +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GpuCacheUpdateList { // The current height of the texture. The render thread // should resize the texture if required. diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index e203f72cb3..ae335e3deb 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.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::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch}; +use api::{ClipId, DeviceUintRect, DocumentId, Epoch}; use api::{ExternalImageData, ExternalImageId}; use api::{ImageFormat, PipelineId}; use api::DebugCommand; @@ -17,7 +17,7 @@ use std::path::PathBuf; use std::sync::Arc; #[cfg(feature = "capture")] -use capture::{CaptureConfig, ExternalCaptureImage}; +use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; use tiling; pub type FastHashMap = HashMap>; @@ -154,7 +154,7 @@ pub enum DebugOutput { #[cfg(feature = "capture")] SaveCapture(CaptureConfig, Vec), #[cfg(feature = "capture")] - LoadCapture(PathBuf), + LoadCapture(PathBuf, Vec), } pub enum ResultMsg { @@ -172,9 +172,3 @@ pub enum ResultMsg { cancel_rendering: bool, }, } - -#[derive(Clone, Copy, Debug)] -pub struct UvRect { - pub uv0: DevicePoint, - pub uv1: DevicePoint, -} diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index c5decf1879..a396bb1132 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -158,6 +158,8 @@ extern crate ws; extern crate image; #[cfg(feature = "debugger")] extern crate base64; +#[cfg(all(feature = "capture", feature = "png"))] +extern crate png; pub extern crate webrender_api; diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 7837b35078..66c3ca91e3 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -3,12 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode}; -use api::{ColorF, ColorU, DeviceIntRect, DevicePixelScale, DevicePoint}; +use api::{ColorF, ColorU, DeviceIntRect, DevicePixelScale}; use api::{ComplexClipRegion, ExtendMode, FontRenderMode}; use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation}; -use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTransform}; -use api::{YuvColorSpace, YuvFormat}; +use api::{LineStyle, PipelineId, PremultipliedColorF, TexelRect, TileOffset}; +use api::{WorldToLayerTransform, YuvColorSpace, YuvFormat}; use border::{BorderCornerInstance, BorderEdgeKind}; use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree}; use clip_scroll_node::ClipScrollNode; @@ -23,7 +23,7 @@ use picture::{PictureKind, PicturePrimitive}; use profiler::FrameProfileCounters; use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem}; use render_task::{RenderTask, RenderTaskId, RenderTaskTree}; -use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH}; +use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ResourceCache}; use scene::{ScenePipeline, SceneProperties}; use segment::SegmentBuilder; @@ -88,41 +88,6 @@ pub struct PrimitiveRunLocalRect { pub local_rect_in_original_parent_space: LayerRect, } -/// Stores two coordinates in texel space. The coordinates -/// are stored in texel coordinates because the texture atlas -/// may grow. Storing them as texel coords and normalizing -/// the UVs in the vertex shader means nothing needs to be -/// updated on the CPU when the texture size changes. -#[derive(Copy, Clone, Debug)] -pub struct TexelRect { - pub uv0: DevicePoint, - pub uv1: DevicePoint, -} - -impl TexelRect { - pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> TexelRect { - TexelRect { - uv0: DevicePoint::new(u0, v0), - uv1: DevicePoint::new(u1, v1), - } - } - - pub fn invalid() -> TexelRect { - TexelRect { - uv0: DevicePoint::new(-1.0, -1.0), - uv1: DevicePoint::new(-1.0, -1.0), - } - } -} - -impl Into for TexelRect { - fn into(self) -> GpuBlockData { - GpuBlockData { - data: [self.uv0.x, self.uv0.y, self.uv1.x, self.uv1.y], - } - } -} - /// For external images, it's not possible to know the /// UV coords of the image (or the image data itself) /// until the render thread receives the frame and issues @@ -361,13 +326,18 @@ pub struct ImagePrimitiveCpu { pub tile_offset: Option, pub tile_spacing: LayerSize, pub alpha_type: AlphaType, - // TODO(gw): Build on demand - pub gpu_blocks: [GpuBlockData; BLOCKS_PER_UV_RECT], + pub stretch_size: LayerSize, + pub texel_rect: TexelRect, } impl ToGpuBlocks for ImagePrimitiveCpu { fn write_gpu_blocks(&self, mut request: GpuDataRequest) { - request.extend_from_slice(&self.gpu_blocks); + // has to match BLOCKS_PER_UV_RECT + request.push([ + self.stretch_size.width, self.stretch_size.height, + self.tile_spacing.width, self.tile_spacing.height, + ]); + request.push(self.texel_rect); } } @@ -708,7 +678,7 @@ impl TextRunPrimitiveCpu { // TODO(gw): If we support chunks() on AuxIter // in the future, this code below could // be much simpler... - let mut gpu_block = GpuBlockData::empty(); + let mut gpu_block = [0.0; 4]; for (i, src) in src_glyphs.enumerate() { let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir); self.glyph_keys.push(key); @@ -716,19 +686,19 @@ impl TextRunPrimitiveCpu { // Two glyphs are packed per GPU block. if (i & 1) == 0 { - gpu_block.data[0] = src.point.x; - gpu_block.data[1] = src.point.y; + gpu_block[0] = src.point.x; + gpu_block[1] = src.point.y; } else { - gpu_block.data[2] = src.point.x; - gpu_block.data[3] = src.point.y; - self.glyph_gpu_blocks.push(gpu_block); + gpu_block[2] = src.point.x; + gpu_block[3] = src.point.y; + self.glyph_gpu_blocks.push(gpu_block.into()); } } // Ensure the last block is added in the case // of an odd number of glyphs. if (self.glyph_keys.len() & 1) != 0 { - self.glyph_gpu_blocks.push(gpu_block); + self.glyph_gpu_blocks.push(gpu_block.into()); } } @@ -739,9 +709,7 @@ impl TextRunPrimitiveCpu { request.push(self.get_color().premultiplied()); // this is the only case where we need to provide plain color to GPU let bg_color = ColorF::from(self.font.bg_color); - request.extend_from_slice(&[ - GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] } - ]); + request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]); request.push([ self.offset.x, self.offset.y, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index b108019e3e..52ac4db414 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -580,16 +580,14 @@ impl RenderBackend { DebugCommand::LoadCapture(root, tx) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; - let msg = ResultMsg::DebugOutput( - DebugOutput::LoadCapture(root.clone()) - ); - self.result_tx.send(msg).unwrap(); + self.load_capture(&root, &mut profile_counters); for (id, doc) in &self.documents { let captured = CapturedDocument { document_id: *id, root_pipeline_id: doc.scene.root_pipeline_id, + window_size: doc.view.window_size, }; tx.send(captured).unwrap(); } @@ -888,7 +886,11 @@ impl RenderBackend { // rather explicitly on what's used before and after scene building // so that, for example, we never miss anything in the code below: - self.resource_cache.load_capture(backend.resources, caches_maybe,root); + let deferred = self.resource_cache.load_capture(backend.resources, caches_maybe, root); + let msg_load = ResultMsg::DebugOutput( + DebugOutput::LoadCapture(root.clone(), deferred) + ); + self.result_tx.send(msg_load).unwrap(); self.gpu_cache = match CaptureConfig::deserialize::(root, "gpu_cache") { Some(gpu_cache) => gpu_cache, @@ -931,13 +933,13 @@ impl RenderBackend { } }; - let msg = ResultMsg::PublishDocument( + let msg_publish = ResultMsg::PublishDocument( id, render_doc, self.resource_cache.pending_updates(), profile_counters.clone(), ); - self.result_tx.send(msg).unwrap(); + self.result_tx.send(msg_publish).unwrap(); profile_counters.reset(); self.notifier.new_document_ready(id, false, true); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index b9fa579a7c..f226d7cea8 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -11,8 +11,9 @@ use api::{BlobImageRenderer, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId}; -use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId, RenderApiSender}; -use api::{RenderNotifier, YUV_COLOR_SPACES, YUV_FORMATS, YuvColorSpace, YuvFormat, channel}; +use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId}; +use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget, YuvColorSpace, YuvFormat}; +use api::{YUV_COLOR_SPACES, YUV_FORMATS, channel}; #[cfg(not(feature = "debugger"))] use api::ApiMsg; use api::DebugCommand; @@ -21,7 +22,7 @@ use api::channel::MsgSender; use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind}; use batch::{BrushImageSourceKind, TransformBatchKind}; #[cfg(feature = "capture")] -use capture::{CaptureConfig, ExternalCaptureImage}; +use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; use debug_colors; use debug_render::DebugRenderer; #[cfg(feature = "debugger")] @@ -29,7 +30,7 @@ use debug_server::{self, DebugServer}; use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, VertexDescriptor, PBO}; use device::{ExternalTexture, FBOId, TextureSlot, VertexAttribute, VertexAttributeKind}; -use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget, +use device::{FileWatcherHandler, ShaderError, TextureFilter, VertexUsageHint, VAO, VBO, CustomVAO}; use device::{ProgramCache, ReadPixelsFormat}; use euclid::{rect, Transform3D}; @@ -490,6 +491,18 @@ pub enum ImageBufferKind { Texture2DArray = 3, } +//TODO: those types are the same, so let's merge them +impl From for ImageBufferKind { + fn from(target: TextureTarget) -> Self { + match target { + TextureTarget::Default => ImageBufferKind::Texture2D, + TextureTarget::Rect => ImageBufferKind::TextureRect, + TextureTarget::Array => ImageBufferKind::Texture2DArray, + TextureTarget::External => ImageBufferKind::TextureExternal, + } + } +} + pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [ ImageBufferKind::Texture2D, ImageBufferKind::TextureRect, @@ -960,7 +973,7 @@ impl CacheTexture { rows.push(CacheRow::new()); // Add enough GPU blocks for this row. cpu_blocks - .extend_from_slice(&[GpuBlockData::empty(); MAX_VERTEX_TEXTURE_WIDTH]); + .extend_from_slice(&[GpuBlockData::EMPTY; MAX_VERTEX_TEXTURE_WIDTH]); } // This row is dirty (needs to be updated in GPU texture). @@ -1526,16 +1539,6 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result Option { - Some(match ext_type { - ExternalImageType::Texture2DHandle => TextureTarget::Default, - ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, - ExternalImageType::TextureRectHandle => TextureTarget::Rect, - ExternalImageType::TextureExternalHandle => TextureTarget::External, - ExternalImageType::ExternalBuffer => return None, - }) -} - struct FileWatcher { notifier: Box, result_tx: Sender, @@ -2424,9 +2427,9 @@ impl Renderer { self.save_capture(config, deferred); } #[cfg(feature = "capture")] - DebugOutput::LoadCapture(root) => { + DebugOutput::LoadCapture(root, deferred) => { self.active_documents.clear(); - self.load_capture(root); + self.load_capture(root, deferred); } }, ResultMsg::DebugCommand(command) => { @@ -4043,8 +4046,12 @@ impl Renderer { .external_image .expect("BUG: Deferred resolves must be external images!"); let image = handler.lock(ext_image.id, ext_image.channel_index); - let texture_target = get_external_image_target(ext_image.image_type) - .expect(&format!("{:?} is not a suitable image type in update_deferred_resolves()", ext_image.image_type)); + let texture_target = match ext_image.image_type { + ExternalImageType::TextureHandle(target) => target, + ExternalImageType::Buffer => { + panic!("{:?} is not a suitable image type in update_deferred_resolves()", ext_image.image_type); + } + }; // In order to produce the handle, the external image handler may call into // the GL context and change some states. @@ -4063,7 +4070,9 @@ impl Renderer { // Just use 0 as the gl handle for this failed case. ExternalTexture::new(0, texture_target) } - _ => panic!("No native texture found."), + ExternalImageSource::RawData(_) => { + panic!("Raw external data is not expected for deferred resolves!"); + } }; self.texture_resolver @@ -4075,7 +4084,7 @@ impl Renderer { block_count: BLOCKS_PER_UV_RECT, address: deferred_resolve.address, }); - list.blocks.push([image.u0, image.v0, image.u1, image.v1].into()); + list.blocks.push(image.uv.into()); list.blocks.push([0f32; 4].into()); } @@ -4636,10 +4645,7 @@ pub enum ExternalImageSource<'a> { /// will know to re-upload the image data to the GPU. /// Note that the UV coords are supplied in texel-space! pub struct ExternalImage<'a> { - pub u0: f32, - pub v0: f32, - pub u1: f32, - pub v1: f32, + pub uv: TexelRect, pub source: ExternalImageSource<'a>, } @@ -4786,21 +4792,27 @@ struct PlainRenderer { external_images: Vec } +#[cfg(feature = "capture")] +enum CapturedExternalImageData { + NativeTexture(gl::GLuint), + Buffer(Arc>), +} + #[cfg(feature = "capture")] struct DummyExternalImageHandler { - data: FastHashMap<(ExternalImageId, u8), Vec>, + data: FastHashMap<(ExternalImageId, u8), (CapturedExternalImageData, TexelRect)>, } #[cfg(feature = "capture")] impl ExternalImageHandler for DummyExternalImageHandler { fn lock(&mut self, key: ExternalImageId, channel_index: u8) -> ExternalImage { - let slice = &self.data[&(key, channel_index)]; + let (ref captured_data, ref uv) = self.data[&(key, channel_index)]; ExternalImage { - u0: 0.0, - v0: 0.0, - u1: 1.0, - v1: 1.0, - source: ExternalImageSource::RawData(slice), + uv: *uv, + source: match *captured_data { + CapturedExternalImageData::NativeTexture(tid) => ExternalImageSource::NativeTexture(tid), + CapturedExternalImageData::Buffer(ref arc) => ExternalImageSource::RawData(&*arc), + } } } fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {} @@ -4838,8 +4850,28 @@ impl Renderer { let bytes_per_layer = (rect.size.width * rect.size.height * bytes_per_pixel) as usize; let mut data = vec![0; bytes_per_layer]; + //TODO: instead of reading from an FBO with `read_pixels*`, we could + // read from textures directly with `get_tex_image*`. + for layer_id in 0 .. texture.get_layer_count() { device.attach_read_texture(texture, layer_id); + #[cfg(feature = "png")] + { + let mut png_data; + let (data_ref, format) = match texture.get_format() { + ImageFormat::RGBAF32 => { + png_data = vec![0; (rect.size.width * rect.size.height * 4) as usize]; + device.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut png_data); + (&png_data, ReadPixelsFormat::Rgba8) + } + fm => (&data, ReadPixelsFormat::Standard(fm)), + }; + CaptureConfig::save_png( + root.join(format!("textures/{}-{}.png", name, layer_id)), + (rect.size.width, rect.size.height), format, + data_ref, + ); + } device.read_pixels_into(rect, read_format, &mut data); file.write_all(&data) .unwrap(); @@ -4861,7 +4893,7 @@ impl Renderer { let mut texels = Vec::new(); assert_eq!(plain.format, texture.get_format()); File::open(root.join(&plain.data)) - .unwrap() + .expect(&format!("Unable to open texture at {}", plain.data)) .read_to_end(&mut texels) .unwrap(); @@ -4874,41 +4906,84 @@ impl Renderer { texels } - fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { + fn save_capture( + &mut self, + config: CaptureConfig, + deferred_images: Vec, + ) { use std::fs; use std::io::Write; use api::{CaptureBits, ExternalImageData}; self.device.begin_frame(); + let _gm = self.gpu_profile.start_marker("read GPU data"); self.device.bind_read_target_impl(self.capture.read_fbo); if !deferred_images.is_empty() { info!("saving external images"); + let mut arc_map = FastHashMap::<*const u8, String>::default(); + let mut tex_map = FastHashMap::::default(); let handler = self.external_image_handler .as_mut() .expect("Unable to lock the external image handler!"); for def in &deferred_images { + info!("\t{}", def.short_path); let ExternalImageData { id, channel_index, image_type } = def.external; - let data = match handler.lock(id, channel_index).source { - ExternalImageSource::RawData(data) => data.to_vec(), + let ext_image = handler.lock(id, channel_index); + let (data, short_path) = match ext_image.source { + ExternalImageSource::RawData(data) => { + let arc_id = arc_map.len() + 1; + match arc_map.entry(data.as_ptr()) { + Entry::Occupied(e) => { + (None, e.get().clone()) + } + Entry::Vacant(e) => { + let short_path = format!("externals/d{}.raw", arc_id); + (Some(data.to_vec()), e.insert(short_path).clone()) + } + } + } ExternalImageSource::NativeTexture(gl_id) => { - let target = get_external_image_target(image_type).unwrap(); - self.device.attach_read_texture_external(gl_id, target, 0); - self.device.read_pixels(&def.descriptor) + let tex_id = tex_map.len() + 1; + match tex_map.entry(gl_id) { + Entry::Occupied(e) => { + (None, e.get().clone()) + } + Entry::Vacant(e) => { + let target = match image_type { + ExternalImageType::TextureHandle(target) => target, + ExternalImageType::Buffer => unreachable!(), + }; + info!("\t\tnative texture of target {:?}", target); + let layer_index = 0; //TODO: what about layered textures? + self.device.attach_read_texture_external(gl_id, target, layer_index); + let data = self.device.read_pixels(&def.descriptor); + let short_path = format!("externals/t{}.raw", tex_id); + (Some(data), e.insert(short_path).clone()) + } + } } ExternalImageSource::Invalid => { - // Create a dummy buffer... - let stride = def.descriptor.compute_stride(); - let total_size = def.descriptor.height * stride; - vec![0xFF; total_size as usize] + info!("\t\tinvalid source!"); + (None, String::new()) } }; - handler.unlock(id, channel_index); - - fs::File::create(config.root.join(&def.short_path)) - .expect(&format!("Unable to create {}", def.short_path)) - .write_all(&data) - .unwrap(); + if let Some(bytes) = data { + fs::File::create(config.root.join(&short_path)) + .expect(&format!("Unable to create {}", short_path)) + .write_all(&bytes) + .unwrap(); + } + let plain = PlainExternalImage { + data: short_path, + id: def.external.id, + channel_index: def.external.channel_index, + uv: ext_image.uv, + }; + config.serialize(&plain, &def.short_path); + } + for def in &deferred_images { + handler.unlock(def.external.id, def.external.channel_index); } } @@ -4944,71 +5019,104 @@ impl Renderer { info!("done."); } - fn load_capture(&mut self, root: PathBuf) { - let renderer = match CaptureConfig::deserialize::(&root, "renderer") { - Some(r) => r, - None => return, + fn load_capture( + &mut self, root: PathBuf, external_images: Vec + ) { + use std::fs::File; + use std::io::Read; + + info!("loading external buffer-backed images"); + assert!(self.texture_resolver.external_images.is_empty()); + let mut raw_map = FastHashMap::>>::default(); + let mut image_handler = DummyExternalImageHandler { + data: FastHashMap::default(), }; + for ext in external_images { + let data = match raw_map.entry(ext.data) { + Entry::Occupied(e) => e.get().clone(), + Entry::Vacant(e) => { + let mut buffer = Vec::new(); + File::open(root.join(e.key())) + .expect(&format!("Unable to open {}", e.key())) + .read_to_end(&mut buffer) + .unwrap(); + e.insert(Arc::new(buffer)).clone() + } + }; + let key = (ext.id, ext.channel_index); + let value = (CapturedExternalImageData::Buffer(data), ext.uv); + image_handler.data.insert(key, value); + } - self.device.begin_frame(); - info!("loading cached textures"); + if let Some(renderer) = CaptureConfig::deserialize::(&root, "renderer") { + info!("loading cached textures"); + self.device.begin_frame(); - for texture in self.texture_resolver.cache_texture_map.drain(..) { - self.device.delete_texture(texture); - } - for texture in renderer.textures { - info!("\t{}", texture.data); - let mut t = self.device.create_texture(TextureTarget::Array, texture.format); - Self::load_texture(&mut t, &texture, &root, &mut self.device); - self.texture_resolver.cache_texture_map.push(t); - } + for texture in self.texture_resolver.cache_texture_map.drain(..) { + self.device.delete_texture(texture); + } + for texture in renderer.textures { + info!("\t{}", texture.data); + let mut t = self.device.create_texture(TextureTarget::Array, texture.format); + Self::load_texture(&mut t, &texture, &root, &mut self.device); + self.texture_resolver.cache_texture_map.push(t); + } - info!("loading gpu cache"); - Self::load_texture( - &mut self.gpu_cache_texture.texture, - &renderer.gpu_cache, - &root, - &mut self.device, - ); - match self.gpu_cache_texture.bus { - CacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => { - rows.clear(); - cpu_blocks.clear(); + info!("loading gpu cache"); + Self::load_texture( + &mut self.gpu_cache_texture.texture, + &renderer.gpu_cache, + &root, + &mut self.device, + ); + match self.gpu_cache_texture.bus { + CacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => { + rows.clear(); + cpu_blocks.clear(); + } + CacheBus::Scatter { .. } => {} } - CacheBus::Scatter { .. } => {} - } - info!("loading external images"); - assert!(self.texture_resolver.external_images.is_empty()); - let mut image_handler = DummyExternalImageHandler { - data: FastHashMap::default(), - }; + info!("loading external texture-backed images"); + let mut native_map = FastHashMap::::default(); + for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images { + let target = match external.image_type { + ExternalImageType::TextureHandle(target) => target, + ExternalImageType::Buffer => continue, + }; + let plain_ext = CaptureConfig::deserialize::(&root, &short_path) + .expect(&format!("Unable to read {}.ron", short_path)); + let key = (external.id, external.channel_index); + + let tid = match native_map.entry(plain_ext.data) { + Entry::Occupied(e) => e.get().clone(), + Entry::Vacant(e) => { + //TODO: provide a way to query both the layer count and the filter from external images + let (layer_count, filter) = (1, TextureFilter::Linear); + let plain_tex = PlainTexture { + data: e.key().clone(), + size: (descriptor.width, descriptor.height, layer_count), + format: descriptor.format, + filter, + render_target: None, + }; + let mut t = self.device.create_texture(target, plain_tex.format); + Self::load_texture(&mut t, &plain_tex, &root, &mut self.device); + let extex = t.into_external(); + self.capture.owned_external_images.insert(key, extex.clone()); + extex.internal_id() + } + }; - for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images { - let target = match get_external_image_target(external.image_type) { - Some(target) => target, - None => continue, - }; - //TODO: provide a way to query both the layer count and the filter from external images - let (layer_count, filter) = (1, TextureFilter::Linear); - let plain = PlainTexture { - data: short_path, - size: (descriptor.width, descriptor.height, layer_count), - format: descriptor.format, - filter, - render_target: None, - }; + let value = (CapturedExternalImageData::NativeTexture(tid), plain_ext.uv); + image_handler.data.insert(key, value); + } - let mut t = self.device.create_texture(target, plain.format); - let data = Self::load_texture(&mut t, &plain, &root, &mut self.device); - let key = (external.id, external.channel_index); - self.capture.owned_external_images.insert(key, t.into_external()); - image_handler.data.insert(key, data); + self.device.end_frame(); } - self.device.end_frame(); - self.external_image_handler = Some(Box::new(image_handler) as Box<_>); self.output_image_handler = Some(Box::new(()) as Box<_>); + self.external_image_handler = Some(Box::new(image_handler) as Box<_>); info!("done."); } } diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index fcd957c04b..963805deb7 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -15,7 +15,7 @@ use api::{TileOffset, TileSize}; use api::{NativeFontHandle}; use app_units::Au; #[cfg(feature = "capture")] -use capture::{ExternalCaptureImage}; +use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; @@ -285,7 +285,7 @@ impl ResourceCache { ImageData::External(info) => { // External handles already represent existing textures so it does // not make sense to tile them into smaller ones. - info.image_type == ExternalImageType::ExternalBuffer && size_check + info.image_type == ExternalImageType::Buffer && size_check } } } @@ -747,16 +747,11 @@ impl ResourceCache { 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, - } - } + ImageData::External(ext_image) => match ext_image.image_type { + ExternalImageType::TextureHandle(_) => Some(ext_image), + // external buffer uses resource_cache. + ExternalImageType::Buffer => None, + }, // raw and blob image are all using resource_cache. ImageData::Raw(..) | ImageData::Blob(..) => None, }; @@ -990,6 +985,7 @@ enum PlainFontTemplate { struct PlainImageTemplate { data: String, descriptor: ImageDescriptor, + epoch: Epoch, tiling: Option, } @@ -1026,6 +1022,7 @@ impl ResourceCache { pub fn save_capture( &mut self, root: &PathBuf ) -> (PlainResources, Vec) { + use device::ReadPixelsFormat; use std::fs; use std::io::Write; @@ -1046,6 +1043,10 @@ impl ResourceCache { if !path_blobs.is_dir() { fs::create_dir(&path_blobs).unwrap(); } + let path_externals = root.join("externals"); + if !path_externals.is_dir() { + fs::create_dir(&path_externals).unwrap(); + } info!("\tfont templates"); let mut font_paths = FastHashMap::default(); @@ -1071,6 +1072,7 @@ impl ResourceCache { info!("\timage templates"); let mut image_paths = FastHashMap::default(); let mut other_paths = FastHashMap::default(); + let mut num_blobs = 0; let mut external_images = Vec::new(); for (&key, template) in res.image_templates.images.iter() { let desc = &template.descriptor; @@ -1082,8 +1084,13 @@ impl ResourceCache { Entry::Vacant(e) => e, }; - //TODO: option to save as PNG: - // https://github.com/servo/webrender/issues/2234 + #[cfg(feature = "png")] + CaptureConfig::save_png( + root.join(format!("images/{}.png", image_id)), + (desc.width, desc.height), + ReadPixelsFormat::Standard(desc.format), + &arc, + ); let file_name = format!("{}.raw", image_id); let short_path = format!("images/{}", file_name); fs::File::create(path_images.join(file_name)) @@ -1100,7 +1107,6 @@ impl ResourceCache { // https://github.com/servo/webrender/issues/2236 tile: None, }; - let renderer = self.blob_image_renderer.as_mut().unwrap(); renderer.request( &self.resources, @@ -1116,8 +1122,17 @@ impl ResourceCache { let result = renderer.resolve(request) .expect("Blob resolve failed"); assert_eq!((result.width, result.height), (desc.width, desc.height)); - - let file_name = format!("{}.raw", other_paths.len() + 1); + assert_eq!(result.data.len(), desc.compute_total_size() as usize); + + num_blobs += 1; + #[cfg(feature = "png")] + CaptureConfig::save_png( + root.join(format!("blobs/{}.png", num_blobs)), + (desc.width, desc.height), + ReadPixelsFormat::Standard(desc.format), + &result.data, + ); + let file_name = format!("{}.raw", num_blobs); let short_path = format!("blobs/{}", file_name); let full_path = path_blobs.clone().join(&file_name); fs::File::create(full_path) @@ -1127,7 +1142,7 @@ impl ResourceCache { other_paths.insert(key, short_path); } ImageData::External(ref ext) => { - let short_path = format!("blobs/{}.raw", other_paths.len() + 1); + let short_path = format!("externals/{}", external_images.len() + 1); other_paths.insert(key, short_path.clone()); external_images.push(ExternalCaptureImage { short_path, @@ -1166,6 +1181,7 @@ impl ResourceCache { }, descriptor: template.descriptor.clone(), tiling: template.tiling, + epoch: template.epoch, }) }) .collect(), @@ -1243,7 +1259,7 @@ impl ResourceCache { resources: PlainResources, caches: Option, root: &PathBuf, - ) { + ) -> Vec { use std::fs::File; use std::io::Read; @@ -1348,29 +1364,46 @@ impl ResourceCache { } info!("\timage templates..."); + let mut external_images = Vec::new(); for (key, template) in resources.image_templates { - let arc = match raw_map.entry(template.data) { - Entry::Occupied(e) => { - e.get().clone() + let data = match CaptureConfig::deserialize::(root, &template.data) { + Some(plain) => { + let ext_data = ExternalImageData { + id: plain.id, + channel_index: plain.channel_index, + image_type: ExternalImageType::Buffer, + }; + external_images.push(plain); + ImageData::External(ext_data) } - Entry::Vacant(e) => { - let mut buffer = Vec::new(); - File::open(root.join(e.key())) - .expect(&format!("Unable to open {}", e.key())) - .read_to_end(&mut buffer) - .unwrap(); - e.insert(Arc::new(buffer)) - .clone() + None => { + let arc = match raw_map.entry(template.data) { + Entry::Occupied(e) => { + e.get().clone() + } + Entry::Vacant(e) => { + let mut buffer = Vec::new(); + File::open(root.join(e.key())) + .expect(&format!("Unable to open {}", e.key())) + .read_to_end(&mut buffer) + .unwrap(); + e.insert(Arc::new(buffer)) + .clone() + } + }; + ImageData::Raw(arc) } }; res.image_templates.images.insert(key, ImageResource { - data: ImageData::Raw(arc), + data, descriptor: template.descriptor, tiling: template.tiling, - epoch: Epoch(0), + epoch: template.epoch, dirty_rect: None, }); } + + external_images } } diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 5c6d35b567..eb2e3a2637 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -1051,13 +1051,10 @@ impl TextureUpdate { 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 => { + ExternalImageType::TextureHandle(_) => { panic!("External texture handle should not go through texture_cache."); } - ExternalImageType::ExternalBuffer => TextureUpdateSource::External { + ExternalImageType::Buffer => TextureUpdateSource::External { id: ext_image.id, channel_index: ext_image.channel_index, }, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 2cf5638b5b..e8e9dc9c4c 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -761,7 +761,6 @@ pub struct Frame { // List of updates that need to be pushed to the // gpu resource cache. - #[cfg_attr(feature = "capture", serde(skip))] pub gpu_cache_updates: Option, // List of textures that we don't know about yet diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 32d4b5c796..2e2160a340 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -430,6 +430,7 @@ bitflags!{ pub struct CapturedDocument { pub document_id: DocumentId, pub root_pipeline_id: Option, + pub window_size: DeviceUintSize, } #[derive(Clone, Deserialize, Serialize)] diff --git a/webrender_api/src/color.rs b/webrender_api/src/color.rs index 669de9df2e..ef95d5bd68 100644 --- a/webrender_api/src/color.rs +++ b/webrender_api/src/color.rs @@ -47,12 +47,12 @@ pub struct ColorF { impl ColorF { /// Constructs a new `ColorF` from its components. - pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF { + pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { ColorF { r, g, b, a } } /// Multiply the RGB channels (but not alpha) with a given factor. - pub fn scale_rgb(&self, scale: f32) -> ColorF { + pub fn scale_rgb(&self, scale: f32) -> Self { ColorF { r: self.r * scale, g: self.g * scale, diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs index b70e3a57b1..15e3f4c9e3 100644 --- a/webrender_api/src/image.rs +++ b/webrender_api/src/image.rs @@ -13,12 +13,10 @@ use std::sync::Arc; pub struct ImageKey(pub IdNamespace, pub u32); impl ImageKey { - pub fn new(namespace: IdNamespace, key: u32) -> ImageKey { - ImageKey(namespace, key) - } + pub const DUMMY: Self = ImageKey(IdNamespace(0), 0); - pub fn dummy() -> ImageKey { - ImageKey(IdNamespace(0), 0) + pub fn new(namespace: IdNamespace, key: u32) -> Self { + ImageKey(namespace, key) } } @@ -29,14 +27,19 @@ impl ImageKey { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ExternalImageId(pub u64); +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum TextureTarget { + Default = 0, + Array = 1, + Rect = 2, + External = 3, +} + #[repr(u32)] #[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, + TextureHandle(TextureTarget), + Buffer, } #[repr(C)] @@ -93,6 +96,10 @@ impl ImageDescriptor { self.stride .unwrap_or(self.width * self.format.bytes_per_pixel()) } + + pub fn compute_total_size(&self) -> u32 { + self.compute_stride() * self.height + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -103,15 +110,15 @@ pub enum ImageData { } impl ImageData { - pub fn new(bytes: Vec) -> ImageData { + pub fn new(bytes: Vec) -> Self { ImageData::Raw(Arc::new(bytes)) } - pub fn new_shared(bytes: Arc>) -> ImageData { + pub fn new_shared(bytes: Arc>) -> Self { ImageData::Raw(bytes) } - pub fn new_blob_image(commands: Vec) -> ImageData { + pub fn new_blob_image(commands: Vec) -> Self { ImageData::Blob(commands) } @@ -125,16 +132,13 @@ impl ImageData { #[inline] pub fn uses_texture_cache(&self) -> bool { - match self { - &ImageData::External(ext_data) => match ext_data.image_type { - ExternalImageType::Texture2DHandle => false, - ExternalImageType::Texture2DArrayHandle => false, - ExternalImageType::TextureRectHandle => false, - ExternalImageType::TextureExternalHandle => false, - ExternalImageType::ExternalBuffer => true, + match *self { + ImageData::External(ref ext_data) => match ext_data.image_type { + ExternalImageType::TextureHandle(_) => false, + ExternalImageType::Buffer => true, }, - &ImageData::Blob(_) => true, - &ImageData::Raw(_) => true, + ImageData::Blob(_) => true, + ImageData::Raw(_) => true, } } } diff --git a/webrender_api/src/units.rs b/webrender_api/src/units.rs index 88818c4a71..1c1b847177 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -114,3 +114,30 @@ pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect { pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D { ScrollLayerVector2D::from_untyped(&vector.to_untyped()) } + +/// Stores two coordinates in texel space. The coordinates +/// are stored in texel coordinates because the texture atlas +/// may grow. Storing them as texel coords and normalizing +/// the UVs in the vertex shader means nothing needs to be +/// updated on the CPU when the texture size changes. +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct TexelRect { + pub uv0: DevicePoint, + pub uv1: DevicePoint, +} + +impl TexelRect { + pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> Self { + TexelRect { + uv0: DevicePoint::new(u0, v0), + uv1: DevicePoint::new(u1, v1), + } + } + + pub fn invalid() -> Self { + TexelRect { + uv0: DevicePoint::new(-1.0, -1.0), + uv1: DevicePoint::new(-1.0, -1.0), + } + } +} diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index da9a196689..635619d319 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -25,7 +25,7 @@ time = "0.1" crossbeam = "0.2" osmesa-sys = { version = "0.1.2", optional = true } osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true } -webrender = {path = "../webrender", features=["capture","debugger","profiler"]} +webrender = {path = "../webrender", features=["capture","debugger","png","profiler"]} webrender_api = {path = "../webrender_api", features=["debug-serialization"]} serde = {version = "1.0", features = ["derive"] } diff --git a/wrench/src/args.yaml b/wrench/src/args.yaml index 11810fb888..6c9f79be1d 100644 --- a/wrench/src/args.yaml +++ b/wrench/src/args.yaml @@ -155,5 +155,5 @@ subcommands: - path: help: directory containing the capture takes_value: true - required: false + required: true index: 1 diff --git a/wrench/src/main.rs b/wrench/src/main.rs index ce82010c2d..a2e8ea54f3 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -171,11 +171,12 @@ impl WindowWrapper { } } - fn get_inner_size_pixels(&self) -> (u32, u32) { - match *self { + fn get_inner_size(&self) -> DeviceUintSize { + let (w, h) = match *self { WindowWrapper::Window(ref window, _) => window.get_inner_size_pixels().unwrap(), WindowWrapper::Headless(ref context, _) => (context.width, context.height), - } + }; + DeviceUintSize::new(w, h) } fn hidpi_factor(&self) -> f32 { @@ -185,6 +186,13 @@ impl WindowWrapper { } } + fn resize(&mut self, size: DeviceUintSize) { + match *self { + WindowWrapper::Window(ref mut window, _) => window.set_inner_size(size.width, size.height), + WindowWrapper::Headless(_, _) => unimplemented!(), // requites Glutin update + } + } + fn create_window_proxy(&mut self) -> Option { match *self { WindowWrapper::Window(ref window, _) => Some(window.create_window_proxy()), @@ -342,8 +350,7 @@ fn main() { let mut window = make_window(size, dp_ratio, args.is_present("vsync"), is_headless); let dp_ratio = dp_ratio.unwrap_or(window.hidpi_factor()); - let (width, height) = window.get_inner_size_pixels(); - let dim = DeviceUintSize::new(width, height); + let dim = window.get_inner_size(); let needs_frame_notifier = ["perf", "reftest", "png", "rawtest"] .iter() @@ -387,14 +394,14 @@ fn main() { wrench.renderer.deinit(); return; } else if let Some(subargs) = args.subcommand_matches("reftest") { - let (w, h) = window.get_inner_size_pixels(); + let dim = window.get_inner_size(); let harness = ReftestHarness::new(&mut wrench, &mut window, rx.unwrap()); let base_manifest = Path::new("reftests/reftest.list"); let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x)); let mut reftest_options = ReftestOptions::default(); if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") { reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1); - reftest_options.allow_num_differences = w as usize * h as usize; + reftest_options.allow_num_differences = dim.width as usize * dim.height as usize; } harness.run(base_manifest, specific_reftest, &reftest_options); return; @@ -420,10 +427,11 @@ fn main() { perf::compare(first_filename, second_filename); return; } else if let Some(subargs) = args.subcommand_matches("load") { - let dir = subargs.value_of("path").unwrap_or("../captures/example"); - let mut documents = wrench.api.load_capture(PathBuf::from(dir)); + let path = PathBuf::from(subargs.value_of("path").unwrap()); + let mut documents = wrench.api.load_capture(path); println!("loaded {:?}", documents.iter().map(|cd| cd.document_id).collect::>()); let captured = documents.swap_remove(0); + window.resize(captured.window_size); wrench.document_id = captured.document_id; Box::new(captured) as Box } else { @@ -434,8 +442,7 @@ fn main() { let mut do_loop = false; let mut cpu_profile_index = 0; - let (width, height) = window.get_inner_size_pixels(); - let dim = DeviceUintSize::new(width, height); + let dim = window.get_inner_size(); wrench.update(dim); thing.do_frame(&mut wrench); @@ -480,7 +487,7 @@ fn main() { VirtualKeyCode::B => { wrench.renderer.toggle_debug_flags(DebugFlags::ALPHA_PRIM_DBG); } - VirtualKeyCode::C => { + VirtualKeyCode::S => { wrench.renderer.toggle_debug_flags(DebugFlags::COMPACT_PROFILER); } VirtualKeyCode::Q => { @@ -514,6 +521,10 @@ fn main() { wrench.renderer.save_cpu_profile(&file_name); cpu_profile_index += 1; } + VirtualKeyCode::C => { + let path = PathBuf::from("../captures/wrench"); + wrench.api.save_capture(path, CaptureBits::all()); + } VirtualKeyCode::Up => { let current_zoom = wrench.get_page_zoom(); let new_zoom_factor = ZoomFactor::new(current_zoom.get() + 0.1); @@ -534,8 +545,7 @@ fn main() { } } - let (width, height) = window.get_inner_size_pixels(); - let dim = DeviceUintSize::new(width, height); + let dim = window.get_inner_size(); wrench.update(dim); if do_frame { diff --git a/wrench/src/png.rs b/wrench/src/png.rs index 0d780526c4..725678fe98 100644 --- a/wrench/src/png.rs +++ b/wrench/src/png.rs @@ -87,11 +87,8 @@ pub fn png( let (device_size, data, settings) = match surface { ReadSurface::Screen => { - let size = window.get_inner_size_pixels(); - let rect = DeviceUintRect::new( - DeviceUintPoint::zero(), - DeviceUintSize::new(size.0, size.1), - ); + let dim = window.get_inner_size(); + let rect = DeviceUintRect::new(DeviceUintPoint::zero(), dim); let data = wrench.renderer .read_pixels_rgba8(rect); (rect.size, data, SaveSettings { diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs index eb817d4a8d..7933f2575b 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -123,8 +123,7 @@ impl<'a> RawtestHarness<'a> { fn test_retained_blob_images_test(&mut self) { println!("\tretained blob images test..."); let blob_img; - let window_size = self.window.get_inner_size_pixels(); - let window_size = DeviceUintSize::new(window_size.0, window_size.1); + let window_size = self.window.get_inner_size(); let test_size = DeviceUintSize::new(400, 400); @@ -204,8 +203,7 @@ impl<'a> RawtestHarness<'a> { fn test_blob_update_epoch_test(&mut self) { println!("\tblob update epoch test..."); let (blob_img, blob_img2); - let window_size = self.window.get_inner_size_pixels(); - let window_size = DeviceUintSize::new(window_size.0, window_size.1); + let window_size = self.window.get_inner_size(); let test_size = DeviceUintSize::new(400, 400); @@ -325,8 +323,7 @@ impl<'a> RawtestHarness<'a> { fn test_blob_update_test(&mut self) { println!("\tblob update test..."); - let window_size = self.window.get_inner_size_pixels(); - let window_size = DeviceUintSize::new(window_size.0, window_size.1); + let window_size = self.window.get_inner_size(); let test_size = DeviceUintSize::new(400, 400); @@ -421,8 +418,7 @@ impl<'a> RawtestHarness<'a> { // Ensures that content doing a save-restore produces the same results as not fn test_save_restore(&mut self) { println!("\tsave/restore..."); - let window_size = self.window.get_inner_size_pixels(); - let window_size = DeviceUintSize::new(window_size.0, window_size.1); + let window_size = self.window.get_inner_size(); let test_size = DeviceUintSize::new(400, 400); @@ -489,9 +485,9 @@ impl<'a> RawtestHarness<'a> { println!("\tcapture..."); let path = "../captures/test"; let layout_size = LayoutSize::new(400., 400.); - let (_, windows_height) = self.window.get_inner_size_pixels(); + let dim = self.window.get_inner_size(); let window_rect = DeviceUintRect::new( - point(0, windows_height - layout_size.height as u32), + point(0, dim.height - layout_size.height as u32), size(layout_size.width as u32, layout_size.height as u32), ); diff --git a/wrench/src/reftest.rs b/wrench/src/reftest.rs index fbde30762d..4a70c57f1b 100644 --- a/wrench/src/reftest.rs +++ b/wrench/src/reftest.rs @@ -336,10 +336,7 @@ impl<'a> ReftestHarness<'a> { ); } - let window_size = DeviceUintSize::new( - self.window.get_inner_size_pixels().0, - self.window.get_inner_size_pixels().1, - ); + let window_size = self.window.get_inner_size(); let reference = match t.reference.extension().unwrap().to_str().unwrap() { "yaml" => { let (reference, _) = self.render_yaml( @@ -468,11 +465,11 @@ impl<'a> ReftestHarness<'a> { self.rx.recv().unwrap(); let stats = self.wrench.render(); - let window_size = self.window.get_inner_size_pixels(); - assert!(size.width <= window_size.0 && size.height <= window_size.1); + let window_size = self.window.get_inner_size(); + assert!(size.width <= window_size.width && size.height <= window_size.height); // taking the bottom left sub-rectangle - let rect = DeviceUintRect::new(DeviceUintPoint::new(0, window_size.1 - size.height), size); + let rect = DeviceUintRect::new(DeviceUintPoint::new(0, window_size.height - size.height), size); let pixels = self.wrench.renderer.read_pixels_rgba8(rect); self.window.swap_buffers(); diff --git a/wrench/src/wrench.rs b/wrench/src/wrench.rs index b288db765e..9a7c50fa6c 100644 --- a/wrench/src/wrench.rs +++ b/wrench/src/wrench.rs @@ -570,9 +570,11 @@ impl Wrench { "O - Toggle showing intermediate targets", "I - Toggle showing texture caches", "B - Toggle showing alpha primitive rects", + "S - Toggle compact profiler", "Q - Toggle GPU queries for time and samples", "M - Trigger memory pressure event", "T - Save CPU profile to a file", + "C - Save a capture to captures/wrench/", ]; let color_and_offset = [(*BLACK_COLOR, 2.0), (*WHITE_COLOR, 0.0)];