aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Eric Karl <ericrk@chromium.org>2017-10-12 12:44:50 -0700
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-10-12 20:05:31 +0000
commit914a36b248ffb538874483d86759254838866dd7 (patch)
treeea6954b9b3fc41bf87a3be1c30d5fd447656b28a
parent708ec81d7a9bba12cd7e574b5c5ae80b2ad77919 (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.h24
-rw-r--r--include/gpu/GrBackendSurface.h3
-rw-r--r--include/gpu/GrGpuResource.h1
-rw-r--r--include/gpu/GrTexture.h15
-rw-r--r--src/gpu/GrSurfacePriv.h1
-rw-r--r--src/gpu/GrTexture.cpp20
-rw-r--r--src/gpu/gl/GrGLTexture.cpp12
-rw-r--r--src/gpu/gl/GrGLTexture.h2
-rw-r--r--src/gpu/mock/GrMockTexture.h4
-rw-r--r--src/gpu/vk/GrVkTexture.h4
-rw-r--r--src/image/SkImage.cpp7
-rw-r--r--src/image/SkImage_Gpu.cpp61
-rw-r--r--tests/ImageTest.cpp88
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
///////////////////////////////////////////////////////////////////////////////////////////////////