diff options
Diffstat (limited to 'content/gpu/gpu_thread.cc')
-rw-r--r-- | content/gpu/gpu_thread.cc | 347 |
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 |