blob: 332f8c5e55b016fadc461f5aa3956eb301aef273 [file] [log] [blame]
Stan Iliev7e910df2017-06-02 10:29:21 -04001/*
2 * Copyright 2017 Google Inc.
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#include "SkTypes.h"
8
Derek Sollenberger7a869872017-06-27 15:37:25 -04009
10#if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
Stan Iliev7e910df2017-06-02 10:29:21 -040011#define GL_GLEXT_PROTOTYPES
12#define EGL_EGLEXT_PROTOTYPES
13#include "GrAHardwareBufferImageGenerator.h"
14
Derek Sollenberger7a869872017-06-27 15:37:25 -040015#include <android/hardware_buffer.h>
16
Stan Iliev7e910df2017-06-02 10:29:21 -040017#include "GrBackendSurface.h"
18#include "GrContext.h"
19#include "GrContextPriv.h"
Robert Phillipsadbe1322018-01-17 13:35:46 -050020#include "GrProxyProvider.h"
Stan Ilievdbba55d2017-06-28 13:24:41 -040021#include "GrResourceCache.h"
Robert Phillips00018282017-06-15 15:35:16 -040022#include "GrResourceProvider.h"
Robert Phillips847d4c52017-06-13 18:21:44 -040023#include "GrTexture.h"
Robert Phillipsade9f612017-06-16 07:32:43 -040024#include "GrTextureProxy.h"
Stan Ilievdbba55d2017-06-28 13:24:41 -040025#include "SkMessageBus.h"
Greg Danielf1251112018-08-27 09:55:03 -040026#include "gl/GrGLDefines.h"
27#include "gl/GrGLTypes.h"
Stan Iliev7e910df2017-06-02 10:29:21 -040028
29#include <EGL/egl.h>
30#include <EGL/eglext.h>
31#include <GLES/gl.h>
32#include <GLES/glext.h>
33
Stan Ilievc01b5c72018-08-28 10:18:19 -040034#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
35#define EGL_PROTECTED_CONTENT_EXT 0x32C0
36
37static bool can_import_protected_content_eglimpl() {
38 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
39 const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
40 size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
41 size_t extsLen = strlen(exts);
42 bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
43 bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen+1);
44 bool atEnd = (cropExtLen+1) < extsLen
45 && !strcmp(" " PROT_CONTENT_EXT_STR,
46 exts + extsLen - (cropExtLen+1));
47 bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
48 return equal || atStart || atEnd || inMiddle;
49}
50
51static bool can_import_protected_content(GrContext* context) {
52 if (kOpenGL_GrBackend == context->contextPriv().getBackend()) {
53 // Only compute whether the extension is present once the first time this
54 // function is called.
55 static bool hasIt = can_import_protected_content_eglimpl();
56 return hasIt;
57 }
58 return false;
59}
60
Stan Iliev7e910df2017-06-02 10:29:21 -040061std::unique_ptr<SkImageGenerator> GrAHardwareBufferImageGenerator::Make(
62 AHardwareBuffer* graphicBuffer, SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
63 AHardwareBuffer_Desc bufferDesc;
64 AHardwareBuffer_describe(graphicBuffer, &bufferDesc);
65 SkColorType colorType;
66 switch (bufferDesc.format) {
67 case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
68 colorType = kRGBA_8888_SkColorType;
69 break;
70 case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
71 colorType = kRGBA_F16_SkColorType;
72 break;
73 case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
74 colorType = kRGB_565_SkColorType;
75 break;
76 default:
77 return nullptr;
78 }
79 SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType,
80 alphaType, std::move(colorSpace));
Stan Ilievc01b5c72018-08-28 10:18:19 -040081 bool createProtectedImage = 0 != (bufferDesc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
Stan Iliev7e910df2017-06-02 10:29:21 -040082 return std::unique_ptr<SkImageGenerator>(new GrAHardwareBufferImageGenerator(info, graphicBuffer,
Stan Ilievc01b5c72018-08-28 10:18:19 -040083 alphaType, createProtectedImage));
Stan Iliev7e910df2017-06-02 10:29:21 -040084}
85
86GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info,
Stan Ilievc01b5c72018-08-28 10:18:19 -040087 AHardwareBuffer* hardwareBuffer, SkAlphaType alphaType, bool isProtectedContent)
Stan Iliev7e910df2017-06-02 10:29:21 -040088 : INHERITED(info)
Stan Ilievc01b5c72018-08-28 10:18:19 -040089 , fHardwareBuffer(hardwareBuffer)
90 , fIsProtectedContent(isProtectedContent) {
Greg Danielf1251112018-08-27 09:55:03 -040091 AHardwareBuffer_acquire(fHardwareBuffer);
92}
93
94void GrAHardwareBufferImageGenerator::releaseTextureRef() {
95 // We must release our ref on the proxy before we send the free message to the actual texture so
96 // that we make sure the last ref (if it is owned by this class) is released on the owning
97 // context.
98 fCachedProxy.reset();
99 if (fOwnedTexture) {
100 SkASSERT(fOwningContextID != SK_InvalidGenID);
101 // Notify the original cache that it can free the last ref, so it happens on the correct
102 // thread.
103 GrGpuResourceFreedMessage msg { fOwnedTexture, fOwningContextID };
104 SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
105 fOwnedTexture = nullptr;
106 fOwningContextID = SK_InvalidGenID;
107 }
Stan Iliev7e910df2017-06-02 10:29:21 -0400108}
109
110GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
Greg Danielf1251112018-08-27 09:55:03 -0400111 this->releaseTextureRef();
112 AHardwareBuffer_release(fHardwareBuffer);
Stan Iliev7e910df2017-06-02 10:29:21 -0400113}
114
Stan Iliev7e910df2017-06-02 10:29:21 -0400115///////////////////////////////////////////////////////////////////////////////////////////////////
116
Stan Iliev7e910df2017-06-02 10:29:21 -0400117sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
Brian Osmanc87cfb62018-07-11 09:08:46 -0400118 GrContext* context, const SkImageInfo& info, const SkIPoint& origin, bool willNeedMipMaps) {
Greg Danielf1251112018-08-27 09:55:03 -0400119 this->makeProxy(context);
120 if (!fCachedProxy) {
Stan Ilievdbba55d2017-06-28 13:24:41 -0400121 return nullptr;
122 }
Stan Iliev7e910df2017-06-02 10:29:21 -0400123
Greg Daniel65c7f662017-10-30 13:39:09 -0400124 bool makingASubset = true;
Stan Ilievdbba55d2017-06-28 13:24:41 -0400125 if (0 == origin.fX && 0 == origin.fY &&
Greg Daniel9af948d2018-08-27 09:53:51 -0400126 info.width() == this->getInfo().width() && info.height() == this->getInfo().height()) {
Greg Daniel65c7f662017-10-30 13:39:09 -0400127 makingASubset = false;
Greg Danielf1251112018-08-27 09:55:03 -0400128 if (!willNeedMipMaps || GrMipMapped::kYes == fCachedProxy->mipMapped()) {
Greg Daniel65c7f662017-10-30 13:39:09 -0400129 // If the caller wants the full texture and we have the correct mip support, we're done
Greg Danielf1251112018-08-27 09:55:03 -0400130 return fCachedProxy;
Greg Daniel65c7f662017-10-30 13:39:09 -0400131 }
Stan Ilievdbba55d2017-06-28 13:24:41 -0400132 }
Greg Daniel65c7f662017-10-30 13:39:09 -0400133 // Otherwise, make a copy for the requested subset or for mip maps.
134 SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
135
136 GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
137
Greg Danielf1251112018-08-27 09:55:03 -0400138 sk_sp<GrTextureProxy> texProxy = GrSurfaceProxy::Copy(context, fCachedProxy.get(), mipMapped,
Greg Daniel65c7f662017-10-30 13:39:09 -0400139 subset, SkBudgeted::kYes);
140 if (!makingASubset && texProxy) {
141 // We are in this case if we wanted the full texture, but we will be mip mapping the
142 // texture. Therefore we want to update the cached texture so that we point to the
143 // mipped version instead of the old one.
144 SkASSERT(willNeedMipMaps);
145 SkASSERT(GrMipMapped::kYes == texProxy->mipMapped());
146
147 // The only way we should get into here is if we just made a new texture in makeProxy or
148 // we found a cached texture in the same context. Thus the current and cached contexts
149 // should match.
150 SkASSERT(context->uniqueID() == fOwningContextID);
151
Greg Danielf1251112018-08-27 09:55:03 -0400152 // Since we no longer will be caching the and reusing the texture that actually wraps the
153 // hardware buffer, we can release our refs on it.
154 this->releaseTextureRef();
Greg Daniel65c7f662017-10-30 13:39:09 -0400155
Greg Danielf1251112018-08-27 09:55:03 -0400156 fCachedProxy = texProxy;
Greg Daniel65c7f662017-10-30 13:39:09 -0400157 }
158 return texProxy;
Stan Ilievdbba55d2017-06-28 13:24:41 -0400159}
Stan Ilievdbba55d2017-06-28 13:24:41 -0400160
Greg Daniel9af948d2018-08-27 09:53:51 -0400161class BufferCleanupHelper {
162public:
163 BufferCleanupHelper(EGLImageKHR image, EGLDisplay display)
164 : fImage(image)
165 , fDisplay(display) { }
166 ~BufferCleanupHelper() {
Greg Danielf1251112018-08-27 09:55:03 -0400167 // eglDestroyImageKHR will remove a ref from the AHardwareBuffer
Greg Daniel9af948d2018-08-27 09:53:51 -0400168 eglDestroyImageKHR(fDisplay, fImage);
169 }
170private:
171 EGLImageKHR fImage;
172 EGLDisplay fDisplay;
173};
174
175
176void GrAHardwareBufferImageGenerator::DeleteEGLImage(void* context) {
177 BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
178 delete cleanupHelper;
179}
180
181static GrBackendTexture make_gl_backend_texture(
182 GrContext* context, AHardwareBuffer* hardwareBuffer,
Greg Danielf1251112018-08-27 09:55:03 -0400183 int width, int height, GrPixelConfig config,
Greg Daniel9af948d2018-08-27 09:53:51 -0400184 GrAHardwareBufferImageGenerator::DeleteImageProc* deleteProc,
Stan Ilievc01b5c72018-08-28 10:18:19 -0400185 GrAHardwareBufferImageGenerator::DeleteImageCtx* deleteCtx,
186 bool isProtectedContent) {
Greg Daniel9af948d2018-08-27 09:53:51 -0400187 while (GL_NO_ERROR != glGetError()) {} //clear GL errors
188
Stan Ilievc01b5c72018-08-28 10:18:19 -0400189 EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
Greg Daniel9af948d2018-08-27 09:53:51 -0400190 EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
Stan Ilievc01b5c72018-08-28 10:18:19 -0400191 isProtectedContent ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
192 isProtectedContent ? EGL_TRUE : EGL_NONE,
Greg Daniel9af948d2018-08-27 09:53:51 -0400193 EGL_NONE };
194 EGLDisplay display = eglGetCurrentDisplay();
Greg Danielf1251112018-08-27 09:55:03 -0400195 // eglCreateImageKHR will add a ref to the AHardwareBuffer
Greg Daniel9af948d2018-08-27 09:53:51 -0400196 EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
197 clientBuffer, attribs);
198 if (EGL_NO_IMAGE_KHR == image) {
199 SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
200 return GrBackendTexture();
201 }
202
203 GrGLuint texID;
204 glGenTextures(1, &texID);
205 if (!texID) {
206 eglDestroyImageKHR(display, image);
207 return GrBackendTexture();
208 }
209 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID);
210 GLenum status = GL_NO_ERROR;
211 if ((status = glGetError()) != GL_NO_ERROR) {
212 SkDebugf("glBindTexture failed (%#x)", (int) status);
213 glDeleteTextures(1, &texID);
214 eglDestroyImageKHR(display, image);
215 return GrBackendTexture();
216 }
217 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
218 if ((status = glGetError()) != GL_NO_ERROR) {
219 SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
220 glDeleteTextures(1, &texID);
221 eglDestroyImageKHR(display, image);
222 return GrBackendTexture();
223 }
224 context->resetContext(kTextureBinding_GrGLBackendState);
225
226 GrGLTextureInfo textureInfo;
227 textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES;
228 textureInfo.fID = texID;
Greg Danielf1251112018-08-27 09:55:03 -0400229 switch (config) {
230 case kRGBA_8888_GrPixelConfig:
231 textureInfo.fFormat = GR_GL_RGBA8;
232 break;
233 case kRGBA_half_GrPixelConfig:
234 textureInfo.fFormat = GR_GL_RGBA16F;
235 break;
236 case kRGB_565_GrPixelConfig:
237 textureInfo.fFormat = GR_GL_RGB565;
238 break;
239 default:
240 SkASSERT(false);
241 }
Greg Daniel9af948d2018-08-27 09:53:51 -0400242
243 *deleteProc = GrAHardwareBufferImageGenerator::DeleteEGLImage;
244 *deleteCtx = new BufferCleanupHelper(image, display);
245
246 return GrBackendTexture(width, height, GrMipMapped::kNo, textureInfo);
247}
248
249static GrBackendTexture make_backend_texture(
250 GrContext* context, AHardwareBuffer* hardwareBuffer,
Greg Danielf1251112018-08-27 09:55:03 -0400251 int width, int height, GrPixelConfig config,
Greg Daniel9af948d2018-08-27 09:53:51 -0400252 GrAHardwareBufferImageGenerator::DeleteImageProc* deleteProc,
Stan Ilievc01b5c72018-08-28 10:18:19 -0400253 GrAHardwareBufferImageGenerator::DeleteImageCtx* deleteCtx,
254 bool isProtectedContent) {
Greg Daniel9af948d2018-08-27 09:53:51 -0400255 if (context->abandoned() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
256 // Check if GrContext is not abandoned and the backend is GL.
257 return GrBackendTexture();
258 }
Stan Ilievc01b5c72018-08-28 10:18:19 -0400259 bool createProtectedImage = isProtectedContent && can_import_protected_content(context);
Greg Danielf1251112018-08-27 09:55:03 -0400260 return make_gl_backend_texture(context, hardwareBuffer, width, height, config, deleteProc,
Stan Ilievc01b5c72018-08-28 10:18:19 -0400261 deleteCtx, createProtectedImage);
Greg Daniel9af948d2018-08-27 09:53:51 -0400262}
263
264static void free_backend_texture(GrBackendTexture* backendTexture) {
265 SkASSERT(backendTexture && backendTexture->isValid());
266
267 switch (backendTexture->backend()) {
268 case kOpenGL_GrBackend: {
269 GrGLTextureInfo texInfo;
270 SkAssertResult(backendTexture->getGLTextureInfo(&texInfo));
271 glDeleteTextures(1, &texInfo.fID);
272 return;
273 }
274 case kVulkan_GrBackend: // fall through
275 default:
276 return;
277 }
278}
279
Greg Danielf1251112018-08-27 09:55:03 -0400280void GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) {
Khushalc421ca12018-06-26 14:38:34 -0700281 if (context->abandoned() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
Stan Iliev7e910df2017-06-02 10:29:21 -0400282 // Check if GrContext is not abandoned and the backend is GL.
Greg Danielf1251112018-08-27 09:55:03 -0400283 return;
Stan Iliev7e910df2017-06-02 10:29:21 -0400284 }
285
Greg Danielf1251112018-08-27 09:55:03 -0400286 if (SK_InvalidGenID != fOwningContextID) {
287 SkASSERT(fCachedProxy);
288 if (context->uniqueID() != fOwningContextID) {
289 this->releaseTextureRef();
290 } else {
291 return;
292 }
Stan Ilievdbba55d2017-06-28 13:24:41 -0400293 }
Greg Danielf1251112018-08-27 09:55:03 -0400294 SkASSERT(!fCachedProxy);
295
296 fOwningContextID = context->uniqueID();
Stan Ilievdbba55d2017-06-28 13:24:41 -0400297
Stan Iliev7e910df2017-06-02 10:29:21 -0400298 GrPixelConfig pixelConfig;
Greg Daniel9af948d2018-08-27 09:53:51 -0400299 switch (this->getInfo().colorType()) {
300 case kRGBA_8888_SkColorType:
301 pixelConfig = kRGBA_8888_GrPixelConfig;
302 break;
303 case kRGBA_F16_SkColorType:
304 pixelConfig = kRGBA_half_GrPixelConfig;
305 break;
306 case kRGB_565_SkColorType:
307 pixelConfig = kRGB_565_GrPixelConfig;
308 break;
309 default:
Greg Danielf1251112018-08-27 09:55:03 -0400310 return;
Stan Iliev7e910df2017-06-02 10:29:21 -0400311 }
312
Greg Danielf1251112018-08-27 09:55:03 -0400313 int width = this->getInfo().width();
314 int height = this->getInfo().height();
Greg Daniel9af948d2018-08-27 09:53:51 -0400315
Greg Danielf1251112018-08-27 09:55:03 -0400316 GrSurfaceDesc desc;
317 desc.fWidth = width;
318 desc.fHeight = height;
319 desc.fConfig = pixelConfig;
Greg Daniel9af948d2018-08-27 09:53:51 -0400320
Greg Danielf1251112018-08-27 09:55:03 -0400321 GrTextureType textureType = GrTextureType::k2D;
322 if (context->contextPriv().getBackend() == kOpenGL_GrBackend) {
323 textureType = GrTextureType::kExternal;
Stan Iliev7e910df2017-06-02 10:29:21 -0400324 }
Greg Daniel6a0176b2018-01-30 09:28:44 -0500325
Greg Danielf1251112018-08-27 09:55:03 -0400326 auto proxyProvider = context->contextPriv().proxyProvider();
327
328 AHardwareBuffer* hardwareBuffer = fHardwareBuffer;
329 AHardwareBuffer_acquire(hardwareBuffer);
330
331 GrTexture** ownedTexturePtr = &fOwnedTexture;
Stan Ilievc01b5c72018-08-28 10:18:19 -0400332 const bool isProtectedContent = fIsProtectedContent;
Greg Danielf1251112018-08-27 09:55:03 -0400333
334 fCachedProxy = proxyProvider->createLazyProxy(
Stan Ilievc01b5c72018-08-28 10:18:19 -0400335 [context, hardwareBuffer, width, height, pixelConfig, ownedTexturePtr,
336 isProtectedContent]
Greg Danielf1251112018-08-27 09:55:03 -0400337 (GrResourceProvider* resourceProvider) {
338 if (!resourceProvider) {
339 AHardwareBuffer_release(hardwareBuffer);
340 return sk_sp<GrTexture>();
341 }
342
343 SkASSERT(!*ownedTexturePtr);
344
345 DeleteImageProc deleteImageProc = nullptr;
346 DeleteImageCtx deleteImageCtx = nullptr;
347
348 GrBackendTexture backendTex = make_backend_texture(context, hardwareBuffer,
349 width, height, pixelConfig,
350 &deleteImageProc,
Stan Ilievc01b5c72018-08-28 10:18:19 -0400351 &deleteImageCtx,
352 isProtectedContent);
Greg Danielf1251112018-08-27 09:55:03 -0400353 if (!backendTex.isValid()) {
354 return sk_sp<GrTexture>();
355 }
356 SkASSERT(deleteImageProc && deleteImageCtx);
357
358 backendTex.fConfig = pixelConfig;
359 sk_sp<GrTexture> tex = resourceProvider->wrapBackendTexture(
360 backendTex, kAdopt_GrWrapOwnership);
361 if (!tex) {
362 free_backend_texture(&backendTex);
363 deleteImageProc(deleteImageCtx);
364 return sk_sp<GrTexture>();
365 }
366
367 sk_sp<GrReleaseProcHelper> releaseProcHelper(
Greg Daniel9af948d2018-08-27 09:53:51 -0400368 new GrReleaseProcHelper(deleteImageProc, deleteImageCtx));
Greg Danielf1251112018-08-27 09:55:03 -0400369 tex->setRelease(releaseProcHelper);
Stan Iliev7e910df2017-06-02 10:29:21 -0400370
Greg Danielf1251112018-08-27 09:55:03 -0400371 *ownedTexturePtr = tex.get();
Stan Iliev7e910df2017-06-02 10:29:21 -0400372
Greg Danielf1251112018-08-27 09:55:03 -0400373 // Attach our texture to this context's resource cache. This ensures that deletion
374 // will happen in the correct thread/context. This adds the only ref to the texture
375 // that will persist from this point. To trigger GrTexture deletion a message is
376 // sent by generator dtor or by makeProxy when it is invoked with a different
377 // context.
378 context->contextPriv().getResourceCache()->insertCrossContextGpuResource(tex.get());
379 return tex;
380 },
381 desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, textureType, SkBackingFit::kExact,
382 SkBudgeted::kNo);
383
384
385 if (!fCachedProxy) {
386 AHardwareBuffer_release(hardwareBuffer);
387 return;
388 }
Stan Iliev7e910df2017-06-02 10:29:21 -0400389}
Stan Iliev7e910df2017-06-02 10:29:21 -0400390
391bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
392 if (nullptr == context) {
393 return false; //CPU backend is not supported, because hardware buffer can be swizzled
394 }
395 // TODO: add Vulkan support
396 return kOpenGL_GrBackend == context->contextPriv().getBackend();
397}
398
399#endif //SK_BUILD_FOR_ANDROID_FRAMEWORK