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