blob: 18a7adc8925ff7bf90c4143c564458e7ea9f9008 [file] [log] [blame]
// Copyright 2017 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/software_output_surface.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/task/common/task_annotator.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
#include "build/build_config.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/software_output_device.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/swap_result.h"
#include "ui/gfx/vsync_provider.h"
#include "ui/latency/latency_info.h"
namespace viz {
SoftwareOutputSurface::SoftwareOutputSurface(
std::unique_ptr<SoftwareOutputDevice> device)
: OutputSurface(std::move(device)) {
capabilities_.pending_swap_params.max_pending_swaps =
software_device()->MaxFramesPending();
capabilities_.resize_based_on_root_surface =
software_device()->SupportsOverridePlatformSize();
}
SoftwareOutputSurface::~SoftwareOutputSurface() = default;
void SoftwareOutputSurface::BindToClient(OutputSurfaceClient* client) {
DCHECK(client);
DCHECK(!client_);
client_ = client;
}
void SoftwareOutputSurface::EnsureBackbuffer() {
software_device()->EnsureBackbuffer();
}
void SoftwareOutputSurface::DiscardBackbuffer() {
software_device()->DiscardBackbuffer();
}
void SoftwareOutputSurface::Reshape(const ReshapeParams& params) {
software_device()->Resize(params.size, params.device_scale_factor);
}
void SoftwareOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
DCHECK(client_);
base::TimeTicks swap_time = base::TimeTicks::Now();
for (auto& latency : frame.latency_info) {
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, swap_time);
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, swap_time);
}
TRACE_EVENT(
"viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
perfetto::Flow::Global(frame.data.swap_trace_id),
[swap_trace_id = frame.data.swap_trace_id](perfetto::EventContext ctx) {
base::TaskAnnotator::EmitTaskTimingDetails(ctx);
auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
auto* data = event->set_chrome_graphics_pipeline();
data->set_step(perfetto::protos::pbzero::ChromeGraphicsPipeline::
StepName::STEP_BUFFER_SWAP_POST_SUBMIT);
data->set_display_trace_id(swap_trace_id);
});
software_device()->OnSwapBuffers(
base::BindOnce(&SoftwareOutputSurface::SwapBuffersCallback,
weak_factory_.GetWeakPtr(), swap_time,
frame.data.swap_trace_id),
frame.data);
gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider();
if (vsync_provider && update_vsync_parameters_callback_) {
vsync_provider->GetVSyncParameters(
base::BindOnce(&SoftwareOutputSurface::UpdateVSyncParameters,
weak_factory_.GetWeakPtr()));
}
}
void SoftwareOutputSurface::SwapBuffersCallback(base::TimeTicks swap_time,
int64_t swap_trace_id,
const gfx::Size& pixel_size) {
TRACE_EVENT(
"viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
perfetto::Flow::Global(swap_trace_id), [&](perfetto::EventContext ctx) {
base::TaskAnnotator::EmitTaskTimingDetails(ctx);
auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
auto* data = event->set_chrome_graphics_pipeline();
data->set_step(perfetto::protos::pbzero::ChromeGraphicsPipeline::
StepName::STEP_FINISH_BUFFER_SWAP);
data->set_display_trace_id(swap_trace_id);
});
gpu::SwapBuffersCompleteParams params;
params.swap_response.timings = {swap_time, swap_time};
params.swap_response.result = gfx::SwapResult::SWAP_ACK;
params.swap_trace_id = swap_trace_id;
client_->DidReceiveSwapBuffersAck(params,
/*release_fence=*/gfx::GpuFenceHandle());
base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta interval_to_next_refresh =
now.SnappedToNextTick(refresh_timebase_, refresh_interval_) - now;
#if BUILDFLAG(IS_LINUX)
if (needs_swap_size_notifications_)
client_->DidSwapWithSize(pixel_size);
#endif
client_->DidReceivePresentationFeedback(
gfx::PresentationFeedback(now, interval_to_next_refresh, 0u));
}
void SoftwareOutputSurface::UpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) {
DCHECK(update_vsync_parameters_callback_);
refresh_timebase_ = timebase;
// We should not be receiving 0 intervals.
refresh_interval_ =
interval.is_zero() ? BeginFrameArgs::DefaultInterval() : interval;
update_vsync_parameters_callback_.Run(timebase, refresh_interval_);
}
void SoftwareOutputSurface::SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) {
update_vsync_parameters_callback_ = std::move(callback);
}
gfx::OverlayTransform SoftwareOutputSurface::GetDisplayTransform() {
return gfx::OVERLAY_TRANSFORM_NONE;
}
#if BUILDFLAG(IS_LINUX)
void SoftwareOutputSurface::SetNeedsSwapSizeNotifications(
bool needs_swap_size_notifications) {
needs_swap_size_notifications_ = needs_swap_size_notifications;
}
#endif
} // namespace viz