1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
|
// 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 "ash/fast_ink/fast_ink_host.h"
#include <algorithm>
#include <memory>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/fast_ink/fast_ink_host_frame_utils.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/common/shared_image_capabilities.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/video_types.h"
namespace ash {
// -----------------------------------------------------------------------------
// FastInkHost::ScopedPaint
FastInkHost::ScopedPaint::ScopedPaint(const FastInkHost* host,
const gfx::Rect& damage_rect_in_window)
: host_(const_cast<FastInkHost*>(host)),
damage_rect_(host->BufferRectFromWindowRect(damage_rect_in_window)),
canvas_(damage_rect_.size(), /*image_scale*/ 1.0f, /*is_opaque*/ false) {
canvas_.Translate(-damage_rect_.OffsetFromOrigin());
canvas_.Transform(host->window_to_buffer_transform_);
}
FastInkHost::ScopedPaint::~ScopedPaint() {
if (damage_rect_.IsEmpty()) {
return;
}
host_->Draw(canvas_.GetBitmap(), damage_rect_);
}
// -----------------------------------------------------------------------------
// FastInkHost:
FastInkHost::FastInkHost() = default;
FastInkHost::~FastInkHost() {
ResetGpuBuffer();
}
void FastInkHost::Init(aura::Window* host_window) {
InitBufferMetadata(host_window);
FrameSinkHost::Init(host_window);
}
void FastInkHost::InitForTesting(aura::Window* host_window,
FrameSinkFactory frame_sink_factory) {
InitBufferMetadata(host_window);
FrameSinkHost::InitForTesting(host_window, std::move(frame_sink_factory));
}
std::unique_ptr<FastInkHost::ScopedPaint> FastInkHost::CreateScopedPaint(
const gfx::Rect& damage_rect_in_window) const {
return std::make_unique<ScopedPaint>(this, damage_rect_in_window);
}
std::unique_ptr<viz::CompositorFrame> FastInkHost::CreateCompositorFrame(
const viz::BeginFrameAck& begin_frame_ack,
UiResourceManager& resource_manager,
bool auto_update,
const gfx::Size& last_submitted_frame_size,
float last_submitted_frame_dsf) {
TRACE_EVENT1("ui", "FastInkHost::SubmitCompositorFrame", "damage",
GetTotalDamage().ToString());
CHECK(client_shared_image_);
auto frame = fast_ink_internal::CreateCompositorFrame(
begin_frame_ack, GetContentRect(), GetTotalDamage(), auto_update,
*host_window(), buffer_size_, &resource_manager, client_shared_image_,
sync_token_);
ResetDamage();
return frame;
}
void FastInkHost::OnFirstFrameRequested() {
CHECK(!client_shared_image_);
InitializeFastInkBuffer(host_window());
}
void FastInkHost::OnFrameSinkLost() {
// The fast ink buffer becomes unusable the GPU crashes, which is one of the
// most common causes of FrameSink loss. A new buffer will be created once
// `OnFirstFrameRequested()` will be called.
ResetGpuBuffer();
FrameSinkHost::OnFrameSinkLost();
}
void FastInkHost::InitBufferMetadata(aura::Window* host_window) {
// Take the root transform and apply this during buffer update instead of
// leaving this up to the compositor. The benefit is that HW requirements
// for being able to take advantage of overlays and direct scanout are
// reduced significantly. Frames are submitted to the compositor with the
// inverse transform to cancel out the transformation that would otherwise
// be done by the compositor.
window_to_buffer_transform_ = host_window->GetHost()->GetRootTransform();
gfx::Rect bounds(host_window->GetBoundsInScreen().size());
// TODO(oshima): Make this eplison default.
constexpr float kEpsilon = 0.001f;
buffer_size_ = cc::MathUtil::MapEnclosingClippedRectIgnoringError(
window_to_buffer_transform_, bounds, kEpsilon)
.size();
}
void FastInkHost::InitializeFastInkBuffer(aura::Window* host_window) {
// Create a single mappable SharedImage. Content will be written into this
// SharedImage without any buffering. The result is that we might be modifying
// the underlying memory while it's being displayed. This provides minimal
// latency but with potential tearing. Note that to avoid flicker, we draw
// into a temporary surface and copy it into the mappable SI (see the
// DrawBitmap() method below).
context_provider_ = fast_ink_internal::GetContextProvider();
gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface();
// This SharedImage will be used by the display compositor, will be updated
// in parallel with being read, and will potentially be used in overlays.
gpu::SharedImageUsageSet usage =
gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
if (sii->GetCapabilities().supports_scanout_shared_images) {
usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
}
CHECK(!client_shared_image_) << "GPU buffer is already initialized";
client_shared_image_ = fast_ink_internal::CreateMappableSharedImage(
buffer_size_, usage, gfx::BufferUsage::SCANOUT_CPU_READ_WRITE);
LOG_IF(ERROR, !client_shared_image_) << "Failed to create MappableSI";
sync_token_ = sii->GenVerifiedSyncToken();
if (switches::ShouldClearFastInkBuffer()) {
std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> mapping;
if (client_shared_image_) {
mapping = client_shared_image_->Map();
}
LOG_IF(ERROR, !mapping) << "Failed to map MappableSI";
if (mapping) {
gfx::Size size = mapping->Size();
int stride = mapping->Stride(0);
// Clear the buffer before usage, since it may be uninitialized.
// (http://b/168735625)
for (int i = 0; i < size.height(); ++i) {
auto row_span = mapping->GetMemoryForPlane(0).subspan(
base::checked_cast<size_t>(i * stride),
base::checked_cast<size_t>(size.width() * 4));
std::ranges::fill(row_span, 0);
}
}
}
// Draw pending bitmaps to the buffer.
for (auto pending_bitmap : pending_bitmaps_) {
DrawBitmap(pending_bitmap.bitmap, pending_bitmap.damage_rect);
}
pending_bitmaps_.clear();
}
gfx::Rect FastInkHost::BufferRectFromWindowRect(
const gfx::Rect& rect_in_window) const {
return fast_ink_internal::BufferRectFromWindowRect(
window_to_buffer_transform_, buffer_size_, rect_in_window);
}
void FastInkHost::Draw(SkBitmap bitmap, const gfx::Rect& damage_rect) {
const bool initialized = client_shared_image_ != nullptr;
if (!initialized) {
// GPU process should be ready soon after start and `pending_bitmaps_`
// should be drawn promptly. 60 is an arbitrary cap that should never
// hit.
DCHECK_LT(pending_bitmaps_.size(), 60u);
pending_bitmaps_.push_back(PendingBitmap(bitmap, damage_rect));
return;
}
DrawBitmap(bitmap, damage_rect);
}
void FastInkHost::DrawBitmap(SkBitmap bitmap, const gfx::Rect& damage_rect) {
std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> mapping;
{
// TODO(zoraiznaeem): Investigate the precision as we will get non trivial
// additional time of printing the error log.
TRACE_EVENT0("ui", "FastInkHost::ScopedPaint::Map");
mapping = client_shared_image_->Map();
if (!mapping) {
LOG(ERROR) << "Failed to map MappableSI";
return;
}
}
// Copy result to the buffer. This is effectively a memcpy and unlike
// drawing to the buffer directly this ensures that the buffer is never in a
// state that would result in flicker.
{
TRACE_EVENT1("ui", "FastInkHost::ScopedPaint::Copy", "damage_rect",
damage_rect.ToString());
const int stride = mapping->Stride(0);
auto buffer_span = mapping->GetMemoryForPlane(0);
size_t offset = base::checked_cast<size_t>(damage_rect.y() * stride +
damage_rect.x() * 4);
auto write_span = buffer_span.subspan(offset);
bitmap.readPixels(
SkImageInfo::MakeN32Premul(damage_rect.width(), damage_rect.height()),
write_span.data(), stride, 0, 0);
}
{
TRACE_EVENT0("ui", "FastInkHost::UpdateBuffer::Unmap");
}
}
void FastInkHost::ResetGpuBuffer() {
if (client_shared_image_) {
CHECK(context_provider_);
context_provider_->SharedImageInterface()->DestroySharedImage(
sync_token_, std::move(client_shared_image_));
client_shared_image_.reset();
sync_token_.Clear();
}
}
} // namespace ash
|