Index: ui/ozone/platform/drm/gpu/drm_device.cc |
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc |
index ed38a2c88a30fcf4277d598524d29a6962004e8d..49b6f5e6c83982aeebd471747b78d68837b05002 100644 |
--- a/ui/ozone/platform/drm/gpu/drm_device.cc |
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc |
@@ -29,6 +29,11 @@ namespace ui { |
namespace { |
+typedef base::Callback<void(uint32_t /* frame */, |
+ uint32_t /* seconds */, |
+ uint32_t /* useconds */, |
+ uint64_t /* id */)> DrmEventHandler; |
+ |
struct PageFlipPayload { |
PageFlipPayload(const scoped_refptr<base::TaskRunner>& task_runner, |
const DrmDevice::PageFlipCallback& callback) |
@@ -73,23 +78,41 @@ bool DrmDestroyDumbBuffer(int fd, uint32_t handle) { |
return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); |
} |
-void HandlePageFlipEventOnIO(int fd, |
- unsigned int frame, |
- unsigned int seconds, |
- unsigned int useconds, |
- void* data) { |
- scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data)); |
- payload->task_runner->PostTask( |
- FROM_HERE, base::Bind(payload->callback, frame, seconds, useconds)); |
-} |
+bool ProcessDrmEvent(int fd, const DrmEventHandler& callback) { |
+ char buffer[1024]; |
+ int len = read(fd, buffer, sizeof(buffer)); |
+ if (len == 0) |
+ return false; |
+ |
+ if (len < static_cast<int>(sizeof(drm_event))) { |
+ PLOG(ERROR) << "Failed to read DRM event"; |
+ return false; |
+ } |
+ |
+ int idx = 0; |
+ while (idx < len) { |
+ DCHECK_LE(static_cast<int>(sizeof(drm_event)), len - idx); |
+ drm_event event; |
+ memcpy(&event, &buffer[idx], sizeof(event)); |
+ switch (event.type) { |
+ case DRM_EVENT_FLIP_COMPLETE: { |
+ DCHECK_LE(static_cast<int>(sizeof(drm_event_vblank)), len - idx); |
+ drm_event_vblank vblank; |
+ memcpy(&vblank, &buffer[idx], sizeof(vblank)); |
+ callback.Run(vblank.sequence, vblank.tv_sec, vblank.tv_usec, |
+ vblank.user_data); |
+ } break; |
+ case DRM_EVENT_VBLANK: |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ |
+ idx += event.length; |
+ } |
-void HandlePageFlipEventOnUI(int fd, |
- unsigned int frame, |
- unsigned int seconds, |
- unsigned int useconds, |
- void* data) { |
- scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data)); |
- payload->callback.Run(frame, seconds, useconds); |
+ return true; |
} |
bool CanQueryForResources(int fd) { |
@@ -102,13 +125,70 @@ bool CanQueryForResources(int fd) { |
} // namespace |
+class DrmDevice::PageFlipManager |
+ : public base::RefCountedThreadSafe<DrmDevice::PageFlipManager> { |
+ public: |
+ PageFlipManager() : next_id_(0) {} |
+ |
+ void OnPageFlip(uint32_t frame, |
+ uint32_t seconds, |
+ uint32_t useconds, |
+ uint64_t id) { |
+ auto it = |
+ std::find_if(callbacks_.begin(), callbacks_.end(), FindCallback(id)); |
+ if (it == callbacks_.end()) { |
+ LOG(WARNING) << "Could not find callback for page flip id=" << id; |
+ return; |
+ } |
+ |
+ DrmDevice::PageFlipCallback callback = it->callback; |
+ callbacks_.erase(it); |
+ callback.Run(frame, seconds, useconds); |
+ } |
+ |
+ uint64_t GetNextId() { return next_id_++; } |
+ |
+ void RegisterCallback(uint64_t id, |
+ const DrmDevice::PageFlipCallback& callback) { |
+ callbacks_.push_back({id, callback}); |
+ } |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<DrmDevice::PageFlipManager>; |
+ ~PageFlipManager() {} |
+ |
+ struct PageFlip { |
+ uint64_t id; |
+ DrmDevice::PageFlipCallback callback; |
+ }; |
+ |
+ struct FindCallback { |
+ FindCallback(uint64_t id) : id(id) {} |
+ |
+ bool operator()(const PageFlip& flip) const { return flip.id == id; } |
+ |
+ uint64_t id; |
+ }; |
+ |
+ uint64_t next_id_; |
+ |
+ std::vector<PageFlip> callbacks_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PageFlipManager); |
+}; |
+ |
class DrmDevice::IOWatcher |
: public base::RefCountedThreadSafe<DrmDevice::IOWatcher>, |
public base::MessagePumpLibevent::Watcher { |
public: |
IOWatcher(int fd, |
- const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) |
- : io_task_runner_(io_task_runner), paused_(true), fd_(fd) {} |
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
+ const scoped_refptr<DrmDevice::PageFlipManager>& page_flip_manager) |
+ : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ io_task_runner_(io_task_runner), |
+ page_flip_manager_(page_flip_manager), |
+ paused_(true), |
+ fd_(fd) {} |
void SetPaused(bool paused) { |
if (paused_ == paused) |
@@ -152,23 +232,33 @@ class DrmDevice::IOWatcher |
done->Signal(); |
} |
+ void OnPageFlipOnIO(uint32_t frame, |
+ uint32_t seconds, |
+ uint32_t useconds, |
+ uint64_t id) { |
+ main_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&DrmDevice::PageFlipManager::OnPageFlip, page_flip_manager_, |
+ frame, seconds, useconds, id)); |
+ } |
+ |
// base::MessagePumpLibevent::Watcher overrides: |
void OnFileCanReadWithoutBlocking(int fd) override { |
DCHECK(base::MessageLoopForIO::IsCurrent()); |
TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd); |
- drmEventContext event; |
- event.version = DRM_EVENT_CONTEXT_VERSION; |
- event.page_flip_handler = HandlePageFlipEventOnIO; |
- event.vblank_handler = nullptr; |
- |
- drmHandleEvent(fd, &event); |
+ if (!ProcessDrmEvent( |
+ fd, base::Bind(&DrmDevice::IOWatcher::OnPageFlipOnIO, this))) |
+ UnregisterOnIO(); |
} |
void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); } |
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
+ scoped_refptr<DrmDevice::PageFlipManager> page_flip_manager_; |
+ |
base::MessagePumpLibevent::FileDescriptorWatcher controller_; |
bool paused_; |
@@ -181,14 +271,17 @@ DrmDevice::DrmDevice(const base::FilePath& device_path) |
: device_path_(device_path), |
file_(device_path, |
base::File::FLAG_OPEN | base::File::FLAG_READ | |
- base::File::FLAG_WRITE) { |
+ base::File::FLAG_WRITE), |
+ page_flip_manager_(new PageFlipManager()) { |
LOG_IF(FATAL, !file_.IsValid()) |
<< "Failed to open '" << device_path_.value() |
<< "': " << base::File::ErrorToString(file_.error_details()); |
} |
DrmDevice::DrmDevice(const base::FilePath& device_path, base::File file) |
- : device_path_(device_path), file_(file.Pass()) { |
+ : device_path_(device_path), |
+ file_(file.Pass()), |
+ page_flip_manager_(new PageFlipManager()) { |
} |
DrmDevice::~DrmDevice() { |
@@ -223,7 +316,8 @@ void DrmDevice::InitializeTaskRunner( |
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { |
DCHECK(!task_runner_); |
task_runner_ = task_runner; |
- watcher_ = new IOWatcher(file_.GetPlatformFile(), task_runner_); |
+ watcher_ = |
+ new IOWatcher(file_.GetPlatformFile(), task_runner_, page_flip_manager_); |
} |
ScopedDrmCrtcPtr DrmDevice::GetCrtc(uint32_t crtc_id) { |
@@ -307,24 +401,20 @@ bool DrmDevice::PageFlip(uint32_t crtc_id, |
// NOTE: Calling drmModeSetCrtc will immediately update the state, though |
// callbacks to already scheduled page flips will be honored by the kernel. |
- scoped_ptr<PageFlipPayload> payload( |
- new PageFlipPayload(base::ThreadTaskRunnerHandle::Get(), callback)); |
+ uint64_t id = page_flip_manager_->GetNextId(); |
if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer, |
- DRM_MODE_PAGE_FLIP_EVENT, payload.get())) { |
+ DRM_MODE_PAGE_FLIP_EVENT, reinterpret_cast<void*>(id))) { |
// If successful the payload will be removed by a PageFlip event. |
- ignore_result(payload.release()); |
+ page_flip_manager_->RegisterCallback(id, callback); |
// If the flip was requested synchronous or if no watcher has been installed |
// yet, then synchronously handle the page flip events. |
if (is_sync || !watcher_) { |
TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile()); |
- drmEventContext event; |
- event.version = DRM_EVENT_CONTEXT_VERSION; |
- event.page_flip_handler = HandlePageFlipEventOnUI; |
- event.vblank_handler = nullptr; |
- |
- drmHandleEvent(file_.GetPlatformFile(), &event); |
+ ProcessDrmEvent( |
+ file_.GetPlatformFile(), |
+ base::Bind(&PageFlipManager::OnPageFlip, page_flip_manager_)); |
} |
return true; |