blob: 3b86db01ad45257e74d074a863dba6cca11676ce [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/surface/accelerated_surface_win.h"
6
Torne (Richard Coles)58218062012-11-14 11:43:16 +00007#include <windows.h>
8#include <algorithm>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/callback.h"
13#include "base/command_line.h"
14#include "base/debug/trace_event.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000015#include "base/files/file_path.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#include "base/lazy_instance.h"
17#include "base/memory/scoped_ptr.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010018#include "base/message_loop/message_loop_proxy.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "base/scoped_native_library.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010020#include "base/strings/stringprintf.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021#include "base/synchronization/waitable_event.h"
22#include "base/threading/thread.h"
23#include "base/threading/thread_restrictions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000024#include "base/win/wrapped_window_proc.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025#include "media/base/video_frame.h"
26#include "media/base/video_util.h"
27#include "third_party/skia/include/core/SkBitmap.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010028#include "ui/base/latency_info.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000029#include "ui/base/win/dpi.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030#include "ui/base/win/hwnd_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000031#include "ui/base/win/shell.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000032#include "ui/gfx/rect.h"
33#include "ui/gl/gl_switches.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000034#include "ui/surface/accelerated_surface_transformer_win.h"
35#include "ui/surface/d3d9_utils_win.h"
36#include "ui/surface/surface_switches.h"
37
38namespace d3d_utils = ui_surface_d3d9_utils;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000039
40namespace {
41
Torne (Richard Coles)58218062012-11-14 11:43:16 +000042UINT GetPresentationInterval() {
43 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
44 return D3DPRESENT_INTERVAL_IMMEDIATE;
45 else
46 return D3DPRESENT_INTERVAL_ONE;
47}
48
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049bool DoFirstShowPresentWithGDI() {
50 return CommandLine::ForCurrentProcess()->HasSwitch(
51 switches::kDoFirstShowPresentWithGDI);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052}
53
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000054bool DoAllShowPresentWithGDI() {
55 return CommandLine::ForCurrentProcess()->HasSwitch(
56 switches::kDoAllShowPresentWithGDI);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000057}
58
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000059// Use a SurfaceReader to copy into one plane of the VideoFrame.
60bool CopyPlane(AcceleratedSurfaceTransformer* gpu_ops,
61 IDirect3DSurface9* src_surface,
62 media::VideoFrame* dst_frame,
63 size_t plane_id) {
64 int width_in_bytes = dst_frame->row_bytes(plane_id);
65 return gpu_ops->ReadFast(src_surface, dst_frame->data(plane_id),
66 width_in_bytes, dst_frame->rows(plane_id),
67 dst_frame->row_bytes(plane_id));
Torne (Richard Coles)58218062012-11-14 11:43:16 +000068}
69
70} // namespace
71
72// A PresentThread is a thread that is dedicated to presenting surfaces to a
73// window. It owns a Direct3D device and a Direct3D query for this purpose.
74class PresentThread : public base::Thread,
75 public base::RefCountedThreadSafe<PresentThread> {
76 public:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010077 PresentThread(const char* name, uint64 adapter_luid);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000078
79 IDirect3DDevice9Ex* device() { return device_.get(); }
80 IDirect3DQuery9* query() { return query_.get(); }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000081 AcceleratedSurfaceTransformer* surface_transformer() {
82 return &surface_transformer_;
83 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000084
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010085 void SetAdapterLUID(uint64 adapter_luid);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000086 void InitDevice();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010087 void LockAndResetDevice();
Torne (Richard Coles)58218062012-11-14 11:43:16 +000088 void ResetDevice();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000089 bool IsDeviceLost();
90
91 base::Lock* lock() {
92 return &lock_;
93 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000094
95 protected:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000096 virtual void Init();
Torne (Richard Coles)58218062012-11-14 11:43:16 +000097 virtual void CleanUp();
98
99 private:
100 friend class base::RefCountedThreadSafe<PresentThread>;
101
102 ~PresentThread();
103
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000104 // The lock is taken while any thread is calling an AcceleratedPresenter
105 // associated with this thread.
106 base::Lock lock_;
107
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108 base::ScopedNativeLibrary d3d_module_;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100109 uint64 adapter_luid_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000110 base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100111
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000112 // This query is used to wait until a certain amount of progress has been
113 // made by the GPU and it is safe for the producer to modify its shared
114 // texture again.
115 base::win::ScopedComPtr<IDirect3DQuery9> query_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000116 AcceleratedSurfaceTransformer surface_transformer_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000117
118 DISALLOW_COPY_AND_ASSIGN(PresentThread);
119};
120
121// There is a fixed sized pool of PresentThreads and therefore the maximum
122// number of Direct3D devices owned by those threads is bounded.
123class PresentThreadPool {
124 public:
125 static const int kNumPresentThreads = 4;
126
127 PresentThreadPool();
128 PresentThread* NextThread();
129
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100130 void SetAdapterLUID(uint64 adapter_luid);
131
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000132 private:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100133 base::Lock lock_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000134 int next_thread_;
135 scoped_refptr<PresentThread> present_threads_[kNumPresentThreads];
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100136 uint64 adapter_luid_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000137
138 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool);
139};
140
141// A thread safe map of presenters by surface ID that returns presenters via
142// a scoped_refptr to keep them alive while they are referenced.
143class AcceleratedPresenterMap {
144 public:
145 AcceleratedPresenterMap();
146 scoped_refptr<AcceleratedPresenter> CreatePresenter(
147 gfx::PluginWindowHandle window);
148 void RemovePresenter(const scoped_refptr<AcceleratedPresenter>& presenter);
149 scoped_refptr<AcceleratedPresenter> GetPresenter(
150 gfx::PluginWindowHandle window);
151
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000152 // Destroy any D3D resources owned by the given present thread. Called on
153 // the given present thread.
154 void ResetPresentThread(PresentThread* present_thread);
155
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000156 private:
157 base::Lock lock_;
158 typedef std::map<gfx::PluginWindowHandle, AcceleratedPresenter*> PresenterMap;
159 PresenterMap presenters_;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100160 uint64 adapter_luid_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000161 DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap);
162};
163
164base::LazyInstance<PresentThreadPool>
165 g_present_thread_pool = LAZY_INSTANCE_INITIALIZER;
166
167base::LazyInstance<AcceleratedPresenterMap>
168 g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER;
169
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100170PresentThread::PresentThread(const char* name, uint64 adapter_luid)
171 : base::Thread(name),
172 adapter_luid_(adapter_luid) {
173}
174
175void PresentThread::SetAdapterLUID(uint64 adapter_luid) {
176 base::AutoLock locked(lock_);
177
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100178 CHECK(message_loop() == base::MessageLoop::current());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100179
180 if (adapter_luid_ == adapter_luid)
181 return;
182
183 adapter_luid_ = adapter_luid;
184 if (device_)
185 ResetDevice();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000186}
187
188void PresentThread::InitDevice() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100189 lock_.AssertAcquired();
190
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000191 if (device_)
192 return;
193
194 TRACE_EVENT0("gpu", "PresentThread::Init");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000195 d3d_utils::LoadD3D9(&d3d_module_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000196 ResetDevice();
197}
198
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100199void PresentThread::LockAndResetDevice() {
200 base::AutoLock locked(lock_);
201 ResetDevice();
202}
203
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000204void PresentThread::ResetDevice() {
205 TRACE_EVENT0("gpu", "PresentThread::ResetDevice");
206
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100207 lock_.AssertAcquired();
208
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000209 // The D3D device must be created on the present thread.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100210 CHECK(message_loop() == base::MessageLoop::current());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000211
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000212 // This will crash some Intel drivers but we can't render anything without
213 // reseting the device, which would be disappointing.
214 query_ = NULL;
215 device_ = NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000216 surface_transformer_.ReleaseAll();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000217
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000218 g_accelerated_presenter_map.Pointer()->ResetPresentThread(this);
219
220 if (!d3d_utils::CreateDevice(d3d_module_,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100221 adapter_luid_,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000222 D3DDEVTYPE_HAL,
223 GetPresentationInterval(),
224 device_.Receive())) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000225 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000226 }
227
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000228 HRESULT hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000229 if (FAILED(hr)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000230 LOG(ERROR) << "Failed to create query";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000231 device_ = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000232 return;
233 }
234
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000235 if (!surface_transformer_.Init(device_)) {
236 LOG(ERROR) << "Failed to initialize surface transformer";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000237 query_ = NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000238 device_ = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000239 return;
240 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000242
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000243bool PresentThread::IsDeviceLost() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100244 lock_.AssertAcquired();
245
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000246 HRESULT hr = device_->CheckDeviceState(NULL);
247 return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED;
248}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000249
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000250void PresentThread::Init() {
251 TRACE_EVENT0("gpu", "Initialize thread");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000252}
253
254void PresentThread::CleanUp() {
255 // The D3D device and query are leaked because destroying the associated D3D
256 // query crashes some Intel drivers.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000257 surface_transformer_.DetachAll();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000258 device_.Detach();
259 query_.Detach();
260}
261
262PresentThread::~PresentThread() {
263 Stop();
264}
265
266PresentThreadPool::PresentThreadPool() : next_thread_(0) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000267}
268
269PresentThread* PresentThreadPool::NextThread() {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100270 base::AutoLock locked(lock_);
271
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000272 next_thread_ = (next_thread_ + 1) % kNumPresentThreads;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000273 PresentThread* thread = present_threads_[next_thread_].get();
274 if (!thread) {
275 thread = new PresentThread(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100276 base::StringPrintf("PresentThread #%d", next_thread_).c_str(),
277 adapter_luid_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000278 thread->Start();
279 present_threads_[next_thread_] = thread;
280 }
281
282 return thread;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000283}
284
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100285void PresentThreadPool::SetAdapterLUID(uint64 adapter_luid) {
286 base::AutoLock locked(lock_);
287
288 adapter_luid_ = adapter_luid;
289
290 for (int i = 0; i < kNumPresentThreads; ++i) {
291 if (!present_threads_[i])
292 continue;
293
294 present_threads_[i]->message_loop()->PostTask(
295 FROM_HERE,
296 base::Bind(&PresentThread::SetAdapterLUID,
297 present_threads_[i],
298 adapter_luid));
299 }
300}
301
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000302AcceleratedPresenterMap::AcceleratedPresenterMap() {
303}
304
305scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::CreatePresenter(
306 gfx::PluginWindowHandle window) {
307 scoped_refptr<AcceleratedPresenter> presenter(
308 new AcceleratedPresenter(window));
309
310 base::AutoLock locked(lock_);
311 DCHECK(presenters_.find(window) == presenters_.end());
312 presenters_[window] = presenter.get();
313
314 return presenter;
315}
316
317void AcceleratedPresenterMap::RemovePresenter(
318 const scoped_refptr<AcceleratedPresenter>& presenter) {
319 base::AutoLock locked(lock_);
320 for (PresenterMap::iterator it = presenters_.begin();
321 it != presenters_.end();
322 ++it) {
323 if (it->second == presenter.get()) {
324 presenters_.erase(it);
325 return;
326 }
327 }
328
329 NOTREACHED();
330}
331
332scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::GetPresenter(
333 gfx::PluginWindowHandle window) {
334 base::AutoLock locked(lock_);
335
336#if defined(USE_AURA)
337 if (!window)
338 return presenters_.begin()->second;
339#endif
340
341 PresenterMap::iterator it = presenters_.find(window);
342 if (it == presenters_.end())
343 return scoped_refptr<AcceleratedPresenter>();
344
345 return it->second;
346}
347
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000348void AcceleratedPresenterMap::ResetPresentThread(
349 PresentThread* present_thread) {
350 base::AutoLock locked(lock_);
351
352 for (PresenterMap::iterator it = presenters_.begin();
353 it != presenters_.end();
354 ++it) {
355 it->second->ResetPresentThread(present_thread);
356 }
357}
358
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000359AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window)
360 : present_thread_(g_present_thread_pool.Pointer()->NextThread()),
361 window_(window),
362 event_(false, false),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000363 hidden_(true),
364 do_present_with_GDI_(DoAllShowPresentWithGDI() ||
365 DoFirstShowPresentWithGDI()),
366 is_session_locked_(false) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000367}
368
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000369// static
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100370void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) {
371 return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid);
372}
373
374
375// static
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000376scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow(
377 gfx::PluginWindowHandle window) {
378 return g_accelerated_presenter_map.Pointer()->GetPresenter(window);
379}
380
381void AcceleratedPresenter::AsyncPresentAndAcknowledge(
382 const gfx::Size& size,
383 int64 surface_handle,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100384 const ui::LatencyInfo& latency_info,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000385 const CompletionTask& completion_task) {
386 if (!surface_handle) {
387 TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle",
388 "surface_handle", surface_handle);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100389 completion_task.Run(
390 true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000391 return;
392 }
393
394 present_thread_->message_loop()->PostTask(
395 FROM_HERE,
396 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge,
397 this,
398 size,
399 surface_handle,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100400 latency_info,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000401 completion_task));
402}
403
404void AcceleratedPresenter::Present(HDC dc) {
405 TRACE_EVENT0("gpu", "Present");
406
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000407 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000408
409 // If invalidated, do nothing. The window is gone.
410 if (!window_)
411 return;
412
413 // Suspended or nothing has ever been presented.
414 if (!swap_chain_)
415 return;
416
417 PresentWithGDI(dc);
418}
419
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000420void AcceleratedPresenter::AsyncCopyTo(
421 const gfx::Rect& requested_src_subrect,
422 const gfx::Size& dst_size,
423 const base::Callback<void(bool, const SkBitmap&)>& callback) {
424 present_thread_->message_loop()->PostTask(
425 FROM_HERE,
426 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge,
427 this,
428 requested_src_subrect,
429 dst_size,
430 base::MessageLoopProxy::current(),
431 callback));
432}
433
434void AcceleratedPresenter::AsyncCopyToVideoFrame(
435 const gfx::Rect& requested_src_subrect,
436 const scoped_refptr<media::VideoFrame>& target,
437 const base::Callback<void(bool)>& callback) {
438 present_thread_->message_loop()->PostTask(
439 FROM_HERE,
440 base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge,
441 this,
442 requested_src_subrect,
443 target,
444 base::MessageLoopProxy::current(),
445 callback));
446}
447
448void AcceleratedPresenter::DoCopyToAndAcknowledge(
449 const gfx::Rect& src_subrect,
450 const gfx::Size& dst_size,
451 scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
452 const base::Callback<void(bool, const SkBitmap&)>& callback) {
453 SkBitmap target;
454 bool result = DoCopyToARGB(src_subrect, dst_size, &target);
455 if (!result)
456 target.reset();
457 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target));
458}
459
460void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge(
461 const gfx::Rect& src_subrect,
462 const scoped_refptr<media::VideoFrame>& target,
463 const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner,
464 const base::Callback<void(bool)>& callback) {
465
466 bool result = DoCopyToYUV(src_subrect, target);
467 callback_runner->PostTask(FROM_HERE, base::Bind(callback, result));
468}
469
470bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect,
471 const gfx::Size& dst_size,
472 SkBitmap* bitmap) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000473 TRACE_EVENT2(
474 "gpu", "CopyTo",
475 "width", dst_size.width(),
476 "height", dst_size.height());
477
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000478 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000479
480 if (!swap_chain_)
481 return false;
482
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000483 AcceleratedSurfaceTransformer* gpu_ops =
484 present_thread_->surface_transformer();
485
486 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
487 HRESULT hr = swap_chain_->GetBackBuffer(0,
488 D3DBACKBUFFER_TYPE_MONO,
489 back_buffer.Receive());
490 if (FAILED(hr)) {
491 LOG(ERROR) << "Failed to get back buffer";
492 return false;
493 }
494
495 D3DSURFACE_DESC desc;
496 hr = back_buffer->GetDesc(&desc);
497 if (FAILED(hr)) {
498 LOG(ERROR) << "Failed to get buffer description";
499 return false;
500 }
501
502 const gfx::Size back_buffer_size(desc.Width, desc.Height);
503 if (back_buffer_size.IsEmpty())
504 return false;
505
506 // With window resizing, it's possible that the back buffer is smaller than
507 // the requested src subset. Clip to the actual back buffer.
508 gfx::Rect src_subrect = requested_src_subrect;
509 src_subrect.Intersect(gfx::Rect(back_buffer_size));
510 base::win::ScopedComPtr<IDirect3DSurface9> final_surface;
511 {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100512 if (!d3d_utils::CreateOrReuseLockableSurface(present_thread_->device(),
513 dst_size,
514 &final_surface)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000515 LOG(ERROR) << "Failed to create temporary lockable surface";
516 return false;
517 }
518 }
519
520 {
521 // Let the surface transformer start the resize into |final_surface|.
522 TRACE_EVENT0("gpu", "ResizeBilinear");
523 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect,
524 final_surface, gfx::Rect(dst_size))) {
525 LOG(ERROR) << "Failed to resize bilinear";
526 return false;
527 }
528 }
529
530 bitmap->setConfig(SkBitmap::kARGB_8888_Config,
531 dst_size.width(), dst_size.height());
532 if (!bitmap->allocPixels())
533 return false;
534 bitmap->setIsOpaque(true);
535
536 // Copy |final_surface| to |bitmap|. This is always a synchronous operation.
537 return gpu_ops->ReadFast(final_surface,
538 reinterpret_cast<uint8*>(bitmap->getPixels()),
539 bitmap->width() * bitmap->bytesPerPixel(),
540 bitmap->height(),
541 static_cast<int>(bitmap->rowBytes()));
542}
543
544bool AcceleratedPresenter::DoCopyToYUV(
545 const gfx::Rect& requested_src_subrect,
546 const scoped_refptr<media::VideoFrame>& frame) {
547 gfx::Size dst_size = frame->coded_size();
548 TRACE_EVENT2(
549 "gpu", "CopyToYUV",
550 "width", dst_size.width(),
551 "height", dst_size.height());
552
553 base::AutoLock locked(*present_thread_->lock());
554
555 if (!swap_chain_)
556 return false;
557
558 AcceleratedSurfaceTransformer* gpu_ops =
559 present_thread_->surface_transformer();
560
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000561 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
562 HRESULT hr = swap_chain_->GetBackBuffer(0,
563 D3DBACKBUFFER_TYPE_MONO,
564 back_buffer.Receive());
565 if (FAILED(hr))
566 return false;
567
568 D3DSURFACE_DESC desc;
569 hr = back_buffer->GetDesc(&desc);
570 if (FAILED(hr))
571 return false;
572
573 const gfx::Size back_buffer_size(desc.Width, desc.Height);
574 if (back_buffer_size.IsEmpty())
575 return false;
576
577 // With window resizing, it's possible that the back buffer is smaller than
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000578 // the requested src subset. Clip to the actual back buffer.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000579 gfx::Rect src_subrect = requested_src_subrect;
580 src_subrect.Intersect(gfx::Rect(back_buffer_size));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100581 if (src_subrect.IsEmpty())
582 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000583
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000584 base::win::ScopedComPtr<IDirect3DSurface9> resized;
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100585 base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture;
586 if (!gpu_ops->GetIntermediateTexture(dst_size,
587 resized_as_texture.Receive(),
588 resized.Receive())) {
589 return false;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000590 }
591
592 // Shrink the source to fit entirely in the destination while preserving
593 // aspect ratio. Fill in any margin with black.
594 // TODO(nick): It would be more efficient all around to implement
595 // letterboxing as a memset() on the dst.
596 gfx::Rect letterbox = media::ComputeLetterboxRegion(gfx::Rect(dst_size),
597 src_subrect.size());
598 if (letterbox != gfx::Rect(dst_size)) {
599 TRACE_EVENT0("gpu", "Letterbox");
600 present_thread_->device()->ColorFill(resized, NULL, 0xFF000000);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000601 }
602
603 {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000604 TRACE_EVENT0("gpu", "ResizeBilinear");
605 if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox))
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000606 return false;
607 }
608
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000609 base::win::ScopedComPtr<IDirect3DSurface9> y, u, v;
610 {
611 TRACE_EVENT0("gpu", "TransformRGBToYV12");
612 if (!gpu_ops->TransformRGBToYV12(resized_as_texture,
613 dst_size,
614 y.Receive(), u.Receive(), v.Receive())) {
615 return false;
616 }
617 }
618
619 if (!CopyPlane(gpu_ops, y, frame, media::VideoFrame::kYPlane))
620 return false;
621 if (!CopyPlane(gpu_ops, u, frame, media::VideoFrame::kUPlane))
622 return false;
623 if (!CopyPlane(gpu_ops, v, frame, media::VideoFrame::kVPlane))
624 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000625 return true;
626}
627
628void AcceleratedPresenter::Suspend() {
629 present_thread_->message_loop()->PostTask(
630 FROM_HERE,
631 base::Bind(&AcceleratedPresenter::DoSuspend,
632 this));
633}
634
635void AcceleratedPresenter::WasHidden() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000636 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000637 hidden_ = true;
638}
639
640void AcceleratedPresenter::ReleaseSurface() {
641 present_thread_->message_loop()->PostTask(
642 FROM_HERE,
643 base::Bind(&AcceleratedPresenter::DoReleaseSurface,
644 this));
645}
646
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000647void AcceleratedPresenter::SetIsSessionLocked(bool locked) {
648 is_session_locked_ = locked;
649}
650
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000651void AcceleratedPresenter::Invalidate() {
652 // Make any pending or future presentation tasks do nothing. Once the last
653 // last pending task has been ignored, the reference count on the presenter
654 // will go to zero and the presenter, and potentially also the present thread
655 // it has a reference count on, will be destroyed.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000656 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000657 window_ = NULL;
658}
659
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000660void AcceleratedPresenter::ResetPresentThread(
661 PresentThread* present_thread) {
662 TRACE_EVENT0("gpu", "ResetPresentThread");
663
664 // present_thread_ can be accessed without the lock because it is immutable.
665 if (present_thread_ != present_thread)
666 return;
667
668 present_thread_->lock()->AssertAcquired();
669
670 source_texture_ = NULL;
671 swap_chain_ = NULL;
672 quantized_size_ = gfx::Size();
673}
674
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000675#if defined(USE_AURA)
676void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) {
677 window_ = window;
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100678 swap_chain_ = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000679}
680#endif
681
682AcceleratedPresenter::~AcceleratedPresenter() {
683}
684
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100685bool AcceleratedPresenter::IsSwapChainInitialized() const {
686 base::AutoLock locked(*present_thread_->lock());
687
688 return !!swap_chain_;
689}
690
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000691void AcceleratedPresenter::DoPresentAndAcknowledge(
692 const gfx::Size& size,
693 int64 surface_handle,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100694 const ui::LatencyInfo& latency_info,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000695 const CompletionTask& completion_task) {
696 TRACE_EVENT2(
697 "gpu", "DoPresentAndAcknowledge",
698 "width", size.width(),
699 "height", size.height());
700
701 HRESULT hr;
702
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000703 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000704
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100705 latency_info_.MergeWith(latency_info);
706
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000707 // Initialize the device lazily since calling Direct3D can crash bots.
708 present_thread_->InitDevice();
709
710 if (!present_thread_->device()) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100711 completion_task.Run(
712 false, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000713 TRACE_EVENT0("gpu", "EarlyOut_NoDevice");
714 return;
715 }
716
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000717 // Ensure the task is acknowledged on early out after this point.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000718 base::ScopedClosureRunner scoped_completion_runner(
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100719 base::Bind(completion_task,
720 true,
721 base::TimeTicks(),
722 base::TimeDelta(),
723 ui::LatencyInfo()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000724
725 // If invalidated, do nothing, the window is gone.
726 if (!window_) {
727 TRACE_EVENT0("gpu", "EarlyOut_NoWindow");
728 return;
729 }
730
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000731 // If the window is a different size than the swap chain that is being
732 // presented then drop the frame.
733 gfx::Size window_size = GetWindowSize();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000734 bool size_mismatch = size != window_size;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100735 if (ui::IsInHighDPIMode()) {
736 // Check if the size mismatch is within allowable round off or truncation
737 // error.
738 gfx::Size dip_size = ui::win::ScreenToDIPSize(window_size);
739 gfx::Size pixel_size = ui::win::DIPToScreenSize(dip_size);
740 size_mismatch = abs(window_size.width() - size.width()) >
741 abs(window_size.width() - pixel_size.width()) ||
742 abs(window_size.height() - size.height()) >
743 abs(window_size.height() - pixel_size.height());
744 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000745 if (hidden_ && size_mismatch) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000746 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize",
747 "backwidth", size.width(), "backheight", size.height());
748 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2",
749 "windowwidth", window_size.width(),
750 "windowheight", window_size.height());
751 return;
752 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000753
754 // Round up size so the swap chain is not continuously resized with the
755 // surface, which could lead to memory fragmentation.
756 const int kRound = 64;
757 gfx::Size quantized_size(
758 std::max(1, (size.width() + kRound - 1) / kRound * kRound),
759 std::max(1, (size.height() + kRound - 1) / kRound * kRound));
760
761 // Ensure the swap chain exists and is the same size (rounded up) as the
762 // surface to be presented.
763 if (!swap_chain_ || quantized_size_ != quantized_size) {
764 TRACE_EVENT0("gpu", "CreateAdditionalSwapChain");
765 quantized_size_ = quantized_size;
766
767 D3DPRESENT_PARAMETERS parameters = { 0 };
768 parameters.BackBufferWidth = quantized_size.width();
769 parameters.BackBufferHeight = quantized_size.height();
770 parameters.BackBufferCount = 1;
771 parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100772 parameters.hDeviceWindow = window_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000773 parameters.Windowed = TRUE;
774 parameters.Flags = 0;
775 parameters.PresentationInterval = GetPresentationInterval();
776 parameters.SwapEffect = D3DSWAPEFFECT_COPY;
777
778 swap_chain_ = NULL;
779 HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain(
780 &parameters,
781 swap_chain_.Receive());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000782 if (FAILED(hr)) {
783 LOG(ERROR) << "Failed to create swap chain "
784 << quantized_size.width() << " x " <<quantized_size.height();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000785 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000786 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000787 }
788
789 if (!source_texture_.get()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000790 TRACE_EVENT0("gpu", "OpenSharedTexture");
791 if (!d3d_utils::OpenSharedTexture(present_thread_->device(),
792 surface_handle,
793 size,
794 source_texture_.Receive())) {
795 LOG(ERROR) << "Failed to open shared texture";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000796 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000797 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000798 }
799
800 base::win::ScopedComPtr<IDirect3DSurface9> source_surface;
801 hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive());
802 if (FAILED(hr)) {
803 TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000804 LOG(ERROR) << "Failed to get source surface";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000805 return;
806 }
807
808 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface;
809 hr = swap_chain_->GetBackBuffer(0,
810 D3DBACKBUFFER_TYPE_MONO,
811 dest_surface.Receive());
812 if (FAILED(hr)) {
813 TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000814 LOG(ERROR) << "Failed to get back buffer";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000815 return;
816 }
817
818 RECT rect = {
819 0, 0,
820 size.width(), size.height()
821 };
822
823 {
824 TRACE_EVENT0("gpu", "Copy");
825
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000826 // Copy while flipping the source texture on the vertical axis.
827 bool result = present_thread_->surface_transformer()->CopyInverted(
828 source_texture_, dest_surface, size);
829 if (!result) {
830 LOG(ERROR) << "Failed to copy shared texture";
831 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000832 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000833 }
834
835 hr = present_thread_->query()->Issue(D3DISSUE_END);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000836 if (FAILED(hr)) {
837 LOG(ERROR) << "Failed to issue query";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000838 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000839 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000840
841 present_size_ = size;
842
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000843 // If it is expected that Direct3D cannot be used reliably because the window
844 // is resizing, fall back to presenting with GDI.
845 if (CheckDirect3DWillWork()) {
846 TRACE_EVENT0("gpu", "PresentD3D");
847
848 hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0);
849
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000850 if (FAILED(hr)) {
851 if (present_thread_->IsDeviceLost())
852 present_thread_->ResetDevice();
853 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000854 }
855 } else {
856 HDC dc = GetDC(window_);
857 PresentWithGDI(dc);
858 ReleaseDC(window_, dc);
859 }
860
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100861 latency_info_.swap_timestamp = base::TimeTicks::HighResNow();
862
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000863 hidden_ = false;
864
865 D3DDISPLAYMODE display_mode;
866 hr = present_thread_->device()->GetDisplayMode(0, &display_mode);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000867 if (FAILED(hr)) {
868 LOG(ERROR) << "Failed to get display mode";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000869 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000870 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000871
872 D3DRASTER_STATUS raster_status;
873 hr = swap_chain_->GetRasterStatus(&raster_status);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000874 if (FAILED(hr)) {
875 LOG(ERROR) << "Failed to get raster status";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000876 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000877 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000878
879 // I can't figure out how to determine how many scanlines are in the
880 // vertical blank so clamp it such that scanline / height <= 1.
881 int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height);
882
883 // The Internet says that on some GPUs, the scanline is not available
884 // while in the vertical blank.
885 if (raster_status.InVBlank)
886 clamped_scanline = display_mode.Height;
887
888 base::TimeTicks current_time = base::TimeTicks::HighResNow();
889
890 // Figure out approximately how far back in time the last vsync was based on
891 // the ratio of the raster scanline to the display height.
892 base::TimeTicks last_vsync_time;
893 base::TimeDelta refresh_period;
894 if (display_mode.Height) {
895 last_vsync_time = current_time -
896 base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) /
897 (display_mode.RefreshRate * display_mode.Height));
898 refresh_period = base::TimeDelta::FromMicroseconds(
899 1000000 / display_mode.RefreshRate);
900 }
901
902 // Wait for the StretchRect to complete before notifying the GPU process
903 // that it is safe to write to its backing store again.
904 {
905 TRACE_EVENT0("gpu", "spin");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000906
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000907 do {
908 hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000909 if (hr == S_FALSE) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000910 Sleep(1);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000911
912 if (present_thread_->IsDeviceLost()) {
913 present_thread_->ResetDevice();
914 return;
915 }
916 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000917 } while (hr == S_FALSE);
918 }
919
920 scoped_completion_runner.Release();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100921 completion_task.Run(true, last_vsync_time, refresh_period, latency_info_);
922 latency_info_.Clear();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000923}
924
925void AcceleratedPresenter::DoSuspend() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000926 base::AutoLock locked(*present_thread_->lock());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000927 swap_chain_ = NULL;
928}
929
930void AcceleratedPresenter::DoReleaseSurface() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000931 base::AutoLock locked(*present_thread_->lock());
932 present_thread_->InitDevice();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000933 source_texture_.Release();
934}
935
936void AcceleratedPresenter::PresentWithGDI(HDC dc) {
937 TRACE_EVENT0("gpu", "PresentWithGDI");
938
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000939 if (!present_thread_->device()) {
940 LOG(ERROR) << "No device";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000941 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000942 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000943
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000944 if (!swap_chain_) {
945 LOG(ERROR) << "No swap chain";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000946 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000947 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000948
949 base::win::ScopedComPtr<IDirect3DTexture9> system_texture;
950 {
951 TRACE_EVENT0("gpu", "CreateSystemTexture");
952 HRESULT hr = present_thread_->device()->CreateTexture(
953 quantized_size_.width(),
954 quantized_size_.height(),
955 1,
956 0,
957 D3DFMT_A8R8G8B8,
958 D3DPOOL_SYSTEMMEM,
959 system_texture.Receive(),
960 NULL);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000961 if (FAILED(hr)) {
962 LOG(ERROR) << "Failed to create system memory texture";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000963 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000964 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000965 }
966
967 base::win::ScopedComPtr<IDirect3DSurface9> system_surface;
968 HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive());
969 DCHECK(SUCCEEDED(hr));
970
971 base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
972 hr = swap_chain_->GetBackBuffer(0,
973 D3DBACKBUFFER_TYPE_MONO,
974 back_buffer.Receive());
975 DCHECK(SUCCEEDED(hr));
976
977 {
978 TRACE_EVENT0("gpu", "GetRenderTargetData");
979 hr = present_thread_->device()->GetRenderTargetData(back_buffer,
980 system_surface);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000981
982 if (FAILED(hr)) {
983 if (present_thread_->IsDeviceLost()) {
984 present_thread_->message_loop()->PostTask(
985 FROM_HERE,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100986 base::Bind(&PresentThread::LockAndResetDevice, present_thread_));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000987 }
988 return;
989 }
990
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000991 DCHECK(SUCCEEDED(hr));
992 }
993
994 D3DLOCKED_RECT locked_surface;
995 hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY);
996 DCHECK(SUCCEEDED(hr));
997
998 BITMAPINFO bitmap_info = {
999 {
1000 sizeof(BITMAPINFOHEADER),
1001 quantized_size_.width(),
1002 -quantized_size_.height(),
1003 1, // planes
1004 32, // bitcount
1005 BI_RGB
1006 },
1007 {
1008 {0, 0, 0, 0}
1009 }
1010 };
1011
1012 {
1013 TRACE_EVENT0("gpu", "StretchDIBits");
1014 StretchDIBits(dc,
1015 0, 0,
1016 present_size_.width(),
1017 present_size_.height(),
1018 0, 0,
1019 present_size_.width(),
1020 present_size_.height(),
1021 locked_surface.pBits,
1022 &bitmap_info,
1023 DIB_RGB_COLORS,
1024 SRCCOPY);
1025 }
1026
1027 system_surface->UnlockRect();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001028}
1029
1030gfx::Size AcceleratedPresenter::GetWindowSize() {
1031 RECT rect;
1032 GetClientRect(window_, &rect);
1033 return gfx::Rect(rect).size();
1034}
1035
1036bool AcceleratedPresenter::CheckDirect3DWillWork() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001037 // On a composited desktop, when the screen saver or logon screen are
1038 // active, D3D presents never make it to the window but GDI presents
1039 // do. If the session is locked GDI presents can be avoided since
1040 // the window gets a message on unlock and forces a repaint.
1041 if (!is_session_locked_ && ui::win::IsAeroGlassEnabled()) {
1042 // Failure to open the input desktop is a sign of running with a non-default
1043 // desktop.
1044 HDESK input_desktop = ::OpenInputDesktop(0, 0, GENERIC_READ);
1045 if (!input_desktop)
1046 return false;
1047 ::CloseDesktop(input_desktop);
1048 }
1049
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001050 gfx::Size window_size = GetWindowSize();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001051 if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001052 last_window_size_ = window_size;
1053 last_window_resize_time_ = base::Time::Now();
1054 return false;
1055 }
1056
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001057 if (do_present_with_GDI_ && hidden_) {
1058 if (DoFirstShowPresentWithGDI())
1059 do_present_with_GDI_ = false;
1060
1061 return false;
1062 }
1063
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001064 return base::Time::Now() - last_window_resize_time_ >
1065 base::TimeDelta::FromMilliseconds(100);
1066}
1067
1068AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window)
1069 : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter(
1070 window)) {
1071}
1072
1073AcceleratedSurface::~AcceleratedSurface() {
1074 g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_);
1075 presenter_->Invalidate();
1076}
1077
1078void AcceleratedSurface::Present(HDC dc) {
1079 presenter_->Present(dc);
1080}
1081
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001082bool AcceleratedSurface::IsReadyForCopy() const {
1083 return !!presenter_ && presenter_->IsSwapChainInitialized();
1084}
1085
1086
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001087void AcceleratedSurface::AsyncCopyTo(
1088 const gfx::Rect& src_subrect,
1089 const gfx::Size& dst_size,
1090 const base::Callback<void(bool, const SkBitmap&)>& callback) {
1091 presenter_->AsyncCopyTo(src_subrect, dst_size, callback);
1092}
1093
1094void AcceleratedSurface::AsyncCopyToVideoFrame(
1095 const gfx::Rect& src_subrect,
1096 const scoped_refptr<media::VideoFrame>& target,
1097 const base::Callback<void(bool)>& callback) {
1098 presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001099}
1100
1101void AcceleratedSurface::Suspend() {
1102 presenter_->Suspend();
1103}
1104
1105void AcceleratedSurface::WasHidden() {
1106 presenter_->WasHidden();
1107}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001108
1109void AcceleratedSurface::SetIsSessionLocked(bool locked) {
1110 presenter_->SetIsSessionLocked(locked);
1111}