blob: bd6ed6f890654535ad52b906ac5b774f1ebc0d37 [file] [log] [blame]
// Copyright 2019 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_device.h"
#include <utility>
#include "base/bind.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "components/viz/service/display/dc_layer_overlay.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
#include "third_party/skia/include/core/SkDeferredDisplayList.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/gpu_fence.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/latency/latency_tracker.h"
namespace viz {
namespace {
using ::perfetto::protos::pbzero::ChromeLatencyInfo;
scoped_refptr<base::SequencedTaskRunner> CreateLatencyTracerRunner() {
if (!base::ThreadPoolInstance::Get())
return nullptr;
return base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
}
void ReportLatency(const gfx::SwapTimings& timings,
ui::LatencyTracker* tracker,
std::vector<ui::LatencyInfo> latency_info,
bool top_controls_visible_height_changed) {
for (auto& latency : latency_info) {
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, timings.swap_start);
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, timings.swap_end);
}
tracker->OnGpuSwapBuffersCompleted(std::move(latency_info),
top_controls_visible_height_changed);
}
} // namespace
SkiaOutputDevice::ScopedPaint::ScopedPaint(
std::vector<GrBackendSemaphore> end_semaphores,
SkiaOutputDevice* device,
SkSurface* sk_surface)
: end_semaphores_(std::move(end_semaphores)),
device_(device),
sk_surface_(sk_surface) {}
SkiaOutputDevice::ScopedPaint::~ScopedPaint() {
DCHECK(end_semaphores_.empty());
device_->EndPaint();
}
SkCanvas* SkiaOutputDevice::ScopedPaint::GetCanvas() {
return device_->GetCanvas(sk_surface_);
}
GrSemaphoresSubmitted SkiaOutputDevice::ScopedPaint::Flush(
VulkanContextProvider* vulkan_context_provider,
std::vector<GrBackendSemaphore> end_semaphores,
base::OnceClosure on_finished) {
return device_->Flush(sk_surface_, vulkan_context_provider,
std::move(end_semaphores), std::move(on_finished));
}
bool SkiaOutputDevice::ScopedPaint::Wait(
int num_semaphores,
const GrBackendSemaphore wait_semaphores[],
bool delete_semaphores_after_wait) {
return device_->Wait(sk_surface_, num_semaphores, wait_semaphores,
delete_semaphores_after_wait);
}
bool SkiaOutputDevice::ScopedPaint::Draw(
sk_sp<const SkDeferredDisplayList> ddl) {
return device_->Draw(sk_surface_, std::move(ddl));
}
SkiaOutputDevice::SkiaOutputDevice(
GrDirectContext* gr_context,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
: gr_context_(gr_context),
did_swap_buffer_complete_callback_(
std::move(did_swap_buffer_complete_callback)),
memory_type_tracker_(
std::make_unique<gpu::MemoryTypeTracker>(memory_tracker)),
latency_tracker_(std::make_unique<ui::LatencyTracker>()),
latency_tracker_runner_(CreateLatencyTracerRunner()) {
DCHECK(gr_context);
capabilities_.max_render_target_size = gr_context->maxRenderTargetSize();
}
SkiaOutputDevice::~SkiaOutputDevice() {
if (latency_tracker_runner_)
latency_tracker_runner_->DeleteSoon(FROM_HERE, std::move(latency_tracker_));
}
std::unique_ptr<SkiaOutputDevice::ScopedPaint>
SkiaOutputDevice::BeginScopedPaint() {
std::vector<GrBackendSemaphore> end_semaphores;
SkSurface* sk_surface = BeginPaint(&end_semaphores);
if (!sk_surface) {
return nullptr;
}
return std::make_unique<SkiaOutputDevice::ScopedPaint>(
std::move(end_semaphores), this, sk_surface);
}
void SkiaOutputDevice::Submit(bool sync_cpu, base::OnceClosure callback) {
gr_context_->submit(sync_cpu);
std::move(callback).Run();
}
void SkiaOutputDevice::CommitOverlayPlanes(BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
NOTREACHED();
}
void SkiaOutputDevice::PostSubBuffer(const gfx::Rect& rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
NOTREACHED();
}
bool SkiaOutputDevice::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
NOTREACHED();
return false;
}
void SkiaOutputDevice::SetEnableDCLayers(bool enable) {
NOTREACHED();
}
void SkiaOutputDevice::SetGpuVSyncEnabled(bool enabled) {
NOTREACHED();
}
bool SkiaOutputDevice::IsPrimaryPlaneOverlay() const {
return false;
}
void SkiaOutputDevice::SchedulePrimaryPlane(
const base::Optional<OverlayProcessorInterface::OutputSurfaceOverlayPlane>&
plane) {
if (plane)
NOTIMPLEMENTED();
}
void SkiaOutputDevice::ScheduleOverlays(
SkiaOutputSurface::OverlayList overlays) {
NOTIMPLEMENTED();
}
void SkiaOutputDevice::EnsureBackbuffer() {}
void SkiaOutputDevice::DiscardBackbuffer() {}
void SkiaOutputDevice::SetDrawTimings(base::TimeTicks submitted,
base::TimeTicks started) {
viz_scheduled_draw_ = submitted;
gpu_started_draw_ = started;
}
void SkiaOutputDevice::SetDependencyTimings(base::TimeTicks task_ready) {
gpu_task_ready_ = task_ready;
}
void SkiaOutputDevice::StartSwapBuffers(BufferPresentedCallback feedback) {
DCHECK_LT(static_cast<int>(pending_swaps_.size()),
capabilities_.max_frames_pending);
pending_swaps_.emplace(++swap_id_, std::move(feedback), viz_scheduled_draw_,
gpu_started_draw_, gpu_task_ready_);
viz_scheduled_draw_ = base::TimeTicks();
gpu_started_draw_ = base::TimeTicks();
gpu_task_ready_ = base::TimeTicks();
}
void SkiaOutputDevice::FinishSwapBuffers(
gfx::SwapCompletionResult result,
const gfx::Size& size,
OutputSurfaceFrame frame,
const base::Optional<gfx::Rect>& damage_area,
std::vector<gpu::Mailbox> released_overlays,
const gpu::Mailbox& primary_plane_mailbox) {
DCHECK(!pending_swaps_.empty());
auto release_fence = std::move(result.release_fence);
const gpu::SwapBuffersCompleteParams& params =
pending_swaps_.front().Complete(std::move(result), damage_area,
std::move(released_overlays),
primary_plane_mailbox);
did_swap_buffer_complete_callback_.Run(params, size,
std::move(release_fence));
pending_swaps_.front().CallFeedback();
if (latency_tracker_runner_) {
// Report latency off GPU main thread, but we still want this to be counted
// as part of the critical flow so emit a flow step.
ui::LatencyInfo::TraceIntermediateFlowEvents(
frame.latency_info, ChromeLatencyInfo::STEP_FINISHED_SWAP_BUFFERS);
latency_tracker_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ReportLatency, params.swap_response.timings,
latency_tracker_.get(), std::move(frame.latency_info),
frame.top_controls_visible_height_changed));
} else {
ReportLatency(params.swap_response.timings, latency_tracker_.get(),
std::move(frame.latency_info),
frame.top_controls_visible_height_changed);
}
pending_swaps_.pop();
}
SkiaOutputDevice::SwapInfo::SwapInfo(
uint64_t swap_id,
SkiaOutputDevice::BufferPresentedCallback feedback,
base::TimeTicks viz_scheduled_draw,
base::TimeTicks gpu_started_draw,
base::TimeTicks gpu_task_ready)
: feedback_(std::move(feedback)) {
params_.swap_response.swap_id = swap_id;
params_.swap_response.timings.swap_start = base::TimeTicks::Now();
params_.swap_response.timings.viz_scheduled_draw = viz_scheduled_draw;
params_.swap_response.timings.gpu_started_draw = gpu_started_draw;
params_.swap_response.timings.gpu_task_ready = gpu_task_ready;
}
SkiaOutputDevice::SwapInfo::SwapInfo(SwapInfo&& other) = default;
SkiaOutputDevice::SwapInfo::~SwapInfo() = default;
const gpu::SwapBuffersCompleteParams& SkiaOutputDevice::SwapInfo::Complete(
gfx::SwapCompletionResult result,
const base::Optional<gfx::Rect>& damage_rect,
std::vector<gpu::Mailbox> released_overlays,
const gpu::Mailbox& primary_plane_mailbox) {
params_.swap_response.result = result.swap_result;
params_.swap_response.timings.swap_end = base::TimeTicks::Now();
params_.frame_buffer_damage_area = damage_rect;
if (result.ca_layer_params)
params_.ca_layer_params = *result.ca_layer_params;
params_.primary_plane_mailbox = primary_plane_mailbox;
params_.released_overlays = std::move(released_overlays);
return params_;
}
void SkiaOutputDevice::SwapInfo::CallFeedback() {
if (feedback_) {
uint32_t flags = 0;
if (params_.swap_response.result != gfx::SwapResult::SWAP_ACK)
flags = gfx::PresentationFeedback::Flags::kFailure;
std::move(feedback_).Run(
gfx::PresentationFeedback(params_.swap_response.timings.swap_start,
/*interval=*/base::TimeDelta(), flags));
}
}
SkCanvas* SkiaOutputDevice::GetCanvas(SkSurface* sk_surface) {
return sk_surface->getCanvas();
}
GrSemaphoresSubmitted SkiaOutputDevice::Flush(
SkSurface* sk_surface,
VulkanContextProvider* vulkan_context_provider,
std::vector<GrBackendSemaphore> end_semaphores,
base::OnceClosure on_finished) {
GrFlushInfo flush_info = {
.fNumSemaphores = end_semaphores.size(),
.fSignalSemaphores = end_semaphores.data(),
};
gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider, &flush_info);
if (on_finished)
gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info);
return sk_surface->flush(flush_info);
}
bool SkiaOutputDevice::Wait(SkSurface* sk_surface,
int num_semaphores,
const GrBackendSemaphore wait_semaphores[],
bool delete_semaphores_after_wait) {
return sk_surface->wait(num_semaphores, wait_semaphores,
delete_semaphores_after_wait);
}
bool SkiaOutputDevice::Draw(SkSurface* sk_surface,
sk_sp<const SkDeferredDisplayList> ddl) {
#if DCHECK_IS_ON()
const auto& characterization = ddl->characterization();
if (!sk_surface->isCompatible(characterization)) {
SkSurfaceCharacterization surface_characterization;
DCHECK(sk_surface->characterize(&surface_characterization));
#define CHECK_PROPERTY(name) \
DCHECK_EQ(characterization.name(), surface_characterization.name());
CHECK_PROPERTY(cacheMaxResourceBytes);
CHECK_PROPERTY(origin);
CHECK_PROPERTY(width);
CHECK_PROPERTY(height);
CHECK_PROPERTY(colorType);
CHECK_PROPERTY(sampleCount);
CHECK_PROPERTY(isTextureable);
CHECK_PROPERTY(isMipMapped);
CHECK_PROPERTY(usesGLFBO0);
CHECK_PROPERTY(vkRTSupportsInputAttachment);
CHECK_PROPERTY(vulkanSecondaryCBCompatible);
CHECK_PROPERTY(isProtected);
}
#endif
return sk_surface->draw(ddl);
}
} // namespace viz