Bug 1360001. Add WebRenderPaintedLayerBlob for painting with BlobImages. r=kats
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Wed, 26 Apr 2017 16:31:59 -0400
changeset 357015 8f1d4b0a03e4285eeb4b2aa326aea5e4f42edc60
parent 357014 de635d62079bfc358c85a692d729bf1e33da15ca
child 357016 ccd2f70fc8d4848babf04e2b5a8c9ade7f1839ae
push id31780
push userkwierso@gmail.com
push dateMon, 08 May 2017 20:34:47 +0000
treeherdermozilla-central@bab7046ee2d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1360001
milestone55.0a1
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
Bug 1360001. Add WebRenderPaintedLayerBlob for painting with BlobImages. r=kats This is a bit of a rough implementation but it works enough to start.
gfx/layers/moz.build
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
gfx/layers/wr/WebRenderPaintedLayerBlob.h
gfx/thebes/gfxPrefs.h
gfx/webrender_bindings/Moz2DImageRenderer.cpp
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -403,16 +403,17 @@ UNIFIED_SOURCES += [
     'wr/WebRenderContainerLayer.cpp',
     'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
     'wr/WebRenderLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
+    'wr/WebRenderPaintedLayerBlob.cpp',
     'wr/WebRenderScrollData.cpp',
     'wr/WebRenderTextLayer.cpp',
     # XXX here are some unified build error.
     #'wr/WebRenderTextureHost.cpp'
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "WebRenderCanvasLayer.h"
 #include "WebRenderColorLayer.h"
 #include "WebRenderContainerLayer.h"
 #include "WebRenderImageLayer.h"
 #include "WebRenderPaintedLayer.h"
+#include "WebRenderPaintedLayerBlob.h"
 #include "WebRenderTextLayer.h"
 #include "WebRenderDisplayItemLayer.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
@@ -493,17 +494,21 @@ void
 WebRenderLayerManager::SetRoot(Layer* aLayer)
 {
   mRoot = aLayer;
 }
 
 already_AddRefed<PaintedLayer>
 WebRenderLayerManager::CreatePaintedLayer()
 {
-  return MakeAndAddRef<WebRenderPaintedLayer>(this);
+  if (gfxPrefs::WebRenderBlobImages()) {
+    return MakeAndAddRef<WebRenderPaintedLayerBlob>(this);
+  } else {
+    return MakeAndAddRef<WebRenderPaintedLayer>(this);
+  }
 }
 
 already_AddRefed<ContainerLayer>
 WebRenderLayerManager::CreateContainerLayer()
 {
   return MakeAndAddRef<WebRenderContainerLayer>(this);
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderPaintedLayerBlob.h"
+
+#include "gfxPrefs.h"
+#include "gfxUtils.h"
+#include "LayersLogging.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/layers/StackingContextHelper.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/UpdateImageHelper.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+WebRenderPaintedLayerBlob::RenderLayer(wr::DisplayListBuilder& aBuilder)
+{
+  LayerIntRegion visibleRegion = GetVisibleRegion();
+  LayerIntRect bounds = visibleRegion.GetBounds();
+  LayerIntSize size = bounds.Size();
+
+  if (visibleRegion.IsEmpty()) {
+    if (gfxPrefs::LayersDump()) {
+      printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
+    }
+    return;
+  }
+
+  nsIntRegion regionToPaint;
+  regionToPaint.Sub(mVisibleRegion.ToUnknownRegion(), mValidRegion);
+
+  // We have something to paint but can't. This usually happens only in
+  // empty transactions
+  if (!regionToPaint.IsEmpty() && !WrManager()->GetPaintedLayerCallback()) {
+    WrManager()->SetTransactionIncomplete();
+    return;
+  }
+
+  IntSize imageSize(size.ToUnknownSize());
+  RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>();
+  RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, imageSize, gfx::SurfaceFormat::B8G8R8X8);
+  RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt);
+
+  if (!regionToPaint.IsEmpty() && WrManager()->GetPaintedLayerCallback()) {
+    dt->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
+    dt->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
+    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dt);
+    MOZ_ASSERT(ctx); // already checked the target above
+
+    WrManager()->GetPaintedLayerCallback()(this,
+                                           ctx,
+                                           visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
+                                           DrawRegionClip::DRAW, nsIntRegion(), WrManager()->GetPaintedLayerCallbackData());
+
+    if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
+      dt->SetTransform(Matrix());
+      dt->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5)));
+    }
+
+  } else {
+    // we need to reuse the blob image
+    MOZ_ASSERT(mExternalImageId);
+    MOZ_ASSERT(mImageContainer->HasCurrentImage());
+    MOZ_ASSERT(GetInvalidRegion().IsEmpty());
+  }
+
+  wr::ByteBuffer bytes;
+  bytes.Allocate(recorder->RecordingSize());
+  DebugOnly<bool> ok = recorder->CopyRecording((char*)bytes.AsSlice().begin().get(), bytes.AsSlice().length());
+  MOZ_ASSERT(ok);
+
+  StackingContextHelper sc(aBuilder, this);
+  LayerRect rect = Bounds();
+  DumpLayerInfo("PaintedLayer", rect);
+
+  LayerRect clipRect = ClipRect().valueOr(rect);
+  Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
+  WrClipRegion clip = aBuilder.BuildClipRegion(
+      sc.ToRelativeWrRect(clipRect),
+      mask.ptrOr(nullptr));
+
+  WrImageKey key = GetImageKey();
+  WrBridge()->SendAddBlobImage(key, imageSize, size.width * 4, dt->GetFormat(), bytes);
+  WrManager()->AddImageKeyForDiscard(key);
+
+  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, wr::ImageRendering::Auto, key);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERPAINTEDLAYERBLOB_H
+#define GFX_WEBRENDERPAINTEDLAYERBLOB_H
+
+#include "Layers.h"
+#include "mozilla/layers/ContentClient.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderPaintedLayerBlob : public WebRenderLayer,
+                                  public PaintedLayer {
+public:
+  typedef RotatedContentBuffer::PaintState PaintState;
+  typedef RotatedContentBuffer::ContentType ContentType;
+
+  explicit WebRenderPaintedLayerBlob(WebRenderLayerManager* aLayerManager)
+    : PaintedLayer(aLayerManager, static_cast<WebRenderLayer*>(this), LayerManager::NONE)
+  {
+    MOZ_COUNT_CTOR(WebRenderPaintedLayerBlob);
+  }
+
+protected:
+  virtual ~WebRenderPaintedLayerBlob()
+  {
+    MOZ_COUNT_DTOR(WebRenderPaintedLayerBlob);
+    if (mExternalImageId.isSome()) {
+      WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
+    }
+  }
+
+  wr::MaybeExternalImageId mExternalImageId;
+
+public:
+  virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+  {
+    mInvalidRegion.Add(aRegion);
+    mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+  }
+
+  Layer* GetLayer() override { return this; }
+  void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
+private:
+  RefPtr<ImageContainer> mImageContainer;
+  RefPtr<ImageClient> mImageClient;
+
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERPAINTEDLAYERBLOB_H
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -679,16 +679,17 @@ private:
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.max-perf-warnings",               WebGLMaxPerfWarnings, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.max-acceptable-fb-status-invals", WebGLMaxAcceptableFBStatusInvals, int32_t, 0);
 
   DECL_GFX_PREF(Live, "webgl.webgl2-compat-mode",              WebGL2CompatMode, bool, false);
