blob: 2e06d978df84358e1db9b704d4bdf275fbfbfd60 [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 <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_util.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/service/display/dc_layer_overlay.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/image_context_impl.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/ipc/single_task_sequence.h"
#include "gpu/vulkan/buildflags.h"
#include "ui/gfx/skia_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_gl_api_implementation.h"
#if BUILDFLAG(ENABLE_VULKAN)
#include "components/viz/common/gpu/vulkan_context_provider.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#endif // BUILDFLAG(ENABLE_VULKAN)
namespace viz {
namespace {
sk_sp<SkPromiseImageTexture> Fulfill(void* texture_context) {
DCHECK(texture_context);
auto* image_context = static_cast<ImageContextImpl*>(texture_context);
return sk_ref_sp(image_context->promise_image_texture());
}
void DoNothing(void* texture_context) {}
template <typename... Args>
void PostAsyncTask(SkiaOutputSurfaceDependency* dependency,
const base::RepeatingCallback<void(Args...)>& callback,
Args... args) {
dependency->PostTaskToClientThread(base::BindOnce(callback, args...));
}
template <typename... Args>
base::RepeatingCallback<void(Args...)> CreateSafeCallback(
SkiaOutputSurfaceDependency* dependency,
const base::RepeatingCallback<void(Args...)>& callback) {
DCHECK(dependency);
return base::BindRepeating(&PostAsyncTask<Args...>, dependency, callback);
}
} // namespace
SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
SkDeferredDisplayListRecorder* root_recorder)
: recorder_(root_recorder), render_pass_id_(0) {}
SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
SkSurfaceCharacterization characterization,
RenderPassId render_pass_id)
: render_pass_id_(render_pass_id) {
recorder_storage_.emplace(characterization);
recorder_ = &recorder_storage_.value();
}
SkiaOutputSurfaceImpl::ScopedPaint::~ScopedPaint() = default;
// static
std::unique_ptr<SkiaOutputSurface> SkiaOutputSurfaceImpl::Create(
std::unique_ptr<SkiaOutputSurfaceDependency> deps,
const RendererSettings& renderer_settings) {
auto output_surface = std::make_unique<SkiaOutputSurfaceImpl>(
util::PassKey<SkiaOutputSurfaceImpl>(), std::move(deps),
renderer_settings);
if (!output_surface->Initialize())
output_surface = nullptr;
return output_surface;
}
SkiaOutputSurfaceImpl::SkiaOutputSurfaceImpl(
util::PassKey<SkiaOutputSurfaceImpl> /* pass_key */,
std::unique_ptr<SkiaOutputSurfaceDependency> deps,
const RendererSettings& renderer_settings)
: dependency_(std::move(deps)),
is_using_vulkan_(dependency_->IsUsingVulkan()),
renderer_settings_(renderer_settings) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
SkiaOutputSurfaceImpl::~SkiaOutputSurfaceImpl() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
current_paint_.reset();
root_recorder_.reset();
std::vector<std::unique_ptr<ImageContextImpl>> render_pass_image_contexts;
render_pass_image_contexts.reserve(render_pass_image_cache_.size());
for (auto& id_and_image_context : render_pass_image_cache_) {
id_and_image_context.second->clear_image();
render_pass_image_contexts.push_back(
std::move(id_and_image_context.second));
}
base::WaitableEvent event;
auto callback = base::BindOnce(
[](std::vector<std::unique_ptr<ImageContextImpl>> render_passes,
std::unique_ptr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
base::WaitableEvent* event) {
if (!render_passes.empty())
impl_on_gpu->RemoveRenderPassResource(std::move(render_passes));
impl_on_gpu = nullptr;
event->Signal();
},
std::move(render_pass_image_contexts), std::move(impl_on_gpu_), &event);
ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
event.Wait();
// Delete task sequence.
task_sequence_ = nullptr;
}
gpu::SurfaceHandle SkiaOutputSurfaceImpl::GetSurfaceHandle() const {
return dependency_->GetSurfaceHandle();
}
void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(client);
DCHECK(!client_);
client_ = client;
}
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(thread_checker_);
DCHECK(capabilities().supports_dc_layers);
DCHECK(!draw_rectangle_);
draw_rectangle_.emplace(draw_rectangle);
}
void SkiaOutputSurfaceImpl::EnsureBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::EnsureBackbuffer,
base::Unretained(impl_on_gpu_.get()));
task_sequence_->ScheduleOrRetainTask(std::move(callback),
std::vector<gpu::SyncToken>());
}
void SkiaOutputSurfaceImpl::DiscardBackbuffer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::DiscardBackbuffer,
base::Unretained(impl_on_gpu_.get()));
task_sequence_->ScheduleOrRetainTask(std::move(callback),
std::vector<gpu::SyncToken>());
}
void SkiaOutputSurfaceImpl::RecreateRootRecorder() {
DCHECK(characterization_.isValid());
root_recorder_.emplace(characterization_);
// This will trigger the lazy initialization of the recorder
ignore_result(root_recorder_->getCanvas());
}
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(thread_checker_);
if (initialize_waitable_event_) {
initialize_waitable_event_->Wait();
initialize_waitable_event_.reset();
}
SkSurfaceCharacterization* characterization = nullptr;
if (characterization_.isValid()) {
sk_sp<SkColorSpace> sk_color_space = color_space.ToSkColorSpace();
if (!SkColorSpace::Equals(characterization_.refColorSpace().get(),
sk_color_space.get())) {
characterization_ = characterization_.createColorSpace(sk_color_space);
}
if (size.width() != characterization_.width() ||
size.height() != characterization_.height()) {
characterization_ =
characterization_.createResized(size.width(), size.height());
}
// TODO(kylechar): Update |characterization_| if |use_alpha| changes.
RecreateRootRecorder();
} else {
characterization = &characterization_;
initialize_waitable_event_ = std::make_unique<base::WaitableEvent>(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
}
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
base::Unretained(impl_on_gpu_.get()), size,
device_scale_factor, color_space, has_alpha,
use_stencil, pre_transform_, characterization,
initialize_waitable_event_.get());
ScheduleGpuTask(std::move(task), {});
}
void SkiaOutputSurfaceImpl::SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) {
update_vsync_parameters_callback_ = std::move(callback);
}
void SkiaOutputSurfaceImpl::SetGpuVSyncEnabled(bool enabled) {
auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetGpuVSyncEnabled,
base::Unretained(impl_on_gpu_.get()), enabled);
task_sequence_->ScheduleOrRetainTask(std::move(task), {});
}
void SkiaOutputSurfaceImpl::SetGpuVSyncCallback(GpuVSyncCallback callback) {
gpu_vsync_callback_ = std::move(callback);
}
void SkiaOutputSurfaceImpl::SetDisplayTransformHint(
gfx::OverlayTransform transform) {
if (capabilities_.supports_pre_transform)
pre_transform_ = transform;
}
gfx::OverlayTransform SkiaOutputSurfaceImpl::GetDisplayTransform() {
return pre_transform_;
}
SkCanvas* SkiaOutputSurfaceImpl::BeginPaintCurrentFrame() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
DCHECK(!current_paint_);
if (initialize_waitable_event_) {
initialize_waitable_event_->Wait();
initialize_waitable_event_ = nullptr;
RecreateRootRecorder();
}
DCHECK(root_recorder_);
current_paint_.emplace(&root_recorder_.value());
if (!renderer_settings_.show_overdraw_feedback)
return current_paint_->recorder()->getCanvas();
DCHECK(!overdraw_surface_recorder_);
DCHECK(renderer_settings_.show_overdraw_feedback);
SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization(
gfx::Size(characterization_.width(), characterization_.height()),
BGRA_8888, false /* mipmap */, characterization_.refColorSpace());
overdraw_surface_recorder_.emplace(characterization);
overdraw_canvas_.emplace((overdraw_surface_recorder_->getCanvas()));
nway_canvas_.emplace(characterization_.width(), characterization_.height());
nway_canvas_->addCanvas(current_paint_->recorder()->getCanvas());
nway_canvas_->addCanvas(&overdraw_canvas_.value());
return &nway_canvas_.value();
}
void SkiaOutputSurfaceImpl::MakePromiseSkImage(ImageContext* image_context) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_paint_);
DCHECK(!image_context->mailbox_holder().mailbox.IsZero());
images_in_current_paint_.push_back(
static_cast<ImageContextImpl*>(image_context));
if (image_context->has_image())
return;
SkColorType color_type = ResourceFormatToClosestSkColorType(
true /* gpu_compositing */, image_context->resource_format());
GrBackendFormat backend_format = GetGrBackendFormatForTexture(
image_context->resource_format(),
image_context->mailbox_holder().texture_target,
image_context->ycbcr_info());
image_context->SetImage(
current_paint_->recorder()->makePromiseTexture(
backend_format, image_context->size().width(),
image_context->size().height(), GrMipMapped::kNo,
image_context->origin(), color_type, image_context->alpha_type(),
image_context->color_space(), Fulfill /* fulfillProc */,
DoNothing /* releaseProc */, DoNothing /* doneProc */,
image_context /* context */),
backend_format);
if (image_context->mailbox_holder().sync_token.HasData()) {
resource_sync_tokens_.push_back(image_context->mailbox_holder().sync_token);
image_context->mutable_mailbox_holder()->sync_token.Clear();
}
}
sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromYUV(
const std::vector<ImageContext*>& contexts,
SkYUVColorSpace yuv_color_space,
sk_sp<SkColorSpace> dst_color_space,
bool has_alpha) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_paint_);
DCHECK((has_alpha && (contexts.size() == 3 || contexts.size() == 4)) ||
(!has_alpha && (contexts.size() == 2 || contexts.size() == 3)));
SkYUVAIndex indices[4];
PrepareYUVATextureIndices(contexts, has_alpha, indices);
GrBackendFormat formats[4] = {};
SkISize yuva_sizes[4] = {};
SkDeferredDisplayListRecorder::PromiseImageTextureContext
texture_contexts[4] = {};
for (size_t i = 0; i < contexts.size(); ++i) {
auto* context = static_cast<ImageContextImpl*>(contexts[i]);
DCHECK(context->origin() == kTopLeft_GrSurfaceOrigin);
formats[i] = GetGrBackendFormatForTexture(
context->resource_format(), context->mailbox_holder().texture_target,
/*ycbcr_info=*/base::nullopt);
yuva_sizes[i].set(context->size().width(), context->size().height());
// NOTE: We don't have promises for individual planes, but still need format
// for fallback
context->SetImage(nullptr, formats[i]);
if (context->mailbox_holder().sync_token.HasData()) {
resource_sync_tokens_.push_back(context->mailbox_holder().sync_token);
context->mutable_mailbox_holder()->sync_token.Clear();
}
images_in_current_paint_.push_back(context);
texture_contexts[i] = context;
}
auto image = current_paint_->recorder()->makeYUVAPromiseTexture(
yuv_color_space, formats, yuva_sizes, indices, yuva_sizes[0].width(),
yuva_sizes[0].height(), kTopLeft_GrSurfaceOrigin, dst_color_space,
Fulfill, DoNothing, DoNothing, texture_contexts);
DCHECK(image);
return image;
}
void SkiaOutputSurfaceImpl::ReleaseImageContexts(
std::vector<std::unique_ptr<ImageContext>> image_contexts) {
if (image_contexts.empty())
return;
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto callback = base::BindOnce(
&SkiaOutputSurfaceImplOnGpu::ReleaseImageContexts,
base::Unretained(impl_on_gpu_.get()), std::move(image_contexts));
task_sequence_->ScheduleOrRetainTask(std::move(callback),
std::vector<gpu::SyncToken>());
}
std::unique_ptr<ExternalUseClient::ImageContext>
SkiaOutputSurfaceImpl::CreateImageContext(
const gpu::MailboxHolder& holder,
const gfx::Size& size,
ResourceFormat format,
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info,
sk_sp<SkColorSpace> color_space) {
return std::make_unique<ImageContextImpl>(holder, size, format, ycbcr_info,
std::move(color_space));
}
gpu::SyncToken SkiaOutputSurfaceImpl::SkiaSwapBuffers(OutputSurfaceFrame frame,
bool wants_sync_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!current_paint_);
gpu::SyncToken sync_token;
if (wants_sync_token) {
sync_token = gpu::SyncToken(
gpu::CommandBufferNamespace::VIZ_SKIA_OUTPUT_SURFACE,
impl_on_gpu_->command_buffer_id(), ++sync_fence_release_);
sync_token.SetVerifyFlush();
}
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto callback =
base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SwapBuffers,
base::Unretained(impl_on_gpu_.get()), std::move(frame),
std::move(deferred_framebuffer_draw_closure_),
sync_token.release_count());
ScheduleGpuTask(std::move(callback), std::move(resource_sync_tokens_));
// Recreate |root_recorder_| after SwapBuffers has been scheduled on GPU
// thread to save some time in BeginPaintCurrentFrame
// TODO(vasilyt): reuse root recorder
RecreateRootRecorder();
return sync_token;
}
void SkiaOutputSurfaceImpl::ScheduleOutputSurfaceAsOverlay(
OverlayProcessor::OutputSurfaceOverlayPlane output_surface_plane) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
auto callback = base::BindOnce(
&SkiaOutputSurfaceImplOnGpu::ScheduleOutputSurfaceAsOverlay,
base::Unretained(impl_on_gpu_.get()), std::move(output_surface_plane));
ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
}
SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
const RenderPassId& id,
const gfx::Size& surface_size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
DCHECK(!current_paint_);
DCHECK(resource_sync_tokens_.empty());
SkSurfaceCharacterization c = CreateSkSurfaceCharacterization(
surface_size, format, mipmap, std::move(color_space));
current_paint_.emplace(c, id);
return current_paint_->recorder()->getCanvas();
}
gpu::SyncToken SkiaOutputSurfaceImpl::SubmitPaint(
base::OnceClosure on_finished) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_paint_);
DCHECK(!deferred_framebuffer_draw_closure_);
// If current_render_pass_id_ is not 0, we are painting a render pass.
// Otherwise we are painting a frame.
bool painting_render_pass = current_paint_->render_pass_id() != 0;
gpu::SyncToken sync_token(
gpu::CommandBufferNamespace::VIZ_SKIA_OUTPUT_SURFACE,
impl_on_gpu_->command_buffer_id(), ++sync_fence_release_);
sync_token.SetVerifyFlush();
auto ddl = current_paint_->recorder()->detach();
DCHECK(ddl);
std::unique_ptr<SkDeferredDisplayList> overdraw_ddl;
if (renderer_settings_.show_overdraw_feedback && !painting_render_pass) {
overdraw_ddl = overdraw_surface_recorder_->detach();
DCHECK(overdraw_ddl);
overdraw_canvas_.reset();
nway_canvas_.reset();
overdraw_surface_recorder_.reset();
}
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
if (painting_render_pass) {
auto it = render_pass_image_cache_.find(current_paint_->render_pass_id());
if (it != render_pass_image_cache_.end()) {
// We are going to overwrite the render pass, so we need reset the
// image_context, so a new promise image will be created when the
// MakePromiseSkImageFromRenderPass() is called.
it->second->clear_image();
}
DCHECK(!on_finished);
auto closure = base::BindOnce(
&SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
base::Unretained(impl_on_gpu_.get()), current_paint_->render_pass_id(),
std::move(ddl), std::move(images_in_current_paint_),
resource_sync_tokens_, sync_fence_release_);
ScheduleGpuTask(std::move(closure), std::move(resource_sync_tokens_));
} else {
deferred_framebuffer_draw_closure_ = base::BindOnce(
&SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame,
base::Unretained(impl_on_gpu_.get()), std::move(ddl),
std::move(overdraw_ddl), std::move(images_in_current_paint_),
resource_sync_tokens_, sync_fence_release_, std::move(on_finished),
draw_rectangle_);
draw_rectangle_.reset();
}
images_in_current_paint_.clear();
current_paint_.reset();
return sync_token;
}
sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromRenderPass(
const RenderPassId& id,
const gfx::Size& size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_paint_);
auto& image_context = render_pass_image_cache_[id];
if (!image_context) {
image_context = std::make_unique<ImageContextImpl>(id, size, format, mipmap,
std::move(color_space));
}
if (!image_context->has_image()) {
SkColorType color_type =
ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
GrBackendFormat backend_format = GetGrBackendFormatForTexture(
format, GL_TEXTURE_2D, /*ycbcr_info=*/base::nullopt);
image_context->SetImage(
current_paint_->recorder()->makePromiseTexture(
backend_format, image_context->size().width(),
image_context->size().height(), image_context->mipmap(),
image_context->origin(), color_type, image_context->alpha_type(),
image_context->color_space(), Fulfill, DoNothing, DoNothing,
image_context.get()),
backend_format);
DCHECK(image_context->has_image());
}
images_in_current_paint_.push_back(image_context.get());
return image_context->image();
}
void SkiaOutputSurfaceImpl::RemoveRenderPassResource(
std::vector<RenderPassId> ids) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!ids.empty());
std::vector<std::unique_ptr<ImageContextImpl>> image_contexts;
image_contexts.reserve(ids.size());
for (const auto id : ids) {
auto it = render_pass_image_cache_.find(id);
// If the render pass was only used for a copy request, there won't be a
// matching entry in |render_pass_image_cache_|.
if (it != render_pass_image_cache_.end()) {
it->second->clear_image();
image_contexts.push_back(std::move(it->second));
render_pass_image_cache_.erase(it);
}
}
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
if (!image_contexts.empty()) {
auto callback = base::BindOnce(
&SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource,
base::Unretained(impl_on_gpu_.get()), std::move(image_contexts));
ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
}
}
void SkiaOutputSurfaceImpl::CopyOutput(
RenderPassId id,
const copy_output::RenderPassGeometry& geometry,
const gfx::ColorSpace& color_space,
std::unique_ptr<CopyOutputRequest> request) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!request->has_result_task_runner())
request->set_result_task_runner(base::ThreadTaskRunnerHandle::Get());
auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::CopyOutput,
base::Unretained(impl_on_gpu_.get()), id,
geometry, color_space, std::move(request),
std::move(deferred_framebuffer_draw_closure_));
ScheduleGpuTask(std::move(callback), std::move(resource_sync_tokens_));
}
void SkiaOutputSurfaceImpl::SetEnableDCLayers(bool enable) {
auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers,
base::Unretained(impl_on_gpu_.get()), enable);
ScheduleGpuTask(std::move(task), {});
}
void SkiaOutputSurfaceImpl::ScheduleDCLayers(
std::vector<DCLayerOverlay> overlays) {
auto task =
base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleDCLayers,
base::Unretained(impl_on_gpu_.get()), std::move(overlays));
ScheduleGpuTask(std::move(task), {});
}
void SkiaOutputSurfaceImpl::SetCapabilitiesForTesting(
bool flipped_output_surface) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(impl_on_gpu_);
capabilities_.flipped_output_surface = flipped_output_surface;
auto callback =
base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetCapabilitiesForTesting,
base::Unretained(impl_on_gpu_.get()), capabilities_);
ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
}
bool SkiaOutputSurfaceImpl::Initialize() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Before start scheduling GPU task, set up |task_sequence_|.
task_sequence_ = dependency_->CreateSequence();
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
bool result = false;
auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::InitializeOnGpuThread,
base::Unretained(this), &event, &result);
ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
event.Wait();
return result;
}
void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event,
bool* result) {
base::Optional<base::ScopedClosureRunner> scoped_runner;
if (event) {
scoped_runner.emplace(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
}
auto did_swap_buffer_complete_callback = CreateSafeCallback(
dependency_.get(),
base::BindRepeating(&SkiaOutputSurfaceImpl::DidSwapBuffersComplete,
weak_ptr_));
auto buffer_presented_callback = CreateSafeCallback(
dependency_.get(),
base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_));
auto context_lost_callback = CreateSafeCallback(
dependency_.get(),
base::BindRepeating(&SkiaOutputSurfaceImpl::ContextLost, weak_ptr_));
auto gpu_vsync_callback = CreateSafeCallback(
dependency_.get(),
base::BindRepeating(&SkiaOutputSurfaceImpl::OnGpuVSync, weak_ptr_));
impl_on_gpu_ = SkiaOutputSurfaceImplOnGpu::Create(
dependency_.get(), renderer_settings_, task_sequence_->GetSequenceId(),
std::move(did_swap_buffer_complete_callback),
std::move(buffer_presented_callback), std::move(context_lost_callback),
std::move(gpu_vsync_callback));
if (!impl_on_gpu_) {
*result = false;
} else {
capabilities_ = impl_on_gpu_->capabilities();
capabilities_.android_surface_control_feature_enabled =
dependency_->GetGpuFeatureInfo()
.status_values[gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] ==
gpu::kGpuFeatureStatusEnabled;
is_displayed_as_overlay_ = impl_on_gpu_->IsDisplayedAsOverlay();
*result = true;
}
}
SkSurfaceCharacterization
SkiaOutputSurfaceImpl::CreateSkSurfaceCharacterization(
const gfx::Size& surface_size,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space) {
auto gr_context_thread_safe = impl_on_gpu_->GetGrContextThreadSafeProxy();
auto cache_max_resource_bytes = impl_on_gpu_->max_resource_cache_bytes();
// LegacyFontHost will get LCD text and skia figures out what type to use.
SkSurfaceProps surface_props(0 /*flags */,
SkSurfaceProps::kLegacyFontHost_InitType);
auto color_type =
ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
auto image_info =
SkImageInfo::Make(surface_size.width(), surface_size.height(), color_type,
kPremul_SkAlphaType, std::move(color_space));
auto backend_format = gr_context_thread_safe->defaultBackendFormat(
color_type, GrRenderable::kYes);
DCHECK(backend_format.isValid());
auto characterization = gr_context_thread_safe->createCharacterization(
cache_max_resource_bytes, image_info, backend_format, 0 /* sampleCount */,
kTopLeft_GrSurfaceOrigin, surface_props, mipmap);
DCHECK(characterization.isValid());
return characterization;
}
void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
gpu::SwapBuffersCompleteParams params,
const gfx::Size& pixel_size) {
DCHECK_CALLED_ON_VALID_THREAD(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.timings);
if (needs_swap_size_notifications_)
client_->DidSwapWithSize(pixel_size);
}
void SkiaOutputSurfaceImpl::BufferPresented(
const gfx::PresentationFeedback& feedback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(client_);
client_->DidReceivePresentationFeedback(feedback);
if (update_vsync_parameters_callback_ &&
feedback.flags & gfx::PresentationFeedback::kVSync) {
// TODO(brianderson): We should not be receiving 0 intervals.
update_vsync_parameters_callback_.Run(
feedback.timestamp, feedback.interval.is_zero()
? BeginFrameArgs::DefaultInterval()
: feedback.interval);
}
}
void SkiaOutputSurfaceImpl::OnGpuVSync(base::TimeTicks timebase,
base::TimeDelta interval) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (gpu_vsync_callback_)
gpu_vsync_callback_.Run(timebase, interval);
}
void SkiaOutputSurfaceImpl::ScheduleGpuTaskForTesting(
base::OnceClosure callback,
std::vector<gpu::SyncToken> sync_tokens) {
ScheduleGpuTask(std::move(callback), std::move(sync_tokens));
}
void SkiaOutputSurfaceImpl::ScheduleGpuTask(
base::OnceClosure callback,
std::vector<gpu::SyncToken> sync_tokens) {
task_sequence_->ScheduleTask(std::move(callback), std::move(sync_tokens));
}
GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture(
ResourceFormat resource_format,
uint32_t gl_texture_target,
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info) {
if (!is_using_vulkan_) {
DCHECK(!ycbcr_info);
// Convert internal format from GLES2 to platform GL.
unsigned int texture_storage_format = gpu::GetGrGLBackendTextureFormat(
impl_on_gpu_->GetFeatureInfo(), resource_format);
return GrBackendFormat::MakeGL(texture_storage_format, gl_texture_target);
} else {
#if BUILDFLAG(ENABLE_VULKAN)
if (!ycbcr_info) {
// YCbCr info is required for YUV images.
DCHECK(resource_format != YVU_420 && resource_format != YUV_420_BIPLANAR);
return GrBackendFormat::MakeVk(ToVkFormat(resource_format));
}
// Assume optimal tiling.
GrVkYcbcrConversionInfo gr_ycbcr_info =
CreateGrVkYcbcrConversionInfo(dependency_->GetVulkanContextProvider()
->GetDeviceQueue()
->GetVulkanPhysicalDevice(),
VK_IMAGE_TILING_OPTIMAL, ycbcr_info);
return GrBackendFormat::MakeVk(gr_ycbcr_info);
#else
NOTREACHED();
return GrBackendFormat();
#endif
}
}
void SkiaOutputSurfaceImpl::SwapBuffers(OutputSurfaceFrame frame) {
NOTREACHED();
}
uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GL_RGB;
}
bool SkiaOutputSurfaceImpl::IsDisplayedAsOverlayPlane() const {
return is_displayed_as_overlay_;
}
unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return 0;
}
gfx::BufferFormat SkiaOutputSurfaceImpl::GetOverlayBufferFormat() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return gfx::BufferFormat::RGBX_8888;
}
bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return false;
}
void SkiaOutputSurfaceImpl::ApplyExternalStencil() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
unsigned SkiaOutputSurfaceImpl::UpdateGpuFence() {
return 0;
}
void SkiaOutputSurfaceImpl::SetNeedsSwapSizeNotifications(
bool needs_swap_size_notifications) {
needs_swap_size_notifications_ = needs_swap_size_notifications;
}
base::ScopedClosureRunner SkiaOutputSurfaceImpl::GetCacheBackBufferCb() {
// TODO(weiliangc) : Add support for this once SkiaRenderer works with
// SurfaceControl.
CHECK(false);
return base::ScopedClosureRunner();
}
void SkiaOutputSurfaceImpl::AddContextLostObserver(
ContextLostObserver* observer) {
observers_.AddObserver(observer);
}
void SkiaOutputSurfaceImpl::RemoveContextLostObserver(
ContextLostObserver* observer) {
observers_.RemoveObserver(observer);
}
void SkiaOutputSurfaceImpl::PrepareYUVATextureIndices(
const std::vector<ImageContext*>& contexts,
bool has_alpha,
SkYUVAIndex indices[4]) {
DCHECK((has_alpha && (contexts.size() == 3 || contexts.size() == 4)) ||
(!has_alpha && (contexts.size() == 2 || contexts.size() == 3)));
bool uv_interleaved = has_alpha ? contexts.size() == 3 : contexts.size() == 2;
indices[SkYUVAIndex::kY_Index].fIndex = 0;
indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR;
if (uv_interleaved) {
indices[SkYUVAIndex::kU_Index].fIndex = 1;
indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR;
indices[SkYUVAIndex::kV_Index].fIndex = 1;
indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kG;
indices[SkYUVAIndex::kA_Index].fIndex = has_alpha ? 2 : -1;
indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;
} else {
indices[SkYUVAIndex::kU_Index].fIndex = 1;
indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR;
indices[SkYUVAIndex::kV_Index].fIndex = 2;
indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kR;
indices[SkYUVAIndex::kA_Index].fIndex = has_alpha ? 3 : -1;
indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;
}
}
void SkiaOutputSurfaceImpl::ContextLost() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto& observer : observers_)
observer.OnContextLost();
}
} // namespace viz