blast: Queue transactions by applying client
Do not update SurfaceFlinger until all the fences in a transaction
have signaled. While waiting on the fences, place the transaction
in a queue with other transactions that were applied by the
same SurfaceComposerClient.
Test: Transaction_test
Bug: 80477568
Change-Id: I6b866bfa955d5eafef28016a0c5de7c3862f1837
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2d6be26..799151a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -73,11 +73,9 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual void setTransactionState(
- const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays,
- uint32_t flags)
- {
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -92,6 +90,7 @@
}
data.writeUint32(flags);
+ data.writeStrongBinder(applyToken);
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -750,7 +749,8 @@
}
uint32_t stateFlags = data.readUint32();
- setTransactionState(state, displays, stateFlags);
+ sp<IBinder> applyToken = data.readStrongBinder();
+ setTransactionState(state, displays, stateFlags, applyToken);
return NO_ERROR;
}
case BOOT_FINISHED: {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9586219..5b004e2 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -264,7 +264,9 @@
mAnimation = false;
mEarlyWakeup = false;
- sf->setTransactionState(composerStates, displayStates, flags);
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
+ sf->setTransactionState(composerStates, displayStates, flags, applyToken);
mStatus = NO_ERROR;
return NO_ERROR;
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3052c0b..8cb40e7 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -124,7 +124,8 @@
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags) = 0;
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 67afbd6..c56304f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -558,8 +558,8 @@
void destroyDisplay(const sp<IBinder>& /*display */) override {}
sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
void setTransactionState(const Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
- override {}
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/) override {}
void bootFinished() override {}
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& /*surface*/) const override {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4a93be6..02cd9d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1568,11 +1568,23 @@
bool SurfaceFlinger::handleMessageTransaction() {
uint32_t transactionFlags = peekTransactionFlags();
+
+ // Apply any ready transactions in the queues if there are still transactions that have not been
+ // applied, wake up during the next vsync period and check again
+ bool transactionNeeded = false;
+ if (!flushTransactionQueues()) {
+ transactionNeeded = true;
+ }
+
if (transactionFlags) {
handleTransaction(transactionFlags);
- return true;
}
- return false;
+
+ if (transactionNeeded) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+
+ return transactionFlags;
}
void SurfaceFlinger::handleMessageRefresh() {
@@ -3314,6 +3326,26 @@
return old;
}
+bool SurfaceFlinger::flushTransactionQueues() {
+ Mutex::Autolock _l(mStateLock);
+ auto it = mTransactionQueues.begin();
+ while (it != mTransactionQueues.end()) {
+ auto& [applyToken, transactionQueue] = *it;
+
+ while (!transactionQueue.empty()) {
+ const auto& [states, displays, flags] = transactionQueue.front();
+ if (composerStateContainsUnsignaledFences(states)) {
+ break;
+ }
+ applyTransactionState(states, displays, flags);
+ transactionQueue.pop();
+ }
+
+ it = (transactionQueue.empty()) ? mTransactionQueues.erase(it) : std::next(it, 1);
+ }
+ return mTransactionQueues.empty();
+}
+
bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
for (const ComposerState& state : states) {
// Here we need to check that the interface we're given is indeed
@@ -3336,19 +3368,44 @@
return false;
}
-void SurfaceFlinger::setTransactionState(
- const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays,
- uint32_t flags)
-{
+bool SurfaceFlinger::composerStateContainsUnsignaledFences(const Vector<ComposerState>& states) {
+ for (const ComposerState& state : states) {
+ const layer_state_t& s = state.state;
+ if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
+ continue;
+ }
+ if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken) {
ATRACE_CALL();
Mutex::Autolock _l(mStateLock);
- uint32_t transactionFlags = 0;
if (containsAnyInvalidClientState(states)) {
return;
}
+ // If its TransactionQueue already has a pending TransactionState or if it is pending
+ if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
+ composerStateContainsUnsignaledFences(states)) {
+ mTransactionQueues[applyToken].emplace(states, displays, flags);
+ setTransactionFlags(eTransactionNeeded);
+ return;
+ }
+
+ applyTransactionState(states, displays, flags);
+}
+
+void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags) {
+ uint32_t transactionFlags = 0;
+
if (flags & eAnimation) {
// For window updates that are part of an animation we must wait for
// previous animation "frames" to be handled.
@@ -3938,7 +3995,7 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0);
+ setTransactionState(state, displays, 0, nullptr);
const auto display = getDisplayDevice(displayToken);
if (!display) return;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b1bfb3a..822bb18 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -442,7 +442,8 @@
virtual void destroyDisplay(const sp<IBinder>& displayToken);
virtual sp<IBinder> getBuiltInDisplay(int32_t id);
virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags);
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken);
virtual void bootFinished();
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const;
@@ -553,6 +554,10 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ void applyTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags)
+ REQUIRES(mStateLock);
+ bool flushTransactionQueues();
uint32_t getTransactionFlags(uint32_t flags);
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
@@ -561,6 +566,7 @@
void latchAndReleaseBuffer(const sp<Layer>& layer);
void commitTransaction();
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
+ bool composerStateContainsUnsignaledFences(const Vector<ComposerState>& states);
uint32_t setClientStateLocked(const ComposerState& composerState);
uint32_t setDisplayStateLocked(const DisplayState& s);
void setDestroyStateLocked(const ComposerState& composerState);
@@ -965,6 +971,22 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& strongPointer) const {
+ return std::hash<IBinder*>{}(strongPointer.get());
+ }
+ };
+ struct TransactionState {
+ TransactionState(const Vector<ComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags)
+ : states(composerStates), displays(displayStates), flags(transactionFlags) {}
+
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ };
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+
/* ------------------------------------------------------------------------
* Feature prototyping
*/
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 604aa7d..f121a95 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -17,6 +17,7 @@
defaults: ["surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
+ "BufferGenerator.cpp",
"Credentials_test.cpp",
"Stress_test.cpp",
"SurfaceInterceptor_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
new file mode 100644
index 0000000..8ddda60
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2018 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 <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include "BufferGenerator.h"
+#include "BufferGeneratorShader.h"
+
+namespace android {
+
+/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
+ * away. The fences are sent to the requester via a callback */
+class SurfaceManager {
+public:
+ /* Returns a fence from egl */
+ using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>;
+
+ /* Listens for a new frame, detaches the buffer and returns the fence
+ * through saved callback. */
+ class BufferListener : public ConsumerBase::FrameAvailableListener {
+ public:
+ BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
+ : mConsumer(consumer), mCallback(callback) {}
+
+ void onFrameAvailable(const BufferItem& /*item*/) {
+ BufferItem item;
+
+ if (mConsumer->acquireBuffer(&item, 0)) return;
+ if (mConsumer->detachBuffer(item.mSlot)) return;
+
+ mCallback(item.mGraphicBuffer, item.mFence->dup());
+ }
+
+ private:
+ sp<IGraphicBufferConsumer> mConsumer;
+ BufferCallback mCallback;
+ };
+
+ /* Creates a buffer listener that waits on a new frame from the buffer
+ * queue. */
+ void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
+ BufferCallback callback) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ consumer->setDefaultBufferSize(width, height);
+ consumer->setDefaultBufferFormat(format);
+
+ mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+ mListener = new BufferListener(consumer, callback);
+ mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+ mSurface = new Surface(producer, true);
+ }
+
+ /* Used by Egl manager. The surface is never displayed. */
+ sp<Surface> getSurface() const { return mSurface; }
+
+private:
+ sp<BufferItemConsumer> mBufferItemConsumer;
+ sp<BufferListener> mListener;
+ /* Used by Egl manager. The surface is never displayed */
+ sp<Surface> mSurface;
+};
+
+/* Used to generate valid fences. It is not possible to create a dummy sync
+ * fence for testing. Egl can generate buffers along with a valid fence.
+ * The buffer cannot be guaranteed to be the same format across all devices so
+ * a CPU filled buffer is used instead. The Egl fence is used along with the
+ * CPU filled buffer. */
+class EglManager {
+public:
+ EglManager()
+ : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
+
+ ~EglManager() { cleanup(); }
+
+ int initialize(sp<Surface> surface) {
+ mSurface = surface;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+ EGLint major;
+ EGLint minor;
+ if (!eglInitialize(mEglDisplay, &major, &minor)) {
+ ALOGW("Could not initialize EGL");
+ return false;
+ }
+
+ /* We're going to use a 1x1 pbuffer surface later on
+ * The configuration distance doesn't really matter for what we're
+ * trying to do */
+ EGLint configAttrs[] = {EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_ALPHA_SIZE,
+ 0,
+ EGL_DEPTH_SIZE,
+ 24,
+ EGL_STENCIL_SIZE,
+ 0,
+ EGL_NONE};
+
+ EGLConfig configs[1];
+ EGLint configCnt;
+ if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) {
+ ALOGW("Could not select EGL configuration");
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ return false;
+ }
+
+ if (configCnt <= 0) {
+ ALOGW("Could not find EGL configuration");
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ return false;
+ }
+
+ /* These objects are initialized below but the default "null" values are
+ * used to cleanup properly at any point in the initialization sequence */
+ EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs);
+ if (mEglContext == EGL_NO_CONTEXT) {
+ ALOGW("Could not create EGL context");
+ cleanup();
+ return false;
+ }
+
+ EGLint majorVersion;
+ if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) {
+ ALOGW("Could not query EGL version");
+ cleanup();
+ return false;
+ }
+
+ if (majorVersion != 3) {
+ ALOGW("Unsupported EGL version");
+ cleanup();
+ return false;
+ }
+
+ EGLint surfaceAttrs[] = {EGL_NONE};
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs);
+ if (mEglSurface == EGL_NO_SURFACE) {
+ ALOGW("Could not create EGL surface");
+ cleanup();
+ return false;
+ }
+
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ ALOGW("Could not change current EGL context");
+ cleanup();
+ return false;
+ }
+
+ return true;
+ }
+
+ void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
+
+ void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
+
+private:
+ void cleanup() {
+ if (mEglDisplay == EGL_NO_DISPLAY) return;
+ if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface);
+ if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext);
+
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ }
+
+ sp<Surface> mSurface;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
+};
+
+class Program {
+public:
+ ~Program() {
+ if (mInitialized) {
+ glDetachShader(mProgram, mVertexShader);
+ glDetachShader(mProgram, mFragmentShader);
+
+ glDeleteShader(mVertexShader);
+ glDeleteShader(mFragmentShader);
+
+ glDeleteProgram(mProgram);
+ }
+ }
+
+ bool initialize(const char* vertex, const char* fragment) {
+ mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+ if (!mVertexShader) {
+ return false;
+ }
+
+ mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+ if (!mFragmentShader) {
+ return false;
+ }
+
+ mProgram = glCreateProgram();
+ glAttachShader(mProgram, mVertexShader);
+ glAttachShader(mProgram, mFragmentShader);
+
+ glLinkProgram(mProgram);
+
+ GLint status;
+ glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ GLint length = 0;
+ glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
+ if (length > 1) {
+ GLchar log[length];
+ glGetProgramInfoLog(mProgram, length, nullptr, &log[0]);
+ ALOGE("%s", log);
+ }
+ ALOGE("Error while linking shaders");
+ return false;
+ }
+ mInitialized = true;
+ return true;
+ }
+
+ void use() const { glUseProgram(mProgram); }
+
+ void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
+
+ void bindVec3(GLint location, const vec3* v, uint32_t count) const {
+ glUniform3fv(location, count, &(v->x));
+ }
+
+ void bindFloat(GLint location, float v) { glUniform1f(location, v); }
+
+private:
+ GLuint buildShader(const char* source, GLenum type) const {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, nullptr);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source);
+ // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+ // use a fixed size instead
+ GLchar log[512];
+ glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
+ ALOGE("Shader info log: %s", log);
+ return 0;
+ }
+
+ return shader;
+ }
+
+ GLuint mProgram = 0;
+ GLuint mVertexShader = 0;
+ GLuint mFragmentShader = 0;
+ bool mInitialized = false;
+};
+
+BufferGenerator::BufferGenerator()
+ : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
+ const float width = 1000.0;
+ const float height = 1000.0;
+
+ auto setBufferWithContext =
+ std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
+ mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+
+ if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
+
+ mEglManager->makeCurrent();
+
+ if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
+ mProgram->use();
+ mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+ mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]);
+
+ mInitialized = true;
+}
+
+BufferGenerator::~BufferGenerator() {
+ mEglManager->makeCurrent();
+}
+
+status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ // mMutex is used to protect get() from getting called by multiple threads at the same time
+ static std::mutex mMutex;
+ std::lock_guard lock(mMutex);
+
+ if (!mInitialized) {
+ if (outBuffer) {
+ *outBuffer = nullptr;
+ }
+ if (*outFence) {
+ *outFence = nullptr;
+ }
+ return -EINVAL;
+ }
+
+ // Generate a buffer and fence. They will be returned through the setBuffer callback
+ mEglManager->makeCurrent();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch;
+ mProgram->bindFloat(1, time.count());
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ mPending = true;
+ mEglManager->present();
+
+ // Wait for the setBuffer callback
+ if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2),
+ [this] { return !mPending; })) {
+ ALOGE("failed to set buffer and fence");
+ return -ETIME;
+ }
+
+ // Return buffer and fence
+ if (outBuffer) {
+ *outBuffer = mGraphicBuffer;
+ }
+ if (outFence) {
+ *outFence = new Fence(mFence);
+ } else {
+ close(mFence);
+ }
+ mGraphicBuffer = nullptr;
+ mFence = -1;
+
+ return NO_ERROR;
+}
+
+// static
+void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
+ void* bufferGenerator) {
+ BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator);
+ generator->mGraphicBuffer = buffer;
+ generator->mFence = fence;
+ generator->mPending = false;
+ generator->mConditionVariable.notify_all();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
new file mode 100644
index 0000000..a3ffe86
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 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 <condition_variable>
+#include <mutex>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+class SurfaceManager;
+class EglManager;
+class Program;
+
+class BufferGenerator {
+public:
+ BufferGenerator();
+ ~BufferGenerator();
+
+ /* Get a new fence */
+ status_t get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+
+ /* Static callback that sets the fence on a particular instance */
+ static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+
+private:
+ bool mInitialized = false;
+
+ std::unique_ptr<SurfaceManager> mSurfaceManager;
+ std::unique_ptr<EglManager> mEglManager;
+ std::unique_ptr<Program> mProgram;
+
+ std::condition_variable_any mConditionVariable;
+
+ sp<GraphicBuffer> mGraphicBuffer;
+ int32_t mFence = -1;
+ bool mPending = false;
+
+ using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
+ Epoch mEpoch = std::chrono::steady_clock::now();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGeneratorShader.h b/services/surfaceflinger/tests/BufferGeneratorShader.h
new file mode 100644
index 0000000..564cda3
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGeneratorShader.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 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 <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+static const char* VERTEX_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) in vec4 mesh_position;
+
+void main() {
+ gl_Position = mesh_position;
+}
+)SHADER__";
+
+static const char* FRAGMENT_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) uniform vec4 resolution;
+layout(location = 1) uniform float time;
+layout(location = 2) uniform vec3[4] SPHERICAL_HARMONICS;
+
+layout(location = 0) out vec4 fragColor;
+
+#define saturate(x) clamp(x, 0.0, 1.0)
+#define PI 3.14159265359
+
+//------------------------------------------------------------------------------
+// Distance field functions
+//------------------------------------------------------------------------------
+
+float sdPlane(in vec3 p) {
+ return p.y;
+}
+
+float sdSphere(in vec3 p, float s) {
+ return length(p) - s;
+}
+
+float sdTorus(in vec3 p, in vec2 t) {
+ return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
+}
+
+vec2 opUnion(vec2 d1, vec2 d2) {
+ return d1.x < d2.x ? d1 : d2;
+}
+
+vec2 scene(in vec3 position) {
+ vec2 scene = opUnion(
+ vec2(sdPlane(position), 1.0),
+ vec2(sdSphere(position - vec3(0.0, 0.4, 0.0), 0.4), 12.0)
+ );
+ return scene;
+}
+
+//------------------------------------------------------------------------------
+// Ray casting
+//------------------------------------------------------------------------------
+
+float shadow(in vec3 origin, in vec3 direction, in float tmin, in float tmax) {
+ float hit = 1.0;
+
+ for (float t = tmin; t < tmax; ) {
+ float h = scene(origin + direction * t).x;
+ if (h < 0.001) return 0.0;
+ t += h;
+ hit = min(hit, 10.0 * h / t);
+ }
+
+ return clamp(hit, 0.0, 1.0);
+}
+
+vec2 traceRay(in vec3 origin, in vec3 direction) {
+ float tmin = 0.02;
+ float tmax = 20.0;
+
+ float material = -1.0;
+ float t = tmin;
+
+ for ( ; t < tmax; ) {
+ vec2 hit = scene(origin + direction * t);
+ if (hit.x < 0.002 || t > tmax) break;
+ t += hit.x;
+ material = hit.y;
+ }
+
+ if (t > tmax) {
+ material = -1.0;
+ }
+
+ return vec2(t, material);
+}
+
+vec3 normal(in vec3 position) {
+ vec3 epsilon = vec3(0.001, 0.0, 0.0);
+ vec3 n = vec3(
+ scene(position + epsilon.xyy).x - scene(position - epsilon.xyy).x,
+ scene(position + epsilon.yxy).x - scene(position - epsilon.yxy).x,
+ scene(position + epsilon.yyx).x - scene(position - epsilon.yyx).x);
+ return normalize(n);
+}
+
+//------------------------------------------------------------------------------
+// BRDF
+//------------------------------------------------------------------------------
+
+float pow5(float x) {
+ float x2 = x * x;
+ return x2 * x2 * x;
+}
+
+float D_GGX(float linearRoughness, float NoH, const vec3 h) {
+ // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
+ float oneMinusNoHSquared = 1.0 - NoH * NoH;
+ float a = NoH * linearRoughness;
+ float k = linearRoughness / (oneMinusNoHSquared + a * a);
+ float d = k * k * (1.0 / PI);
+ return d;
+}
+
+float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
+ // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
+ float a2 = linearRoughness * linearRoughness;
+ float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
+ float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
+ return 0.5 / (GGXV + GGXL);
+}
+
+vec3 F_Schlick(const vec3 f0, float VoH) {
+ // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
+ return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
+}
+
+float F_Schlick(float f0, float f90, float VoH) {
+ return f0 + (f90 - f0) * pow5(1.0 - VoH);
+}
+
+float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
+ // Burley 2012, "Physically-Based Shading at Disney"
+ float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
+ float lightScatter = F_Schlick(1.0, f90, NoL);
+ float viewScatter = F_Schlick(1.0, f90, NoV);
+ return lightScatter * viewScatter * (1.0 / PI);
+}
+
+float Fd_Lambert() {
+ return 1.0 / PI;
+}
+
+//------------------------------------------------------------------------------
+// Indirect lighting
+//------------------------------------------------------------------------------
+
+vec3 Irradiance_SphericalHarmonics(const vec3 n) {
+ return max(
+ SPHERICAL_HARMONICS[0]
+ + SPHERICAL_HARMONICS[1] * (n.y)
+ + SPHERICAL_HARMONICS[2] * (n.z)
+ + SPHERICAL_HARMONICS[3] * (n.x)
+ , 0.0);
+}
+
+vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
+ // Karis 2014, "Physically Based Material on Mobile"
+ const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
+ const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
+
+ vec4 r = roughness * c0 + c1;
+ float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
+
+ return vec2(-1.04, 1.04) * a004 + r.zw;
+}
+
+//------------------------------------------------------------------------------
+// Tone mapping and transfer functions
+//------------------------------------------------------------------------------
+
+vec3 Tonemap_ACES(const vec3 x) {
+ // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+ const float a = 2.51;
+ const float b = 0.03;
+ const float c = 2.43;
+ const float d = 0.59;
+ const float e = 0.14;
+ return (x * (a * x + b)) / (x * (c * x + d) + e);
+}
+
+vec3 OECF_sRGBFast(const vec3 linear) {
+ return pow(linear, vec3(1.0 / 2.2));
+}
+
+//------------------------------------------------------------------------------
+// Rendering
+//------------------------------------------------------------------------------
+
+vec3 render(in vec3 origin, in vec3 direction, out float distance) {
+ // Sky gradient
+ vec3 color = vec3(0.65, 0.85, 1.0) + direction.y * 0.72;
+
+ // (distance, material)
+ vec2 hit = traceRay(origin, direction);
+ distance = hit.x;
+ float material = hit.y;
+
+ // We've hit something in the scene
+ if (material > 0.0) {
+ vec3 position = origin + distance * direction;
+
+ vec3 v = normalize(-direction);
+ vec3 n = normal(position);
+ vec3 l = normalize(vec3(0.6, 0.7, -0.7));
+ vec3 h = normalize(v + l);
+ vec3 r = normalize(reflect(direction, n));
+
+ float NoV = abs(dot(n, v)) + 1e-5;
+ float NoL = saturate(dot(n, l));
+ float NoH = saturate(dot(n, h));
+ float LoH = saturate(dot(l, h));
+
+ vec3 baseColor = vec3(0.0);
+ float roughness = 0.0;
+ float metallic = 0.0;
+
+ float intensity = 2.0;
+ float indirectIntensity = 0.64;
+
+ if (material < 4.0) {
+ // Checkerboard floor
+ float f = mod(floor(6.0 * position.z) + floor(6.0 * position.x), 2.0);
+ baseColor = 0.4 + f * vec3(0.6);
+ roughness = 0.1;
+ } else if (material < 16.0) {
+ // Metallic objects
+ baseColor = vec3(0.3, 0.0, 0.0);
+ roughness = 0.2;
+ }
+
+ float linearRoughness = roughness * roughness;
+ vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
+ vec3 f0 = 0.04 * (1.0 - metallic) + baseColor.rgb * metallic;
+
+ float attenuation = shadow(position, l, 0.02, 2.5);
+
+ // specular BRDF
+ float D = D_GGX(linearRoughness, NoH, h);
+ float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
+ vec3 F = F_Schlick(f0, LoH);
+ vec3 Fr = (D * V) * F;
+
+ // diffuse BRDF
+ vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
+
+ color = Fd + Fr;
+ color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
+
+ // diffuse indirect
+ vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
+
+ vec2 indirectHit = traceRay(position, r);
+ vec3 indirectSpecular = vec3(0.65, 0.85, 1.0) + r.y * 0.72;
+ if (indirectHit.y > 0.0) {
+ if (indirectHit.y < 4.0) {
+ vec3 indirectPosition = position + indirectHit.x * r;
+ // Checkerboard floor
+ float f = mod(floor(6.0 * indirectPosition.z) + floor(6.0 * indirectPosition.x), 2.0);
+ indirectSpecular = 0.4 + f * vec3(0.6);
+ } else if (indirectHit.y < 16.0) {
+ // Metallic objects
+ indirectSpecular = vec3(0.3, 0.0, 0.0);
+ }
+ }
+
+ // indirect contribution
+ vec2 dfg = PrefilteredDFG_Karis(roughness, NoV);
+ vec3 specularColor = f0 * dfg.x + dfg.y;
+ vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
+
+ color += ibl * indirectIntensity;
+ }
+
+ return color;
+}
+
+//------------------------------------------------------------------------------
+// Setup and execution
+//------------------------------------------------------------------------------
+
+mat3 setCamera(in vec3 origin, in vec3 target, float rotation) {
+ vec3 forward = normalize(target - origin);
+ vec3 orientation = vec3(sin(rotation), cos(rotation), 0.0);
+ vec3 left = normalize(cross(forward, orientation));
+ vec3 up = normalize(cross(left, forward));
+ return mat3(left, up, forward);
+}
+
+void main() {
+ // Normalized coordinates
+ vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
+ // Aspect ratio
+ p.x *= resolution.x / resolution.y;
+
+ // Camera position and "look at"
+ vec3 origin = vec3(0.0, 1.0, 0.0);
+ vec3 target = vec3(0.0);
+
+ origin.x += 2.0 * cos(time * 0.2);
+ origin.z += 2.0 * sin(time * 0.2);
+
+ mat3 toWorld = setCamera(origin, target, 0.0);
+ vec3 direction = toWorld * normalize(vec3(p.xy, 2.0));
+
+ // Render scene
+ float distance;
+ vec3 color = render(origin, direction, distance);
+
+ // Tone mapping
+ color = Tonemap_ACES(color);
+
+ // Exponential distance fog
+ color = mix(color, 0.8 * vec3(0.7, 0.8, 1.0), 1.0 - exp2(-0.011 * distance * distance));
+
+ // Gamma compression
+ color = OECF_sRGBFast(color);
+
+ fragColor = vec4(color, 1.0);
+}
+)SHADER__";
+
+static const android::vec3 SPHERICAL_HARMONICS[4] =
+ {{0.754554516862612, 0.748542953903366, 0.790921515418539},
+ {-0.083856548007422, 0.092533500963210, 0.322764661032516},
+ {0.308152705331738, 0.366796330467391, 0.466698181299906},
+ {-0.188884931542396, -0.277402551592231, -0.377844212327557}};
+
+static const android::vec4 TRIANGLE[3] = {{-1.0f, -1.0f, 1.0f, 1.0f},
+ {3.0f, -1.0f, 1.0f, 1.0f},
+ {-1.0f, 3.0f, 1.0f, 1.0f}};
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 037d32f..d118ad6 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -28,7 +28,6 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
-
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
@@ -41,6 +40,8 @@
#include <math.h>
#include <math/vec3.h>
+#include "BufferGenerator.h"
+
namespace android {
namespace {
@@ -478,6 +479,11 @@
return screenshot;
}
+ static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.get(outBuffer, outFence);
+ }
+
sp<SurfaceComposerClient> mClient;
sp<IBinder> mDisplay;
@@ -2164,6 +2170,36 @@
TEST_F(LayerTransactionTest, SetFenceBasic_BufferState) {
sp<SurfaceControl> layer;
+ Transaction transaction;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ sp<Fence> fence;
+ if (getBuffer(nullptr, &fence) != NO_ERROR) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+ status_t status = fence->wait(1000);
+ ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+ std::this_thread::sleep_for(200ms);
+
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_F(LayerTransactionTest, SetFenceNull_BufferState) {
+ sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
@@ -2514,7 +2550,7 @@
}
void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
- ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
mTransactionResult = transactionResult;
mExpectedSurfaceResults.emplace(std::piecewise_construct,
@@ -2524,7 +2560,7 @@
void addSurfaces(ExpectedResult::Transaction transactionResult,
const std::vector<sp<SurfaceControl>>& layers,
- ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
for (const auto& layer : layers) {
addSurface(transactionResult, layer, bufferResult, previousBufferResult);
@@ -2631,24 +2667,22 @@
return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
}
- static void fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
- const sp<SurfaceControl>& layer = nullptr) {
+ static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+ const sp<SurfaceControl>& layer = nullptr) {
if (layer) {
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY |
- BufferUsage::GPU_TEXTURE,
- "test");
- fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
- sp<Fence> fence = new Fence(-1);
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ int err = getBuffer(&buffer, &fence);
+ if (err != NO_ERROR) {
+ return err;
+ }
transaction.setBuffer(layer, buffer).setAcquireFence(layer, fence);
}
transaction.addTransactionCompletedCallback(callbackHelper->function,
callbackHelper->getContext());
+ return NO_ERROR;
}
static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
@@ -2680,7 +2714,11 @@
Transaction transaction;
CallbackHelper callback;
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.apply();
@@ -2695,19 +2733,28 @@
Transaction transaction;
CallbackHelper callback;
- fillTransaction(transaction, &callback);
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
ExpectedResult expected;
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
TEST_F(LayerCallbackTest, NoStateChange) {
Transaction transaction;
CallbackHelper callback;
- fillTransaction(transaction, &callback);
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.apply();
@@ -2721,7 +2768,11 @@
Transaction transaction;
CallbackHelper callback;
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
@@ -2737,8 +2788,16 @@
Transaction transaction1, transaction2;
CallbackHelper callback1, callback2;
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2756,8 +2815,16 @@
Transaction transaction1, transaction2;
CallbackHelper callback;
- fillTransaction(transaction1, &callback, layer1);
- fillTransaction(transaction2, &callback, layer2);
+ int err = fillTransaction(transaction1, &callback, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction2.merge(std::move(transaction1)).apply();
@@ -2773,8 +2840,16 @@
Transaction transaction1, transaction2;
CallbackHelper callback1, callback2;
- fillTransaction(transaction1, &callback1, layer);
- fillTransaction(transaction2, &callback2, layer);
+ int err = fillTransaction(transaction1, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction2.merge(std::move(transaction1)).apply();
@@ -2784,25 +2859,6 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
}
-TEST_F(LayerCallbackTest, Merge_SingleBuffer) {
- sp<SurfaceControl> layer1, layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
- ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
- Transaction transaction1, transaction2;
- CallbackHelper callback1, callback2;
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2);
-
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
- ExpectedResult expected;
- expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
- EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
TEST_F(LayerCallbackTest, Merge_DifferentClients) {
sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
client2(new SurfaceComposerClient);
@@ -2818,8 +2874,16 @@
Transaction transaction1, transaction2;
CallbackHelper callback1, callback2;
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2837,13 +2901,17 @@
Transaction transaction;
CallbackHelper callback;
for (size_t i = 0; i < 10; i++) {
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::Buffer::ACQUIRED,
(i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
: ExpectedResult::PreviousBuffer::RELEASED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
@@ -2861,10 +2929,18 @@
ExpectedResult expected;
if (i == 0) {
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
} else {
- fillTransaction(transaction, &callback);
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
}
transaction.apply();
@@ -2882,9 +2958,17 @@
CallbackHelper callback;
for (size_t i = 0; i < 10; i++) {
if (i == 0) {
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
} else {
- fillTransaction(transaction, &callback);
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
}
transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
@@ -2892,7 +2976,9 @@
ExpectedResult expected;
expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
: ExpectedResult::Transaction::NOT_PRESENTED,
- layer);
+ layer,
+ (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+ : ExpectedResult::Buffer::NOT_ACQUIRED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
}
ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
@@ -2906,15 +2992,23 @@
Transaction transaction1, transaction2;
CallbackHelper callback1, callback2;
for (size_t i = 0; i < 10; i++) {
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
- ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::Buffer::ACQUIRED,
(i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
: ExpectedResult::PreviousBuffer::RELEASED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
@@ -2939,15 +3033,23 @@
Transaction transaction1, transaction2;
CallbackHelper callback1, callback2;
for (size_t i = 0; i < 10; i++) {
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
- ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::Buffer::ACQUIRED,
(i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
: ExpectedResult::PreviousBuffer::RELEASED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
@@ -2973,8 +3075,16 @@
CallbackHelper callback1, callback2;
// Normal call to set up test
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2986,8 +3096,16 @@
expected.reset();
// Test
- fillTransaction(transaction1, &callback1);
- fillTransaction(transaction2, &callback2);
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction2.merge(std::move(transaction1)).apply();
@@ -3012,8 +3130,16 @@
CallbackHelper callback1, callback2;
// Normal call to set up test
- fillTransaction(transaction1, &callback1, layer1);
- fillTransaction(transaction2, &callback2, layer2);
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -3025,12 +3151,21 @@
expected.reset();
// Test
- fillTransaction(transaction1, &callback1);
- fillTransaction(transaction2, &callback2);
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2);
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
}
@@ -3047,13 +3182,16 @@
for (auto& expected : expectedResults) {
expected.reset();
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
- ExpectedResult::Buffer::NOT_ACQUIRED, previousBufferResult);
+ ExpectedResult::Buffer::ACQUIRED, previousBufferResult);
previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED;
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.apply();
- std::this_thread::sleep_for(200ms);
}
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}
@@ -3062,23 +3200,33 @@
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+ // Normal call to set up test
Transaction transaction;
CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+ // Test
std::vector<ExpectedResult> expectedResults(50);
- bool first = true;
for (auto& expected : expectedResults) {
expected.reset();
- if (first) {
- fillTransaction(transaction, &callback, layer);
- expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
- first = false;
- } else {
- fillTransaction(transaction, &callback);
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
}
transaction.apply();
- std::this_thread::sleep_for(200ms);
}
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}
@@ -3090,7 +3238,11 @@
// Normal call to set up test
Transaction transaction;
CallbackHelper callback;
- fillTransaction(transaction, &callback, layer);
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
@@ -3102,13 +3254,16 @@
std::vector<ExpectedResult> expectedResults(50);
for (auto& expected : expectedResults) {
expected.reset();
- expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
- fillTransaction(transaction, &callback);
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
- std::this_thread::sleep_for(200ms);
}
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}