summaryrefslogtreecommitdiff
path: root/chromium/content/browser/gpu/gpu_process_host.cc
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/browser/gpu/gpu_process_host.cc
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/content/browser/gpu/gpu_process_host.cc')
-rw-r--r--chromium/content/browser/gpu/gpu_process_host.cc1285
1 files changed, 1285 insertions, 0 deletions
diff --git a/chromium/content/browser/gpu/gpu_process_host.cc b/chromium/content/browser/gpu/gpu_process_host.cc
new file mode 100644
index 00000000000..8cbb7aa1d82
--- /dev/null
+++ b/chromium/content/browser/gpu/gpu_process_host.cc
@@ -0,0 +1,1285 @@
+// Copyright (c) 2012 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/browser/gpu/gpu_process_host.h"
+
+#include "base/base64.h"
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram.h"
+#include "base/sha1.h"
+#include "base/threading/thread.h"
+#include "content/browser/browser_child_process_host_impl.h"
+#include "content/browser/gpu/gpu_data_manager_impl.h"
+#include "content/browser/gpu/gpu_process_host_ui_shim.h"
+#include "content/browser/gpu/shader_disk_cache.h"
+#include "content/browser/renderer_host/render_widget_helper.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/child_process_host_impl.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "content/common/view_messages.h"
+#include "content/gpu/gpu_child_thread.h"
+#include "content/gpu/gpu_process.h"
+#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/result_codes.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_switches.h"
+#include "ui/base/latency_info.h"
+#include "ui/gl/gl_switches.h"
+
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#include "content/common/sandbox_win.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "ui/surface/accelerated_surface_win.h"
+#endif
+
+namespace content {
+
+bool GpuProcessHost::gpu_enabled_ = true;
+bool GpuProcessHost::hardware_gpu_enabled_ = true;
+
+namespace {
+
+enum GPUProcessLifetimeEvent {
+ LAUNCHED,
+ DIED_FIRST_TIME,
+ DIED_SECOND_TIME,
+ DIED_THIRD_TIME,
+ DIED_FOURTH_TIME,
+ GPU_PROCESS_LIFETIME_EVENT_MAX = 100
+};
+
+// Indexed by GpuProcessKind. There is one of each kind maximum. This array may
+// only be accessed from the IO thread.
+GpuProcessHost* g_gpu_process_hosts[GpuProcessHost::GPU_PROCESS_KIND_COUNT];
+
+
+void SendGpuProcessMessage(GpuProcessHost::GpuProcessKind kind,
+ CauseForGpuLaunch cause,
+ IPC::Message* message) {
+ GpuProcessHost* host = GpuProcessHost::Get(kind, cause);
+ if (host) {
+ host->Send(message);
+ } else {
+ delete message;
+ }
+}
+
+void AcceleratedSurfaceBuffersSwappedCompletedForGPU(int host_id,
+ int route_id,
+ bool alive) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU,
+ host_id,
+ route_id,
+ alive));
+ return;
+ }
+
+ GpuProcessHost* host = GpuProcessHost::FromID(host_id);
+ if (host) {
+ if (alive) {
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.sync_point = 0;
+ host->Send(
+ new AcceleratedSurfaceMsg_BufferPresented(route_id, ack_params));
+ } else {
+ host->ForceShutdown();
+ }
+ }
+}
+
+#if defined(OS_WIN)
+// This sends a ViewMsg_SwapBuffers_ACK directly to the renderer process
+// (RenderWidget).
+void AcceleratedSurfaceBuffersSwappedCompletedForRenderer(
+ int surface_id,
+ base::TimeTicks timebase,
+ base::TimeDelta interval,
+ const ui::LatencyInfo& latency_info) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForRenderer,
+ surface_id, timebase, interval, latency_info));
+ return;
+ }
+
+ int render_process_id = 0;
+ int render_widget_id = 0;
+ if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface(
+ surface_id, &render_process_id, &render_widget_id)) {
+ RenderWidgetHostImpl::CompositorFrameDrawn(latency_info);
+ return;
+ }
+ RenderWidgetHost* rwh =
+ RenderWidgetHost::FromID(render_process_id, render_widget_id);
+ if (!rwh)
+ return;
+ RenderWidgetHostImpl::From(rwh)->AcknowledgeSwapBuffersToRenderer();
+ if (interval != base::TimeDelta())
+ RenderWidgetHostImpl::From(rwh)->UpdateVSyncParameters(timebase, interval);
+ RenderWidgetHostImpl::From(rwh)->FrameSwapped(latency_info);
+ RenderWidgetHostImpl::From(rwh)->DidReceiveRendererFrame();
+}
+
+void AcceleratedSurfaceBuffersSwappedCompleted(
+ int host_id,
+ int route_id,
+ int surface_id,
+ bool alive,
+ base::TimeTicks timebase,
+ base::TimeDelta interval,
+ const ui::LatencyInfo& latency_info) {
+ AcceleratedSurfaceBuffersSwappedCompletedForGPU(host_id, route_id,
+ alive);
+ AcceleratedSurfaceBuffersSwappedCompletedForRenderer(surface_id, timebase,
+ interval, latency_info);
+}
+
+// NOTE: changes to this class need to be reviewed by the security team.
+class GpuSandboxedProcessLauncherDelegate
+ : public SandboxedProcessLauncherDelegate {
+ public:
+ explicit GpuSandboxedProcessLauncherDelegate(CommandLine* cmd_line)
+ : cmd_line_(cmd_line) {}
+ virtual ~GpuSandboxedProcessLauncherDelegate() {}
+
+ virtual void ShouldSandbox(bool* in_sandbox) OVERRIDE {
+ if (cmd_line_->HasSwitch(switches::kDisableGpuSandbox)) {
+ *in_sandbox = false;
+ DVLOG(1) << "GPU sandbox is disabled";
+ }
+ }
+
+ virtual void PreSandbox(bool* disable_default_policy,
+ base::FilePath* exposed_dir) OVERRIDE {
+ *disable_default_policy = true;
+ }
+
+ // For the GPU process we gotten as far as USER_LIMITED. The next level
+ // which is USER_RESTRICTED breaks both the DirectX backend and the OpenGL
+ // backend. Note that the GPU process is connected to the interactive
+ // desktop.
+ virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
+ bool* success) {
+ if (base::win::GetVersion() > base::win::VERSION_XP) {
+ if (cmd_line_->GetSwitchValueASCII(switches::kUseGL) ==
+ gfx::kGLImplementationDesktopName) {
+ // Open GL path.
+ policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ SetJobLevel(*cmd_line_, sandbox::JOB_UNPROTECTED, 0, policy);
+ policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ } else {
+ if (cmd_line_->GetSwitchValueASCII(switches::kUseGL) ==
+ gfx::kGLImplementationSwiftShaderName ||
+ cmd_line_->HasSwitch(switches::kReduceGpuSandbox) ||
+ cmd_line_->HasSwitch(switches::kDisableImageTransportSurface)) {
+ // Swiftshader path.
+ policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ } else {
+ // Angle + DirectX path.
+ policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_RESTRICTED);
+ // This is a trick to keep the GPU out of low-integrity processes. It
+ // starts at low-integrity for UIPI to work, then drops below
+ // low-integrity after warm-up.
+ policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
+ }
+
+ // UI restrictions break when we access Windows from outside our job.
+ // However, we don't want a proxy window in this process because it can
+ // introduce deadlocks where the renderer blocks on the gpu, which in
+ // turn blocks on the browser UI thread. So, instead we forgo a window
+ // message pump entirely and just add job restrictions to prevent child
+ // processes.
+ SetJobLevel(*cmd_line_,
+ sandbox::JOB_LIMITED_USER,
+ JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS |
+ JOB_OBJECT_UILIMIT_DESKTOP |
+ JOB_OBJECT_UILIMIT_EXITWINDOWS |
+ JOB_OBJECT_UILIMIT_DISPLAYSETTINGS,
+ policy);
+
+ policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ }
+ } else {
+ SetJobLevel(*cmd_line_, sandbox::JOB_UNPROTECTED, 0, policy);
+ policy->SetTokenLevel(sandbox::USER_UNPROTECTED,
+ sandbox::USER_LIMITED);
+ }
+
+ // Allow the server side of GPU sockets, which are pipes that have
+ // the "chrome.gpu" namespace and an arbitrary suffix.
+ sandbox::ResultCode result = policy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\chrome.gpu.*");
+ if (result != sandbox::SBOX_ALL_OK) {
+ *success = false;
+ return;
+ }
+
+ // Block this DLL even if it is not loaded by the browser process.
+ policy->AddDllToUnload(L"cmsetac.dll");
+
+#ifdef USE_AURA
+ // GPU also needs to add sections to the browser for aura
+ // TODO(jschuh): refactor the GPU channel to remove this. crbug.com/128786
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER,
+ L"Section");
+ if (result != sandbox::SBOX_ALL_OK) {
+ *success = false;
+ return;
+ }
+#endif
+
+ if (cmd_line_->HasSwitch(switches::kEnableLogging)) {
+ string16 log_file_path = logging::GetLogFileFullPath();
+ if (!log_file_path.empty()) {
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ log_file_path.c_str());
+ if (result != sandbox::SBOX_ALL_OK) {
+ *success = false;
+ return;
+ }
+ }
+ }
+ }
+
+ private:
+ CommandLine* cmd_line_;
+};
+#endif // defined(OS_WIN)
+
+} // anonymous namespace
+
+// Single process not supported in multiple dll mode currently.
+#if !defined(CHROME_MULTIPLE_DLL)
+// This class creates a GPU thread (instead of a GPU process), when running
+// with --in-process-gpu or --single-process.
+class GpuMainThread : public base::Thread {
+ public:
+ explicit GpuMainThread(const std::string& channel_id)
+ : base::Thread("Chrome_InProcGpuThread"),
+ channel_id_(channel_id),
+ gpu_process_(NULL) {
+ }
+
+ virtual ~GpuMainThread() {
+ Stop();
+ }
+
+ protected:
+ virtual void Init() OVERRIDE {
+ gpu_process_ = new GpuProcess();
+ // The process object takes ownership of the thread object, so do not
+ // save and delete the pointer.
+ gpu_process_->set_main_thread(new GpuChildThread(channel_id_));
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ delete gpu_process_;
+ }
+
+ private:
+ std::string channel_id_;
+ // Deleted in CleanUp() on the gpu thread, so don't use smart pointers.
+ GpuProcess* gpu_process_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuMainThread);
+};
+#endif // !CHROME_MULTIPLE_DLL
+
+// static
+bool GpuProcessHost::ValidateHost(GpuProcessHost* host) {
+ if (!host)
+ return false;
+
+ // The Gpu process is invalid if it's not using SwiftShader, the card is
+ // blacklisted, and we can kill it and start over.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) ||
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU) ||
+ (host->valid_ &&
+ (host->swiftshader_rendering_ ||
+ !GpuDataManagerImpl::GetInstance()->ShouldUseSwiftShader()))) {
+ return true;
+ }
+
+ host->ForceShutdown();
+ return false;
+}
+
+// static
+GpuProcessHost* GpuProcessHost::Get(GpuProcessKind kind,
+ CauseForGpuLaunch cause) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Don't grant further access to GPU if it is not allowed.
+ GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
+ DCHECK(gpu_data_manager);
+ if (!gpu_data_manager->GpuAccessAllowed(NULL))
+ return NULL;
+
+ if (g_gpu_process_hosts[kind] && ValidateHost(g_gpu_process_hosts[kind]))
+ return g_gpu_process_hosts[kind];
+
+ if (cause == CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH)
+ return NULL;
+
+ static int last_host_id = 0;
+ int host_id;
+ host_id = ++last_host_id;
+
+ UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLaunchCause",
+ cause,
+ CAUSE_FOR_GPU_LAUNCH_MAX_ENUM);
+
+ GpuProcessHost* host = new GpuProcessHost(host_id, kind);
+ if (host->Init())
+ return host;
+
+ delete host;
+ return NULL;
+}
+
+// static
+void GpuProcessHost::GetProcessHandles(
+ const GpuDataManager::GetGpuProcessHandlesCallback& callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GpuProcessHost::GetProcessHandles, callback));
+ return;
+ }
+ std::list<base::ProcessHandle> handles;
+ for (size_t i = 0; i < arraysize(g_gpu_process_hosts); ++i) {
+ GpuProcessHost* host = g_gpu_process_hosts[i];
+ if (host && ValidateHost(host))
+ handles.push_back(host->process_->GetHandle());
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, handles));
+}
+
+// static
+void GpuProcessHost::SendOnIO(GpuProcessKind kind,
+ CauseForGpuLaunch cause,
+ IPC::Message* message) {
+ if (!BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &SendGpuProcessMessage, kind, cause, message))) {
+ delete message;
+ }
+}
+
+// static
+GpuProcessHost* GpuProcessHost::FromID(int host_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ for (int i = 0; i < GPU_PROCESS_KIND_COUNT; ++i) {
+ GpuProcessHost* host = g_gpu_process_hosts[i];
+ if (host && host->host_id_ == host_id && ValidateHost(host))
+ return host;
+ }
+
+ return NULL;
+}
+
+GpuProcessHost::GpuProcessHost(int host_id, GpuProcessKind kind)
+ : host_id_(host_id),
+ valid_(true),
+ in_process_(false),
+ swiftshader_rendering_(false),
+ kind_(kind),
+ process_launched_(false),
+ initialized_(false),
+ uma_memory_stats_received_(false) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) ||
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) {
+ in_process_ = true;
+ }
+
+ // If the 'single GPU process' policy ever changes, we still want to maintain
+ // it for 'gpu thread' mode and only create one instance of host and thread.
+ DCHECK(!in_process_ || g_gpu_process_hosts[kind] == NULL);
+
+ g_gpu_process_hosts[kind] = this;
+
+ // Post a task to create the corresponding GpuProcessHostUIShim. The
+ // GpuProcessHostUIShim will be destroyed if either the browser exits,
+ // in which case it calls GpuProcessHostUIShim::DestroyAll, or the
+ // GpuProcessHost is destroyed, which happens when the corresponding GPU
+ // process terminates or fails to launch.
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&GpuProcessHostUIShim::Create), host_id));
+
+ process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_GPU, this));
+}
+
+GpuProcessHost::~GpuProcessHost() {
+ DCHECK(CalledOnValidThread());
+
+ SendOutstandingReplies();
+
+ // Maximum number of times the gpu process is allowed to crash in a session.
+ // Once this limit is reached, any request to launch the gpu process will
+ // fail.
+ const int kGpuMaxCrashCount = 3;
+
+ // Number of times the gpu process has crashed in the current browser session.
+ static int gpu_crash_count = 0;
+ static int gpu_recent_crash_count = 0;
+ static base::Time last_gpu_crash_time;
+ static bool crashed_before = false;
+ static int swiftshader_crash_count = 0;
+
+ // Ending only acts as a failure if the GPU process was actually started and
+ // was intended for actual rendering (and not just checking caps or other
+ // options).
+ if (process_launched_ && kind_ == GPU_PROCESS_KIND_SANDBOXED) {
+ if (swiftshader_rendering_) {
+ UMA_HISTOGRAM_ENUMERATION("GPU.SwiftShaderLifetimeEvents",
+ DIED_FIRST_TIME + swiftshader_crash_count,
+ GPU_PROCESS_LIFETIME_EVENT_MAX);
+
+ if (++swiftshader_crash_count >= kGpuMaxCrashCount) {
+ // SwiftShader is too unstable to use. Disable it for current session.
+ gpu_enabled_ = false;
+ }
+ } else {
+ ++gpu_crash_count;
+ UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents",
+ std::min(DIED_FIRST_TIME + gpu_crash_count,
+ GPU_PROCESS_LIFETIME_EVENT_MAX - 1),
+ GPU_PROCESS_LIFETIME_EVENT_MAX);
+
+ // Allow about 1 GPU crash per hour to be removed from the crash count,
+ // so very occasional crashes won't eventually add up and prevent the
+ // GPU process from launching.
+ ++gpu_recent_crash_count;
+ base::Time current_time = base::Time::Now();
+ if (crashed_before) {
+ int hours_different = (current_time - last_gpu_crash_time).InHours();
+ gpu_recent_crash_count =
+ std::max(0, gpu_recent_crash_count - hours_different);
+ }
+
+ crashed_before = true;
+ last_gpu_crash_time = current_time;
+
+ if (gpu_recent_crash_count >= kGpuMaxCrashCount ||
+ !initialized_) {
+#if !defined(OS_CHROMEOS)
+ // The gpu process is too unstable to use. Disable it for current
+ // session.
+ hardware_gpu_enabled_ = false;
+ GpuDataManagerImpl::GetInstance()->DisableHardwareAcceleration();
+#endif
+ }
+ }
+ }
+
+ // In case we never started, clean up.
+ while (!queued_messages_.empty()) {
+ delete queued_messages_.front();
+ queued_messages_.pop();
+ }
+
+ // This is only called on the IO thread so no race against the constructor
+ // for another GpuProcessHost.
+ if (g_gpu_process_hosts[kind_] == this)
+ g_gpu_process_hosts[kind_] = NULL;
+
+ // If there are any remaining offscreen contexts at the point the
+ // GPU process exits, assume something went wrong, and block their
+ // URLs from accessing client 3D APIs without prompting.
+ BlockLiveOffscreenContexts();
+
+ UMA_HISTOGRAM_COUNTS_100("GPU.AtExitSurfaceCount",
+ GpuSurfaceTracker::Get()->GetSurfaceCount());
+ UMA_HISTOGRAM_BOOLEAN("GPU.AtExitReceivedMemoryStats",
+ uma_memory_stats_received_);
+
+ if (uma_memory_stats_received_) {
+ UMA_HISTOGRAM_COUNTS_100("GPU.AtExitManagedMemoryClientCount",
+ uma_memory_stats_.client_count);
+ UMA_HISTOGRAM_COUNTS_100("GPU.AtExitContextGroupCount",
+ uma_memory_stats_.context_group_count);
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "GPU.AtExitMBytesAllocated",
+ uma_memory_stats_.bytes_allocated_current / 1024 / 1024, 1, 2000, 50);
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "GPU.AtExitMBytesAllocatedMax",
+ uma_memory_stats_.bytes_allocated_max / 1024 / 1024, 1, 2000, 50);
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "GPU.AtExitMBytesLimit",
+ uma_memory_stats_.bytes_limit / 1024 / 1024, 1, 2000, 50);
+ }
+
+ std::string message;
+ if (!in_process_) {
+ int exit_code;
+ base::TerminationStatus status = process_->GetTerminationStatus(&exit_code);
+ UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessTerminationStatus",
+ status,
+ base::TERMINATION_STATUS_MAX_ENUM);
+
+ if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
+ status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
+ UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessExitCode",
+ exit_code,
+ RESULT_CODE_LAST_CODE);
+ }
+
+ switch (status) {
+ case base::TERMINATION_STATUS_NORMAL_TERMINATION:
+ message = "The GPU process exited normally. Everything is okay.";
+ break;
+ case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
+ message = base::StringPrintf(
+ "The GPU process exited with code %d.",
+ exit_code);
+ break;
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
+ message = "You killed the GPU process! Why?";
+ break;
+ case base::TERMINATION_STATUS_PROCESS_CRASHED:
+ message = "The GPU process crashed!";
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GpuProcessHostUIShim::Destroy,
+ host_id_,
+ message));
+}
+
+bool GpuProcessHost::Init() {
+ init_start_time_ = base::TimeTicks::Now();
+
+ TRACE_EVENT_INSTANT0("gpu", "LaunchGpuProcess", TRACE_EVENT_SCOPE_THREAD);
+
+ std::string channel_id = process_->GetHost()->CreateChannel();
+ if (channel_id.empty())
+ return false;
+
+ // Single process not supported in multiple dll mode currently.
+#if !defined(CHROME_MULTIPLE_DLL)
+ if (in_process_) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisableGpuWatchdog);
+
+ in_process_gpu_thread_.reset(new GpuMainThread(channel_id));
+ in_process_gpu_thread_->Start();
+
+ OnProcessLaunched(); // Fake a callback that the process is ready.
+ } else
+#endif // !CHROME_MULTIPLE_DLL
+ if (!LaunchGpuProcess(channel_id)) {
+ return false;
+ }
+
+ if (!Send(new GpuMsg_Initialize()))
+ return false;
+
+ return true;
+}
+
+void GpuProcessHost::RouteOnUIThread(const IPC::Message& message) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&RouteToGpuProcessHostUIShimTask, host_id_, message));
+}
+
+bool GpuProcessHost::Send(IPC::Message* msg) {
+ DCHECK(CalledOnValidThread());
+ if (process_->GetHost()->IsChannelOpening()) {
+ queued_messages_.push(msg);
+ return true;
+ }
+
+ bool result = process_->Send(msg);
+ if (!result) {
+ // Channel is hosed, but we may not get destroyed for a while. Send
+ // outstanding channel creation failures now so that the caller can restart
+ // with a new process/channel without waiting.
+ SendOutstandingReplies();
+ }
+ return result;
+}
+
+void GpuProcessHost::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
+ DCHECK(CalledOnValidThread());
+ process_->GetHost()->AddFilter(filter);
+}
+
+bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {
+ DCHECK(CalledOnValidThread());
+ IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_Initialized, OnInitialized)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_CommandBufferCreated, OnCommandBufferCreated)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyCommandBuffer, OnDestroyCommandBuffer)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_ImageCreated, OnImageCreated)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DidCreateOffscreenContext,
+ OnDidCreateOffscreenContext)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DidLoseContext, OnDidLoseContext)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DidDestroyOffscreenContext,
+ OnDidDestroyOffscreenContext)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_GpuMemoryUmaStats,
+ OnGpuMemoryUmaStatsReceived)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
+ OnAcceleratedSurfaceBuffersSwapped)
+#endif
+#if defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
+ OnAcceleratedSurfaceBuffersSwapped)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfacePostSubBuffer,
+ OnAcceleratedSurfacePostSubBuffer)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSuspend,
+ OnAcceleratedSurfaceSuspend)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceRelease,
+ OnAcceleratedSurfaceRelease)
+#endif
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyChannel,
+ OnDestroyChannel)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_CacheShader,
+ OnCacheShader)
+
+ IPC_MESSAGE_UNHANDLED(RouteOnUIThread(message))
+ IPC_END_MESSAGE_MAP()
+
+ return true;
+}
+
+void GpuProcessHost::OnChannelConnected(int32 peer_pid) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnChannelConnected");
+
+ while (!queued_messages_.empty()) {
+ Send(queued_messages_.front());
+ queued_messages_.pop();
+ }
+}
+
+void GpuProcessHost::EstablishGpuChannel(
+ int client_id,
+ bool share_context,
+ const EstablishChannelCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ TRACE_EVENT0("gpu", "GpuProcessHost::EstablishGpuChannel");
+
+ // If GPU features are already blacklisted, no need to establish the channel.
+ if (!GpuDataManagerImpl::GetInstance()->GpuAccessAllowed(NULL)) {
+ callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
+ return;
+ }
+
+ if (Send(new GpuMsg_EstablishChannel(client_id, share_context))) {
+ channel_requests_.push(callback);
+ } else {
+ callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
+ }
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuShaderDiskCache)) {
+ CreateChannelCache(client_id);
+ }
+}
+
+void GpuProcessHost::CreateViewCommandBuffer(
+ const gfx::GLSurfaceHandle& compositing_surface,
+ int surface_id,
+ int client_id,
+ const GPUCreateCommandBufferConfig& init_params,
+ const CreateCommandBufferCallback& callback) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::CreateViewCommandBuffer");
+
+ DCHECK(CalledOnValidThread());
+
+ if (!compositing_surface.is_null() &&
+ Send(new GpuMsg_CreateViewCommandBuffer(
+ compositing_surface, surface_id, client_id, init_params))) {
+ create_command_buffer_requests_.push(callback);
+ surface_refs_.insert(std::make_pair(surface_id,
+ GpuSurfaceTracker::GetInstance()->GetSurfaceRefForSurface(surface_id)));
+ } else {
+ callback.Run(MSG_ROUTING_NONE);
+ }
+}
+
+void GpuProcessHost::CreateImage(gfx::PluginWindowHandle window,
+ int client_id,
+ int image_id,
+ const CreateImageCallback& callback) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::CreateImage");
+
+ DCHECK(CalledOnValidThread());
+
+ if (Send(new GpuMsg_CreateImage(window, client_id, image_id))) {
+ create_image_requests_.push(callback);
+ } else {
+ callback.Run(gfx::Size());
+ }
+}
+
+void GpuProcessHost::DeleteImage(int client_id,
+ int image_id,
+ int sync_point) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::DeleteImage");
+
+ DCHECK(CalledOnValidThread());
+
+ Send(new GpuMsg_DeleteImage(client_id, image_id, sync_point));
+}
+
+void GpuProcessHost::OnInitialized(bool result, const gpu::GPUInfo& gpu_info) {
+ UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", result);
+ initialized_ = result;
+
+#if defined(OS_WIN)
+ if (kind_ == GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED)
+ AcceleratedPresenter::SetAdapterLUID(gpu_info.adapter_luid);
+#endif
+
+ if (!initialized_)
+ GpuDataManagerImpl::GetInstance()->OnGpuProcessInitFailure();
+}
+
+void GpuProcessHost::OnChannelEstablished(
+ const IPC::ChannelHandle& channel_handle) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnChannelEstablished");
+
+ if (channel_requests_.empty()) {
+ // This happens when GPU process is compromised.
+ RouteOnUIThread(GpuHostMsg_OnLogMessage(
+ logging::LOG_WARNING,
+ "WARNING",
+ "Received a ChannelEstablished message but no requests in queue."));
+ return;
+ }
+ EstablishChannelCallback callback = channel_requests_.front();
+ channel_requests_.pop();
+
+ // Currently if any of the GPU features are blacklisted, we don't establish a
+ // GPU channel.
+ if (!channel_handle.name.empty() &&
+ !GpuDataManagerImpl::GetInstance()->GpuAccessAllowed(NULL)) {
+ Send(new GpuMsg_CloseChannel(channel_handle));
+ callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
+ RouteOnUIThread(GpuHostMsg_OnLogMessage(
+ logging::LOG_WARNING,
+ "WARNING",
+ "Hardware acceleration is unavailable."));
+ return;
+ }
+
+ callback.Run(channel_handle,
+ GpuDataManagerImpl::GetInstance()->GetGPUInfo());
+}
+
+void GpuProcessHost::OnCommandBufferCreated(const int32 route_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnCommandBufferCreated");
+
+ if (create_command_buffer_requests_.empty())
+ return;
+
+ CreateCommandBufferCallback callback =
+ create_command_buffer_requests_.front();
+ create_command_buffer_requests_.pop();
+ callback.Run(route_id);
+}
+
+void GpuProcessHost::OnDestroyCommandBuffer(int32 surface_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnDestroyCommandBuffer");
+ SurfaceRefMap::iterator it = surface_refs_.find(surface_id);
+ if (it != surface_refs_.end()) {
+ surface_refs_.erase(it);
+ }
+}
+
+void GpuProcessHost::OnImageCreated(const gfx::Size size) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnImageCreated");
+
+ if (create_image_requests_.empty())
+ return;
+
+ CreateImageCallback callback = create_image_requests_.front();
+ create_image_requests_.pop();
+ callback.Run(size);
+}
+
+void GpuProcessHost::OnDidCreateOffscreenContext(const GURL& url) {
+ urls_with_live_offscreen_contexts_.insert(url);
+}
+
+void GpuProcessHost::OnDidLoseContext(bool offscreen,
+ gpu::error::ContextLostReason reason,
+ const GURL& url) {
+ // TODO(kbr): would be nice to see the "offscreen" flag too.
+ TRACE_EVENT2("gpu", "GpuProcessHost::OnDidLoseContext",
+ "reason", reason,
+ "url",
+ url.possibly_invalid_spec());
+
+ if (!offscreen || url.is_empty()) {
+ // Assume that the loss of the compositor's or accelerated canvas'
+ // context is a serious event and blame the loss on all live
+ // offscreen contexts. This more robustly handles situations where
+ // the GPU process may not actually detect the context loss in the
+ // offscreen context.
+ BlockLiveOffscreenContexts();
+ return;
+ }
+
+ GpuDataManagerImpl::DomainGuilt guilt;
+ switch (reason) {
+ case gpu::error::kGuilty:
+ guilt = GpuDataManagerImpl::DOMAIN_GUILT_KNOWN;
+ break;
+ case gpu::error::kUnknown:
+ guilt = GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN;
+ break;
+ case gpu::error::kInnocent:
+ return;
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ GpuDataManagerImpl::GetInstance()->BlockDomainFrom3DAPIs(url, guilt);
+}
+
+void GpuProcessHost::OnDidDestroyOffscreenContext(const GURL& url) {
+ urls_with_live_offscreen_contexts_.erase(url);
+}
+
+void GpuProcessHost::OnGpuMemoryUmaStatsReceived(
+ const GPUMemoryUmaStats& stats) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnGpuMemoryUmaStatsReceived");
+ uma_memory_stats_received_ = true;
+ uma_memory_stats_ = stats;
+}
+
+#if defined(OS_MACOSX)
+void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped");
+
+ gfx::GLSurfaceHandle surface_handle =
+ GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id);
+ // Compositor window is always gfx::kNullPluginWindow.
+ // TODO(jbates) http://crbug.com/105344 This will be removed when there are no
+ // plugin windows.
+ if (surface_handle.handle != gfx::kNullPluginWindow ||
+ surface_handle.transport_type == gfx::TEXTURE_TRANSPORT) {
+ RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
+ return;
+ }
+
+ base::ScopedClosureRunner scoped_completion_runner(
+ base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU,
+ host_id_, params.route_id,
+ true /* alive */));
+
+ int render_process_id = 0;
+ int render_widget_id = 0;
+ if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface(
+ params.surface_id, &render_process_id, &render_widget_id)) {
+ return;
+ }
+ RenderWidgetHelper* helper =
+ RenderWidgetHelper::FromProcessHostID(render_process_id);
+ if (!helper)
+ return;
+
+ // Pass the SwapBuffers on to the RenderWidgetHelper to wake up the UI thread
+ // if the browser is waiting for a new frame. Otherwise the RenderWidgetHelper
+ // will forward to the RenderWidgetHostView via RenderProcessHostImpl and
+ // RenderWidgetHostImpl.
+ scoped_completion_runner.Release();
+
+ ViewHostMsg_CompositorSurfaceBuffersSwapped_Params view_params;
+ view_params.surface_id = params.surface_id;
+ view_params.surface_handle = params.surface_handle;
+ view_params.route_id = params.route_id;
+ view_params.size = params.size;
+ view_params.scale_factor = params.scale_factor;
+ view_params.gpu_process_host_id = host_id_;
+ view_params.latency_info = params.latency_info;
+ helper->DidReceiveBackingStoreMsg(ViewHostMsg_CompositorSurfaceBuffersSwapped(
+ render_widget_id,
+ view_params));
+}
+#endif // OS_MACOSX
+
+#if defined(OS_WIN)
+void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped");
+
+ base::ScopedClosureRunner scoped_completion_runner(
+ base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted,
+ host_id_, params.route_id, params.surface_id,
+ true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo()));
+
+ gfx::GLSurfaceHandle handle =
+ GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id);
+
+ if (handle.is_null())
+ return;
+
+ if (handle.transport_type == gfx::TEXTURE_TRANSPORT) {
+ TRACE_EVENT1("gpu", "SurfaceIDNotFound_RoutingToUI",
+ "surface_id", params.surface_id);
+ // This is a content area swap, send it on to the UI thread.
+ scoped_completion_runner.Release();
+ RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
+ return;
+ }
+
+ // Otherwise it's the UI swap.
+
+ scoped_refptr<AcceleratedPresenter> presenter(
+ AcceleratedPresenter::GetForWindow(handle.handle));
+ if (!presenter) {
+ TRACE_EVENT1("gpu",
+ "EarlyOut_NativeWindowNotFound",
+ "handle",
+ handle.handle);
+ scoped_completion_runner.Release();
+ AcceleratedSurfaceBuffersSwappedCompleted(host_id_,
+ params.route_id,
+ params.surface_id,
+ true,
+ base::TimeTicks(),
+ base::TimeDelta(),
+ params.latency_info);
+ return;
+ }
+
+ scoped_completion_runner.Release();
+ presenter->AsyncPresentAndAcknowledge(
+ params.size,
+ params.surface_handle,
+ params.latency_info,
+ base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted,
+ host_id_,
+ params.route_id,
+ params.surface_id));
+
+ FrameSubscriberMap::iterator it = frame_subscribers_.find(params.surface_id);
+ if (it != frame_subscribers_.end() && it->second) {
+ const base::Time present_time = base::Time::Now();
+ scoped_refptr<media::VideoFrame> target_frame;
+ RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback copy_callback;
+ if (it->second->ShouldCaptureFrame(present_time,
+ &target_frame, &copy_callback)) {
+ // It is a potential improvement to do the copy in present, but we use a
+ // simpler approach for now.
+ presenter->AsyncCopyToVideoFrame(
+ gfx::Rect(params.size), target_frame,
+ base::Bind(copy_callback, present_time));
+ }
+ }
+}
+
+void GpuProcessHost::OnAcceleratedSurfacePostSubBuffer(
+ const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfacePostSubBuffer");
+
+ NOTIMPLEMENTED();
+}
+
+void GpuProcessHost::OnAcceleratedSurfaceSuspend(int32 surface_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceSuspend");
+
+ gfx::PluginWindowHandle handle =
+ GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id).handle;
+
+ if (!handle) {
+#if defined(USE_AURA)
+ RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceSuspend(surface_id));
+#endif
+ return;
+ }
+
+ scoped_refptr<AcceleratedPresenter> presenter(
+ AcceleratedPresenter::GetForWindow(handle));
+ if (!presenter)
+ return;
+
+ presenter->Suspend();
+}
+
+void GpuProcessHost::OnAcceleratedSurfaceRelease(
+ const GpuHostMsg_AcceleratedSurfaceRelease_Params& params) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceRelease");
+
+ gfx::PluginWindowHandle handle =
+ GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id).handle;
+ if (!handle) {
+#if defined(USE_AURA)
+ RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceRelease(params));
+ return;
+#endif
+ }
+
+ scoped_refptr<AcceleratedPresenter> presenter(
+ AcceleratedPresenter::GetForWindow(handle));
+ if (!presenter)
+ return;
+
+ presenter->ReleaseSurface();
+}
+
+#endif // OS_WIN
+
+void GpuProcessHost::OnProcessLaunched() {
+ UMA_HISTOGRAM_TIMES("GPU.GPUProcessLaunchTime",
+ base::TimeTicks::Now() - init_start_time_);
+}
+
+void GpuProcessHost::OnProcessCrashed(int exit_code) {
+ SendOutstandingReplies();
+ GpuDataManagerImpl::GetInstance()->ProcessCrashed(
+ process_->GetTerminationStatus(NULL));
+}
+
+GpuProcessHost::GpuProcessKind GpuProcessHost::kind() {
+ return kind_;
+}
+
+void GpuProcessHost::ForceShutdown() {
+ // This is only called on the IO thread so no race against the constructor
+ // for another GpuProcessHost.
+ if (g_gpu_process_hosts[kind_] == this)
+ g_gpu_process_hosts[kind_] = NULL;
+
+ process_->ForceShutdown();
+}
+
+void GpuProcessHost::BeginFrameSubscription(
+ int surface_id,
+ base::WeakPtr<RenderWidgetHostViewFrameSubscriber> subscriber) {
+ frame_subscribers_[surface_id] = subscriber;
+}
+
+void GpuProcessHost::EndFrameSubscription(int surface_id) {
+ frame_subscribers_.erase(surface_id);
+}
+
+bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
+ if (!(gpu_enabled_ &&
+ GpuDataManagerImpl::GetInstance()->ShouldUseSwiftShader()) &&
+ !hardware_gpu_enabled_) {
+ SendOutstandingReplies();
+ return false;
+ }
+
+ const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+
+ CommandLine::StringType gpu_launcher =
+ browser_command_line.GetSwitchValueNative(switches::kGpuLauncher);
+
+#if defined(OS_LINUX)
+ int child_flags = gpu_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF :
+ ChildProcessHost::CHILD_NORMAL;
+#else
+ int child_flags = ChildProcessHost::CHILD_NORMAL;
+#endif
+
+ base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags);
+ if (exe_path.empty())
+ return false;
+
+ CommandLine* cmd_line = new CommandLine(exe_path);
+ cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
+ cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
+
+ if (kind_ == GPU_PROCESS_KIND_UNSANDBOXED)
+ cmd_line->AppendSwitch(switches::kDisableGpuSandbox);
+
+ // Propagate relevant command line switches.
+ static const char* const kSwitchNames[] = {
+ switches::kDisableAcceleratedVideoDecode,
+ switches::kDisableBreakpad,
+ switches::kDisableGLMultisampling,
+ switches::kDisableGpuSandbox,
+ switches::kDisableGpuWatchdog,
+ switches::kDisableImageTransportSurface,
+ switches::kDisableLogging,
+ switches::kDisableSeccompFilterSandbox,
+ switches::kEnableLogging,
+ switches::kEnableShareGroupAsyncTextureUpload,
+ switches::kEnableVirtualGLContexts,
+ switches::kGpuStartupDialog,
+ switches::kGpuSandboxAllowSysVShm,
+ switches::kLoggingLevel,
+ switches::kNoSandbox,
+ switches::kReduceGpuSandbox,
+ switches::kTestGLLib,
+ switches::kTraceStartup,
+ switches::kV,
+ switches::kVModule,
+#if defined(OS_MACOSX)
+ switches::kEnableSandboxLogging,
+#endif
+#if defined(USE_AURA)
+ switches::kUIPrioritizeInGpuProcess,
+#endif
+ };
+ cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
+ arraysize(kSwitchNames));
+ cmd_line->CopySwitchesFrom(
+ browser_command_line, switches::kGpuSwitches, switches::kNumGpuSwitches);
+ cmd_line->CopySwitchesFrom(
+ browser_command_line, switches::kGLSwitchesCopiedFromGpuProcessHost,
+ switches::kGLSwitchesCopiedFromGpuProcessHostNumSwitches);
+
+ GetContentClient()->browser()->AppendExtraCommandLineSwitches(
+ cmd_line, process_->GetData().id);
+
+ GpuDataManagerImpl::GetInstance()->AppendGpuCommandLine(cmd_line);
+
+ if (cmd_line->HasSwitch(switches::kUseGL)) {
+ swiftshader_rendering_ =
+ (cmd_line->GetSwitchValueASCII(switches::kUseGL) == "swiftshader");
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("GPU.GPU.GPUProcessSoftwareRendering",
+ swiftshader_rendering_);
+
+ // If specified, prepend a launcher program to the command line.
+ if (!gpu_launcher.empty())
+ cmd_line->PrependWrapper(gpu_launcher);
+
+ process_->Launch(
+#if defined(OS_WIN)
+ new GpuSandboxedProcessLauncherDelegate(cmd_line),
+#elif defined(OS_POSIX)
+ false,
+ base::EnvironmentVector(),
+#endif
+ cmd_line);
+ process_launched_ = true;
+
+ UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents",
+ LAUNCHED, GPU_PROCESS_LIFETIME_EVENT_MAX);
+ return true;
+}
+
+void GpuProcessHost::SendOutstandingReplies() {
+ valid_ = false;
+ // First send empty channel handles for all EstablishChannel requests.
+ while (!channel_requests_.empty()) {
+ EstablishChannelCallback callback = channel_requests_.front();
+ channel_requests_.pop();
+ callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
+ }
+
+ while (!create_command_buffer_requests_.empty()) {
+ CreateCommandBufferCallback callback =
+ create_command_buffer_requests_.front();
+ create_command_buffer_requests_.pop();
+ callback.Run(MSG_ROUTING_NONE);
+ }
+}
+
+void GpuProcessHost::BlockLiveOffscreenContexts() {
+ for (std::multiset<GURL>::iterator iter =
+ urls_with_live_offscreen_contexts_.begin();
+ iter != urls_with_live_offscreen_contexts_.end(); ++iter) {
+ GpuDataManagerImpl::GetInstance()->BlockDomainFrom3DAPIs(
+ *iter, GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN);
+ }
+}
+
+std::string GpuProcessHost::GetShaderPrefixKey() {
+ if (shader_prefix_key_.empty()) {
+ gpu::GPUInfo info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
+
+ std::string in_str = GetContentClient()->GetProduct() + "-" +
+ info.gl_vendor + "-" + info.gl_renderer + "-" +
+ info.driver_version + "-" + info.driver_vendor;
+
+ base::Base64Encode(base::SHA1HashString(in_str), &shader_prefix_key_);
+ }
+
+ return shader_prefix_key_;
+}
+
+void GpuProcessHost::LoadedShader(const std::string& key,
+ const std::string& data) {
+ std::string prefix = GetShaderPrefixKey();
+ if (!key.compare(0, prefix.length(), prefix))
+ Send(new GpuMsg_LoadedShader(data));
+}
+
+void GpuProcessHost::CreateChannelCache(int32 client_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::CreateChannelCache");
+
+ scoped_refptr<ShaderDiskCache> cache =
+ ShaderCacheFactory::GetInstance()->Get(client_id);
+ if (!cache.get())
+ return;
+
+ cache->set_host_id(host_id_);
+
+ client_id_to_shader_cache_[client_id] = cache;
+}
+
+void GpuProcessHost::OnDestroyChannel(int32 client_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnDestroyChannel");
+ client_id_to_shader_cache_.erase(client_id);
+}
+
+void GpuProcessHost::OnCacheShader(int32 client_id,
+ const std::string& key,
+ const std::string& shader) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnCacheShader");
+ ClientIdToShaderCacheMap::iterator iter =
+ client_id_to_shader_cache_.find(client_id);
+ // If the cache doesn't exist then this is an off the record profile.
+ if (iter == client_id_to_shader_cache_.end())
+ return;
+ iter->second->Cache(GetShaderPrefixKey() + ":" + key, shader);
+}
+
+} // namespace content