Backed out changeset 42d3ccf69629 (bug 1583879) for causing reftest failures on sort-startSame-1b.svg CLOSED TREE
authorarthur.iakab <aiakab@mozilla.com>
Fri, 11 Oct 2019 18:27:52 +0300
changeset 497282 d19d644cb240a27a1d0389a0ca603b4bbffddd42
parent 497281 71259d26450add8a19281b10f903e28d126351a0
child 497283 d4837cfe8ba0a5f25658d2aeb9bfc5cb99d06f33
push id114148
push usershindli@mozilla.com
push dateMon, 14 Oct 2019 10:49:50 +0000
treeherdermozilla-inbound@585fe4556335 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1583879
milestone71.0a1
backs out42d3ccf69629e077b151538a9aad733f82a52a0c
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 42d3ccf69629 (bug 1583879) for causing reftest failures on sort-startSame-1b.svg CLOSED TREE
gfx/webrender_bindings/Moz2DImageRenderer.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/moz2d_renderer.rs
gfx/wr/examples/blob.rs
gfx/wr/webrender/src/clip.rs
gfx/wr/webrender/src/prim_store/borders.rs
gfx/wr/webrender/src/prim_store/image.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/webrender/src/resource_cache.rs
gfx/wr/webrender/src/texture_cache.rs
gfx/wr/webrender_api/src/image.rs
gfx/wr/wrench/src/blob.rs
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -328,47 +328,46 @@ struct Reader {
   size_t ReadSize() { return Read<size_t>(); }
   int ReadInt() { return Read<int>(); }
 
   IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); }
 
   layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
 };
 
-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) {
+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) {
   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 BlobRenderStatus::Error;
+    return false;
   }
 
   auto stride = size.width * gfx::BytesPerPixel(aFormat);
 
   if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
-    return BlobRenderStatus::Error;
+    return false;
   }
 
   // In bindings.rs we allocate a buffer filled with opaque white.
   bool uninitialized = false;
 
   RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
       gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat,
       uninitialized);
 
   if (!dt) {
-    return BlobRenderStatus::Error;
+    return false;
   }
 
   // We try hard to not have empty blobs but we can end up with
   // them because of CompositorHitTestInfo and merging.
   size_t footerSize = sizeof(size_t);
   MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize);
   size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize);
 
@@ -388,48 +387,45 @@ Moz2DRenderCallback(const Range<const ui
     gfx::Rect dirty(aDirtyRect->origin.x, aDirtyRect->origin.y,
                     aDirtyRect->size.width, aDirtyRect->size.height);
     dt->PushClipRect(dirty);
     bounds = bounds.Intersect(
         IntRect(aDirtyRect->origin.x, aDirtyRect->origin.y,
                 aDirtyRect->size.width, aDirtyRect->size.height));
   }
 
