'<(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',
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
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
#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
*/
#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;
* 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;
*/
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.
}
}
+void SkCanvas::onDiscard() {
+ if (NULL != fSurfaceBase) {
+ fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
+ }
+}
+
void SkCanvas::drawPaint(const SkPaint& paint) {
this->internalDrawPaint(paint);
}
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;
fBufferLockSupport = false;
fPathRenderingSupport = false;
fDstReadInShaderSupport = false;
+ fDiscardRenderTargetSupport = false;
fReuseScratchTextures = true;
fGpuTracingSupport = false;
fBufferLockSupport = other.fBufferLockSupport;
fPathRenderingSupport = other.fPathRenderingSupport;
fDstReadInShaderSupport = other.fDstReadInShaderSupport;
+ fDiscardRenderTargetSupport = other.fDiscardRenderTargetSupport;
fReuseScratchTextures = other.fReuseScratchTextures;
fGpuTracingSupport = other.fGpuTracingSupport;
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
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
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
bool fBufferLockSupport : 1;
bool fPathRenderingSupport : 1;
bool fDstReadInShaderSupport : 1;
+ bool fDiscardRenderTargetSupport: 1;
bool fReuseScratchTextures : 1;
bool fGpuTracingSupport : 1;
rect = &r;
}
Clear* clr = this->recordClear();
+ GrColorIsPMAssert(color);
clr->fColor = color;
clr->fRect = *rect;
clr->fCanIgnoreRect = canIgnoreRect;
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();
++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:
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;
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); }
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) {
fUseNonVBOVertexAndIndexDynamicData = false;
fIsCoreProfile = false;
fFixedFunctionSupport = false;
- fDiscardFBSupport = false;
fFullClearIsFree = false;
fDropsTileOnZeroDivide = false;
}
fUseNonVBOVertexAndIndexDynamicData = caps.fUseNonVBOVertexAndIndexDynamicData;
fIsCoreProfile = caps.fIsCoreProfile;
fFixedFunctionSupport = caps.fFixedFunctionSupport;
- fDiscardFBSupport = caps.fDiscardFBSupport;
fFullClearIsFree = caps.fFullClearIsFree;
fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
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;
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;
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; }
bool fUseNonVBOVertexAndIndexDynamicData : 1;
bool fIsCoreProfile : 1;
bool fFixedFunctionSupport : 1;
- bool fDiscardFBSupport : 1;
bool fFullClearIsFree : 1;
bool fDropsTileOnZeroDivide : 1;
#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
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;
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,
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
// 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;
}
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.
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;
}
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
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;
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) {
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;
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());
+ }
}
///////////////////////////////////////////////////////////////////////////////