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

Commit f023a323 authored by Alec Mouri's avatar Alec Mouri
Browse files

[HWUI] Use ANativeWindow inteception methods in ReliableSurface

Test: boots
Test: manually test with opening and scrolling through settings app
Change-Id: I8d7a44d3ead0b2350318e1514153e256f97ccca5
parent 5e6abe41
Loading
Loading
Loading
Loading
+24 −20
Original line number Diff line number Diff line
@@ -143,10 +143,11 @@ void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) {
    ATRACE_CALL();

    if (surface) {
        mNativeSurface = new ReliableSurface{std::move(surface)};
        mNativeSurface = std::make_unique<ReliableSurface>(std::move(surface));
        mNativeSurface->init();
        if (enableTimeout) {
            // TODO: Fix error handling & re-shorten timeout
            ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms);
            ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms);
        }
    } else {
        mNativeSurface = nullptr;
@@ -161,7 +162,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) {
    }

    ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
    bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode,
    bool hasSurface = mRenderPipeline->setSurface(
            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode,
            mRenderAheadCapacity);

    mFrameNumber = -1;
@@ -428,7 +430,7 @@ void CanvasContext::setPresentTime() {
        presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
                (frameIntervalNanos * (renderAhead + 1));
    }
    native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
    native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
}

void CanvasContext::draw() {
@@ -489,16 +491,18 @@ void CanvasContext::draw() {
        swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
        swap.vsyncTime = mRenderThread.timeLord().latestVsync();
        if (didDraw) {
            nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get());
            nsecs_t dequeueStart =
                    ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow());
            if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
                // Ignoring dequeue duration as it happened prior to frame render start
                // and thus is not part of the frame.
                swap.dequeueDuration = 0;
            } else {
                swap.dequeueDuration =
                        ANativeWindow_getLastDequeueDuration(mNativeSurface.get());
                        ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow());
            }
            swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get());
            swap.queueDuration =
                    ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow());
        } else {
            swap.dequeueDuration = 0;
            swap.queueDuration = 0;
@@ -567,15 +571,17 @@ void CanvasContext::doFrame() {
}

SkISize CanvasContext::getNextFrameSize() const {
    ReliableSurface* surface = mNativeSurface.get();
    if (surface) {
    static constexpr SkISize defaultFrameSize = {INT32_MAX, INT32_MAX};
    if (mNativeSurface == nullptr) {
        return defaultFrameSize;
    }
    ANativeWindow* anw = mNativeSurface->getNativeWindow();

    SkISize size;
        size.fWidth = ANativeWindow_getWidth(surface);
        size.fHeight = ANativeWindow_getHeight(surface);
    size.fWidth = ANativeWindow_getWidth(anw);
    size.fHeight = ANativeWindow_getHeight(anw);
    return size;
}
    return {INT32_MAX, INT32_MAX};
}

void CanvasContext::prepareAndDraw(RenderNode* node) {
    ATRACE_CALL();
@@ -702,11 +708,9 @@ bool CanvasContext::surfaceRequiresRedraw() {
    if (!mNativeSurface) return false;
    if (mHaveNewSurface) return true;

    int width = -1;
    int height = -1;
    ReliableSurface* surface = mNativeSurface.get();
    surface->query(NATIVE_WINDOW_WIDTH, &width);
    surface->query(NATIVE_WINDOW_HEIGHT, &height);
    ANativeWindow* anw = mNativeSurface->getNativeWindow();
    const int width = ANativeWindow_getWidth(anw);
    const int height = ANativeWindow_getHeight(anw);

    return width != mLastFrameWidth || height != mLastFrameHeight;
}
+1 −1
Original line number Diff line number Diff line
@@ -220,7 +220,7 @@ private:
    int32_t mLastFrameHeight = 0;

    RenderThread& mRenderThread;
    sp<ReliableSurface> mNativeSurface;
    std::unique_ptr<ReliableSurface> mNativeSurface;
    // stopped indicates the CanvasContext will reject actual redraw operations,
    // and defer repaint until it is un-stopped
    bool mStopped = false;
+103 −162
Original line number Diff line number Diff line
@@ -26,64 +26,38 @@ namespace android::uirenderer::renderthread {
// to propagate this error back to the caller
constexpr bool DISABLE_BUFFER_PREFETCH = true;

// TODO: Make surface less protected
// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
// that instead
struct SurfaceExposer : Surface {
    // Make warnings happy
    SurfaceExposer() = delete;

    using Surface::cancelBuffer;
    using Surface::dequeueBuffer;
    using Surface::lockBuffer_DEPRECATED;
    using Surface::perform;
    using Surface::queueBuffer;
    using Surface::setBufferCount;
    using Surface::setSwapInterval;
};

#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)

ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
    LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");

    ANativeWindow::setSwapInterval = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
    ANativeWindow::cancelBuffer = hook_cancelBuffer;
    ANativeWindow::queueBuffer = hook_queueBuffer;
    ANativeWindow::query = hook_query;
    ANativeWindow::perform = hook_perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
    ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
}

ReliableSurface::~ReliableSurface() {
    clearReservedBuffer();
    // Clear out the interceptors for proper hygiene.
    // As a concrete example, if the underlying ANativeWindow is associated with
    // an EGLSurface that is still in use, then if we don't clear out the
    // interceptors then we walk into undefined behavior.
    ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr);
    ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr);
    ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr);
    ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr);
}

