Home | History | Annotate | Download | only in surfacetexture
      1 /*
      2  * Copyright (C) 2018 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 "ImageConsumer.h"
     18 #include <gui/BufferQueue.h>
     19 #include "Properties.h"
     20 #include "SurfaceTexture.h"
     21 #include "renderstate/RenderState.h"
     22 #include "renderthread/EglManager.h"
     23 #include "renderthread/RenderThread.h"
     24 #include "renderthread/VulkanManager.h"
     25 #include "utils/Color.h"
     26 #include <GrAHardwareBufferUtils.h>
     27 #include <GrBackendSurface.h>
     28 
     29 // Macro for including the SurfaceTexture name in log messages
     30 #define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
     31 
     32 using namespace android::uirenderer::renderthread;
     33 
     34 namespace android {
     35 
     36 void ImageConsumer::onFreeBufferLocked(int slotIndex) {
     37     // This callback may be invoked on any thread.
     38     mImageSlots[slotIndex].clear();
     39 }
     40 
     41 void ImageConsumer::onAcquireBufferLocked(BufferItem* item) {
     42     // If item->mGraphicBuffer is not null, this buffer has not been acquired
     43     // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage.
     44     if (item->mGraphicBuffer != nullptr) {
     45         mImageSlots[item->mSlot].clear();
     46     }
     47 }
     48 
     49 void ImageConsumer::onReleaseBufferLocked(int buf) {
     50     mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
     51 }
     52 
     53 /**
     54  * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object
     55  * that keeps GPU resources alive until the last SKImage object using them is destroyed.
     56  */
     57 class AutoBackendTextureRelease {
     58 public:
     59     static void releaseProc(SkImage::ReleaseContext releaseContext);
     60 
     61     AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer);
     62 
     63     const GrBackendTexture& getTexture() const { return mBackendTexture; }
     64 
     65     void ref() { mUsageCount++; }
     66 
     67     void unref(bool releaseImage);
     68 
     69     inline sk_sp<SkImage> getImage() { return mImage; }
     70 
     71     void makeImage(sp<GraphicBuffer>& graphicBuffer, android_dataspace dataspace,
     72                    GrContext* context);
     73 
     74 private:
     75     // The only way to invoke dtor is with unref, when mUsageCount is 0.
     76     ~AutoBackendTextureRelease() {}
     77 
     78     GrBackendTexture mBackendTexture;
     79     GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
     80     GrAHardwareBufferUtils::DeleteImageCtx mDeleteCtx;
     81 
     82     // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs
     83     // are held by SkImages.
     84     int mUsageCount = 1;
     85 
     86     // mImage is the SkImage created from mBackendTexture.
     87     sk_sp<SkImage> mImage;
     88 };
     89 
     90 AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer) {
     91     bool createProtectedImage =
     92         0 != (buffer->getUsage() & GraphicBuffer::USAGE_PROTECTED);
     93     GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(
     94         context,
     95         reinterpret_cast<AHardwareBuffer*>(buffer),
     96         buffer->getPixelFormat(),
     97         false);
     98     mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
     99         context,
    100         reinterpret_cast<AHardwareBuffer*>(buffer),
    101         buffer->getWidth(),
    102         buffer->getHeight(),
    103         &mDeleteProc,
    104         &mDeleteCtx,
    105         createProtectedImage,
    106         backendFormat,
    107         false);
    108 }
    109 
    110 void AutoBackendTextureRelease::unref(bool releaseImage) {
    111     if (!RenderThread::isCurrent()) {
    112         // EGLImage needs to be destroyed on RenderThread to prevent memory leak.
    113         // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not
    114         // thread safe.
    115         RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); });
    116         return;
    117     }
    118 
    119     if (releaseImage) {
    120         mImage.reset();
    121     }
    122 
    123     mUsageCount--;
    124     if (mUsageCount <= 0) {
    125         if (mBackendTexture.isValid()) {
    126             mDeleteProc(mDeleteCtx);
    127             mBackendTexture = {};
    128         }
    129         delete this;
    130     }
    131 }
    132 
    133 void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) {
    134     AutoBackendTextureRelease* textureRelease =
    135         reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
    136     textureRelease->unref(false);
    137 }
    138 
    139 void AutoBackendTextureRelease::makeImage(sp<GraphicBuffer>& graphicBuffer,
    140                                           android_dataspace dataspace, GrContext* context) {
    141     SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(
    142         graphicBuffer->getPixelFormat());
    143     mImage = SkImage::MakeFromTexture(context,
    144         mBackendTexture,
    145         kTopLeft_GrSurfaceOrigin,
    146         colorType,
    147         kPremul_SkAlphaType,
    148         uirenderer::DataSpaceToColorSpace(dataspace),
    149         releaseProc,
    150         this);
    151     if (mImage.get()) {
    152         // The following ref will be counteracted by releaseProc, when SkImage is discarded.
    153         ref();
    154     }
    155 }
    156 
    157 void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
    158                                               android_dataspace dataspace, bool forceCreate,
    159                                               GrContext* context) {
    160     if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace
    161             || forceCreate) {
    162         if (!graphicBuffer.get()) {
    163             clear();
    164             return;
    165         }
    166 
    167         if (!mTextureRelease) {
    168             mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get());
    169         }
    170 
    171         mDataspace = dataspace;
    172         mTextureRelease->makeImage(graphicBuffer, dataspace, context);
    173     }
    174 }
    175 
    176 void ImageConsumer::ImageSlot::clear() {
    177     if (mTextureRelease) {
    178         // The following unref counteracts the initial mUsageCount of 1, set by default initializer.
    179         mTextureRelease->unref(true);
    180         mTextureRelease = nullptr;
    181     }
    182 }
    183 
    184 sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() {
    185     return mTextureRelease ? mTextureRelease->getImage() : nullptr;
    186 }
    187 
    188 sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
    189                                            uirenderer::RenderState& renderState) {
    190     BufferItem item;
    191     status_t err;
    192     err = st.acquireBufferLocked(&item, 0);
    193     if (err != OK) {
    194         if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
    195             IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
    196         } else {
    197             int slot = st.mCurrentTexture;
    198             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
    199                 *queueEmpty = true;
    200                 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
    201                         st.mCurrentDataSpace, false, renderState.getRenderThread().getGrContext());
    202                 return mImageSlots[slot].getImage();
    203             }
    204         }
    205         return nullptr;
    206     }
    207 
    208     int slot = item.mSlot;
    209     if (item.mFence->isValid()) {
    210         // Wait on the producer fence for the buffer to be ready.
    211         if (uirenderer::Properties::getRenderPipelineType() ==
    212             uirenderer::RenderPipelineType::SkiaGL) {
    213             err = renderState.getRenderThread().eglManager().fenceWait(item.mFence);
    214         } else {
    215             err = renderState.getRenderThread().vulkanManager().fenceWait(
    216                     item.mFence, renderState.getRenderThread().getGrContext());
    217         }
    218         if (err != OK) {
    219             st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
    220                                    EGL_NO_SYNC_KHR);
    221             return nullptr;
    222         }
    223     }
    224 
    225     // Release old buffer.
    226     if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
    227         // If needed, set the released slot's fence to guard against a producer accessing the
    228         // buffer before the outstanding accesses have completed.
    229         sp<Fence> releaseFence;
    230         EGLDisplay display = EGL_NO_DISPLAY;
    231         if (uirenderer::Properties::getRenderPipelineType() ==
    232             uirenderer::RenderPipelineType::SkiaGL) {
    233             auto& eglManager = renderState.getRenderThread().eglManager();
    234             display = eglManager.eglDisplay();
    235             err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(),
    236                                                 releaseFence);
    237         } else {
    238             err = renderState.getRenderThread().vulkanManager().createReleaseFence(
    239                     releaseFence, renderState.getRenderThread().getGrContext());
    240         }
    241         if (OK != err) {
    242             st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
    243                                    EGL_NO_SYNC_KHR);
    244             return nullptr;
    245         }
    246 
    247         if (releaseFence.get()) {
    248             status_t err = st.addReleaseFenceLocked(
    249                     st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence);
    250             if (err != OK) {
    251                 IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
    252                 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
    253                                        EGL_NO_SYNC_KHR);
    254                 return nullptr;
    255             }
    256         }
    257 
    258         // Finally release the old buffer.
    259         status_t status = st.releaseBufferLocked(
    260                 st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
    261                 mImageSlots[st.mCurrentTexture].eglFence());
    262         if (status < NO_ERROR) {
    263             IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
    264             err = status;
    265             // Keep going, with error raised.
    266         }
    267     }
    268 
    269     // Update the state.
    270     st.mCurrentTexture = slot;
    271     st.mCurrentCrop = item.mCrop;
    272     st.mCurrentTransform = item.mTransform;
    273     st.mCurrentScalingMode = item.mScalingMode;
    274     st.mCurrentTimestamp = item.mTimestamp;
    275     st.mCurrentDataSpace = item.mDataSpace;
    276     st.mCurrentFence = item.mFence;
    277     st.mCurrentFenceTime = item.mFenceTime;
    278     st.mCurrentFrameNumber = item.mFrameNumber;
    279     st.computeCurrentTransformMatrixLocked();
    280 
    281     *queueEmpty = false;
    282     mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true,
    283         renderState.getRenderThread().getGrContext());
    284     return mImageSlots[slot].getImage();
    285 }
    286 
    287 } /* namespace android */
    288