diff options
author | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-05 19:03:58 +0000 |
---|---|---|
committer | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-05 19:03:58 +0000 |
commit | 2547bdecf061037a17249cf8230cfcae485aa870 (patch) | |
tree | 72a5e4cff3495f1c219a3a6f069b689b259ede32 /ui/gfx | |
parent | 61dc220ca5e40d489a0b50d20f32e8f88e3f9f86 (diff) | |
download | chromium_src-2547bdecf061037a17249cf8230cfcae485aa870.zip chromium_src-2547bdecf061037a17249cf8230cfcae485aa870.tar.gz chromium_src-2547bdecf061037a17249cf8230cfcae485aa870.tar.bz2 |
On Windows, D3D devices used to present the compositor's output are recycled.
This is because some drivers seem to crash when a D3D query object is released. To avoid crashing, the D3D device and associated query are stored in a cache to be used by future tabs. The device's swap chain is resized to 1 x 1 during this time so that memory is not wasted.
In order for the D3D objects to outlive the AcceleratedSurface object, I broke them out into another class.
I considered cleaning up the D3D objects at the time where the browser process terminates. That would result in a crash on browser termination, which should not impact the user. However, I went with the former approach because it is simpler. The D3D device will likely crash if it is destroyed on the main thread, the thread where the LazyInstances are cleaned up. By the time the LazyInstances are cleaned up, the only thread on which the D3D device can correctly be destroyed can likely no longer do so.
BUG=108317
Review URL: http://codereview.chromium.org/9087022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116520 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx')
-rw-r--r-- | ui/gfx/surface/accelerated_surface_win.cc | 188 | ||||
-rw-r--r-- | ui/gfx/surface/accelerated_surface_win.h | 63 |
2 files changed, 152 insertions, 99 deletions
diff --git a/ui/gfx/surface/accelerated_surface_win.cc b/ui/gfx/surface/accelerated_surface_win.cc index 29f8993..9845f8f 100644 --- a/ui/gfx/surface/accelerated_surface_win.cc +++ b/ui/gfx/surface/accelerated_surface_win.cc @@ -4,6 +4,7 @@ #include "ui/gfx/surface/accelerated_surface_win.h" +#include <d3d9.h> #include <windows.h> #include <list> @@ -14,11 +15,16 @@ #include "base/debug/trace_event.h" #include "base/file_path.h" #include "base/lazy_instance.h" +#include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_native_library.h" #include "base/stringprintf.h" +#include "base/synchronization/lock.h" #include "base/threading/thread.h" #include "base/tracked_objects.h" +#include "base/win/scoped_comptr.h" #include "base/win/wrapped_window_proc.h" #include "ipc/ipc_message.h" #include "ui/base/win/hwnd_util.h" @@ -54,6 +60,9 @@ class PresentThreadPool { base::LazyInstance<PresentThreadPool> g_present_thread_pool = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance<std::vector<linked_ptr<AcceleratedPresenter> > > + g_unused_accelerated_presenters; + PresentThreadPool::PresentThreadPool() : next_thread_(0) { for (int i = 0; i < kNumPresentThreads; ++i) { present_threads_[i].reset(new base::Thread( @@ -85,32 +94,78 @@ UINT GetPresentationInterval() { } // namespace anonymous -AcceleratedSurface::AcceleratedSurface(HWND parent) - : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), - window_(parent), - num_pending_resizes_(0) { -} +class AcceleratedPresenter { + public: + AcceleratedPresenter(); + ~AcceleratedPresenter(); -AcceleratedSurface::~AcceleratedSurface() { - // Destroy should have been called prior to the last reference going away. - DCHECK(!device_); -} + void AsyncPresentAndAcknowledge(gfx::NativeWindow window, + const gfx::Size& size, + int64 surface_id, + const base::Closure& completion_task); + + bool Present(gfx::NativeWindow window); + + void Suspend(); + + private: + void DoInitialize(); + void DoResize(const gfx::Size& size); + void DoReset(); + void DoPresentAndAcknowledge(gfx::NativeWindow window, + const gfx::Size& size, + int64 surface_id, + const base::Closure& completion_task); + + // Immutable and accessible from any thread without the lock. + const int thread_affinity_; + base::ScopedNativeLibrary d3d_module_; + + // The size of the swap chain once any pending resizes have been processed. + // Only accessed on the UI thread so the lock is unnecessary. + gfx::Size pending_size_; + + // The current size of the swap chain. This is only accessed on the thread + // with which the surface has affinity. + gfx::Size size_; + + // The number of pending resizes. This is accessed with atomic operations so + // the lock is not necessary. + base::AtomicRefCount num_pending_resizes_; + + // Take the lock before accessing any other state. + base::Lock lock_; + + // This device's swap chain is presented to the child window. Copy semantics + // are used so it is possible to represent it to quickly validate the window. + base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; + + // This query is used to wait until a certain amount of progress has been + // made by the GPU and it is safe for the producer to modify its shared + // texture again. + base::win::ScopedComPtr<IDirect3DQuery9> query_; + + DISALLOW_COPY_AND_ASSIGN(AcceleratedPresenter); +}; -void AcceleratedSurface::Initialize() { +AcceleratedPresenter::AcceleratedPresenter() + : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), + num_pending_resizes_(0) { g_present_thread_pool.Pointer()->PostTask( thread_affinity_, FROM_HERE, - base::Bind(&AcceleratedSurface::DoInitialize, this)); + base::Bind(&AcceleratedPresenter::DoInitialize, base::Unretained(this))); } -void AcceleratedSurface::Destroy() { - g_present_thread_pool.Pointer()->PostTask( - thread_affinity_, - FROM_HERE, - base::Bind(&AcceleratedSurface::DoDestroy, this)); +AcceleratedPresenter::~AcceleratedPresenter() { + // The D3D device and query are leaked because destroying the associated D3D + // query crashes some Intel drivers. + device_.Detach(); + query_.Detach(); } -void AcceleratedSurface::AsyncPresentAndAcknowledge( +void AcceleratedPresenter::AsyncPresentAndAcknowledge( + gfx::NativeWindow window, const gfx::Size& size, int64 surface_id, const base::Closure& completion_task) { @@ -126,7 +181,9 @@ void AcceleratedSurface::AsyncPresentAndAcknowledge( g_present_thread_pool.Pointer()->PostTask( thread_affinity_, FROM_HERE, - base::Bind(&AcceleratedSurface::DoResize, this, quantized_size)); + base::Bind(&AcceleratedPresenter::DoResize, + base::Unretained(this), + quantized_size)); } // This might unnecessarily post to the thread with which the swap chain has @@ -135,14 +192,15 @@ void AcceleratedSurface::AsyncPresentAndAcknowledge( num_pending_resizes_ ? thread_affinity_ : g_present_thread_pool.Pointer()->NextThread(), FROM_HERE, - base::Bind(&AcceleratedSurface::DoPresentAndAcknowledge, - this, + base::Bind(&AcceleratedPresenter::DoPresentAndAcknowledge, + base::Unretained(this), + window, size, surface_id, completion_task)); } -bool AcceleratedSurface::Present() { +bool AcceleratedPresenter::Present(gfx::NativeWindow window) { TRACE_EVENT0("surface", "Present"); HRESULT hr; @@ -153,14 +211,14 @@ bool AcceleratedSurface::Present() { return true; RECT rect; - if (!GetClientRect(window_, &rect)) + if (!GetClientRect(window, &rect)) return true; { TRACE_EVENT0("surface", "PresentEx"); hr = device_->PresentEx(&rect, &rect, - NULL, + window, NULL, D3DPRESENT_INTERVAL_IMMEDIATE); @@ -170,7 +228,7 @@ bool AcceleratedSurface::Present() { g_present_thread_pool.Pointer()->PostTask( thread_affinity_, FROM_HERE, - base::Bind(&AcceleratedSurface::DoReset, this)); + base::Bind(&AcceleratedPresenter::DoReset, base::Unretained(this))); // A device hang destroys the contents of the back buffer so no point in // scheduling a present. @@ -197,7 +255,21 @@ bool AcceleratedSurface::Present() { return true; } -void AcceleratedSurface::DoInitialize() { +void AcceleratedPresenter::Suspend() { + // Resize the swap chain to 1 x 1 to save memory while the presenter is not + // in use. + pending_size_ = gfx::Size(1, 1); + base::AtomicRefCountInc(&num_pending_resizes_); + + g_present_thread_pool.Pointer()->PostTask( + thread_affinity_, + FROM_HERE, + base::Bind(&AcceleratedPresenter::DoResize, + base::Unretained(this), + gfx::Size(1, 1))); +} + +void AcceleratedPresenter::DoInitialize() { TRACE_EVENT0("surface", "DoInitialize"); HRESULT hr; @@ -214,12 +286,16 @@ void AcceleratedSurface::DoInitialize() { if (FAILED(hr)) return; + // Any old window will do to create the device. In practice the window to + // present to is an argument to IDirect3DDevice9::Present. + HWND window = GetShellWindow(); + D3DPRESENT_PARAMETERS parameters = { 0 }; parameters.BackBufferWidth = 1; parameters.BackBufferHeight = 1; parameters.BackBufferCount = 1; parameters.BackBufferFormat = D3DFMT_A8R8G8B8; - parameters.hDeviceWindow = window_; + parameters.hDeviceWindow = window; parameters.Windowed = TRUE; parameters.Flags = 0; parameters.PresentationInterval = GetPresentationInterval(); @@ -228,7 +304,7 @@ void AcceleratedSurface::DoInitialize() { hr = d3d->CreateDeviceEx( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, - window_, + window, D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, ¶meters, @@ -246,22 +322,13 @@ void AcceleratedSurface::DoInitialize() { return; } -void AcceleratedSurface::DoDestroy() { - TRACE_EVENT0("surface", "DoDestroy"); - - base::AutoLock locked(lock_); - - query_ = NULL; - device_ = NULL; -} - -void AcceleratedSurface::DoResize(const gfx::Size& size) { +void AcceleratedPresenter::DoResize(const gfx::Size& size) { TRACE_EVENT0("surface", "DoResize"); size_ = size; DoReset(); } -void AcceleratedSurface::DoReset() { +void AcceleratedPresenter::DoReset() { TRACE_EVENT0("surface", "DoReset"); HRESULT hr; @@ -273,12 +340,14 @@ void AcceleratedSurface::DoReset() { if (!device_) return; + gfx::NativeWindow window = GetShellWindow(); + D3DPRESENT_PARAMETERS parameters = { 0 }; parameters.BackBufferWidth = size_.width(); parameters.BackBufferHeight = size_.height(); parameters.BackBufferCount = 1; parameters.BackBufferFormat = D3DFMT_A8R8G8B8; - parameters.hDeviceWindow = window_; + parameters.hDeviceWindow = window; parameters.Windowed = TRUE; parameters.Flags = 0; parameters.PresentationInterval = GetPresentationInterval(); @@ -291,7 +360,8 @@ void AcceleratedSurface::DoReset() { device_->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0); } -void AcceleratedSurface::DoPresentAndAcknowledge( +void AcceleratedPresenter::DoPresentAndAcknowledge( + gfx::NativeWindow window, const gfx::Size& size, int64 surface_id, const base::Closure& completion_task) { @@ -307,9 +377,6 @@ void AcceleratedSurface::DoPresentAndAcknowledge( if (!device_) return; - if (!window_) - return; - HANDLE handle = reinterpret_cast<HANDLE>(surface_id); if (!handle) return; @@ -364,7 +431,7 @@ void AcceleratedSurface::DoPresentAndAcknowledge( query_->GetData(NULL, 0, D3DGETDATA_FLUSH); ::SetWindowPos( - window_, + window, NULL, 0, 0, size.width(), size.height(), @@ -390,7 +457,7 @@ void AcceleratedSurface::DoPresentAndAcknowledge( { TRACE_EVENT0("surface", "Present"); - hr = device_->Present(&rect, &rect, NULL, NULL); + hr = device_->Present(&rect, &rect, window, NULL); // If the device hung, force a resize to reset the device. if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICEHUNG) { @@ -398,10 +465,39 @@ void AcceleratedSurface::DoPresentAndAcknowledge( g_present_thread_pool.Pointer()->PostTask( thread_affinity_, FROM_HERE, - base::Bind(&AcceleratedSurface::DoReset, this)); + base::Bind(&AcceleratedPresenter::DoReset, base::Unretained(this))); // A device hang destroys the contents of the back buffer so no point in // scheduling a present. } } } + +AcceleratedSurface::AcceleratedSurface() { + if (g_unused_accelerated_presenters.Pointer()->empty()) { + presenter_.reset(new AcceleratedPresenter); + } else { + presenter_ = g_unused_accelerated_presenters.Pointer()->back(); + g_unused_accelerated_presenters.Pointer()->pop_back(); + } +} + +AcceleratedSurface::~AcceleratedSurface() { + presenter_->Suspend(); + g_unused_accelerated_presenters.Pointer()->push_back(presenter_); +} + +void AcceleratedSurface::AsyncPresentAndAcknowledge( + HWND window, + const gfx::Size& size, + int64 surface_id, + const base::Closure& completion_task) { + presenter_->AsyncPresentAndAcknowledge(window, + size, + surface_id, + completion_task); +} + +bool AcceleratedSurface::Present(HWND window) { + return presenter_->Present(window); +} diff --git a/ui/gfx/surface/accelerated_surface_win.h b/ui/gfx/surface/accelerated_surface_win.h index 593972b..a2b6c66 100644 --- a/ui/gfx/surface/accelerated_surface_win.h +++ b/ui/gfx/surface/accelerated_surface_win.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,75 +6,32 @@ #define UI_GFX_SURFACE_ACCELERATED_SURFACE_WIN_H_ #pragma once -#include <d3d9.h> - #include "base/callback_forward.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop_proxy.h" -#include "base/scoped_native_library.h" -#include "base/synchronization/lock.h" -#include "base/win/scoped_comptr.h" +#include "base/memory/linked_ptr.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/size.h" #include "ui/gfx/surface/surface_export.h" -class SURFACE_EXPORT AcceleratedSurface - : public base::RefCountedThreadSafe<AcceleratedSurface> { +class AcceleratedPresenter; + +class SURFACE_EXPORT AcceleratedSurface { public: - explicit AcceleratedSurface(gfx::NativeWindow parent); + AcceleratedSurface(); ~AcceleratedSurface(); - void Initialize(); - void Destroy(); - // Schedule a frame to be presented. The completion callback will be invoked // when it is safe to write to the surface on another thread. The lock for // this surface will be held while the completion callback runs. - void AsyncPresentAndAcknowledge(const gfx::Size& size, + void AsyncPresentAndAcknowledge(HWND window, + const gfx::Size& size, int64 surface_id, const base::Closure& completion_task); // Synchronously present a frame with no acknowledgement. - bool Present(); + bool Present(HWND window); private: - void DoInitialize(); - void DoDestroy(); - void DoResize(const gfx::Size& size); - void DoReset(); - void DoPresentAndAcknowledge(const gfx::Size& size, - int64 surface_id, - const base::Closure& completion_task); - - // Immutable and accessible from any thread without the lock. - const int thread_affinity_; - const gfx::NativeWindow window_; - base::ScopedNativeLibrary d3d_module_; - - // The size of the swap chain once any pending resizes have been processed. - // Only accessed on the UI thread so the lock is unnecessary. - gfx::Size pending_size_; - - // The current size of the swap chain. This is only accessed on the thread - // with which the surface has affinity. - gfx::Size size_; - - // The number of pending resizes. This is accessed with atomic operations so - // the lock is not necessary. - base::AtomicRefCount num_pending_resizes_; - - // Take the lock before accessing any other state. - base::Lock lock_; - - // This device's swap chain is presented to the child window. Copy semantics - // are used so it is possible to represent it to quickly validate the window. - base::win::ScopedComPtr<IDirect3DDevice9Ex> device_; - - // This query is used to wait until a certain amount of progress has been - // made by the GPU and it is safe for the producer to modify its shared - // texture again. - base::win::ScopedComPtr<IDirect3DQuery9> query_; - + linked_ptr<AcceleratedPresenter> presenter_; DISALLOW_COPY_AND_ASSIGN(AcceleratedSurface); }; |