summaryrefslogtreecommitdiffstats
path: root/ui/gfx/surface/accelerated_surface_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ui/gfx/surface/accelerated_surface_win.cc')
-rw-r--r--ui/gfx/surface/accelerated_surface_win.cc188
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,
&parameters,
@@ -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);
+}