blob: fbaec61317e8a45b22053455354ddd9cb461231c [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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/skia_output_device_gl.h"
#include <tuple>
#include <utility>
#include "base/debug/alias.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "skia/ext/legacy_display_globals.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_version_info.h"
namespace viz {
namespace {
base::TimeTicks g_last_reshape_failure = base::TimeTicks();
NOINLINE void CheckForLoopFailures() {
const auto threshold = base::Seconds(1);
auto now = base::TimeTicks::Now();
if (!g_last_reshape_failure.is_null() &&
now - g_last_reshape_failure < threshold) {
CHECK(false);
}
g_last_reshape_failure = now;
}
} // namespace
SkiaOutputDeviceGL::SkiaOutputDeviceGL(
gpu::SharedContextState* context_state,
scoped_refptr<gl::GLSurface> gl_surface,
scoped_refptr<gpu::gles2::FeatureInfo> feature_info,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
: SkiaOutputDevice(context_state->gr_context(),
context_state->graphite_context(),
memory_tracker,
std::move(did_swap_buffer_complete_callback)),
context_state_(context_state),
gl_surface_(std::move(gl_surface)),
supports_async_swap_(gl_surface_->SupportsAsyncSwap()) {
capabilities_.uses_default_gl_framebuffer = true;
capabilities_.output_surface_origin = gl_surface_->GetOrigin();
capabilities_.supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer();
if (feature_info->workarounds()
.disable_post_sub_buffers_for_onscreen_surfaces) {
capabilities_.supports_post_sub_buffer = false;
}
if (feature_info->workarounds().supports_two_yuv_hardware_overlays) {
capabilities_.supports_two_yuv_hardware_overlays = true;
}
capabilities_.pending_swap_params.max_pending_swaps =
gl_surface_->GetBufferCount() - 1;
capabilities_.supports_gpu_vsync = gl_surface_->SupportsGpuVSync();
#if BUILDFLAG(IS_ANDROID)
// TODO(weiliangc): This capability is used to check whether we should do
// overlay. Since currently none of the other overlay system is implemented,
// only update this for Android.
// This output device is never offscreen.
capabilities_.supports_surfaceless = gl_surface_->IsSurfaceless();
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
// If Chrome OS is run on Linux for development purposes, we need to
// advertise a hardware orientation mode since Ash manages a separate device
// rotation independent of the host's native windowing system.
capabilities_.orientation_mode = OutputSurface::OrientationMode::kHardware;
#endif // IS_CHROMEOS_ASH
DCHECK(context_state_);
DCHECK(gl_surface_);
if (gl_surface_->SupportsSwapTimestamps()) {
gl_surface_->SetEnableSwapTimestamps();
// Changes to swap timestamp queries are only picked up when making current.
context_state_->ReleaseCurrent(nullptr);
context_state_->MakeCurrent(gl_surface_.get());
}
DCHECK(context_state_->gr_context());
DCHECK(context_state_->context());
GrDirectContext* gr_context = context_state_->gr_context();
gl::CurrentGL* current_gl = context_state_->context()->GetCurrentGL();
// Get alpha bits from the default frame buffer.
int alpha_bits = 0;
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
gr_context->resetContext(kRenderTarget_GrGLBackendState);
const auto* version = current_gl->Version.get();
if (version->is_desktop_core_profile) {
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER, GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
&alpha_bits);
} else {
glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
}
CHECK_GL_ERROR();
auto color_type = kRGBA_8888_SkColorType;
if (alpha_bits == 0) {
color_type = gl_surface_->GetFormat().GetBufferSize() == 16
? kRGB_565_SkColorType
: kRGB_888x_SkColorType;
// Skia disables RGBx on some GPUs, fallback to RGBA if it's the
// case. This doesn't change framebuffer itself, as we already allocated it,
// but will change any temporary buffer Skia needs to allocate.
if (!context_state_->gr_context()
->defaultBackendFormat(color_type, GrRenderable::kYes)
.isValid()) {
color_type = kRGBA_8888_SkColorType;
}
}
// SRGB
capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] =
color_type;
capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] =
color_type;
capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] =
color_type;
capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRX_8888)] =
color_type;
// HDR10
capabilities_
.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_1010102)] =
kRGBA_1010102_SkColorType;
// scRGB linear
capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_F16)] =
kRGBA_F16_SkColorType;
}
SkiaOutputDeviceGL::~SkiaOutputDeviceGL() {
// gl_surface_ will be destructed soon.
memory_type_tracker_->TrackMemFree(backbuffer_estimated_size_);
}
bool SkiaOutputDeviceGL::Reshape(const SkImageInfo& image_info,
const gfx::ColorSpace& color_space,
int sample_count,
float device_scale_factor,
gfx::OverlayTransform transform) {
#if !BUILDFLAG(IS_CHROMEOS_ASH)
DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE);
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
const gfx::Size size = gfx::SkISizeToSize(image_info.dimensions());
const SkColorType color_type = image_info.colorType();
const bool has_alpha = !image_info.isOpaque();
if (!gl_surface_->Resize(size, device_scale_factor, color_space, has_alpha)) {
CheckForLoopFailures();
// To prevent tail call, so we can see the stack.
base::debug::Alias(nullptr);
return false;
}
SkSurfaceProps surface_props{0, kUnknown_SkPixelGeometry};
GrGLFramebufferInfo framebuffer_info = {0};
DCHECK_EQ(gl_surface_->GetBackingFramebufferObject(), 0u);
auto* gr_context = context_state_->gr_context();
GrBackendFormat backend_format =
gr_context->defaultBackendFormat(color_type, GrRenderable::kYes);
DCHECK(backend_format.isValid()) << "color_type: " << color_type;
framebuffer_info.fFormat = backend_format.asGLFormatEnum();
GrBackendRenderTarget render_target(size.width(), size.height(), sample_count,
/*stencilBits=*/0, framebuffer_info);
auto origin = (gl_surface_->GetOrigin() == gfx::SurfaceOrigin::kTopLeft)
? kTopLeft_GrSurfaceOrigin
: kBottomLeft_GrSurfaceOrigin;
sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
gr_context, render_target, origin, color_type, image_info.refColorSpace(),
&surface_props);
if (!sk_surface_) {
LOG(ERROR) << "Couldn't create surface:"
<< "\n abandoned()=" << gr_context->abandoned()
<< "\n color_type=" << color_type
<< "\n framebuffer_info.fFBOID=" << framebuffer_info.fFBOID
<< "\n framebuffer_info.fFormat=" << framebuffer_info.fFormat
<< "\n color_space=" << color_space.ToString()
<< "\n size=" << size.ToString();
CheckForLoopFailures();
// To prevent tail call, so we can see the stack.
base::debug::Alias(nullptr);
}
memory_type_tracker_->TrackMemFree(backbuffer_estimated_size_);
GLenum format = gpu::gles2::TextureManager::ExtractFormatFromStorageFormat(
framebuffer_info.fFormat);
GLenum type = gpu::gles2::TextureManager::ExtractTypeFromStorageFormat(
framebuffer_info.fFormat);
uint32_t estimated_size;
gpu::gles2::GLES2Util::ComputeImageDataSizes(
size.width(), size.height(), 1 /* depth */, format, type,
4 /* alignment */, &estimated_size, nullptr, nullptr);
backbuffer_estimated_size_ = estimated_size * gl_surface_->GetBufferCount();
memory_type_tracker_->TrackMemAlloc(backbuffer_estimated_size_);
return !!sk_surface_;
}
void SkiaOutputDeviceGL::Present(const absl::optional<gfx::Rect>& update_rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
StartSwapBuffers({});
gfx::Size surface_size =
gfx::Size(sk_surface_->width(), sk_surface_->height());
auto data = frame.data;
if (supports_async_swap_) {
auto callback = base::BindOnce(
&SkiaOutputDeviceGL::DoFinishSwapBuffersAsync,
weak_ptr_factory_.GetWeakPtr(), surface_size, std::move(frame));
if (update_rect) {
gl_surface_->PostSubBufferAsync(
update_rect->x(), update_rect->y(), update_rect->width(),
update_rect->height(), std::move(callback), std::move(feedback),
std::move(data));
} else {
gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback),
std::move(data));
}
} else {
gfx::SwapResult result;
if (update_rect) {
result = gl_surface_->PostSubBuffer(
update_rect->x(), update_rect->y(), update_rect->width(),
update_rect->height(), std::move(feedback), std::move(data));
} else {
result = gl_surface_->SwapBuffers(std::move(feedback), std::move(data));
}
DoFinishSwapBuffers(surface_size, std::move(frame),
gfx::SwapCompletionResult(result));
}
}
void SkiaOutputDeviceGL::DoFinishSwapBuffersAsync(
const gfx::Size& size,
OutputSurfaceFrame frame,
gfx::SwapCompletionResult result) {
DCHECK(result.release_fence.is_null());
FinishSwapBuffers(std::move(result), size, std::move(frame));
}
void SkiaOutputDeviceGL::DoFinishSwapBuffers(const gfx::Size& size,
OutputSurfaceFrame frame,
gfx::SwapCompletionResult result) {
DCHECK(result.release_fence.is_null());
FinishSwapBuffers(std::move(result), size, std::move(frame));
}
void SkiaOutputDeviceGL::EnsureBackbuffer() {
gl_surface_->SetBackbufferAllocation(true);
}
void SkiaOutputDeviceGL::DiscardBackbuffer() {
gl_surface_->SetBackbufferAllocation(false);
}
SkSurface* SkiaOutputDeviceGL::BeginPaint(
std::vector<GrBackendSemaphore>* end_semaphores) {
DCHECK(sk_surface_);
return sk_surface_.get();
}
void SkiaOutputDeviceGL::EndPaint() {}
} // namespace viz