diff options
author | John Reck <jreck@google.com> | 2018-12-03 13:26:43 -0800 |
---|---|---|
committer | John Reck <jreck@google.com> | 2018-12-06 15:18:27 -0800 |
commit | 848f651554704ca506b8f72e90f5a99b19abc09f (patch) | |
tree | 7f9defac502cc8593754c0483fe17c309f5bf262 /libs/hwui/renderthread/ReliableSurface.cpp | |
parent | f660c012b080496eb70ea77f141f346e80bb6cfa (diff) |
Revert "Revert "Make HWUI's surface Reliable^TM""
This reverts commit dd08224e4d75689c42bf452d44617f84072649a1.
Bug: 120097554
Bug: 120342364
Bug: 120421537
Test: verified emulator can boot OK, with no log warnings
Test: verified keyboard works in landscape
Change-Id: Iae2cd1140d81755b126adfb21ec885181f3bc6b6
Diffstat (limited to 'libs/hwui/renderthread/ReliableSurface.cpp')
-rw-r--r-- | libs/hwui/renderthread/ReliableSurface.cpp | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp new file mode 100644 index 000000000000..6f2b9df918e3 --- /dev/null +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "ReliableSurface.h" + +#include <private/android/AHardwareBufferHelpers.h> + +namespace android::uirenderer::renderthread { + +// TODO: Re-enable after addressing more of the TODO's +// With this disabled we won't have a good up-front signal that the surface is no longer valid, +// however we can at least handle that reactively post-draw. There's just not a good mechanism +// 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::setBufferCount; + using Surface::setSwapInterval; + using Surface::dequeueBuffer; + using Surface::queueBuffer; + using Surface::cancelBuffer; + using Surface::lockBuffer_DEPRECATED; + using Surface::perform; +}; + +#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(); +} + +void ReliableSurface::perform(int operation, va_list args) { + std::lock_guard _lock{mMutex}; + + 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; + } +} + +int ReliableSurface::reserveNext() { + { + std::lock_guard _lock{mMutex}; + if (mReservedBuffer) { + ALOGW("reserveNext called but there was already a buffer reserved?"); + return OK; + } + if (mInErrorState) { + return UNKNOWN_ERROR; + } + if (mHasDequeuedBuffer) { + return OK; + } + if constexpr (DISABLE_BUFFER_PREFETCH) { + return OK; + } + } + + // TODO: Update this to better handle when requested dimensions have changed + // Currently the driver does this via query + perform but that's after we've already + // reserved a buffer. Should we do that logic instead? Or should we drop + // the backing Surface to the ground and go full manual on the IGraphicBufferProducer instead? + + int fenceFd = -1; + ANativeWindowBuffer* buffer = nullptr; + int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd); + + { + std::lock_guard _lock{mMutex}; + LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext"); + mReservedBuffer = buffer; + mReservedFenceFd.reset(fenceFd); + } + + return result; +} + +void ReliableSurface::clearReservedBuffer() { + ANativeWindowBuffer* buffer = nullptr; + int releaseFd = -1; + { + std::lock_guard _lock{mMutex}; + if (mReservedBuffer) { + ALOGW("Reserved buffer %p was never used", mReservedBuffer); + buffer = mReservedBuffer; + releaseFd = mReservedFenceFd.release(); + } + mReservedBuffer = nullptr; + mReservedFenceFd.reset(); + 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(); + *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; + } + + int result = callProtected(mSurface, queueBuffer, buffer, fenceFd); + return result; +} + +bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const { + if (!mScratchBuffer || !windowBuffer) { + return false; + } + ANativeWindowBuffer* scratchBuffer = + AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); + return windowBuffer == scratchBuffer; +} + +ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { + std::lock_guard _lock{mMutex}; + mInErrorState = true; + + if (mScratchBuffer) { + return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); + } + + AHardwareBuffer_Desc desc; + desc.usage = mUsage; + desc.format = mFormat; + desc.width = 1; + desc.height = 1; + desc.layers = 1; + desc.rfu0 = 0; + desc.rfu1 = 0; + AHardwareBuffer* newBuffer = nullptr; + int err = AHardwareBuffer_allocate(&desc, &newBuffer); + if (err) { + // Allocate failed, that sucks + ALOGW("Failed to allocate scratch buffer, error=%d", err); + return nullptr; + } + mScratchBuffer.reset(newBuffer); + 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, 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 ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, + int fenceFd) { + return getSelf(window)->queueBuffer(buffer, fenceFd); +} + +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; + } + 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; + } + *buffer = buf; + return result; +} + +int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + return window->cancelBuffer(window, buffer, -1); +} + +int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + // This method is a no-op in Surface as well + return OK; +} + +int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + return window->queueBuffer(window, buffer, -1); +} + +int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) { + return getWrapped(window)->query(what, value); +} + +int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { + // 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); + + switch (operation) { + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + case NATIVE_WINDOW_SET_USAGE: + case NATIVE_WINDOW_SET_USAGE64: + va_start(args, operation); + getSelf(window)->perform(operation, args); + va_end(args); + break; + default: + break; + } + + return result; +} + +}; // namespace android::uirenderer::renderthread
\ No newline at end of file |