Index: src/gpu/SkGpuDevice.cpp |
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp |
index 808e896ef7da21f053a7c56fd5e4500cdc00b904..6f718e62064e5d5c59a7b00a9d121b1b793be262 100644 |
--- a/src/gpu/SkGpuDevice.cpp |
+++ b/src/gpu/SkGpuDevice.cpp |
@@ -23,6 +23,7 @@ |
#include "SkGlyphCache.h" |
#include "SkGrTexturePixelRef.h" |
#include "SkImageFilter.h" |
+#include "SkImagePriv.h" |
#include "SkImage_Base.h" |
#include "SkLayerInfo.h" |
#include "SkMaskFilter.h" |
@@ -48,16 +49,17 @@ |
enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 }; |
#if 0 |
- extern bool (*gShouldDrawProc)(); |
- #define CHECK_SHOULD_DRAW(draw) \ |
- do { \ |
- if (gShouldDrawProc && !gShouldDrawProc()) return; \ |
- this->prepareDraw(draw); \ |
- } while (0) |
+extern bool (*gShouldDrawProc)(); |
+#define CHECK_SHOULD_DRAW(draw) \ |
+ do { \ |
+ if (gShouldDrawProc && !gShouldDrawProc()) return; \ |
+ if (!this->prepareDraw(draw)) return; \ |
+ } while (0) |
#else |
- #define CHECK_SHOULD_DRAW(draw) this->prepareDraw(draw) |
+#define CHECK_SHOULD_DRAW(draw) if (!this->prepareDraw(draw)) return; |
#endif |
+ |
// This constant represents the screen alignment criterion in texels for |
// requiring texture domain clamping to prevent color bleeding when drawing |
// a sub region of a larger source image. |
@@ -166,9 +168,20 @@ static SkSurfaceProps copy_or_default_props(const SkSurfaceProps* props) { |
} |
} |
+namespace { |
+ |
+void wrap_surface(GrSurface* rt, SkBitmap* bm) { |
+ bm->setInfo(rt->surfacePriv().info()); |
+ SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (rt->surfacePriv().info(), rt)); |
+ bm->setPixelRef(pr)->unref(); |
+} |
+ |
+}; |
+ |
SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, const SkSurfaceProps* props, unsigned flags) |
: INHERITED(surfaceprops_to_deviceprops(props)) |
, fSurfaceProps(copy_or_default_props(props)) |
+ , fSurface(NULL) |
{ |
fDrawProcs = NULL; |
@@ -177,11 +190,7 @@ SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, const SkSurfaceProps* props, unsign |
fRenderTarget = SkRef(rt); |
- SkImageInfo info = rt->surfacePriv().info(); |
- SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, rt)); |
- fLegacyBitmap.setInfo(info); |
- fLegacyBitmap.setPixelRef(pr)->unref(); |
- |
+ wrap_surface(fRenderTarget, &fLegacyBitmap); |
bool useDFT = fSurfaceProps.isUseDistanceFieldFonts(); |
fTextContext = fContext->createTextContext(fRenderTarget, this, this->getLeakyProperties(), |
useDFT); |
@@ -250,6 +259,13 @@ SkGpuDevice::~SkGpuDevice() { |
/////////////////////////////////////////////////////////////////////////////// |
+void SkGpuDevice::discard() { |
+ if (fSurface) { |
+ fSurface->notifyContentWillChange(); |
+ } |
+ this->prepareBackendRenderTarget(kDiscard_ModifyMode); |
+} |
+ |
bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, |
int x, int y) { |
DO_DEFERRED_CLEAR(); |
@@ -275,6 +291,11 @@ bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, siz |
if (kUnknown_GrPixelConfig == config) { |
return false; |
} |
+ |
+ if (!this->prepareWrite()) { |
+ return false; |
+ } |
+ |
uint32_t flags = 0; |
if (kUnpremul_SkAlphaType == info.alphaType()) { |
flags = GrContext::kUnpremul_PixelOpsFlag; |
@@ -288,7 +309,9 @@ bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, siz |
} |
const SkBitmap& SkGpuDevice::onAccessBitmap() { |
- DO_DEFERRED_CLEAR(); |
+ // Must prepare for write because user might obtain the texture from the bitmap and then modify |
+ // it. |
+ this->prepareWrite(); |
return fLegacyBitmap; |
} |
@@ -307,18 +330,38 @@ void SkGpuDevice::onDetachFromCanvas() { |
// call this every draw call, to ensure that the context reflects our state, |
// and not the state from some other canvas/device |
-void SkGpuDevice::prepareDraw(const SkDraw& draw) { |
+bool SkGpuDevice::prepareDraw(const SkDraw& draw) { |
SkASSERT(fClipStack.get()); |
SkASSERT(draw.fClipStack && draw.fClipStack == fClipStack); |
fClip.setClipStack(fClipStack, &this->getOrigin()); |
- DO_DEFERRED_CLEAR(); |
+ if (!this->prepareBackendRenderTarget(kDrawContent_ModifyMode)) { |
+ return false; |
+ } |
+ |
+ if (fSurface) { |
+ fSurface->notifyContentWillChange(); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool SkGpuDevice::prepareWrite() { |
+ if (!this->prepareBackendRenderTarget(kDrawContent_ModifyMode)) { |
+ return false; |
+ } |
+ if (fSurface) { |
+ fSurface->notifyContentWillChange(); |
+ } |
+ return true; |
} |
GrRenderTarget* SkGpuDevice::accessRenderTarget() { |
- DO_DEFERRED_CLEAR(); |
+ // The render target could be read through the ptr, so we must do deferred clear. The render |
+ // target could be written to through the ptr, so we must detach the snapshot. |
+ this->prepareWrite(); |
return fRenderTarget; |
} |
@@ -330,38 +373,81 @@ void SkGpuDevice::clearAll() { |
fNeedClear = false; |
} |
-void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) { |
- // Caller must have accessed the render target, because it knows the rt must be replaced. |
- SkASSERT(!fNeedClear); |
+bool SkGpuDevice::prepareBackendRenderTarget(ModifyMode writeMode) { |
+ // We are preparing for modifying the render target. This will detach the snapshot in all |
+ // cases, so do the detach right away. |
+ SkAutoTUnref<SkImage> snapshot; |
+ snapshot.swap(&fSnapshot); |
+ |
+ bool needNewRenderTarget = false; |
+ |
+ if (NULL != snapshot) { |
+ SkASSERT(!fNeedClear); |
+ needNewRenderTarget = fRenderTarget->asTexture() == SkTextureImageGetTexture(snapshot) && |
+ !snapshot->unique(); |
+ } |
+ |
+ if (!needNewRenderTarget) { |
+ if (writeMode == kDiscard_ModifyMode) { |
+ fRenderTarget->discard(); |
+ fNeedClear = false; |
+ } |
+ if (fRenderTarget->wasDestroyed()) { |
+ return false; |
+ } |
+ |
+ DO_DEFERRED_CLEAR(); |
+ return true; |
+ } |
SkSurface::Budgeted budgeted = |
fRenderTarget->resourcePriv().isBudgeted() ? SkSurface::kYes_Budgeted |
: SkSurface::kNo_Budgeted; |
SkAutoTUnref<GrRenderTarget> newRT(CreateRenderTarget( |
- fRenderTarget->getContext(), budgeted, this->imageInfo(), fRenderTarget->numSamples())); |
+ this->context(), budgeted, this->imageInfo(), fRenderTarget->numSamples())); |
if (NULL == newRT) { |
- return; |
+ return false; |
} |
- if (shouldRetainContent) { |
+ if (writeMode == kDrawContent_ModifyMode) { |
if (fRenderTarget->wasDestroyed()) { |
- return; |
+ return false; |
} |
- this->context()->copySurface(newRT, fRenderTarget); |
+ |
+ if (fNeedClear) { |
+ // Clear the snapshot render target and clear the new render target. |
+ this->clearAll(); |
+ fNeedClear = true; // Clears the new render target. |
+ } else { |
+ this->context()->copySurface(newRT, fRenderTarget); |
+ } |
+ } else { |
+ fNeedClear = false; |
} |
SkASSERT(fRenderTarget != newRT); |
- |
fRenderTarget->unref(); |
fRenderTarget = newRT.detach(); |
SkASSERT(fRenderTarget->surfacePriv().info() == fLegacyBitmap.info()); |
- SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (fRenderTarget->surfacePriv().info(), fRenderTarget)); |
- fLegacyBitmap.setPixelRef(pr)->unref(); |
+ wrap_surface(fRenderTarget, &fLegacyBitmap); |
+ |
+ SkASSERT(!fRenderTarget->wasDestroyed()); |
+ |
+ DO_DEFERRED_CLEAR(); |
+ |
+ if (snapshot) { |
+ SkTextureImageApplyBudgetedDecision(snapshot); |
+ } |
+ |
+ return true; |
} |
+bool SkGpuDevice::isBackendBudgeted() const { |
+ return fRenderTarget->resourcePriv().isBudgeted(); |
+} |
/////////////////////////////////////////////////////////////////////////////// |
SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch); |
@@ -759,13 +845,6 @@ GrTexture* create_mask_GPU(GrContext* context, |
return mask; |
} |
-SkBitmap wrap_texture(GrTexture* texture) { |
- SkBitmap result; |
- result.setInfo(texture->surfacePriv().info()); |
- result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (result.info(), texture)))->unref(); |
- return result; |
-} |
- |
}; |
void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, |
@@ -1507,7 +1586,9 @@ bool SkGpuDevice::filterTexture(GrContext* context, GrTexture* texture, |
SkDeviceImageFilterProxy proxy(this, SkSurfaceProps(0, getLeakyProperties().pixelGeometry())); |
if (filter->canFilterImageGPU()) { |
- return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset); |
+ SkBitmap filterInput; |
+ wrap_surface(texture, &filterInput); |
+ return filter->filterImageGPU(&proxy, filterInput, ctx, result, offset); |
} else { |
return false; |
} |
@@ -1643,24 +1724,22 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, |
int x, int y, const SkPaint& paint) { |
// clear of the source device must occur before CHECK_SHOULD_DRAW |
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawDevice", fContext); |
- SkGpuDevice* dev = static_cast<SkGpuDevice*>(device); |
- |
// TODO: If the source device covers the whole of this device, we could |
// omit fNeedsClear -related flushing. |
- // TODO: if source needs clear, we could maybe omit the draw fully. |
+ // TODO: if source is a gpu device, we could inspect its fNeedsClear, |
+ // and we could maybe omit the draw fully. |
- // drawDevice is defined to be in device coords. |
- CHECK_SHOULD_DRAW(draw); |
- |
- GrRenderTarget* devRT = dev->accessRenderTarget(); |
- GrTexture* devTex; |
- if (NULL == (devTex = devRT->asTexture())) { |
+ SkAutoTUnref<SkImage> devImage(device->newImageSnapshot()); |
+ if (NULL == devImage) { |
+ return; |
+ } |
+ GrTexture* devTex = devImage->getTexture(); |
+ if (NULL == devTex) { |
return; |
} |
- const SkImageInfo ii = dev->imageInfo(); |
- int w = ii.width(); |
- int h = ii.height(); |
+ int w = devImage->width(); |
+ int h = devImage->height(); |
SkImageFilter* filter = paint.getImageFilter(); |
// This bitmap will own the filtered result as a texture. |
@@ -1686,6 +1765,8 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, |
return; |
} |
} |
+ // drawDevice is defined to be in device coords. |
+ CHECK_SHOULD_DRAW(draw); |
GrPaint grPaint; |
grPaint.addColorTextureProcessor(devTex, SkMatrix::I()); |
@@ -1905,6 +1986,28 @@ void SkGpuDevice::flush() { |
fRenderTarget->prepareForExternalRead(); |
} |
+SkImage* SkGpuDevice::newImageSnapshot() { |
+ return this->newImageSnapshot(&fSurfaceProps, SkSurface::kYes_Budgeted); |
+} |
+ |
+SkImage* SkGpuDevice::newImageSnapshot(const SkSurfaceProps* props, SkSurface::Budgeted budgeted) { |
+ if (NULL == fSnapshot) { |
+ if (fRenderTarget->wasDestroyed()) { |
+ return NULL; |
+ } |
+ DO_DEFERRED_CLEAR(); |
+ const int sampleCount = fRenderTarget->numSamples(); |
+ SkImage* image = SkNewImageFromBitmapTexture(fLegacyBitmap, sampleCount, budgeted); |
+ if (!image) { |
+ return NULL; |
+ } |
+ if (props) { |
+ as_IB(image)->initWithProps(*props); |
+ } |
+ fSnapshot.reset(image); |
+ } |
+ return SkRef(fSnapshot.get()); |
+} |
/////////////////////////////////////////////////////////////////////////////// |
SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { |