blob: 4bff715822e8fb8fb07fbe878a5ff15262df46bf [file] [log] [blame]
Stan Ilievc8e22a62018-08-14 13:30:17 -04001/*
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 <cutils/compiler.h>
18#include <gui/BufferQueue.h>
19#include <math/mat4.h>
20#include <system/window.h>
21
22#include <utils/Trace.h>
23
24#include "Matrix.h"
25#include "SurfaceTexture.h"
26
27namespace android {
28
29// Macros for including the SurfaceTexture name in log messages
30#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
31#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
32#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
33#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
34
35static const mat4 mtxIdentity;
36
37SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
38 uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
39 : ConsumerBase(bq, isControlledByApp)
40 , mCurrentCrop(Rect::EMPTY_RECT)
41 , mCurrentTransform(0)
42 , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
43 , mCurrentFence(Fence::NO_FENCE)
44 , mCurrentTimestamp(0)
45 , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
46 , mCurrentFrameNumber(0)
47 , mDefaultWidth(1)
48 , mDefaultHeight(1)
49 , mFilteringEnabled(true)
50 , mTexName(tex)
51 , mUseFenceSync(useFenceSync)
52 , mTexTarget(texTarget)
53 , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
54 , mOpMode(OpMode::attachedToGL) {
55 SFT_LOGV("SurfaceTexture");
56
57 memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
58
59 mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
60}
61
62SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
63 bool useFenceSync, bool isControlledByApp)
64 : ConsumerBase(bq, isControlledByApp)
65 , mCurrentCrop(Rect::EMPTY_RECT)
66 , mCurrentTransform(0)
67 , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
68 , mCurrentFence(Fence::NO_FENCE)
69 , mCurrentTimestamp(0)
70 , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
71 , mCurrentFrameNumber(0)
72 , mDefaultWidth(1)
73 , mDefaultHeight(1)
74 , mFilteringEnabled(true)
75 , mTexName(0)
76 , mUseFenceSync(useFenceSync)
77 , mTexTarget(texTarget)
78 , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
79 , mOpMode(OpMode::detached) {
80 SFT_LOGV("SurfaceTexture");
81
82 memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
83
84 mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
85}
86
87status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
88 Mutex::Autolock lock(mMutex);
89 if (mAbandoned) {
90 SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
91 return NO_INIT;
92 }
93 mDefaultWidth = w;
94 mDefaultHeight = h;
95 return mConsumer->setDefaultBufferSize(w, h);
96}
97
98status_t SurfaceTexture::updateTexImage() {
99 ATRACE_CALL();
100 SFT_LOGV("updateTexImage");
101 Mutex::Autolock lock(mMutex);
102
103 if (mAbandoned) {
104 SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
105 return NO_INIT;
106 }
107
108 return mEGLConsumer.updateTexImage(*this);
109}
110
111status_t SurfaceTexture::releaseTexImage() {
112 // releaseTexImage can be invoked even when not attached to a GL context.
113 ATRACE_CALL();
114 SFT_LOGV("releaseTexImage");
115 Mutex::Autolock lock(mMutex);
116
117 if (mAbandoned) {
118 SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
119 return NO_INIT;
120 }
121
122 return mEGLConsumer.releaseTexImage(*this);
123}
124
125status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
126 uint64_t maxFrameNumber) {
127 status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
128 if (err != NO_ERROR) {
129 return err;
130 }
131
132 switch (mOpMode) {
133 case OpMode::attachedToView:
134 mImageConsumer.onAcquireBufferLocked(item);
135 break;
136 case OpMode::attachedToGL:
137 mEGLConsumer.onAcquireBufferLocked(item, *this);
138 break;
139 case OpMode::detached:
140 break;
141 }
142
143 return NO_ERROR;
144}
145
146status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
147 EGLDisplay display, EGLSyncKHR eglFence) {
148 // release the buffer if it hasn't already been discarded by the
149 // BufferQueue. This can happen, for example, when the producer of this
150 // buffer has reallocated the original buffer slot after this buffer
151 // was acquired.
152 status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
153 // We could be releasing an EGL buffer, even if not currently attached to a GL context.
154 mImageConsumer.onReleaseBufferLocked(buf);
155 mEGLConsumer.onReleaseBufferLocked(buf);
156 return err;
157}
158
159status_t SurfaceTexture::detachFromContext() {
160 ATRACE_CALL();
161 SFT_LOGV("detachFromContext");
162 Mutex::Autolock lock(mMutex);
163
164 if (mAbandoned) {
165 SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
166 return NO_INIT;
167 }
168
169 if (mOpMode != OpMode::attachedToGL) {
170 SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
171 return INVALID_OPERATION;
172 }
173
174 status_t err = mEGLConsumer.detachFromContext(*this);
175 if (err == OK) {
176 mOpMode = OpMode::detached;
177 }
178
179 return err;
180}
181
182status_t SurfaceTexture::attachToContext(uint32_t tex) {
183 ATRACE_CALL();
184 SFT_LOGV("attachToContext");
185 Mutex::Autolock lock(mMutex);
186
187 if (mAbandoned) {
188 SFT_LOGE("attachToContext: abandoned SurfaceTexture");
189 return NO_INIT;
190 }
191
192 if (mOpMode != OpMode::detached) {
193 SFT_LOGE(
194 "attachToContext: SurfaceTexture is already attached to a "
195 "context");
196 return INVALID_OPERATION;
197 }
198
199 if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
200 // release possible ImageConsumer cache
201 mImageConsumer.onFreeBufferLocked(mCurrentTexture);
202 }
203
204 return mEGLConsumer.attachToContext(tex, *this);
205}
206
207void SurfaceTexture::attachToView() {
208 ATRACE_CALL();
209 Mutex::Autolock _l(mMutex);
210 if (mAbandoned) {
211 SFT_LOGE("attachToView: abandoned SurfaceTexture");
212 return;
213 }
214 if (mOpMode == OpMode::detached) {
215 mOpMode = OpMode::attachedToView;
216
217 if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
218 // release possible EGLConsumer texture cache
219 mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
220 mEGLConsumer.onAbandonLocked();
221 }
222 } else {
223 SFT_LOGE("attachToView: already attached");
224 }
225}
226
227void SurfaceTexture::detachFromView() {
228 ATRACE_CALL();
229 Mutex::Autolock _l(mMutex);
230
231 if (mAbandoned) {
232 SFT_LOGE("detachFromView: abandoned SurfaceTexture");
233 return;
234 }
235
236 if (mOpMode == OpMode::attachedToView) {
237 mOpMode = OpMode::detached;
238 } else {
239 SFT_LOGE("detachFromView: not attached to View");
240 }
241}
242
243uint32_t SurfaceTexture::getCurrentTextureTarget() const {
244 return mTexTarget;
245}
246
247void SurfaceTexture::getTransformMatrix(float mtx[16]) {
248 Mutex::Autolock lock(mMutex);
249 memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
250}
251
252void SurfaceTexture::setFilteringEnabled(bool enabled) {
253 Mutex::Autolock lock(mMutex);
254 if (mAbandoned) {
255 SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
256 return;
257 }
258 bool needsRecompute = mFilteringEnabled != enabled;
259 mFilteringEnabled = enabled;
260
261 if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
262 SFT_LOGD("setFilteringEnabled called with no current item");
263 }
264
265 if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
266 computeCurrentTransformMatrixLocked();
267 }
268}
269
270void SurfaceTexture::computeCurrentTransformMatrixLocked() {
271 SFT_LOGV("computeCurrentTransformMatrixLocked");
272 sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
273 ? nullptr
274 : mSlots[mCurrentTexture].mGraphicBuffer;
275 if (buf == nullptr) {
276 SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
277 }
278 computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
279 mFilteringEnabled);
280}
281
282void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
283 const Rect& cropRect, uint32_t transform,
284 bool filtering) {
285 // Transform matrices
286 static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
287 static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
288 static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
289
290 mat4 xform;
291 if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
292 xform *= mtxFlipH;
293 }
294 if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
295 xform *= mtxFlipV;
296 }
297 if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
298 xform *= mtxRot90;
299 }
300
301 if (!cropRect.isEmpty() && buf.get()) {
302 float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
303 float bufferWidth = buf->getWidth();
304 float bufferHeight = buf->getHeight();
305 float shrinkAmount = 0.0f;
306 if (filtering) {
307 // In order to prevent bilinear sampling beyond the edge of the
308 // crop rectangle we may need to shrink it by 2 texels in each
309 // dimension. Normally this would just need to take 1/2 a texel
310 // off each end, but because the chroma channels of YUV420 images
311 // are subsampled we may need to shrink the crop region by a whole
312 // texel on each side.
313 switch (buf->getPixelFormat()) {
314 case PIXEL_FORMAT_RGBA_8888:
315 case PIXEL_FORMAT_RGBX_8888:
316 case PIXEL_FORMAT_RGBA_FP16:
317 case PIXEL_FORMAT_RGBA_1010102:
318 case PIXEL_FORMAT_RGB_888:
319 case PIXEL_FORMAT_RGB_565:
320 case PIXEL_FORMAT_BGRA_8888:
321 // We know there's no subsampling of any channels, so we
322 // only need to shrink by a half a pixel.
323 shrinkAmount = 0.5;
324 break;
325
326 default:
327 // If we don't recognize the format, we must assume the
328 // worst case (that we care about), which is YUV420.
329 shrinkAmount = 1.0;
330 break;
331 }
332 }
333
334 // Only shrink the dimensions that are not the size of the buffer.
335 if (cropRect.width() < bufferWidth) {
336 tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
337 sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
338 }
339 if (cropRect.height() < bufferHeight) {
340 ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
341 sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
342 }
343
344 mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
345 xform = crop * xform;
346 }
347
348 // SurfaceFlinger expects the top of its window textures to be at a Y
349 // coordinate of 0, so SurfaceTexture must behave the same way. We don't
350 // want to expose this to applications, however, so we must add an
351 // additional vertical flip to the transform after all the other transforms.
352 xform = mtxFlipV * xform;
353
354 memcpy(outTransform, xform.asArray(), sizeof(xform));
355}
356
357Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
358 Rect outCrop = crop;
359
360 uint32_t newWidth = static_cast<uint32_t>(crop.width());
361 uint32_t newHeight = static_cast<uint32_t>(crop.height());
362
363 if (newWidth * bufferHeight > newHeight * bufferWidth) {
364 newWidth = newHeight * bufferWidth / bufferHeight;
365 ALOGV("too wide: newWidth = %d", newWidth);
366 } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
367 newHeight = newWidth * bufferHeight / bufferWidth;
368 ALOGV("too tall: newHeight = %d", newHeight);
369 }
370
371 uint32_t currentWidth = static_cast<uint32_t>(crop.width());
372 uint32_t currentHeight = static_cast<uint32_t>(crop.height());
373
374 // The crop is too wide
375 if (newWidth < currentWidth) {
376 uint32_t dw = currentWidth - newWidth;
377 auto halfdw = dw / 2;
378 outCrop.left += halfdw;
379 // Not halfdw because it would subtract 1 too few when dw is odd
380 outCrop.right -= (dw - halfdw);
381 // The crop is too tall
382 } else if (newHeight < currentHeight) {
383 uint32_t dh = currentHeight - newHeight;
384 auto halfdh = dh / 2;
385 outCrop.top += halfdh;
386 // Not halfdh because it would subtract 1 too few when dh is odd
387 outCrop.bottom -= (dh - halfdh);
388 }
389
390 ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
391 outCrop.bottom);
392
393 return outCrop;
394}
395
396nsecs_t SurfaceTexture::getTimestamp() {
397 SFT_LOGV("getTimestamp");
398 Mutex::Autolock lock(mMutex);
399 return mCurrentTimestamp;
400}
401
402android_dataspace SurfaceTexture::getCurrentDataSpace() {
403 SFT_LOGV("getCurrentDataSpace");
404 Mutex::Autolock lock(mMutex);
405 return mCurrentDataSpace;
406}
407
408uint64_t SurfaceTexture::getFrameNumber() {
409 SFT_LOGV("getFrameNumber");
410 Mutex::Autolock lock(mMutex);
411 return mCurrentFrameNumber;
412}
413
414Rect SurfaceTexture::getCurrentCrop() const {
415 Mutex::Autolock lock(mMutex);
416 return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
417 ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
418 : mCurrentCrop;
419}
420
421uint32_t SurfaceTexture::getCurrentTransform() const {
422 Mutex::Autolock lock(mMutex);
423 return mCurrentTransform;
424}
425
426uint32_t SurfaceTexture::getCurrentScalingMode() const {
427 Mutex::Autolock lock(mMutex);
428 return mCurrentScalingMode;
429}
430
431sp<Fence> SurfaceTexture::getCurrentFence() const {
432 Mutex::Autolock lock(mMutex);
433 return mCurrentFence;
434}
435
436std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
437 Mutex::Autolock lock(mMutex);
438 return mCurrentFenceTime;
439}
440
441void SurfaceTexture::freeBufferLocked(int slotIndex) {
442 SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
443 if (slotIndex == mCurrentTexture) {
444 mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
445 }
446 // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure.
447 // Buffers can be freed after SurfaceTexture has detached from GL context or View.
448 mImageConsumer.onFreeBufferLocked(slotIndex);
449 mEGLConsumer.onFreeBufferLocked(slotIndex);
450 ConsumerBase::freeBufferLocked(slotIndex);
451}
452
453void SurfaceTexture::abandonLocked() {
454 SFT_LOGV("abandonLocked");
455 mEGLConsumer.onAbandonLocked();
456 ConsumerBase::abandonLocked();
457}
458
459status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
460 return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
461}
462
463void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
464 result.appendFormat(
465 "%smTexName=%d mCurrentTexture=%d\n"
466 "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
467 prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top,
468 mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform);
469
470 ConsumerBase::dumpLocked(result, prefix);
471}
472
473sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
474 bool* queueEmpty,
475 uirenderer::RenderState& renderState) {
476 Mutex::Autolock _l(mMutex);
477
478 if (mAbandoned) {
479 SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
480 return nullptr;
481 }
482
483 if (mOpMode != OpMode::attachedToView) {
484 SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
485 return nullptr;
486 }
487
488 auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
489 if (image.get()) {
490 uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
491 dataSpace = mCurrentDataSpace;
492 }
493 return image;
494}
495
496}; // namespace android