diff options
| author | 2019-10-27 04:44:29 +0000 | |
|---|---|---|
| committer | 2019-10-27 04:44:29 +0000 | |
| commit | 1aed8b369dd977fba69134fc03bf3762c26c560c (patch) | |
| tree | 5840f75cb9669f1da1c4aae8ab1cf2dedd016fb8 /libs | |
| parent | 19c8513c7b7d75ddd78d1b4c6948e4dadc1e50f7 (diff) | |
| parent | 0205f87a0121f1e6057055752365a01b390c60ec (diff) | |
Merge "Merge Coral/Flame into AOSP master"
Diffstat (limited to 'libs')
27 files changed, 766 insertions, 192 deletions
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 40f6b43802..8e762f193f 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -46,6 +46,7 @@ static const char* native_processes_to_dump[] = { static const char* hal_interfaces_to_dump[] { "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", + "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 24b6c2d6de..4a39069a0e 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -213,7 +213,8 @@ void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { case GraphicsEnv::Driver::GL: case GraphicsEnv::Driver::GL_UPDATED: case GraphicsEnv::Driver::ANGLE: { - if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE) { + if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE || + mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) { mGpuStats.glDriverToLoad = driver; break; } @@ -225,7 +226,8 @@ void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { } case Driver::VULKAN: case Driver::VULKAN_UPDATED: { - if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE) { + if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE || + mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) { mGpuStats.vkDriverToLoad = driver; break; } diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 59cb8e0a2b..a615fbf812 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -34,6 +34,7 @@ cc_library_shared { "BufferItemConsumer.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", + "DebugEGLImageTracker.cpp", "DisplayEventReceiver.cpp", "GLConsumer.cpp", "GuiConfig.cpp", diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 9c311a314f..92ab41019e 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -936,6 +936,15 @@ status_t BufferQueueProducer::queueBuffer(int slot, } } + // Make sure to merge the damage rect from the frame we're about + // to drop into the new frame's damage rect. + if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT || + item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) { + item.mSurfaceDamage = Region::INVALID_REGION; + } else { + item.mSurfaceDamage |= last.mSurfaceDamage; + } + // Overwrite the droppable buffer with the incoming one mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item; frameReplacedListener = mCore->mConsumerListener; diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp new file mode 100644 index 0000000000..ab6f36444a --- /dev/null +++ b/libs/gui/DebugEGLImageTracker.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2019 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 <android-base/stringprintf.h> +#include <cutils/properties.h> +#include <gui/DebugEGLImageTracker.h> + +#include <cinttypes> +#include <unordered_map> + +using android::base::StringAppendF; + +std::mutex DebugEGLImageTracker::mInstanceLock; +std::atomic<DebugEGLImageTracker *> DebugEGLImageTracker::mInstance; + +class DebugEGLImageTrackerNoOp : public DebugEGLImageTracker { +public: + DebugEGLImageTrackerNoOp() = default; + ~DebugEGLImageTrackerNoOp() override = default; + void create(const char * /*from*/) override {} + void destroy(const char * /*from*/) override {} + + void dump(std::string & /*result*/) override {} +}; + +class DebugEGLImageTrackerImpl : public DebugEGLImageTracker { +public: + DebugEGLImageTrackerImpl() = default; + ~DebugEGLImageTrackerImpl() override = default; + void create(const char * /*from*/) override; + void destroy(const char * /*from*/) override; + + void dump(std::string & /*result*/) override; + +private: + std::mutex mLock; + std::unordered_map<std::string, int64_t> mCreateTracker; + std::unordered_map<std::string, int64_t> mDestroyTracker; + + int64_t mTotalCreated = 0; + int64_t mTotalDestroyed = 0; +}; + +DebugEGLImageTracker *DebugEGLImageTracker::getInstance() { + std::lock_guard lock(mInstanceLock); + if (mInstance == nullptr) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.enable_egl_image_tracker", value, "0"); + const bool enabled = static_cast<bool>(atoi(value)); + + if (enabled) { + mInstance = new DebugEGLImageTrackerImpl(); + } else { + mInstance = new DebugEGLImageTrackerNoOp(); + } + } + + return mInstance; +} + +void DebugEGLImageTrackerImpl::create(const char *from) { + std::lock_guard lock(mLock); + mCreateTracker[from]++; + mTotalCreated++; +} + +void DebugEGLImageTrackerImpl::destroy(const char *from) { + std::lock_guard lock(mLock); + mDestroyTracker[from]++; + mTotalDestroyed++; +} + +void DebugEGLImageTrackerImpl::dump(std::string &result) { + std::lock_guard lock(mLock); + StringAppendF(&result, "Live EGL Image objects: %" PRIi64 "\n", + mTotalCreated - mTotalDestroyed); + StringAppendF(&result, "Total EGL Image created: %" PRIi64 "\n", mTotalCreated); + for (const auto &[from, count] : mCreateTracker) { + StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); + } + StringAppendF(&result, "Total EGL Image destroyed: %" PRIi64 "\n", mTotalDestroyed); + for (const auto &[from, count] : mDestroyTracker) { + StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); + } +} diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index f5cf1c4d5a..b8faa2df4c 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -32,10 +32,11 @@ namespace android { // --------------------------------------------------------------------------- -DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { +DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource, + ISurfaceComposer::ConfigChanged configChanged) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); if (sf != nullptr) { - mEventConnection = sf->createDisplayEventConnection(vsyncSource); + mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged); if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 8d66154bdd..8199c98582 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -34,6 +34,7 @@ #include <math/mat4.h> #include <gui/BufferItem.h> +#include <gui/DebugEGLImageTracker.h> #include <gui/GLConsumer.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> @@ -944,6 +945,7 @@ GLConsumer::EglImage::~EglImage() { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("~EglImage: eglDestroyImageKHR failed"); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); } } @@ -957,6 +959,7 @@ status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); mEglImage = EGL_NO_IMAGE_KHR; mEglDisplay = EGL_NO_DISPLAY; @@ -1006,7 +1009,10 @@ EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, EGLint error = eglGetError(); ALOGE("error creating EGLImage: %#x", error); eglTerminate(dpy); + } else { + DEBUG_EGL_IMAGE_TRACKER_CREATE(); } + return image; } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 9590df7c8f..12deaf0bd6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -278,8 +278,8 @@ public: return NO_ERROR; } - virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource) - { + virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource, + ConfigChanged configChanged) { Parcel data, reply; sp<IDisplayEventConnection> result; int err = data.writeInterfaceToken( @@ -288,6 +288,7 @@ public: return result; } data.writeInt32(static_cast<int32_t>(vsyncSource)); + data.writeInt32(static_cast<int32_t>(configChanged)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); @@ -1155,8 +1156,11 @@ status_t BnSurfaceComposer::onTransact( } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IDisplayEventConnection> connection(createDisplayEventConnection( - static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()))); + auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()); + auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32()); + + sp<IDisplayEventConnection> connection( + createDisplayEventConnection(vsyncSource, configChanged)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index e6eb327c6f..9fe5de82d1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1920,7 +1920,8 @@ status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) return OK; } -status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer) { +status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, + Dataspace dataspace) { if (buffer == nullptr) { return BAD_VALUE; } @@ -1929,6 +1930,11 @@ status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffe if (err != OK) { return err; } + ui::Dataspace tmpDataspace = surface->getBuffersDataSpace(); + err = surface->setBuffersDataSpace(dataspace); + if (err != OK) { + return err; + } err = surface->attachBuffer(buffer->getNativeBuffer()); if (err != OK) { return err; @@ -1937,6 +1943,10 @@ status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffe if (err != OK) { return err; } + err = surface->setBuffersDataSpace(tmpDataspace); + if (err != OK) { + return err; + } err = surface->disconnect(NATIVE_WINDOW_API_CPU); return err; } diff --git a/libs/gui/include/gui/DebugEGLImageTracker.h b/libs/gui/include/gui/DebugEGLImageTracker.h new file mode 100644 index 0000000000..5d369c9a35 --- /dev/null +++ b/libs/gui/include/gui/DebugEGLImageTracker.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <atomic> +#include <mutex> +#include <string> + +class DebugEGLImageTracker { +public: + static DebugEGLImageTracker *getInstance(); + + virtual void create(const char *from) = 0; + virtual void destroy(const char *from) = 0; + + virtual void dump(std::string &result) = 0; + +protected: + DebugEGLImageTracker() = default; + virtual ~DebugEGLImageTracker() = default; + DebugEGLImageTracker(const DebugEGLImageTracker &) = delete; + + static std::mutex mInstanceLock; + static std::atomic<DebugEGLImageTracker *> mInstance; +}; + +#define DEBUG_EGL_IMAGE_TRACKER_CREATE() \ + (DebugEGLImageTracker::getInstance()->create(__PRETTY_FUNCTION__)) +#define DEBUG_EGL_IMAGE_TRACKER_DESTROY() \ + (DebugEGLImageTracker::getInstance()->destroy(__PRETTY_FUNCTION__))
\ No newline at end of file diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 22de751498..a558cf9e18 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -88,10 +88,13 @@ public: * DisplayEventReceiver creates and registers an event connection with * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate * or requestNextVsync to receive them. + * To receive Config Changed events specify this in the constructor. * Other events start being delivered immediately. */ explicit DisplayEventReceiver( - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp); + ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, + ISurfaceComposer::ConfigChanged configChanged = + ISurfaceComposer::eConfigChangedSuppress); /* * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e2f77365b3..c84910b6ec 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -90,6 +90,8 @@ public: eVsyncSourceSurfaceFlinger = 1 }; + enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 }; + /* * Create a connection with SurfaceFlinger. */ @@ -97,7 +99,8 @@ public: /* return an IDisplayEventConnection */ virtual sp<IDisplayEventConnection> createDisplayEventConnection( - VsyncSource vsyncSource = eVsyncSourceApp) = 0; + VsyncSource vsyncSource = eVsyncSourceApp, + ConfigChanged configChanged = eConfigChangedSuppress) = 0; /* create a virtual display * requires ACCESS_SURFACE_FLINGER permission. diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 0c471bb701..5c6a1ee383 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -292,7 +292,8 @@ public: ui::Dataspace getBuffersDataSpace(); - static status_t attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer); + static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, + ui::Dataspace dataspace); protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 960cf1846e..d3708586f5 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -548,8 +548,8 @@ public: } sp<ISurfaceComposerClient> createConnection() override { return nullptr; } - sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource) - override { + sp<IDisplayEventConnection> createDisplayEventConnection( + ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override { return nullptr; } sp<IBinder> createDisplay(const String8& /*displayName*/, diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 36211ca733..cc252d67ae 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -26,6 +26,7 @@ cc_defaults { "libgui", "liblog", "libnativewindow", + "libprocessgroup", "libsync", "libui", "libutils", @@ -51,6 +52,7 @@ filegroup { "gl/GLExtensions.cpp", "gl/GLFramebuffer.cpp", "gl/GLImage.cpp", + "gl/ImageManager.cpp", "gl/Program.cpp", "gl/ProgramCache.cpp", ], diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 46a8e9eecf..d2a7525113 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -19,9 +19,8 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "GLESRenderEngine.h" - -#include <math.h> +#include <sched.h> +#include <cmath> #include <fstream> #include <sstream> #include <unordered_set> @@ -31,6 +30,7 @@ #include <android-base/stringprintf.h> #include <cutils/compiler.h> #include <cutils/properties.h> +#include <gui/DebugEGLImageTracker.h> #include <renderengine/Mesh.h> #include <renderengine/Texture.h> #include <renderengine/private/Description.h> @@ -42,6 +42,7 @@ #include <ui/Region.h> #include <utils/KeyedVector.h> #include <utils/Trace.h> +#include "GLESRenderEngine.h" #include "GLExtensions.h" #include "GLFramebuffer.h" #include "GLImage.h" @@ -422,10 +423,13 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG mTraceGpuCompletion = true; mFlushTracer = std::make_unique<FlushTracer>(this); } + mImageManager = std::make_unique<ImageManager>(this); mDrawingBuffer = createFramebuffer(); } GLESRenderEngine::~GLESRenderEngine() { + // Destroy the image manager first. + mImageManager = nullptr; std::lock_guard<std::mutex> lock(mRenderingMutex); unbindFrameBuffer(mDrawingBuffer.get()); mDrawingBuffer = nullptr; @@ -433,6 +437,7 @@ GLESRenderEngine::~GLESRenderEngine() { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -614,64 +619,51 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } } -status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - return cacheExternalTextureBufferLocked(buffer); -} - status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, const sp<Fence>& bufferFence) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - return bindExternalTextureBufferLocked(texName, buffer, bufferFence); -} - -status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) { if (buffer == nullptr) { return BAD_VALUE; } ATRACE_CALL(); - if (mImageCache.count(buffer->getId()) > 0) { - return NO_ERROR; + bool found = false; + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto cachedImage = mImageCache.find(buffer->getId()); + found = (cachedImage != mImageCache.end()); } - std::unique_ptr<Image> newImage = createImage(); - - bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), - buffer->getUsage() & GRALLOC_USAGE_PROTECTED); - if (!created) { - 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 NO_INIT; + // If we couldn't find the image in the cache at this time, then either + // SurfaceFlinger messed up registering the buffer ahead of time or we got + // backed up creating other EGLImages. + if (!found) { + status_t cacheResult = mImageManager->cache(buffer); + if (cacheResult != NO_ERROR) { + return cacheResult; + } } - mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); - return NO_ERROR; -} - -status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, - const sp<GraphicBuffer>& buffer, - const sp<Fence>& bufferFence) { - ATRACE_CALL(); - status_t cacheResult = cacheExternalTextureBufferLocked(buffer); - - if (cacheResult != NO_ERROR) { - return cacheResult; - } + // Whether or not we needed to cache, re-check mImageCache to make sure that + // there's an EGLImage. The current threading model guarantees that we don't + // destroy a cached image until it's really not needed anymore (i.e. this + // function should not be called), so the only possibility is that something + // terrible went wrong and we should just bind something and move on. + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto cachedImage = mImageCache.find(buffer->getId()); - auto cachedImage = mImageCache.find(buffer->getId()); + if (cachedImage == mImageCache.end()) { + // We failed creating the image if we got here, so bail out. + ALOGE("Failed to create an EGLImage when rendering"); + bindExternalTextureImage(texName, *createImage()); + return NO_INIT; + } - if (cachedImage == mImageCache.end()) { - // We failed creating the image if we got here, so bail out. - bindExternalTextureImage(texName, *createImage()); - return NO_INIT; + bindExternalTextureImage(texName, *cachedImage->second); } - bindExternalTextureImage(texName, *cachedImage->second); - // Wait for the new buffer to be ready. if (bufferFence != nullptr && bufferFence->isValid()) { if (GLExtensions::getInstance().hasWaitSync()) { @@ -696,13 +688,81 @@ status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, return NO_ERROR; } +void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + mImageManager->cacheAsync(buffer, nullptr); +} + +std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting( + const sp<GraphicBuffer>& buffer) { + auto barrier = std::make_shared<ImageManager::Barrier>(); + mImageManager->cacheAsync(buffer, barrier); + return barrier; +} + +status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) { + if (buffer == nullptr) { + return BAD_VALUE; + } + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (mImageCache.count(buffer->getId()) > 0) { + // If there's already an image then fail fast here. + return NO_ERROR; + } + } + ATRACE_CALL(); + + // Create the image without holding a lock so that we don't block anything. + std::unique_ptr<Image> newImage = createImage(); + + bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), + buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + if (!created) { + 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 NO_INIT; + } + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (mImageCache.count(buffer->getId()) > 0) { + // In theory it's possible for another thread to recache the image, + // so bail out if another thread won. + return NO_ERROR; + } + mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); + } + + return NO_ERROR; +} + void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - const auto& cachedImage = mImageCache.find(bufferId); - if (cachedImage != mImageCache.end()) { - ALOGV("Destroying image for buffer: %" PRIu64, bufferId); - mImageCache.erase(bufferId); - return; + mImageManager->releaseAsync(bufferId, nullptr); +} + +std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting( + uint64_t bufferId) { + auto barrier = std::make_shared<ImageManager::Barrier>(); + mImageManager->releaseAsync(bufferId, barrier); + return barrier; +} + +void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) { + std::unique_ptr<Image> image; + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + + if (cachedImage != mImageCache.end()) { + ALOGV("Destroying image for buffer: %" PRIu64, bufferId); + // Move the buffer out of cache first, so that we can destroy + // without holding the cache's lock. + image = std::move(cachedImage->second); + mImageCache.erase(bufferId); + return; + } } ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); } @@ -842,6 +902,7 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer bool useFramebufferCache) { sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer); if (useFramebufferCache) { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); for (const auto& image : mFramebufferImageCache) { if (image.first == graphicBuffer->getId()) { return image.second; @@ -857,14 +918,20 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer nativeBuffer, attributes); if (useFramebufferCache) { if (image != EGL_NO_IMAGE_KHR) { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mFramebufferImageCache.push_back({graphicBuffer->getId(), image}); } } + + if (image != EGL_NO_IMAGE_KHR) { + DEBUG_EGL_IMAGE_TRACKER_CREATE(); + } return image; } @@ -889,127 +956,123 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, return BAD_VALUE; } - { - std::lock_guard<std::mutex> lock(mRenderingMutex); + BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); - BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); - - if (fbo.getStatus() != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->handle); - checkErrors(); - return fbo.getStatus(); - } - - // clear the entire buffer, sometimes when we reuse buffers we'd persist - // ghost images otherwise. - // we also require a full transparent framebuffer for overlays. This is - // probably not quite efficient on all GPUs, since we could filter out - // opaque layers. - clearWithColor(0.0, 0.0, 0.0, 0.0); + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } - setViewportAndProjection(display.physicalDisplay, display.clip); + // clear the entire buffer, sometimes when we reuse buffers we'd persist + // ghost images otherwise. + // we also require a full transparent framebuffer for overlays. This is + // probably not quite efficient on all GPUs, since we could filter out + // opaque layers. + clearWithColor(0.0, 0.0, 0.0, 0.0); - setOutputDataSpace(display.outputDataspace); - setDisplayMaxLuminance(display.maxLuminance); + setViewportAndProjection(display.physicalDisplay, display.clip); - mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; - mState.projectionMatrix = projectionMatrix; - if (!display.clearRegion.isEmpty()) { - glDisable(GL_BLEND); - fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); - } + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); - Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); - for (auto layer : layers) { - mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + mState.projectionMatrix = projectionMatrix; + if (!display.clearRegion.isEmpty()) { + glDisable(GL_BLEND); + fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); + } - const FloatRect bounds = layer.geometry.boundaries; - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - position[0] = vec2(bounds.left, bounds.top); - position[1] = vec2(bounds.left, bounds.bottom); - position[2] = vec2(bounds.right, bounds.bottom); - position[3] = vec2(bounds.right, bounds.top); + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); + for (auto layer : layers) { + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; - setupLayerCropping(layer, mesh); - setColorTransform(display.colorTransform * layer.colorTransform); + const FloatRect bounds = layer.geometry.boundaries; + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = vec2(bounds.left, bounds.top); + position[1] = vec2(bounds.left, bounds.bottom); + position[2] = vec2(bounds.right, bounds.bottom); + position[3] = vec2(bounds.right, bounds.top); - bool usePremultipliedAlpha = true; - bool disableTexture = true; - bool isOpaque = false; + setupLayerCropping(layer, mesh); + setColorTransform(display.colorTransform * layer.colorTransform); - if (layer.source.buffer.buffer != nullptr) { - disableTexture = false; - isOpaque = layer.source.buffer.isOpaque; + bool usePremultipliedAlpha = true; + bool disableTexture = true; + bool isOpaque = false; - sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; - bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf, - layer.source.buffer.fence); + if (layer.source.buffer.buffer != nullptr) { + disableTexture = false; + isOpaque = layer.source.buffer.isOpaque; - usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; - Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); - mat4 texMatrix = layer.source.buffer.textureTransform; + sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, + layer.source.buffer.fence); - texture.setMatrix(texMatrix.asArray()); - texture.setFiltering(layer.source.buffer.useTextureFiltering); + usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; + Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); + mat4 texMatrix = layer.source.buffer.textureTransform; - texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); - setSourceY410BT2020(layer.source.buffer.isY410BT2020); + texture.setMatrix(texMatrix.asArray()); + texture.setFiltering(layer.source.buffer.useTextureFiltering); - renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); - texCoords[0] = vec2(0.0, 0.0); - texCoords[1] = vec2(0.0, 1.0); - texCoords[2] = vec2(1.0, 1.0); - texCoords[3] = vec2(1.0, 0.0); - setupLayerTexturing(texture); - } + texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); + setSourceY410BT2020(layer.source.buffer.isY410BT2020); - const half3 solidColor = layer.source.solidColor; - const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); - // Buffer sources will have a black solid color ignored in the shader, - // so in that scenario the solid color passed here is arbitrary. - setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, - layer.geometry.roundedCornersRadius); - if (layer.disableBlending) { - glDisable(GL_BLEND); - } - setSourceDataSpace(layer.sourceDataspace); - - // We only want to do a special handling for rounded corners when having rounded corners - // is the only reason it needs to turn on blending, otherwise, we handle it like the - // usual way since it needs to turn on blending anyway. - if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { - handleRoundedCorners(display, layer, mesh); - } else { - drawMesh(mesh); - } + renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); + texCoords[0] = vec2(0.0, 0.0); + texCoords[1] = vec2(0.0, 1.0); + texCoords[2] = vec2(1.0, 1.0); + texCoords[3] = vec2(1.0, 0.0); + setupLayerTexturing(texture); + } - // Cleanup if there's a buffer source - if (layer.source.buffer.buffer != nullptr) { - disableBlending(); - setSourceY410BT2020(false); - disableTexturing(); - } + const half3 solidColor = layer.source.solidColor; + const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + // Buffer sources will have a black solid color ignored in the shader, + // so in that scenario the solid color passed here is arbitrary. + setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, + layer.geometry.roundedCornersRadius); + if (layer.disableBlending) { + glDisable(GL_BLEND); } + setSourceDataSpace(layer.sourceDataspace); - if (drawFence != nullptr) { - *drawFence = flush(); + // We only want to do a special handling for rounded corners when having rounded corners + // is the only reason it needs to turn on blending, otherwise, we handle it like the + // usual way since it needs to turn on blending anyway. + if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { + handleRoundedCorners(display, layer, mesh); + } else { + drawMesh(mesh); } - // If flush failed or we don't support native fences, we need to force the - // gl command stream to be executed. - if (drawFence == nullptr || drawFence->get() < 0) { - bool success = finish(); - if (!success) { - ALOGE("Failed to flush RenderEngine commands"); - checkErrors(); - // Chances are, something illegal happened (either the caller passed - // us bad parameters, or we messed up our shader generation). - return INVALID_OPERATION; - } + + // Cleanup if there's a buffer source + if (layer.source.buffer.buffer != nullptr) { + disableBlending(); + setSourceY410BT2020(false); + disableTexturing(); } + } - checkErrors(); + if (drawFence != nullptr) { + *drawFence = flush(); } + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + if (drawFence == nullptr || drawFence->get() < 0) { + bool success = finish(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + checkErrors(); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + } + + checkErrors(); return NO_ERROR; } @@ -1306,6 +1369,23 @@ void GLESRenderEngine::dump(std::string& result) { StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, unused] : mImageCache) { + StringAppendF(&result, "0x%" PRIx64 "\n", id); + } + } + { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); + StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n", + mFramebufferImageCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, unused] : mFramebufferImageCache) { + StringAppendF(&result, "0x%" PRIx64 "\n", id); + } + } } GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { @@ -1432,7 +1512,7 @@ bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { } bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mRenderingMutex); + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), [=](std::pair<uint64_t, EGLImageKHR> image) { return image.first == bufferId; diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index de793c2142..dd60e50c67 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -17,9 +17,7 @@ #ifndef SF_GLESRENDERENGINE_H_ #define SF_GLESRENDERENGINE_H_ -#include <android-base/thread_annotations.h> #include <stdint.h> -#include <sys/types.h> #include <condition_variable> #include <deque> #include <mutex> @@ -30,8 +28,11 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> +#include <android-base/thread_annotations.h> #include <renderengine/RenderEngine.h> #include <renderengine/private/Description.h> +#include <sys/types.h> +#include "ImageManager.h" #define EGL_NO_CONFIG ((EGLConfig)0) @@ -74,7 +75,7 @@ public: void bindExternalTextureImage(uint32_t texName, const Image& image) override; status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, const sp<Fence>& fence) EXCLUDES(mRenderingMutex); - status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); status_t bindFrameBuffer(Framebuffer* framebuffer) override; void unbindFrameBuffer(Framebuffer* framebuffer) override; @@ -85,25 +86,32 @@ public: bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, ANativeWindowBuffer* buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence, base::unique_fd* drawFence) - EXCLUDES(mRenderingMutex) override; + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; // internal to RenderEngine EGLDisplay getEGLDisplay() const { return mEGLDisplay; } EGLConfig getEGLConfig() const { return mEGLConfig; } // Creates an output image for rendering to EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, - bool useFramebufferCache); + bool useFramebufferCache) + EXCLUDES(mFramebufferImageCacheMutex); // Test-only methods // Returns true iff mImageCache contains an image keyed by bufferId bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); // Returns true iff mFramebufferImageCache contains an image keyed by bufferId - bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + bool isFramebufferImageCachedForTesting(uint64_t bufferId) + EXCLUDES(mFramebufferImageCacheMutex); + // These are wrappers around public methods above, but exposing Barrier + // objects so that tests can block. + std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting( + const sp<GraphicBuffer>& buffer); + std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId); protected: Framebuffer* getFramebufferForDrawing() override; - void dump(std::string& result) override; + void dump(std::string& result) override EXCLUDES(mRenderingMutex) + EXCLUDES(mFramebufferImageCacheMutex); void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, ui::Transform::orientation_flags rotation) override; void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, @@ -145,6 +153,9 @@ private: void setScissor(const Rect& region); void disableScissor(); bool waitSync(EGLSyncKHR sync, EGLint flags); + status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) + EXCLUDES(mRenderingMutex); + void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); // A data space is considered HDR data space if it has BT2020 color space // with PQ or HLG transfer function. @@ -200,7 +211,11 @@ private: uint32_t mFramebufferImageCacheSize = 0; // Cache of output images, keyed by corresponding GraphicBuffer ID. - std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache; + std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache + GUARDED_BY(mFramebufferImageCacheMutex); + // The only reason why we have this mutex is so that we don't segfault when + // dumping info. + std::mutex mFramebufferImageCacheMutex; // Current dataspace of layer being rendered ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; @@ -220,15 +235,6 @@ private: // multiple threads is guaranteed thread-safe. std::mutex mRenderingMutex; - // See bindExternalTextureBuffer above, but requiring that mRenderingMutex - // is held. - status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) REQUIRES(mRenderingMutex); - // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex - // is held. - status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) - REQUIRES(mRenderingMutex); - std::unique_ptr<Framebuffer> mDrawingBuffer; class FlushTracer { @@ -253,7 +259,9 @@ private: bool mRunning = true; }; friend class FlushTracer; + friend class ImageManager; std::unique_ptr<FlushTracer> mFlushTracer; + std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this); }; } // namespace gl diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp index dacf8d3d82..5fbb5ba7d7 100644 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -22,6 +22,7 @@ #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <gui/DebugEGLImageTracker.h> #include <nativebase/nativebase.h> #include <utils/Trace.h> #include "GLESRenderEngine.h" @@ -47,6 +48,7 @@ bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, boo if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!usingFramebufferCache) { eglDestroyImageKHR(mEGLDisplay, mEGLImage); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mEGLImage = EGL_NO_IMAGE_KHR; mBufferWidth = 0; diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp index 77e648e70f..8497721956 100644 --- a/libs/renderengine/gl/GLImage.cpp +++ b/libs/renderengine/gl/GLImage.cpp @@ -20,6 +20,7 @@ #include <vector> +#include <gui/DebugEGLImageTracker.h> #include <log/log.h> #include <utils/Trace.h> #include "GLESRenderEngine.h" @@ -58,6 +59,7 @@ bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtecte if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { ALOGE("failed to destroy image: %#x", eglGetError()); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); mEGLImage = EGL_NO_IMAGE_KHR; } @@ -69,6 +71,7 @@ bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtecte ALOGE("failed to create EGLImage: %#x", eglGetError()); return false; } + DEBUG_EGL_IMAGE_TRACKER_CREATE(); mProtected = isProtected; } diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp new file mode 100644 index 0000000000..5af0e4f857 --- /dev/null +++ b/libs/renderengine/gl/ImageManager.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <pthread.h> + +#include <processgroup/sched_policy.h> +#include <utils/Trace.h> +#include "GLESRenderEngine.h" +#include "ImageManager.h" + +namespace android { +namespace renderengine { +namespace gl { + +ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) { + pthread_setname_np(mThread.native_handle(), "ImageManager"); + // Use SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for ImageManager"); + } +} + +ImageManager::~ImageManager() { + { + std::lock_guard<std::mutex> lock(mMutex); + mRunning = false; + } + mCondition.notify_all(); + if (mThread.joinable()) { + mThread.join(); + } +} + +void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer, + const std::shared_ptr<Barrier>& barrier) { + if (buffer == nullptr) { + { + std::lock_guard<std::mutex> lock(barrier->mutex); + barrier->isOpen = true; + barrier->result = BAD_VALUE; + } + barrier->condition.notify_one(); + return; + } + ATRACE_CALL(); + QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier}; + queueOperation(std::move(entry)); +} + +status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) { + ATRACE_CALL(); + auto barrier = std::make_shared<Barrier>(); + cacheAsync(buffer, barrier); + std::lock_guard<std::mutex> lock(barrier->mutex); + barrier->condition.wait(barrier->mutex, + [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; }); + return barrier->result; +} + +void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) { + ATRACE_CALL(); + QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier}; + queueOperation(std::move(entry)); +} + +void ImageManager::queueOperation(const QueueEntry&& entry) { + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.emplace(entry); + ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); + } + mCondition.notify_one(); +} + +void ImageManager::threadMain() { + set_sched_policy(0, SP_FOREGROUND); + bool run; + { + std::lock_guard<std::mutex> lock(mMutex); + run = mRunning; + } + while (run) { + QueueEntry entry; + { + std::lock_guard<std::mutex> lock(mMutex); + mCondition.wait(mMutex, + [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); + run = mRunning; + + if (!mRunning) { + // if mRunning is false, then ImageManager is being destroyed, so + // bail out now. + break; + } + + entry = mQueue.front(); + mQueue.pop(); + ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); + } + + status_t result = NO_ERROR; + switch (entry.op) { + case QueueEntry::Operation::Delete: + mEngine->unbindExternalTextureBufferInternal(entry.bufferId); + break; + case QueueEntry::Operation::Insert: + result = mEngine->cacheExternalTextureBufferInternal(entry.buffer); + break; + } + if (entry.barrier != nullptr) { + { + std::lock_guard<std::mutex> entryLock(entry.barrier->mutex); + entry.barrier->result = result; + entry.barrier->isOpen = true; + } + entry.barrier->condition.notify_one(); + } + } +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h new file mode 100644 index 0000000000..b5ba554c4f --- /dev/null +++ b/libs/renderengine/gl/ImageManager.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include <ui/GraphicBuffer.h> + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class ImageManager { +public: + struct Barrier { + std::mutex mutex; + std::condition_variable_any condition; + bool isOpen GUARDED_BY(mutex) = false; + status_t result GUARDED_BY(mutex) = NO_ERROR; + }; + ImageManager(GLESRenderEngine* engine); + ~ImageManager(); + void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier) + EXCLUDES(mMutex); + status_t cache(const sp<GraphicBuffer>& buffer); + void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex); + +private: + struct QueueEntry { + enum class Operation { Delete, Insert }; + + Operation op = Operation::Delete; + sp<GraphicBuffer> buffer = nullptr; + uint64_t bufferId = 0; + std::shared_ptr<Barrier> barrier = nullptr; + }; + + void queueOperation(const QueueEntry&& entry); + void threadMain(); + GLESRenderEngine* const mEngine; + std::thread mThread = std::thread([this]() { threadMain(); }); + std::condition_variable_any mCondition; + std::mutex mMutex; + std::queue<QueueEntry> mQueue GUARDED_BY(mMutex); + + bool mRunning GUARDED_BY(mMutex) = true; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index e7070041f2..c6a7bd843a 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -116,10 +116,20 @@ public: const sp<Fence>& fence) = 0; // Caches Image resources for this buffer, but does not bind the buffer to // a particular texture. - virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; + // Note that work is deferred to an additional thread, i.e. this call + // is made asynchronously, but the caller can expect that cache/unbind calls + // are performed in a manner that's conflict serializable, i.e. unbinding + // a buffer should never occur before binding the buffer if the caller + // called {bind, cache}ExternalTextureBuffer before calling unbind. + virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; // Removes internal resources referenced by the bufferId. This method should be // invoked when the caller will no longer hold a reference to a GraphicBuffer // and needs to clean up its resources. + // Note that work is deferred to an additional thread, i.e. this call + // is made asynchronously, but the caller can expect that cache/unbind calls + // are performed in a manner that's conflict serializable, i.e. unbinding + // a buffer should never occur before binding the buffer if the caller + // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; // When binding a native buffer, it must be done before setViewportAndProjection // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. @@ -176,6 +186,17 @@ public: // should be called for every display that needs to be rendered via the GPU. // @param display The display-wide settings that should be applied prior to // drawing any layers. + // + // Assumptions when calling this method: + // 1. There is exactly one caller - i.e. multi-threading is not supported. + // 2. Additional threads may be calling the {bind,cache}ExternalTexture + // methods above. But the main thread is responsible for holding resources + // such that Image destruction does not occur while this method is called. + // + // TODO(b/136806342): This should behavior should ideally be fixed since + // the above two assumptions are brittle, as conditional thread safetyness + // may be insufficient when maximizing rendering performance in the future. + // // @param layers The layers to draw onto the display, in Z-order. // @param buffer The buffer which will be drawn to. This buffer will be // ready once drawFence fires. diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index e33bcfd994..b4d3ef24ba 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -51,7 +51,7 @@ public: MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); - MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&)); + MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&)); MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 9b483ef51d..e98babc30c 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "libgui", "liblog", "libnativewindow", + "libprocessgroup", "libsync", "libui", "libutils", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7acaecf465..f47c7fd053 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ -#include <gtest/gtest.h> +#include <chrono> +#include <condition_variable> +#include <gtest/gtest.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> @@ -1001,8 +1003,15 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { invokeDraw(settings, layers, mBuffer); uint64_t bufferId = layer.source.buffer.buffer->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->unbindExternalTextureBufferForTesting(bufferId); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(NO_ERROR, barrier->result); } TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) { @@ -1019,21 +1028,52 @@ TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { sRE->bindExternalTextureBuffer(texName, buf, nullptr); uint64_t bufferId = buf->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->unbindExternalTextureBufferForTesting(bufferId); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) { - status_t result = sRE->cacheExternalTextureBuffer(nullptr); - ASSERT_EQ(BAD_VALUE, result); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->cacheExternalTextureBufferForTesting(nullptr); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_TRUE(barrier->isOpen); + EXPECT_EQ(BAD_VALUE, barrier->result); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) { sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); uint64_t bufferId = buf->getId(); - sRE->cacheExternalTextureBuffer(buf); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->cacheExternalTextureBufferForTesting(buf); + { + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); + } EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); + { + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); + } EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 1dd59f43a7..f1aa89ca36 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -20,6 +20,7 @@ #include <ui/GraphicBufferAllocator.h> +#include <limits.h> #include <stdio.h> #include <grallocusage/GrallocUsageConversion.h> @@ -114,6 +115,14 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (!width || !height) width = height = 1; + const uint32_t bpp = bytesPerPixel(format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + width, height, layerCount, format, usage); + return BAD_VALUE; + } + // Ensure that layerCount is valid. if (layerCount < 1) layerCount = 1; @@ -126,7 +135,6 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (error == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); - uint32_t bpp = bytesPerPixel(format); alloc_rec_t rec; rec.width = width; rec.height = height; diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp index a7c248c105..127f7eedd6 100644 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ b/libs/ui/tests/GraphicBuffer_test.cpp @@ -35,6 +35,22 @@ constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; class GraphicBufferTest : public testing::Test {}; +TEST_F(GraphicBufferTest, AllocateNoError) { + PixelFormat format = PIXEL_FORMAT_RGBA_8888; + sp<GraphicBuffer> gb(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount, + kTestUsage, std::string("test"))); + ASSERT_EQ(NO_ERROR, gb->initCheck()); +} + +TEST_F(GraphicBufferTest, AllocateBadDimensions) { + PixelFormat format = PIXEL_FORMAT_RGBA_8888; + uint32_t width, height; + width = height = std::numeric_limits<uint32_t>::max(); + sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage, + std::string("test"))); + ASSERT_EQ(BAD_VALUE, gb->initCheck()); +} + TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { std::unique_ptr<BufferHubBuffer> b1 = BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, |