Bug 1244228 - fix DrawTargetSkia::OptimizeSourceSurface to still create Skia surfaces for GPU canvases even if creating a GPU surface failed. r=jmuizelaar
authorLee Salzman <lsalzman@mozilla.com>
Wed, 03 Feb 2016 13:49:36 -0500
changeset 283052 e9875981edf2f0e125d95079d2e6ef4d0a8869af
parent 283010 751e2bc571abba9fca0f9c94f9ac88240b7fd2cb
child 283053 43f8fc8c8a85f75d5ae1a63123ea9a03ad3ac4f1
push id29974
push usercbook@mozilla.com
push dateFri, 05 Feb 2016 10:53:43 +0000
treeherdermozilla-central@1dbe350b57b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmuizelaar
bugs1244228
milestone47.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 1244228 - fix DrawTargetSkia::OptimizeSourceSurface to still create Skia surfaces for GPU canvases even if creating a GPU surface failed. r=jmuizelaar
gfx/2d/DrawTargetSkia.cpp
gfx/2d/DrawTargetSkia.h
gfx/2d/HelpersSkia.h
gfx/2d/SourceSurfaceSkia.cpp
gfx/2d/SourceSurfaceSkia.h
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -692,17 +692,18 @@ DrawTargetSkia::CreateSourceSurfaceFromD
 
 already_AddRefed<DrawTarget>
 DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
 {
   RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
 #ifdef USE_SKIA_GPU
   if (UsingSkiaGPU()) {
     // Try to create a GPU draw target first if we're currently using the GPU.
-    if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat)) {
+    // Mark the DT as cached so that shadow DTs, extracted subrects, and similar can be reused.
+    if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
       return target.forget();
     }
     // Otherwise, just fall back to a software draw target.
   }
 #endif
   if (!target->Init(aSize, aFormat)) {
     return nullptr;
   }
@@ -726,27 +727,41 @@ DrawTargetSkia::OptimizeSourceSurface(So
   if (UsingSkiaGPU()) {
     // Check if the underlying SkBitmap already has an associated GrTexture.
     if (aSurface->GetType() == SurfaceType::SKIA &&
         static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap().getTexture()) {
       RefPtr<SourceSurface> surface(aSurface);
       return surface.forget();
     }
 
+    SkBitmap bitmap = GetBitmapForSurface(aSurface);
+
     // Upload the SkBitmap to a GrTexture otherwise.
     SkAutoTUnref<GrTexture> texture(
-      GrRefCachedBitmapTexture(mGrContext.get(),
-                               GetBitmapForSurface(aSurface),
-                               GrTextureParams::ClampBilerp()));
+      GrRefCachedBitmapTexture(mGrContext.get(), bitmap, GrTextureParams::ClampBilerp()));
 
-    // Create a new SourceSurfaceSkia whose SkBitmap contains the GrTexture.
-    RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
-    if (surface->InitFromGrTexture(texture, aSurface->GetSize(), aSurface->GetFormat())) {
+    if (texture) {
+      // Create a new SourceSurfaceSkia whose SkBitmap contains the GrTexture.
+      RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+      if (surface->InitFromGrTexture(texture, aSurface->GetSize(), aSurface->GetFormat())) {
+        return surface.forget();
+      }
+    }
+
+    // The data was too big to fit in a GrTexture.
+    if (aSurface->GetType() == SurfaceType::SKIA) {
+      // It is already a Skia source surface, so just reuse it as-is.
+      RefPtr<SourceSurface> surface(aSurface);
       return surface.forget();
     }
+
+    // Wrap it in a Skia source surface so that can do tiled uploads on-demand.
+    RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
+    surface->InitFromBitmap(bitmap);
+    return surface.forget();
   }
 #endif
 
   if (aSurface->GetType() == SurfaceType::SKIA) {
     RefPtr<SourceSurface> surface(aSurface);
     return surface.forget();
   }
 
@@ -842,30 +857,53 @@ DrawTargetSkia::Init(const IntSize &aSiz
   mCanvas.adopt(new SkCanvas(bitmap));
   mSize = aSize;
 
   mFormat = aFormat;
   return true;
 }
 
 #ifdef USE_SKIA_GPU
+/** Indicating a DT should be cached means that space will be reserved in Skia's cache
+ * for the render target at creation time, with any unused resources exceeding the cache
+ * limits being purged. When the DT is freed, it will then be guaranteed to be kept around
+ * for subsequent allocations until it gets incidentally purged.
+ *
+ * If it is not marked as cached, no space will be purged to make room for the render
+ * target in the cache. When the DT is freed, If there is space within the resource limits
+ * it may be added to the cache, otherwise it will be freed immediately if the cache is
+ * already full.
+ *
+ * If you want to ensure that the resources will be kept around for reuse, it is better
+ * to mark them as cached. Such resources should be short-lived to ensure they don't
+ * permanently tie up cache resource limits. Long-lived resources should generally be
+ * left as uncached.
+ *
+ * In neither case will cache resource limits affect whether the resource allocation
+ * succeeds. The amount of in-use GPU resources is allowed to exceed the size of the cache.
+ * Thus, only hard GPU out-of-memory conditions will cause resource allocation to fail.
+ */
 bool
 DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
                                   const IntSize &aSize,
-                                  SurfaceFormat aFormat)
+                                  SurfaceFormat aFormat,
+                                  bool aCached)
 {
   MOZ_ASSERT(aGrContext, "null GrContext");
 
   if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
     return false;
   }
 
   // Create a GPU rendertarget/texture using the supplied GrContext.
   // NewRenderTarget also implicitly clears the underlying texture on creation.
