diff options
author | Eric Karl <ericrk@chromium.org> | 2017-10-12 12:44:50 -0700 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-10-12 20:05:31 +0000 |
commit | 914a36b248ffb538874483d86759254838866dd7 (patch) | |
tree | ea6954b9b3fc41bf87a3be1c30d5fd447656b28a | |
parent | 708ec81d7a9bba12cd7e574b5c5ae80b2ad77919 (diff) |
MakeBackendTextureFromSkImage
Creates a static function on SkImage which converts the SkImage to a
GrBackendTexture. The texture is unowned by Skia, and must be deleted
by the caller. Allows for a no-copy / no-conversion fast path if the
provided image is unowned (unique()) and texture backed.
Change-Id: I8a48f9cc39de792725cd72057d98cd1c4594daab
Reviewed-on: https://skia-review.googlesource.com/52440
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Eric Karl <ericrk@chromium.org>
-rw-r--r-- | include/core/SkImage.h | 24 | ||||
-rw-r--r-- | include/gpu/GrBackendSurface.h | 3 | ||||
-rw-r--r-- | include/gpu/GrGpuResource.h | 1 | ||||
-rw-r--r-- | include/gpu/GrTexture.h | 15 | ||||
-rw-r--r-- | src/gpu/GrSurfacePriv.h | 1 | ||||
-rw-r--r-- | src/gpu/GrTexture.cpp | 20 | ||||
-rw-r--r-- | src/gpu/gl/GrGLTexture.cpp | 12 | ||||
-rw-r--r-- | src/gpu/gl/GrGLTexture.h | 2 | ||||
-rw-r--r-- | src/gpu/mock/GrMockTexture.h | 4 | ||||
-rw-r--r-- | src/gpu/vk/GrVkTexture.h | 4 | ||||
-rw-r--r-- | src/image/SkImage.cpp | 7 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 61 | ||||
-rw-r--r-- | tests/ImageTest.cpp | 88 |
13 files changed, 241 insertions, 1 deletions
diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 9e82be1bfa..e89f86ce8c 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -427,6 +427,30 @@ public: */ static sk_sp<SkImage> MakeFromDeferredTextureImageData(GrContext*, const void*, SkBudgeted); + typedef std::function<void(GrBackendTexture)> BackendTextureReleaseProc; + + /** + * Creates a GrBackendTexture from the provided SkImage. Returns true on success. The + * GrBackendTexture and BackendTextureReleaseProc are populated on success. It is the callers + * responsibility to call the BackendTextureReleaseProc once they have deleted the texture. + * + * Note that the BackendTextureReleaseProc allows Skia to clean up auxiliary data related + * to the GrBackendTexture, and is not a substitute for the client deleting the GrBackendTexture + * themselves. + * + * Note that if the SkImage is both texture backed and unowned (the only reference is + * std::moved into this function), Skia can return the texture directly, avoiding any + * conversions or copies. + * + * If the SkImage is not texture backed, this function will generate a texture with the image's + * contents and return that. + */ + static bool MakeBackendTextureFromSkImage(GrContext*, + sk_sp<SkImage>, + GrBackendTexture*, + BackendTextureReleaseProc*); + + // Helper functions to convert to SkBitmap enum LegacyBitmapMode { diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h index 647d20d52b..bf1e52c959 100644 --- a/include/gpu/GrBackendSurface.h +++ b/include/gpu/GrBackendSurface.h @@ -69,9 +69,10 @@ public: // it returns nullptr. const GrMockTextureInfo* getMockTextureInfo() const; -private: + // Returns true if the backend texture has been initialized. bool isValid() const { return fConfig != kUnknown_GrPixelConfig; } +private: int fWidth; //<! width in pixels int fHeight; //<! height in pixels GrPixelConfig fConfig; diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index 6d28c1231d..70c94c0e55 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -91,6 +91,7 @@ protected: bool internalHasPendingIO() const { return SkToBool(fPendingWrites | fPendingReads); } bool internalHasRef() const { return SkToBool(fRefCnt); } + bool internalHasUniqueRef() const { return fRefCnt == 1; } private: friend class GrIORefProxy; // needs to forward on wrapped IO calls diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h index 7b8cd908f2..c32c0e97a8 100644 --- a/include/gpu/GrTexture.h +++ b/include/gpu/GrTexture.h @@ -9,8 +9,10 @@ #ifndef GrTexture_DEFINED #define GrTexture_DEFINED +#include "GrBackendSurface.h" #include "GrSamplerState.h" #include "GrSurface.h" +#include "SkImage.h" #include "SkPoint.h" #include "SkRefCnt.h" #include "../private/GrTypesPriv.h" @@ -34,6 +36,17 @@ public: */ virtual void textureParamsModified() = 0; + /** + * This function steals the backend texture from a uniquely owned GrTexture with no pending + * IO, passing it out to the caller. The GrTexture is deleted in the process. + * + * Note that if the GrTexture is not uniquely owned (no other refs), or has pending IO, this + * function will fail. + */ + static bool StealBackendTexture(sk_sp<GrTexture>&&, + GrBackendTexture*, + SkImage::BackendTextureReleaseProc*); + #ifdef SK_DEBUG void validate() const { this->INHERITED::validate(); @@ -54,6 +67,8 @@ protected: GrTexture(GrGpu*, const GrSurfaceDesc&, GrSLType samplerType, GrSamplerState::Filter highestFilterMode, GrMipMapsStatus); + virtual bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) = 0; + private: void computeScratchKey(GrScratchKey*) const override; size_t onGpuMemorySize() const override; diff --git a/src/gpu/GrSurfacePriv.h b/src/gpu/GrSurfacePriv.h index ce144c8a01..d655dfe710 100644 --- a/src/gpu/GrSurfacePriv.h +++ b/src/gpu/GrSurfacePriv.h @@ -36,6 +36,7 @@ public: bool hasPendingRead() const { return fSurface->hasPendingRead(); } bool hasPendingWrite() const { return fSurface->hasPendingWrite(); } bool hasPendingIO() const { return fSurface->hasPendingIO(); } + bool hasUniqueRef() const { return fSurface->internalHasUniqueRef(); } private: explicit GrSurfacePriv(GrSurface* surface) : fSurface(surface) {} diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp index 250b6dd167..b0dba5142e 100644 --- a/src/gpu/GrTexture.cpp +++ b/src/gpu/GrTexture.cpp @@ -10,6 +10,7 @@ #include "GrGpu.h" #include "GrResourceKey.h" #include "GrRenderTarget.h" +#include "GrSurfacePriv.h" #include "GrTexture.h" #include "GrTexturePriv.h" #include "GrTypes.h" @@ -56,6 +57,25 @@ GrTexture::GrTexture(GrGpu* gpu, const GrSurfaceDesc& desc, GrSLType samplerType } } +bool GrTexture::StealBackendTexture(sk_sp<GrTexture>&& texture, + GrBackendTexture* backendTexture, + SkImage::BackendTextureReleaseProc* releaseProc) { + if (!texture->surfacePriv().hasUniqueRef() || texture->surfacePriv().hasPendingIO()) { + return false; + } + + if (!texture->onStealBackendTexture(backendTexture, releaseProc)) { + return false; + } + + // Release any not-stolen data being held by this class. + texture->onRelease(); + // Abandon the GrTexture so it can't be re-used. + texture->abandon(); + + return true; +} + void GrTexture::computeScratchKey(GrScratchKey* key) const { const GrRenderTarget* rt = this->asRenderTarget(); int sampleCount = 0; diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp index b5c8c697e1..f7259a57ad 100644 --- a/src/gpu/gl/GrGLTexture.cpp +++ b/src/gpu/gl/GrGLTexture.cpp @@ -117,3 +117,15 @@ sk_sp<GrGLTexture> GrGLTexture::MakeWrapped(GrGLGpu* gpu, const GrSurfaceDesc& d return sk_sp<GrGLTexture>(new GrGLTexture(gpu, kWrapped, desc, mipMapsStatus, idDesc)); } +bool GrGLTexture::onStealBackendTexture(GrBackendTexture* backendTexture, + SkImage::BackendTextureReleaseProc* releaseProc) { + *backendTexture = GrBackendTexture(width(), height(), config(), fInfo); + // Set the release proc to a no-op function. GL doesn't require any special cleanup. + *releaseProc = [](GrBackendTexture){}; + + // It's important that we only abandon this texture's objects, not subclass objects such as + // those held by GrGLTextureRenderTarget. Those objects are not being stolen and need to be + // cleaned up by us. + this->GrGLTexture::onAbandon(); + return true; +} diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h index bb46d8d9b6..dfce602bf1 100644 --- a/src/gpu/gl/GrGLTexture.h +++ b/src/gpu/gl/GrGLTexture.h @@ -85,6 +85,8 @@ protected: void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump, const SkString& dumpName) const override; + bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) override; + private: void invokeReleaseProc() { if (fReleaseProc) { diff --git a/src/gpu/mock/GrMockTexture.h b/src/gpu/mock/GrMockTexture.h index 0db583f332..44ca32030b 100644 --- a/src/gpu/mock/GrMockTexture.h +++ b/src/gpu/mock/GrMockTexture.h @@ -45,6 +45,10 @@ protected: , fReleaseProc(nullptr) , fReleaseCtx(nullptr) {} + bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) override { + return false; + } + private: GrMockTextureInfo fInfo; ReleaseProc fReleaseProc; diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h index d3acce8a34..0749f6b62c 100644 --- a/src/gpu/vk/GrVkTexture.h +++ b/src/gpu/vk/GrVkTexture.h @@ -52,6 +52,10 @@ protected: void onAbandon() override; void onRelease() override; + bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) override { + return false; + } + private: enum Wrapped { kWrapped }; GrVkTexture(GrVkGpu*, SkBudgeted, const GrSurfaceDesc&, diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 7e1b238318..b60224038e 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -354,6 +354,13 @@ sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, con return nullptr; } +bool SkImage::MakeBackendTextureFromSkImage(GrContext*, + sk_sp<SkImage>, + GrBackendTexture*, + BackendTextureReleaseProc*) { + return false; +} + sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, const GrBackendTexture& tex, GrSurfaceOrigin origin, SkAlphaType at, sk_sp<SkColorSpace> cs) { diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 3809266180..681d7b1c6d 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -22,8 +22,10 @@ #include "GrRenderTargetContext.h" #include "GrResourceProvider.h" #include "GrSemaphore.h" +#include "GrSurfacePriv.h" #include "GrTextureAdjuster.h" #include "GrTexture.h" +#include "GrTexturePriv.h" #include "GrTextureProxy.h" #include "effects/GrNonlinearColorSpaceXformEffect.h" #include "effects/GrYUVEffect.h" @@ -912,6 +914,65 @@ sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, con /////////////////////////////////////////////////////////////////////////////////////////////////// +bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx, + sk_sp<SkImage> image, + GrBackendTexture* backendTexture, + BackendTextureReleaseProc* releaseProc) { + if (!image || !ctx || !backendTexture || !releaseProc) { + return false; + } + + // Ensure we have a texture backed image. + if (!image->isTextureBacked()) { + image = image->makeTextureImage(ctx, nullptr); + if (!image) { + return false; + } + } + GrTexture* texture = image->getTexture(); + SkASSERT(texture); + + // If the image's context doesn't match the provided context, fail. + if (texture->getContext() != ctx) { + return false; + } + + // Flush any pending IO on the texture. + ctx->contextPriv().prepareSurfaceForExternalIO(as_IB(image)->peekProxy()); + SkASSERT(!texture->surfacePriv().hasPendingIO()); + + // We must make a copy of the image if the image is not unique, if the GrTexture owned by the + // image is not unique, or if the texture wraps an external object. + if (!image->unique() || !texture->surfacePriv().hasUniqueRef() || + texture->resourcePriv().refsWrappedObjects()) { + // onMakeSubset will always copy the image. + image = as_IB(image)->onMakeSubset(image->bounds()); + if (!image) { + return false; + } + + texture = image->getTexture(); + SkASSERT(texture); + + // Flush to ensure that the copy is completed before we return the texture. + ctx->contextPriv().prepareSurfaceForExternalIO(as_IB(image)->peekProxy()); + SkASSERT(!texture->surfacePriv().hasPendingIO()); + } + + SkASSERT(!texture->resourcePriv().refsWrappedObjects()); + SkASSERT(texture->surfacePriv().hasUniqueRef()); + SkASSERT(image->unique()); + + // Take a reference to the GrTexture and release the image. + sk_sp<GrTexture> textureRef(SkSafeRef(texture)); + image = nullptr; + + // Steal the backend texture from the GrTexture, releasing the GrTexture in the process. + return GrTexture::StealBackendTexture(std::move(textureRef), backendTexture, releaseProc); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info, const GrMipLevel texels[], int mipLevelCount, SkBudgeted budgeted, diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index a35d56caf3..8eecbbd0a5 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -1132,6 +1132,94 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) { context->flush(); } } + +static uint32_t GetIdForBackendObject(GrContext* ctx, GrBackendObject object) { + if (!object) { + return 0; + } + + if (ctx->contextPriv().getBackend() != kOpenGL_GrBackend) { + return 0; + } + + return reinterpret_cast<const GrGLTextureInfo*>(object)->fID; +} + +static uint32_t GetIdForBackendTexture(GrBackendTexture texture) { + if (!texture.isValid()) { + return 0; + } + + if (texture.backend() != kOpenGL_GrBackend) { + return 0; + } + + return texture.getGLTextureInfo()->fID; +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(makeBackendTexture, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + sk_gpu_test::TestContext* testContext = ctxInfo.testContext(); + sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy(); + + GrContextFactory otherFactory; + ContextInfo otherContextInfo = otherFactory.getContextInfo(ctxInfo.type()); + + testContext->makeCurrent(); + REPORTER_ASSERT(reporter, proxy); + auto createLarge = [context] { + return create_image_large(context->caps()->maxTextureSize()); + }; + struct { + std::function<sk_sp<SkImage> ()> fImageFactory; + bool fExpectation; + bool fCanTakeDirectly; + } testCases[] = { + { create_image, true, false }, + { create_codec_image, true, false }, + { create_data_image, true, false }, + { create_picture_image, true, false }, + { [context] { return create_gpu_image(context); }, true, true }, + // Create a texture image in a another GrContext. + { [testContext, otherContextInfo] { + otherContextInfo.testContext()->makeCurrent(); + sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext()); + testContext->makeCurrent(); + return otherContextImage; + }, false, false }, + // Create an image that is too large to be texture backed. + { createLarge, false, false } + }; + + for (auto testCase : testCases) { + sk_sp<SkImage> image(testCase.fImageFactory()); + if (!image) { + ERRORF(reporter, "Failed to create image!"); + continue; + } + + uint32_t originalID = GetIdForBackendObject(context, image->getTextureHandle(true, nullptr)); + GrBackendTexture texture; + SkImage::BackendTextureReleaseProc proc; + bool result = + SkImage::MakeBackendTextureFromSkImage(context, std::move(image), &texture, &proc); + if (result != testCase.fExpectation) { + static const char *const kFS[] = { "fail", "succeed" }; + ERRORF(reporter, "This image was expected to %s but did not.", + kFS[testCase.fExpectation]); + } + + bool tookDirectly = result && originalID == GetIdForBackendTexture(texture); + if (testCase.fCanTakeDirectly != tookDirectly) { + static const char *const kExpectedState[] = { "not expected", "expected" }; + ERRORF(reporter, "This backend texture was %s to be taken directly.", + kExpectedState[testCase.fCanTakeDirectly]); + } + + testContext->makeCurrent(); + context->flush(); + } +} #endif /////////////////////////////////////////////////////////////////////////////////////////////////// |