1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/android/GrAHardwareBufferUtils.h"
9
10 #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
11 #define GL_GLEXT_PROTOTYPES
12 #define EGL_EGLEXT_PROTOTYPES
13
14 #include "include/gpu/ganesh/GrBackendSurface.h"
15 #include "include/gpu/ganesh/GrDirectContext.h"
16 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
17 #include "include/gpu/ganesh/gl/GrGLTypes.h"
18 #include "src/gpu/ganesh/GrDirectContextPriv.h"
19 #include "src/gpu/ganesh/gl/GrGLDefines.h"
20 #include "src/gpu/ganesh/gl/GrGLUtil.h"
21
22 #include <android/hardware_buffer.h>
23 #include <EGL/egl.h>
24 #include <EGL/eglext.h>
25 #include <GLES/gl.h>
26 #include <GLES/glext.h>
27
28 #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
29 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
30
31 namespace GrAHardwareBufferUtils {
32
GetGLBackendFormat(GrDirectContext * dContext,uint32_t bufferFormat,bool requireKnownFormat)33 GrBackendFormat GetGLBackendFormat(GrDirectContext* dContext,
34 uint32_t bufferFormat, bool requireKnownFormat) {
35 GrBackendApi backend = dContext->backend();
36 if (backend != GrBackendApi::kOpenGL) {
37 return GrBackendFormat();
38 }
39 switch (bufferFormat) {
40 //TODO: find out if we can detect, which graphic buffers support GR_GL_TEXTURE_2D
41 case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
42 case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
43 return GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL);
44 case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
45 return GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL);
46 case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
47 return GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL);
48 case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
49 return GrBackendFormats::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_EXTERNAL);
50 case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
51 return GrBackendFormats::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_EXTERNAL);
52 #if __ANDROID_API__ >= 33
53 case AHARDWAREBUFFER_FORMAT_R8_UNORM:
54 return GrBackendFormats::MakeGL(GR_GL_R8, GR_GL_TEXTURE_EXTERNAL);
55 #endif
56 default:
57 if (requireKnownFormat) {
58 return GrBackendFormat();
59 } else {
60 return GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL);
61 }
62 }
63 SkUNREACHABLE;
64 }
65
66 class GLTextureHelper {
67 public:
GLTextureHelper(GrGLuint texID,EGLImageKHR image,EGLDisplay display,GrGLuint texTarget)68 GLTextureHelper(GrGLuint texID, EGLImageKHR image, EGLDisplay display, GrGLuint texTarget)
69 : fTexID(texID)
70 , fImage(image)
71 , fDisplay(display)
72 , fTexTarget(texTarget) { }
~GLTextureHelper()73 ~GLTextureHelper() {
74 glDeleteTextures(1, &fTexID);
75 // eglDestroyImageKHR will remove a ref from the AHardwareBuffer
76 eglDestroyImageKHR(fDisplay, fImage);
77 }
78 void rebind(GrDirectContext*);
79
80 private:
81 GrGLuint fTexID;
82 EGLImageKHR fImage;
83 EGLDisplay fDisplay;
84 GrGLuint fTexTarget;
85 };
86
rebind(GrDirectContext * dContext)87 void GLTextureHelper::rebind(GrDirectContext* dContext) {
88 glBindTexture(fTexTarget, fTexID);
89 GLenum status = GL_NO_ERROR;
90 if ((status = glGetError()) != GL_NO_ERROR) {
91 SkDebugf("glBindTexture(%#x, %d) failed (%#x)", (int) fTexTarget,
92 (int) fTexID, (int) status);
93 return;
94 }
95 glEGLImageTargetTexture2DOES(fTexTarget, fImage);
96 if ((status = glGetError()) != GL_NO_ERROR) {
97 SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
98 return;
99 }
100 dContext->resetContext(kTextureBinding_GrGLBackendState);
101 }
102
delete_gl_texture(void * context)103 void delete_gl_texture(void* context) {
104 GLTextureHelper* cleanupHelper = static_cast<GLTextureHelper*>(context);
105 delete cleanupHelper;
106 }
107
update_gl_texture(void * context,GrDirectContext * dContext)108 void update_gl_texture(void* context, GrDirectContext* dContext) {
109 GLTextureHelper* cleanupHelper = static_cast<GLTextureHelper*>(context);
110 cleanupHelper->rebind(dContext);
111 }
112
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)113 static GrBackendTexture make_gl_backend_texture(
114 GrDirectContext* dContext,
115 AHardwareBuffer* hardwareBuffer,
116 int width, int height,
117 DeleteImageProc* deleteProc,
118 UpdateImageProc* updateProc,
119 TexImageCtx* imageCtx,
120 bool isProtectedContent,
121 const GrBackendFormat& backendFormat,
122 bool isRenderable) {
123 while (GL_NO_ERROR != glGetError()) {} //clear GL errors
124
125 EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
126 EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
127 isProtectedContent ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
128 isProtectedContent ? EGL_TRUE : EGL_NONE,
129 EGL_NONE };
130 EGLDisplay display = eglGetCurrentDisplay();
131 // eglCreateImageKHR will add a ref to the AHardwareBuffer
132 EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
133 clientBuffer, attribs);
134 if (EGL_NO_IMAGE_KHR == image) {
135 SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
136 return GrBackendTexture();
137 }
138
139 GrGLuint texID;
140 glGenTextures(1, &texID);
141 if (!texID) {
142 eglDestroyImageKHR(display, image);
143 return GrBackendTexture();
144 }
145
146 GrGLuint target = isRenderable ? GR_GL_TEXTURE_2D : GR_GL_TEXTURE_EXTERNAL;
147
148 glBindTexture(target, texID);
149 GLenum status = GL_NO_ERROR;
150 if ((status = glGetError()) != GL_NO_ERROR) {
151 SkDebugf("glBindTexture failed (%#x)", (int) status);
152 glDeleteTextures(1, &texID);
153 eglDestroyImageKHR(display, image);
154 return GrBackendTexture();
155 }
156 glEGLImageTargetTexture2DOES(target, image);
157 if ((status = glGetError()) != GL_NO_ERROR) {
158 SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
159 glDeleteTextures(1, &texID);
160 eglDestroyImageKHR(display, image);
161 return GrBackendTexture();
162 }
163 dContext->resetContext(kTextureBinding_GrGLBackendState);
164
165 GrGLTextureInfo textureInfo;
166 textureInfo.fID = texID;
167 SkASSERT(backendFormat.isValid());
168 textureInfo.fTarget = target;
169 textureInfo.fFormat = GrBackendFormats::AsGLFormatEnum(backendFormat);
170 textureInfo.fProtected = skgpu::Protected(isProtectedContent);
171
172 *deleteProc = delete_gl_texture;
173 *updateProc = update_gl_texture;
174 *imageCtx = new GLTextureHelper(texID, image, display, target);
175
176 return GrBackendTextures::MakeGL(width, height, skgpu::Mipmapped::kNo, textureInfo);
177 }
178
can_import_protected_content_eglimpl()179 static bool can_import_protected_content_eglimpl() {
180 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
181 const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
182 size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
183 size_t extsLen = strlen(exts);
184 bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
185 bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen+1);
186 bool atEnd = (cropExtLen+1) < extsLen
187 && !strcmp(" " PROT_CONTENT_EXT_STR,
188 exts + extsLen - (cropExtLen+1));
189 bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
190 return equal || atStart || atEnd || inMiddle;
191 }
192
can_import_protected_content(GrDirectContext * dContext)193 static bool can_import_protected_content(GrDirectContext* dContext) {
194 SkASSERT(GrBackendApi::kOpenGL == dContext->backend());
195 // Only compute whether the extension is present once the first time this
196 // function is called.
197 static bool hasIt = can_import_protected_content_eglimpl();
198 return hasIt;
199 }
200
MakeGLBackendTexture(GrDirectContext * dContext,AHardwareBuffer * hardwareBuffer,int width,int height,DeleteImageProc * deleteProc,UpdateImageProc * updateProc,TexImageCtx * imageCtx,bool isProtectedContent,const GrBackendFormat & backendFormat,bool isRenderable)201 GrBackendTexture MakeGLBackendTexture(GrDirectContext* dContext,
202 AHardwareBuffer* hardwareBuffer,
203 int width, int height,
204 DeleteImageProc* deleteProc,
205 UpdateImageProc* updateProc,
206 TexImageCtx* imageCtx,
207 bool isProtectedContent,
208 const GrBackendFormat& backendFormat,
209 bool isRenderable) {
210 SkASSERT(dContext);
211 if (!dContext || dContext->abandoned()) {
212 return GrBackendTexture();
213 }
214
215 if (GrBackendApi::kOpenGL != dContext->backend()) {
216 return GrBackendTexture();
217 }
218
219 if (isProtectedContent && !can_import_protected_content(dContext)) {
220 return GrBackendTexture();
221 }
222
223 return make_gl_backend_texture(dContext, hardwareBuffer, width, height, deleteProc,
224 updateProc, imageCtx, isProtectedContent, backendFormat,
225 isRenderable);
226 }
227
228 } // namespace GrAHardwareBufferUtils
229
230 #endif
231