Home | History | Annotate | Download | only in surface
      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 
     38 namespace d3d_utils = ui_surface_d3d9_utils;
     39 
     40 namespace {
     41 
     42 UINT GetPresentationInterval() {
     43   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
     44     return D3DPRESENT_INTERVAL_IMMEDIATE;
     45   else
     46     return D3DPRESENT_INTERVAL_ONE;
     47 }
     48 
     49 bool DoFirstShowPresentWithGDI() {
     50   return CommandLine::ForCurrentProcess()->HasSwitch(
     51       switches::kDoFirstShowPresentWithGDI);
     52 }
     53 
     54 bool DoAllShowPresentWithGDI() {
     55   return CommandLine::ForCurrentProcess()->HasSwitch(
     56       switches::kDoAllShowPresentWithGDI);
     57 }
     58 
     59 // Use a SurfaceReader to copy into one plane of the VideoFrame.
     60 bool 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.
     74 class 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.
    123 class 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.
    143 class 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 
    164 base::LazyInstance<PresentThreadPool>
    165     g_present_thread_pool = LAZY_INSTANCE_INITIALIZER;
    166 
    167 base::LazyInstance<AcceleratedPresenterMap>
    168     g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER;
    169 
    170 PresentThread::PresentThread(const char* name, uint64 adapter_luid)
    171     : base::Thread(name),
    172       adapter_luid_(adapter_luid) {
    173 }
    174 
    175 void 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 
    188 void 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 
    199 void PresentThread::LockAndResetDevice() {
    200   base::AutoLock locked(lock_);
    201   ResetDevice();
    202 }
    203 
    204 void 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 
    243 bool PresentThread::IsDeviceLost() {
    244   lock_.AssertAcquired();
    245 
    246   HRESULT hr = device_->CheckDeviceState(NULL);
    247   return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED;
    248 }
    249 
    250 void PresentThread::Init() {
    251   TRACE_EVENT0("gpu", "Initialize thread");
    252 }
    253 
    254 void 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 
    262 PresentThread::~PresentThread() {
    263   Stop();
    264 }
    265 
    266 PresentThreadPool::PresentThreadPool() : next_thread_(0) {
    267 }
    268 
    269 PresentThread* 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 
    285 void 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 
    302 AcceleratedPresenterMap::AcceleratedPresenterMap() {
    303 }
    304 
    305 scoped_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 
    317 void 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 
    332 scoped_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 
    348 void 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 
    359 AcceleratedPresenter::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
    370 void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) {
    371   return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid);
    372 }
    373 
    374 
    375 // static
    376 scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow(
    377     gfx::PluginWindowHandle window) {
    378   return g_accelerated_presenter_map.Pointer()->GetPresenter(window);
    379 }
    380 
    381 void 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 
    404 void 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 
    420 void 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 
    434 void 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 
    448 void 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 
    460 void 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 
    470 bool 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 
    544 bool 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 
    628 void AcceleratedPresenter::Suspend() {
    629   present_thread_->message_loop()->PostTask(
    630       FROM_HERE,
    631       base::Bind(&AcceleratedPresenter::DoSuspend,
    632                  this));
    633 }
    634 
    635 void AcceleratedPresenter::WasHidden() {
    636   base::AutoLock locked(*present_thread_->lock());
    637   hidden_ = true;
    638 }
    639 
    640 void AcceleratedPresenter::ReleaseSurface() {
    641   present_thread_->message_loop()->PostTask(
    642       FROM_HERE,
    643       base::Bind(&AcceleratedPresenter::DoReleaseSurface,
    644                  this));
    645 }
    646 
    647 void AcceleratedPresenter::SetIsSessionLocked(bool locked) {
    648   is_session_locked_ = locked;
    649 }
    650 
    651 void 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 
    660 void 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)
    676 void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window) {
    677   window_ = window;
    678   swap_chain_ = NULL;
    679 }
    680 #endif
    681 
    682 AcceleratedPresenter::~AcceleratedPresenter() {
    683 }
    684 
    685 bool AcceleratedPresenter::IsSwapChainInitialized() const {
    686   base::AutoLock locked(*present_thread_->lock());
    687 
    688   return !!swap_chain_;
    689 }
    690 
    691 void 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 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();
    734   bool size_mismatch = size != window_size;
    735   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   }
    745   if (hidden_ && size_mismatch) {
    746     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   }
    753 
    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;
    772     parameters.hDeviceWindow = window_;
    773     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());
    782     if (FAILED(hr)) {
    783       LOG(ERROR) << "Failed to create swap chain "
    784                  << quantized_size.width() << " x " <<quantized_size.height();
    785       return;
    786     }
    787   }
    788 
    789   if (!source_texture_.get()) {
    790     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";
    796       return;
    797     }
    798   }
    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");
    804     LOG(ERROR) << "Failed to get source surface";
    805     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");
    814     LOG(ERROR) << "Failed to get back buffer";
    815     return;
    816   }
    817 
    818   RECT rect = {
    819     0, 0,
    820     size.width(), size.height()
    821   };
    822 
    823   {
    824     TRACE_EVENT0("gpu", "Copy");
    825 
    826     // 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;
    832     }
    833   }
    834 
    835   hr = present_thread_->query()->Issue(D3DISSUE_END);
    836   if (FAILED(hr)) {
    837     LOG(ERROR) << "Failed to issue query";
    838     return;
    839   }
    840 
    841   present_size_ = size;
    842 
    843   // 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 
    850     if (FAILED(hr)) {
    851       if (present_thread_->IsDeviceLost())
    852         present_thread_->ResetDevice();
    853       return;
    854     }
    855   } else {
    856     HDC dc = GetDC(window_);
    857     PresentWithGDI(dc);
    858     ReleaseDC(window_, dc);
    859   }
    860 
    861   latency_info_.swap_timestamp = base::TimeTicks::HighResNow();
    862 
    863   hidden_ = false;
    864 
    865   D3DDISPLAYMODE display_mode;
    866   hr = present_thread_->device()->GetDisplayMode(0, &display_mode);
    867   if (FAILED(hr)) {
    868     LOG(ERROR) << "Failed to get display mode";
    869     return;
    870   }
    871 
    872   D3DRASTER_STATUS raster_status;
    873   hr = swap_chain_->GetRasterStatus(&raster_status);
    874   if (FAILED(hr)) {
    875     LOG(ERROR) << "Failed to get raster status";
    876     return;
    877   }
    878 
    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");
    906 
    907     do {
    908       hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH);
    909       if (hr == S_FALSE) {
    910         Sleep(1);
    911 
    912         if (present_thread_->IsDeviceLost()) {
    913           present_thread_->ResetDevice();
    914           return;
    915         }
    916       }
    917     } while (hr == S_FALSE);
    918   }
    919 
    920   scoped_completion_runner.Release();
    921   completion_task.Run(true, last_vsync_time, refresh_period, latency_info_);
    922   latency_info_.Clear();
    923 }
    924 
    925 void AcceleratedPresenter::DoSuspend() {
    926   base::AutoLock locked(*present_thread_->lock());
    927   swap_chain_ = NULL;
    928 }
    929 
    930 void AcceleratedPresenter::DoReleaseSurface() {
    931   base::AutoLock locked(*present_thread_->lock());
    932   present_thread_->InitDevice();
    933   source_texture_.Release();
    934 }
    935 
    936 void AcceleratedPresenter::PresentWithGDI(HDC dc) {
    937   TRACE_EVENT0("gpu", "PresentWithGDI");
    938 
    939   if (!present_thread_->device()) {
    940     LOG(ERROR) << "No device";
    941     return;
    942   }
    943 
    944   if (!swap_chain_) {
    945     LOG(ERROR) << "No swap chain";
    946     return;
    947   }
    948 
    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);
    961     if (FAILED(hr)) {
    962       LOG(ERROR) << "Failed to create system memory texture";
    963       return;
    964     }
    965   }
    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);
    981 
    982     if (FAILED(hr)) {
    983       if (present_thread_->IsDeviceLost()) {
    984         present_thread_->message_loop()->PostTask(
    985             FROM_HERE,
    986             base::Bind(&PresentThread::LockAndResetDevice, present_thread_));
    987       }
    988       return;
    989     }
    990 
    991     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();
   1028 }
   1029 
   1030 gfx::Size AcceleratedPresenter::GetWindowSize() {
   1031   RECT rect;
   1032   GetClientRect(window_, &rect);
   1033   return gfx::Rect(rect).size();
   1034 }
   1035 
   1036 bool AcceleratedPresenter::CheckDirect3DWillWork() {
   1037   // 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 
   1050   gfx::Size window_size = GetWindowSize();
   1051   if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) {
   1052     last_window_size_ = window_size;
   1053     last_window_resize_time_ = base::Time::Now();
   1054     return false;
   1055   }
   1056 
   1057   if (do_present_with_GDI_ && hidden_) {
   1058     if (DoFirstShowPresentWithGDI())
   1059       do_present_with_GDI_ = false;
   1060 
   1061     return false;
   1062   }
   1063 
   1064   return base::Time::Now() - last_window_resize_time_ >
   1065       base::TimeDelta::FromMilliseconds(100);
   1066 }
   1067 
   1068 AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window)
   1069     : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter(
   1070           window)) {
   1071 }
   1072 
   1073 AcceleratedSurface::~AcceleratedSurface() {
   1074   g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_);
   1075   presenter_->Invalidate();
   1076 }
   1077 
   1078 void AcceleratedSurface::Present(HDC dc) {
   1079   presenter_->Present(dc);
   1080 }
   1081 
   1082 bool AcceleratedSurface::IsReadyForCopy() const {
   1083   return !!presenter_ && presenter_->IsSwapChainInitialized();
   1084 }
   1085 
   1086 
   1087 void 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 
   1094 void 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);
   1099 }
   1100 
   1101 void AcceleratedSurface::Suspend() {
   1102   presenter_->Suspend();
   1103 }
   1104 
   1105 void AcceleratedSurface::WasHidden() {
   1106   presenter_->WasHidden();
   1107 }
   1108 
   1109 void AcceleratedSurface::SetIsSessionLocked(bool locked) {
   1110   presenter_->SetIsSessionLocked(locked);
   1111 }
   1112