blob: caa8467eb58995ec39b56e3d3b21f4711f709c83 [file] [log] [blame]
// Copyright 2018 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/skia_output_surface_impl.h"
#include "base/atomic_sequence_num.h"
#include "base/callback_helpers.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/resources/resource_metadata.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display_embedder/viz_process_context_provider.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
#include "gpu/command_buffer/service/gpu_preferences.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/command_buffer/service/texture_base.h"
#include "gpu/ipc/service/image_transport_surface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_gl_api_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_version_info.h"
namespace viz {
namespace {
base::AtomicSequenceNumber g_next_command_buffer_id;
} // namespace
struct SkiaOutputSurfaceImpl::PromiseTextureInfo {
PromiseTextureInfo(SkiaOutputSurfaceImpl* skia_renderer,
ResourceMetadata&& metadata)
: skia_renderer(skia_renderer), resource_metadata(std::move(metadata)) {}
~PromiseTextureInfo() = default;
SkiaOutputSurfaceImpl* const skia_renderer;
ResourceMetadata resource_metadata;
};
SkiaOutputSurfaceImpl::SkiaOutputSurfaceImpl(
GpuServiceImpl* gpu_service,
gpu::SurfaceHandle surface_handle,
scoped_refptr<VizProcessContextProvider> context_provider,
SyntheticBeginFrameSource* synthetic_begin_frame_source)
: SkiaOutputSurface(context_provider),
command_buffer_id_(gpu::CommandBufferId::FromUnsafeValue(
g_next_command_buffer_id.GetNext() + 1)),
gpu_service_(gpu_service),
surface_handle_(surface_handle),
synthetic_begin_frame_source_(synthetic_begin_frame_source),
gpu_thread_weak_ptr_factory_(this),
client_thread_weak_ptr_factory_(this) {
DETACH_FROM_THREAD(client_thread_checker_);
DETACH_FROM_THREAD(gpu_thread_checker_);
}
SkiaOutputSurfaceImpl::~SkiaOutputSurfaceImpl() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
recorder_ = nullptr;
// TODO(penghuang): avoid blocking compositor thread.
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::DestroyOnGpuThread,
base::Unretained(this), &event);
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
event.Wait();
}
void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(client);
DCHECK(!client_);
client_ = client;
client_thread_weak_ptr_ = client_thread_weak_ptr_factory_.GetWeakPtr();
client_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
// TODO(penghuang): avoid blocking compositor thread.
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::InitializeOnGpuThread,
base::Unretained(this), &event);
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
event.Wait();
}
void SkiaOutputSurfaceImpl::EnsureBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
NOTIMPLEMENTED();
}
void SkiaOutputSurfaceImpl::DiscardBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
NOTIMPLEMENTED();
}
void SkiaOutputSurfaceImpl::BindFramebuffer() {
// TODO(penghuang): remove this method when GLRenderer is removed.
}
void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
NOTIMPLEMENTED();
}
void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha,
bool use_stencil) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
recorder_ = nullptr;
SkSurfaceCharacterization* characterization = nullptr;
std::unique_ptr<base::WaitableEvent> event;
if (characterization_.isValid()) {
characterization_ =
characterization_.createResized(size.width(), size.height());
} else {
characterization = &characterization_;
// TODO(penghuang): avoid blocking compositor thread.
// We don't have a valid surface characterization, so we have to wait until
// reshape is finished on Gpu thread.
event = std::make_unique<base::WaitableEvent>(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
}
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::ReshapeOnGpuThread,
base::Unretained(this), size,
device_scale_factor, color_space, has_alpha,
use_stencil, characterization, event.get());
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
if (event)
event->Wait();
RecreateRecorder();
}
void SkiaOutputSurfaceImpl::SwapBuffers(OutputSurfaceFrame frame) {
NOTREACHED();
}
uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return GL_RGB;
}
OverlayCandidateValidator* SkiaOutputSurfaceImpl::GetOverlayCandidateValidator()
const {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return nullptr;
}
bool SkiaOutputSurfaceImpl::IsDisplayedAsOverlayPlane() const {
return false;
}
unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return 0;
}
gfx::BufferFormat SkiaOutputSurfaceImpl::GetOverlayBufferFormat() const {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return gfx::BufferFormat::RGBX_8888;
}
bool SkiaOutputSurfaceImpl::SurfaceIsSuspendForRecycle() const {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return false;
}
bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return false;
}
void SkiaOutputSurfaceImpl::ApplyExternalStencil() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
}
#if BUILDFLAG(ENABLE_VULKAN)
gpu::VulkanSurface* SkiaOutputSurfaceImpl::GetVulkanSurface() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
return nullptr;
}
#endif
SkCanvas* SkiaOutputSurfaceImpl::GetSkCanvasForCurrentFrame() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(recorder_);
return recorder_->getCanvas();
}
sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImage(
ResourceMetadata metadata) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(recorder_);
// Convert internal format from GLES2 to platform GL.
const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
metadata.backend_format = GrBackendFormat::MakeGL(
gl::GetInternalFormat(version_info,
*metadata.backend_format.getGLFormat()),
*metadata.backend_format.getGLTarget());
DCHECK(!metadata.mailbox.IsZero());
resource_sync_tokens_.push_back(metadata.sync_token);
auto texture_info =
std::make_unique<PromiseTextureInfo>(this, std::move(metadata));
const auto& data = texture_info->resource_metadata;
auto image = recorder_->makePromiseTexture(
data.backend_format, data.size.width(), data.size.height(),
data.mip_mapped, data.origin, data.color_type, data.alpha_type,
data.color_space, SkiaOutputSurfaceImpl::PromiseTextureFullfillStub,
SkiaOutputSurfaceImpl::PromiseTextureReleaseStub,
SkiaOutputSurfaceImpl::PromiseTextureDoneStub, texture_info.get());
if (image)
texture_info.release();
return image;
}
gpu::SyncToken SkiaOutputSurfaceImpl::SkiaSwapBuffers(
OutputSurfaceFrame frame) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(recorder_);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
command_buffer_id_, ++sync_fence_release_);
sync_token.SetVerifyFlush();
auto ddl = recorder_->detach();
DCHECK(ddl);
RecreateRecorder();
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::SwapBuffersOnGpuThread,
base::Unretained(this), base::Passed(&frame),
base::Passed(&ddl), sync_fence_release_);
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
return sync_token;
}
void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
gpu::SwapBuffersCompleteParams params) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
client_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&SkiaOutputSurfaceImpl::DidSwapBuffersCompleteOnClientThread,
client_thread_weak_ptr_, params));
}
const gpu::gles2::FeatureInfo* SkiaOutputSurfaceImpl::GetFeatureInfo() const {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
NOTIMPLEMENTED();
return nullptr;
}
const gpu::GpuPreferences& SkiaOutputSurfaceImpl::GetGpuPreferences() const {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
NOTIMPLEMENTED();
return gpu_preferences_;
}
void SkiaOutputSurfaceImpl::SetSnapshotRequestedCallback(
const base::Closure& callback) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
NOTIMPLEMENTED();
}
void SkiaOutputSurfaceImpl::UpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
client_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&SkiaOutputSurfaceImpl::UpdateVSyncParametersOnClientThread,
client_thread_weak_ptr_, timebase, interval));
}
void SkiaOutputSurfaceImpl::BufferPresented(
uint64_t swap_id,
const gfx::PresentationFeedback& feedback) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
client_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SkiaOutputSurfaceImpl::BufferPresentedOnClientThread,
client_thread_weak_ptr_, swap_id, feedback));
}
void SkiaOutputSurfaceImpl::AddFilter(IPC::MessageFilter* message_filter) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
NOTIMPLEMENTED();
}
int32_t SkiaOutputSurfaceImpl::GetRouteID() const {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
NOTIMPLEMENTED();
return 0;
}
void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
base::ScopedClosureRunner scoped_runner(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
sync_point_client_state_ =
gpu_service_->sync_point_manager()->CreateSyncPointClientState(
gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_,
gpu_service_->skia_output_surface_sequence_id());
surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
gpu_thread_weak_ptr_factory_.GetWeakPtr(), surface_handle_,
gl::GLSurfaceFormat());
DCHECK(surface_);
if (!gpu_service_->CreateGrContextIfNecessary(surface_.get())) {
LOG(FATAL) << "Failed to create GrContext";
// TODO(penghuang): handle the failure.
}
DCHECK(gpu_service_->context_for_skia());
DCHECK(gpu_service_->gr_context());
if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): Handle the failure.
}
capabilities_.flipped_output_surface = surface_->FlipsVertically();
// Get stencil bits from the default frame buffer.
auto* current_gl = gpu_service_->context_for_skia()->GetCurrentGL();
const auto* version = current_gl->Version;
auto* api = current_gl->Api;
GLint stencil_bits = 0;
if (version->is_desktop_core_profile) {
api->glGetFramebufferAttachmentParameterivEXTFn(
GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
&stencil_bits);
} else {
api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
}
capabilities_.supports_stencil = stencil_bits > 0;
}
void SkiaOutputSurfaceImpl::DestroyOnGpuThread(base::WaitableEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
base::ScopedClosureRunner scoped_runner(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
gpu_thread_weak_ptr_factory_.InvalidateWeakPtrs();
// Destroy the surface with the context current, some surface destructors make
// GL calls.
if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): Handle the failure.
}
surface_ = nullptr;
sync_point_client_state_ = nullptr;
}
void SkiaOutputSurfaceImpl::ReshapeOnGpuThread(
const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha,
bool use_stencil,
SkSurfaceCharacterization* characterization,
base::WaitableEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
std::unique_ptr<base::ScopedClosureRunner> scoped_runner;
if (event) {
scoped_runner = std::make_unique<base::ScopedClosureRunner>(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
}
if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): Handle the failure.
}
gl::GLSurface::ColorSpace surface_color_space =
color_space == gfx::ColorSpace::CreateSCRGBLinear()
? gl::GLSurface::ColorSpace::SCRGB_LINEAR
: gl::GLSurface::ColorSpace::UNSPECIFIED;
if (!surface_->Resize(size, device_scale_factor, surface_color_space,
has_alpha)) {
LOG(FATAL) << "Failed to resize.";
// TODO(penghuang): Handle the failure.
}
DCHECK(gpu_service_->context_for_skia()->IsCurrent(surface_.get()));
DCHECK(gpu_service_->gr_context());
SkSurfaceProps surface_props =
SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
GrGLFramebufferInfo framebuffer_info;
framebuffer_info.fFBOID = 0;
const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
framebuffer_info.fFormat = version_info->is_es ? GL_BGRA8_EXT : GL_RGBA8;
GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
framebuffer_info);
sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
gpu_service_->gr_context(), render_target, kBottomLeft_GrSurfaceOrigin,
kBGRA_8888_SkColorType, nullptr, &surface_props);
DCHECK(sk_surface_);
if (characterization) {
sk_surface_->characterize(characterization);
DCHECK(characterization->isValid());
}
}
void SkiaOutputSurfaceImpl::SwapBuffersOnGpuThread(
OutputSurfaceFrame frame,
std::unique_ptr<SkDeferredDisplayList> ddl,
uint64_t sync_fence_release) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
DCHECK(ddl);
DCHECK(sk_surface_);
if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): Handle the failure.
}
sk_surface_->draw(ddl.get());
gpu_service_->gr_context()->flush();
surface_->SwapBuffers(
base::BindRepeating([](const gfx::PresentationFeedback&) {}));
sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
}
void SkiaOutputSurfaceImpl::RecreateRecorder() {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(characterization_.isValid());
recorder_ =
std::make_unique<SkDeferredDisplayListRecorder>(characterization_);
// TODO(penghuang): remove the unnecessary getCanvas() call, when the recorder
// crash is fixed in skia.
recorder_->getCanvas();
}
void SkiaOutputSurfaceImpl::DidSwapBuffersCompleteOnClientThread(
gpu::SwapBuffersCompleteParams params) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(client_);
if (!params.texture_in_use_responses.empty())
client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses);
if (!params.ca_layer_params.is_empty)
client_->DidReceiveCALayerParams(params.ca_layer_params);
client_->DidReceiveSwapBuffersAck(params.swap_response.swap_id);
}
void SkiaOutputSurfaceImpl::UpdateVSyncParametersOnClientThread(
base::TimeTicks timebase,
base::TimeDelta interval) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
if (synthetic_begin_frame_source_) {
// TODO(brianderson): We should not be receiving 0 intervals.
synthetic_begin_frame_source_->OnUpdateVSyncParameters(
timebase,
interval.is_zero() ? BeginFrameArgs::DefaultInterval() : interval);
}
}
void SkiaOutputSurfaceImpl::BufferPresentedOnClientThread(
uint64_t swap_id,
const gfx::PresentationFeedback& feedback) {
DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
DCHECK(client_);
client_->DidReceivePresentationFeedback(swap_id, feedback);
}
// static
void SkiaOutputSurfaceImpl::PromiseTextureFullfillStub(
void* texture_context,
GrBackendTexture* backend_texture) {
DCHECK(texture_context);
auto* info = static_cast<PromiseTextureInfo*>(texture_context);
info->skia_renderer->OnPromiseTextureFullfill(info->resource_metadata,
backend_texture);
}
// static
void SkiaOutputSurfaceImpl::PromiseTextureReleaseStub(void* texture_context) {
DCHECK(texture_context);
auto* info = static_cast<PromiseTextureInfo*>(texture_context);
info->skia_renderer->OnPromiseTextureRelease(info->resource_metadata);
}
// static
void SkiaOutputSurfaceImpl::PromiseTextureDoneStub(void* texture_context) {
DCHECK(texture_context);
std::unique_ptr<PromiseTextureInfo> info(
static_cast<PromiseTextureInfo*>(texture_context));
}
void SkiaOutputSurfaceImpl::OnPromiseTextureFullfill(
const ResourceMetadata& metadata,
GrBackendTexture* backend_texture) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
auto* mailbox_manager = gpu_service_->mailbox_manager();
auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
if (!texture_base) {
DLOG(ERROR) << "Failed to full fill the promise texture.";
return;
}
GrGLTextureInfo texture_info;
texture_info.fTarget = texture_base->target();
texture_info.fID = texture_base->service_id();
// TODO(penghuang): Get the format correctly.
texture_info.fFormat = GL_RGBA8;
*backend_texture =
GrBackendTexture(metadata.size.width(), metadata.size.height(),
metadata.mip_mapped, texture_info);
}
void SkiaOutputSurfaceImpl::OnPromiseTextureRelease(
const ResourceMetadata& metadata) {
DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
}
} // namespace viz