diff options
author | Stan Iliev <stani@google.com> | 2017-06-02 10:29:21 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-06-02 14:51:24 +0000 |
commit | 7e910df7f133e80293117bdd069ed25998d10f8c (patch) | |
tree | 5bfeea96c4904ba67fa551a76def78f17470ad73 /src | |
parent | affa6a3da87e9ea85f1d4fe3137b5bccbbc56f92 (diff) |
Implement an SkImage backed by a Android hardware buffer
Create a new SkImage public API to make an image from an Android
hardware buffer. Implementation is using a SkImageGenerator
derived class GrAndroidBufferImageGenerator.
A new EGLImage texture is created, which is then wrapped with
GrTextureProxy.
Bug: skia:
Change-Id: I610a4c5a58198686ce7c03e9a0adad3f9d2342e0
Reviewed-on: https://skia-review.googlesource.com/17789
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Stan Iliev <stani@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrAHardwareBufferImageGenerator.cpp | 208 | ||||
-rw-r--r-- | src/gpu/GrAHardwareBufferImageGenerator.h | 52 | ||||
-rw-r--r-- | src/gpu/GrBackendTextureImageGenerator.h | 2 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 11 |
4 files changed, 272 insertions, 1 deletions
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp new file mode 100644 index 0000000000..94861d46ae --- /dev/null +++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkTypes.h" + +//TODO: This define is temporary and we will compile with NDK after +//TODO: Skia bug 6672 is resolved. +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#include "GrAHardwareBufferImageGenerator.h" + +#include "GrBackendSurface.h" +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +class BufferCleanupHelper { +public: + BufferCleanupHelper(EGLImageKHR image, EGLDisplay display) + : fImage(image) + , fDisplay(display) { } + ~BufferCleanupHelper() { + eglDestroyImageKHR(fDisplay, fImage); + } +private: + EGLImageKHR fImage; + EGLDisplay fDisplay; +}; + +std::unique_ptr<SkImageGenerator> GrAHardwareBufferImageGenerator::Make( + AHardwareBuffer* graphicBuffer, SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) { + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(graphicBuffer, &bufferDesc); + SkColorType colorType; + switch (bufferDesc.format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + colorType = kRGBA_8888_SkColorType; + break; + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + colorType = kRGBA_F16_SkColorType; + break; + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + colorType = kRGB_565_SkColorType; + break; + default: + return nullptr; + } + SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, + alphaType, std::move(colorSpace)); + return std::unique_ptr<SkImageGenerator>(new GrAHardwareBufferImageGenerator(info, graphicBuffer, + alphaType)); +} + +GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info, + AHardwareBuffer* graphicBuffer, SkAlphaType alphaType) + : INHERITED(info) + , fGraphicBuffer(graphicBuffer) + , fAlphaType(alphaType) { + AHardwareBuffer_acquire(fGraphicBuffer); +} + +GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() { + AHardwareBuffer_release(fGraphicBuffer); +} + +void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) { + BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context); + delete cleanupHelper; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + + +sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture( + GrContext* context, const SkImageInfo& info, const SkIPoint& origin) { + // TODO: return a cached GrTextureProxy if invoked with the same context + // TODO: if we cache GrTextureProxy, then deleteImageTexture may be invoked on the wrong thread + + if (!context->getGpu() || kOpenGL_GrBackend != context->contextPriv().getBackend()) { + // Check if GrContext is not abandoned and the backend is GL. + return nullptr; + } + + while (GL_NO_ERROR != glGetError()) {} //clear GL errors + + EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer); + EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE }; + EGLDisplay display = eglGetCurrentDisplay(); + EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + clientBuffer, attribs); + if (EGL_NO_IMAGE_KHR == image) { + SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() ); + return nullptr; + } + GrGLuint texID; + glGenTextures(1, &texID); + if (!texID) { + eglDestroyImageKHR(display, image); + return nullptr; + } + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID); + GLenum status = GL_NO_ERROR; + if ((status = glGetError()) != GL_NO_ERROR) { + SkDebugf("glBindTexture failed (%#x)", (int) status); + glDeleteTextures(1, &texID); + eglDestroyImageKHR(display, image); + return nullptr; + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); + if ((status = glGetError()) != GL_NO_ERROR) { + SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status); + glDeleteTextures(1, &texID); + eglDestroyImageKHR(display, image); + return nullptr; + } + context->resetContext(kTextureBinding_GrGLBackendState); + + GrGLTextureInfo textureInfo; + textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES; + textureInfo.fID = texID; + + GrPixelConfig pixelConfig; + switch (getInfo().colorType()) { + case kRGBA_8888_SkColorType: + pixelConfig = kRGBA_8888_GrPixelConfig; + break; + case kRGBA_F16_SkColorType: + pixelConfig = kRGBA_half_GrPixelConfig; + break; + case kRGB_565_SkColorType: + pixelConfig = kRGB_565_GrPixelConfig; + break; + default: + glDeleteTextures(1, &texID); + eglDestroyImageKHR(display, image); + return nullptr; + } + + GrBackendTexture backendTex(getInfo().width(), getInfo().height(), pixelConfig, textureInfo); + if (backendTex.width() <= 0 || backendTex.height() <= 0) { + glDeleteTextures(1, &texID); + eglDestroyImageKHR(display, image); + return nullptr; + } + GrBackendTextureFlags flags = kNone_GrBackendTextureFlag; + sk_sp<GrTexture> tex = context->resourceProvider()->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + flags, + 0, + kAdopt_GrWrapOwnership); + if (!tex) { + glDeleteTextures(1, &texID); + eglDestroyImageKHR(display, image); + return nullptr; + } + tex->setRelease(deleteImageTexture, new BufferCleanupHelper(image, display)); + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex))); + + if (0 == origin.fX && 0 == origin.fY && + info.width() == backendTex.width() && info.height() == backendTex.height()) { + // If the caller wants the entire texture, we're done + return proxy; + } else { + // Otherwise, make a copy of the requested subset. + GrSurfaceDesc desc; + desc.fConfig = proxy->config(); + desc.fWidth = info.width(); + desc.fHeight = info.height(); + desc.fOrigin = proxy->origin(); + desc.fIsMipMapped = proxy->isMipMapped(); + + sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeDeferredSurfaceContext( + desc, SkBackingFit::kExact, SkBudgeted::kYes)); + if (!sContext) { + return nullptr; + } + + SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height()); + if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) { + return nullptr; + } + + return sContext->asTextureProxyRef(); + } +} +#endif + +bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const { + if (nullptr == context) { + return false; //CPU backend is not supported, because hardware buffer can be swizzled + } + // TODO: add Vulkan support + return kOpenGL_GrBackend == context->contextPriv().getBackend(); +} + +#endif //SK_BUILD_FOR_ANDROID_FRAMEWORK diff --git a/src/gpu/GrAHardwareBufferImageGenerator.h b/src/gpu/GrAHardwareBufferImageGenerator.h new file mode 100644 index 0000000000..4a5004b02e --- /dev/null +++ b/src/gpu/GrAHardwareBufferImageGenerator.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef GrAHardwareBufferImageGenerator_DEFINED +#define GrAHardwareBufferImageGenerator_DEFINED + +#include "SkImageGenerator.h" + +#include <android/hardware_buffer.h> + +/** + * GrAHardwareBufferImageGenerator allows to create an SkImage attached to + * an existing android native hardware buffer. A hardware buffer has to be + * created with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE usage, because it is + * bound to an external texture using an EGLImage. The image generator will + * keep a reference to the hardware buffer for its lifetime. A hardware buffer + * can be shared between processes and same buffer can be used in multiple GPU + * contexts. + * To implement certain features like tiling, Skia may copy the texture to + * avoid OpenGL API limitations. + */ +class GrAHardwareBufferImageGenerator : public SkImageGenerator { +public: + static std::unique_ptr<SkImageGenerator> Make(AHardwareBuffer*, SkAlphaType, + sk_sp<SkColorSpace>); + + ~GrAHardwareBufferImageGenerator() override; + +protected: + + bool onIsValid(GrContext*) const override; + +#if SK_SUPPORT_GPU + bool onCanGenerateTexture() const override { return true; } + sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, + const SkIPoint&) override; +#endif + +private: + GrAHardwareBufferImageGenerator(const SkImageInfo&, AHardwareBuffer*, SkAlphaType); + + static void deleteImageTexture(void* ctx); + + AHardwareBuffer* fGraphicBuffer; + SkAlphaType fAlphaType; + + typedef SkImageGenerator INHERITED; +}; +#endif // GrAHardwareBufferImageGenerator_DEFINED diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h index 1a316d1bac..2ada687ab7 100644 --- a/src/gpu/GrBackendTextureImageGenerator.h +++ b/src/gpu/GrBackendTextureImageGenerator.h @@ -19,7 +19,7 @@ public: static std::unique_ptr<SkImageGenerator> Make(sk_sp<GrTexture>, sk_sp<GrSemaphore>, SkAlphaType, sk_sp<SkColorSpace>); - ~GrBackendTextureImageGenerator(); + ~GrBackendTextureImageGenerator() override; protected: // NOTE: We would like to validate that the owning context hasn't been abandoned, but we can't diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index efdd2d7468..b1eac9736c 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -12,6 +12,9 @@ #include "SkAutoPixmapStorage.h" #include "GrBackendSurface.h" #include "GrBackendTextureImageGenerator.h" +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK +#include "GrAHardwareBufferImageGenerator.h" +#endif #include "GrBitmapTextureMaker.h" #include "GrCaps.h" #include "GrContext.h" @@ -502,6 +505,14 @@ sk_sp<SkImage> SkImage::MakeCrossContextFromEncoded(GrContext* context, sk_sp<Sk return SkImage::MakeFromGenerator(std::move(gen)); } +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK +sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at, + sk_sp<SkColorSpace> cs) { + auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, cs); + return SkImage::MakeFromGenerator(std::move(gen)); +} +#endif + sk_sp<SkImage> SkImage::makeNonTextureImage() const { if (!this->isTextureBacked()) { return sk_ref_sp(const_cast<SkImage*>(this)); |