blob: be6a8d099a75dd1bc9c84adda285779a02bebb81 [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"
Stan Iliev7e910df2017-06-02 10:29:21 -040026
27#include <EGL/egl.h>
28#include <EGL/eglext.h>
29#include <GLES/gl.h>
30#include <GLES/glext.h>
31
32class BufferCleanupHelper {
33public:
34 BufferCleanupHelper(EGLImageKHR image, EGLDisplay display)
35 : fImage(image)
36 , fDisplay(display) { }
37 ~BufferCleanupHelper() {
38 eglDestroyImageKHR(fDisplay, fImage);
39 }
40private:
41 EGLImageKHR fImage;
42 EGLDisplay fDisplay;
43};
44
45std::unique_ptr<SkImageGenerator> GrAHardwareBufferImageGenerator::Make(
46 AHardwareBuffer* graphicBuffer, SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
47 AHardwareBuffer_Desc bufferDesc;
48 AHardwareBuffer_describe(graphicBuffer, &bufferDesc);
49 SkColorType colorType;
50 switch (bufferDesc.format) {
51 case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
52 colorType = kRGBA_8888_SkColorType;
53 break;
54 case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
55 colorType = kRGBA_F16_SkColorType;
56 break;
57 case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
58 colorType = kRGB_565_SkColorType;
59 break;
60 default:
61 return nullptr;
62 }
63 SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType,
64 alphaType, std::move(colorSpace));
65 return std::unique_ptr<SkImageGenerator>(new GrAHardwareBufferImageGenerator(info, graphicBuffer,
66 alphaType));
67}
68
69GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info,
70 AHardwareBuffer* graphicBuffer, SkAlphaType alphaType)
71 : INHERITED(info)
Derek Sollenberger7a869872017-06-27 15:37:25 -040072 , fGraphicBuffer(graphicBuffer) {
Stan Iliev7e910df2017-06-02 10:29:21 -040073 AHardwareBuffer_acquire(fGraphicBuffer);
74}
75
76GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
77 AHardwareBuffer_release(fGraphicBuffer);
Stan Ilievdbba55d2017-06-28 13:24:41 -040078 this->clear();
79}
80
81void GrAHardwareBufferImageGenerator::clear() {
82 if (fOriginalTexture) {
83 // Notify the original cache that it can free the last ref, so it happens on the correct
84 // thread.
85 GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
86 SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
87 fOriginalTexture = nullptr;
88 }
Stan Iliev7e910df2017-06-02 10:29:21 -040089}
90
91void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
Stan Ilievdbba55d2017-06-28 13:24:41 -040092 BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
93 delete cleanupHelper;
Stan Iliev7e910df2017-06-02 10:29:21 -040094}
95
96///////////////////////////////////////////////////////////////////////////////////////////////////
97
98#if SK_SUPPORT_GPU
99
Stan Iliev7e910df2017-06-02 10:29:21 -0400100sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
Christopher Cameron77e96662017-07-08 01:47:47 -0700101 GrContext* context, const SkImageInfo& info, const SkIPoint& origin,
Greg Danielf88c12e2017-10-09 09:57:35 -0400102 SkTransferFunctionBehavior, bool willNeedMipMaps) {
Stan Ilievdbba55d2017-06-28 13:24:41 -0400103 auto proxy = this->makeProxy(context);
104 if (!proxy) {
105 return nullptr;
106 }
Stan Iliev7e910df2017-06-02 10:29:21 -0400107
Greg Daniel65c7f662017-10-30 13:39:09 -0400108 bool makingASubset = true;
Stan Ilievdbba55d2017-06-28 13:24:41 -0400109 if (0 == origin.fX && 0 == origin.fY &&
110 info.width() == getInfo().width() && info.height() == getInfo().height()) {
Greg Daniel65c7f662017-10-30 13:39:09 -0400111 makingASubset = false;
112 if (!willNeedMipMaps || GrMipMapped::kYes == proxy->mipMapped()) {
113 // If the caller wants the full texture and we have the correct mip support, we're done
114 return proxy;
115 }
Stan Ilievdbba55d2017-06-28 13:24:41 -0400116 }
Greg Daniel65c7f662017-10-30 13:39:09 -0400117 // Otherwise, make a copy for the requested subset or for mip maps.
118 SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
119
120 GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
121
122 sk_sp<GrTextureProxy> texProxy = GrSurfaceProxy::Copy(context, proxy.get(), mipMapped,
123 subset, SkBudgeted::kYes);
124 if (!makingASubset && texProxy) {
125 // We are in this case if we wanted the full texture, but we will be mip mapping the
126 // texture. Therefore we want to update the cached texture so that we point to the
127 // mipped version instead of the old one.
128 SkASSERT(willNeedMipMaps);
129 SkASSERT(GrMipMapped::kYes == texProxy->mipMapped());
130
131 // The only way we should get into here is if we just made a new texture in makeProxy or
132 // we found a cached texture in the same context. Thus the current and cached contexts
133 // should match.
134 SkASSERT(context->uniqueID() == fOwningContextID);
135
136 // Clear out the old cached texture.
137 this->clear();
138
139 // We need to get the actual GrTexture so force instantiation of the GrTextureProxy
Robert Phillips6be756b2018-01-16 15:07:54 -0500140 texProxy->instantiate(context->contextPriv().resourceProvider());
Greg Daniel65c7f662017-10-30 13:39:09 -0400141 GrTexture* texture = texProxy->priv().peekTexture();
142 SkASSERT(texture);
143 fOriginalTexture = texture;
144 }
145 return texProxy;
Stan Ilievdbba55d2017-06-28 13:24:41 -0400146}
147#endif
148
149sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) {
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500150 if (context->contextPriv().abandoned() ||
151 kOpenGL_GrBackend != context->contextPriv().getBackend()) {
Stan Iliev7e910df2017-06-02 10:29:21 -0400152 // Check if GrContext is not abandoned and the backend is GL.
153 return nullptr;
154 }
155
Robert Phillipsadbe1322018-01-17 13:35:46 -0500156 auto proxyProvider = context->contextPriv().proxyProvider();
157
Stan Ilievdbba55d2017-06-28 13:24:41 -0400158 // return a cached GrTexture if invoked with the same context
159 if (fOriginalTexture && fOwningContextID == context->uniqueID()) {
Robert Phillipsadbe1322018-01-17 13:35:46 -0500160 return proxyProvider->createWrapped(sk_ref_sp(fOriginalTexture),
161 kTopLeft_GrSurfaceOrigin);
Stan Ilievdbba55d2017-06-28 13:24:41 -0400162 }
163
Stan Iliev7e910df2017-06-02 10:29:21 -0400164 while (GL_NO_ERROR != glGetError()) {} //clear GL errors
165
166 EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer);
167 EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
168 EGL_NONE };
169 EGLDisplay display = eglGetCurrentDisplay();
170 EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
171 clientBuffer, attribs);
172 if (EGL_NO_IMAGE_KHR == image) {
173 SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
174 return nullptr;
175 }
176 GrGLuint texID;
177 glGenTextures(1, &texID);
178 if (!texID) {
179 eglDestroyImageKHR(display, image);
180 return nullptr;
181 }
182 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID);
183 GLenum status = GL_NO_ERROR;
184 if ((status = glGetError()) != GL_NO_ERROR) {
185 SkDebugf("glBindTexture failed (%#x)", (int) status);
186 glDeleteTextures(1, &texID);
187 eglDestroyImageKHR(display, image);
188 return nullptr;
189 }
190 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
191 if ((status = glGetError()) != GL_NO_ERROR) {
192 SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
193 glDeleteTextures(1, &texID);
194 eglDestroyImageKHR(display, image);
195 return nullptr;
196 }
197 context->resetContext(kTextureBinding_GrGLBackendState);
198
199 GrGLTextureInfo textureInfo;
200 textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES;
201 textureInfo.fID = texID;
202
203 GrPixelConfig pixelConfig;
204 switch (getInfo().colorType()) {
205 case kRGBA_8888_SkColorType:
206 pixelConfig = kRGBA_8888_GrPixelConfig;
207 break;
208 case kRGBA_F16_SkColorType:
209 pixelConfig = kRGBA_half_GrPixelConfig;
210 break;
211 case kRGB_565_SkColorType:
212 pixelConfig = kRGB_565_GrPixelConfig;
213 break;
214 default:
215 glDeleteTextures(1, &texID);
216 eglDestroyImageKHR(display, image);
217 return nullptr;
218 }
219
220 GrBackendTexture backendTex(getInfo().width(), getInfo().height(), pixelConfig, textureInfo);
221 if (backendTex.width() <= 0 || backendTex.height() <= 0) {
222 glDeleteTextures(1, &texID);
223 eglDestroyImageKHR(display, image);
224 return nullptr;
225 }
Robert Phillips6be756b2018-01-16 15:07:54 -0500226 sk_sp<GrTexture> tex = context->contextPriv().resourceProvider()->wrapBackendTexture(
227 backendTex, kAdopt_GrWrapOwnership);
Stan Iliev7e910df2017-06-02 10:29:21 -0400228 if (!tex) {
229 glDeleteTextures(1, &texID);
230 eglDestroyImageKHR(display, image);
231 return nullptr;
232 }
Greg Daniel6a0176b2018-01-30 09:28:44 -0500233 sk_sp<GrReleaseProcHelper> releaseHelper(
234 new GrReleaseProcHelper(deleteImageTexture, new BufferCleanupHelper(image, display)));
235
236 tex->setRelease(std::move(releaseHelper));
Stan Iliev7e910df2017-06-02 10:29:21 -0400237
Stan Ilievdbba55d2017-06-28 13:24:41 -0400238 // We fail this assert, if the context has changed. This will be fully handled after
239 // skbug.com/6812 is ready.
240 SkASSERT(!fOriginalTexture);
Stan Iliev7e910df2017-06-02 10:29:21 -0400241
Stan Ilievdbba55d2017-06-28 13:24:41 -0400242 this->clear();
243 fOriginalTexture = tex.get();
244 fOwningContextID = context->uniqueID();
245 // Attach our texture to this context's resource cache. This ensures that deletion will happen
246 // in the correct thread/context. This adds the only ref to the texture that will persist from
247 // this point. To trigger GrTexture deletion a message is sent by generator dtor or by
248 // makeProxy when it is invoked with a different context.
249 //TODO: GrResourceCache should delete GrTexture, when GrContext is deleted. Currently
250 //TODO: SkMessageBus ignores messages for deleted contexts and GrTexture will leak.
Robert Phillips6be756b2018-01-16 15:07:54 -0500251 context->contextPriv().getResourceCache()->insertCrossContextGpuResource(fOriginalTexture);
Robert Phillipsadbe1322018-01-17 13:35:46 -0500252 return proxyProvider->createWrapped(std::move(tex), kTopLeft_GrSurfaceOrigin);
Stan Iliev7e910df2017-06-02 10:29:21 -0400253}
Stan Iliev7e910df2017-06-02 10:29:21 -0400254
255bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
256 if (nullptr == context) {
257 return false; //CPU backend is not supported, because hardware buffer can be swizzled
258 }
259 // TODO: add Vulkan support
260 return kOpenGL_GrBackend == context->contextPriv().getBackend();
261}
262
263#endif //SK_BUILD_FOR_ANDROID_FRAMEWORK