-  SkAutoTUnref<SkSurface> gpuSurface(SkSurface::NewRenderTarget(aGrContext, SkSurface::kNo_Budgeted, MakeSkiaImageInfo(aSize, aFormat)));
+  SkAutoTUnref<SkSurface> gpuSurface(
+    SkSurface::NewRenderTarget(aGrContext,
+                               SkSurface::Budgeted(aCached),
+                               MakeSkiaImageInfo(aSize, aFormat)));
   if (!gpuSurface) {
     return false;
   }
 
   mGrContext = aGrContext;
   mSize = aSize;
   mFormat = aFormat;
 
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -111,17 +111,24 @@ public:
   virtual void *GetNativeSurface(NativeSurfaceType aType) override;
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   void Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
 
 #ifdef USE_SKIA_GPU
   bool InitWithGrContext(GrContext* aGrContext,
                          const IntSize &aSize,
-                         SurfaceFormat aFormat) override;
+                         SurfaceFormat aFormat,
+                         bool aCached);
+  virtual bool
+    InitWithGrContext(GrContext* aGrContext,
+                      const IntSize &aSize,
+                      SurfaceFormat aFormat) override {
+    return InitWithGrContext(aGrContext, aSize, aFormat, false);
+  }
 #endif
 
   // Skia assumes that texture sizes fit in 16-bit signed integers.
   static size_t GetMaxSurfaceSize() {
     return 32767;
   }
 
   operator std::string() const {
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -35,22 +35,23 @@ GfxFormatToSkiaColorType(SurfaceFormat f
     case SurfaceFormat::A8:
       return kAlpha_8_SkColorType;
     default:
       return kRGBA_8888_SkColorType;
   }
 }
 
 static inline SurfaceFormat
-SkiaColorTypeToGfxFormat(SkColorType type)
+SkiaColorTypeToGfxFormat(SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType)
 {
-  switch (type)
+  switch (aColorType)
   {
     case kBGRA_8888_SkColorType:
-      return SurfaceFormat::B8G8R8A8;
+      return aAlphaType == kOpaque_SkAlphaType ?
+               SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
     case kRGB_565_SkColorType:
       return SurfaceFormat::R5G6B5_UINT16;
     case kAlpha_8_SkColorType:
       return SurfaceFormat::A8;
     default:
       return SurfaceFormat::B8G8R8A8;
   }
 }
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -76,16 +76,26 @@ SourceSurfaceSkia::InitFromData(unsigned
   }
 
   mSize = aSize;
   mFormat = aFormat;
   mStride = mBitmap.rowBytes();
   return true;
 }
 
+void
+SourceSurfaceSkia::InitFromBitmap(const SkBitmap& aBitmap)
+{
+  mBitmap = aBitmap;
+
+  mSize = IntSize(mBitmap.width(), mBitmap.height());
+  mFormat = SkiaColorTypeToGfxFormat(mBitmap.colorType(), mBitmap.alphaType());
+  mStride = mBitmap.rowBytes();
+}
+
 #ifdef USE_SKIA_GPU
 bool
 SourceSurfaceSkia::InitFromGrTexture(GrTexture* aTexture,
                                      const IntSize &aSize,
                                      SurfaceFormat aFormat)
 {
   if (!aTexture) {
     return false;
--- a/gfx/2d/SourceSurfaceSkia.h
+++ b/gfx/2d/SourceSurfaceSkia.h
@@ -34,16 +34,18 @@ public:
                     const IntSize &aSize,
                     int32_t aStride,
                     SurfaceFormat aFormat);
 
   bool InitFromCanvas(SkCanvas* aCanvas,
                       SurfaceFormat aFormat,
                       DrawTargetSkia* aOwner);
 
+  void InitFromBitmap(const SkBitmap& aBitmap);
+
 #ifdef USE_SKIA_GPU
   /**
    * NOTE: While wrapping a Texture for SkiaGL, the texture *must* be created
    *       with the same GLcontext of DrawTargetSkia
    */
   bool InitFromGrTexture(GrTexture* aTexture,
                          const IntSize &aSize,
                          SurfaceFormat aFormat);