summaryrefslogtreecommitdiffstats
path: root/content/gpu/gpu_thread.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/gpu/gpu_thread.cc')
-rw-r--r--content/gpu/gpu_thread.cc347
1 files changed, 347 insertions, 0 deletions
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