Merge "BLASTBufferQueue: fix AsyncWorker race condition" into sc-dev
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index f6c2e55..9881371 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -526,6 +526,9 @@
* callback timings, and changes to the time interval at which the system releases buffers back to
* the application.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
* value that indicates the app will accept the system's choice for the display frame rate, which is
* the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 95eba87..4b3a53f 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -133,6 +133,21 @@
return OK;
}
+void RpcConnection::startThread(unique_fd client) {
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<RpcConnection> holdThis = sp<RpcConnection>::fromExisting(this);
+ int fd = client.release();
+ auto thread = std::thread([=] {
+ holdThis->join(unique_fd(fd));
+ {
+ std::lock_guard<std::mutex> _l(holdThis->mSocketMutex);
+ size_t erased = mThreads.erase(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(erased != 0, "Could not erase thread.");
+ }
+ });
+ mThreads[thread.get_id()] = std::move(thread);
+}
+
void RpcConnection::join(unique_fd client) {
// must be registered to allow arbitrary client code executing commands to
// be able to do nested calls (we can't only read from it)
@@ -164,7 +179,7 @@
mClients.size());
}
- if (!setupOneSocketClient(addr)) return false;
+ if (!setupOneSocketClient(addr, RPC_CONNECTION_ID_NEW)) return false;
// TODO(b/185167543): we should add additional connections dynamically
// instead of all at once.
@@ -186,7 +201,7 @@
for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
// TODO(b/185167543): avoid race w/ accept4 not being called on server
for (size_t tries = 0; tries < 5; tries++) {
- if (setupOneSocketClient(addr)) break;
+ if (setupOneSocketClient(addr, mId.value())) break;
usleep(10000);
}
}
@@ -194,7 +209,7 @@
return true;
}
-bool RpcConnection::setupOneSocketClient(const RpcSocketAddress& addr) {
+bool RpcConnection::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
if (serverFd == -1) {
@@ -209,6 +224,13 @@
return false;
}
+ if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+ int savedErrno = errno;
+ ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return false;
+ }
+
LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
addClient(std::move(serverFd));
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 5f024ca..4df12ce 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -126,40 +126,61 @@
{
std::lock_guard<std::mutex> _l(mLock);
LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join.");
- // TODO(b/185167543): support more than one client at once
- mConnection = RpcConnection::make();
- mConnection->setForServer(sp<RpcServer>::fromExisting(this), 42 /*placeholder id*/);
-
- mStarted = true;
- for (size_t i = 0; i < mMaxThreads; i++) {
- pool.push_back(std::thread([=] {
- // TODO(b/185167543): do this dynamically, instead of from a static number
- // of threads
- unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
- if (clientFd < 0) {
- // If this log becomes confusing, should save more state from
- // setupUnixDomainServer in order to output here.
- ALOGE("Could not accept4 socket: %s", strerror(errno));
- return;
- }
-
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
- mConnection->join(std::move(clientFd));
- }));
- }
}
- // TODO(b/185167543): don't waste extra thread for join, and combine threads
- // between clients
- for (auto& t : pool) t.join();
+ while (true) {
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
+
+ if (clientFd < 0) {
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ continue;
+ }
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ // TODO(b/183988761): cannot trust this simple ID
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ int32_t id;
+ if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+ ALOGE("Could not read ID from fd %d", clientFd.get());
+ continue;
+ }
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ sp<RpcConnection> connection;
+ if (id == RPC_CONNECTION_ID_NEW) {
+ // new client!
+ LOG_ALWAYS_FATAL_IF(mConnectionIdCounter >= INT32_MAX, "Out of connection IDs");
+ mConnectionIdCounter++;
+
+ connection = RpcConnection::make();
+ connection->setForServer(wp<RpcServer>::fromExisting(this), mConnectionIdCounter);
+
+ mConnections[mConnectionIdCounter] = connection;
+ } else {
+ auto it = mConnections.find(id);
+ if (it == mConnections.end()) {
+ ALOGE("Cannot add thread, no record of connection with ID %d", id);
+ continue;
+ }
+ connection = it->second;
+ }
+
+ connection->startThread(std::move(clientFd));
+ }
+ }
}
std::vector<sp<RpcConnection>> RpcServer::listConnections() {
std::lock_guard<std::mutex> _l(mLock);
- if (mConnection == nullptr) return {};
- return {mConnection};
+ std::vector<sp<RpcConnection>> connections;
+ for (auto& [id, connection] : mConnections) {
+ (void)id;
+ connections.push_back(connection);
+ }
+ return connections;
}
bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 56af0d3..a7e8a52 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -51,6 +51,8 @@
RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID = 2,
};
+constexpr int32_t RPC_CONNECTION_ID_NEW = -1;
+
// serialization is like:
// |RpcWireHeader|struct desginated by 'command'| (over and over again)
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index 7e31e8a..87984d7 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -21,7 +21,9 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <map>
#include <optional>
+#include <thread>
#include <vector>
// WARNING: This is a feature which is still in development, and it is subject
@@ -113,6 +115,7 @@
status_t readId();
+ void startThread(base::unique_fd client);
void join(base::unique_fd client);
struct ConnectionSocket : public RefBase {
@@ -124,7 +127,7 @@
};
bool setupSocketClient(const RpcSocketAddress& address);
- bool setupOneSocketClient(const RpcSocketAddress& address);
+ bool setupOneSocketClient(const RpcSocketAddress& address, int32_t connectionId);
void addClient(base::unique_fd fd);
void setForServer(const wp<RpcServer>& server, int32_t connectionId);
sp<ConnectionSocket> assignServerToThisThread(base::unique_fd fd);
@@ -179,11 +182,18 @@
std::unique_ptr<RpcState> mState;
std::mutex mSocketMutex; // for all below
+
std::condition_variable mSocketCv; // for mWaitingThreads
size_t mWaitingThreads = 0;
size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
std::vector<sp<ConnectionSocket>> mClients;
std::vector<sp<ConnectionSocket>> mServers;
+
+ // TODO(b/185167543): use for reverse connections (allow client to also
+ // serve calls on a connection).
+ // TODO(b/185167543): allow sharing between different connections in a
+ // process? (or combine with mServers)
+ std::map<std::thread::id, std::thread> mThreads;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 5535d8a..81ea3a7 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -97,6 +97,8 @@
/**
* You must have at least one client connection before calling this.
+ *
+ * TODO(b/185167543): way to shut down?
*/
void join();
@@ -120,7 +122,8 @@
std::mutex mLock; // for below
sp<IBinder> mRootObject;
- sp<RpcConnection> mConnection;
+ std::map<int32_t, sp<RpcConnection>> mConnections;
+ int32_t mConnectionIdCounter = 0;
};
} // namespace android
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 57c9013..49d3401 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -50,6 +50,8 @@
"//apex_available:platform",
"com.android.virt",
],
+ lints: "none",
+ clippy_lints: "none",
}
rust_bindgen {
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 9095af2..1d1a295 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -16,14 +16,6 @@
//! Generated Rust bindings to libbinder_ndk
-#![allow(
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- unused,
- improper_ctypes,
- missing_docs
-)]
use std::error::Error;
use std::fmt;
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 2bdb264..814e094 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,8 +18,8 @@
oneway void sendString(@utf8InCpp String str);
@utf8InCpp String doubleString(@utf8InCpp String str);
- // number of known RPC binders to process, RpcState::countBinders
- int countBinders();
+ // number of known RPC binders to process, RpcState::countBinders by connection
+ int[] countBinders();
// Caller sends server, callee pings caller's server and returns error code.
int pingMe(IBinder binder);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index d23df8e..50bff91 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -88,24 +88,21 @@
*strstr = str + str;
return Status::ok();
}
- Status countBinders(int32_t* out) override {
+ Status countBinders(std::vector<int32_t>* out) override {
sp<RpcServer> spServer = server.promote();
if (spServer == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- size_t count = 0;
+ out->clear();
for (auto connection : spServer->listConnections()) {
- count += connection->state()->countBinders();
- }
- // help debugging if we don't have one binder (this call is always made
- // in this test when exactly one binder is held, which is held only to
- // call this method - all other binders should be cleaned up)
- if (count != 1) {
- for (auto connection : spServer->listConnections()) {
+ size_t count = connection->state()->countBinders();
+ if (count != 1) {
+ // this is called when there is only one binder held remaining,
+ // so to aid debugging
connection->state()->dump();
}
+ out->push_back(count);
}
- *out = count;
return Status::ok();
}
Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
@@ -232,25 +229,33 @@
// reference to process hosting a socket server
Process host;
- // client connection object associated with other process
- sp<RpcConnection> connection;
+ struct ConnectionInfo {
+ sp<RpcConnection> connection;
+ sp<IBinder> root;
+ };
- // pre-fetched root object
- sp<IBinder> rootBinder;
-
- // whether connection should be invalidated by end of run
- bool expectInvalid = false;
+ // client connection objects associated with other process
+ // each one represents a separate connection
+ std::vector<ConnectionInfo> connections;
ProcessConnection(ProcessConnection&&) = default;
~ProcessConnection() {
- rootBinder = nullptr;
- EXPECT_NE(nullptr, connection);
- EXPECT_NE(nullptr, connection->state());
- EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+ for (auto& connection : connections) {
+ connection.root = nullptr;
+ }
- wp<RpcConnection> weakConnection = connection;
- connection = nullptr;
- EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ for (auto& info : connections) {
+ sp<RpcConnection>& connection = info.connection;
+
+ EXPECT_NE(nullptr, connection);
+ EXPECT_NE(nullptr, connection->state());
+ EXPECT_EQ(0, connection->state()->countBinders())
+ << (connection->state()->dump(), "dump:");
+
+ wp<RpcConnection> weakConnection = connection;
+ connection = nullptr;
+ EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ }
}
};
@@ -259,19 +264,25 @@
struct BinderRpcTestProcessConnection {
ProcessConnection proc;
- // pre-fetched root object
+ // pre-fetched root object (for first connection)
sp<IBinder> rootBinder;
- // pre-casted root object
+ // pre-casted root object (for first connection)
sp<IBinderRpcTest> rootIface;
+ // whether connection should be invalidated by end of run
+ bool expectInvalid = false;
+
BinderRpcTestProcessConnection(BinderRpcTestProcessConnection&&) = default;
~BinderRpcTestProcessConnection() {
- if (!proc.expectInvalid) {
- int32_t remoteBinders = 0;
- EXPECT_OK(rootIface->countBinders(&remoteBinders));
- // should only be the root binder object, iface
- EXPECT_EQ(remoteBinders, 1);
+ if (!expectInvalid) {
+ std::vector<int32_t> remoteCounts;
+ // calling over any connections counts across all connections
+ EXPECT_OK(rootIface->countBinders(&remoteCounts));
+ EXPECT_EQ(remoteCounts.size(), proc.connections.size());
+ for (auto remoteCount : remoteCounts) {
+ EXPECT_EQ(remoteCount, 1);
+ }
}
rootIface = nullptr;
@@ -306,7 +317,10 @@
// This creates a new process serving an interface on a certain number of
// threads.
ProcessConnection createRpcTestSocketServerProcess(
- size_t numThreads, const std::function<void(const sp<RpcServer>&)>& configure) {
+ size_t numThreads, size_t numConnections,
+ const std::function<void(const sp<RpcServer>&)>& configure) {
+ CHECK_GE(numConnections, 1) << "Must have at least one connection to a server";
+
SocketType socketType = GetParam();
std::string addr = allocateSocketAddress();
@@ -346,7 +360,6 @@
server->join();
}),
- .connection = RpcConnection::make(),
};
unsigned int inetPort = 0;
@@ -356,35 +369,37 @@
CHECK_NE(0, inetPort);
}
- // create remainder of connections
- for (size_t tries = 0; tries < 10; tries++) {
- usleep(10000);
- switch (socketType) {
- case SocketType::UNIX:
- if (ret.connection->setupUnixDomainClient(addr.c_str())) goto success;
- break;
+ for (size_t i = 0; i < numConnections; i++) {
+ sp<RpcConnection> connection = RpcConnection::make();
+ for (size_t tries = 0; tries < 10; tries++) {
+ usleep(10000);
+ switch (socketType) {
+ case SocketType::UNIX:
+ if (connection->setupUnixDomainClient(addr.c_str())) goto success;
+ break;
#ifdef __BIONIC__
- case SocketType::VSOCK:
- if (ret.connection->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
- break;
+ case SocketType::VSOCK:
+ if (connection->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+ break;
#endif // __BIONIC__
- case SocketType::INET:
- if (ret.connection->setupInetClient("127.0.0.1", inetPort)) goto success;
- break;
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
+ case SocketType::INET:
+ if (connection->setupInetClient("127.0.0.1", inetPort)) goto success;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
}
+ LOG_ALWAYS_FATAL("Could not connect");
+ success:
+ ret.connections.push_back({connection, connection->getRootObject()});
}
- LOG_ALWAYS_FATAL("Could not connect");
- success:
-
- ret.rootBinder = ret.connection->getRootObject();
return ret;
}
- BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+ BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads,
+ size_t numConnections = 1) {
BinderRpcTestProcessConnection ret{
- .proc = createRpcTestSocketServerProcess(numThreads,
+ .proc = createRpcTestSocketServerProcess(numThreads, numConnections,
[&](const sp<RpcServer>& server) {
sp<MyBinderRpcTest> service =
new MyBinderRpcTest;
@@ -393,7 +408,7 @@
}),
};
- ret.rootBinder = ret.proc.rootBinder;
+ ret.rootBinder = ret.proc.connections.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
@@ -401,16 +416,12 @@
};
TEST_P(BinderRpc, RootObjectIsNull) {
- auto proc = createRpcTestSocketServerProcess(1, [](const sp<RpcServer>& server) {
+ auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) {
// this is the default, but to be explicit
server->setRootObject(nullptr);
});
- // retrieved by getRootObject when process is created above
- EXPECT_EQ(nullptr, proc.rootBinder);
-
- // make sure we can retrieve it again (process doesn't crash)
- EXPECT_EQ(nullptr, proc.connection->getRootObject());
+ EXPECT_EQ(nullptr, proc.connections.at(0).root);
}
TEST_P(BinderRpc, Ping) {
@@ -425,6 +436,14 @@
EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
}
+TEST_P(BinderRpc, MultipleConnections) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*connections*/);
+ for (auto connection : proc.proc.connections) {
+ ASSERT_NE(nullptr, connection.root);
+ EXPECT_EQ(OK, connection.root->pingBinder());
+ }
+}
+
TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
auto proc = createRpcTestSocketServerProcess(1);
Parcel data;
@@ -572,6 +591,15 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoConnectionsToTheSameServer) {
+ auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*connections*/);
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc.connections.at(1).root, &outBinder)
+ .transactionError());
+}
+
TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
auto proc = createRpcTestSocketServerProcess(1);
@@ -856,7 +884,7 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
- proc.proc.expectInvalid = true;
+ proc.expectInvalid = true;
}
}
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index e707684..0827bbe 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -119,9 +119,11 @@
info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
if (info.mStretchEffect.hasEffect()) {
const auto& se = info.mStretchEffect;
- StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n",
- se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX,
- se.vectorY, se.maxAmount);
+ StringAppendF(&result,
+ " StretchEffect width = %f, height = %f vec=(%f, %f) "
+ "maxAmount=(%f, %f)\n",
+ se.width, se.height,
+ se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
}
StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 808b731..11b8eba 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1648,8 +1648,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
- const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
- float vecY, float maxAmount) {
+ const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1657,10 +1656,7 @@
}
s->what |= layer_state_t::eStretchChanged;
- s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
- .vectorX = vecX,
- .vectorY = vecY,
- .maxAmount = maxAmount};
+ s->stretchEffect = stretchEffect;
return *this;
}
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index f3439c4..35757be 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -538,9 +538,20 @@
// transactions from blocking each other.
Transaction& setApplyToken(const sp<IBinder>& token);
- Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
- float right, float bottom, float vecX, float vecY,
- float maxAmount);
+ /**
+ * Provides the stretch effect configured on a container that the
+ * surface is rendered within.
+ * @param sc target surface the stretch should be applied to
+ * @param stretchEffect the corresponding stretch effect to be applied
+ * to the surface. This can be directly on the surface itself or
+ * configured from a parent of the surface in which case the
+ * StretchEffect provided has parameters mapping the position of
+ * the surface within the container that has the stretch configured
+ * on it
+ * @return The transaction being constructed
+ */
+ Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
+ const StretchEffect& stretchEffect);
Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 31027b6..61d72ad 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -87,8 +87,10 @@
// Search system repository.
std::string path;
- // Treblized input device config files will be located /odm/usr or /vendor/usr.
- const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+ // Treblized input device config files will be located /product/usr, /system_ext/usr,
+ // /odm/usr or /vendor/usr.
+ const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
+ getenv("ANDROID_ROOT")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 3865ba5..f0e1c4d 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -300,6 +300,9 @@
* this ANativeWindow is consumed by something other than the system compositor,
* e.g. a media codec, this call has no effect.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* Available since API level 31.
*
* \param window pointer to an ANativeWindow object.
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f395ab4..570c7bc 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -95,6 +95,7 @@
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/StretchShaderFactory.cpp"
],
}
@@ -117,7 +118,7 @@
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia"],
+ whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d87315f..9400037 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -249,7 +249,7 @@
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL");
+ LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
}
const auto eglVersion = eglQueryString(display, EGL_VERSION);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index e976a5a..6e98352 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -222,7 +222,9 @@
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.blurRegionTransform == rhs.blurRegionTransform &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
// Defining PrintTo helps with Google Tests.
@@ -272,6 +274,21 @@
*os << "\n}";
}
+static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) {
+ *os << "StretchEffect {";
+ *os << "\n .width = " << effect.width;
+ *os << "\n .height = " << effect.height;
+ *os << "\n .vectorX = " << effect.vectorX;
+ *os << "\n .vectorY = " << effect.vectorY;
+ *os << "\n .maxAmountX = " << effect.maxAmountX;
+ *os << "\n .maxAmountY = " << effect.maxAmountY;
+ *os << "\n .mappedLeft = " << effect.mappedChildBounds.left;
+ *os << "\n .mappedTop = " << effect.mappedChildBounds.top;
+ *os << "\n .mappedRight = " << effect.mappedChildBounds.right;
+ *os << "\n .mappedBottom = " << effect.mappedChildBounds.bottom;
+ *os << "\n}";
+}
+
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
*os << "LayerSettings {";
*os << "\n .geometry = ";
@@ -290,6 +307,8 @@
}
*os << "\n .shadow = ";
PrintTo(settings.shadow, os);
+ *os << "\n .stretchEffect = ";
+ PrintTo(settings.stretchEffect, os);
*os << "\n}";
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index acdb78a..540e36a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -56,6 +56,7 @@
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
+#include "skia/filters/StretchShaderFactory.h"
#include "system/graphics-base-v1.0.h"
namespace {
@@ -365,6 +366,10 @@
return mProtectedEGLContext != EGL_NO_CONTEXT;
}
+GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
+ return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (useProtectedContext == mInProtectedContext) {
return true;
@@ -372,6 +377,12 @@
if (useProtectedContext && !supportsProtectedContent()) {
return false;
}
+
+ // release any scratch resources before switching into a new mode
+ if (getActiveGrContext()) {
+ getActiveGrContext()->purgeUnlockedResources(true);
+ }
+
const EGLSurface surface =
useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
@@ -379,6 +390,11 @@
if (success) {
mInProtectedContext = useProtectedContext;
+ // given that we are sharing the same thread between two GrContexts we need to
+ // make sure that the thread state is reset when switching between the two.
+ if (getActiveGrContext()) {
+ getActiveGrContext()->resetContext();
+ }
}
return success;
}
@@ -489,18 +505,23 @@
if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
return;
}
+ // we currently don't attempt to map a buffer if the buffer contains protected content
+ // because GPU resources for protected buffers is much more limited.
+ const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+ if (isProtectedBuffer) {
+ return;
+ }
ATRACE_CALL();
// We need to switch the currently bound context if the buffer is protected but the current
// context is not. The current state must then be restored after the buffer is cached.
const bool protectedContextState = mInProtectedContext;
- if (!useProtectedContext(protectedContextState ||
- (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
+ if (!useProtectedContext(protectedContextState || isProtectedBuffer)) {
ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
return;
}
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -508,7 +529,7 @@
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
isRenderable);
cache.insert({buffer->getId(), imageTextureRef});
@@ -541,14 +562,19 @@
}
}
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings* layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- if (layer->stretchEffect.hasEffect()) {
- // TODO: Implement
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+ sk_sp<SkShader> shader,
+ const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
+ bool requiresLinearEffect) {
+ const auto stretchEffect = layer->stretchEffect;
+ if (stretchEffect.hasEffect()) {
+ const auto targetBuffer = layer->source.buffer.buffer;
+ const auto graphicsBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ if (graphicsBuffer && shader) {
+ shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
+ }
}
+
if (requiresLinearEffect) {
const ui::Dataspace inputDataspace =
mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
@@ -634,6 +660,44 @@
int mSaveCount;
};
+void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect,
+ SkCanvas* canvas, const SkPaint& paint) {
+ float top = bounds.top();
+ float left = bounds.left();
+ float bottom = bounds.bottom();
+ float right = bounds.right();
+ // Adjust the drawing bounds based on the stretch itself.
+ float stretchOffsetX =
+ round(bounds.width() * stretchEffect.getStretchWidthMultiplier());
+ float stretchOffsetY =
+ round(bounds.height() * stretchEffect.getStretchHeightMultiplier());
+ if (stretchEffect.vectorY < 0.f) {
+ top -= stretchOffsetY;
+ } else if (stretchEffect.vectorY > 0.f){
+ bottom += stretchOffsetY;
+ }
+
+ if (stretchEffect.vectorX < 0.f) {
+ left -= stretchOffsetX;
+ } else if (stretchEffect.vectorX > 0.f) {
+ right += stretchOffsetX;
+ }
+
+ auto stretchBounds = SkRect::MakeLTRB(left, top, right, bottom);
+ canvas->drawRect(stretchBounds, paint);
+}
+
+static SkRRect getBlurRRect(const BlurRegion& region) {
+ const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+ const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+ SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+ SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+ SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ return roundedRect;
+}
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
@@ -662,7 +726,7 @@
validateOutputBufferUsage(buffer->getBuffer());
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
@@ -670,7 +734,7 @@
surfaceTextureRef = it->second;
} else {
surfaceTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->getBuffer()
->toAHardwareBuffer(),
true);
@@ -678,8 +742,7 @@
const ui::Dataspace dstDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get());
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -809,7 +872,7 @@
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
- const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto [bounds, roundRectClip] = getBoundsAndClip(layer);
if (mBlurFilter && layerHasBlur(layer)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -819,32 +882,40 @@
blurInput = activeSurface->makeImageSnapshot();
}
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // if the clip needs to be applied then apply it now and make sure
+ // it is restored before we attempt to draw any shadows.
+ SkAutoCanvasRestore acr(canvas, true);
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
if (layer->backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
auto blurredImage =
- mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
- blurInput, blurRect);
+ mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
+ blurRect);
cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
- mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect,
- blurredImage, blurInput);
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ blurRect, blurredImage, blurInput);
}
- SkAutoCanvasRestore acr(canvas, true);
+
canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
for (auto region : layer->blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+ mBlurFilter->generate(grContext, region.blurRadius, blurInput,
blurRect);
}
- mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+ mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+ region.alpha, blurRect,
cachedBlurs[region.blurRadius], blurInput);
}
}
@@ -857,7 +928,7 @@
if (layer->shadow.length > 0) {
const auto rect = layer->geometry.roundedCornersRadius > 0
? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds;
+ : bounds.rect();
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
@@ -897,7 +968,7 @@
// we didn't find anything in the cache then we intentionally did not cache this
// buffer's resources.
imageTextureRef = std::make_shared<
- AutoBackendTexture::LocalRef>(grContext.get(),
+ AutoBackendTexture::LocalRef>(grContext,
item.buffer->getBuffer()->toAHardwareBuffer(),
false);
}
@@ -906,7 +977,7 @@
imageTextureRef->makeImage(layerDataspace,
item.usePremultipliedAlpha ? kPremul_SkAlphaType
: kUnpremul_SkAlphaType,
- grContext.get());
+ grContext);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -922,7 +993,7 @@
// The shader does not respect the translation, so we add it to the texture
// transform for the SkImage. This will make sure that the correct layer contents
// are drawn in the correct part of the screen.
- matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+ matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
sk_sp<SkShader> shader;
@@ -984,11 +1055,25 @@
paint.setColorFilter(displayColorTransform);
- if (layer->geometry.roundedCornersRadius > 0) {
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ if (!bounds.isRect()) {
paint.setAntiAlias(true);
- canvas->drawRRect(getRoundedRect(layer), paint);
+ canvas->drawRRect(bounds, paint);
} else {
- canvas->drawRect(bounds, paint);
+ auto& stretchEffect = layer->stretchEffect;
+ // TODO (njawad) temporarily disable manipulation of geometry
+ // the layer bounds will be updated in HWUI instead of RenderEngine
+ // in a subsequent CL
+ // Keep the method call in a dead code path to make -Werror happy
+ // with unused methods
+ if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) {
+ drawStretch(bounds.rect(), stretchEffect, canvas, paint);
+ } else {
+ canvas->drawRect(bounds.rect(), paint);
+ }
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
@@ -1036,25 +1121,76 @@
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
-inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(
+ const LayerSettings* layer) {
+ const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto crop = getSkRect(layer->geometry.roundedCornersCrop);
const auto cornerRadius = layer->geometry.roundedCornersRadius;
- return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
-}
-inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.boundaries);
- const auto cornersRadius = layer->geometry.roundedCornersRadius;
- return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
- .cornerRadiusTL = cornersRadius,
- .cornerRadiusTR = cornersRadius,
- .cornerRadiusBL = cornersRadius,
- .cornerRadiusBR = cornersRadius,
- .alpha = 1,
- .left = static_cast<int>(rect.fLeft),
- .top = static_cast<int>(rect.fTop),
- .right = static_cast<int>(rect.fRight),
- .bottom = static_cast<int>(rect.fBottom)};
+ SkRRect clip;
+ if (cornerRadius > 0) {
+ // it the crop and the bounds are equivalent then we don't need a clip
+ if (bounds == crop) {
+ return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ }
+
+ // This makes an effort to speed up common, simple bounds + clip combinations by
+ // converting them to a single RRect draw. It is possible there are other cases
+ // that can be converted.
+ if (crop.contains(bounds)) {
+ bool intersectionIsRoundRect = true;
+ // check each cropped corner to ensure that it exactly matches the crop or is full
+ SkVector radii[4];
+
+ const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+
+ // compute the UpperLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) {
+ radii[0].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the UpperRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) {
+ radii[1].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) {
+ radii[2].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) {
+ radii[3].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+
+ if (intersectionIsRoundRect) {
+ SkRRect intersectionBounds;
+ intersectionBounds.setRectRadii(bounds, radii);
+ return {intersectionBounds, clip};
+ }
+ }
+
+ // we didn't it any of our fast paths so set the clip to the cropRect
+ clip.setRectXY(crop, cornerRadius, cornerRadius);
+ }
+
+ // if we hit this point then we either don't have rounded corners or we are going to rely
+ // on the clip to round the corners for us
+ return {SkRRect::MakeRect(bounds), clip};
}
inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
@@ -1226,13 +1362,11 @@
const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
// start by resizing the current context
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// if it is possible to switch contexts then we will resize the other context
if (useProtectedContext(!mInProtectedContext)) {
- grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// reset back to the initial context that was active when this method was called
useProtectedContext(!mInProtectedContext);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e71c560..d9caf35 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -39,6 +39,7 @@
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
#include "filters/LinearEffect.h"
+#include "filters/StretchShaderFactory.h"
namespace android {
namespace renderengine {
@@ -87,12 +88,12 @@
int hwcFormat, Protection protection);
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
- inline SkRRect getRoundedRect(const LayerSettings* layer);
- inline BlurRegion getBlurRegion(const LayerSettings* layer);
+ inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer);
inline bool layerHasBlur(const LayerSettings* layer);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
+ inline GrDirectContext* getActiveGrContext() const;
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
@@ -101,7 +102,8 @@
const ShadowSettings& shadowSettings);
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+ sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
+ const LayerSettings* layer,
const DisplaySettings& display,
bool undoPremultipliedAlpha,
bool requiresLinearEffect);
@@ -116,14 +118,20 @@
const PixelFormat mDefaultPixelFormat;
const bool mUseColorManagement;
+ // Identifier used or various mappings of layers to various
+ // textures or shaders
+ using LayerId = uint64_t;
+
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
- std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
+ std::unordered_map<LayerId, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
// Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+ std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>>
mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+
+ StretchShaderFactory mStretchShaderFactory;
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 5cba8fb..4ad6e94 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -139,23 +139,21 @@
return matrix;
}
-void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
sk_sp<SkImage> input) {
ATRACE_CALL();
SkPaint paint;
- paint.setAlphaf(effectRegion.alpha);
- if (effectRegion.alpha == 1.0f) {
- paint.setBlendMode(SkBlendMode::kSrc);
- }
+ paint.setAlphaf(blurAlpha);
const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < kMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -168,30 +166,21 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
paint.setShader(blurShader);
}
- // TODO we should AA at least the drawRoundRect which would mean no SRC blending
- // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
- auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
- effectRegion.bottom);
-
- if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
- effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
- const SkVector radii[4] =
- {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
- SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
- SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
- SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
- SkRRect roundedRect;
- roundedRect.setRectRadii(rect, radii);
- canvas->drawRRect(roundedRect, paint);
+ if (effectRegion.isRect()) {
+ if (blurAlpha == 1.0f) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ canvas->drawRect(effectRegion.rect(), paint);
} else {
- canvas->drawRect(rect, paint);
+ paint.setAntiAlias(true);
+ canvas->drawRRect(effectRegion, paint);
}
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 731ba11..a8e6dd7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,7 +20,6 @@
#include <SkImage.h>
#include <SkRuntimeEffect.h>
#include <SkSurface.h>
-#include <ui/BlurRegion.h>
using namespace std;
@@ -52,8 +51,19 @@
sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
- void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
- sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+ /**
+ * Draw the blurred content (from the generate method) into the canvas.
+ * @param canvas is the destination/output for the blur
+ * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage
+ * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect
+ * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn
+ * @param blurRect bounds of the blurredImage translated into canvas coordinates
+ * @param blurredImage down-sampled blurred content that was produced by the generate() method
+ * @param input original unblurred input that is used to crossfade with the blurredImage
+ */
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
+ const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
new file mode 100644
index 0000000..9b62789
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2021 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 "StretchShaderFactory.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include "log/log.h"
+#include <memory>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString stretchShader = SkString(R"(
+ uniform shader uContentTexture;
+
+ // multiplier to apply to scale effect
+ uniform float uMaxStretchIntensity;
+
+ // Maximum percentage to stretch beyond bounds of target
+ uniform float uStretchAffectedDistX;
+ uniform float uStretchAffectedDistY;
+
+ // Distance stretched as a function of the normalized overscroll times
+ // scale intensity
+ uniform float uDistanceStretchedX;
+ uniform float uDistanceStretchedY;
+ uniform float uInverseDistanceStretchedX;
+ uniform float uInverseDistanceStretchedY;
+ uniform float uDistDiffX;
+
+ // Difference between the peak stretch amount and overscroll amount normalized
+ uniform float uDistDiffY;
+
+ // Horizontal offset represented as a ratio of pixels divided by the target width
+ uniform float uScrollX;
+ // Vertical offset represented as a ratio of pixels divided by the target height
+ uniform float uScrollY;
+
+ // Normalized overscroll amount in the horizontal direction
+ uniform float uOverscrollX;
+
+ // Normalized overscroll amount in the vertical direction
+ uniform float uOverscrollY;
+ uniform float viewportWidth; // target height in pixels
+ uniform float viewportHeight; // target width in pixels
+
+ // uInterpolationStrength is the intensity of the interpolation.
+ // if uInterpolationStrength is 0, then the stretch is constant for all the
+ // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity
+ // is interpolated based on the pixel position in the uStretchAffectedDist area;
+ // The closer we are from the scroll anchor point, the more it stretches,
+ // and the other way around.
+ uniform float uInterpolationStrength;
+
+ float easeInCubic(float t, float d) {
+ float tmp = t * d;
+ return tmp * tmp * tmp;
+ }
+
+ float computeOverscrollStart(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = uStretchAffectedDist - inPos;
+ float posBasedVariation = mix(
+ 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = overscroll * posBasedVariation;
+ return distanceStretched - (offsetPos / (1. + stretchIntensity));
+ }
+
+ float computeOverscrollEnd(
+ float inPos,
+ float overscroll,
+ float reverseStretchDist,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float interpolationStrength
+ ) {
+ float offsetPos = inPos - reverseStretchDist;
+ float posBasedVariation = mix(
+ 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+ float stretchIntensity = (-overscroll) * posBasedVariation;
+ return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+ }
+
+ // Prefer usage of return values over out parameters as it enables
+ // SKSL to properly inline method calls and works around potential GPU
+ // driver issues on Wembly. See b/182566543 for details
+ float computeOverscroll(
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float uInverseStretchAffectedDist,
+ float distanceStretched,
+ float distanceDiff,
+ float interpolationStrength
+ ) {
+ float outPos = inPos;
+ // overscroll is provided via uniform so there is no concern
+ // for potential incoherent branches
+ if (overscroll > 0) {
+ if (inPos <= uStretchAffectedDist) {
+ outPos = computeOverscrollStart(
+ inPos,
+ overscroll,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos >= distanceStretched) {
+ outPos = distanceDiff + inPos;
+ }
+ }
+ if (overscroll < 0) {
+ float stretchAffectedDist = 1. - uStretchAffectedDist;
+ if (inPos >= stretchAffectedDist) {
+ outPos = computeOverscrollEnd(
+ inPos,
+ overscroll,
+ stretchAffectedDist,
+ uStretchAffectedDist,
+ uInverseStretchAffectedDist,
+ distanceStretched,
+ interpolationStrength
+ );
+ } else if (inPos < stretchAffectedDist) {
+ outPos = -distanceDiff + inPos;
+ }
+ }
+ return outPos;
+ }
+
+ vec4 main(vec2 coord) {
+ // Normalize SKSL pixel coordinate into a unit vector
+ float inU = coord.x / viewportWidth;
+ float inV = coord.y / viewportHeight;
+ float outU;
+ float outV;
+ float stretchIntensity;
+ // Add the normalized scroll position within scrolling list
+ inU += uScrollX;
+ inV += uScrollY;
+ outU = inU;
+ outV = inV;
+ outU = computeOverscroll(
+ inU,
+ uOverscrollX,
+ uStretchAffectedDistX,
+ uInverseDistanceStretchedX,
+ uDistanceStretchedX,
+ uDistDiffX,
+ uInterpolationStrength
+ );
+ outV = computeOverscroll(
+ inV,
+ uOverscrollY,
+ uStretchAffectedDistY,
+ uInverseDistanceStretchedY,
+ uDistanceStretchedY,
+ uDistDiffY,
+ uInterpolationStrength
+ );
+ coord.x = (outU - uScrollX) * viewportWidth;
+ coord.y = (outV - uScrollY) * viewportHeight;
+ return sample(uContentTexture, coord);
+ })");
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+sk_sp<SkShader> StretchShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect) {
+ if (!stretchEffect.hasEffect()) {
+ return nullptr;
+ }
+
+ float viewportWidth = stretchEffect.width;
+ float viewportHeight = stretchEffect.height;
+ float normOverScrollDistX = stretchEffect.vectorX;
+ float normOverScrollDistY = stretchEffect.vectorY;
+ float distanceStretchedX =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
+ float distanceStretchedY =
+ StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
+ float inverseDistanceStretchedX =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float inverseDistanceStretchedY =
+ 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffX =
+ distanceStretchedX - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ float diffY =
+ distanceStretchedY - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+ auto& srcBounds = stretchEffect.mappedChildBounds;
+ float normalizedScrollX = srcBounds.left / viewportWidth;
+ float normalizedScrollY = srcBounds.top / viewportHeight;
+
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance =
+ SkRuntimeEffect::MakeForShader(stretchShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ mBuilder->child("uContentTexture") = inputShader;
+ mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
+ mBuilder->uniform("uStretchAffectedDistX").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uStretchAffectedDistY").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+ mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+ mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+ mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
+ mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
+ mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+ mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+ mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+ mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+ mBuilder->uniform("uScrollX").set(&normalizedScrollX, 1);
+ mBuilder->uniform("uScrollY").set(&normalizedScrollY, 1);
+ mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+ mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+ return mBuilder->makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.h b/libs/renderengine/skia/filters/StretchShaderFactory.h
new file mode 100644
index 0000000..9c3ab7c
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <ui/StretchEffect.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+class StretchShaderFactory {
+public:
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader,
+ const StretchEffect& stretchEffect);
+
+private:
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 85322a9..72e32ed 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -263,6 +263,11 @@
}
}
+ void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance);
+ }
+
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
@@ -1887,6 +1892,51 @@
0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(&redLayer);
+
+ // Green layer with 1/2 size with parent crop rect.
+ renderengine::LayerSettings greenLayer = redLayer;
+ greenLayer.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
+ greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+
+ layers.push_back(&greenLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // top middle should be green and the bottom middle red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+
+ // the bottom edge of the green layer should not be rounded
+ expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
index 0803df3..cf08acb 100644
--- a/libs/ui/include/ui/StretchEffect.h
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -25,31 +25,49 @@
namespace android {
struct StretchEffect : public LightFlattenablePod<StretchEffect> {
- FloatRect area = {0, 0, 0, 0};
- float vectorX = 0;
- float vectorY = 0;
- float maxAmount = 0;
+ constexpr static const float CONTENT_DISTANCE_STRETCHED = 1.f;
- bool operator==(const StretchEffect& other) const {
- return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
- maxAmount == other.maxAmount;
+ float width = 0;
+ float height = 0;
+ float vectorX = 0;
+ float vectorY = 0;
+ float maxAmountX = 0;
+ float maxAmountY = 0;
+ FloatRect mappedChildBounds = {0, 0, 0, 0};
+
+ bool operator==(const StretchEffect& other) const {
+ return width == other.width && height == other.height &&
+ vectorX == other.vectorX &&
+ vectorY == other.vectorY &&
+ maxAmountX == other.maxAmountX &&
+ maxAmountY == other.maxAmountY &&
+ mappedChildBounds == other.mappedChildBounds;
+ }
+
+ static bool isZero(float value) {
+ constexpr float NON_ZERO_EPSILON = 0.001f;
+ return fabsf(value) <= NON_ZERO_EPSILON;
+ }
+
+ bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+ bool hasEffect() const { return !isNoOp(); }
+
+ void sanitize() {
+ // If the area is empty, or the max amount is zero, then reset back to defaults
+ if (width == 0.f || height == 0.f || isZero(maxAmountX) ||
+ isZero(maxAmountY)) {
+ *this = StretchEffect{};
}
+ }
- static bool isZero(float value) {
- constexpr float NON_ZERO_EPSILON = 0.001f;
- return fabsf(value) <= NON_ZERO_EPSILON;
- }
+ float getStretchWidthMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorX));
+ }
- bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
-
- bool hasEffect() const { return !isNoOp(); }
-
- void sanitize() {
- // If the area is empty, or the max amount is zero, then reset back to defaults
- if (area.isEmpty() || isZero(maxAmount)) {
- *this = StretchEffect{};
- }
- }
+ float getStretchHeightMultiplier() const {
+ return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorY));
+ }
};
static_assert(std::is_trivially_copyable<StretchEffect>::value,
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9885352..f20bfe1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,7 +25,7 @@
name: "libsurfaceflinger_defaults",
defaults: [
"surfaceflinger_defaults",
- "skia_deps",
+ "skia_renderengine_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 570a41a..a273230 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -51,9 +51,6 @@
return flags;
}
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
- ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 2d9f01b..b1ee3fb 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -36,7 +36,8 @@
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
- lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+ lhs.stretchEffect == rhs.stretchEffect;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 0cc2c6e..5565396 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -101,9 +101,9 @@
}
void dumpVal(std::string& out, const char* name, const StretchEffect& effect) {
- StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name,
- effect.area.left, effect.area.top, effect.area.right, effect.area.bottom,
- effect.vectorX, effect.vectorY, effect.maxAmount);
+ StringAppendF(&out, "%s={ width =%f, height = %f, vec=(%f, %f), max=(%f, %f) } ", name,
+ effect.width, effect.height,
+ effect.vectorX, effect.vectorY, effect.maxAmountX, effect.maxAmountY);
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 0033dbe..f19e2a7 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -730,9 +730,11 @@
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
+ while (mPredictions.size() >= kMaxTokens) {
+ mPredictions.erase(mPredictions.begin());
+ }
const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = {systemTime(), predictions};
- flushTokens(systemTime());
+ mPredictions[assignedToken] = predictions;
return assignedToken;
}
@@ -740,23 +742,11 @@
std::scoped_lock lock(mMutex);
auto predictionsIterator = mPredictions.find(token);
if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second.predictions;
+ return predictionsIterator->second;
}
return {};
}
-void TokenManager::flushTokens(nsecs_t flushTime) {
- for (auto it = mPredictions.begin(); it != mPredictions.end();) {
- if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
- it = mPredictions.erase(it);
- } else {
- // Tokens are ordered by time. If i'th token is within the retention time, then the
- // i+1'th token will also be within retention time.
- break;
- }
- }
-}
-
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
JankClassificationThresholds thresholds)
: mMaxDisplayFrames(kDefaultMaxDisplayFrames),
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 0563a53..42be55a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -92,11 +92,6 @@
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
-struct TokenManagerPrediction {
- nsecs_t timestamp = 0;
- TimelineItem predictions;
-};
-
struct JankClassificationThresholds {
// The various thresholds for App and SF. If the actual timestamp falls within the threshold
// compared to prediction, we treat it as on time.
@@ -334,11 +329,10 @@
void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
- std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
+ std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
int64_t mCurrentToken GUARDED_BY(mMutex);
mutable std::mutex mMutex;
- static constexpr nsecs_t kMaxRetentionTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+ static constexpr size_t kMaxTokens = 500;
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0d673ea..21c9d74 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -635,7 +635,7 @@
layerSettings.blurRegionTransform =
getActiveTransform(getDrawingState()).inverse().asMatrix4();
}
- layerSettings.stretchEffect = getDrawingState().stretchEffect;
+ layerSettings.stretchEffect = getStretchEffect();
// Record the name of the layer for debugging further down the stack.
layerSettings.name = getName();
return layerSettings;
@@ -858,7 +858,7 @@
const State& s(getDrawingState());
State& c(getCurrentState());
- if (getActiveGeometry(c) != getActiveGeometry(s)) {
+ if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
@@ -933,20 +933,18 @@
}
bool Layer::setPosition(float x, float y) {
- if (mCurrentState.requested_legacy.transform.tx() == x &&
- mCurrentState.requested_legacy.transform.ty() == y)
- return false;
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false;
mCurrentState.sequence++;
// We update the requested and active position simultaneously because
// we want to apply the position portion of the transform matrix immediately,
// but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
- mCurrentState.requested_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
// Here we directly update the active state
// unlike other setters, because we store it within
// the transform, but use different latching rules.
// b/38182305
- mCurrentState.active_legacy.transform.set(x, y);
+ mCurrentState.transform.set(x, y);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1065,6 +1063,7 @@
setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
return true;
}
+
bool Layer::setAlpha(float alpha) {
if (mCurrentState.color.a == alpha) return false;
mCurrentState.sequence++;
@@ -1143,8 +1142,7 @@
return false;
}
mCurrentState.sequence++;
- mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
- matrix.dsdy);
+ mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1560,20 +1558,20 @@
info.mVisibleRegion = getVisibleRegion(display);
info.mSurfaceDamageRegion = surfaceDamageRegion;
info.mLayerStack = getLayerStack();
- info.mX = ds.active_legacy.transform.tx();
- info.mY = ds.active_legacy.transform.ty();
+ info.mX = ds.transform.tx();
+ info.mY = ds.transform.ty();
info.mZ = ds.z;
- info.mWidth = ds.active_legacy.w;
- info.mHeight = ds.active_legacy.h;
+ info.mWidth = ds.width;
+ info.mHeight = ds.height;
info.mCrop = ds.crop;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
- info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
- info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
- info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
- info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
+ info.mMatrix[0][0] = ds.transform[0][0];
+ info.mMatrix[0][1] = ds.transform[0][1];
+ info.mMatrix[1][0] = ds.transform[1][0];
+ info.mMatrix[1][1] = ds.transform[1][1];
{
sp<const GraphicBuffer> buffer = getBuffer();
if (buffer != 0) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index a83408b..284adbd 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -484,12 +484,9 @@
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
- virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
- virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
- virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
- virtual ui::Transform getActiveTransform(const Layer::State& s) const {
- return s.active_legacy.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6497919..beda834 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1741,12 +1741,6 @@
}
void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
- const auto vsyncIn = [&] {
- if (!ATRACE_ENABLED()) return 0.f;
- return (expectedVSyncTime - systemTime()) / 1e6f;
- }();
-
- ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
switch (what) {
case MessageQueue::INVALIDATE: {
onMessageInvalidate(vsyncId, expectedVSyncTime);
@@ -1760,8 +1754,6 @@
}
void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
- ATRACE_CALL();
-
const nsecs_t frameStart = systemTime();
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
@@ -1776,6 +1768,13 @@
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVSyncTime;
+ const auto vsyncIn = [&] {
+ if (!ATRACE_ENABLED()) return 0.f;
+ return (mExpectedPresentTime - systemTime()) / 1e6f;
+ }();
+ ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
+ mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
@@ -3105,7 +3104,7 @@
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
mAnimTransactionPending = false;
}
@@ -3527,7 +3526,9 @@
// Generate a CountDownLatch pending state if this is a synchronous transaction.
if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
- (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+ (state.inputWindowCommands.syncInputWindows
+ ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
+ : CountDownLatch::eSyncTransaction));
}
mTransactionQueue.emplace(state);
@@ -3552,10 +3553,10 @@
}
}
-void SurfaceFlinger::signalSynchronousTransactions() {
+void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
for (auto it = mTransactionCommittedSignals.begin();
it != mTransactionCommittedSignals.end();) {
- if ((*it)->countDown() == 0) {
+ if ((*it)->countDown(flag)) {
it = mTransactionCommittedSignals.erase(it);
} else {
it++;
@@ -6175,7 +6176,7 @@
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
- signalSynchronousTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cf1a545..4bbdd48 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -467,24 +467,31 @@
class CountDownLatch {
public:
- explicit CountDownLatch(int32_t count) : mCount(count) {}
+ enum {
+ eSyncTransaction = 1 << 0,
+ eSyncInputWindows = 1 << 1,
+ };
+ explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
- int32_t countDown() {
+ // True if there is no waiting condition after count down.
+ bool countDown(uint32_t flag) {
std::unique_lock<std::mutex> lock(mMutex);
- if (mCount == 0) {
- return 0;
+ if (mFlags == 0) {
+ return true;
}
- if (--mCount == 0) {
+ mFlags &= ~flag;
+ if (mFlags == 0) {
mCountDownComplete.notify_all();
+ return true;
}
- return mCount;
+ return false;
}
// Return true if triggered.
bool wait_until(const std::chrono::seconds& timeout) const {
std::unique_lock<std::mutex> lock(mMutex);
const auto untilTime = std::chrono::system_clock::now() + timeout;
- while (mCount != 0) {
+ while (mFlags != 0) {
// Conditional variables can be woken up sporadically, so we check count
// to verify the wakeup was triggered by |countDown|.
if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
@@ -495,7 +502,7 @@
}
private:
- int32_t mCount;
+ uint32_t mFlags;
mutable std::condition_variable mCountDownComplete;
mutable std::mutex mMutex;
};
@@ -1124,7 +1131,7 @@
// Add transaction to the Transaction Queue
void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
- void signalSynchronousTransactions();
+ void signalSynchronousTransactions(const uint32_t flag);
/*
* Generic Layer Metadata
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 113f463..c5f1598 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -130,8 +130,8 @@
transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
const int32_t layerId(getLayerId(layer));
- addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
- layer->mCurrentState.active_legacy.transform.ty());
+ addPositionLocked(transaction, layerId, layer->mCurrentState.transform.tx(),
+ layer->mCurrentState.transform.ty());
addDepthLocked(transaction, layerId, layer->mCurrentState.z);
addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
addTransparentRegionLocked(transaction, layerId,
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 6ed6148..c6a4115 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -73,7 +73,7 @@
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
- maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+ maxTokens = mTokenManager->kMaxTokens;
}
// Each tracing session can be used for a single block of Start -> Stop.
@@ -111,9 +111,11 @@
mFrameTimeline->setSfPresent(2500, presentFence1);
}
- void flushTokens(nsecs_t flushTime) {
- std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
- mTokenManager->flushTokens(flushTime);
+ void flushTokens() {
+ for (size_t i = 0; i < maxTokens; i++) {
+ mTokenManager->generateTokenForPredictions({});
+ }
+ EXPECT_EQ(getPredictions().size(), maxTokens);
}
SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
@@ -132,7 +134,7 @@
a.presentTime == b.presentTime;
}
- const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
+ const std::map<int64_t, TimelineItem>& getPredictions() const {
return mTokenManager->mPredictions;
}
@@ -155,7 +157,7 @@
TraceCookieCounter* mTraceCookieCounter;
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
- nsecs_t maxTokenRetentionTime;
+ size_t maxTokens;
static constexpr pid_t kSurfaceFlingerPid = 666;
static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
@@ -177,12 +179,11 @@
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1u);
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
// token1 should have expired
- EXPECT_EQ(getPredictions().size(), 1u);
EXPECT_EQ(predictions.has_value(), false);
predictions = mTokenManager->getPredictionsForToken(token2);
@@ -212,7 +213,7 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
@@ -707,7 +708,7 @@
sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1065,7 +1066,7 @@
tracingSession->StartBlocking();
int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -1283,7 +1284,7 @@
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
@@ -1359,7 +1360,7 @@
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
// Flush the token so that it would expire
- flushTokens(systemTime() + maxTokenRetentionTime);
+ flushTokens();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index eb24a22..80e9a3c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -106,42 +106,6 @@
],
}
-cc_binary {
- name: "vr_hwc",
- enabled: false,
- system_ext_specific: true,
- vintf_fragments: ["manifest_vr_hwc.xml"],
- srcs: [
- "vr_hardware_composer_service.cpp",
- ],
- static_libs: [
- "libvr_hwc-impl",
- // NOTE: This needs to be included after the *-impl lib otherwise the
- // symbols in the *-binder library get optimized out.
- "libvr_hwc-binder",
- ],
- shared_libs: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.3",
- "libbase",
- "libbinder",
- "liblog",
- "libhardware",
- "libhidlbase",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-Wall",
- "-Werror",
- ],
- init_rc: [
- "vr_hwc.rc",
- ],
-}
-
cc_test {
name: "vr_hwc_test",
gtest: true,
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
deleted file mode 100644
index 1068cac..0000000
--- a/services/vr/hardware_composer/manifest_vr_hwc.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal>
- <name>android.hardware.graphics.composer</name>
- <transport>hwbinder</transport>
- <version>2.1</version>
- <interface>
- <name>IComposer</name>
- <instance>vr</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
deleted file mode 100644
index 7701847..0000000
--- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2017 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 <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <hwbinder/IPCThreadState.h>
-#include <impl/vr_hwc.h>
-#include <inttypes.h>
-
-#include "vr_composer.h"
-
-int main() {
- android::ProcessState::self()->startThreadPool();
-
- // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR
- // mode.
- android::sp<android::dvr::VrHwc> service = new android::dvr::VrHwc();
-
- LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
- LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
-
- const char instance[] = "vr";
- LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK,
- "Failed to register service");
-
- android::sp<android::dvr::VrComposer> composer =
- new android::dvr::VrComposer(service.get());
-
- android::sp<android::IServiceManager> sm(android::defaultServiceManager());
-
- // Register the binder service used by VR Window Manager service to receive
- // frame information from VR HWC HAL.
- android::status_t status = sm->addService(
- android::dvr::VrComposer::SERVICE_NAME(), composer.get(),
- false /* allowIsolated */);
- LOG_ALWAYS_FATAL_IF(status != android::OK,
- "VrDisplay service failed to start: %" PRId32, status);
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::hardware::IPCThreadState::self()->joinThreadPool();
-
- return 0;
-}
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
deleted file mode 100644
index 645ab80..0000000
--- a/services/vr/hardware_composer/vr_hwc.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vr_hwc /system/bin/vr_hwc
- class hal animation
- user system
- group system graphics
- onrestart restart surfaceflinger
- writepid /dev/cpuset/system-background/tasks