Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Unified Diff: ui/gfx/compositor/compositor_win.cc

Issue 7067029: Prototype compositor to render views to a texture using d3d 10. This (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix gyp Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/compositor/compositor_win.cc
diff --git a/ui/gfx/compositor/compositor_win.cc b/ui/gfx/compositor/compositor_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a303f7bba088c15a25b3adc5f7cba971c3b5c77c
--- /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

Powered by Google App Engine
This is Rietveld 408576698