From 4d7909d7855882ac7d2ed7edb793936408626382 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 26 Jul 2019 14:55:50 +0000 Subject: [PATCH] Bug 1548339 - WR swizzle configuration of texture units r=gw Allow the swizzle to be configurable with a texture binding. This is still experimental and needs to be tested well on all platforms. Basic approach is the following: - WR device figures out how it can use BGRA and makes the texture cache format configurable at run-time. It tries to make the uploads to the shared texture cache pages to be done without any driver conversions, and without extra memory allocated. - it also reports the preferred input format for the images, which may be different from the texture cache format - if WR texture cache is asked to allocate a shared texture with a different (swizzled) format from the preferred, it associates the cache entry with a swizzle - the swizzle becomes a part of the `SourceTexture`, which affects batch splitting - when a texture reaches binding by GL device, it checks whether the current swizzle on this texture doesn't match the given one, and configures the texture sampling accordingly - we can't use blits with swizzling, so when that needs to happen we use `cs_copy` path, which is now mostly rewritten The idea is that Gecko would ask WR for the preferred format and configure its image decoding to produce image data that doesn't require any swizzling. The PR changes existing texture upload (and batching) paths. On Linux, if texture storage is available, we now use it and provide the data as RGBA, assuming no conversion by the driver. The swizzling kicks in when we sample this data in the shader. On Windows/Angle we use BGRA as an internal format for texture cache and expect Gecko to provide BGRA data, this should be unchanged. Differential Revision: https://phabricator.services.mozilla.com/D21965 --HG-- extra : moz-landing-system : lando --- gfx/webrender_bindings/src/bindings.rs | 2 +- gfx/wr/webrender/doc/swizzling.md | 31 ++ gfx/wr/webrender/res/cs_scale.glsl | 27 +- gfx/wr/webrender/src/batch.rs | 12 +- gfx/wr/webrender/src/debug_render.rs | 4 +- gfx/wr/webrender/src/device/gl.rs | 295 +++++++++++++------ gfx/wr/webrender/src/glyph_rasterizer/mod.rs | 11 +- gfx/wr/webrender/src/gpu_types.rs | 5 +- gfx/wr/webrender/src/internal_types.rs | 18 +- gfx/wr/webrender/src/picture.rs | 3 +- gfx/wr/webrender/src/prim_store/image.rs | 34 ++- gfx/wr/webrender/src/profiler.rs | 22 +- gfx/wr/webrender/src/render_task.rs | 93 ++++-- gfx/wr/webrender/src/renderer.rs | 180 ++++++----- gfx/wr/webrender/src/resource_cache.rs | 13 +- gfx/wr/webrender/src/texture_cache.rs | 239 +++++++++------ gfx/wr/webrender/src/tiling.rs | 148 +++++++--- gfx/wr/wrench/reftests/border/reftest.list | 2 +- gfx/wr/wrench/src/rawtest.rs | 19 +- 19 files changed, 757 insertions(+), 401 deletions(-) create mode 100644 gfx/wr/webrender/doc/swizzling.md diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index b8f25458458c..b2083645f12b 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -1148,7 +1148,7 @@ fn wr_device_new(gl_context: *mut c_void, pc: Option<&mut WrProgramCache>) None => None, }; - Device::new(gl, resource_override_path, upload_method, cached_programs, false, None) + Device::new(gl, resource_override_path, upload_method, cached_programs, false, true, true, None) } // Call MakeCurrent before this. diff --git a/gfx/wr/webrender/doc/swizzling.md b/gfx/wr/webrender/doc/swizzling.md new file mode 100644 index 000000000000..4b38791940e7 --- /dev/null +++ b/gfx/wr/webrender/doc/swizzling.md @@ -0,0 +1,31 @@ +> It'd be great to have some (in-tree) docs describing the process you've worked through here, the overall motivation, how it works on different GPUs / platforms etc. Perhaps as a follow up? + +# Swizzling in WR + +## Problem statement + +The goal is to avoid the CPU conversion of data done by the driver on texture data uploads. It's slow and always done synchronously, hurting our "Renderer" thread CPU utilization. + +Gecko produces all of the image data in BGRA. Switching "imagelib" to RGBA is possible, but modifying Skia to follow is less trivial. +OpenGL support for BGRA8 as an internal texture format is a complex story: it's officially supported in Angle and a fair share of Android devices, but it's not available on the desktop GL (and until recently wasn't available in the Android emulator). Unofficially, when textures are initialized with `glTexImage` (as opposed to `glTexStorage`) with RGBA internal format, the desktop GL drivers often prefer to store the data in BGRA8 format, actually. + +The only way to avoid the CPU conversion is to provide the data in exactly the same format that the driver is using internally for a texture. In this case, the driver does a straght `memcpy` into its CPU-visible memory, which is the best we can hope for with OpenGL API. + +## Solution: swizzling + +https://phabricator.services.mozilla.com/D21965 is providing the solution to this problem. The main principles are: + + 1. Use `glTexStorage` whenever it's available. Doing so gives us full control of the internal format, also allows to avoid allocating memory for mipmaps that we don't use. + 2. Make the shared texture cache format to be determined at the init time, based on the GL device capabilities. For Angle and OpenGL ES this is BGRA8, for desktop this is RGBA8 (since desktop GL doesn't support BGRA internal formats). WebRender is now able to tell Gecko, which color format it prefers the texture data to use. + 3. If the data comes in a format that is different from our best case, we pretend that the data is actually in our best case format, and associate the allocated cache entry with the `Swizzle`. That swizzle configuration changes the way shaders sample from a texture, adjusting for the fact the data was provided in a different format. + 4. The lifetime of a "swizzled" texture cache data is starting at the point the data is uploaded and ending at a point where any shader samples from this data. Any other operation on that data (copying or blitting) is not configurable by `Swizzle` and thus would produce incorrect results. To address this, the change enhances `cs_copy` shader to be used in place of blitting from the texture cache, where needed. + 5. Swizzling becomes a part of the batch key per texture. Mixing up draw calls with texture data that is differently swizzled then introduces the batch breaks. This is a downside for the swizzling approach in general, but it's not clear to what extent this would affect Gecko. + +## Code paths + +Windows/Angle and Android: + - we use `glTexStorage` with BGRA8 internal format, no swizzling is needed in general case. + +Windows (non-Angle), Mac, Linux: + - if `glTexStorage` is available, we use it with RGBA8 internal format, swizzling everything on texture sampling. + - otherwise, we use RGBA unsized format with `gTexImage` and expect the data to come in BGRA format, no swizzling is involved. diff --git a/gfx/wr/webrender/res/cs_scale.glsl b/gfx/wr/webrender/res/cs_scale.glsl index eb398b9c29c4..183e6774cc47 100644 --- a/gfx/wr/webrender/res/cs_scale.glsl +++ b/gfx/wr/webrender/res/cs_scale.glsl @@ -9,35 +9,20 @@ flat varying vec4 vUvRect; #ifdef WR_VERTEX_SHADER -in int aScaleRenderTaskAddress; -in int aScaleSourceTaskAddress; - -struct ScaleTask { - RenderTaskCommonData common_data; -}; - -ScaleTask fetch_scale_task(int address) { - RenderTaskData task_data = fetch_render_task_data(address); - - ScaleTask task = ScaleTask(task_data.common_data); - - return task; -} +in vec4 aScaleTargetRect; +in ivec4 aScaleSourceRect; +in int aScaleSourceLayer; void main(void) { - ScaleTask scale_task = fetch_scale_task(aScaleRenderTaskAddress); - RenderTaskCommonData src_task = fetch_render_task_common_data(aScaleSourceTaskAddress); - - RectWithSize src_rect = src_task.task_rect; - RectWithSize target_rect = scale_task.common_data.task_rect; + RectWithSize src_rect = RectWithSize(vec2(aScaleSourceRect.xy), vec2(aScaleSourceRect.zw)); vec2 texture_size = vec2(textureSize(sColor0, 0).xy); - vUv.z = src_task.texture_layer_index; + vUv.z = float(aScaleSourceLayer); vUvRect = vec4(src_rect.p0 + vec2(0.5), src_rect.p0 + src_rect.size - vec2(0.5)) / texture_size.xyxy; - vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy; + vec2 pos = aScaleTargetRect.xy + aScaleTargetRect.zw * aPosition.xy; vUv.xy = (src_rect.p0 + src_rect.size * aPosition.xy) / texture_size; gl_Position = uTransform * vec4(pos, 0.0, 1.0); diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs index 544a559141b4..6117f9e673c1 100644 --- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -13,7 +13,7 @@ use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, Z use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, SnapOffsets}; use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; -use crate::internal_types::{FastHashMap, SavedTargetIndex, TextureSource, Filter}; +use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSource, Filter}; use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask}; use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; @@ -1309,14 +1309,18 @@ impl BatchBuilder { // Gets the saved render task ID of the content, which is // deeper in the render task graph than the direct child. let secondary_id = picture.secondary_render_task_id.expect("no secondary!?"); - let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?"); - debug_assert_ne!(saved_index, SavedTargetIndex::PENDING); + let content_source = { + let secondary_task = &render_tasks[secondary_id]; + let saved_index = secondary_task.saved_index.expect("no saved index!?"); + debug_assert_ne!(saved_index, SavedTargetIndex::PENDING); + TextureSource::RenderTaskCache(saved_index, Swizzle::default()) + }; // Build BatchTextures for shadow/content let shadow_textures = BatchTextures::render_target_cache(); let content_textures = BatchTextures { colors: [ - TextureSource::RenderTaskCache(saved_index), + content_source, TextureSource::Invalid, TextureSource::Invalid, ], diff --git a/gfx/wr/webrender/src/debug_render.rs b/gfx/wr/webrender/src/debug_render.rs index 440c06434754..a65136d9231e 100644 --- a/gfx/wr/webrender/src/debug_render.rs +++ b/gfx/wr/webrender/src/debug_render.rs @@ -8,7 +8,7 @@ use crate::debug_font_data; use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO}; use crate::device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint}; use euclid::{Point2D, Rect, Size2D, Transform3D, default}; -use crate::internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE}; +use crate::internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, Swizzle}; use std::f32; #[cfg_attr(feature = "capture", derive(Serialize))] @@ -360,7 +360,7 @@ impl DebugRenderer { if !self.font_indices.is_empty() { device.bind_program(&self.font_program); device.set_uniforms(&self.font_program, &projection); - device.bind_texture(DebugSampler::Font, &self.font_texture); + device.bind_texture(DebugSampler::Font, &self.font_texture, Swizzle::default()); device.bind_vao(&self.font_vao); device.update_vao_indices(&self.font_vao, &self.font_indices, VertexUsageHint::Dynamic); device.update_vao_main_vertices( diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs index 9fd3bec8f7b0..4a7194d8f954 100644 --- a/gfx/wr/webrender/src/device/gl.rs +++ b/gfx/wr/webrender/src/device/gl.rs @@ -8,29 +8,31 @@ use api::{MixBlendMode, TextureTarget, VoidPtrToSizeFn}; use api::units::*; use euclid::default::Transform3D; use gleam::gl; -use crate::internal_types::{FastHashMap, LayerIndex, RenderTargetInfo}; -use log::Level; +use crate::internal_types::{FastHashMap, LayerIndex, RenderTargetInfo, Swizzle}; +use crate::util::round_up_to_multiple; use crate::profiler; +use log::Level; use sha2::{Digest, Sha256}; use smallvec::SmallVec; -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::collections::hash_map::Entry; -use std::marker::PhantomData; -use std::mem; -use std::num::NonZeroUsize; -use std::os::raw::c_void; -use std::ops::Add; -use std::path::PathBuf; -use std::ptr; -use std::rc::Rc; -use std::slice; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; -use std::time::Duration; -use crate::util::round_up_to_multiple; +use std::{ + borrow::Cow, + cell::{Cell, RefCell}, + cmp, + collections::hash_map::Entry, + marker::PhantomData, + mem, + num::NonZeroUsize, + os::raw::c_void, + ops::Add, + path::PathBuf, + ptr, + rc::Rc, + slice, + sync::Arc, + sync::atomic::{AtomicUsize, Ordering}, + thread, + time::Duration, +}; use webrender_build::shader::ProgramSourceDigest; use webrender_build::shader::{parse_shader_source, shader_source_from_file}; @@ -105,6 +107,26 @@ pub enum TextureFilter { Trilinear, } +/// A structure defining a particular workflow of texture transfers. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct TextureFormatPair { + /// Format the GPU natively stores texels in. + pub internal: T, + /// Format we expect the users to provide the texels in. + pub external: T, +} + +impl From for TextureFormatPair { + fn from(value: T) -> Self { + TextureFormatPair { + internal: value, + external: value, + } + } +} + #[derive(Debug)] pub enum VertexAttributeKind { F32, @@ -455,16 +477,19 @@ impl Drop for VBO { } #[cfg_attr(feature = "replay", derive(Clone))] +#[derive(Debug)] pub struct ExternalTexture { id: gl::GLuint, target: gl::GLuint, + swizzle: Swizzle, } impl ExternalTexture { - pub fn new(id: u32, target: TextureTarget) -> Self { + pub fn new(id: u32, target: TextureTarget, swizzle: Swizzle) -> Self { ExternalTexture { id, target: get_gl_target(target), + swizzle, } } @@ -496,6 +521,8 @@ pub struct Texture { size: DeviceIntSize, filter: TextureFilter, flags: TextureFlags, + /// An internally mutable swizzling state that may change between batches. + active_swizzle: Cell, /// Framebuffer Objects, one for each layer of the texture, allowing this /// texture to be rendered to. Empty if this texture is not used as a render /// target. @@ -586,6 +613,7 @@ impl Texture { let ext = ExternalTexture { id: self.id, target: self.target, + swizzle: Swizzle::default(), }; self.id = 0; // don't complain, moved out ext @@ -894,10 +922,11 @@ impl UniformLocation { } pub struct Capabilities { + /// Whether multisampled render targets are supported. pub supports_multisampling: bool, - /// Whether the function glCopyImageSubData is available. + /// Whether the function `glCopyImageSubData` is available. pub supports_copy_image_sub_data: bool, - /// Whether we are able to use glBlitFramebuffers with the draw fbo + /// Whether we are able to use `glBlitFramebuffers` with the draw fbo /// bound to a non-0th layer of a texture array. This is buggy on /// Adreno devices. pub supports_blit_to_texture_array: bool, @@ -969,8 +998,10 @@ pub struct Device { // HW or API capabilities capabilities: Capabilities, - bgra_format_internal: gl::GLuint, - bgra_format_external: gl::GLuint, + color_formats: TextureFormatPair, + bgra_formats: TextureFormatPair, + // the swizzle required on sampling a texture with `TextureFormat::BGRA` format + bgra_swizzle: Swizzle, /// Map from texture dimensions to shared depth buffers for render targets. /// @@ -1182,6 +1213,8 @@ impl Device { upload_method: UploadMethod, cached_programs: Option>, allow_pixel_local_storage_support: bool, + allow_texture_storage_support: bool, + allow_texture_swizzling: bool, dump_shader_source: Option, ) -> Device { let mut max_texture_size = [0]; @@ -1218,15 +1251,19 @@ impl Device { }); } + if supports_extension(&extensions, "GL_ANGLE_provoking_vertex") { + gl.provoking_vertex_angle(gl::FIRST_VERTEX_CONVENTION); + } + // Our common-case image data in Firefox is BGRA, so we make an effort // to use BGRA as the internal texture storage format to avoid the need // to swizzle during upload. Currently we only do this on GLES (and thus // for Windows, via ANGLE). // // On Mac, Apple docs [1] claim that BGRA is a more efficient internal - // format, so we may want to consider doing that at some point, since it - // would give us both a more efficient internal format and avoid the - // swizzling in the common case. + // format, but they don't support it with glTextureStorage. As a workaround, + // we pretend that it's RGBA8 for the purposes of texture transfers, + // but swizzle R with B for the texture sampling. // // We also need our internal format types to be sized, since glTexStorage* // will reject non-sized internal format types. @@ -1239,64 +1276,84 @@ impl Device { // The extension is available on ANGLE, but on Android this usually // means we must fall back to using unsized BGRA and glTexImage*. // + // Overall, we have the following factors in play when choosing the formats: + // - with glTexStorage, the internal format needs to match the external format, + // or the driver would have to do the conversion, which is slow + // - on desktop GL, there is no BGRA internal format. However, initializing + // the textures with glTexImage as RGBA appears to use BGRA internally, + // preferring BGRA external data [4]. + // - when glTexStorage + BGRA internal format is not supported, + // and the external data is BGRA, we have the following options: + // 1. use glTexImage with RGBA internal format, this costs us VRAM for mipmaps + // 2. use glTexStorage with RGBA internal format, this costs us the conversion by the driver + // 3. pretend we are uploading RGBA and set up the swizzling of the texture unit - this costs us batch breaks + // // [1] https://developer.apple.com/library/archive/documentation/ // GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/ // opengl_texturedata.html#//apple_ref/doc/uid/TP40001987-CH407-SW22 // [2] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_format_BGRA8888.txt // [3] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_storage.txt - let supports_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888"); - let supports_texture_storage = match gl.get_type() { - gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"), - gl::GlType::Gles => true, - }; + // [4] http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf - if supports_extension(&extensions, "GL_ANGLE_provoking_vertex") { - gl.provoking_vertex_angle(gl::FIRST_VERTEX_CONVENTION); - } + // To support BGRA8 with glTexStorage* we specifically need + // GL_EXT_texture_storage and GL_EXT_texture_format_BGRA8888. + let supports_gles_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888"); - let (bgra_format_internal, bgra_format_external, texture_storage_usage) = if supports_bgra { - assert_eq!(gl.get_type(), gl::GlType::Gles, "gleam only detects bgra on gles"); - // To support BGRA8 with glTexStorage* we specifically need - // GL_EXT_texture_storage and GL_EXT_texture_format_BGRA8888. - if supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888") && supports_extension(&extensions, "GL_EXT_texture_storage") { - // We can use glTexStorage with BGRA8 as the internal format. - (gl::BGRA8_EXT, gl::BGRA_EXT, TexStorageUsage::Always) - } else { - // For BGRA8 textures we must must use the unsized BGRA internal - // format and glTexImage. If texture storage is supported we can - // use it for other formats. - ( - gl::BGRA_EXT, - gl::BGRA_EXT, - if supports_texture_storage { - TexStorageUsage::NonBGRA8 - } else { - TexStorageUsage::Never - }, - ) - } - } else { + let (color_formats, bgra_formats, bgra_swizzle, texture_storage_usage) = match gl.get_type() { + // There is `glTexStorage`, use it and expect RGBA on the input. + gl::GlType::Gl if + allow_texture_storage_support && + allow_texture_swizzling && + supports_extension(&extensions, "GL_ARB_texture_storage") + => ( + TextureFormatPair::from(ImageFormat::RGBA8), + TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, + Swizzle::Bgra, // pretend it's RGBA, rely on swizzling + TexStorageUsage::Always + ), + // There is no `glTexStorage`, upload as `glTexImage` with BGRA input. + gl::GlType::Gl => ( + TextureFormatPair { internal: ImageFormat::RGBA8, external: ImageFormat::BGRA8 }, + TextureFormatPair { internal: gl::RGBA, external: gl::BGRA }, + Swizzle::Rgba, // converted on uploads by the driver, no swizzling needed + TexStorageUsage::Never + ), + // We can use glTexStorage with BGRA8 as the internal format. + gl::GlType::Gles if supports_gles_bgra && allow_texture_storage_support && supports_extension(&extensions, "GL_EXT_texture_storage") => ( + TextureFormatPair::from(ImageFormat::BGRA8), + TextureFormatPair { internal: gl::BGRA8_EXT, external: gl::BGRA_EXT }, + Swizzle::Rgba, // no conversion needed + TexStorageUsage::Always, + ), + // For BGRA8 textures we must use the unsized BGRA internal + // format and glTexImage. If texture storage is supported we can + // use it for other formats. + // We can't use glTexStorage with BGRA8 as the internal format. + gl::GlType::Gles if supports_gles_bgra => ( + TextureFormatPair::from(ImageFormat::RGBA8), + TextureFormatPair::from(gl::BGRA_EXT), + Swizzle::Rgba, // no conversion needed + TexStorageUsage::NonBGRA8, + ), // BGRA is not supported as an internal format, therefore we will - // use RGBA. On non-gles we can swizzle during upload. This is not - // allowed on gles, so we must us RGBA for the external format too. - // Red and blue will appear reversed, but it is the best we can do. - // Since the internal format will actually be RGBA, if texture - // storage is supported we can use it for such textures. - ( - gl::RGBA8, - if gl.get_type() == gl::GlType::Gles { - gl::RGBA - } else { - gl::BGRA - }, - if supports_texture_storage { - TexStorageUsage::Always - } else { - TexStorageUsage::Never - }, - ) + // use RGBA. The swizzling will happen at the texture unit. + gl::GlType::Gles if allow_texture_swizzling => ( + TextureFormatPair::from(ImageFormat::RGBA8), + TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, + Swizzle::Bgra, // pretend it's RGBA, rely on swizzling + TexStorageUsage::Always, + ), + // BGRA and swizzling are not supported. We force the conversion done by the driver. + gl::GlType::Gles => ( + TextureFormatPair::from(ImageFormat::RGBA8), + TextureFormatPair { internal: gl::RGBA8, external: gl::BGRA }, + Swizzle::Rgba, + TexStorageUsage::Always, + ), }; + info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}", + color_formats, bgra_formats, bgra_swizzle, texture_storage_usage); let supports_copy_image_sub_data = supports_extension(&extensions, "GL_EXT_copy_image") || supports_extension(&extensions, "GL_ARB_copy_image"); @@ -1349,8 +1406,9 @@ impl Device { supports_khr_debug, }, - bgra_format_internal, - bgra_format_external, + color_formats, + bgra_formats, + bgra_swizzle, depth_targets: FastHashMap::default(), @@ -1410,6 +1468,14 @@ impl Device { &self.capabilities } + pub fn preferred_color_formats(&self) -> TextureFormatPair { + self.color_formats.clone() + } + + pub fn bgra_swizzle(&self) -> Swizzle { + self.bgra_swizzle + } + pub fn get_optimal_pbo_stride(&self) -> NonZeroUsize { self.optimal_pbo_stride } @@ -1530,29 +1596,47 @@ impl Device { self.frame_id } - fn bind_texture_impl(&mut self, slot: TextureSlot, id: gl::GLuint, target: gl::GLenum) { + fn bind_texture_impl( + &mut self, slot: TextureSlot, id: gl::GLuint, target: gl::GLenum, set_swizzle: Option + ) { debug_assert!(self.inside_frame); - if self.bound_textures[slot.0] != id { - self.bound_textures[slot.0] = id; + if self.bound_textures[slot.0] != id || set_swizzle.is_some() { self.gl.active_texture(gl::TEXTURE0 + slot.0 as gl::GLuint); self.gl.bind_texture(target, id); + if let Some(swizzle) = set_swizzle { + let components = match swizzle { + Swizzle::Rgba => [gl::RED, gl::GREEN, gl::BLUE, gl::ALPHA], + Swizzle::Bgra => [gl::BLUE, gl::GREEN, gl::RED, gl::ALPHA], + }; + self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_R, components[0] as i32); + self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_G, components[1] as i32); + self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_B, components[2] as i32); + self.gl.tex_parameter_i(target, gl::TEXTURE_SWIZZLE_A, components[3] as i32); + } self.gl.active_texture(gl::TEXTURE0); + self.bound_textures[slot.0] = id; } } - pub fn bind_texture(&mut self, sampler: S, texture: &Texture) + pub fn bind_texture(&mut self, slot: S, texture: &Texture, swizzle: Swizzle) where S: Into, { - self.bind_texture_impl(sampler.into(), texture.id, texture.target); + let old_swizzle = texture.active_swizzle.replace(swizzle); + let set_swizzle = if old_swizzle != swizzle { + Some(swizzle) + } else { + None + }; + self.bind_texture_impl(slot.into(), texture.id, texture.target, set_swizzle); } - pub fn bind_external_texture(&mut self, sampler: S, external_texture: &ExternalTexture) + pub fn bind_external_texture(&mut self, slot: S, external_texture: &ExternalTexture) where S: Into, { - self.bind_texture_impl(sampler.into(), external_texture.id, external_texture.target); + self.bind_texture_impl(slot.into(), external_texture.id, external_texture.target, None); } pub fn bind_read_target_impl(&mut self, fbo_id: FBOId) { @@ -1846,13 +1930,14 @@ impl Device { layer_count, format, filter, + active_swizzle: Cell::default(), fbos: vec![], fbos_with_depth: vec![], blit_workaround_buffer: None, last_frame_used: self.frame_id, flags: TextureFlags::default(), }; - self.bind_texture(DEFAULT_TEXTURE, &texture); + self.bind_texture(DEFAULT_TEXTURE, &texture, Swizzle::default()); self.set_texture_parameters(texture.target, filter); // Allocate storage. @@ -2246,6 +2331,7 @@ impl Device { DEFAULT_TEXTURE, id, target, + None, // not depending on swizzle ); // Copy from intermediate buffer to the texture layer. @@ -2309,7 +2395,7 @@ impl Device { for bound_texture in &mut self.bound_textures { if *bound_texture == texture.id { - *bound_texture = 0 + *bound_texture = 0; } } @@ -2478,7 +2564,7 @@ impl Device { rect.origin.y as _, rect.size.width as _, rect.size.height as _, - gl_format.external, + gl_format.read, gl_format.pixel_type, ); } @@ -2528,7 +2614,7 @@ impl Device { upload_count: usize, ) -> TextureUploader<'a, T> { debug_assert!(self.inside_frame); - self.bind_texture(DEFAULT_TEXTURE, texture); + self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let buffer = match self.upload_method { UploadMethod::Immediate => None, @@ -2550,7 +2636,7 @@ impl Device { TextureUploader { target: UploadTarget { gl: &*self.gl, - bgra_format: self.bgra_format_external, + bgra_format: self.bgra_formats.external, optimal_pbo_stride: self.optimal_pbo_stride, texture, }, @@ -2565,7 +2651,7 @@ impl Device { texture: &Texture, pixels: &[T] ) { - self.bind_texture(DEFAULT_TEXTURE, texture); + self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let desc = self.gl_describe_format(texture.format); match texture.target { gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => @@ -2604,7 +2690,7 @@ impl Device { 0, 0, img_desc.size.width as i32, img_desc.size.height as i32, - desc.external, + desc.read, desc.pixel_type, ) } @@ -2627,7 +2713,7 @@ impl Device { rect.origin.y as _, rect.size.width as _, rect.size.height as _, - desc.external, + desc.read, desc.pixel_type, output, ); @@ -2640,7 +2726,7 @@ impl Device { format: ImageFormat, output: &mut [u8], ) { - self.bind_texture(DEFAULT_TEXTURE, texture); + self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let desc = self.gl_describe_format(format); self.gl.get_tex_image_into_buffer( texture.target, @@ -3213,17 +3299,20 @@ impl Device { ImageFormat::R8 => FormatDesc { internal: gl::R8, external: gl::RED, + read: gl::RED, pixel_type: gl::UNSIGNED_BYTE, }, ImageFormat::R16 => FormatDesc { internal: gl::R16, external: gl::RED, + read: gl::RED, pixel_type: gl::UNSIGNED_SHORT, }, ImageFormat::BGRA8 => { FormatDesc { - internal: self.bgra_format_internal, - external: self.bgra_format_external, + internal: self.bgra_formats.internal, + external: self.bgra_formats.external, + read: gl::BGRA, pixel_type: gl::UNSIGNED_BYTE, } }, @@ -3231,27 +3320,32 @@ impl Device { FormatDesc { internal: gl::RGBA8, external: gl::RGBA, + read: gl::RGBA, pixel_type: gl::UNSIGNED_BYTE, } }, ImageFormat::RGBAF32 => FormatDesc { internal: gl::RGBA32F, external: gl::RGBA, + read: gl::RGBA, pixel_type: gl::FLOAT, }, ImageFormat::RGBAI32 => FormatDesc { internal: gl::RGBA32I, external: gl::RGBA_INTEGER, + read: gl::RGBA_INTEGER, pixel_type: gl::INT, }, ImageFormat::RG8 => FormatDesc { internal: gl::RG8, external: gl::RG, + read: gl::RG, pixel_type: gl::UNSIGNED_BYTE, }, ImageFormat::RG16 => FormatDesc { internal: gl::RG16, external: gl::RG, + read: gl::RG, pixel_type: gl::UNSIGNED_SHORT, }, } @@ -3262,8 +3356,7 @@ impl Device { match format { ImageFormat::R8 => gl::R8, ImageFormat::R16 => gl::R16UI, - // BGRA8 is not usable with renderbuffers so use RGBA8. - ImageFormat::BGRA8 => gl::RGBA8, + ImageFormat::BGRA8 => panic!("Unable to render to BGRA format!"), ImageFormat::RGBAF32 => gl::RGBA32F, ImageFormat::RG8 => gl::RG8, ImageFormat::RG16 => gl::RG16, @@ -3283,8 +3376,14 @@ impl Device { } struct FormatDesc { + /// Format the texel data is internally stored in within a texture. internal: gl::GLenum, + /// Format that we expect the data to be provided when filling the texture. external: gl::GLuint, + /// Format to read the texels as, so that they can be uploaded as `external` + /// later on. + read: gl::GLuint, + /// Associated pixel type. pixel_type: gl::GLuint, } diff --git a/gfx/wr/webrender/src/glyph_rasterizer/mod.rs b/gfx/wr/webrender/src/glyph_rasterizer/mod.rs index 3a371fd7a7ed..d2524f7857bf 100644 --- a/gfx/wr/webrender/src/glyph_rasterizer/mod.rs +++ b/gfx/wr/webrender/src/glyph_rasterizer/mod.rs @@ -175,7 +175,7 @@ impl GlyphRasterizer { ImageDescriptor { size: size2(glyph.width, glyph.height), stride: None, - format: ImageFormat::BGRA8, + format: FORMAT, is_opaque: false, allow_mipmaps: false, offset: 0, @@ -205,6 +205,9 @@ impl GlyphRasterizer { } } +#[allow(dead_code)] +pub const FORMAT: ImageFormat = ImageFormat::BGRA8; + #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -1023,7 +1026,7 @@ mod test_glyph_rasterizer { use crate::render_backend::FrameId; use thread_profiler::register_thread_with_profiler; use std::sync::Arc; - use crate::glyph_rasterizer::{FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; + use crate::glyph_rasterizer::{FORMAT, FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; let worker = ThreadPoolBuilder::new() .thread_name(|idx|{ format!("WRWorker#{}", idx) }) @@ -1035,7 +1038,7 @@ mod test_glyph_rasterizer { let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap(); let mut glyph_cache = GlyphCache::new(GlyphCache::DEFAULT_MAX_BYTES_USED); let mut gpu_cache = GpuCache::new_for_testing(); - let mut texture_cache = TextureCache::new_for_testing(2048, 1024); + let mut texture_cache = TextureCache::new_for_testing(2048, 1024, FORMAT); let mut render_task_cache = RenderTaskCache::new(); let mut render_task_tree = RenderTaskGraph::new(FrameId::INVALID, &RenderTaskGraphCounters::new()); let mut font_file = @@ -1087,7 +1090,7 @@ mod test_glyph_rasterizer { glyph_rasterizer.resolve_glyphs( &mut glyph_cache, - &mut TextureCache::new_for_testing(4096, 1024), + &mut TextureCache::new_for_testing(4096, 1024, FORMAT), &mut gpu_cache, &mut render_task_cache, &mut render_task_tree, diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs index 1003e2459e44..0f1b9b0f9f5c 100644 --- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs @@ -105,8 +105,9 @@ pub struct BlurInstance { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ScalingInstance { - pub task_address: RenderTaskAddress, - pub src_task_address: RenderTaskAddress, + pub target_rect: DeviceRect, + pub source_rect: DeviceIntRect, + pub source_layer: i32, } #[derive(Debug)] diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs index 38ae03e8d816..43a300b3f5a2 100644 --- a/gfx/wr/webrender/src/internal_types.rs +++ b/gfx/wr/webrender/src/internal_types.rs @@ -161,6 +161,20 @@ impl From for Filter { } } +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +pub enum Swizzle { + Rgba, + Bgra, +} + +impl Default for Swizzle { + fn default() -> Self { + Swizzle::Rgba + } +} + /// An ID for a texture that is owned by the `texture_cache` module. /// /// This can include atlases or standalone textures allocated via the texture @@ -210,7 +224,7 @@ pub enum TextureSource { /// Equivalent to `None`, allowing us to avoid using `Option`s everywhere. Invalid, /// An entry in the texture cache. - TextureCache(CacheTextureId), + TextureCache(CacheTextureId, Swizzle), /// An external image texture, mananged by the embedding. External(ExternalImageData), /// The alpha target of the immediately-preceding pass. @@ -220,7 +234,7 @@ pub enum TextureSource { /// A render target from an earlier pass. Unlike the immediately-preceding /// passes, these are not made available automatically, but are instead /// opt-in by the `RenderTask` (see `mark_for_saving()`). - RenderTaskCache(SavedTargetIndex), + RenderTaskCache(SavedTargetIndex, Swizzle), } // See gpu_types.rs where we declare the number of possible documents and diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs index 7110a1163fc5..34b740da8d80 100644 --- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -28,7 +28,7 @@ use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; use crate::print_tree::PrintTreePrinter; use crate::render_backend::DataStores; -use crate::render_task::{ClearMode, RenderTask}; +use crate::render_task::{ClearMode, RenderTargetKind, RenderTask}; use crate::render_task::{RenderTaskId, RenderTaskLocation, BlurTaskCache}; use crate::resource_cache::ResourceCache; use crate::scene::SceneProperties; @@ -38,7 +38,6 @@ use smallvec::SmallVec; use std::{mem, u16}; use std::sync::atomic::{AtomicUsize, Ordering}; use crate::texture_cache::TextureCacheHandle; -use crate::tiling::RenderTargetKind; use crate::util::{ComparableVec, TransformedRectKind, MatrixHelpers, MaxRect, scale_factors}; use crate::filterdata::{FilterDataHandle}; diff --git a/gfx/wr/webrender/src/prim_store/image.rs b/gfx/wr/webrender/src/prim_store/image.rs index ea585772cb5c..b8b86c37b8f2 100644 --- a/gfx/wr/webrender/src/prim_store/image.rs +++ b/gfx/wr/webrender/src/prim_store/image.rs @@ -12,7 +12,7 @@ use crate::display_list_flattener::{CreateShadow, IsVisible}; use crate::frame_builder::FrameBuildingState; use crate::gpu_cache::{GpuCache, GpuDataRequest}; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; -use crate::internal_types::LayoutPrimitiveInfo; +use crate::internal_types::{LayoutPrimitiveInfo}; use crate::prim_store::{ EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData, PrimKey, PrimKeyCommonData, @@ -21,7 +21,7 @@ use crate::prim_store::{ }; use crate::render_task::{ BlitSource, RenderTask, RenderTaskCacheEntryHandle, RenderTaskCacheKey, - RenderTaskCacheKeyKind + RenderTaskCacheKeyKind, RenderTargetKind, }; use crate::resource_cache::{ImageRequest, ResourceCache}; use crate::util::pack_as_float; @@ -88,7 +88,6 @@ impl ImageKey { prim_size: LayoutSize, image: Image, ) -> Self { - ImageKey { common: PrimKeyCommonData { is_backface_visible, @@ -208,7 +207,6 @@ impl ImageData { 0, ); - let inner_size = *size; size.width += padding.horizontal(); size.height += padding.vertical(); @@ -218,6 +216,11 @@ impl ImageData { request, texel_rect: self.sub_rect, }; + let target_kind = if image_properties.descriptor.format.bytes_per_pixel() == 1 { + RenderTargetKind::Alpha + } else { + RenderTargetKind::Color + }; // Request a pre-rendered image task. *handle = Some(frame_state.resource_cache.request_render_task( @@ -233,16 +236,27 @@ impl ImageData { // Create a task to blit from the texture cache to // a normal transient render task surface. This will // copy only the sub-rect, if specified. - let cache_to_target_task = RenderTask::new_blit_with_padding( - inner_size, - &padding, - BlitSource::Image { key: image_cache_key }, - ); + let cache_to_target_task = if false { + // TODO: figure out if/when this can be used + RenderTask::new_blit_with_padding( + *size, + padding, + BlitSource::Image { key: image_cache_key }, + ) + } else { + RenderTask::new_scaling_with_padding( + BlitSource::Image { key: image_cache_key }, + render_tasks, + target_kind, + *size, + padding, + ) + }; let cache_to_target_task_id = render_tasks.add(cache_to_target_task); // Create a task to blit the rect from the child render // task above back into the right spot in the persistent - // render target cache. + // render target cache. let target_to_cache_task = RenderTask::new_blit( *size, BlitSource::RenderTask { diff --git a/gfx/wr/webrender/src/profiler.rs b/gfx/wr/webrender/src/profiler.rs index 848fe13a0a77..9a150ab507a0 100644 --- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -431,20 +431,20 @@ impl FrameProfileCounters { #[derive(Clone)] pub struct TextureCacheProfileCounters { - pub pages_a8_linear: ResourceProfileCounter, - pub pages_a16_linear: ResourceProfileCounter, - pub pages_rgba8_linear: ResourceProfileCounter, - pub pages_rgba8_nearest: ResourceProfileCounter, + pub pages_alpha8_linear: ResourceProfileCounter, + pub pages_alpha16_linear: ResourceProfileCounter, + pub pages_color8_linear: ResourceProfileCounter, + pub pages_color8_nearest: ResourceProfileCounter, pub pages_picture: ResourceProfileCounter, } impl TextureCacheProfileCounters { pub fn new() -> Self { TextureCacheProfileCounters { - pages_a8_linear: ResourceProfileCounter::new("Texture A8 cached pages"), - pages_a16_linear: ResourceProfileCounter::new("Texture A16 cached pages"), - pages_rgba8_linear: ResourceProfileCounter::new("Texture RGBA8 cached pages (L)"), - pages_rgba8_nearest: ResourceProfileCounter::new("Texture RGBA8 cached pages (N)"), + pages_alpha8_linear: ResourceProfileCounter::new("Texture A8 cached pages"), + pages_alpha16_linear: ResourceProfileCounter::new("Texture A16 cached pages"), + pages_color8_linear: ResourceProfileCounter::new("Texture RGBA8 cached pages (L)"), + pages_color8_nearest: ResourceProfileCounter::new("Texture RGBA8 cached pages (N)"), pages_picture: ResourceProfileCounter::new("Picture cached pages"), } } @@ -1227,9 +1227,9 @@ impl Profiler { Profiler::draw_counters( &[ - &backend_profile.resources.texture_cache.pages_a8_linear, - &backend_profile.resources.texture_cache.pages_rgba8_linear, - &backend_profile.resources.texture_cache.pages_rgba8_nearest, + &backend_profile.resources.texture_cache.pages_alpha8_linear, + &backend_profile.resources.texture_cache.pages_color8_linear, + &backend_profile.resources.texture_cache.pages_color8_nearest, &backend_profile.ipc.display_lists, ], debug_renderer, diff --git a/gfx/wr/webrender/src/render_task.rs b/gfx/wr/webrender/src/render_task.rs index 4ec0ca378303..c48efaa075e6 100644 --- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.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::{ImageDescriptor, ImageFormat, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind}; +use api::{ImageDescriptor, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind}; use api::{LineStyle, LineOrientation, ClipMode, DirtyRect, MixBlendMode, ColorF, ColorSpace}; use api::units::*; use crate::border::BorderSegmentCacheKey; @@ -27,7 +27,6 @@ use crate::resource_cache::{CacheItem, ResourceCache}; use std::{ops, mem, usize, f32, i32, u32}; use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction}; use crate::tiling::{RenderPass, RenderTargetIndex}; -use crate::tiling::{RenderTargetKind}; use std::io; @@ -44,6 +43,15 @@ fn render_task_sanity_check(size: &DeviceIntSize) { } } +/// A tag used to identify the output format of a `RenderTarget`. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum RenderTargetKind { + Color, // RGBA8 + Alpha, // R8 +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -449,6 +457,7 @@ pub enum RenderTaskLocation { layer: LayerIndex, /// The target region within the above layer. rect: DeviceIntRect, + }, /// This render task will be drawn to a picture cache texture that is /// persisted between both frames and scenes, if the content remains valid. @@ -479,6 +488,16 @@ impl RenderTaskLocation { RenderTaskLocation::PictureCache { size, .. } => *size, } } + + pub fn to_source_rect(&self) -> (DeviceIntRect, LayerIndex) { + match *self { + RenderTaskLocation::Fixed(rect) => (rect, 0), + RenderTaskLocation::Dynamic(None, _) => panic!("Expected position to be set for the task!"), + RenderTaskLocation::Dynamic(Some((origin, layer)), size) => (DeviceIntRect::new(origin, size), layer.0 as LayerIndex), + RenderTaskLocation::TextureCache { rect, layer, .. } => (rect, layer), + RenderTaskLocation::PictureCache { layer, size, .. } => (size.into(), layer as LayerIndex), + } + } } #[derive(Debug)] @@ -539,7 +558,9 @@ impl BlurTask { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ScalingTask { pub target_kind: RenderTargetKind, + pub image: Option, uv_rect_kind: UvRectKind, + pub padding: DeviceIntSideOffsets, } // Where the source data for a blit task can be found. @@ -821,34 +842,30 @@ impl RenderTask { size: DeviceIntSize, source: BlitSource, ) -> Self { - RenderTask::new_blit_with_padding(size, &DeviceIntSideOffsets::zero(), source) + RenderTask::new_blit_with_padding(size, DeviceIntSideOffsets::zero(), source) } pub fn new_blit_with_padding( - mut size: DeviceIntSize, - padding: &DeviceIntSideOffsets, + padded_size: DeviceIntSize, + padding: DeviceIntSideOffsets, source: BlitSource, ) -> Self { - let mut children = Vec::new(); - // If this blit uses a render task as a source, // ensure it's added as a child task. This will // ensure it gets allocated in the correct pass // and made available as an input when this task // executes. - if let BlitSource::RenderTask { task_id } = source { - children.push(task_id); - } - - size.width += padding.horizontal(); - size.height += padding.vertical(); + let children = match source { + BlitSource::RenderTask { task_id } => vec![task_id], + BlitSource::Image { .. } => vec![], + }; RenderTask::with_dynamic_location( - size, + padded_size, children, RenderTaskKind::Blit(BlitTask { source, - padding: *padding, + padding, }), ClearMode::Transparent, ) @@ -1158,16 +1175,37 @@ impl RenderTask { src_task_id: RenderTaskId, render_tasks: &mut RenderTaskGraph, target_kind: RenderTargetKind, - target_size: DeviceIntSize, + size: DeviceIntSize, ) -> Self { - let uv_rect_kind = render_tasks[src_task_id].uv_rect_kind(); + Self::new_scaling_with_padding( + BlitSource::RenderTask { task_id: src_task_id }, + render_tasks, + target_kind, + size, + DeviceIntSideOffsets::zero(), + ) + } + + pub fn new_scaling_with_padding( + source: BlitSource, + render_tasks: &mut RenderTaskGraph, + target_kind: RenderTargetKind, + padded_size: DeviceIntSize, + padding: DeviceIntSideOffsets, + ) -> Self { + let (uv_rect_kind, children, image) = match source { + BlitSource::RenderTask { task_id } => (render_tasks[task_id].uv_rect_kind(), vec![task_id], None), + BlitSource::Image { key } => (UvRectKind::Rect, vec![], Some(key)), + }; RenderTask::with_dynamic_location( - target_size, - vec![src_task_id], + padded_size, + children, RenderTaskKind::Scaling(ScalingTask { target_kind, + image, uv_rect_kind, + padding, }), ClearMode::DontCare, ) @@ -1932,15 +1970,11 @@ impl RenderTaskCache { } fn alloc_render_task( + render_task: &mut RenderTask, entry: &mut RenderTaskCacheEntry, - render_task_id: RenderTaskId, gpu_cache: &mut GpuCache, texture_cache: &mut TextureCache, - render_tasks: &mut RenderTaskGraph, ) { - let render_task = &mut render_tasks[render_task_id]; - let target_kind = render_task.target_kind(); - // Find out what size to alloc in the texture cache. let size = match render_task.location { RenderTaskLocation::Fixed(..) | @@ -1952,9 +1986,9 @@ impl RenderTaskCache { }; // Select the right texture page to allocate from. - let image_format = match target_kind { - RenderTargetKind::Color => ImageFormat::BGRA8, - RenderTargetKind::Alpha => ImageFormat::R8, + let image_format = match render_task.target_kind() { + RenderTargetKind::Color => texture_cache.shared_color_expected_format(), + RenderTargetKind::Alpha => texture_cache.shared_alpha_expected_format(), }; let descriptor = ImageDescriptor::new( @@ -1992,7 +2026,7 @@ impl RenderTaskCache { // this in the render task. The renderer will draw this // task into the appropriate layer and rect of the texture // cache on this frame. - let (texture_id, texture_layer, uv_rect, _) = + let (texture_id, texture_layer, uv_rect, _, _) = texture_cache.get_cache_location(&entry.handle); render_task.location = RenderTaskLocation::TextureCache { @@ -2039,11 +2073,10 @@ impl RenderTaskCache { cache_entry.is_opaque = is_opaque; RenderTaskCache::alloc_render_task( + &mut render_tasks[render_task_id], cache_entry, - render_task_id, gpu_cache, texture_cache, - render_tasks, ); } diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index 5544447379a2..b11a524aa822 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -53,7 +53,7 @@ use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Te use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot}; use crate::device::{ShaderError, TextureFilter, TextureFlags, VertexUsageHint, VAO, VBO, CustomVAO}; -use crate::device::{ProgramCache}; +use crate::device::ProgramCache; use crate::device::query::GpuTimer; use euclid::{rect, Transform3D, Scale, default}; use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig}; @@ -66,7 +66,7 @@ use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgF use crate::internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError}; use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg}; use crate::internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource}; -use crate::internal_types::{RenderTargetInfo, SavedTargetIndex}; +use crate::internal_types::{RenderTargetInfo, SavedTargetIndex, Swizzle}; use malloc_size_of::MallocSizeOfOps; use crate::picture::{RecordedDirtyRegion, TILE_SIZE_WIDTH, TILE_SIZE_HEIGHT}; use crate::prim_store::DeferredResolve; @@ -77,12 +77,16 @@ use crate::device::query::{GpuProfiler, GpuDebugMethod}; use rayon::{ThreadPool, ThreadPoolBuilder}; use crate::record::ApiRecordingReceiver; use crate::render_backend::{FrameId, RenderBackend}; +use crate::render_task::{RenderTargetKind, RenderTask, RenderTaskData, RenderTaskKind, RenderTaskGraph}; +use crate::resource_cache::ResourceCache; use crate::scene_builder::{SceneBuilder, LowPrioritySceneBuilder}; use crate::screen_capture::AsyncScreenshotGrabber; use crate::shade::{Shaders, WrShaders}; use smallvec::SmallVec; -use crate::render_task::{RenderTask, RenderTaskData, RenderTaskKind, RenderTaskGraph}; -use crate::resource_cache::ResourceCache; +use crate::texture_cache::TextureCache; +use crate::tiling::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget}; +use crate::tiling::{BlitJob, BlitJobSource, RenderPassKind, RenderTargetList}; +use crate::tiling::{Frame, RenderTarget, TextureCacheRenderTarget}; use crate::util::drain_filter; use std; @@ -100,11 +104,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver}; use std::thread; use std::cell::RefCell; -use crate::texture_cache::TextureCache; use thread_profiler::{register_thread_with_profiler, write_profile}; -use crate::tiling::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget}; -use crate::tiling::{BlitJob, BlitJobSource, RenderPassKind, RenderTargetList}; -use crate::tiling::{Frame, RenderTarget, RenderTargetKind, TextureCacheRenderTarget}; use time::precise_time_ns; cfg_if! { @@ -567,14 +567,19 @@ pub(crate) mod desc { ], instance_attributes: &[ VertexAttribute { - name: "aScaleRenderTaskAddress", - count: 1, - kind: VertexAttributeKind::U16, + name: "aScaleTargetRect", + count: 4, + kind: VertexAttributeKind::F32, }, VertexAttribute { - name: "aScaleSourceTaskAddress", + name: "aScaleSourceRect", + count: 4, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aScaleSourceLayer", count: 1, - kind: VertexAttributeKind::U16, + kind: VertexAttributeKind::I32, }, ], }; @@ -950,7 +955,7 @@ impl TextureResolver { let dummy_cache_texture = device .create_texture( TextureTarget::Array, - ImageFormat::BGRA8, + ImageFormat::RGBA8, 1, 1, TextureFilter::Linear, @@ -1061,37 +1066,45 @@ impl TextureResolver { } // Bind a source texture to the device. - fn bind(&self, texture_id: &TextureSource, sampler: TextureSampler, device: &mut Device) { + fn bind(&self, texture_id: &TextureSource, sampler: TextureSampler, device: &mut Device) -> Swizzle { match *texture_id { - TextureSource::Invalid => {} + TextureSource::Invalid => { + Swizzle::default() + } TextureSource::PrevPassAlpha => { let texture = match self.prev_pass_alpha { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, }; - device.bind_texture(sampler, texture); + let swizzle = Swizzle::default(); + device.bind_texture(sampler, texture, swizzle); + swizzle } TextureSource::PrevPassColor => { let texture = match self.prev_pass_color { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, }; - device.bind_texture(sampler, texture); + let swizzle = Swizzle::default(); + device.bind_texture(sampler, texture, swizzle); + swizzle } TextureSource::External(external_image) => { let texture = self.external_images .get(&(external_image.id, external_image.channel_index)) .expect(&format!("BUG: External image should be resolved by now")); device.bind_external_texture(sampler, texture); + Swizzle::default() } - TextureSource::TextureCache(index) => { + TextureSource::TextureCache(index, swizzle) => { let texture = &self.texture_cache_map[&index]; - device.bind_texture(sampler, texture); + device.bind_texture(sampler, texture, swizzle); + swizzle } - TextureSource::RenderTaskCache(saved_index) => { + TextureSource::RenderTaskCache(saved_index, swizzle) => { if saved_index.0 < self.saved_targets.len() { let texture = &self.saved_targets[saved_index.0]; - device.bind_texture(sampler, texture) + device.bind_texture(sampler, texture, swizzle) } else { // Check if this saved index is referring to a the prev pass if Some(saved_index) == self.prev_pass_color.as_ref().and_then(|at| at.saved_index) { @@ -1099,15 +1112,16 @@ impl TextureResolver { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, }; - device.bind_texture(sampler, texture); + device.bind_texture(sampler, texture, swizzle); } else if Some(saved_index) == self.prev_pass_alpha.as_ref().and_then(|at| at.saved_index) { let texture = match self.prev_pass_alpha { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, }; - device.bind_texture(sampler, texture); + device.bind_texture(sampler, texture, swizzle); } } + swizzle } } } @@ -1115,29 +1129,31 @@ impl TextureResolver { // 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: &TextureSource) -> Option<&Texture> { + fn resolve(&self, texture_id: &TextureSource) -> Option<(&Texture, Swizzle)> { match *texture_id { TextureSource::Invalid => None, - TextureSource::PrevPassAlpha => Some( + TextureSource::PrevPassAlpha => Some(( match self.prev_pass_alpha { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, - } - ), - TextureSource::PrevPassColor => Some( + }, + Swizzle::default(), + )), + TextureSource::PrevPassColor => Some(( match self.prev_pass_color { Some(ref at) => &at.texture, None => &self.dummy_cache_texture, - } - ), + }, + Swizzle::default(), + )), TextureSource::External(..) => { panic!("BUG: External textures cannot be resolved, they can only be bound."); } - TextureSource::TextureCache(index) => { - Some(&self.texture_cache_map[&index]) + TextureSource::TextureCache(index, swizzle) => { + Some((&self.texture_cache_map[&index], swizzle)) } - TextureSource::RenderTaskCache(saved_index) => { - Some(&self.saved_targets[saved_index.0]) + TextureSource::RenderTaskCache(saved_index, swizzle) => { + Some((&self.saved_targets[saved_index.0], swizzle)) } } } @@ -1836,9 +1852,13 @@ impl Renderer { options.upload_method.clone(), options.cached_programs.take(), options.allow_pixel_local_storage_support, + options.allow_texture_storage_support, + options.allow_texture_swizzling, options.dump_shader_source.take(), ); + let color_cache_formats = device.preferred_color_formats(); + let bgra_swizzle = device.bgra_swizzle(); let supports_dual_source_blending = match gl_type { gl::GlType::Gl => device.supports_extension("GL_ARB_blend_func_extended") && device.supports_extension("GL_ARB_explicit_attrib_location"), @@ -2145,6 +2165,8 @@ impl Renderer { &[] }, start_size, + color_cache_formats, + bgra_swizzle, ); let glyph_cache = GlyphCache::new(max_glyph_cache_size); @@ -2295,6 +2317,10 @@ impl Renderer { } } + pub fn preferred_color_format(&self) -> ImageFormat { + self.device.preferred_color_formats().external + } + pub fn flush_pipeline_info(&mut self) -> PipelineInfo { mem::replace(&mut self.pipeline_info, PipelineInfo::default()) } @@ -3069,6 +3095,7 @@ impl Renderer { self.device.bind_texture( TextureSampler::GpuCache, self.gpu_cache_texture.texture.as_ref().unwrap(), + Swizzle::default(), ); } @@ -3229,17 +3256,26 @@ impl Renderer { textures: &BatchTextures, stats: &mut RendererStats, ) { + let mut swizzles = [Swizzle::default(); 3]; for i in 0 .. textures.colors.len() { - self.texture_resolver.bind( + let swizzle = self.texture_resolver.bind( &textures.colors[i], TextureSampler::color(i), &mut self.device, ); + if cfg!(debug_assertions) { + swizzles[i] = swizzle; + for j in 0 .. i { + if textures.colors[j] == textures.colors[i] && swizzles[j] != swizzle { + error!("Swizzling conflict in {:?}", textures); + } + } + } } // TODO: this probably isn't the best place for this. if let Some(ref texture) = self.dither_matrix_texture { - self.device.bind_texture(TextureSampler::Dither, texture); + self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default()); } self.draw_instanced_batch_with_previously_bound_textures(data, vertex_array_kind, stats) @@ -3295,7 +3331,7 @@ impl Renderer { self.device.disable_scissor(); } - let cache_texture = self.texture_resolver + let (cache_texture, _) = self.texture_resolver .resolve(&TextureSource::PrevPassColor) .unwrap(); @@ -3356,12 +3392,8 @@ impl Renderer { } } - //TODO: make this nicer. Currently we can't accept `&mut self` because the `DrawTarget` parameter - // needs to borrow self.texture_resolver fn handle_blits( - gpu_profile: &mut GpuProfiler, - device: &mut Device, - texture_resolver: &TextureResolver, + &mut self, blits: &[BlitJob], render_tasks: &RenderTaskGraph, draw_target: DrawTarget, @@ -3371,7 +3403,7 @@ impl Renderer { return; } - let _timer = gpu_profile.start_timer(GPU_TAG_BLIT); + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT); // TODO(gw): For now, we don't bother batching these by source texture. // If if ever shows up as an issue, we can easily batch them. @@ -3390,17 +3422,23 @@ impl Renderer { (TextureSource::PrevPassColor, layer.0, source_rect) } }; + debug_assert_eq!(source_rect.size, blit.target_rect.size); - let texture = texture_resolver + let (texture, swizzle) = self.texture_resolver .resolve(&source) .expect("BUG: invalid source texture"); + + if swizzle != Swizzle::default() { + error!("Swizzle {:?} can't be handled by a blit", swizzle); + } + let read_target = DrawTarget::from_texture( texture, layer, false, ); - device.blit_render_target( + self.device.blit_render_target( read_target.into(), read_target.to_framebuffer_rect(source_rect), draw_target, @@ -3412,8 +3450,7 @@ impl Renderer { fn handle_scaling( &mut self, - scalings: &[ScalingInstance], - source: TextureSource, + scalings: &FastHashMap>, projection: &default::Transform3D, stats: &mut RendererStats, ) { @@ -3432,12 +3469,14 @@ impl Renderer { &mut self.renderer_errors, ); - self.draw_instanced_batch( - &scalings, - VertexArrayKind::Scale, - &BatchTextures::color(source), - stats, - ); + for (source, instances) in scalings { + self.draw_instanced_batch( + instances, + VertexArrayKind::Scale, + &BatchTextures::color(*source), + stats, + ); + } } fn handle_svg_filters( @@ -3791,8 +3830,7 @@ impl Renderer { } // Handle any blits from the texture cache to this target. - Self::handle_blits( - &mut self.gpu_profile, &mut self.device, &self.texture_resolver, + self.handle_blits( &target.blits, render_tasks, draw_target, &content_origin, ); @@ -3830,7 +3868,6 @@ impl Renderer { self.handle_scaling( &target.scalings, - TextureSource::PrevPassColor, projection, stats, ); @@ -4050,7 +4087,6 @@ impl Renderer { self.handle_scaling( &target.scalings, - TextureSource::PrevPassAlpha, projection, stats, ); @@ -4094,9 +4130,9 @@ impl Renderer { render_tasks: &RenderTaskGraph, stats: &mut RendererStats, ) { - let texture_source = TextureSource::TextureCache(*texture); + let texture_source = TextureSource::TextureCache(*texture, Swizzle::default()); let projection = { - let texture = self.texture_resolver + let (texture, _) = self.texture_resolver .resolve(&texture_source) .expect("BUG: invalid target texture"); let target_size = texture.get_dimensions(); @@ -4117,7 +4153,7 @@ impl Renderer { self.set_blend(false, FramebufferKind::Other); { - let texture = self.texture_resolver + let (texture, _) = self.texture_resolver .resolve(&texture_source) .expect("BUG: invalid target texture"); let draw_target = DrawTarget::from_texture( @@ -4140,8 +4176,7 @@ impl Renderer { } // Handle any blits to this texture from child tasks. - Self::handle_blits( - &mut self.gpu_profile, &mut self.device, &self.texture_resolver, + self.handle_blits( &target.blits, render_tasks, draw_target, &DeviceIntPoint::zero(), ); } @@ -4295,7 +4330,7 @@ impl Renderer { let texture = match image.source { ExternalImageSource::NativeTexture(texture_id) => { - ExternalTexture::new(texture_id, texture_target) + ExternalTexture::new(texture_id, texture_target, Swizzle::default()) } ExternalImageSource::Invalid => { warn!("Invalid ext-image"); @@ -4305,7 +4340,7 @@ impl Renderer { ext_image.channel_index ); // Just use 0 as the gl handle for this failed case. - ExternalTexture::new(0, texture_target) + ExternalTexture::new(0, texture_target, Swizzle::default()) } ExternalImageSource::RawData(_) => { panic!("Raw external data is not expected for deferred resolves!"); @@ -4430,6 +4465,7 @@ impl Renderer { self.device.bind_texture( TextureSampler::PrimitiveHeadersF, &self.prim_header_f_texture.texture(), + Swizzle::default(), ); self.prim_header_i_texture.update( @@ -4439,6 +4475,7 @@ impl Renderer { self.device.bind_texture( TextureSampler::PrimitiveHeadersI, &self.prim_header_i_texture.texture(), + Swizzle::default(), ); self.transforms_texture.update( @@ -4448,6 +4485,7 @@ impl Renderer { self.device.bind_texture( TextureSampler::TransformPalette, &self.transforms_texture.texture(), + Swizzle::default(), ); self.render_task_texture @@ -4455,6 +4493,7 @@ impl Renderer { self.device.bind_texture( TextureSampler::RenderTasks, &self.render_task_texture.texture(), + Swizzle::default(), ); debug_assert!(self.texture_resolver.prev_pass_alpha.is_none()); @@ -4571,7 +4610,7 @@ impl Renderer { for picture_target in picture_cache { stats.color_target_count += 1; - let texture = self.texture_resolver + let (texture, _) = self.texture_resolver .resolve(&picture_target.texture) .expect("bug"); let draw_target = DrawTarget::from_texture( @@ -5477,6 +5516,13 @@ pub struct RendererOptions { /// and not complete. This option will probably be removed once support is /// complete, and WR can implicitly choose whether to make use of PLS. pub allow_pixel_local_storage_support: bool, + /// If true, allow textures to be initialized with glTexStorage. + /// This affects VRAM consumption and data upload paths. + pub allow_texture_storage_support: bool, + /// If true, we allow the data uploaded in a different format from the + /// one expected by the driver, pretending the format is matching, and + /// swizzling the components on all the shader sampling. + pub allow_texture_swizzling: bool, /// Number of batches to look back in history for adding the current /// transparent instance into. pub batch_lookback_count: usize, @@ -5526,6 +5572,8 @@ impl Default for RendererOptions { allow_dual_source_blending: true, allow_advanced_blend_equation: false, allow_pixel_local_storage_support: false, + allow_texture_storage_support: true, + allow_texture_swizzling: true, batch_lookback_count: DEFAULT_BATCH_LOOKBACK_COUNT, // For backwards compatibility we set this to true by default, so // that if the debugger feature is enabled, the debug server will diff --git a/gfx/wr/webrender/src/resource_cache.rs b/gfx/wr/webrender/src/resource_cache.rs index 04b91f762af1..ae82ea68f6ea 100644 --- a/gfx/wr/webrender/src/resource_cache.rs +++ b/gfx/wr/webrender/src/resource_cache.rs @@ -555,7 +555,10 @@ impl ResourceCache { user_data: Option<[f32; 3]>, is_opaque: bool, f: F, - ) -> RenderTaskCacheEntryHandle where F: FnOnce(&mut RenderTaskGraph) -> RenderTaskId { + ) -> RenderTaskCacheEntryHandle + where + F: FnOnce(&mut RenderTaskGraph) -> RenderTaskId, + { self.cached_render_tasks.request_render_task( key, &mut self.texture_cache, @@ -563,7 +566,7 @@ impl ResourceCache { render_tasks, user_data, is_opaque, - |render_task_tree| Ok(f(render_task_tree)) + |render_graph| Ok(f(render_graph)) ).expect("Failed to request a render task from the resource cache!") } @@ -923,6 +926,10 @@ impl ResourceCache { _ => {} } + if image.descriptor.format != descriptor.format { + // could be a stronger warning/error? + trace!("Format change {:?} -> {:?}", image.descriptor.format, descriptor.format); + } *image = ImageResource { descriptor, data, @@ -2196,6 +2203,8 @@ impl ResourceCache { self.texture_cache.max_texture_layers(), &self.texture_cache.picture_tile_sizes(), DeviceIntSize::zero(), + self.texture_cache.color_formats(), + self.texture_cache.bgra_swizzle(), ); } } diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs index de96445f3bf4..84c39f2f14c2 100644 --- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -7,12 +7,15 @@ use api::{DebugFlags, ImageDescriptor}; use api::units::*; #[cfg(test)] use api::IdNamespace; -use crate::device::{TextureFilter, total_gpu_bytes_allocated}; +use crate::device::{TextureFilter, TextureFormatPair, total_gpu_bytes_allocated}; use crate::freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; use crate::gpu_cache::{GpuCache, GpuCacheHandle}; use crate::gpu_types::{ImageSource, UvRectKind}; -use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, TextureUpdateList, TextureUpdateSource}; -use crate::internal_types::{TextureSource, TextureCacheAllocInfo, TextureCacheUpdate}; +use crate::internal_types::{ + CacheTextureId, FastHashMap, LayerIndex, Swizzle, + TextureUpdateList, TextureUpdateSource, TextureSource, + TextureCacheAllocInfo, TextureCacheUpdate, +}; use crate::profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use crate::render_backend::{FrameId, FrameStamp}; use crate::resource_cache::{CacheItem, CachedImageData}; @@ -28,7 +31,7 @@ pub const TEXTURE_REGION_DIMENSIONS: i32 = 512; const PICTURE_TEXTURE_ADD_SLICES: usize = 4; /// The chosen image format for picture tiles. -const PICTURE_TILE_FORMAT: ImageFormat = ImageFormat::BGRA8; +const PICTURE_TILE_FORMAT: ImageFormat = ImageFormat::RGBA8; /// The number of pixels in a region. Derived from the above. const TEXTURE_REGION_PIXELS: usize = @@ -108,9 +111,10 @@ struct CacheEntry { last_access: FrameStamp, /// Handle to the resource rect in the GPU cache. uv_rect_handle: GpuCacheHandle, - /// Image format of the item. - format: ImageFormat, + /// Image format of the data that the entry expects. + input_format: ImageFormat, filter: TextureFilter, + swizzle: Swizzle, /// The actual device texture ID this is part of. texture_id: CacheTextureId, /// Optional notice when the entry is evicted from the cache. @@ -127,6 +131,7 @@ impl CacheEntry { texture_id: CacheTextureId, last_access: FrameStamp, params: &CacheAllocParams, + swizzle: Swizzle, ) -> Self { CacheEntry { size: params.descriptor.size, @@ -134,8 +139,9 @@ impl CacheEntry { last_access, details: EntryDetails::Standalone, texture_id, - format: params.descriptor.format, + input_format: params.descriptor.format, filter: params.filter, + swizzle, uv_rect_handle: GpuCacheHandle::new(), eviction_notice: None, uv_rect_kind: params.uv_rect_kind, @@ -227,39 +233,39 @@ impl EvictionNotice { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct SharedTextures { - array_rgba8_nearest: TextureArray, - array_a8_linear: TextureArray, - array_a16_linear: TextureArray, - array_rgba8_linear: TextureArray, + array_color8_nearest: TextureArray, + array_alpha8_linear: TextureArray, + array_alpha16_linear: TextureArray, + array_color8_linear: TextureArray, } impl SharedTextures { /// Mints a new set of shared textures. - fn new() -> Self { + fn new(color_formats: TextureFormatPair) -> Self { Self { // Used primarily for cached shadow masks. There can be lots of // these on some pages like francine, but most pages don't use it // much. - array_a8_linear: TextureArray::new( - ImageFormat::R8, + array_alpha8_linear: TextureArray::new( + TextureFormatPair::from(ImageFormat::R8), TextureFilter::Linear, ), // Used for experimental hdr yuv texture support, but not used in // production Firefox. - array_a16_linear: TextureArray::new( - ImageFormat::R16, + array_alpha16_linear: TextureArray::new( + TextureFormatPair::from(ImageFormat::R16), TextureFilter::Linear, ), // The primary cache for images, glyphs, etc. - array_rgba8_linear: TextureArray::new( - ImageFormat::BGRA8, + array_color8_linear: TextureArray::new( + color_formats.clone(), TextureFilter::Linear, ), // Used for image-rendering: crisp. This is mostly favicons, which // are small. Some other images use it too, but those tend to be // larger than 512x512 and thus don't use the shared cache anyway. - array_rgba8_nearest: TextureArray::new( - ImageFormat::BGRA8, + array_color8_nearest: TextureArray::new( + color_formats, TextureFilter::Nearest, ), } @@ -267,36 +273,58 @@ impl SharedTextures { /// Returns the cumulative number of GPU bytes consumed by all the shared textures. fn size_in_bytes(&self) -> usize { - self.array_a8_linear.size_in_bytes() + - self.array_a16_linear.size_in_bytes() + - self.array_rgba8_linear.size_in_bytes() + - self.array_rgba8_nearest.size_in_bytes() + self.array_alpha8_linear.size_in_bytes() + + self.array_alpha16_linear.size_in_bytes() + + self.array_color8_linear.size_in_bytes() + + self.array_color8_nearest.size_in_bytes() } /// Returns the cumulative number of GPU bytes consumed by empty regions. fn empty_region_bytes(&self) -> usize { - self.array_a8_linear.empty_region_bytes() + - self.array_a16_linear.empty_region_bytes() + - self.array_rgba8_linear.empty_region_bytes() + - self.array_rgba8_nearest.empty_region_bytes() + self.array_alpha8_linear.empty_region_bytes() + + self.array_alpha16_linear.empty_region_bytes() + + self.array_color8_linear.empty_region_bytes() + + self.array_color8_nearest.empty_region_bytes() } /// Clears each texture in the set, with the given set of pending updates. fn clear(&mut self, updates: &mut TextureUpdateList) { - self.array_a8_linear.clear(updates); - self.array_a16_linear.clear(updates); - self.array_rgba8_linear.clear(updates); - self.array_rgba8_nearest.clear(updates); + self.array_alpha8_linear.clear(updates); + self.array_alpha16_linear.clear(updates); + self.array_color8_linear.clear(updates); + self.array_color8_nearest.clear(updates); } /// Returns a mutable borrow for the shared texture array matching the parameters. - fn select(&mut self, format: ImageFormat, filter: TextureFilter) -> &mut TextureArray { - match (format, filter) { - (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear, - (ImageFormat::R16, TextureFilter::Linear) => &mut self.array_a16_linear, - (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear, - (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest, - (_, _) => unreachable!(), + fn select( + &mut self, external_format: ImageFormat, filter: TextureFilter + ) -> (&mut TextureArray, Swizzle) { + match external_format { + ImageFormat::R8 => { + assert_eq!(filter, TextureFilter::Linear); + (&mut self.array_alpha8_linear, Swizzle::default()) + } + ImageFormat::R16 => { + assert_eq!(filter, TextureFilter::Linear); + (&mut self.array_alpha16_linear, Swizzle::default()) + } + ImageFormat::RGBA8 | + ImageFormat::BGRA8 => { + let array = match filter { + TextureFilter::Linear => &mut self.array_color8_linear, + TextureFilter::Nearest => &mut self.array_color8_nearest, + _ => panic!("Unexpexcted filter {:?}", filter), + }; + let swizzle = if array.formats.external == external_format { + Swizzle::default() + } else { + // TODO: figure out how to check this properly + //assert_eq!(array.formats.internal, external_format); + Swizzle::Bgra + }; + (array, swizzle) + } + _ => panic!("Unexpected format {:?}", external_format), } } } @@ -488,6 +516,9 @@ pub struct TextureCache { /// Maximum number of texture layers supported by hardware. max_texture_layers: usize, + /// Swizzle required on sampling a texture with BGRA8 format. + bgra_swizzle: Swizzle, + /// The current set of debug flags. debug_flags: DebugFlags, @@ -531,6 +562,8 @@ impl TextureCache { mut max_texture_layers: usize, picture_tile_sizes: &[DeviceIntSize], initial_size: DeviceIntSize, + color_formats: TextureFormatPair, + bgra_swizzle: Swizzle, ) -> Self { if cfg!(target_os = "macos") { // On MBP integrated Intel GPUs, texture arrays appear to be @@ -585,13 +618,19 @@ impl TextureCache { picture_textures.push(picture_texture); } + // Shared texture cache controls swizzling on a per-entry basis, assuming that + // the texture as a whole doesn't need to be swizzled (but only some entries do). + // It would be possible to support this, but not needed at the moment. + assert!(color_formats.internal != ImageFormat::BGRA8 || bgra_swizzle == Swizzle::default()); + TextureCache { - shared_textures: SharedTextures::new(), + shared_textures: SharedTextures::new(color_formats), picture_textures, reached_reclaim_threshold: None, entries: FreeList::new(), max_texture_size, max_texture_layers, + bgra_swizzle, debug_flags: DebugFlags::empty(), next_id: CacheTextureId(next_texture_id), pending_updates, @@ -606,8 +645,19 @@ impl TextureCache { /// is useful for avoiding panics when instantiating the `TextureCache` /// directly from unit test code. #[cfg(test)] - pub fn new_for_testing(max_texture_size: i32, max_texture_layers: usize) -> Self { - let mut cache = Self::new(max_texture_size, max_texture_layers, &[], DeviceIntSize::zero()); + pub fn new_for_testing( + max_texture_size: i32, + max_texture_layers: usize, + image_format: ImageFormat, + ) -> Self { + let mut cache = Self::new( + max_texture_size, + max_texture_layers, + &[], + DeviceIntSize::zero(), + TextureFormatPair::from(image_format), + Swizzle::default(), + ); let mut now = FrameStamp::first(DocumentId::new(IdNamespace(1), 1)); now.advance(); cache.begin_frame(now); @@ -765,14 +815,14 @@ impl TextureCache { self.expire_old_entries(EntryKind::Standalone, threshold); self.expire_old_entries(EntryKind::Picture, threshold); - self.shared_textures.array_a8_linear - .update_profile(&mut texture_cache_profile.pages_a8_linear); - self.shared_textures.array_a16_linear - .update_profile(&mut texture_cache_profile.pages_a16_linear); - self.shared_textures.array_rgba8_linear - .update_profile(&mut texture_cache_profile.pages_rgba8_linear); - self.shared_textures.array_rgba8_nearest - .update_profile(&mut texture_cache_profile.pages_rgba8_nearest); + self.shared_textures.array_alpha8_linear + .update_profile(&mut texture_cache_profile.pages_alpha8_linear); + self.shared_textures.array_alpha16_linear + .update_profile(&mut texture_cache_profile.pages_alpha16_linear); + self.shared_textures.array_color8_linear + .update_profile(&mut texture_cache_profile.pages_color8_linear); + self.shared_textures.array_color8_nearest + .update_profile(&mut texture_cache_profile.pages_color8_nearest); // For now, this profile counter just accumulates the slices and bytes // from all picture cache texture arrays. @@ -830,6 +880,16 @@ impl TextureCache { self.picture_textures.iter().map(|pt| pt.size).collect() } + #[cfg(feature = "replay")] + pub fn color_formats(&self) -> TextureFormatPair { + self.shared_textures.array_color8_linear.formats.clone() + } + + #[cfg(feature = "replay")] + pub fn bgra_swizzle(&self) -> Swizzle { + self.bgra_swizzle + } + pub fn pending_updates(&mut self) -> TextureUpdateList { mem::replace(&mut self.pending_updates, TextureUpdateList::new()) } @@ -858,7 +918,7 @@ impl TextureCache { // - Exists in the cache but dimensions / format have changed. let realloc = match self.entries.get_opt(handle) { Some(entry) => { - entry.size != descriptor.size || entry.format != descriptor.format + entry.size != descriptor.size || entry.input_format != descriptor.format } None => { // Not allocated, or was previously allocated but has been evicted. @@ -920,7 +980,7 @@ impl TextureCache { // or otherwise indicate the handle is invalid. pub fn get_allocated_size(&self, handle: &TextureCacheHandle) -> Option { self.entries.get_opt(handle).map(|entry| { - (entry.format.bytes_per_pixel() * entry.size.area()) as usize + (entry.input_format.bytes_per_pixel() * entry.size.area()) as usize }) } @@ -930,10 +990,10 @@ impl TextureCache { // This function will assert in debug modes if the caller // tries to get a handle that was not requested this frame. pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem { - let (texture_id, layer_index, uv_rect, uv_rect_handle) = self.get_cache_location(handle); + let (texture_id, layer_index, uv_rect, swizzle, uv_rect_handle) = self.get_cache_location(handle); CacheItem { uv_rect_handle, - texture_id: TextureSource::TextureCache(texture_id), + texture_id: TextureSource::TextureCache(texture_id, swizzle), uv_rect, texture_layer: layer_index as i32, } @@ -947,7 +1007,7 @@ impl TextureCache { pub fn get_cache_location( &self, handle: &TextureCacheHandle, - ) -> (CacheTextureId, LayerIndex, DeviceIntRect, GpuCacheHandle) { + ) -> (CacheTextureId, LayerIndex, DeviceIntRect, Swizzle, GpuCacheHandle) { let entry = self.entries .get_opt(handle) .expect("BUG: was dropped from cache or not updated!"); @@ -956,6 +1016,7 @@ impl TextureCache { (entry.texture_id, layer_index as usize, DeviceIntRect::new(origin, entry.size), + entry.swizzle, entry.uv_rect_handle) } @@ -1068,7 +1129,7 @@ impl TextureCache { } EntryDetails::Cache { origin, layer_index } => { // Free the block in the given region. - let texture_array = self.shared_textures.select(entry.format, entry.filter); + let (texture_array, _swizzle) = self.shared_textures.select(entry.input_format, entry.filter); let region = &mut texture_array.regions[layer_index]; if self.debug_flags.contains( @@ -1091,10 +1152,10 @@ impl TextureCache { // Attempt to allocate a block from the shared cache. fn allocate_from_shared_cache( &mut self, - params: &CacheAllocParams + params: &CacheAllocParams, ) -> Option { // Mutably borrow the correct texture. - let texture_array = self.shared_textures.select( + let (texture_array, swizzle) = self.shared_textures.select( params.descriptor.format, params.filter, ); @@ -1108,7 +1169,7 @@ impl TextureCache { let info = TextureCacheAllocInfo { width: TEXTURE_REGION_DIMENSIONS, height: TEXTURE_REGION_DIMENSIONS, - format: params.descriptor.format, + format: texture_array.formats.internal, filter: texture_array.filter, layer_count: 1, is_shared_cache: true, @@ -1122,32 +1183,18 @@ impl TextureCache { // Do the allocation. This can fail and return None // if there are no free slots or regions available. - texture_array.alloc(params, self.now) + texture_array.alloc(params, self.now, swizzle) } // Returns true if the given image descriptor *may* be // placed in the shared texture cache. pub fn is_allowed_in_shared_cache( &self, - filter: TextureFilter, + _filter: TextureFilter, descriptor: &ImageDescriptor, ) -> bool { let mut allowed_in_shared_cache = true; - // TODO(sotaro): For now, anything that requests RGBA8 just fails to allocate - // in a texture page, and gets a standalone texture. - if descriptor.format == ImageFormat::RGBA8 { - allowed_in_shared_cache = false; - } - - // TODO(gw): For now, anything that requests nearest filtering and isn't BGRA8 - // just fails to allocate in a texture page, and gets a standalone - // texture. This is probably rare enough that it can be fixed up later. - if filter == TextureFilter::Nearest && - descriptor.format != ImageFormat::BGRA8 { - allowed_in_shared_cache = false; - } - // Anything larger than TEXTURE_REGION_DIMENSIONS 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 @@ -1180,10 +1227,18 @@ impl TextureCache { }; self.pending_updates.push_alloc(texture_id, info); + // Special handing for BGRA8 textures that may need to be swizzled. + let swizzle = if params.descriptor.format == ImageFormat::BGRA8 { + self.bgra_swizzle + } else { + Swizzle::default() + }; + CacheEntry::new_standalone( texture_id, self.now, params, + swizzle, ) } @@ -1219,7 +1274,10 @@ impl TextureCache { // realistically should only happen on mac, where we have a tighter // layer limit). let num_regions = self.shared_textures - .select(params.descriptor.format, params.filter).regions.len(); + .select(params.descriptor.format, params.filter) + .0 + .regions + .len(); let threshold = if num_regions == self.max_texture_layers { EvictionThresholdBuilder::new(self.now).max_frames(1).build() } else { @@ -1234,14 +1292,14 @@ impl TextureCache { let added_layer = { // If we've hit our layer limit, allocate standalone. - let texture_array = + let (texture_array, _swizzle) = self.shared_textures.select(params.descriptor.format, params.filter); // Add a layer, unless we've hit our limit. if num_regions < self.max_texture_layers as usize { let info = TextureCacheAllocInfo { width: TEXTURE_REGION_DIMENSIONS, height: TEXTURE_REGION_DIMENSIONS, - format: params.descriptor.format, + format: texture_array.formats.internal, filter: texture_array.filter, layer_count: (num_regions + 1) as i32, is_shared_cache: true, @@ -1352,6 +1410,14 @@ impl TextureCache { .expect("BUG: handle must be valid now") .update_gpu_cache(gpu_cache); } + + pub fn shared_alpha_expected_format(&self) -> ImageFormat { + self.shared_textures.array_alpha8_linear.formats.external + } + + pub fn shared_color_expected_format(&self) -> ImageFormat { + self.shared_textures.array_color8_linear.formats.external + } } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -1500,7 +1566,7 @@ impl TextureRegion { #[cfg_attr(feature = "replay", derive(Deserialize))] struct TextureArray { filter: TextureFilter, - format: ImageFormat, + formats: TextureFormatPair, regions: Vec, empty_regions: usize, texture_id: Option, @@ -1508,11 +1574,11 @@ struct TextureArray { impl TextureArray { fn new( - format: ImageFormat, + formats: TextureFormatPair, filter: TextureFilter, ) -> Self { TextureArray { - format, + formats, filter, regions: Vec::new(), empty_regions: 0, @@ -1522,13 +1588,13 @@ impl TextureArray { /// Returns the number of GPU bytes consumed by this texture array. fn size_in_bytes(&self) -> usize { - let bpp = self.format.bytes_per_pixel() as usize; + let bpp = self.formats.internal.bytes_per_pixel() as usize; self.regions.len() * TEXTURE_REGION_PIXELS * bpp } /// Returns the number of GPU bytes consumed by empty regions. fn empty_region_bytes(&self) -> usize { - let bpp = self.format.bytes_per_pixel() as usize; + let bpp = self.formats.internal.bytes_per_pixel() as usize; self.empty_regions * TEXTURE_REGION_PIXELS * bpp } @@ -1557,6 +1623,7 @@ impl TextureArray { &mut self, params: &CacheAllocParams, now: FrameStamp, + swizzle: Swizzle, ) -> Option { // Quantize the size of the allocation to select a region to // allocate from. @@ -1610,8 +1677,9 @@ impl TextureArray { last_access: now, details, uv_rect_handle: GpuCacheHandle::new(), - format: self.format, + input_format: params.descriptor.format, filter: self.filter, + swizzle, texture_id: self.texture_id.unwrap(), eviction_notice: None, uv_rect_kind: params.uv_rect_kind, @@ -1694,8 +1762,9 @@ impl WholeTextureArray { layer_index, }, uv_rect_handle, - format: self.format, + input_format: self.format, filter: self.filter, + swizzle: Swizzle::default(), texture_id, eviction_notice: None, uv_rect_kind: UvRectKind::Rect, diff --git a/gfx/wr/webrender/src/tiling.rs b/gfx/wr/webrender/src/tiling.rs index ecede3521458..3345b190e347 100644 --- a/gfx/wr/webrender/src/tiling.rs +++ b/gfx/wr/webrender/src/tiling.rs @@ -14,14 +14,14 @@ use crate::frame_builder::FrameGlobalResources; use crate::gpu_cache::{GpuCache, GpuCacheAddress}; use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; use crate::gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator}; -use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource, Filter}; +use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex, Swizzle, TextureSource, Filter}; use crate::picture::{RecordedDirtyRegion, SurfaceInfo}; use crate::prim_store::gradient::GRADIENT_FP_STOPS; use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask}; use crate::profiler::FrameProfileCounters; use crate::render_backend::{DataStores, FrameId}; -use crate::render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind, SvgFilterTask, SvgFilterInfo}; -use crate::render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskGraph, ScalingTask}; +use crate::render_task::{BlitSource, RenderTargetKind, RenderTaskAddress, RenderTask, RenderTaskId, RenderTaskKind}; +use crate::render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskGraph, ScalingTask, SvgFilterTask, SvgFilterInfo}; use crate::resource_cache::ResourceCache; use std::{cmp, usize, f32, i32, mem}; use crate::texture_allocator::{ArrayAllocationTracker, FreeRectSlice}; @@ -121,15 +121,6 @@ pub trait RenderTarget { fn add_used(&mut self, rect: DeviceIntRect); } -/// A tag used to identify the output format of a `RenderTarget`. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum RenderTargetKind { - Color, // RGBA8 - Alpha, // R8 -} - /// A series of `RenderTarget` instances, serving as the high-level container /// into which `RenderTasks` are assigned. /// @@ -337,7 +328,7 @@ pub struct ColorRenderTarget { pub vertical_blurs: Vec, pub horizontal_blurs: Vec, pub readbacks: Vec, - pub scalings: Vec, + pub scalings: FastHashMap>, pub svg_filters: Vec<(BatchTextures, Vec)>, pub blits: Vec, // List of frame buffer outputs for this render target. @@ -360,7 +351,7 @@ impl RenderTarget for ColorRenderTarget { vertical_blurs: Vec::new(), horizontal_blurs: Vec::new(), readbacks: Vec::new(), - scalings: Vec::new(), + scalings: FastHashMap::default(), svg_filters: Vec::new(), blits: Vec::new(), outputs: Vec::new(), @@ -533,14 +524,18 @@ impl RenderTarget for ColorRenderTarget { RenderTaskKind::Readback(device_rect) => { self.readbacks.push(device_rect); } - RenderTaskKind::Scaling(..) => { - self.scalings.push(ScalingInstance { - task_address: render_tasks.get_task_address(task_id), - src_task_address: render_tasks.get_task_address(task.children[0]), - }); + RenderTaskKind::Scaling(ref info) => { + info.add_instances( + &mut self.scalings, + task, + task.children.first().map(|&child| &render_tasks[child]), + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ); } RenderTaskKind::Blit(ref task_info) => { - match task_info.source { + let source = match task_info.source { BlitSource::Image { key } => { // Get the cache item for the source texture. let cache_item = resolve_image( @@ -564,24 +559,25 @@ impl RenderTarget for ColorRenderTarget { // Store the blit job for the renderer to execute, including // the allocated destination rect within this target. - let (target_rect, _) = task.get_target_rect(); - self.blits.push(BlitJob { - source: BlitJobSource::Texture( - cache_item.texture_id, - cache_item.texture_layer, - source_rect, - ), - target_rect: target_rect.inner_rect(task_info.padding) - }); + BlitJobSource::Texture( + cache_item.texture_id, + cache_item.texture_layer, + source_rect, + ) } BlitSource::RenderTask { task_id } => { - let (target_rect, _) = task.get_target_rect(); - self.blits.push(BlitJob { - source: BlitJobSource::RenderTask(task_id), - target_rect: target_rect.inner_rect(task_info.padding) - }); + BlitJobSource::RenderTask(task_id) } - } + }; + + let target_rect = task + .get_target_rect() + .0 + .inner_rect(task_info.padding); + self.blits.push(BlitJob { + source, + target_rect, + }); } #[cfg(test)] RenderTaskKind::Test(..) => {} @@ -614,7 +610,7 @@ pub struct AlphaRenderTarget { // List of blur operations to apply for this render target. pub vertical_blurs: Vec, pub horizontal_blurs: Vec, - pub scalings: Vec, + pub scalings: FastHashMap>, pub zero_clears: Vec, pub one_clears: Vec, // Track the used rect of the render target, so that @@ -632,7 +628,7 @@ impl RenderTarget for AlphaRenderTarget { clip_batcher: ClipBatcher::new(gpu_supports_fast_clears), vertical_blurs: Vec::new(), horizontal_blurs: Vec::new(), - scalings: Vec::new(), + scalings: FastHashMap::default(), zero_clears: Vec::new(), one_clears: Vec::new(), used_rect: DeviceIntRect::zero(), @@ -647,7 +643,7 @@ impl RenderTarget for AlphaRenderTarget { render_tasks: &RenderTaskGraph, clip_store: &ClipStore, transforms: &mut TransformPalette, - _: &mut Vec, + deferred_resolves: &mut Vec, ) { let task = &render_tasks[task_id]; let (target_rect, _) = task.get_target_rect(); @@ -726,8 +722,11 @@ impl RenderTarget for AlphaRenderTarget { RenderTaskKind::Scaling(ref info) => { info.add_instances( &mut self.scalings, - render_tasks.get_task_address(task_id), - render_tasks.get_task_address(task.children[0]), + task, + task.children.first().map(|&child| &render_tasks[child]), + ctx.resource_cache, + gpu_cache, + deferred_resolves, ); } #[cfg(test)] @@ -1399,16 +1398,67 @@ impl BlurTask { impl ScalingTask { fn add_instances( &self, - instances: &mut Vec, - task_address: RenderTaskAddress, - src_task_address: RenderTaskAddress, + instances: &mut FastHashMap>, + target_task: &RenderTask, + source_task: Option<&RenderTask>, + resource_cache: &ResourceCache, + gpu_cache: &mut GpuCache, + deferred_resolves: &mut Vec, ) { - let instance = ScalingInstance { - task_address, - src_task_address, + let target_rect = target_task + .get_target_rect() + .0 + .inner_rect(self.padding) + .to_f32(); + + let (source, (source_rect, source_layer)) = match self.image { + Some(key) => { + assert!(source_task.is_none()); + + // Get the cache item for the source texture. + let cache_item = resolve_image( + key.request, + resource_cache, + gpu_cache, + deferred_resolves, + ); + + // Work out a source rect to copy from the texture, depending on whether + // a sub-rect is present or not. + let source_rect = key.texel_rect.map_or(cache_item.uv_rect, |sub_rect| { + DeviceIntRect::new( + DeviceIntPoint::new( + cache_item.uv_rect.origin.x + sub_rect.origin.x, + cache_item.uv_rect.origin.y + sub_rect.origin.y, + ), + sub_rect.size, + ) + }); + + ( + cache_item.texture_id, + (source_rect, cache_item.texture_layer as LayerIndex), + ) + } + None => { + ( + match self.target_kind { + RenderTargetKind::Color => TextureSource::PrevPassColor, + RenderTargetKind::Alpha => TextureSource::PrevPassAlpha, + }, + source_task.unwrap().location.to_source_rect(), + ) + } }; - instances.push(instance); + instances + .entry(source) + .or_insert(Vec::new()) + .push(ScalingInstance { + target_rect, + source_rect, + source_layer: source_layer as i32, + }); } } @@ -1427,14 +1477,14 @@ impl SvgFilterTask { if let Some(saved_index) = input_1_task.map(|id| &render_tasks[id].saved_index) { textures.colors[0] = match saved_index { - Some(saved_index) => TextureSource::RenderTaskCache(*saved_index), + Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()), None => TextureSource::PrevPassColor, }; } if let Some(saved_index) = input_2_task.map(|id| &render_tasks[id].saved_index) { textures.colors[1] = match saved_index { - Some(saved_index) => TextureSource::RenderTaskCache(*saved_index), + Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()), None => TextureSource::PrevPassColor, }; } diff --git a/gfx/wr/wrench/reftests/border/reftest.list b/gfx/wr/wrench/reftests/border/reftest.list index f597c55ed585..e6d85f71f682 100644 --- a/gfx/wr/wrench/reftests/border/reftest.list +++ b/gfx/wr/wrench/reftests/border/reftest.list @@ -9,7 +9,7 @@ fuzzy(1,68) == border-overlapping-corner.yaml border-overlapping-corner-ref.yaml == border-overlapping-edge.yaml border-overlapping-edge-ref.yaml == border-invisible.yaml border-invisible-ref.yaml platform(linux,mac) == border-suite.yaml border-suite.png -platform(linux,mac) fuzzy(8,8) == border-suite-2.yaml border-suite-2.png +platform(linux,mac) fuzzy(8,80) == border-suite-2.yaml border-suite-2.png platform(linux,mac) == border-suite-3.yaml border-suite-3.png skip_on(android,device) == border-double-simple.yaml border-double-simple-ref.yaml # Fails on Pixel2 == border-double-simple-2.yaml border-double-simple-2-ref.yaml diff --git a/gfx/wr/wrench/src/rawtest.rs b/gfx/wr/wrench/src/rawtest.rs index cda68fee5aa6..dc3cbe55a68e 100644 --- a/gfx/wr/wrench/src/rawtest.rs +++ b/gfx/wr/wrench/src/rawtest.rs @@ -54,18 +54,18 @@ impl<'a> RawtestHarness<'a> { self.wrench.renderer.read_pixels_rgba8(window_rect) } - fn compare_pixels(&self, reference: Vec, test: Vec, size: FramebufferIntSize) { + fn compare_pixels(&self, data1: Vec, data2: Vec, size: FramebufferIntSize) { let size = DeviceIntSize::new(size.width, size.height); - let reference = ReftestImage { - data: reference, + let image1 = ReftestImage { + data: data1, size, }; - let test = ReftestImage { - data: test, + let image2 = ReftestImage { + data: data2, size, }; - match reference.compare(&test) { + match image1.compare(&image2) { ReftestImageComparison::Equal => {} ReftestImageComparison::NotEqual { max_difference, count_different } => { let t = "rawtest"; @@ -78,11 +78,8 @@ impl<'a> RawtestHarness<'a> { "number of differing pixels", count_different ); - println!("REFTEST IMAGE 1 (TEST): {}", test.create_data_uri()); - println!( - "REFTEST IMAGE 2 (REFERENCE): {}", - reference.create_data_uri() - ); + println!("REFTEST IMAGE 1: {}", image1.create_data_uri()); + println!("REFTEST IMAGE 2: {}", image2.create_data_uri()); println!("REFTEST TEST-END | {}", t); panic!(); }