blob: a95e1f8273a5d4dcb1096337d6c392f2c08f5772 [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 "components/viz/service/display_embedder/viz_process_context_provider.h"
#include <stdint.h>
#include <utility>
#include "base/lazy_instance.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/system/sys_info.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "components/viz/common/gpu/context_lost_observer.h"
#include "components/viz/common/gpu/context_lost_reason.h"
#include "components/viz/common/resources/platform_color.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/raster_implementation_gles.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
#include "gpu/command_buffer/client/transfer_buffer.h"
#include "gpu/command_buffer/common/skia_utils.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h"
#include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
namespace viz {
namespace {
gpu::ContextCreationAttribs CreateAttributes(bool requires_alpha_channel) {
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = requires_alpha_channel ? 8 : -1;
attributes.depth_size = 0;
#if defined(OS_CHROMEOS)
// Chrome OS uses surfaceless when running on a real device and stencil
// buffers can then be added dynamically so supporting them does not have an
// impact on normal usage. If we are not running on a real Chrome OS device
// but instead on a workstation for development, then stencil support is
// useful as it allows the overdraw feedback debugging feature to be used.
attributes.stencil_size = 8;
#else
attributes.stencil_size = 0;
#endif
attributes.samples = 0;
attributes.sample_buffers = 0;
attributes.bind_generates_resource = false;
attributes.fail_if_major_perf_caveat = false;
attributes.lose_context_when_out_of_memory = true;
#if defined(OS_ANDROID)
// TODO(cblume): We should add wide gamut code here, setting
// attributes.color_space.
if (requires_alpha_channel) {
attributes.alpha_size = 8;
} else if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 512) {
// See compositor_impl_android.cc for more information about this.
// It is inside GetCompositorContextAttributes().
attributes.alpha_size = 0;
attributes.red_size = 5;
attributes.green_size = 6;
attributes.blue_size = 5;
}
attributes.enable_swap_timestamps_if_supported = true;
#endif // defined(OS_ANDROID)
return attributes;
}
void UmaRecordContextLost(ContextLostReason reason) {
UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.DisplayCompositor", reason);
}
} // namespace
VizProcessContextProvider::VizProcessContextProvider(
scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
gpu::SurfaceHandle surface_handle,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
gpu::ImageFactory* image_factory,
gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
const gpu::SharedMemoryLimits& limits,
bool requires_alpha_channel)
: attributes_(CreateAttributes(requires_alpha_channel)) {
InitializeContext(std::move(task_executor), surface_handle,
gpu_memory_buffer_manager, image_factory,
gpu_channel_manager_delegate, limits);
if (context_result_ == gpu::ContextResult::kSuccess) {
// |gles2_implementation_| is owned here so bind an unretained pointer or
// there will be a circular reference preventing destruction.
gles2_implementation_->SetLostContextCallback(base::BindOnce(
&VizProcessContextProvider::OnContextLost, base::Unretained(this)));
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "VizProcessContextProvider", base::ThreadTaskRunnerHandle::Get());
} else {
UmaRecordContextLost(CONTEXT_INIT_FAILED);
}
}
VizProcessContextProvider::~VizProcessContextProvider() {
if (context_result_ == gpu::ContextResult::kSuccess) {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
}
void VizProcessContextProvider::AddRef() const {
base::RefCountedThreadSafe<VizProcessContextProvider>::AddRef();
}
void VizProcessContextProvider::Release() const {
base::RefCountedThreadSafe<VizProcessContextProvider>::Release();
}
gpu::ContextResult VizProcessContextProvider::BindToCurrentThread() {
return context_result_;
}
gpu::gles2::GLES2Interface* VizProcessContextProvider::ContextGL() {
return gles2_implementation_.get();
}
gpu::ContextSupport* VizProcessContextProvider::ContextSupport() {
return gles2_implementation_.get();
}
class GrContext* VizProcessContextProvider::GrContext() {
if (gr_context_)
return gr_context_->get();
size_t max_resource_cache_bytes;
size_t max_glyph_cache_texture_bytes;
gpu::raster::DetermineGrCacheLimitsFromAvailableMemory(
&max_resource_cache_bytes, &max_glyph_cache_texture_bytes);
gr_context_ = std::make_unique<skia_bindings::GrContextForGLES2Interface>(
ContextGL(), ContextSupport(), ContextCapabilities(),
max_resource_cache_bytes, max_glyph_cache_texture_bytes);
return gr_context_->get();
}
gpu::SharedImageInterface* VizProcessContextProvider::SharedImageInterface() {
return command_buffer_->GetSharedImageInterface();
}
ContextCacheController* VizProcessContextProvider::CacheController() {
return cache_controller_.get();
}
base::Lock* VizProcessContextProvider::GetLock() {
// Locking isn't supported on display compositor contexts.
return nullptr;
}
const gpu::Capabilities& VizProcessContextProvider::ContextCapabilities()
const {
return command_buffer_->GetCapabilities();
}
const gpu::GpuFeatureInfo& VizProcessContextProvider::GetGpuFeatureInfo()
const {
return command_buffer_->GetGpuFeatureInfo();
}
void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) {
observers_.AddObserver(obs);
}
void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {
observers_.RemoveObserver(obs);
}
void VizProcessContextProvider::SetUpdateVSyncParametersCallback(
const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
callback) {
command_buffer_->SetUpdateVSyncParametersCallback(callback);
}
bool VizProcessContextProvider::UseRGB565PixelFormat() const {
return attributes_.alpha_size == 0 && attributes_.red_size == 5 &&
attributes_.green_size == 6 && attributes_.blue_size == 5;
}
uint32_t VizProcessContextProvider::GetCopyTextureInternalFormat() {
return attributes_.alpha_size > 0 ? GL_RGBA : GL_RGB;
}
void VizProcessContextProvider::InitializeContext(
scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
gpu::SurfaceHandle surface_handle,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
gpu::ImageFactory* image_factory,
gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
const gpu::SharedMemoryLimits& mem_limits) {
const bool is_offscreen = surface_handle == gpu::kNullSurfaceHandle;
command_buffer_ =
std::make_unique<gpu::InProcessCommandBuffer>(std::move(task_executor));
context_result_ = command_buffer_->Initialize(
/*surface=*/nullptr, is_offscreen, surface_handle, attributes_,
/*share_command_buffer=*/nullptr, gpu_memory_buffer_manager,
image_factory, gpu_channel_manager_delegate,
base::ThreadTaskRunnerHandle::Get(), nullptr, nullptr);
if (context_result_ != gpu::ContextResult::kSuccess) {
DLOG(ERROR) << "Failed to initialize InProcessCommmandBuffer";
return;
}
// Create the GLES2 helper, which writes the command buffer protocol.
gles2_helper_ =
std::make_unique<gpu::gles2::GLES2CmdHelper>(command_buffer_.get());
context_result_ = gles2_helper_->Initialize(mem_limits.command_buffer_size);
if (context_result_ != gpu::ContextResult::kSuccess) {
DLOG(ERROR) << "Failed to initialize GLES2CmdHelper";
return;
}
transfer_buffer_ = std::make_unique<gpu::TransferBuffer>(gles2_helper_.get());
// Create the object exposing the OpenGL API.
gles2_implementation_ =
std::make_unique<skia_bindings::GLES2ImplementationWithGrContextSupport>(
gles2_helper_.get(), /*share_group=*/nullptr, transfer_buffer_.get(),
attributes_.bind_generates_resource,
attributes_.lose_context_when_out_of_memory,
/*support_client_side_arrays=*/false, command_buffer_.get());
context_result_ = gles2_implementation_->Initialize(mem_limits);
if (context_result_ != gpu::ContextResult::kSuccess) {
DLOG(ERROR) << "Failed to initialize GLES2Implementation";
return;
}
cache_controller_ = std::make_unique<ContextCacheController>(
gles2_implementation_.get(), base::ThreadTaskRunnerHandle::Get());
// TraceEndCHROMIUM is implicit when the context is destroyed
gles2_implementation_->TraceBeginCHROMIUM("VizCompositor",
"DisplayCompositor");
}
void VizProcessContextProvider::OnContextLost() {
for (auto& observer : observers_)
observer.OnContextLost();
if (gr_context_)
gr_context_->OnLostContext();
gpu::CommandBuffer::State state = command_buffer_->GetLastState();
UmaRecordContextLost(
GetContextLostReason(state.error, state.context_lost_reason));
}
bool VizProcessContextProvider::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
DCHECK_EQ(context_result_, gpu::ContextResult::kSuccess);
gles2_implementation_->OnMemoryDump(args, pmd);
gles2_helper_->OnMemoryDump(args, pmd);
if (gr_context_) {
gpu::raster::DumpGrMemoryStatistics(
gr_context_->get(), pmd,
gles2_implementation_->ShareGroupTracingGUID());
}
return true;
}
} // namespace viz