diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-12 01:00:41 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-12 01:00:41 +0000 |
commit | 623c0bd198a26b6609c7545a0cce0578dbad5316 (patch) | |
tree | e5cfac9d974797d95a8b4ee0aa0e4204826f089c /content/gpu | |
parent | 23716fb643383cb737e564d55234a7c2d58eba00 (diff) | |
download | chromium_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')
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(¶ms); + 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, + ¶m.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_ |