blob: 133ed5be9fd22380ec420e75625a820e62c1689c [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_buffer_queue.h"
#include <iterator>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/cxx20_erase.h"
#include "base/debug/alias.h"
#include "base/notreached.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
#include "components/viz/common/switches.h"
#include "components/viz/service/display/overlay_candidate.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "gpu/command_buffer/common/capabilities.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/config/gpu_finch_features.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/gpu_fence_handle.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_image.h"
#include "ui/gl/gl_surface.h"
namespace {
base::TimeTicks g_last_reshape_failure = base::TimeTicks();
NOINLINE void CheckForLoopFailuresBufferQueue() {
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
namespace viz {
class SkiaOutputDeviceBufferQueue::OverlayData {
public:
OverlayData() = default;
OverlayData(std::unique_ptr<gpu::OverlayImageRepresentation> representation,
std::unique_ptr<gpu::OverlayImageRepresentation::ScopedReadAccess>
scoped_read_access)
: representation_(std::move(representation)),
scoped_read_access_(std::move(scoped_read_access)),
ref_(1) {
DCHECK(representation_);
DCHECK(scoped_read_access_);
}
OverlayData(OverlayData&& other) { *this = std::move(other); }
~OverlayData() { Reset(); }
OverlayData& operator=(OverlayData&& other) {
DCHECK(!IsInUseByWindowServer());
DCHECK(!ref_);
DCHECK(!scoped_read_access_);
DCHECK(!representation_);
scoped_read_access_ = std::move(other.scoped_read_access_);
representation_ = std::move(other.representation_);
ref_ = other.ref_;
other.ref_ = 0;
return *this;
}
bool IsInUseByWindowServer() const {
if (!scoped_read_access_)
return false;
auto* gl_image = scoped_read_access_->gl_image();
if (!gl_image)
return false;
return gl_image->IsInUseByWindowServer();
}
void Ref() { ++ref_; }
void Unref() {
DCHECK_GT(ref_, 0);
if (ref_ > 1) {
--ref_;
} else if (ref_ == 1) {
DCHECK(!IsInUseByWindowServer());
Reset();
}
}
void OnContextLost() { representation_->OnContextLost(); }
bool unique() const { return ref_ == 1; }
const gpu::Mailbox& mailbox() const { return representation_->mailbox(); }
gpu::OverlayImageRepresentation::ScopedReadAccess* scoped_read_access()
const {
return scoped_read_access_.get();
}
private:
void Reset() {
scoped_read_access_.reset();
representation_.reset();
ref_ = 0;
}
std::unique_ptr<gpu::OverlayImageRepresentation> representation_;
std::unique_ptr<gpu::OverlayImageRepresentation::ScopedReadAccess>
scoped_read_access_;
int ref_ = 0;
};
SkiaOutputDeviceBufferQueue::SkiaOutputDeviceBufferQueue(
std::unique_ptr<OutputPresenter> presenter,
SkiaOutputSurfaceDependency* deps,
gpu::SharedImageRepresentationFactory* representation_factory,
gpu::MemoryTracker* memory_tracker,
const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
bool needs_background_image,
bool supports_non_backed_solid_color_images)
: SkiaOutputDevice(deps->GetSharedContextState()->gr_context(),
memory_tracker,
did_swap_buffer_complete_callback),
presenter_(std::move(presenter)),
context_state_(deps->GetSharedContextState()),
representation_factory_(representation_factory),
needs_background_image_(needs_background_image),
supports_non_backed_solid_color_images_(
supports_non_backed_solid_color_images) {
capabilities_.uses_default_gl_framebuffer = false;
capabilities_.preserve_buffer_content = true;
capabilities_.only_invalidates_damage_rect = false;
capabilities_.number_of_buffers = 3;
#if BUILDFLAG(IS_ANDROID)
capabilities_.renderer_allocates_images = true;
if (::features::IncreaseBufferCountForHighFrameRate()) {
capabilities_.number_of_buffers = 5;
}
#endif
capabilities_.orientation_mode = OutputSurface::OrientationMode::kHardware;
// Force the number of max pending frames to one when the switch
// "double-buffer-compositing" is passed.
// This will keep compositing in double buffered mode assuming |buffer_queue|
// allocates at most one additional buffer.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kDoubleBufferCompositing))
capabilities_.number_of_buffers = 2;
capabilities_.pending_swap_params.max_pending_swaps =
capabilities_.number_of_buffers - 1;
#if BUILDFLAG(IS_ANDROID)
if (::features::IncreaseBufferCountForHighFrameRate() &&
capabilities_.number_of_buffers == 5) {
capabilities_.pending_swap_params.max_pending_swaps = 2;
capabilities_.pending_swap_params.max_pending_swaps_90hz = 3;
capabilities_.pending_swap_params.max_pending_swaps_120hz = 4;
}
#endif
DCHECK_LT(capabilities_.pending_swap_params.max_pending_swaps,
capabilities_.number_of_buffers);
DCHECK_LT(
capabilities_.pending_swap_params.max_pending_swaps_90hz.value_or(0),
capabilities_.number_of_buffers);
DCHECK_LT(
capabilities_.pending_swap_params.max_pending_swaps_120hz.value_or(0),
capabilities_.number_of_buffers);
presenter_->InitializeCapabilities(&capabilities_);
if (capabilities_.supports_post_sub_buffer)
capabilities_.supports_target_damage = true;
}
SkiaOutputDeviceBufferQueue::~SkiaOutputDeviceBufferQueue() {
// TODO(vasilyt): We should not need this when we stop using
// GLImageBacking.
if (context_state_->context_lost()) {
for (auto& overlay : overlays_) {
overlay.OnContextLost();
}
for (auto& image : images_) {
image->OnContextLost();
}
}
FreeAllSurfaces();
// Clear and cancel swap_completion_callbacks_ to free all resource bind to
// callbacks.
swap_completion_callbacks_.clear();
}
OutputPresenter::Image* SkiaOutputDeviceBufferQueue::GetNextImage() {
DCHECK(!capabilities_.renderer_allocates_images);
CHECK(!available_images_.empty());
auto* image = available_images_.front();
available_images_.pop_front();
return image;
}
void SkiaOutputDeviceBufferQueue::PageFlipComplete(
OutputPresenter::Image* image,
gfx::GpuFenceHandle release_fence) {
if (displayed_image_) {
DCHECK(!capabilities_.renderer_allocates_images);
DCHECK_EQ(displayed_image_->skia_representation()->size(), image_size_);
DCHECK_EQ(displayed_image_->GetPresentCount() > 1,
displayed_image_ == image);
// MakeCurrent is necessary for inserting release fences and for
// BeginWriteSkia below.
context_state_->MakeCurrent(/*surface=*/nullptr);
displayed_image_->EndPresent(std::move(release_fence));
if (!displayed_image_->GetPresentCount()) {
available_images_.push_back(displayed_image_);
// Call BeginWriteSkia() for the next frame here to avoid some expensive
// operations on the critical code path.
if (!available_images_.front()->sk_surface()) {
// BeginWriteSkia() may alter GL's state.
context_state_->set_need_context_state_reset(true);
available_images_.front()->BeginWriteSkia(sample_count_);
}
}
}
displayed_image_ = image;
swap_completion_callbacks_.pop_front();
}
void SkiaOutputDeviceBufferQueue::FreeAllSurfaces() {
images_.clear();
current_image_ = nullptr;
submitted_image_ = nullptr;
displayed_image_ = nullptr;
available_images_.clear();
primary_plane_waiting_on_paint_ = true;
}
bool SkiaOutputDeviceBufferQueue::IsPrimaryPlaneOverlay() const {
return true;
}
void SkiaOutputDeviceBufferQueue::SchedulePrimaryPlane(
const absl::optional<OverlayProcessorInterface::OutputSurfaceOverlayPlane>&
plane) {
// See |needs_background_image|.
MaybeScheduleBackgroundImage();
if (plane) {
DCHECK(!capabilities_.renderer_allocates_images);
// If the current_image_ is nullptr, it means there is no change on the
// primary plane. So we just need to schedule the last submitted image.
auto* image =
current_image_ ? current_image_.get() : submitted_image_.get();
// |image| can be null if there was a fullscreen overlay last frame (e.g.
// no primary plane). If the fullscreen quad suddenly fails the fullscreen
// overlay check this frame (e.g. TestPageFlip failing) and then gets
// promoted via a different strategy like single-on-top, the quad's damage
// is still removed from the primary plane's damage. With no damage, we
// never invoke |BeginPaint| which initializes a new image. Since there
// still really isn't any primary plane content, it's fine to early-exit.
if (!image && primary_plane_waiting_on_paint_)
return;
DCHECK(image);
image->BeginPresent();
presenter_->SchedulePrimaryPlane(plane.value(), image,
image == submitted_image_);
} else {
primary_plane_waiting_on_paint_ = true;
current_frame_has_no_primary_plane_ = true;
// Even if there is no primary plane, |current_image_| may be non-null if
// an overlay just transitioned from an underlay strategy to a fullscreen
// strategy (e.g. a the media controls disappearing on a fullscreen video).
// In this case, there is still damage which triggers a render pass, but
// since we promote via fullscreen, we remove the primary plane in the end.
// We need to recycle |current_image_| to avoid a use-after-free error.
if (current_image_) {
available_images_.push_back(current_image_);
current_image_ = nullptr;
}
}
}
#if defined(USE_OZONE)
const gpu::Mailbox SkiaOutputDeviceBufferQueue::GetImageMailboxForColor(
const SkColor4f& color) {
// Currently the Wayland protocol does not have protocol to support solid
// color quads natively as surfaces. Here we create tiny 4x4 image buffers
// in the color space of the frame buffer and clear them to the quad's solid
// color. These freshly created buffers are then treated like any other
// overlay via the mailbox interface.
std::unique_ptr<OutputPresenter::Image> solid_color = nullptr;
// First try for an existing same color image.
auto it = solid_color_cache_.find(color.toSkColor());
if (it != solid_color_cache_.end()) {
// This is a prefect color match so use this directly.
solid_color = std::move(it->second);
solid_color_cache_.erase(it);
} else {
// Try to reuse an existing image even if the color is different.
// Only do this if there are more cached images than those in flight (a
// sensible upper bound).
if (!solid_color_cache_.empty() &&
solid_color_cache_.size() > solid_color_images_.size()) {
auto it = solid_color_cache_.begin();
solid_color = std::move(it->second);
solid_color_cache_.erase(it);
} else {
// Worst case allocate a new image. This definitely will occur on startup.
solid_color =
presenter_->AllocateSingleImage(color_space_, gfx::Size(4, 4));
}
solid_color->BeginWriteSkia(/*sample_count=*/1);
solid_color->sk_surface()->getCanvas()->clear(color);
solid_color->EndWriteSkia(/*force_flush=*/true);
}
DCHECK(solid_color);
auto image_mailbox = solid_color->skia_representation()->mailbox();
solid_color_images_.insert(std::make_pair(
image_mailbox,
std::make_pair(color.toSkColor(), std::move(solid_color))));
return image_mailbox;
}
#endif
SkiaOutputDeviceBufferQueue::OverlayData*
SkiaOutputDeviceBufferQueue::GetOrCreateOverlayData(const gpu::Mailbox& mailbox,
bool* is_existing) {
if (is_existing)
*is_existing = false;
if (!mailbox.IsSharedImage())
return nullptr;
auto it = overlays_.find(mailbox);
if (it != overlays_.end()) {
// If the overlay is in |overlays_|, we will reuse it, and a ref will be
// added to keep it alive. This ref will be removed, when the overlay is
// replaced by a new frame.
it->Ref();
if (is_existing)
*is_existing = true;
return &*it;
}
auto shared_image = representation_factory_->ProduceOverlay(mailbox);
// When display is re-opened, the first few frames might not have video
// resource ready. Possible investigation crbug.com/1023971.
if (!shared_image) {
LOG(ERROR) << "Invalid mailbox.";
return nullptr;
}
// Fuchsia does not provide a GLImage overlay.
#if BUILDFLAG(IS_FUCHSIA)
const bool needs_gl_image = false;
#else
const bool needs_gl_image = true;
#endif // BUILDFLAG(IS_FUCHSIA)
// TODO(penghuang): do not depend on GLImage.
auto shared_image_access =
shared_image->BeginScopedReadAccess(needs_gl_image);
if (!shared_image_access) {
LOG(ERROR) << "Could not access SharedImage for read.";
return nullptr;
}
// TODO(penghuang): do not depend on GLImage.
DLOG_IF(FATAL, needs_gl_image && !shared_image_access->gl_image())
<< "Cannot get GLImage.";
bool result;
std::tie(it, result) = overlays_.emplace(std::move(shared_image),
std::move(shared_image_access));
DCHECK(result);
DCHECK(it->unique());
// Add an extra ref to keep it alive. This extra ref will be removed when
// the backing is not used by system compositor anymore.
it->Ref();
return &*it;
}
void SkiaOutputDeviceBufferQueue::ScheduleOverlays(
SkiaOutputSurface::OverlayList overlays) {
DCHECK(pending_overlay_mailboxes_.empty());
// The fence that will be created for current ScheduleOverlays. This fence is
// required and passed with overlay data iff DelegatedCompositing is enabled
// and the overlay's shared image backing is created for raster op. Given
// rasterization tasks create fences when gpu operations are issued, we end up
// having multiple number of fences, which creation is costly. Instead, a
// single fence is created during overlays' scheduling, which is dupped and
// inserted into each OverlayPlaneData if the underlying shared image was
// created for rasterization.
//
// TODO(msisov): find a better place for this fence.
std::unique_ptr<gfx::GpuFence> current_frame_fence;
for (const auto& overlay : overlays) {
auto mailbox = overlay.mailbox;
#if defined(USE_OZONE)
if (overlay.is_solid_color) {
DCHECK(overlay.color.has_value());
// TODO(msisov): reconsider this once Linux Wayland compositors also
// support that. See https://bit.ly/2ZqUO0w.
if (!supports_non_backed_solid_color_images_) {
mailbox = GetImageMailboxForColor(overlay.color.value());
} else {
presenter_->ScheduleOverlayPlane(overlay, nullptr, nullptr);
continue;
}
}
#endif
OutputPresenter::ScopedOverlayAccess* access = nullptr;
bool overlay_has_been_submitted;
auto* overlay_data =
GetOrCreateOverlayData(mailbox, &overlay_has_been_submitted);
if (overlay_data) {
access = overlay_data->scoped_read_access();
pending_overlay_mailboxes_.emplace_back(mailbox);
}
std::unique_ptr<gfx::GpuFence> acquire_fence;
if (context_state_->GrContextIsGL() && access &&
!overlay_has_been_submitted &&
(access->representation()->usage() &
gpu::SHARED_IMAGE_USAGE_RASTER_DELEGATED_COMPOSITING) &&
gl::GLFence::IsGpuFenceSupported()) {
DCHECK(features::IsDelegatedCompositingEnabled());
// Create a single fence that will be duplicated and inserted into each
// overlay plane data. This avoids unnecessary cost as creating multiple
// number of fences at the end of each raster task at the ShareImage
// level is costly. Thus, at this point, the gpu tasks have been
// dispatched and it's safe to create just a single fence.
if (!current_frame_fence)
current_frame_fence = gl::GLFence::CreateForGpuFence()->GetGpuFence();
// Dup the fence - it must be inserted into each shared image before
// ScopedReadAccess is created.
acquire_fence = std::make_unique<gfx::GpuFence>(
current_frame_fence->GetGpuFenceHandle().Clone());
}
presenter_->ScheduleOverlayPlane(overlay, access, std::move(acquire_fence));
}
}
void SkiaOutputDeviceBufferQueue::Submit(bool sync_cpu,
base::OnceClosure callback) {
// The current image may be missing, for example during WebXR presentation.
// The SkSurface may also be missing due to a rare edge case (seen at ~1CPM
// on CrOS)- if we end up skipping the swap for a frame and don't have
// damage in the next frame (e.g.fullscreen overlay),
// |current_image_->BeginWriteSkia| won't get called before |Submit|. In
// this case, we shouldn't call |PreGrContextSubmit| since there's no active
// surface to flush.
if (current_image_ && current_image_->sk_surface())
current_image_->PreGrContextSubmit();
SkiaOutputDevice::Submit(sync_cpu, std::move(callback));
}
void SkiaOutputDeviceBufferQueue::SwapBuffers(BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
StartSwapBuffers({});
if (current_frame_has_no_primary_plane_) {
DCHECK(!current_image_);
submitted_image_ = nullptr;
current_frame_has_no_primary_plane_ = false;
} else {
DCHECK(current_image_);
submitted_image_ = current_image_;
current_image_ = nullptr;
}
// Cancelable callback uses weak ptr to drop this task upon destruction.
// Thus it is safe to use |base::Unretained(this)|.
// Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could
// be released due to reshape() or destruction.
swap_completion_callbacks_.emplace_back(
std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce(
&SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
base::Unretained(this), GetSwapBuffersSize(), std::move(frame),
submitted_image_ ? submitted_image_->GetWeakPtr() : nullptr,
std::move(committed_overlay_mailboxes_))));
committed_overlay_mailboxes_.clear();
presenter_->SwapBuffers(swap_completion_callbacks_.back()->callback(),
std::move(feedback));
std::swap(committed_overlay_mailboxes_, pending_overlay_mailboxes_);
}
void SkiaOutputDeviceBufferQueue::PostSubBuffer(
const gfx::Rect& rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
StartSwapBuffers({});
if (current_frame_has_no_primary_plane_) {
DCHECK(!current_image_);
submitted_image_ = nullptr;
current_frame_has_no_primary_plane_ = false;
} else {
if (current_image_) {
submitted_image_ = current_image_;
current_image_ = nullptr;
}
DCHECK(submitted_image_);
}
#if BUILDFLAG(IS_MAC)
presenter_->SetCALayerErrorCode(frame.ca_layer_error_code);
#endif
// Cancelable callback uses weak ptr to drop this task upon destruction.
// Thus it is safe to use |base::Unretained(this)|.
// Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could
// be released due to reshape() or destruction.
swap_completion_callbacks_.emplace_back(
std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce(
&SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
base::Unretained(this), GetSwapBuffersSize(), std::move(frame),
submitted_image_ ? submitted_image_->GetWeakPtr() : nullptr,
std::move(committed_overlay_mailboxes_))));
committed_overlay_mailboxes_.clear();
presenter_->PostSubBuffer(rect, swap_completion_callbacks_.back()->callback(),
std::move(feedback));
std::swap(committed_overlay_mailboxes_, pending_overlay_mailboxes_);
}
void SkiaOutputDeviceBufferQueue::CommitOverlayPlanes(
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
StartSwapBuffers({});
// There is no drawing for this frame on the main buffer.
DCHECK(!current_image_);
if (current_frame_has_no_primary_plane_) {
submitted_image_ = nullptr;
current_frame_has_no_primary_plane_ = false;
} else {
DCHECK(submitted_image_);
}
// Cancelable callback uses weak ptr to drop this task upon destruction.
// Thus it is safe to use |base::Unretained(this)|.
// Bind submitted_image_->GetWeakPtr(), since the |submitted_image_| could
// be released due to reshape() or destruction.
swap_completion_callbacks_.emplace_back(
std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce(
&SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
base::Unretained(this), GetSwapBuffersSize(), std::move(frame),
submitted_image_ ? submitted_image_->GetWeakPtr() : nullptr,
std::move(committed_overlay_mailboxes_))));
committed_overlay_mailboxes_.clear();
presenter_->CommitOverlayPlanes(swap_completion_callbacks_.back()->callback(),
std::move(feedback));
std::swap(committed_overlay_mailboxes_, pending_overlay_mailboxes_);
}
void SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers(
const gfx::Size& size,
OutputSurfaceFrame frame,
const base::WeakPtr<OutputPresenter::Image>& image,
std::vector<gpu::Mailbox> overlay_mailboxes,
gfx::SwapCompletionResult result) {
// |overlay_mailboxes| are for overlays used by previous frame, they should
// have been replaced.
for (const auto& mailbox : overlay_mailboxes) {
auto it = overlays_.find(mailbox);
DCHECK(it != overlays_.end());
if (!result.release_fence.is_null())
it->scoped_read_access()->SetReleaseFence(result.release_fence.Clone());
it->Unref();
}
#if defined(USE_OZONE)
std::set<gpu::Mailbox> released_solid_color_overlays;
for (const auto& mailbox : overlay_mailboxes) {
auto it = solid_color_images_.find(mailbox);
if (it != solid_color_images_.end()) {
released_solid_color_overlays.insert(mailbox);
solid_color_cache_.insert(
std::make_pair(it->second.first, std::move(it->second.second)));
solid_color_images_.erase(it);
}
}
#endif
// Code below can destroy last representation of the overlay shared image. On
// MacOS it needs context to be current.
#if BUILDFLAG(IS_APPLE)
// TODO(vasilyt): We shouldn't need this after we stop using
// GLImageBacking as backing.
if (!context_state_->MakeCurrent(nullptr)) {
for (auto& overlay : overlays_) {
overlay.OnContextLost();
}
}
#endif
std::vector<gpu::Mailbox> released_overlays;
auto on_overlay_release =
#if BUILDFLAG(IS_APPLE)
[&released_overlays](const OverlayData& overlay) {
// Right now, only macOS needs to return maliboxes of released
// overlays, so SkiaRenderer can unlock resources for them.
released_overlays.push_back(overlay.mailbox());
};
#elif defined(USE_OZONE)
[&released_overlays,
&released_solid_color_overlays](const OverlayData& overlay) {
// Delegated compositing on Ozone needs to return mailboxes of released
// overlays, so SkiaRenderer can unlock resources for them. However, the
// solid color buffers originating in this class and should not
// propagate up to SkiaRenderer.
if (released_solid_color_overlays.find(overlay.mailbox()) ==
released_solid_color_overlays.end()) {
released_overlays.push_back(overlay.mailbox());
}
};
#else
[](const OverlayData& overlay) {};
#endif
// Go through backings of all overlays, and release overlay backings which are
// not used.
base::EraseIf(overlays_, [&on_overlay_release](auto& overlay) {
if (!overlay.unique())
return false;
if (overlay.IsInUseByWindowServer())
return false;
on_overlay_release(overlay);
overlay.Unref();
return true;
});
bool should_reallocate =
result.swap_result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS;
const auto& mailbox =
image ? image->skia_representation()->mailbox() : gpu::Mailbox();
auto release_fence = result.release_fence.Clone();
FinishSwapBuffers(std::move(result), size, std::move(frame),
/*damage_area=*/absl::nullopt, std::move(released_overlays),
mailbox);
PageFlipComplete(image.get(), std::move(release_fence));
if (should_reallocate)
RecreateImages();
}
gfx::Size SkiaOutputDeviceBufferQueue::GetSwapBuffersSize() {
switch (overlay_transform_) {
case gfx::OVERLAY_TRANSFORM_ROTATE_90:
case gfx::OVERLAY_TRANSFORM_ROTATE_270:
return gfx::Size(image_size_.height(), image_size_.width());
case gfx::OVERLAY_TRANSFORM_INVALID:
case gfx::OVERLAY_TRANSFORM_NONE:
case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL:
case gfx::OVERLAY_TRANSFORM_ROTATE_180:
return image_size_;
}
}
bool SkiaOutputDeviceBufferQueue::Reshape(
const SkSurfaceCharacterization& characterization,
const gfx::ColorSpace& color_space,
float device_scale_factor,
gfx::OverlayTransform transform) {
DCHECK(pending_overlay_mailboxes_.empty());
if (!presenter_->Reshape(characterization, color_space, device_scale_factor,
transform)) {
LOG(ERROR) << "Failed to resize.";
CheckForLoopFailuresBufferQueue();
// To prevent tail call, so we can see the stack.
base::debug::Alias(nullptr);
return false;
}
overlay_transform_ = transform;
gfx::Size size = gfx::SkISizeToSize(characterization.dimensions());
if (color_space_ == color_space && image_size_ == size)
return true;
color_space_ = color_space;
image_size_ = size;
sample_count_ = characterization.sampleCount();
bool success = RecreateImages();
if (!success) {
CheckForLoopFailuresBufferQueue();
// To prevent tail call, so we can see the stack.
base::debug::Alias(nullptr);
}
return success;
}
void SkiaOutputDeviceBufferQueue::SetViewportSize(
const gfx::Size& viewport_size) {
viewport_size_ = viewport_size;
}
bool SkiaOutputDeviceBufferQueue::RecreateImages() {
if (capabilities_.renderer_allocates_images) {
return true;
}
FreeAllSurfaces();
size_t number_to_allocate =
capabilities_.supports_dynamic_frame_buffer_allocation
? number_of_images_to_allocate_
: capabilities_.number_of_buffers;
if (!number_to_allocate)
return true;
images_ =
presenter_->AllocateImages(color_space_, image_size_, number_to_allocate);
for (auto& image : images_) {
available_images_.push_back(image.get());
}
DCHECK(images_.empty() || images_.size() == number_to_allocate);
return !images_.empty();
}
void SkiaOutputDeviceBufferQueue::MaybeScheduleBackgroundImage() {
if (!needs_background_image_)
return;
gpu::OverlayImageRepresentation::ScopedReadAccess* access = nullptr;
OutputPresenter::OverlayPlaneCandidate candidate;
#if defined(USE_OZONE)
candidate.color_space = color_space_;
candidate.display_rect = gfx::RectF(gfx::SizeF(viewport_size_));
candidate.color = SkColors::kTransparent;
candidate.plane_z_order = INT32_MIN;
candidate.is_solid_color = supports_non_backed_solid_color_images_;
if (!supports_non_backed_solid_color_images_) {
auto mailbox = GetImageMailboxForColor(candidate.color.value());
DCHECK(mailbox.IsSharedImage());
auto* overlay_data = GetOrCreateOverlayData(mailbox);
DCHECK(overlay_data);
access = overlay_data->scoped_read_access();
pending_overlay_mailboxes_.emplace_back(mailbox);
}
#else // defined(USE_OZONE)
NOTREACHED();
#endif // !defined(USE_OZONE)
presenter_->ScheduleOverlayPlane(candidate, access,
/*acquire_fence=*/nullptr);
}
SkSurface* SkiaOutputDeviceBufferQueue::BeginPaint(
std::vector<GrBackendSemaphore>* end_semaphores) {
DCHECK(!capabilities_.renderer_allocates_images);
primary_plane_waiting_on_paint_ = false;
if (!current_image_) {
current_image_ = GetNextImage();
}
if (!current_image_->sk_surface())
current_image_->BeginWriteSkia(sample_count_);
*end_semaphores = current_image_->TakeEndWriteSkiaSemaphores();
return current_image_->sk_surface();
}
void SkiaOutputDeviceBufferQueue::EndPaint() {
DCHECK(!capabilities_.renderer_allocates_images);
DCHECK(current_image_);
current_image_->EndWriteSkia();
}
bool SkiaOutputDeviceBufferQueue::EnsureMinNumberOfBuffers(size_t n) {
DCHECK(!capabilities_.renderer_allocates_images);
DCHECK(capabilities_.supports_dynamic_frame_buffer_allocation);
DCHECK_GT(n, 0u);
DCHECK_LE(n, static_cast<size_t>(capabilities_.number_of_buffers));
if (number_of_images_to_allocate_ >= n)
return true;
number_of_images_to_allocate_ = n;
if (image_size_.IsEmpty())
return true;
return RecreateImages();
}
bool SkiaOutputDeviceBufferQueue::OverlayDataComparator::operator()(
const OverlayData& lhs,
const OverlayData& rhs) const {
return lhs.mailbox() < rhs.mailbox();
}
bool SkiaOutputDeviceBufferQueue::OverlayDataComparator::operator()(
const OverlayData& lhs,
const gpu::Mailbox& rhs) const {
return lhs.mailbox() < rhs;
}
bool SkiaOutputDeviceBufferQueue::OverlayDataComparator::operator()(
const gpu::Mailbox& lhs,
const OverlayData& rhs) const {
return lhs < rhs.mailbox();
}
} // namespace viz