Add discard API to SkCanvas, plumb it to glDiscardFramebuffer()
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 28 Mar 2014 16:08:05 +0000 (16:08 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 28 Mar 2014 16:08:05 +0000 (16:08 +0000)
BUG=skia:2349
R=robertphillips@google.com, reed@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/211683002

git-svn-id: http://skia.googlecode.com/svn/trunk@13976 2bbb7eff-a529-9590-31e7-b0007b416f81

22 files changed:
gyp/core.gypi
include/core/SkCanvas.h
include/gpu/GrColor.h
include/gpu/GrContext.h
include/gpu/GrRenderTarget.h
src/core/SkCanvas.cpp
src/gpu/GrContext.cpp
src/gpu/GrDrawTarget.cpp
src/gpu/GrDrawTarget.h
src/gpu/GrDrawTargetCaps.h
src/gpu/GrInOrderDrawBuffer.cpp
src/gpu/GrInOrderDrawBuffer.h
src/gpu/GrRenderTarget.cpp
src/gpu/gl/GrGLCaps.cpp
src/gpu/gl/GrGLCaps.h
src/gpu/gl/GrGLDefines.h
src/gpu/gl/GrGpuGL.cpp
src/gpu/gl/GrGpuGL.h
src/image/SkSurface.cpp
src/image/SkSurface_Base.h
src/image/SkSurface_Gpu.cpp
src/image/SkSurface_Picture.cpp

index e98400f..40d8c5c 100644 (file)
         '<(skia_src_path)/image/SkImage_Picture.cpp',
         '<(skia_src_path)/image/SkImage_Raster.cpp',
         '<(skia_src_path)/image/SkSurface.cpp',
+        '<(skia_src_path)/image/SkSurface_Base.h',
 #        '<(skia_src_path)/image/SkSurface_Gpu.cpp',
         '<(skia_src_path)/image/SkSurface_Picture.cpp',
         '<(skia_src_path)/image/SkSurface_Raster.cpp',
         '<(skia_include_path)/core/SkFontHost.h',
         '<(skia_include_path)/core/SkGeometry.h',
         '<(skia_include_path)/core/SkGraphics.h',
+        '<(skia_include_path)/core/SkImage.h',
         '<(skia_include_path)/core/SkImageDecoder.h',
         '<(skia_include_path)/core/SkImageEncoder.h',
         '<(skia_include_path)/core/SkImageFilter.h',
         '<(skia_include_path)/core/SkString.h',
         '<(skia_include_path)/core/SkStringUtils.h',
         '<(skia_include_path)/core/SkStrokeRec.h',
+        '<(skia_include_path)/core/SkSurface.h',
         '<(skia_include_path)/core/SkTArray.h',
         '<(skia_include_path)/core/SkTDArray.h',
         '<(skia_include_path)/core/SkTDStack.h',
index 3eadb4c..4d9c462 100644 (file)
@@ -597,6 +597,20 @@ public:
     virtual void clear(SkColor);
 
     /**
+     * This makes the contents of the canvas undefined. Subsequent calls that
+     * require reading the canvas contents will produce undefined results. Examples
+     * include blending and readPixels. The actual implementation is backend-
+     * dependent and one legal implementation is to do nothing. Like clear(), this
+     * ignores the clip.
+     *
+     * This function should only be called if the caller intends to subsequently
+     * draw to the canvas. The canvas may do real work at discard() time in order
+     * to optimize performance on subsequent draws. Thus, if you call this and then
+     * never draw to the canvas subsequently you may pay a perfomance penalty.
+     */
+    void discard() { this->onDiscard(); }
+
+    /**
      *  Fill the entire canvas' bitmap (restricted to the current clip) with the
      *  specified paint.
      *  @param paint    The paint used to fill the canvas
@@ -1176,6 +1190,8 @@ protected:
     virtual void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle);
     virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op);
 
+    virtual void onDiscard();
+
     // Returns the canvas to be used by DrawIter. Default implementation
     // returns this. Subclasses that encapsulate an indirect canvas may
     // need to overload this method. The impl must keep track of this, as it
index cf7e7df..b0bce3f 100644 (file)
 #include "GrTypes.h"
 
 /**
- *  GrColor is 4 bytes for R, G, B, A, in a compile-time specific order. The
- *  components are stored premultiplied.
+ * GrColor is 4 bytes for R, G, B, A, in a specific order defined below. The components are stored
+ * premultiplied.
  */
 typedef uint32_t GrColor;
 
-
 // shift amount to assign a component to a GrColor int
 // These shift values are chosen for compatibility with GL attrib arrays
 // ES doesn't allow BGRA vertex attrib order so if they were not in this order
-// we'd have to swizzle in shaders. Note the assumption that the cpu is little
-// endian.
-#define GrColor_SHIFT_R     0
-#define GrColor_SHIFT_G     8
-#define GrColor_SHIFT_B     16
-#define GrColor_SHIFT_A     24
+// we'd have to swizzle in shaders.
+#ifdef SK_CPU_BENDIAN
+    #define GrColor_SHIFT_R     24
+    #define GrColor_SHIFT_G     16
+    #define GrColor_SHIFT_B     8
+    #define GrColor_SHIFT_A     0
+#else
+    #define GrColor_SHIFT_R     0
+    #define GrColor_SHIFT_G     8
+    #define GrColor_SHIFT_B     16
+    #define GrColor_SHIFT_A     24
+#endif
 
 /**
  *  Pack 4 components (RGBA) into a GrColor int
@@ -58,6 +63,22 @@ static inline GrColor GrColorPackRGBA(unsigned r, unsigned g,
  */
 #define GrColor_ILLEGAL     (~(0xFF << GrColor_SHIFT_A))
 
+/**
+ * Assert in debug builds that a GrColor is premultiplied.
+ */
+static inline void GrColorIsPMAssert(GrColor c) {
+#ifdef SK_DEBUG
+    unsigned a = GrColorUnpackA(c);
+    unsigned r = GrColorUnpackR(c);
+    unsigned g = GrColorUnpackG(c);
+    unsigned b = GrColorUnpackB(c);
+
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+#endif
+}
+
 /** Converts a GrColor to an rgba array of GrGLfloat */
 static inline void GrColorToRGBAFloat(GrColor color, float rgba[4]) {
     static const float ONE_OVER_255 = 1.f / 255.f;
index 5f96457..d2a3ada 100644 (file)
@@ -653,7 +653,13 @@ public:
      * perform a resolve to a GrTexture used as the source of a draw or before
      * reading pixels back from a GrTexture or GrRenderTarget.
      */
-    void resolveRenderTarget(GrRenderTarget* target);
+    void resolveRenderTarget(GrRenderTarget*);
+
+    /**
+     * Provides a perfomance hint that the render target's contents are allowed
+     * to become undefined.
+     */
+    void discardRenderTarget(GrRenderTarget*);
 
 #ifdef SK_DEVELOPER
     void dumpFontCache() const;
index f43aba9..ac3cbee 100644 (file)
@@ -121,6 +121,12 @@ public:
      */
     void resolve();
 
+    /**
+     * Provide a performance hint that the render target's contents are allowed
+     * to become undefined.
+     */
+    void discard();
+
     // a MSAA RT may require explicit resolving , it may auto-resolve (e.g. FBO
     // 0 in GL), or be unresolvable because the client didn't give us the
     // resolve destination.
index e8336ff..e575aec 100644 (file)
@@ -1912,6 +1912,12 @@ void SkCanvas::clear(SkColor color) {
     }
 }
 
+void SkCanvas::onDiscard() {
+    if (NULL != fSurfaceBase) {
+        fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
+    }
+}
+
 void SkCanvas::drawPaint(const SkPaint& paint) {
     this->internalDrawPaint(paint);
 }
index 3a2bbe8..c26327e 100644 (file)
@@ -1440,6 +1440,14 @@ void GrContext::resolveRenderTarget(GrRenderTarget* target) {
     fGpu->resolveRenderTarget(target);
 }
 
+void GrContext::discardRenderTarget(GrRenderTarget* target) {
+    SkASSERT(target);
+    ASSERT_OWNED_RESOURCE(target);
+    AutoRestoreEffects are;
+    AutoCheckFlush acf(this);
+    this->prepareToDraw(NULL, BUFFERED_DRAW, &are, &acf)->discard(target);
+}
+
 void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst, const SkIPoint* topLeft) {
     if (NULL == src || NULL == dst) {
         return;
index 8d3ded2..1069844 100644 (file)
@@ -1019,6 +1019,7 @@ void GrDrawTargetCaps::reset() {
     fBufferLockSupport = false;
     fPathRenderingSupport = false;
     fDstReadInShaderSupport = false;
+    fDiscardRenderTargetSupport = false;
     fReuseScratchTextures = true;
     fGpuTracingSupport = false;
 
@@ -1042,6 +1043,7 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) {
     fBufferLockSupport = other.fBufferLockSupport;
     fPathRenderingSupport = other.fPathRenderingSupport;
     fDstReadInShaderSupport = other.fDstReadInShaderSupport;
+    fDiscardRenderTargetSupport = other.fDiscardRenderTargetSupport;
     fReuseScratchTextures = other.fReuseScratchTextures;
     fGpuTracingSupport = other.fGpuTracingSupport;
 
@@ -1057,23 +1059,24 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) {
 SkString GrDrawTargetCaps::dump() const {
     SkString r;
     static const char* gNY[] = {"NO", "YES"};
-    r.appendf("8 Bit Palette Support       : %s\n", gNY[f8BitPaletteSupport]);
-    r.appendf("MIP Map Support             : %s\n", gNY[fMipMapSupport]);
-    r.appendf("NPOT Texture Tile Support   : %s\n", gNY[fNPOTTextureTileSupport]);
-    r.appendf("Two Sided Stencil Support   : %s\n", gNY[fTwoSidedStencilSupport]);
-    r.appendf("Stencil Wrap Ops  Support   : %s\n", gNY[fStencilWrapOpsSupport]);
-    r.appendf("HW AA Lines Support         : %s\n", gNY[fHWAALineSupport]);
-    r.appendf("Shader Derivative Support   : %s\n", gNY[fShaderDerivativeSupport]);
-    r.appendf("Geometry Shader Support     : %s\n", gNY[fGeometryShaderSupport]);
-    r.appendf("Dual Source Blending Support: %s\n", gNY[fDualSourceBlendingSupport]);
-    r.appendf("Buffer Lock Support         : %s\n", gNY[fBufferLockSupport]);
-    r.appendf("Path Rendering Support      : %s\n", gNY[fPathRenderingSupport]);
-    r.appendf("Dst Read In Shader Support  : %s\n", gNY[fDstReadInShaderSupport]);
-    r.appendf("Reuse Scratch Textures      : %s\n", gNY[fReuseScratchTextures]);
-    r.appendf("Gpu Tracing Support         : %s\n", gNY[fGpuTracingSupport]);
-    r.appendf("Max Texture Size            : %d\n", fMaxTextureSize);
-    r.appendf("Max Render Target Size      : %d\n", fMaxRenderTargetSize);
-    r.appendf("Max Sample Count            : %d\n", fMaxSampleCount);
+    r.appendf("8 Bit Palette Support        : %s\n", gNY[f8BitPaletteSupport]);
+    r.appendf("MIP Map Support              : %s\n", gNY[fMipMapSupport]);
+    r.appendf("NPOT Texture Tile Support    : %s\n", gNY[fNPOTTextureTileSupport]);
+    r.appendf("Two Sided Stencil Support    : %s\n", gNY[fTwoSidedStencilSupport]);
+    r.appendf("Stencil Wrap Ops  Support    : %s\n", gNY[fStencilWrapOpsSupport]);
+    r.appendf("HW AA Lines Support          : %s\n", gNY[fHWAALineSupport]);
+    r.appendf("Shader Derivative Support    : %s\n", gNY[fShaderDerivativeSupport]);
+    r.appendf("Geometry Shader Support      : %s\n", gNY[fGeometryShaderSupport]);
+    r.appendf("Dual Source Blending Support : %s\n", gNY[fDualSourceBlendingSupport]);
+    r.appendf("Buffer Lock Support          : %s\n", gNY[fBufferLockSupport]);
+    r.appendf("Path Rendering Support       : %s\n", gNY[fPathRenderingSupport]);
+    r.appendf("Dst Read In Shader Support   : %s\n", gNY[fDstReadInShaderSupport]);
+    r.appendf("Discard Render Target Support: %s\n", gNY[fDiscardRenderTargetSupport]);
+    r.appendf("Reuse Scratch Textures       : %s\n", gNY[fReuseScratchTextures]);
+    r.appendf("Gpu Tracing Support          : %s\n", gNY[fGpuTracingSupport]);
+    r.appendf("Max Texture Size             : %d\n", fMaxTextureSize);
+    r.appendf("Max Render Target Size       : %d\n", fMaxRenderTargetSize);
+    r.appendf("Max Sample Count             : %d\n", fMaxSampleCount);
 
     static const char* kConfigNames[] = {
         "Unknown",  // kUnknown_GrPixelConfig
index 5b26ff5..6b3d463 100644 (file)
@@ -438,6 +438,12 @@ public:
                        GrRenderTarget* renderTarget = NULL) = 0;
 
     /**
+     * Discards the contents render target. NULL indicates that the current render target should
+     * be discarded.
+     **/
+    virtual void discard(GrRenderTarget* = NULL) = 0;
+
+    /**
      * Called at start and end of gpu trace marking
      * GR_CREATE_GPU_TRACE_MARKER(marker_str, target) will automatically call these at the start
      * and end of a code block respectively
index b33217a..a77bce4 100644 (file)
@@ -40,6 +40,7 @@ public:
     bool bufferLockSupport() const { return fBufferLockSupport; }
     bool pathRenderingSupport() const { return fPathRenderingSupport; }
     bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; }
+    bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; }
     bool gpuTracingSupport() const { return fGpuTracingSupport; }
 
     // Scratch textures not being reused means that those scratch textures
@@ -71,6 +72,7 @@ protected:
     bool fBufferLockSupport         : 1;
     bool fPathRenderingSupport      : 1;
     bool fDstReadInShaderSupport    : 1;
+    bool fDiscardRenderTargetSupport: 1;
     bool fReuseScratchTextures      : 1;
     bool fGpuTracingSupport         : 1;
 
index 178df75..8d34e6c 100644 (file)
@@ -504,6 +504,7 @@ void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color,
         rect = &r;
     }
     Clear* clr = this->recordClear();
+    GrColorIsPMAssert(color);
     clr->fColor = color;
     clr->fRect = *rect;
     clr->fCanIgnoreRect = canIgnoreRect;
@@ -511,6 +512,20 @@ void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color,
     renderTarget->ref();
 }
 
+void GrInOrderDrawBuffer::discard(GrRenderTarget* renderTarget) {
+    if (!this->caps()->discardRenderTargetSupport()) {
+        return;
+    }
+    if (NULL == renderTarget) {
+        renderTarget = this->drawState()->getRenderTarget();
+        SkASSERT(NULL != renderTarget);
+    }
+    Clear* clr = this->recordClear();
+    clr->fColor = GrColor_ILLEGAL;
+    clr->fRenderTarget = renderTarget;
+    renderTarget->ref();
+}
+
 void GrInOrderDrawBuffer::reset() {
     SkASSERT(1 == fGeoPoolStateStack.count());
     this->resetVertexSource();
@@ -630,10 +645,14 @@ void GrInOrderDrawBuffer::flush() {
                 ++currClip;
                 break;
             case kClear_Cmd:
-                fDstGpu->clear(&fClears[currClear].fRect,
-                               fClears[currClear].fColor,
-                               fClears[currClear].fCanIgnoreRect,
-                               fClears[currClear].fRenderTarget);
+                if (GrColor_ILLEGAL == fClears[currClear].fColor) {
+                    fDstGpu->discard(fClears[currClear].fRenderTarget);
+                } else {
+                    fDstGpu->clear(&fClears[currClear].fRect,
+                                   fClears[currClear].fColor,
+                                   fClears[currClear].fCanIgnoreRect,
+                                   fClears[currClear].fRenderTarget);
+                }
                 ++currClear;
                 break;
             case kCopySurface_Cmd:
index 72d0f96..0e0c57d 100644 (file)
@@ -72,7 +72,9 @@ public:
     virtual void clear(const SkIRect* rect,
                        GrColor color,
                        bool canIgnoreRect,
-                       GrRenderTarget* renderTarget = NULL) SK_OVERRIDE;
+                       GrRenderTarget* renderTarget) SK_OVERRIDE;
+
+    virtual void discard(GrRenderTarget*) SK_OVERRIDE;
 
     virtual void initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) SK_OVERRIDE;
 
@@ -125,6 +127,7 @@ private:
         GrDeviceCoordTexture fDstCopy;
     };
 
+    // This is also used to record a discard by setting the color to GrColor_ILLEGAL
     struct Clear : public ::SkNoncopyable {
         Clear() : fRenderTarget(NULL) {}
         ~Clear() { SkSafeUnref(fRenderTarget); }
index f18df2c..729aa0d 100644 (file)
@@ -54,6 +54,15 @@ void GrRenderTarget::resolve() {
     context->resolveRenderTarget(this);
 }
 
+void GrRenderTarget::discard() {
+    // go through context so that all necessary flushing occurs
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return;
+    }
+    context->discardRenderTarget(this);
+}
+
 size_t GrRenderTarget::sizeInBytes() const {
     size_t colorBits;
     if (kUnknown_GrPixelConfig == fDesc.fConfig) {
index 47f3f0f..bf7939c 100644 (file)
@@ -45,7 +45,6 @@ void GrGLCaps::reset() {
     fUseNonVBOVertexAndIndexDynamicData = false;
     fIsCoreProfile = false;
     fFixedFunctionSupport = false;
-    fDiscardFBSupport = false;
     fFullClearIsFree = false;
     fDropsTileOnZeroDivide = false;
 }
@@ -83,7 +82,6 @@ GrGLCaps& GrGLCaps::operator = (const GrGLCaps& caps) {
     fUseNonVBOVertexAndIndexDynamicData = caps.fUseNonVBOVertexAndIndexDynamicData;
     fIsCoreProfile = caps.fIsCoreProfile;
     fFixedFunctionSupport = caps.fFixedFunctionSupport;
-    fDiscardFBSupport = caps.fDiscardFBSupport;
     fFullClearIsFree = caps.fFullClearIsFree;
     fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
 
@@ -224,7 +222,7 @@ void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
         fUseNonVBOVertexAndIndexDynamicData = true;
     }
 
-    fDiscardFBSupport = ctxInfo.hasExtension("GL_EXT_discard_framebuffer");
+    fDiscardRenderTargetSupport = ctxInfo.hasExtension("GL_EXT_discard_framebuffer");
 
     if (kARM_GrGLVendor == ctxInfo.vendor() || kImagination_GrGLVendor == ctxInfo.vendor()) {
         fFullClearIsFree = true;
@@ -664,7 +662,6 @@ SkString GrGLCaps::dump() const {
     r.appendf("Vertex array object support: %s\n", (fVertexArrayObjectSupport ? "YES": "NO"));
     r.appendf("Use non-VBO for dynamic data: %s\n",
              (fUseNonVBOVertexAndIndexDynamicData ? "YES" : "NO"));
-    r.appendf("Discard FrameBuffer support: %s\n", (fDiscardFBSupport ? "YES" : "NO"));
     r.appendf("Full screen clear is free: %s\n", (fFullClearIsFree ? "YES" : "NO"));
     r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
     return r;
index 0939f80..21176d1 100644 (file)
@@ -245,9 +245,6 @@ public:
 
     bool fixedFunctionSupport() const { return fFixedFunctionSupport; }
 
-    /// Is there support for discarding the frame buffer
-    bool discardFBSupport() const { return fDiscardFBSupport; }
-
     bool fullClearIsFree() const { return fFullClearIsFree; }
 
     bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
@@ -332,7 +329,6 @@ private:
     bool fUseNonVBOVertexAndIndexDynamicData : 1;
     bool fIsCoreProfile : 1;
     bool fFixedFunctionSupport : 1;
-    bool fDiscardFBSupport : 1;
     bool fFullClearIsFree : 1;
     bool fDropsTileOnZeroDivide : 1;
 
index 0dbd425..a4dc2f7 100644 (file)
 #define GR_GL_DEPTH_ATTACHMENT               0x8D00
 #define GR_GL_STENCIL_ATTACHMENT             0x8D20
 
+// GL_EXT_discard_framebuffer
+#define GR_GL_COLOR                          0x1800
+#define GR_GL_DEPTH                          0x1801
+#define GR_GL_STENCIL                        0x1802
+
 #define GR_GL_NONE                           0
 
 #define GR_GL_FRAMEBUFFER_COMPLETE                      0x8CD5
index dc2ef7d..78bf8f9 100644 (file)
@@ -1295,6 +1295,25 @@ void GrGpuGL::onClear(const SkIRect* rect, GrColor color, bool canIgnoreRect) {
     GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
 }
 
+void GrGpuGL::discard(GrRenderTarget* renderTarget) {
+    if (NULL == renderTarget) {
+        renderTarget = this->drawState()->getRenderTarget();
+        if (NULL == renderTarget) {
+            return;
+        }
+    }
+
+    GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(renderTarget);
+    if (renderTarget != fHWBoundRenderTarget) {
+        fHWBoundRenderTarget = NULL;
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, glRT->renderFBOID()));
+    }
+    GrGLenum attachments[] = { GR_GL_COLOR };
+    GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments), attachments));
+    renderTarget->flagAsResolved();
+}
+
+
 void GrGpuGL::clearStencil() {
     if (NULL == this->getDrawState().getRenderTarget()) {
         return;
index 6a89575..e173aef 100644 (file)
@@ -40,6 +40,8 @@ public:
     GrGLSLGeneration glslGeneration() const { return fGLContext.glslGeneration(); }
     const GrGLCaps& glCaps() const { return *fGLContext.caps(); }
 
+    virtual void discard(GrRenderTarget*) SK_OVERRIDE;
+
     // Used by GrGLProgram and GrGLTexGenProgramEffects to configure OpenGL state.
     void bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture);
     void setProjectionMatrix(const SkMatrix& matrix,
index ecb8042..edc6ef5 100644 (file)
@@ -43,11 +43,7 @@ void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
 void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
     this->dirtyGenerationID();
 
-    if (NULL != fCachedCanvas) {
-        SkASSERT(fCachedCanvas->getSurfaceBase() == this || \
-                 NULL == fCachedCanvas->getSurfaceBase());
-        fCachedCanvas->setSurfaceBase(NULL);
-    }
+    SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
 
     if (NULL != fCachedImage) {
         // the surface may need to fork its backend, if its sharing it with
@@ -61,12 +57,13 @@ void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
         // that the next request will get our new contents.
         fCachedImage->unref();
         fCachedImage = NULL;
+    } else if (kDiscard_ContentChangeMode == mode) {
+        this->onDiscard();
     }
 }
 
 uint32_t SkSurface_Base::newGenerationID() {
-    this->installIntoCanvasForDirtyNotification();
-
+    SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
     static int32_t gID;
     return sk_atomic_inc(&gID) + 1;
 }
index 34f32ee..2314341 100644 (file)
@@ -47,6 +47,12 @@ public:
     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
 
     /**
+     * Called as a performance hint when the Surface is allowed to make it's contents
+     * undefined.
+     */
+    virtual void onDiscard() {}
+
+    /**
      *  If the surface is about to change, we call this so that our subclass
      *  can optionally fork their backend (copy-on-write) in case it was
      *  being shared with the cachedImage.
@@ -67,15 +73,15 @@ private:
     friend class SkCanvas;
     friend class SkSurface;
 
-    inline void installIntoCanvasForDirtyNotification();
-
     typedef SkSurface INHERITED;
 };
 
 SkCanvas* SkSurface_Base::getCachedCanvas() {
     if (NULL == fCachedCanvas) {
         fCachedCanvas = this->onNewCanvas();
-        this->installIntoCanvasForDirtyNotification();
+        if (NULL != fCachedCanvas) {
+            fCachedCanvas->setSurfaceBase(this);
+        }
     }
     return fCachedCanvas;
 }
@@ -83,15 +89,9 @@ SkCanvas* SkSurface_Base::getCachedCanvas() {
 SkImage* SkSurface_Base::getCachedImage() {
     if (NULL == fCachedImage) {
         fCachedImage = this->onNewImageSnapshot();
-        this->installIntoCanvasForDirtyNotification();
+        SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
     }
     return fCachedImage;
 }
 
-void SkSurface_Base::installIntoCanvasForDirtyNotification() {
-    if (NULL != fCachedCanvas) {
-        fCachedCanvas->setSurfaceBase(this);
-    }
-}
-
 #endif
index 50d6606..2f32d2f 100644 (file)
@@ -23,6 +23,7 @@ public:
     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
                         const SkPaint*) SK_OVERRIDE;
     virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
+    virtual void onDiscard() SK_OVERRIDE;
 
 private:
     SkGpuDevice* fDevice;
@@ -86,9 +87,15 @@ void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
 
         this->getCachedCanvas()->setRootDevice(newDevice);
         SkRefCnt_SafeAssign(fDevice, newDevice);
+    } else if (kDiscard_ContentChangeMode == mode) {
+        this->SkSurface_Gpu::onDiscard();
     }
 }
 
+void SkSurface_Gpu::onDiscard() {
+    fDevice->accessRenderTarget()->discard();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target) {
index 8cfe6e8..8f65b3e 100644 (file)
@@ -25,6 +25,7 @@ public:
     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
                         const SkPaint*) SK_OVERRIDE;
     virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
+    virtual void onDiscard() SK_OVERRIDE;
 
 private:
     SkPicture*  fPicture;
@@ -75,9 +76,18 @@ void SkSurface_Picture::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
     SkImagePrivDrawPicture(canvas, fPicture, x, y, paint);
 }
 
-void SkSurface_Picture::onCopyOnWrite(ContentChangeMode /*mode*/) {
+void SkSurface_Picture::onCopyOnWrite(ContentChangeMode mode) {
     // We always spawn a copy of the recording picture when we
     // are asked for a snapshot, so we never need to do anything here.
+    if (kDiscard_ContentChangeMode == mode) {
+        this->SkSurface_Picture::onDiscard();
+    }
+}
+
+void SkSurface_Picture::onDiscard() {
+    if (NULL != fPicture) {
+        fPicture->beginRecording(this->width(), this->height());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////