blob: f9208b144ba2bb9ded9e4ef626349dcb5568d3e9 [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) {
Stan Iliev7e910df2017-06-02 10:29:21 -0400150 if (!context->getGpu() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
151 // Check if GrContext is not abandoned and the backend is GL.
152 return nullptr;
153 }
154
Robert Phillipsadbe1322018-01-17 13:35:46 -0500155 auto proxyProvider = context->contextPriv().proxyProvider();
156
Stan Ilievdbba55d2017-06-28 13:24:41 -0400157 // return a cached GrTexture if invoked with the same context
158 if (fOriginalTexture && fOwningContextID == context->uniqueID()) {
Robert Phillipsadbe1322018-01-17 13:35:46 -0500159 return proxyProvider->createWrapped(sk_ref_sp(fOriginalTexture),
160 kTopLeft_GrSurfaceOrigin);
Stan Ilievdbba55d2017-06-28 13:24:41 -0400161 }
162
Stan Iliev7e910df2017-06-02 10:29:21 -0400163 while (GL_NO_ERROR != glGetError()) {} //clear GL errors
164
165 EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer);
166 EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
167 EGL_NONE };
168 EGLDisplay display = eglGetCurrentDisplay();
169 EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
170 clientBuffer, attribs);
171 if (EGL_NO_IMAGE_KHR == image) {
172 SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
173 return nullptr;
174 }
175 GrGLuint texID;
176 glGenTextures(1, &texID);
177 if (!texID) {
178 eglDestroyImageKHR(display, image);
179 return nullptr;
180 }
181 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID);
182 GLenum status = GL_NO_ERROR;
183 if ((status = glGetError()) != GL_NO_ERROR) {
184 SkDebugf("glBindTexture failed (%#x)", (int) status);
185 glDeleteTextures(1, &texID);
186 eglDestroyImageKHR(display, image);
187 return nullptr;
188 }
189 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
190 if ((status = glGetError()) != GL_NO_ERROR) {
191 SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
192 glDeleteTextures(1, &texID);
193 eglDestroyImageKHR(display, image);
194 return nullptr;
195 }
196 context->resetContext(kTextureBinding_GrGLBackendState);
197
198 GrGLTextureInfo textureInfo;
199 textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES;
200 textureInfo.fID = texID;
201
202 GrPixelConfig pixelConfig;
203 switch (getInfo().colorType()) {
204 case kRGBA_8888_SkColorType:
205 pixelConfig = kRGBA_8888_GrPixelConfig;
206 break;
207 case kRGBA_F16_SkColorType:
208 pixelConfig = kRGBA_half_GrPixelConfig;
209 break;
210 case kRGB_565_SkColorType:
211 pixelConfig = kRGB_565_GrPixelConfig;
212 break;
213 default:
214 glDeleteTextures(1, &texID);
215 eglDestroyImageKHR(display, image);
216 return nullptr;
217 }
218
219 GrBackendTexture backendTex(getInfo().width(), getInfo().height(), pixelConfig, textureInfo);
220 if (backendTex.width() <= 0 || backendTex.height() <= 0) {
221 glDeleteTextures(1, &texID);
222 eglDestroyImageKHR(display, image);
223 return nullptr;
224 }
Robert Phillips6be756b2018-01-16 15:07:54 -0500225 sk_sp<GrTexture> tex = context->contextPriv().resourceProvider()->wrapBackendTexture(
226 backendTex, kAdopt_GrWrapOwnership);
Stan Iliev7e910df2017-06-02 10:29:21 -0400227 if (!tex) {
228 glDeleteTextures(1, &texID);
229 eglDestroyImageKHR(display, image);
230 return nullptr;
231 }
232 tex->setRelease(deleteImageTexture, new BufferCleanupHelper(image, display));
Stan Iliev7e910df2017-06-02 10:29:21 -0400233
Stan Ilievdbba55d2017-06-28 13:24:41 -0400234 // We fail this assert, if the context has changed. This will be fully handled after
235 // skbug.com/6812 is ready.
236 SkASSERT(!fOriginalTexture);
Stan Iliev7e910df2017-06-02 10:29:21 -0400237
Stan Ilievdbba55d2017-06-28 13:24:41 -0400238 this->clear();
239 fOriginalTexture = tex.get();
240 fOwningContextID = context->uniqueID();
241 // Attach our texture to this context's resource cache. This ensures that deletion will happen
242 // in the correct thread/context. This adds the only ref to the texture that will persist from
243 // this point. To trigger GrTexture deletion a message is sent by generator dtor or by
244 // makeProxy when it is invoked with a different context.
245 //TODO: GrResourceCache should delete GrTexture, when GrContext is deleted. Currently
246 //TODO: SkMessageBus ignores messages for deleted contexts and GrTexture will leak.
Robert Phillips6be756b2018-01-16 15:07:54 -0500247 context->contextPriv().getResourceCache()->insertCrossContextGpuResource(fOriginalTexture);
Robert Phillipsadbe1322018-01-17 13:35:46 -0500248 return proxyProvider->createWrapped(std::move(tex), kTopLeft_GrSurfaceOrigin);
Stan Iliev7e910df2017-06-02 10:29:21 -0400249}
Stan Iliev7e910df2017-06-02 10:29:21 -0400250
251bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
252 if (nullptr == context) {
253 return false; //CPU backend is not supported, because hardware buffer can be swizzled
254 }
255 // TODO: add Vulkan support
256 return kOpenGL_GrBackend == context->contextPriv().getBackend();
257}
258
259#endif //SK_BUILD_FOR_ANDROID_FRAMEWORK