blob: 65d95ad36a6fdfae9f198f3880fd36f8b2f69189 [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
74private:
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
90AutoBackendTextureRelease::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
110void 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
133void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) {
134 AutoBackendTextureRelease* textureRelease =
135 reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
136 textureRelease->unref(false);
137}
138
139void 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
Derek Sollenbergerd01b5912018-10-19 15:55:33 -0400157void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500158 android_dataspace dataspace, bool forceCreate,
159 GrContext* context) {
Stan Ilievee3754a2019-03-15 12:07:35 -0400160 if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace
161 || forceCreate) {
Stan Iliev902ce2a2019-03-07 18:13:55 -0500162 if (!graphicBuffer.get()) {
163 clear();
164 return;
165 }
166
Stan Ilievee3754a2019-03-15 12:07:35 -0400167 if (!mTextureRelease) {
168 mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get());
Stan Iliev902ce2a2019-03-07 18:13:55 -0500169 }
Stan Ilievee3754a2019-03-15 12:07:35 -0400170
Derek Sollenbergerd01b5912018-10-19 15:55:33 -0400171 mDataspace = dataspace;
Stan Ilievee3754a2019-03-15 12:07:35 -0400172 mTextureRelease->makeImage(graphicBuffer, dataspace, context);
Stan Iliev902ce2a2019-03-07 18:13:55 -0500173 }
174}
175
176void ImageConsumer::ImageSlot::clear() {
Stan Ilievee3754a2019-03-15 12:07:35 -0400177 if (mTextureRelease) {
178 // The following unref counteracts the initial mUsageCount of 1, set by default initializer.
179 mTextureRelease->unref(true);
180 mTextureRelease = nullptr;
Stan Iliev564ca3e2018-09-04 22:00:00 +0000181 }
182}
183
Stan Ilievee3754a2019-03-15 12:07:35 -0400184sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() {
185 return mTextureRelease ? mTextureRelease->getImage() : nullptr;
186}
187
Stan Iliev564ca3e2018-09-04 22:00:00 +0000188sk_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;
Stan Ilievf6a4ee52018-12-17 17:37:38 -0500200 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500201 st.mCurrentDataSpace, false, renderState.getRenderThread().getGrContext());
202 return mImageSlots[slot].getImage();
Stan Iliev564ca3e2018-09-04 22:00:00 +0000203 }
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(item.mFence);
216 }
217 if (err != OK) {
218 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
219 EGL_NO_SYNC_KHR);
220 return nullptr;
221 }
222 }
223
224 // Release old buffer.
225 if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
226 // If needed, set the released slot's fence to guard against a producer accessing the
227 // buffer before the outstanding accesses have completed.
228 sp<Fence> releaseFence;
229 EGLDisplay display = EGL_NO_DISPLAY;
230 if (uirenderer::Properties::getRenderPipelineType() ==
231 uirenderer::RenderPipelineType::SkiaGL) {
232 auto& eglManager = renderState.getRenderThread().eglManager();
233 display = eglManager.eglDisplay();
Stan Iliev902ce2a2019-03-07 18:13:55 -0500234 err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(),
Stan Iliev564ca3e2018-09-04 22:00:00 +0000235 releaseFence);
236 } else {
237 err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence);
238 }
239 if (OK != err) {
240 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
241 EGL_NO_SYNC_KHR);
242 return nullptr;
243 }
244
245 if (releaseFence.get()) {
246 status_t err = st.addReleaseFenceLocked(
247 st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence);
248 if (err != OK) {
249 IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
250 st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
251 EGL_NO_SYNC_KHR);
252 return nullptr;
253 }
254 }
255
256 // Finally release the old buffer.
257 status_t status = st.releaseBufferLocked(
258 st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
Stan Iliev902ce2a2019-03-07 18:13:55 -0500259 mImageSlots[st.mCurrentTexture].eglFence());
Stan Iliev564ca3e2018-09-04 22:00:00 +0000260 if (status < NO_ERROR) {
261 IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
262 err = status;
263 // Keep going, with error raised.
264 }
265 }
266
267 // Update the state.
268 st.mCurrentTexture = slot;
269 st.mCurrentCrop = item.mCrop;
270 st.mCurrentTransform = item.mTransform;
271 st.mCurrentScalingMode = item.mScalingMode;
272 st.mCurrentTimestamp = item.mTimestamp;
273 st.mCurrentDataSpace = item.mDataSpace;
274 st.mCurrentFence = item.mFence;
275 st.mCurrentFenceTime = item.mFenceTime;
276 st.mCurrentFrameNumber = item.mFrameNumber;
277 st.computeCurrentTransformMatrixLocked();
278
279 *queueEmpty = false;
Stan Iliev902ce2a2019-03-07 18:13:55 -0500280 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true,
281 renderState.getRenderThread().getGrContext());
282 return mImageSlots[slot].getImage();
Stan Iliev564ca3e2018-09-04 22:00:00 +0000283}
284
285} /* namespace android */