summaryrefslogtreecommitdiffstats
path: root/content/gpu
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-12 01:00:41 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-12 01:00:41 +0000
commit623c0bd198a26b6609c7545a0cce0578dbad5316 (patch)
treee5cfac9d974797d95a8b4ee0aa0e4204826f089c /content/gpu
parent23716fb643383cb737e564d55234a7c2d58eba00 (diff)
downloadchromium_src-623c0bd198a26b6609c7545a0cce0578dbad5316.zip
chromium_src-623c0bd198a26b6609c7545a0cce0578dbad5316.tar.gz
chromium_src-623c0bd198a26b6609c7545a0cce0578dbad5316.tar.bz2
Move chrome\gpu to content\gpu.
TBR=avi Review URL: http://codereview.chromium.org/6684015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77903 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/gpu')
-rw-r--r--content/gpu/DEPS10
-rw-r--r--content/gpu/gpu_channel.cc260
-rw-r--r--content/gpu/gpu_channel.h127
-rw-r--r--content/gpu/gpu_command_buffer_stub.cc416
-rw-r--r--content/gpu/gpu_command_buffer_stub.h131
-rw-r--r--content/gpu/gpu_config.h13
-rw-r--r--content/gpu/gpu_dx_diagnostics_win.cc134
-rw-r--r--content/gpu/gpu_idirect3d9_mock_win.cc9
-rw-r--r--content/gpu/gpu_idirect3d9_mock_win.h77
-rw-r--r--content/gpu/gpu_info_collector.cc121
-rw-r--r--content/gpu/gpu_info_collector.h57
-rw-r--r--content/gpu/gpu_info_collector_linux.cc279
-rw-r--r--content/gpu/gpu_info_collector_mac.mm104
-rw-r--r--content/gpu/gpu_info_collector_unittest.cc177
-rw-r--r--content/gpu/gpu_info_collector_win.cc264
-rw-r--r--content/gpu/gpu_info_unittest_win.cc59
-rw-r--r--content/gpu/gpu_main.cc87
-rw-r--r--content/gpu/gpu_process.cc11
-rw-r--r--content/gpu/gpu_process.h20
-rw-r--r--content/gpu/gpu_thread.cc347
-rw-r--r--content/gpu/gpu_thread.h97
-rw-r--r--content/gpu/gpu_video_decoder.cc427
-rw-r--r--content/gpu/gpu_video_decoder.h209
-rw-r--r--content/gpu/gpu_video_decoder_unittest.cc264
-rw-r--r--content/gpu/gpu_video_service.cc82
-rw-r--r--content/gpu/gpu_video_service.h51
-rw-r--r--content/gpu/gpu_watchdog_thread.cc243
-rw-r--r--content/gpu/gpu_watchdog_thread.h74
-rw-r--r--content/gpu/media/fake_gl_video_decode_engine.cc132
-rw-r--r--content/gpu/media/fake_gl_video_decode_engine.h68
-rw-r--r--content/gpu/media/fake_gl_video_device.cc58
-rw-r--r--content/gpu/media/fake_gl_video_device.h27
-rw-r--r--content/gpu/media/gpu_video_device.h56
-rw-r--r--content/gpu/media/mft_angle_video_device.cc52
-rw-r--r--content/gpu/media/mft_angle_video_device.h42
-rw-r--r--content/gpu/x_util.cc11
-rw-r--r--content/gpu/x_util.h47
37 files changed, 4643 insertions, 0 deletions
diff --git a/content/gpu/DEPS b/content/gpu/DEPS
new file mode 100644
index 0000000..66112a0
--- /dev/null
+++ b/content/gpu/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+chrome/app",
+ "+gpu/command_buffer",
+ "+libEGL",
+ "+libGLESv2",
+ "+media/base",
+ "+media/video",
+ "+sandbox",
+ "+skia",
+]
diff --git a/content/gpu/gpu_channel.cc b/content/gpu/gpu_channel.cc
new file mode 100644
index 0000000..7f9435c
--- /dev/null
+++ b/content/gpu/gpu_channel.cc
@@ -0,0 +1,260 @@
+// 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.
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "content/gpu/gpu_channel.h"
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "content/common/child_process.h"
+#include "content/common/content_switches.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gpu_thread.h"
+#include "content/gpu/gpu_video_service.h"
+
+#if defined(OS_POSIX)
+#include "ipc/ipc_channel_posix.h"
+#endif
+
+GpuChannel::GpuChannel(GpuThread* gpu_thread,
+ int renderer_id)
+ : gpu_thread_(gpu_thread),
+ renderer_id_(renderer_id),
+ renderer_process_(NULL),
+ renderer_pid_(NULL) {
+ DCHECK(gpu_thread);
+ DCHECK(renderer_id);
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
+}
+
+GpuChannel::~GpuChannel() {
+#if defined(OS_WIN)
+ if (renderer_process_)
+ CloseHandle(renderer_process_);
+#endif
+}
+
+bool GpuChannel::OnMessageReceived(const IPC::Message& message) {
+ if (log_messages_) {
+ VLOG(1) << "received message @" << &message << " on channel @" << this
+ << " with type " << message.type();
+ }
+
+ if (message.routing_id() == MSG_ROUTING_CONTROL)
+ return OnControlMessageReceived(message);
+
+ if (!router_.RouteMessage(message)) {
+ // Respond to sync messages even if router failed to route.
+ if (message.is_sync()) {
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
+ reply->set_reply_error();
+ Send(reply);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+void GpuChannel::OnChannelError() {
+ gpu_thread_->RemoveChannel(renderer_id_);
+}
+
+void GpuChannel::OnChannelConnected(int32 peer_pid) {
+ renderer_pid_ = peer_pid;
+}
+
+bool GpuChannel::Send(IPC::Message* message) {
+ if (log_messages_) {
+ VLOG(1) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type();
+ }
+
+ if (!channel_.get()) {
+ delete message;
+ return false;
+ }
+
+ return channel_->Send(message);
+}
+
+void GpuChannel::CreateViewCommandBuffer(
+ gfx::PluginWindowHandle window,
+ int32 render_view_id,
+ const GPUCreateCommandBufferConfig& init_params,
+ int32* route_id) {
+ *route_id = MSG_ROUTING_NONE;
+
+#if defined(ENABLE_GPU)
+ *route_id = GenerateRouteID();
+ scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
+ this, window, NULL, gfx::Size(), init_params.allowed_extensions,
+ init_params.attribs, 0, *route_id, renderer_id_, render_view_id));
+ router_.AddRoute(*route_id, stub.get());
+ stubs_.AddWithID(stub.release(), *route_id);
+#endif // ENABLE_GPU
+}
+
+#if defined(OS_MACOSX)
+void GpuChannel::AcceleratedSurfaceBuffersSwapped(
+ int32 route_id, uint64 swap_buffers_count) {
+ GpuCommandBufferStub* stub = stubs_.Lookup(route_id);
+ if (stub == NULL)
+ return;
+ stub->AcceleratedSurfaceBuffersSwapped(swap_buffers_count);
+}
+
+void GpuChannel::DidDestroySurface(int32 renderer_route_id) {
+ // Since acclerated views are created in the renderer process and then sent
+ // to the browser process during GPU channel construction, it is possible that
+ // this is called before a GpuCommandBufferStub for |renderer_route_id| was
+ // put into |stubs_|. Hence, do not walk |stubs_| here but instead remember
+ // all |renderer_route_id|s this was called for and use them later.
+ destroyed_renderer_routes_.insert(renderer_route_id);
+}
+
+bool GpuChannel::IsRenderViewGone(int32 renderer_route_id) {
+ return destroyed_renderer_routes_.count(renderer_route_id) > 0;
+}
+#endif
+
+bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_Initialize, OnInitialize)
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer,
+ OnCreateOffscreenCommandBuffer)
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
+ OnDestroyCommandBuffer)
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateVideoDecoder,
+ OnCreateVideoDecoder)
+ IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyVideoDecoder,
+ OnDestroyVideoDecoder)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ DCHECK(handled);
+ return handled;
+}
+
+int GpuChannel::GenerateRouteID() {
+ static int last_id = 0;
+ return ++last_id;
+}
+
+void GpuChannel::OnInitialize(base::ProcessHandle renderer_process) {
+ // Initialize should only happen once.
+ DCHECK(!renderer_process_);
+
+ // Verify that the renderer has passed its own process handle.
+ if (base::GetProcId(renderer_process) == renderer_pid_)
+ renderer_process_ = renderer_process;
+}
+
+void GpuChannel::OnCreateOffscreenCommandBuffer(
+ int32 parent_route_id,
+ const gfx::Size& size,
+ const GPUCreateCommandBufferConfig& init_params,
+ uint32 parent_texture_id,
+ int32* route_id) {
+#if defined(ENABLE_GPU)
+ *route_id = GenerateRouteID();
+ GpuCommandBufferStub* parent_stub = NULL;
+ if (parent_route_id != 0)
+ parent_stub = stubs_.Lookup(parent_route_id);
+
+ scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
+ this,
+ gfx::kNullPluginWindow,
+ parent_stub,
+ size,
+ init_params.allowed_extensions,
+ init_params.attribs,
+ parent_texture_id,
+ *route_id,
+ 0, 0));
+ router_.AddRoute(*route_id, stub.get());
+ stubs_.AddWithID(stub.release(), *route_id);
+#else
+ *route_id = MSG_ROUTING_NONE;
+#endif
+}
+
+void GpuChannel::OnDestroyCommandBuffer(int32 route_id) {
+#if defined(ENABLE_GPU)
+ if (router_.ResolveRoute(route_id)) {
+ router_.RemoveRoute(route_id);
+ stubs_.Remove(route_id);
+ }
+#endif
+}
+
+void GpuChannel::OnCreateVideoDecoder(int32 context_route_id,
+ int32 decoder_host_id) {
+// TODO(cevans): do NOT re-enable this until GpuVideoService has been checked
+// for integer overflows, including the classic "width * height" overflow.
+#if 0
+ VLOG(1) << "GpuChannel::OnCreateVideoDecoder";
+ GpuVideoService* service = GpuVideoService::GetInstance();
+ if (service == NULL) {
+ // TODO(hclam): Need to send a failure message.
+ return;
+ }
+
+ // The context ID corresponds to the command buffer used.
+ GpuCommandBufferStub* stub = stubs_.Lookup(context_route_id);
+ int32 decoder_id = GenerateRouteID();
+
+ // TODO(hclam): Need to be careful about the lifetime of the command buffer
+ // decoder.
+ bool ret = service->CreateVideoDecoder(
+ this, &router_, decoder_host_id, decoder_id,
+ stub->processor()->decoder());
+ DCHECK(ret) << "Failed to create a GpuVideoDecoder";
+#endif
+}
+
+void GpuChannel::OnDestroyVideoDecoder(int32 decoder_id) {
+#if defined(ENABLE_GPU)
+ LOG(ERROR) << "GpuChannel::OnDestroyVideoDecoder";
+ GpuVideoService* service = GpuVideoService::GetInstance();
+ if (service == NULL)
+ return;
+ service->DestroyVideoDecoder(&router_, decoder_id);
+#endif
+}
+
+bool GpuChannel::Init() {
+ // Check whether we're already initialized.
+ if (channel_.get())
+ return true;
+
+ // Map renderer ID to a (single) channel to that process.
+ std::string channel_name = GetChannelName();
+ channel_.reset(new IPC::SyncChannel(
+ channel_name, IPC::Channel::MODE_SERVER, this,
+ ChildProcess::current()->io_message_loop(), false,
+ ChildProcess::current()->GetShutDownEvent()));
+
+ return true;
+}
+
+std::string GpuChannel::GetChannelName() {
+ return StringPrintf("%d.r%d.gpu", base::GetCurrentProcId(), renderer_id_);
+}
+
+#if defined(OS_POSIX)
+int GpuChannel::GetRendererFileDescriptor() {
+ int fd = -1;
+ if (channel_.get()) {
+ fd = channel_->GetClientFileDescriptor();
+ }
+ return fd;
+}
+#endif // defined(OS_POSIX)
+
diff --git a/content/gpu/gpu_channel.h b/content/gpu/gpu_channel.h
new file mode 100644
index 0000000..5ad31e3
--- /dev/null
+++ b/content/gpu/gpu_channel.h
@@ -0,0 +1,127 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_CHANNEL_H_
+#define CONTENT_GPU_GPU_CHANNEL_H_
+#pragma once
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/id_map.h"
+#include "base/process.h"
+#include "base/scoped_ptr.h"
+#include "build/build_config.h"
+#include "content/common/message_router.h"
+#include "content/gpu/gpu_command_buffer_stub.h"
+#include "ipc/ipc_sync_channel.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+class GpuThread;
+struct GPUCreateCommandBufferConfig;
+
+// Encapsulates an IPC channel between the GPU process and one renderer
+// process. On the renderer side there's a corresponding GpuChannelHost.
+class GpuChannel : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public base::RefCountedThreadSafe<GpuChannel> {
+ public:
+ // Takes ownership of the renderer process handle.
+ GpuChannel(GpuThread* gpu_thread,
+ int renderer_id);
+ virtual ~GpuChannel();
+
+ bool Init();
+
+ // Get the GpuThread that owns this channel.
+ GpuThread* gpu_thread() const { return gpu_thread_; }
+
+ // Returns the name of the associated IPC channel.
+ std::string GetChannelName();
+
+#if defined(OS_POSIX)
+ int GetRendererFileDescriptor();
+#endif // defined(OS_POSIX)
+
+ base::ProcessHandle renderer_process() const {
+ return renderer_process_;
+ }
+
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+ virtual void OnChannelError();
+ virtual void OnChannelConnected(int32 peer_pid);
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+
+ void CreateViewCommandBuffer(
+ gfx::PluginWindowHandle window,
+ int32 render_view_id,
+ const GPUCreateCommandBufferConfig& init_params,
+ int32* route_id);
+
+#if defined(OS_MACOSX)
+ virtual void AcceleratedSurfaceBuffersSwapped(
+ int32 route_id, uint64 swap_buffers_count);
+ void DidDestroySurface(int32 renderer_route_id);
+
+ bool IsRenderViewGone(int32 renderer_route_id);
+#endif
+
+ private:
+ bool OnControlMessageReceived(const IPC::Message& msg);
+
+ int GenerateRouteID();
+
+ // Message handlers.
+ void OnInitialize(base::ProcessHandle renderer_process);
+ void OnCreateOffscreenCommandBuffer(
+ int32 parent_route_id,
+ const gfx::Size& size,
+ const GPUCreateCommandBufferConfig& init_params,
+ uint32 parent_texture_id,
+ int32* route_id);
+ void OnDestroyCommandBuffer(int32 route_id);
+
+ void OnCreateVideoDecoder(int32 context_route_id,
+ int32 decoder_host_id);
+ void OnDestroyVideoDecoder(int32 decoder_id);
+
+ // The lifetime of objects of this class is managed by a GpuThread. The
+ // GpuThreadss destroy all the GpuChannels that they own when they
+ // are destroyed. So a raw pointer is safe.
+ GpuThread* gpu_thread_;
+
+ scoped_ptr<IPC::SyncChannel> channel_;
+
+ // The id of the renderer who is on the other side of the channel.
+ int renderer_id_;
+
+ // Handle to the renderer process that is on the other side of the channel.
+ base::ProcessHandle renderer_process_;
+
+ // The process id of the renderer process.
+ base::ProcessId renderer_pid_;
+
+ // Used to implement message routing functionality to CommandBuffer objects
+ MessageRouter router_;
+
+#if defined(ENABLE_GPU)
+ typedef IDMap<GpuCommandBufferStub, IDMapOwnPointer> StubMap;
+ StubMap stubs_;
+
+#if defined(OS_MACOSX)
+ std::set<int32> destroyed_renderer_routes_;
+#endif // defined (OS_MACOSX)
+#endif // defined (ENABLE_GPU)
+
+ bool log_messages_; // True if we should log sent and received messages.
+
+ DISALLOW_COPY_AND_ASSIGN(GpuChannel);
+};
+
+#endif // CONTENT_GPU_GPU_CHANNEL_H_
diff --git a/content/gpu/gpu_command_buffer_stub.cc b/content/gpu/gpu_command_buffer_stub.cc
new file mode 100644
index 0000000..a67dfd5
--- /dev/null
+++ b/content/gpu/gpu_command_buffer_stub.cc
@@ -0,0 +1,416 @@
+// 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.
+
+#if defined(ENABLE_GPU)
+
+#include "base/process_util.h"
+#include "base/shared_memory.h"
+#include "build/build_config.h"
+#include "content/common/child_thread.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gpu_channel.h"
+#include "content/gpu/gpu_command_buffer_stub.h"
+#include "content/gpu/gpu_thread.h"
+
+using gpu::Buffer;
+
+#if defined(OS_WIN)
+#define kCompositorWindowOwner L"CompositorWindowOwner"
+#endif // defined(OS_WIN)
+
+GpuCommandBufferStub::GpuCommandBufferStub(
+ GpuChannel* channel,
+ gfx::PluginWindowHandle handle,
+ GpuCommandBufferStub* parent,
+ const gfx::Size& size,
+ const std::string& allowed_extensions,
+ const std::vector<int32>& attribs,
+ uint32 parent_texture_id,
+ int32 route_id,
+ int32 renderer_id,
+ int32 render_view_id)
+ : channel_(channel),
+ handle_(handle),
+ parent_(
+ parent ? parent->AsWeakPtr() : base::WeakPtr<GpuCommandBufferStub>()),
+ initial_size_(size),
+ allowed_extensions_(allowed_extensions),
+ requested_attribs_(attribs),
+ parent_texture_id_(parent_texture_id),
+ route_id_(route_id),
+#if defined(OS_WIN)
+ compositor_window_(NULL),
+#endif // defined(OS_WIN)
+ renderer_id_(renderer_id),
+ render_view_id_(render_view_id) {
+}
+
+#if defined(OS_WIN)
+static LRESULT CALLBACK CompositorWindowProc(
+ HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ switch (message) {
+ case WM_ERASEBKGND:
+ return 0;
+ case WM_DESTROY:
+ RemoveProp(hwnd, kCompositorWindowOwner);
+ return 0;
+ case WM_PAINT: {
+ PAINTSTRUCT paint;
+ HDC dc = BeginPaint(hwnd, &paint);
+ if (dc) {
+ HANDLE h = GetProp(hwnd, kCompositorWindowOwner);
+ if (h) {
+ GpuCommandBufferStub* stub =
+ reinterpret_cast<GpuCommandBufferStub*>(h);
+ stub->OnCompositorWindowPainted();
+ }
+ EndPaint(hwnd, &paint);
+ }
+ break;
+ }
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ return 0;
+}
+
+bool GpuCommandBufferStub::CreateCompositorWindow() {
+ DCHECK(handle_ != gfx::kNullPluginWindow);
+ HWND host_window = static_cast<HWND>(handle_);
+
+ // Create the compositor window itself.
+ DCHECK(host_window);
+ static ATOM window_class = 0;
+ if (!window_class) {
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = 0;
+ wcex.lpfnWndProc = CompositorWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = NULL;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"CompositorWindowClass";
+ wcex.hIconSm = 0;
+ window_class = RegisterClassEx(&wcex);
+ DCHECK(window_class);
+ }
+
+ HWND compositor_window = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ MAKEINTATOM(window_class),
+ 0,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED,
+ 0, 0,
+ 0, 0,
+ host_window,
+ 0,
+ GetModuleHandle(NULL),
+ 0);
+ if (!compositor_window) {
+ compositor_window_ = gfx::kNullPluginWindow;
+ return false;
+ }
+ SetProp(compositor_window, kCompositorWindowOwner,
+ reinterpret_cast<HANDLE>(this));
+
+ RECT parent_rect;
+ GetClientRect(host_window, &parent_rect);
+
+ UINT flags = SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER |
+ SWP_NOACTIVATE | SWP_DEFERERASE | SWP_SHOWWINDOW;
+ SetWindowPos(compositor_window,
+ NULL,
+ 0, 0,
+ parent_rect.right - parent_rect.left,
+ parent_rect.bottom - parent_rect.top,
+ flags);
+ compositor_window_ = static_cast<gfx::PluginWindowHandle>(compositor_window);
+ return true;
+}
+
+void GpuCommandBufferStub::OnCompositorWindowPainted() {
+ GpuThread* gpu_thread = channel_->gpu_thread();
+ gpu_thread->Send(new GpuHostMsg_ScheduleComposite(
+ renderer_id_, render_view_id_));
+}
+#endif // defined(OS_WIN)
+
+
+GpuCommandBufferStub::~GpuCommandBufferStub() {
+ if (processor_.get()) {
+ processor_->Destroy();
+ }
+#if defined(OS_WIN)
+ if (compositor_window_) {
+ DestroyWindow(static_cast<HWND>(compositor_window_));
+ compositor_window_ = NULL;
+ }
+#endif // defined(OS_WIN)
+
+ GpuThread* gpu_thread = channel_->gpu_thread();
+ gpu_thread->Send(new GpuHostMsg_DestroyCommandBuffer(
+ handle_, renderer_id_, render_view_id_));
+}
+
+bool GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GpuCommandBufferStub, message)
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Initialize, OnInitialize);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_GetState, OnGetState);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncGetState, OnAsyncGetState);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Flush, OnFlush);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncFlush, OnAsyncFlush);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateTransferBuffer,
+ OnCreateTransferBuffer);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_RegisterTransferBuffer,
+ OnRegisterTransferBuffer);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyTransferBuffer,
+ OnDestroyTransferBuffer);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_GetTransferBuffer,
+ OnGetTransferBuffer);
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ResizeOffscreenFrameBuffer,
+ OnResizeOffscreenFrameBuffer);
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetWindowSize, OnSetWindowSize);
+#endif // defined(OS_MACOSX)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ DCHECK(handled);
+ return handled;
+}
+
+bool GpuCommandBufferStub::Send(IPC::Message* message) {
+ return channel_->Send(message);
+}
+
+void GpuCommandBufferStub::OnInitialize(
+ base::SharedMemoryHandle ring_buffer,
+ int32 size,
+ bool* result) {
+ DCHECK(!command_buffer_.get());
+
+ *result = false;
+
+ command_buffer_.reset(new gpu::CommandBufferService);
+
+ // Create the child window, if needed
+#if defined(OS_WIN)
+ gfx::PluginWindowHandle output_window_handle;
+ if (handle_) {
+ if (!CreateCompositorWindow()) {
+ return;
+ }
+ output_window_handle = compositor_window_;
+ } else {
+ output_window_handle = handle_;
+ }
+#else
+ gfx::PluginWindowHandle output_window_handle = handle_;
+#endif // defined(OS_WIN)
+
+#if defined(OS_WIN)
+ // Windows dups the shared memory handle it receives into the current process
+ // and closes it when this variable goes out of scope.
+ base::SharedMemory shared_memory(ring_buffer,
+ false,
+ channel_->renderer_process());
+#else
+ // POSIX receives a dup of the shared memory handle and closes the dup when
+ // this variable goes out of scope.
+ base::SharedMemory shared_memory(ring_buffer, false);
+#endif
+
+ // Initialize the CommandBufferService and GPUProcessor.
+ if (command_buffer_->Initialize(&shared_memory, size)) {
+ gpu::GPUProcessor* parent_processor =
+ parent_ ? parent_->processor_.get() : NULL;
+ processor_.reset(new gpu::GPUProcessor(command_buffer_.get(), NULL));
+ if (processor_->Initialize(
+ output_window_handle,
+ initial_size_,
+ allowed_extensions_.c_str(),
+ requested_attribs_,
+ parent_processor,
+ parent_texture_id_)) {
+ command_buffer_->SetPutOffsetChangeCallback(
+ NewCallback(processor_.get(),
+ &gpu::GPUProcessor::ProcessCommands));
+ processor_->SetSwapBuffersCallback(
+ NewCallback(this, &GpuCommandBufferStub::OnSwapBuffers));
+
+#if defined(OS_MACOSX)
+ if (handle_) {
+ // This context conceptually puts its output directly on the
+ // screen, rendered by the accelerated plugin layer in
+ // RenderWidgetHostViewMac. Set up a pathway to notify the
+ // browser process when its contents change.
+ processor_->SetSwapBuffersCallback(
+ NewCallback(this,
+ &GpuCommandBufferStub::SwapBuffersCallback));
+ }
+#endif // defined(OS_MACOSX)
+
+ // Set up a pathway for resizing the output window or framebuffer at the
+ // right time relative to other GL commands.
+ processor_->SetResizeCallback(
+ NewCallback(this, &GpuCommandBufferStub::ResizeCallback));
+
+ *result = true;
+ } else {
+ processor_.reset();
+ command_buffer_.reset();
+ }
+ }
+}
+
+void GpuCommandBufferStub::OnGetState(gpu::CommandBuffer::State* state) {
+ *state = command_buffer_->GetState();
+}
+
+void GpuCommandBufferStub::OnAsyncGetState() {
+ gpu::CommandBuffer::State state = command_buffer_->GetState();
+ Send(new GpuCommandBufferMsg_UpdateState(route_id_, state));
+}
+
+void GpuCommandBufferStub::OnFlush(int32 put_offset,
+ gpu::CommandBuffer::State* state) {
+#if defined(OS_MACOSX)
+ // See comment in |DidDestroySurface()| in gpu_processor_mac.cc.
+ if (channel_->IsRenderViewGone(render_view_id_))
+ processor_->DidDestroySurface();
+#endif
+ *state = command_buffer_->FlushSync(put_offset);
+}
+
+void GpuCommandBufferStub::OnAsyncFlush(int32 put_offset) {
+ gpu::CommandBuffer::State state = command_buffer_->FlushSync(put_offset);
+ Send(new GpuCommandBufferMsg_UpdateState(route_id_, state));
+}
+
+void GpuCommandBufferStub::OnCreateTransferBuffer(int32 size, int32* id) {
+ *id = command_buffer_->CreateTransferBuffer(size);
+}
+
+void GpuCommandBufferStub::OnRegisterTransferBuffer(
+ base::SharedMemoryHandle transfer_buffer,
+ size_t size,
+ int32* id) {
+#if defined(OS_WIN)
+ // Windows dups the shared memory handle it receives into the current process
+ // and closes it when this variable goes out of scope.
+ base::SharedMemory shared_memory(transfer_buffer,
+ false,
+ channel_->renderer_process());
+#else
+ // POSIX receives a dup of the shared memory handle and closes the dup when
+ // this variable goes out of scope.
+ base::SharedMemory shared_memory(transfer_buffer, false);
+#endif
+
+ *id = command_buffer_->RegisterTransferBuffer(&shared_memory, size);
+}
+
+void GpuCommandBufferStub::OnDestroyTransferBuffer(int32 id) {
+ command_buffer_->DestroyTransferBuffer(id);
+}
+
+void GpuCommandBufferStub::OnGetTransferBuffer(
+ int32 id,
+ base::SharedMemoryHandle* transfer_buffer,
+ uint32* size) {
+ *transfer_buffer = base::SharedMemoryHandle();
+ *size = 0;
+
+ // Fail if the renderer process has not provided its process handle.
+ if (!channel_->renderer_process())
+ return;
+
+ Buffer buffer = command_buffer_->GetTransferBuffer(id);
+ if (buffer.shared_memory) {
+ // Assume service is responsible for duplicating the handle to the calling
+ // process.
+ buffer.shared_memory->ShareToProcess(channel_->renderer_process(),
+ transfer_buffer);
+ *size = buffer.size;
+ }
+}
+
+void GpuCommandBufferStub::OnResizeOffscreenFrameBuffer(const gfx::Size& size) {
+ processor_->ResizeOffscreenFrameBuffer(size);
+}
+
+void GpuCommandBufferStub::OnSwapBuffers() {
+ Send(new GpuCommandBufferMsg_SwapBuffers(route_id_));
+}
+
+#if defined(OS_MACOSX)
+void GpuCommandBufferStub::OnSetWindowSize(const gfx::Size& size) {
+ GpuThread* gpu_thread = channel_->gpu_thread();
+ // Try using the IOSurface version first.
+ uint64 new_backing_store = processor_->SetWindowSizeForIOSurface(size);
+ if (new_backing_store) {
+ GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params params;
+ params.renderer_id = renderer_id_;
+ params.render_view_id = render_view_id_;
+ params.window = handle_;
+ params.width = size.width();
+ params.height = size.height();
+ params.identifier = new_backing_store;
+ gpu_thread->Send(new GpuHostMsg_AcceleratedSurfaceSetIOSurface(params));
+ } else {
+ // TODO(kbr): figure out what to do here. It wouldn't be difficult
+ // to support the compositor on 10.5, but the performance would be
+ // questionable.
+ NOTREACHED();
+ }
+}
+
+void GpuCommandBufferStub::SwapBuffersCallback() {
+ OnSwapBuffers();
+ GpuThread* gpu_thread = channel_->gpu_thread();
+ GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
+ params.renderer_id = renderer_id_;
+ params.render_view_id = render_view_id_;
+ params.window = handle_;
+ params.surface_id = processor_->GetSurfaceId();
+ params.route_id = route_id();
+ params.swap_buffers_count = processor_->swap_buffers_count();
+ gpu_thread->Send(new GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
+}
+
+void GpuCommandBufferStub::AcceleratedSurfaceBuffersSwapped(
+ uint64 swap_buffers_count) {
+ processor_->set_acknowledged_swap_buffers_count(swap_buffers_count);
+ // Wake up the GpuProcessor to start doing work again.
+ processor_->ScheduleProcessCommands();
+}
+#endif // defined(OS_MACOSX)
+
+void GpuCommandBufferStub::ResizeCallback(gfx::Size size) {
+ if (handle_ == gfx::kNullPluginWindow) {
+ processor_->decoder()->ResizeOffscreenFrameBuffer(size);
+ processor_->decoder()->UpdateOffscreenFrameBufferSize();
+ } else {
+#if defined(OS_LINUX) && !defined(TOUCH_UI)
+ GpuThread* gpu_thread = channel_->gpu_thread();
+ bool result = false;
+ gpu_thread->Send(
+ new GpuHostMsg_ResizeXID(handle_, size, &result));
+#elif defined(OS_WIN)
+ HWND hwnd = static_cast<HWND>(compositor_window_);
+ UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS |
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE;
+ SetWindowPos(hwnd, NULL, 0, 0, size.width(), size.height(), swp_flags);
+#endif // defined(OS_LINUX)
+ }
+}
+
+#endif // defined(ENABLE_GPU)
diff --git a/content/gpu/gpu_command_buffer_stub.h b/content/gpu/gpu_command_buffer_stub.h
new file mode 100644
index 0000000..69fbcc0
--- /dev/null
+++ b/content/gpu/gpu_command_buffer_stub.h
@@ -0,0 +1,131 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_COMMAND_BUFFER_STUB_H_
+#define CONTENT_GPU_GPU_COMMAND_BUFFER_STUB_H_
+#pragma once
+
+#if defined(ENABLE_GPU)
+
+#include <vector>
+#include <string>
+
+#include "base/process.h"
+#include "base/weak_ptr.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/gpu_processor.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_message.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+class GpuChannel;
+
+class GpuCommandBufferStub
+ : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public base::SupportsWeakPtr<GpuCommandBufferStub> {
+ public:
+ GpuCommandBufferStub(GpuChannel* channel,
+ gfx::PluginWindowHandle handle,
+ GpuCommandBufferStub* parent,
+ const gfx::Size& size,
+ const std::string& allowed_extensions,
+ const std::vector<int32>& attribs,
+ uint32 parent_texture_id,
+ int32 route_id,
+ int32 renderer_id,
+ int32 render_view_id);
+
+ virtual ~GpuCommandBufferStub();
+
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+
+ // Get the GLContext associated with this object.
+ gpu::GPUProcessor* processor() const { return processor_.get(); }
+
+ // Identifies the various GpuCommandBufferStubs in the GPU process belonging
+ // to the same renderer process.
+ int32 route_id() const { return route_id_; }
+
+ // Identifies the various render views in the renderer process.
+ int32 renderer_route_id() const { return renderer_id_; }
+
+#if defined(OS_WIN)
+ // Called only by the compositor window's window proc
+ void OnCompositorWindowPainted();
+#endif // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+ // Called only by the GpuChannel.
+ void AcceleratedSurfaceBuffersSwapped(uint64 swap_buffers_count);
+#endif // defined(OS_MACOSX)
+
+ private:
+ // Message handlers:
+ void OnInitialize(base::SharedMemoryHandle ring_buffer,
+ int32 size,
+ bool* result);
+ void OnGetState(gpu::CommandBuffer::State* state);
+ void OnAsyncGetState();
+ void OnFlush(int32 put_offset, gpu::CommandBuffer::State* state);
+ void OnAsyncFlush(int32 put_offset);
+ void OnCreateTransferBuffer(int32 size, int32* id);
+ void OnRegisterTransferBuffer(base::SharedMemoryHandle transfer_buffer,
+ size_t size,
+ int32* id);
+ void OnDestroyTransferBuffer(int32 id);
+ void OnGetTransferBuffer(int32 id,
+ base::SharedMemoryHandle* transfer_buffer,
+ uint32* size);
+ void OnResizeOffscreenFrameBuffer(const gfx::Size& size);
+
+ void OnSwapBuffers();
+
+#if defined(OS_MACOSX)
+ void OnSetWindowSize(const gfx::Size& size);
+ void SwapBuffersCallback();
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_WIN)
+ bool CreateCompositorWindow();
+#endif // defined(OS_WIN)
+
+ void ResizeCallback(gfx::Size size);
+
+ // The lifetime of objects of this class is managed by a GpuChannel. The
+ // GpuChannels destroy all the GpuCommandBufferStubs that they own when they
+ // are destroyed. So a raw pointer is safe.
+ GpuChannel* channel_;
+
+ gfx::PluginWindowHandle handle_;
+ base::WeakPtr<GpuCommandBufferStub> parent_;
+ gfx::Size initial_size_;
+ std::string allowed_extensions_;
+ std::vector<int32> requested_attribs_;
+ uint32 parent_texture_id_;
+ int32 route_id_;
+
+#if defined(OS_WIN)
+ HWND compositor_window_;
+#endif // defined(OS_WIN)
+
+ // The following two fields are used on Mac OS X to identify the window
+ // for the rendering results on the browser side.
+ int32 renderer_id_;
+ int32 render_view_id_;
+
+ scoped_ptr<gpu::CommandBufferService> command_buffer_;
+ scoped_ptr<gpu::GPUProcessor> processor_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferStub);
+};
+
+#endif // defined(ENABLE_GPU)
+
+#endif // CONTENT_GPU_GPU_COMMAND_BUFFER_STUB_H_
diff --git a/content/gpu/gpu_config.h b/content/gpu/gpu_config.h
new file mode 100644
index 0000000..8aba7bf
--- /dev/null
+++ b/content/gpu/gpu_config.h
@@ -0,0 +1,13 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_CONFIG_H_
+#define CONTENT_GPU_GPU_CONFIG_H_
+#pragma once
+
+// This file declares common preprocessor configuration for the GPU process.
+
+#include "build/build_config.h"
+
+#endif // CONTENT_GPU_GPU_CONFIG_H_
diff --git a/content/gpu/gpu_dx_diagnostics_win.cc b/content/gpu/gpu_dx_diagnostics_win.cc
new file mode 100644
index 0000000..c36ea8f
--- /dev/null
+++ b/content/gpu/gpu_dx_diagnostics_win.cc
@@ -0,0 +1,134 @@
+// 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.
+//
+// Functions to enumerate the Dx Diagnostic Tool hierarchy and build up
+// a tree of nodes with name / value properties.
+
+#define INITGUID
+#include <dxdiag.h>
+#include <windows.h>
+
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/gpu/gpu_info_collector.h"
+
+namespace {
+
+// Traverses the IDxDiagContainer tree and populates a tree of DxDiagNode
+// structures that contains property name / value pairs and subtrees of DirectX
+// diagnostic information.
+void RecurseDiagnosticTree(DxDiagNode* output,
+ IDxDiagContainer* container,
+ int depth) {
+ HRESULT hr;
+
+ VARIANT variant;
+ VariantInit(&variant);
+
+ DWORD prop_count;
+ hr = container->GetNumberOfProps(&prop_count);
+ if (SUCCEEDED(hr)) {
+ for (DWORD i = 0; i < prop_count; i++) {
+ WCHAR prop_name16[256];
+ hr = container->EnumPropNames(i, prop_name16, arraysize(prop_name16));
+ if (SUCCEEDED(hr)) {
+ std::string prop_name8 = WideToUTF8(prop_name16);
+
+ hr = container->GetProp(prop_name16, &variant);
+ if (SUCCEEDED(hr)) {
+ switch (variant.vt) {
+ case VT_UI4:
+ output->values[prop_name8] = base::UintToString(variant.ulVal);
+ break;
+ case VT_I4:
+ output->values[prop_name8] = base::IntToString(variant.lVal);
+ break;
+ case VT_BOOL:
+ output->values[prop_name8] = variant.boolVal ? "true" : "false";
+ break;
+ case VT_BSTR:
+ output->values[prop_name8] = WideToUTF8(variant.bstrVal);
+ break;
+ default:
+ break;
+ }
+
+ // Clear the variant (this is needed to free BSTR memory).
+ VariantClear(&variant);
+ }
+ }
+ }
+ }
+
+ if (depth > 0) {
+ DWORD child_count;
+ hr = container->GetNumberOfChildContainers(&child_count);
+ if (SUCCEEDED(hr)) {
+ for (DWORD i = 0; i < child_count; i++) {
+ WCHAR child_name16[256];
+ hr = container->EnumChildContainerNames(i,
+ child_name16,
+ arraysize(child_name16));
+ if (SUCCEEDED(hr)) {
+ std::string child_name8 = WideToUTF8(child_name16);
+ DxDiagNode* output_child =
+ &output->children[child_name8];
+
+ IDxDiagContainer* child_container = NULL;
+ hr = container->GetChildContainer(child_name16, &child_container);
+ if (SUCCEEDED(hr)) {
+ RecurseDiagnosticTree(output_child, child_container, depth - 1);
+
+ child_container->Release();
+ }
+ }
+ }
+ }
+ }
+}
+} // namespace anonymous
+
+namespace gpu_info_collector {
+
+bool GetDxDiagnostics(DxDiagNode* output) {
+ HRESULT hr;
+ bool success = false;
+
+ IDxDiagProvider* provider = NULL;
+ hr = CoCreateInstance(CLSID_DxDiagProvider,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IDxDiagProvider,
+ reinterpret_cast<void**>(&provider));
+ if (SUCCEEDED(hr)) {
+ DXDIAG_INIT_PARAMS params = { sizeof(params) };
+ params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
+ params.bAllowWHQLChecks = FALSE;
+ params.pReserved = NULL;
+
+ hr = provider->Initialize(&params);
+ if (SUCCEEDED(hr)) {
+ IDxDiagContainer* root = NULL;
+ hr = provider->GetRootContainer(&root);
+ if (SUCCEEDED(hr)) {
+ // Limit to the DisplayDevices subtree. The tree in its entirity is
+ // enormous and only this branch contains useful information.
+ IDxDiagContainer* display_devices = NULL;
+ hr = root->GetChildContainer(L"DxDiag_DisplayDevices",
+ &display_devices);
+ if (SUCCEEDED(hr)) {
+ RecurseDiagnosticTree(output, display_devices, 1);
+ success = true;
+ display_devices->Release();
+ }
+
+ root->Release();
+ }
+ }
+ provider->Release();
+ }
+
+ return success;
+}
+} // namespace gpu_info_collector
diff --git a/content/gpu/gpu_idirect3d9_mock_win.cc b/content/gpu/gpu_idirect3d9_mock_win.cc
new file mode 100644
index 0000000..68893eb
--- /dev/null
+++ b/content/gpu/gpu_idirect3d9_mock_win.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2010 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 "content/gpu/gpu_idirect3d9_mock_win.h"
+
+IDirect3D9Mock::IDirect3D9Mock() {}
+
+IDirect3D9Mock::~IDirect3D9Mock() {}
diff --git a/content/gpu/gpu_idirect3d9_mock_win.h b/content/gpu/gpu_idirect3d9_mock_win.h
new file mode 100644
index 0000000..25752a5
--- /dev/null
+++ b/content/gpu/gpu_idirect3d9_mock_win.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_IDIRECT3D9_MOCK_WIN_H_
+#define CONTENT_GPU_GPU_IDIRECT3D9_MOCK_WIN_H_
+#pragma once
+
+#include <d3d9.h>
+#include <windows.h>
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+class IDirect3D9Mock : public IDirect3D9 {
+ public:
+ IDirect3D9Mock();
+ virtual ~IDirect3D9Mock();
+
+ MOCK_METHOD5_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CheckDepthStencilMatch,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType,
+ D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat,
+ D3DFORMAT DepthStencilFormat));
+ MOCK_METHOD6_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CheckDeviceFormat,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType,
+ D3DFORMAT AdapterFormat, DWORD Usage,
+ D3DRESOURCETYPE RType, D3DFORMAT CheckFormat));
+ MOCK_METHOD4_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CheckDeviceFormatConversion,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType,
+ D3DFORMAT SourceFormat, D3DFORMAT TargetFormat));
+ MOCK_METHOD6_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CheckDeviceMultiSampleType,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType,
+ D3DFORMAT SurfaceFormat, BOOL Windowed,
+ D3DMULTISAMPLE_TYPE MultiSampleType,
+ DWORD* pQualityLevels));
+ MOCK_METHOD5_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CheckDeviceType,
+ HRESULT(UINT Adapter, D3DDEVTYPE DevType,
+ D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
+ BOOL bWindowed));
+ MOCK_METHOD6_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, CreateDevice,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow,
+ DWORD BehaviorFlags,
+ D3DPRESENT_PARAMETERS* pPresentationParameters,
+ IDirect3DDevice9** ppReturnedDeviceInterface));
+ MOCK_METHOD4_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, EnumAdapterModes,
+ HRESULT(UINT Adapter, D3DFORMAT Format, UINT Mode,
+ D3DDISPLAYMODE* pMode));
+ MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, GetAdapterCount, UINT());
+ MOCK_METHOD2_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, GetAdapterDisplayMode,
+ HRESULT(UINT Adapter, D3DDISPLAYMODE* pMode));
+ MOCK_METHOD3_WITH_CALLTYPE(
+ STDMETHODCALLTYPE, GetAdapterIdentifier,
+ HRESULT(UINT Adapter, DWORD Flags,
+ D3DADAPTER_IDENTIFIER9* pIdentifier));
+ MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetAdapterModeCount,
+ UINT(UINT Adapter, D3DFORMAT Format));
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetAdapterMonitor,
+ HMONITOR(UINT Adapter));
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, GetDeviceCaps,
+ HRESULT(UINT Adapter, D3DDEVTYPE DeviceType,
+ D3DCAPS9* pCaps));
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, RegisterSoftwareDevice,
+ HRESULT(void* pInitializeFunction));
+ MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, QueryInterface,
+ HRESULT(REFIID riid, void** ppvObj));
+ MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG());
+ MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG());
+};
+
+#endif // CONTENT_GPU_GPU_IDIRECT3D9_MOCK_WIN_H_
diff --git a/content/gpu/gpu_info_collector.cc b/content/gpu/gpu_info_collector.cc
new file mode 100644
index 0000000..5211e9d
--- /dev/null
+++ b/content/gpu/gpu_info_collector.cc
@@ -0,0 +1,121 @@
+// 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 "content/gpu/gpu_info_collector.h"
+
+#include <string>
+#include <vector>
+
+#include "app/gfx/gl/gl_bindings.h"
+#include "app/gfx/gl/gl_context.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/string_split.h"
+
+namespace {
+
+// This creates an offscreen GL context for gl queries. Returned GLContext
+// should be deleted in FinalizeGLContext.
+gfx::GLContext* InitializeGLContext() {
+ if (!gfx::GLContext::InitializeOneOff()) {
+ LOG(ERROR) << "gfx::GLContext::InitializeOneOff() failed";
+ return NULL;
+ }
+ gfx::GLContext* context = gfx::GLContext::CreateOffscreenGLContext(NULL);
+ if (context == NULL) {
+ LOG(ERROR) << "gfx::GLContext::CreateOffscreenGLContext(NULL) failed";
+ return NULL;
+ }
+ if (!context->MakeCurrent()) {
+ LOG(ERROR) << "gfx::GLContext::MakeCurrent() failed";
+ context->Destroy();
+ delete context;
+ return NULL;
+ }
+ return context;
+}
+
+// This destroy and delete the GL context.
+void FinalizeGLContext(gfx::GLContext** context) {
+ DCHECK(context);
+ if (*context) {
+ (*context)->Destroy();
+ delete *context;
+ *context = NULL;
+ }
+}
+
+std::string GetGLString(unsigned int pname) {
+ const char* gl_string =
+ reinterpret_cast<const char*>(glGetString(pname));
+ if (gl_string)
+ return std::string(gl_string);
+ return "";
+}
+
+uint32 GetVersionNumberFromString(const std::string& version_string) {
+ int major = 0, minor = 0;
+ size_t begin = version_string.find_first_of("0123456789");
+ if (begin != std::string::npos) {
+ size_t end = version_string.find_first_not_of("01234567890.", begin);
+ std::string sub_string;
+ if (end != std::string::npos)
+ sub_string = version_string.substr(begin, end - begin);
+ else
+ sub_string = version_string.substr(begin);
+ std::vector<std::string> pieces;
+ base::SplitString(sub_string, '.', &pieces);
+ if (pieces.size() >= 2) {
+ base::StringToInt(pieces[0], &major);
+ base::StringToInt(pieces[1], &minor);
+ }
+ }
+ return ((major << 8) + minor);
+}
+
+} // namespace anonymous
+
+namespace gpu_info_collector {
+
+bool CollectGraphicsInfoGL(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ gfx::GLContext* context = InitializeGLContext();
+ if (context == NULL)
+ return false;
+
+ gpu_info->gl_renderer = GetGLString(GL_RENDERER);
+ gpu_info->gl_vendor = GetGLString(GL_VENDOR);
+ gpu_info->gl_version_string =GetGLString(GL_VERSION);
+ gpu_info->gl_extensions =GetGLString(GL_EXTENSIONS);
+
+ bool validGLVersionInfo = CollectGLVersionInfo(gpu_info);
+ bool validVideoCardInfo = CollectVideoCardInfo(gpu_info);
+ bool validDriverInfo = CollectDriverInfoGL(gpu_info);
+
+ FinalizeGLContext(&context);
+
+ return (validGLVersionInfo && validVideoCardInfo && validDriverInfo);
+}
+
+bool CollectGLVersionInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ std::string gl_version_string = gpu_info->gl_version_string;
+ std::string glsl_version_string =
+ GetGLString(GL_SHADING_LANGUAGE_VERSION);
+
+ uint32 gl_version = GetVersionNumberFromString(gl_version_string);
+ gpu_info->gl_version = gl_version;
+
+ uint32 glsl_version = GetVersionNumberFromString(glsl_version_string);
+ gpu_info->pixel_shader_version = glsl_version;
+ gpu_info->vertex_shader_version = glsl_version;
+
+ return true;
+}
+
+} // namespace gpu_info_collector
+
diff --git a/content/gpu/gpu_info_collector.h b/content/gpu/gpu_info_collector.h
new file mode 100644
index 0000000..eb669a0
--- /dev/null
+++ b/content/gpu/gpu_info_collector.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_INFO_COLLECTOR_H_
+#define CONTENT_GPU_GPU_INFO_COLLECTOR_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "content/common/gpu_info.h"
+
+struct IDirect3D9;
+
+namespace gpu_info_collector {
+
+// Populate variables with necessary graphics card information.
+// Returns true on success.
+bool CollectGraphicsInfo(GPUInfo* gpu_info);
+
+// Similar to CollectGraphicsInfo, only this collects a subset of variables
+// without creating a GL/DirectX context (and without the danger of crashing).
+// The subset each platform collects may be different.
+bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info);
+
+#if defined(OS_WIN)
+// Windows provides two ways of doing graphics so we need two ways of
+// collecting info based on what's on a user's machine.
+// The selection between the two methods is done in the cc file.
+
+// A D3D argument is passed in for testing purposes
+bool CollectGraphicsInfoD3D(IDirect3D9* d3d, GPUInfo* gpu_info);
+
+// Collects D3D driver version/date through registry lookup.
+// Note that this does not require a D3D context.
+// device_id here is the raw data in DISPLAY_DEVICE.
+bool CollectDriverInfoD3D(const std::wstring& device_id, GPUInfo* gpu_info);
+
+// Collect the DirectX Disagnostics information about the attached displays.
+bool GetDxDiagnostics(DxDiagNode* output);
+#endif
+
+// All platforms have a GL version for collecting information
+bool CollectGraphicsInfoGL(GPUInfo* gpu_info);
+
+// Collect GL and Shading language version information
+bool CollectGLVersionInfo(GPUInfo* gpu_info);
+
+// Platform specific method for collecting vendor and device ids
+bool CollectVideoCardInfo(GPUInfo* gpu_info);
+
+// Each platform stores the driver version on the GL_VERSION string differently
+bool CollectDriverInfoGL(GPUInfo* gpu_info);
+
+} // namespace gpu_info_collector
+
+#endif // CONTENT_GPU_GPU_INFO_COLLECTOR_H_
diff --git a/content/gpu/gpu_info_collector_linux.cc b/content/gpu/gpu_info_collector_linux.cc
new file mode 100644
index 0000000..08b1f9c
--- /dev/null
+++ b/content/gpu/gpu_info_collector_linux.cc
@@ -0,0 +1,279 @@
+// 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 "content/gpu/gpu_info_collector.h"
+
+#include <dlfcn.h>
+#include <vector>
+
+#include "app/gfx/gl/gl_bindings.h"
+#include "app/gfx/gl/gl_context.h"
+#include "app/gfx/gl/gl_implementation.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+
+namespace {
+
+// PciDevice and PciAccess are defined to access libpci functions. Their
+// members match the corresponding structures defined by libpci in size up to
+// fields we may access. For those members we don't use, their names are
+// defined as "fieldX", etc., or, left out if they are declared after the
+// members we care about in libpci.
+
+struct PciDevice {
+ PciDevice* next;
+
+ uint16 field0;
+ uint8 field1;
+ uint8 field2;
+ uint8 field3;
+ int field4;
+
+ uint16 vendor_id;
+ uint16 device_id;
+ uint16 device_class;
+};
+
+struct PciAccess {
+ unsigned int field0;
+ int field1;
+ int field2;
+ char* field3;
+ int field4;
+ int field5;
+ unsigned int field6;
+ int field7;
+
+ void (*function0)();
+ void (*function1)();
+ void (*function2)();
+
+ PciDevice* device_list;
+};
+
+// Define function types.
+typedef PciAccess* (*FT_pci_alloc)();
+typedef void (*FT_pci_init)(PciAccess*);
+typedef void (*FT_pci_cleanup)(PciAccess*);
+typedef void (*FT_pci_scan_bus)(PciAccess*);
+typedef void (*FT_pci_scan_bus)(PciAccess*);
+typedef int (*FT_pci_fill_info)(PciDevice*, int);
+typedef char* (*FT_pci_lookup_name)(PciAccess*, char*, int, int, ...);
+
+// This includes dynamically linked library handle and functions pointers from
+// libpci.
+struct PciInterface {
+ void* lib_handle;
+
+ FT_pci_alloc pci_alloc;
+ FT_pci_init pci_init;
+ FT_pci_cleanup pci_cleanup;
+ FT_pci_scan_bus pci_scan_bus;
+ FT_pci_fill_info pci_fill_info;
+ FT_pci_lookup_name pci_lookup_name;
+};
+
+// This checks if a system supports PCI bus.
+// We check the existence of /sys/bus/pci or /sys/bug/pci_express.
+bool IsPciSupported() {
+ const FilePath pci_path("/sys/bus/pci/");
+ const FilePath pcie_path("/sys/bus/pci_express/");
+ return (file_util::PathExists(pci_path) ||
+ file_util::PathExists(pcie_path));
+}
+
+// This dynamically opens libpci and get function pointers we need. Return
+// NULL if library fails to open or any functions can not be located.
+// Returned interface (if not NULL) should be deleted in FinalizeLibPci.
+PciInterface* InitializeLibPci(const char* lib_name) {
+ void* handle = dlopen(lib_name, RTLD_LAZY);
+ if (handle == NULL) {
+ LOG(INFO) << "Failed to dlopen " << lib_name;
+ return NULL;
+ }
+ PciInterface* interface = new struct PciInterface;
+ interface->lib_handle = handle;
+ interface->pci_alloc = reinterpret_cast<FT_pci_alloc>(
+ dlsym(handle, "pci_alloc"));
+ interface->pci_init = reinterpret_cast<FT_pci_init>(
+ dlsym(handle, "pci_init"));
+ interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>(
+ dlsym(handle, "pci_cleanup"));
+ interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>(
+ dlsym(handle, "pci_scan_bus"));
+ interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>(
+ dlsym(handle, "pci_fill_info"));
+ interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>(
+ dlsym(handle, "pci_lookup_name"));
+ if (interface->pci_alloc == NULL ||
+ interface->pci_init == NULL ||
+ interface->pci_cleanup == NULL ||
+ interface->pci_scan_bus == NULL ||
+ interface->pci_fill_info == NULL ||
+ interface->pci_lookup_name == NULL) {
+ LOG(ERROR) << "Missing required function(s) from " << lib_name;
+ dlclose(handle);
+ delete interface;
+ return NULL;
+ }
+ return interface;
+}
+
+// This close the dynamically opened libpci and delete the interface.
+void FinalizeLibPci(PciInterface** interface) {
+ DCHECK(interface && *interface && (*interface)->lib_handle);
+ dlclose((*interface)->lib_handle);
+ delete (*interface);
+ *interface = NULL;
+}
+
+} // namespace anonymous
+
+namespace gpu_info_collector {
+
+bool CollectGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ // TODO(zmo): need to consider the case where we are running on top of
+ // desktop GL and GL_ARB_robustness extension is available.
+ gpu_info->can_lose_context =
+ (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
+ gpu_info->level = GPUInfo::kComplete;
+ return CollectGraphicsInfoGL(gpu_info);
+}
+
+bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ gpu_info->level = GPUInfo::kPartial;
+
+ bool rt = true;
+ if (!CollectVideoCardInfo(gpu_info))
+ rt = false;
+
+ // TODO(zmo): if vendor is ATI, consider passing /etc/ati/amdpcsdb.default
+ // for driver information.
+
+ return rt;
+}
+
+bool CollectVideoCardInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ if (IsPciSupported() == false) {
+ LOG(INFO) << "PCI bus scanning is not supported";
+ return false;
+ }
+
+ // TODO(zmo): be more flexible about library name.
+ PciInterface* interface = InitializeLibPci("libpci.so.3");
+ if (interface == NULL)
+ interface = InitializeLibPci("libpci.so");
+ if (interface == NULL) {
+ LOG(ERROR) << "Failed to locate libpci";
+ return false;
+ }
+
+ PciAccess* access = (interface->pci_alloc)();
+ DCHECK(access != NULL);
+ (interface->pci_init)(access);
+ (interface->pci_scan_bus)(access);
+ std::vector<PciDevice*> gpu_list;
+ PciDevice* gpu_active = NULL;
+ for (PciDevice* device = access->device_list;
+ device != NULL; device = device->next) {
+ (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields.
+ // TODO(zmo): there might be other classes that qualify as display devices.
+ if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA.
+ gpu_list.push_back(device);
+ }
+ }
+ if (gpu_list.size() == 1) {
+ gpu_active = gpu_list[0];
+ } else {
+ // If more than one graphics card are identified, find the one that matches
+ // gl VENDOR and RENDERER info.
+ std::string gl_vendor_string = gpu_info->gl_vendor;
+ std::string gl_renderer_string = gpu_info->gl_renderer;
+ const int buffer_size = 255;
+ scoped_array<char> buffer(new char[buffer_size]);
+ std::vector<PciDevice*> candidates;
+ for (size_t i = 0; i < gpu_list.size(); ++i) {
+ PciDevice* gpu = gpu_list[i];
+ // The current implementation of pci_lookup_name returns the same pointer
+ // as the passed in upon success, and a different one (NULL or a pointer
+ // to an error message) upon failure.
+ if ((interface->pci_lookup_name)(access,
+ buffer.get(),
+ buffer_size,
+ 1,
+ gpu->vendor_id) != buffer.get())
+ continue;
+ std::string vendor_string = buffer.get();
+ const bool kCaseSensitive = false;
+ if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive))
+ continue;
+ if ((interface->pci_lookup_name)(access,
+ buffer.get(),
+ buffer_size,
+ 2,
+ gpu->vendor_id,
+ gpu->device_id) != buffer.get())
+ continue;
+ std::string device_string = buffer.get();
+ size_t begin = device_string.find_first_of('[');
+ size_t end = device_string.find_last_of(']');
+ if (begin != std::string::npos && end != std::string::npos &&
+ begin < end) {
+ device_string = device_string.substr(begin + 1, end - begin - 1);
+ }
+ if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) {
+ gpu_active = gpu;
+ break;
+ }
+ // If a device's vendor matches gl VENDOR string, we want to consider the
+ // possibility that libpci may not return the exact same name as gl
+ // RENDERER string.
+ candidates.push_back(gpu);
+ }
+ if (gpu_active == NULL && candidates.size() == 1)
+ gpu_active = candidates[0];
+ }
+ if (gpu_active != NULL) {
+ gpu_info->vendor_id = gpu_active->vendor_id;
+ gpu_info->device_id = gpu_active->device_id;
+ }
+ (interface->pci_cleanup)(access);
+ FinalizeLibPci(&interface);
+ return (gpu_active != NULL);
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ std::string gl_version_string = gpu_info->gl_version_string;
+ std::vector<std::string> pieces;
+ base::SplitStringAlongWhitespace(gl_version_string, &pieces);
+ // In linux, the gl version string might be in the format of
+ // GLVersion DriverVendor DriverVersion
+ if (pieces.size() < 3)
+ return false;
+
+ std::string driver_version = pieces[2];
+ size_t pos = driver_version.find_first_not_of("0123456789.");
+ if (pos == 0)
+ return false;
+ if (pos != std::string::npos)
+ driver_version = driver_version.substr(0, pos);
+
+ gpu_info->driver_vendor = pieces[1];
+ gpu_info->driver_version = driver_version;
+ return true;
+}
+
+} // namespace gpu_info_collector
diff --git a/content/gpu/gpu_info_collector_mac.mm b/content/gpu/gpu_info_collector_mac.mm
new file mode 100644
index 0000000..e1b625d
--- /dev/null
+++ b/content/gpu/gpu_info_collector_mac.mm
@@ -0,0 +1,104 @@
+// 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 "content/gpu/gpu_info_collector.h"
+
+#include "app/gfx/gl/gl_bindings.h"
+#include "app/gfx/gl/gl_context.h"
+#include "app/gfx/gl/gl_implementation.h"
+#include "app/gfx/gl/gl_interface.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/sys_string_conversions.h"
+
+#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
+#import <IOKit/IOKitLib.h>
+
+namespace {
+
+CFTypeRef SearchPortForProperty(io_registry_entry_t dspPort,
+ CFStringRef propertyName) {
+ return IORegistryEntrySearchCFProperty(dspPort,
+ kIOServicePlane,
+ propertyName,
+ kCFAllocatorDefault,
+ kIORegistryIterateRecursively |
+ kIORegistryIterateParents);
+}
+
+UInt32 IntValueOfCFData(CFDataRef data_ref) {
+ DCHECK(data_ref);
+
+ UInt32 value = 0;
+ const UInt32* value_pointer =
+ reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data_ref));
+ if (value_pointer != NULL)
+ value = *value_pointer;
+ return value;
+}
+
+} // namespace anonymous
+
+namespace gpu_info_collector {
+
+bool CollectGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ gpu_info->can_lose_context =
+ (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
+ gpu_info-> level = GPUInfo::kComplete;
+ return CollectGraphicsInfoGL(gpu_info);
+}
+
+bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ gpu_info->level = GPUInfo::kPartial;
+
+ bool rt = true;
+ if (!CollectVideoCardInfo(gpu_info))
+ rt = false;
+
+ return rt;
+}
+
+bool CollectVideoCardInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ UInt32 vendor_id = 0, device_id = 0;
+ io_registry_entry_t dsp_port = CGDisplayIOServicePort(kCGDirectMainDisplay);
+ CFTypeRef vendor_id_ref = SearchPortForProperty(dsp_port, CFSTR("vendor-id"));
+ if (vendor_id_ref) {
+ vendor_id = IntValueOfCFData((CFDataRef)vendor_id_ref);
+ CFRelease(vendor_id_ref);
+ }
+ CFTypeRef device_id_ref = SearchPortForProperty(dsp_port, CFSTR("device-id"));
+ if (device_id_ref) {
+ device_id = IntValueOfCFData((CFDataRef)device_id_ref);
+ CFRelease(device_id_ref);
+ }
+
+ gpu_info->vendor_id = vendor_id;
+ gpu_info->device_id = device_id;
+ return true;
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ // Extract the OpenGL driver version string from the GL_VERSION string.
+ // Mac OpenGL drivers have the driver version
+ // at the end of the gl version string preceded by a dash.
+ // Use some jiggery-pokery to turn that utf8 string into a std::wstring.
+ std::string gl_version_string = gpu_info->gl_version_string;
+ size_t pos = gl_version_string.find_last_of('-');
+ if (pos == std::string::npos)
+ return false;
+ gpu_info->driver_version = gl_version_string.substr(pos + 1);
+ return true;
+}
+
+} // namespace gpu_info_collector
diff --git a/content/gpu/gpu_info_collector_unittest.cc b/content/gpu/gpu_info_collector_unittest.cc
new file mode 100644
index 0000000..a441484
--- /dev/null
+++ b/content/gpu/gpu_info_collector_unittest.cc
@@ -0,0 +1,177 @@
+// 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 "app/gfx/gl/gl_implementation.h"
+#include "base/scoped_ptr.h"
+#include "content/common/gpu_info.h"
+#include "content/gpu/gpu_info_collector.h"
+#include "gpu/command_buffer/common/gl_mock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::gfx::MockGLInterface;
+using ::testing::Return;
+
+class GPUInfoCollectorTest : public testing::Test {
+ public:
+ GPUInfoCollectorTest() {}
+ virtual ~GPUInfoCollectorTest() { }
+
+ void SetUp() {
+ gfx::InitializeGLBindings(gfx::kGLImplementationMockGL);
+ gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>());
+ ::gfx::GLInterface::SetGLInterface(gl_.get());
+#if defined(OS_WIN)
+ const uint32 vendor_id = 0x10de;
+ const uint32 device_id = 0x0658;
+ const char* driver_vendor = ""; // not implemented
+ const char* driver_version = "";
+ const uint32 shader_version = 0x00000128;
+ const uint32 gl_version = 0x00000301;
+ const char* gl_renderer = "Quadro FX 380/PCI/SSE2";
+ const char* gl_vendor = "NVIDIA Corporation";
+ const char* gl_version_string = "3.1.0";
+ const char* gl_shading_language_version = "1.40 NVIDIA via Cg compiler";
+ const char* gl_extensions =
+ "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+ "GL_EXT_read_format_bgra";
+#elif defined(OS_MACOSX)
+ const uint32 vendor_id = 0x10de;
+ const uint32 device_id = 0x0640;
+ const char* driver_vendor = ""; // not implemented
+ const char* driver_version = "1.6.18";
+ const uint32 shader_version = 0x00000114;
+ const uint32 gl_version = 0x00000201;
+ const char* gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
+ const char* gl_vendor = "NVIDIA Corporation";
+ const char* gl_version_string = "2.1 NVIDIA-1.6.18";
+ const char* gl_shading_language_version = "1.20 ";
+ const char* gl_extensions =
+ "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+ "GL_EXT_read_format_bgra";
+#else // defined (OS_LINUX)
+ const uint32 vendor_id = 0x10de;
+ const uint32 device_id = 0x0658;
+ const char* driver_vendor = "NVIDIA";
+ const char* driver_version = "195.36.24";
+ const uint32 shader_version = 0x00000132;
+ const uint32 gl_version = 0x00000302;
+ const char* gl_renderer = "Quadro FX 380/PCI/SSE2";
+ const char* gl_vendor = "NVIDIA Corporation";
+ const char* gl_version_string = "3.2.0 NVIDIA 195.36.24";
+ const char* gl_shading_language_version = "1.50 NVIDIA via Cg compiler";
+ const char* gl_extensions =
+ "GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 "
+ "GL_EXT_read_format_bgra";
+#endif
+ test_values_.vendor_id = vendor_id;
+ test_values_.device_id = device_id;
+ test_values_.driver_vendor = driver_vendor;
+ test_values_.driver_version =driver_version;
+ test_values_.driver_date = "";
+ test_values_.pixel_shader_version = shader_version;
+ test_values_.vertex_shader_version = shader_version;
+ test_values_.gl_version = gl_version;
+ test_values_.gl_renderer = gl_renderer;
+ test_values_.gl_vendor = gl_vendor;
+ test_values_.gl_version_string = gl_version_string;
+ test_values_.gl_extensions = gl_extensions;
+ test_values_.can_lose_context = false;
+
+ EXPECT_CALL(*gl_, GetString(GL_EXTENSIONS))
+ .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+ gl_extensions)));
+ EXPECT_CALL(*gl_, GetString(GL_SHADING_LANGUAGE_VERSION))
+ .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+ gl_shading_language_version)));
+ EXPECT_CALL(*gl_, GetString(GL_VERSION))
+ .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+ gl_version_string)));
+ EXPECT_CALL(*gl_, GetString(GL_VENDOR))
+ .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+ gl_vendor)));
+ EXPECT_CALL(*gl_, GetString(GL_RENDERER))
+ .WillRepeatedly(Return(reinterpret_cast<const GLubyte*>(
+ gl_renderer)));
+ }
+
+ void TearDown() {
+ ::gfx::GLInterface::SetGLInterface(NULL);
+ gl_.reset();
+ }
+
+ public:
+ // Use StrictMock to make 100% sure we know how GL will be called.
+ scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
+ GPUInfo test_values_;
+};
+
+// TODO(rlp): Test the vendor and device id collection if deemed necessary as
+// it involves several complicated mocks for each platform.
+
+TEST_F(GPUInfoCollectorTest, DriverVendorGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string driver_vendor = gpu_info.driver_vendor;
+ EXPECT_EQ(test_values_.driver_vendor, driver_vendor);
+}
+
+// Skip Windows because the driver version is obtained from bot registry.
+#if !defined(OS_WIN)
+TEST_F(GPUInfoCollectorTest, DriverVersionGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string driver_version = gpu_info.driver_version;
+ EXPECT_EQ(test_values_.driver_version, driver_version);
+}
+#endif
+
+TEST_F(GPUInfoCollectorTest, PixelShaderVersionGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ uint32 ps_version = gpu_info.pixel_shader_version;
+ EXPECT_EQ(test_values_.pixel_shader_version, ps_version);
+}
+
+TEST_F(GPUInfoCollectorTest, VertexShaderVersionGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ uint32 vs_version = gpu_info.vertex_shader_version;
+ EXPECT_EQ(test_values_.vertex_shader_version, vs_version);
+}
+
+TEST_F(GPUInfoCollectorTest, GLVersionGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ uint32 gl_version = gpu_info.gl_version;
+ EXPECT_EQ(test_values_.gl_version, gl_version);
+}
+
+TEST_F(GPUInfoCollectorTest, GLVersionStringGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string gl_version_string = gpu_info.gl_version_string;
+ EXPECT_EQ(test_values_.gl_version_string, gl_version_string);
+}
+
+TEST_F(GPUInfoCollectorTest, GLRendererGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string gl_renderer = gpu_info.gl_renderer;
+ EXPECT_EQ(test_values_.gl_renderer, gl_renderer);
+}
+
+TEST_F(GPUInfoCollectorTest, GLVendorGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string gl_vendor = gpu_info.gl_vendor;
+ EXPECT_EQ(test_values_.gl_vendor, gl_vendor);
+}
+
+TEST_F(GPUInfoCollectorTest, GLExtensionsGL) {
+ GPUInfo gpu_info;
+ gpu_info_collector::CollectGraphicsInfoGL(&gpu_info);
+ std::string gl_extensions = gpu_info.gl_extensions;
+ EXPECT_EQ(test_values_.gl_extensions, gl_extensions);
+}
diff --git a/content/gpu/gpu_info_collector_win.cc b/content/gpu/gpu_info_collector_win.cc
new file mode 100644
index 0000000..35ab7c0
--- /dev/null
+++ b/content/gpu/gpu_info_collector_win.cc
@@ -0,0 +1,264 @@
+// 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 "content/gpu/gpu_info_collector.h"
+
+#include <d3d9.h>
+#include <setupapi.h>
+#include <windows.h>
+
+#include "app/gfx/gl/gl_context_egl.h"
+#include "app/gfx/gl/gl_implementation.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+
+// ANGLE seems to require that main.h be included before any other ANGLE header.
+#include "libEGL/main.h"
+#include "libEGL/Display.h"
+
+// Setup API functions
+typedef HDEVINFO (WINAPI*SetupDiGetClassDevsWFunc)(
+ CONST GUID *ClassGuid,
+ PCWSTR Enumerator,
+ HWND hwndParent,
+ DWORD Flags
+);
+typedef BOOL (WINAPI*SetupDiEnumDeviceInfoFunc)(
+ HDEVINFO DeviceInfoSet,
+ DWORD MemberIndex,
+ PSP_DEVINFO_DATA DeviceInfoData
+);
+typedef BOOL (WINAPI*SetupDiGetDeviceRegistryPropertyWFunc)(
+ HDEVINFO DeviceInfoSet,
+ PSP_DEVINFO_DATA DeviceInfoData,
+ DWORD Property,
+ PDWORD PropertyRegDataType,
+ PBYTE PropertyBuffer,
+ DWORD PropertyBufferSize,
+ PDWORD RequiredSize
+);
+typedef BOOL (WINAPI*SetupDiDestroyDeviceInfoListFunc)(
+ HDEVINFO DeviceInfoSet
+);
+
+namespace gpu_info_collector {
+
+bool CollectGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
+ gpu_info->level = GPUInfo::kComplete;
+ return CollectGraphicsInfoGL(gpu_info);
+ }
+
+ // Set to partial now in case this function returns false below.
+ gpu_info->level = GPUInfo::kPartial;
+
+ // TODO(zmo): the following code only works if running on top of ANGLE.
+ // Need to handle the case when running on top of real EGL/GLES2 drivers.
+
+ egl::Display* display = static_cast<egl::Display*>(
+ gfx::BaseEGLContext::GetDisplay());
+ if (!display) {
+ LOG(ERROR) << "gfx::BaseEGLContext::GetDisplay() failed";
+ return false;
+ }
+
+ IDirect3DDevice9* device = display->getDevice();
+ if (!device) {
+ LOG(ERROR) << "display->getDevice() failed";
+ return false;
+ }
+
+ IDirect3D9* d3d = NULL;
+ if (FAILED(device->GetDirect3D(&d3d))) {
+ LOG(ERROR) << "device->GetDirect3D(&d3d) failed";
+ return false;
+ }
+
+ if (!CollectGraphicsInfoD3D(d3d, gpu_info))
+ return false;
+
+ // DirectX diagnostics are collected asynchronously because it takes a
+ // couple of seconds. Do not mark gpu_info as complete until that is done.
+ return true;
+}
+
+bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ gpu_info->level = GPUInfo::kPreliminary;
+
+ bool rt = true;
+ if (!CollectVideoCardInfo(gpu_info))
+ rt = false;
+
+ return rt;
+}
+
+bool CollectGraphicsInfoD3D(IDirect3D9* d3d, GPUInfo* gpu_info) {
+ DCHECK(d3d);
+ DCHECK(gpu_info);
+
+ bool succeed = CollectVideoCardInfo(gpu_info);
+
+ // Get version information
+ D3DCAPS9 d3d_caps;
+ if (d3d->GetDeviceCaps(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ &d3d_caps) == D3D_OK) {
+ gpu_info->pixel_shader_version = d3d_caps.PixelShaderVersion;
+ gpu_info->vertex_shader_version = d3d_caps.VertexShaderVersion;
+ } else {
+ LOG(ERROR) << "d3d->GetDeviceCaps() failed";
+ succeed = false;
+ }
+
+ // Get can_lose_context
+ bool can_lose_context = false;
+ IDirect3D9Ex* d3dex = NULL;
+ if (SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex),
+ reinterpret_cast<void**>(&d3dex)))) {
+ d3dex->Release();
+ } else {
+ can_lose_context = true;
+ }
+ gpu_info->can_lose_context = can_lose_context;
+
+ d3d->Release();
+ return true;
+}
+
+bool CollectVideoCardInfo(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ // Taken from http://developer.nvidia.com/object/device_ids.html
+ DISPLAY_DEVICE dd;
+ dd.cb = sizeof(DISPLAY_DEVICE);
+ int i = 0;
+ std::wstring id;
+ for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
+ if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
+ id = dd.DeviceID;
+ break;
+ }
+ }
+
+ if (id.length() > 20) {
+ int vendor_id = 0, device_id = 0;
+ std::wstring vendor_id_string = id.substr(8, 4);
+ std::wstring device_id_string = id.substr(17, 4);
+ base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id);
+ base::HexStringToInt(WideToASCII(device_id_string), &device_id);
+ gpu_info->vendor_id = vendor_id;
+ gpu_info->device_id = device_id;
+ // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
+ return CollectDriverInfoD3D(id, gpu_info);
+ }
+ return false;
+}
+
+bool CollectDriverInfoD3D(const std::wstring& device_id, GPUInfo* gpu_info) {
+ HMODULE lib_setupapi = LoadLibraryW(L"setupapi.dll");
+ if (!lib_setupapi) {
+ LOG(ERROR) << "Open setupapi.dll failed";
+ return false;
+ }
+ SetupDiGetClassDevsWFunc fp_get_class_devs =
+ reinterpret_cast<SetupDiGetClassDevsWFunc>(
+ GetProcAddress(lib_setupapi, "SetupDiGetClassDevsW"));
+ SetupDiEnumDeviceInfoFunc fp_enum_device_info =
+ reinterpret_cast<SetupDiEnumDeviceInfoFunc>(
+ GetProcAddress(lib_setupapi, "SetupDiEnumDeviceInfo"));
+ SetupDiGetDeviceRegistryPropertyWFunc fp_get_device_registry_property =
+ reinterpret_cast<SetupDiGetDeviceRegistryPropertyWFunc>(
+ GetProcAddress(lib_setupapi, "SetupDiGetDeviceRegistryPropertyW"));
+ SetupDiDestroyDeviceInfoListFunc fp_destroy_device_info_list =
+ reinterpret_cast<SetupDiDestroyDeviceInfoListFunc>(
+ GetProcAddress(lib_setupapi, "SetupDiDestroyDeviceInfoList"));
+ if (!fp_get_class_devs || !fp_enum_device_info ||
+ !fp_get_device_registry_property || !fp_destroy_device_info_list) {
+ FreeLibrary(lib_setupapi);
+ LOG(ERROR) << "Retrieve setupapi.dll functions failed";
+ return false;
+ }
+
+ // create device info for the display device
+ HDEVINFO device_info = fp_get_class_devs(
+ NULL, device_id.c_str(), NULL,
+ DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
+ if (device_info == INVALID_HANDLE_VALUE) {
+ FreeLibrary(lib_setupapi);
+ LOG(ERROR) << "Creating device info failed";
+ return false;
+ }
+
+ DWORD index = 0;
+ bool found = false;
+ SP_DEVINFO_DATA device_info_data;
+ device_info_data.cbSize = sizeof(device_info_data);
+ while (fp_enum_device_info(device_info, index++, &device_info_data)) {
+ WCHAR value[255];
+ if (fp_get_device_registry_property(device_info,
+ &device_info_data,
+ SPDRP_DRIVER,
+ NULL,
+ reinterpret_cast<PBYTE>(value),
+ sizeof(value),
+ NULL)) {
+ HKEY key;
+ std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
+ driver_key += value;
+ LONG result = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
+ if (result == ERROR_SUCCESS) {
+ DWORD dwcb_data = sizeof(value);
+ std::string driver_version;
+ result = RegQueryValueExW(
+ key, L"DriverVersion", NULL, NULL,
+ reinterpret_cast<LPBYTE>(value), &dwcb_data);
+ if (result == ERROR_SUCCESS)
+ driver_version = WideToASCII(std::wstring(value));
+
+ std::string driver_date;
+ dwcb_data = sizeof(value);
+ result = RegQueryValueExW(
+ key, L"DriverDate", NULL, NULL,
+ reinterpret_cast<LPBYTE>(value), &dwcb_data);
+ if (result == ERROR_SUCCESS)
+ driver_date = WideToASCII(std::wstring(value));
+
+ gpu_info->driver_version = driver_version;
+ gpu_info->driver_date = driver_date;
+ found = true;
+ RegCloseKey(key);
+ break;
+ }
+ }
+ }
+ fp_destroy_device_info_list(device_info);
+ FreeLibrary(lib_setupapi);
+ return found;
+}
+
+bool CollectDriverInfoGL(GPUInfo* gpu_info) {
+ DCHECK(gpu_info);
+
+ std::string gl_version_string = gpu_info->gl_version_string;
+
+ // TODO(zmo): We assume the driver version is in the end of GL_VERSION
+ // string. Need to verify if it is true for majority drivers.
+
+ size_t pos = gl_version_string.find_last_not_of("0123456789.");
+ if (pos != std::string::npos && pos < gl_version_string.length() - 1) {
+ gpu_info->driver_version = gl_version_string.substr(pos + 1);
+ return true;
+ }
+ return false;
+}
+
+} // namespace gpu_info_collector
diff --git a/content/gpu/gpu_info_unittest_win.cc b/content/gpu/gpu_info_unittest_win.cc
new file mode 100644
index 0000000..d468a48
--- /dev/null
+++ b/content/gpu/gpu_info_unittest_win.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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 "base/scoped_ptr.h"
+#include "content/common/gpu_info.h"
+#include "content/gpu/gpu_idirect3d9_mock_win.h"
+#include "content/gpu/gpu_info_collector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+class GPUInfoTest : public testing::Test {
+ public:
+ GPUInfoTest() { }
+ virtual ~GPUInfoTest() { }
+
+ protected:
+ void SetUp() {
+ // Test variables taken from Lenovo T61
+ test_identifier_.VendorId = 0x10de;
+ test_identifier_.DeviceId = 0x429;
+ test_identifier_.DriverVersion.QuadPart = 0x6000e000b1e23; // 6.14.11.7715
+ test_caps_.PixelShaderVersion = 0xffff0300; // 3.0
+ test_caps_.VertexShaderVersion = 0xfffe0300; // 3.0
+
+ EXPECT_CALL(d3d_, GetDeviceCaps(_, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(test_caps_),
+ Return(D3D_OK)));
+ EXPECT_CALL(d3d_, QueryInterface(__uuidof(IDirect3D9Ex), _))
+ .WillOnce(Return(E_NOINTERFACE));
+ EXPECT_CALL(d3d_, Release());
+ }
+ void TearDown() {
+ }
+
+ public:
+ IDirect3D9Mock d3d_;
+ private:
+ D3DADAPTER_IDENTIFIER9 test_identifier_;
+ D3DCAPS9 test_caps_;
+};
+
+TEST_F(GPUInfoTest, PixelShaderVersionD3D) {
+ GPUInfo gpu_info;
+ ASSERT_TRUE(gpu_info_collector::CollectGraphicsInfoD3D(&d3d_, &gpu_info));
+ uint32 ps_version = gpu_info.pixel_shader_version;
+ EXPECT_EQ(ps_version, D3DPS_VERSION(3, 0));
+}
+
+TEST_F(GPUInfoTest, VertexShaderVersionD3D) {
+ GPUInfo gpu_info;
+ ASSERT_TRUE(gpu_info_collector::CollectGraphicsInfoD3D(&d3d_, &gpu_info));
+ uint32 vs_version = gpu_info.vertex_shader_version;
+ EXPECT_EQ(vs_version, D3DVS_VERSION(3, 0));
+}
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
new file mode 100644
index 0000000..ec79ba9
--- /dev/null
+++ b/content/gpu/gpu_main.cc
@@ -0,0 +1,87 @@
+// 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 <stdlib.h>
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "app/win/scoped_com_initializer.h"
+#include "base/environment.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "content/common/content_switches.h"
+#include "content/common/main_function_params.h"
+#include "content/gpu/gpu_config.h"
+#include "content/gpu/gpu_process.h"
+#include "content/gpu/gpu_thread.h"
+
+#if defined(OS_MACOSX)
+#include "content/common/chrome_application_mac.h"
+#endif
+
+#if defined(USE_X11)
+#include "ui/base/x/x11_util.h"
+#endif
+
+// Main function for starting the Gpu process.
+int GpuMain(const MainFunctionParams& parameters) {
+ base::Time start_time = base::Time::Now();
+
+ const CommandLine& command_line = parameters.command_line_;
+ if (command_line.HasSwitch(switches::kGpuStartupDialog)) {
+ ChildProcess::WaitForDebugger("Gpu");
+ }
+
+#if defined(OS_MACOSX)
+ chrome_application_mac::RegisterCrApp();
+#endif
+
+ MessageLoop main_message_loop(MessageLoop::TYPE_UI);
+ base::PlatformThread::SetName("CrGpuMain");
+
+ if (!command_line.HasSwitch(switches::kSingleProcess)) {
+#if defined(OS_WIN)
+ // Prevent Windows from displaying a modal dialog on failures like not being
+ // able to load a DLL.
+ SetErrorMode(
+ SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX |
+ SEM_NOOPENFILEERRORBOX);
+#elif defined(USE_X11)
+ ui::SetDefaultX11ErrorHandlers();
+#endif
+ }
+
+ app::win::ScopedCOMInitializer com_initializer;
+
+ // We can not tolerate early returns from this code, because the
+ // detection of early return of a child process is implemented using
+ // an IPC channel error. If the IPC channel is not fully set up
+ // between the browser and GPU process, and the GPU process crashes
+ // or exits early, the browser process will never detect it. For
+ // this reason we defer all work related to the GPU until receiving
+ // the GpuMsg_Initialize message from the browser.
+ GpuProcess gpu_process;
+
+ GpuThread* gpu_thread =
+#if defined(OS_WIN)
+ new GpuThread(parameters.sandbox_info_.TargetServices());
+#else
+ new GpuThread;
+#endif
+
+ gpu_thread->Init(start_time);
+
+ gpu_process.set_main_thread(gpu_thread);
+
+ main_message_loop.Run();
+
+ gpu_thread->StopWatchdog();
+
+ return 0;
+}
diff --git a/content/gpu/gpu_process.cc b/content/gpu/gpu_process.cc
new file mode 100644
index 0000000..252ceb1
--- /dev/null
+++ b/content/gpu/gpu_process.cc
@@ -0,0 +1,11 @@
+// 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 "content/gpu/gpu_process.h"
+
+GpuProcess::GpuProcess() {
+}
+
+GpuProcess::~GpuProcess() {
+}
diff --git a/content/gpu/gpu_process.h b/content/gpu/gpu_process.h
new file mode 100644
index 0000000..8dfe0e3
--- /dev/null
+++ b/content/gpu/gpu_process.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_PROCESS_H_
+#define CONTENT_GPU_GPU_PROCESS_H_
+#pragma once
+
+#include "content/common/child_process.h"
+
+class GpuProcess : public ChildProcess {
+ public:
+ GpuProcess();
+ ~GpuProcess();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GpuProcess);
+};
+
+#endif // CONTENT_GPU_GPU_PROCESS_H_
diff --git a/content/gpu/gpu_thread.cc b/content/gpu/gpu_thread.cc
new file mode 100644
index 0000000..7eaf7fd
--- /dev/null
+++ b/content/gpu/gpu_thread.cc
@@ -0,0 +1,347 @@
+// 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 "content/gpu/gpu_thread.h"
+
+#include <string>
+#include <vector>
+
+#include "app/gfx/gl/gl_context.h"
+#include "app/gfx/gl/gl_implementation.h"
+#include "app/win/scoped_com_initializer.h"
+#include "base/command_line.h"
+#include "base/threading/worker_pool.h"
+#include "build/build_config.h"
+#include "content/common/child_process.h"
+#include "content/common/content_client.h"
+#include "content/common/content_switches.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gpu_info_collector.h"
+#include "content/gpu/gpu_watchdog_thread.h"
+#include "ipc/ipc_channel_handle.h"
+
+#if defined(OS_MACOSX)
+#include "content/common/sandbox_init_wrapper.h"
+#include "content/common/sandbox_mac.h"
+#elif defined(OS_WIN)
+#include "sandbox/src/sandbox.h"
+#endif
+
+const int kGpuTimeout = 10000;
+
+namespace {
+
+bool InitializeGpuSandbox() {
+#if defined(OS_MACOSX)
+ CommandLine* parsed_command_line = CommandLine::ForCurrentProcess();
+ SandboxInitWrapper sandbox_wrapper;
+ return sandbox_wrapper.InitializeSandbox(*parsed_command_line,
+ switches::kGpuProcess);
+#else
+ // TODO(port): Create GPU sandbox for linux.
+ return true;
+#endif
+}
+
+} // namespace
+
+#if defined(OS_WIN)
+GpuThread::GpuThread(sandbox::TargetServices* target_services)
+ : target_services_(target_services) {
+}
+#else
+GpuThread::GpuThread() {
+}
+#endif
+
+GpuThread::GpuThread(const std::string& channel_id)
+ : ChildThread(channel_id) {
+#if defined(OS_WIN)
+ target_services_ = NULL;
+#endif
+}
+
+
+GpuThread::~GpuThread() {
+ logging::SetLogMessageHandler(NULL);
+}
+
+void GpuThread::Init(const base::Time& process_start_time) {
+ process_start_time_ = process_start_time;
+}
+
+void GpuThread::RemoveChannel(int renderer_id) {
+ gpu_channels_.erase(renderer_id);
+}
+
+bool GpuThread::OnControlMessageReceived(const IPC::Message& msg) {
+ bool msg_is_ok = true;
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(GpuThread, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(GpuMsg_Initialize, OnInitialize)
+ IPC_MESSAGE_HANDLER(GpuMsg_EstablishChannel, OnEstablishChannel)
+ IPC_MESSAGE_HANDLER(GpuMsg_CloseChannel, OnCloseChannel)
+ IPC_MESSAGE_HANDLER(GpuMsg_CreateViewCommandBuffer,
+ OnCreateViewCommandBuffer);
+ IPC_MESSAGE_HANDLER(GpuMsg_Synchronize, OnSynchronize)
+ IPC_MESSAGE_HANDLER(GpuMsg_CollectGraphicsInfo, OnCollectGraphicsInfo)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(GpuMsg_AcceleratedSurfaceBuffersSwappedACK,
+ OnAcceleratedSurfaceBuffersSwappedACK)
+ IPC_MESSAGE_HANDLER(GpuMsg_DidDestroyAcceleratedSurface,
+ OnDidDestroyAcceleratedSurface)
+#endif
+ IPC_MESSAGE_HANDLER(GpuMsg_Crash, OnCrash)
+ IPC_MESSAGE_HANDLER(GpuMsg_Hang, OnHang)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+namespace {
+
+bool GpuProcessLogMessageHandler(int severity,
+ const char* file, int line,
+ size_t message_start,
+ const std::string& str) {
+ std::string header = str.substr(0, message_start);
+ std::string message = str.substr(message_start);
+ ChildThread::current()->Send(
+ new GpuHostMsg_OnLogMessage(severity, header, message));
+ return false;
+}
+
+} // namespace
+
+void GpuThread::OnInitialize() {
+ // Redirect LOG messages to the GpuProcessHost
+ bool single_process = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSingleProcess);
+ if (!single_process)
+ logging::SetLogMessageHandler(GpuProcessLogMessageHandler);
+
+ // Load the GL implementation and locate the bindings before starting the GPU
+ // watchdog because this can take a lot of time and the GPU watchdog might
+ // terminate the GPU process.
+ if (!gfx::GLContext::InitializeOneOff()) {
+ LOG(INFO) << "GLContext::InitializeOneOff failed";
+ MessageLoop::current()->Quit();
+ return;
+ }
+ bool gpu_info_result = gpu_info_collector::CollectGraphicsInfo(&gpu_info_);
+ if (!gpu_info_result) {
+ gpu_info_.collection_error = true;
+ LOG(ERROR) << "gpu_info_collector::CollectGraphicsInfo() failed";
+ }
+
+ content::GetContentClient()->SetGpuInfo(gpu_info_);
+ LOG(INFO) << "gpu_info_collector::CollectGraphicsInfo complete";
+
+ // Record initialization only after collecting the GPU info because that can
+ // take a significant amount of time.
+ gpu_info_.initialization_time = base::Time::Now() - process_start_time_;
+
+#if defined (OS_MACOSX)
+ // Note that kNoSandbox will also disable the GPU sandbox.
+ bool no_gpu_sandbox = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kNoGpuSandbox);
+ if (!no_gpu_sandbox) {
+ if (!InitializeGpuSandbox()) {
+ LOG(ERROR) << "Failed to initialize the GPU sandbox";
+ MessageLoop::current()->Quit();
+ return;
+ }
+ } else {
+ LOG(ERROR) << "Running without GPU sandbox";
+ }
+#elif defined(OS_WIN)
+ // For windows, if the target_services interface is not zero, the process
+ // is sandboxed and we must call LowerToken() before rendering untrusted
+ // content.
+ if (target_services_)
+ target_services_->LowerToken();
+#endif
+
+ // In addition to disabling the watchdog if the command line switch is
+ // present, disable it in two other cases. OSMesa is expected to run very
+ // slowly. Also disable the watchdog on valgrind because the code is expected
+ // to run slowly in that case.
+ bool enable_watchdog =
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuWatchdog) &&
+ gfx::GetGLImplementation() != gfx::kGLImplementationOSMesaGL &&
+ !RunningOnValgrind();
+
+ // Disable the watchdog in debug builds because they tend to only be run by
+ // developers who will not appreciate the watchdog killing the GPU process.
+#ifndef NDEBUG
+ enable_watchdog = false;
+#endif
+
+ // Disable the watchdog for Windows. It tends to abort when the GPU process
+ // is not hung but still taking a long time to do something. Instead, the
+ // browser process displays a dialog when it notices that the child window
+ // is hung giving the user an opportunity to terminate it. This is the
+ // same mechanism used to abort hung plugins.
+#if defined(OS_WIN)
+ enable_watchdog = false;
+#endif
+
+ // Start the GPU watchdog only after anything that is expected to be time
+ // consuming has completed, otherwise the process is liable to be aborted.
+ if (enable_watchdog) {
+ watchdog_thread_ = new GpuWatchdogThread(kGpuTimeout);
+ watchdog_thread_->Start();
+ }
+}
+
+void GpuThread::StopWatchdog() {
+ if (watchdog_thread_.get()) {
+ watchdog_thread_->Stop();
+ }
+}
+
+void GpuThread::OnEstablishChannel(int renderer_id) {
+ scoped_refptr<GpuChannel> channel;
+ IPC::ChannelHandle channel_handle;
+ GPUInfo gpu_info;
+
+ GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id);
+ if (iter == gpu_channels_.end())
+ channel = new GpuChannel(this, renderer_id);
+ else
+ channel = iter->second;
+
+ DCHECK(channel != NULL);
+
+ if (channel->Init())
+ gpu_channels_[renderer_id] = channel;
+ else
+ channel = NULL;
+
+ if (channel.get()) {
+ channel_handle.name = channel->GetChannelName();
+#if defined(OS_POSIX)
+ // On POSIX, pass the renderer-side FD. Also mark it as auto-close so
+ // that it gets closed after it has been sent.
+ int renderer_fd = channel->GetRendererFileDescriptor();
+ channel_handle.socket = base::FileDescriptor(renderer_fd, false);
+#endif
+ }
+
+ Send(new GpuHostMsg_ChannelEstablished(channel_handle, gpu_info_));
+}
+
+void GpuThread::OnCloseChannel(const IPC::ChannelHandle& channel_handle) {
+ for (GpuChannelMap::iterator iter = gpu_channels_.begin();
+ iter != gpu_channels_.end(); ++iter) {
+ if (iter->second->GetChannelName() == channel_handle.name) {
+ gpu_channels_.erase(iter);
+ return;
+ }
+ }
+}
+
+void GpuThread::OnSynchronize() {
+ Send(new GpuHostMsg_SynchronizeReply());
+}
+
+void GpuThread::OnCollectGraphicsInfo(GPUInfo::Level level) {
+#if defined(OS_WIN)
+ if (level == GPUInfo::kComplete && gpu_info_.level <= GPUInfo::kPartial) {
+ // Prevent concurrent collection of DirectX diagnostics.
+ gpu_info_.level = GPUInfo::kCompleting;
+
+ // Asynchronously collect the DirectX diagnostics because this can take a
+ // couple of seconds.
+ if (!base::WorkerPool::PostTask(
+ FROM_HERE,
+ NewRunnableFunction(&GpuThread::CollectDxDiagnostics, this),
+ true)) {
+
+ // Flag GPU info as complete if the DirectX diagnostics cannot be
+ // collected.
+ gpu_info_.level = GPUInfo::kComplete;
+ } else {
+ // Do not send response if we are still completing the GPUInfo struct
+ return;
+ }
+ }
+#endif
+ Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_));
+}
+
+void GpuThread::OnCreateViewCommandBuffer(
+ gfx::PluginWindowHandle window,
+ int32 render_view_id,
+ int32 renderer_id,
+ const GPUCreateCommandBufferConfig& init_params) {
+ int32 route_id = MSG_ROUTING_NONE;
+
+ GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id);
+ if (iter != gpu_channels_.end()) {
+ iter->second->CreateViewCommandBuffer(
+ window, render_view_id, init_params, &route_id);
+ }
+
+ Send(new GpuHostMsg_CommandBufferCreated(route_id));
+}
+
+#if defined(OS_MACOSX)
+void GpuThread::OnAcceleratedSurfaceBuffersSwappedACK(
+ int renderer_id, int32 route_id, uint64 swap_buffers_count) {
+ GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id);
+ if (iter == gpu_channels_.end())
+ return;
+ scoped_refptr<GpuChannel> channel = iter->second;
+ channel->AcceleratedSurfaceBuffersSwapped(route_id, swap_buffers_count);
+}
+void GpuThread::OnDidDestroyAcceleratedSurface(
+ int renderer_id, int32 renderer_route_id) {
+ GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id);
+ if (iter == gpu_channels_.end())
+ return;
+ scoped_refptr<GpuChannel> channel = iter->second;
+ channel->DidDestroySurface(renderer_route_id);
+}
+#endif
+
+void GpuThread::OnCrash() {
+ LOG(INFO) << "GPU: Simulating GPU crash";
+ // Good bye, cruel world.
+ volatile int* it_s_the_end_of_the_world_as_we_know_it = NULL;
+ *it_s_the_end_of_the_world_as_we_know_it = 0xdead;
+}
+
+void GpuThread::OnHang() {
+ LOG(INFO) << "GPU: Simulating GPU hang";
+ for (;;) {
+ // Do not sleep here. The GPU watchdog timer tracks the amount of user
+ // time this thread is using and it doesn't use much while calling Sleep.
+ }
+}
+
+#if defined(OS_WIN)
+
+// Runs on a worker thread. The GpuThread never terminates voluntarily so it is
+// safe to assume that its message loop is valid.
+void GpuThread::CollectDxDiagnostics(GpuThread* thread) {
+ app::win::ScopedCOMInitializer com_initializer;
+
+ DxDiagNode node;
+ gpu_info_collector::GetDxDiagnostics(&node);
+
+ thread->message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableFunction(&GpuThread::SetDxDiagnostics, thread, node));
+}
+
+// Runs on the GPU thread.
+void GpuThread::SetDxDiagnostics(GpuThread* thread, const DxDiagNode& node) {
+ thread->gpu_info_.dx_diagnostics = node;
+ thread->gpu_info_.level = GPUInfo::kComplete;
+ thread->Send(new GpuHostMsg_GraphicsInfoCollected(thread->gpu_info_));
+}
+
+#endif
diff --git a/content/gpu/gpu_thread.h b/content/gpu/gpu_thread.h
new file mode 100644
index 0000000..21ad923
--- /dev/null
+++ b/content/gpu/gpu_thread.h
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_THREAD_H_
+#define CONTENT_GPU_GPU_THREAD_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "build/build_config.h"
+#include "content/common/child_thread.h"
+#include "content/common/gpu_info.h"
+#include "content/gpu/gpu_channel.h"
+#include "content/gpu/gpu_config.h"
+#include "content/gpu/x_util.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace IPC {
+struct ChannelHandle;
+}
+
+namespace sandbox {
+class TargetServices;
+}
+
+class GpuWatchdogThread;
+
+class GpuThread : public ChildThread {
+ public:
+#if defined(OS_WIN)
+ explicit GpuThread(sandbox::TargetServices* target_services);
+#else
+ GpuThread();
+#endif
+
+ // For single-process mode.
+ explicit GpuThread(const std::string& channel_id);
+
+ ~GpuThread();
+
+ void Init(const base::Time& process_start_time);
+ void StopWatchdog();
+
+ // Remove the channel for a particular renderer.
+ void RemoveChannel(int renderer_id);
+
+ private:
+ // ChildThread overrides.
+ virtual bool OnControlMessageReceived(const IPC::Message& msg);
+
+ // Message handlers.
+ void OnInitialize();
+ void OnEstablishChannel(int renderer_id);
+ void OnCloseChannel(const IPC::ChannelHandle& channel_handle);
+ void OnSynchronize();
+ void OnCollectGraphicsInfo(GPUInfo::Level level);
+ void OnCreateViewCommandBuffer(
+ gfx::PluginWindowHandle window,
+ int32 render_view_id,
+ int32 renderer_id,
+ const GPUCreateCommandBufferConfig& init_params);
+#if defined(OS_MACOSX)
+ void OnAcceleratedSurfaceBuffersSwappedACK(
+ int renderer_id, int32 route_id, uint64 swap_buffers_count);
+ void OnDidDestroyAcceleratedSurface(int renderer_id, int32 renderer_route_id);
+#endif
+ void OnCrash();
+ void OnHang();
+
+#if defined(OS_WIN)
+ static void CollectDxDiagnostics(GpuThread* thread);
+ static void SetDxDiagnostics(GpuThread* thread, const DxDiagNode& node);
+#endif
+
+ base::Time process_start_time_;
+ scoped_refptr<GpuWatchdogThread> watchdog_thread_;
+
+ typedef base::hash_map<int, scoped_refptr<GpuChannel> > GpuChannelMap;
+ GpuChannelMap gpu_channels_;
+
+ // Information about the GPU, such as device and vendor ID.
+ GPUInfo gpu_info_;
+
+#if defined(OS_WIN)
+ // Windows specific client sandbox interface.
+ sandbox::TargetServices* target_services_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(GpuThread);
+};
+
+#endif // CONTENT_GPU_GPU_THREAD_H_
diff --git a/content/gpu/gpu_video_decoder.cc b/content/gpu/gpu_video_decoder.cc
new file mode 100644
index 0000000..0416fcd
--- /dev/null
+++ b/content/gpu/gpu_video_decoder.cc
@@ -0,0 +1,427 @@
+// 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 "content/gpu/gpu_video_decoder.h"
+
+#include "base/command_line.h"
+#include "content/common/child_thread.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gpu_channel.h"
+#include "content/gpu/media/fake_gl_video_decode_engine.h"
+#include "content/gpu/media/fake_gl_video_device.h"
+#include "media/base/data_buffer.h"
+#include "media/base/media_switches.h"
+#include "media/base/video_frame.h"
+
+#if defined(OS_WIN)
+#include "content/gpu/media/mft_angle_video_device.h"
+#include "media/video/mft_h264_decode_engine.h"
+#include <d3d9.h>
+#endif
+
+struct GpuVideoDecoder::PendingAllocation {
+ size_t n;
+ size_t width;
+ size_t height;
+ media::VideoFrame::Format format;
+ std::vector<scoped_refptr<media::VideoFrame> >* frames;
+ Task* task;
+};
+
+void GpuVideoDecoder::OnChannelConnected(int32 peer_pid) {
+}
+
+void GpuVideoDecoder::OnChannelError() {
+}
+
+bool GpuVideoDecoder::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoder, msg)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Initialize,
+ OnInitialize)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Destroy,
+ OnUninitialize)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Flush,
+ OnFlush)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Preroll,
+ OnPreroll)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_EmptyThisBuffer,
+ OnEmptyThisBuffer)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_ProduceVideoFrame,
+ OnProduceVideoFrame)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_VideoFrameAllocated,
+ OnVideoFrameAllocated)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ DCHECK(handled);
+ return handled;
+}
+
+bool GpuVideoDecoder::CreateInputTransferBuffer(
+ uint32 size,
+ base::SharedMemoryHandle* handle) {
+ input_transfer_buffer_.reset(new base::SharedMemory);
+ if (!input_transfer_buffer_.get())
+ return false;
+
+ if (!input_transfer_buffer_->CreateAndMapAnonymous(size))
+ return false;
+
+ if (!input_transfer_buffer_->ShareToProcess(renderer_handle_, handle))
+ return false;
+
+ return true;
+}
+
+void GpuVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) {
+ info_ = info;
+ GpuVideoDecoderInitDoneParam param;
+ param.success = false;
+ param.input_buffer_handle = base::SharedMemory::NULLHandle();
+
+ if (!info.success) {
+ SendInitializeDone(param);
+ return;
+ }
+
+ // TODO(jiesun): Check the assumption of input size < original size.
+ param.input_buffer_size =
+ info.stream_info.surface_width * info.stream_info.surface_height * 3 / 2;
+ if (!CreateInputTransferBuffer(param.input_buffer_size,
+ &param.input_buffer_handle)) {
+ SendInitializeDone(param);
+ return;
+ }
+
+ param.success = true;
+ SendInitializeDone(param);
+}
+
+void GpuVideoDecoder::OnUninitializeComplete() {
+ SendUninitializeDone();
+}
+
+void GpuVideoDecoder::OnFlushComplete() {
+ SendFlushDone();
+}
+
+void GpuVideoDecoder::OnSeekComplete() {
+ SendPrerollDone();
+}
+
+void GpuVideoDecoder::OnError() {
+ NOTIMPLEMENTED();
+}
+
+void GpuVideoDecoder::OnFormatChange(VideoStreamInfo stream_info) {
+ NOTIMPLEMENTED();
+}
+
+void GpuVideoDecoder::ProduceVideoSample(scoped_refptr<Buffer> buffer) {
+ SendEmptyBufferDone();
+}
+
+void GpuVideoDecoder::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame,
+ const PipelineStatistics& statistics) {
+ // TODO(sjl): Do something with the statistics...
+
+ if (frame->IsEndOfStream()) {
+ SendConsumeVideoFrame(0, 0, 0, kGpuVideoEndOfStream);
+ return;
+ }
+
+ int32 frame_id = -1;
+ for (VideoFrameMap::iterator i = video_frame_map_.begin();
+ i != video_frame_map_.end(); ++i) {
+ if (i->second == frame) {
+ frame_id = i->first;
+ break;
+ }
+ }
+
+ if (frame_id == -1) {
+ NOTREACHED() << "VideoFrame not recognized";
+ return;
+ }
+
+ SendConsumeVideoFrame(frame_id, frame->GetTimestamp().InMicroseconds(),
+ frame->GetDuration().InMicroseconds(), 0);
+}
+
+void* GpuVideoDecoder::GetDevice() {
+ bool ret = gles2_decoder_->MakeCurrent();
+ DCHECK(ret) << "Failed to switch context";
+
+ // Simply delegate the method call to GpuVideoDevice.
+ return video_device_->GetDevice();
+}
+
+void GpuVideoDecoder::AllocateVideoFrames(
+ int n, size_t width, size_t height, media::VideoFrame::Format format,
+ std::vector<scoped_refptr<media::VideoFrame> >* frames, Task* task) {
+ // Since the communication between Renderer and GPU process is by GL textures.
+ // We need to obtain a set of GL textures by sending IPC commands to the
+ // Renderer process. The recipient of these commands will be IpcVideoDecoder.
+ //
+ // After IpcVideoDecoder replied with a set of textures. We'll assign these
+ // textures to GpuVideoDevice. They will be used to generate platform
+ // specific VideoFrames objects that are used by VideoDecodeEngine.
+ //
+ // After GL textures are assigned we'll proceed with allocation the
+ // VideoFrames. GpuVideoDevice::CreateVideoFramesFromGlTextures() will be
+ // called.
+ //
+ // When GpuVideoDevice replied with a set of VideoFrames we'll give
+ // that to VideoDecodeEngine and the cycle of video frame allocation is done.
+ //
+ // Note that this method is called when there's no video frames allocated or
+ // they were all released.
+ DCHECK(video_frame_map_.empty());
+
+ // Save the parameters for allocation.
+ pending_allocation_.reset(new PendingAllocation());
+ pending_allocation_->n = n;
+ pending_allocation_->width = width;
+ pending_allocation_->height = height;
+ pending_allocation_->format = format;
+ pending_allocation_->frames = frames;
+ pending_allocation_->task = task;
+ SendAllocateVideoFrames(n, width, height, format);
+}
+
+void GpuVideoDecoder::ReleaseAllVideoFrames() {
+ // This method will first call to GpuVideoDevice to release all the resource
+ // associated with a VideoFrame.
+ //
+ // And then we'll call GpuVideoDevice::ReleaseVideoFrame() to remove the set
+ // of Gl textures associated with the context.
+ //
+ // And finally we'll send IPC commands to IpcVideoDecoder to destroy all
+ // GL textures generated.
+ bool ret = gles2_decoder_->MakeCurrent();
+ DCHECK(ret) << "Failed to switch context";
+
+ for (VideoFrameMap::iterator i = video_frame_map_.begin();
+ i != video_frame_map_.end(); ++i) {
+ video_device_->ReleaseVideoFrame(i->second);
+ }
+ video_frame_map_.clear();
+ SendReleaseAllVideoFrames();
+}
+
+void GpuVideoDecoder::ConvertToVideoFrame(
+ void* buffer,
+ scoped_refptr<media::VideoFrame> frame,
+ Task* task) {
+ // This method is called by VideoDecodeEngine to upload a buffer to a
+ // VideoFrame. We should just delegate this to GpuVideoDevice which contains
+ // the actual implementation.
+ bool ret = gles2_decoder_->MakeCurrent();
+ DCHECK(ret) << "Failed to switch context";
+
+ // Actually doing the upload on the main thread.
+ ret = video_device_->ConvertToVideoFrame(buffer, frame);
+ DCHECK(ret) << "Failed to upload video content to a VideoFrame.";
+ task->Run();
+ delete task;
+}
+
+void GpuVideoDecoder::Destroy(Task* task) {
+ // TODO(hclam): I still need to think what I should do here.
+}
+
+void GpuVideoDecoder::SetVideoDecodeEngine(media::VideoDecodeEngine* engine) {
+ decode_engine_.reset(engine);
+}
+
+void GpuVideoDecoder::SetGpuVideoDevice(GpuVideoDevice* device) {
+ video_device_.reset(device);
+}
+
+GpuVideoDecoder::GpuVideoDecoder(
+ MessageLoop* message_loop,
+ int32 decoder_host_id,
+ IPC::Message::Sender* sender,
+ base::ProcessHandle handle,
+ gpu::gles2::GLES2Decoder* decoder)
+ : message_loop_(message_loop),
+ decoder_host_id_(decoder_host_id),
+ sender_(sender),
+ renderer_handle_(handle),
+ gles2_decoder_(decoder) {
+ memset(&info_, 0, sizeof(info_));
+
+ // TODO(jiesun): find a better way to determine which VideoDecodeEngine
+ // to return on current platform.
+#if defined(OS_WIN)
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kEnableAcceleratedDecoding)) {
+ // The following code are removed so that we don't link them.
+ // TODO(hclam): Enable the code once the crash is solved on XP.
+ // decode_engine_.reset(new media::MftH264DecodeEngine(true));
+ // video_device_.reset(new MftAngleVideoDevice());
+ }
+#else
+ decode_engine_.reset(new FakeGlVideoDecodeEngine());
+ video_device_.reset(new FakeGlVideoDevice());
+#endif
+}
+
+GpuVideoDecoder::~GpuVideoDecoder() {}
+
+void GpuVideoDecoder::OnInitialize(const GpuVideoDecoderInitParam& param) {
+ // TODO(jiesun): codec id should come from |param|.
+ media::VideoCodecConfig config(media::kCodecH264,
+ param.width,
+ param.height,
+ param.frame_rate_num,
+ param.frame_rate_den,
+ NULL,
+ 0);
+ decode_engine_->Initialize(message_loop_, this, this, config);
+}
+
+void GpuVideoDecoder::OnUninitialize() {
+ decode_engine_->Uninitialize();
+}
+
+void GpuVideoDecoder::OnFlush() {
+ decode_engine_->Flush();
+}
+
+void GpuVideoDecoder::OnPreroll() {
+ decode_engine_->Seek();
+}
+
+void GpuVideoDecoder::OnEmptyThisBuffer(
+ const GpuVideoDecoderInputBufferParam& buffer) {
+ DCHECK(input_transfer_buffer_->memory());
+
+ uint8* src = static_cast<uint8*>(input_transfer_buffer_->memory());
+
+ scoped_refptr<Buffer> input_buffer;
+ uint8* dst = buffer.size ? new uint8[buffer.size] : NULL;
+ input_buffer = new media::DataBuffer(dst, buffer.size);
+ memcpy(dst, src, buffer.size);
+ SendEmptyBufferACK();
+
+ // Delegate the method call to VideoDecodeEngine.
+ decode_engine_->ConsumeVideoSample(input_buffer);
+}
+
+void GpuVideoDecoder::OnProduceVideoFrame(int32 frame_id) {
+ VideoFrameMap::iterator i = video_frame_map_.find(frame_id);
+ if (i == video_frame_map_.end()) {
+ NOTREACHED() << "Received a request of unknown frame ID.";
+ }
+
+ // Delegate the method call to VideoDecodeEngine.
+ decode_engine_->ProduceVideoFrame(i->second);
+}
+
+void GpuVideoDecoder::OnVideoFrameAllocated(int32 frame_id,
+ std::vector<uint32> textures) {
+ bool ret = gles2_decoder_->MakeCurrent();
+ DCHECK(ret) << "Failed to switch context";
+
+ // This method is called in response to a video frame allocation request sent
+ // to the Renderer process.
+ // We should use the textures to generate a VideoFrame by using
+ // GpuVideoDevice. The VideoFrame created is added to the internal map.
+ // If we have generated enough VideoFrame, we call |allocation_callack_| to
+ // complete the allocation process.
+ for (size_t i = 0; i < textures.size(); ++i) {
+ media::VideoFrame::GlTexture gl_texture;
+ // Translate the client texture id to service texture id.
+ ret = gles2_decoder_->GetServiceTextureId(textures[i], &gl_texture);
+ DCHECK(ret) << "Cannot translate client texture ID to service ID";
+ textures[i] = gl_texture;
+ }
+
+ // Use GpuVideoDevice to allocate VideoFrame objects.
+ scoped_refptr<media::VideoFrame> frame;
+
+ ret = video_device_->CreateVideoFrameFromGlTextures(
+ pending_allocation_->width, pending_allocation_->height,
+ pending_allocation_->format, textures, &frame);
+
+ DCHECK(ret) << "Failed to allocation VideoFrame from GL textures)";
+ pending_allocation_->frames->push_back(frame);
+ video_frame_map_.insert(std::make_pair(frame_id, frame));
+
+ if (video_frame_map_.size() == pending_allocation_->n) {
+ pending_allocation_->task->Run();
+ delete pending_allocation_->task;
+ pending_allocation_.reset();
+ }
+}
+
+void GpuVideoDecoder::SendInitializeDone(
+ const GpuVideoDecoderInitDoneParam& param) {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_InitializeACK(decoder_host_id(), param))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_InitializeACK failed";
+ }
+}
+
+void GpuVideoDecoder::SendUninitializeDone() {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_DestroyACK(decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_DestroyACK failed";
+ }
+}
+
+void GpuVideoDecoder::SendFlushDone() {
+ if (!sender_->Send(new GpuVideoDecoderHostMsg_FlushACK(decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_FlushACK failed";
+ }
+}
+
+void GpuVideoDecoder::SendPrerollDone() {
+ if (!sender_->Send(new GpuVideoDecoderHostMsg_PrerollDone(
+ decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_PrerollDone failed";
+ }
+}
+
+void GpuVideoDecoder::SendEmptyBufferDone() {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_EmptyThisBufferDone(decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferDone failed";
+ }
+}
+
+void GpuVideoDecoder::SendEmptyBufferACK() {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_EmptyThisBufferACK(decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferACK failed";
+ }
+}
+
+void GpuVideoDecoder::SendConsumeVideoFrame(
+ int32 frame_id, int64 timestamp, int64 duration, int32 flags) {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_ConsumeVideoFrame(
+ decoder_host_id(), frame_id, timestamp, duration, flags))) {
+ LOG(ERROR) << "GpuVideoDecodeHostMsg_ConsumeVideoFrame failed.";
+ }
+}
+
+void GpuVideoDecoder::SendAllocateVideoFrames(
+ int n, size_t width, size_t height, media::VideoFrame::Format format) {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_AllocateVideoFrames(
+ decoder_host_id(), n, width, height,
+ static_cast<int32>(format)))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_AllocateVideoFrames failed";
+ }
+}
+
+void GpuVideoDecoder::SendReleaseAllVideoFrames() {
+ if (!sender_->Send(
+ new GpuVideoDecoderHostMsg_ReleaseAllVideoFrames(
+ decoder_host_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_ReleaseAllVideoFrames failed";
+ }
+}
diff --git a/content/gpu/gpu_video_decoder.h b/content/gpu/gpu_video_decoder.h
new file mode 100644
index 0000000..ce91491
--- /dev/null
+++ b/content/gpu/gpu_video_decoder.h
@@ -0,0 +1,209 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_VIDEO_DECODER_H_
+#define CONTENT_GPU_GPU_VIDEO_DECODER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/process.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "content/gpu/media/gpu_video_device.h"
+#include "media/video/video_decode_context.h"
+#include "media/video/video_decode_engine.h"
+#include "ipc/ipc_channel.h"
+
+using media::Buffer;
+using media::PipelineStatistics;
+using media::VideoCodecConfig;
+using media::VideoCodecInfo;
+using media::VideoStreamInfo;
+using media::VideoFrame;
+
+namespace gpu {
+namespace gles2 {
+class GLES2Decoder;
+} // namespace gles2
+} // namespace gpu
+
+class GpuChannel;
+struct GpuVideoDecoderInitDoneParam;
+struct GpuVideoDecoderInitParam;
+struct GpuVideoDecoderInputBufferParam;
+
+// A GpuVideoDecoder is a platform independent video decoder that uses platform
+// specific VideoDecodeEngine and GpuVideoDevice for the actual decoding
+// operations.
+//
+// In addition to delegating video related commamnds to VideoDecodeEngine it
+// has the following important functions:
+//
+// BUFFER ALLOCATION
+//
+// VideoDecodeEngine requires platform specific video frame buffer to operate.
+// In order to abstract the platform specific bits GpuVideoDecoderContext is
+// used to allocate video frames that a VideoDecodeEngine can use.
+//
+// Since all the video frames appear to the Renderer process as textures, the
+// first thing GpuVideoDecoder needs to do is to ask Renderer process to
+// generate and allocate textures. This will establish a texture record in the
+// command buffer decoder. After the texture is allocated, this class will
+// pass the textures meaningful in the local GLES context to
+// GpuVideoDevice for generating VideoFrames that VideoDecodeEngine
+// can actually use.
+//
+// In order to coordinate these operations, GpuVideoDecoder implements
+// VideoDecodeContext and is injected into the VideoDecodeEngine.
+//
+// The sequence of operations is:
+// 1. VideoDecodeEngine requests by call AllocateVideoFrames().
+// 2. GpuVideoDecoder receives AllocateVideoFrames() and then call to the
+// Renderer process to generate textures.
+// 3. Renderer process replied with textures.
+// 4. Textures generated are passed into GpuVideoDevice.
+// 5. GpuVideoDevice::AllocateVideoFrames() is called to generate
+// VideoFrame(s) from the textures.
+// 6. GpuVideoDecoder sends the VideoFrame(s) generated to VideoDecodeEngine.
+//
+// BUFFER UPLOADING
+//
+// A VideoDecodeEngine always produces some device specific buffer. In order to
+// use them in Chrome we always upload them to GL textures. The upload step is
+// different on each platform and each subsystem. We perform these special
+// upload steps by using GpuVideoDevice which are written for each
+// VideoDecodeEngine.
+//
+// BUFFER MAPPING
+//
+// GpuVideoDecoder will be working with VideoDecodeEngine, they exchange
+// buffers that are only meaningful to VideoDecodeEngine. In order to map that
+// to something we can transport in the IPC channel we need a mapping between
+// VideoFrame and buffer ID known between GpuVideoDecoder and
+// GpuVideoDecoderHost in the Renderer process.
+//
+// After texture allocation and VideoFrame allocation are done, GpuVideoDecoder
+// will maintain such mapping.
+//
+class GpuVideoDecoder
+ : public base::RefCountedThreadSafe<GpuVideoDecoder>,
+ public IPC::Channel::Listener,
+ public media::VideoDecodeEngine::EventHandler,
+ public media::VideoDecodeContext {
+ public:
+ // Constructor and destructor.
+ GpuVideoDecoder(MessageLoop* message_loop,
+ int32 decoder_host_id,
+ IPC::Message::Sender* sender,
+ base::ProcessHandle handle,
+ gpu::gles2::GLES2Decoder* decoder);
+ virtual ~GpuVideoDecoder();
+
+ // IPC::Channel::Listener implementation.
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // VideoDecodeEngine::EventHandler implementation.
+ virtual void OnInitializeComplete(const VideoCodecInfo& info);
+ virtual void OnUninitializeComplete();
+ virtual void OnFlushComplete();
+ virtual void OnSeekComplete();
+ virtual void OnError();
+ virtual void OnFormatChange(VideoStreamInfo stream_info);
+ virtual void ProduceVideoSample(scoped_refptr<Buffer> buffer);
+ virtual void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame,
+ const PipelineStatistics& statistics);
+
+ // VideoDecodeContext implementation.
+ virtual void* GetDevice();
+ virtual void AllocateVideoFrames(
+ int n, size_t width, size_t height, media::VideoFrame::Format format,
+ std::vector<scoped_refptr<media::VideoFrame> >* frames, Task* task);
+ virtual void ReleaseAllVideoFrames();
+ virtual void ConvertToVideoFrame(void* buffer,
+ scoped_refptr<media::VideoFrame> frame,
+ Task* task);
+ virtual void Destroy(Task* task);
+
+ // These methods are used in unit test only.
+ void SetVideoDecodeEngine(media::VideoDecodeEngine* engine);
+ void SetGpuVideoDevice(GpuVideoDevice* device);
+
+ private:
+ struct PendingAllocation;
+
+ int32 decoder_host_id() { return decoder_host_id_; }
+
+ bool CreateInputTransferBuffer(uint32 size,
+ base::SharedMemoryHandle* handle);
+
+ // These methods are message handlers for the messages sent from the Renderer
+ // process.
+ void OnInitialize(const GpuVideoDecoderInitParam& param);
+ void OnUninitialize();
+ void OnFlush();
+ void OnPreroll();
+ void OnEmptyThisBuffer(const GpuVideoDecoderInputBufferParam& buffer);
+ void OnProduceVideoFrame(int32 frame_id);
+ void OnVideoFrameAllocated(int32 frame_id, std::vector<uint32> textures);
+
+ // Helper methods for sending messages to the Renderer process.
+ void SendInitializeDone(const GpuVideoDecoderInitDoneParam& param);
+ void SendUninitializeDone();
+ void SendFlushDone();
+ void SendPrerollDone();
+ void SendEmptyBufferDone();
+ void SendEmptyBufferACK();
+ void SendConsumeVideoFrame(int32 frame_id, int64 timestamp, int64 duration,
+ int32 flags);
+ void SendAllocateVideoFrames(
+ int n, size_t width, size_t height, media::VideoFrame::Format format);
+ void SendReleaseAllVideoFrames();
+
+ // The message loop that this object should run on.
+ MessageLoop* message_loop_;
+
+ // ID of GpuVideoDecoderHost in the Renderer Process.
+ int32 decoder_host_id_;
+
+ // Used only in system memory path. i.e. Remove this later.
+ scoped_refptr<VideoFrame> frame_;
+
+ IPC::Message::Sender* sender_;
+ base::ProcessHandle renderer_handle_;
+
+ // The GLES2 decoder has the context associated with this decoder. This object
+ // is used to switch context and translate client texture ID to service ID.
+ gpu::gles2::GLES2Decoder* gles2_decoder_;
+
+ // Memory for transfering the input data for the hardware video decoder.
+ scoped_ptr<base::SharedMemory> input_transfer_buffer_;
+
+ // VideoDecodeEngine is used to do the actual video decoding.
+ scoped_ptr<media::VideoDecodeEngine> decode_engine_;
+
+ // GpuVideoDevice is used to generate VideoFrame(s) from GL textures. The
+ // frames generated are understood by the decode engine.
+ scoped_ptr<GpuVideoDevice> video_device_;
+
+ // Contain information for allocation VideoFrame(s).
+ scoped_ptr<PendingAllocation> pending_allocation_;
+
+ // Contains the mapping between a |frame_id| and VideoFrame generated by
+ // GpuVideoDevice from the associated GL textures.
+ // TODO(hclam): Using a faster data structure than map.
+ typedef std::map<int32, scoped_refptr<media::VideoFrame> > VideoFrameMap;
+ VideoFrameMap video_frame_map_;
+
+ media::VideoCodecInfo info_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoder);
+};
+
+#endif // CONTENT_GPU_GPU_VIDEO_DECODER_H_
diff --git a/content/gpu/gpu_video_decoder_unittest.cc b/content/gpu/gpu_video_decoder_unittest.cc
new file mode 100644
index 0000000..afb396b
--- /dev/null
+++ b/content/gpu/gpu_video_decoder_unittest.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2010 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 "base/message_loop.h"
+#include "base/process.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gpu_video_decoder.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
+#include "ipc/ipc_message_utils.h"
+#include "media/base/pipeline.h"
+#include "media/video/video_mock_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::NotNull;
+using testing::Return;
+using testing::SetArgumentPointee;
+
+static const int32 kFrameId = 10;
+static const int32 kDecoderHostId = 50;
+static const media::VideoFrame::GlTexture kClientTexture = 101;
+static const media::VideoFrame::GlTexture kServiceTexture = 102;
+static const size_t kWidth = 320;
+static const size_t kHeight = 240;
+
+class MockGpuVideoDevice : public GpuVideoDevice {
+ public:
+ MockGpuVideoDevice() {}
+ virtual ~MockGpuVideoDevice() {}
+
+ MOCK_METHOD0(GetDevice, void*());
+ MOCK_METHOD5(CreateVideoFrameFromGlTextures,
+ bool(size_t, size_t, media::VideoFrame::Format,
+ const std::vector<media::VideoFrame::GlTexture>&,
+ scoped_refptr<media::VideoFrame>*));
+ MOCK_METHOD1(ReleaseVideoFrame,
+ void(const scoped_refptr<media::VideoFrame>& frame));
+ MOCK_METHOD2(ConvertToVideoFrame,
+ bool(void* buffer, scoped_refptr<media::VideoFrame> frame));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockGpuVideoDevice);
+};
+
+ACTION_P(InitializationDone, handler) {
+ media::VideoCodecInfo info;
+ info.success = true;
+ info.provides_buffers = false;
+ info.stream_info.surface_format = media::VideoFrame::RGBA;
+ info.stream_info.surface_type = media::VideoFrame::TYPE_SYSTEM_MEMORY;
+ info.stream_info.surface_width = kWidth;
+ info.stream_info.surface_height = kHeight;
+ handler->OnInitializeComplete(info);
+}
+
+ACTION_P(SendVideoFrameAllocated, handler) {
+ std::vector<media::VideoFrame::GlTexture> textures;
+ textures.push_back(kClientTexture);
+ GpuVideoDecoderMsg_VideoFrameAllocated msg(0, kFrameId, textures);
+ handler->OnMessageReceived(msg);
+}
+
+ACTION_P2(SendConsumeVideoFrame, handler, frame) {
+ media::PipelineStatistics statistics;
+ handler->ConsumeVideoFrame(frame, statistics);
+}
+
+class GpuVideoDecoderTest : public testing::Test,
+ public IPC::Message::Sender {
+ public:
+ GpuVideoDecoderTest() {
+ // Create the mock objects.
+ gles2_decoder_.reset(new gpu::gles2::MockGLES2Decoder());
+
+ gpu_video_decoder_ = new GpuVideoDecoder(
+ &message_loop_, kDecoderHostId, this, base::kNullProcessHandle,
+ gles2_decoder_.get());
+
+ // Create the mock objects.
+ mock_engine_ = new media::MockVideoDecodeEngine();
+ mock_device_ = new MockGpuVideoDevice();
+
+ // Inject the mock objects.
+ gpu_video_decoder_->SetVideoDecodeEngine(mock_engine_);
+ gpu_video_decoder_->SetGpuVideoDevice(mock_device_);
+
+ // VideoFrame for GpuVideoDevice.
+ media::VideoFrame::GlTexture textures[] = { kServiceTexture, 0, 0 };
+ media::VideoFrame::CreateFrameGlTexture(media::VideoFrame::RGBA,
+ kWidth, kHeight, textures,
+ &device_frame_);
+ }
+
+ virtual ~GpuVideoDecoderTest() {
+ gpu_video_decoder_->SetVideoDecodeEngine(NULL);
+ gpu_video_decoder_->SetGpuVideoDevice(NULL);
+ }
+
+ // This method is used to dispatch IPC messages to mock methods.
+ virtual bool Send(IPC::Message* msg) {
+ EXPECT_TRUE(msg);
+ if (!msg)
+ return false;
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderTest, *msg)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_InitializeACK,
+ OnInitializeDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_DestroyACK,
+ OnUninitializeDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_FlushACK,
+ OnFlushDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferACK,
+ OnEmptyThisBufferACK)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferDone,
+ OnEmptyThisBufferDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_AllocateVideoFrames,
+ OnAllocateVideoFrames)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ReleaseAllVideoFrames,
+ OnReleaseAllVideoFrames)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ConsumeVideoFrame,
+ OnConsumeVideoFrame)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP()
+ EXPECT_TRUE(handled);
+ delete msg;
+ return true;
+ }
+
+ // Mock methods for handling output IPC messages.
+ MOCK_METHOD1(OnInitializeDone,
+ void(const GpuVideoDecoderInitDoneParam& param));
+ MOCK_METHOD0(OnUninitializeDone, void());
+ MOCK_METHOD0(OnFlushDone, void());
+ MOCK_METHOD0(OnEmptyThisBufferDone, void());
+ MOCK_METHOD4(OnConsumeVideoFrame, void(int32 device_frame_id, int64 timestamp,
+ int64 duration, int32 flags));
+ MOCK_METHOD0(OnEmptyThisBufferACK, void());
+ MOCK_METHOD4(OnAllocateVideoFrames, void(int32 n, uint32 width,
+ uint32 height, int32 format));
+ MOCK_METHOD0(OnReleaseAllVideoFrames, void());
+
+ // Receive events from GpuVideoDecoder.
+ MOCK_METHOD0(VideoFramesAllocated, void());
+
+ void Initialize() {
+ // VideoDecodeEngine is called.
+ EXPECT_CALL(*mock_engine_, Initialize(_, _, _, _))
+ .WillOnce(InitializationDone(gpu_video_decoder_));
+
+ // Expect that initialization is completed.
+ EXPECT_CALL(*this, OnInitializeDone(_));
+
+ // Send an initialiaze message to GpuVideoDecoder.
+ GpuVideoDecoderInitParam param;
+ param.width = kWidth;
+ param.height = kHeight;
+
+ GpuVideoDecoderMsg_Initialize msg(0, param);
+ gpu_video_decoder_->OnMessageReceived(msg);
+ }
+
+ void AllocateVideoFrames() {
+ // Expect that IPC messages are sent. We'll reply with some GL textures.
+ EXPECT_CALL(*this, OnAllocateVideoFrames(
+ 1, kWidth, kHeight, static_cast<int32>(media::VideoFrame::RGBA)))
+ .WillOnce(SendVideoFrameAllocated(gpu_video_decoder_));
+
+ // Expect that MakeCurrent() is called.
+ EXPECT_CALL(*gles2_decoder_.get(), MakeCurrent())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+
+ // Expect that translate method is called.
+ EXPECT_CALL(*gles2_decoder_.get(),
+ GetServiceTextureId(kClientTexture, NotNull()))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kServiceTexture), Return(true)));
+
+ // And then GpuVideoDevice is called to create VideoFrame from GL textures.
+ EXPECT_CALL(*mock_device_,
+ CreateVideoFrameFromGlTextures(kWidth, kHeight,
+ media::VideoFrame::RGBA, _,
+ NotNull()))
+ .WillOnce(DoAll(SetArgumentPointee<4>(device_frame_), Return(true)));
+
+ // Finally the task is called.
+ EXPECT_CALL(*this, VideoFramesAllocated());
+
+ // Pretend calling GpuVideoDecoder for allocating frames.
+ gpu_video_decoder_->AllocateVideoFrames(
+ 1, kWidth, kHeight, media::VideoFrame::RGBA, &decoder_frames_,
+ NewRunnableMethod(this, &GpuVideoDecoderTest::VideoFramesAllocated));
+ }
+
+ void ReleaseVideoFrames() {
+ // Expect that MakeCurrent() is called.
+ EXPECT_CALL(*gles2_decoder_.get(), MakeCurrent())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+
+ // Expect that video frame is released.
+ EXPECT_CALL(*mock_device_, ReleaseVideoFrame(device_frame_));
+
+ // Expect that IPC message is send to release video frame.
+ EXPECT_CALL(*this, OnReleaseAllVideoFrames());
+
+ // Call to GpuVideoDecoder to release all video frames.
+ gpu_video_decoder_->ReleaseAllVideoFrames();
+ }
+
+ void BufferExchange() {
+ // Expect that we call to produce video frame.
+ EXPECT_CALL(*mock_engine_, ProduceVideoFrame(device_frame_))
+ .WillOnce(SendConsumeVideoFrame(gpu_video_decoder_, device_frame_))
+ .RetiresOnSaturation();
+
+ // Expect that consume video frame is called.
+ EXPECT_CALL(*this, OnConsumeVideoFrame(kFrameId, 0, 0, 0))
+ .RetiresOnSaturation();
+
+ // Ask the GpuVideoDecoder to produce a video frame.
+ GpuVideoDecoderMsg_ProduceVideoFrame msg(0, kFrameId);
+ gpu_video_decoder_->OnMessageReceived(msg);
+ }
+
+ private:
+ scoped_refptr<GpuVideoDecoder> gpu_video_decoder_;
+ MockGpuVideoDevice* mock_device_;
+ media::MockVideoDecodeEngine* mock_engine_;
+ scoped_ptr<gpu::gles2::MockGLES2Decoder> gles2_decoder_;
+ std::vector<scoped_refptr<media::VideoFrame> > decoder_frames_;
+ scoped_refptr<media::VideoFrame> device_frame_;
+
+ MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoderTest);
+};
+
+TEST_F(GpuVideoDecoderTest, Initialize) {
+ Initialize();
+}
+
+TEST_F(GpuVideoDecoderTest, AllocateVideoFrames) {
+ Initialize();
+ AllocateVideoFrames();
+}
+
+TEST_F(GpuVideoDecoderTest, ReleaseVideoFrames) {
+ Initialize();
+ AllocateVideoFrames();
+ ReleaseVideoFrames();
+}
+
+TEST_F(GpuVideoDecoderTest, BufferExchange) {
+ Initialize();
+ AllocateVideoFrames();
+ BufferExchange();
+ BufferExchange();
+ ReleaseVideoFrames();
+}
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(GpuVideoDecoderTest);
diff --git a/content/gpu/gpu_video_service.cc b/content/gpu/gpu_video_service.cc
new file mode 100644
index 0000000..355aabc
--- /dev/null
+++ b/content/gpu/gpu_video_service.cc
@@ -0,0 +1,82 @@
+// 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 "content/common/gpu_messages.h"
+#include "content/gpu/gpu_channel.h"
+#include "content/gpu/gpu_video_decoder.h"
+#include "content/gpu/gpu_video_service.h"
+
+struct GpuVideoService::GpuVideoDecoderInfo {
+ scoped_refptr<GpuVideoDecoder> decoder;
+ GpuChannel* channel;
+};
+
+
+GpuVideoService::GpuVideoService() {
+ // TODO(jiesun): move this time consuming stuff out of here.
+ IntializeGpuVideoService();
+}
+
+GpuVideoService::~GpuVideoService() {
+ // TODO(jiesun): move this time consuming stuff out of here.
+ UnintializeGpuVideoService();
+}
+
+// static
+GpuVideoService* GpuVideoService::GetInstance() {
+ return Singleton<GpuVideoService>::get();
+}
+
+void GpuVideoService::OnChannelConnected(int32 peer_pid) {
+ LOG(ERROR) << "GpuVideoService::OnChannelConnected";
+}
+
+void GpuVideoService::OnChannelError() {
+ LOG(ERROR) << "GpuVideoService::OnChannelError";
+}
+
+bool GpuVideoService::OnMessageReceived(const IPC::Message& msg) {
+#if 0
+ IPC_BEGIN_MESSAGE_MAP(GpuVideoService, msg)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP()
+#endif
+ return false;
+}
+
+bool GpuVideoService::IntializeGpuVideoService() {
+ return true;
+}
+
+bool GpuVideoService::UnintializeGpuVideoService() {
+ return true;
+}
+
+bool GpuVideoService::CreateVideoDecoder(
+ GpuChannel* channel,
+ MessageRouter* router,
+ int32 decoder_host_id,
+ int32 decoder_id,
+ gpu::gles2::GLES2Decoder* gles2_decoder) {
+ GpuVideoDecoderInfo decoder_info;
+ decoder_info.decoder = new GpuVideoDecoder(MessageLoop::current(),
+ decoder_host_id,
+ channel,
+ channel->renderer_process(),
+ gles2_decoder);
+ decoder_info.channel = channel;
+ decoder_map_[decoder_id] = decoder_info;
+ router->AddRoute(decoder_id, decoder_info.decoder);
+
+ channel->Send(new GpuVideoDecoderHostMsg_CreateVideoDecoderDone(
+ decoder_host_id, decoder_id));
+ return true;
+}
+
+void GpuVideoService::DestroyVideoDecoder(
+ MessageRouter* router,
+ int32 decoder_id) {
+ router->RemoveRoute(decoder_id);
+ decoder_map_.erase(decoder_id);
+}
diff --git a/content/gpu/gpu_video_service.h b/content/gpu/gpu_video_service.h
new file mode 100644
index 0000000..5e9d015
--- /dev/null
+++ b/content/gpu/gpu_video_service.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_VIDEO_SERVICE_H_
+#define CONTENT_GPU_GPU_VIDEO_SERVICE_H_
+
+#include <map>
+
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "content/gpu/gpu_video_decoder.h"
+#include "ipc/ipc_channel.h"
+
+class GpuChannel;
+
+class GpuVideoService : public IPC::Channel::Listener {
+ public:
+ static GpuVideoService* GetInstance();
+
+ // IPC::Channel::Listener.
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // TODO(hclam): Remove return value.
+ bool CreateVideoDecoder(GpuChannel* channel,
+ MessageRouter* router,
+ int32 decoder_host_id,
+ int32 decoder_id,
+ gpu::gles2::GLES2Decoder* gles2_decoder);
+ void DestroyVideoDecoder(MessageRouter* router,
+ int32 decoder_id);
+
+ private:
+ struct GpuVideoDecoderInfo;
+
+ GpuVideoService();
+ virtual ~GpuVideoService();
+
+ std::map<int32, GpuVideoDecoderInfo> decoder_map_;
+
+ // Specialize video service on different platform will override.
+ virtual bool IntializeGpuVideoService();
+ virtual bool UnintializeGpuVideoService();
+
+ friend struct DefaultSingletonTraits<GpuVideoService>;
+ DISALLOW_COPY_AND_ASSIGN(GpuVideoService);
+};
+
+#endif // CONTENT_GPU_GPU_VIDEO_SERVICE_H_
diff --git a/content/gpu/gpu_watchdog_thread.cc b/content/gpu/gpu_watchdog_thread.cc
new file mode 100644
index 0000000..a7d723c
--- /dev/null
+++ b/content/gpu/gpu_watchdog_thread.cc
@@ -0,0 +1,243 @@
+// 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.
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "content/gpu/gpu_watchdog_thread.h"
+
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+namespace {
+const int64 kCheckPeriod = 2000;
+
+void DoNothing() {
+}
+}
+
+GpuWatchdogThread::GpuWatchdogThread(int timeout)
+ : base::Thread("Watchdog"),
+ watched_message_loop_(MessageLoop::current()),
+ timeout_(timeout),
+ armed_(false),
+#if defined(OS_WIN)
+ watched_thread_handle_(0),
+ arm_cpu_time_(0),
+#endif
+ ALLOW_THIS_IN_INITIALIZER_LIST(task_observer_(this)) {
+ DCHECK(timeout >= 0);
+
+#if defined(OS_WIN)
+ // GetCurrentThread returns a pseudo-handle that cannot be used by one thread
+ // to identify another. DuplicateHandle creates a "real" handle that can be
+ // used for this purpose.
+ BOOL result = DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &watched_thread_handle_,
+ THREAD_QUERY_INFORMATION,
+ FALSE,
+ 0);
+ DCHECK(result);
+#endif
+
+ watched_message_loop_->AddTaskObserver(&task_observer_);
+}
+
+GpuWatchdogThread::~GpuWatchdogThread() {
+ // Verify that the thread was explicitly stopped. If the thread is stopped
+ // implicitly by the destructor, CleanUp() will not be called.
+ DCHECK(!method_factory_.get());
+
+#if defined(OS_WIN)
+ CloseHandle(watched_thread_handle_);
+#endif
+
+ watched_message_loop_->RemoveTaskObserver(&task_observer_);
+}
+
+void GpuWatchdogThread::PostAcknowledge() {
+ // Called on the monitored thread. Responds with OnAcknowledge. Cannot use
+ // the method factory. Rely on reference counting instead.
+ message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &GpuWatchdogThread::OnAcknowledge));
+}
+
+void GpuWatchdogThread::Init() {
+ // The method factory must be created on the watchdog thread.
+ method_factory_.reset(new MethodFactory(this));
+
+ // Schedule the first check.
+ OnCheck();
+}
+
+void GpuWatchdogThread::CleanUp() {
+ // The method factory must be destroyed on the watchdog thread.
+ method_factory_->RevokeAll();
+ method_factory_.reset();
+}
+
+GpuWatchdogThread::GpuWatchdogTaskObserver::GpuWatchdogTaskObserver(
+ GpuWatchdogThread* watchdog)
+ : watchdog_(watchdog) {
+}
+
+GpuWatchdogThread::GpuWatchdogTaskObserver::~GpuWatchdogTaskObserver() {
+}
+
+void GpuWatchdogThread::GpuWatchdogTaskObserver::WillProcessTask(
+ const Task* task)
+{
+ CheckArmed();
+}
+
+void GpuWatchdogThread::GpuWatchdogTaskObserver::DidProcessTask(
+ const Task* task)
+{
+ CheckArmed();
+}
+
+void GpuWatchdogThread::GpuWatchdogTaskObserver::CheckArmed()
+{
+ // Acknowledge the watchdog if it has armed itself. The watchdog will not
+ // change its armed state until it is acknowledged.
+ if (watchdog_->armed()) {
+ watchdog_->PostAcknowledge();
+ }
+}
+
+void GpuWatchdogThread::OnAcknowledge() {
+ // The check has already been acknowledged and another has already been
+ // scheduled by a previous call to OnAcknowledge. It is normal for a
+ // watched thread to see armed_ being true multiple times before
+ // the OnAcknowledge task is run on the watchdog thread.
+ if (!armed_)
+ return;
+
+ // Revoke any pending hang termination.
+ method_factory_->RevokeAll();
+ armed_ = false;
+
+ // The monitored thread has responded. Post a task to check it again.
+ message_loop()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnCheck),
+ kCheckPeriod);
+}
+
+#if defined(OS_WIN)
+int64 GpuWatchdogThread::GetWatchedThreadTime() {
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME user_time;
+ FILETIME kernel_time;
+ BOOL result = GetThreadTimes(watched_thread_handle_,
+ &creation_time,
+ &exit_time,
+ &kernel_time,
+ &user_time);
+ DCHECK(result);
+
+ ULARGE_INTEGER user_time64;
+ user_time64.HighPart = user_time.dwHighDateTime;
+ user_time64.LowPart = user_time.dwLowDateTime;
+
+ ULARGE_INTEGER kernel_time64;
+ kernel_time64.HighPart = kernel_time.dwHighDateTime;
+ kernel_time64.LowPart = kernel_time.dwLowDateTime;
+
+ // Time is reported in units of 100 nanoseconds. Kernel and user time are
+ // summed to deal with to kinds of hangs. One is where the GPU process is
+ // stuck in user level, never calling into the kernel and kernel time is
+ // not increasing. The other is where either the kernel hangs and never
+ // returns to user level or where user level code
+ // calls into kernel level repeatedly, giving up its quanta before it is
+ // tracked, for example a loop that repeatedly Sleeps.
+ return static_cast<int64>(
+ (user_time64.QuadPart + kernel_time64.QuadPart) / 10000);
+}
+#endif
+
+void GpuWatchdogThread::OnCheck() {
+ if (armed_)
+ return;
+
+ // Must set armed before posting the task. This task might be the only task
+ // that will activate the TaskObserver on the watched thread and it must not
+ // miss the false -> true transition.
+ armed_ = true;
+
+#if defined(OS_WIN)
+ arm_cpu_time_ = GetWatchedThreadTime();
+#endif
+
+ arm_absolute_time_ = base::Time::Now();
+
+ // Post a task to the monitored thread that does nothing but wake up the
+ // TaskObserver. Any other tasks that are pending on the watched thread will
+ // also wake up the observer. This simply ensures there is at least one.
+ watched_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableFunction(DoNothing));
+
+ // Post a task to the watchdog thread to exit if the monitored thread does
+ // not respond in time.
+ message_loop()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_->NewRunnableMethod(
+ &GpuWatchdogThread::DeliberatelyCrashingToRecoverFromHang),
+ timeout_);
+}
+
+// Use the --disable-gpu-watchdog command line switch to disable this.
+void GpuWatchdogThread::DeliberatelyCrashingToRecoverFromHang() {
+#if defined(OS_WIN)
+ // Defer termination until a certain amount of CPU time has elapsed on the
+ // watched thread.
+ int64 time_since_arm = GetWatchedThreadTime() - arm_cpu_time_;
+ if (time_since_arm < timeout_) {
+ message_loop()->PostDelayedTask(
+ FROM_HERE,
+ method_factory_->NewRunnableMethod(
+ &GpuWatchdogThread::DeliberatelyCrashingToRecoverFromHang),
+ timeout_ - time_since_arm);
+ return;
+ }
+#endif
+
+ // If the watchdog woke up significantly behind schedule, disarm and reset
+ // the watchdog check. This is to prevent the watchdog thread from terminating
+ // when a machine wakes up from sleep or hibernation, which would otherwise
+ // appear to be a hang.
+ if ((base::Time::Now() - arm_absolute_time_).InMilliseconds() >
+ timeout_ * 2) {
+ armed_ = false;
+ OnCheck();
+ return;
+ }
+
+ // Make sure the timeout period is on the stack before crashing.
+ volatile int timeout = timeout_;
+
+ // For minimal developer annoyance, don't keep crashing.
+ static bool crashed = false;
+ if (crashed)
+ return;
+
+#if defined(OS_WIN)
+ if (IsDebuggerPresent())
+ return;
+#endif
+
+ LOG(ERROR) << "The GPU process hung. Terminating after "
+ << timeout_ << " ms.";
+
+ volatile int* null_pointer = NULL;
+ *null_pointer = timeout;
+
+ crashed = true;
+}
diff --git a/content/gpu/gpu_watchdog_thread.h b/content/gpu/gpu_watchdog_thread.h
new file mode 100644
index 0000000..b0812a6
--- /dev/null
+++ b/content/gpu/gpu_watchdog_thread.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef CONTENT_GPU_GPU_WATCHDOG_THREAD_H_
+#define CONTENT_GPU_GPU_WATCHDOG_THREAD_H_
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "base/threading/thread.h"
+#include "base/time.h"
+
+// A thread that intermitently sends tasks to a group of watched message loops
+// and deliberately crashes if one of them does not respond after a timeout.
+class GpuWatchdogThread : public base::Thread,
+ public base::RefCountedThreadSafe<GpuWatchdogThread> {
+ public:
+ explicit GpuWatchdogThread(int timeout);
+ virtual ~GpuWatchdogThread();
+
+ // Accessible on watched thread but only modified by watchdog thread.
+ bool armed() const { return armed_; }
+ void PostAcknowledge();
+
+ protected:
+ virtual void Init();
+ virtual void CleanUp();
+
+ private:
+
+ // An object of this type intercepts the reception and completion of all tasks
+ // on the watched thread and checks whether the watchdog is armed.
+ class GpuWatchdogTaskObserver : public MessageLoop::TaskObserver {
+ public:
+ explicit GpuWatchdogTaskObserver(GpuWatchdogThread* watchdog);
+ virtual ~GpuWatchdogTaskObserver();
+
+ // Implements MessageLoop::TaskObserver.
+ virtual void WillProcessTask(const Task* task);
+ virtual void DidProcessTask(const Task* task);
+
+ private:
+ void CheckArmed();
+ GpuWatchdogThread* watchdog_;
+ };
+
+ void OnAcknowledge();
+ void OnCheck();
+ void DeliberatelyCrashingToRecoverFromHang();
+ void Disable();
+
+ int64 GetWatchedThreadTime();
+
+ MessageLoop* watched_message_loop_;
+ int timeout_;
+ volatile bool armed_;
+ GpuWatchdogTaskObserver task_observer_;
+
+#if defined(OS_WIN)
+ void* watched_thread_handle_;
+ int64 arm_cpu_time_;
+#endif
+
+ base::Time arm_absolute_time_;
+
+ typedef ScopedRunnableMethodFactory<GpuWatchdogThread> MethodFactory;
+ scoped_ptr<MethodFactory> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuWatchdogThread);
+};
+
+#endif // CONTENT_GPU_GPU_WATCHDOG_THREAD_H_
diff --git a/content/gpu/media/fake_gl_video_decode_engine.cc b/content/gpu/media/fake_gl_video_decode_engine.cc
new file mode 100644
index 0000000..0486a01
--- /dev/null
+++ b/content/gpu/media/fake_gl_video_decode_engine.cc
@@ -0,0 +1,132 @@
+// 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 "content/gpu/media/fake_gl_video_decode_engine.h"
+
+#include "media/base/limits.h"
+#include "media/base/video_frame.h"
+#include "media/video/video_decode_context.h"
+
+FakeGlVideoDecodeEngine::FakeGlVideoDecodeEngine()
+ : width_(0),
+ height_(0),
+ handler_(NULL),
+ context_(NULL) {
+}
+
+FakeGlVideoDecodeEngine::~FakeGlVideoDecodeEngine() {
+}
+
+void FakeGlVideoDecodeEngine::Initialize(
+ MessageLoop* message_loop,
+ media::VideoDecodeEngine::EventHandler* event_handler,
+ media::VideoDecodeContext* context,
+ const media::VideoCodecConfig& config) {
+ handler_ = event_handler;
+ context_ = context;
+ width_ = config.width();
+ height_ = config.height();
+
+ // Create an internal VideoFrame that we can write to. This is going to be
+ // uploaded through VideoDecodeContext.
+ media::VideoFrame::CreateFrame(
+ media::VideoFrame::RGBA, width_, height_, base::TimeDelta(),
+ base::TimeDelta(), &internal_frame_);
+ memset(internal_frame_->data(media::VideoFrame::kRGBPlane), 0,
+ height_ * internal_frame_->stride(media::VideoFrame::kRGBPlane));
+
+ // Use VideoDecodeContext to allocate VideoFrame that can be consumed by
+ // external body.
+ // TODO(hclam): It is horrible to use constants everywhere in the code!
+ // The number of frames come from VideoRendererBase in the renderer, this is
+ // horrible!
+ context_->AllocateVideoFrames(
+ media::Limits::kMaxVideoFrames, width_, height_, media::VideoFrame::RGBA,
+ &external_frames_,
+ NewRunnableMethod(this,
+ &FakeGlVideoDecodeEngine::AllocationCompleteTask));
+}
+
+void FakeGlVideoDecodeEngine::AllocationCompleteTask() {
+ DCHECK(media::Limits::kMaxVideoFrames == external_frames_.size());
+ DCHECK_EQ(media::VideoFrame::TYPE_GL_TEXTURE, external_frames_[0]->type());
+
+ media::VideoCodecInfo info;
+ info.success = true;
+ info.provides_buffers = true;
+ info.stream_info.surface_format = media::VideoFrame::RGBA;
+ info.stream_info.surface_type = media::VideoFrame::TYPE_GL_TEXTURE;
+ info.stream_info.surface_width = width_;
+ info.stream_info.surface_height = height_;
+ handler_->OnInitializeComplete(info);
+}
+
+void FakeGlVideoDecodeEngine::Uninitialize() {
+ handler_->OnUninitializeComplete();
+}
+
+void FakeGlVideoDecodeEngine::Flush() {
+ handler_->OnFlushComplete();
+}
+
+void FakeGlVideoDecodeEngine::Seek() {
+ // TODO(hclam): This is a big hack to continue playing because we need to
+ // *push* decoded frames to downstream. The model used in VideoRendererBase
+ // to wait for *push* is flawed.
+ for (size_t i = 0; i < external_frames_.size(); ++i)
+ handler_->ConsumeVideoFrame(external_frames_[i], dummy_stats_);
+ handler_->OnSeekComplete();
+}
+
+void FakeGlVideoDecodeEngine::ConsumeVideoSample(
+ scoped_refptr<media::Buffer> sample) {
+ DCHECK(!pending_frames_.empty());
+ scoped_refptr<media::VideoFrame> frame = pending_frames_.front();
+ pending_frames_.pop();
+
+ frame->SetDuration(sample->GetDuration());
+ frame->SetTimestamp(sample->GetTimestamp());
+
+ // Generate a pattern to the internal frame and then uploads it.
+ int size = width_ * height_ * 4;
+ scoped_array<uint8> buffer(new uint8[size]);
+ memset(buffer.get(), 255, size);
+
+ uint8* row = internal_frame_->data(media::VideoFrame::kRGBPlane);
+ static int seed = 0;
+
+ for (int y = 0; y < height_; ++y) {
+ int offset = y % 3;
+ for (int x = 0; x < width_; ++x) {
+ row[x * 4 + offset + 1] = seed++;
+ seed &= 255;
+ }
+ row += width_ * 4;
+ }
+ ++seed;
+
+ // After we have filled the content upload the internal frame to the
+ // VideoFrame allocated through VideoDecodeContext.
+ context_->ConvertToVideoFrame(
+ internal_frame_, frame,
+ NewRunnableMethod(this, &FakeGlVideoDecodeEngine::UploadCompleteTask,
+ frame));
+}
+
+void FakeGlVideoDecodeEngine::ProduceVideoFrame(
+ scoped_refptr<media::VideoFrame> frame) {
+ // Enqueue the frame to the pending queue.
+ pending_frames_.push(frame);
+
+ // Fake that we need some buffer.
+ handler_->ProduceVideoSample(NULL);
+}
+
+void FakeGlVideoDecodeEngine::UploadCompleteTask(
+ scoped_refptr<media::VideoFrame> frame) {
+ // |frame| is the upload target. We can immediately send this frame out.
+ handler_->ConsumeVideoFrame(frame, dummy_stats_);
+}
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(FakeGlVideoDecodeEngine);
diff --git a/content/gpu/media/fake_gl_video_decode_engine.h b/content/gpu/media/fake_gl_video_decode_engine.h
new file mode 100644
index 0000000..e065228
--- /dev/null
+++ b/content/gpu/media/fake_gl_video_decode_engine.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DECODE_ENGINE_H_
+#define CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DECODE_ENGINE_H_
+
+#include <queue>
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "media/base/pipeline.h"
+#include "media/video/video_decode_engine.h"
+
+namespace media {
+class VideoDecodeContext;
+class VideoFrame;
+} // namespace media
+
+class FakeGlVideoDecodeEngine : public media::VideoDecodeEngine {
+ public:
+ FakeGlVideoDecodeEngine();
+ virtual ~FakeGlVideoDecodeEngine();
+
+ virtual void Initialize(
+ MessageLoop* message_loop,
+ media::VideoDecodeEngine::EventHandler* event_handler,
+ media::VideoDecodeContext* context,
+ const media::VideoCodecConfig& config);
+
+ virtual void Uninitialize();
+ virtual void Flush();
+ virtual void Seek();
+ virtual void ConsumeVideoSample(scoped_refptr<media::Buffer> buffer);
+ virtual void ProduceVideoFrame(scoped_refptr<media::VideoFrame> frame);
+
+ private:
+ // This method is called when video frames allocation is completed by
+ // VideoDecodeContext.
+ void AllocationCompleteTask();
+
+ // This method is called by VideoDecodeContext when uploading to a VideoFrame
+ // has completed.
+ void UploadCompleteTask(scoped_refptr<media::VideoFrame> frame);
+
+ int width_;
+ int height_;
+ media::VideoDecodeEngine::EventHandler* handler_;
+ media::VideoDecodeContext* context_;
+
+ // Internal video frame that is to be uploaded through VideoDecodeContext.
+ scoped_refptr<media::VideoFrame> internal_frame_;
+
+ // VideoFrame(s) allocated through VideoDecodeContext. These frames are
+ // opaque to us. And we need an extra upload step.
+ std::vector<scoped_refptr<media::VideoFrame> > external_frames_;
+
+ // These are the video frames that are waiting for input buffer to generate
+ // fake pattern in them.
+ std::queue<scoped_refptr<media::VideoFrame> > pending_frames_;
+
+ // Dummy statistics.
+ media::PipelineStatistics dummy_stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeGlVideoDecodeEngine);
+};
+
+#endif // CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DECODE_ENGINE_H_
diff --git a/content/gpu/media/fake_gl_video_device.cc b/content/gpu/media/fake_gl_video_device.cc
new file mode 100644
index 0000000..f4e8746e6
--- /dev/null
+++ b/content/gpu/media/fake_gl_video_device.cc
@@ -0,0 +1,58 @@
+// 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 "content/gpu/media/fake_gl_video_device.h"
+
+#include "app/gfx/gl/gl_bindings.h"
+#include "media/base/video_frame.h"
+
+void* FakeGlVideoDevice::GetDevice() {
+ // No actual hardware device should be used.
+ return NULL;
+}
+
+bool FakeGlVideoDevice::CreateVideoFrameFromGlTextures(
+ size_t width, size_t height, media::VideoFrame::Format format,
+ const std::vector<media::VideoFrame::GlTexture>& textures,
+ scoped_refptr<media::VideoFrame>* frame) {
+ media::VideoFrame::GlTexture texture_array[media::VideoFrame::kMaxPlanes];
+ memset(texture_array, 0, sizeof(texture_array));
+
+ for (size_t i = 0; i < textures.size(); ++i) {
+ texture_array[i] = textures[i];
+ }
+
+ media::VideoFrame::CreateFrameGlTexture(format,
+ width,
+ height,
+ texture_array,
+ frame);
+ return *frame != NULL;
+}
+
+void FakeGlVideoDevice::ReleaseVideoFrame(
+ const scoped_refptr<media::VideoFrame>& frame) {
+ // We didn't need to anything here because we didin't allocate any resources
+ // for the VideoFrame(s) generated.
+}
+
+bool FakeGlVideoDevice::ConvertToVideoFrame(
+ void* buffer, scoped_refptr<media::VideoFrame> frame) {
+ // Assume we are in the right context and then upload the content to the
+ // texture.
+ glBindTexture(GL_TEXTURE_2D,
+ frame->gl_texture(media::VideoFrame::kRGBPlane));
+
+ // |buffer| is also a VideoFrame.
+ scoped_refptr<media::VideoFrame> frame_to_upload(
+ reinterpret_cast<media::VideoFrame*>(buffer));
+ DCHECK_EQ(frame->width(), frame_to_upload->width());
+ DCHECK_EQ(frame->height(), frame_to_upload->height());
+ DCHECK_EQ(frame->format(), frame_to_upload->format());
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_RGBA, frame_to_upload->width(),
+ frame_to_upload->height(), 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, frame_to_upload->data(media::VideoFrame::kRGBPlane));
+ return true;
+}
diff --git a/content/gpu/media/fake_gl_video_device.h b/content/gpu/media/fake_gl_video_device.h
new file mode 100644
index 0000000..f5acb1a
--- /dev/null
+++ b/content/gpu/media/fake_gl_video_device.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DEVICE_H_
+#define CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DEVICE_H_
+
+#include "content/gpu/media/gpu_video_device.h"
+
+// A simple GpuVideoDevice that create VideoFrame with GL textures.
+// It uploads frames in RGBA format in system memory to the GL texture.
+class FakeGlVideoDevice : public GpuVideoDevice {
+ public:
+ virtual ~FakeGlVideoDevice() {}
+
+ virtual void* GetDevice();
+ virtual bool CreateVideoFrameFromGlTextures(
+ size_t width, size_t height, media::VideoFrame::Format format,
+ const std::vector<media::VideoFrame::GlTexture>& textures,
+ scoped_refptr<media::VideoFrame>* frame);
+ virtual void ReleaseVideoFrame(
+ const scoped_refptr<media::VideoFrame>& frame);
+ virtual bool ConvertToVideoFrame(void* buffer,
+ scoped_refptr<media::VideoFrame> frame);
+};
+
+#endif // CONTENT_GPU_MEDIA_FAKE_GL_VIDEO_DEVICE_H_
diff --git a/content/gpu/media/gpu_video_device.h b/content/gpu/media/gpu_video_device.h
new file mode 100644
index 0000000..dcba5f2
--- /dev/null
+++ b/content/gpu/media/gpu_video_device.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef CONTENT_GPU_MEDIA_GPU_VIDEO_DEVICE_H_
+#define CONTENT_GPU_MEDIA_GPU_VIDEO_DEVICE_H_
+
+#include <vector>
+
+#include "media/base/video_frame.h"
+#include "media/video/video_decode_context.h"
+
+// A GpuVideoDevice is used by GpuVideoDecoder to allocate video frames
+// meaningful to a corresponding VideoDecodeEngine.
+//
+// GpuVideoDecoder will provide a set of GL textures to this class and then
+// GpuVideoDevice will transform the textures into a set of VideoFrame
+// objects that can be used by VideoDecodeEngine.
+//
+// See text in GpuVideoDecoder for the overall flow for buffer allocation.
+//
+// Since all graphics commands execute on the main thread in the GPU process
+// all the methods provided by this class are synchronous.
+class GpuVideoDevice {
+ public:
+ virtual ~GpuVideoDevice() {}
+
+ // Get the hardware video decoding device handle.
+ virtual void* GetDevice() = 0;
+
+ // The following method is used by GpuVideoDecoder to create VideoFrame(s)
+ // associated with some GL textures.
+ //
+ // VideoFrame generated is used by VideoDecodeEngine for output buffer.
+ //
+ // |frame| will contain the VideoFrame generated.
+ //
+ // Return true if the operation was successful.
+ virtual bool CreateVideoFrameFromGlTextures(
+ size_t width, size_t height, media::VideoFrame::Format format,
+ const std::vector<media::VideoFrame::GlTexture>& textures,
+ scoped_refptr<media::VideoFrame>* frame) = 0;
+
+ // Release VideoFrame generated.
+ virtual void ReleaseVideoFrame(
+ const scoped_refptr<media::VideoFrame>& frame) = 0;
+
+ // Upload a device specific buffer to a VideoFrame object that can be used in
+ // the GPU process.
+ //
+ // Return true if successful.
+ virtual bool ConvertToVideoFrame(void* buffer,
+ scoped_refptr<media::VideoFrame> frame) = 0;
+};
+
+#endif // CONTENT_GPU_MEDIA_GPU_VIDEO_DEVICE_H_
diff --git a/content/gpu/media/mft_angle_video_device.cc b/content/gpu/media/mft_angle_video_device.cc
new file mode 100644
index 0000000..355ffa3
--- /dev/null
+++ b/content/gpu/media/mft_angle_video_device.cc
@@ -0,0 +1,52 @@
+// 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 "content/gpu/media/mft_angle_video_device.h"
+
+#include <d3d9.h>
+
+#include "media/base/video_frame.h"
+#include "third_party/angle/src/libGLESv2/main.h"
+
+MftAngleVideoDevice::MftAngleVideoDevice()
+ : device_(reinterpret_cast<egl::Display*>(
+ eglGetCurrentDisplay())->getDevice()) {
+}
+
+void* MftAngleVideoDevice::GetDevice() {
+ return device_;
+}
+
+bool MftAngleVideoDevice::CreateVideoFrameFromGlTextures(
+ size_t width, size_t height, media::VideoFrame::Format format,
+ const std::vector<media::VideoFrame::GlTexture>& textures,
+ scoped_refptr<media::VideoFrame>* frame) {
+ media::VideoFrame::GlTexture texture_array[media::VideoFrame::kMaxPlanes];
+ memset(texture_array, 0, sizeof(texture_array));
+
+ for (size_t i = 0; i < textures.size(); ++i) {
+ texture_array[i] = textures[i];
+ }
+
+ media::VideoFrame::CreateFrameGlTexture(format,
+ width,
+ height,
+ texture_array,
+ frame);
+ return *frame != NULL;
+}
+
+void MftAngleVideoDevice::ReleaseVideoFrame(
+ const scoped_refptr<media::VideoFrame>& frame) {
+ // We didn't need to anything here because we didn't allocate any resources
+ // for the VideoFrame(s) generated.
+}
+
+bool MftAngleVideoDevice::ConvertToVideoFrame(
+ void* buffer, scoped_refptr<media::VideoFrame> frame) {
+ gl::Context* context = (gl::Context*)eglGetCurrentContext();
+ // TODO(hclam): Connect ANGLE to upload the surface to texture when changes
+ // to ANGLE is done.
+ return true;
+}
diff --git a/content/gpu/media/mft_angle_video_device.h b/content/gpu/media/mft_angle_video_device.h
new file mode 100644
index 0000000..6d85de2
--- /dev/null
+++ b/content/gpu/media/mft_angle_video_device.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CONTENT_GPU_MEDIA_MFT_ANGLE_VIDEO_DEVICE_H_
+#define CONTENT_GPU_MEDIA_MFT_ANGLE_VIDEO_DEVICE_H_
+
+#include "base/scoped_comptr_win.h"
+#include "content/gpu/media/gpu_video_device.h"
+
+struct IDirect3DDevice9;
+extern "C" const GUID IID_IDirect3DDevice9;
+
+namespace media {
+class VideoFrame;
+} // namespace media
+
+// This class is used to provide hardware video device, video frames and
+// allow video frames to be uploaded to their final render target.
+//
+// This specifically serves MftH264DecodeEngine in the context of ANGLE.
+class MftAngleVideoDevice : public GpuVideoDevice {
+ public:
+ MftAngleVideoDevice();
+ virtual ~MftAngleVideoDevice() {}
+
+ // GpuVideoDevice implementation.
+ virtual void* GetDevice();
+ virtual bool CreateVideoFrameFromGlTextures(
+ size_t width, size_t height, media::VideoFrame::Format format,
+ const std::vector<media::VideoFrame::GlTexture>& textures,
+ scoped_refptr<media::VideoFrame>* frame);
+ virtual void ReleaseVideoFrame(
+ const scoped_refptr<media::VideoFrame>& frame);
+ virtual bool ConvertToVideoFrame(void* buffer,
+ scoped_refptr<media::VideoFrame> frame);
+
+ private:
+ ScopedComPtr<IDirect3DDevice9, &IID_IDirect3DDevice9> device_;
+};
+
+#endif // CONTENT_GPU_MEDIA_MFT_ANGLE_VIDEO_DEVICE_H_
diff --git a/content/gpu/x_util.cc b/content/gpu/x_util.cc
new file mode 100644
index 0000000..72089e6
--- /dev/null
+++ b/content/gpu/x_util.cc
@@ -0,0 +1,11 @@
+// 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 "content/gpu/x_util.h"
+
+#include <X11/Xutil.h>
+
+void ScopedPtrXFree::operator()(void* x) const {
+ ::XFree(x);
+}
diff --git a/content/gpu/x_util.h b/content/gpu/x_util.h
new file mode 100644
index 0000000..c854080
--- /dev/null
+++ b/content/gpu/x_util.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_GPU_X_UTIL_H_
+#define CHROME_GPU_X_UTIL_H_
+#pragma once
+
+// Some X-Windows specific stuff. This can be included on any platform, and will
+// be a NOP on non-Linux ones.
+
+#include "build/build_config.h"
+#include "content/gpu/gpu_config.h"
+
+#if defined(OS_LINUX)
+
+// Forward declares ------------------------------------------------------------
+//
+// X Windows headers do a lot of evil stuff, like "#define Status int" which
+// will cause many problems when combined with our other header files (like
+// ones that define a class local enum called "Status."
+//
+// These definitions are not Kosher, but allow us to remove this dependency and
+// actually compile X at all.
+
+typedef unsigned long XID;
+
+extern "C" {
+
+typedef struct _XDisplay Display;
+typedef struct __GLXcontextRec *GLXContext;
+
+} // extern "C"
+
+// Utils -----------------------------------------------------------------------
+
+// scoped_ptr functor for XFree(). Use as follows:
+// scoped_ptr_mallox<XVisualInfo, ScopedPtrXFree> foo(...);
+// where "XVisualInfo" is any X type that is freed with XFree.
+class ScopedPtrXFree {
+ public:
+ void operator()(void* x) const;
+};
+
+#endif // OS_LINUX
+
+#endif // CHROME_GPU_X_UTIL_H_