From 9a1ed5d81dbfc7d5b67b568dfe12b4033a9af154 Mon Sep 17 00:00:00 2001 From: erikchen Date: Wed, 10 Feb 2016 16:32:34 -0800 Subject: [PATCH] skia: Add support for CHROMIUM_image backed textures. I created a new abstract base class TextureStorageAllocator that consumers of Skia can subclass and pass back to Skia. When a surface is created with a pointer to a TextureStorageAllocator, any textures it creates, or that are derived from the original surface, will allocate and deallocate storage using the methods on TextureStorageAllocator. BUG=https://code.google.com/p/chromium/issues/detail?id=579664 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1623653002 Committed: https://skia.googlesource.com/skia/+/92098e691f10a010e7421125ba4d44c02506bb55 Committed: https://skia.googlesource.com/skia/+/7fec91ce6660190f8d7c5eb6f3061e4550cc672b Committed: https://skia.googlesource.com/skia/+/b8d6e088590160f1198110c2371b802c1d541a36 Review URL: https://codereview.chromium.org/1623653002 --- include/core/SkSurface.h | 10 +- include/gpu/GrTypes.h | 64 ++++++++++++- src/gpu/GrTexture.cpp | 3 +- src/gpu/GrTextureProvider.cpp | 3 +- src/gpu/SkGpuDevice.cpp | 17 ++-- src/gpu/SkGpuDevice.h | 6 +- src/gpu/SkGrPixelRef.cpp | 1 + src/gpu/gl/GrGLGpu.cpp | 152 ++++++++++++++++++++---------- src/gpu/gl/GrGLGpu.h | 12 +++ src/gpu/gl/GrGLTexture.cpp | 8 +- src/image/SkSurface.cpp | 2 +- src/image/SkSurface_Gpu.cpp | 8 +- tests/TextureStorageAllocator.cpp | 109 +++++++++++++++++++++ 13 files changed, 322 insertions(+), 73 deletions(-) create mode 100644 tests/TextureStorageAllocator.cpp 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 @@ -422,6 +425,58 @@ enum GrSurfaceOrigin { kBottomLeft_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. */ @@ -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 rt(CreateRenderTarget(context, budgeted, info, sampleCount)); + SkAutoTUnref 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 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(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(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(&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 753f422e79..24ac6d3312 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -120,9 +120,11 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurf } SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, Budgeted budgeted, const SkImageInfo& info, - int sampleCount, const SkSurfaceProps* props) { - SkAutoTUnref device(SkGpuDevice::Create(ctx, budgeted, info, sampleCount, props, - SkGpuDevice::kClear_InitContents)); + int sampleCount, const SkSurfaceProps* props, + GrTextureStorageAllocator customAllocator) { + SkAutoTUnref 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..122839ca59 --- /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(ctx); + if (!allocator->m_allowAllocation) + return GrTextureStorageAllocator::Result::kFailed; + SkAutoTMalloc 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(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(ctx); + GrGLTextureInfo* info = reinterpret_cast(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; + + SkAutoTUnref 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; + + SkAutoTUnref 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); + SkAutoTUnref 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; + SkAutoTUnref surface(SkSurface_Gpu::NewRenderTarget( + context, SkSurface_Gpu::kNo_Budgeted, SkImageInfo::MakeN32Premul(kWidth, kHeight), 0, + NULL, grAllocator)); + REPORTER_ASSERT(reporter, !surface); +} + +#endif