diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-24 23:08:58 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-24 23:08:58 +0000 |
commit | 9c8a1980f7b81a78100034852c59777c2cc4d83d (patch) | |
tree | ac2106bc2ebe10d2ff30e4d2e24c9002d898facf /ui/gfx/compositor | |
parent | 28f536187411f659ca873c76d081d12e08f51383 (diff) | |
download | chromium_src-9c8a1980f7b81a78100034852c59777c2cc4d83d.zip chromium_src-9c8a1980f7b81a78100034852c59777c2cc4d83d.tar.gz chromium_src-9c8a1980f7b81a78100034852c59777c2cc4d83d.tar.bz2 |
Prototype compositor to render views to a texture using d3d 10. This
is just a prototype for us to play with. There are plenty of issues to
resolve before its made real, but I wanted to check something in for
others to play with rather than keeping it all local. To get it to
compile you need to set the GYP_DEFINE to views_compositor.
BUG=none
TEST=none
R=ben@chromium.org,apatrick@chromium.org
Review URL: http://codereview.chromium.org/7067029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86516 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx/compositor')
-rw-r--r-- | ui/gfx/compositor/compositor.gyp | 24 | ||||
-rw-r--r-- | ui/gfx/compositor/compositor_win.cc | 563 |
2 files changed, 587 insertions, 0 deletions
diff --git a/ui/gfx/compositor/compositor.gyp b/ui/gfx/compositor/compositor.gyp index 5d816b5..d5379fe 100644 --- a/ui/gfx/compositor/compositor.gyp +++ b/ui/gfx/compositor/compositor.gyp @@ -31,6 +31,7 @@ 'compositor.cc', 'compositor.h', 'compositor_gl.cc', + 'compositor_win.cc', ], 'conditions': [ ['os_posix == 1 and OS != "mac"', { @@ -43,6 +44,29 @@ ], }, }], + ['OS == "win" and views_compositor == 1', { + 'sources!': [ + 'compositor.cc', + ], + # TODO(sky): before we make this real need to remove + # IDR_BITMAP_BRUSH_IMAGE. + 'dependencies': [ + '<(DEPTH)/ui/ui.gyp:gfx_resources', + ], + 'link_settings': { + 'libraries': [ + '-ld3d10.lib', + '-ld3dx10d.lib', + '-ldxerr.lib', + '-ldxguid.lib', + ] + }, + }], + ['OS == "win" and views_compositor == 0', { + 'sources/': [ + ['exclude', '^compositor_win.cc'], + ], + }], ], }, ], diff --git a/ui/gfx/compositor/compositor_win.cc b/ui/gfx/compositor/compositor_win.cc new file mode 100644 index 0000000..a303f7bb --- /dev/null +++ b/ui/gfx/compositor/compositor_win.cc @@ -0,0 +1,563 @@ +// 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/compositor/compositor.h" + +#include <algorithm> +#include <d3dx10.h> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/stl_util-inl.h" +#include "base/string_piece.h" +#include "base/win/scoped_comptr.h" +#include "grit/gfx_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/transform.h" + +// TODO(sky): this is a hack, figure out real error handling. +#define RETURN_IF_FAILED(error) \ + if (error != S_OK) { \ + this->Errored(error); \ + VLOG(1) << "D3D failed" << error; \ + return; \ + } + +using base::win::ScopedComPtr; + +namespace ui { + +namespace { + +class ViewTexture; + +// ViewTexture talks to its host by way of this interface. +class ViewTextureHost { + public: + // Invoked to update the perspective needed by this texture. |transform| is + // the transform for the texture, and |size| the size of the texture. + virtual void UpdatePerspective(const ui::Transform& transform, + const gfx::Size& size) = 0; + + // Returns the overall size of the compositor. + virtual const gfx::Size& GetHostSize() = 0; + + protected: + virtual ~ViewTextureHost() {} +}; + +// D3D 10 Texture implementation. Creates a quad representing the view and +// a texture with the bitmap data. The quad has an origin of 0,0,0 with a size +// matching that of |SetBitmap|. +class ViewTexture : public Texture { + public: + ViewTexture(ViewTextureHost* host, + ID3D10Device* device, + ID3D10Effect* effect); + + ~ViewTexture(); + + void Init(); + + // Texture: + virtual void SetBitmap(const SkBitmap& bitmap, + const gfx::Point& origin, + const gfx::Size& overall_size) OVERRIDE; + virtual void Draw(const ui::Transform& transform) OVERRIDE; + + private: + struct Vertex { + D3DXVECTOR3 position; + D3DXVECTOR2 texture_offset; + }; + + void Errored(HRESULT result); + + void ConvertBitmapToD3DData(const SkBitmap& bitmap, + scoped_array<uint32>* converted_data); + + void CreateVertexBuffer(const gfx::Size& size); + + // TODO: this should be shared among all textures. + void CreateIndexBuffer(); + + ViewTextureHost* host_; + + // Size of the corresponding View. + gfx::Size view_size_; + + ScopedComPtr<ID3D10Device> device_; + ScopedComPtr<ID3D10Effect, NULL> effect_; + ScopedComPtr<ID3D10Texture2D> texture_; + ScopedComPtr<ID3D10ShaderResourceView> shader_view_; + ScopedComPtr<ID3D10Buffer> vertex_buffer_; + ScopedComPtr<ID3D10Buffer> index_buffer_; + + DISALLOW_COPY_AND_ASSIGN(ViewTexture); +}; + +ViewTexture::ViewTexture(ViewTextureHost* host, + ID3D10Device* device, + ID3D10Effect* effect) + : host_(host), + device_(device), + effect_(effect) { +} + +ViewTexture::~ViewTexture() { +} + +void ViewTexture::Init() { + CreateIndexBuffer(); +} + +void ViewTexture::SetBitmap(const SkBitmap& bitmap, + const gfx::Point& origin, + const gfx::Size& overall_size) { + if (view_size_ != overall_size) + CreateVertexBuffer(overall_size); + view_size_ = overall_size; + + scoped_array<uint32> converted_data; + ConvertBitmapToD3DData(bitmap, &converted_data); + if (gfx::Size(bitmap.width(), bitmap.height()) == overall_size) { + shader_view_.Release(); + texture_.Release(); + + D3D10_TEXTURE2D_DESC texture_desc; + texture_desc.Width = bitmap.width(); + texture_desc.Height = bitmap.height(); + texture_desc.MipLevels = 1; + texture_desc.ArraySize = 1; + texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + texture_desc.SampleDesc.Count = 1; + texture_desc.SampleDesc.Quality = 0; + texture_desc.Usage = D3D10_USAGE_DEFAULT; + texture_desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; + texture_desc.CPUAccessFlags = 0; + texture_desc.MiscFlags = 0; + D3D10_SUBRESOURCE_DATA texture_data; + texture_data.pSysMem = converted_data.get(); + texture_data.SysMemPitch = texture_desc.Width * 4; + texture_data.SysMemSlicePitch = 0; + RETURN_IF_FAILED(device_->CreateTexture2D(&texture_desc, + &texture_data, + texture_.Receive())); + RETURN_IF_FAILED( + device_->CreateShaderResourceView(texture_.get(), NULL, + shader_view_.Receive())); + } else { + // Only part of the texture was updated. + DCHECK(texture_.get()); + D3D10_BOX dst_box = { origin.x(), origin.y(), 0, + origin.x() + bitmap.width(), + origin.y() + bitmap.height(), 0 }; + device_->UpdateSubresource(texture_.get(), 0, &dst_box, + converted_data.get(), bitmap.width() * 4, 0); + } +} + +void ViewTexture::Draw(const ui::Transform& transform) { + host_->UpdatePerspective(transform, view_size_); + + // Make texture active. + RETURN_IF_FAILED( + effect_->GetVariableByName("textureMap")->AsShaderResource()-> + SetResource(shader_view_.get())); + + ID3D10EffectTechnique* technique = effect_->GetTechniqueByName("ViewTech"); + DCHECK(technique); + D3D10_TECHNIQUE_DESC tech_desc; + technique->GetDesc(&tech_desc); + for(UINT p = 0; p < tech_desc.Passes; ++p) + technique->GetPassByIndex(p)->Apply(0); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer* vertex_buffer = vertex_buffer_.get(); + device_->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset); + device_->IASetIndexBuffer(index_buffer_.get(), DXGI_FORMAT_R32_UINT, 0); + device_->DrawIndexed(6, 0, 0); +} + +void ViewTexture::Errored(HRESULT result) { + // TODO: figure out error handling. + DCHECK(false); +} + +void ViewTexture::ConvertBitmapToD3DData(const SkBitmap& bitmap, + scoped_array<uint32>* converted_data) { + int width = bitmap.width(); + int height = bitmap.height(); + SkAutoLockPixels pixel_lock(bitmap); + // D3D wants the data in a different format (and not pre-multiplied). + converted_data->reset(new uint32[width * height]); + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + SkColor color = bitmap.getColor(x, y); + int alpha = SkColorGetA(color); + (*converted_data)[y * width + x] = + (SkColorGetA(color) << 24) | + (SkColorGetB(color) << 16) | + (SkColorGetG(color) << 8) | + (SkColorGetR(color)); + } + } +} + +void ViewTexture::CreateVertexBuffer(const gfx::Size& size) { + vertex_buffer_.Release(); + const gfx::Size& host_size = host_->GetHostSize(); + float x = static_cast<float>(host_size.width()) / 2.0f; + float y = static_cast<float>(host_size.height()) / 2.0f; + float w = static_cast<float>(size.width()); + float h = static_cast<float>(size.height()); + Vertex vertices[] = { + { D3DXVECTOR3(0.0f, -h, 0.0f), D3DXVECTOR2(0.0f, 1.0f) }, + { D3DXVECTOR3(0.0f, 0.0f, 0.0f), D3DXVECTOR2(0.0f, 0.0f) }, + { D3DXVECTOR3( w, 0.0f, 0.0f), D3DXVECTOR2(1.0f, 0.0f) }, + { D3DXVECTOR3( w, -h, 0.0f), D3DXVECTOR2(1.0f, 1.0f) }, + }; + + // Create the vertex buffer containing the points. + D3D10_BUFFER_DESC buffer_desc; + buffer_desc.Usage = D3D10_USAGE_IMMUTABLE; + buffer_desc.ByteWidth = sizeof(Vertex) * ARRAYSIZE_UNSAFE(vertices); + buffer_desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; + buffer_desc.CPUAccessFlags = 0; + buffer_desc.MiscFlags = 0; + D3D10_SUBRESOURCE_DATA init_data; + init_data.pSysMem = vertices; + RETURN_IF_FAILED(device_->CreateBuffer(&buffer_desc, &init_data, + vertex_buffer_.Receive())); +} + +void ViewTexture::CreateIndexBuffer() { + index_buffer_.Release(); + + // Then the index buffer. + DWORD indices[] = { + 0, 1, 2, + 0, 2, 3, + }; + D3D10_BUFFER_DESC index_buffer; + index_buffer.Usage = D3D10_USAGE_IMMUTABLE; + index_buffer.ByteWidth = sizeof(DWORD) * ARRAYSIZE_UNSAFE(indices); + index_buffer.BindFlags = D3D10_BIND_INDEX_BUFFER; + index_buffer.CPUAccessFlags = 0; + index_buffer.MiscFlags = 0; + D3D10_SUBRESOURCE_DATA init_data2; + init_data2.pSysMem = indices; + RETURN_IF_FAILED(device_->CreateBuffer(&index_buffer, &init_data2, + index_buffer_.Receive())); +} + +// D3D 10 Compositor implementation. +class CompositorWin : public Compositor, public ViewTextureHost { + public: + explicit CompositorWin(gfx::AcceleratedWidget widget); + + void Init(); + + // ViewTextureHost. + virtual void UpdatePerspective(const ui::Transform& transform, + const gfx::Size& view_size) OVERRIDE; + virtual const gfx::Size& GetHostSize() OVERRIDE; + + // Compositor: + virtual Texture* CreateTexture() OVERRIDE; + virtual void NotifyStart() OVERRIDE; + virtual void NotifyEnd() OVERRIDE; + + private: + ~CompositorWin(); + + void Errored(HRESULT error_code); + + // Returns the bounds of the hosting window. + gfx::Rect HostBounds(); + + void CreateDevice(); + + void LoadEffects(); + + void InitVertexLayout(); + + void Resize(const gfx::Rect& bounds); + + gfx::AcceleratedWidget host_; + + // Bounds the device was last created at. + gfx::Rect last_bounds_; + + ScopedComPtr<ID3D10Device> device_; + ScopedComPtr<IDXGISwapChain> swap_chain_; + ScopedComPtr<ID3D10RenderTargetView> render_target_view_; + ScopedComPtr<ID3D10Texture2D> depth_stencil_buffer_; + ScopedComPtr<ID3D10DepthStencilView> depth_stencil_view_; + ScopedComPtr<ID3D10Effect, NULL> fx_; + ID3D10EffectTechnique* technique_; + ScopedComPtr<ID3D10InputLayout> vertex_layout_; + + DISALLOW_COPY_AND_ASSIGN(CompositorWin); +}; + +CompositorWin::CompositorWin(gfx::AcceleratedWidget widget) + : host_(widget), + technique_(NULL) { +} + +void CompositorWin::Init() { + CreateDevice(); + LoadEffects(); + Resize(last_bounds_); + InitVertexLayout(); +} + +void CompositorWin::UpdatePerspective(const ui::Transform& transform, + const gfx::Size& view_size) { + // Apply transform from view. + const SkMatrix& sk_matrix(transform.matrix()); + // Use -1 * kMTransY for y-translation as origin for views is upper left. + D3DXMATRIX transform_matrix( + // row 1 + sk_matrix[SkMatrix::kMScaleX], sk_matrix[SkMatrix::kMSkewX], 0.0f, + sk_matrix[SkMatrix::kMPersp0], + // row 2 + sk_matrix[SkMatrix::kMSkewY], sk_matrix[SkMatrix::kMScaleY], 0.0f, + sk_matrix[SkMatrix::kMPersp1], + // row 3 + 0.0f, 0.0f, 1.0f, sk_matrix[SkMatrix::kMPersp2], + // row 4. + sk_matrix[SkMatrix::kMTransX], -sk_matrix[SkMatrix::kMTransY], 0.0f, + 1.0f); + + // Scale so x and y are from 0-2. + D3DXMATRIX scale_matrix; + D3DXMatrixScaling( + &scale_matrix, + 2.0f / static_cast<float>(last_bounds_.width()), + 2.0f / static_cast<float>(last_bounds_.height()), + 1.0f); + + // Translate so x and y are from -1,-1 to 1,1. + D3DXMATRIX translate_matrix; + D3DXMatrixTranslation(&translate_matrix, -1.0f, 1.0f, 0.0f); + + D3DXMATRIX projection_matrix; + D3DXMatrixIdentity(&projection_matrix); + D3DXMatrixPerspectiveFovLH(&projection_matrix, + atanf(.5f) * 2.0f, 1.0f, 1.0f, 1000.0f); + D3DXVECTOR3 pos(0.0f, 0.0f, -2.0f); + D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); + D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); + D3DXMATRIX view; + D3DXMatrixIdentity(&view); + D3DXMatrixLookAtLH(&view, &pos, &target, &up); + + D3DXMATRIX wvp = transform_matrix * scale_matrix * translate_matrix * view * + projection_matrix; + fx_->GetVariableByName("gWVP")->AsMatrix()->SetMatrix((float*)&wvp); +} + +const gfx::Size& CompositorWin::GetHostSize() { + return last_bounds_.size(); +} + +Texture* CompositorWin::CreateTexture() { + ViewTexture* texture = new ViewTexture(this, device_.get(), fx_.get()); + texture->Init(); + return texture; +} + +void CompositorWin::NotifyStart() { + gfx::Rect bounds = HostBounds(); + if (bounds != last_bounds_) + Resize(bounds); + + // Clear the background and stencil view. + device_->ClearRenderTargetView(render_target_view_.get(), + D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f)); + device_->ClearDepthStencilView( + depth_stencil_view_.get(), D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, + 1.0f, 0); + + // TODO: these steps may not be necessary each time through. + device_->OMSetDepthStencilState(0, 0); + float blend_factors[] = {0.0f, 0.0f, 0.0f, 0.0f}; + device_->OMSetBlendState(0, blend_factors, 0xffffffff); + device_->IASetInputLayout(vertex_layout_.get()); + device_->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); +} + +void CompositorWin::NotifyEnd() { + swap_chain_->Present(0, 0); + + // We may delete the shader resource view before drawing again. Unset it so + // that d3d doesn't generate a warning when we do that. + fx_->GetVariableByName("textureMap")->AsShaderResource()-> + SetResource(NULL); + D3D10_TECHNIQUE_DESC tech_desc; + technique_->GetDesc(&tech_desc); + for(UINT i = 0; i < tech_desc.Passes; ++i) + technique_->GetPassByIndex(i)->Apply(0); +} + +CompositorWin::~CompositorWin() { +} + +void CompositorWin::Errored(HRESULT error_code) { + // TODO: figure out error handling. + DCHECK(false); +} + +gfx::Rect CompositorWin::HostBounds() { + RECT client_rect; + GetClientRect(host_, &client_rect); + return gfx::Rect(client_rect); +} + +void CompositorWin::CreateDevice() { + last_bounds_ = HostBounds(); + + DXGI_SWAP_CHAIN_DESC sd; + sd.BufferDesc.Width = last_bounds_.width(); + sd.BufferDesc.Height = last_bounds_.height(); + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + // No multisampling. + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = host_; + sd.Windowed = true; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + // Create the device. + UINT createDeviceFlags = 0; +#if !defined(NDEBUG) + createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG; +#endif + RETURN_IF_FAILED( + D3D10CreateDeviceAndSwapChain( + 0, //default adapter + D3D10_DRIVER_TYPE_HARDWARE, + 0, // no software device + createDeviceFlags, + D3D10_SDK_VERSION, + &sd, + swap_chain_.Receive(), + device_.Receive())); +} + +void CompositorWin::LoadEffects() { + DWORD shader_flags = D3D10_SHADER_ENABLE_STRICTNESS; +#if !defined(NDEBUG) + shader_flags |= D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION; +#endif + ScopedComPtr<ID3D10Blob> compilation_errors; + const base::StringPiece& fx_data = ResourceBundle::GetSharedInstance(). + GetRawDataResource(IDR_COMPOSITOR_FX); + DCHECK(!fx_data.empty()); + RETURN_IF_FAILED( + D3DX10CreateEffectFromMemory( + fx_data.data(), fx_data.size(), "compositor.fx", NULL, NULL, + "fx_4_0", shader_flags, 0, device_.get(), NULL, NULL, fx_.Receive(), + compilation_errors.Receive(), NULL)); + technique_ = fx_->GetTechniqueByName("ViewTech"); + DCHECK(technique_); +} + +void CompositorWin::InitVertexLayout() { + D3D10_INPUT_ELEMENT_DESC vertex_desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXC", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, + D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + + // Create the input layout + D3D10_PASS_DESC pass_desc; + RETURN_IF_FAILED(technique_->GetPassByIndex(0)->GetDesc(&pass_desc)); + RETURN_IF_FAILED( + device_->CreateInputLayout(vertex_desc, ARRAYSIZE_UNSAFE(vertex_desc), + pass_desc.pIAInputSignature, + pass_desc.IAInputSignatureSize, + vertex_layout_.Receive())); +} + +void CompositorWin::Resize(const gfx::Rect& bounds) { + render_target_view_ = NULL; + depth_stencil_buffer_ = NULL; + depth_stencil_view_ = NULL; + + // Resize the swap chain and recreate the render target view. + RETURN_IF_FAILED(swap_chain_->ResizeBuffers( + 1, bounds.width(), bounds.height(), DXGI_FORMAT_R8G8B8A8_UNORM, 0)); + ScopedComPtr<ID3D10Texture2D> back_buffer; + RETURN_IF_FAILED(swap_chain_->GetBuffer( + 0, __uuidof(ID3D10Texture2D), + reinterpret_cast<void**>(back_buffer.Receive()))); + RETURN_IF_FAILED(device_->CreateRenderTargetView( + back_buffer.get(), 0, render_target_view_.Receive())); + + // Create the depth/stencil buffer and view. + D3D10_TEXTURE2D_DESC depth_stencil_desc; + depth_stencil_desc.Width = bounds.width(); + depth_stencil_desc.Height = bounds.height(); + depth_stencil_desc.MipLevels = 1; + depth_stencil_desc.ArraySize = 1; + depth_stencil_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + depth_stencil_desc.SampleDesc.Count = 1; // multisampling must match + depth_stencil_desc.SampleDesc.Quality = 0; // swap chain values. + depth_stencil_desc.Usage = D3D10_USAGE_DEFAULT; + depth_stencil_desc.BindFlags = D3D10_BIND_DEPTH_STENCIL; + depth_stencil_desc.CPUAccessFlags = 0; + depth_stencil_desc.MiscFlags = 0; + + RETURN_IF_FAILED(device_->CreateTexture2D(&depth_stencil_desc, 0, + depth_stencil_buffer_.Receive())); + RETURN_IF_FAILED(device_->CreateDepthStencilView( + depth_stencil_buffer_.get(), 0, + depth_stencil_view_.Receive())); + + + // Bind the render target view and depth/stencil view to the pipeline. + ID3D10RenderTargetView* target_view = render_target_view_.get(); + device_->OMSetRenderTargets(1, &target_view, depth_stencil_view_.get()); + + // Set the viewport transform. + D3D10_VIEWPORT vp; + vp.TopLeftX = bounds.x(); + vp.TopLeftY = bounds.y(); + vp.Width = bounds.width(); + vp.Height = bounds.height(); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + + device_->RSSetViewports(1, &vp); + + last_bounds_ = bounds; +} + +} // namespace + +// static +Compositor* Compositor::Create(gfx::AcceleratedWidget widget) { + CompositorWin* compositor = new CompositorWin(widget); + compositor->Init(); + return compositor; +} + +} // namespace ui |