You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
396 lines
13 KiB
396 lines
13 KiB
/*
|
|
* 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>
|
|
|
|
#include "gl/GLESRenderEngine.h"
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace android {
|
|
namespace renderengine {
|
|
namespace threaded {
|
|
|
|
std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
|
|
RenderEngineType type) {
|
|
return std::make_unique<RenderEngineThreaded>(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<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 {
|
|
std::unique_lock<std::mutex> lock(mInitializedMutex);
|
|
mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
|
|
}
|
|
|
|
std::future<void> RenderEngineThreaded::primeCache() {
|
|
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](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<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::genTextures(size_t count, uint32_t* names) {
|
|
ATRACE_CALL();
|
|
std::promise<void> resultPromise;
|
|
std::future<void> 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<void> resultPromise;
|
|
std::future<void> 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<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(const 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([=](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<const LayerSettings*>& layers,
|
|
const std::shared_ptr<ExternalTexture>& buffer,
|
|
const bool useFramebufferCache,
|
|
base::unique_fd&& bufferFence,
|
|
base::unique_fd* drawFence) {
|
|
ATRACE_CALL();
|
|
std::promise<status_t> resultPromise;
|
|
std::future<status_t> 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<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::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<double> 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
|