Skip to content

Commit

Permalink
ash: Refactor to use FastInkHost early
Browse files Browse the repository at this point in the history
- Defer gpu buffer creation until receiving first begin frame
  from viz.
- Defer ScopedPaint copy until gpu buffer is created.
- Remove motion blur cursor's five-second workaround.

BUG=b:305603148

Change-Id: I7ebf8e918765d004aff9a7d6de9b3b4dd80a4263
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4939854
Auto-Submit: Yichen Zhou <yichenz@chromium.org>
Reviewed-by: Zoraiz Naeem <zoraiznaeem@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Commit-Queue: Yichen Zhou <yichenz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1211823}
  • Loading branch information
Yichen authored and Chromium LUCI CQ committed Oct 18, 2023
1 parent 58249e7 commit 605336d
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 105 deletions.
1 change: 1 addition & 0 deletions ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3257,6 +3257,7 @@ test("ash_unittests") {
"frame/default_frame_header_unittest.cc",
"frame/non_client_frame_view_ash_unittest.cc",
"frame_sink/frame_sink_holder_unittest.cc",
"frame_sink/frame_sink_host_unittest.cc",
"frame_sink/ui_resource_manager_unittest.cc",
"frame_throttler/frame_throttling_controller_unittest.cc",
"game_dashboard/game_dashboard_context_unittest.cc",
Expand Down
14 changes: 3 additions & 11 deletions ash/display/cursor_window_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,7 @@ CursorWindowController::CursorWindowController()
: delegate_(new CursorWindowDelegate()),
is_cursor_motion_blur_enabled_(
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshEnableCursorMotionBlur)),
// TODO(b/296641218): Find another way to make sure gpu process is fully
// initialized first before updating cursor view.
start_time_(base::TimeTicks::Now()) {}
switches::kAshEnableCursorMotionBlur)) {}

