aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Stan Iliev <stani@google.com>2017-06-02 10:29:21 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-06-02 14:51:24 +0000
commit7e910df7f133e80293117bdd069ed25998d10f8c (patch)
tree5bfeea96c4904ba67fa551a76def78f17470ad73
parentaffa6a3da87e9ea85f1d4fe3137b5bccbbc56f92 (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>
-rw-r--r--gn/gn_to_bp.py2
-rw-r--r--gn/gpu.gni2
-rw-r--r--include/core/SkImage.h14
-rw-r--r--src/gpu/GrAHardwareBufferImageGenerator.cpp208
-rw-r--r--src/gpu/GrAHardwareBufferImageGenerator.h52
-rw-r--r--src/gpu/GrBackendTextureImageGenerator.h2
-rw-r--r--src/image/SkImage_Gpu.cpp11
-rw-r--r--tools/viewer/sk_app/android/GLWindowContext_android.cpp24
8 files changed, 302 insertions, 13 deletions
diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py
index 92f9aadf5e..9e58b8006b 100644
--- a/gn/gn_to_bp.py
+++ b/gn/gn_to_bp.py
@@ -34,6 +34,7 @@ tool_shared_libs = [
'libdng_sdk',
'libpiex',
'libcutils',
+ 'libnativewindow',
]
# The ordering here is important: libsfntly needs to come after libskia.
@@ -132,6 +133,7 @@ cc_library {
"libvulkan",
"libz",
"libcutils",
+ "libnativewindow",
],
static_libs: [
"libarect",
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 83e904b916..a0d67bc35e 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -50,6 +50,8 @@ skia_gpu_sources = [
"$_src/gpu/GrBackendSurface.cpp",
"$_src/gpu/GrBackendTextureImageGenerator.cpp",
"$_src/gpu/GrBackendTextureImageGenerator.h",
+ "$_src/gpu/GrAHardwareBufferImageGenerator.cpp",
+ "$_src/gpu/GrAHardwareBufferImageGenerator.h",
"$_src/gpu/GrBitmapTextureMaker.cpp",
"$_src/gpu/GrBitmapTextureMaker.h",
"$_src/gpu/GrBlend.cpp",
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 2b0a164833..bc0597c300 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -29,6 +29,10 @@ class GrContext;
class GrContextThreadSafeProxy;
class GrTexture;
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+struct AHardwareBuffer;
+#endif
+
/**
* SkImage is an abstraction for drawing a rectagle of pixels, though the
* particular type of image could be actually storing its data on the GPU, or
@@ -232,6 +236,16 @@ public:
const SkMatrix*, const SkPaint*, BitDepth,
sk_sp<SkColorSpace>);
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+ /**
+ * Create a new image from the an Android hardware buffer.
+ * The new image takes a reference on the buffer.
+ */
+ static sk_sp<SkImage> MakeFromAHardwareBuffer(AHardwareBuffer*,
+ SkAlphaType = kPremul_SkAlphaType,
+ sk_sp<SkColorSpace> = nullptr);
+#endif
+
///////////////////////////////////////////////////////////////////////////////////////////////
int width() const { return fWidth; }
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));
diff --git a/tools/viewer/sk_app/android/GLWindowContext_android.cpp b/tools/viewer/sk_app/android/GLWindowContext_android.cpp
index b7a1fa8a90..12c12a7218 100644
--- a/tools/viewer/sk_app/android/GLWindowContext_android.cpp
+++ b/tools/viewer/sk_app/android/GLWindowContext_android.cpp
@@ -32,7 +32,7 @@ private:
EGLDisplay fDisplay;
EGLContext fEGLContext;
- EGLSurface fSurface;
+ EGLSurface fSurfaceAndroid;
// For setDisplayParams and resize which call onInitializeContext with null platformData
ANativeWindow* fNativeWindow = nullptr;
@@ -45,7 +45,7 @@ GLWindowContext_android::GLWindowContext_android(ANativeWindow* window,
: INHERITED(params)
, fDisplay(EGL_NO_DISPLAY)
, fEGLContext(EGL_NO_CONTEXT)
- , fSurface(EGL_NO_SURFACE)
+ , fSurfaceAndroid(EGL_NO_SURFACE)
, fNativeWindow(window) {
// any config code here (particularly for msaa)?
@@ -113,14 +113,14 @@ void GLWindowContext_android::onInitializeContext() {
windowAttribs = srgbWindowAttribs;
}
- fSurface = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, windowAttribs);
- if (EGL_NO_SURFACE == fSurface && windowAttribs) {
+ fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, windowAttribs);
+ if (EGL_NO_SURFACE == fSurfaceAndroid && windowAttribs) {
// Try again without sRGB
- fSurface = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, nullptr);
+ fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, nullptr);
}
- SkASSERT(EGL_NO_SURFACE != fSurface);
+ SkASSERT(EGL_NO_SURFACE != fSurfaceAndroid);
- SkAssertResult(eglMakeCurrent(fDisplay, fSurface, fSurface, fEGLContext));
+ SkAssertResult(eglMakeCurrent(fDisplay, fSurfaceAndroid, fSurfaceAndroid, fEGLContext));
// GLWindowContext::initializeContext will call GrGLCreateNativeInterface so we
// won't call it here.
@@ -134,19 +134,19 @@ void GLWindowContext_android::onInitializeContext() {
}
void GLWindowContext_android::onDestroyContext() {
- if (!fDisplay || !fEGLContext || !fSurface) {
+ if (!fDisplay || !fEGLContext || !fSurfaceAndroid) {
return;
}
eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- SkAssertResult(eglDestroySurface(fDisplay, fSurface));
+ SkAssertResult(eglDestroySurface(fDisplay, fSurfaceAndroid));
SkAssertResult(eglDestroyContext(fDisplay, fEGLContext));
fEGLContext = EGL_NO_CONTEXT;
- fSurface = EGL_NO_SURFACE;
+ fSurfaceAndroid = EGL_NO_SURFACE;
}
void GLWindowContext_android::onSwapBuffers() {
- if (fDisplay && fEGLContext && fSurface) {
- eglSwapBuffers(fDisplay, fSurface);
+ if (fDisplay && fEGLContext && fSurfaceAndroid) {
+ eglSwapBuffers(fDisplay, fSurfaceAndroid);
}
}