| Index: gpu/ipc/service/gpu_vsync_provider_win.cc
|
| diff --git a/gpu/ipc/service/gpu_vsync_provider_win.cc b/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| index a996e6b6b027a7b6831cd05438ed80647b029cbc..1ee693e9e0236741d65505701ff7c1856e53c6ad 100644
|
| --- a/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| +++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
|
| @@ -2,7 +2,7 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "gpu/ipc/service/gpu_vsync_provider.h"
|
| +#include "gpu/ipc/service/gpu_vsync_provider_win.h"
|
|
|
| #include <string>
|
|
|
| @@ -10,6 +10,10 @@
|
| #include "base/strings/stringprintf.h"
|
| #include "base/threading/thread.h"
|
| #include "base/trace_event/trace_event.h"
|
| +#include "gpu/ipc/common/gpu_messages.h"
|
| +#include "ipc/ipc_message_macros.h"
|
| +#include "ipc/message_filter.h"
|
| +#include "ui/gl/vsync_provider_win.h"
|
|
|
| #include <windows.h>
|
|
|
| @@ -49,18 +53,22 @@ typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(
|
|
|
| // The actual implementation of background tasks plus any state that might be
|
| // needed on the worker thread.
|
| -class GpuVSyncWorker : public base::Thread {
|
| +class GpuVSyncWorker : public base::Thread,
|
| + public base::RefCountedThreadSafe<GpuVSyncWorker> {
|
| public:
|
| - GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
|
| - SurfaceHandle surface_handle);
|
| - ~GpuVSyncWorker() override;
|
| + GpuVSyncWorker(const VSyncCallback& callback, SurfaceHandle surface_handle);
|
|
|
| + void CleanupAndStop();
|
| void Enable(bool enabled);
|
| void StartRunningVSyncOnThread();
|
| void WaitForVSyncOnThread();
|
| void SendVSyncUpdate(base::TimeTicks timestamp);
|
| + bool BelongsToWorkerThread();
|
|
|
| private:
|
| + friend class base::RefCountedThreadSafe<GpuVSyncWorker>;
|
| + ~GpuVSyncWorker() override;
|
| +
|
| void Reschedule();
|
| void OpenAdapter(const wchar_t* device_name);
|
| void CloseAdapter();
|
| @@ -74,7 +82,7 @@ class GpuVSyncWorker : public base::Thread {
|
| // threads but can be changed on the main thread only.
|
| base::subtle::AtomicWord enabled_ = false;
|
|
|
| - const GpuVSyncProvider::VSyncCallback callback_;
|
| + const VSyncCallback callback_;
|
| const SurfaceHandle surface_handle_;
|
|
|
| PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_;
|
| @@ -86,7 +94,7 @@ class GpuVSyncWorker : public base::Thread {
|
| D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0;
|
| };
|
|
|
| -GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
|
| +GpuVSyncWorker::GpuVSyncWorker(const VSyncCallback& callback,
|
| SurfaceHandle surface_handle)
|
| : base::Thread(base::StringPrintf("VSync-%d", surface_handle)),
|
| callback_(callback),
|
| @@ -120,7 +128,9 @@ GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
|
| }
|
| }
|
|
|
| -GpuVSyncWorker::~GpuVSyncWorker() {
|
| +GpuVSyncWorker::~GpuVSyncWorker() = default;
|
| +
|
| +void GpuVSyncWorker::CleanupAndStop() {
|
| // Thread::Close() call below will block until this task has finished running
|
| // so it is safe to post it here and pass unretained pointer.
|
| task_runner()->PostTask(FROM_HERE, base::Bind(&GpuVSyncWorker::CloseAdapter,
|
| @@ -140,8 +150,12 @@ void GpuVSyncWorker::Enable(bool enabled) {
|
| base::Unretained(this)));
|
| }
|
|
|
| +bool GpuVSyncWorker::BelongsToWorkerThread() {
|
| + return base::PlatformThread::CurrentId() == GetThreadId();
|
| +}
|
| +
|
| void GpuVSyncWorker::StartRunningVSyncOnThread() {
|
| - DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
|
| + DCHECK(BelongsToWorkerThread());
|
|
|
| if (!running_) {
|
| running_ = true;
|
| @@ -150,7 +164,7 @@ void GpuVSyncWorker::StartRunningVSyncOnThread() {
|
| }
|
|
|
| void GpuVSyncWorker::WaitForVSyncOnThread() {
|
| - DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
|
| + DCHECK(BelongsToWorkerThread());
|
|
|
| TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
|
|
|
| @@ -237,16 +251,135 @@ bool GpuVSyncWorker::WaitForVBlankEvent() {
|
| return result == STATUS_SUCCESS;
|
| }
|
|
|
| -/* static */
|
| -std::unique_ptr<GpuVSyncProvider> GpuVSyncProvider::Create(
|
| - const VSyncCallback& callback,
|
| - SurfaceHandle surface_handle) {
|
| - return std::unique_ptr<GpuVSyncProvider>(
|
| - new GpuVSyncProvider(callback, surface_handle));
|
| +// MessageFilter class for sending and receiving IPC messages
|
| +// directly, avoiding routing them through the main GPU thread.
|
| +class GpuVSyncMessageFilter : public IPC::MessageFilter {
|
| + public:
|
| + explicit GpuVSyncMessageFilter(
|
| + const scoped_refptr<GpuVSyncWorker>& vsync_worker,
|
| + int32_t route_id)
|
| + : vsync_worker_(vsync_worker), route_id_(route_id) {}
|
| +
|
| + // IPC::MessageFilter overrides.
|
| + void OnChannelError() override { Reset(); }
|
| + void OnChannelClosing() override { Reset(); }
|
| + void OnFilterAdded(IPC::Channel* channel) override;
|
| + void OnFilterRemoved() override { Reset(); }
|
| + bool OnMessageReceived(const IPC::Message& msg) override;
|
| +
|
| + // Send can be called from GpuVSyncWorker thread.
|
| + void Send(std::unique_ptr<IPC::Message> message);
|
| +
|
| + int32_t route_id() const { return route_id_; }
|
| +
|
| + private:
|
| + ~GpuVSyncMessageFilter() = default;
|
| + void SendOnIOThread(std::unique_ptr<IPC::Message> message);
|
| + void Reset();
|
| +
|
| + scoped_refptr<GpuVSyncWorker> vsync_worker_;
|
| + // The sender to which this filter was added.
|
| + IPC::Sender* sender_ = nullptr;
|
| + // The sender must be invoked on IO thread.
|
| + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
|
| + int32_t route_id_;
|
| +};
|
| +
|
| +void GpuVSyncMessageFilter::OnFilterAdded(IPC::Channel* channel) {
|
| + io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
|
| + sender_ = channel;
|
| +}
|
| +
|
| +void GpuVSyncMessageFilter::Reset() {
|
| + sender_ = nullptr;
|
| + vsync_worker_->Enable(false);
|
| +}
|
| +
|
| +bool GpuVSyncMessageFilter::OnMessageReceived(const IPC::Message& msg) {
|
| + if (msg.routing_id() != route_id_)
|
| + return false;
|
| +
|
| + IPC_BEGIN_MESSAGE_MAP(GpuVSyncMessageFilter, msg)
|
| + IPC_MESSAGE_FORWARD(GpuCommandBufferMsg_SetNeedsVSync,
|
| + vsync_worker_.get(),
|
| + GpuVSyncWorker::Enable);
|
| + IPC_MESSAGE_UNHANDLED(return false)
|
| + IPC_END_MESSAGE_MAP()
|
| + return true;
|
| +}
|
| +
|
| +void GpuVSyncMessageFilter::Send(std::unique_ptr<IPC::Message> message) {
|
| + CHECK(io_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&GpuVSyncMessageFilter::SendOnIOThread, this,
|
| + base::Passed(&message))));
|
| +}
|
| +
|
| +void GpuVSyncMessageFilter::SendOnIOThread(
|
| + std::unique_ptr<IPC::Message> message) {
|
| + DCHECK(io_task_runner_->BelongsToCurrentThread());
|
| + if (!sender_ || message->is_sync()) {
|
| + DCHECK(!message->is_sync());
|
| + return;
|
| + }
|
| + sender_->Send(message.release());
|
| +}
|
| +
|
| +GpuVSyncProviderWin::GpuVSyncProviderWin(
|
| + base::WeakPtr<ImageTransportSurfaceDelegate> delegate,
|
| + SurfaceHandle surface_handle)
|
| + : vsync_interval_provider_(new gl::VSyncProviderWin(surface_handle)) {
|
| + vsync_worker_ = new GpuVSyncWorker(
|
| + base::Bind(&GpuVSyncProviderWin::OnVSync, base::Unretained(this)),
|
| + surface_handle);
|
| + message_filter_ = new GpuVSyncMessageFilter(
|
| + vsync_worker_, delegate->GetRouteID());
|
| + delegate->AddFilter(message_filter_.get());
|
| +
|
| + // Start the thread.
|
| + base::Thread::Options options;
|
| + // TODO(stanisc): might consider even higher priority - REALTIME_AUDIO.
|
| + options.priority = base::ThreadPriority::DISPLAY;
|
| + vsync_worker_->StartWithOptions(options);
|
| +}
|
| +
|
| +GpuVSyncProviderWin::~GpuVSyncProviderWin() {
|
| + vsync_worker_->CleanupAndStop();
|
| +}
|
| +
|
| +void GpuVSyncProviderWin::GetVSyncParameters(
|
| + const UpdateVSyncCallback& callback) {
|
| + // Note that |callback| is never called back. Instead GetVSyncParameters is
|
| + // routed to an internal handler where VSync interval is stored and used later
|
| + // with GpuCommandBufferMsg_UpdateVSyncParameters message posted from the
|
| + // worker thread.
|
| + vsync_interval_provider_->GetVSyncParameters(
|
| + base::Bind(&GpuVSyncProviderWin::OnVSyncParametersUpdated,
|
| + base::Unretained(this)));
|
| }
|
|
|
| -GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback,
|
| - SurfaceHandle surface_handle)
|
| +void GpuVSyncProviderWin::OnVSync(base::TimeTicks timestamp) {
|
| + DCHECK(vsync_worker_->BelongsToWorkerThread());
|
| + base::TimeDelta interval;
|
| +
|
| + {
|
| + base::AutoLock lock(vsync_interval_lock_);
|
| + interval = vsync_interval_;
|
| + }
|
| +
|
| + message_filter_->Send(
|
| + std::unique_ptr<IPC::Message>(
|
| + new GpuCommandBufferMsg_UpdateVSyncParameters(
|
| + message_filter_->route_id(), timestamp, interval)));
|
| +}
|
| +
|
| +void GpuVSyncProviderWin::OnVSyncParametersUpdated(base::TimeTicks timestamp,
|
| + base::TimeDelta interval) {
|
| + base::AutoLock lock(vsync_interval_lock_);
|
| + vsync_interval_ = interval;
|
| +}
|
| +
|
| +GpuVSyncProviderForTest::GpuVSyncProviderForTest(const VSyncCallback& callback,
|
| + SurfaceHandle surface_handle)
|
| : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) {
|
| // Start the thread.
|
| base::Thread::Options options;
|
| @@ -255,10 +388,12 @@ GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback,
|
| vsync_worker_->StartWithOptions(options);
|
| }
|
|
|
| -GpuVSyncProvider::~GpuVSyncProvider() = default;
|
| +GpuVSyncProviderForTest::~GpuVSyncProviderForTest() {
|
| + vsync_worker_->CleanupAndStop();
|
| +}
|
|
|
| -void GpuVSyncProvider::EnableVSync(bool enabled) {
|
| - vsync_worker_->Enable(enabled);
|
| +void GpuVSyncProviderForTest::SetNeedsVSync(bool needs_vsync) {
|
| + vsync_worker_->Enable(needs_vsync);
|
| }
|
|
|
| } // namespace gpu
|
|
|