-  wr::BlobRenderStatus status = wr::BlobRenderStatus::Empty;
+  bool ret = true;
   size_t offset = 0;
   auto absBounds = IntRectAbsolute::FromRect(bounds);
   while (reader.pos < reader.len) {
     size_t end = reader.ReadSize();
     size_t extra_end = reader.ReadSize();
     MOZ_RELEASE_ASSERT(extra_end >= end);
     MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
 
     auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
     if (combinedBounds.IsEmpty()) {
       offset = extra_end;
       continue;
     }
 
-    status = BlobRenderStatus::Ok;
-
     layers::WebRenderTranslator translator(dt);
     Reader fontReader(aBlob.begin().get() + end, extra_end - end);
     size_t count = fontReader.ReadSize();
     for (size_t i = 0; i < count; i++) {
       layers::BlobFont blobFont = fontReader.ReadBlobFont();
       RefPtr<ScaledFont> scaledFont =
           GetScaledFont(&translator, blobFont.mFontInstanceKey);
       translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
     }
 
     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
-    bool ok =
+    ret =
         translator.TranslateRecording((char*)blob.begin().get(), blob.length());
-    if (!ok) {
-      status = BlobRenderStatus::Error;
+    if (!ret) {
       gfxCriticalNote << "Replay failure: " << translator.GetError();
       MOZ_RELEASE_ASSERT(false);
     }
     offset = extra_end;
   }
 
   if (StaticPrefs::gfx_webrender_blob_paint_flashing()) {
     dt->SetTransform(gfx::Matrix());
@@ -446,33 +442,32 @@ Moz2DRenderCallback(const Range<const ui
 
 #if 0
   static int i = 0;
   char filename[40];
   sprintf(filename, "out%d.png", i++);
   gfxUtils::WriteAsPNG(dt, filename);
 #endif
 
-  return status;
+  return ret;
 }
 
 }  // namespace wr
 }  // namespace mozilla
 
 extern "C" {
 
-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) {
+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) {
   return mozilla::wr::Moz2DRenderCallback(
       mozilla::wr::ByteSliceToRange(blob),
       mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
       aRenderRect, aTileSize, aTileOffset, aDirtyRect,
       mozilla::wr::MutByteSliceToRange(output));
 }
 
 }  // extern
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -3258,34 +3258,30 @@ pub extern "C" fn wr_add_ref_arc(arc: &A
     Arc::into_raw(arc.clone())
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_dec_ref_arc(arc: *const VecU8) {
     Arc::from_raw(arc);
 }
 
-#[repr(u32)]
-pub enum BlobRenderStatus {
-    Ok,
-    Empty,
-    Error,
-}
-
+// TODO: nical
+// Update for the new blob image interface changes.
+//
 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)
-                              -> BlobRenderStatus;
+     // 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;
 }
 
 #[no_mangle]
 pub extern "C" fn wr_root_scroll_node_id() -> WrSpatialId {
     // The PipelineId doesn't matter here, since we just want the numeric part of the id
     // produced for any given root reference frame.
     WrSpatialId { id: SpatialId::root_scroll_node(PipelineId(0, 0)).0 }
 }
--- a/gfx/webrender_bindings/src/moz2d_renderer.rs
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -3,18 +3,17 @@
 //! Provides the webrender-side implementation of gecko blob images.
 //!
 //! Pretty much this is just a shim that calls back into Moz2DImageRenderer, but
 //! it also handles merging "partial" blob images (see `merge_blob_images`) and
 //! registering fonts found in the blob (see `prepare_request`).
 
 use webrender::api::*;
 use webrender::api::units::{BlobDirtyRect, BlobToDeviceTranslation, DeviceIntRect};
-use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb, ArcVecU8};
-use bindings::{BlobRenderStatus, gecko_profiler_start_marker, gecko_profiler_end_marker};
+use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb, ArcVecU8, gecko_profiler_start_marker, gecko_profiler_end_marker};
 use rayon::ThreadPool;
 use rayon::prelude::*;
 
 use std::collections::hash_map::HashMap;
 use std::collections::hash_map;
 use std::collections::btree_map::BTreeMap;
 use std::collections::Bound::Included;
 use std::mem;
