0

Destroy BufferQueue buffers when delegating.

This CL enables destroying buffers on Lacros, and adds support for destroying buffers on Mac (which is currently a no-op because BQ is not enabled on Mac).

Records a metric to make sure we aren't destroying buffers and recreating them immediately.

I've confirmed with local testing that destroying buffers works as
expected. Buffers are destroyed when delegating and recreated without problem when delegation fails (like Poster Circle).

Memory benchmarks also confirm that primary plane buffers are no longer
present, and memory savings of ~25MB for 3 1080p buffers is observed.

Change-Id: I51e33dbd9c0b80ae0e2536dfa45bb055ec84220b
Bug: 1322528
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4163533
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Reviewed-by: Peter McNeeley <petermcneeley@chromium.org>
Commit-Queue: Kevin Haslett <khaslett@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1094616}
This commit is contained in:
Kevin Haslett
2023-01-19 19:48:22 +00:00
committed by Chromium LUCI CQ
parent b252f64663
commit af5724b69b
5 changed files with 109 additions and 1 deletions
components/viz/service
tools/metrics/histograms/metadata/compositing

@ -902,7 +902,19 @@ void SkiaRenderer::FinishDrawingFrame() {
current_frame()->overlay_list.begin(), surface_candidate);
#endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
}
} else {
#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
// If there's no primary plane on these platforms it mean's we're delegating
// to the system compositor, and don't need the buffers anymore. If those
// buffers are managed by buffer_queue_, we can tell it to destroy them.
// They'll be recreated when we need them again when GetCurrentBuffer() is
// called.
if (buffer_queue_) {
buffer_queue_->DestroyBuffers();
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
}
ScheduleOverlays();
debug_tint_modulate_count_++;
}

