-
Notifications
You must be signed in to change notification settings - Fork 483
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Android hardware buffer (#2315)
- Loading branch information
1 parent
1925223
commit fc7bb5f
Showing
6 changed files
with
316 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
245 changes: 245 additions & 0 deletions
245
package/android/cpp/rnskia-android/GrAHardwareBufferUtils.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
/* | ||
* Copyright 2023 Google LLC | ||
* | ||
* Use of this source code is governed by a BSD-style license that can be | ||
* found in the LICENSE file. | ||
*/ | ||
|
||
#include "GrAHardwareBufferUtils.h" | ||
#if __ANDROID_API__ >= 26 | ||
|
||
#define GL_GLEXT_PROTOTYPES | ||
#define EGL_EGLEXT_PROTOTYPES | ||
|
||
#include "include/gpu/GrBackendSurface.h" | ||
#include "include/gpu/GrDirectContext.h" | ||
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h" | ||
#include "include/gpu/gl/GrGLTypes.h" | ||
#include "src/gpu/ganesh/gl/GrGLDefines.h" | ||
// #include "src/gpu/ganesh/GrDirectContextPriv.h" | ||
// #include "src/gpu/ganesh/gl/GrGLUtil.h" | ||
|
||
#include <EGL/egl.h> | ||
#include <EGL/eglext.h> | ||
#include <GLES/gl.h> | ||
#include <GLES/glext.h> | ||
#include <android/hardware_buffer.h> | ||
|
||
#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" | ||
#define EGL_PROTECTED_CONTENT_EXT 0x32C0 | ||
|
||
namespace RNSkia { | ||
|
||
typedef EGLClientBuffer (*EGLGetNativeClientBufferANDROIDProc)( | ||
const struct AHardwareBuffer *); | ||
typedef EGLImageKHR (*EGLCreateImageKHRProc)(EGLDisplay, EGLContext, EGLenum, | ||
EGLClientBuffer, const EGLint *); | ||
typedef void (*EGLImageTargetTexture2DOESProc)(EGLenum, void *); | ||
|
||
GrBackendFormat GetGLBackendFormat(GrDirectContext *dContext, | ||
uint32_t bufferFormat, | ||
bool requireKnownFormat) { | ||
GrBackendApi backend = dContext->backend(); | ||
if (backend != GrBackendApi::kOpenGL) { | ||
return GrBackendFormat(); | ||
} | ||
switch (bufferFormat) { | ||
// TODO: find out if we can detect, which graphic buffers support | ||
// GR_GL_TEXTURE_2D | ||
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: | ||
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: | ||
return GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); | ||
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: | ||
return GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL); | ||
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: | ||
return GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL); | ||
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: | ||
return GrBackendFormats::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_EXTERNAL); | ||
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: | ||
return GrBackendFormats::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_EXTERNAL); | ||
#if __ANDROID_API__ >= 33 | ||
case AHARDWAREBUFFER_FORMAT_R8_UNORM: | ||
return GrBackendFormats::MakeGL(GR_GL_R8, GR_GL_TEXTURE_EXTERNAL); | ||
#endif | ||
default: | ||
if (requireKnownFormat) { | ||
return GrBackendFormat(); | ||
} else { | ||
return GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); | ||
} | ||
} | ||
SkUNREACHABLE; | ||
} | ||
|
||
class GLTextureHelper { | ||
public: | ||
GLTextureHelper(GrGLuint texID, EGLImageKHR image, EGLDisplay display, | ||
GrGLuint texTarget) | ||
: fTexID(texID), fImage(image), fDisplay(display), fTexTarget(texTarget) { | ||
} | ||
~GLTextureHelper() { | ||
glDeleteTextures(1, &fTexID); | ||
// eglDestroyImageKHR will remove a ref from the AHardwareBuffer | ||
eglDestroyImageKHR(fDisplay, fImage); | ||
} | ||
void rebind(GrDirectContext *); | ||
|
||
private: | ||
GrGLuint fTexID; | ||
EGLImageKHR fImage; | ||
EGLDisplay fDisplay; | ||
GrGLuint fTexTarget; | ||
}; | ||
|
||
void GLTextureHelper::rebind(GrDirectContext *dContext) { | ||
glBindTexture(fTexTarget, fTexID); | ||
GLenum status = GL_NO_ERROR; | ||
if ((status = glGetError()) != GL_NO_ERROR) { | ||
SkDebugf("glBindTexture(%#x, %d) failed (%#x)", | ||
static_cast<int>(fTexTarget), static_cast<int>(fTexID), | ||
static_cast<int>(status)); | ||
return; | ||
} | ||
glEGLImageTargetTexture2DOES(fTexTarget, fImage); | ||
if ((status = glGetError()) != GL_NO_ERROR) { | ||
SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", | ||
static_cast<int>(status)); | ||
return; | ||
} | ||
dContext->resetContext(kTextureBinding_GrGLBackendState); | ||
} | ||
|
||
void delete_gl_texture(void *context) { | ||
GLTextureHelper *cleanupHelper = static_cast<GLTextureHelper *>(context); | ||
delete cleanupHelper; | ||
} | ||
|
||
void update_gl_texture(void *context, GrDirectContext *dContext) { | ||
GLTextureHelper *cleanupHelper = static_cast<GLTextureHelper *>(context); | ||
cleanupHelper->rebind(dContext); | ||
} | ||
|
||
static GrBackendTexture make_gl_backend_texture( | ||
GrDirectContext *dContext, AHardwareBuffer *hardwareBuffer, int width, | ||
int height, DeleteImageProc *deleteProc, UpdateImageProc *updateProc, | ||
TexImageCtx *imageCtx, bool isProtectedContent, | ||
const GrBackendFormat &backendFormat, bool isRenderable) { | ||
while (GL_NO_ERROR != glGetError()) { | ||
} // clear GL errors | ||
|
||
EGLGetNativeClientBufferANDROIDProc eglGetNativeClientBufferANDROID = | ||
(EGLGetNativeClientBufferANDROIDProc)eglGetProcAddress( | ||
"eglGetNativeClientBufferANDROID"); | ||
if (!eglGetNativeClientBufferANDROID) { | ||
RNSkLogger::logToConsole( | ||
"Failed to get the eglGetNativeClientBufferAndroid proc"); | ||
return GrBackendTexture(); | ||
} | ||
|
||
EGLClientBuffer clientBuffer = | ||
eglGetNativeClientBufferANDROID(hardwareBuffer); | ||
EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, | ||
isProtectedContent ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, | ||
isProtectedContent ? EGL_TRUE : EGL_NONE, EGL_NONE}; | ||
EGLDisplay display = eglGetCurrentDisplay(); | ||
// eglCreateImageKHR will add a ref to the AHardwareBuffer | ||
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)", | ||
static_cast<int>(eglGetError())); | ||
return GrBackendTexture(); | ||
} | ||
|
||
GrGLuint texID; | ||
glGenTextures(1, &texID); | ||
if (!texID) { | ||
eglDestroyImageKHR(display, image); | ||
return GrBackendTexture(); | ||
} | ||
|
||
GrGLuint target = isRenderable ? GR_GL_TEXTURE_2D : GR_GL_TEXTURE_EXTERNAL; | ||
|
||
glBindTexture(target, texID); | ||
GLenum status = GL_NO_ERROR; | ||
if ((status = glGetError()) != GL_NO_ERROR) { | ||
SkDebugf("glBindTexture failed (%#x)", static_cast<int>(status)); | ||
glDeleteTextures(1, &texID); | ||
eglDestroyImageKHR(display, image); | ||
return GrBackendTexture(); | ||
} | ||
glEGLImageTargetTexture2DOES(target, image); | ||
if ((status = glGetError()) != GL_NO_ERROR) { | ||
SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", | ||
static_cast<int>(status)); | ||
glDeleteTextures(1, &texID); | ||
eglDestroyImageKHR(display, image); | ||
return GrBackendTexture(); | ||
} | ||
dContext->resetContext(kTextureBinding_GrGLBackendState); | ||
|
||
GrGLTextureInfo textureInfo; | ||
textureInfo.fID = texID; | ||
SkASSERT(backendFormat.isValid()); | ||
textureInfo.fTarget = target; | ||
textureInfo.fFormat = GrBackendFormats::AsGLFormatEnum(backendFormat); | ||
textureInfo.fProtected = skgpu::Protected(isProtectedContent); | ||
|
||
*deleteProc = delete_gl_texture; | ||
*updateProc = update_gl_texture; | ||
*imageCtx = new GLTextureHelper(texID, image, display, target); | ||
|
||
return GrBackendTextures::MakeGL(width, height, skgpu::Mipmapped::kNo, | ||
textureInfo); | ||
} | ||
|
||
static bool can_import_protected_content_eglimpl() { | ||
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); | ||
const char *exts = eglQueryString(dpy, EGL_EXTENSIONS); | ||
size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); | ||
size_t extsLen = strlen(exts); | ||
bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); | ||
bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); | ||
bool atEnd = | ||
(cropExtLen + 1) < extsLen && | ||
!strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); | ||
bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); | ||
return equal || atStart || atEnd || inMiddle; | ||
} | ||
|
||
static bool can_import_protected_content(GrDirectContext *dContext) { | ||
SkASSERT(GrBackendApi::kOpenGL == dContext->backend()); | ||
// Only compute whether the extension is present once the first time this | ||
// function is called. | ||
static bool hasIt = can_import_protected_content_eglimpl(); | ||
return hasIt; | ||
} | ||
|
||
GrBackendTexture | ||
MakeGLBackendTexture(GrDirectContext *dContext, AHardwareBuffer *hardwareBuffer, | ||
int width, int height, DeleteImageProc *deleteProc, | ||
UpdateImageProc *updateProc, TexImageCtx *imageCtx, | ||
bool isProtectedContent, | ||
const GrBackendFormat &backendFormat, bool isRenderable) { | ||
SkASSERT(dContext); | ||
if (!dContext || dContext->abandoned()) { | ||
return GrBackendTexture(); | ||
} | ||
|
||
if (GrBackendApi::kOpenGL != dContext->backend()) { | ||
return GrBackendTexture(); | ||
} | ||
|
||
if (isProtectedContent && !can_import_protected_content(dContext)) { | ||
return GrBackendTexture(); | ||
} | ||
|
||
return make_gl_backend_texture( | ||
dContext, hardwareBuffer, width, height, deleteProc, updateProc, imageCtx, | ||
isProtectedContent, backendFormat, isRenderable); | ||
} | ||
|
||
} // namespace RNSkia | ||
|
||
#endif |
33 changes: 33 additions & 0 deletions
33
package/android/cpp/rnskia-android/GrAHardwareBufferUtils.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#pragma once | ||
|
||
#include "include/core/SkTypes.h" | ||
|
||
#include "RNSkLog.h" | ||
|
||
#if __ANDROID_API__ >= 26 | ||
|
||
#include "include/gpu/GrBackendSurface.h" | ||
#include "include/gpu/GrTypes.h" | ||
|
||
class GrDirectContext; | ||
|
||
extern "C" { | ||
typedef struct AHardwareBuffer AHardwareBuffer; | ||
} | ||
|
||
namespace RNSkia { | ||
|
||
typedef void *TexImageCtx; | ||
typedef void (*DeleteImageProc)(TexImageCtx); | ||
typedef void (*UpdateImageProc)(TexImageCtx, GrDirectContext *); | ||
|
||
GrBackendTexture | ||
MakeGLBackendTexture(GrDirectContext *dContext, AHardwareBuffer *hardwareBuffer, | ||
int width, int height, DeleteImageProc *deleteProc, | ||
UpdateImageProc *updateProc, TexImageCtx *imageCtx, | ||
bool isProtectedContent, | ||
const GrBackendFormat &backendFormat, bool isRenderable); | ||
|
||
} // namespace RNSkia | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters