surfaceflinger: remove EGL/GLES dependency from BufferLayerConsumer
Use RE::Image and RenderEngine for image creation and texture
binding. After this change, BufferLayerConsumer no longer depends
on EGL/GLES.
Test: SurfaceFlinger_test
Change-Id: I7bade001181ffacf550130adf356b023a7da2d02
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index fb6fa03..e39ebcc 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -27,10 +27,6 @@
#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>
@@ -49,11 +45,6 @@
#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
@@ -65,50 +56,6 @@
static const mat4 mtxIdentity;
-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, RenderEngine& engine,
uint32_t tex, Layer* layer)
: ConsumerBase(bq, false),
@@ -125,7 +72,6 @@
mDefaultHeight(1),
mFilteringEnabled(true),
mRE(engine),
- mEglDisplay(mRE.getEGLDisplay()),
mTexName(tex),
mLayer(layer),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
@@ -326,13 +272,20 @@
// 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);
+ mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE);
}
return NO_ERROR;
}
+bool BufferLayerConsumer::canUseImageCrop(const Rect& crop) const {
+ // If the crop rect is not at the origin, 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.
+ return mRE.supportsImageCrop() && crop.left == 0 && crop.top == 0;
+}
+
status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
PendingRelease* pendingRelease) {
status_t err = NO_ERROR;
@@ -344,10 +297,10 @@
// 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);
+ const Rect& imageCrop = canUseImageCrop(item.mCrop) ? item.mCrop : Rect::EMPTY_RECT;
+ err = mImages[slot]->createIfNeeded(imageCrop);
if (err != NO_ERROR) {
- BLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
- slot);
+ BLC_LOGW("updateAndRelease: unable to createImage on slot=%d", slot);
releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
return UNKNOWN_ERROR;
}
@@ -372,7 +325,7 @@
// 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;
+ sp<Image> nextTextureImage = mImages[slot];
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
@@ -414,19 +367,21 @@
status_t BufferLayerConsumer::bindTextureImageLocked() {
mRE.checkErrors();
- glBindTexture(sTexTarget, mTexName);
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == NULL) {
BLC_LOGE("bindTextureImage: no currently-bound texture");
+ mRE.bindExternalTextureImage(mTexName, RE::Image(mRE));
return NO_INIT;
}
- status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop);
+ const Rect& imageCrop = canUseImageCrop(mCurrentCrop) ? mCurrentCrop : Rect::EMPTY_RECT;
+ status_t err = mCurrentTextureImage->createIfNeeded(imageCrop);
if (err != NO_ERROR) {
- BLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
- mCurrentTexture);
+ BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
+ mRE.bindExternalTextureImage(mTexName, RE::Image(mRE));
return UNKNOWN_ERROR;
}
- mCurrentTextureImage->bindToTextureTarget(sTexTarget);
+
+ mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image());
// Wait for the new buffer to be ready.
return doFenceWaitLocked();
@@ -488,10 +443,9 @@
BLC_LOGD("computeCurrentTransformMatrixLocked: "
"mCurrentTextureImage is NULL");
}
- GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf,
- isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT
- : mCurrentCrop,
- mCurrentTransform, mFilteringEnabled);
+ const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop;
+ GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
+ mFilteringEnabled);
}
nsecs_t BufferLayerConsumer::getTimestamp() {
@@ -592,7 +546,7 @@
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
- mEglSlots[slotIndex].mEglImage.clear();
+ mImages[slotIndex].clear();
ConsumerBase::freeBufferLocked(slotIndex);
}
@@ -649,106 +603,37 @@
ConsumerBase::dumpLocked(result, prefix);
}
-BufferLayerConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer, const RenderEngine& engine)
: mGraphicBuffer(graphicBuffer),
- mEglImage(EGL_NO_IMAGE_KHR),
- mEglDisplay(EGL_NO_DISPLAY),
- mCropRect(Rect::EMPTY_RECT) {}
+ mImage{engine},
+ mCreated(false),
+ mCropWidth(0),
+ mCropHeight(0) {}
-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) {
- // 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)) {
- if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
- ALOGE("createIfNeeded: eglDestroyImageKHR failed");
- }
- eglTerminate(mEglDisplay);
- mEglImage = EGL_NO_IMAGE_KHR;
- mEglDisplay = EGL_NO_DISPLAY;
+status_t BufferLayerConsumer::Image::createIfNeeded(const Rect& imageCrop) {
+ const int32_t cropWidth = imageCrop.width();
+ const int32_t cropHeight = imageCrop.height();
+ if (mCreated && mCropWidth == cropWidth && mCropHeight == cropHeight) {
+ return OK;
}
- // If there's no image, create one.
- if (mEglImage == EGL_NO_IMAGE_KHR) {
- mEglDisplay = eglDisplay;
- mCropRect = cropRect;
- mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
- }
+ mCreated = mImage.setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(),
+ mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED,
+ cropWidth, cropHeight);
+ if (mCreated) {
+ mCropWidth = cropWidth;
+ mCropHeight = cropHeight;
+ } else {
+ mCropWidth = 0;
+ mCropHeight = 0;
- // 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;
+ return mCreated ? OK : UNKNOWN_ERROR;
}
}; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 087a9a0..6a177f9 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -17,8 +17,7 @@
#ifndef ANDROID_BUFFERLAYERCONSUMER_H
#define ANDROID_BUFFERLAYERCONSUMER_H
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
+#include "RenderEngine/Image.h"
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
@@ -206,10 +205,12 @@
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.
+ // mImages array in addition to the ConsumerBase behavior.
virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
+ bool canUseImageCrop(const Rect& crop) const;
+
struct PendingRelease {
PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
@@ -227,59 +228,48 @@
status_t updateAndReleaseLocked(const BufferItem& item,
PendingRelease* pendingRelease = nullptr);
- // Binds mTexName and the current buffer to sTexTarget. Uses
+ // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. Uses
// mCurrentTexture if it's set, mCurrentTextureImage if not. If the
// bind succeeds, this calls doFenceWait.
status_t bindTextureImageLocked();
private:
- // EglImage is a utility class for tracking and creating EGLImageKHRs. There
+ // Image is a utility class for tracking and creating RE::Images. There
// is primarily just one image per slot, but there is also special cases:
// - 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> {
+ // Reference counting RE::Images lets us handle all these cases easily while
+ // also only creating new RE::Images from buffers when required.
+ class Image : public LightRefBase<Image> {
public:
- EglImage(sp<GraphicBuffer> graphicBuffer);
+ Image(sp<GraphicBuffer> graphicBuffer, const RenderEngine& engine);
- // 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);
+ Image(const Image& rhs) = delete;
+ Image& operator=(const Image& rhs) = delete;
- // This calls glEGLImageTargetTexture2DOES to bind the image to the
- // texture in the specified texture target.
- void bindToTextureTarget(uint32_t texTarget);
+ // createIfNeeded creates an RE::Image if required (we haven't created
+ // one yet, or the crop-rect has changed).
+ status_t createIfNeeded(const Rect& imageCrop);
const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
const native_handle* graphicBufferHandle() {
return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
}
+ const RE::Image& image() const { return mImage; }
+
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);
+ friend class LightRefBase<Image>;
+ virtual ~Image() = default;
// 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;
+ // mImage is the image created from mGraphicBuffer.
+ RE::Image mImage;
+ bool mCreated;
+ int32_t mCropWidth;
+ int32_t mCropHeight;
};
// freeBufferLocked frees up the given buffer slot. If the slot has been
@@ -312,20 +302,16 @@
// the outstanding accesses have completed.
status_t syncForReleaseLocked();
- // sTexTarget is the GL texture target with which the GL texture object is
- // associated.
- static constexpr uint32_t sTexTarget = 0x8D65; // GL_TEXTURE_EXTERNAL_OES
-
// 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
+ // mCurrentTextureImage is the Image/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;
+ sp<Image> mCurrentTextureImage;
// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set each time updateTexImage is called.
@@ -379,9 +365,6 @@
RenderEngine& mRE;
- // mEglDisplay is initialized to RenderEngine's EGLDisplay.
- const EGLDisplay mEglDisplay;
-
// 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.
const uint32_t mTexName;
@@ -391,21 +374,14 @@
wp<ContentsChangedListener> mContentsChangedListener;
- // EGLSlot contains the information and object references that
- // BufferLayerConsumer maintains about a BufferQueue buffer slot.
- struct EglSlot {
- // mEglImage is the EGLImage created from mGraphicBuffer.
- sp<EglImage> mEglImage;
- };
-
- // mEGLSlots stores the buffers that have been allocated by the BufferQueue
+ // mImages 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];
+ sp<Image> mImages[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,