Added Display increments, more controls, and a readme to the replayer
Change-Id: Id21144b7f808858149772de511951546fddfb252
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
index cad11a0..77de8dc 100644
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
@@ -23,7 +23,7 @@
using namespace android;
BufferQueueScheduler::BufferQueueScheduler(
- const sp<SurfaceControl>& surfaceControl, const RGB& color, int id)
+ const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
: mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
void BufferQueueScheduler::startScheduling() {
@@ -44,6 +44,7 @@
bufferUpdate(event.dimensions);
fillSurface(event.event);
+ mColor.modulate();
lock.lock();
mBufferEvents.pop();
}
@@ -64,7 +65,7 @@
}
void BufferQueueScheduler::setSurfaceControl(
- const sp<SurfaceControl>& surfaceControl, const RGB& color) {
+ const sp<SurfaceControl>& surfaceControl, const HSV& color) {
std::lock_guard<std::mutex> lock(mMutex);
mSurfaceControl = surfaceControl;
mColor = color;
@@ -76,7 +77,7 @@
s->setBuffersDimensions(dimensions.width, dimensions.height);
}
-void BufferQueueScheduler::fillSurface(std::shared_ptr<Event> event) {
+void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
ANativeWindow_Buffer outBuffer;
sp<Surface> s = mSurfaceControl->getSurface();
@@ -87,13 +88,15 @@
return;
}
+ auto color = mColor.getRGB();
+
auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
for (int y = 0; y < outBuffer.height; y++) {
for (int x = 0; x < outBuffer.width; x++) {
uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
- pixel[0] = mColor.r;
- pixel[1] = mColor.g;
- pixel[2] = mColor.b;
+ pixel[0] = color.r;
+ pixel[1] = color.g;
+ pixel[2] = color.b;
pixel[3] = LAYER_ALPHA;
}
}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
index fc8e20c..cb20fcc 100644
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
@@ -52,23 +52,23 @@
class BufferQueueScheduler {
public:
- BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const RGB& color, int id);
+ BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
void startScheduling();
void addEvent(const BufferEvent&);
void stopScheduling();
- void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const RGB& color);
+ void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
private:
void bufferUpdate(const Dimensions& dimensions);
// Lock and fill the surface, block until the event is signaled by the main loop,
// then unlock and post the buffer.
- void fillSurface(std::shared_ptr<Event>);
+ void fillSurface(const std::shared_ptr<Event>& event);
sp<SurfaceControl> mSurfaceControl;
- RGB mColor;
+ HSV mColor;
const int mSurfaceId;
bool mContinueScheduling;
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
index d644b8d..ce644be 100644
--- a/cmds/surfacereplayer/replayer/Color.h
+++ b/cmds/surfacereplayer/replayer/Color.h
@@ -22,76 +22,96 @@
namespace android {
-typedef struct RGB {
- RGB() = default;
+constexpr double modulateFactor = .0001;
+constexpr double modulateLimit = .80;
+
+struct RGB {
RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
-} RGB;
+};
-typedef struct HSV {
+struct HSV {
HSV() = default;
HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
double h = 0;
double s = 0;
double v = 0;
-} HSV;
-static inline RGB HSVToRGB(HSV hsv) {
+ RGB getRGB() const;
+
+ bool modulateUp = false;
+
+ void modulate();
+};
+
+void inline HSV::modulate() {
+ if(modulateUp) {
+ v += modulateFactor;
+ } else {
+ v -= modulateFactor;
+ }
+
+ if(v <= modulateLimit || v >= 1) {
+ modulateUp = !modulateUp;
+ }
+}
+
+inline RGB HSV::getRGB() const {
using namespace std;
double r = 0, g = 0, b = 0;
- if (hsv.s == 0) {
- r = hsv.v;
- g = hsv.v;
- b = hsv.v;
+ if (s == 0) {
+ r = v;
+ g = v;
+ b = v;
} else {
- hsv.h = static_cast<int>(hsv.h) % 360;
- hsv.h = hsv.h / 60;
+ auto tempHue = static_cast<int>(h) % 360;
+ tempHue = tempHue / 60;
- int i = static_cast<int>(trunc(hsv.h));
- double f = hsv.h - i;
+ int i = static_cast<int>(trunc(tempHue));
+ double f = h - i;
- double x = hsv.v * (1.0 - hsv.s);
- double y = hsv.v * (1.0 - (hsv.s * f));
- double z = hsv.v * (1.0 - (hsv.s * (1.0 - f)));
+ double x = v * (1.0 - s);
+ double y = v * (1.0 - (s * f));
+ double z = v * (1.0 - (s * (1.0 - f)));
switch (i) {
case 0:
- r = hsv.v;
+ r = v;
g = z;
b = x;
break;
case 1:
r = y;
- g = hsv.v;
+ g = v;
b = x;
break;
case 2:
r = x;
- g = hsv.v;
+ g = v;
b = z;
break;
case 3:
r = x;
g = y;
- b = hsv.v;
+ b = v;
break;
case 4:
r = z;
g = x;
- b = hsv.v;
+ b = v;
break;
default:
- r = hsv.v;
+ r = v;
g = x;
b = y;
break;
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
index bf5da52..dd1dd7d 100644
--- a/cmds/surfacereplayer/replayer/Main.cpp
+++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -41,9 +41,16 @@
std::cout << " -m Stops the replayer at the start of the trace and switches ";
"to manual replay\n";
- std::cout << " -t [Number of Threads] Specifies the number of threads to be used while "
+ std::cout << "\n -t [Number of Threads] Specifies the number of threads to be used while "
"replaying (default is " << android::DEFAULT_THREADS << ")\n";
+ std::cout << "\n -s [Timestamp] Specify at what timestamp should the replayer switch "
+ "to manual replay\n";
+
+ std::cout << " -n Ignore timestamps and run through trace as fast as possible\n";
+
+ std::cout << " -l Indefinitely loop the replayer\n";
+
std::cout << " -h Display help menu\n";
std::cout << std::endl;
@@ -51,11 +58,14 @@
int main(int argc, char** argv) {
std::string filename;
+ bool loop = false;
+ bool wait = true;
bool pauseBeginning = false;
int numThreads = DEFAULT_THREADS;
+ long stopHere = -1;
int opt = 0;
- while ((opt = getopt(argc, argv, "pt:h?")) != -1) {
+ while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
switch (opt) {
case 'm':
pauseBeginning = true;
@@ -63,6 +73,15 @@
case 't':
numThreads = atoi(optarg);
break;
+ case 's':
+ stopHere = atol(optarg);
+ break;
+ case 'n':
+ wait = false;
+ break;
+ case 'l':
+ loop = true;
+ break;
case 'h':
case '?':
printHelpMenu();
@@ -81,8 +100,11 @@
}
filename.assign(input[0]);
- android::Replayer r(filename, pauseBeginning, numThreads);
- auto status = r.replay();
+ status_t status = NO_ERROR;
+ do {
+ android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
+ status = r.replay();
+ } while(loop);
if (status == NO_ERROR) {
std::cout << "Successfully finished replaying trace" << std::endl;
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
new file mode 100644
index 0000000..893f0dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/README.md
@@ -0,0 +1,262 @@
+SurfaceReplayer Documentation
+===================
+
+[go/SurfaceReplayer](go/SurfaceReplayer)
+
+SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
+[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
+
+* Creation and deletion of surfaces/displays
+* Alterations to the surfaces/displays called Transactions
+* Buffer Updates to surfaces
+* VSync events
+
+At their specified times to be as close to the original trace.
+
+Usage
+--------
+
+###Creating a trace
+
+SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
+utilize it. To allow it to write to the device, run
+
+`setenforce 0`
+
+To start recording a trace, run
+
+`service call SurfaceFlinger 1020 i32 1`
+
+To stop recording, run
+
+`service call SurfaceFlinger 1020 i32 0`
+
+The default location for the trace is `/data/SurfaceTrace.dat`
+
+###Executable
+
+To replay a specific trace, execute
+
+`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
+
+inside the android shell. This will replay the full trace and then exit. Running this command
+outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
+off VSync injections if it interrupted in any way other than fully replaying the trace
+
+The replay will not fill surfaces with their contents during the capture. Rather they are given a
+random color which will be the same every time the trace is replayed. Surfaces modulate their color
+at buffer updates.
+
+**Options:**
+
+- -m pause the replayer at the start of the trace for manual replay
+- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
+- -s [Timestamp] switches to manual replay at specified timestamp
+- -n Ignore timestamps and run through trace as fast as possible
+- -l Indefinitely loop the replayer
+- -h displays help menu
+
+**Manual Replay:**
+When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
+by the user. Pressing CTRL-C again will exit the replayer.
+
+Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
+input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
+without inputting a command repeats the previous command.
+
+- n - steps the replayer to the next VSync event
+- ni - steps the replayer to the next increment
+- c - continues normal replaying
+- c [milliseconds] - continue until specified number of milliseconds have passed
+- s [timestamp] - continue and stop at specified timestamp
+- l - list out timestamp of current increment
+- h - displays help menu
+
+###Shared Library
+
+To use the shared library include these shared libraries
+
+`libsurfacereplayer`
+`libprotobuf-cpp-full`
+`libutils`
+
+And the static library
+
+`libtrace_proto`
+
+Include the replayer header at the top of your file
+
+`#include <replayer/Replayer.h>`
+
+There are two constructors for the replayer
+
+`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
+`Replayer(Trace& trace, ... ditto ...)`
+
+The first constructor takes in the filepath where the trace is located and loads in the trace
+object internally.
+- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
+- numThreads - Number of worker threads the replayer will use.
+- wait - **False**: Replayer ignores waits in between increments
+- stopHere - Time stamp of where the replayer should run to then switch to manual replay
+
+The second constructor includes all of the same parameters but takes in a preloaded trace object.
+To use add
+
+`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
+
+To your file
+
+After initializing the Replayer call
+
+ replayer.replay();
+
+And the trace will start replaying. Once the trace is finished replaying, the function will return.
+The layers that are visible at the end of the trace will remain on screen until the program
+terminates.
+
+
+**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
+never executed. This can be fixed by executing
+
+`service call SurfaceFlinger 23 i32 0`
+
+in the android shell
+
+Code Breakdown
+-------------
+
+The Replayer is composed of 5 components.
+
+- The data format of the trace (Trace.proto)
+- The Replayer object (Replayer.cpp)
+- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
+- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
+- The Main executable (Main.cpp)
+
+### Traces
+
+Traces are represented as a protobuf message located in surfacereplayer/proto/src.
+
+**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
+**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
+
+ - Transaction
+ - SurfaceCreation
+ - SurfaceDeletion
+ - DisplayCreation
+ - DisplayDeleteion
+ - BufferUpdate
+ - VSyncEvent
+ - PowerModeUpdate
+
+**Transactions** contain whether the transaction was synchronous or animated and *repeated*
+**SurfaceChanges** and **DisplayChanges**
+
+- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
+position, alpha, hidden, size, etc.
+- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
+size, layer stack, projection, etc.
+
+**Surface/Display Creation** contain the id of the surface/display and the name of the
+surface/display
+
+**Surface/Display Deletion** contain the id of the surface/display to be deleted
+
+**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
+buffer, and the frame number.
+
+**VSyncEvents** contain when the VSync event has occurred.
+
+**PowerModeUpdates** contain the id of the display being updated and what mode it is being
+changed to.
+
+To output the contents of a trace in a readable format, execute
+
+`**aprotoc** --decode=Trace \
+-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
+$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
+ < **YourTraceFile.dat** > **YourOutputName.txt**`
+
+
+###Replayer
+
+Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
+amount of time until the increment should be executed, then executing the increment. The first
+increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
+goes from there.
+
+Increments from the trace are played asynchronously rather than one by one, being dispatched by
+the main thread, queued up in a thread pool and completed when the main thread deems they are
+ready to finish execution.
+
+When an increment is dispatched, it completes as much work as it can before it has to be
+synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
+(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
+object. The main thread holds a queue of these Event objects and completes the
+corresponding Event base on its time stamp. After completing an increment, the main thread will
+dispatch another increment and continue.
+
+The main thread's execution flow is outlined below
+
+ initReplay() //queue up the initial increments
+ while(!pendingIncrements.empty()) { //while increments remaining
+ event = pendingIncrement.pop();
+ wait(event.time_stamp(); //waitUntil it is time to complete this increment
+
+ event.complete() //signal to let event finish
+ if(increments remaing()) {
+ dispatchEvent() //queue up another increment
+ }
+ }
+
+A worker thread's flow looks like so
+
+ //dispatched!
+ Execute non-time sensitive work here
+ ...
+ event.readyToExecute() //time sensitive point...waiting for Main Thread
+ ...
+ Finish execution
+
+
+### Event
+
+An Event is a simple synchronization mechanism used to facilitate communication between the main
+and worker threads. Every time an increment is dispatched, an Event object is also created.
+
+An Event can be in 4 different states:
+
+- **SettingUp** - The worker is in the process of completing all non-time sensitive work
+- **Waiting** - The worker is waiting on the main thread to signal it.
+- **Signaled** - The worker has just been signaled by the main thread
+- **Running** - The worker is running again and finishing the rest of its work.
+
+When the main thread wants to finish the execution of a worker, the worker can either still be
+**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
+main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
+state once **Signaled**. This last step exists in order to communicate back to the main thread that
+the worker thread has actually started completing its execution, rather than being preempted right
+after signalling. Once this happens, the main thread schedules the next worker. This makes sure
+there is a constant amount of workers running at one time.
+
+This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
+worker and main thread respectively.
+
+### BufferQueueScheduler
+
+During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
+buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
+**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
+which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
+handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
+
+When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
+**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
+every time an Event is completed.
+
+### Main
+
+The main exectuable reads in the command line arguments. Creates the Replayer using those
+arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
+gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 950ebbb..ace10d1 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -22,6 +22,7 @@
#include <binder/IMemory.h>
+#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <private/gui/ComposerService.h>
@@ -40,15 +41,24 @@
#include <functional>
#include <iostream>
#include <mutex>
+#include <sstream>
#include <string>
#include <thread>
+#include <vector>
using namespace android;
std::atomic_bool Replayer::sReplayingManually(false);
-Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads)
- : mTrace(), mLoaded(false), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads) {
+Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
+ nsecs_t stopHere)
+ : mTrace(),
+ mLoaded(false),
+ mIncrementIndex(0),
+ mCurrentTime(0),
+ mNumThreads(numThreads),
+ mWaitForTimeStamps(wait),
+ mStopTimeStamp(stopHere) {
srand(RAND_COLOR_SEED);
std::fstream input(filename, std::ios::in | std::ios::binary);
@@ -62,19 +72,32 @@
mCurrentTime = mTrace.increment(0).time_stamp();
sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
}
-Replayer::Replayer(const Trace& t, bool replayManually, int numThreads)
- : mTrace(t), mLoaded(true), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads) {
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+ : mTrace(t),
+ mLoaded(true),
+ mIncrementIndex(0),
+ mCurrentTime(0),
+ mNumThreads(numThreads),
+ mWaitForTimeStamps(wait),
+ mStopTimeStamp(stopHere) {
srand(RAND_COLOR_SEED);
mCurrentTime = mTrace.increment(0).time_stamp();
sReplayingManually.store(replayManually);
+
+ if (stopHere < 0) {
+ mHasStopped = true;
+ }
}
status_t Replayer::replay() {
- // for manual control
- signal(SIGINT, Replayer::stopAutoReplayHandler);
+ signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
ALOGV("There are %d increments.", mTrace.increment_size());
@@ -91,11 +114,18 @@
ALOGV("Starting actual Replay!");
while (!mPendingIncrements.empty()) {
+ mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+ if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+ mHasStopped = true;
+ sReplayingManually.store(true);
+ }
+
waitForConsoleCommmand();
- auto pastIncrement = mTrace.increment(mIncrementIndex);
-
- waitUntilTimestamp(pastIncrement.time_stamp());
+ if (mWaitForTimeStamps) {
+ waitUntilTimestamp(mCurrentIncrement.time_stamp());
+ }
auto event = mPendingIncrements.front();
mPendingIncrements.pop();
@@ -116,7 +146,7 @@
}
mIncrementIndex++;
- mCurrentTime = pastIncrement.time_stamp();
+ mCurrentTime = mCurrentIncrement.time_stamp();
}
SurfaceComposerClient::enableVSyncInjections(false);
@@ -146,6 +176,21 @@
sReplayingManually.store(true);
}
+std::vector<std::string> split(const std::string& s, const char delim) {
+ std::vector<std::string> elems;
+ std::stringstream ss(s);
+ std::string item;
+ while (getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+bool isNumber(const std::string& s) {
+ return !s.empty() &&
+ std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
void Replayer::waitForConsoleCommmand() {
if (!sReplayingManually || mWaitingForNextVSync) {
return;
@@ -158,18 +203,65 @@
if (input.empty()) {
input = mLastInput;
+ } else {
+ mLastInput = input;
}
- input = mLastInput;
- if (input == "n") { // next vsync
+ if (mLastInput.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> inputs = split(input, ' ');
+
+ if (inputs[0] == "n") { // next vsync
mWaitingForNextVSync = true;
break;
- } else if (input == "c") { // continue
+
+ } else if (inputs[0] == "ni") { // next increment
+ break;
+
+ } else if (inputs[0] == "c") { // continue
+ if (inputs.size() > 1 && isNumber(inputs[1])) {
+ long milliseconds = stoi(inputs[1]);
+ std::thread([&] {
+ std::cout << "Started!" << std::endl;
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+ sReplayingManually.store(true);
+ std::cout << "Should have stopped!" << std::endl;
+ }).detach();
+ }
sReplayingManually.store(false);
mWaitingForNextVSync = false;
break;
- } else if (input == "h") { // help
- // add help menu
+
+ } else if (inputs[0] == "s") { // stop at this timestamp
+ if (inputs.size() < 1) {
+ std::cout << "No time stamp given" << std::endl;
+ continue;
+ }
+ sReplayingManually.store(false);
+ mStopTimeStamp = stol(inputs[1]);
+ mHasStopped = false;
+ break;
+ } else if (inputs[0] == "l") { // list
+ std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+ continue;
+ } else if (inputs[0] == "q") { // quit
+ SurfaceComposerClient::enableVSyncInjections(false);
+ exit(0);
+
+ } else if (inputs[0] == "h") { // help
+ // add help menu
+ std::cout << "Manual Replay options:\n";
+ std::cout << " n - Go to next VSync\n";
+ std::cout << " ni - Go to next increment\n";
+ std::cout << " c - Continue\n";
+ std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+ std::cout << " s [timestamp] - Continue and stop at specified timestamp\n";
+ std::cout << " l - List out timestamp of current increment\n";
+ std::cout << " h - Display help menu\n";
+ std::cout << std::endl;
+ continue;
}
std::cout << "Invalid Command" << std::endl;
@@ -186,11 +278,13 @@
case increment.kTransaction: {
std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
} break;
- case increment.kCreate: {
- std::thread(&Replayer::createSurfaceControl, this, increment.create(), event).detach();
+ case increment.kSurfaceCreation: {
+ std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+ .detach();
} break;
- case increment.kDelete: {
- std::thread(&Replayer::deleteSurfaceControl, this, increment.delete_(), event).detach();
+ case increment.kSurfaceDeletion: {
+ std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+ .detach();
} break;
case increment.kBufferUpdate: {
std::lock_guard<std::mutex> lock1(mLayerLock);
@@ -216,6 +310,18 @@
case increment.kVsyncEvent: {
std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
} break;
+ case increment.kDisplayCreation: {
+ std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+ .detach();
+ } break;
+ case increment.kDisplayDeletion: {
+ std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+ .detach();
+ } break;
+ case increment.kPowerModeUpdate: {
+ std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+ .detach();
+ } break;
default:
ALOGE("Unknown Increment Type: %d", increment.increment_case());
status = BAD_VALUE;
@@ -228,83 +334,12 @@
status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
ALOGV("Started Transaction");
- if (t.change_size() == 0) {
- event->readyToExecute();
- return NO_ERROR;
- }
-
- Change change = t.change(0);
-
- std::unique_lock<std::mutex> lock(mLayerLock);
- if (mLayers[change.id()] == nullptr) {
- mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
- }
- lock.unlock();
-
SurfaceComposerClient::openGlobalTransaction();
status_t status = NO_ERROR;
- for (const Change& change : t.change()) {
- std::unique_lock<std::mutex> lock(mLayerLock);
- if (mLayers[change.id()] == nullptr) {
- mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
- }
-
- switch (change.Change_case()) {
- case Change::ChangeCase::kPosition:
- status = setPosition(change.id(), change.position());
- break;
- case Change::ChangeCase::kSize:
- status = setSize(change.id(), change.size());
- break;
- case Change::ChangeCase::kAlpha:
- status = setAlpha(change.id(), change.alpha());
- break;
- case Change::ChangeCase::kLayer:
- status = setLayer(change.id(), change.layer());
- break;
- case Change::ChangeCase::kCrop:
- status = setCrop(change.id(), change.crop());
- break;
- case Change::ChangeCase::kMatrix:
- status = setMatrix(change.id(), change.matrix());
- break;
- case Change::ChangeCase::kFinalCrop:
- status = setFinalCrop(change.id(), change.final_crop());
- break;
- case Change::ChangeCase::kOverrideScalingMode:
- status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
- break;
- case Change::ChangeCase::kTransparentRegionHint:
- status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
- break;
- case Change::ChangeCase::kLayerStack:
- status = setLayerStack(change.id(), change.layer_stack());
- break;
- case Change::ChangeCase::kHiddenFlag:
- status = setHiddenFlag(change.id(), change.hidden_flag());
- break;
- case Change::ChangeCase::kOpaqueFlag:
- status = setOpaqueFlag(change.id(), change.opaque_flag());
- break;
- case Change::ChangeCase::kSecureFlag:
- status = setSecureFlag(change.id(), change.secure_flag());
- break;
- case Change::ChangeCase::kDeferredTransaction:
- waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
- status = setDeferredTransaction(change.id(), change.deferred_transaction());
- break;
- default:
- status = NO_ERROR;
- break;
- }
-
- if (status != NO_ERROR) {
- ALOGE("SET TRANSACTION FAILED");
- return status;
- }
- }
+ status = doSurfaceTransaction(t.surface_change());
+ doDisplayTransaction(t.display_change());
if (t.animation()) {
SurfaceComposerClient::setAnimationTransaction();
@@ -319,27 +354,120 @@
return status;
}
-status_t Replayer::setPosition(uint32_t id, const PositionChange& pc) {
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+ status_t status = NO_ERROR;
+
+ for (const SurfaceChange& change : surfaceChanges) {
+ std::unique_lock<std::mutex> lock(mLayerLock);
+ if (mLayers[change.id()] == nullptr) {
+ mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+ }
+
+ switch (change.SurfaceChange_case()) {
+ case SurfaceChange::SurfaceChangeCase::kPosition:
+ status = setPosition(change.id(), change.position());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSize:
+ status = setSize(change.id(), change.size());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kAlpha:
+ status = setAlpha(change.id(), change.alpha());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayer:
+ status = setLayer(change.id(), change.layer());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kCrop:
+ status = setCrop(change.id(), change.crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kMatrix:
+ status = setMatrix(change.id(), change.matrix());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+ status = setFinalCrop(change.id(), change.final_crop());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+ status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+ status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kLayerStack:
+ status = setLayerStack(change.id(), change.layer_stack());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+ status = setHiddenFlag(change.id(), change.hidden_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+ status = setOpaqueFlag(change.id(), change.opaque_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+ status = setSecureFlag(change.id(), change.secure_flag());
+ break;
+ case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+ waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+ status = setDeferredTransaction(change.id(), change.deferred_transaction());
+ break;
+ default:
+ status = NO_ERROR;
+ break;
+ }
+
+ if (status != NO_ERROR) {
+ ALOGE("SET TRANSACTION FAILED");
+ return status;
+ }
+ }
+ return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+ for (const DisplayChange& change : displayChanges) {
+ ALOGV("Doing display transaction");
+ std::unique_lock<std::mutex> lock(mDisplayLock);
+ if (mDisplays[change.id()] == nullptr) {
+ mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+ }
+
+ switch (change.DisplayChange_case()) {
+ case DisplayChange::DisplayChangeCase::kSurface:
+ setDisplaySurface(change.id(), change.surface());
+ break;
+ case DisplayChange::DisplayChangeCase::kLayerStack:
+ setDisplayLayerStack(change.id(), change.layer_stack());
+ break;
+ case DisplayChange::DisplayChangeCase::kSize:
+ setDisplaySize(change.id(), change.size());
+ break;
+ case DisplayChange::DisplayChangeCase::kProjection:
+ setDisplayProjection(change.id(), change.projection());
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
return mLayers[id]->setPosition(pc.x(), pc.y());
}
-status_t Replayer::setSize(uint32_t id, const SizeChange& sc) {
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
return mLayers[id]->setSize(sc.w(), sc.h());
}
-status_t Replayer::setLayer(uint32_t id, const LayerChange& lc) {
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
return mLayers[id]->setLayer(lc.layer());
}
-status_t Replayer::setAlpha(uint32_t id, const AlphaChange& ac) {
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
return mLayers[id]->setAlpha(ac.alpha());
}
-status_t Replayer::setCrop(uint32_t id, const CropChange& cc) {
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
cc.rectangle().bottom());
@@ -349,7 +477,7 @@
return mLayers[id]->setCrop(r);
}
-status_t Replayer::setFinalCrop(uint32_t id, const FinalCropChange& fcc) {
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
fcc.rectangle().bottom());
@@ -358,18 +486,18 @@
return mLayers[id]->setFinalCrop(r);
}
-status_t Replayer::setMatrix(uint32_t id, const MatrixChange& mc) {
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
mc.dtdx(), mc.dsdy(), mc.dtdy());
return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
}
-status_t Replayer::setOverrideScalingMode(uint32_t id, const OverrideScalingModeChange& osmc) {
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
}
-status_t Replayer::setTransparentRegionHint(uint32_t id, const TransparentRegionHintChange& trhc) {
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
ALOGV("Setting Transparent Region Hint");
Region re = Region();
@@ -381,33 +509,33 @@
return mLayers[id]->setTransparentRegionHint(re);
}
-status_t Replayer::setLayerStack(uint32_t id, const LayerStackChange& lsc) {
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
return mLayers[id]->setLayerStack(lsc.layer_stack());
}
-status_t Replayer::setHiddenFlag(uint32_t id, const HiddenFlagChange& hfc) {
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
- uint32_t flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+ layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
}
-status_t Replayer::setOpaqueFlag(uint32_t id, const OpaqueFlagChange& ofc) {
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
- uint32_t flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+ layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
}
-status_t Replayer::setSecureFlag(uint32_t id, const SecureFlagChange& sfc) {
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
- uint32_t flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+ layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
}
-status_t Replayer::setDeferredTransaction(uint32_t id, const DeferredTransactionChange& dtc) {
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
"frame_number=%llu",
id, dtc.layer_id(), dtc.frame_number());
@@ -421,7 +549,32 @@
return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
}
-status_t Replayer::createSurfaceControl(const Create& create, const std::shared_ptr<Event>& event) {
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+ sp<IGraphicBufferProducer> outProducer;
+ sp<IGraphicBufferConsumer> outConsumer;
+ BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+ SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+ SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+ SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+ Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+ pc.viewport().bottom());
+ Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+ SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+ const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
event->readyToExecute();
ALOGV("Creating Surface Control: ID: %d", create.id());
@@ -437,7 +590,7 @@
auto& layer = mLayers[create.id()];
layer = surfaceControl;
- mColors[create.id()] = HSVToRGB(HSV(rand() % 360, 1, 1));
+ mColors[create.id()] = HSV(rand() % 360, 1, 1);
mLayerCond.notify_all();
@@ -451,7 +604,7 @@
}
status_t Replayer::deleteSurfaceControl(
- const Delete& delete_, const std::shared_ptr<Event>& event) {
+ const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
ALOGV("Deleting %d Surface Control", delete_.id());
event->readyToExecute();
@@ -459,13 +612,15 @@
mLayersPendingRemoval.push_back(delete_.id());
- auto iterator = mBufferQueueSchedulers.find(delete_.id());
+ const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
if (iterator != mBufferQueueSchedulers.end()) {
(*iterator).second->stopScheduling();
}
std::lock_guard<std::mutex> lock2(mLayerLock);
- mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+ if (mLayers[delete_.id()] != nullptr) {
+ mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+ }
return NO_ERROR;
}
@@ -496,6 +651,35 @@
return NO_ERROR;
}
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+ ALOGV("Creating display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ sp<IBinder> display = SurfaceComposerClient::createDisplay(
+ String8(create.name().c_str()), create.is_secure());
+ mDisplays[create.id()] = display;
+
+ mDisplayCond.notify_all();
+
+ ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+ ALOGV("Delete display");
+ event->readyToExecute();
+
+ std::lock_guard<std::mutex> lock(mDisplayLock);
+ SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+ mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+ ALOGV("Updating power mode");
+ event->readyToExecute();
+ SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
void Replayer::waitUntilTimestamp(int64_t timestamp) {
ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index ff951d1..f757fc3 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -40,16 +40,21 @@
namespace android {
const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
-const auto RAND_COLOR_SEED = 1000;
+const auto RAND_COLOR_SEED = 700;
const auto DEFAULT_THREADS = 3;
-typedef uint32_t layer_id;
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
class Replayer {
public:
Replayer(const std::string& filename, bool replayManually = false,
- int numThreads = DEFAULT_THREADS);
- Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS);
+ int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+ Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+ bool wait = true, nsecs_t stopHere = -1);
status_t replay();
@@ -62,24 +67,37 @@
status_t dispatchEvent(int index);
status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
- status_t createSurfaceControl(const Create& create, const std::shared_ptr<Event>& event);
- status_t deleteSurfaceControl(const Delete& delete_, const std::shared_ptr<Event>& event);
+ status_t createSurfaceControl(const SurfaceCreation& create,
+ const std::shared_ptr<Event>& event);
+ status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+ const std::shared_ptr<Event>& event);
status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+ void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+ void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+ void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
- status_t setPosition(uint32_t id, const PositionChange& pc);
- status_t setSize(uint32_t id, const SizeChange& sc);
- status_t setAlpha(uint32_t id, const AlphaChange& ac);
- status_t setLayer(uint32_t id, const LayerChange& lc);
- status_t setCrop(uint32_t id, const CropChange& cc);
- status_t setFinalCrop(uint32_t id, const FinalCropChange& fcc);
- status_t setMatrix(uint32_t id, const MatrixChange& mc);
- status_t setOverrideScalingMode(uint32_t id, const OverrideScalingModeChange& osmc);
- status_t setTransparentRegionHint(uint32_t id, const TransparentRegionHintChange& trgc);
- status_t setLayerStack(uint32_t id, const LayerStackChange& lsc);
- status_t setHiddenFlag(uint32_t id, const HiddenFlagChange& hfc);
- status_t setOpaqueFlag(uint32_t id, const OpaqueFlagChange& ofc);
- status_t setSecureFlag(uint32_t id, const SecureFlagChange& sfc);
- status_t setDeferredTransaction(uint32_t id, const DeferredTransactionChange& dtc);
+ status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+ void doDisplayTransaction(const DisplayChanges& displayChange);
+
+ status_t setPosition(layer_id id, const PositionChange& pc);
+ status_t setSize(layer_id id, const SizeChange& sc);
+ status_t setAlpha(layer_id id, const AlphaChange& ac);
+ status_t setLayer(layer_id id, const LayerChange& lc);
+ status_t setCrop(layer_id id, const CropChange& cc);
+ status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+ status_t setMatrix(layer_id id, const MatrixChange& mc);
+ status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+ status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+ status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+ status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+ status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+ status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+ status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+ void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+ void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+ void setDisplaySize(display_id id, const SizeChange& sc);
+ void setDisplayProjection(display_id id, const ProjectionChange& pc);
void doDeleteSurfaceControls();
void waitUntilTimestamp(int64_t timestamp);
@@ -93,15 +111,20 @@
int64_t mCurrentTime = 0;
int32_t mNumThreads = DEFAULT_THREADS;
+ Increment mCurrentIncrement;
+
std::string mLastInput;
static atomic_bool sReplayingManually;
bool mWaitingForNextVSync;
+ bool mWaitForTimeStamps;
+ nsecs_t mStopTimeStamp;
+ bool mHasStopped;
std::mutex mLayerLock;
std::condition_variable mLayerCond;
std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
- std::unordered_map<layer_id, RGB> mColors;
+ std::unordered_map<layer_id, HSV> mColors;
std::mutex mPendingLayersLock;
std::vector<layer_id> mLayersPendingRemoval;
@@ -109,6 +132,10 @@
std::mutex mBufferQueueSchedulerLock;
std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
+ std::mutex mDisplayLock;
+ std::condition_variable mDisplayCond;
+ std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
sp<SurfaceComposerClient> mComposerClient;
std::queue<std::shared_ptr<Event>> mPendingIncrements;
};