blob: f4cebc05ecd6aa83de0fb2958abb1f7f1561b87e [file] [log] [blame]
/*
* 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 <sched.h>
#include <chrono>
#include <future>
#include <android-base/stringprintf.h>
#include <private/gui/SyncFeatures.h>
#include <processgroup/processgroup.h>
#include <utils/Trace.h>
using namespace std::chrono_literals;
namespace android {
namespace renderengine {
namespace threaded {
std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
return std::make_unique<RenderEngineThreaded>(std::move(factory));
}
RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
: RenderEngine(Threaded::YES) {
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, &param) != 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();
pthread_setname_np(pthread_self(), mThreadName);
{
std::scoped_lock lock(mInitializedMutex);
mIsInitialized = true;
}
mInitializedCondition.notify_all();
while (mRunning) {
const auto getNextTask = [this]() -> std::optional<Work> {
std::scoped_lock lock(mThreadMutex);
if (!mFunctionCalls.empty()) {
Work task = mFunctionCalls.front();
mFunctionCalls.pop();
return std::make_optional<Work>(task);
}
return std::nullopt;
};
const auto task = getNextTask();
if (task) {
(*task)(*mRenderEngine);
}
std::unique_lock<std::mutex> 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 {
if (!mIsInitialized) {
std::unique_lock<std::mutex> lock(mInitializedMutex);
mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); });
}
}
std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) {
const auto resultPromise = std::make_shared<std::promise<void>>();
std::future<void> 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, shouldPrimeUltraHDR](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::primeCache");
if (setSchedFifo(false) != NO_ERROR) {
ALOGW("Couldn't set SCHED_OTHER for primeCache");
}
instance.primeCache(shouldPrimeUltraHDR);
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<std::string> resultPromise;
std::future<std::string> 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::mapExternalTextureBuffer(const sp<GraphicBuffer>& 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(sp<GraphicBuffer>&& 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(
[=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
instance.unmapExternalTextureBuffer(std::move(buffer));
});
}
mCondition.notify_one();
}
size_t RenderEngineThreaded::getMaxTextureSize() const {
waitUntilInitialized();
return mRenderEngine->getMaxTextureSize();
}
size_t RenderEngineThreaded::getMaxViewportDims() const {
waitUntilInitialized();
return mRenderEngine->getMaxViewportDims();
}
bool RenderEngineThreaded::supportsProtectedContent() const {
waitUntilInitialized();
return mRenderEngine->supportsProtectedContent();
}
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::cleanupPostRender");
instance.cleanupPostRender();
});
mNeedsPostRenderCleanup = false;
}
mCondition.notify_one();
}
bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
return !mNeedsPostRenderCleanup;
}
void RenderEngineThreaded::drawLayersInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
resultPromise->set_value(Fence::NO_FENCE);
return;
}
ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
ATRACE_CALL();
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
int fd = bufferFence.release();
{
std::lock_guard lock(mThreadMutex);
mNeedsPostRenderCleanup = true;
mFunctionCalls.push(
[resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::drawLayers");
instance.updateProtectedContext(layers, buffer);
instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
base::unique_fd(fd));
});
}
mCondition.notify_one();
return resultFuture;
}
int RenderEngineThreaded::getContextPriority() {
std::promise<int> resultPromise;
std::future<int> 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::onActiveDisplaySizeChanged(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::onActiveDisplaySizeChanged");
instance.onActiveDisplaySizeChanged(size);
});
}
mCondition.notify_one();
}
std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
std::promise<pid_t> tidPromise;
std::future<pid_t> tidFuture = tidPromise.get_future();
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
tidPromise.set_value(gettid());
});
}
mCondition.notify_one();
return std::make_optional(tidFuture.get());
}
void RenderEngineThreaded::setEnableTracing(bool tracingEnabled) {
// 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([tracingEnabled](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::setEnableTracing");
instance.setEnableTracing(tracingEnabled);
});
}
mCondition.notify_one();
}
} // namespace threaded
} // namespace renderengine
} // namespace android