blob: b05605c4a0e1790c3ee55ff907f30b5a260994d1 [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"
Stan Ilievdbba55d2017-06-28 13:24:41 -040020#include "GrResourceCache.h"
Robert Phillips00018282017-06-15 15:35:16 -040021#include "GrResourceProvider.h"
Robert Phillips847d4c52017-06-13 18:21:44 -040022#include "GrTexture.h"
Robert Phillipsade9f612017-06-16 07:32:43 -040023#include "GrTextureProxy.h"
Stan Ilievdbba55d2017-06-28 13:24:41 -040024#include "SkMessageBus.h"
Stan Iliev7e910df2017-06-02 10:29:21 -040025
26#include <EGL/egl.h>
27#include <EGL/eglext.h>
28#include <GLES/gl.h>
29#include <GLES/glext.h>
30
31class BufferCleanupHelper {
32public:
33 BufferCleanupHelper(EGLImageKHR image, EGLDisplay display)
34 : fImage(image)
35 , fDisplay(display) { }
36 ~BufferCleanupHelper() {
37 eglDestroyImageKHR(fDisplay, fImage);
38 }
39private:
40 EGLImageKHR fImage;
41 EGLDisplay fDisplay;
42};
43
44std::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
68GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info,
69 AHardwareBuffer* graphicBuffer, SkAlphaType alphaType)
70 : INHERITED(info)
Derek Sollenberger7a869872017-06-27 15:37:25 -040071 , fGraphicBuffer(graphicBuffer) {
Stan Iliev7e910df2017-06-02 10:29:21 -040072 AHardwareBuffer_acquire(fGraphicBuffer);
73}
74
75GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
76 AHardwareBuffer_release(fGraphicBuffer);
Stan Ilievdbba55d2017-06-28 13:24:41 -040077 this->clear();
78}
79
80void 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 }
Stan Iliev7e910df2017-06-02 10:29:21 -040088}
89
90void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
Stan Ilievdbba55d2017-06-28 13:24:41 -040091 BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
92 delete cleanupHelper;
Stan Iliev7e910df2017-06-02 10:29:21 -040093}
94
95///////////////////////////////////////////////////////////////////////////////////////////////////
96
97#if SK_SUPPORT_GPU
98
99
100sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
101 GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
Stan Ilievdbba55d2017-06-28 13:24:41 -0400102 auto proxy = this->makeProxy(context);
103 if (!proxy) {
104 return nullptr;
105 }
Stan Iliev7e910df2017-06-02 10:29:21 -0400106
Stan Ilievdbba55d2017-06-28 13:24:41 -0400107 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
121sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) {
Stan Iliev7e910df2017-06-02 10:29:21 -0400122 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
Stan Ilievdbba55d2017-06-28 13:24:41 -0400127 // 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
Stan Iliev7e910df2017-06-02 10:29:21 -0400132 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));
Stan Iliev7e910df2017-06-02 10:29:21 -0400206
Stan Ilievdbba55d2017-06-28 13:24:41 -0400207 // We fail this assert, if the context has changed. This will be fully handled after
208 // skbug.com/6812 is ready.
209 SkASSERT(!fOriginalTexture);
Stan Iliev7e910df2017-06-02 10:29:21 -0400210
Stan Ilievdbba55d2017-06-28 13:24:41 -0400211 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));
Stan Iliev7e910df2017-06-02 10:29:21 -0400222}
Stan Iliev7e910df2017-06-02 10:29:21 -0400223
224bool 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