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.
5 #include "ui/surface/accelerated_surface_win.h"
11 #include "accelerated_surface_win_hlsl_compiled.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/debug/trace_event.h"
17 #include "base/file_path.h"
18 #include "base/lazy_instance.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/message_loop_proxy.h"
21 #include "base/scoped_native_library.h"
22 #include "base/string_number_conversions.h"
23 #include "base/stringprintf.h"
24 #include "base/synchronization/waitable_event.h"
25 #include "base/threading/thread.h"
26 #include "base/threading/thread_restrictions.h"
27 #include "base/time.h"
28 #include "base/tracked_objects.h"
29 #include "base/win/wrapped_window_proc.h"
30 #include "ui/base/win/hwnd_util.h"
31 #include "ui/gfx/rect.h"
32 #include "ui/gl/gl_switches.h"
35 using ui_surface::AcceleratedSurfaceWinHLSL::kVsOneTexture
;
36 using ui_surface::AcceleratedSurfaceWinHLSL::kPsOneTexture
;
41 typedef HRESULT (WINAPI
*Direct3DCreate9ExFunc
)(UINT sdk_version
,
44 const wchar_t kD3D9ModuleName
[] = L
"d3d9.dll";
45 const char kCreate3D9DeviceExName
[] = "Direct3DCreate9Ex";
47 const char kUseOcclusionQuery
[] = "use-occlusion-query";
54 const static D3DVERTEXELEMENT9 g_vertexElements
[] = {
55 { 0, 0, D3DDECLTYPE_FLOAT4
, 0, D3DDECLUSAGE_POSITION
, 0 },
56 { 0, 16, D3DDECLTYPE_FLOAT2
, 0, D3DDECLUSAGE_TEXCOORD
, 0 },
60 UINT
GetPresentationInterval() {
61 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync
))
62 return D3DPRESENT_INTERVAL_IMMEDIATE
;
64 return D3DPRESENT_INTERVAL_ONE
;
67 bool UsingOcclusionQuery() {
68 return CommandLine::ForCurrentProcess()->HasSwitch(kUseOcclusionQuery
);
71 // Calculate the number necessary to transform |src_subrect| into |dst_size|
72 // by repeating downsampling of the image of |src_subrect| by a factor no more
74 int GetResampleCount(const gfx::Rect
& src_subrect
,
75 const gfx::Size
& dst_size
,
76 const gfx::Size
& back_buffer_size
) {
77 // At least one copy is required, since the back buffer itself is not
79 int min_resample_count
= 1;
81 int width
= src_subrect
.width();
82 while (width
> dst_size
.width()) {
87 int height
= src_subrect
.height();
88 while (height
> dst_size
.height()) {
92 return std::max(std::max(width_count
, height_count
),
96 // Returns half the size of |size| no smaller than |min_size|.
97 gfx::Size
GetHalfSizeNoLessThan(const gfx::Size
& size
,
98 const gfx::Size
& min_size
) {
99 return gfx::Size(std::max(min_size
.width(), size
.width() / 2),
100 std::max(min_size
.height(), size
.height() / 2));
103 bool CreateTemporarySurface(IDirect3DDevice9
* device
,
104 const gfx::Size
& size
,
105 IDirect3DSurface9
** surface
) {
106 HRESULT hr
= device
->CreateRenderTarget(
115 return SUCCEEDED(hr
);
120 // A PresentThread is a thread that is dedicated to presenting surfaces to a
121 // window. It owns a Direct3D device and a Direct3D query for this purpose.
122 class PresentThread
: public base::Thread
,
123 public base::RefCountedThreadSafe
<PresentThread
> {
125 explicit PresentThread(const char* name
);
127 IDirect3DDevice9Ex
* device() { return device_
.get(); }
128 IDirect3DQuery9
* query() { return query_
.get(); }
135 virtual void CleanUp();
138 friend class base::RefCountedThreadSafe
<PresentThread
>;
142 base::ScopedNativeLibrary d3d_module_
;
143 base::win::ScopedComPtr
<IDirect3DDevice9Ex
> device_
;
145 // This query is used to wait until a certain amount of progress has been
146 // made by the GPU and it is safe for the producer to modify its shared
148 base::win::ScopedComPtr
<IDirect3DQuery9
> query_
;
150 DISALLOW_COPY_AND_ASSIGN(PresentThread
);
153 // There is a fixed sized pool of PresentThreads and therefore the maximum
154 // number of Direct3D devices owned by those threads is bounded.
155 class PresentThreadPool
{
157 static const int kNumPresentThreads
= 4;
160 PresentThread
* NextThread();
164 scoped_refptr
<PresentThread
> present_threads_
[kNumPresentThreads
];
166 DISALLOW_COPY_AND_ASSIGN(PresentThreadPool
);
169 // A thread safe map of presenters by surface ID that returns presenters via
170 // a scoped_refptr to keep them alive while they are referenced.
171 class AcceleratedPresenterMap
{
173 AcceleratedPresenterMap();
174 scoped_refptr
<AcceleratedPresenter
> CreatePresenter(
175 gfx::PluginWindowHandle window
);
176 void RemovePresenter(const scoped_refptr
<AcceleratedPresenter
>& presenter
);
177 scoped_refptr
<AcceleratedPresenter
> GetPresenter(
178 gfx::PluginWindowHandle window
);
182 typedef std::map
<gfx::PluginWindowHandle
, AcceleratedPresenter
*> PresenterMap
;
183 PresenterMap presenters_
;
184 DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenterMap
);
187 base::LazyInstance
<PresentThreadPool
>
188 g_present_thread_pool
= LAZY_INSTANCE_INITIALIZER
;
190 base::LazyInstance
<AcceleratedPresenterMap
>
191 g_accelerated_presenter_map
= LAZY_INSTANCE_INITIALIZER
;
193 PresentThread::PresentThread(const char* name
) : base::Thread(name
) {
196 void PresentThread::InitDevice() {
200 TRACE_EVENT0("gpu", "PresentThread::Init");
201 d3d_module_
.Reset(base::LoadNativeLibrary(FilePath(kD3D9ModuleName
), NULL
));
205 void PresentThread::ResetDevice() {
206 TRACE_EVENT0("gpu", "PresentThread::ResetDevice");
208 // This will crash some Intel drivers but we can't render anything without
209 // reseting the device, which would be disappointing.
213 Direct3DCreate9ExFunc create_func
= reinterpret_cast<Direct3DCreate9ExFunc
>(
214 d3d_module_
.GetFunctionPointer(kCreate3D9DeviceExName
));
218 base::win::ScopedComPtr
<IDirect3D9Ex
> d3d
;
219 HRESULT hr
= create_func(D3D_SDK_VERSION
, d3d
.Receive());
223 // Any old window will do to create the device. In practice the window to
224 // present to is an argument to IDirect3DDevice9::Present.
225 HWND window
= GetShellWindow();
227 D3DPRESENT_PARAMETERS parameters
= { 0 };
228 parameters
.BackBufferWidth
= 1;
229 parameters
.BackBufferHeight
= 1;
230 parameters
.BackBufferCount
= 1;
231 parameters
.BackBufferFormat
= D3DFMT_A8R8G8B8
;
232 parameters
.hDeviceWindow
= window
;
233 parameters
.Windowed
= TRUE
;
234 parameters
.Flags
= 0;
235 parameters
.PresentationInterval
= GetPresentationInterval();
236 parameters
.SwapEffect
= D3DSWAPEFFECT_COPY
;
238 hr
= d3d
->CreateDeviceEx(
242 D3DCREATE_FPU_PRESERVE
| D3DCREATE_SOFTWARE_VERTEXPROCESSING
|
243 D3DCREATE_DISABLE_PSGP_THREADING
| D3DCREATE_MULTITHREADED
,
250 if (UsingOcclusionQuery()) {
251 hr
= device_
->CreateQuery(D3DQUERYTYPE_OCCLUSION
, query_
.Receive());
257 hr
= device_
->CreateQuery(D3DQUERYTYPE_EVENT
, query_
.Receive());
264 base::win::ScopedComPtr
<IDirect3DVertexShader9
> vertex_shader
;
265 hr
= device_
->CreateVertexShader(
266 reinterpret_cast<const DWORD
*>(kVsOneTexture
),
267 vertex_shader
.Receive());
274 device_
->SetVertexShader(vertex_shader
);
276 base::win::ScopedComPtr
<IDirect3DPixelShader9
> pixel_shader
;
277 hr
= device_
->CreatePixelShader(
278 reinterpret_cast<const DWORD
*>(kPsOneTexture
),
279 pixel_shader
.Receive());
287 device_
->SetPixelShader(pixel_shader
);
289 base::win::ScopedComPtr
<IDirect3DVertexDeclaration9
> vertex_declaration
;
290 hr
= device_
->CreateVertexDeclaration(g_vertexElements
,
291 vertex_declaration
.Receive());
298 device_
->SetVertexDeclaration(vertex_declaration
);
301 void PresentThread::Init() {
302 TRACE_EVENT0("gpu", "Initialize thread");
305 void PresentThread::CleanUp() {
306 // The D3D device and query are leaked because destroying the associated D3D
307 // query crashes some Intel drivers.
312 PresentThread::~PresentThread() {
316 PresentThreadPool::PresentThreadPool() : next_thread_(0) {
319 PresentThread
* PresentThreadPool::NextThread() {
320 next_thread_
= (next_thread_
+ 1) % kNumPresentThreads
;
321 PresentThread
* thread
= present_threads_
[next_thread_
].get();
323 thread
= new PresentThread(
324 base::StringPrintf("PresentThread #%d", next_thread_
).c_str());
326 present_threads_
[next_thread_
] = thread
;
332 AcceleratedPresenterMap::AcceleratedPresenterMap() {
335 scoped_refptr
<AcceleratedPresenter
> AcceleratedPresenterMap::CreatePresenter(
336 gfx::PluginWindowHandle window
) {
337 scoped_refptr
<AcceleratedPresenter
> presenter(
338 new AcceleratedPresenter(window
));
340 base::AutoLock
locked(lock_
);
341 DCHECK(presenters_
.find(window
) == presenters_
.end());
342 presenters_
[window
] = presenter
.get();
347 void AcceleratedPresenterMap::RemovePresenter(
348 const scoped_refptr
<AcceleratedPresenter
>& presenter
) {
349 base::AutoLock
locked(lock_
);
350 for (PresenterMap::iterator it
= presenters_
.begin();
351 it
!= presenters_
.end();
353 if (it
->second
== presenter
.get()) {
354 presenters_
.erase(it
);
362 scoped_refptr
<AcceleratedPresenter
> AcceleratedPresenterMap::GetPresenter(
363 gfx::PluginWindowHandle window
) {
364 base::AutoLock
locked(lock_
);
366 #if defined(USE_AURA)
368 return presenters_
.begin()->second
;
371 PresenterMap::iterator it
= presenters_
.find(window
);
372 if (it
== presenters_
.end())
373 return scoped_refptr
<AcceleratedPresenter
>();
378 AcceleratedPresenter::AcceleratedPresenter(gfx::PluginWindowHandle window
)
379 : present_thread_(g_present_thread_pool
.Pointer()->NextThread()),
381 event_(false, false),
385 scoped_refptr
<AcceleratedPresenter
> AcceleratedPresenter::GetForWindow(
386 gfx::PluginWindowHandle window
) {
387 return g_accelerated_presenter_map
.Pointer()->GetPresenter(window
);
390 void AcceleratedPresenter::AsyncPresentAndAcknowledge(
391 const gfx::Size
& size
,
392 int64 surface_handle
,
393 const CompletionTask
& completion_task
) {
394 if (!surface_handle
) {
395 TRACE_EVENT1("gpu", "EarlyOut_ZeroSurfaceHandle",
396 "surface_handle", surface_handle
);
397 completion_task
.Run(true, base::TimeTicks(), base::TimeDelta());
401 present_thread_
->message_loop()->PostTask(
403 base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge
,
410 void AcceleratedPresenter::Present(HDC dc
) {
411 TRACE_EVENT0("gpu", "Present");
413 base::AutoLock
locked(lock_
);
415 // If invalidated, do nothing. The window is gone.
419 // Suspended or nothing has ever been presented.
426 void AcceleratedPresenter::AsyncCopyTo(
427 const gfx::Rect
& requested_src_subrect
,
428 const gfx::Size
& dst_size
,
430 const base::Callback
<void(bool)>& callback
) {
431 present_thread_
->message_loop()->PostTask(
433 base::Bind(&AcceleratedPresenter::DoCopyToAndAcknowledge
,
435 requested_src_subrect
,
438 base::MessageLoopProxy::current(),
442 void AcceleratedPresenter::DoCopyToAndAcknowledge(
443 const gfx::Rect
& src_subrect
,
444 const gfx::Size
& dst_size
,
446 scoped_refptr
<base::SingleThreadTaskRunner
> callback_runner
,
447 const base::Callback
<void(bool)>& callback
) {
449 bool result
= DoCopyTo(src_subrect
, dst_size
, buf
);
450 callback_runner
->PostTask(
452 base::Bind(callback
, result
));
455 bool AcceleratedPresenter::DoCopyTo(const gfx::Rect
& requested_src_subrect
,
456 const gfx::Size
& dst_size
,
460 "width", dst_size
.width(),
461 "height", dst_size
.height());
463 base::AutoLock
locked(lock_
);
465 TRACE_EVENT0("gpu", "CopyTo_locked");
470 base::win::ScopedComPtr
<IDirect3DSurface9
> back_buffer
;
471 HRESULT hr
= swap_chain_
->GetBackBuffer(0,
472 D3DBACKBUFFER_TYPE_MONO
,
473 back_buffer
.Receive());
477 D3DSURFACE_DESC desc
;
478 hr
= back_buffer
->GetDesc(&desc
);
482 const gfx::Size
back_buffer_size(desc
.Width
, desc
.Height
);
483 if (back_buffer_size
.IsEmpty())
486 // With window resizing, it's possible that the back buffer is smaller than
487 // the requested src subset. Clip to the actual back buffer.
488 gfx::Rect src_subrect
= requested_src_subrect
;
489 src_subrect
.Intersect(gfx::Rect(back_buffer_size
));
491 // Set up intermediate buffers needed for downsampling.
492 const int resample_count
=
493 GetResampleCount(src_subrect
, dst_size
, back_buffer_size
);
494 base::win::ScopedComPtr
<IDirect3DSurface9
> final_surface
;
495 base::win::ScopedComPtr
<IDirect3DSurface9
> temp_buffer
[2];
496 if (resample_count
== 0)
497 final_surface
= back_buffer
;
498 if (resample_count
> 0) {
499 TRACE_EVENT0("gpu", "CreateTemporarySurface");
500 if (!CreateTemporarySurface(present_thread_
->device(),
502 final_surface
.Receive()))
505 const gfx::Size half_size
=
506 GetHalfSizeNoLessThan(src_subrect
.size(), dst_size
);
507 if (resample_count
> 1) {
508 TRACE_EVENT0("gpu", "CreateTemporarySurface");
509 if (!CreateTemporarySurface(present_thread_
->device(),
511 temp_buffer
[0].Receive()))
514 if (resample_count
> 2) {
515 TRACE_EVENT0("gpu", "CreateTemporarySurface");
516 const gfx::Size quarter_size
= GetHalfSizeNoLessThan(half_size
, dst_size
);
517 if (!CreateTemporarySurface(present_thread_
->device(),
519 temp_buffer
[1].Receive()))
523 // Repeat downsampling the surface until its size becomes identical to
524 // |dst_size|. We keep the factor of each downsampling no more than two
525 // because using a factor more than two can introduce aliasing.
526 RECT read_rect
= src_subrect
.ToRECT();
527 gfx::Size write_size
= half_size
;
528 int read_buffer_index
= 1;
529 int write_buffer_index
= 0;
530 for (int i
= 0; i
< resample_count
; ++i
) {
531 TRACE_EVENT0("gpu", "StretchRect");
532 base::win::ScopedComPtr
<IDirect3DSurface9
> read_buffer
=
533 (i
== 0) ? back_buffer
: temp_buffer
[read_buffer_index
];
534 base::win::ScopedComPtr
<IDirect3DSurface9
> write_buffer
=
535 (i
== resample_count
- 1) ? final_surface
:
536 temp_buffer
[write_buffer_index
];
537 RECT write_rect
= gfx::Rect(write_size
).ToRECT();
538 hr
= present_thread_
->device()->StretchRect(read_buffer
,
545 read_rect
= write_rect
;
546 write_size
= GetHalfSizeNoLessThan(write_size
, dst_size
);
547 std::swap(read_buffer_index
, write_buffer_index
);
549 D3DLOCKED_RECT locked_rect
;
551 // Empirical evidence seems to suggest that LockRect and memcpy are faster
552 // than would be GetRenderTargetData to an offscreen surface wrapping *buf.
554 TRACE_EVENT0("gpu", "LockRect");
555 hr
= final_surface
->LockRect(&locked_rect
, NULL
,
556 D3DLOCK_READONLY
| D3DLOCK_NOSYSLOCK
);
562 TRACE_EVENT0("gpu", "memcpy");
563 size_t bytesPerDstRow
= 4 * dst_size
.width();
564 size_t bytesPerSrcRow
= locked_rect
.Pitch
;
565 if (bytesPerDstRow
== bytesPerSrcRow
) {
566 memcpy(reinterpret_cast<int8
*>(buf
),
567 reinterpret_cast<int8
*>(locked_rect
.pBits
),
568 bytesPerDstRow
* dst_size
.height());
570 for (int i
= 0; i
< dst_size
.height(); ++i
) {
571 memcpy(reinterpret_cast<int8
*>(buf
) + bytesPerDstRow
* i
,
572 reinterpret_cast<int8
*>(locked_rect
.pBits
) + bytesPerSrcRow
* i
,
577 final_surface
->UnlockRect();
582 void AcceleratedPresenter::Suspend() {
583 present_thread_
->message_loop()->PostTask(
585 base::Bind(&AcceleratedPresenter::DoSuspend
,
589 void AcceleratedPresenter::WasHidden() {
590 base::AutoLock
locked(lock_
);
594 void AcceleratedPresenter::ReleaseSurface() {
595 present_thread_
->message_loop()->PostTask(
597 base::Bind(&AcceleratedPresenter::DoReleaseSurface
,
601 void AcceleratedPresenter::Invalidate() {
602 // Make any pending or future presentation tasks do nothing. Once the last
603 // last pending task has been ignored, the reference count on the presenter
604 // will go to zero and the presenter, and potentially also the present thread
605 // it has a reference count on, will be destroyed.
606 base::AutoLock
locked(lock_
);
610 #if defined(USE_AURA)
611 void AcceleratedPresenter::SetNewTargetWindow(gfx::PluginWindowHandle window
) {
616 AcceleratedPresenter::~AcceleratedPresenter() {
619 static base::TimeDelta
GetSwapDelay() {
620 CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
622 if (cmd_line
->HasSwitch(switches::kGpuSwapDelay
)) {
623 base::StringToInt(cmd_line
->GetSwitchValueNative(
624 switches::kGpuSwapDelay
).c_str(), &delay
);
626 return base::TimeDelta::FromMilliseconds(delay
);
629 void AcceleratedPresenter::DoPresentAndAcknowledge(
630 const gfx::Size
& size
,
631 int64 surface_handle
,
632 const CompletionTask
& completion_task
) {
634 "gpu", "DoPresentAndAcknowledge",
635 "width", size
.width(),
636 "height", size
.height());
640 base::AutoLock
locked(lock_
);
642 // Initialize the device lazily since calling Direct3D can crash bots.
643 present_thread_
->InitDevice();
645 if (!present_thread_
->device()) {
646 if (!completion_task
.is_null())
647 completion_task
.Run(false, base::TimeTicks(), base::TimeDelta());
648 TRACE_EVENT0("gpu", "EarlyOut_NoDevice");
652 // Ensure the task is always run and while the lock is taken.
653 base::ScopedClosureRunner
scoped_completion_runner(
654 base::Bind(completion_task
, true, base::TimeTicks(), base::TimeDelta()));
656 // If invalidated, do nothing, the window is gone.
658 TRACE_EVENT0("gpu", "EarlyOut_NoWindow");
662 #if !defined(USE_AURA)
663 // If the window is a different size than the swap chain that is being
664 // presented then drop the frame.
665 gfx::Size window_size
= GetWindowSize();
666 if (hidden_
&& size
!= window_size
) {
667 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize",
668 "backwidth", size
.width(), "backheight", size
.height());
669 TRACE_EVENT2("gpu", "EarlyOut_WrongWindowSize2",
670 "windowwidth", window_size
.width(),
671 "windowheight", window_size
.height());
676 // Round up size so the swap chain is not continuously resized with the
677 // surface, which could lead to memory fragmentation.
678 const int kRound
= 64;
679 gfx::Size
quantized_size(
680 std::max(1, (size
.width() + kRound
- 1) / kRound
* kRound
),
681 std::max(1, (size
.height() + kRound
- 1) / kRound
* kRound
));
683 // Ensure the swap chain exists and is the same size (rounded up) as the
684 // surface to be presented.
685 if (!swap_chain_
|| quantized_size_
!= quantized_size
) {
686 TRACE_EVENT0("gpu", "CreateAdditionalSwapChain");
687 quantized_size_
= quantized_size
;
689 D3DPRESENT_PARAMETERS parameters
= { 0 };
690 parameters
.BackBufferWidth
= quantized_size
.width();
691 parameters
.BackBufferHeight
= quantized_size
.height();
692 parameters
.BackBufferCount
= 1;
693 parameters
.BackBufferFormat
= D3DFMT_A8R8G8B8
;
694 parameters
.hDeviceWindow
= GetShellWindow();
695 parameters
.Windowed
= TRUE
;
696 parameters
.Flags
= 0;
697 parameters
.PresentationInterval
= GetPresentationInterval();
698 parameters
.SwapEffect
= D3DSWAPEFFECT_COPY
;
701 HRESULT hr
= present_thread_
->device()->CreateAdditionalSwapChain(
703 swap_chain_
.Receive());
708 if (!source_texture_
.get()) {
709 TRACE_EVENT0("gpu", "CreateTexture");
710 HANDLE handle
= reinterpret_cast<HANDLE
>(surface_handle
);
711 hr
= present_thread_
->device()->CreateTexture(size
.width(),
714 D3DUSAGE_RENDERTARGET
,
717 source_texture_
.Receive(),
723 base::win::ScopedComPtr
<IDirect3DSurface9
> source_surface
;
724 hr
= source_texture_
->GetSurfaceLevel(0, source_surface
.Receive());
726 TRACE_EVENT0("gpu", "EarlyOut_NoSurfaceLevel");
730 base::win::ScopedComPtr
<IDirect3DSurface9
> dest_surface
;
731 hr
= swap_chain_
->GetBackBuffer(0,
732 D3DBACKBUFFER_TYPE_MONO
,
733 dest_surface
.Receive());
735 TRACE_EVENT0("gpu", "EarlyOut_NoBackbuffer");
741 size
.width(), size
.height()
745 TRACE_EVENT0("gpu", "Copy");
747 // Use a simple pixel / vertex shader pair to render a quad that flips the
748 // source texture on the vertical axis.
749 IDirect3DSurface9
*default_render_target
= NULL
;
750 present_thread_
->device()->GetRenderTarget(0, &default_render_target
);
752 present_thread_
->device()->SetRenderTarget(0, dest_surface
);
753 present_thread_
->device()->SetTexture(0, source_texture_
);
755 D3DVIEWPORT9 viewport
= {
757 size
.width(), size
.height(),
760 present_thread_
->device()->SetViewport(&viewport
);
762 float halfPixelX
= -1.0f
/ size
.width();
763 float halfPixelY
= 1.0f
/ size
.height();
764 Vertex vertices
[] = {
765 { halfPixelX
- 1, halfPixelY
+ 1, 0.5f
, 1, 0, 1 },
766 { halfPixelX
+ 1, halfPixelY
+ 1, 0.5f
, 1, 1, 1 },
767 { halfPixelX
+ 1, halfPixelY
- 1, 0.5f
, 1, 1, 0 },
768 { halfPixelX
- 1, halfPixelY
- 1, 0.5f
, 1, 0, 0 }
771 if (UsingOcclusionQuery()) {
772 present_thread_
->query()->Issue(D3DISSUE_BEGIN
);
775 present_thread_
->device()->BeginScene();
776 present_thread_
->device()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN
,
779 sizeof(vertices
[0]));
780 present_thread_
->device()->EndScene();
782 present_thread_
->device()->SetTexture(0, NULL
);
783 present_thread_
->device()->SetRenderTarget(0, default_render_target
);
784 default_render_target
->Release();
787 hr
= present_thread_
->query()->Issue(D3DISSUE_END
);
791 present_size_
= size
;
793 static const base::TimeDelta swap_delay
= GetSwapDelay();
794 if (swap_delay
.ToInternalValue())
795 base::PlatformThread::Sleep(swap_delay
);
797 // If it is expected that Direct3D cannot be used reliably because the window
798 // is resizing, fall back to presenting with GDI.
799 if (CheckDirect3DWillWork()) {
800 TRACE_EVENT0("gpu", "PresentD3D");
802 hr
= swap_chain_
->Present(&rect
, &rect
, window_
, NULL
, 0);
804 // For latency_tests.cc:
805 UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete");
808 FAILED(present_thread_
->device()->CheckDeviceState(window_
))) {
809 present_thread_
->ResetDevice();
812 HDC dc
= GetDC(window_
);
814 ReleaseDC(window_
, dc
);
817 // Early out if failed to reset device.
818 if (!present_thread_
->device())
823 D3DDISPLAYMODE display_mode
;
824 hr
= present_thread_
->device()->GetDisplayMode(0, &display_mode
);
828 D3DRASTER_STATUS raster_status
;
829 hr
= swap_chain_
->GetRasterStatus(&raster_status
);
833 // I can't figure out how to determine how many scanlines are in the
834 // vertical blank so clamp it such that scanline / height <= 1.
835 int clamped_scanline
= std::min(raster_status
.ScanLine
, display_mode
.Height
);
837 // The Internet says that on some GPUs, the scanline is not available
838 // while in the vertical blank.
839 if (raster_status
.InVBlank
)
840 clamped_scanline
= display_mode
.Height
;
842 base::TimeTicks current_time
= base::TimeTicks::HighResNow();
844 // Figure out approximately how far back in time the last vsync was based on
845 // the ratio of the raster scanline to the display height.
846 base::TimeTicks last_vsync_time
;
847 base::TimeDelta refresh_period
;
848 if (display_mode
.Height
) {
849 last_vsync_time
= current_time
-
850 base::TimeDelta::FromMilliseconds((clamped_scanline
* 1000) /
851 (display_mode
.RefreshRate
* display_mode
.Height
));
852 refresh_period
= base::TimeDelta::FromMicroseconds(
853 1000000 / display_mode
.RefreshRate
);
856 // Wait for the StretchRect to complete before notifying the GPU process
857 // that it is safe to write to its backing store again.
859 TRACE_EVENT0("gpu", "spin");
861 hr
= present_thread_
->query()->GetData(NULL
, 0, D3DGETDATA_FLUSH
);
865 } while (hr
== S_FALSE
);
868 scoped_completion_runner
.Release();
869 completion_task
.Run(true, last_vsync_time
, refresh_period
);
872 void AcceleratedPresenter::DoSuspend() {
873 base::AutoLock
locked(lock_
);
877 void AcceleratedPresenter::DoReleaseSurface() {
878 base::AutoLock
locked(lock_
);
879 present_thread_
->InitDevice();
880 source_texture_
.Release();
883 void AcceleratedPresenter::PresentWithGDI(HDC dc
) {
884 TRACE_EVENT0("gpu", "PresentWithGDI");
886 if (!present_thread_
->device())
892 base::win::ScopedComPtr
<IDirect3DTexture9
> system_texture
;
894 TRACE_EVENT0("gpu", "CreateSystemTexture");
895 HRESULT hr
= present_thread_
->device()->CreateTexture(
896 quantized_size_
.width(),
897 quantized_size_
.height(),
902 system_texture
.Receive(),
908 base::win::ScopedComPtr
<IDirect3DSurface9
> system_surface
;
909 HRESULT hr
= system_texture
->GetSurfaceLevel(0, system_surface
.Receive());
910 DCHECK(SUCCEEDED(hr
));
912 base::win::ScopedComPtr
<IDirect3DSurface9
> back_buffer
;
913 hr
= swap_chain_
->GetBackBuffer(0,
914 D3DBACKBUFFER_TYPE_MONO
,
915 back_buffer
.Receive());
916 DCHECK(SUCCEEDED(hr
));
919 TRACE_EVENT0("gpu", "GetRenderTargetData");
920 hr
= present_thread_
->device()->GetRenderTargetData(back_buffer
,
922 DCHECK(SUCCEEDED(hr
));
925 D3DLOCKED_RECT locked_surface
;
926 hr
= system_surface
->LockRect(&locked_surface
, NULL
, D3DLOCK_READONLY
);
927 DCHECK(SUCCEEDED(hr
));
929 BITMAPINFO bitmap_info
= {
931 sizeof(BITMAPINFOHEADER
),
932 quantized_size_
.width(),
933 -quantized_size_
.height(),
944 TRACE_EVENT0("gpu", "StretchDIBits");
947 present_size_
.width(),
948 present_size_
.height(),
950 present_size_
.width(),
951 present_size_
.height(),
952 locked_surface
.pBits
,
958 system_surface
->UnlockRect();
960 // For latency_tests.cc:
961 UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete");
964 gfx::Size
AcceleratedPresenter::GetWindowSize() {
966 GetClientRect(window_
, &rect
);
967 return gfx::Rect(rect
).size();
970 bool AcceleratedPresenter::CheckDirect3DWillWork() {
971 gfx::Size window_size
= GetWindowSize();
972 if (window_size
!= last_window_size_
&& last_window_size_
.GetArea() != 0) {
973 last_window_size_
= window_size
;
974 last_window_resize_time_
= base::Time::Now();
978 return base::Time::Now() - last_window_resize_time_
>
979 base::TimeDelta::FromMilliseconds(100);
982 AcceleratedSurface::AcceleratedSurface(gfx::PluginWindowHandle window
)
983 : presenter_(g_accelerated_presenter_map
.Pointer()->CreatePresenter(
987 AcceleratedSurface::~AcceleratedSurface() {
988 g_accelerated_presenter_map
.Pointer()->RemovePresenter(presenter_
);
989 presenter_
->Invalidate();
992 void AcceleratedSurface::Present(HDC dc
) {
993 presenter_
->Present(dc
);
996 void AcceleratedSurface::AsyncCopyTo(
997 const gfx::Rect
& src_subrect
,
998 const gfx::Size
& dst_size
,
1000 const base::Callback
<void(bool)>& callback
) {
1001 presenter_
->AsyncCopyTo(src_subrect
, dst_size
, buf
, callback
);
1004 void AcceleratedSurface::Suspend() {
1005 presenter_
->Suspend();
1008 void AcceleratedSurface::WasHidden() {
1009 presenter_
->WasHidden();