зеркало из https://github.com/mozilla/moz-skia.git
Fix unbound memory consumption problem with run away deferred canvases.
With this CL, deferred canvases will trigger a flush when then the memory allocated for recording commands (including flattened objects) exceeds 64MB. TEST=DeferredCanvas skia unit test, test step TestDeferredCanvasMemoryLimit BUG=http://code.google.com/p/chromium/issues/detail?id=137884 Review URL: https://codereview.appspot.com/6425053 git-svn-id: http://skia.googlecode.com/svn/trunk@4714 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
47580694fb
Коммит
bfeddae9da
|
@ -85,6 +85,14 @@ public:
|
|||
*/
|
||||
void setDeferredDrawing(bool deferred);
|
||||
|
||||
/**
|
||||
* Specify the maximum number of bytes to be allocated for the purpose
|
||||
* of recording draw commands to this canvas. The default limit, is
|
||||
* 64MB.
|
||||
* @param maxStorage The maximum number of bytes to be allocated.
|
||||
*/
|
||||
void setMaxRecordingStorage(size_t maxStorage);
|
||||
|
||||
// Overrides of the SkCanvas interface
|
||||
virtual int save(SaveFlags flags) SK_OVERRIDE;
|
||||
virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
|
||||
|
@ -171,6 +179,7 @@ protected:
|
|||
void playback();
|
||||
void reset();
|
||||
bool hasRecorded() {return fAllocator.blockCount() != 0;}
|
||||
size_t storageAllocatedForRecording() {return fAllocator.totalCapacity();}
|
||||
private:
|
||||
enum {
|
||||
kMinBlockSize = 4096
|
||||
|
@ -212,7 +221,7 @@ public:
|
|||
/**
|
||||
* Returns the recording canvas.
|
||||
*/
|
||||
SkCanvas* recordingCanvas() const {return fRecordingCanvas;}
|
||||
SkCanvas* recordingCanvas();
|
||||
|
||||
/**
|
||||
* Returns the immediate (non deferred) canvas.
|
||||
|
@ -233,6 +242,7 @@ public:
|
|||
void flushPending();
|
||||
void contentsCleared();
|
||||
void flushIfNeeded(const SkBitmap& bitmap);
|
||||
void setMaxRecordingStorage(size_t);
|
||||
|
||||
virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
|
||||
virtual int width() const SK_OVERRIDE;
|
||||
|
@ -330,6 +340,7 @@ public:
|
|||
SkCanvas* fRecordingCanvas;
|
||||
DeviceContext* fDeviceContext;
|
||||
bool fFreshFrame;
|
||||
size_t fMaxRecordingStorageBytes;
|
||||
};
|
||||
|
||||
DeferredDevice* getDeferredDevice() const;
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
|
||||
SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext)
|
||||
|
||||
enum {
|
||||
// Deferred canvas will auto-flush when recording reaches this limit
|
||||
kDefaultMaxRecordingStorageBytes = 64*1024*1024,
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
bool isPaintOpaque(const SkPaint* paint,
|
||||
|
@ -100,6 +105,11 @@ void SkDeferredCanvas::init() {
|
|||
fDeferredDrawing = true; // On by default
|
||||
}
|
||||
|
||||
void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) {
|
||||
validate();
|
||||
this->getDeferredDevice()->setMaxRecordingStorage(maxStorage);
|
||||
}
|
||||
|
||||
void SkDeferredCanvas::validate() const {
|
||||
SkASSERT(getDevice());
|
||||
}
|
||||
|
@ -482,7 +492,7 @@ void SkDeferredCanvas::DeferredPipeController::playback() {
|
|||
fBlockList.reset();
|
||||
|
||||
if (fBlock) {
|
||||
fReader.playback(fBlock,fBytesWritten);
|
||||
fReader.playback(fBlock, fBytesWritten);
|
||||
fBlock = NULL;
|
||||
}
|
||||
|
||||
|
@ -507,6 +517,7 @@ SkDeferredCanvas::DeferredDevice::DeferredDevice(
|
|||
immediateDevice->height(), immediateDevice->isOpaque())
|
||||
, fFreshFrame(true) {
|
||||
|
||||
fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
|
||||
fDeviceContext = deviceContext;
|
||||
SkSafeRef(fDeviceContext);
|
||||
fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
|
||||
|
@ -522,6 +533,10 @@ SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
|
|||
SkSafeUnref(fDeviceContext);
|
||||
}
|
||||
|
||||
void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
|
||||
fMaxRecordingStorageBytes = maxStorage;
|
||||
recordingCanvas(); // Accessing the recording canvas applies the new limit.
|
||||
}
|
||||
|
||||
void SkDeferredCanvas::DeferredDevice::endRecording() {
|
||||
#if SK_DEFERRED_CANVAS_USES_GPIPE
|
||||
|
@ -613,10 +628,19 @@ void SkDeferredCanvas::DeferredDevice::flushPending() {
|
|||
}
|
||||
|
||||
void SkDeferredCanvas::DeferredDevice::flush() {
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
fImmediateCanvas->flush();
|
||||
}
|
||||
|
||||
SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() {
|
||||
#if SK_DEFERRED_CANVAS_USES_GPIPE
|
||||
if (fPipeController.storageAllocatedForRecording() > fMaxRecordingStorageBytes) {
|
||||
this->flushPending();
|
||||
}
|
||||
#endif
|
||||
return fRecordingCanvas;
|
||||
}
|
||||
|
||||
void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
|
||||
#if SK_DEFERRED_CANVAS_USES_GPIPE
|
||||
if (bitmap.isImmutable()) {
|
||||
|
@ -632,7 +656,7 @@ void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
|
|||
// For now, drawing a writable bitmap triggers a flush
|
||||
// TODO: implement read-only semantics and auto buffer duplication on write
|
||||
// in SkBitmap/SkPixelRef, which will make deferral possible in this case.
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
}
|
||||
|
||||
uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() {
|
||||
|
@ -648,7 +672,7 @@ int SkDeferredCanvas::DeferredDevice::height() const {
|
|||
}
|
||||
|
||||
SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
return fImmediateDevice->accessRenderTarget();
|
||||
}
|
||||
|
||||
|
@ -664,7 +688,7 @@ void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
|
|||
SkCanvas::kNative_Premul_Config8888 != config8888 &&
|
||||
kPMColorAlias != config8888) {
|
||||
//Special case config: no deferral
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
fImmediateDevice->writePixels(bitmap, x, y, config8888);
|
||||
}
|
||||
|
||||
|
@ -675,7 +699,7 @@ void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
|
|||
}
|
||||
|
||||
const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
return fImmediateDevice->accessBitmap(false);
|
||||
}
|
||||
|
||||
|
@ -694,7 +718,7 @@ SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
|
|||
|
||||
bool SkDeferredCanvas::DeferredDevice::onReadPixels(
|
||||
const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
|
||||
flushPending();
|
||||
this->flushPending();
|
||||
return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
|
||||
x, y, config8888);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "SkDeferredCanvas.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
|
||||
static const int gWidth = 2;
|
||||
static const int gHeight = 2;
|
||||
|
||||
|
@ -177,10 +176,54 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
|
|||
}
|
||||
}
|
||||
|
||||
class MockDevice : public SkDevice {
|
||||
public:
|
||||
MockDevice(const SkBitmap& bm) : SkDevice(bm) {
|
||||
fDrawBitmapCallCount = 0;
|
||||
}
|
||||
virtual void drawBitmap(const SkDraw&, const SkBitmap&,
|
||||
const SkIRect*,
|
||||
const SkMatrix&, const SkPaint&) {
|
||||
fDrawBitmapCallCount++;
|
||||
}
|
||||
|
||||
int fDrawBitmapCallCount;
|
||||
};
|
||||
|
||||
// Verifies that the deferred canvas triggers a flush when its memory
|
||||
// limit is exceeded
|
||||
static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
|
||||
SkBitmap store;
|
||||
store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
|
||||
store.allocPixels();
|
||||
MockDevice mockDevice(store);
|
||||
SkDeferredCanvas canvas(&mockDevice);
|
||||
canvas.setMaxRecordingStorage(160000);
|
||||
|
||||
SkBitmap sourceImage;
|
||||
// 100 by 100 image, takes 40,000 bytes in memory
|
||||
sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
|
||||
sourceImage.allocPixels();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
sourceImage.notifyPixelsChanged(); // to force re-serialization
|
||||
canvas.drawBitmap(sourceImage, 0, 0, NULL);
|
||||
}
|
||||
|
||||
// FIXME: Test temporarily disabled because the SkPicture path is not
|
||||
// fixed and the SkGPipe path does not yet serialize images, but it
|
||||
// will soon.
|
||||
#if 0
|
||||
REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void TestDeferredCanvas(skiatest::Reporter* reporter) {
|
||||
TestDeferredCanvasBitmapAccess(reporter);
|
||||
TestDeferredCanvasFlush(reporter);
|
||||
TestDeferredCanvasFreshFrame(reporter);
|
||||
TestDeferredCanvasMemoryLimit(reporter);
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
|
|
Загрузка…
Ссылка в новой задаче