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: 33 BufferCleanupHelper(EGLImageKHR image, EGLDisplay display) 34 : fImage(image) 35 , fDisplay(display) { } 36 ~BufferCleanupHelper() { 37 eglDestroyImageKHR(fDisplay, fImage); 38 } 39 private: 40 EGLImageKHR fImage; 41 EGLDisplay fDisplay; 42 }; 43 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 68 GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info, 69 AHardwareBuffer* graphicBuffer, SkAlphaType alphaType) 70 : INHERITED(info) 71 , fGraphicBuffer(graphicBuffer) { 72 AHardwareBuffer_acquire(fGraphicBuffer); 73 } 74 75 GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() { 76 AHardwareBuffer_release(fGraphicBuffer); 77 this->clear(); 78 } 79 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 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 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 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 224 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