• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
9 
10 #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
11 #define GL_GLEXT_PROTOTYPES
12 #define EGL_EGLEXT_PROTOTYPES
13 #include "GrAHardwareBufferImageGenerator.h"
14 
15 #include <android/hardware_buffer.h>
16 
17 #include "GrBackendSurface.h"
18 #include "GrContext.h"
19 #include "GrContextPriv.h"
20 #include "GrResourceCache.h"
21 #include "GrResourceProvider.h"
22 #include "GrTexture.h"
23 #include "GrTextureProxy.h"
24 #include "SkMessageBus.h"
25 
26 #include <EGL/egl.h>
27 #include <EGL/eglext.h>
28 #include <GLES/gl.h>
29 #include <GLES/glext.h>
30 
31 class BufferCleanupHelper {
32 public:
BufferCleanupHelper(EGLImageKHR image,EGLDisplay display)33     BufferCleanupHelper(EGLImageKHR image, EGLDisplay display)
34         : fImage(image)
35         , fDisplay(display) { }
~BufferCleanupHelper()36     ~BufferCleanupHelper() {
37         eglDestroyImageKHR(fDisplay, fImage);
38     }
39 private:
40     EGLImageKHR fImage;
41     EGLDisplay fDisplay;
42 };
43 
Make(AHardwareBuffer * graphicBuffer,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)44 std::unique_ptr<SkImageGenerator> GrAHardwareBufferImageGenerator::Make(
45         AHardwareBuffer* graphicBuffer, SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
46     AHardwareBuffer_Desc bufferDesc;
47     AHardwareBuffer_describe(graphicBuffer, &bufferDesc);
48     SkColorType colorType;
49     switch (bufferDesc.format) {
50     case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
51         colorType = kRGBA_8888_SkColorType;
52         break;
53     case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
54         colorType = kRGBA_F16_SkColorType;
55         break;
56     case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
57         colorType = kRGB_565_SkColorType;
58         break;
59     default:
60         return nullptr;
61     }
62     SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType,
63                                          alphaType, std::move(colorSpace));
64     return std::unique_ptr<SkImageGenerator>(new GrAHardwareBufferImageGenerator(info, graphicBuffer,
65             alphaType));
66 }
67 
GrAHardwareBufferImageGenerator(const SkImageInfo & info,AHardwareBuffer * graphicBuffer,SkAlphaType alphaType)68 GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info,
69         AHardwareBuffer* graphicBuffer, SkAlphaType alphaType)
70     : INHERITED(info)
71     , fGraphicBuffer(graphicBuffer) {
72     AHardwareBuffer_acquire(fGraphicBuffer);
73 }
74 
~GrAHardwareBufferImageGenerator()75 GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
76     AHardwareBuffer_release(fGraphicBuffer);
77     this->clear();
78 }
79 
clear()80 void GrAHardwareBufferImageGenerator::clear() {
81     if (fOriginalTexture) {
82         // Notify the original cache that it can free the last ref, so it happens on the correct
83         // thread.
84         GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
85         SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
86         fOriginalTexture = nullptr;
87     }
88 }
89 
deleteImageTexture(void * context)90 void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
91     BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
92     delete cleanupHelper;
93 }
94 
95 ///////////////////////////////////////////////////////////////////////////////////////////////////
96 
97 #if SK_SUPPORT_GPU
98 
onGenerateTexture(GrContext * context,const SkImageInfo & info,const SkIPoint & origin,SkTransferFunctionBehavior)99 sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
100         GrContext* context, const SkImageInfo& info, const SkIPoint& origin,
101         SkTransferFunctionBehavior) {
102     auto proxy = this->makeProxy(context);
103     if (!proxy) {
104         return nullptr;
105     }
106 
107     if (0 == origin.fX && 0 == origin.fY &&
108             info.width() == getInfo().width() && info.height() == getInfo().height()) {
109         // If the caller wants the entire texture, we're done
110         return proxy;
111     } else {
112         // Otherwise, make a copy of the requested subset.
113         return GrSurfaceProxy::Copy(context, proxy.get(),
114                                     SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(),
115                                                       info.height()),
116                                     SkBudgeted::kYes);
117     }
118 }
119 #endif
120 
makeProxy(GrContext * context)121 sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) {
122     if (!context->getGpu() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
123         // Check if GrContext is not abandoned and the backend is GL.
124         return nullptr;
125     }
126 
127     // return a cached GrTexture if invoked with the same context
128     if (fOriginalTexture && fOwningContextID == context->uniqueID()) {
129         return GrSurfaceProxy::MakeWrapped(sk_ref_sp(fOriginalTexture));
130     }
131 
132     while (GL_NO_ERROR != glGetError()) {} //clear GL errors
133 
134     EGLClientBuffer  clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer);
135     EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
136                          EGL_NONE };
137     EGLDisplay display = eglGetCurrentDisplay();
138     EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
139                                           clientBuffer, attribs);
140     if (EGL_NO_IMAGE_KHR == image) {
141         SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
142         return nullptr;
143     }
144     GrGLuint texID;
145     glGenTextures(1, &texID);
146     if (!texID) {
147         eglDestroyImageKHR(display, image);
148         return nullptr;
149     }
150     glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID);
151     GLenum status = GL_NO_ERROR;
152     if ((status = glGetError()) != GL_NO_ERROR) {
153         SkDebugf("glBindTexture failed (%#x)", (int) status);
154         glDeleteTextures(1, &texID);
155         eglDestroyImageKHR(display, image);
156         return nullptr;
157     }
158     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
159     if ((status = glGetError()) != GL_NO_ERROR) {
160         SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
161         glDeleteTextures(1, &texID);
162         eglDestroyImageKHR(display, image);
163         return nullptr;
164     }
165     context->resetContext(kTextureBinding_GrGLBackendState);
166 
167     GrGLTextureInfo textureInfo;
168     textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES;
169     textureInfo.fID = texID;
170 
171     GrPixelConfig pixelConfig;
172     switch (getInfo().colorType()) {
173     case kRGBA_8888_SkColorType:
174         pixelConfig = kRGBA_8888_GrPixelConfig;
175         break;
176     case kRGBA_F16_SkColorType:
177         pixelConfig = kRGBA_half_GrPixelConfig;
178         break;
179     case kRGB_565_SkColorType:
180         pixelConfig = kRGB_565_GrPixelConfig;
181         break;
182     default:
183         glDeleteTextures(1, &texID);
184         eglDestroyImageKHR(display, image);
185         return nullptr;
186     }
187 
188     GrBackendTexture backendTex(getInfo().width(), getInfo().height(), pixelConfig, textureInfo);
189     if (backendTex.width() <= 0 || backendTex.height() <= 0) {
190         glDeleteTextures(1, &texID);
191         eglDestroyImageKHR(display, image);
192         return nullptr;
193     }
194     GrBackendTextureFlags flags = kNone_GrBackendTextureFlag;
195     sk_sp<GrTexture> tex = context->resourceProvider()->wrapBackendTexture(backendTex,
196                                                                        kTopLeft_GrSurfaceOrigin,
197                                                                        flags,
198                                                                        0,
199                                                                        kAdopt_GrWrapOwnership);
200     if (!tex) {
201         glDeleteTextures(1, &texID);
202         eglDestroyImageKHR(display, image);
203         return nullptr;
204     }
205     tex->setRelease(deleteImageTexture, new BufferCleanupHelper(image, display));
206 
207     // We fail this assert, if the context has changed. This will be fully handled after
208     // skbug.com/6812 is ready.
209     SkASSERT(!fOriginalTexture);
210 
211     this->clear();
212     fOriginalTexture = tex.get();
213     fOwningContextID = context->uniqueID();
214     // Attach our texture to this context's resource cache. This ensures that deletion will happen
215     // in the correct thread/context. This adds the only ref to the texture that will persist from
216     // this point. To trigger GrTexture deletion a message is sent by generator dtor or by
217     // makeProxy when it is invoked with a different context.
218     //TODO: GrResourceCache should delete GrTexture, when GrContext is deleted. Currently
219     //TODO: SkMessageBus ignores messages for deleted contexts and GrTexture will leak.
220     context->getResourceCache()->insertCrossContextGpuResource(fOriginalTexture);
221     return GrSurfaceProxy::MakeWrapped(std::move(tex));
222 }
223 
onIsValid(GrContext * context) const224 bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
225     if (nullptr == context) {
226         return false; //CPU backend is not supported, because hardware buffer can be swizzled
227     }
228     // TODO: add Vulkan support
229     return kOpenGL_GrBackend == context->contextPriv().getBackend();
230 }
231 
232 #endif //SK_BUILD_FOR_ANDROID_FRAMEWORK
233