mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 1583879 - Skip uploading and rendering empty blob tiles. r=jrmuizel
This patch adds a notion of "fully transparent" image in the resource cache. These are not uploaded in the texture cache and image requests return the necessary information to allow the frame building code to skip emitting primitives accordingly. Differential Revision: https://phabricator.services.mozilla.com/D47878 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
24ff5a9aa6
commit
c1f7e63ce2
@ -333,25 +333,26 @@ struct Reader {
|
||||
layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
|
||||
};
|
||||
|
||||
static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const mozilla::wr::DeviceIntRect* aVisibleRect,
|
||||
const mozilla::wr::LayoutIntRect* aRenderRect,
|
||||
const uint16_t* aTileSize,
|
||||
const mozilla::wr::TileOffset* aTileOffset,
|
||||
const mozilla::wr::LayoutIntRect* aDirtyRect,
|
||||
Range<uint8_t> aOutput) {
|
||||
static wr::BlobRenderStatus
|
||||
Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const mozilla::wr::DeviceIntRect* aVisibleRect,
|
||||
const mozilla::wr::LayoutIntRect* aRenderRect,
|
||||
const uint16_t* aTileSize,
|
||||
const mozilla::wr::TileOffset* aTileOffset,
|
||||
const mozilla::wr::LayoutIntRect* aDirtyRect,
|
||||
Range<uint8_t> aOutput) {
|
||||
IntSize size(aRenderRect->size.width, aRenderRect->size.height);
|
||||
AUTO_PROFILER_TRACING("WebRender", "RasterizeSingleBlob", GRAPHICS);
|
||||
MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0);
|
||||
if (size.width <= 0 || size.height <= 0) {
|
||||
return false;
|
||||
return BlobRenderStatus::Error;
|
||||
}
|
||||
|
||||
auto stride = size.width * gfx::BytesPerPixel(aFormat);
|
||||
|
||||
if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
|
||||
return false;
|
||||
return BlobRenderStatus::Error;
|
||||
}
|
||||
|
||||
// In bindings.rs we allocate a buffer filled with opaque white.
|
||||
@ -362,7 +363,7 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
uninitialized);
|
||||
|
||||
if (!dt) {
|
||||
return false;
|
||||
return BlobRenderStatus::Error;
|
||||
}
|
||||
|
||||
// We try hard to not have empty blobs but we can end up with
|
||||
@ -392,7 +393,7 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
aDirtyRect->size.width, aDirtyRect->size.height));
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
wr::BlobRenderStatus status = wr::BlobRenderStatus::Empty;
|
||||
size_t offset = 0;
|
||||
auto absBounds = IntRectAbsolute::FromRect(bounds);
|
||||
while (reader.pos < reader.len) {
|
||||
@ -407,6 +408,8 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
continue;
|
||||
}
|
||||
|
||||
status = BlobRenderStatus::Ok;
|
||||
|
||||
layers::WebRenderTranslator translator(dt);
|
||||
Reader fontReader(aBlob.begin().get() + end, extra_end - end);
|
||||
size_t count = fontReader.ReadSize();
|
||||
@ -418,9 +421,10 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
}
|
||||
|
||||
Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
|
||||
ret =
|
||||
bool ok =
|
||||
translator.TranslateRecording((char*)blob.begin().get(), blob.length());
|
||||
if (!ret) {
|
||||
if (!ok) {
|
||||
status = BlobRenderStatus::Error;
|
||||
gfxCriticalNote << "Replay failure: " << translator.GetError();
|
||||
MOZ_RELEASE_ASSERT(false);
|
||||
}
|
||||
@ -447,7 +451,7 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
gfxUtils::WriteAsPNG(dt, filename);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
@ -455,14 +459,15 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
|
||||
mozilla::wr::ImageFormat aFormat,
|
||||
const mozilla::wr::LayoutIntRect* aRenderRect,
|
||||
const mozilla::wr::DeviceIntRect* aVisibleRect,
|
||||
const uint16_t* aTileSize,
|
||||
const mozilla::wr::TileOffset* aTileOffset,
|
||||
const mozilla::wr::LayoutIntRect* aDirtyRect,
|
||||
mozilla::wr::MutByteSlice output) {
|
||||
mozilla::wr::BlobRenderStatus
|
||||
wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
|
||||
mozilla::wr::ImageFormat aFormat,
|
||||
const mozilla::wr::LayoutIntRect* aRenderRect,
|
||||
const mozilla::wr::DeviceIntRect* aVisibleRect,
|
||||
const uint16_t* aTileSize,
|
||||
const mozilla::wr::TileOffset* aTileOffset,
|
||||
const mozilla::wr::LayoutIntRect* aDirtyRect,
|
||||
mozilla::wr::MutByteSlice output) {
|
||||
return mozilla::wr::Moz2DRenderCallback(
|
||||
mozilla::wr::ByteSliceToRange(blob),
|
||||
mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
|
||||
|
@ -3263,20 +3263,24 @@ pub unsafe extern "C" fn wr_dec_ref_arc(arc: *const VecU8) {
|
||||
Arc::from_raw(arc);
|
||||
}
|
||||
|
||||
// TODO: nical
|
||||
// Update for the new blob image interface changes.
|
||||
//
|
||||
#[repr(u32)]
|
||||
pub enum BlobRenderStatus {
|
||||
Ok,
|
||||
Empty,
|
||||
Error,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// TODO: figure out the API for tiled blob images.
|
||||
pub fn wr_moz2d_render_cb(blob: ByteSlice,
|
||||
format: ImageFormat,
|
||||
render_rect: &LayoutIntRect,
|
||||
visible_rect: &DeviceIntRect,
|
||||
tile_size: Option<&u16>,
|
||||
tile_offset: Option<&TileOffset>,
|
||||
dirty_rect: Option<&LayoutIntRect>,
|
||||
output: MutByteSlice)
|
||||
-> bool;
|
||||
// TODO: figure out the API for tiled blob images.
|
||||
pub fn wr_moz2d_render_cb(blob: ByteSlice,
|
||||
format: ImageFormat,
|
||||
render_rect: &LayoutIntRect,
|
||||
visible_rect: &DeviceIntRect,
|
||||
tile_size: Option<&u16>,
|
||||
tile_offset: Option<&TileOffset>,
|
||||
dirty_rect: Option<&LayoutIntRect>,
|
||||
output: MutByteSlice)
|
||||
-> BlobRenderStatus;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::{BlobDirtyRect, BlobToDeviceTranslation, DeviceIntRect};
|
||||
use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb, ArcVecU8, gecko_profiler_start_marker, gecko_profiler_end_marker};
|
||||
use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb, ArcVecU8};
|
||||
use bindings::{BlobRenderStatus, gecko_profiler_start_marker, gecko_profiler_end_marker};
|
||||
use rayon::ThreadPool;
|
||||
use rayon::prelude::*;
|
||||
|
||||
@ -579,7 +580,7 @@ fn rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult) {
|
||||
assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
|
||||
|
||||
let result = unsafe {
|
||||
if wr_moz2d_render_cb(
|
||||
match wr_moz2d_render_cb(
|
||||
ByteSlice::new(&job.commands[..]),
|
||||
descriptor.format,
|
||||
&descriptor.rect,
|
||||
@ -589,18 +590,27 @@ fn rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult) {
|
||||
dirty_rect.as_ref(),
|
||||
MutByteSlice::new(output.as_mut_slice()),
|
||||
) {
|
||||
// We want the dirty rect local to the tile rather than the whole image.
|
||||
// TODO(nical): move that up and avoid recomupting the tile bounds in the callback
|
||||
let dirty_rect = job.dirty_rect.to_subrect_of(&descriptor.rect);
|
||||
let tx: BlobToDeviceTranslation = (-descriptor.rect.origin.to_vector()).into();
|
||||
let rasterized_rect = tx.transform_rect(&dirty_rect);
|
||||
BlobRenderStatus::Ok => {
|
||||
// We want the dirty rect local to the tile rather than the whole image.
|
||||
// TODO(nical): move that up and avoid recomputing the tile bounds in the callback
|
||||
let local_dirty_rect = job.dirty_rect.to_subrect_of(&descriptor.rect);
|
||||
let tx: BlobToDeviceTranslation = (-descriptor.rect.origin.to_vector()).into();
|
||||
let rasterized_rect = tx.transform_rect(&local_dirty_rect);
|
||||
|
||||
Ok(RasterizedBlobImage {
|
||||
rasterized_rect,
|
||||
data: Arc::new(output),
|
||||
})
|
||||
} else {
|
||||
panic!("Moz2D replay problem");
|
||||
Ok(RasterizedBlobImage {
|
||||
rasterized_rect,
|
||||
data: Some(Arc::new(output)),
|
||||
})
|
||||
}
|
||||
BlobRenderStatus::Empty => {
|
||||
Ok(RasterizedBlobImage {
|
||||
rasterized_rect: DeviceIntRect::zero(),
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
BlobRenderStatus::Error => {
|
||||
panic!("Moz2D replay problem");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -106,7 +106,7 @@ fn render_blob(
|
||||
}
|
||||
|
||||
Ok(api::RasterizedBlobImage {
|
||||
data: Arc::new(texels),
|
||||
data: Some(Arc::new(texels)),
|
||||
rasterized_rect: size2(w, h).into(),
|
||||
})
|
||||
}
|
||||
|
@ -403,21 +403,26 @@ impl ClipNodeInfo {
|
||||
tile_size as i32,
|
||||
);
|
||||
for tile in tiles {
|
||||
let mut render = true;
|
||||
if request_resources {
|
||||
resource_cache.request_image(
|
||||
render = resource_cache.request_image(
|
||||
request.with_tile(tile.offset),
|
||||
gpu_cache,
|
||||
);
|
||||
).should_render();
|
||||
}
|
||||
if render {
|
||||
mask_tiles.push(VisibleMaskImageTile {
|
||||
tile_offset: tile.offset,
|
||||
tile_rect: tile.rect,
|
||||
});
|
||||
}
|
||||
mask_tiles.push(VisibleMaskImageTile {
|
||||
tile_offset: tile.offset,
|
||||
tile_rect: tile.rect,
|
||||
});
|
||||
}
|
||||
}
|
||||
visible_tiles = Some(mask_tiles);
|
||||
} else if request_resources {
|
||||
resource_cache.request_image(request, gpu_cache);
|
||||
if resource_cache.request_image(request, gpu_cache).should_skip() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the supplied image key doesn't exist in the resource cache,
|
||||
|
@ -17,7 +17,7 @@ use crate::prim_store::{
|
||||
PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData,
|
||||
PrimitiveStore, InternablePrimitive,
|
||||
};
|
||||
use crate::resource_cache::{ImageRequest, ResourceCache};
|
||||
use crate::resource_cache::{ImageRequest, ResourceCache, ImageRequestStatus};
|
||||
use crate::storage;
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
@ -260,11 +260,11 @@ impl ImageBorderData {
|
||||
&mut self,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
) {
|
||||
) -> ImageRequestStatus {
|
||||
resource_cache.request_image(
|
||||
self.request,
|
||||
gpu_cache,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn write_prim_gpu_blocks(
|
||||
|
@ -24,7 +24,7 @@ use crate::render_task::{BlitSource, RenderTask};
|
||||
use crate::render_task_cache::{
|
||||
RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind
|
||||
};
|
||||
use crate::resource_cache::{ImageRequest, ResourceCache};
|
||||
use crate::resource_cache::{ImageRequest, ResourceCache, ImageRequestStatus};
|
||||
use crate::util::pack_as_float;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -457,11 +457,11 @@ impl YuvImageData {
|
||||
&mut self,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
) {
|
||||
) -> ImageRequestStatus {
|
||||
let channel_num = self.format.get_plane_num();
|
||||
debug_assert!(channel_num <= 3);
|
||||
for channel in 0 .. channel_num {
|
||||
resource_cache.request_image(
|
||||
let status = resource_cache.request_image(
|
||||
ImageRequest {
|
||||
key: self.yuv_key[channel],
|
||||
rendering: self.image_rendering,
|
||||
@ -469,7 +469,13 @@ impl YuvImageData {
|
||||
},
|
||||
gpu_cache,
|
||||
);
|
||||
|
||||
if status != ImageRequestStatus::Requested {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
ImageRequestStatus::Requested
|
||||
}
|
||||
|
||||
pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
|
||||
|
@ -2364,11 +2364,12 @@ impl PrimitiveStore {
|
||||
|
||||
match image_properties {
|
||||
Some(ImageProperties { tiling: None, .. }) => {
|
||||
|
||||
frame_state.resource_cache.request_image(
|
||||
if frame_state.resource_cache.request_image(
|
||||
request,
|
||||
frame_state.gpu_cache,
|
||||
);
|
||||
).should_skip() {
|
||||
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
|
||||
}
|
||||
}
|
||||
Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => {
|
||||
image_instance.visible_tiles.clear();
|
||||
@ -2433,17 +2434,17 @@ impl PrimitiveStore {
|
||||
);
|
||||
|
||||
for tile in tiles {
|
||||
frame_state.resource_cache.request_image(
|
||||
if frame_state.resource_cache.request_image(
|
||||
request.with_tile(tile.offset),
|
||||
frame_state.gpu_cache,
|
||||
);
|
||||
|
||||
image_instance.visible_tiles.push(VisibleImageTile {
|
||||
tile_offset: tile.offset,
|
||||
edge_flags: tile.edge_flags & edge_flags,
|
||||
local_rect: tile.rect,
|
||||
local_clip_rect: tight_clip_rect,
|
||||
});
|
||||
).should_render() {
|
||||
image_instance.visible_tiles.push(VisibleImageTile {
|
||||
tile_offset: tile.offset,
|
||||
edge_flags: tile.edge_flags & edge_flags,
|
||||
local_rect: tile.rect,
|
||||
local_clip_rect: tight_clip_rect,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2457,17 +2458,21 @@ impl PrimitiveStore {
|
||||
}
|
||||
PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
|
||||
let prim_data = &mut frame_state.data_stores.image_border[data_handle];
|
||||
prim_data.kind.request_resources(
|
||||
if prim_data.kind.request_resources(
|
||||
frame_state.resource_cache,
|
||||
frame_state.gpu_cache,
|
||||
);
|
||||
).should_skip() {
|
||||
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
|
||||
}
|
||||
}
|
||||
PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
|
||||
let prim_data = &mut frame_state.data_stores.yuv_image[data_handle];
|
||||
prim_data.kind.request_resources(
|
||||
if prim_data.kind.request_resources(
|
||||
frame_state.resource_cache,
|
||||
frame_state.gpu_cache,
|
||||
);
|
||||
).should_skip() {
|
||||
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -101,6 +101,8 @@ pub enum CachedImageData {
|
||||
/// An image owned by the embedding, and referenced by WebRender. This may
|
||||
/// take the form of a texture or a heap-allocated buffer.
|
||||
External(ExternalImageData),
|
||||
/// The image data is only transparent pixels, we can skip rendering it.
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl From<ImageData> for CachedImageData {
|
||||
@ -131,8 +133,9 @@ impl CachedImageData {
|
||||
ExternalImageType::TextureHandle(_) => false,
|
||||
ExternalImageType::Buffer => true,
|
||||
},
|
||||
CachedImageData::Blob => true,
|
||||
CachedImageData::Raw(_) => true,
|
||||
CachedImageData::Blob
|
||||
| CachedImageData::Raw(_)
|
||||
| CachedImageData::Empty => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,6 +226,7 @@ struct CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle,
|
||||
dirty_rect: ImageDirtyRect,
|
||||
manual_eviction: bool,
|
||||
is_fully_transparent: bool
|
||||
}
|
||||
|
||||
impl CachedImageInfo {
|
||||
@ -456,6 +460,27 @@ pub struct AsyncBlobImageInfo {
|
||||
pub clear_requests: Vec<BlobImageClearParams>,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ImageRequestStatus {
|
||||
Requested,
|
||||
FullyTransparent,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl ImageRequestStatus {
|
||||
pub fn should_render(self) -> bool {
|
||||
match self {
|
||||
Self::Requested => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_skip(self) -> bool {
|
||||
!self.should_render()
|
||||
}
|
||||
}
|
||||
|
||||
/// High-level container for resources managed by the `RenderBackend`.
|
||||
///
|
||||
/// This includes a variety of things, including images, fonts, and glyphs,
|
||||
@ -549,7 +574,9 @@ impl ResourceCache {
|
||||
fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &CachedImageData) -> bool {
|
||||
let size_check = descriptor.size.width > limit || descriptor.size.height > limit;
|
||||
match *data {
|
||||
CachedImageData::Raw(_) | CachedImageData::Blob => size_check,
|
||||
CachedImageData::Raw(_)
|
||||
| CachedImageData::Blob
|
||||
| CachedImageData::Empty => size_check,
|
||||
CachedImageData::External(info) => {
|
||||
// External handles already represent existing textures so it does
|
||||
// not make sense to tile them into smaller ones.
|
||||
@ -783,6 +810,20 @@ impl ResourceCache {
|
||||
|| { RasterizedBlob::Tiled(FastHashMap::default()) }
|
||||
);
|
||||
|
||||
if data.data.is_none() {
|
||||
let entry = ensure_cached_image_entry(
|
||||
&mut self.cached_images,
|
||||
ImageRequest {
|
||||
key: request.key.as_image(),
|
||||
tile: request.tile,
|
||||
rendering: ImageRendering::Auto,
|
||||
},
|
||||
);
|
||||
|
||||
// Mark the cached image as empty to skip rendering it later.
|
||||
entry.is_fully_transparent = true;
|
||||
}
|
||||
|
||||
if let Some(tile) = request.tile {
|
||||
if let RasterizedBlob::NonTiled(..) = *image {
|
||||
*image = RasterizedBlob::Tiled(FastHashMap::default());
|
||||
@ -1110,11 +1151,13 @@ impl ResourceCache {
|
||||
self.active_image_keys.insert(image_key);
|
||||
}
|
||||
|
||||
// Returns false if the image should not be rendered, either due to an error,
|
||||
// or if the image only contains fully transparent pixels.
|
||||
pub fn request_image(
|
||||
&mut self,
|
||||
request: ImageRequest,
|
||||
gpu_cache: &mut GpuCache,
|
||||
) {
|
||||
) -> ImageRequestStatus {
|
||||
debug_assert_eq!(self.state, State::AddResources);
|
||||
|
||||
let template = match self.resources.image_templates.get(request.key) {
|
||||
@ -1122,13 +1165,13 @@ impl ResourceCache {
|
||||
None => {
|
||||
warn!("ERROR: Trying to render deleted / non-existent key");
|
||||
debug!("key={:?}", request.key);
|
||||
return
|
||||
return ImageRequestStatus::Error;
|
||||
}
|
||||
};
|
||||
|
||||
// Images that don't use the texture cache can early out.
|
||||
if !template.data.uses_texture_cache() {
|
||||
return;
|
||||
return ImageRequestStatus::Requested;
|
||||
}
|
||||
|
||||
let side_size =
|
||||
@ -1139,76 +1182,27 @@ impl ResourceCache {
|
||||
warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
|
||||
template.descriptor.size.width, template.descriptor.size.height, template.tiling.unwrap_or(0));
|
||||
self.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize));
|
||||
return;
|
||||
return ImageRequestStatus::Error;
|
||||
}
|
||||
|
||||
let storage = match self.cached_images.entry(request.key) {
|
||||
Occupied(e) => {
|
||||
// We might have an existing untiled entry, and need to insert
|
||||
// a second entry. In such cases we need to move the old entry
|
||||
// out first, replacing it with a dummy entry, and then creating
|
||||
// the tiled/multi-entry variant.
|
||||
let entry = e.into_mut();
|
||||
if !request.is_untiled_auto() {
|
||||
let untiled_entry = match entry {
|
||||
&mut ImageResult::UntiledAuto(ref mut entry) => {
|
||||
Some(mem::replace(entry, CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
}))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
if let Some(untiled_entry) = untiled_entry {
|
||||
let mut entries = ResourceClassCache::new();
|
||||
let untiled_key = CachedImageKey {
|
||||
rendering: ImageRendering::Auto,
|
||||
tile: None,
|
||||
};
|
||||
entries.insert(untiled_key, untiled_entry);
|
||||
*entry = ImageResult::Multi(entries);
|
||||
}
|
||||
}
|
||||
entry
|
||||
}
|
||||
Vacant(entry) => {
|
||||
entry.insert(if request.is_untiled_auto() {
|
||||
ImageResult::UntiledAuto(CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
})
|
||||
} else {
|
||||
ImageResult::Multi(ResourceClassCache::new())
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// If this image exists in the texture cache, *and* the dirty rect
|
||||
// in the cache is empty, then it is valid to use as-is.
|
||||
let entry = match *storage {
|
||||
ImageResult::UntiledAuto(ref mut entry) => entry,
|
||||
ImageResult::Multi(ref mut entries) => {
|
||||
entries.entry(request.into())
|
||||
.or_insert(CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
})
|
||||
},
|
||||
ImageResult::Err(_) => panic!("Errors should already have been handled"),
|
||||
};
|
||||
let entry = ensure_cached_image_entry(&mut self.cached_images, request);
|
||||
|
||||
let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_cache);
|
||||
|
||||
|
||||
if entry.is_fully_transparent {
|
||||
// Empty tile, skip rendering.
|
||||
return ImageRequestStatus::FullyTransparent;
|
||||
}
|
||||
|
||||
// If this image exists in the texture cache, *and* the dirty rect
|
||||
// in the cache is empty, then it is valid to use as-is.
|
||||
if !needs_upload && entry.dirty_rect.is_empty() {
|
||||
return
|
||||
return ImageRequestStatus::Requested;
|
||||
}
|
||||
|
||||
if !self.pending_image_requests.insert(request) {
|
||||
return
|
||||
return ImageRequestStatus::Requested;
|
||||
}
|
||||
|
||||
// By this point, we know that the image request is considered dirty, and will
|
||||
@ -1256,6 +1250,8 @@ impl ResourceCache {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return ImageRequestStatus::Requested;
|
||||
}
|
||||
|
||||
pub fn create_blob_scene_builder_requests(
|
||||
@ -1583,6 +1579,14 @@ impl ResourceCache {
|
||||
pub fn get_cached_image(&self, request: ImageRequest) -> Result<CacheItem, ()> {
|
||||
debug_assert_eq!(self.state, State::QueryResources);
|
||||
let image_info = self.get_image_info(request)?;
|
||||
if image_info.is_fully_transparent {
|
||||
// TODO(nical): Ideally we should have discarded the primitive already.
|
||||
// If a blob image was not rasterized before frame building we end up
|
||||
// knowing whether its content is transparent after the culling phase
|
||||
// so we have to check again here. The plan is to remove the late blob
|
||||
// rasterization code, after which we can simplify this.
|
||||
return Err(())
|
||||
}
|
||||
Ok(self.get_texture_cache_item(&image_info.texture_cache_handle))
|
||||
}
|
||||
|
||||
@ -1620,7 +1624,9 @@ impl ResourceCache {
|
||||
ExternalImageType::Buffer => None,
|
||||
},
|
||||
// raw and blob image are all using resource_cache.
|
||||
CachedImageData::Raw(..) | CachedImageData::Blob => None,
|
||||
CachedImageData::Raw(..)
|
||||
| CachedImageData::Blob
|
||||
| CachedImageData::Empty => None,
|
||||
};
|
||||
|
||||
ImageProperties {
|
||||
@ -1727,7 +1733,9 @@ impl ResourceCache {
|
||||
let mut updates: SmallVec<[(CachedImageData, Option<DeviceIntRect>); 1]> = SmallVec::new();
|
||||
|
||||
match image_template.data {
|
||||
CachedImageData::Raw(..) | CachedImageData::External(..) => {
|
||||
CachedImageData::Raw(..)
|
||||
| CachedImageData::External(..)
|
||||
| CachedImageData::Empty => {
|
||||
// Safe to clone here since the Raw image data is an
|
||||
// Arc, and the external image data is small.
|
||||
updates.push((image_template.data.clone(), None));
|
||||
@ -1738,17 +1746,29 @@ impl ResourceCache {
|
||||
match (blob_image, request.tile) {
|
||||
(RasterizedBlob::Tiled(ref tiles), Some(tile)) => {
|
||||
let img = &tiles[&tile];
|
||||
updates.push((
|
||||
CachedImageData::Raw(Arc::clone(&img.data)),
|
||||
Some(img.rasterized_rect)
|
||||
));
|
||||
updates.push(
|
||||
if let Some(ref data) = img.data {
|
||||
(
|
||||
CachedImageData::Raw(Arc::clone(data)),
|
||||
Some(img.rasterized_rect)
|
||||
)
|
||||
} else {
|
||||
(CachedImageData::Empty, None)
|
||||
}
|
||||
);
|
||||
}
|
||||
(RasterizedBlob::NonTiled(ref mut queue), None) => {
|
||||
for img in queue.drain(..) {
|
||||
updates.push((
|
||||
CachedImageData::Raw(img.data),
|
||||
Some(img.rasterized_rect)
|
||||
));
|
||||
updates.push(
|
||||
if let Some(data) = img.data {
|
||||
(
|
||||
CachedImageData::Raw(data),
|
||||
Some(img.rasterized_rect)
|
||||
)
|
||||
} else {
|
||||
(CachedImageData::Empty, None)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -1926,7 +1946,9 @@ impl ResourceCache {
|
||||
for (_, image) in self.resources.image_templates.images.iter() {
|
||||
report.images += match image.data {
|
||||
CachedImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) },
|
||||
CachedImageData::Blob | CachedImageData::External(..) => 0,
|
||||
CachedImageData::Blob
|
||||
| CachedImageData::External(..)
|
||||
| CachedImageData::Empty => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1971,6 +1993,72 @@ impl Drop for ResourceCache {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn ensure_cached_image_entry(cached_images: &mut ImageCache, request: ImageRequest) -> &mut CachedImageInfo {
|
||||
let storage = match cached_images.entry(request.key) {
|
||||
Occupied(e) => {
|
||||
// We might have an existing untiled entry, and need to insert
|
||||
// a second entry. In such cases we need to move the old entry
|
||||
// out first, replacing it with a dummy entry, and then creating
|
||||
// the tiled/multi-entry variant.
|
||||
let entry = e.into_mut();
|
||||
if !request.is_untiled_auto() {
|
||||
let untiled_entry = match entry {
|
||||
&mut ImageResult::UntiledAuto(ref mut entry) => {
|
||||
Some(mem::replace(entry, CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
is_fully_transparent: false,
|
||||
}))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
if let Some(untiled_entry) = untiled_entry {
|
||||
let mut entries = ResourceClassCache::new();
|
||||
let untiled_key = CachedImageKey {
|
||||
rendering: ImageRendering::Auto,
|
||||
tile: None,
|
||||
};
|
||||
entries.insert(untiled_key, untiled_entry);
|
||||
*entry = ImageResult::Multi(entries);
|
||||
}
|
||||
}
|
||||
entry
|
||||
}
|
||||
Vacant(entry) => {
|
||||
entry.insert(
|
||||
if request.is_untiled_auto() {
|
||||
ImageResult::UntiledAuto(CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
is_fully_transparent: false,
|
||||
})
|
||||
} else {
|
||||
ImageResult::Multi(ResourceClassCache::new())
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
match *storage {
|
||||
ImageResult::UntiledAuto(ref mut entry) => entry,
|
||||
ImageResult::Multi(ref mut entries) => {
|
||||
entries.entry(request.into())
|
||||
.or_insert(CachedImageInfo {
|
||||
texture_cache_handle: TextureCacheHandle::invalid(),
|
||||
dirty_rect: DirtyRect::All,
|
||||
manual_eviction: false,
|
||||
is_fully_transparent: false,
|
||||
})
|
||||
},
|
||||
ImageResult::Err(_) => panic!("Errors should already have been handled"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn get_blob_tiling(
|
||||
tiling: Option<TileSize>,
|
||||
size: DeviceIntSize,
|
||||
@ -2153,7 +2241,7 @@ impl ResourceCache {
|
||||
warn!("Tiled blob images aren't supported yet");
|
||||
RasterizedBlobImage {
|
||||
rasterized_rect: desc.size.into(),
|
||||
data: Arc::new(vec![0; desc.compute_total_size() as usize])
|
||||
data: None,
|
||||
}
|
||||
} else {
|
||||
let blob_handler = self.blob_image_handler.as_mut().unwrap();
|
||||
@ -2163,25 +2251,26 @@ impl ResourceCache {
|
||||
result.expect("Blob rasterization failed")
|
||||
};
|
||||
|
||||
assert_eq!(result.rasterized_rect.size, desc.size);
|
||||
assert_eq!(result.data.len(), desc.compute_total_size() as usize);
|
||||
|
||||
num_blobs += 1;
|
||||
#[cfg(feature = "png")]
|
||||
CaptureConfig::save_png(
|
||||
root.join(format!("blobs/{}.png", num_blobs)),
|
||||
desc.size,
|
||||
desc.format,
|
||||
&result.data,
|
||||
);
|
||||
let file_name = format!("{}.raw", num_blobs);
|
||||
let short_path = format!("blobs/{}", file_name);
|
||||
let full_path = path_blobs.clone().join(&file_name);
|
||||
fs::File::create(full_path)
|
||||
.expect(&format!("Unable to create {}", short_path))
|
||||
.write_all(&result.data)
|
||||
.unwrap();
|
||||
other_paths.insert(key, short_path);
|
||||
assert_eq!(result.rasterized_rect.size, desc.size);
|
||||
if let Some(ref data) = result.data {
|
||||
assert_eq!(data.len(), desc.compute_total_size() as usize);
|
||||
#[cfg(feature = "png")]
|
||||
CaptureConfig::save_png(
|
||||
root.join(format!("blobs/{}.png", num_blobs)),
|
||||
desc.size,
|
||||
desc.format,
|
||||
data,
|
||||
);
|
||||
let file_name = format!("{}.raw", num_blobs);
|
||||
let short_path = format!("blobs/{}", file_name);
|
||||
let full_path = path_blobs.clone().join(&file_name);
|
||||
fs::File::create(full_path)
|
||||
.expect(&format!("Unable to create {}", short_path))
|
||||
.write_all(data)
|
||||
.unwrap();
|
||||
other_paths.insert(key, short_path);
|
||||
}
|
||||
}
|
||||
CachedImageData::External(ref ext) => {
|
||||
let short_path = format!("externals/{}", external_images.len() + 1);
|
||||
@ -2192,6 +2281,7 @@ impl ResourceCache {
|
||||
external: ext.clone(),
|
||||
});
|
||||
}
|
||||
CachedImageData::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -913,6 +913,12 @@ impl TextureCache {
|
||||
) {
|
||||
debug_assert!(self.now.is_valid());
|
||||
|
||||
if let Some(CachedImageData::Empty) = data {
|
||||
self.mark_unused(handle);
|
||||
*handle = TextureCacheHandle::invalid();
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if we need to allocate texture cache memory
|
||||
// for this item. We need to reallocate if any of the following
|
||||
// is true:
|
||||
@ -1846,6 +1852,9 @@ impl TextureCacheUpdate {
|
||||
CachedImageData::Blob => {
|
||||
panic!("The vector image should have been rasterized.");
|
||||
}
|
||||
CachedImageData::Empty => {
|
||||
panic!("The entry should have been cleared instead.");
|
||||
}
|
||||
CachedImageData::External(ext_image) => match ext_image.image_type {
|
||||
ExternalImageType::TextureHandle(_) => {
|
||||
panic!("External texture handle should not go through texture_cache.");
|
||||
|
@ -490,7 +490,10 @@ pub struct RasterizedBlobImage {
|
||||
/// image or tile.
|
||||
pub rasterized_rect: DeviceIntRect,
|
||||
/// Backing store. The format is stored out of band in `BlobImageDescriptor`.
|
||||
pub data: Arc<Vec<u8>>,
|
||||
///
|
||||
/// None means the image only has fully transparent content. We can skip storing
|
||||
/// and rendering it.
|
||||
pub data: Option<Arc<Vec<u8>>>,
|
||||
}
|
||||
|
||||
/// Error code for when blob rasterization failed.
|
||||
|
@ -98,7 +98,7 @@ fn render_blob(
|
||||
}
|
||||
|
||||
Ok(RasterizedBlobImage {
|
||||
data: Arc::new(texels),
|
||||
data: Some(Arc::new(texels)),
|
||||
rasterized_rect,
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user