CursorWindowController::~CursorWindowController() {
SetContainer(NULL);
Expand Down Expand Up @@ -292,7 +289,7 @@ void CursorWindowController::SetCursorColor(SkColor cursor_color) {
}

bool CursorWindowController::ShouldEnableCursorCompositing() {
if (CanEnableMotionBlur()) {
if (is_cursor_motion_blur_enabled_) {
return true;
}

Expand Down Expand Up @@ -518,7 +515,7 @@ void CursorWindowController::SetContainer(aura::Window* container) {
bounds_in_screen_ = display_.bounds();
rotation_ = display_.rotation();

if (CanEnableMotionBlur()) {
if (is_cursor_motion_blur_enabled_) {
UpdateCursorView();
} else {
delegate_->SetCursorWindow(nullptr);
Expand Down Expand Up @@ -659,9 +656,4 @@ const gfx::ImageSkia& CursorWindowController::GetCursorImageForTest() const {
return delegate_->cursor_images()[0];
}

bool CursorWindowController::CanEnableMotionBlur() const {
return is_cursor_motion_blur_enabled_ &&
base::TimeTicks::Now() - start_time_ > base::Seconds(5);
}

} // namespace ash
1 change: 0 additions & 1 deletion ash/display/cursor_window_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ class ASH_EXPORT CursorWindowController : public aura::WindowObserver {
views::UniqueWidgetPtr cursor_view_widget_;

const bool is_cursor_motion_blur_enabled_;
base::TimeTicks start_time_;
base::ScopedObservation<aura::Window, aura::WindowObserver>
scoped_container_observer_{this};
};
Expand Down
151 changes: 91 additions & 60 deletions ash/fast_ink/fast_ink_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,9 @@ namespace ash {

FastInkHost::ScopedPaint::ScopedPaint(const FastInkHost* host,
const gfx::Rect& damage_rect_in_window)
: gpu_memory_buffer_(host->gpu_memory_buffer_.get()),
damage_rect_(fast_ink_internal::BufferRectFromWindowRect(
host->window_to_buffer_transform_,
gpu_memory_buffer_->GetSize(),
damage_rect_in_window)),
canvas_(damage_rect_.size(), 1.0f, false) {
: 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_);
}
Expand All @@ -48,38 +45,7 @@ FastInkHost::ScopedPaint::~ScopedPaint() {
if (damage_rect_.IsEmpty()) {
return;
}

{
// TODO(zoraiznaeem): Investigate the precision as we will get non trivial
// additional time of printing the error log.
TRACE_EVENT0("ui", "FastInkHost::ScopedPaint::Map");

if (!gpu_memory_buffer_->Map()) {
LOG(ERROR) << "Failed to map GPU memory buffer";
return;
}
}

// Copy result to GPU memory 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());

uint8_t* data = static_cast<uint8_t*>(gpu_memory_buffer_->memory(0));
int stride = gpu_memory_buffer_->stride(0);
canvas_.GetBitmap().readPixels(
SkImageInfo::MakeN32Premul(damage_rect_.width(), damage_rect_.height()),
data + damage_rect_.y() * stride + damage_rect_.x() * 4, stride, 0, 0);
}

{
TRACE_EVENT0("ui", "FastInkHost::UpdateBuffer::Unmap");

// Unmap to flush writes to buffer.
gpu_memory_buffer_->Unmap();
}
host_->Draw(canvas_.GetBitmap(), damage_rect_);
}

// -----------------------------------------------------------------------------
Expand All @@ -89,18 +55,45 @@ FastInkHost::FastInkHost() = default;
FastInkHost::~FastInkHost() = default;

void FastInkHost::Init(aura::Window* host_window) {
InitializeFastInkBuffer(host_window);
InitBufferMetadata(host_window);
FrameSinkHost::Init(host_window);
}

void FastInkHost::InitForTesting(
aura::Window* host_window,
std::unique_ptr<cc::LayerTreeFrameSink> layer_tree_frame_sink) {
InitializeFastInkBuffer(host_window);
InitBufferMetadata(host_window);
FrameSinkHost::InitForTesting(host_window, std::move(layer_tree_frame_sink));
}

void FastInkHost::InitializeFastInkBuffer(aura::Window* host_window) {
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());

auto frame = fast_ink_internal::CreateCompositorFrame(
begin_frame_ack, GetContentRect(), GetTotalDamage(), auto_update,
*host_window(), gpu_memory_buffer_.get(), &resource_manager);

ResetDamage();

return frame;
}

void FastInkHost::OnFirstFrameRequested() {
InitializeFastInkBuffer(host_window());
}

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
Expand All @@ -109,18 +102,22 @@ void FastInkHost::InitializeFastInkBuffer(aura::Window* host_window) {
// be done by the compositor.
window_to_buffer_transform_ = host_window->GetHost()->GetRootTransform();
gfx::Rect bounds(host_window->GetBoundsInScreen().size());

const gfx::Size buffer_size =
buffer_size_ =
cc::MathUtil::MapEnclosingClippedRect(window_to_buffer_transform_, bounds)
.size();
}

void FastInkHost::InitializeFastInkBuffer(aura::Window* host_window) {
// `gpu_memory_buffer_` should only be initialized once.
DCHECK(!gpu_memory_buffer_);

// Create a single GPU memory buffer. Content will be written into this
// buffer without any buffering. The result is that we might be modifying
// the buffer while it's being displayed. This provides minimal latency
// but with potential tearing. Note that we have to draw into a temporary
// surface and copy it into GPU memory buffer to avoid flicker.
gpu_memory_buffer_ = fast_ink_internal::CreateGpuBuffer(
buffer_size,
buffer_size_,
gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
SK_B32_SHIFT ? gfx::BufferFormat::RGBA_8888
: gfx::BufferFormat::BGRA_8888));
Expand All @@ -141,29 +138,63 @@ void FastInkHost::InitializeFastInkBuffer(aura::Window* host_window) {
}
gpu_memory_buffer_->Unmap();
}

// Draw pending bitmaps to the buffer.
for (auto pending_bitmap : pending_bitmaps_) {
DrawBitmap(pending_bitmap.bitmap, pending_bitmap.damage_rect);
}
pending_bitmaps_.clear();
}

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);
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);
}

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());
void FastInkHost::Draw(SkBitmap bitmap, const gfx::Rect& damage_rect) {
if (!gpu_memory_buffer_) {
// 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);
}

auto frame = fast_ink_internal::CreateCompositorFrame(
begin_frame_ack, GetContentRect(), GetTotalDamage(), auto_update,
*host_window(), gpu_memory_buffer_.get(), &resource_manager);
void FastInkHost::DrawBitmap(SkBitmap bitmap, const gfx::Rect& damage_rect) {
{
// TODO(zoraiznaeem): Investigate the precision as we will get non trivial
// additional time of printing the error log.
TRACE_EVENT0("ui", "FastInkHost::ScopedPaint::Map");

ResetDamage();
if (!gpu_memory_buffer_->Map()) {
LOG(ERROR) << "Failed to map GPU memory buffer";
return;
}
}
// Copy result to GPU memory 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());

return frame;
uint8_t* data = static_cast<uint8_t*>(gpu_memory_buffer_->memory(0));
const int stride = gpu_memory_buffer_->stride(0);
bitmap.readPixels(
SkImageInfo::MakeN32Premul(damage_rect.width(), damage_rect.height()),
data + damage_rect.y() * stride + damage_rect.x() * 4, stride, 0, 0);
}

{
TRACE_EVENT0("ui", "FastInkHost::UpdateBuffer::Unmap");

// Unmap to flush writes to buffer.
gpu_memory_buffer_->Unmap();
}
}

} // namespace ash
26 changes: 24 additions & 2 deletions ash/fast_ink/fast_ink_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ class ASH_EXPORT FastInkHost : public FrameSinkHost {
gfx::Canvas& canvas() { return canvas_; }

private:
raw_ptr<gfx::GpuMemoryBuffer, ExperimentalAsh> gpu_memory_buffer_;
const raw_ptr<FastInkHost> host_;

// Damage rect in the buffer coordinates.
const gfx::Rect damage_rect_;
gfx::Rect damage_rect_;
gfx::Canvas canvas_;
};

