accelerated_surface_win.cc revision 3551c9c881056c480085172ff9840cab31610854
1// 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
7#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"
15#include "base/files/file_path.h"
16#include "base/lazy_instance.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/message_loop/message_loop_proxy.h"
19#include "base/scoped_native_library.h"
20#include "base/strings/stringprintf.h"
21#include "base/synchronization/waitable_event.h"
22#include "base/threading/thread.h"
23#include "base/threading/thread_restrictions.h"
24#include "base/win/wrapped_window_proc.h"
25#include "media/base/video_frame.h"
26#include "media/base/video_util.h"
27#include "third_party/skia/include/core/SkBitmap.h"
28#include "ui/base/latency_info.h"
29#include "ui/base/win/dpi.h"
30#include "ui/base/win/hwnd_util.h"
31#include "ui/base/win/shell.h"
32#include "ui/gfx/rect.h"
33#include "ui/gl/gl_switches.h"
34#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;
39
40namespace {
41
42UINT GetPresentationInterval() {
43  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
44    return D3DPRESENT_INTERVAL_IMMEDIATE;
45  else
46    return D3DPRESENT_INTERVAL_ONE;
47}
48
49bool DoFirstShowPresentWithGDI() {
50  return CommandLine::ForCurrentProcess()->HasSwitch(
51      switches::kDoFirstShowPresentWithGDI);
52}
53
54bool DoAllShowPresentWithGDI() {
55  return CommandLine::ForCurrentProcess()->HasSwitch(
56      switches::kDoAllShowPresentWithGDI);
57}
58
59// 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));
68}
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:
77  PresentThread(const char* name, uint64 adapter_luid);
78
79  IDirect3DDevice9Ex* device() { return device_.get(); }
80  IDirect3DQuery9* query() { return query_.get(); }
81  AcceleratedSurfaceTransformer* surface_transformer() {
82    return &surface_transformer_;
83  }
84
85  void SetAdapterLUID(uint64 adapter_luid);
86  void InitDevice();
87  void LockAndResetDevice();
88  void ResetDevice();
89  bool IsDeviceLost();
90
91  base::Lock* lock() {
92    return &lock_;
93  }
94
95 protected:
96  virtual void Init();
97  virtual void CleanUp();
98
99 private:
100  friend class base::RefCountedThreadSafe<PresentThread>;
101
102  ~PresentThread();
103
104  // The lock is taken while any thread is calling an AcceleratedPresenter
105  // associated with this thread.
106  base::Lock lock_;
107
108  base::ScopedNativeLibrary d3d_module_;
109  uint64 adapter_luid_;
110  base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
111
112  // 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_;
116  AcceleratedSurfaceTransformer surface_transformer_;
117
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
130  void SetAdapterLUID(uint64 adapter_luid);
131
132 private:
133  base::Lock lock_;
134  int next_thread_;
135  scoped_refptr<PresentThread> present_threads_[kNumPresentThreads];
136  uint64 adapter_luid_;
137
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
152  // Destroy any D3D resources owned by the given present thread. Called on
153  // the given present thread.
154  void ResetPresentThread(PresentThread* present_thread);
155
156 private:
157  base::Lock lock_;
158  typedef std::map<gfx::PluginWindowHandle, AcceleratedPresenter*> PresenterMap;
159  PresenterMap presenters_;
160  uint64 adapter_luid_;
161  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
170PresentThread::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
178  CHECK(message_loop() == base::MessageLoop::current());
179
180  if (adapter_luid_ == adapter_luid)
181    return;
182
183  adapter_luid_ = adapter_luid;
184  if (device_)
185    ResetDevice();
186}
187
188void PresentThread::InitDevice() {
189  lock_.AssertAcquired();
190
191  if (device_)
192    return;
193
194  TRACE_EVENT0("gpu", "PresentThread::Init");
195  d3d_utils::LoadD3D9(&d3d_module_);
196  ResetDevice();
197}
198
199void PresentThread::LockAndResetDevice() {
200  base::AutoLock locked(lock_);
201  ResetDevice();
202}
203
204void PresentThread::ResetDevice() {
205  TRACE_EVENT0("gpu", "PresentThread::ResetDevice");
206
207  lock_.AssertAcquired();
208
209  // The D3D device must be created on the present thread.
210  CHECK(message_loop() == base::MessageLoop::current());
211
212  // 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;
216  surface_transformer_.ReleaseAll();
217
218  g_accelerated_presenter_map.Pointer()->ResetPresentThread(this);
219
220  if (!d3d_utils::CreateDevice(d3d_module_,
221                               adapter_luid_,
222                               D3DDEVTYPE_HAL,
223                               GetPresentationInterval(),
224                               device_.Receive())) {
225    return;
226  }
227
228  HRESULT hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
229  if (FAILED(hr)) {
230    LOG(ERROR) << "Failed to create query";
231    device_ = NULL;
232    return;
233  }
234
235  if (!surface_transformer_.Init(device_)) {
236    LOG(ERROR) << "Failed to initialize surface transformer";
237    query_ = NULL;
238    device_ = NULL;
239    return;
240  }
241}
242
243bool PresentThread::IsDeviceLost() {
244  lock_.AssertAcquired();
245
246  HRESULT hr = device_->CheckDeviceState(NULL);
247  return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED;
248}
249
250void PresentThread::Init() {
251  TRACE_EVENT0("gpu", "Initialize thread");
252}
253
254void PresentThread::CleanUp() {
255  // The D3D device and query are leaked because destroying the associated D3D
256  // query crashes some Intel drivers.
257  surface_transformer_.DetachAll();
258  device_.Detach();
259  query_.Detach();
260}
261
262PresentThread::~PresentThread() {
263  Stop();
264}
265
266PresentThreadPool::PresentThreadPool() : next_thread_(0) {
267}
268
269PresentThread* PresentThreadPool::NextThread() {
270  base::AutoLock locked(lock_);
271
272  next_thread_ = (next_thread_ + 1) % kNumPresentThreads;
273  PresentThread* thread = present_threads_[next_thread_].get();
274  if (!thread) {
275    thread = new PresentThread(
276        base::StringPrintf("PresentThread #%d", next_thread_).c_str(),
277        adapter_luid_);
278    thread->Start();
279    present_threads_[next_thread_] = thread;
280  }
281
282  return thread;
283}
284
285void 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
302AcceleratedPresenterMap::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
348void 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
359AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window)
360    : present_thread_(g_present_thread_pool.Pointer()->NextThread()),
361      window_(window),
362      event_(false, false),
363      hidden_(true),
364      do_present_with_GDI_(DoAllShowPresentWithGDI() ||
365                           DoFirstShowPresentWithGDI()),
366      is_session_locked_(false) {
367}
368
369// static
370void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) {
371  return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid);
372}
373
374
375// static
376scoped_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,
384    const ui::LatencyInfo& latency_info,
385    const CompletionTask& completion_task) {
386  if (!surface_handle) {
387    TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle",
388                 "surface_handle", surface_handle);
389    completion_task.Run(
390        true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
391    return;
392  }
393
394  present_thread_->message_loop()->PostTask(
395      FROM_HERE,
396      base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge,
397                 this,
398                 size,
399                 surface_handle,
400                 latency_info,
401                 completion_task));
402}
403
404void AcceleratedPresenter::Present(HDC dc) {
405  TRACE_EVENT0("gpu", "Present");
406
407  base::AutoLock locked(*present_thread_->lock());
408
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
420void 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) {
473  TRACE_EVENT2(
474      "gpu", "CopyTo",
475      "width", dst_size.width(),
476      "height", dst_size.height());
477
478  base::AutoLock locked(*present_thread_->lock());
479
480  if (!swap_chain_)
481    return false;
482
483  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  {
512    if (!d3d_utils::CreateOrReuseLockableSurface(present_thread_->device(),
513                                                 dst_size,
514                                                 &final_surface)) {
515      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
561  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
578  // the requested src subset. Clip to the actual back buffer.
579  gfx::Rect src_subrect = requested_src_subrect;
580  src_subrect.Intersect(gfx::Rect(back_buffer_size));
581  if (src_subrect.IsEmpty())
582    return false;
583
584  base::win::ScopedComPtr<IDirect3DSurface9> resized;
585  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;
590  }
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);
601  }
602
603  {
604    TRACE_EVENT0("gpu", "ResizeBilinear");
605    if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox))
606      return false;
607  }
608
609  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;
625  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() {
636  base::AutoLock locked(*present_thread_->lock());
637  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
647void AcceleratedPresenter::SetIsSessionLocked(bool locked) {
648  is_session_locked_ = locked;
649}
650
651void 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.
656  base::AutoLock locked(*present_thread_->lock());
657  window_ = NULL;
658}
659
660void 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
675#if defined(USE_AURA)
676void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) {
677  window_ = window;
678  swap_chain_ = NULL;
679}
680#endif
681
682AcceleratedPresenter::~AcceleratedPresenter() {
683}
684
685bool AcceleratedPresenter::IsSwapChainInitialized() const {
686  base::AutoLock locked(*present_thread_->lock());
687
688  return !!swap_chain_;
689}
690
691void AcceleratedPresenter::DoPresentAndAcknowledge(
692    const gfx::Size& size,
693    int64 surface_handle,
694    const ui::LatencyInfo& latency_info,
695    const CompletionTask& completion_task) {
696  TRACE_EVENT2(
697      "gpu", "DoPresentAndAcknowledge",
698      "width", size.width(),
699      "height", size.height());
700
701  HRESULT hr;
702
703  base::AutoLock locked(*present_thread_->lock());
704
705  latency_info_.MergeWith(latency_info);
706
707  // Initialize the device lazily since calling Direct3D can crash bots.
708  present_thread_->InitDevice();
709
710  if (!present_thread_->device()) {
711    completion_task.Run(
712        false, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
713    TRACE_EVENT0("gpu", "EarlyOut_NoDevice");
714    return;
715  }
716
717  // Ensure the task is acknowledged on early out after this point.
718  base::ScopedClosureRunner scoped_completion_runner(
719      base::Bind(completion_task,
720                 true,
721                 base::TimeTicks(),
722                 base::TimeDelta(),
723                 ui::LatencyInfo()));
724
725  // If invalidated, do nothing, the window is gone.
726  if (!window_) {
727    TRACE_EVENT0("gpu", "EarlyOut_NoWindow");
728    return;
729  }
730
731#if !defined(USE_AURA)
732  // If the window is a different size than the swap chain that is being
733  // presented then drop the frame.
734  gfx::Size window_size = GetWindowSize();
735  bool size_mismatch = size != window_size;
736  if (ui::IsInHighDPIMode()) {
737    // Check if the size mismatch is within allowable round off or truncation
738    // error.
739    gfx::Size dip_size = ui::win::ScreenToDIPSize(window_size);
740    gfx::Size pixel_size = ui::win::DIPToScreenSize(dip_size);
741    size_mismatch = abs(window_size.width() - size.width()) >
742        abs(window_size.width() - pixel_size.width()) ||
743        abs(window_size.height() - size.height()) >
744        abs(window_size.height() - pixel_size.height());
745  }
746  if (hidden_ && size_mismatch) {
747    TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize",
748                 "backwidth", size.width(), "backheight", size.height());
749    TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2",
750                 "windowwidth", window_size.width(),
751                 "windowheight", window_size.height());
752    return;
753  }
754#endif
755  // Round up size so the swap chain is not continuously resized with the
756  // surface, which could lead to memory fragmentation.
757  const int kRound = 64;
758  gfx::Size quantized_size(
759      std::max(1, (size.width() + kRound - 1) / kRound * kRound),
760      std::max(1, (size.height() + kRound - 1) / kRound * kRound));
761
762  // Ensure the swap chain exists and is the same size (rounded up) as the
763  // surface to be presented.
764  if (!swap_chain_ || quantized_size_ != quantized_size) {
765    TRACE_EVENT0("gpu", "CreateAdditionalSwapChain");
766    quantized_size_ = quantized_size;
767
768    D3DPRESENT_PARAMETERS parameters = { 0 };
769    parameters.BackBufferWidth = quantized_size.width();
770    parameters.BackBufferHeight = quantized_size.height();
771    parameters.BackBufferCount = 1;
772    parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
773    parameters.hDeviceWindow = window_;
774    parameters.Windowed = TRUE;
775    parameters.Flags = 0;
776    parameters.PresentationInterval = GetPresentationInterval();
777    parameters.SwapEffect = D3DSWAPEFFECT_COPY;
778
779    swap_chain_ = NULL;
780    HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain(
781        &parameters,
782        swap_chain_.Receive());
783    if (FAILED(hr)) {
784      LOG(ERROR) << "Failed to create swap chain "
785                 << quantized_size.width() << " x " <<quantized_size.height();
786      return;
787    }
788  }
789
790  if (!source_texture_.get()) {
791    TRACE_EVENT0("gpu", "OpenSharedTexture");
792    if (!d3d_utils::OpenSharedTexture(present_thread_->device(),
793                                      surface_handle,
794                                      size,
795                                      source_texture_.Receive())) {
796      LOG(ERROR) << "Failed to open shared texture";
797      return;
798    }
799  }
800
801  base::win::ScopedComPtr<IDirect3DSurface9> source_surface;
802  hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive());
803  if (FAILED(hr)) {
804    TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel");
805    LOG(ERROR) << "Failed to get source surface";
806    return;
807  }
808
809  base::win::ScopedComPtr<IDirect3DSurface9> dest_surface;
810  hr = swap_chain_->GetBackBuffer(0,
811                                  D3DBACKBUFFER_TYPE_MONO,
812                                  dest_surface.Receive());
813  if (FAILED(hr)) {
814    TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer");
815    LOG(ERROR) << "Failed to get back buffer";
816    return;
817  }
818
819  RECT rect = {
820    0, 0,
821    size.width(), size.height()
822  };
823
824  {
825    TRACE_EVENT0("gpu", "Copy");
826
827    // Copy while flipping the source texture on the vertical axis.
828    bool result = present_thread_->surface_transformer()->CopyInverted(
829        source_texture_, dest_surface, size);
830    if (!result) {
831      LOG(ERROR) << "Failed to copy shared texture";
832      return;
833    }
834  }
835
836  hr = present_thread_->query()->Issue(D3DISSUE_END);
837  if (FAILED(hr)) {
838    LOG(ERROR) << "Failed to issue query";
839    return;
840  }
841
842  present_size_ = size;
843
844  // If it is expected that Direct3D cannot be used reliably because the window
845  // is resizing, fall back to presenting with GDI.
846  if (CheckDirect3DWillWork()) {
847    TRACE_EVENT0("gpu", "PresentD3D");
848
849    hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0);
850
851    if (FAILED(hr)) {
852      if (present_thread_->IsDeviceLost())
853        present_thread_->ResetDevice();
854      return;
855    }
856  } else {
857    HDC dc = GetDC(window_);
858    PresentWithGDI(dc);
859    ReleaseDC(window_, dc);
860  }
861
862  latency_info_.swap_timestamp = base::TimeTicks::HighResNow();
863
864  hidden_ = false;
865
866  D3DDISPLAYMODE display_mode;
867  hr = present_thread_->device()->GetDisplayMode(0, &display_mode);
868  if (FAILED(hr)) {
869    LOG(ERROR) << "Failed to get display mode";
870    return;
871  }
872
873  D3DRASTER_STATUS raster_status;
874  hr = swap_chain_->GetRasterStatus(&raster_status);
875  if (FAILED(hr)) {
876    LOG(ERROR) << "Failed to get raster status";
877    return;
878  }
879
880  // I can't figure out how to determine how many scanlines are in the
881  // vertical blank so clamp it such that scanline / height <= 1.
882  int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height);
883
884  // The Internet says that on some GPUs, the scanline is not available
885  // while in the vertical blank.
886  if (raster_status.InVBlank)
887    clamped_scanline = display_mode.Height;
888
889  base::TimeTicks current_time = base::TimeTicks::HighResNow();
890
891  // Figure out approximately how far back in time the last vsync was based on
892  // the ratio of the raster scanline to the display height.
893  base::TimeTicks last_vsync_time;
894  base::TimeDelta refresh_period;
895  if (display_mode.Height) {
896      last_vsync_time = current_time -
897        base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) /
898            (display_mode.RefreshRate * display_mode.Height));
899      refresh_period = base::TimeDelta::FromMicroseconds(
900          1000000 / display_mode.RefreshRate);
901  }
902
903  // Wait for the StretchRect to complete before notifying the GPU process
904  // that it is safe to write to its backing store again.
905  {
906    TRACE_EVENT0("gpu", "spin");
907
908    do {
909      hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH);
910      if (hr == S_FALSE) {
911        Sleep(1);
912
913        if (present_thread_->IsDeviceLost()) {
914          present_thread_->ResetDevice();
915          return;
916        }
917      }
918    } while (hr == S_FALSE);
919  }
920
921  scoped_completion_runner.Release();
922  completion_task.Run(true, last_vsync_time, refresh_period, latency_info_);
923  latency_info_.Clear();
924}
925
926void AcceleratedPresenter::DoSuspend() {
927  base::AutoLock locked(*present_thread_->lock());
928  swap_chain_ = NULL;
929}
930
931void AcceleratedPresenter::DoReleaseSurface() {
932  base::AutoLock locked(*present_thread_->lock());
933  present_thread_->InitDevice();
934  source_texture_.Release();
935}
936
937void AcceleratedPresenter::PresentWithGDI(HDC dc) {
938  TRACE_EVENT0("gpu", "PresentWithGDI");
939
940  if (!present_thread_->device()) {
941    LOG(ERROR) << "No device";
942    return;
943  }
944
945  if (!swap_chain_) {
946    LOG(ERROR) << "No swap chain";
947    return;
948  }
949
950  base::win::ScopedComPtr<IDirect3DTexture9> system_texture;
951  {
952    TRACE_EVENT0("gpu", "CreateSystemTexture");
953    HRESULT hr = present_thread_->device()->CreateTexture(
954        quantized_size_.width(),
955        quantized_size_.height(),
956        1,
957        0,
958        D3DFMT_A8R8G8B8,
959        D3DPOOL_SYSTEMMEM,
960        system_texture.Receive(),
961        NULL);
962    if (FAILED(hr)) {
963      LOG(ERROR) << "Failed to create system memory texture";
964      return;
965    }
966  }
967
968  base::win::ScopedComPtr<IDirect3DSurface9> system_surface;
969  HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive());
970  DCHECK(SUCCEEDED(hr));
971
972  base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
973  hr = swap_chain_->GetBackBuffer(0,
974                                  D3DBACKBUFFER_TYPE_MONO,
975                                  back_buffer.Receive());
976  DCHECK(SUCCEEDED(hr));
977
978  {
979    TRACE_EVENT0("gpu", "GetRenderTargetData");
980    hr = present_thread_->device()->GetRenderTargetData(back_buffer,
981                                                        system_surface);
982
983    if (FAILED(hr)) {
984      if (present_thread_->IsDeviceLost()) {
985        present_thread_->message_loop()->PostTask(
986            FROM_HERE,
987            base::Bind(&PresentThread::LockAndResetDevice, present_thread_));
988      }
989      return;
990    }
991
992    DCHECK(SUCCEEDED(hr));
993  }
994
995  D3DLOCKED_RECT locked_surface;
996  hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY);
997  DCHECK(SUCCEEDED(hr));
998
999  BITMAPINFO bitmap_info = {
1000    {
1001      sizeof(BITMAPINFOHEADER),
1002      quantized_size_.width(),
1003      -quantized_size_.height(),
1004      1,  // planes
1005      32,  // bitcount
1006      BI_RGB
1007    },
1008    {
1009      {0, 0, 0, 0}
1010    }
1011  };
1012
1013  {
1014    TRACE_EVENT0("gpu", "StretchDIBits");
1015    StretchDIBits(dc,
1016                  0, 0,
1017                  present_size_.width(),
1018                  present_size_.height(),
1019                  0, 0,
1020                  present_size_.width(),
1021                  present_size_.height(),
1022                  locked_surface.pBits,
1023                  &bitmap_info,
1024                  DIB_RGB_COLORS,
1025                  SRCCOPY);
1026  }
1027
1028  system_surface->UnlockRect();
1029}
1030
1031gfx::Size AcceleratedPresenter::GetWindowSize() {
1032  RECT rect;
1033  GetClientRect(window_, &rect);
1034  return gfx::Rect(rect).size();
1035}
1036
1037bool AcceleratedPresenter::CheckDirect3DWillWork() {
1038  // On a composited desktop, when the screen saver or logon screen are
1039  // active, D3D presents never make it to the window but GDI presents
1040  // do. If the session is locked GDI presents can be avoided since
1041  // the window gets a message on unlock and forces a repaint.
1042  if (!is_session_locked_ && ui::win::IsAeroGlassEnabled()) {
1043    // Failure to open the input desktop is a sign of running with a non-default
1044    // desktop.
1045    HDESK input_desktop = ::OpenInputDesktop(0, 0, GENERIC_READ);
1046    if (!input_desktop)
1047      return false;
1048    ::CloseDesktop(input_desktop);
1049  }
1050
1051  gfx::Size window_size = GetWindowSize();
1052  if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) {
1053    last_window_size_ = window_size;
1054    last_window_resize_time_ = base::Time::Now();
1055    return false;
1056  }
1057
1058  if (do_present_with_GDI_ && hidden_) {
1059    if (DoFirstShowPresentWithGDI())
1060      do_present_with_GDI_ = false;
1061
1062    return false;
1063  }
1064
1065  return base::Time::Now() - last_window_resize_time_ >
1066      base::TimeDelta::FromMilliseconds(100);
1067}
1068
1069AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window)
1070    : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter(
1071          window)) {
1072}
1073
1074AcceleratedSurface::~AcceleratedSurface() {
1075  g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_);
1076  presenter_->Invalidate();
1077}
1078
1079void AcceleratedSurface::Present(HDC dc) {
1080  presenter_->Present(dc);
1081}
1082
1083bool AcceleratedSurface::IsReadyForCopy() const {
1084  return !!presenter_ && presenter_->IsSwapChainInitialized();
1085}
1086
1087
1088void AcceleratedSurface::AsyncCopyTo(
1089    const gfx::Rect& src_subrect,
1090    const gfx::Size& dst_size,
1091    const base::Callback<void(bool, const SkBitmap&)>& callback) {
1092  presenter_->AsyncCopyTo(src_subrect, dst_size, callback);
1093}
1094
1095void AcceleratedSurface::AsyncCopyToVideoFrame(
1096    const gfx::Rect& src_subrect,
1097    const scoped_refptr<media::VideoFrame>& target,
1098    const base::Callback<void(bool)>& callback) {
1099  presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback);
1100}
1101
1102void AcceleratedSurface::Suspend() {
1103  presenter_->Suspend();
1104}
1105
1106void AcceleratedSurface::WasHidden() {
1107  presenter_->WasHidden();
1108}
1109
1110void AcceleratedSurface::SetIsSessionLocked(bool locked) {
1111  presenter_->SetIsSessionLocked(locked);
1112}
1113