diff options
author | 2017-11-27 11:29:21 -0800 | |
---|---|---|
committer | 2017-12-11 11:11:26 -0800 | |
commit | f1405186f4677f38135561931cd60334ef9dfda2 (patch) | |
tree | 8847a24865b24fbccf32271c9184345904b66aaa | |
parent | f49c189d6b20a1c72d0079b418b7050e0a9a1ef2 (diff) |
surfaceflinger: add BufferLayerConsumer
Copied from GLConsumer.{cpp,h}. Run sed and clang-format on them.
Minor fixes (incorrect header path, already defined macros) to make
them compile.
Make SurfaceFlingerConsumer inherit from BufferLayerConsumer.
Test: boots
Change-Id: I44ef6b15e77fb84b60ab788457a8ed0a1c784a95
-rw-r--r-- | services/surfaceflinger/Android.mk | 1 | ||||
-rw-r--r-- | services/surfaceflinger/BufferLayerConsumer.cpp | 1104 | ||||
-rw-r--r-- | services/surfaceflinger/BufferLayerConsumer.h | 508 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlingerConsumer.cpp | 6 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlingerConsumer.h | 14 |
5 files changed, 1623 insertions, 10 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index deead06b6d..788b851fb8 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES := \ GpuService.cpp \ Layer.cpp \ BufferLayer.cpp \ + BufferLayerConsumer.cpp \ ColorLayer.cpp \ LayerRejecter.cpp \ LayerVector.cpp \ diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp new file mode 100644 index 0000000000..585d4032b2 --- /dev/null +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -0,0 +1,1104 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "BufferLayerConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include "BufferLayerConsumer.h" + +#include <inttypes.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <cutils/compiler.h> + +#include <hardware/hardware.h> + +#include <math/mat4.h> + +#include <gui/BufferItem.h> +#include <gui/GLConsumer.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/gui/ComposerService.h> +#include <private/gui/SyncFeatures.h> + +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/Trace.h> + +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); +#define CROP_EXT_STR "EGL_ANDROID_image_crop" +#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" +#define EGL_PROTECTED_CONTENT_EXT 0x32C0 + +namespace android { + +// Macros for including the BufferLayerConsumer name in log messages +#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) +//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) + +static const struct { + uint32_t width, height; + char const* bits; +} kDebugData = {15, 12, + "_______________" + "_______________" + "_____XX_XX_____" + "__X_X_____X_X__" + "__X_XXXXXXX_X__" + "__XXXXXXXXXXX__" + "___XX_XXX_XX___" + "____XXXXXXX____" + "_____X___X_____" + "____X_____X____" + "_______________" + "_______________"}; + +static const mat4 mtxIdentity; + +Mutex BufferLayerConsumer::sStaticInitLock; +sp<GraphicBuffer> BufferLayerConsumer::sReleasedTexImageBuffer; + +static bool hasEglAndroidImageCropImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(CROP_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(CROP_EXT_STR, exts); + bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglAndroidImageCrop() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglAndroidImageCropImpl(); + return hasIt; +} + +static bool hasEglProtectedContentImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); + bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglProtectedContent() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglProtectedContentImpl(); + return hasIt; +} + +static bool isEglImageCroppable(const Rect& crop) { + return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); +} + +BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, + bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(tex), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(true) { + BLC_LOGV("BufferLayerConsumer"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(0), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(false) { + BLC_LOGV("BufferLayerConsumer"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!"); + return NO_INIT; + } + mDefaultWidth = w; + mDefaultHeight = h; + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t BufferLayerConsumer::updateTexImage() { + ATRACE_CALL(); + BLC_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(); + if (err != NO_ERROR) { + return err; + } + + BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = acquireBufferLocked(&item, 0); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + BLC_LOGV("updateTexImage: no buffers were available"); + glBindTexture(mTexTarget, mTexName); + err = NO_ERROR; + } else { + BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = updateAndReleaseLocked(item); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(mTexTarget, mTexName); + return err; + } + + // Bind the new buffer to the GL texture, and wait until it's ready. + return bindTextureImageLocked(); +} + +status_t BufferLayerConsumer::releaseTexImage() { + ATRACE_CALL(); + BLC_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + BLC_LOGE("releaseTexImage: BufferLayerConsumer is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + if (mAttached) { + err = checkAndUpdateEglStateLocked(true); + if (err != NO_ERROR) { + return err; + } + } else { + // if we're detached, no need to validate EGL's state -- we won't use it. + } + + // Update the BufferLayerConsumer state. + int buf = mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + BLC_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached); + + if (mAttached) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay); + if (err != NO_ERROR) { + BLC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } else { + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + } + + err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + BLC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); + return err; + } + + if (mReleasedTexImage == NULL) { + mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + } + + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImage = mReleasedTexImage; + mCurrentCrop.makeInvalid(); + mCurrentTransform = 0; + mCurrentTimestamp = 0; + mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; + mCurrentFence = Fence::NO_FENCE; + mCurrentFenceTime = FenceTime::NO_FENCE; + + if (mAttached) { + // This binds a dummy buffer (mReleasedTexImage). + status_t result = bindTextureImageLocked(); + if (result != NO_ERROR) { + return result; + } + } else { + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + } + } + + return NO_ERROR; +} + +sp<GraphicBuffer> BufferLayerConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp<GraphicBuffer> buffer = + new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY, + "[BufferLayerConsumer debug texture]"); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); + uint32_t stride = buffer->getStride(); + uint32_t height = buffer->getHeight(); + memset(bits, 0, stride * height * 4); + for (uint32_t y = 0; y < kDebugData.height; y++) { + for (uint32_t x = 0; x < kDebugData.width; x++) { + bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 + : 0xFFFFFFFF; + } + bits += stride; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); + if (err != NO_ERROR) { + return err; + } + + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior EglImage created is using a stale buffer. This + // replaces any old EglImage with a new one (using the new buffer). + if (item->mGraphicBuffer != NULL) { + int slot = item->mSlot; + mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); + } + + return NO_ERROR; +} + +status_t BufferLayerConsumer::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; + return err; +} + +status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, + PendingRelease* pendingRelease) { + status_t err = NO_ERROR; + + int slot = item.mSlot; + + if (!mAttached) { + BLC_LOGE("updateAndRelease: BufferLayerConsumer is not attached to an OpenGL " + "ES context"); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return INVALID_OPERATION; + } + + // Confirm state. + err = checkAndUpdateEglStateLocked(); + if (err != NO_ERROR) { + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + + // Ensure we have a valid EglImageKHR for the slot, creating an EglImage + // if nessessary, for the gralloc buffer currently in the slot in + // ConsumerBase. + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired). + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop); + if (err != NO_ERROR) { + BLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, + slot); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return UNKNOWN_ERROR; + } + + // Do whatever sync ops we need to do before releasing the old slot. + if (slot != mCurrentTexture) { + err = syncForReleaseLocked(mEglDisplay); + if (err != NO_ERROR) { + // Release the buffer we just acquired. It's not safe to + // release the old buffer, so instead we just drop the new frame. + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + } + + BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, + mCurrentTextureImage != NULL ? mCurrentTextureImage->graphicBufferHandle() : 0, slot, + mSlots[slot].mGraphicBuffer->handle); + + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in shared buffer mode and both buffers are + // the same. + sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage; + + // release old buffer + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (pendingRelease == nullptr) { + status_t status = + releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), + mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), + status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = mCurrentTexture; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence; + pendingRelease->isPending = true; + } + } + + // Update the BufferLayerConsumer state. + mCurrentTexture = slot; + mCurrentTextureImage = nextTextureImage; + mCurrentCrop = item.mCrop; + mCurrentTransform = item.mTransform; + mCurrentScalingMode = item.mScalingMode; + mCurrentTimestamp = item.mTimestamp; + mCurrentDataSpace = item.mDataSpace; + mCurrentFence = item.mFence; + mCurrentFenceTime = item.mFenceTime; + mCurrentFrameNumber = item.mFrameNumber; + + computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t BufferLayerConsumer::bindTextureImageLocked() { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + BLC_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(mTexTarget, mTexName); + if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == NULL) { + BLC_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; + } + + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop); + if (err != NO_ERROR) { + BLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(mTexTarget); + + // In the rare case that the display is terminated and then initialized + // again, we can't detect that the display changed (it didn't), but the + // image is invalid. In this case, repeat the exact same steps while + // forcing the creation of a new image. + if ((error = glGetError()) != GL_NO_ERROR) { + glBindTexture(mTexTarget, mTexName); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop, true); + if (result != NO_ERROR) { + BLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(mTexTarget); + if ((error = glGetError()) != GL_NO_ERROR) { + BLC_LOGE("bindTextureImage: error binding external image: %#04x", error); + return UNKNOWN_ERROR; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(); +} + +status_t BufferLayerConsumer::checkAndUpdateEglStateLocked(bool contextCheck) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_CONTEXT) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { + BLC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { + BLC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { + if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + status_t err = + addReleaseFence(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); + } + } +} + +status_t BufferLayerConsumer::detachFromContext() { + ATRACE_CALL(); + BLC_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + BLC_LOGE("detachFromContext: abandoned BufferLayerConsumer"); + return NO_INIT; + } + + if (!mAttached) { + BLC_LOGE("detachFromContext: BufferLayerConsumer is not attached to a " + "context"); + return INVALID_OPERATION; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + BLC_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + BLC_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &mTexName); + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mAttached = false; + + return OK; +} + +status_t BufferLayerConsumer::attachToContext(uint32_t tex) { + ATRACE_CALL(); + BLC_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + BLC_LOGE("attachToContext: abandoned BufferLayerConsumer"); + return NO_INIT; + } + + if (mAttached) { + BLC_LOGE("attachToContext: BufferLayerConsumer is already attached to a " + "context"); + return INVALID_OPERATION; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + BLC_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + BLC_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(mTexTarget, GLuint(tex)); + + mEglDisplay = dpy; + mEglContext = ctx; + mTexName = tex; + mAttached = true; + + if (mCurrentTextureImage != NULL) { + // This may wait for a buffer a second time. This is likely required if + // this is a different context, since otherwise the wait could be skipped + // by bouncing through another context. For the same context the extra + // wait is redundant. + status_t err = bindTextureImageLocked(); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +status_t BufferLayerConsumer::syncForReleaseLocked(EGLDisplay dpy) { + BLC_LOGV("syncForReleaseLocked"); + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + if (sync == EGL_NO_SYNC_KHR) { + BLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + BLC_LOGE("syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + sp<Fence> fence(new Fence(fenceFd)); + status_t err = addReleaseFenceLocked(mCurrentTexture, + mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + BLC_LOGE("syncForReleaseLocked: error adding release fence: " + "%s (%d)", + strerror(-err), err); + return err; + } + } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { + EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + BLC_LOGE("syncForReleaseLocked: error waiting for previous " + "fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + BLC_LOGE("syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + if (fence == EGL_NO_SYNC_KHR) { + BLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +uint32_t BufferLayerConsumer::getCurrentTextureTarget() const { + return mTexTarget; +} + +void BufferLayerConsumer::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void BufferLayerConsumer::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTextureImage == NULL) { + BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); + } + + if (needsRecompute && mCurrentTextureImage != NULL) { + computeCurrentTransformMatrixLocked(); + } +} + +void BufferLayerConsumer::computeCurrentTransformMatrixLocked() { + BLC_LOGV("computeCurrentTransformMatrixLocked"); + sp<GraphicBuffer> buf = + (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); + if (buf == nullptr) { + BLC_LOGD("computeCurrentTransformMatrixLocked: " + "mCurrentTextureImage is NULL"); + } + computeTransformMatrix(mCurrentTransformMatrix, buf, + isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, + mCurrentTransform, mFilteringEnabled); +} + +void BufferLayerConsumer::computeTransformMatrix(float outTransform[16], + const sp<GraphicBuffer>& buf, const Rect& cropRect, + uint32_t transform, bool filtering) { + // Transform matrices + static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); + static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; + } + + mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); + xform = crop * xform; + } + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so BufferLayerConsumer must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +Rect BufferLayerConsumer::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, + uint32_t bufferHeight) { + Rect outCrop = crop; + + uint32_t newWidth = static_cast<uint32_t>(crop.width()); + uint32_t newHeight = static_cast<uint32_t>(crop.height()); + + if (newWidth * bufferHeight > newHeight * bufferWidth) { + newWidth = newHeight * bufferWidth / bufferHeight; + ALOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * bufferHeight < newHeight * bufferWidth) { + newHeight = newWidth * bufferHeight / bufferWidth; + ALOGV("too tall: newHeight = %d", newHeight); + } + + uint32_t currentWidth = static_cast<uint32_t>(crop.width()); + uint32_t currentHeight = static_cast<uint32_t>(crop.height()); + + // The crop is too wide + if (newWidth < currentWidth) { + uint32_t dw = currentWidth - newWidth; + auto halfdw = dw / 2; + outCrop.left += halfdw; + // Not halfdw because it would subtract 1 too few when dw is odd + outCrop.right -= (dw - halfdw); + // The crop is too tall + } else if (newHeight < currentHeight) { + uint32_t dh = currentHeight - newHeight; + auto halfdh = dh / 2; + outCrop.top += halfdh; + // Not halfdh because it would subtract 1 too few when dh is odd + outCrop.bottom -= (dh - halfdh); + } + + ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, + outCrop.bottom); + + return outCrop; +} + +nsecs_t BufferLayerConsumer::getTimestamp() { + BLC_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +android_dataspace BufferLayerConsumer::getCurrentDataSpace() { + BLC_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + +uint64_t BufferLayerConsumer::getFrameNumber() { + BLC_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + +sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const { + Mutex::Autolock lock(mMutex); + + if (outSlot != nullptr) { + *outSlot = mCurrentTexture; + } + + return (mCurrentTextureImage == nullptr) ? NULL : mCurrentTextureImage->graphicBuffer(); +} + +Rect BufferLayerConsumer::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) + : mCurrentCrop; +} + +uint32_t BufferLayerConsumer::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t BufferLayerConsumer::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp<Fence> BufferLayerConsumer::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + +status_t BufferLayerConsumer::doGLFenceWaitLocked() const { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + BLC_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + BLC_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (mCurrentFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync()) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = mCurrentFence->dup(); + if (fenceFd == -1) { + BLC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + BLC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + BLC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = mCurrentFence->waitForever("BufferLayerConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + BLC_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void BufferLayerConsumer::freeBufferLocked(int slotIndex) { + BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + mEglSlots[slotIndex].mEglImage.clear(); + ConsumerBase::freeBufferLocked(slotIndex); +} + +void BufferLayerConsumer::abandonLocked() { + BLC_LOGV("abandonLocked"); + mCurrentTextureImage.clear(); + ConsumerBase::abandonLocked(); +} + +status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) { + return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); +} + +void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat("%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, + mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, + mCurrentTransform); + + ConsumerBase::dumpLocked(result, prefix); +} + +BufferLayerConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) + : mGraphicBuffer(graphicBuffer), + mEglImage(EGL_NO_IMAGE_KHR), + mEglDisplay(EGL_NO_DISPLAY), + mCropRect(Rect::EMPTY_RECT) {} + +BufferLayerConsumer::EglImage::~EglImage() { + if (mEglImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("~EglImage: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + } +} + +status_t BufferLayerConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, const Rect& cropRect, + bool forceCreation) { + // If there's an image and it's no longer valid, destroy it. + bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; + bool displayInvalid = mEglDisplay != eglDisplay; + bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; + if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("createIfNeeded: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + mEglImage = EGL_NO_IMAGE_KHR; + mEglDisplay = EGL_NO_DISPLAY; + } + + // If there's no image, create one. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = eglDisplay; + mCropRect = cropRect; + mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); + } + + // Fail if we can't create a valid image. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = EGL_NO_DISPLAY; + mCropRect.makeInvalid(); + const sp<GraphicBuffer>& buffer = mGraphicBuffer; + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return UNKNOWN_ERROR; + } + + return OK; +} + +void BufferLayerConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { + glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage)); +} + +EGLImageKHR BufferLayerConsumer::EglImage::createImage(EGLDisplay dpy, + const sp<GraphicBuffer>& graphicBuffer, + const Rect& crop) { + EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); + const bool createProtectedImage = + (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, + EGL_TRUE, + EGL_IMAGE_CROP_LEFT_ANDROID, + crop.left, + EGL_IMAGE_CROP_TOP_ANDROID, + crop.top, + EGL_IMAGE_CROP_RIGHT_ANDROID, + crop.right, + EGL_IMAGE_CROP_BOTTOM_ANDROID, + crop.bottom, + createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + createProtectedImage ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + if (!crop.isValid()) { + // No crop rect to set, so leave the crop out of the attrib array. Make + // sure to propagate the protected content attrs if they are set. + attrs[2] = attrs[10]; + attrs[3] = attrs[11]; + attrs[4] = EGL_NONE; + } else if (!isEglImageCroppable(crop)) { + // The crop rect is not at the origin, so we can't set the crop on the + // EGLImage because that's not allowed by the EGL_ANDROID_image_crop + // extension. In the future we can add a layered extension that + // removes this restriction if there is hardware that can support it. + attrs[2] = attrs[10]; + attrs[3] = attrs[11]; + attrs[4] = EGL_NONE; + } + eglInitialize(dpy, 0, 0); + EGLImageKHR image = + eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ALOGE("error creating EGLImage: %#x", error); + eglTerminate(dpy); + } + return image; +} + +}; // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h new file mode 100644 index 0000000000..2b7c2a31ac --- /dev/null +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFERLAYERCONSUMER_H +#define ANDROID_BUFFERLAYERCONSUMER_H + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueueDefs.h> +#include <gui/ConsumerBase.h> + +#include <ui/FenceTime.h> +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class String8; + +/* + * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue, + * and makes them available to OpenGL as a texture. + * + * A typical usage pattern is to set up the BufferLayerConsumer with the + * desired options, and call updateTexImage() when a new frame is desired. + * If a new frame is available, the texture will be updated. If not, + * the previous contents are retained. + * + * By default, the texture is attached to the GL_TEXTURE_EXTERNAL_OES + * texture target, in the EGL context of the first thread that calls + * updateTexImage(). + * + * This class was previously called SurfaceTexture. + */ +class BufferLayerConsumer : public ConsumerBase { +public: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + // BufferLayerConsumer constructs a new BufferLayerConsumer object. If the constructor with + // the tex parameter is used, tex indicates the name of the OpenGL ES + // texture to which images are to be streamed. texTarget specifies the + // OpenGL ES texture target to which the texture will be bound in + // updateTexImage. useFenceSync specifies whether fences should be used to + // synchronize access to buffers if that behavior is enabled at + // compile-time. + // + // A BufferLayerConsumer may be detached from one OpenGL ES context and then + // attached to a different context using the detachFromContext and + // attachToContext methods, respectively. The intention of these methods is + // purely to allow a BufferLayerConsumer to be transferred from one consumer + // context to another. If such a transfer is not needed there is no + // requirement that either of these methods be called. + // + // If the constructor with the tex parameter is used, the BufferLayerConsumer is + // created in a state where it is considered attached to an OpenGL ES + // context for the purposes of the attachToContext and detachFromContext + // methods. However, despite being considered "attached" to a context, the + // specific OpenGL ES context doesn't get latched until the first call to + // updateTexImage. After that point, all calls to updateTexImage must be + // made with the same OpenGL ES context current. + // + // If the constructor without the tex parameter is used, the BufferLayerConsumer is + // created in a detached state, and attachToContext must be called before + // calls to updateTexImage. + BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + // updateTexImage acquires the most recently queued buffer, and sets the + // image contents of the target texture to it. + // + // This call may only be made while the OpenGL ES context to which the + // target texture belongs is bound to the calling thread. + // + // This calls doGLFenceWait to ensure proper synchronization. + status_t updateTexImage(); + + // releaseTexImage releases the texture acquired in updateTexImage(). + // This is intended to be used in single buffer mode. + // + // This call may only be made while the OpenGL ES context to which the + // target texture belongs is bound to the calling thread. + status_t releaseTexImage(); + + // setReleaseFence stores a fence that will signal when the current buffer + // is no longer being read. This fence will be returned to the producer + // when the current buffer is released by updateTexImage(). Multiple + // fences can be set for a given buffer; they will be merged into a single + // union fence. + virtual void setReleaseFence(const sp<Fence>& fence); + + // getTransformMatrix retrieves the 4x4 texture coordinate transform matrix + // associated with the texture image set by the most recent call to + // updateTexImage. + // + // This transform matrix maps 2D homogeneous texture coordinates of the form + // (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture + // coordinate that should be used to sample that location from the texture. + // Sampling the texture outside of the range of this transform is undefined. + // + // This transform is necessary to compensate for transforms that the stream + // content producer may implicitly apply to the content. By forcing users of + // a BufferLayerConsumer to apply this transform we avoid performing an extra + // copy of the data that would be needed to hide the transform from the + // user. + // + // The matrix is stored in column-major order so that it may be passed + // directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv + // functions. + void getTransformMatrix(float mtx[16]); + + // Computes the transform matrix documented by getTransformMatrix + // from the BufferItem sub parts. + static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, + const Rect& cropRect, uint32_t transform, bool filtering); + + // Scale the crop down horizontally or vertically such that it has the + // same aspect ratio as the buffer does. + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + + // getTimestamp retrieves the timestamp associated with the texture image + // set by the most recent call to updateTexImage. + // + // The timestamp is in nanoseconds, and is monotonically increasing. Its + // other semantics (zero point, etc) are source-dependent and should be + // documented by the source. + int64_t getTimestamp(); + + // getDataSpace retrieves the DataSpace associated with the texture image + // set by the most recent call to updateTexImage. + android_dataspace getCurrentDataSpace(); + + // getFrameNumber retrieves the frame number associated with the texture + // image set by the most recent call to updateTexImage. + // + // The frame number is an incrementing counter set to 0 at the creation of + // the BufferQueue associated with this consumer. + uint64_t getFrameNumber(); + + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a with and height of zero is requested. + // A call to setDefaultBufferSize() may trigger requestBuffers() to + // be called from the client. + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + // setFilteringEnabled sets whether the transform matrix should be computed + // for use with bilinear filtering. + void setFilteringEnabled(bool enabled); + + // getCurrentBuffer returns the buffer associated with the current image. + // When outSlot is not nullptr, the current buffer slot index is also + // returned. + sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const; + + // getCurrentTextureTarget returns the texture target of the current + // texture as returned by updateTexImage(). + uint32_t getCurrentTextureTarget() const; + + // getCurrentCrop returns the cropping rectangle of the current buffer. + Rect getCurrentCrop() const; + + // getCurrentTransform returns the transform of the current buffer. + uint32_t getCurrentTransform() const; + + // getCurrentScalingMode returns the scaling mode of the current buffer. + uint32_t getCurrentScalingMode() const; + + // getCurrentFence returns the fence indicating when the current buffer is + // ready to be read from. + sp<Fence> getCurrentFence() const; + + // getCurrentFence returns the FenceTime indicating when the current + // buffer is ready to be read from. + std::shared_ptr<FenceTime> getCurrentFenceTime() const; + + // setConsumerUsageBits overrides the ConsumerBase method to OR + // DEFAULT_USAGE_FLAGS to usage. + status_t setConsumerUsageBits(uint64_t usage); + + // detachFromContext detaches the BufferLayerConsumer from the calling thread's + // current OpenGL ES context. This context must be the same as the context + // that was current for previous calls to updateTexImage. + // + // Detaching a BufferLayerConsumer from an OpenGL ES context will result in the + // deletion of the OpenGL ES texture object into which the images were being + // streamed. After a BufferLayerConsumer has been detached from the OpenGL ES + // context calls to updateTexImage will fail returning INVALID_OPERATION + // until the BufferLayerConsumer is attached to a new OpenGL ES context using the + // attachToContext method. + status_t detachFromContext(); + + // attachToContext attaches a BufferLayerConsumer that is currently in the + // 'detached' state to the current OpenGL ES context. A BufferLayerConsumer is + // in the 'detached' state iff detachFromContext has successfully been + // called and no calls to attachToContext have succeeded since the last + // detachFromContext call. Calls to attachToContext made on a + // BufferLayerConsumer that is not in the 'detached' state will result in an + // INVALID_OPERATION error. + // + // The tex argument specifies the OpenGL ES texture object name in the + // new context into which the image contents will be streamed. A successful + // call to attachToContext will result in this texture object being bound to + // the texture target and populated with the image contents that were + // current at the time of the last call to detachFromContext. + status_t attachToContext(uint32_t tex); + +protected: + // abandonLocked overrides the ConsumerBase method to clear + // mCurrentTextureImage in addition to the ConsumerBase behavior. + virtual void abandonLocked(); + + // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer- + // specific info in addition to the ConsumerBase behavior. + virtual void dumpLocked(String8& result, const char* prefix) const; + + // acquireBufferLocked overrides the ConsumerBase method to update the + // mEglSlots array in addition to the ConsumerBase behavior. + virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) override; + + // releaseBufferLocked overrides the ConsumerBase method to update the + // mEglSlots array in addition to the ConsumerBase. + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) override; + + status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, + EGLSyncKHR eglFence) { + return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); + } + + struct PendingRelease { + PendingRelease() + : isPending(false), + currentTexture(-1), + graphicBuffer(), + display(nullptr), + fence(nullptr) {} + + bool isPending; + int currentTexture; + sp<GraphicBuffer> graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + + // This releases the buffer in the slot referenced by mCurrentTexture, + // then updates state to refer to the BufferItem, which must be a + // newly-acquired buffer. If pendingRelease is not null, the parameters + // which would have been passed to releaseBufferLocked upon the successful + // completion of the method will instead be returned to the caller, so that + // it may call releaseBufferLocked itself later. + status_t updateAndReleaseLocked(const BufferItem& item, + PendingRelease* pendingRelease = nullptr); + + // Binds mTexName and the current buffer to mTexTarget. Uses + // mCurrentTexture if it's set, mCurrentTextureImage if not. If the + // bind succeeds, this calls doGLFenceWait. + status_t bindTextureImageLocked(); + + // Gets the current EGLDisplay and EGLContext values, and compares them + // to mEglDisplay and mEglContext. If the fields have been previously + // set, the values must match; if not, the fields are set to the current + // values. + // The contextCheck argument is used to ensure that a GL context is + // properly set; when set to false, the check is not performed. + status_t checkAndUpdateEglStateLocked(bool contextCheck = false); + +private: + // EglImage is a utility class for tracking and creating EGLImageKHRs. There + // is primarily just one image per slot, but there is also special cases: + // - For releaseTexImage, we use a debug image (mReleasedTexImage) + // - After freeBuffer, we must still keep the current image/buffer + // Reference counting EGLImages lets us handle all these cases easily while + // also only creating new EGLImages from buffers when required. + class EglImage : public LightRefBase<EglImage> { + public: + EglImage(sp<GraphicBuffer> graphicBuffer); + + // createIfNeeded creates an EGLImage if required (we haven't created + // one yet, or the EGLDisplay or crop-rect has changed). + status_t createIfNeeded(EGLDisplay display, const Rect& cropRect, bool forceCreate = false); + + // This calls glEGLImageTargetTexture2DOES to bind the image to the + // texture in the specified texture target. + void bindToTextureTarget(uint32_t texTarget); + + const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } + const native_handle* graphicBufferHandle() { + return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle; + } + + private: + // Only allow instantiation using ref counting. + friend class LightRefBase<EglImage>; + virtual ~EglImage(); + + // createImage creates a new EGLImage from a GraphicBuffer. + EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer, + const Rect& crop); + + // Disallow copying + EglImage(const EglImage& rhs); + void operator=(const EglImage& rhs); + + // mGraphicBuffer is the buffer that was used to create this image. + sp<GraphicBuffer> mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEGLDisplay is the EGLDisplay that was used to create mEglImage. + EGLDisplay mEglDisplay; + + // mCropRect is the crop rectangle passed to EGL when mEglImage + // was created. + Rect mCropRect; + }; + + // freeBufferLocked frees up the given buffer slot. If the slot has been + // initialized this will release the reference to the GraphicBuffer in that + // slot and destroy the EGLImage in that slot. Otherwise it has no effect. + // + // This method must be called with mMutex locked. + virtual void freeBufferLocked(int slotIndex); + + // computeCurrentTransformMatrixLocked computes the transform matrix for the + // current texture. It uses mCurrentTransform and the current GraphicBuffer + // to compute this matrix and stores it in mCurrentTransformMatrix. + // mCurrentTextureImage must not be NULL. + void computeCurrentTransformMatrixLocked(); + + // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + // stream to ensure that it is safe for future OpenGL ES commands to + // access the current texture buffer. + status_t doGLFenceWaitLocked() const; + + // syncForReleaseLocked performs the synchronization needed to release the + // current slot from an OpenGL ES context. If needed it will set the + // current slot's fence to guard against a producer accessing the buffer + // before the outstanding accesses have completed. + status_t syncForReleaseLocked(EGLDisplay dpy); + + // returns a graphic buffer used when the texture image has been released + static sp<GraphicBuffer> getDebugTexImageBuffer(); + + // The default consumer usage flags that BufferLayerConsumer always sets on its + // BufferQueue instance; these will be OR:d with any additional flags passed + // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always + // consume buffers as hardware textures. + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + // mCurrentTextureImage is the EglImage/buffer of the current texture. It's + // possible that this buffer is not associated with any buffer slot, so we + // must track it separately in order to support the getCurrentBuffer method. + sp<EglImage> mCurrentTextureImage; + + // mCurrentCrop is the crop rectangle that applies to the current texture. + // It gets set each time updateTexImage is called. + Rect mCurrentCrop; + + // mCurrentTransform is the transform identifier for the current texture. It + // gets set each time updateTexImage is called. + uint32_t mCurrentTransform; + + // mCurrentScalingMode is the scaling mode for the current texture. It gets + // set each time updateTexImage is called. + uint32_t mCurrentScalingMode; + + // mCurrentFence is the fence received from BufferQueue in updateTexImage. + sp<Fence> mCurrentFence; + + // The FenceTime wrapper around mCurrentFence. + std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE}; + + // mCurrentTransformMatrix is the transform matrix for the current texture. + // It gets computed by computeTransformMatrix each time updateTexImage is + // called. + float mCurrentTransformMatrix[16]; + + // mCurrentTimestamp is the timestamp for the current texture. It + // gets set each time updateTexImage is called. + int64_t mCurrentTimestamp; + + // mCurrentDataSpace is the dataspace for the current texture. It + // gets set each time updateTexImage is called. + android_dataspace mCurrentDataSpace; + + // mCurrentFrameNumber is the frame counter for the current texture. + // It gets set each time updateTexImage is called. + uint64_t mCurrentFrameNumber; + + uint32_t mDefaultWidth, mDefaultHeight; + + // mFilteringEnabled indicates whether the transform matrix is computed for + // use with bilinear filtering. It defaults to true and is changed by + // setFilteringEnabled(). + bool mFilteringEnabled; + + // mTexName is the name of the OpenGL texture to which streamed images will + // be bound when updateTexImage is called. It is set at construction time + // and can be changed with a call to attachToContext. + uint32_t mTexName; + + // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + // extension should be used to prevent buffers from being dequeued before + // it's safe for them to be written. It gets set at construction time and + // never changes. + const bool mUseFenceSync; + + // mTexTarget is the GL texture target with which the GL texture object is + // associated. It is set in the constructor and never changed. It is + // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android + // Browser. In that case it is set to GL_TEXTURE_2D to allow + // glCopyTexSubImage to read from the texture. This is a hack to work + // around a GL driver limitation on the number of FBO attachments, which the + // browser's tile cache exceeds. + const uint32_t mTexTarget; + + // EGLSlot contains the information and object references that + // BufferLayerConsumer maintains about a BufferQueue buffer slot. + struct EglSlot { + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + // mEglImage is the EGLImage created from mGraphicBuffer. + sp<EglImage> mEglImage; + + // mFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + // on a compile-time option) set to a new sync object in updateTexImage. + EGLSyncKHR mEglFence; + }; + + // mEglDisplay is the EGLDisplay with which this BufferLayerConsumer is currently + // associated. It is intialized to EGL_NO_DISPLAY and gets set to the + // current display when updateTexImage is called for the first time and when + // attachToContext is called. + EGLDisplay mEglDisplay; + + // mEglContext is the OpenGL ES context with which this BufferLayerConsumer is + // currently associated. It is initialized to EGL_NO_CONTEXT and gets set + // to the current GL context when updateTexImage is called for the first + // time and when attachToContext is called. + EGLContext mEglContext; + + // mEGLSlots stores the buffers that have been allocated by the BufferQueue + // for each buffer slot. It is initialized to null pointers, and gets + // filled in with the result of BufferQueue::acquire when the + // client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; + + // mCurrentTexture is the buffer slot index of the buffer that is currently + // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + // indicating that no buffer slot is currently bound to the texture. Note, + // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + // that no buffer is bound to the texture. A call to setBufferCount will + // reset mCurrentTexture to INVALID_BUFFER_SLOT. + int mCurrentTexture; + + // mAttached indicates whether the ConsumerBase is currently attached to + // an OpenGL ES context. For legacy reasons, this is initialized to true, + // indicating that the ConsumerBase is considered to be attached to + // whatever context is current at the time of the first updateTexImage call. + // It is set to false by detachFromContext, and then set to true again by + // attachToContext. + bool mAttached; + + // protects static initialization + static Mutex sStaticInitLock; + + // mReleasedTexImageBuffer is a dummy buffer used when in single buffer + // mode and releaseTexImage() has been called + static sp<GraphicBuffer> sReleasedTexImageBuffer; + sp<EglImage> mReleasedTexImage; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFERLAYERCONSUMER_H diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index d5ba09d174..c29dad3c94 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -42,7 +42,7 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, Mutex::Autolock lock(mMutex); if (mAbandoned) { - ALOGE("updateTexImage: GLConsumer is abandoned!"); + ALOGE("updateTexImage: BufferLayerConsumer is abandoned!"); return NO_INIT; } @@ -116,7 +116,7 @@ status_t SurfaceFlingerConsumer::bindTextureImage() status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, uint64_t maxFrameNumber) { - status_t result = GLConsumer::acquireBufferLocked(item, presentWhen, + status_t result = BufferLayerConsumer::acquireBufferLocked(item, presentWhen, maxFrameNumber); if (result == NO_ERROR) { mTransformToDisplayInverse = item->mTransformToDisplayInverse; @@ -190,7 +190,7 @@ sp<Fence> SurfaceFlingerConsumer::getPrevFinalReleaseFence() const { void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) { if (!mPendingRelease.isPending) { - GLConsumer::setReleaseFence(fence); + BufferLayerConsumer::setReleaseFence(fence); return; } auto currentTexture = mPendingRelease.currentTexture; diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h index 7397f2d6aa..cd13938ce5 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.h +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -17,10 +17,10 @@ #ifndef ANDROID_SURFACEFLINGERCONSUMER_H #define ANDROID_SURFACEFLINGERCONSUMER_H +#include "BufferLayerConsumer.h" #include "DispSync.h" #include <ui/Region.h> -#include <gui/GLConsumer.h> namespace android { // ---------------------------------------------------------------------------- @@ -28,9 +28,9 @@ namespace android { class Layer; /* - * This is a thin wrapper around GLConsumer. + * This is a thin wrapper around BufferLayerConsumer. */ -class SurfaceFlingerConsumer : public GLConsumer { +class SurfaceFlingerConsumer : public BufferLayerConsumer { public: static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8; @@ -40,7 +40,7 @@ public: SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer, uint32_t tex, Layer* layer) - : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false), + : BufferLayerConsumer(consumer, tex, BufferLayerConsumer::TEXTURE_EXTERNAL, false, false), mTransformToDisplayInverse(false), mSurfaceDamage(), mLayer(layer) {} @@ -57,14 +57,14 @@ public: uint64_t maxFrameNumber = 0) override; // This version of updateTexImage() takes a functor that may be used to - // reject the newly acquired buffer. Unlike the GLConsumer version, + // reject the newly acquired buffer. Unlike the BufferLayerConsumer version, // this does not guarantee that the buffer has been bound to the GL // texture. status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber); - // See GLConsumer::bindTextureImageLocked(). + // See BufferLayerConsumer::bindTextureImageLocked(). status_t bindTextureImage(); bool getTransformToDisplayInverse() const; @@ -93,7 +93,7 @@ private: wp<ContentsChangedListener> mContentsChangedListener; // Indicates this buffer must be transformed by the inverse transform of the screen - // it is displayed onto. This is applied after GLConsumer::mCurrentTransform. + // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform. // This must be set/read from SurfaceFlinger's main thread. bool mTransformToDisplayInverse; |