@@ -575,47 +574,38 @@ fn rasterize_blob(job: Job) -> (BlobImag
 
     let dirty_rect = match job.dirty_rect {
         DirtyRect::Partial(rect) => Some(rect),
         DirtyRect::All => None,
     };
     assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height  > 0);
 
     let result = unsafe {
-        match wr_moz2d_render_cb(
+        if wr_moz2d_render_cb(
             ByteSlice::new(&job.commands[..]),
             descriptor.format,
             &descriptor.rect,
             &job.visible_rect,
             job.tile_size.as_ref(),
             job.request.tile.as_ref(),
             dirty_rect.as_ref(),
             MutByteSlice::new(output.as_mut_slice()),
         ) {
-            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);
+            // 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);
 
-                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");
-            }
+            Ok(RasterizedBlobImage {
+                rasterized_rect,
+                data: Arc::new(output),
+            })
+        } else {
+            panic!("Moz2D replay problem");
         }
     };
 
     (job.request, result)
 }
 
 impl BlobImageHandler for Moz2dBlobImageHandler {
     fn add(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect, tile_size: Option<TileSize>) {
--- a/gfx/wr/examples/blob.rs
+++ b/gfx/wr/examples/blob.rs
@@ -101,17 +101,17 @@ fn render_blob(
                         format!("Unsupported image format"),
                     ));
                 }
             }
         }
     }
 
     Ok(api::RasterizedBlobImage {
-        data: Some(Arc::new(texels)),
+        data: Arc::new(texels),
         rasterized_rect: size2(w, h).into(),
     })
 }
 
 struct CheckerboardRenderer {
     // We are going to defer the rendering work to worker threads.
     // Using a pre-built Arc<ThreadPool> rather than creating our own threads
     // makes it possible to share the same thread pool as the glyph renderer (if we
--- a/gfx/wr/webrender/src/clip.rs
+++ b/gfx/wr/webrender/src/clip.rs
@@ -398,36 +398,31 @@ impl ClipNodeInfo {
                         };
                         let tiles = image::tiles(
                             &layout_image_rect,
                             &visible_rect,
                             &props.visible_rect,
                             tile_size as i32,
                         );
                         for tile in tiles {
-                            let mut render = true;
                             if request_resources {
-                                render = resource_cache.request_image(
+                                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 {
-                    if resource_cache.request_image(request, gpu_cache).should_skip() {
-                        return None;
-                    }
+                    resource_cache.request_image(request, gpu_cache);
                 }
             } else {
                 // If the supplied image key doesn't exist in the resource cache,
                 // skip the clip node since there is nothing to mask with.
                 warn!("Clip mask with missing image key {:?}", request.key);
                 return None;
             }
         }
--- a/gfx/wr/webrender/src/prim_store/borders.rs
+++ b/gfx/wr/webrender/src/prim_store/borders.rs
@@ -12,17 +12,17 @@ use crate::gpu_cache::{GpuCache, GpuData
 use crate::intern;
 use crate::internal_types::LayoutPrimitiveInfo;
 use crate::prim_store::{
     BorderSegmentInfo, BrushSegment, NinePatchDescriptor, PrimKey,
     PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
     PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData,
     PrimitiveStore, InternablePrimitive,
 };
-use crate::resource_cache::{ImageRequest, ResourceCache, ImageRequestStatus};
+use crate::resource_cache::{ImageRequest, ResourceCache};
 use crate::storage;
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
 pub struct NormalBorderPrim {
     pub border: NormalBorderAu,
     pub widths: LayoutSideOffsetsAu,
@@ -255,21 +255,21 @@ impl ImageBorderData {
             PrimitiveOpacity::opaque()
         }
     }
 
     pub fn request_resources(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
-    ) -> ImageRequestStatus {
+    ) {
         resource_cache.request_image(
             self.request,
             gpu_cache,
-        )
+        );
     }
 
     fn write_prim_gpu_blocks(
         &self,
         request: &mut GpuDataRequest,
         prim_size: &LayoutSize,
     ) {
         // Border primitives currently used for
--- a/gfx/wr/webrender/src/prim_store/image.rs
+++ b/gfx/wr/webrender/src/prim_store/image.rs
@@ -19,17 +19,17 @@ use crate::prim_store::{
     PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
     SizeKey, InternablePrimitive,
 };
 use crate::render_target::RenderTargetKind;
 use crate::render_task::{BlitSource, RenderTask};
 use crate::render_task_cache::{
     RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind
 };
-use crate::resource_cache::{ImageRequest, ResourceCache, ImageRequestStatus};
+use crate::resource_cache::{ImageRequest, ResourceCache};
 use crate::util::pack_as_float;
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct VisibleImageTile {
     pub tile_offset: TileOffset,
     pub edge_flags: EdgeAaSegmentMask,
@@ -452,35 +452,29 @@ impl YuvImageData {
         // YUV images never have transparency
         common.opacity = PrimitiveOpacity::opaque();
     }
 
     pub fn request_resources(
         &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 {
-            let status = resource_cache.request_image(
+            resource_cache.request_image(
                 ImageRequest {
                     key: self.yuv_key[channel],
                     rendering: self.image_rendering,
                     tile: None,
                 },
                 gpu_cache,
             );
-
-            if status != ImageRequestStatus::Requested {
-                return status;
-            }
         }
-
-        ImageRequestStatus::Requested
     }
 
     pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
         request.push([
             self.color_depth.rescaling_factor(),
             pack_as_float(self.color_space as u32),
             pack_as_float(self.format as u32),
             0.0
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -2359,22 +2359,21 @@ impl PrimitiveStore {
                 let request = ImageRequest {
                     key: image_data.key,
                     rendering: image_data.image_rendering,
                     tile: None,
                 };
 
                 match image_properties {
                     Some(ImageProperties { tiling: None, .. }) => {
-                        if frame_state.resource_cache.request_image(
+
+                        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();
                         // TODO: rename the blob's visible_rect into something that doesn't conflict
                         // with the terminology we use during culling since it's not really the same
                         // thing.
                         let active_rect = visible_rect;
 
@@ -2429,55 +2428,51 @@ impl PrimitiveStore {
                             let tiles = crate::image::tiles(
                                 &layout_image_rect,
                                 &visible_rect,
                                 &active_rect,
                                 tile_size as i32,
                             );
 
                             for tile in tiles {
-                                if frame_state.resource_cache.request_image(
+                                frame_state.resource_cache.request_image(
                                     request.with_tile(tile.offset),
                                     frame_state.gpu_cache,
-                                ).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,
-                                    });
-                                }
+                                );
+
+                                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,
+                                });
                             }
                         }
 
                         if image_instance.visible_tiles.is_empty() {
                             // Mark as invisible
                             prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
                         }
                     }
                     None => {}
                 }
             }
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
                 let prim_data = &mut frame_state.data_stores.image_border[data_handle];
-                if prim_data.kind.request_resources(
+                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];
-                if prim_data.kind.request_resources(
+                prim_data.kind.request_resources(
                     frame_state.resource_cache,
                     frame_state.gpu_cache,
-                ).should_skip() {
-                    prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
-                }
+                );
             }
             _ => {}
         }
     }
 
     pub fn get_opacity_binding(
         &self,
         opacity_binding_index: OpacityBindingIndex,
--- a/gfx/wr/webrender/src/resource_cache.rs
+++ b/gfx/wr/webrender/src/resource_cache.rs
@@ -96,18 +96,16 @@ pub enum CachedImageData {
     /// An series of commands that can be rasterized into an image via an
     /// embedding-provided callback.
     ///
     /// The commands are stored elsewhere and this variant is used as a placeholder.
     Blob,
     /// 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 {
     fn from(img_data: ImageData) -> Self {
         match img_data {
             ImageData::Raw(data) => CachedImageData::Raw(data),
             ImageData::External(data) => CachedImageData::External(data),
         }
@@ -128,19 +126,18 @@ impl CachedImageData {
     /// cache.
     #[inline]
     pub fn uses_texture_cache(&self) -> bool {
         match *self {
             CachedImageData::External(ref ext_data) => match ext_data.image_type {
                 ExternalImageType::TextureHandle(_) => false,
                 ExternalImageType::Buffer => true,
             },
-            CachedImageData::Blob
-            | CachedImageData::Raw(_)
-            | CachedImageData::Empty => true,
+            CachedImageData::Blob => true,
+            CachedImageData::Raw(_) => true,
         }
     }
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ImageProperties {
@@ -221,17 +218,16 @@ impl ImageTemplates {
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct CachedImageInfo {
     texture_cache_handle: TextureCacheHandle,
     dirty_rect: ImageDirtyRect,
     manual_eviction: bool,
-    is_fully_transparent: bool
 }
 
 impl CachedImageInfo {
     fn mark_unused(&mut self, texture_cache: &mut TextureCache) {
         texture_cache.mark_unused(&self.texture_cache_handle);
         self.manual_eviction = false;
     }
 }
@@ -455,37 +451,16 @@ pub struct BlobImageClearParams {
 
 /// Information attached to AsyncBlobImageRasterizer.
 #[derive(Clone, Debug)]
 pub struct AsyncBlobImageInfo {
     pub epoch: BlobImageRasterizerEpoch,
     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,
 /// which may be stored as memory buffers, GPU textures, or handles to resources
 /// managed by the OS or other parts of WebRender.
 pub struct ResourceCache {
     cached_glyphs: GlyphCache,
     cached_images: ImageCache,
@@ -569,19 +544,17 @@ impl ResourceCache {
 
     pub fn max_texture_size(&self) -> i32 {
         self.texture_cache.max_texture_size()
     }
 
     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
-            | CachedImageData::Empty => size_check,
+            CachedImageData::Raw(_) | CachedImageData::Blob => size_check,
             CachedImageData::External(info) => {
                 // External handles already represent existing textures so it does
                 // not make sense to tile them into smaller ones.
                 info.image_type == ExternalImageType::Buffer && size_check
             }
         }
     }
 
@@ -805,30 +778,16 @@ impl ResourceCache {
             texture_cache_profile.rasterized_blob_pixels.inc(data.rasterized_rect.area() as usize);
 
             // First make sure we have an entry for this key (using a placeholder
             // if need be).
             let image = self.rasterized_blob_images.entry(request.key).or_insert_with(
                 || { 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());
                 }
 
                 if let RasterizedBlob::Tiled(ref mut tiles) = *image {
                     tiles.insert(tile, data);
                 }
@@ -1146,68 +1105,115 @@ impl ResourceCache {
 
     pub fn set_image_active(
         &mut self,
         image_key: ImageKey,
     ) {
         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) {
             Some(template) => template,
             None => {
                 warn!("ERROR: Trying to render deleted / non-existent key");
                 debug!("key={:?}", request.key);
-                return ImageRequestStatus::Error;
+                return
             }
         };
 
         // Images that don't use the texture cache can early out.
         if !template.data.uses_texture_cache() {
-            return ImageRequestStatus::Requested;
+            return;
         }
 
         let side_size =
             template.tiling.map_or(cmp::max(template.descriptor.size.width, template.descriptor.size.height),
                                    |tile_size| tile_size as i32);
         if side_size > self.texture_cache.max_texture_size() {
             // The image or tiling size is too big for hardware texture size.
             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 ImageRequestStatus::Error;
+            return;
         }
 
-        let entry = ensure_cached_image_entry(&mut self.cached_images, request);
+        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 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 ImageRequestStatus::Requested;
+            return
         }
 
         if !self.pending_image_requests.insert(request) {
-            return ImageRequestStatus::Requested;
+            return
         }
 
         // By this point, we know that the image request is considered dirty, and will
         // require a texture cache modification.
         self.dirty_image_keys.insert(request.key);
 
         if template.data.is_blob() {
             let request: BlobImageRequest = request.into();
@@ -1245,18 +1251,16 @@ impl ResourceCache {
                     BlobImageParams {
                         request,
                         descriptor,
                         dirty_rect: DirtyRect::All,
                     }
                 );
             }
         }
-
-        return ImageRequestStatus::Requested;
     }
 
     pub fn create_blob_scene_builder_requests(
         &mut self,
         keys: &[BlobImageKey]
     ) -> (Option<(Box<dyn AsyncBlobImageRasterizer>, AsyncBlobImageInfo)>, Vec<BlobImageParams>) {
         if self.blob_image_handler.is_none() || keys.is_empty() {
             return (None, Vec::new());
@@ -1574,24 +1578,16 @@ impl ResourceCache {
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         self.glyph_rasterizer.get_glyph_index(font_key, ch)
     }
 
     #[inline]
     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))
     }
 
     pub fn get_cached_render_task(
         &self,
         handle: &RenderTaskCacheEntryHandle,
     ) -> &RenderTaskCacheEntry {
         self.cached_render_tasks.get_cache_entry(handle)
@@ -1619,19 +1615,17 @@ impl ResourceCache {
         image_template.map(|image_template| {
             let external_image = match image_template.data {
                 CachedImageData::External(ext_image) => match ext_image.image_type {
                     ExternalImageType::TextureHandle(_) => Some(ext_image),
                     // external buffer uses resource_cache.
                     ExternalImageType::Buffer => None,
                 },
                 // raw and blob image are all using resource_cache.
-                CachedImageData::Raw(..)
-                | CachedImageData::Blob
-                | CachedImageData::Empty => None,
+                CachedImageData::Raw(..) | CachedImageData::Blob => None,
             };
 
             ImageProperties {
                 descriptor: image_template.descriptor,
                 external_image,
                 tiling: image_template.tiling,
                 visible_rect: image_template.visible_rect,
             }
@@ -1728,52 +1722,38 @@ impl ResourceCache {
     fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
         for request in self.pending_image_requests.drain() {
             let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
             debug_assert!(image_template.data.uses_texture_cache());
 
             let mut updates: SmallVec<[(CachedImageData, Option<DeviceIntRect>); 1]> = SmallVec::new();
 
             match image_template.data {
-                CachedImageData::Raw(..)
-                | CachedImageData::External(..)
-                | CachedImageData::Empty => {
+                CachedImageData::Raw(..) | CachedImageData::External(..) => {
                     // 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));
                 }
                 CachedImageData::Blob => {
 
                     let blob_image = self.rasterized_blob_images.get_mut(&BlobImageKey(request.key)).unwrap();
                     match (blob_image, request.tile) {
                         (RasterizedBlob::Tiled(ref tiles), Some(tile)) => {
                             let img = &tiles[&tile];
-                            updates.push(
-                                if let Some(ref data) = img.data {
-                                    (
-                                        CachedImageData::Raw(Arc::clone(data)),
-                                        Some(img.rasterized_rect)
-                                    )
-                                } else {
-                                    (CachedImageData::Empty, None)
-                                }
-                            );
+                            updates.push((
+                                CachedImageData::Raw(Arc::clone(&img.data)),
+                                Some(img.rasterized_rect)
+                            ));
                         }
                         (RasterizedBlob::NonTiled(ref mut queue), None) => {
                             for img in queue.drain(..) {
-                                updates.push(
-                                    if let Some(data) = img.data {
-                                        (
-                                            CachedImageData::Raw(data),
-                                            Some(img.rasterized_rect)
-                                        )
-                                    } else {
-                                        (CachedImageData::Empty, None)
-                                    }
-                                );
+                                updates.push((
+                                    CachedImageData::Raw(img.data),
+                                    Some(img.rasterized_rect)
+                                ));
                             }
                         }
                         _ =>  {
                             debug_assert!(false, "invalid blob image request during frame building");
                             continue;
                         }
                     }
                 }
@@ -1941,19 +1921,17 @@ impl ResourceCache {
                 report.fonts += unsafe { op(raw.as_ptr() as *const c_void) };
             }
         }
 
         // Measure images.
         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(..)
-                | CachedImageData::Empty => 0,
+                CachedImageData::Blob | CachedImageData::External(..) => 0,
             }
         }
 
         // Mesure rasterized blobs.
         // TODO(gw): Temporarily disabled while we roll back a crash. We can re-enable
         //           these when that crash is fixed.
         /*
         for (_, image) in self.rasterized_blob_images.iter() {
@@ -1988,82 +1966,16 @@ impl ResourceCache {
 }
 
 impl Drop for ResourceCache {
     fn drop(&mut self) {
         self.clear_images(|_| true);
     }
 }
 
-
-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,
     max_texture_size: i32,
 ) -> Option<TileSize> {
     if tiling.is_none() &&
         (size.width > max_texture_size ||
          size.height > max_texture_size) {
@@ -2236,57 +2148,55 @@ impl ResourceCache {
                         Some(tile_size) => desc.size.width > tile_size as i32 || desc.size.height > tile_size as i32,
                         None => false,
                     };
 
                     let result = if requires_tiling {
                         warn!("Tiled blob images aren't supported yet");
                         RasterizedBlobImage {
                             rasterized_rect: desc.size.into(),
-                            data: None,
+                            data: Arc::new(vec![0; desc.compute_total_size() as usize])
                         }
                     } else {
                         let blob_handler = self.blob_image_handler.as_mut().unwrap();
                         blob_handler.prepare_resources(&self.resources, blob_request_params);
                         let mut rasterizer = blob_handler.create_blob_rasterizer();
                         let (_, result) = rasterizer.rasterize(blob_request_params, false).pop().unwrap();
                         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;
-                    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);
-                    }
+                    #[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);
                 }
                 CachedImageData::External(ref ext) => {
                     let short_path = format!("externals/{}", external_images.len() + 1);
                     other_paths.insert(key, short_path.clone());
                     external_images.push(ExternalCaptureImage {
                         short_path,
                         descriptor: desc.clone(),
                         external: ext.clone(),
                     });
                 }
-                CachedImageData::Empty => {}
             }
         }
 
         let resources = PlainResources {
             font_templates: res.font_templates
                 .iter()
                 .map(|(key, template)| {
                     (*key, match *template {
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -908,22 +908,16 @@ impl TextureCache {
         mut dirty_rect: ImageDirtyRect,
         gpu_cache: &mut GpuCache,
         eviction_notice: Option<&EvictionNotice>,
         uv_rect_kind: UvRectKind,
         eviction: Eviction,
     ) {
         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:
         // - Never been in the cache
         // - Has been in the cache but was evicted.
         // - Exists in the cache but dimensions / format have changed.
         let realloc = match self.entries.get_opt(handle) {
             Some(entry) => {
@@ -1847,19 +1841,16 @@ impl TextureCacheUpdate {
         layer_index: i32,
         use_upload_format: bool,
         dirty_rect: &ImageDirtyRect,
     ) -> TextureCacheUpdate {
         let source = match data {
             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.");
                 }
                 ExternalImageType::Buffer => TextureUpdateSource::External {
                     id: ext_image.id,
                     channel_index: ext_image.channel_index,
                 },
--- a/gfx/wr/webrender_api/src/image.rs
+++ b/gfx/wr/webrender_api/src/image.rs
@@ -485,20 +485,17 @@ pub struct BlobImageDescriptor {
 
 /// Representation of a rasterized blob image. This is obtained by passing
 /// `BlobImageData` to the embedding via the rasterization callback.
 pub struct RasterizedBlobImage {
     /// The rectangle that was rasterized in device pixels, relative to the
     /// image or tile.
     pub rasterized_rect: DeviceIntRect,
     /// Backing store. The format is stored out of band in `BlobImageDescriptor`.
-    ///
-    /// None means the image only has fully transparent content. We can skip storing
-    /// and rendering it.
-    pub data: Option<Arc<Vec<u8>>>,
+    pub data: Arc<Vec<u8>>,
 }
 
 /// Error code for when blob rasterization failed.
 #[derive(Clone, Debug)]
 pub enum BlobImageError {
     /// Out of memory.
     Oom,
     /// Other failure, embedding-specified.
--- a/gfx/wr/wrench/src/blob.rs
+++ b/gfx/wr/wrench/src/blob.rs
@@ -93,17 +93,17 @@ fn render_blob(
                         format!("Unsupported image format {:?}", descriptor.format),
                     ));
                 }
             }
         }
     }
 
     Ok(RasterizedBlobImage {
-        data: Some(Arc::new(texels)),
+        data: Arc::new(texels),
         rasterized_rect,
     })
 }
 
 /// See rawtest.rs. We use this to test that blob images are requested the right
 /// amount of times.
 pub struct BlobCallbacks {
     pub request: Box<dyn Fn(&[BlobImageParams]) + Send + 'static>,