Home | History | Annotate | Download | only in gpu
      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