void ReliableSurface::perform(int operation, va_list args) {
    std::lock_guard _lock{mMutex};
void ReliableSurface::init() {
    int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this);
    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
                        result);

    switch (operation) {
        case NATIVE_WINDOW_SET_USAGE:
            mUsage = va_arg(args, uint32_t);
            break;
        case NATIVE_WINDOW_SET_USAGE64:
            mUsage = va_arg(args, uint64_t);
            break;
        case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
            /* width */ va_arg(args, uint32_t);
            /* height */ va_arg(args, uint32_t);
            mFormat = va_arg(args, PixelFormat);
            break;
        case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
            mFormat = va_arg(args, PixelFormat);
            break;
    }
    result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this);
    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
                        result);

    result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this);
    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
                        result);

    result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this);
    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
                        result);
}

int ReliableSurface::reserveNext() {
@@ -111,7 +85,9 @@ int ReliableSurface::reserveNext() {

    int fenceFd = -1;
    ANativeWindowBuffer* buffer = nullptr;
    int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);

    // Note that this calls back into our own hooked method.
    int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd);

    {
        std::lock_guard _lock{mMutex};
@@ -138,59 +114,11 @@ void ReliableSurface::clearReservedBuffer() {
        mHasDequeuedBuffer = false;
    }
    if (buffer) {
        callProtected(mSurface, cancelBuffer, buffer, releaseFd);
    }
}

int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
    clearReservedBuffer();
    if (isFallbackBuffer(buffer)) {
        if (fenceFd > 0) {
            close(fenceFd);
        }
        return OK;
    }
    int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
    return result;
}

int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
    {
        std::lock_guard _lock{mMutex};
        if (mReservedBuffer) {
            *buffer = mReservedBuffer;
            *fenceFd = mReservedFenceFd.release();
            mReservedBuffer = nullptr;
            return OK;
        }
    }


    int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
    if (result != OK) {
        ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
        *buffer = acquireFallbackBuffer(result);
        *fenceFd = -1;
        return *buffer ? OK : INVALID_OPERATION;
    } else {
        std::lock_guard _lock{mMutex};
        mHasDequeuedBuffer = true;
    }
    return OK;
}

int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
    clearReservedBuffer();

    if (isFallbackBuffer(buffer)) {
        if (fenceFd > 0) {
            close(fenceFd);
        }
        return OK;
        // Note that clearReservedBuffer may be reentrant here, so
        // mReservedBuffer must be cleared once we reach here to avoid recursing
        // forever.
        ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd);
    }

    int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
    return result;
}

bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
@@ -229,82 +157,95 @@ ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) {
    return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
}

Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
    return getSelf(window)->mSurface.get();
}

int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
    return callProtected(getWrapped(window), setSwapInterval, interval);
int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window,
                                        ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
                                        ANativeWindowBuffer** buffer, int* fenceFd) {
    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
    {
        std::lock_guard _lock{rs->mMutex};
        if (rs->mReservedBuffer) {
            *buffer = rs->mReservedBuffer;
            *fenceFd = rs->mReservedFenceFd.release();
            rs->mReservedBuffer = nullptr;
            return OK;
        }

int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
                                        int* fenceFd) {
    return getSelf(window)->dequeueBuffer(buffer, fenceFd);
    }

int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
                                       int fenceFd) {
    return getSelf(window)->cancelBuffer(buffer, fenceFd);
    int result = dequeueBuffer(window, buffer, fenceFd);
    if (result != OK) {
        ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
        *buffer = rs->acquireFallbackBuffer(result);
        *fenceFd = -1;
        return *buffer ? OK : INVALID_OPERATION;
    } else {
        std::lock_guard _lock{rs->mMutex};
        rs->mHasDequeuedBuffer = true;
    }

int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
                                      int fenceFd) {
    return getSelf(window)->queueBuffer(buffer, fenceFd);
    return OK;
}

