blob: 17ee17d5cd1d861dca9b2d1a1025aaee0e025d05 [file] [log] [blame]
Stan Iliev564ca3e2018-09-04 22:00:00 +00001/*
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"
Derek Sollenbergerd01b5912018-10-19 15:55:33 -040025#include "utils/Color.h"
Stan Iliev902ce2a2019-03-07 18:13:55 -050026#include <GrAHardwareBufferUtils.h>
Stan Ilievee3754a2019-03-15 12:07:35 -040027#include <GrBackendSurface.h>
Stan Iliev564ca3e2018-09-04 22:00:00 +000028
29// Macro for including the SurfaceTexture name in log messages
30#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
31
Stan Ilievee3754a2019-03-15 12:07:35 -040032using namespace android::uirenderer::renderthread;
33
Stan Iliev564ca3e2018-09-04 22:00:00 +000034namespace android {
35
36void ImageConsumer::onFreeBufferLocked(int slotIndex) {
Stan Ilievee3754a2019-03-15 12:07:35 -040037 // This callback may be invoked on any thread.
Stan Iliev902ce2a2019-03-07 18:13:55 -050038 mImageSlots[slotIndex].clear();
Stan Iliev564ca3e2018-09-04 22:00:00 +000039}
40
41void 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) {
Stan Iliev902ce2a2019-03-07 18:13:55 -050045 mImageSlots[item->mSlot].clear();
Stan Iliev564ca3e2018-09-04 22:00:00 +000046 }
47}
48
49void ImageConsumer::onReleaseBufferLocked(int buf) {
Stan Iliev902ce2a2019-03-07 18:13:55 -050050 mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
Stan Iliev564ca3e2018-09-04 22:00:00 +000051}
52
Stan Ilievee3754a2019-03-15 12:07:35 -040053/**
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 */
57class AutoBackendTextureRelease {
58public:
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
Stan Iliev883c9d92019-08-01 14:41:52 -040074 void newBufferContent(GrContext* context);
75
Stan Ilievee3754a2019-03-15 12:07:35 -040076private:
77 // The only way to invoke dtor is with unref, when mUsageCount is 0.
78 ~AutoBackendTextureRelease() {}
79
80 GrBackendTexture mBackendTexture;
81 GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
Stan Iliev883c9d92019-08-01 14:41:52 -040082 GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
83 GrAHardwareBufferUtils::TexImageCtx mImageCtx;
Stan Ilievee3754a2019-03-15 12:07:35 -040084
85 // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs
86 // are held by SkImages.
87 int mUsageCount = 1;
88
89 // mImage is the SkImage created from mBackendTexture.
90 sk_sp<SkImage> mImage;
91};
92
93AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer) {
94 bool createProtectedImage =
95 0 != (buffer->getUsage() & GraphicBuffer::USAGE_PROTECTED);
96 GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(
97 context,
98 reinterpret_cast<AHardwareBuffer*>(buffer),
99 buffer->getPixelFormat(),
100 false);
101 mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
102 context,
103 reinterpret_cast<AHardwareBuffer*>(buffer),
104 buffer->getWidth(),
105 buffer->getHeight(),
106 &mDeleteProc,
Stan Iliev883c9d92019-08-01 14:41:52 -0400107 &mUpdateProc,
108 &mImageCtx,
Stan Ilievee3754a2019-03-15 12:07:35 -0400109 createProtectedImage,
110 backendFormat,
111 false);
112}
113
114void AutoBackendTextureRelease::unref(bool releaseImage) {
115 if (!RenderThread::isCurrent()) {
116 // EGLImage needs to be destroyed on RenderThread to prevent memory leak.
117 // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not
118 // thread safe.
119 RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); });
120 return;
121 }
122
123 if (releaseImage) {
124 mImage.reset();
125 }
126
127 mUsageCount--;
128 if (mUsageCount <= 0) {
129 if (mBackendTexture.isValid()) {
Stan Iliev883c9d92019-08-01 14:41:52 -0400130 mDeleteProc(mImageCtx);
Stan Ilievee3754a2019-03-15 12:07:35 -0400131 mBackendTexture = {};
132 }
133 delete this;
134 }
135}
136
137void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) {
138 AutoBackendTextureRelease* textureRelease =
139 reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
140 textureRelease->unref(false);
141}
142
143void AutoBackendTextureRelease::makeImage(sp<GraphicBuffer>& graphicBuffer,
144 android_dataspace dataspace, GrContext* context) {
145 SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(
146 graphicBuffer->getPixelFormat());
147 mImage = SkImage::MakeFromTexture(context,
148 mBackendTexture,
149 kTopLeft_GrSurfaceOrigin,
150 colorType,
151 kPremul_SkAlphaType,
152 uirenderer::DataSpaceToColorSpace(dataspace),
153 releaseProc,
154 this);
155 if (mImage.get()) {
156 // The following ref will be counteracted by releaseProc, when SkImage is discarded.
157 ref();
158 }
159}
160
Stan Iliev883c9d92019-08-01 14:41:52 -0400161void AutoBackendTextureRelease::newBufferContent(GrContext* context) {
162 if (mBackendTexture.isValid()) {
163 mUpdateProc(mImageCtx, context);
164 }
165}
166
Derek Sollenbergerd01b5912018-10-19 15:55:33 -0400167void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500168 android_dataspace dataspace, bool forceCreate,
169 GrContext* context) {
Stan Ilievee3754a2019-03-15 12:07:35 -0400170 if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace
171 || forceCreate) {
Stan Iliev902ce2a2019-03-07 18:13:55 -0500172 if (!graphicBuffer.get()) {
173 clear();
174 return;
175 }
176
Stan Ilievee3754a2019-03-15 12:07:35 -0400177 if (!mTextureRelease) {
178 mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get());
Stan Iliev883c9d92019-08-01 14:41:52 -0400179 } else {
180 mTextureRelease->newBufferContent(context);
Stan Iliev902ce2a2019-03-07 18:13:55 -0500181 }
Stan Ilievee3754a2019-03-15 12:07:35 -0400182
Derek Sollenbergerd01b5912018-10-19 15:55:33 -0400183 mDataspace = dataspace;
Stan Ilievee3754a2019-03-15 12:07:35 -0400184 mTextureRelease->makeImage(graphicBuffer, dataspace, context);
Stan Iliev902ce2a2019-03-07 18:13:55 -0500185 }
186}
187
188void ImageConsumer::ImageSlot::clear() {
Stan Ilievee3754a2019-03-15 12:07:35 -0400189 if (mTextureRelease) {
190 // The following unref counteracts the initial mUsageCount of 1, set by default initializer.
191 mTextureRelease->unref(true);
192 mTextureRelease = nullptr;
Stan Iliev564ca3e2018-09-04 22:00:00 +0000193 }
194}
195
Stan Ilievee3754a2019-03-15 12:07:35 -0400196sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() {
197 return mTextureRelease ? mTextureRelease->getImage() : nullptr;
198}
199
Stan Iliev564ca3e2018-09-04 22:00:00 +0000200sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
201 uirenderer::RenderState& renderState) {
202 BufferItem item;
203 status_t err;
204 err = st.acquireBufferLocked(&item, 0);
205 if (err != OK) {
206 if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
207 IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
208 } else {
209 int slot = st.mCurrentTexture;
210 if (slot != BufferItem::INVALID_BUFFER_SLOT) {
211 *queueEmpty = true;
Stan Ilievf6a4ee52018-12-17 17:37:38 -0500212 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500213 st.mCurrentDataSpace, false, renderState.getRenderThread().getGrContext());
214 return mImageSlots[slot].getImage();
Stan Iliev564ca3e2018-09-04 22:00:00 +0000215 }
216 }
217 return nullptr;
218 }
219
220 int slot = item.mSlot;
221 if (item.mFence->isValid()) {
222 // Wait on the producer fence for the buffer to be ready.
223 if (uirenderer::Properties::getRenderPipelineType() ==
224 uirenderer::RenderPipelineType::SkiaGL) {
225 err = renderState.getRenderThread().eglManager().fenceWait(item.mFence);
226 } else {
Greg Danield92a9b12019-04-23 10:11:04 -0400227 err = renderState.getRenderThread().vulkanManager().fenceWait(
228 item.mFence, renderState.getRenderThread().getGrContext());
Stan Iliev564ca3e2018-09-04 22:00:00 +0000229 }
230 if (err != OK) {
231 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
232 EGL_NO_SYNC_KHR);
233 return nullptr;
234 }
235 }
236
237 // Release old buffer.
238 if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
239 // If needed, set the released slot's fence to guard against a producer accessing the
240 // buffer before the outstanding accesses have completed.
241 sp<Fence> releaseFence;
242 EGLDisplay display = EGL_NO_DISPLAY;
243 if (uirenderer::Properties::getRenderPipelineType() ==
244 uirenderer::RenderPipelineType::SkiaGL) {
245 auto& eglManager = renderState.getRenderThread().eglManager();
246 display = eglManager.eglDisplay();
Stan Iliev902ce2a2019-03-07 18:13:55 -0500247 err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(),
Stan Iliev564ca3e2018-09-04 22:00:00 +0000248 releaseFence);
249 } else {
Greg Danield92a9b12019-04-23 10:11:04 -0400250 err = renderState.getRenderThread().vulkanManager().createReleaseFence(
251 releaseFence, renderState.getRenderThread().getGrContext());
Stan Iliev564ca3e2018-09-04 22:00:00 +0000252 }
253 if (OK != err) {
254 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
255 EGL_NO_SYNC_KHR);
256 return nullptr;
257 }
258
259 if (releaseFence.get()) {
260 status_t err = st.addReleaseFenceLocked(
261 st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence);
262 if (err != OK) {
263 IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
264 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
265 EGL_NO_SYNC_KHR);
266 return nullptr;
267 }
268 }
269
270 // Finally release the old buffer.
271 status_t status = st.releaseBufferLocked(
272 st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500273 mImageSlots[st.mCurrentTexture].eglFence());
Stan Iliev564ca3e2018-09-04 22:00:00 +0000274 if (status < NO_ERROR) {
275 IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
276 err = status;
277 // Keep going, with error raised.
278 }
279 }
280
281 // Update the state.
282 st.mCurrentTexture = slot;
283 st.mCurrentCrop = item.mCrop;
284 st.mCurrentTransform = item.mTransform;
285 st.mCurrentScalingMode = item.mScalingMode;
286 st.mCurrentTimestamp = item.mTimestamp;
287 st.mCurrentDataSpace = item.mDataSpace;
288 st.mCurrentFence = item.mFence;
289 st.mCurrentFenceTime = item.mFenceTime;
290 st.mCurrentFrameNumber = item.mFrameNumber;
291 st.computeCurrentTransformMatrixLocked();
292
293 *queueEmpty = false;
Stan Iliev902ce2a2019-03-07 18:13:55 -0500294 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true,
295 renderState.getRenderThread().getGrContext());
296 return mImageSlots[slot].getImage();
Stan Iliev564ca3e2018-09-04 22:00:00 +0000297}
298
299} /* namespace android */