diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs index 4fa5eca9f05c..1019b33f6511 100644 --- a/gfx/wr/webrender/src/device/gl.rs +++ b/gfx/wr/webrender/src/device/gl.rs @@ -8,7 +8,7 @@ use api::{MixBlendMode, TextureTarget, VoidPtrToSizeFn}; use api::units::*; use euclid::default::Transform3D; use gleam::gl; -use crate::internal_types::{FastHashMap, LayerIndex, RenderTargetInfo, Swizzle}; +use crate::internal_types::{FastHashMap, LayerIndex, RenderTargetInfo, Swizzle, SwizzleSettings}; use crate::util::round_up_to_multiple; use crate::profiler; use log::Level; @@ -940,6 +940,8 @@ pub struct Capabilities { /// Whether KHR_debug is supported for getting debug messages from /// the driver. pub supports_khr_debug: bool, + /// Whether we can configure texture units to do swizzling on sampling. + pub supports_texture_swizzle: bool, } #[derive(Clone, Debug)] @@ -973,6 +975,7 @@ enum TexStorageUsage { Always, } + pub struct Device { gl: Rc, @@ -1001,8 +1004,7 @@ pub struct Device { color_formats: TextureFormatPair, bgra_formats: TextureFormatPair, - // the swizzle required on sampling a texture with `TextureFormat::BGRA` format - bgra_swizzle: Swizzle, + swizzle_settings: SwizzleSettings, /// Map from texture dimensions to shared depth buffers for render targets. /// @@ -1300,7 +1302,7 @@ impl Device { // GL_EXT_texture_storage and GL_EXT_texture_format_BGRA8888. let supports_gles_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888"); - let (color_formats, bgra_formats, bgra_swizzle, texture_storage_usage) = match gl.get_type() { + let (color_formats, bgra_formats, bgra8_sampling_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 && @@ -1354,7 +1356,7 @@ impl Device { }; info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}", - color_formats, bgra_formats, bgra_swizzle, texture_storage_usage); + color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage); let supports_copy_image_sub_data = supports_extension(&extensions, "GL_EXT_copy_image") || supports_extension(&extensions, "GL_ARB_copy_image"); @@ -1380,6 +1382,9 @@ impl Device { supports_extension(&extensions, "GL_KHR_blend_equation_advanced") && !renderer_name.starts_with("Adreno"); + let supports_texture_swizzle = allow_texture_swizzling && + (gl.get_type() == gl::GlType::Gles || supports_extension(&extensions, "GL_ARB_texture_storage")); + // On Adreno GPUs PBO texture upload is only performed asynchronously // if the stride of the data in the PBO is a multiple of 256 bytes. // Other platforms may have similar requirements and should be added @@ -1405,11 +1410,14 @@ impl Device { supports_pixel_local_storage, supports_advanced_blend_equation, supports_khr_debug, + supports_texture_swizzle, }, color_formats, bgra_formats, - bgra_swizzle, + swizzle_settings: SwizzleSettings { + bgra8_sampling_swizzle, + }, depth_targets: FastHashMap::default(), @@ -1473,8 +1481,12 @@ impl Device { self.color_formats.clone() } - pub fn bgra_swizzle(&self) -> Swizzle { - self.bgra_swizzle + pub fn swizzle_settings(&self) -> Option { + if self.capabilities.supports_texture_swizzle { + Some(self.swizzle_settings) + } else { + None + } } pub fn get_optimal_pbo_stride(&self) -> NonZeroUsize { @@ -1606,14 +1618,18 @@ impl Device { 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); + if self.capabilities.supports_texture_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); + } else { + debug_assert_eq!(swizzle, Swizzle::default()); + } } self.gl.active_texture(gl::TEXTURE0); self.bound_textures[slot.0] = id; @@ -3393,6 +3409,7 @@ struct UploadChunk { layer_index: i32, stride: Option, offset: usize, + format_override: Option, } struct PixelBuffer { @@ -3447,6 +3464,7 @@ impl<'a, T> TextureUploader<'a, T> { mut rect: DeviceIntRect, layer_index: i32, stride: Option, + format_override: Option, data: &[T], ) -> usize { // Textures dimensions may have been clamped by the hardware. Crop the @@ -3538,15 +3556,21 @@ impl<'a, T> TextureUploader<'a, T> { } buffer.chunks.push(UploadChunk { - rect, layer_index, stride: Some(dst_stride as i32), + rect, + layer_index, + stride: Some(dst_stride as i32), offset: buffer.size_used, + format_override, }); buffer.size_used += dst_size; } None => { self.target.update_impl(UploadChunk { - rect, layer_index, stride, + rect, + layer_index, + stride, offset: data.as_ptr() as _, + format_override, }); } } @@ -3557,7 +3581,8 @@ impl<'a, T> TextureUploader<'a, T> { impl<'a> UploadTarget<'a> { fn update_impl(&mut self, chunk: UploadChunk) { - let (gl_format, bpp, data_type) = match self.texture.format { + let format = chunk.format_override.unwrap_or(self.texture.format); + let (gl_format, bpp, data_type) = match format { ImageFormat::R8 => (gl::RED, 1, gl::UNSIGNED_BYTE), ImageFormat::R16 => (gl::RED, 2, gl::UNSIGNED_SHORT), ImageFormat::BGRA8 => (self.bgra_format, 4, gl::UNSIGNED_BYTE), diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs index 43a300b3f5a2..16edf0f39743 100644 --- a/gfx/wr/webrender/src/internal_types.rs +++ b/gfx/wr/webrender/src/internal_types.rs @@ -175,6 +175,15 @@ impl Default for Swizzle { } } +/// Swizzle settings of the texture cache. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +pub struct SwizzleSettings { + /// Swizzle required on sampling a texture with BGRA8 format. + pub bgra8_sampling_swizzle: Swizzle, +} + /// An ID for a texture that is owned by the `texture_cache` module. /// /// This can include atlases or standalone textures allocated via the texture @@ -307,6 +316,7 @@ pub struct TextureCacheUpdate { pub stride: Option, pub offset: i32, pub layer_index: i32, + pub format_override: Option, pub source: TextureUpdateSource, } @@ -364,10 +374,11 @@ impl TextureUpdateList { self.push_update(TextureCacheUpdate { id, rect, - source: TextureUpdateSource::DebugClear, stride: None, offset: 0, layer_index: layer_index as i32, + format_override: None, + source: TextureUpdateSource::DebugClear, }); } diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index b11a524aa822..48583a6405f4 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -1468,7 +1468,7 @@ impl GpuCacheTexture { DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, 1), ); - uploader.upload(rect, 0, None, &*row.cpu_blocks); + uploader.upload(rect, 0, None, None, &*row.cpu_blocks); row.is_dirty = false; } @@ -1592,7 +1592,7 @@ impl VertexDataTexture { ); device .upload_texture(self.texture(), &self.pbo, 0) - .upload(rect, 0, None, data); + .upload(rect, 0, None, None, data); } fn deinit(mut self, device: &mut Device) { @@ -1858,7 +1858,7 @@ impl Renderer { ); let color_cache_formats = device.preferred_color_formats(); - let bgra_swizzle = device.bgra_swizzle(); + let swizzle_settings = device.swizzle_settings(); 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"), @@ -2166,7 +2166,7 @@ impl Renderer { }, start_size, color_cache_formats, - bgra_swizzle, + swizzle_settings, ); let glyph_cache = GlyphCache::new(max_glyph_cache_size); @@ -3168,7 +3168,7 @@ impl Renderer { } for update in update_list.updates { - let TextureCacheUpdate { id, rect, stride, offset, layer_index, source } = update; + let TextureCacheUpdate { id, rect, stride, offset, layer_index, format_override, source } = update; let texture = &self.texture_resolver.texture_cache_map[&id]; let bytes_uploaded = match source { @@ -3179,7 +3179,10 @@ impl Renderer { 0, ); uploader.upload( - rect, layer_index, stride, + rect, + layer_index, + stride, + format_override, &data[offset as usize ..], ) } @@ -3193,12 +3196,10 @@ impl Renderer { .as_mut() .expect("Found external image, but no handler set!"); // The filter is only relevant for NativeTexture external images. - let size = match handler.lock(id, channel_index, ImageRendering::Auto).source { + let dummy_data; + let data = match handler.lock(id, channel_index, ImageRendering::Auto).source { ExternalImageSource::RawData(data) => { - uploader.upload( - rect, layer_index, stride, - &data[offset as usize ..], - ) + &data[offset as usize ..] } ExternalImageSource::Invalid => { // Create a local buffer to fill the pbo. @@ -3207,13 +3208,20 @@ impl Renderer { let total_size = width * rect.size.height; // WR haven't support RGBAF32 format in texture_cache, so // we use u8 type here. - let dummy_data: Vec = vec![255; total_size as usize]; - uploader.upload(rect, layer_index, stride, &dummy_data) + dummy_data = vec![0xFFu8; total_size as usize]; + &dummy_data } ExternalImageSource::NativeTexture(eid) => { panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id); } }; + let size = uploader.upload( + rect, + layer_index, + stride, + format_override, + data, + ); handler.unlock(id, channel_index); size } diff --git a/gfx/wr/webrender/src/resource_cache.rs b/gfx/wr/webrender/src/resource_cache.rs index ae82ea68f6ea..db8a5deb8bb8 100644 --- a/gfx/wr/webrender/src/resource_cache.rs +++ b/gfx/wr/webrender/src/resource_cache.rs @@ -2204,7 +2204,7 @@ impl ResourceCache { &self.texture_cache.picture_tile_sizes(), DeviceIntSize::zero(), self.texture_cache.color_formats(), - self.texture_cache.bgra_swizzle(), + self.texture_cache.swizzle_settings(), ); } } diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs index 05d1f4241f16..f55fd93c86f6 100644 --- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -12,7 +12,7 @@ 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, Swizzle, + CacheTextureId, FastHashMap, LayerIndex, Swizzle, SwizzleSettings, TextureUpdateList, TextureUpdateSource, TextureSource, TextureCacheAllocInfo, TextureCacheUpdate, }; @@ -173,6 +173,14 @@ impl CacheEntry { eviction_notice.notify(); } } + + fn alternative_input_format(&self) -> ImageFormat { + match self.input_format { + ImageFormat::RGBA8 => ImageFormat::BGRA8, + ImageFormat::BGRA8 => ImageFormat::RGBA8, + other => other, + } + } } @@ -299,31 +307,23 @@ impl SharedTextures { /// Returns a mutable borrow for the shared texture array matching the parameters. fn select( &mut self, external_format: ImageFormat, filter: TextureFilter - ) -> (&mut TextureArray, Swizzle) { + ) -> &mut TextureArray { match external_format { ImageFormat::R8 => { assert_eq!(filter, TextureFilter::Linear); - (&mut self.array_alpha8_linear, Swizzle::default()) + &mut self.array_alpha8_linear } ImageFormat::R16 => { assert_eq!(filter, TextureFilter::Linear); - (&mut self.array_alpha16_linear, Swizzle::default()) + &mut self.array_alpha16_linear } ImageFormat::RGBA8 | ImageFormat::BGRA8 => { - let array = match filter { + 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), } @@ -517,8 +517,8 @@ 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, + /// Settings on using texture unit swizzling. + swizzle: Option, /// The current set of debug flags. debug_flags: DebugFlags, @@ -564,7 +564,7 @@ impl TextureCache { picture_tile_sizes: &[DeviceIntSize], initial_size: DeviceIntSize, color_formats: TextureFormatPair, - bgra_swizzle: Swizzle, + swizzle: Option, ) -> Self { if cfg!(target_os = "macos") { // On MBP integrated Intel GPUs, texture arrays appear to be @@ -622,7 +622,9 @@ impl TextureCache { // 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()); + assert!(color_formats.internal != ImageFormat::BGRA8 || + swizzle.map_or(true, |s| s.bgra8_sampling_swizzle == Swizzle::default()) + ); TextureCache { shared_textures: SharedTextures::new(color_formats), @@ -631,7 +633,7 @@ impl TextureCache { entries: FreeList::new(), max_texture_size, max_texture_layers, - bgra_swizzle, + swizzle, debug_flags: DebugFlags::empty(), next_id: CacheTextureId(next_texture_id), pending_updates, @@ -657,7 +659,7 @@ impl TextureCache { &[], DeviceIntSize::zero(), TextureFormatPair::from(image_format), - Swizzle::default(), + None, ); let mut now = FrameStamp::first(DocumentId::new(IdNamespace(1), 1)); now.advance(); @@ -887,8 +889,8 @@ impl TextureCache { } #[cfg(feature = "replay")] - pub fn bgra_swizzle(&self) -> Swizzle { - self.bgra_swizzle + pub fn swizzle_settings(&self) -> Option { + self.swizzle } pub fn pending_updates(&mut self) -> TextureUpdateList { @@ -919,7 +921,8 @@ 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.input_format != descriptor.format + entry.size != descriptor.size || (entry.input_format != descriptor.format && + entry.alternative_input_format() != descriptor.format) } None => { // Not allocated, or was previously allocated but has been evicted. @@ -957,6 +960,10 @@ impl TextureCache { // to upload the new image data into the correct location // in GPU memory. if let Some(data) = data { + // If the swizzling is supported, we always upload in the internal + // texture format (thus avoiding the conversion by the driver). + // Otherwise, pass the external format to the driver. + let use_upload_format = !self.swizzle.is_some(); let (layer_index, origin) = entry.details.describe(); let op = TextureCacheUpdate::new_update( data, @@ -965,6 +972,7 @@ impl TextureCache { entry.size, entry.texture_id, layer_index as i32, + use_upload_format, &dirty_rect, ); self.pending_updates.push_update(op); @@ -1130,7 +1138,7 @@ impl TextureCache { } EntryDetails::Cache { origin, layer_index } => { // Free the block in the given region. - let (texture_array, _swizzle) = self.shared_textures.select(entry.input_format, entry.filter); + let texture_array = self.shared_textures.select(entry.input_format, entry.filter); let unit = texture_array.units .iter_mut() .find(|unit| unit.texture_id == entry.texture_id) @@ -1159,7 +1167,7 @@ impl TextureCache { &mut self, params: &CacheAllocParams, ) -> bool { - let (texture_array, _swizzle) = self.shared_textures.select( + let texture_array = self.shared_textures.select( params.descriptor.format, params.filter, ); @@ -1175,10 +1183,18 @@ impl TextureCache { params: &CacheAllocParams, ) -> CacheEntry { // Mutably borrow the correct texture. - let (texture_array, swizzle) = self.shared_textures.select( + let texture_array = self.shared_textures.select( params.descriptor.format, params.filter, ); + let swizzle = if texture_array.formats.external == params.descriptor.format { + Swizzle::default() + } else { + match self.swizzle { + Some(_) => Swizzle::Bgra, + None => Swizzle::default(), + } + }; let max_texture_layers = self.max_texture_layers; let slab_size = SlabSize::new(params.descriptor.size); @@ -1277,16 +1293,16 @@ impl TextureCache { // Special handing for BGRA8 textures that may need to be swizzled. let swizzle = if params.descriptor.format == ImageFormat::BGRA8 { - self.bgra_swizzle + self.swizzle.map(|s| s.bgra8_sampling_swizzle) } else { - Swizzle::default() + None }; CacheEntry::new_standalone( texture_id, self.now, params, - swizzle, + swizzle.unwrap_or_default(), ) } @@ -1823,6 +1839,7 @@ impl TextureCacheUpdate { size: DeviceIntSize, texture_id: CacheTextureId, layer_index: i32, + use_upload_format: bool, dirty_rect: &ImageDirtyRect, ) -> TextureCacheUpdate { let source = match data { @@ -1847,8 +1864,13 @@ impl TextureCacheUpdate { TextureUpdateSource::Bytes { data: bytes } } }; + let format_override = if use_upload_format { + Some(descriptor.format) + } else { + None + }; - let update_op = match *dirty_rect { + match *dirty_rect { DirtyRect::Partial(dirty) => { // the dirty rectangle doesn't have to be within the area but has to intersect it, at least let stride = descriptor.compute_stride(); @@ -1866,6 +1888,7 @@ impl TextureCacheUpdate { source, stride: Some(stride), offset, + format_override, layer_index, } } @@ -1876,12 +1899,11 @@ impl TextureCacheUpdate { source, stride: descriptor.stride, offset: descriptor.offset, + format_override, layer_index, } } - }; - - update_op + } } } diff --git a/gfx/wr/webrender/src/tiling.rs b/gfx/wr/webrender/src/tiling.rs index 3345b190e347..e5816188c5d5 100644 --- a/gfx/wr/webrender/src/tiling.rs +++ b/gfx/wr/webrender/src/tiling.rs @@ -948,7 +948,7 @@ impl RenderPass { kind: RenderPassKind::OffScreen { color: RenderTargetList::new( screen_size, - ImageFormat::BGRA8, + ImageFormat::RGBA8, gpu_supports_fast_clears, ), alpha: RenderTargetList::new( diff --git a/gfx/wr/wrench/src/yaml_frame_writer.rs b/gfx/wr/wrench/src/yaml_frame_writer.rs index 59ccb6ed4569..494b1726656b 100644 --- a/gfx/wr/wrench/src/yaml_frame_writer.rs +++ b/gfx/wr/wrench/src/yaml_frame_writer.rs @@ -807,9 +807,10 @@ impl YamlFrameWriter { ); assert!(data.stride > 0); - let (color_type, bpp) = match data.format { - ImageFormat::BGRA8 => (ColorType::RGBA(8), 4), - ImageFormat::R8 => (ColorType::Gray(8), 1), + let (color_type, bpp, do_unpremultiply) = match data.format { + ImageFormat::RGBA8 | + ImageFormat::BGRA8 => (ColorType::RGBA(8), 4, true), + ImageFormat::R8 => (ColorType::Gray(8), 1, false), _ => { println!( "Failed to write image with format {:?}, dimensions {}x{}, stride {}", @@ -823,7 +824,7 @@ impl YamlFrameWriter { }; if data.stride == data.width * bpp { - if data.format == ImageFormat::BGRA8 { + if do_unpremultiply { unpremultiply(bytes.as_mut_slice()); } save_buffer( @@ -842,7 +843,7 @@ impl YamlFrameWriter { chunk[.. (data.width * bpp) as usize].iter().cloned() }) .collect(); - if data.format == ImageFormat::BGRA8 { + if do_unpremultiply { unpremultiply(tmp.as_mut_slice()); }