Index: ui/gfx/surface/accelerated_surface_win.cc |
=================================================================== |
--- ui/gfx/surface/accelerated_surface_win.cc (revision 0) |
+++ ui/gfx/surface/accelerated_surface_win.cc (revision 0) |
@@ -0,0 +1,335 @@ |
+// Copyright (c) 2011 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. |
+ |
+#include "ui/gfx/surface/accelerated_surface_win.h" |
+ |
+#include <windows.h> |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "base/lazy_instance.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/stringprintf.h" |
+#include "base/threading/thread.h" |
+#include "base/tracked_objects.h" |
+#include "base/win/wrapped_window_proc.h" |
+#include "ipc/ipc_message.h" |
+#include "ui/base/win/hwnd_util.h" |
+ |
+#pragma comment(lib, "d3d9.lib") |
+ |
+namespace { |
+ |
+class PresentThreadPool { |
+ public: |
+ static const int kNumPresentThreads = 4; |
+ |
+ PresentThreadPool(); |
+ |
+ int NextThread(); |
+ |
+ void PostTask(int thread, |
+ const tracked_objects::Location& from_here, |
+ base::Closure task); |
+ private: |
+ int next_thread_; |
+ scoped_ptr<base::Thread> present_threads_[kNumPresentThreads]; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PresentThreadPool); |
+}; |
+ |
+base::LazyInstance<PresentThreadPool> |
+ g_present_thread_pool(base::LINKER_INITIALIZED); |
+ |
+PresentThreadPool::PresentThreadPool() : next_thread_(0) { |
+ for (int i = 0; i < kNumPresentThreads; ++i) { |
+ present_threads_[i].reset(new base::Thread( |
+ base::StringPrintf("PresentThread #%d", i).c_str())); |
+ present_threads_[i]->Start(); |
+ } |
+} |
+ |
+int PresentThreadPool::NextThread() { |
+ next_thread_ = (next_thread_ + 1) % kNumPresentThreads; |
+ return next_thread_; |
+} |
+ |
+void PresentThreadPool::PostTask(int thread, |
+ const tracked_objects::Location& from_here, |
+ base::Closure task) { |
+ DCHECK_GE(thread, 0); |
+ DCHECK_LT(thread, kNumPresentThreads); |
+ |
+ present_threads_[thread]->message_loop()->PostTask(from_here, task); |
+} |
+ |
+} // namespace anonymous |
+ |
+AcceleratedSurface::AcceleratedSurface(HWND parent) |
+ : thread_affinity_(g_present_thread_pool.Pointer()->NextThread()), |
+ window_(parent), |
+ num_pending_resizes_(0) { |
+} |
+ |
+AcceleratedSurface::~AcceleratedSurface() { |
+ // Destroy should have been called prior to the last reference going away. |
+ DCHECK(!device_); |
+} |
+ |
+void AcceleratedSurface::Initialize() { |
+ g_present_thread_pool.Pointer()->PostTask( |
+ thread_affinity_, |
+ FROM_HERE, |
+ base::Bind(&AcceleratedSurface::DoInitialize, this)); |
+} |
+ |
+void AcceleratedSurface::Destroy() { |
+ g_present_thread_pool.Pointer()->PostTask( |
+ thread_affinity_, |
+ FROM_HERE, |
+ base::Bind(&AcceleratedSurface::DoDestroy, |
+ this, |
+ MessageLoop::current()->message_loop_proxy())); |
+} |
+ |
+void AcceleratedSurface::AsyncPresentAndAcknowledge( |
+ const gfx::Size& size, |
+ int64 surface_id, |
+ base::Closure completion_task) { |
+ const int kRound = 64; |
+ gfx::Size quantized_size( |
+ std::max(1, (size.width() + kRound - 1) / kRound * kRound), |
+ std::max(1, (size.height() + kRound - 1) / kRound * kRound)); |
+ |
+ if (pending_size_ != quantized_size) { |
+ pending_size_ = quantized_size; |
+ base::AtomicRefCountInc(&num_pending_resizes_); |
+ |
+ g_present_thread_pool.Pointer()->PostTask( |
+ thread_affinity_, |
+ FROM_HERE, |
+ base::Bind(&AcceleratedSurface::DoResize, this, quantized_size)); |
+ } |
+ |
+ // This might unnecessarily post to the thread with which the swap chain has |
+ // affinity. This will only result in potentially delaying the present. |
+ g_present_thread_pool.Pointer()->PostTask( |
+ num_pending_resizes_ ? |
+ thread_affinity_ : g_present_thread_pool.Pointer()->NextThread(), |
+ FROM_HERE, |
+ base::Bind(&AcceleratedSurface::DoPresentAndAcknowledge, |
+ this, |
+ size, |
+ surface_id, |
+ completion_task)); |
+} |
+ |
+void AcceleratedSurface::Present() { |
+ HRESULT hr; |
+ |
+ base::AutoLock locked(lock_); |
+ |
+ if (!device_) |
+ return; |
+ |
+ RECT rect; |
+ if (!GetClientRect(window_, &rect)) |
+ return; |
+ |
+ hr = device_->PresentEx(&rect, |
+ &rect, |
+ NULL, |
+ NULL, |
+ D3DPRESENT_INTERVAL_IMMEDIATE); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ hr = query_->Issue(D3DISSUE_END); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ do { |
+ hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); |
+ |
+ if (hr == S_FALSE) |
+ Sleep(0); |
+ } while (hr == S_FALSE); |
+} |
+ |
+void AcceleratedSurface::DoInitialize() { |
+ HRESULT hr; |
+ |
+ base::win::ScopedComPtr<IDirect3D9Ex> d3d; |
+ hr = Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.Receive()); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ D3DPRESENT_PARAMETERS parameters = { 0 }; |
+ parameters.BackBufferWidth = 1; |
+ parameters.BackBufferHeight = 1; |
+ parameters.BackBufferCount = 1; |
+ parameters.BackBufferFormat = D3DFMT_A8R8G8B8; |
+ parameters.hDeviceWindow = window_; |
+ parameters.Windowed = TRUE; |
+ parameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; |
+ parameters.SwapEffect = D3DSWAPEFFECT_COPY; |
+ |
+ hr = d3d->CreateDeviceEx( |
+ D3DADAPTER_DEFAULT, |
+ D3DDEVTYPE_HAL, |
+ window_, |
+ D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING | |
+ D3DCREATE_MULTITHREADED, |
+ ¶meters, |
+ NULL, |
+ device_.Receive()); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive()); |
+ if (FAILED(hr)) { |
+ device_ = NULL; |
+ return; |
+ } |
+ |
+ return; |
+} |
+ |
+void AcceleratedSurface::DoDestroy( |
+ const scoped_refptr<base::MessageLoopProxy>& ui_message_loop) { |
+ base::AutoLock locked(lock_); |
+ |
+ device_ = NULL; |
+ query_ = NULL; |
+} |
+ |
+void AcceleratedSurface::DoResize(const gfx::Size& size) { |
+ HRESULT hr; |
+ |
+ base::AtomicRefCountDec(&num_pending_resizes_); |
+ |
+ D3DPRESENT_PARAMETERS parameters = { 0 }; |
+ parameters.BackBufferWidth = size.width(); |
+ parameters.BackBufferHeight = size.height(); |
+ parameters.BackBufferCount = 1; |
+ parameters.BackBufferFormat = D3DFMT_A8R8G8B8; |
+ parameters.hDeviceWindow = window_; |
+ parameters.Windowed = TRUE; |
+ parameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; |
+ parameters.SwapEffect = D3DSWAPEFFECT_COPY; |
+ |
+ hr = device_->ResetEx(¶meters, NULL); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ size_ = size; |
+ |
+ device_->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0); |
+} |
+ |
+void AcceleratedSurface::DoPresentAndAcknowledge( |
+ const gfx::Size& size, |
+ int64 surface_id, |
+ base::Closure completion_task) { |
+ HRESULT hr; |
+ |
+ base::AutoLock locked(lock_); |
+ |
+ if (!window_) |
+ return; |
+ |
+ HANDLE handle = reinterpret_cast<HANDLE>(surface_id); |
+ if (!handle) |
+ return; |
+ |
+ base::win::ScopedComPtr<IDirect3DTexture9> source_texture; |
+ hr = device_->CreateTexture(size.width(), |
+ size.height(), |
+ 1, |
+ D3DUSAGE_RENDERTARGET, |
+ D3DFMT_A8R8G8B8, |
+ D3DPOOL_DEFAULT, |
+ source_texture.Receive(), |
+ &handle); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ base::win::ScopedComPtr<IDirect3DSurface9> source_surface; |
+ hr = source_texture->GetSurfaceLevel(0, source_surface.Receive()); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; |
+ hr = device_->GetRenderTarget(0, dest_surface.Receive()); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ RECT rect = { |
+ 0, 0, |
+ size.width(), size.height() |
+ }; |
+ |
+ hr = device_->StretchRect(source_surface, |
+ &rect, |
+ dest_surface, |
+ &rect, |
+ D3DTEXF_NONE); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ hr = query_->Issue(D3DISSUE_END); |
+ if (FAILED(hr)) |
+ return; |
+ |
+ // Flush so the StretchRect can be processed by the GPU while the window is |
+ // being resized. |
+ query_->GetData(NULL, 0, D3DGETDATA_FLUSH); |
+ |
+ ::SetWindowPos( |
+ window_, |
+ NULL, |
+ 0, 0, |
+ size.width(), size.height(), |
+ SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |SWP_NOOWNERZORDER | |
+ SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOSENDCHANGING | |
+ SWP_ASYNCWINDOWPOS); |
+ |
+ // Wait for the StretchRect to complete before notifying the GPU process |
+ // that it is safe to write to its backing store again. |
+ do { |
+ hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); |
+ |
+ if (hr == S_FALSE) |
+ Sleep(0); |
+ } while (hr == S_FALSE); |
+ |
+ if (!completion_task.is_null()) |
+ completion_task.Run(); |
+ |
+ hr = device_->Present(&rect, &rect, NULL, NULL); |
+ if (FAILED(hr)) |
+ return; |
+} |
+ |
+LRESULT CALLBACK AcceleratedSurface::ChildWndProc(HWND window, |
+ unsigned int message, |
+ WPARAM wparam, |
+ LPARAM lparam) { |
+ AcceleratedSurface* self = static_cast<AcceleratedSurface*>( |
+ ui::GetWindowUserData(window)); |
+ switch (message) { |
+ case WM_PAINT: { |
+ PAINTSTRUCT paint_struct; |
+ if (BeginPaint(window, &paint_struct)) { |
+ self->Present(); |
+ EndPaint(window, &paint_struct); |
+ } |
+ return 0; |
+ } |
+ case WM_ERASEBKGND: |
+ return 1; |
+ } |
+ |
+ return DefWindowProc(window, message, wparam, lparam); |
+} |
Property changes on: ui\gfx\surface\accelerated_surface_win.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |