Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "OpenGLReadback.h"
     18 
     19 #include "Caches.h"
     20 #include "GlLayer.h"
     21 #include "GlopBuilder.h"
     22 #include "Image.h"
     23 #include "renderstate/RenderState.h"
     24 #include "renderthread/EglManager.h"
     25 #include "utils/GLUtils.h"
     26 
     27 #include <GLES2/gl2.h>
     28 #include <gui/Surface.h>
     29 #include <ui/Fence.h>
     30 #include <ui/GraphicBuffer.h>
     31 
     32 namespace android {
     33 namespace uirenderer {
     34 
     35 CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
     36                                            SkBitmap* bitmap) {
     37     ATRACE_CALL();
     38     // Setup the source
     39     sp<GraphicBuffer> sourceBuffer;
     40     sp<Fence> sourceFence;
     41     Matrix4 texTransform;
     42     status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
     43     texTransform.invalidateType();
     44     if (err != NO_ERROR) {
     45         ALOGW("Failed to get last queued buffer, error = %d", err);
     46         return CopyResult::UnknownError;
     47     }
     48     if (!sourceBuffer.get()) {
     49         ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
     50         return CopyResult::SourceEmpty;
     51     }
     52     if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
     53         ALOGW("Surface is protected, unable to copy from it");
     54         return CopyResult::SourceInvalid;
     55     }
     56     err = sourceFence->wait(500 /* ms */);
     57     if (err != NO_ERROR) {
     58         ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
     59         return CopyResult::Timeout;
     60     }
     61 
     62     return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
     63 }
     64 
     65 CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
     66                                                  Matrix4& texTransform, const Rect& srcRect,
     67                                                  SkBitmap* bitmap) {
     68     mRenderThread.eglManager().initialize();
     69     // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
     70     // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
     71     // to be able to properly sample from the buffer.
     72 
     73     // Create the EGLImage object that maps the GraphicBuffer
     74     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     75     EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
     76     EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
     77 
     78     EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
     79                                                 clientBuffer, attrs);
     80 
     81     if (sourceImage == EGL_NO_IMAGE_KHR) {
     82         ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
     83         return CopyResult::UnknownError;
     84     }
     85 
     86     uint32_t width = graphicBuffer->getWidth();
     87     uint32_t height = graphicBuffer->getHeight();
     88     CopyResult copyResult =
     89             copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap);
     90 
     91     // All we're flushing & finishing is the deletion of the texture since
     92     // copyImageInto already did a major flush & finish as an implicit
     93     // part of glReadPixels, so this shouldn't pose any major stalls.
     94     glFinish();
     95     eglDestroyImageKHR(display, sourceImage);
     96     return copyResult;
     97 }
     98 
     99 CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
    100     Rect srcRect;
    101     Matrix4 transform;
    102     transform.loadScale(1, -1, 1);
    103     transform.translate(0, -1);
    104     return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
    105 }
    106 
    107 static float sFlipVInit[16] = {
    108         1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
    109 };
    110 
    111 static const Matrix4 sFlipV(sFlipVInit);
    112 
    113 ////////////////////////////////////////////////////////////////////////////////
    114 ////////////////////////////////////////////////////////////////////////////////
    115 
    116 inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, Texture& sourceTexture,
    117                                   const Matrix4& texTransform, const Rect& srcRect,
    118                                   SkBitmap* bitmap) {
    119     int destWidth = bitmap->width();
    120     int destHeight = bitmap->height();
    121     if (destWidth > caches.maxTextureSize || destHeight > caches.maxTextureSize) {
    122         ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", destWidth,
    123               destHeight, caches.maxTextureSize);
    124         return CopyResult::DestinationInvalid;
    125     }
    126 
    127     if (bitmap->colorType() == kRGBA_F16_SkColorType &&
    128         !caches.extensions().hasRenderableFloatTextures()) {
    129         ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
    130         return CopyResult::DestinationInvalid;
    131     }
    132 
    133     GLuint fbo = renderState.createFramebuffer();
    134     if (!fbo) {
    135         ALOGW("Could not obtain an FBO");
    136         return CopyResult::UnknownError;
    137     }
    138 
    139     GLuint texture;
    140 
    141     GLenum format;
    142     GLenum internalFormat;
    143     GLenum type;
    144 
    145     switch (bitmap->colorType()) {
    146         case kAlpha_8_SkColorType:
    147             format = GL_ALPHA;
    148             internalFormat = GL_ALPHA;
    149             type = GL_UNSIGNED_BYTE;
    150             break;
    151         case kRGB_565_SkColorType:
    152             format = GL_RGB;
    153             internalFormat = GL_RGB;
    154             type = GL_UNSIGNED_SHORT_5_6_5;
    155             break;
    156         case kARGB_4444_SkColorType:
    157             format = GL_RGBA;
    158             internalFormat = GL_RGBA;
    159             type = GL_UNSIGNED_SHORT_4_4_4_4;
    160             break;
    161         case kRGBA_F16_SkColorType:
    162             format = GL_RGBA;
    163             internalFormat = GL_RGBA16F;
    164             type = GL_HALF_FLOAT;
    165             break;
    166         case kN32_SkColorType:
    167         default:
    168             format = GL_RGBA;
    169             internalFormat = GL_RGBA;
    170             type = GL_UNSIGNED_BYTE;
    171             break;
    172     }
    173 
    174     renderState.bindFramebuffer(fbo);
    175 
    176     // TODO: Use layerPool or something to get this maybe? But since we
    177     // need explicit format control we can't currently.
    178 
    179     // Setup the rendertarget
    180     glGenTextures(1, &texture);
    181     caches.textureState().activateTexture(0);
    182     caches.textureState().bindTexture(texture);
    183     glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
    184     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    185     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    186     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    187     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    188     glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight, 0, format, type, nullptr);
    189     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    190 
    191     {
    192         bool requiresFilter;
    193         // Draw & readback
    194         renderState.setViewport(destWidth, destHeight);
    195         renderState.scissor().setEnabled(false);
    196         renderState.blend().syncEnabled();
    197         renderState.stencil().disable();
    198 
    199         Matrix4 croppedTexTransform(texTransform);
    200         if (!srcRect.isEmpty()) {
    201             // We flipV to convert to 0,0 top-left for the srcRect
    202             // coordinates then flip back to 0,0 bottom-left for
    203             // GLES coordinates.
    204             croppedTexTransform.multiply(sFlipV);
    205             croppedTexTransform.translate(srcRect.left / sourceTexture.width(),
    206                                           srcRect.top / sourceTexture.height(), 0);
    207             croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
    208                                       srcRect.getHeight() / sourceTexture.height(), 1);
    209             croppedTexTransform.multiply(sFlipV);
    210             requiresFilter = srcRect.getWidth() != (float)destWidth ||
    211                              srcRect.getHeight() != (float)destHeight;
    212         } else {
    213             requiresFilter = sourceTexture.width() != (uint32_t)destWidth ||
    214                              sourceTexture.height() != (uint32_t)destHeight;
    215         }
    216         Glop glop;
    217         GlopBuilder(renderState, caches, &glop)
    218                 .setRoundRectClipState(nullptr)
    219                 .setMeshTexturedUnitQuad(nullptr)
    220                 .setFillExternalTexture(sourceTexture, croppedTexTransform, requiresFilter)
    221                 .setTransform(Matrix4::identity(), TransformFlags::None)
    222                 .setModelViewMapUnitToRect(Rect(destWidth, destHeight))
    223                 .build();
    224         Matrix4 ortho;
    225         ortho.loadOrtho(destWidth, destHeight);
    226         renderState.render(glop, ortho, false);
    227 
    228         // TODO: We should convert to linear space when the target is RGBA16F
    229         glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels());
    230         bitmap->notifyPixelsChanged();
    231     }
    232 
    233     // Cleanup
    234     caches.textureState().deleteTexture(texture);
    235     renderState.deleteFramebuffer(fbo);
    236 
    237     GL_CHECKPOINT(MODERATE);
    238 
    239     return CopyResult::Success;
    240 }
    241 
    242 CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
    243                                              int imgWidth, int imgHeight, const Rect& srcRect,
    244                                              SkBitmap* bitmap) {
    245     // If this is a 90 or 270 degree rotation we need to swap width/height
    246     // This is a fuzzy way of checking that.
    247     if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
    248         std::swap(imgWidth, imgHeight);
    249     }
    250 
    251     Caches& caches = Caches::getInstance();
    252     GLuint sourceTexId;
    253     // Create a 2D texture to sample from the EGLImage
    254     glGenTextures(1, &sourceTexId);
    255     caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
    256     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
    257 
    258     GLenum status = GL_NO_ERROR;
    259     while ((status = glGetError()) != GL_NO_ERROR) {
    260         ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
    261         return CopyResult::UnknownError;
    262     }
    263 
    264     Texture sourceTexture(caches);
    265     sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
    266                        GL_TEXTURE_EXTERNAL_OES);
    267 
    268     CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), sourceTexture,
    269                                             imgTransform, srcRect, bitmap);
    270     sourceTexture.deleteTexture();
    271     return copyResult;
    272 }
    273 
    274 bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer,
    275                                        SkBitmap* bitmap) {
    276     if (!layer.isRenderable()) {
    277         // layer has never been updated by DeferredLayerUpdater, abort copy
    278         return false;
    279     }
    280 
    281     return CopyResult::Success == copyTextureInto(Caches::getInstance(), renderThread.renderState(),
    282                                                   layer.getTexture(), layer.getTexTransform(),
    283                                                   Rect(), bitmap);
    284 }
    285 
    286 }  // namespace uirenderer
    287 }  // namespace android
    288