diff options
author | junov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-07 16:48:22 +0000 |
---|---|---|
committer | junov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-07 16:48:22 +0000 |
commit | 88e29146c1efc5ff8eec06076c9dce12684f2c11 (patch) | |
tree | df238dc7d1814e2a183e585ef3045804ba698ec3 | |
parent | 53e845e7e79774ed5ad7c57d69e06b91fad31387 (diff) |
Moving DeferredDevice and DeferredGPipeController classes out of the deferred canvas API header
Review URL: https://codereview.appspot.com/6449104
git-svn-id: http://skia.googlecode.com/svn/trunk@4989 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/utils/SkDeferredCanvas.h | 194 | ||||
-rw-r--r-- | src/utils/SkDeferredCanvas.cpp | 668 | ||||
-rw-r--r-- | tests/CanvasTest.cpp | 73 | ||||
-rw-r--r-- | tests/DeferredCanvasTest.cpp | 29 |
4 files changed, 482 insertions, 482 deletions
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h index 01a71140fa..67ef2aecc6 100644 --- a/include/utils/SkDeferredCanvas.h +++ b/include/utils/SkDeferredCanvas.h @@ -9,11 +9,9 @@ #define SkDeferredCanvas_DEFINED #include "SkCanvas.h" -#include "SkDevice.h" #include "SkPixelRef.h" -#include "SkGPipe.h" -#include "SkChunkAlloc.h" +class DeferredDevice; /** \class SkDeferredCanvas Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred @@ -80,7 +78,19 @@ public: /** * Returns true if deferred drawing is currenlty enabled. */ - bool isDeferredDrawing(); + bool isDeferredDrawing() const; + + /** + * Returns true if the canvas contains a fresh frame. A frame is + * considered fresh when its content do not depend on the contents + * of the previous frame. For example, if a canvas is cleared before + * drawing each frame, the frames will all be considered fresh. + * A frame is defined as the graphics image produced by as a result + * of all the canvas draws operation executed between two successive + * calls to isFreshFrame. The result of isFreshFrame is computed + * conservatively, so it may report false negatives. + */ + bool isFreshFrame() const; /** * Specify the maximum number of bytes to be allocated for the purpose @@ -177,188 +187,18 @@ public: }; protected: - class DeferredPipeController : public SkGPipeController { - public: - DeferredPipeController(); - void setPlaybackCanvas(SkCanvas*); - virtual ~DeferredPipeController(); - virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE; - virtual void notifyWritten(size_t bytes) SK_OVERRIDE; - void playback(); - void reset(); - bool hasRecorded() const { return fAllocator.blockCount() != 0; } - size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); } - private: - enum { - kMinBlockSize = 4096 - }; - struct PipeBlock { - PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; } - void* fBlock; - size_t fSize; - }; - void* fBlock; - size_t fBytesWritten; - SkChunkAlloc fAllocator; - SkTDArray<PipeBlock> fBlockList; - SkGPipeReader fReader; - }; - -public: - class DeferredDevice : public SkDevice { - public: - /** - * Constructor - * @param immediateDevice device to be drawn to when flushing - * deferred operations - * @param deviceContext callback interface for managing graphics - * context state, can be NULL. - */ - DeferredDevice(SkDevice* immediateDevice, - DeviceContext* deviceContext = NULL); - ~DeferredDevice(); - - /** - * Sets the device context to be use with the device. - * @param deviceContext callback interface for managing graphics - * context state, can be NULL. - */ - void setDeviceContext(DeviceContext* deviceContext); - - /** - * Returns the recording canvas. - */ - SkCanvas* recordingCanvas(); - - /** - * Returns the immediate (non deferred) canvas. - */ - SkCanvas* immediateCanvas() const {return fImmediateCanvas;} - - /** - * Returns the immediate (non deferred) device. - */ - SkDevice* immediateDevice() const {return fImmediateDevice;} - - /** - * Returns true if an opaque draw operation covering the entire canvas - * was performed since the last call to isFreshFrame(). - */ - bool isFreshFrame(); - - size_t storageAllocatedForRecording() const; - size_t freeMemoryIfPossible(size_t bytesToFree); - void flushPending(); - void contentsCleared(); - void setMaxRecordingStorage(size_t); - - virtual uint32_t getDeviceCapabilities() SK_OVERRIDE; - virtual int width() const SK_OVERRIDE; - virtual int height() const SK_OVERRIDE; - virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE; - - virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, - int width, int height, - bool isOpaque, - Usage usage) SK_OVERRIDE; - - virtual void writePixels(const SkBitmap& bitmap, int x, int y, - SkCanvas::Config8888 config8888) SK_OVERRIDE; - - protected: - virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE; - virtual bool onReadPixels(const SkBitmap& bitmap, - int x, int y, - SkCanvas::Config8888 config8888) SK_OVERRIDE; - - // The following methods are no-ops on a deferred device - virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) - SK_OVERRIDE - {return false;} - virtual void setMatrixClip(const SkMatrix&, const SkRegion&, - const SkClipStack&) SK_OVERRIDE - {} - - // None of the following drawing methods should ever get called on the - // deferred device - virtual void clear(SkColor color) - {SkASSERT(0);} - virtual void drawPaint(const SkDraw&, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, - size_t count, const SkPoint[], - const SkPaint& paint) - {SkASSERT(0);} - virtual void drawRect(const SkDraw&, const SkRect& r, - const SkPaint& paint) - {SkASSERT(0);} - virtual void drawPath(const SkDraw&, const SkPath& path, - const SkPaint& paint, - const SkMatrix* prePathMatrix = NULL, - bool pathIsMutable = false) - {SkASSERT(0);} - virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, - const SkIRect* srcRectOrNull, - const SkMatrix& matrix, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, - int x, int y, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawText(const SkDraw&, const void* text, size_t len, - SkScalar x, SkScalar y, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawPosText(const SkDraw&, const void* text, size_t len, - const SkScalar pos[], SkScalar constY, - int scalarsPerPos, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawTextOnPath(const SkDraw&, const void* text, - size_t len, const SkPath& path, - const SkMatrix* matrix, - const SkPaint& paint) - {SkASSERT(0);} - virtual void drawPosTextOnPath(const SkDraw& draw, const void* text, - size_t len, const SkPoint pos[], - const SkPaint& paint, - const SkPath& path, - const SkMatrix* matrix) - {SkASSERT(0);} - virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, - int vertexCount, const SkPoint verts[], - const SkPoint texs[], const SkColor colors[], - SkXfermode* xmode, const uint16_t indices[], - int indexCount, const SkPaint& paint) - {SkASSERT(0);} - virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, - const SkPaint&) - {SkASSERT(0);} - private: - virtual void flush(); - - void endRecording(); - void beginRecording(); - - DeferredPipeController fPipeController; - SkGPipeWriter fPipeWriter; - SkDevice* fImmediateDevice; - SkCanvas* fImmediateCanvas; - SkCanvas* fRecordingCanvas; - DeviceContext* fDeviceContext; - bool fFreshFrame; - size_t fMaxRecordingStorageBytes; - }; - - DeferredDevice* getDeferredDevice() const; - -protected: virtual SkCanvas* canvasForDrawIter(); + DeferredDevice* getDeferredDevice() const; private: SkCanvas* drawingCanvas() const; + SkCanvas* immediateCanvas() const; bool isFullFrame(const SkRect*, const SkPaint*) const; void validate() const; void init(); bool fDeferredDrawing; + friend class SkDeferredCanvasTester; // for unit testing typedef SkCanvas INHERITED; }; diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp index 731e80b2ce..6259f949ee 100644 --- a/src/utils/SkDeferredCanvas.cpp +++ b/src/utils/SkDeferredCanvas.cpp @@ -8,10 +8,13 @@ #include "SkDeferredCanvas.h" -#include "SkPaint.h" -#include "SkShader.h" +#include "SkChunkAlloc.h" #include "SkColorFilter.h" +#include "SkDevice.h" #include "SkDrawFilter.h" +#include "SkGPipe.h" +#include "SkPaint.h" +#include "SkShader.h" SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext) @@ -139,6 +142,400 @@ bool isPaintOpaque(const SkPaint* paint, } // unnamed namespace +//----------------------------------------------------------------------------- +// DeferredPipeController +//----------------------------------------------------------------------------- + +class DeferredPipeController : public SkGPipeController { +public: + DeferredPipeController(); + void setPlaybackCanvas(SkCanvas*); + virtual ~DeferredPipeController(); + virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE; + virtual void notifyWritten(size_t bytes) SK_OVERRIDE; + void playback(); + void reset(); + bool hasRecorded() const { return fAllocator.blockCount() != 0; } + size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); } +private: + enum { + kMinBlockSize = 4096 + }; + struct PipeBlock { + PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; } + void* fBlock; + size_t fSize; + }; + void* fBlock; + size_t fBytesWritten; + SkChunkAlloc fAllocator; + SkTDArray<PipeBlock> fBlockList; + SkGPipeReader fReader; +}; + +DeferredPipeController::DeferredPipeController() : + fAllocator(kMinBlockSize) { + fBlock = NULL; + fBytesWritten = 0; +} + +DeferredPipeController::~DeferredPipeController() { + fAllocator.reset(); +} + +void DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) { + fReader.setCanvas(canvas); +} + +void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) { + if (fBlock) { + // Save the previous block for later + PipeBlock previousBloc(fBlock, fBytesWritten); + fBlockList.push(previousBloc); + } + int32_t blockSize = SkMax32(minRequest, kMinBlockSize); + fBlock = fAllocator.allocThrow(blockSize); + fBytesWritten = 0; + *actual = blockSize; + return fBlock; +} + +void DeferredPipeController::notifyWritten(size_t bytes) { + fBytesWritten += bytes; +} + +void DeferredPipeController::playback() { + + for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) { + fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize); + } + fBlockList.reset(); + + if (fBlock) { + fReader.playback(fBlock, fBytesWritten); + fBlock = NULL; + } + + // Release all allocated blocks + fAllocator.reset(); +} + +void DeferredPipeController::reset() { + fBlockList.reset(); + fBlock = NULL; + fAllocator.reset(); +} + +//----------------------------------------------------------------------------- +// DeferredDevice +//----------------------------------------------------------------------------- + +class DeferredDevice : public SkDevice { +public: + DeferredDevice(SkDevice* immediateDevice, + SkDeferredCanvas::DeviceContext* deviceContext = NULL); + ~DeferredDevice(); + + void setDeviceContext(SkDeferredCanvas::DeviceContext* deviceContext); + SkCanvas* recordingCanvas(); + SkCanvas* immediateCanvas() const {return fImmediateCanvas;} + SkDevice* immediateDevice() const {return fImmediateDevice;} + bool isFreshFrame(); + size_t storageAllocatedForRecording() const; + size_t freeMemoryIfPossible(size_t bytesToFree); + void flushPending(); + void contentsCleared(); + void setMaxRecordingStorage(size_t); + + virtual uint32_t getDeviceCapabilities() SK_OVERRIDE; + virtual int width() const SK_OVERRIDE; + virtual int height() const SK_OVERRIDE; + virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE; + + virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, + int width, int height, + bool isOpaque, + Usage usage) SK_OVERRIDE; + + virtual void writePixels(const SkBitmap& bitmap, int x, int y, + SkCanvas::Config8888 config8888) SK_OVERRIDE; + +protected: + virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE; + virtual bool onReadPixels(const SkBitmap& bitmap, + int x, int y, + SkCanvas::Config8888 config8888) SK_OVERRIDE; + + // The following methods are no-ops on a deferred device + virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) + SK_OVERRIDE + {return false;} + virtual void setMatrixClip(const SkMatrix&, const SkRegion&, + const SkClipStack&) SK_OVERRIDE + {} + + // None of the following drawing methods should ever get called on the + // deferred device + virtual void clear(SkColor color) + {SkASSERT(0);} + virtual void drawPaint(const SkDraw&, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, + size_t count, const SkPoint[], + const SkPaint& paint) + {SkASSERT(0);} + virtual void drawRect(const SkDraw&, const SkRect& r, + const SkPaint& paint) + {SkASSERT(0);} + virtual void drawPath(const SkDraw&, const SkPath& path, + const SkPaint& paint, + const SkMatrix* prePathMatrix = NULL, + bool pathIsMutable = false) + {SkASSERT(0);} + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkIRect* srcRectOrNull, + const SkMatrix& matrix, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawTextOnPath(const SkDraw&, const void* text, + size_t len, const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) + {SkASSERT(0);} + virtual void drawPosTextOnPath(const SkDraw& draw, const void* text, + size_t len, const SkPoint pos[], + const SkPaint& paint, + const SkPath& path, + const SkMatrix* matrix) + {SkASSERT(0);} + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, + int vertexCount, const SkPoint verts[], + const SkPoint texs[], const SkColor colors[], + SkXfermode* xmode, const uint16_t indices[], + int indexCount, const SkPaint& paint) + {SkASSERT(0);} + virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, + const SkPaint&) + {SkASSERT(0);} +private: + virtual void flush(); + + void endRecording(); + void beginRecording(); + + DeferredPipeController fPipeController; + SkGPipeWriter fPipeWriter; + SkDevice* fImmediateDevice; + SkCanvas* fImmediateCanvas; + SkCanvas* fRecordingCanvas; + SkDeferredCanvas::DeviceContext* fDeviceContext; + bool fFreshFrame; + size_t fMaxRecordingStorageBytes; +}; + +DeferredDevice::DeferredDevice( + SkDevice* immediateDevice, SkDeferredCanvas::DeviceContext* deviceContext) : + SkDevice(SkBitmap::kNo_Config, immediateDevice->width(), + immediateDevice->height(), immediateDevice->isOpaque()) + , fFreshFrame(true) { + + fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; + fDeviceContext = deviceContext; + SkSafeRef(fDeviceContext); + fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas + fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice)); + fPipeController.setPlaybackCanvas(fImmediateCanvas); + this->beginRecording(); +} + +DeferredDevice::~DeferredDevice() { + this->flushPending(); + SkSafeUnref(fImmediateCanvas); + SkSafeUnref(fDeviceContext); +} + +void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) { + fMaxRecordingStorageBytes = maxStorage; + this->recordingCanvas(); // Accessing the recording canvas applies the new limit. +} + +void DeferredDevice::endRecording() { + fPipeWriter.endRecording(); + fPipeController.reset(); + fRecordingCanvas = NULL; +} + +void DeferredDevice::beginRecording() { + fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0); +} + +void DeferredDevice::setDeviceContext( + SkDeferredCanvas::DeviceContext* deviceContext) { + SkRefCnt_SafeAssign(fDeviceContext, deviceContext); +} + +void DeferredDevice::contentsCleared() { + if (!fRecordingCanvas->isDrawingToLayer()) { + fFreshFrame = true; + + // TODO: find a way to transfer the state stack and layers + // to the new recording canvas. For now, purging only works + // with an empty stack. + if (fRecordingCanvas->getSaveCount() == 0) { + + // Save state that is trashed by the purge + SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter(); + SkSafeRef(drawFilter); // So that it survives the purge + SkMatrix matrix = fRecordingCanvas->getTotalMatrix(); + SkRegion clipRegion = fRecordingCanvas->getTotalClip(); + + // beginRecording creates a new recording canvas and discards the + // old one, hence purging deferred draw ops. + this->endRecording(); + this->beginRecording(); + + // Restore pre-purge state + if (!clipRegion.isEmpty()) { + fRecordingCanvas->clipRegion(clipRegion, + SkRegion::kReplace_Op); + } + if (!matrix.isIdentity()) { + fRecordingCanvas->setMatrix(matrix); + } + if (drawFilter) { + fRecordingCanvas->setDrawFilter(drawFilter)->unref(); + } + } + } +} + +bool DeferredDevice::isFreshFrame() { + bool ret = fFreshFrame; + fFreshFrame = false; + return ret; +} + +void DeferredDevice::flushPending() { + if (!fPipeController.hasRecorded()) { + return; + } + if (fDeviceContext) { + fDeviceContext->prepareForDraw(); + } + + fPipeWriter.flushRecording(true); + fPipeController.playback(); +} + +void DeferredDevice::flush() { + this->flushPending(); + fImmediateCanvas->flush(); +} + +size_t DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) { + return fPipeWriter.freeMemoryIfPossible(bytesToFree); +} + +size_t DeferredDevice::storageAllocatedForRecording() const { + return (fPipeController.storageAllocatedForRecording() + + fPipeWriter.storageAllocatedForRecording()); +} + +SkCanvas* DeferredDevice::recordingCanvas() { + size_t storageAllocated = this->storageAllocatedForRecording(); + if (storageAllocated > fMaxRecordingStorageBytes) { + // First, attempt to reduce cache without flushing + size_t tryFree = storageAllocated - fMaxRecordingStorageBytes; + if (this->freeMemoryIfPossible(tryFree) < tryFree) { + // Flush is necessary to free more space. + this->flushPending(); + // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes + // which could cause a high flushing frequency. + this->freeMemoryIfPossible(~0); + } + } + return fRecordingCanvas; +} + +uint32_t DeferredDevice::getDeviceCapabilities() { + return fImmediateDevice->getDeviceCapabilities(); +} + +int DeferredDevice::width() const { + return fImmediateDevice->width(); +} + +int DeferredDevice::height() const { + return fImmediateDevice->height(); +} + +SkGpuRenderTarget* DeferredDevice::accessRenderTarget() { + this->flushPending(); + return fImmediateDevice->accessRenderTarget(); +} + +void DeferredDevice::writePixels(const SkBitmap& bitmap, + int x, int y, SkCanvas::Config8888 config8888) { + + if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() && + (y + bitmap.height()) >= height()) { + this->contentsCleared(); + } + + if (SkBitmap::kARGB_8888_Config == bitmap.config() && + SkCanvas::kNative_Premul_Config8888 != config8888 && + kPMColorAlias != config8888) { + //Special case config: no deferral + this->flushPending(); + fImmediateDevice->writePixels(bitmap, x, y, config8888); + return; + } + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + if (shouldDrawImmediately(&bitmap, NULL)) { + this->flushPending(); + fImmediateCanvas->drawSprite(bitmap, x, y, &paint); + } else { + this->recordingCanvas()->drawSprite(bitmap, x, y, &paint); + } +} + +const SkBitmap& DeferredDevice::onAccessBitmap(SkBitmap*) { + this->flushPending(); + return fImmediateDevice->accessBitmap(false); +} + +SkDevice* DeferredDevice::onCreateCompatibleDevice( + SkBitmap::Config config, int width, int height, bool isOpaque, + Usage usage) { + + // Save layer usage not supported, and not required by SkDeferredCanvas. + SkASSERT(usage != kSaveLayer_Usage); + // Create a compatible non-deferred device. + SkAutoTUnref<SkDevice> compatibleDevice + (fImmediateDevice->createCompatibleDevice(config, width, height, + isOpaque)); + return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext)); +} + +bool DeferredDevice::onReadPixels( + const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) { + this->flushPending(); + return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap), + x, y, config8888); +} + + SkDeferredCanvas::SkDeferredCanvas() { this->init(); } @@ -182,8 +579,13 @@ SkCanvas* SkDeferredCanvas::drawingCanvas() const { this->getDeferredDevice()->immediateCanvas(); } -SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const { - return static_cast<SkDeferredCanvas::DeferredDevice*>(this->getDevice()); +SkCanvas* SkDeferredCanvas::immediateCanvas() const { + this->validate(); + return this->getDeferredDevice()->immediateCanvas(); +} + +DeferredDevice* SkDeferredCanvas::getDeferredDevice() const { + return static_cast<DeferredDevice*>(this->getDevice()); } void SkDeferredCanvas::setDeferredDrawing(bool val) { @@ -197,10 +599,14 @@ void SkDeferredCanvas::setDeferredDrawing(bool val) { } } -bool SkDeferredCanvas::isDeferredDrawing() { +bool SkDeferredCanvas::isDeferredDrawing() const { return fDeferredDrawing; } +bool SkDeferredCanvas::isFreshFrame() const { + return this->getDeferredDevice()->isFreshFrame(); +} + SkDeferredCanvas::~SkDeferredCanvas() { } @@ -505,255 +911,3 @@ SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) { SkCanvas* SkDeferredCanvas::canvasForDrawIter() { return this->drawingCanvas(); } - -// SkDeferredCanvas::DeferredPipeController -//------------------------------------------- - -SkDeferredCanvas::DeferredPipeController::DeferredPipeController() : - fAllocator(kMinBlockSize) { - fBlock = NULL; - fBytesWritten = 0; -} - -SkDeferredCanvas::DeferredPipeController::~DeferredPipeController() { - fAllocator.reset(); -} - -void SkDeferredCanvas::DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) { - fReader.setCanvas(canvas); -} - -void* SkDeferredCanvas::DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) { - if (fBlock) { - // Save the previous block for later - PipeBlock previousBloc(fBlock, fBytesWritten); - fBlockList.push(previousBloc); - } - int32_t blockSize = SkMax32(minRequest, kMinBlockSize); - fBlock = fAllocator.allocThrow(blockSize); - fBytesWritten = 0; - *actual = blockSize; - return fBlock; -} - -void SkDeferredCanvas::DeferredPipeController::notifyWritten(size_t bytes) { - fBytesWritten += bytes; -} - -void SkDeferredCanvas::DeferredPipeController::playback() { - - for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) { - fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize); - } - fBlockList.reset(); - - if (fBlock) { - fReader.playback(fBlock, fBytesWritten); - fBlock = NULL; - } - - // Release all allocated blocks - fAllocator.reset(); -} - -void SkDeferredCanvas::DeferredPipeController::reset() { - fBlockList.reset(); - fBlock = NULL; - fAllocator.reset(); -} - -// SkDeferredCanvas::DeferredDevice -//------------------------------------ - -SkDeferredCanvas::DeferredDevice::DeferredDevice( - SkDevice* immediateDevice, DeviceContext* deviceContext) : - SkDevice(SkBitmap::kNo_Config, immediateDevice->width(), - immediateDevice->height(), immediateDevice->isOpaque()) - , fFreshFrame(true) { - - fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes; - fDeviceContext = deviceContext; - SkSafeRef(fDeviceContext); - fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas - fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice)); - fPipeController.setPlaybackCanvas(fImmediateCanvas); - this->beginRecording(); -} - -SkDeferredCanvas::DeferredDevice::~DeferredDevice() { - this->flushPending(); - SkSafeUnref(fImmediateCanvas); - SkSafeUnref(fDeviceContext); -} - -void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) { - fMaxRecordingStorageBytes = maxStorage; - this->recordingCanvas(); // Accessing the recording canvas applies the new limit. -} - -void SkDeferredCanvas::DeferredDevice::endRecording() { - fPipeWriter.endRecording(); - fPipeController.reset(); - fRecordingCanvas = NULL; -} - -void SkDeferredCanvas::DeferredDevice::beginRecording() { - fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0); -} - -void SkDeferredCanvas::DeferredDevice::setDeviceContext( - DeviceContext* deviceContext) { - SkRefCnt_SafeAssign(fDeviceContext, deviceContext); -} - -void SkDeferredCanvas::DeferredDevice::contentsCleared() { - if (!fRecordingCanvas->isDrawingToLayer()) { - fFreshFrame = true; - - // TODO: find a way to transfer the state stack and layers - // to the new recording canvas. For now, purging only works - // with an empty stack. - if (fRecordingCanvas->getSaveCount() == 0) { - - // Save state that is trashed by the purge - SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter(); - SkSafeRef(drawFilter); // So that it survives the purge - SkMatrix matrix = fRecordingCanvas->getTotalMatrix(); - SkRegion clipRegion = fRecordingCanvas->getTotalClip(); - - // beginRecording creates a new recording canvas and discards the - // old one, hence purging deferred draw ops. - this->endRecording(); - this->beginRecording(); - - // Restore pre-purge state - if (!clipRegion.isEmpty()) { - fRecordingCanvas->clipRegion(clipRegion, - SkRegion::kReplace_Op); - } - if (!matrix.isIdentity()) { - fRecordingCanvas->setMatrix(matrix); - } - if (drawFilter) { - fRecordingCanvas->setDrawFilter(drawFilter)->unref(); - } - } - } -} - -bool SkDeferredCanvas::DeferredDevice::isFreshFrame() { - bool ret = fFreshFrame; - fFreshFrame = false; - return ret; -} - -void SkDeferredCanvas::DeferredDevice::flushPending() { - if (!fPipeController.hasRecorded()) { - return; - } - if (fDeviceContext) { - fDeviceContext->prepareForDraw(); - } - - fPipeWriter.flushRecording(true); - fPipeController.playback(); -} - -void SkDeferredCanvas::DeferredDevice::flush() { - this->flushPending(); - fImmediateCanvas->flush(); -} - -size_t SkDeferredCanvas::DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) { - return fPipeWriter.freeMemoryIfPossible(bytesToFree); -} - -size_t SkDeferredCanvas::DeferredDevice::storageAllocatedForRecording() const { - return (fPipeController.storageAllocatedForRecording() - + fPipeWriter.storageAllocatedForRecording()); -} - -SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() { - size_t storageAllocated = this->storageAllocatedForRecording(); - if (storageAllocated > fMaxRecordingStorageBytes) { - // First, attempt to reduce cache without flushing - size_t tryFree = storageAllocated - fMaxRecordingStorageBytes; - if (this->freeMemoryIfPossible(tryFree) < tryFree) { - // Flush is necessary to free more space. - this->flushPending(); - // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes - // which could cause a high flushing frequency. - this->freeMemoryIfPossible(~0); - } - } - return fRecordingCanvas; -} - -uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() { - return fImmediateDevice->getDeviceCapabilities(); -} - -int SkDeferredCanvas::DeferredDevice::width() const { - return fImmediateDevice->width(); -} - -int SkDeferredCanvas::DeferredDevice::height() const { - return fImmediateDevice->height(); -} - -SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() { - this->flushPending(); - return fImmediateDevice->accessRenderTarget(); -} - -void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap, - int x, int y, SkCanvas::Config8888 config8888) { - - if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() && - (y + bitmap.height()) >= height()) { - this->contentsCleared(); - } - - if (SkBitmap::kARGB_8888_Config == bitmap.config() && - SkCanvas::kNative_Premul_Config8888 != config8888 && - kPMColorAlias != config8888) { - //Special case config: no deferral - this->flushPending(); - fImmediateDevice->writePixels(bitmap, x, y, config8888); - return; - } - - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - if (shouldDrawImmediately(&bitmap, NULL)) { - this->flushPending(); - fImmediateCanvas->drawSprite(bitmap, x, y, &paint); - } else { - this->recordingCanvas()->drawSprite(bitmap, x, y, &paint); - } -} - -const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) { - this->flushPending(); - return fImmediateDevice->accessBitmap(false); -} - -SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice( - SkBitmap::Config config, int width, int height, bool isOpaque, - Usage usage) { - - // Save layer usage not supported, and not required by SkDeferredCanvas. - SkASSERT(usage != kSaveLayer_Usage); - // Create a compatible non-deferred device. - SkAutoTUnref<SkDevice> compatibleDevice - (fImmediateDevice->createCompatibleDevice(config, width, height, - isOpaque)); - return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext)); -} - -bool SkDeferredCanvas::DeferredDevice::onReadPixels( - const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) { - this->flushPending(); - return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap), - x, y, config8888); -} diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp index 4347edcdf6..866bf0ca22 100644 --- a/tests/CanvasTest.cpp +++ b/tests/CanvasTest.cpp @@ -741,39 +741,44 @@ public: } }; -static void TestDeferredCanvasStateConsistency( - skiatest::Reporter* reporter, - CanvasTestStep* testStep, - const SkCanvas& referenceCanvas) { - - SkBitmap deferredStore; - createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); - SkDevice deferredDevice(deferredStore); - SkDeferredCanvas deferredCanvas(&deferredDevice); - testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat); - testStep->draw(&deferredCanvas, reporter); - testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, - testStep); - - deferredCanvas.flush(); - testStep->setAssertMessageFormat( - kDeferredPostFlushPlaybackAssertMessageFormat); - AssertCanvasStatesEqual(reporter, - deferredCanvas.getDeferredDevice()->immediateCanvas(), - &referenceCanvas, testStep); - - // Verified that deferred canvas state is not affected by flushing - // pending draw operations - - // The following test code is commented out because it currently fails. - // Issue: http://code.google.com/p/skia/issues/detail?id=496 - /* - testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, - testStep); - */ -} +// The following class groups static functions that need to access +// the privates members of SkDeferredCanvas +class SkDeferredCanvasTester { +public: + static void TestDeferredCanvasStateConsistency( + skiatest::Reporter* reporter, + CanvasTestStep* testStep, + const SkCanvas& referenceCanvas) { + + SkBitmap deferredStore; + createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); + SkDevice deferredDevice(deferredStore); + SkDeferredCanvas deferredCanvas(&deferredDevice); + testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat); + testStep->draw(&deferredCanvas, reporter); + testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat); + AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, + testStep); + + deferredCanvas.flush(); + testStep->setAssertMessageFormat( + kDeferredPostFlushPlaybackAssertMessageFormat); + AssertCanvasStatesEqual(reporter, + deferredCanvas.immediateCanvas(), + &referenceCanvas, testStep); + + // Verified that deferred canvas state is not affected by flushing + // pending draw operations + + // The following test code is commented out because it currently fails. + // Issue: http://code.google.com/p/skia/issues/detail?id=496 + /* + testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat); + AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, + testStep); + */ + } +}; // unused static void TestProxyCanvasStateConsistency( @@ -849,7 +854,7 @@ static void TestOverrideStateConsistency(skiatest::Reporter* reporter, testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat); testStep->draw(&referenceCanvas, reporter); - TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas); + SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas); // The following test code is disabled because SkProxyCanvas is // missing a lot of virtual overrides on get* methods, which are used diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp index 5e5e425e12..69ca75ad69 100644 --- a/tests/DeferredCanvasTest.cpp +++ b/tests/DeferredCanvasTest.cpp @@ -8,6 +8,7 @@ #include "Test.h" #include "SkBitmap.h" #include "SkDeferredCanvas.h" +#include "SkDevice.h" #include "SkShader.h" static const int gWidth = 2; @@ -63,25 +64,25 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { SkDeferredCanvas canvas(&device); // verify that frame is intially fresh - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); // no clearing op since last call to isFreshFrame -> not fresh - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); // Verify that clear triggers a fresh frame canvas.clear(0x00000000); - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); // Verify that clear with saved state triggers a fresh frame canvas.save(SkCanvas::kMatrixClip_SaveFlag); canvas.clear(0x00000000); canvas.restore(); - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); // Verify that clear within a layer does NOT trigger a fresh frame canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); canvas.clear(0x00000000); canvas.restore(); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); // Verify that a clear with clipping triggers a fresh frame // (clear is not affected by clipping) @@ -89,7 +90,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false); canvas.clear(0x00000000); canvas.restore(); - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); // Verify that full frame rects with different forms of opaque paint // trigger frames to be marked as fresh @@ -98,7 +99,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setStyle( SkPaint::kFill_Style ); paint.setAlpha( 255 ); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); } { SkPaint paint; @@ -110,7 +111,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); paint.setShader(shader)->unref(); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, canvas.isFreshFrame()); } // Verify that full frame rects with different forms of non-opaque paint @@ -120,7 +121,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setStyle( SkPaint::kFill_Style ); paint.setAlpha( 254 ); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } { SkPaint paint; @@ -132,7 +133,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); paint.setShader(shader)->unref(); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } // Verify that incomplete coverage does not trigger a fresh frame @@ -141,7 +142,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setStyle(SkPaint::kFill_Style); paint.setAlpha(255); canvas.drawRect(partialRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } // Verify that incomplete coverage due to clipping does not trigger a fresh @@ -153,7 +154,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setStyle(SkPaint::kFill_Style); paint.setAlpha(255); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } // Verify that stroked rect does not trigger a fresh frame @@ -162,7 +163,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setStyle( SkPaint::kStroke_Style ); paint.setAlpha( 255 ); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } // Verify kSrcMode triggers a fresh frame even with transparent color @@ -172,7 +173,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { paint.setAlpha( 100 ); paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame()); + REPORTER_ASSERT(reporter, !canvas.isFreshFrame()); } } |