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/callback.h"
     12 #include "base/callback_helpers.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/metrics/histogram.h"
     18 #include "base/memory/scoped_ptr.h"
     19 #include "base/message_loop/message_loop_proxy.h"
     20 #include "base/scoped_native_library.h"
     21 #include "base/strings/stringprintf.h"
     22 #include "base/synchronization/waitable_event.h"
     23 #include "base/threading/thread.h"
     24 #include "base/threading/thread_restrictions.h"
     25 #include "base/win/wrapped_window_proc.h"
     26 #include "media/base/video_frame.h"
     27 #include "media/base/video_util.h"
     28 #include "third_party/skia/include/core/SkBitmap.h"
     29 #include "ui/base/win/shell.h"
     30 #include "ui/events/latency_info.h"
     31 #include "ui/gfx/frame_time.h"
     32 #include "ui/gfx/rect.h"
     33 #include "ui/gfx/win/dpi.h"
     34 #include "ui/gfx/win/hwnd_util.h"
     35 #include "ui/gl/gl_switches.h"
     36 #include "ui/surface/accelerated_surface_transformer_win.h"
     37 #include "ui/surface/d3d9_utils_win.h"
     38 #include "ui/surface/surface_switches.h"
     39 
     40 namespace d3d_utils = ui_surface_d3d9_utils;
     41 
     42 namespace {
     43 
     44 UINT GetPresentationInterval() {
     45   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync))
     46     return D3DPRESENT_INTERVAL_IMMEDIATE;
     47   else
     48     return D3DPRESENT_INTERVAL_ONE;
     49 }
     50 
     51 bool DoFirstShowPresentWithGDI() {
     52   return CommandLine::ForCurrentProcess()->HasSwitch(
     53       switches::kDoFirstShowPresentWithGDI);
     54 }
     55 
     56 bool DoAllShowPresentWithGDI() {
     57   return CommandLine::ForCurrentProcess()->HasSwitch(
     58       switches::kDoAllShowPresentWithGDI);
     59 }
     60 
     61 // Use a SurfaceReader to copy into one plane of the VideoFrame.
     62 bool CopyPlane(AcceleratedSurfaceTransformer* gpu_ops,
     63                IDirect3DSurface9* src_surface,
     64                media::VideoFrame* dst_frame,
     65                size_t plane_id) {
     66   int width_in_bytes = dst_frame->row_bytes(plane_id);
     67   return gpu_ops->ReadFast(src_surface, dst_frame->data(plane_id),
     68                            width_in_bytes, dst_frame->rows(plane_id),
     69                            dst_frame->row_bytes(plane_id));
     70 }
     71 
     72 }  // namespace
     73 
     74 // A PresentThread is a thread that is dedicated to presenting surfaces to a
     75 // window. It owns a Direct3D device and a Direct3D query for this purpose.
     76 class PresentThread : public base::Thread,
     77                       public base::RefCountedThreadSafe<PresentThread> {
     78  public:
     79   PresentThread(const char* name, uint64 adapter_luid);
     80 
     81   IDirect3DDevice9Ex* device() { return device_.get(); }
     82   IDirect3DQuery9* query() { return query_.get(); }
     83   AcceleratedSurfaceTransformer* surface_transformer() {
     84     return &surface_transformer_;
     85   }
     86 
     87   void SetAdapterLUID(uint64 adapter_luid);
     88   void InitDevice();
     89   void LockAndResetDevice();
     90   void ResetDevice();
     91   bool IsDeviceLost();
     92 
     93   base::Lock* lock() {
     94     return &lock_;
     95   }
     96 
     97  protected:
     98   virtual void Init();
     99   virtual void CleanUp();
    100 
    101  private:
    102   friend class base::RefCountedThreadSafe<PresentThread>;
    103 
    104   ~PresentThread();
    105 
    106   // The lock is taken while any thread is calling an AcceleratedPresenter
    107   // associated with this thread.
    108   base::Lock lock_;
    109 
    110   base::ScopedNativeLibrary d3d_module_;
    111   uint64 adapter_luid_;
    112   base::win::ScopedComPtr<IDirect3DDevice9Ex> device_;
    113 
    114   // This query is used to wait until a certain amount of progress has been
    115   // made by the GPU and it is safe for the producer to modify its shared
    116   // texture again.
    117   base::win::ScopedComPtr<IDirect3DQuery9> query_;
    118   AcceleratedSurfaceTransformer surface_transformer_;
    119 
    120   DISALLOW_COPY_AND_ASSIGN(PresentThread);
    121 };
    122 
    123 // There is a fixed sized pool of PresentThreads and therefore the maximum
    124 // number of Direct3D devices owned by those threads is bounded.
    125 class PresentThreadPool {
    126  public:
    127   static const int kNumPresentThreads = 4;
    128 
    129   PresentThreadPool();
    130   PresentThread* NextThread();
    131 
    132   void SetAdapterLUID(uint64 adapter_luid);
    133 
    134  private:
    135   base::Lock lock_;
    136   int next_thread_;
    137   scoped_refptr<PresentThread> present_threads_[kNumPresentThreads];
    138   uint64 adapter_luid_;
    139 
    140   DISALLOW_COPY_AND_ASSIGN(PresentThreadPool);
    141 };
    142 
    143 // A thread safe map of presenters by surface ID that returns presenters via
    144 // a scoped_refptr to keep them alive while they are referenced.
    145 class AcceleratedPresenterMap {
    146  public:
    147   AcceleratedPresenterMap();
    148   scoped_refptr<AcceleratedPresenter> CreatePresenter(
    149       gfx::PluginWindowHandle window);
    150   void RemovePresenter(const scoped_refptr<AcceleratedPresenter>& presenter);
    151   scoped_refptr<AcceleratedPresenter> GetPresenter(
    152       gfx::PluginWindowHandle window);
    153 
    154   // Destroy any D3D resources owned by the given present thread. Called on
    155   // the given present thread.
    156   void ResetPresentThread(PresentThread* present_thread);
    157 
    158  private:
    159   base::Lock lock_;
    160   typedef std::map<gfx::PluginWindowHandle, AcceleratedPresenter*> PresenterMap;
    161   PresenterMap presenters_;
    162   uint64 adapter_luid_;
    163   DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap);
    164 };
    165 
    166 base::LazyInstance<PresentThreadPool>
    167     g_present_thread_pool = LAZY_INSTANCE_INITIALIZER;
    168 
    169 base::LazyInstance<AcceleratedPresenterMap>
    170     g_accelerated_presenter_map = LAZY_INSTANCE_INITIALIZER;
    171 
    172 PresentThread::PresentThread(const char* name, uint64 adapter_luid)
    173     : base::Thread(name),
    174       adapter_luid_(adapter_luid) {
    175 }
    176 
    177 void PresentThread::SetAdapterLUID(uint64 adapter_luid) {
    178   base::AutoLock locked(lock_);
    179 
    180   CHECK(message_loop() == base::MessageLoop::current());
    181 
    182   if (adapter_luid_ == adapter_luid)
    183     return;
    184 
    185   adapter_luid_ = adapter_luid;
    186   if (device_)
    187     ResetDevice();
    188 }
    189 
    190 void PresentThread::InitDevice() {
    191   lock_.AssertAcquired();
    192 
    193   if (device_)
    194     return;
    195 
    196   TRACE_EVENT0("gpu", "PresentThread::Init");
    197   d3d_utils::LoadD3D9(&d3d_module_);
    198   ResetDevice();
    199 }
    200 
    201 void PresentThread::LockAndResetDevice() {
    202   base::AutoLock locked(lock_);
    203   ResetDevice();
    204 }
    205 
    206 void PresentThread::ResetDevice() {
    207   TRACE_EVENT0("gpu", "PresentThread::ResetDevice");
    208 
    209   lock_.AssertAcquired();
    210 
    211   // The D3D device must be created on the present thread.
    212   CHECK(message_loop() == base::MessageLoop::current());
    213 
    214   // This will crash some Intel drivers but we can't render anything without
    215   // reseting the device, which would be disappointing.
    216   query_ = NULL;
    217   device_ = NULL;
    218   surface_transformer_.ReleaseAll();
    219 
    220   g_accelerated_presenter_map.Pointer()->ResetPresentThread(this);
    221 
    222   if (!d3d_utils::CreateDevice(d3d_module_,
    223                                adapter_luid_,
    224                                D3DDEVTYPE_HAL,
    225                                GetPresentationInterval(),
    226                                device_.Receive())) {
    227     return;
    228   }
    229 
    230   HRESULT hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
    231   if (FAILED(hr)) {
    232     LOG(ERROR) << "Failed to create query";
    233     device_ = NULL;
    234     return;
    235   }
    236 
    237   if (!surface_transformer_.Init(device_)) {
    238     LOG(ERROR) << "Failed to initialize surface transformer";
    239     query_ = NULL;
    240     device_ = NULL;
    241     return;
    242   }
    243 }
    244 
    245 bool PresentThread::IsDeviceLost() {
    246   lock_.AssertAcquired();
    247 
    248   HRESULT hr = device_->CheckDeviceState(NULL);
    249   return FAILED(hr) || hr == S_PRESENT_MODE_CHANGED;
    250 }
    251 
    252 void PresentThread::Init() {
    253   TRACE_EVENT0("gpu", "Initialize thread");
    254 }
    255 
    256 void PresentThread::CleanUp() {
    257   // The D3D device and query are leaked because destroying the associated D3D
    258   // query crashes some Intel drivers.
    259   surface_transformer_.DetachAll();
    260   device_.Detach();
    261   query_.Detach();
    262 }
    263 
    264 PresentThread::~PresentThread() {
    265   Stop();
    266 }
    267 
    268 PresentThreadPool::PresentThreadPool() : next_thread_(0) {
    269 }
    270 
    271 PresentThread* PresentThreadPool::NextThread() {
    272   base::AutoLock locked(lock_);
    273 
    274   next_thread_ = (next_thread_ + 1) % kNumPresentThreads;
    275   PresentThread* thread = present_threads_[next_thread_].get();
    276   if (!thread) {
    277     thread = new PresentThread(
    278         base::StringPrintf("PresentThread #%d", next_thread_).c_str(),
    279         adapter_luid_);
    280     thread->Start();
    281     present_threads_[next_thread_] = thread;
    282   }
    283 
    284   return thread;
    285 }
    286 
    287 void PresentThreadPool::SetAdapterLUID(uint64 adapter_luid) {
    288   base::AutoLock locked(lock_);
    289 
    290   adapter_luid_ = adapter_luid;
    291 
    292   for (int i = 0; i < kNumPresentThreads; ++i) {
    293     if (!present_threads_[i])
    294       continue;
    295 
    296     present_threads_[i]->message_loop()->PostTask(
    297         FROM_HERE,
    298         base::Bind(&PresentThread::SetAdapterLUID,
    299                    present_threads_[i],
    300                    adapter_luid));
    301   }
    302 }
    303 
    304 AcceleratedPresenterMap::AcceleratedPresenterMap() {
    305 }
    306 
    307 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::CreatePresenter(
    308     gfx::PluginWindowHandle window) {
    309   scoped_refptr<AcceleratedPresenter> presenter(
    310       new AcceleratedPresenter(window));
    311 
    312   base::AutoLock locked(lock_);
    313   DCHECK(presenters_.find(window) == presenters_.end());
    314   presenters_[window] = presenter.get();
    315 
    316   return presenter;
    317 }
    318 
    319 void AcceleratedPresenterMap::RemovePresenter(
    320     const scoped_refptr<AcceleratedPresenter>& presenter) {
    321   base::AutoLock locked(lock_);
    322   for (PresenterMap::iterator it = presenters_.begin();
    323       it != presenters_.end();
    324       ++it) {
    325     if (it->second == presenter.get()) {
    326       presenters_.erase(it);
    327       return;
    328     }
    329   }
    330 
    331   NOTREACHED();
    332 }
    333 
    334 scoped_refptr<AcceleratedPresenter> AcceleratedPresenterMap::GetPresenter(
    335     gfx::PluginWindowHandle window) {
    336   base::AutoLock locked(lock_);
    337   PresenterMap::iterator it = presenters_.find(window);
    338   if (it == presenters_.end())
    339     return scoped_refptr<AcceleratedPresenter>();
    340 
    341   return it->second;
    342 }
    343 
    344 void AcceleratedPresenterMap::ResetPresentThread(
    345     PresentThread* present_thread) {
    346   base::AutoLock locked(lock_);
    347 
    348   for (PresenterMap::iterator it = presenters_.begin();
    349       it != presenters_.end();
    350       ++it) {
    351     it->second->ResetPresentThread(present_thread);
    352   }
    353 }
    354 
    355 AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window)
    356     : present_thread_(g_present_thread_pool.Pointer()->NextThread()),
    357       window_(window),
    358       event_(false, false),
    359       hidden_(true),
    360       do_present_with_GDI_(DoAllShowPresentWithGDI() ||
    361                            DoFirstShowPresentWithGDI()),
    362       is_session_locked_(false) {
    363 }
    364 
    365 // static
    366 void AcceleratedPresenter::SetAdapterLUID(uint64 adapter_luid) {
    367   return g_present_thread_pool.Pointer()->SetAdapterLUID(adapter_luid);
    368 }
    369 
    370 
    371 // static
    372 scoped_refptr<AcceleratedPresenter> AcceleratedPresenter::GetForWindow(
    373     gfx::PluginWindowHandle window) {
    374   return g_accelerated_presenter_map.Pointer()->GetPresenter(window);
    375 }
    376 
    377 void AcceleratedPresenter::AsyncPresentAndAcknowledge(
    378     const gfx::Size& size,
    379     int64 surface_handle,
    380     const ui::LatencyInfo& latency_info,
    381     const CompletionTask& completion_task) {
    382   if (!surface_handle) {
    383     TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle",
    384                  "surface_handle", surface_handle);
    385     completion_task.Run(
    386         true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
    387     return;
    388   }
    389 
    390   present_thread_->message_loop()->PostTask(
    391       FROM_HERE,
    392       base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge,
    393                  this,
    394                  size,
    395                  surface_handle,
    396                  latency_info,
    397                  completion_task));
    398 }
    399 
    400 void AcceleratedPresenter::Present(HDC dc) {
    401   TRACE_EVENT0("gpu", "Present");
    402 
    403   base::AutoLock locked(*present_thread_->lock());
    404 
    405   // If invalidated, do nothing. The window is gone.
    406   if (!window_)
    407     return;
    408 
    409   // Suspended or nothing has ever been presented.
    410   if (!swap_chain_)
    411     return;
    412 
    413   PresentWithGDI(dc);
    414 }
    415 
    416 void AcceleratedPresenter::AsyncCopyTo(
    417     const gfx::Rect& requested_src_subrect,
    418     const gfx::Size& dst_size,
    419     const base::Callback<void(bool, const SkBitmap&)>& callback) {
    420   present_thread_->message_loop()->PostTask(
    421       FROM_HERE,
    422       base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge,
    423                  this,
    424                  requested_src_subrect,
    425                  dst_size,
    426                  base::MessageLoopProxy::current(),
    427                  callback));
    428 }
    429 
    430 void AcceleratedPresenter::AsyncCopyToVideoFrame(
    431     const gfx::Rect& requested_src_subrect,
    432     const scoped_refptr<media::VideoFrame>& target,
    433     const base::Callback<void(bool)>& callback) {
    434   present_thread_->message_loop()->PostTask(
    435       FROM_HERE,
    436       base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge,
    437                  this,
    438                  requested_src_subrect,
    439                  target,
    440                  base::MessageLoopProxy::current(),
    441                  callback));
    442 }
    443 
    444 void AcceleratedPresenter::DoCopyToAndAcknowledge(
    445     const gfx::Rect& src_subrect,
    446     const gfx::Size& dst_size,
    447     scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
    448     const base::Callback<void(bool, const SkBitmap&)>& callback) {
    449   SkBitmap target;
    450   bool result = DoCopyToARGB(src_subrect, dst_size, &target);
    451   if (!result)
    452     target.reset();
    453   callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target));
    454 }
    455 
    456 void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge(
    457     const gfx::Rect& src_subrect,
    458     const scoped_refptr<media::VideoFrame>& target,
    459     const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner,
    460     const base::Callback<void(bool)>& callback) {
    461 
    462   bool result = DoCopyToYUV(src_subrect, target);
    463   callback_runner->PostTask(FROM_HERE, base::Bind(callback, result));
    464 }
    465 
    466 bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect,
    467                                         const gfx::Size& dst_size,
    468                                         SkBitmap* bitmap) {
    469   TRACE_EVENT2(
    470       "gpu", "CopyTo",
    471       "width", dst_size.width(),
    472       "height", dst_size.height());
    473 
    474   base::AutoLock locked(*present_thread_->lock());
    475 
    476   if (!swap_chain_)
    477     return false;
    478 
    479   AcceleratedSurfaceTransformer* gpu_ops =
    480       present_thread_->surface_transformer();
    481 
    482   base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
    483   HRESULT hr = swap_chain_->GetBackBuffer(0,
    484                                           D3DBACKBUFFER_TYPE_MONO,
    485                                           back_buffer.Receive());
    486   if (FAILED(hr)) {
    487     LOG(ERROR) << "Failed to get back buffer";
    488     return false;
    489   }
    490 
    491   D3DSURFACE_DESC desc;
    492   hr = back_buffer->GetDesc(&desc);
    493   if (FAILED(hr)) {
    494     LOG(ERROR) << "Failed to get buffer description";
    495     return false;
    496   }
    497 
    498   const gfx::Size back_buffer_size(desc.Width, desc.Height);
    499   if (back_buffer_size.IsEmpty())
    500     return false;
    501 
    502   // With window resizing, it's possible that the back buffer is smaller than
    503   // the requested src subset. Clip to the actual back buffer.
    504   gfx::Rect src_subrect = requested_src_subrect;
    505   src_subrect.Intersect(gfx::Rect(back_buffer_size));
    506   base::win::ScopedComPtr<IDirect3DSurface9> final_surface;
    507   {
    508     if (!d3d_utils::CreateOrReuseLockableSurface(present_thread_->device(),
    509                                                  dst_size,
    510                                                  &final_surface)) {
    511       LOG(ERROR) << "Failed to create temporary lockable surface";
    512       return false;
    513     }
    514   }
    515 
    516   {
    517     // Let the surface transformer start the resize into |final_surface|.
    518     TRACE_EVENT0("gpu", "ResizeBilinear");
    519     if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect,
    520                                  final_surface, gfx::Rect(dst_size))) {
    521       LOG(ERROR) << "Failed to resize bilinear";
    522       return false;
    523     }
    524   }
    525 
    526   bitmap->setConfig(SkBitmap::kARGB_8888_Config, dst_size.width(),
    527                     dst_size.height(), 0, kOpaque_SkAlphaType);
    528   if (!bitmap->allocPixels())
    529     return false;
    530 
    531   // Copy |final_surface| to |bitmap|. This is always a synchronous operation.
    532   return gpu_ops->ReadFast(final_surface,
    533                            reinterpret_cast<uint8*>(bitmap->getPixels()),
    534                            bitmap->width() * bitmap->bytesPerPixel(),
    535                            bitmap->height(),
    536                            static_cast<int>(bitmap->rowBytes()));
    537 }
    538 
    539 bool AcceleratedPresenter::DoCopyToYUV(
    540     const gfx::Rect& requested_src_subrect,
    541     const scoped_refptr<media::VideoFrame>& frame) {
    542   gfx::Size dst_size = frame->coded_size();
    543   TRACE_EVENT2(
    544       "gpu", "CopyToYUV",
    545       "width", dst_size.width(),
    546       "height", dst_size.height());
    547 
    548   base::AutoLock locked(*present_thread_->lock());
    549 
    550   if (!swap_chain_)
    551     return false;
    552 
    553   AcceleratedSurfaceTransformer* gpu_ops =
    554       present_thread_->surface_transformer();
    555 
    556   base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
    557   HRESULT hr = swap_chain_->GetBackBuffer(0,
    558                                           D3DBACKBUFFER_TYPE_MONO,
    559                                           back_buffer.Receive());
    560   if (FAILED(hr))
    561     return false;
    562 
    563   D3DSURFACE_DESC desc;
    564   hr = back_buffer->GetDesc(&desc);
    565   if (FAILED(hr))
    566     return false;
    567 
    568   const gfx::Size back_buffer_size(desc.Width, desc.Height);
    569   if (back_buffer_size.IsEmpty())
    570     return false;
    571 
    572   // With window resizing, it's possible that the back buffer is smaller than
    573   // the requested src subset. Clip to the actual back buffer.
    574   gfx::Rect src_subrect = requested_src_subrect;
    575   src_subrect.Intersect(gfx::Rect(back_buffer_size));
    576   if (src_subrect.IsEmpty())
    577     return false;
    578 
    579   base::win::ScopedComPtr<IDirect3DSurface9> resized;
    580   base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture;
    581   if (!gpu_ops->GetIntermediateTexture(dst_size,
    582                                        resized_as_texture.Receive(),
    583                                        resized.Receive())) {
    584     return false;
    585   }
    586 
    587   // Shrink the source to fit entirely in the destination while preserving
    588   // aspect ratio. Fill in any margin with black.
    589   // TODO(nick): It would be more efficient all around to implement
    590   // letterboxing as a memset() on the dst.
    591   gfx::Rect letterbox = media::ComputeLetterboxRegion(gfx::Rect(dst_size),
    592                                                       src_subrect.size());
    593   if (letterbox != gfx::Rect(dst_size)) {
    594     TRACE_EVENT0("gpu", "Letterbox");
    595     present_thread_->device()->ColorFill(resized, NULL, 0xFF000000);
    596   }
    597 
    598   {
    599     TRACE_EVENT0("gpu", "ResizeBilinear");
    600     if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox))
    601       return false;
    602   }
    603 
    604   base::win::ScopedComPtr<IDirect3DSurface9> y, u, v;
    605   {
    606     TRACE_EVENT0("gpu", "TransformRGBToYV12");
    607     if (!gpu_ops->TransformRGBToYV12(resized_as_texture,
    608                                      dst_size,
    609                                      y.Receive(), u.Receive(), v.Receive())) {
    610       return false;
    611     }
    612   }
    613 
    614   if (!CopyPlane(gpu_ops, y, frame, media::VideoFrame::kYPlane))
    615     return false;
    616   if (!CopyPlane(gpu_ops, u, frame, media::VideoFrame::kUPlane))
    617     return false;
    618   if (!CopyPlane(gpu_ops, v, frame, media::VideoFrame::kVPlane))
    619     return false;
    620   return true;
    621 }
    622 
    623 void AcceleratedPresenter::Suspend() {
    624   present_thread_->message_loop()->PostTask(
    625       FROM_HERE,
    626       base::Bind(&AcceleratedPresenter::DoSuspend,
    627                  this));
    628 }
    629 
    630 void AcceleratedPresenter::WasHidden() {
    631   base::AutoLock locked(*present_thread_->lock());
    632   hidden_ = true;
    633 }
    634 
    635 void AcceleratedPresenter::ReleaseSurface() {
    636   present_thread_->message_loop()->PostTask(
    637       FROM_HERE,
    638       base::Bind(&AcceleratedPresenter::DoReleaseSurface,
    639                  this));
    640 }
    641 
    642 void AcceleratedPresenter::SetIsSessionLocked(bool locked) {
    643   is_session_locked_ = locked;
    644 }
    645 
    646 void AcceleratedPresenter::Invalidate() {
    647   // Make any pending or future presentation tasks do nothing. Once the last
    648   // last pending task has been ignored, the reference count on the presenter
    649   // will go to zero and the presenter, and potentially also the present thread
    650   // it has a reference count on, will be destroyed.
    651   base::AutoLock locked(*present_thread_->lock());
    652   window_ = NULL;
    653 }
    654 
    655 void AcceleratedPresenter::ResetPresentThread(
    656     PresentThread* present_thread) {
    657   TRACE_EVENT0("gpu", "ResetPresentThread");
    658 
    659   // present_thread_ can be accessed without the lock because it is immutable.
    660   if (present_thread_ != present_thread)
    661     return;
    662 
    663   present_thread_->lock()->AssertAcquired();
    664 
    665   source_texture_ = NULL;
    666   swap_chain_ = NULL;
    667   quantized_size_ = gfx::Size();
    668 }
    669 
    670 AcceleratedPresenter::~AcceleratedPresenter() {
    671 }
    672 
    673 bool AcceleratedPresenter::IsSwapChainInitialized() const {
    674   base::AutoLock locked(*present_thread_->lock());
    675 
    676   return !!swap_chain_;
    677 }
    678 
    679 void AcceleratedPresenter::DoPresentAndAcknowledge(
    680     const gfx::Size& size,
    681     int64 surface_handle,
    682     const ui::LatencyInfo& latency_info,
    683     const CompletionTask& completion_task) {
    684   TRACE_EVENT2(
    685       "gpu", "DoPresentAndAcknowledge",
    686       "width", size.width(),
    687       "height", size.height());
    688 
    689   HRESULT hr;
    690 
    691   base::AutoLock locked(*present_thread_->lock());
    692 
    693   latency_info_.MergeWith(latency_info);
    694 
    695   // Initialize the device lazily since calling Direct3D can crash bots.
    696   present_thread_->InitDevice();
    697 
    698   if (!present_thread_->device()) {
    699     completion_task.Run(
    700         false, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo());
    701     TRACE_EVENT0("gpu", "EarlyOut_NoDevice");
    702     return;
    703   }
    704 
    705   // Ensure the task is acknowledged on early out after this point.
    706   base::ScopedClosureRunner scoped_completion_runner(
    707       base::Bind(completion_task,
    708                  true,
    709                  base::TimeTicks(),
    710                  base::TimeDelta(),
    711                  ui::LatencyInfo()));
    712 
    713   // If invalidated, do nothing, the window is gone.
    714   if (!window_) {
    715     TRACE_EVENT0("gpu", "EarlyOut_NoWindow");
    716     return;
    717   }
    718 
    719 #if !defined(USE_AURA)
    720   // If the window is a different size than the swap chain that is being
    721   // presented then drop the frame.
    722   gfx::Size window_size = GetWindowSize();
    723   bool size_mismatch = size != window_size;
    724   if (gfx::IsInHighDPIMode()) {
    725     // Check if the size mismatch is within allowable round off or truncation
    726     // error.
    727     gfx::Size dip_size = gfx::win::ScreenToDIPSize(window_size);
    728     gfx::Size pixel_size = gfx::win::DIPToScreenSize(dip_size);
    729     size_mismatch = abs(window_size.width() - size.width()) >
    730         abs(window_size.width() - pixel_size.width()) ||
    731         abs(window_size.height() - size.height()) >
    732         abs(window_size.height() - pixel_size.height());
    733   }
    734   if (hidden_ && size_mismatch) {
    735     TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize",
    736                  "backwidth", size.width(), "backheight", size.height());
    737     TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2",
    738                  "windowwidth", window_size.width(),
    739                  "windowheight", window_size.height());
    740     return;
    741   }
    742 #endif
    743   // Round up size so the swap chain is not continuously resized with the
    744   // surface, which could lead to memory fragmentation.
    745   const int kRound = 64;
    746   gfx::Size quantized_size(
    747       std::max(1, (size.width() + kRound - 1) / kRound * kRound),
    748       std::max(1, (size.height() + kRound - 1) / kRound * kRound));
    749 
    750   // Ensure the swap chain exists and is the same size (rounded up) as the
    751   // surface to be presented.
    752   if (!swap_chain_ || quantized_size_ != quantized_size) {
    753     TRACE_EVENT0("gpu", "CreateAdditionalSwapChain");
    754     quantized_size_ = quantized_size;
    755 
    756     D3DPRESENT_PARAMETERS parameters = { 0 };
    757     parameters.BackBufferWidth = quantized_size.width();
    758     parameters.BackBufferHeight = quantized_size.height();
    759     parameters.BackBufferCount = 1;
    760     parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
    761     parameters.hDeviceWindow = window_;
    762     parameters.Windowed = TRUE;
    763     parameters.Flags = 0;
    764     parameters.PresentationInterval = GetPresentationInterval();
    765     parameters.SwapEffect = D3DSWAPEFFECT_COPY;
    766 
    767     swap_chain_ = NULL;
    768     HRESULT hr = present_thread_->device()->CreateAdditionalSwapChain(
    769         &parameters,
    770         swap_chain_.Receive());
    771     if (FAILED(hr)) {
    772       LOG(ERROR) << "Failed to create swap chain "
    773                  << quantized_size.width() << " x " <<quantized_size.height();
    774       return;
    775     }
    776   }
    777 
    778   if (!source_texture_.get()) {
    779     TRACE_EVENT0("gpu", "OpenSharedTexture");
    780     if (!d3d_utils::OpenSharedTexture(present_thread_->device(),
    781                                       surface_handle,
    782                                       size,
    783                                       source_texture_.Receive())) {
    784       LOG(ERROR) << "Failed to open shared texture";
    785       return;
    786     }
    787   }
    788 
    789   base::win::ScopedComPtr<IDirect3DSurface9> source_surface;
    790   hr = source_texture_->GetSurfaceLevel(0, source_surface.Receive());
    791   if (FAILED(hr)) {
    792     TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel");
    793     LOG(ERROR) << "Failed to get source surface";
    794     return;
    795   }
    796 
    797   base::win::ScopedComPtr<IDirect3DSurface9> dest_surface;
    798   hr = swap_chain_->GetBackBuffer(0,
    799                                   D3DBACKBUFFER_TYPE_MONO,
    800                                   dest_surface.Receive());
    801   if (FAILED(hr)) {
    802     TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer");
    803     LOG(ERROR) << "Failed to get back buffer";
    804     return;
    805   }
    806 
    807   RECT rect = {
    808     0, 0,
    809     size.width(), size.height()
    810   };
    811 
    812   {
    813     TRACE_EVENT0("gpu", "Copy");
    814 
    815     // Copy while flipping the source texture on the vertical axis.
    816     bool result = present_thread_->surface_transformer()->CopyInverted(
    817         source_texture_, dest_surface, size);
    818     if (!result) {
    819       LOG(ERROR) << "Failed to copy shared texture";
    820       return;
    821     }
    822   }
    823 
    824   hr = present_thread_->query()->Issue(D3DISSUE_END);
    825   if (FAILED(hr)) {
    826     LOG(ERROR) << "Failed to issue query";
    827     return;
    828   }
    829 
    830   present_size_ = size;
    831 
    832   // If it is expected that Direct3D cannot be used reliably because the window
    833   // is resizing, fall back to presenting with GDI.
    834   if (CheckDirect3DWillWork()) {
    835     TRACE_EVENT0("gpu", "PresentD3D");
    836 
    837     hr = swap_chain_->Present(&rect, &rect, window_, NULL, 0);
    838 
    839     if (FAILED(hr)) {
    840       if (present_thread_->IsDeviceLost())
    841         present_thread_->ResetDevice();
    842       return;
    843     }
    844   } else {
    845     HDC dc = GetDC(window_);
    846     PresentWithGDI(dc);
    847     ReleaseDC(window_, dc);
    848   }
    849 
    850   latency_info_.AddLatencyNumber(
    851       ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
    852 
    853   hidden_ = false;
    854 
    855   D3DDISPLAYMODE display_mode;
    856   hr = present_thread_->device()->GetDisplayMode(0, &display_mode);
    857   if (FAILED(hr)) {
    858     LOG(ERROR) << "Failed to get display mode";
    859     return;
    860   }
    861 
    862   D3DRASTER_STATUS raster_status;
    863   hr = swap_chain_->GetRasterStatus(&raster_status);
    864   if (FAILED(hr)) {
    865     LOG(ERROR) << "Failed to get raster status";
    866     return;
    867   }
    868 
    869   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.AcceleratedSurfaceRefreshRate",
    870                               display_mode.RefreshRate, 0, 121, 122);
    871 
    872   // I can't figure out how to determine how many scanlines are in the
    873   // vertical blank so clamp it such that scanline / height <= 1.
    874   int clamped_scanline = std::min(raster_status.ScanLine, display_mode.Height);
    875 
    876   // The Internet says that on some GPUs, the scanline is not available
    877   // while in the vertical blank.
    878   if (raster_status.InVBlank)
    879     clamped_scanline = display_mode.Height;
    880 
    881   // Figure out approximately how far back in time the last vsync was based on
    882   // the ratio of the raster scanline to the display height.
    883   base::TimeTicks last_vsync_time;
    884   base::TimeDelta refresh_period;
    885 
    886   if (display_mode.Height) {
    887     refresh_period = base::TimeDelta::FromMicroseconds(
    888         1000000 / display_mode.RefreshRate);
    889     // If FrameTime is not high resolution, we use a timebase of zero to avoid
    890     // introducing jitter into our frame start times.
    891     if (gfx::FrameTime::TimestampsAreHighRes()) {
    892       base::TimeTicks current_time = gfx::FrameTime::Now();
    893       last_vsync_time = current_time -
    894         base::TimeDelta::FromMilliseconds((clamped_scanline * 1000) /
    895             (display_mode.RefreshRate * display_mode.Height));
    896     }
    897   }
    898 
    899   // Wait for the StretchRect to complete before notifying the GPU process
    900   // that it is safe to write to its backing store again.
    901   {
    902     TRACE_EVENT0("gpu", "spin");
    903 
    904     do {
    905       hr = present_thread_->query()->GetData(NULL, 0, D3DGETDATA_FLUSH);
    906       if (hr == S_FALSE) {
    907         Sleep(1);
    908 
    909         if (present_thread_->IsDeviceLost()) {
    910           present_thread_->ResetDevice();
    911           return;
    912         }
    913       }
    914     } while (hr == S_FALSE);
    915   }
    916 
    917   scoped_completion_runner.Release();
    918   completion_task.Run(true, last_vsync_time, refresh_period, latency_info_);
    919   latency_info_.Clear();
    920 }
    921 
    922 void AcceleratedPresenter::DoSuspend() {
    923   base::AutoLock locked(*present_thread_->lock());
    924   swap_chain_ = NULL;
    925 }
    926 
    927 void AcceleratedPresenter::DoReleaseSurface() {
    928   base::AutoLock locked(*present_thread_->lock());
    929   present_thread_->InitDevice();
    930   source_texture_.Release();
    931 }
    932 
    933 void AcceleratedPresenter::PresentWithGDI(HDC dc) {
    934   TRACE_EVENT0("gpu", "PresentWithGDI");
    935 
    936   if (!present_thread_->device()) {
    937     LOG(ERROR) << "No device";
    938     return;
    939   }
    940 
    941   if (!swap_chain_) {
    942     LOG(ERROR) << "No swap chain";
    943     return;
    944   }
    945 
    946   base::win::ScopedComPtr<IDirect3DTexture9> system_texture;
    947   {
    948     TRACE_EVENT0("gpu", "CreateSystemTexture");
    949     HRESULT hr = present_thread_->device()->CreateTexture(
    950         quantized_size_.width(),
    951         quantized_size_.height(),
    952         1,
    953         0,
    954         D3DFMT_A8R8G8B8,
    955         D3DPOOL_SYSTEMMEM,
    956         system_texture.Receive(),
    957         NULL);
    958     if (FAILED(hr)) {
    959       LOG(ERROR) << "Failed to create system memory texture";
    960       return;
    961     }
    962   }
    963 
    964   base::win::ScopedComPtr<IDirect3DSurface9> system_surface;
    965   HRESULT hr = system_texture->GetSurfaceLevel(0, system_surface.Receive());
    966   DCHECK(SUCCEEDED(hr));
    967 
    968   base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
    969   hr = swap_chain_->GetBackBuffer(0,
    970                                   D3DBACKBUFFER_TYPE_MONO,
    971                                   back_buffer.Receive());
    972   DCHECK(SUCCEEDED(hr));
    973 
    974   {
    975     TRACE_EVENT0("gpu", "GetRenderTargetData");
    976     hr = present_thread_->device()->GetRenderTargetData(back_buffer,
    977                                                         system_surface);
    978 
    979     if (FAILED(hr)) {
    980       if (present_thread_->IsDeviceLost()) {
    981         present_thread_->message_loop()->PostTask(
    982             FROM_HERE,
    983             base::Bind(&PresentThread::LockAndResetDevice, present_thread_));
    984       }
    985       return;
    986     }
    987 
    988     DCHECK(SUCCEEDED(hr));
    989   }
    990 
    991   D3DLOCKED_RECT locked_surface;
    992   hr = system_surface->LockRect(&locked_surface, NULL, D3DLOCK_READONLY);
    993   DCHECK(SUCCEEDED(hr));
    994 
    995   BITMAPINFO bitmap_info = {
    996     {
    997       sizeof(BITMAPINFOHEADER),
    998       quantized_size_.width(),
    999       -quantized_size_.height(),
   1000       1,  // planes
   1001       32,  // bitcount
   1002       BI_RGB
   1003     },
   1004     {
   1005       {0, 0, 0, 0}
   1006     }
   1007   };
   1008 
   1009   {
   1010     TRACE_EVENT0("gpu", "StretchDIBits");
   1011     StretchDIBits(dc,
   1012                   0, 0,
   1013                   present_size_.width(),
   1014                   present_size_.height(),
   1015                   0, 0,
   1016                   present_size_.width(),
   1017                   present_size_.height(),
   1018                   locked_surface.pBits,
   1019                   &bitmap_info,
   1020                   DIB_RGB_COLORS,
   1021                   SRCCOPY);
   1022   }
   1023 
   1024   system_surface->UnlockRect();
   1025 }
   1026 
   1027 gfx::Size AcceleratedPresenter::GetWindowSize() {
   1028   RECT rect;
   1029   GetClientRect(window_, &rect);
   1030   return gfx::Rect(rect).size();
   1031 }
   1032 
   1033 bool AcceleratedPresenter::CheckDirect3DWillWork() {
   1034   // On a composited desktop, when the screen saver or logon screen are
   1035   // active, D3D presents never make it to the window but GDI presents
   1036   // do. If the session is locked GDI presents can be avoided since
   1037   // the window gets a message on unlock and forces a repaint.
   1038   if (!is_session_locked_ && ui::win::IsAeroGlassEnabled()) {
   1039     // Failure to open the input desktop is a sign of running with a non-default
   1040     // desktop.
   1041     HDESK input_desktop = ::OpenInputDesktop(0, 0, GENERIC_READ);
   1042     if (!input_desktop)
   1043       return false;
   1044     ::CloseDesktop(input_desktop);
   1045   }
   1046 
   1047   gfx::Size window_size = GetWindowSize();
   1048   if (window_size != last_window_size_ && last_window_size_.GetArea() != 0) {
   1049     last_window_size_ = window_size;
   1050     last_window_resize_time_ = base::Time::Now();
   1051     return false;
   1052   }
   1053 
   1054   if (do_present_with_GDI_ && hidden_) {
   1055     if (DoFirstShowPresentWithGDI())
   1056       do_present_with_GDI_ = false;
   1057 
   1058     return false;
   1059   }
   1060 
   1061   return base::Time::Now() - last_window_resize_time_ >
   1062       base::TimeDelta::FromMilliseconds(100);
   1063 }
   1064 
   1065 AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window)
   1066     : presenter_(g_accelerated_presenter_map.Pointer()->CreatePresenter(
   1067           window)) {
   1068 }
   1069 
   1070 AcceleratedSurface::~AcceleratedSurface() {
   1071   g_accelerated_presenter_map.Pointer()->RemovePresenter(presenter_);
   1072   presenter_->Invalidate();
   1073 }
   1074 
   1075 void AcceleratedSurface::Present(HDC dc) {
   1076   presenter_->Present(dc);
   1077 }
   1078 
   1079 bool AcceleratedSurface::IsReadyForCopy() const {
   1080   return !!presenter_ && presenter_->IsSwapChainInitialized();
   1081 }
   1082 
   1083 
   1084 void AcceleratedSurface::AsyncCopyTo(
   1085     const gfx::Rect& src_subrect,
   1086     const gfx::Size& dst_size,
   1087     const base::Callback<void(bool, const SkBitmap&)>& callback) {
   1088   presenter_->AsyncCopyTo(src_subrect, dst_size, callback);
   1089 }
   1090 
   1091 void AcceleratedSurface::AsyncCopyToVideoFrame(
   1092     const gfx::Rect& src_subrect,
   1093     const scoped_refptr<media::VideoFrame>& target,
   1094     const base::Callback<void(bool)>& callback) {
   1095   presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback);
   1096 }
   1097 
   1098 void AcceleratedSurface::Suspend() {
   1099   presenter_->Suspend();
   1100 }
   1101 
   1102 void AcceleratedSurface::WasHidden() {
   1103   presenter_->WasHidden();
   1104 }
   1105 
   1106 void AcceleratedSurface::SetIsSessionLocked(bool locked) {
   1107   presenter_->SetIsSessionLocked(locked);
   1108 }
   1109