drm_hwcomposer: fix typo in formats switch statement
am: 78beee0ecd
Change-Id: I13a0894555d719e5d15b656c35918bc891f836db
diff --git a/Android.mk b/Android.mk
index 2c4f7e3..f46276c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -55,11 +55,9 @@
system/core/libsync/include \
LOCAL_SRC_FILES := \
- autolock.cpp \
drmresources.cpp \
drmcomposition.cpp \
drmcompositor.cpp \
- drmcompositorworker.cpp \
drmconnector.cpp \
drmcrtc.cpp \
drmdisplaycomposition.cpp \
diff --git a/autolock.cpp b/autolock.cpp
deleted file mode 100644
index 1a2ded7..0000000
--- a/autolock.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#define LOG_TAG "hwc-drm-auto-lock"
-
-#include "autolock.h"
-
-#include <errno.h>
-#include <pthread.h>
-
-#include <cutils/log.h>
-
-namespace android {
-
-int AutoLock::Lock() {
- if (locked_) {
- ALOGE("Invalid attempt to double lock AutoLock %s", name_);
- return -EINVAL;
- }
- int ret = pthread_mutex_lock(mutex_);
- if (ret) {
- ALOGE("Failed to acquire %s lock %d", name_, ret);
- return ret;
- }
- locked_ = true;
- return 0;
-}
-
-int AutoLock::Unlock() {
- if (!locked_) {
- ALOGE("Invalid attempt to unlock unlocked AutoLock %s", name_);
- return -EINVAL;
- }
- int ret = pthread_mutex_unlock(mutex_);
- if (ret) {
- ALOGE("Failed to release %s lock %d", name_, ret);
- return ret;
- }
- locked_ = false;
- return 0;
-}
-}
diff --git a/autolock.h b/autolock.h
deleted file mode 100644
index 3b824e2..0000000
--- a/autolock.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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 <pthread.h>
-
-namespace android {
-
-class AutoLock {
- public:
- AutoLock(pthread_mutex_t *mutex, const char *const name)
- : mutex_(mutex), name_(name) {
- }
- ~AutoLock() {
- if (locked_)
- Unlock();
- }
-
- AutoLock(const AutoLock &rhs) = delete;
- AutoLock &operator=(const AutoLock &rhs) = delete;
-
- int Lock();
- int Unlock();
-
- private:
- pthread_mutex_t *const mutex_;
- bool locked_ = false;
- const char *const name_;
-};
-}
diff --git a/drmcompositorworker.cpp b/drmcompositorworker.cpp
deleted file mode 100644
index a4e7fc9..0000000
--- a/drmcompositorworker.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "hwc-drm-compositor-worker"
-
-#include "drmdisplaycompositor.h"
-#include "drmcompositorworker.h"
-#include "worker.h"
-
-#include <stdlib.h>
-
-#include <cutils/log.h>
-#include <hardware/hardware.h>
-
-namespace android {
-
-static const int64_t kSquashWait = 500000000LL;
-
-DrmCompositorWorker::DrmCompositorWorker(DrmDisplayCompositor *compositor)
- : Worker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
- compositor_(compositor) {
-}
-
-DrmCompositorWorker::~DrmCompositorWorker() {
-}
-
-int DrmCompositorWorker::Init() {
- return InitWorker();
-}
-
-void DrmCompositorWorker::Routine() {
- int ret;
- if (!compositor_->HaveQueuedComposites()) {
- Lock();
-
- // Only use a timeout if we didn't do a SquashAll last time. This will
- // prevent wait_ret == -ETIMEDOUT which would trigger a SquashAll and be a
- // pointless drain on resources.
- int wait_ret = did_squash_all_ ? WaitForSignalOrExitLocked()
- : WaitForSignalOrExitLocked(kSquashWait);
- Unlock();
-
- switch (wait_ret) {
- case 0:
- break;
- case -EINTR:
- return;
- case -ETIMEDOUT:
- ret = compositor_->SquashAll();
- if (ret)
- ALOGE("Failed to squash all %d", ret);
- did_squash_all_ = true;
- return;
- default:
- ALOGE("Failed to wait for signal, %d", wait_ret);
- return;
- }
- }
-
- ret = compositor_->Composite();
- if (ret)
- ALOGE("Failed to composite! %d", ret);
- did_squash_all_ = false;
-}
-}
diff --git a/drmcompositorworker.h b/drmcompositorworker.h
deleted file mode 100644
index 731bc65..0000000
--- a/drmcompositorworker.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef ANDROID_DRM_COMPOSITOR_WORKER_H_
-#define ANDROID_DRM_COMPOSITOR_WORKER_H_
-
-#include "worker.h"
-
-namespace android {
-
-class DrmDisplayCompositor;
-
-class DrmCompositorWorker : public Worker {
- public:
- DrmCompositorWorker(DrmDisplayCompositor *compositor);
- ~DrmCompositorWorker() override;
-
- int Init();
-
- protected:
- void Routine() override;
-
- DrmDisplayCompositor *compositor_;
- bool did_squash_all_ = false;
-};
-}
-
-#endif
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index a1baed1..642a1c7 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -19,10 +19,13 @@
#include "drmdisplaycompositor.h"
-#include <pthread.h>
#include <sched.h>
#include <stdlib.h>
#include <time.h>
+#include <algorithm>
+#include <bitset>
+#include <cinttypes>
+#include <mutex>
#include <sstream>
#include <vector>
@@ -31,7 +34,6 @@
#include <sync/sync.h>
#include <utils/Trace.h>
-#include "autolock.h"
#include "drmcrtc.h"
#include "drmplane.h"
#include "drmresources.h"
@@ -41,6 +43,8 @@
namespace android {
+static const int64_t kSquashWait = 500LL;
+
void SquashState::Init(DrmHwcLayer *layers, size_t num_layers) {
generation_number_++;
valid_history_ = 0;
@@ -177,64 +181,53 @@
}
DrmDisplayCompositor::FrameWorker::FrameWorker(DrmDisplayCompositor *compositor)
- : Worker("frame-worker", HAL_PRIORITY_URGENT_DISPLAY),
+ : QueueWorker("frame-worker", HAL_PRIORITY_URGENT_DISPLAY),
compositor_(compositor) {
}
-DrmDisplayCompositor::FrameWorker::~FrameWorker() {
-}
-
int DrmDisplayCompositor::FrameWorker::Init() {
+ set_max_queue_size(DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH);
return InitWorker();
}
void DrmDisplayCompositor::FrameWorker::QueueFrame(
std::unique_ptr<DrmDisplayComposition> composition, int status) {
- Lock();
- FrameState frame;
- frame.composition = std::move(composition);
- frame.status = status;
- frame_queue_.push(std::move(frame));
- Unlock();
- Signal();
+ std::unique_ptr<FrameState> frame(
+ new FrameState(std::move(composition), status));
+
+ auto start = std::chrono::high_resolution_clock::now();
+ int ret = QueueWork(std::move(frame));
+ if (ret) {
+ ALOGE("Unable to queue frame work (%d)", ret);
+ // TODO: error handling (timeout or exit)
+ return;
+ }
+ auto end = std::chrono::high_resolution_clock::now();
+
+ uint64_t duration_us =
+ std::chrono::duration_cast<std::chrono::microseconds>(end - start)
+ .count();
+ if (duration_us > max_duration_us)
+ max_duration_us = duration_us;
}
-void DrmDisplayCompositor::FrameWorker::Routine() {
- int wait_ret = 0;
-
- Lock();
- if (frame_queue_.empty()) {
- wait_ret = WaitForSignalOrExitLocked();
- }
-
- FrameState frame;
- if (!frame_queue_.empty()) {
- frame = std::move(frame_queue_.front());
- frame_queue_.pop();
- }
- Unlock();
-
- if (wait_ret == -EINTR) {
- return;
- } else if (wait_ret) {
- ALOGE("Failed to wait for signal, %d", wait_ret);
- return;
- }
- compositor_->ApplyFrame(std::move(frame.composition), frame.status);
+void DrmDisplayCompositor::FrameWorker::ProcessWork(
+ std::unique_ptr<FrameState> frame) {
+ compositor_->ApplyFrame(std::move(frame->composition), frame->status);
}
DrmDisplayCompositor::DrmDisplayCompositor()
- : drm_(NULL),
+ : QueueWorker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+ drm_(NULL),
display_(-1),
- worker_(this),
frame_worker_(this),
- initialized_(false),
active_(false),
use_hw_overlays_(true),
framebuffer_index_(0),
squash_framebuffer_index_(0),
dump_frames_composited_(0),
- dump_last_timestamp_ns_(0) {
+ dump_last_timestamp_ns_(0),
+ max_duration_us(0) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
return;
@@ -242,58 +235,32 @@
}
DrmDisplayCompositor::~DrmDisplayCompositor() {
- if (!initialized_)
+ if (!initialized())
return;
- worker_.Exit();
frame_worker_.Exit();
+ Exit();
- int ret = pthread_mutex_lock(&lock_);
- if (ret)
- ALOGE("Failed to acquire compositor lock %d", ret);
+ std::lock_guard<std::mutex> lk(mutex_);
if (mode_.blob_id)
drm_->DestroyPropertyBlob(mode_.blob_id);
if (mode_.old_blob_id)
drm_->DestroyPropertyBlob(mode_.old_blob_id);
- while (!composite_queue_.empty()) {
- composite_queue_.front().reset();
- composite_queue_.pop();
- }
active_composition_.reset();
-
- ret = pthread_mutex_unlock(&lock_);
- if (ret)
- ALOGE("Failed to acquire compositor lock %d", ret);
-
- pthread_mutex_destroy(&lock_);
}
int DrmDisplayCompositor::Init(DrmResources *drm, int display) {
drm_ = drm;
display_ = display;
- int ret = pthread_mutex_init(&lock_, NULL);
- if (ret) {
- ALOGE("Failed to initialize drm compositor lock %d\n", ret);
- return ret;
- }
- ret = worker_.Init();
- if (ret) {
- pthread_mutex_destroy(&lock_);
- ALOGE("Failed to initialize compositor worker %d\n", ret);
- return ret;
- }
- ret = frame_worker_.Init();
- if (ret) {
- pthread_mutex_destroy(&lock_);
- ALOGE("Failed to initialize frame worker %d\n", ret);
- return ret;
- }
+ frame_worker_.Init();
- initialized_ = true;
- return 0;
+ set_max_queue_size(DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH);
+ set_idle_timeout(kSquashWait);
+
+ return InitWorker();
}
std::unique_ptr<DrmDisplayComposition> DrmDisplayCompositor::CreateComposition()
@@ -324,29 +291,23 @@
return -ENOENT;
}
- int ret = pthread_mutex_lock(&lock_);
+ auto start = std::chrono::high_resolution_clock::now();
+
+ int ret = QueueWork(std::move(composition));
if (ret) {
- ALOGE("Failed to acquire compositor lock %d", ret);
+ ALOGE("Unable to queue work (%d)", ret);
+ // TODO: error handling (timeout or exit)
return ret;
}
- // Block the queue if it gets too large. Otherwise, SurfaceFlinger will start
- // to eat our buffer handles when we get about 1 second behind.
- while (composite_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH) {
- pthread_mutex_unlock(&lock_);
- sched_yield();
- pthread_mutex_lock(&lock_);
- }
+ auto end = std::chrono::high_resolution_clock::now();
- composite_queue_.push(std::move(composition));
+ uint64_t duration_us =
+ std::chrono::duration_cast<std::chrono::microseconds>(end - start)
+ .count();
+ if (duration_us > max_duration_us)
+ max_duration_us = duration_us;
- ret = pthread_mutex_unlock(&lock_);
- if (ret) {
- ALOGE("Failed to release compositor lock %d", ret);
- return ret;
- }
-
- worker_.Signal();
return 0;
}
@@ -859,11 +820,7 @@
}
void DrmDisplayCompositor::ClearDisplay() {
- AutoLock lock(&lock_, "compositor");
- int ret = lock.Lock();
- if (ret)
- return;
-
+ std::lock_guard<std::mutex> lk(mutex_);
if (!active_composition_)
return;
@@ -894,19 +851,12 @@
if (active_composition_)
active_composition_->SignalCompositionDone();
- ret = pthread_mutex_lock(&lock_);
- if (ret)
- ALOGE("Failed to acquire lock for active_composition swap");
-
+ std::lock_guard<std::mutex> lk(mutex_);
active_composition_.swap(composition);
-
- if (!ret)
- ret = pthread_mutex_unlock(&lock_);
- if (ret)
- ALOGE("Failed to release lock for active_composition swap");
}
-int DrmDisplayCompositor::Composite() {
+void DrmDisplayCompositor::ProcessWork(
+ std::unique_ptr<DrmDisplayComposition> composition) {
ATRACE_CALL();
if (!pre_compositor_) {
@@ -914,39 +864,17 @@
int ret = pre_compositor_->Init();
if (ret) {
ALOGE("Failed to initialize OpenGL compositor %d", ret);
- return ret;
+ return;
}
}
- int ret = pthread_mutex_lock(&lock_);
- if (ret) {
- ALOGE("Failed to acquire compositor lock %d", ret);
- return ret;
- }
- if (composite_queue_.empty()) {
- ret = pthread_mutex_unlock(&lock_);
- if (ret)
- ALOGE("Failed to release compositor lock %d", ret);
- return ret;
- }
-
- std::unique_ptr<DrmDisplayComposition> composition(
- std::move(composite_queue_.front()));
-
- composite_queue_.pop();
-
- ret = pthread_mutex_unlock(&lock_);
- if (ret) {
- ALOGE("Failed to release compositor lock %d", ret);
- return ret;
- }
-
+ int ret;
switch (composition->type()) {
case DRM_COMPOSITION_TYPE_FRAME:
ret = PrepareFrame(composition.get());
if (ret) {
ALOGE("Failed to prepare frame for display %d", display_);
- return ret;
+ return;
}
if (composition->geometry_changed()) {
// Send the composition to the kernel to ensure we can commit it. This
@@ -972,7 +900,7 @@
// to signal the release fences from that composition to avoid
// hanging.
ClearDisplay();
- return ret;
+ return;
}
}
frame_worker_.QueueFrame(std::move(composition), ret);
@@ -981,7 +909,7 @@
ret = ApplyDpms(composition.get());
if (ret)
ALOGE("Failed to apply dpms for display %d", display_);
- return ret;
+ break;
case DRM_COMPOSITION_TYPE_MODESET:
mode_.mode = composition->display_mode();
if (mode_.blob_id)
@@ -989,41 +917,19 @@
std::tie(ret, mode_.blob_id) = CreateModeBlob(mode_.mode);
if (ret) {
ALOGE("Failed to create mode blob for display %d", display_);
- return ret;
+ return;
}
mode_.needs_modeset = true;
- return 0;
+ break;
default:
ALOGE("Unknown composition type %d", composition->type());
- return -EINVAL;
+ break;
}
-
- return ret;
-}
-
-bool DrmDisplayCompositor::HaveQueuedComposites() const {
- int ret = pthread_mutex_lock(&lock_);
- if (ret) {
- ALOGE("Failed to acquire compositor lock %d", ret);
- return false;
- }
-
- bool empty_ret = !composite_queue_.empty();
-
- ret = pthread_mutex_unlock(&lock_);
- if (ret) {
- ALOGE("Failed to release compositor lock %d", ret);
- return false;
- }
-
- return empty_ret;
}
int DrmDisplayCompositor::SquashAll() {
- AutoLock lock(&lock_, "compositor");
- int ret = lock.Lock();
- if (ret)
- return ret;
+ std::unique_lock<std::mutex> lk(mutex_);
+ int ret;
if (!active_composition_)
return 0;
@@ -1032,7 +938,7 @@
ret = SquashFrame(active_composition_.get(), comp.get());
// ApplyFrame needs the lock
- lock.Unlock();
+ lk.unlock();
if (!ret)
ApplyFrame(std::move(comp), 0);
@@ -1164,17 +1070,13 @@
}
void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
- int ret = pthread_mutex_lock(&lock_);
- if (ret)
- return;
-
+ std::lock_guard<std::mutex> lk(mutex_);
uint64_t num_frames = dump_frames_composited_;
dump_frames_composited_ = 0;
struct timespec ts;
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (ret) {
- pthread_mutex_unlock(&lock_);
return;
}
@@ -1188,11 +1090,21 @@
dump_last_timestamp_ns_ = cur_ts;
+ *out << "----Jank Stats: "
+ << " compositor_max_q_wait_us=" << max_duration_us
+ << " frameworker_max_q_wait_us=" << frame_worker_.max_duration_us
+ << "\n";
+
+ max_duration_us = 0;
+ frame_worker_.max_duration_us = 0;
+
if (active_composition_)
active_composition_->Dump(out);
squash_state_.Dump(out);
+}
- pthread_mutex_unlock(&lock_);
+void DrmDisplayCompositor::ProcessIdle() {
+ SquashAll();
}
}
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index 9487cac..961fe72 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -17,13 +17,13 @@
#ifndef ANDROID_DRM_DISPLAY_COMPOSITOR_H_
#define ANDROID_DRM_DISPLAY_COMPOSITOR_H_
-#include "drmhwcomposer.h"
#include "drmcomposition.h"
-#include "drmcompositorworker.h"
#include "drmframebuffer.h"
+#include "drmhwcomposer.h"
+#include "queue_worker.h"
#include "separate_rects.h"
-#include <pthread.h>
+#include <chrono>
#include <memory>
#include <queue>
#include <sstream>
@@ -81,7 +81,7 @@
std::vector<Region> regions_;
};
-class DrmDisplayCompositor {
+class DrmDisplayCompositor : public QueueWorker<DrmDisplayComposition> {
public:
DrmDisplayCompositor();
~DrmDisplayCompositor();
@@ -90,39 +90,42 @@
std::unique_ptr<DrmDisplayComposition> CreateComposition() const;
int QueueComposition(std::unique_ptr<DrmDisplayComposition> composition);
- int Composite();
+ void ProcessWork(std::unique_ptr<DrmDisplayComposition> composition);
+ void ProcessIdle();
int SquashAll();
void Dump(std::ostringstream *out) const;
std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
- bool HaveQueuedComposites() const;
-
SquashState *squash_state() {
return &squash_state_;
}
private:
struct FrameState {
+ FrameState(std::unique_ptr<DrmDisplayComposition> composition, int status)
+ : composition(std::move(composition)), status(status) {
+ }
+
std::unique_ptr<DrmDisplayComposition> composition;
int status = 0;
};
- class FrameWorker : public Worker {
+ class FrameWorker : public QueueWorker<FrameState> {
public:
FrameWorker(DrmDisplayCompositor *compositor);
- ~FrameWorker() override;
int Init();
void QueueFrame(std::unique_ptr<DrmDisplayComposition> composition,
int status);
+ mutable uint64_t max_duration_us;
+
protected:
- void Routine() override;
+ void ProcessWork(std::unique_ptr<FrameState> frame);
private:
DrmDisplayCompositor *compositor_;
- std::queue<FrameState> frame_queue_;
};
struct ModeState {
@@ -158,13 +161,10 @@
DrmResources *drm_;
int display_;
- DrmCompositorWorker worker_;
FrameWorker frame_worker_;
- std::queue<std::unique_ptr<DrmDisplayComposition>> composite_queue_;
std::unique_ptr<DrmDisplayComposition> active_composition_;
- bool initialized_;
bool active_;
bool use_hw_overlays_;
@@ -179,12 +179,13 @@
DrmFramebuffer squash_framebuffers_[2];
// mutable since we need to acquire in HaveQueuedComposites
- mutable pthread_mutex_t lock_;
+ mutable std::mutex mutex_;
// State tracking progress since our last Dump(). These are mutable since
// we need to reset them on every Dump() call.
mutable uint64_t dump_frames_composited_;
mutable uint64_t dump_last_timestamp_ns_;
+ mutable uint64_t max_duration_us;
};
}
diff --git a/drmeventlistener.cpp b/drmeventlistener.cpp
index 0514aa6..7a21980 100644
--- a/drmeventlistener.cpp
+++ b/drmeventlistener.cpp
@@ -14,15 +14,17 @@
* limitations under the License.
*/
+#include <assert.h>
+
#define LOG_TAG "hwc-drm-event-listener"
#include "drmeventlistener.h"
#include "drmresources.h"
-#include <assert.h>
#include <linux/netlink.h>
#include <sys/socket.h>
+#include <assert.h>
#include <cutils/log.h>
#include <xf86drm.h>
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index c0aafef..875056d 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -33,7 +33,6 @@
#include <errno.h>
#include <fcntl.h>
-#include <pthread.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <xf86drm.h>
diff --git a/queue_worker.h b/queue_worker.h
new file mode 100644
index 0000000..7e96eec
--- /dev/null
+++ b/queue_worker.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ANDROID_QUEUE_WORKER_H_
+#define ANDROID_QUEUE_WORKER_H_
+
+#include "worker.h"
+
+#include <queue>
+
+namespace android {
+
+template <typename T>
+class QueueWorker : public Worker {
+ public:
+ static const size_t kDefaultMaxQueueSize = 2;
+ static const int64_t kTimeoutDisabled = -1;
+
+ QueueWorker(const char *name, int priority)
+ : Worker(name, priority),
+ max_queue_size_(kDefaultMaxQueueSize),
+ queue_timeout_ms_(kTimeoutDisabled),
+ idle_timeout_ms_(kTimeoutDisabled),
+ idled_out_(false) {
+ }
+
+ int QueueWork(std::unique_ptr<T> workitem);
+
+ bool IsWorkPending() const {
+ return !queue_.empty();
+ }
+ bool idle() const {
+ return idled_out_;
+ }
+
+ int64_t idle_timeout() {
+ return idle_timeout_ms_;
+ }
+ void set_idle_timeout(int64_t timeout_ms) {
+ idle_timeout_ms_ = timeout_ms;
+ }
+
+ int64_t queue_timeout() {
+ return queue_timeout_ms_;
+ }
+ void set_queue_timeout(int64_t timeout_ms) {
+ queue_timeout_ms_ = timeout_ms;
+ }
+
+ size_t max_queue_size() const {
+ return max_queue_size_;
+ }
+ void set_max_queue_size(size_t size) {
+ max_queue_size_ = size;
+ }
+
+ protected:
+ virtual void ProcessWork(std::unique_ptr<T> workitem) = 0;
+ virtual void ProcessIdle(){}
+ virtual void Routine();
+
+ template <typename Predicate>
+ int WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred,
+ int64_t max_msecs);
+
+ private:
+ std::queue<std::unique_ptr<T>> queue_;
+ size_t max_queue_size_;
+ int64_t queue_timeout_ms_;
+ int64_t idle_timeout_ms_;
+ bool idled_out_;
+};
+
+template <typename T>
+template <typename Predicate>
+int QueueWorker<T>::WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred,
+ int64_t max_msecs) {
+ bool ret = true;
+ auto wait_func = [&] { return pred() || should_exit(); };
+
+ if (max_msecs < 0) {
+ cond_.wait(lock, wait_func);
+ } else {
+ auto timeout = std::chrono::milliseconds(max_msecs);
+ ret = cond_.wait_for(lock, timeout, wait_func);
+ }
+
+ if (!ret)
+ return -ETIMEDOUT;
+ else if (should_exit())
+ return -EINTR;
+
+ return 0;
+}
+
+template <typename T>
+void QueueWorker<T>::Routine() {
+ std::unique_lock<std::mutex> lk(mutex_);
+ std::unique_ptr<T> workitem;
+
+ auto wait_func = [&] { return !queue_.empty(); };
+ int ret =
+ WaitCond(lk, wait_func, idled_out_ ? kTimeoutDisabled : idle_timeout_ms_);
+ switch (ret) {
+ case 0:
+ break;
+ case -ETIMEDOUT:
+ ProcessIdle();
+ idled_out_ = true;
+ return;
+ case -EINTR:
+ default:
+ return;
+ }
+
+ if (!queue_.empty()) {
+ workitem = std::move(queue_.front());
+ queue_.pop();
+ }
+ lk.unlock();
+ cond_.notify_all();
+
+ idled_out_ = false;
+ ProcessWork(std::move(workitem));
+}
+
+template <typename T>
+int QueueWorker<T>::QueueWork(std::unique_ptr<T> workitem) {
+ std::unique_lock<std::mutex> lk(mutex_);
+
+ auto wait_func = [&] { return queue_.size() < max_queue_size_; };
+ int ret = WaitCond(lk, wait_func, queue_timeout_ms_);
+ if (ret)
+ return ret;
+
+ queue_.push(std::move(workitem));
+ lk.unlock();
+
+ cond_.notify_one();
+
+ return 0;
+}
+};
+#endif
diff --git a/tests/Android.mk b/tests/Android.mk
index 5bbda93..b86cca6 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -3,6 +3,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
+ queue_worker_test.cpp \
worker_test.cpp
LOCAL_MODULE := hwc-drm-tests
diff --git a/tests/queue_worker_test.cpp b/tests/queue_worker_test.cpp
new file mode 100644
index 0000000..d1c0470
--- /dev/null
+++ b/tests/queue_worker_test.cpp
@@ -0,0 +1,201 @@
+#include <gtest/gtest.h>
+#include <hardware/hardware.h>
+
+#include <chrono>
+#include <mutex>
+
+#include "queue_worker.h"
+
+using android::QueueWorker;
+
+#define UNUSED_ARG(x) (void)(x)
+
+struct TestData {
+ TestData(int val) : value(val) {
+ }
+ virtual ~TestData() {
+ }
+
+ virtual void CheckValue(int prev_value) {
+ ASSERT_EQ(prev_value + 1, value);
+ }
+
+ int value;
+};
+
+struct TestQueueWorker : public QueueWorker<TestData> {
+ TestQueueWorker()
+ : QueueWorker("test-queueworker", HAL_PRIORITY_URGENT_DISPLAY), value(0) {
+ }
+
+ int Init() {
+ return InitWorker();
+ }
+
+ void ProcessWork(std::unique_ptr<TestData> data) {
+ std::lock_guard<std::mutex> blk(block);
+ data->CheckValue(value);
+ {
+ std::lock_guard<std::mutex> lk(lock);
+ value = data->value;
+ }
+ cond.notify_one();
+ }
+
+ void ProcessIdle() {
+ ASSERT_FALSE(idle());
+ }
+
+ std::mutex lock;
+ std::mutex block;
+ std::condition_variable cond;
+ int value;
+};
+
+struct QueueWorkerTest : public testing::Test {
+ static const int kTimeoutMs = 1000;
+ TestQueueWorker qw;
+
+ virtual void SetUp() {
+ qw.Init();
+ }
+ bool QueueValue(int val) {
+ std::unique_ptr<TestData> data(new TestData(val));
+ return !qw.QueueWork(std::move(data));
+ }
+
+ bool WaitFor(int val, int timeout_ms = kTimeoutMs) {
+ std::unique_lock<std::mutex> lk(qw.lock);
+
+ auto timeout = std::chrono::milliseconds(timeout_ms);
+ return qw.cond.wait_for(lk, timeout, [&] { return qw.value == val; });
+ }
+};
+
+struct IdleQueueWorkerTest : public QueueWorkerTest {
+ const int64_t kIdleTimeoutMs = 100;
+
+ virtual void SetUp() {
+ qw.set_idle_timeout(kIdleTimeoutMs);
+ qw.Init();
+ }
+};
+
+TEST_F(QueueWorkerTest, single_queue) {
+ // already isInitialized so should fail
+ ASSERT_NE(qw.Init(), 0);
+
+ ASSERT_EQ(qw.value, 0);
+ ASSERT_TRUE(QueueValue(1));
+ ASSERT_TRUE(WaitFor(1));
+ ASSERT_EQ(qw.value, 1);
+ ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, multiple_waits) {
+ for (int i = 1; i <= 100; i++) {
+ ASSERT_TRUE(QueueValue(i));
+ ASSERT_TRUE(WaitFor(i));
+ ASSERT_EQ(qw.value, i);
+ ASSERT_FALSE(qw.IsWorkPending());
+ }
+}
+
+TEST_F(QueueWorkerTest, multiple_queue) {
+ for (int i = 1; i <= 100; i++) {
+ ASSERT_TRUE(QueueValue(i));
+ }
+ ASSERT_TRUE(WaitFor(100));
+ ASSERT_EQ(qw.value, 100);
+ ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, blocking) {
+ // First wait for inital value to be setup
+ ASSERT_TRUE(QueueValue(1));
+ ASSERT_TRUE(WaitFor(1));
+
+ // Block processing and fill up the queue
+ std::unique_lock<std::mutex> lk(qw.block);
+ size_t expected_value = qw.max_queue_size() + 2;
+ for (size_t i = 2; i <= expected_value; i++) {
+ ASSERT_TRUE(QueueValue(i));
+ }
+
+ qw.set_queue_timeout(100);
+ // any additional queueing should fail
+ ASSERT_FALSE(QueueValue(expected_value + 1));
+
+ // make sure value is not changed while blocked
+ {
+ std::unique_lock<std::mutex> lock(qw.lock);
+ auto timeout = std::chrono::milliseconds(100);
+ ASSERT_FALSE(
+ qw.cond.wait_for(lock, timeout, [&] { return qw.value != 1; }));
+ }
+ ASSERT_EQ(qw.value, 1);
+ ASSERT_TRUE(qw.IsWorkPending());
+
+ // unblock and wait for value to be reached
+ lk.unlock();
+ ASSERT_TRUE(WaitFor(expected_value));
+ ASSERT_FALSE(qw.IsWorkPending());
+}
+
+TEST_F(QueueWorkerTest, exit_slow) {
+ struct SlowData : public TestData {
+ SlowData(int val) : TestData(val) {
+ }
+ void CheckValue(int prev_value) {
+ UNUSED_ARG(prev_value);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ };
+ std::unique_ptr<SlowData> data(new SlowData(1));
+ ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
+ data = std::unique_ptr<SlowData>(new SlowData(2));
+ ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
+ qw.Exit();
+ ASSERT_FALSE(qw.initialized());
+}
+
+TEST_F(QueueWorkerTest, exit_empty) {
+ qw.Exit();
+ ASSERT_FALSE(qw.initialized());
+}
+
+TEST_F(QueueWorkerTest, queue_worker_noidling) {
+ ASSERT_TRUE(QueueValue(1));
+ ASSERT_TRUE(WaitFor(1));
+
+ ASSERT_FALSE(qw.idle());
+ auto timeout = std::chrono::milliseconds(200);
+ std::this_thread::sleep_for(timeout);
+ ASSERT_FALSE(qw.idle());
+}
+
+TEST_F(IdleQueueWorkerTest, queue_worker_idling) {
+ ASSERT_TRUE(QueueValue(1));
+ ASSERT_TRUE(WaitFor(1));
+ ASSERT_FALSE(qw.idle());
+
+ auto timeout = std::chrono::milliseconds(kIdleTimeoutMs + 10);
+ std::this_thread::sleep_for(timeout);
+ ASSERT_TRUE(qw.idle());
+ ASSERT_TRUE(QueueValue(2));
+ ASSERT_TRUE(WaitFor(2));
+ ASSERT_FALSE(qw.idle());
+
+ std::this_thread::sleep_for(3 * timeout);
+ ASSERT_TRUE(qw.idle());
+
+ ASSERT_TRUE(QueueValue(3));
+ ASSERT_TRUE(WaitFor(3));
+ for (int i = 4; i <= 100; i++) {
+ QueueValue(i);
+ }
+ ASSERT_FALSE(qw.idle());
+ qw.Exit();
+ ASSERT_FALSE(qw.initialized());
+}
\ No newline at end of file
diff --git a/virtualcompositorworker.cpp b/virtualcompositorworker.cpp
index 639dc86..c1a6d2f 100644
--- a/virtualcompositorworker.cpp
+++ b/virtualcompositorworker.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015-2016 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.
@@ -17,15 +17,8 @@
#define LOG_TAG "hwc-virtual-compositor-worker"
#include "virtualcompositorworker.h"
-#include "worker.h"
-
-#include <errno.h>
-#include <stdlib.h>
#include <cutils/log.h>
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-#include <sched.h>
#include <sw_sync.h>
#include <sync/sync.h>
@@ -35,7 +28,7 @@
static const int kAcquireWaitTimeoutMs = 3000;
VirtualCompositorWorker::VirtualCompositorWorker()
- : Worker("virtual-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+ : QueueWorker("virtual-compositor", HAL_PRIORITY_URGENT_DISPLAY),
timeline_fd_(-1),
timeline_(0),
timeline_current_(0) {
@@ -56,6 +49,8 @@
return ret;
}
timeline_fd_ = ret;
+
+ set_max_queue_size(kMaxQueueDepth);
return InitWorker();
}
@@ -81,41 +76,7 @@
composition->release_timeline = timeline_;
- Lock();
- while (composite_queue_.size() >= kMaxQueueDepth) {
- Unlock();
- sched_yield();
- Lock();
- }
-
- composite_queue_.push(std::move(composition));
- Unlock();
- Signal();
-}
-
-void VirtualCompositorWorker::Routine() {
- int wait_ret = 0;
-
- Lock();
- if (composite_queue_.empty()) {
- wait_ret = WaitForSignalOrExitLocked();
- }
-
- std::unique_ptr<VirtualComposition> composition;
- if (!composite_queue_.empty()) {
- composition = std::move(composite_queue_.front());
- composite_queue_.pop();
- }
- Unlock();
-
- if (wait_ret == -EINTR) {
- return;
- } else if (wait_ret) {
- ALOGE("Failed to wait for signal, %d", wait_ret);
- return;
- }
-
- Compose(std::move(composition));
+ QueueWork(std::move(composition));
}
int VirtualCompositorWorker::CreateNextTimelineFence() {
@@ -135,7 +96,7 @@
return ret;
}
-void VirtualCompositorWorker::Compose(
+void VirtualCompositorWorker::ProcessWork(
std::unique_ptr<VirtualComposition> composition) {
if (!composition.get())
return;
diff --git a/virtualcompositorworker.h b/virtualcompositorworker.h
index 1fc5e43..885cf31 100644
--- a/virtualcompositorworker.h
+++ b/virtualcompositorworker.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015-2016 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.
@@ -18,13 +18,17 @@
#define ANDROID_VIRTUAL_COMPOSITOR_WORKER_H_
#include "drmhwcomposer.h"
-#include "worker.h"
-
-#include <queue>
+#include "queue_worker.h"
namespace android {
-class VirtualCompositorWorker : public Worker {
+struct VirtualComposition {
+ UniqueFd outbuf_acquire_fence;
+ std::vector<UniqueFd> layer_acquire_fences;
+ int release_timeline;
+};
+
+class VirtualCompositorWorker : public QueueWorker<VirtualComposition> {
public:
VirtualCompositorWorker();
~VirtualCompositorWorker() override;
@@ -33,20 +37,12 @@
void QueueComposite(hwc_display_contents_1_t *dc);
protected:
- void Routine() override;
+ void ProcessWork(std::unique_ptr<VirtualComposition> composition);
private:
- struct VirtualComposition {
- UniqueFd outbuf_acquire_fence;
- std::vector<UniqueFd> layer_acquire_fences;
- int release_timeline;
- };
-
int CreateNextTimelineFence();
int FinishComposition(int timeline);
- void Compose(std::unique_ptr<VirtualComposition> composition);
- std::queue<std::unique_ptr<VirtualComposition>> composite_queue_;
int timeline_fd_;
int timeline_;
int timeline_current_;
diff --git a/vsyncworker.cpp b/vsyncworker.cpp
index 3ad16fe..2177521 100644
--- a/vsyncworker.cpp
+++ b/vsyncworker.cpp
@@ -126,6 +126,7 @@
bool enabled = enabled_;
int display = display_;
std::shared_ptr<VsyncCallback> callback(callback_);
+
Unlock();
if (!enabled)