diff options
Diffstat (limited to 'ui/gfx/surface/accelerated_surface_win.cc')
-rw-r--r-- | ui/gfx/surface/accelerated_surface_win.cc | 188 |
1 files changed, 142 insertions, 46 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); +} |