2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "SurfaceTexture"
18 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
19 //#define LOG_NDEBUG 0
21 #define GL_GLEXT_PROTOTYPES
22 #define EGL_EGLEXT_PROTOTYPES
25 #include <EGL/eglext.h>
26 #include <GLES2/gl2.h>
27 #include <GLES2/gl2ext.h>
29 #include <hardware/hardware.h>
31 #include <gui/IGraphicBufferAlloc.h>
32 #include <gui/ISurfaceComposer.h>
33 #include <gui/SurfaceComposerClient.h>
34 #include <gui/SurfaceTexture.h>
36 #include <private/gui/ComposerService.h>
38 #include <utils/Log.h>
39 #include <utils/String8.h>
40 #include <utils/Trace.h>
42 // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension
43 // to synchronize access to the buffers. It will cause dequeueBuffer to stall,
44 // waiting for the GL reads for the buffer being dequeued to complete before
45 // allowing the buffer to be dequeued.
47 #ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
48 #error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible"
52 // Macros for including the SurfaceTexture name in log messages
53 #define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
54 #define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
55 #define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
56 #define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
57 #define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
62 static float mtxIdentity[16] = {
68 static float mtxFlipH[16] = {
74 static float mtxFlipV[16] = {
80 static float mtxRot90[16] = {
86 static float mtxRot180[16] = {
92 static float mtxRot270[16] = {
99 static void mtxMul(float out[16], const float a[16], const float b[16]);
102 SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
103 GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
104 ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
105 mCurrentTransform(0),
106 mCurrentTimestamp(0),
107 mFilteringEnabled(true),
109 #ifdef USE_FENCE_SYNC
110 mUseFenceSync(useFenceSync),
112 mUseFenceSync(false),
114 mTexTarget(texTarget),
115 mEglDisplay(EGL_NO_DISPLAY),
116 mEglContext(EGL_NO_CONTEXT),
117 mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
120 ST_LOGV("SurfaceTexture");
122 memcpy(mCurrentTransformMatrix, mtxIdentity,
123 sizeof(mCurrentTransformMatrix));
125 mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
128 status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
129 Mutex::Autolock lock(mMutex);
130 return mBufferQueue->setBufferCountServer(bufferCount);
134 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
136 Mutex::Autolock lock(mMutex);
139 return mBufferQueue->setDefaultBufferSize(w, h);
142 status_t SurfaceTexture::updateTexImage() {
143 return SurfaceTexture::updateTexImage(NULL);
146 status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
147 status_t err = ConsumerBase::acquireBufferLocked(item);
148 if (err != NO_ERROR) {
152 int slot = item->mBuf;
153 if (item->mGraphicBuffer != NULL) {
154 if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
155 eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
156 mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
160 // Update the GL texture object. We may have to do this even when
161 // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
162 // detaching from a context but the buffer has not been re-allocated.
163 EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
164 if (image == EGL_NO_IMAGE_KHR) {
165 return UNKNOWN_ERROR;
167 mEglSlots[slot].mEglImage = image;
172 status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
173 EGLSyncKHR eglFence, const sp<Fence>& fence) {
174 status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
177 mEglSlots[mCurrentTexture].mEglFence = EGL_NO_SYNC_KHR;
182 status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
184 ST_LOGV("updateTexImage");
185 Mutex::Autolock lock(mMutex);
187 status_t err = NO_ERROR;
190 ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
195 ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
197 return INVALID_OPERATION;
200 EGLDisplay dpy = eglGetCurrentDisplay();
201 EGLContext ctx = eglGetCurrentContext();
203 if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
204 dpy == EGL_NO_DISPLAY) {
205 ST_LOGE("updateTexImage: invalid current EGLDisplay");
206 return INVALID_OPERATION;
209 if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
210 ctx == EGL_NO_CONTEXT) {
211 ST_LOGE("updateTexImage: invalid current EGLContext");
212 return INVALID_OPERATION;
218 BufferQueue::BufferItem item;
220 // In asynchronous mode the list is guaranteed to be one buffer
221 // deep, while in synchronous mode we use the oldest buffer.
222 err = acquireBufferLocked(&item);
223 if (err == NO_ERROR) {
226 // we call the rejecter here, in case the caller has a reason to
227 // not accept this buffer. this is used by SurfaceFlinger to
228 // reject buffers which have the wrong size
229 if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
230 releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
231 glBindTexture(mTexTarget, mTexName);
236 while ((error = glGetError()) != GL_NO_ERROR) {
237 ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
240 EGLImageKHR image = mEglSlots[buf].mEglImage;
241 glBindTexture(mTexTarget, mTexName);
242 glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
244 while ((error = glGetError()) != GL_NO_ERROR) {
245 ST_LOGE("updateTexImage: error binding external texture image %p "
246 "(slot %d): %#04x", image, buf, error);
250 if (err == NO_ERROR) {
251 err = syncForReleaseLocked(dpy);
254 if (err != NO_ERROR) {
255 // Release the buffer we just acquired. It's not safe to
256 // release the old buffer, so instead we just drop the new frame.
257 releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
261 ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
263 mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
264 buf, mSlots[buf].mGraphicBuffer->handle);
266 // release old buffer
267 if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
268 status_t status = releaseBufferLocked(mCurrentTexture, dpy,
269 mEglSlots[mCurrentTexture].mEglFence,
270 mSlots[mCurrentTexture].mFence);
271 if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
272 ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
273 strerror(-status), status);
278 // Update the SurfaceTexture state.
279 mCurrentTexture = buf;
280 mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
281 mCurrentCrop = item.mCrop;
282 mCurrentTransform = item.mTransform;
283 mCurrentScalingMode = item.mScalingMode;
284 mCurrentTimestamp = item.mTimestamp;
285 mCurrentFence = item.mFence;
286 computeCurrentTransformMatrix();
289 ST_LOGE("updateTexImage failed on acquire %d", err);
291 // We always bind the texture even if we don't update its contents.
292 glBindTexture(mTexTarget, mTexName);
299 void SurfaceTexture::setReleaseFence(int fenceFd) {
300 sp<Fence> fence(new Fence(fenceFd));
301 if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
303 if (!mSlots[mCurrentTexture].mFence.get()) {
304 mSlots[mCurrentTexture].mFence = fence;
306 sp<Fence> mergedFence = Fence::merge(
307 String8("SurfaceTexture merged release"),
308 mSlots[mCurrentTexture].mFence, fence);
309 if (!mergedFence.get()) {
310 ST_LOGE("failed to merge release fences");
311 // synchronization is broken, the best we can do is hope fences
312 // signal in order so the new fence will act like a union
313 mSlots[mCurrentTexture].mFence = fence;
316 mSlots[mCurrentTexture].mFence = mergedFence;
320 status_t SurfaceTexture::detachFromContext() {
322 ST_LOGV("detachFromContext");
323 Mutex::Autolock lock(mMutex);
326 ST_LOGE("detachFromContext: abandoned SurfaceTexture");
331 ST_LOGE("detachFromContext: SurfaceTexture is not attached to a "
333 return INVALID_OPERATION;
336 EGLDisplay dpy = eglGetCurrentDisplay();
337 EGLContext ctx = eglGetCurrentContext();
339 if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
340 ST_LOGE("detachFromContext: invalid current EGLDisplay");
341 return INVALID_OPERATION;
344 if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
345 ST_LOGE("detachFromContext: invalid current EGLContext");
346 return INVALID_OPERATION;
349 if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
350 status_t err = syncForReleaseLocked(dpy);
355 glDeleteTextures(1, &mTexName);
358 // Because we're giving up the EGLDisplay we need to free all the EGLImages
359 // that are associated with it. They'll be recreated when the
360 // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
362 for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
363 EGLImageKHR img = mEglSlots[i].mEglImage;
364 if (img != EGL_NO_IMAGE_KHR) {
365 eglDestroyImageKHR(mEglDisplay, img);
366 mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
370 mEglDisplay = EGL_NO_DISPLAY;
371 mEglContext = EGL_NO_CONTEXT;
377 status_t SurfaceTexture::attachToContext(GLuint tex) {
379 ST_LOGV("attachToContext");
380 Mutex::Autolock lock(mMutex);
383 ST_LOGE("attachToContext: abandoned SurfaceTexture");
388 ST_LOGE("attachToContext: SurfaceTexture is already attached to a "
390 return INVALID_OPERATION;
393 EGLDisplay dpy = eglGetCurrentDisplay();
394 EGLContext ctx = eglGetCurrentContext();
396 if (dpy == EGL_NO_DISPLAY) {
397 ST_LOGE("attachToContext: invalid current EGLDisplay");
398 return INVALID_OPERATION;
401 if (ctx == EGL_NO_CONTEXT) {
402 ST_LOGE("attachToContext: invalid current EGLContext");
403 return INVALID_OPERATION;
406 // We need to bind the texture regardless of whether there's a current
408 glBindTexture(mTexTarget, tex);
410 if (mCurrentTextureBuf != NULL) {
411 // The EGLImageKHR that was associated with the slot was destroyed when
412 // the SurfaceTexture was detached from the old context, so we need to
414 EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
415 if (image == EGL_NO_IMAGE_KHR) {
416 return UNKNOWN_ERROR;
419 // Attach the current buffer to the GL texture.
420 glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
424 while ((error = glGetError()) != GL_NO_ERROR) {
425 ST_LOGE("attachToContext: error binding external texture image %p "
426 "(slot %d): %#04x", image, mCurrentTexture, error);
430 // We destroy the EGLImageKHR here because the current buffer may no
431 // longer be associated with one of the buffer slots, so we have
432 // nowhere to to store it. If the buffer is still associated with a
433 // slot then another EGLImageKHR will be created next time that buffer
434 // gets acquired in updateTexImage.
435 eglDestroyImageKHR(dpy, image);
450 status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
451 ST_LOGV("syncForReleaseLocked");
453 if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
454 EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
455 if (fence != EGL_NO_SYNC_KHR) {
456 // There is already a fence for the current slot. We need to wait
457 // on that before replacing it with another fence to ensure that all
458 // outstanding buffer accesses have completed before the producer
460 EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
461 if (result == EGL_FALSE) {
462 ST_LOGE("syncForReleaseLocked: error waiting for previous "
463 "fence: %#x", eglGetError());
464 return UNKNOWN_ERROR;
465 } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
466 ST_LOGE("syncForReleaseLocked: timeout waiting for previous "
470 eglDestroySyncKHR(dpy, fence);
473 // Create a fence for the outstanding accesses in the current OpenGL ES
475 fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
476 if (fence == EGL_NO_SYNC_KHR) {
477 ST_LOGE("syncForReleaseLocked: error creating fence: %#x",
479 return UNKNOWN_ERROR;
482 mEglSlots[mCurrentTexture].mEglFence = fence;
488 bool SurfaceTexture::isExternalFormat(uint32_t format)
491 // supported YUV formats
492 case HAL_PIXEL_FORMAT_YV12:
493 // Legacy/deprecated YUV formats
494 case HAL_PIXEL_FORMAT_YCbCr_422_SP:
495 case HAL_PIXEL_FORMAT_YCrCb_420_SP:
496 case HAL_PIXEL_FORMAT_YCbCr_422_I:
500 // Any OEM format needs to be considered
501 if (format>=0x100 && format<=0x1FF)
507 GLenum SurfaceTexture::getCurrentTextureTarget() const {
511 void SurfaceTexture::getTransformMatrix(float mtx[16]) {
512 Mutex::Autolock lock(mMutex);
513 memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
516 void SurfaceTexture::setFilteringEnabled(bool enabled) {
517 Mutex::Autolock lock(mMutex);
518 bool needsRecompute = mFilteringEnabled != enabled;
519 mFilteringEnabled = enabled;
520 if (needsRecompute) {
521 computeCurrentTransformMatrix();
525 void SurfaceTexture::computeCurrentTransformMatrix() {
526 ST_LOGV("computeCurrentTransformMatrix");
529 for (int i = 0; i < 16; i++) {
530 xform[i] = mtxIdentity[i];
532 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
534 mtxMul(result, xform, mtxFlipH);
535 for (int i = 0; i < 16; i++) {
536 xform[i] = result[i];
539 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
541 mtxMul(result, xform, mtxFlipV);
542 for (int i = 0; i < 16; i++) {
543 xform[i] = result[i];
546 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
548 mtxMul(result, xform, mtxRot90);
549 for (int i = 0; i < 16; i++) {
550 xform[i] = result[i];
554 sp<GraphicBuffer>& buf(mCurrentTextureBuf);
555 Rect cropRect = mCurrentCrop;
556 float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
557 float bufferWidth = buf->getWidth();
558 float bufferHeight = buf->getHeight();
559 if (!cropRect.isEmpty()) {
560 float shrinkAmount = 0.0f;
561 if (mFilteringEnabled) {
562 // In order to prevent bilinear sampling beyond the edge of the
563 // crop rectangle we may need to shrink it by 2 texels in each
564 // dimension. Normally this would just need to take 1/2 a texel
565 // off each end, but because the chroma channels of YUV420 images
566 // are subsampled we may need to shrink the crop region by a whole
567 // texel on each side.
568 switch (buf->getPixelFormat()) {
569 case PIXEL_FORMAT_RGBA_8888:
570 case PIXEL_FORMAT_RGBX_8888:
571 case PIXEL_FORMAT_RGB_888:
572 case PIXEL_FORMAT_RGB_565:
573 case PIXEL_FORMAT_BGRA_8888:
574 case PIXEL_FORMAT_RGBA_5551:
575 case PIXEL_FORMAT_RGBA_4444:
576 // We know there's no subsampling of any channels, so we
577 // only need to shrink by a half a pixel.
582 // If we don't recognize the format, we must assume the
583 // worst case (that we care about), which is YUV420.
589 // Only shrink the dimensions that are not the size of the buffer.
590 if (cropRect.width() < bufferWidth) {
591 tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
592 sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
595 if (cropRect.height() < bufferHeight) {
596 ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
598 sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
609 float mtxBeforeFlipV[16];
610 mtxMul(mtxBeforeFlipV, crop, xform);
612 // SurfaceFlinger expects the top of its window textures to be at a Y
613 // coordinate of 0, so SurfaceTexture must behave the same way. We don't
614 // want to expose this to applications, however, so we must add an
615 // additional vertical flip to the transform after all the other transforms.
616 mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
619 nsecs_t SurfaceTexture::getTimestamp() {
620 ST_LOGV("getTimestamp");
621 Mutex::Autolock lock(mMutex);
622 return mCurrentTimestamp;
625 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
626 const sp<GraphicBuffer>& graphicBuffer) {
627 EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
629 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
632 EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
633 EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
634 if (image == EGL_NO_IMAGE_KHR) {
635 EGLint error = eglGetError();
636 ST_LOGE("error creating EGLImage: %#x", error);
641 sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
642 Mutex::Autolock lock(mMutex);
643 return mCurrentTextureBuf;
646 Rect SurfaceTexture::getCurrentCrop() const {
647 Mutex::Autolock lock(mMutex);
649 Rect outCrop = mCurrentCrop;
650 if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
651 int32_t newWidth = mCurrentCrop.width();
652 int32_t newHeight = mCurrentCrop.height();
654 if (newWidth * mDefaultHeight > newHeight * mDefaultWidth) {
655 newWidth = newHeight * mDefaultWidth / mDefaultHeight;
656 ST_LOGV("too wide: newWidth = %d", newWidth);
657 } else if (newWidth * mDefaultHeight < newHeight * mDefaultWidth) {
658 newHeight = newWidth * mDefaultHeight / mDefaultWidth;
659 ST_LOGV("too tall: newHeight = %d", newHeight);
662 // The crop is too wide
663 if (newWidth < mCurrentCrop.width()) {
664 int32_t dw = (newWidth - mCurrentCrop.width())/2;
667 // The crop is too tall
668 } else if (newHeight < mCurrentCrop.height()) {
669 int32_t dh = (newHeight - mCurrentCrop.height())/2;
671 outCrop.bottom += dh;
674 ST_LOGV("getCurrentCrop final crop [%d,%d,%d,%d]",
675 outCrop.left, outCrop.top,
676 outCrop.right,outCrop.bottom);
682 uint32_t SurfaceTexture::getCurrentTransform() const {
683 Mutex::Autolock lock(mMutex);
684 return mCurrentTransform;
687 uint32_t SurfaceTexture::getCurrentScalingMode() const {
688 Mutex::Autolock lock(mMutex);
689 return mCurrentScalingMode;
692 sp<Fence> SurfaceTexture::getCurrentFence() const {
693 Mutex::Autolock lock(mMutex);
694 return mCurrentFence;
697 bool SurfaceTexture::isSynchronousMode() const {
698 Mutex::Autolock lock(mMutex);
699 return mBufferQueue->isSynchronousMode();
702 void SurfaceTexture::freeBufferLocked(int slotIndex) {
703 ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
704 if (slotIndex == mCurrentTexture) {
705 mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
707 EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
708 if (img != EGL_NO_IMAGE_KHR) {
709 ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
710 eglDestroyImageKHR(mEglDisplay, img);
712 mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
715 void SurfaceTexture::abandonLocked() {
716 ST_LOGV("abandonLocked");
717 mCurrentTextureBuf.clear();
718 ConsumerBase::abandonLocked();
721 void SurfaceTexture::setName(const String8& name) {
722 Mutex::Autolock _l(mMutex);
724 mBufferQueue->setConsumerName(name);
727 status_t SurfaceTexture::setDefaultBufferFormat(uint32_t defaultFormat) {
728 Mutex::Autolock lock(mMutex);
729 return mBufferQueue->setDefaultBufferFormat(defaultFormat);
732 status_t SurfaceTexture::setConsumerUsageBits(uint32_t usage) {
733 Mutex::Autolock lock(mMutex);
734 usage |= DEFAULT_USAGE_FLAGS;
735 return mBufferQueue->setConsumerUsageBits(usage);
738 status_t SurfaceTexture::setTransformHint(uint32_t hint) {
739 Mutex::Autolock lock(mMutex);
740 return mBufferQueue->setTransformHint(hint);
743 // Used for refactoring BufferQueue from SurfaceTexture
744 // Should not be in final interface once users of SurfaceTexture are clean up.
745 status_t SurfaceTexture::setSynchronousMode(bool enabled) {
746 Mutex::Autolock lock(mMutex);
747 return mBufferQueue->setSynchronousMode(enabled);
750 void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
751 char* buffer, size_t size) const
753 snprintf(buffer, size,
754 "%smTexName=%d mCurrentTexture=%d\n"
755 "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
756 prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
757 mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
759 result.append(buffer);
761 ConsumerBase::dumpLocked(result, prefix, buffer, size);
764 static void mtxMul(float out[16], const float a[16], const float b[16]) {
765 out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
766 out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
767 out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];
768 out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];
770 out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];
771 out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];
772 out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];
773 out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];
775 out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];
776 out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];
777 out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];
778 out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];
780 out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];
781 out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];
782 out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];
783 out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
786 }; // namespace android