aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkSurface.h10
-rw-r--r--include/gpu/GrTypes.h64
-rw-r--r--src/gpu/GrTexture.cpp3
-rw-r--r--src/gpu/GrTextureProvider.cpp3
-rw-r--r--src/gpu/SkGpuDevice.cpp17
-rw-r--r--src/gpu/SkGpuDevice.h6
-rw-r--r--src/gpu/SkGrPixelRef.cpp1
-rw-r--r--src/gpu/gl/GrGLGpu.cpp152
-rw-r--r--src/gpu/gl/GrGLGpu.h12
-rw-r--r--src/gpu/gl/GrGLTexture.cpp8
-rw-r--r--src/image/SkSurface.cpp2
-rw-r--r--src/image/SkSurface_Gpu.cpp8
-rw-r--r--tests/TextureStorageAllocator.cpp109
13 files changed, 322 insertions, 73 deletions
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 52097be66e..45262d78e6 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -120,12 +120,16 @@ public:
/**
* Return a new surface whose contents will be drawn to an offscreen
* render target, allocated by the surface.
+ *
+ * The GrTextureStorageAllocator will be reused if SkImage snapshots create
+ * additional textures.
*/
- static SkSurface* NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&, int sampleCount,
- const SkSurfaceProps* = NULL);
+ static SkSurface* NewRenderTarget(
+ GrContext*, Budgeted, const SkImageInfo&, int sampleCount, const SkSurfaceProps* = NULL,
+ GrTextureStorageAllocator = GrTextureStorageAllocator());
static SkSurface* NewRenderTarget(GrContext* gr, Budgeted b, const SkImageInfo& info) {
- return NewRenderTarget(gr, b, info, 0, NULL);
+ return NewRenderTarget(gr, b, info, 0);
}
int width() const { return fWidth; }
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index dbcb9a6583..767e07206e 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -409,6 +409,9 @@ enum GrSurfaceFlags {
GR_MAKE_BITFIELD_OPS(GrSurfaceFlags)
+// opaque type for 3D API object handles
+typedef intptr_t GrBackendObject;
+
/**
* Some textures will be stored such that the upper and left edges of the content meet at the
* the origin (in texture coord space) and for other textures the lower and left edges meet at
@@ -423,6 +426,58 @@ enum GrSurfaceOrigin {
};
/**
+ * An container of function pointers which consumers of Skia can fill in and
+ * pass to Skia. Skia will use these function pointers in place of its backend
+ * API texture creation function. Either all of the function pointers should be
+ * filled in, or they should all be nullptr.
+ */
+struct GrTextureStorageAllocator {
+ GrTextureStorageAllocator()
+ : fAllocateTextureStorage(nullptr)
+ , fDeallocateTextureStorage(nullptr) {
+ }
+
+ enum class Result {
+ kSucceededAndUploaded,
+ kSucceededWithoutUpload,
+ kFailed
+ };
+ typedef Result (*AllocateTextureStorageProc)(
+ void* ctx, GrBackendObject texture, unsigned width,
+ unsigned height, GrPixelConfig config, const void* srcData, GrSurfaceOrigin);
+ typedef void (*DeallocateTextureStorageProc)(void* ctx, GrBackendObject texture);
+
+ /*
+ * Generates and binds a texture to |textureStorageTarget()|. Allocates
+ * storage for the texture.
+ *
+ * In OpenGL, the MIN and MAX filters for the created texture must be
+ * GL_LINEAR. The WRAP_S and WRAP_T must be GL_CLAMP_TO_EDGE.
+ *
+ * If |srcData| is not nullptr, then the implementation of this function
+ * may attempt to upload the data into the texture. On successful upload,
+ * or if |srcData| is nullptr, returns kSucceededAndUploaded.
+ */
+ AllocateTextureStorageProc fAllocateTextureStorage;
+
+ /*
+ * Deallocate the storage for the given texture.
+ *
+ * Skia does not always destroy its outstanding textures. See
+ * GrContext::abandonContext() for more details. The consumer of Skia is
+ * responsible for making sure that all textures are destroyed, even if this
+ * callback is not invoked.
+ */
+ DeallocateTextureStorageProc fDeallocateTextureStorage;
+
+ /*
+ * The context to use when invoking fAllocateTextureStorage and
+ * fDeallocateTextureStorage.
+ */
+ void* fCtx;
+};
+
+/**
* Describes a surface to be created.
*/
struct GrSurfaceDesc {
@@ -454,6 +509,12 @@ struct GrSurfaceDesc {
* max supported count.
*/
int fSampleCnt;
+
+ /**
+ * A custom platform-specific allocator to use in place of the backend APIs
+ * usual texture creation method (e.g. TexImage2D in OpenGL).
+ */
+ GrTextureStorageAllocator fTextureStorageAllocator;
};
// Legacy alias
@@ -469,9 +530,6 @@ enum GrClipType {
///////////////////////////////////////////////////////////////////////////////
-// opaque type for 3D API object handles
-typedef intptr_t GrBackendObject;
-
/** Ownership rules for external GPU resources imported into Skia. */
enum GrWrapOwnership {
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 249fe98c20..fe5b2c3c37 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -85,7 +85,8 @@ GrTexture::GrTexture(GrGpu* gpu, LifeCycle lifeCycle, const GrSurfaceDesc& desc)
: INHERITED(gpu, lifeCycle, desc)
, fMipMapsStatus(kNotAllocated_MipMapsStatus) {
- if (!this->isExternal() && !GrPixelConfigIsCompressed(desc.fConfig)) {
+ if (!this->isExternal() && !GrPixelConfigIsCompressed(desc.fConfig) &&
+ !desc.fTextureStorageAllocator.fAllocateTextureStorage) {
GrScratchKey key;
GrTexturePriv::ComputeScratchKey(desc, &key);
this->setScratchKey(key);
diff --git a/src/gpu/GrTextureProvider.cpp b/src/gpu/GrTextureProvider.cpp
index 105cce2fb7..7d720eb121 100644
--- a/src/gpu/GrTextureProvider.cpp
+++ b/src/gpu/GrTextureProvider.cpp
@@ -39,7 +39,8 @@ GrTexture* GrTextureProvider::createTexture(const GrSurfaceDesc& desc, bool budg
!fGpu->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
return nullptr;
}
- if (!GrPixelConfigIsCompressed(desc.fConfig)) {
+ if (!GrPixelConfigIsCompressed(desc.fConfig) &&
+ !desc.fTextureStorageAllocator.fAllocateTextureStorage) {
static const uint32_t kFlags = kExact_ScratchTextureFlag |
kNoCreate_ScratchTextureFlag;
if (GrTexture* texture = this->refScratchTexture(desc, kFlags)) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ed4ca74297..060b2a1e45 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -149,13 +149,15 @@ SkGpuDevice* SkGpuDevice::Create(GrRenderTarget* rt, int width, int height,
SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgeted,
const SkImageInfo& info, int sampleCount,
- const SkSurfaceProps* props, InitContents init) {
+ const SkSurfaceProps* props, InitContents init,
+ GrTextureStorageAllocator customAllocator) {
unsigned flags;
if (!CheckAlphaTypeAndGetFlags(&info, init, &flags)) {
return nullptr;
}
- SkAutoTUnref<GrRenderTarget> rt(CreateRenderTarget(context, budgeted, info, sampleCount));
+ SkAutoTUnref<GrRenderTarget> rt(CreateRenderTarget(
+ context, budgeted, info, sampleCount, customAllocator));
if (nullptr == rt) {
return nullptr;
}
@@ -182,8 +184,9 @@ SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, int width, int height,
}
}
-GrRenderTarget* SkGpuDevice::CreateRenderTarget(GrContext* context, SkSurface::Budgeted budgeted,
- const SkImageInfo& origInfo, int sampleCount) {
+GrRenderTarget* SkGpuDevice::CreateRenderTarget(
+ GrContext* context, SkSurface::Budgeted budgeted, const SkImageInfo& origInfo,
+ int sampleCount, GrTextureStorageAllocator textureStorageAllocator) {
if (kUnknown_SkColorType == origInfo.colorType() ||
origInfo.width() < 0 || origInfo.height() < 0) {
return nullptr;
@@ -212,6 +215,7 @@ GrRenderTarget* SkGpuDevice::CreateRenderTarget(GrContext* context, SkSurface::B
desc.fHeight = info.height();
desc.fConfig = SkImageInfo2GrPixelConfig(info);
desc.fSampleCnt = sampleCount;
+ desc.fTextureStorageAllocator = textureStorageAllocator;
GrTexture* texture = context->textureProvider()->createTexture(
desc, SkToBool(budgeted), nullptr, 0);
if (nullptr == texture) {
@@ -322,7 +326,8 @@ void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) {
: SkSurface::kNo_Budgeted;
SkAutoTUnref<GrRenderTarget> newRT(CreateRenderTarget(
- this->context(), budgeted, this->imageInfo(), fRenderTarget->desc().fSampleCnt));
+ this->context(), budgeted, this->imageInfo(), fRenderTarget->desc().fSampleCnt,
+ fRenderTarget->desc().fTextureStorageAllocator));
if (nullptr == newRT) {
return;
@@ -1474,7 +1479,7 @@ void SkGpuDevice::drawProducerNine(const SkDraw& draw, GrTextureProducer* produc
SkRect srcR, dstR;
while (iter.next(&srcR, &dstR)) {
- this->drawTextureProducer(producer, &srcR, &dstR, SkCanvas::kStrict_SrcRectConstraint,
+ this->drawTextureProducer(producer, &srcR, &dstR, SkCanvas::kStrict_SrcRectConstraint,
*draw.fMatrix, fClip, paint);
}
return;
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 8bf04b1182..ec3e158706 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -18,6 +18,7 @@
#include "GrDrawContext.h"
#include "GrContext.h"
#include "GrSurfacePriv.h"
+#include "GrTypes.h"
class GrAccelData;
class GrTextureProducer;
@@ -52,7 +53,8 @@ public:
* the resource cache budget. On failure, returns nullptr.
*/
static SkGpuDevice* Create(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
- int sampleCount, const SkSurfaceProps*, InitContents);
+ int sampleCount, const SkSurfaceProps*,
+ InitContents, GrTextureStorageAllocator = GrTextureStorageAllocator());
~SkGpuDevice() override {}
@@ -255,7 +257,7 @@ private:
bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
- int sampleCount);
+ int sampleCount, GrTextureStorageAllocator);
friend class GrAtlasTextContext;
friend class SkSurface_Gpu; // for access to surfaceProps
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
index 58f516a19c..3876f17411 100644
--- a/src/gpu/SkGrPixelRef.cpp
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -76,6 +76,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp
}
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT);
+ desc.fTextureStorageAllocator = texture->desc().fTextureStorageAllocator;
GrTexture* dst = context->textureProvider()->createTexture(desc, false, nullptr, 0);
if (nullptr == dst) {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index cd28ecaab5..e29a7f81f3 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -929,14 +929,24 @@ bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc,
!(0 == left && 0 == top && desc.fWidth == width && desc.fHeight == height)) {
succeeded = false;
} else {
- CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
- GL_ALLOC_CALL(this->glInterface(), TexImage2D(target, 0, internalFormat, desc.fWidth,
- desc.fHeight, 0, externalFormat,
- externalType, dataOrOffset));
- GrGLenum error = check_alloc_error(desc, this->glInterface());
- if (error != GR_GL_NO_ERROR) {
- succeeded = false;
- }
+ if (desc.fTextureStorageAllocator.fAllocateTextureStorage) {
+ if (dataOrOffset) {
+ GL_CALL(TexSubImage2D(target,
+ 0, // level
+ left, top,
+ width, height,
+ externalFormat, externalType, dataOrOffset));
+ }
+ } else {
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ GL_ALLOC_CALL(this->glInterface(), TexImage2D(
+ target, 0, internalFormat, desc.fWidth, desc.fHeight, 0, externalFormat,
+ externalType, dataOrOffset));
+ GrGLenum error = check_alloc_error(desc, this->glInterface());
+ if (error != GR_GL_NO_ERROR) {
+ succeeded = false;
+ }
+ }
}
} else {
if (swFlipY || glFlipY) {
@@ -1205,52 +1215,10 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
GrGLTexture::IDDesc idDesc;
- idDesc.fInfo.fID = 0;
- GL_CALL(GenTextures(1, &idDesc.fInfo.fID));
idDesc.fLifeCycle = lifeCycle;
- // We only support GL_TEXTURE_2D at the moment.
- idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D;
-
- if (!idDesc.fInfo.fID) {
- return return_null_texture();
- }
-
- this->setScratchTextureUnit();
- GL_CALL(BindTexture(idDesc.fInfo.fTarget, idDesc.fInfo.fID));
-
- if (renderTarget && this->glCaps().textureUsageSupport()) {
- // provides a hint about how this texture will be used
- GL_CALL(TexParameteri(idDesc.fInfo.fTarget,
- GR_GL_TEXTURE_USAGE,
- GR_GL_FRAMEBUFFER_ATTACHMENT));
- }
-
- // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
- // drivers have a bug where an FBO won't be complete if it includes a
- // texture that is not mipmap complete (considering the filter in use).
GrGLTexture::TexParams initialTexParams;
- // we only set a subset here so invalidate first
- initialTexParams.invalidate();
- initialTexParams.fMinFilter = GR_GL_NEAREST;
- initialTexParams.fMagFilter = GR_GL_NEAREST;
- initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE;
- initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE;
- GL_CALL(TexParameteri(idDesc.fInfo.fTarget,
- GR_GL_TEXTURE_MAG_FILTER,
- initialTexParams.fMagFilter));
- GL_CALL(TexParameteri(idDesc.fInfo.fTarget,
- GR_GL_TEXTURE_MIN_FILTER,
- initialTexParams.fMinFilter));
- GL_CALL(TexParameteri(idDesc.fInfo.fTarget,
- GR_GL_TEXTURE_WRAP_S,
- initialTexParams.fWrapS));
- GL_CALL(TexParameteri(idDesc.fInfo.fTarget,
- GR_GL_TEXTURE_WRAP_T,
- initialTexParams.fWrapT));
- if (!this->uploadTexData(desc, idDesc.fInfo.fTarget, kNewTexture_UploadType, 0, 0,
- desc.fWidth, desc.fHeight,
- desc.fConfig, srcData, rowBytes)) {
- GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID));
+ if (!this->createTextureImpl(desc, &idDesc.fInfo, renderTarget, srcData,
+ &initialTexParams, rowBytes)) {
return return_null_texture();
}
@@ -1473,6 +1441,86 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) {
return this->glCaps().getStencilFormatIndexForConfig(config);
}
+bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info,
+ bool renderTarget, const void* srcData,
+ GrGLTexture::TexParams* initialTexParams, size_t rowBytes) {
+ // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
+ // drivers have a bug where an FBO won't be complete if it includes a
+ // texture that is not mipmap complete (considering the filter in use).
+
+ // we only set a subset here so invalidate first
+ initialTexParams->invalidate();
+ initialTexParams->fMinFilter = GR_GL_NEAREST;
+ initialTexParams->fMagFilter = GR_GL_NEAREST;
+ initialTexParams->fWrapS = GR_GL_CLAMP_TO_EDGE;
+ initialTexParams->fWrapT = GR_GL_CLAMP_TO_EDGE;
+
+ if (desc.fTextureStorageAllocator.fAllocateTextureStorage) {
+ return this->createTextureExternalAllocatorImpl(desc, info, srcData, rowBytes);
+ }
+
+ info->fID = 0;
+ info->fTarget = GR_GL_TEXTURE_2D;
+ GL_CALL(GenTextures(1, &(info->fID)));
+
+ if (!info->fID) {
+ return false;
+ }
+
+ this->setScratchTextureUnit();
+ GL_CALL(BindTexture(info->fTarget, info->fID));
+
+ if (renderTarget && this->glCaps().textureUsageSupport()) {
+ // provides a hint about how this texture will be used
+ GL_CALL(TexParameteri(info->fTarget,
+ GR_GL_TEXTURE_USAGE,
+ GR_GL_FRAMEBUFFER_ATTACHMENT));
+ }
+
+ GL_CALL(TexParameteri(info->fTarget,
+ GR_GL_TEXTURE_MAG_FILTER,
+ initialTexParams->fMagFilter));
+ GL_CALL(TexParameteri(info->fTarget,
+ GR_GL_TEXTURE_MIN_FILTER,
+ initialTexParams->fMinFilter));
+ GL_CALL(TexParameteri(info->fTarget,
+ GR_GL_TEXTURE_WRAP_S,
+ initialTexParams->fWrapS));
+ GL_CALL(TexParameteri(info->fTarget,
+ GR_GL_TEXTURE_WRAP_T,
+ initialTexParams->fWrapT));
+ if (!this->uploadTexData(desc, info->fTarget, kNewTexture_UploadType, 0, 0,
+ desc.fWidth, desc.fHeight,
+ desc.fConfig, srcData, rowBytes)) {
+ GL_CALL(DeleteTextures(1, &(info->fID)));
+ return false;
+ }
+ return true;
+}
+
+bool GrGLGpu::createTextureExternalAllocatorImpl(
+ const GrSurfaceDesc& desc, GrGLTextureInfo* info, const void* srcData, size_t rowBytes) {
+ switch (desc.fTextureStorageAllocator.fAllocateTextureStorage(
+ desc.fTextureStorageAllocator.fCtx, reinterpret_cast<GrBackendObject>(info),
+ desc.fWidth, desc.fHeight, desc.fConfig, srcData, desc.fOrigin)) {
+ case GrTextureStorageAllocator::Result::kSucceededAndUploaded:
+ return true;
+ case GrTextureStorageAllocator::Result::kFailed:
+ return false;
+ case GrTextureStorageAllocator::Result::kSucceededWithoutUpload:
+ break;
+ }
+
+ if (!this->uploadTexData(desc, info->fTarget, kNewTexture_UploadType, 0, 0,
+ desc.fWidth, desc.fHeight,
+ desc.fConfig, srcData, rowBytes)) {
+ desc.fTextureStorageAllocator.fDeallocateTextureStorage(
+ desc.fTextureStorageAllocator.fCtx, reinterpret_cast<GrBackendObject>(info));
+ return false;
+ }
+ return true;
+}
+
GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
int width,
int height) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index ec93bbfe21..ce2f95ef30 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -159,6 +159,18 @@ private:
// compatible stencil format, or negative if there is no compatible stencil format.
int getCompatibleStencilIndex(GrPixelConfig config);
+ // If |desc.fTextureStorageAllocator| exists, use that to create the
+ // texture. Otherwise, create the texture directly.
+ // Returns whether the texture is successfully created. On success, the
+ // result is stored in |info|.
+ // The texture is populated with |srcData|, if it exists.
+ // The texture parameters are cached in |initialTexParams|.
+ bool createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info,
+ bool renderTarget, const void* srcData,
+ GrGLTexture::TexParams* initialTexParams, size_t rowBytes);
+ bool createTextureExternalAllocatorImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info,
+ const void* srcData, size_t rowBytes);
+
void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override;
void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override;
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 39a8d9279f..864547ae26 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -37,7 +37,13 @@ void GrGLTexture::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) {
void GrGLTexture::onRelease() {
if (fInfo.fID) {
if (GrGpuResource::kBorrowed_LifeCycle != fTextureIDLifecycle) {
- GL_CALL(DeleteTextures(1, &fInfo.fID));
+ if (this->desc().fTextureStorageAllocator.fDeallocateTextureStorage) {
+ this->desc().fTextureStorageAllocator.fDeallocateTextureStorage(
+ this->desc().fTextureStorageAllocator.fCtx,
+ reinterpret_cast<GrBackendObject>(&fInfo));
+ } else {
+ GL_CALL(DeleteTextures(1, &fInfo.fID));
+ }
}
fInfo.fID = 0;
}
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 0315f6cc84..fed13a284b 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -204,7 +204,7 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget*, const SkSurfaceProp
}
SkSurface* SkSurface::NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&, int,
- const SkSurfaceProps*) {
+ const SkSurfaceProps*, GrTextureStorageAllocator) {
return nullptr;
}
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 2d5645b2b0..f7a31c009b 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -121,9 +121,11 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurf
}
SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, Budgeted budgeted, const SkImageInfo& info,
- int sampleCount, const SkSurfaceProps* props) {
- SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(ctx, budgeted, info, sampleCount, props,
- SkGpuDevice::kClear_InitContents));
+ int sampleCount, const SkSurfaceProps* props,
+ GrTextureStorageAllocator customAllocator) {
+ SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(
+ ctx, budgeted, info, sampleCount, props, SkGpuDevice::kClear_InitContents,
+ customAllocator));
if (!device) {
return nullptr;
}
diff --git a/tests/TextureStorageAllocator.cpp b/tests/TextureStorageAllocator.cpp
new file mode 100644
index 0000000000..da5bff2986
--- /dev/null
+++ b/tests/TextureStorageAllocator.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "gl/GrGLGpu.h"
+#include "GrContext.h"
+#include "SkSurface_Gpu.h"
+#include "../include/gpu/gl/SkGLContext.h"
+#include "../include/gpu/GrTypes.h"
+#include "../include/private/SkTemplates.h"
+
+class TestStorageAllocator {
+ public:
+ static GrTextureStorageAllocator::Result allocateTextureStorage(void* ctx,
+ GrBackendObject texture, unsigned width, unsigned height, GrPixelConfig config,
+ const void* srcData, GrSurfaceOrigin) {
+ TestStorageAllocator* allocator = static_cast<TestStorageAllocator*>(ctx);
+ if (!allocator->m_allowAllocation)
+ return GrTextureStorageAllocator::Result::kFailed;
+ SkAutoTMalloc<uint8_t> pixels(width * height * 4);
+ memset(pixels.get(), 0, width * height * 4);
+
+ GrGLuint id;
+ GrGLenum target = GR_GL_TEXTURE_2D;
+ GR_GL_CALL(allocator->m_gl, GenTextures(1, &id));
+ GR_GL_CALL(allocator->m_gl, BindTexture(target, id));
+ GR_GL_CALL(allocator->m_gl, TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, GR_GL_NEAREST));
+ GR_GL_CALL(allocator->m_gl, TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, GR_GL_NEAREST));
+ GR_GL_CALL(allocator->m_gl, TexParameteri(target, GR_GL_TEXTURE_WRAP_S, GR_GL_CLAMP_TO_EDGE));
+ GR_GL_CALL(allocator->m_gl, TexParameteri(target, GR_GL_TEXTURE_WRAP_T, GR_GL_CLAMP_TO_EDGE));
+ GR_GL_CALL(allocator->m_gl, TexImage2D(target, 0, GR_GL_RGBA, width, height, 0, GR_GL_RGBA,
+ GR_GL_UNSIGNED_BYTE, pixels.get()));
+
+ GrGLTextureInfo* info = reinterpret_cast<GrGLTextureInfo*>(texture);
+ info->fID = id;
+ info->fTarget = target;
+ allocator->m_mostRecentlyAllocatedStorage = id;
+ return GrTextureStorageAllocator::Result::kSucceededWithoutUpload;
+ }
+ static void deallocateTextureStorage(void* ctx, GrBackendObject texture) {
+ TestStorageAllocator* allocator = static_cast<TestStorageAllocator*>(ctx);
+ GrGLTextureInfo* info = reinterpret_cast<GrGLTextureInfo*>(texture);
+ GR_GL_CALL(allocator->m_gl, DeleteTextures(1, &(info->fID)));
+ }
+
+ GrGLuint m_mostRecentlyAllocatedStorage;
+ const GrGLInterface* m_gl;
+ bool m_allowAllocation;
+};
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CustomTexture, reporter, context, glContext) {
+ static const int kWidth = 13;
+ static const int kHeight = 13;
+
+ const GrGLInterface* gl = glContext->gl();
+ TestStorageAllocator allocator;
+ allocator.m_allowAllocation = true;
+ allocator.m_gl = gl;
+ GrTextureStorageAllocator grAllocator;
+ grAllocator.fAllocateTextureStorage = &TestStorageAllocator::allocateTextureStorage;
+ grAllocator.fDeallocateTextureStorage= &TestStorageAllocator::deallocateTextureStorage;
+ grAllocator.fCtx = &allocator;
+
+ SkSurface* surface = SkSurface_Gpu::NewRenderTarget(
+ context, SkSurface_Gpu::kNo_Budgeted, SkImageInfo::MakeN32Premul(kWidth, kHeight), 0,
+ NULL, grAllocator);
+ REPORTER_ASSERT(reporter, surface);
+ GrGLuint id = allocator.m_mostRecentlyAllocatedStorage;
+
+ SkImage* image = surface->newImageSnapshot();
+ REPORTER_ASSERT(reporter, image->isTextureBacked());
+ SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(1,1);
+ GrColor dest = 0x11223344;
+ REPORTER_ASSERT(reporter, image->readPixels(imageInfo, &dest, 4 * kWidth, 0, 0));
+ REPORTER_ASSERT(reporter, GrColorUnpackG(dest) == 0);
+
+ surface->getCanvas()->clear(SK_ColorGREEN);
+ SkImage* image2 = surface->newImageSnapshot();
+ REPORTER_ASSERT(reporter, image2->isTextureBacked());
+ REPORTER_ASSERT(reporter, allocator.m_mostRecentlyAllocatedStorage != id);
+
+ REPORTER_ASSERT(reporter, image2->readPixels(imageInfo, &dest, 4 * kWidth, 0, 0));
+ REPORTER_ASSERT(reporter, GrColorUnpackG(dest) == 255);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CustomTextureFailure, reporter, context, glContext) {
+ static const int kWidth = 13;
+ static const int kHeight = 13;
+
+ const GrGLInterface* gl = glContext->gl();
+ TestStorageAllocator allocator;
+ allocator.m_allowAllocation = false;
+ allocator.m_gl = gl;
+ GrTextureStorageAllocator grAllocator;
+ grAllocator.fAllocateTextureStorage = &TestStorageAllocator::allocateTextureStorage;
+ grAllocator.fDeallocateTextureStorage= &TestStorageAllocator::deallocateTextureStorage;
+ grAllocator.fCtx = &allocator;
+ SkSurface* surface = SkSurface_Gpu::NewRenderTarget(
+ context, SkSurface_Gpu::kNo_Budgeted, SkImageInfo::MakeN32Premul(kWidth, kHeight), 0,
+ NULL, grAllocator);
+ REPORTER_ASSERT(reporter, !surface);
+}
+
+#endif