| 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
|
|
|
|
|