+  DECL_GFX_PREF(Live, "webrender.blob-images",                 WebRenderBlobImages, bool, false);
   DECL_GFX_PREF(Live, "webrender.highlight-painted-layers",    WebRenderHighlightPaintedLayers, bool, false);
 
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
   // Manage the singleton:
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -1,40 +1,100 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "gfxUtils.h"
 #include "mozilla/Range.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/InlineTranslator.h"
+#include "mozilla/gfx/RecordedEvent.h"
 #include "WebRenderTypes.h"
 
+#include <iostream>
+
 namespace mozilla {
 namespace wr {
 
+class InMemoryStreamBuffer: public std::streambuf
+{
+public:
+  explicit InMemoryStreamBuffer(const Range<const uint8_t> aBlob)
+  {
+    // we need to cast away the const because C++ doesn't
+    // have a separate type of streambuf that can only
+    // be read from
+    auto start = const_cast<char*>(reinterpret_cast<const char*>(aBlob.begin().get()));
+    setg(start, start, start + aBlob.length());
+  }
+};
+
 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
                                 gfx::IntSize aSize,
                                 gfx::SurfaceFormat aFormat,
-                                Range<const uint8_t> output)
+                                Range<uint8_t> aOutput)
 {
-  return false; // TODO(nical)
+  MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
+  if (aSize.width <= 0 || aSize.height <= 0) {
+    return false;
+  }
+
+  auto stride = aSize.width * gfx::BytesPerPixel(aFormat);
+
+  if (aOutput.length() < static_cast<size_t>(aSize.height * stride)) {
+    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(),
+    aSize,
+    stride,
+    aFormat,
+    uninitialized
+  );
+
+  if (!dt) {
+    return false;
+  }
+
+  InMemoryStreamBuffer streamBuffer(aBlob);
+  std::istream stream(&streamBuffer);
+
+  gfx::Matrix baseTransform;
+  gfx::InlineTranslator translator(dt, baseTransform);
+
+  auto ret = translator.TranslateRecording(stream);
+
+#if 0
+  static int i = 0;
+  char filename[40];
+  sprintf(filename, "out%d.png", i++);
+  gfxUtils::WriteAsPNG(dt, filename);
+#endif
+
+  return ret;
 }
 
 } // namespace
 } // namespace
 
 extern "C" {
 
 bool wr_moz2d_render_cb(const WrByteSlice blob,
                         uint32_t width, uint32_t height,
                         mozilla::wr::ImageFormat aFormat,
-                        WrByteSlice output)
+                        MutByteSlice output)
 {
   return mozilla::wr::Moz2DRenderCallback(mozilla::wr::ByteSliceToRange(blob),
                                           mozilla::gfx::IntSize(width, height),
                                           mozilla::wr::WrImageFormatToSurfaceFormat(aFormat),
-                                          mozilla::wr::ByteSliceToRange(output));
+                                          mozilla::wr::MutByteSliceToRange(output));
 }
 
 } // extern
 
 
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -519,16 +519,20 @@ struct ByteBuffer
 inline WrByteSlice RangeToByteSlice(mozilla::Range<uint8_t> aRange) {
   return WrByteSlice { aRange.begin().get(), aRange.length() };
 }
 
 inline mozilla::Range<const uint8_t> ByteSliceToRange(WrByteSlice aWrSlice) {
   return mozilla::Range<const uint8_t>(aWrSlice.buffer, aWrSlice.len);
 }
 
+inline mozilla::Range<uint8_t> MutByteSliceToRange(MutByteSlice aWrSlice) {
+  return mozilla::Range<uint8_t>(aWrSlice.buffer, aWrSlice.len);
+}
+
 struct BuiltDisplayList {
   VecU8 dl;
   WrBuiltDisplayListDescriptor dl_desc;
   VecU8 aux;
   WrAuxiliaryListsDescriptor aux_desc;
 };
 
 } // namespace wr
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1631,58 +1631,77 @@ pub unsafe extern "C" fn wr_dp_push_buil
 
     let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
     let aux = AuxiliaryLists::from_data(aux_vec, aux_descriptor);
 
     state.frame_builder.dl_builder.push_built_display_list(dl, aux);
 }
 
 struct Moz2dImageRenderer {
-    _images: HashMap<WrImageKey, BlobImageResult>,
+    images: HashMap<ImageKey, BlobImageData>,
+
+    // The images rendered in the current frame (not kept here between frames)
+    rendered_images: HashMap<BlobImageRequest, BlobImageResult>,
 }
 
 impl BlobImageRenderer for Moz2dImageRenderer {
-    fn add(&mut self, _key: ImageKey, _data: BlobImageData, _tiling: Option<TileSize>) {
-        // Not implemented yet.
+    fn add(&mut self, key: ImageKey, data: BlobImageData, _tiling: Option<TileSize>) {
+        self.images.insert(key, data);
     }
 
-    fn update(&mut self, _key: ImageKey, _data: BlobImageData) {
-        // Not implemented yet.
+    fn update(&mut self, key: ImageKey, data: BlobImageData) {
+        self.images.insert(key, data);
     }
 
-    fn delete(&mut self, _key: ImageKey) {
-        // Not implemented yet.
+    fn delete(&mut self, key: ImageKey) {
+        self.images.remove(&key);
     }
 
     fn request(&mut self,
-               _key: BlobImageRequest,
-               _descriptor: &BlobImageDescriptor,
+               request: BlobImageRequest,
+               descriptor: &BlobImageDescriptor,
                _dirty_rect: Option<DeviceUintRect>,
                _images: &ImageStore) {
-        // Not implemented yet.
-    }
+        let data = self.images.get(&request.key).unwrap();
+        let buf_size = (descriptor.width * descriptor.height * descriptor.format.bytes_per_pixel().unwrap()) as usize;
+        let mut output = vec![255u8; buf_size];
 
-    fn resolve(&mut self, _key: BlobImageRequest) -> BlobImageResult {
-        // Not implemented yet.
-        Err(BlobImageError::Other("unimplemented!".to_string()))
+        unsafe {
+            if wr_moz2d_render_cb(WrByteSlice::new(&data[..]),
+                                  descriptor.width,
+                                  descriptor.height,
+                                  descriptor.format,
+                                  MutByteSlice::new(output.as_mut_slice())) {
+                self.rendered_images.insert(request,
+                                            Ok(RasterizedBlobImage {
+                              width: descriptor.width,
+                              height: descriptor.height,
+                              data: output,
+                          }));
+            }
+        }
+    }
+    fn resolve(&mut self, request: BlobImageRequest) -> BlobImageResult {
+        self.rendered_images.remove(&request).unwrap_or(Err(BlobImageError::InvalidKey))
     }
 }
 
 impl Moz2dImageRenderer {
     fn new() -> Self {
         Moz2dImageRenderer {
-            _images: HashMap::new(),
+            images: HashMap::new(),
+            rendered_images: HashMap::new()
         }
     }
 }
 
 // TODO: nical
 // Update for the new blob image interface changes.
 //
-// extern "C" {
-//     // TODO: figure out the API for tiled blob images.
-//     fn wr_moz2d_render_cb(blob: WrByteSlice,
-//                           width: u32,
-//                           height: u32,
-//                           format: WrImageFormat,
-//                           output: MutByteSlice)
-//                           -> bool;
-// }
+extern "C" {
+     // TODO: figure out the API for tiled blob images.
+     fn wr_moz2d_render_cb(blob: WrByteSlice,
+                           width: u32,
+                           height: u32,
+                           format: WrImageFormat,
+                           output: MutByteSlice)
+                           -> bool;
+}
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.1.5 */
+/* Generated with cbindgen:0.1.6 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file, clone `https://github.com/rlhunt/cbindgen` or run `cargo install cbindgen`,
  * then run `cbindgen -c wr gfx/webrender_bindings/ gfx/webrender_bindings/webrender_ffi_generated.h` */
 
 enum class WrBorderStyle : uint32_t {
   None = 0,
   Solid = 1,
@@ -454,16 +454,42 @@ struct WrGlyphInstance {
   WrPoint point;
 
   bool operator==(const WrGlyphInstance& aOther) const {
     return index == aOther.index &&
       point == aOther.point;
   }
 };
 
+struct MutByteSlice {
+  uint8_t* buffer;
+  size_t len;
+
+  bool operator==(const MutByteSlice& aOther) const {
+    return buffer == aOther.buffer &&
+      len == aOther.len;
+  }
+};
+
+struct WrWindowId {
+  uint64_t mHandle;
+
+  bool operator==(const WrWindowId& aOther) const {
+    return mHandle == aOther.mHandle;
+  }
+
+  bool operator<(const WrWindowId& aOther) const {
+    return mHandle < aOther.mHandle;
+  }
+
+  bool operator<=(const WrWindowId& aOther) const {
+    return mHandle <= aOther.mHandle;
+  }
+};
+
 struct WrRenderedEpochs;
 
 struct WrRenderer;
 
 struct WrExternalImage {
   WrExternalImageType image_type;
   uint32_t handle;
   float u0;
@@ -496,32 +522,16 @@ struct WrExternalImageHandler {
 
   bool operator==(const WrExternalImageHandler& aOther) const {
     return external_image_obj == aOther.external_image_obj &&
       lock_func == aOther.lock_func &&
       unlock_func == aOther.unlock_func;
   }
 };
 
-struct WrWindowId {
-  uint64_t mHandle;
-
-  bool operator==(const WrWindowId& aOther) const {
-    return mHandle == aOther.mHandle;
-  }
-
-  bool operator<(const WrWindowId& aOther) const {
-    return mHandle < aOther.mHandle;
-  }
-
-  bool operator<=(const WrWindowId& aOther) const {
-    return mHandle <= aOther.mHandle;
-  }
-};
-
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file, clone `https://github.com/rlhunt/cbindgen` or run `cargo install cbindgen`,
  * then run `cbindgen -c wr gfx/webrender_bindings/ gfx/webrender_bindings/webrender_ffi_generated.h` */
 
 WR_INLINE void
 wr_api_add_blob_image(WrAPI* api,
     WrImageKey image_key,
     const WrImageDescriptor* descriptor,