Bug 1572843 - WR swizzling part-2 r=gw

This is a medium-size follow-up to D21965.

Refactors the use of swizzling by the texture cache. Adds a device
capability flag that is checked at run-time. Also makes the texture
cache to communicate with the texture uploader if there is a format
conversion needed, which fixes the case on platforms that don't
support swizzling.

Differential Revision: https://phabricator.services.mozilla.com/D41446

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dzmitry Malyshau 2019-08-12 15:39:23 +00:00
parent dc10950243
commit 0aa1247a1e
7 changed files with 140 additions and 73 deletions

View File

@ -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<dyn gl::Gl>,
@ -1001,8 +1004,7 @@ pub struct Device {
color_formats: TextureFormatPair<ImageFormat>,
bgra_formats: TextureFormatPair<gl::GLuint>,
// 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<SwizzleSettings> {
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<i32>,
offset: usize,
format_override: Option<ImageFormat>,
}
struct PixelBuffer {
@ -3447,6 +3464,7 @@ impl<'a, T> TextureUploader<'a, T> {
mut rect: DeviceIntRect,
layer_index: i32,
stride: Option<i32>,
format_override: Option<ImageFormat>,
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),

View File

@ -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<i32>,
pub offset: i32,
pub layer_index: i32,
pub format_override: Option<ImageFormat>,
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,
});
}

View File

@ -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<T> VertexDataTexture<T> {
);
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<u8> = 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
}

View File

@ -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(),
);
}
}

View File

@ -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<SwizzleSettings>,
/// 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<ImageFormat>,
bgra_swizzle: Swizzle,
swizzle: Option<SwizzleSettings>,
) -> 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<SwizzleSettings> {
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
}
}
}

View File

@ -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(

View File

@ -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());
}