• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
onFreeBufferLocked(int slotIndex)36 void ImageConsumer::onFreeBufferLocked(int slotIndex) {
37     // This callback may be invoked on any thread.
38     mImageSlots[slotIndex].clear();
39 }
40 
onAcquireBufferLocked(BufferItem * item)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 
onReleaseBufferLocked(int buf)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 
getTexture() const63     const GrBackendTexture& getTexture() const { return mBackendTexture; }
64 
ref()65     void ref() { mUsageCount++; }
66 
67     void unref(bool releaseImage);
68 
getImage()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.
~AutoBackendTextureRelease()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 
AutoBackendTextureRelease(GrContext * context,GraphicBuffer * buffer)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 
unref(bool releaseImage)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 
releaseProc(SkImage::ReleaseContext releaseContext)133 void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) {
134     AutoBackendTextureRelease* textureRelease =
135         reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
136     textureRelease->unref(false);
137 }
138 
makeImage(sp<GraphicBuffer> & graphicBuffer,android_dataspace dataspace,GrContext * context)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 
createIfNeeded(sp<GraphicBuffer> graphicBuffer,android_dataspace dataspace,bool forceCreate,GrContext * context)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 
clear()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 
getImage()184 sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() {
185     return mTextureRelease ? mTextureRelease->getImage() : nullptr;
186 }
187 
dequeueImage(bool * queueEmpty,SurfaceTexture & st,uirenderer::RenderState & renderState)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