blob: 146e54890d444ba693e980fc312e13b02f055098 [file] [log] [blame]
// Copyright 2016 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 "services/ui/gpu/gpu_service.h"
#include "base/bind.h"
#include "base/debug/crash_logging.h"
#include "base/lazy_instance.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/output/in_process_context_provider.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/config/gpu_util.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "gpu/ipc/common/memory_stats.h"
#include "gpu/ipc/gpu_in_process_thread_service.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_sync_channel.h"
#include "ipc/ipc_sync_message_filter.h"
#include "media/gpu/ipc/service/gpu_jpeg_decode_accelerator.h"
#include "media/gpu/ipc/service/gpu_video_decode_accelerator.h"
#include "media/gpu/ipc/service/gpu_video_encode_accelerator.h"
#include "media/gpu/ipc/service/media_gpu_channel_manager.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_switching_manager.h"
#include "ui/gl/init/gl_factory.h"
#include "url/gurl.h"
#if defined(OS_ANDROID)
#include "base/android/throw_uncaught_exception.h"
#include "media/gpu/content_video_view_overlay_allocator.h"
#endif
namespace ui {
namespace {
static base::LazyInstance<base::Callback<
void(int severity, size_t message_start, const std::string& message)>>::
Leaky g_log_callback = LAZY_INSTANCE_INITIALIZER;
bool GpuLogMessageHandler(int severity,
const char* file,
int line,
size_t message_start,
const std::string& message) {
g_log_callback.Get().Run(severity, message_start, message);
return false;
}
// Returns a callback which does a PostTask to run |callback| on the |runner|
// task runner.
template <typename Param>
const base::Callback<void(const Param&)> WrapCallback(
scoped_refptr<base::SingleThreadTaskRunner> runner,
const base::Callback<void(const Param&)>& callback) {
return base::Bind(
[](scoped_refptr<base::SingleThreadTaskRunner> runner,
const base::Callback<void(const Param&)>& callback,
const Param& param) {
runner->PostTask(FROM_HERE, base::Bind(callback, param));
},
runner, callback);
}
void DestroyBinding(mojo::BindingSet<mojom::GpuService>* binding,
base::WaitableEvent* wait) {
binding->CloseAllBindings();
wait->Signal();
}
} // namespace
GpuService::GpuService(const gpu::GPUInfo& gpu_info,
std::unique_ptr<gpu::GpuWatchdogThread> watchdog_thread,
scoped_refptr<base::SingleThreadTaskRunner> io_runner,
const gpu::GpuFeatureInfo& gpu_feature_info)
: main_runner_(base::ThreadTaskRunnerHandle::Get()),
io_runner_(std::move(io_runner)),
watchdog_thread_(std::move(watchdog_thread)),
gpu_memory_buffer_factory_(
gpu::GpuMemoryBufferFactory::CreateNativeType()),
gpu_workarounds_(base::CommandLine::ForCurrentProcess()),
gpu_info_(gpu_info),
gpu_feature_info_(gpu_feature_info),
sync_point_manager_(nullptr),
bindings_(base::MakeUnique<mojo::BindingSet<mojom::GpuService>>()),
weak_ptr_factory_(this) {
DCHECK(!io_runner_->BelongsToCurrentThread());
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
}
GpuService::~GpuService() {
DCHECK(main_runner_->BelongsToCurrentThread());
bind_task_tracker_.TryCancelAll();
logging::SetLogMessageHandler(nullptr);
g_log_callback.Get() =
base::Callback<void(int, size_t, const std::string&)>();
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
if (io_runner_->PostTask(
FROM_HERE, base::Bind(&DestroyBinding, bindings_.get(), &wait))) {
wait.Wait();
}
media_gpu_channel_manager_.reset();
gpu_channel_manager_.reset();
owned_sync_point_manager_.reset();
// Signal this event before destroying the child process. That way all
// background threads can cleanup. For example, in the renderer the
// RenderThread instances will be able to notice shutdown before the render
// process begins waiting for them to exit.
if (owned_shutdown_event_)
owned_shutdown_event_->Signal();
}
void GpuService::UpdateGPUInfoFromPreferences(
const gpu::GpuPreferences& preferences) {
DCHECK(main_runner_->BelongsToCurrentThread());
DCHECK(!gpu_host_);
gpu_preferences_ = preferences;
gpu_info_.video_decode_accelerator_capabilities =
media::GpuVideoDecodeAccelerator::GetCapabilities(gpu_preferences_,
gpu_workarounds_);
gpu_info_.video_encode_accelerator_supported_profiles =
media::GpuVideoEncodeAccelerator::GetSupportedProfiles(gpu_preferences_);
gpu_info_.jpeg_decode_accelerator_supported =
media::GpuJpegDecodeAcceleratorFactoryProvider::
IsAcceleratedJpegDecodeSupported();
// Record initialization only after collecting the GPU info because that can
// take a significant amount of time.
gpu_info_.initialization_time = base::Time::Now() - start_time_;
}
void GpuService::InitializeWithHost(mojom::GpuHostPtr gpu_host,
gpu::GpuProcessActivityFlags activity_flags,
gpu::SyncPointManager* sync_point_manager,
base::WaitableEvent* shutdown_event) {
DCHECK(main_runner_->BelongsToCurrentThread());
gpu_host->DidInitialize(gpu_info_, gpu_feature_info_);
gpu_host_ =
mojom::ThreadSafeGpuHostPtr::Create(gpu_host.PassInterface(), io_runner_);
if (!in_host_process_) {
// The global callback is reset from the dtor. So Unretained() here is safe.
// Note that the callback can be called from any thread. Consequently, the
// callback cannot use a WeakPtr.
g_log_callback.Get() =
base::Bind(&GpuService::RecordLogMessage, base::Unretained(this));
logging::SetLogMessageHandler(GpuLogMessageHandler);
}
sync_point_manager_ = sync_point_manager;
if (!sync_point_manager_) {
owned_sync_point_manager_ = base::MakeUnique<gpu::SyncPointManager>();
sync_point_manager_ = owned_sync_point_manager_.get();
}
shutdown_event_ = shutdown_event;
if (!shutdown_event_) {
owned_shutdown_event_ = base::MakeUnique<base::WaitableEvent>(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
shutdown_event_ = owned_shutdown_event_.get();
}
// Defer creation of the render thread. This is to prevent it from handling
// IPC messages before the sandbox has been enabled and all other necessary
// initialization has succeeded.
gpu_channel_manager_.reset(new gpu::GpuChannelManager(
gpu_preferences_, gpu_workarounds_, this, watchdog_thread_.get(),
base::ThreadTaskRunnerHandle::Get(), io_runner_, sync_point_manager_,
gpu_memory_buffer_factory_.get(), gpu_feature_info_,
std::move(activity_flags)));
media_gpu_channel_manager_.reset(
new media::MediaGpuChannelManager(gpu_channel_manager_.get()));
if (watchdog_thread())
watchdog_thread()->AddPowerObserver();
}
void GpuService::Bind(mojom::GpuServiceRequest request) {
if (main_runner_->BelongsToCurrentThread()) {
bind_task_tracker_.PostTask(
io_runner_.get(), FROM_HERE,
base::Bind(&GpuService::Bind, base::Unretained(this),
base::Passed(std::move(request))));
return;
}
bindings_->AddBinding(this, std::move(request));
}
gpu::ImageFactory* GpuService::gpu_image_factory() {
return gpu_memory_buffer_factory_
? gpu_memory_buffer_factory_->AsImageFactory()
: nullptr;
}
void GpuService::RecordLogMessage(int severity,
size_t message_start,
const std::string& str) {
// This can be run from any thread.
std::string header = str.substr(0, message_start);
std::string message = str.substr(message_start);
(*gpu_host_)->RecordLogMessage(severity, header, message);
}
void GpuService::CreateGpuMemoryBuffer(
gfx::GpuMemoryBufferId id,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int client_id,
gpu::SurfaceHandle surface_handle,
const CreateGpuMemoryBufferCallback& callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
// This needs to happen in the IO thread.
callback.Run(gpu_memory_buffer_factory_->CreateGpuMemoryBuffer(
id, size, format, usage, client_id, surface_handle));
}
void GpuService::DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
int client_id,
const gpu::SyncToken& sync_token) {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::DestroyGpuMemoryBuffer, weak_ptr_,
id, client_id, sync_token));
return;
}
gpu_channel_manager_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
}
void GpuService::GetVideoMemoryUsageStats(
const GetVideoMemoryUsageStatsCallback& callback) {
if (io_runner_->BelongsToCurrentThread()) {
auto wrap_callback = WrapCallback(io_runner_, callback);
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::GetVideoMemoryUsageStats, weak_ptr_,
wrap_callback));
return;
}
gpu::VideoMemoryUsageStats video_memory_usage_stats;
gpu_channel_manager_->gpu_memory_manager()->GetVideoMemoryUsageStats(
&video_memory_usage_stats);
callback.Run(video_memory_usage_stats);
}
void GpuService::RequestCompleteGpuInfo(
const RequestCompleteGpuInfoCallback& callback) {
if (io_runner_->BelongsToCurrentThread()) {
auto wrap_callback = WrapCallback(io_runner_, callback);
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::RequestCompleteGpuInfo, weak_ptr_,
wrap_callback));
return;
}
UpdateGpuInfoPlatform();
callback.Run(gpu_info_);
#if defined(OS_WIN)
if (!in_host_process_) {
// The unsandboxed GPU process fulfilled its duty. Rest in peace.
base::MessageLoop::current()->QuitWhenIdle();
}
#endif
}
#if defined(OS_MACOSX)
void GpuService::UpdateGpuInfoPlatform() {
DCHECK(main_runner_->BelongsToCurrentThread());
// gpu::CollectContextGraphicsInfo() is already called during gpu process
// initialization (see GpuInit::InitializeAndStartSandbox()) on non-mac
// platforms, and during in-browser gpu thread initialization on all platforms
// (See InProcessGpuThread::Init()).
if (in_host_process_)
return;
DCHECK_EQ(gpu::kCollectInfoNone, gpu_info_.context_info_state);
gpu::CollectInfoResult result = gpu::CollectContextGraphicsInfo(&gpu_info_);
switch (result) {
case gpu::kCollectInfoFatalFailure:
LOG(ERROR) << "gpu::CollectGraphicsInfo failed (fatal).";
// TODO(piman): can we signal overall failure?
break;
case gpu::kCollectInfoNonFatalFailure:
DVLOG(1) << "gpu::CollectGraphicsInfo failed (non-fatal).";
break;
case gpu::kCollectInfoNone:
NOTREACHED();
break;
case gpu::kCollectInfoSuccess:
break;
}
gpu::SetKeysForCrashLogging(gpu_info_);
}
#elif defined(OS_WIN)
void GpuService::UpdateGpuInfoPlatform() {
DCHECK(main_runner_->BelongsToCurrentThread());
// GPU full info collection should only happen on un-sandboxed GPU process
// or single process/in-process gpu mode on Windows.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
DCHECK(command_line->HasSwitch("disable-gpu-sandbox") || in_host_process_);
// This is slow, but it's the only thing the unsandboxed GPU process does,
// and GpuDataManager prevents us from sending multiple collecting requests,
// so it's OK to be blocking.
gpu::GetDxDiagnostics(&gpu_info_.dx_diagnostics);
gpu_info_.dx_diagnostics_info_state = gpu::kCollectInfoSuccess;
}
#else
void GpuService::UpdateGpuInfoPlatform() {}
#endif
void GpuService::DidCreateOffscreenContext(const GURL& active_url) {
DCHECK(main_runner_->BelongsToCurrentThread());
(*gpu_host_)->DidCreateOffscreenContext(active_url);
}
void GpuService::DidDestroyChannel(int client_id) {
DCHECK(main_runner_->BelongsToCurrentThread());
media_gpu_channel_manager_->RemoveChannel(client_id);
(*gpu_host_)->DidDestroyChannel(client_id);
}
void GpuService::DidDestroyOffscreenContext(const GURL& active_url) {
DCHECK(main_runner_->BelongsToCurrentThread());
(*gpu_host_)->DidDestroyOffscreenContext(active_url);
}
void GpuService::DidLoseContext(bool offscreen,
gpu::error::ContextLostReason reason,
const GURL& active_url) {
DCHECK(main_runner_->BelongsToCurrentThread());
(*gpu_host_)->DidLoseContext(offscreen, reason, active_url);
}
void GpuService::StoreShaderToDisk(int client_id,
const std::string& key,
const std::string& shader) {
DCHECK(main_runner_->BelongsToCurrentThread());
(*gpu_host_)->StoreShaderToDisk(client_id, key, shader);
}
#if defined(OS_WIN)
void GpuService::SendAcceleratedSurfaceCreatedChildWindow(
gpu::SurfaceHandle parent_window,
gpu::SurfaceHandle child_window) {
DCHECK(main_runner_->BelongsToCurrentThread());
(*gpu_host_)->SetChildSurface(parent_window, child_window);
}
#endif
void GpuService::SetActiveURL(const GURL& url) {
DCHECK(main_runner_->BelongsToCurrentThread());
constexpr char kActiveURL[] = "url-chunk";
base::debug::SetCrashKeyValue(kActiveURL, url.possibly_invalid_spec());
}
void GpuService::EstablishGpuChannel(
int32_t client_id,
uint64_t client_tracing_id,
bool is_gpu_host,
const EstablishGpuChannelCallback& callback) {
if (io_runner_->BelongsToCurrentThread()) {
EstablishGpuChannelCallback wrap_callback = base::Bind(
[](scoped_refptr<base::SingleThreadTaskRunner> runner,
const EstablishGpuChannelCallback& cb,
mojo::ScopedMessagePipeHandle handle) {
runner->PostTask(FROM_HERE,
base::Bind(cb, base::Passed(std::move(handle))));
},
io_runner_, callback);
main_runner_->PostTask(
FROM_HERE,
base::Bind(&GpuService::EstablishGpuChannel, weak_ptr_, client_id,
client_tracing_id, is_gpu_host, wrap_callback));
return;
}
gpu::GpuChannel* gpu_channel = gpu_channel_manager_->EstablishChannel(
client_id, client_tracing_id, is_gpu_host);
mojo::MessagePipe pipe;
gpu_channel->Init(base::MakeUnique<gpu::SyncChannelFilteredSender>(
pipe.handle0.release(), gpu_channel, io_runner_, shutdown_event_));
media_gpu_channel_manager_->AddChannel(client_id);
callback.Run(std::move(pipe.handle1));
}
void GpuService::CloseChannel(int32_t client_id) {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::CloseChannel, weak_ptr_, client_id));
return;
}
gpu_channel_manager_->RemoveChannel(client_id);
}
void GpuService::LoadedShader(const std::string& data) {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::LoadedShader, weak_ptr_, data));
return;
}
gpu_channel_manager_->PopulateShaderCache(data);
}
void GpuService::DestroyingVideoSurface(
int32_t surface_id,
const DestroyingVideoSurfaceCallback& callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
#if defined(OS_ANDROID)
main_runner_->PostTaskAndReply(
FROM_HERE,
base::Bind(
[](int32_t surface_id) {
media::ContentVideoViewOverlayAllocator::GetInstance()
->OnSurfaceDestroyed(surface_id);
},
surface_id),
callback);
#else
NOTREACHED() << "DestroyingVideoSurface() not supported on this platform.";
#endif
}
void GpuService::WakeUpGpu() {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(FROM_HERE,
base::Bind(&GpuService::WakeUpGpu, weak_ptr_));
return;
}
#if defined(OS_ANDROID)
gpu_channel_manager_->WakeUpGpu();
#else
NOTREACHED() << "WakeUpGpu() not supported on this platform.";
#endif
}
void GpuService::GpuSwitched() {
DVLOG(1) << "GPU: GPU has switched";
if (!in_host_process_)
ui::GpuSwitchingManager::GetInstance()->NotifyGpuSwitched();
}
void GpuService::DestroyAllChannels() {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(
FROM_HERE, base::Bind(&GpuService::DestroyAllChannels, weak_ptr_));
return;
}
DVLOG(1) << "GPU: Removing all contexts";
gpu_channel_manager_->DestroyAllChannels();
}
void GpuService::Crash() {
DCHECK(io_runner_->BelongsToCurrentThread());
DVLOG(1) << "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 GpuService::Hang() {
DCHECK(io_runner_->BelongsToCurrentThread());
main_runner_->PostTask(FROM_HERE, base::Bind([] {
DVLOG(1) << "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.
}
}));
}
void GpuService::ThrowJavaException() {
DCHECK(io_runner_->BelongsToCurrentThread());
#if defined(OS_ANDROID)
main_runner_->PostTask(
FROM_HERE, base::Bind([] { base::android::ThrowUncaughtException(); }));
#else
NOTREACHED() << "Java exception not supported on this platform.";
#endif
}
void GpuService::Stop(const StopCallback& callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
main_runner_->PostTaskAndReply(FROM_HERE, base::Bind([] {
base::MessageLoop::current()->QuitWhenIdle();
}),
callback);
}
} // namespace ui