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