Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Unified Diff: gpu/ipc/service/gpu_vsync_provider_win.cc

Issue 2596123002: GpuVSyncProvider with unit test (Closed)
Patch Set: Added comment for GpuVSyncWorker destructor Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gpu/ipc/service/gpu_vsync_provider_unittest_win.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
new file mode 100644
index 0000000000000000000000000000000000000000..a996e6b6b027a7b6831cd05438ed80647b029cbc
--- /dev/null
+++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// 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 <string>
+
+#include "base/atomicops.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/trace_event.h"
+
+#include <windows.h>
+
+namespace gpu {
+
+namespace {
+// from <D3dkmthk.h>
+typedef LONG NTSTATUS;
+typedef UINT D3DKMT_HANDLE;
+typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
+
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+
+typedef struct _D3DKMT_OPENADAPTERFROMHDC {
+ HDC hDc;
+ D3DKMT_HANDLE hAdapter;
+ LUID AdapterLuid;
+ D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
+} D3DKMT_OPENADAPTERFROMHDC;
+
+typedef struct _D3DKMT_CLOSEADAPTER {
+ D3DKMT_HANDLE hAdapter;
+} D3DKMT_CLOSEADAPTER;
+
+typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT {
+ D3DKMT_HANDLE hAdapter;
+ D3DKMT_HANDLE hDevice;
+ D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
+} D3DKMT_WAITFORVERTICALBLANKEVENT;
+
+typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)(
+ D3DKMT_OPENADAPTERFROMHDC*);
+typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*);
+typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(
+ D3DKMT_WAITFORVERTICALBLANKEVENT*);
+} // namespace
+
+// The actual implementation of background tasks plus any state that might be
+// needed on the worker thread.
+class GpuVSyncWorker : public base::Thread {
+ public:
+ GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
+ SurfaceHandle surface_handle);
+ ~GpuVSyncWorker() override;
+
+ void Enable(bool enabled);
+ void StartRunningVSyncOnThread();
+ void WaitForVSyncOnThread();
+ void SendVSyncUpdate(base::TimeTicks timestamp);
+
+ private:
+ void Reschedule();
+ void OpenAdapter(const wchar_t* device_name);
+ void CloseAdapter();
+ bool WaitForVBlankEvent();
+
+ // Specifies whether background tasks are running.
+ // This can be set on background thread only.
+ bool running_ = false;
+
+ // Specified whether the worker is enabled. This is accessed from both
+ // threads but can be changed on the main thread only.
+ base::subtle::AtomicWord enabled_ = false;
+
+ const GpuVSyncProvider::VSyncCallback callback_;
+ const SurfaceHandle surface_handle_;
+
+ PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_;
+ PFND3DKMTCLOSEADAPTER close_adapter_ptr_;
+ PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_;
+
+ std::wstring current_device_name_;
+ D3DKMT_HANDLE current_adapter_handle_ = 0;
+ D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0;
+};
+
+GpuVSyncWorker::GpuVSyncWorker(const GpuVSyncProvider::VSyncCallback& callback,
+ SurfaceHandle surface_handle)
+ : base::Thread(base::StringPrintf("VSync-%d", surface_handle)),
+ callback_(callback),
+ surface_handle_(surface_handle) {
+ HMODULE gdi32 = GetModuleHandle(L"gdi32");
+ if (!gdi32) {
+ NOTREACHED() << "Can't open gdi32.dll";
+ return;
+ }
+
+ open_adapter_from_hdc_ptr_ = reinterpret_cast<PFND3DKMTOPENADAPTERFROMHDC>(
+ ::GetProcAddress(gdi32, "D3DKMTOpenAdapterFromHdc"));
+ if (!open_adapter_from_hdc_ptr_) {
+ NOTREACHED() << "Can't find D3DKMTOpenAdapterFromHdc in gdi32.dll";
+ return;
+ }
+
+ close_adapter_ptr_ = reinterpret_cast<PFND3DKMTCLOSEADAPTER>(
+ ::GetProcAddress(gdi32, "D3DKMTCloseAdapter"));
+ if (!close_adapter_ptr_) {
+ NOTREACHED() << "Can't find D3DKMTCloseAdapter in gdi32.dll";
+ return;
+ }
+
+ wait_for_vertical_blank_event_ptr_ =
+ reinterpret_cast<PFND3DKMTWAITFORVERTICALBLANKEVENT>(
+ ::GetProcAddress(gdi32, "D3DKMTWaitForVerticalBlankEvent"));
+ if (!wait_for_vertical_blank_event_ptr_) {
+ NOTREACHED() << "Can't find D3DKMTWaitForVerticalBlankEvent in gdi32.dll";
+ return;
+ }
+}
+
+GpuVSyncWorker::~GpuVSyncWorker() {
+ // 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,
+ base::Unretained(this)));
+ Stop();
+
+ DCHECK_EQ(0u, current_adapter_handle_);
+ DCHECK(current_device_name_.empty());
+}
+
+void GpuVSyncWorker::Enable(bool enabled) {
+ auto was_enabled = base::subtle::NoBarrier_AtomicExchange(&enabled_, enabled);
+
+ if (enabled && !was_enabled)
+ task_runner()->PostTask(
+ FROM_HERE, base::Bind(&GpuVSyncWorker::StartRunningVSyncOnThread,
+ base::Unretained(this)));
+}
+
+void GpuVSyncWorker::StartRunningVSyncOnThread() {
+ DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
+
+ if (!running_) {
+ running_ = true;
+ WaitForVSyncOnThread();
+ }
+}
+
+void GpuVSyncWorker::WaitForVSyncOnThread() {
+ DCHECK(base::PlatformThread::CurrentId() == GetThreadId());
+
+ TRACE_EVENT0("gpu", "GpuVSyncWorker::WaitForVSyncOnThread");
+
+ HMONITOR monitor =
+ MonitorFromWindow(surface_handle_, MONITOR_DEFAULTTONEAREST);
+ MONITORINFOEX monitor_info;
+ monitor_info.cbSize = sizeof(MONITORINFOEX);
+ BOOL success = GetMonitorInfo(monitor, &monitor_info);
+ CHECK(success);
+
+ if (current_device_name_.compare(monitor_info.szDevice) != 0) {
+ // Monitor changed. Close the current adapter handle and open a new one.
+ CloseAdapter();
+ OpenAdapter(monitor_info.szDevice);
+ }
+
+ if (WaitForVBlankEvent()) {
+ // Note: this sends update on background thread which the callback is
+ // expected to handle.
+ SendVSyncUpdate(base::TimeTicks::Now());
+ }
+
+ Reschedule();
+}
+
+void GpuVSyncWorker::SendVSyncUpdate(base::TimeTicks timestamp) {
+ if (base::subtle::NoBarrier_Load(&enabled_)) {
+ TRACE_EVENT0("gpu", "GpuVSyncWorker::SendVSyncUpdate");
+ callback_.Run(timestamp);
+ }
+}
+
+void GpuVSyncWorker::Reschedule() {
+ // Restart the task if still enabled.
+ if (base::subtle::NoBarrier_Load(&enabled_)) {
+ task_runner()->PostTask(FROM_HERE,
+ base::Bind(&GpuVSyncWorker::WaitForVSyncOnThread,
+ base::Unretained(this)));
+ } else {
+ running_ = false;
+ }
+}
+
+void GpuVSyncWorker::OpenAdapter(const wchar_t* device_name) {
+ DCHECK_EQ(0u, current_adapter_handle_);
+
+ HDC hdc = CreateDC(NULL, device_name, NULL, NULL);
+
+ D3DKMT_OPENADAPTERFROMHDC open_adapter_data;
+ open_adapter_data.hDc = hdc;
+
+ NTSTATUS result = open_adapter_from_hdc_ptr_(&open_adapter_data);
+ DeleteDC(hdc);
+
+ CHECK(result == STATUS_SUCCESS);
+
+ current_device_name_ = device_name;
+ current_adapter_handle_ = open_adapter_data.hAdapter;
+ current_source_id_ = open_adapter_data.VidPnSourceId;
+}
+
+void GpuVSyncWorker::CloseAdapter() {
+ if (current_adapter_handle_ != 0) {
+ D3DKMT_CLOSEADAPTER close_adapter_data;
+ close_adapter_data.hAdapter = current_adapter_handle_;
+
+ NTSTATUS result = close_adapter_ptr_(&close_adapter_data);
+ CHECK(result == STATUS_SUCCESS);
+
+ current_adapter_handle_ = 0;
+ current_device_name_.clear();
+ }
+}
+
+bool GpuVSyncWorker::WaitForVBlankEvent() {
+ D3DKMT_WAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_data;
+ wait_for_vertical_blank_event_data.hAdapter = current_adapter_handle_;
+ wait_for_vertical_blank_event_data.hDevice = 0;
+ wait_for_vertical_blank_event_data.VidPnSourceId = current_source_id_;
+
+ NTSTATUS result =
+ wait_for_vertical_blank_event_ptr_(&wait_for_vertical_blank_event_data);
+
+ 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));
+}
+
+GpuVSyncProvider::GpuVSyncProvider(const VSyncCallback& callback,
+ SurfaceHandle surface_handle)
+ : vsync_worker_(new GpuVSyncWorker(callback, surface_handle)) {
+ // 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);
+}
+
+GpuVSyncProvider::~GpuVSyncProvider() = default;
+
+void GpuVSyncProvider::EnableVSync(bool enabled) {
+ vsync_worker_->Enable(enabled);
+}
+
+} // namespace gpu
« no previous file with comments | « gpu/ipc/service/gpu_vsync_provider_unittest_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698