Expand All @@ -65,6 +65,14 @@ class ASH_EXPORT FastInkHost : public FrameSinkHost {
return window_to_buffer_transform_;
}

gfx::GpuMemoryBuffer* gpu_memory_buffer_for_test() {
return gpu_memory_buffer_.get();
}

int get_pending_bitmaps_size_for_test() const {
return pending_bitmaps_.size();
}

// FrameSinkHost:
void Init(aura::Window* host_window) override;
void InitForTesting(
Expand All @@ -79,13 +87,27 @@ class ASH_EXPORT FastInkHost : public FrameSinkHost {
bool auto_update,
const gfx::Size& last_submitted_frame_size,
float last_submitted_frame_dsf) override;
void OnFirstFrameRequested() override;

private:
void InitBufferMetadata(aura::Window* host_window);
void InitializeFastInkBuffer(aura::Window* host_window);
gfx::Rect BufferRectFromWindowRect(const gfx::Rect& rect_in_window) const;
void Draw(SkBitmap bitmap, const gfx::Rect& damage_rect);
void DrawBitmap(SkBitmap bitmap, const gfx::Rect& damage_rect);

std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;

gfx::Transform window_to_buffer_transform_;

gfx::Size buffer_size_;

struct PendingBitmap {
SkBitmap bitmap;
gfx::Rect damage_rect;
};

std::vector<PendingBitmap> pending_bitmaps_;
};

} // namespace ash
Expand Down

0 comments on commit 605336d

Please sign in to comment.