/* * 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 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::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) : 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(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); va_start(args, operation); getSelf(window)->perform(operation, args); va_end(args); return result; } }; // namespace android::uirenderer::renderthread