/* * Copyright 2020 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 "RenderEngineThreaded.h" #include #include #include #include #include #include #include #include "gl/GLESRenderEngine.h" using namespace std::chrono_literals; namespace android { namespace renderengine { namespace threaded { std::unique_ptr RenderEngineThreaded::create(CreateInstanceFactory factory, RenderEngineType type) { return std::make_unique(std::move(factory), type); } RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type) : RenderEngine(type) { ATRACE_CALL(); std::lock_guard lockThread(mThreadMutex); mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory); } RenderEngineThreaded::~RenderEngineThreaded() { mRunning = false; mCondition.notify_one(); if (mThread.joinable()) { mThread.join(); } } status_t RenderEngineThreaded::setSchedFifo(bool enabled) { static constexpr int kFifoPriority = 2; static constexpr int kOtherPriority = 0; struct sched_param param = {0}; int sched_policy; if (enabled) { sched_policy = SCHED_FIFO; param.sched_priority = kFifoPriority; } else { sched_policy = SCHED_OTHER; param.sched_priority = kOtherPriority; } if (sched_setscheduler(0, sched_policy, ¶m) != 0) { return -errno; } return NO_ERROR; } // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS { ATRACE_CALL(); if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) { ALOGW("Failed to set render-engine task profile!"); } if (setSchedFifo(true) != NO_ERROR) { ALOGW("Couldn't set SCHED_FIFO"); } mRenderEngine = factory(); mIsProtected = mRenderEngine->isProtected(); pthread_setname_np(pthread_self(), mThreadName); { std::scoped_lock lock(mInitializedMutex); mIsInitialized = true; } mInitializedCondition.notify_all(); while (mRunning) { const auto getNextTask = [this]() -> std::optional { std::scoped_lock lock(mThreadMutex); if (!mFunctionCalls.empty()) { Work task = mFunctionCalls.front(); mFunctionCalls.pop(); return std::make_optional(task); } return std::nullopt; }; const auto task = getNextTask(); if (task) { (*task)(*mRenderEngine); } std::unique_lock lock(mThreadMutex); mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) { return !mRunning || !mFunctionCalls.empty(); }); } // we must release the RenderEngine on the thread that created it mRenderEngine.reset(); } void RenderEngineThreaded::waitUntilInitialized() const { std::unique_lock lock(mInitializedMutex); mInitializedCondition.wait(lock, [=] { return mIsInitialized; }); } std::future RenderEngineThreaded::primeCache() { const auto resultPromise = std::make_shared>(); std::future resultFuture = resultPromise->get_future(); ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::primeCache"); if (setSchedFifo(false) != NO_ERROR) { ALOGW("Couldn't set SCHED_OTHER for primeCache"); } instance.primeCache(); resultPromise->set_value(); if (setSchedFifo(true) != NO_ERROR) { ALOGW("Couldn't set SCHED_FIFO for primeCache"); } }); } mCondition.notify_one(); return resultFuture; } void RenderEngineThreaded::dump(std::string& result) { std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::dump"); std::string localResult = result; instance.dump(localResult); resultPromise.set_value(std::move(localResult)); }); } mCondition.notify_one(); // Note: This is an rvalue. result.assign(resultFuture.get()); } void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { ATRACE_CALL(); std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::genTextures"); instance.genTextures(count, names); resultPromise.set_value(); }); } mCondition.notify_one(); resultFuture.wait(); } void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { ATRACE_CALL(); std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::deleteTextures"); instance.deleteTextures(count, names); resultPromise.set_value(); }); } mCondition.notify_one(); resultFuture.wait(); } void RenderEngineThreaded::mapExternalTextureBuffer(const sp& buffer, bool isRenderable) { ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::mapExternalTextureBuffer"); instance.mapExternalTextureBuffer(buffer, isRenderable); }); } mCondition.notify_one(); } void RenderEngineThreaded::unmapExternalTextureBuffer(const sp& buffer) { ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); instance.unmapExternalTextureBuffer(buffer); }); } mCondition.notify_one(); } size_t RenderEngineThreaded::getMaxTextureSize() const { waitUntilInitialized(); return mRenderEngine->getMaxTextureSize(); } size_t RenderEngineThreaded::getMaxViewportDims() const { waitUntilInitialized(); return mRenderEngine->getMaxViewportDims(); } bool RenderEngineThreaded::isProtected() const { waitUntilInitialized(); std::lock_guard lock(mThreadMutex); return mIsProtected; } bool RenderEngineThreaded::supportsProtectedContent() const { waitUntilInitialized(); return mRenderEngine->supportsProtectedContent(); } void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { if (isProtected() == useProtectedContext || (useProtectedContext && !supportsProtectedContent())) { return; } { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::useProtectedContext"); instance.useProtectedContext(useProtectedContext); if (instance.isProtected() != useProtectedContext) { ALOGE("Failed to switch RenderEngine context."); // reset the cached mIsProtected value to a good state, but this does not // prevent other callers of this method and isProtected from reading the // invalid cached value. mIsProtected = instance.isProtected(); } }); mIsProtected = useProtectedContext; } mCondition.notify_one(); } void RenderEngineThreaded::cleanupPostRender() { if (canSkipPostRenderCleanup()) { return; } // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); instance.cleanupPostRender(); }); } mCondition.notify_one(); } bool RenderEngineThreaded::canSkipPostRenderCleanup() const { waitUntilInitialized(); return mRenderEngine->canSkipPostRenderCleanup(); } status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, const std::vector& layers, const std::shared_ptr& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache, &bufferFence, &drawFence](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::drawLayers"); status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache, std::move(bufferFence), drawFence); resultPromise.set_value(status); }); } mCondition.notify_one(); return resultFuture.get(); } void RenderEngineThreaded::cleanFramebufferCache() { ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::cleanFramebufferCache"); instance.cleanFramebufferCache(); }); } mCondition.notify_one(); } int RenderEngineThreaded::getContextPriority() { std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::getContextPriority"); int priority = instance.getContextPriority(); resultPromise.set_value(priority); }); } mCondition.notify_one(); return resultFuture.get(); } bool RenderEngineThreaded::supportsBackgroundBlur() { waitUntilInitialized(); return mRenderEngine->supportsBackgroundBlur(); } void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) { // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([size](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged"); instance.onPrimaryDisplaySizeChanged(size); }); } mCondition.notify_one(); } // HUANGLONG begin // set Keystone mode to skiaRenderEngine void RenderEngineThreaded::setKeystoneMode(const uint32_t flags, const std::vector matrix) { // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([flags, matrix](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::setKeystoneMode"); instance.setKeystoneMode(flags, matrix); }); } mCondition.notify_one(); } // HUANGLONG end } // namespace threaded } // namespace renderengine } // namespace android