Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6748db43 authored by Chia-I Wu's avatar Chia-I Wu
Browse files

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
parent 3498e3c5
Loading
Loading
Loading
Loading
+47 −162
Original line number Diff line number Diff line
@@ -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 @@ namespace android {

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 @@ BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, R
        mDefaultHeight(1),
        mFilteringEnabled(true),
        mRE(engine),
        mEglDisplay(mRE.getEGLDisplay()),
        mTexName(tex),
        mLayer(layer),
        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
@@ -326,13 +272,20 @@ status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t pres
    // 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 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
    // 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 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
    // 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::updateAndReleaseLocked(const BufferItem& item,
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 @@ void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
        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 @@ void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
    if (slotIndex == mCurrentTexture) {
        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
    }
    mEglSlots[slotIndex].mEglImage.clear();
    mImages[slotIndex].clear();
    ConsumerBase::freeBufferLocked(slotIndex);
}

@@ -649,106 +603,37 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const
    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) {}

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;
        mImage{engine},
        mCreated(false),
        mCropWidth(0),
        mCropHeight(0) {}

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
+28 −52
Original line number Diff line number Diff line
@@ -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 @@ protected:
    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 @@ protected:
    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 @@ private:
    // 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 @@ private:

    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 @@ private:

    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,