@ -6,10 +6,10 @@
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/service/display/skia_output_surface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
namespace viz {
@ -156,6 +156,8 @@ void BufferQueue::AllocateBuffers(size_t n) {
}
std::unique_ptr<BufferQueue::AllocatedBuffer> BufferQueue::GetNextBuffer() {
RecreateBuffersIfDestroyed();
DCHECK(!available_buffers_.empty());
std::unique_ptr<AllocatedBuffer> buffer =
@ -165,6 +167,12 @@ std::unique_ptr<BufferQueue::AllocatedBuffer> BufferQueue::GetNextBuffer() {
}
gpu::Mailbox BufferQueue::GetLastSwappedBuffer() {
if (buffers_destroyed_) {
// Buffers will not be destroyed on platforms where we need to use a buffer
// for overlay testing (Ash).
return gpu::Mailbox();
}
// The last swapped buffer will generally be in displayed_buffer_, as long as
// SwapBuffersComplete() has been called at least once for a non-empty swap
// since the last Reshape().
@ -199,6 +207,27 @@ void BufferQueue::EnsureMinNumberOfBuffers(size_t n) {
number_of_buffers_ = n;
}
void BufferQueue::DestroyBuffers() {
if (buffers_destroyed_) {
return;
}
buffers_destroyed_ = true;
destroyed_timer_ = base::ElapsedTimer();
FreeAllBuffers();
}
void BufferQueue::RecreateBuffersIfDestroyed() {
if (!buffers_destroyed_) {
return;
}
AllocateBuffers(number_of_buffers_);
buffers_destroyed_ = false;
base::TimeDelta elapsed = destroyed_timer_->Elapsed();
UMA_HISTOGRAM_TIMES("Compositing.BufferQueue.TimeUntilBuffersRecreatedMs",
elapsed);
destroyed_timer_.reset();
}
BufferQueue::AllocatedBuffer::AllocatedBuffer(const gpu::Mailbox& mailbox,
const gfx::Rect& rect)
: mailbox(mailbox), damage(rect) {}

@ -13,6 +13,7 @@
#include "base/containers/circular_deque.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/elapsed_timer.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/ipc/common/surface_handle.h"
@ -49,6 +50,10 @@ class VIZ_SERVICE_EXPORT BufferQueue {
// target for compositing).
gpu::Mailbox GetCurrentBuffer();
// Returns a mailbox to be used for overlay testing. This will be the last
// swapped buffer if one exists, or another buffer in the queue if not. This
// will return a zero-mailbox if DestroyBuffers() has been called and buffers
// have not been recreated since.
gpu::Mailbox GetLastSwappedBuffer();
// Returns a rectangle whose contents may have changed since the current
@ -95,6 +100,15 @@ class VIZ_SERVICE_EXPORT BufferQueue {
// |available_buffers_|.
void RecreateBuffers();
// Destroys all allocated buffers. This should be used when the renderer knows
// these buffers will no longer be needed, e.g. when delegating to the system
// compositor.
// Buffers will be recreated the next time GetCurrentBuffer() is called.
// NOTE: This should not be used on platforms that use buffers for
// overlay testing because GetLastSwappedBuffer() will not recreate the
// buffers.
void DestroyBuffers();
private:
friend class BufferQueueTest;
friend class BufferQueueMockedSharedImageInterfaceTest;
@ -127,6 +141,10 @@ class VIZ_SERVICE_EXPORT BufferQueue {
// Return a buffer that is available to be drawn into.
std::unique_ptr<AllocatedBuffer> GetNextBuffer();
// If |buffers_destroyed_| = true, this will create |number_of_buffers_|
// buffers with the settings last set by Reshape().
void RecreateBuffersIfDestroyed();
// Used to create and destroy shared images.
const raw_ptr<SkiaOutputSurface> skia_output_surface_;
// Used when creating shared images.
@ -153,6 +171,15 @@ class VIZ_SERVICE_EXPORT BufferQueue {
// may be nullptr, if they represent frames that have been destroyed, or
// frames where SwapBuffers() was called without calling GetCurrentBuffer().
base::circular_deque<std::unique_ptr<AllocatedBuffer>> in_flight_buffers_;
// Whether the buffers have been destroyed and are not yet recreated.
bool buffers_destroyed_ = false;
// Started when DestroyBuffers() destroys all buffers, and reported the next
// time RecreateBuffersIfDestroyed() is called. The timer will be reset after
// reporting.
// Used to see how often we destroy buffers and recreate them very soon, which
// we want to be rare.
absl::optional<base::ElapsedTimer> destroyed_timer_;
};
} // namespace viz

@ -591,4 +591,32 @@ TEST_F(BufferQueueTest, RecreateBuffers) {
EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb4);
}
TEST_F(BufferQueueTest, DestroyBuffers) {
EXPECT_TRUE(buffer_queue_->Reshape(screen_size, kBufferQueueColorSpace,
kBufferQueueFormat));
auto mb1 = SendDamagedFrame(small_damage);
auto mb2 = SendDamagedFrame(small_damage);
auto mb3 = SendDamagedFrame(small_damage);
std::vector<gpu::Mailbox> original_buffers = {mb1, mb2, mb3};
EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb1);
buffer_queue_->SwapBuffers(small_damage);
EXPECT_EQ(buffer_queue_->GetCurrentBuffer(), mb2);
buffer_queue_->SwapBuffers(small_damage);
buffer_queue_->DestroyBuffers();
// All buffers are destroyed, and GetLastSwappedBuffer should not recreate
// them.
EXPECT_TRUE(buffer_queue_->GetLastSwappedBuffer().IsZero());
buffer_queue_->SwapBuffersComplete(); // mb1
EXPECT_TRUE(buffer_queue_->GetLastSwappedBuffer().IsZero());
// GetCurrentBuffer should create the new buffers.
auto mb4 = buffer_queue_->GetCurrentBuffer();
EXPECT_FALSE(mb4.IsZero());
EXPECT_THAT(original_buffers, Not(Contains(mb4)));
EXPECT_FALSE(buffer_queue_->GetLastSwappedBuffer().IsZero());
}
} // namespace viz

@ -86,6 +86,18 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="Compositing.BufferQueue.TimeUntilBuffersRecreatedMs"
units="ms" expires_after="2023-09-30">
<owner>khaslett@chromium.org</owner>
<owner>petermcneeley@chromium.org</owner>
<summary>
The time since a BufferQueue's buffers were destroyed until they were
recreated. If this time is commonly very small, that is a problem. Recorded
when the buffer recreates buffers that were previously destroyed. For a
single BufferQueue this should be once per frame at worst.
</summary>
</histogram>
<histogram name="Compositing.ColorGamut" enum="ColorGamut"
expires_after="2022-02-06">
<owner>cblume@chromium.org</owner>