int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
                                                   ANativeWindowBuffer** buffer) {
    ANativeWindowBuffer* buf;
    int fenceFd = -1;
    int result = window->dequeueBuffer(window, &buf, &fenceFd);
    if (result != OK) {
        return result;
int ReliableSurface::hook_cancelBuffer(ANativeWindow* window,
                                       ANativeWindow_cancelBufferFn cancelBuffer, void* data,
                                       ANativeWindowBuffer* buffer, int fenceFd) {
    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
    rs->clearReservedBuffer();
    if (rs->isFallbackBuffer(buffer)) {
        if (fenceFd > 0) {
            close(fenceFd);
        }
    sp<Fence> fence(new Fence(fenceFd));
    int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
    if (waitResult != OK) {
        ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
        window->cancelBuffer(window, buf, -1);
        return waitResult;
        return OK;
    }
    *buffer = buf;
    return result;
    return cancelBuffer(window, buffer, fenceFd);
}

int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
                                                  ANativeWindowBuffer* buffer) {
    return window->cancelBuffer(window, buffer, -1);
}
int ReliableSurface::hook_queueBuffer(ANativeWindow* window,
                                      ANativeWindow_queueBufferFn queueBuffer, void* data,
                                      ANativeWindowBuffer* buffer, int fenceFd) {
    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
    rs->clearReservedBuffer();

int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
                                                ANativeWindowBuffer* buffer) {
    // This method is a no-op in Surface as well
    return OK;
    if (rs->isFallbackBuffer(buffer)) {
        if (fenceFd > 0) {
            close(fenceFd);
        }

int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
                                                 ANativeWindowBuffer* buffer) {
    return window->queueBuffer(window, buffer, -1);
        return OK;
    }

int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
    return getWrapped(window)->query(what, value);
    return queueBuffer(window, buffer, fenceFd);
}

int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform,
                                  void* data, int operation, va_list args) {
    // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
    // TODO: Filter to things that only affect the reserved buffer
    // TODO: Can we mutate the reserved buffer in some cases?
    getSelf(window)->clearReservedBuffer();
    va_list args;
    va_start(args, operation);
    int result = callProtected(getWrapped(window), perform, operation, args);
    va_end(args);
    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
    rs->clearReservedBuffer();

    va_start(args, operation);
    getSelf(window)->perform(operation, args);
    va_end(args);
    va_list argsCopy;
    va_copy(argsCopy, args);
    int result = perform(window, operation, argsCopy);

    {
        std::lock_guard _lock{rs->mMutex};

        switch (operation) {
            case ANATIVEWINDOW_PERFORM_SET_USAGE:
                rs->mUsage = va_arg(args, uint32_t);
                break;
            case ANATIVEWINDOW_PERFORM_SET_USAGE64:
                rs->mUsage = va_arg(args, uint64_t);
                break;
            case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
                /* width */ va_arg(args, uint32_t);
                /* height */ va_arg(args, uint32_t);
                rs->mFormat = va_arg(args, PixelFormat);
                break;
            case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
                rs->mFormat = va_arg(args, PixelFormat);
                break;
        }
    }
    return result;
}

+24 −23
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#pragma once

#include <apex/window.h>
#include <gui/Surface.h>
#include <utils/Macros.h>
#include <utils/StrongPointer.h>
@@ -24,13 +25,20 @@

namespace android::uirenderer::renderthread {

class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
class ReliableSurface {
    PREVENT_COPY_AND_ASSIGN(ReliableSurface);

public:
    ReliableSurface(sp<Surface>&& surface);
    ~ReliableSurface();

    // Performs initialization that is not safe to do in the constructor.
    // For instance, registering ANativeWindow interceptors with ReliableSurface
    // passed as the data pointer is not safe.
    void init();

    ANativeWindow* getNativeWindow() { return mSurface.get(); }

    int reserveNext();

    void allocateBuffers() { mSurface->allocateBuffers(); }
@@ -61,7 +69,7 @@ public:
    }

private:
    const sp<Surface> mSurface;
    sp<Surface> mSurface;

    mutable std::mutex mMutex;

@@ -78,27 +86,20 @@ private:
    ANativeWindowBuffer* acquireFallbackBuffer(int error);
    void clearReservedBuffer();

    void perform(int operation, va_list args);
    int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
    int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
    int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);

    static Surface* getWrapped(const ANativeWindow*);

    // ANativeWindow hooks
    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
    static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
                                  int* fenceFd);
    static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);

    static int hook_perform(ANativeWindow* window, int operation, ...);
    static int hook_query(const ANativeWindow* window, int what, int* value);
    static int hook_setSwapInterval(ANativeWindow* window, int interval);

    static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
    static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
    static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
    static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
    // ANativeWindow hooks. When an ANativeWindow_* method is called on the
    // underlying ANativeWindow, these methods will intercept the original call.
    // For example, an EGL driver would call into these hooks instead of the
    // original methods.
    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindow_cancelBufferFn cancelBuffer,
                                 void* data, ANativeWindowBuffer* buffer, int fenceFd);
    static int hook_dequeueBuffer(ANativeWindow* window,
                                  ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
                                  ANativeWindowBuffer** buffer, int* fenceFd);
    static int hook_queueBuffer(ANativeWindow* window, ANativeWindow_queueBufferFn queueBuffer,
                                void* data, ANativeWindowBuffer* buffer, int fenceFd);

    static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data,
                            int operation, va_list args);
};

};  // namespace android::uirenderer::renderthread