'<(skia_src_path)/gpu/SkGrPixelRef.cpp',
'<(skia_src_path)/gpu/SkGrTexturePixelRef.cpp',
+ '<(skia_src_path)/image/SkImage_Gpu.h',
'<(skia_src_path)/image/SkImage_Gpu.cpp',
'<(skia_src_path)/image/SkSurface_Gpu.h',
'<(skia_src_path)/image/SkSurface_Gpu.cpp',
/**
* Returns an image of the current state of the surface pixels up to this
* point. Subsequent changes to the surface (by drawing into its canvas)
- * will not be reflected in this image.
+ * will not be reflected in this image. If a copy must be made the Budgeted
+ * parameter controls whether it counts against the resource budget
+ * (currently for the gpu backend only).
*/
- SkImage* newImageSnapshot();
+ SkImage* newImageSnapshot(Budgeted = kYes_Budgeted);
/**
- * Thought the caller could get a snapshot image explicitly, and draw that,
+ * Though the caller could get a snapshot image explicitly, and draw that,
* it seems that directly drawing a surface into another canvas might be
* a common pattern, and that we could possibly be more efficient, since
* we'd know that the "snapshot" need only live until we've handed it off
#define SkImagePriv_DEFINED
#include "SkImage.h"
+#include "SkSurface.h"
// Call this if you explicitly want to use/share this pixelRef in the image
extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*, size_t rowBytes,
// in which case the surface may need to perform a copy-on-write.
extern GrTexture* SkTextureImageGetTexture(SkImage* textureImage);
+// When a texture is shared by a surface and an image its budgeted status is that of the
+// surface. This function is used when the surface makes a new texture for itself in order
+// for the orphaned image to determine whether the original texture counts against the
+// budget or not.
+extern void SkTextureImageApplyBudgetedDecision(SkImage* textureImage);
+
// Update the texture wrapped by an image created with NewTexture. This
// is called when a surface and image share the same GrTexture and the
// surface needs to perform a copy-on-write
extern void SkTextureImageSetTexture(SkImage* image, GrTexture* texture);
-extern SkImage* SkNewImageFromBitmapTexture(const SkBitmap&, int sampleCountForNewSurfaces);
+extern SkImage* SkNewImageFromBitmapTexture(const SkBitmap&, int sampleCountForNewSurfaces,
+ SkSurface::Budgeted);
#endif
* found in the LICENSE file.
*/
-#include "SkImage_Base.h"
-#include "SkImagePriv.h"
-#include "SkBitmap.h"
+#include "SkImage_Gpu.h"
#include "SkCanvas.h"
-#include "SkSurface.h"
#include "GrContext.h"
-#include "GrTexture.h"
-class SkImage_Gpu : public SkImage_Base {
-public:
- SK_DECLARE_INST_COUNT(SkImage_Gpu)
-
- SkImage_Gpu(const SkBitmap&, int sampleCountForNewSurfaces);
-
- void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const SK_OVERRIDE;
- void onDrawRect(SkCanvas*, const SkRect* src, const SkRect& dst,
- const SkPaint*) const SK_OVERRIDE;
- SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const SK_OVERRIDE;
- GrTexture* onGetTexture() const SK_OVERRIDE;
- bool getROPixels(SkBitmap*) const SK_OVERRIDE;
-
- GrTexture* getTexture() const { return fBitmap.getTexture(); }
-
- SkShader* onNewShader(SkShader::TileMode,
- SkShader::TileMode,
- const SkMatrix* localMatrix) const SK_OVERRIDE;
-
- bool isOpaque() const SK_OVERRIDE;
-
-private:
- SkBitmap fBitmap;
- const int fSampleCountForNewSurfaces; // 0 if we don't know
-
- typedef SkImage_Base INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkImage_Gpu::SkImage_Gpu(const SkBitmap& bitmap, int sampleCountForNewSurfaces)
+SkImage_Gpu::SkImage_Gpu(const SkBitmap& bitmap, int sampleCountForNewSurfaces,
+ SkSurface::Budgeted budgeted)
: INHERITED(bitmap.width(), bitmap.height(), NULL)
, fBitmap(bitmap)
, fSampleCountForNewSurfaces(sampleCountForNewSurfaces)
+ , fBudgeted(budgeted)
{
SkASSERT(fBitmap.getTexture());
}
///////////////////////////////////////////////////////////////////////////////
-SkImage* SkNewImageFromBitmapTexture(const SkBitmap& bitmap, int sampleCountForNewSurfaces) {
+SkImage* SkNewImageFromBitmapTexture(const SkBitmap& bitmap, int sampleCountForNewSurfaces,
+ SkSurface::Budgeted budgeted) {
if (0 == bitmap.width() || 0 == bitmap.height() || NULL == bitmap.getTexture()) {
return NULL;
}
- return SkNEW_ARGS(SkImage_Gpu, (bitmap, sampleCountForNewSurfaces));
+ return SkNEW_ARGS(SkImage_Gpu, (bitmap, sampleCountForNewSurfaces, budgeted));
}
GrTexture* SkTextureImageGetTexture(SkImage* image) {
return ((SkImage_Gpu*)image)->getTexture();
}
+extern void SkTextureImageApplyBudgetedDecision(SkImage* image) {
+ ((SkImage_Gpu*)image)->applyBudgetDecision();
+}
--- /dev/null
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkImage_Gpu_DEFINED
+#define SkImage_Gpu_DEFINED
+
+#include "GrTexture.h"
+#include "GrGpuResourceCacheAccess.h"
+#include "SkBitmap.h"
+#include "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkSurface.h"
+
+class SkImage_Gpu : public SkImage_Base {
+public:
+ SK_DECLARE_INST_COUNT(SkImage_Gpu)
+
+ SkImage_Gpu(const SkBitmap&, int sampleCountForNewSurfaces, SkSurface::Budgeted);
+
+ void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const SK_OVERRIDE;
+ void onDrawRect(SkCanvas*, const SkRect* src, const SkRect& dst,
+ const SkPaint*) const SK_OVERRIDE;
+ SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const SK_OVERRIDE;
+ GrTexture* onGetTexture() const SK_OVERRIDE;
+ bool getROPixels(SkBitmap*) const SK_OVERRIDE;
+
+ GrTexture* getTexture() const { return fBitmap.getTexture(); }
+
+ SkShader* onNewShader(SkShader::TileMode,
+ SkShader::TileMode,
+ const SkMatrix* localMatrix) const SK_OVERRIDE;
+
+ bool isOpaque() const SK_OVERRIDE;
+
+ void applyBudgetDecision() const {
+ if (fBudgeted) {
+ fBitmap.getTexture()->cacheAccess().makeBudgeted();
+ } else {
+ fBitmap.getTexture()->cacheAccess().makeUnbudgeted();
+ }
+ }
+
+private:
+ SkBitmap fBitmap;
+ const int fSampleCountForNewSurfaces; // 0 if we don't know
+ SkSurface::Budgeted fBudgeted;
+
+ typedef SkImage_Base INHERITED;
+};
+
+#endif
}
void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
- SkImage* image = this->newImageSnapshot();
+ SkImage* image = this->newImageSnapshot(kYes_Budgeted);
if (image) {
canvas->drawImage(image, x, y, paint);
image->unref();
return asSB(this)->getCachedCanvas();
}
-SkImage* SkSurface::newImageSnapshot() {
- SkImage* image = asSB(this)->getCachedImage();
+SkImage* SkSurface::newImageSnapshot(Budgeted budgeted) {
+ SkImage* image = asSB(this)->getCachedImage(budgeted);
SkSafeRef(image); // the caller will call unref() to balance this
return image;
}
* Allocate an SkImage that represents the current contents of the surface.
* This needs to be able to outlive the surface itself (if need be), and
* must faithfully represent the current contents, even if the surface
- * is chaged after this calle (e.g. it is drawn to via its canvas).
+ * is changed after this called (e.g. it is drawn to via its canvas).
*/
- virtual SkImage* onNewImageSnapshot() = 0;
+ virtual SkImage* onNewImageSnapshot(Budgeted) = 0;
/**
* Default implementation:
virtual void onCopyOnWrite(ContentChangeMode) = 0;
inline SkCanvas* getCachedCanvas();
- inline SkImage* getCachedImage();
+ inline SkImage* getCachedImage(Budgeted);
// called by SkSurface to compute a new genID
uint32_t newGenerationID();
return fCachedCanvas;
}
-SkImage* SkSurface_Base::getCachedImage() {
+SkImage* SkSurface_Base::getCachedImage(Budgeted budgeted) {
if (NULL == fCachedImage) {
- fCachedImage = this->onNewImageSnapshot();
+ fCachedImage = this->onNewImageSnapshot(budgeted);
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
}
return fCachedImage;
&this->props());
}
-SkImage* SkSurface_Gpu::onNewImageSnapshot() {
+SkImage* SkSurface_Gpu::onNewImageSnapshot(Budgeted budgeted) {
const int sampleCount = fDevice->accessRenderTarget()->numSamples();
- SkImage* image = SkNewImageFromBitmapTexture(fDevice->accessBitmap(false), sampleCount);
+ SkImage* image = SkNewImageFromBitmapTexture(fDevice->accessBitmap(false), sampleCount,
+ budgeted);
if (image) {
as_IB(image)->initWithProps(this->props());
}
// doesn't force an OpenGL flush.
void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
GrRenderTarget* rt = fDevice->accessRenderTarget();
- // are we sharing our render target with the image?
- SkASSERT(this->getCachedImage());
- if (rt->asTexture() == SkTextureImageGetTexture(this->getCachedImage())) {
+ // are we sharing our render target with the image? Note this call should never create a new
+ // image because onCopyOnWrite is only called when there is a cached image.
+ SkImage* image = this->getCachedImage(kNo_Budgeted);
+ SkASSERT(image);
+ if (rt->asTexture() == SkTextureImageGetTexture(image)) {
GrRenderTarget* oldRT = this->fDevice->accessRenderTarget();
SkSurface::Budgeted budgeted = oldRT->cacheAccess().isBudgeted() ? kYes_Budgeted :
kNo_Budgeted;
this->getCachedCanvas()->setRootDevice(newDevice);
SkRefCnt_SafeAssign(fDevice, newDevice.get());
- // For now we always treat the image snapshots as budgeted. We could make newImageSnapshot
- // take a Budgeted param.
- oldRT->cacheAccess().makeBudgeted();
-
+ SkTextureImageApplyBudgetedDecision(image);
} else if (kDiscard_ContentChangeMode == mode) {
this->SkSurface_Gpu::onDiscard();
}
SkCanvas* onNewCanvas() SK_OVERRIDE;
SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
- SkImage* onNewImageSnapshot() SK_OVERRIDE;
+ SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE;
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
const SkPaint*) SK_OVERRIDE;
void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
SkCanvas* onNewCanvas() SK_OVERRIDE;
SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
- SkImage* onNewImageSnapshot() SK_OVERRIDE;
+ SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE;
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
const SkPaint*) SK_OVERRIDE;
void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
canvas->drawBitmap(fBitmap, x, y, paint);
}
-SkImage* SkSurface_Raster::onNewImageSnapshot() {
+SkImage* SkSurface_Raster::onNewImageSnapshot(Budgeted) {
return SkNewImageFromBitmap(fBitmap, fWeOwnThePixels, &this->props());
}
void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
// are we sharing pixelrefs with the image?
- SkASSERT(this->getCachedImage());
- if (SkBitmapImageGetPixelRef(this->getCachedImage()) == fBitmap.pixelRef()) {
+ SkASSERT(this->getCachedImage(kNo_Budgeted));
+ if (SkBitmapImageGetPixelRef(this->getCachedImage(kNo_Budgeted)) == fBitmap.pixelRef()) {
SkASSERT(fWeOwnThePixels);
if (kDiscard_ContentChangeMode == mode) {
fBitmap.setPixelRef(NULL);
return NULL;
}
- SkImage* onNewImageSnapshot() SK_OVERRIDE {
+ SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE {
return SkNewImageFromBitmap(fBitmap, true, &this->props());
}
surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
REPORTER_ASSERT(reporter, image->getTexture() == texture);
}
+
+#include "GrGpuResourceCacheAccess.h"
+#include "SkGpuDevice.h"
+#include "SkImage_Gpu.h"
+#include "SkSurface_Gpu.h"
+
+SkSurface::Budgeted is_budgeted(SkSurface* surf) {
+ return ((SkSurface_Gpu*)surf)->getDevice()->accessRenderTarget()->cacheAccess().isBudgeted() ?
+ SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
+}
+
+SkSurface::Budgeted is_budgeted(SkImage* image) {
+ return ((SkImage_Gpu*)image)->getTexture()->cacheAccess().isBudgeted() ?
+ SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
+}
+
+static void test_surface_budget(skiatest::Reporter* reporter, GrContext* context) {
+ SkImageInfo info = SkImageInfo::MakeN32Premul(8,8);
+ for (int i = 0; i < 2; ++i) {
+ SkSurface::Budgeted sbudgeted = i ? SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
+ for (int j = 0; j < 2; ++j) {
+ SkSurface::Budgeted ibudgeted = j ? SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
+ SkAutoTUnref<SkSurface>
+ surface(SkSurface::NewRenderTarget(context, sbudgeted, info, 0));
+ SkASSERT(surface);
+ REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
+
+ SkImage* image = surface->newImageSnapshot(ibudgeted);
+
+ // Initially the image shares a texture with the surface, and the surface decides
+ // whether it is budgeted or not.
+ REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
+ REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(image));
+
+ // Now trigger copy-on-write
+ surface->getCanvas()->clear(SK_ColorBLUE);
+
+ // They don't share a texture anymore. They should each have made their own budget
+ // decision.
+ REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
+ REPORTER_ASSERT(reporter, ibudgeted == is_budgeted(image));
+ }
+ }
+}
+
#endif
static void TestSurfaceNoCanvas(skiatest::Reporter* reporter,
TestGetTexture(reporter, kGpu_SurfaceType, context);
TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
test_empty_surface(reporter, context);
+ test_surface_budget(reporter, context);
}
}
}