blob: 668c9f68a61a29b84d0c7a12fbfcdd5183a4a620 [file] [log] [blame] [edit]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include <drm_fourcc.h>
#include <overlay-prioritizer-client-protocol.h>
#include <cstdint>
#include <memory>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/rrect_f.h"
#include "ui/gfx/gpu_fence_handle.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/gfx/overlay_priority_hint.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/ozone/platform/wayland/common/wayland_overlay_config.h"
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
#include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_frame_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
#include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
#include "ui/ozone/platform/wayland/test/test_overlay_prioritized_surface.h"
#include "ui/ozone/platform/wayland/test/test_zwp_linux_buffer_params.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/platform_window/platform_window_init_properties.h"
using testing::_;
using testing::Truly;
using testing::Values;
namespace ui {
namespace {
using MockTerminateGpuCallback =
base::MockCallback<base::OnceCallback<void(std::string)>>;
constexpr gfx::Size kDefaultSize(1024, 768);
// TODO(msisov): add a test to exercise buffer management with non-default scale
// once all the patches land.
constexpr float kDefaultScale = 1.f;
struct InputData {
bool has_file = false;
gfx::Size size;
uint32_t planes_count = 0;
std::vector<uint32_t> strides;
std::vector<uint32_t> offsets;
std::vector<uint64_t> modifiers;
uint32_t format = 0;
uint32_t buffer_id = 0;
};
class MockSurfaceGpu : public WaylandSurfaceGpu {
public:
MockSurfaceGpu(WaylandBufferManagerGpu* buffer_manager,
gfx::AcceleratedWidget widget)
: buffer_manager_(buffer_manager), widget_(widget) {
buffer_manager_->RegisterSurface(widget_, this);
}
MockSurfaceGpu(const MockSurfaceGpu&) = delete;
MockSurfaceGpu& operator=(const MockSurfaceGpu&) = delete;
~MockSurfaceGpu() override { buffer_manager_->UnregisterSurface(widget_); }
MOCK_METHOD3(OnSubmission,
void(uint32_t buffer_id,
const gfx::SwapResult& swap_result,
gfx::GpuFenceHandle release_fence));
MOCK_METHOD2(OnPresentation,
void(uint32_t buffer_id,
const gfx::PresentationFeedback& feedback));
private:
const raw_ptr<WaylandBufferManagerGpu> buffer_manager_;
const gfx::AcceleratedWidget widget_;
};
} // namespace
class WaylandBufferManagerTest : public WaylandTest {
public:
WaylandBufferManagerTest() = default;
WaylandBufferManagerTest(const WaylandBufferManagerTest&) = delete;
WaylandBufferManagerTest& operator=(const WaylandBufferManagerTest&) = delete;
~WaylandBufferManagerTest() override = default;
void SetUp() override {
// Viewport surface scaling is only checked once on surface creation and
// persisted, so we must make sure the configuration is done before we
// create the surface.
connection_->set_supports_viewporter_surface_scaling(
GetParam().supports_viewporter_surface_scaling);
WaylandTest::SetUp();
manager_host_ = connection_->buffer_manager_host();
EXPECT_TRUE(manager_host_);
// Use the helper methods below, which automatically set the termination
// callback and bind the interface again if the manager failed.
manager_host_->SetTerminateGpuCallback(callback_.Get());
auto interface_ptr = manager_host_->BindInterface();
buffer_manager_gpu_->Initialize(std::move(interface_ptr), {},
/*supports_dma_buf=*/false,
/*supports_viewporter=*/true,
/*supports_acquire_fence=*/false,
/*supports_overlays=*/true,
/*supports_single_pixel_buffer=*/true);
surface_id_ = window_->root_surface()->get_surface_id();
}
void TearDown() override {
// MockGpuSurface will unregister itself from the WaylandBuffermanagerGpu,
// which will remove objects associated with this surface on another thread.
// This run loop gives it a chance to do that.
base::RunLoop().RunUntilIdle();
WaylandTest::TearDown();
}
protected:
void SendFrameCallbackForSurface(uint32_t surface_id) {
PostToServerAndWait([surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(surface_id);
ASSERT_TRUE(mock_surface);
mock_surface->SendFrameCallback();
});
}
// Might be null. Make sure to not manipulate the returned resource on the
// client thread. Though, if the client does that, the server is able to catch
// calls for libwayland-server on a wrong thread.
wl_resource* GetSurfaceAttachedBuffer(uint32_t surface_id) {
wl_resource* wl_buffer = nullptr;
PostToServerAndWait(
[&wl_buffer, surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(surface_id);
ASSERT_TRUE(mock_surface);
wl_buffer = mock_surface->attached_buffer();
});
return wl_buffer;
}
base::ScopedFD MakeFD() {
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
auto file =
base::File(temp_path, base::File::FLAG_READ | base::File::FLAG_WRITE |
base::File::FLAG_CREATE_ALWAYS);
return base::ScopedFD(file.TakePlatformFile());
}
// Sets the terminate gpu callback expectation, calls OnChannelDestroyed,
// sets the same callback again and re-establishes mojo connection again
// for convenience.
void SetTerminateCallbackExpectationAndDestroyChannel(
MockTerminateGpuCallback* callback,
bool fail) {
channel_destroyed_error_message_.clear();
if (!fail) {
// To avoid warning messages as "Expected to be never called, but has 0
// WillOnce()s", split the expecations based on the expected call times.
EXPECT_CALL(*callback, Run(_)).Times(0);
} else {
EXPECT_CALL(*callback, Run(_))
.Times(1)
.WillRepeatedly(
::testing::Invoke([this, callback](std::string error_string) {
channel_destroyed_error_message_ = error_string;
manager_host_->OnChannelDestroyed();
manager_host_->SetTerminateGpuCallback(callback->Get());
auto interface_ptr = manager_host_->BindInterface();
// Recreate the gpu side manager (the production code does the
// same).
buffer_manager_gpu_ =
std::make_unique<WaylandBufferManagerGpu>();
buffer_manager_gpu_->Initialize(
std::move(interface_ptr), {},
/*supports_dma_buf=*/false,
/*supports_viewporter=*/true,
/*supports_acquire_fence=*/false,
/*supports_overlays=*/true,
/*supports_single_pixel_buffer=*/true);
}));
}
}
void CreateDmabufBasedBufferAndSetTerminateExpectation(
bool fail,
uint32_t buffer_id,
base::ScopedFD fd = base::ScopedFD(),
const gfx::Size& size = kDefaultSize,
const std::vector<uint32_t>& strides = {1},
const std::vector<uint32_t>& offsets = {2},
const std::vector<uint64_t>& modifiers = {3},
uint32_t format = DRM_FORMAT_R8,
uint32_t planes_count = 1) {
if (!fd.is_valid())
fd = MakeFD();
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, fail);
buffer_manager_gpu_->CreateDmabufBasedBuffer(
std::move(fd), kDefaultSize, strides, offsets, modifiers, format,
planes_count, buffer_id);
base::RunLoop().RunUntilIdle();
}
void CreateShmBasedBufferAndSetTerminateExpecation(
bool fail,
uint32_t buffer_id,
const gfx::Size& size = kDefaultSize,
size_t length = 0) {
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, fail);
if (!length)
length = size.width() * size.height() * 4;
buffer_manager_gpu_->CreateShmBasedBuffer(MakeFD(), length, size,
buffer_id);
base::RunLoop().RunUntilIdle();
}
void DestroyBufferAndSetTerminateExpectation(uint32_t buffer_id, bool fail) {
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, fail);
buffer_manager_gpu_->DestroyBuffer(buffer_id);
base::RunLoop().RunUntilIdle();
}
void ProcessCreatedBufferResourcesWithExpectation(size_t expected_size,
bool fail) {
PostToServerAndWait(
[expected_size, fail](wl::TestWaylandServerThread* server) {
auto params_vector = server->zwp_linux_dmabuf_v1()->buffer_params();
// To ensure, no other buffers are created, test the size of the
// vector.
EXPECT_EQ(params_vector.size(), expected_size);
for (wl::TestZwpLinuxBufferParamsV1* mock_params : params_vector) {
if (!fail) {
zwp_linux_buffer_params_v1_send_created(
mock_params->resource(), mock_params->buffer_resource());
} else {
zwp_linux_buffer_params_v1_send_failed(mock_params->resource());
}
}
});
}
std::unique_ptr<WaylandWindow> CreateWindow(
PlatformWindowType type = PlatformWindowType::kWindow,
gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget) {
testing::Mock::VerifyAndClearExpectations(&delegate_);
PlatformWindowInitProperties properties;
properties.bounds = gfx::Rect(0, 0, 800, 600);
properties.type = type;
properties.parent_widget = parent_widget;
auto new_window = WaylandWindow::Create(&delegate_, connection_.get(),
std::move(properties));
EXPECT_TRUE(new_window);
WaylandTestBase::SyncDisplay();
EXPECT_NE(new_window->GetWidget(), gfx::kNullAcceleratedWidget);
return new_window;
}
wl::WaylandOverlayConfig CreateBasicWaylandOverlayConfig(
int z_order,
uint32_t buffer_id,
const gfx::Rect& bounds_rect) {
return CreateBasicWaylandOverlayConfig(z_order, buffer_id,
gfx::RectF(bounds_rect));
}
wl::WaylandOverlayConfig CreateBasicWaylandOverlayConfig(
int z_order,
uint32_t buffer_id,
const gfx::RectF& bounds_rect) {
wl::WaylandOverlayConfig config;
config.z_order = z_order;
config.buffer_id = buffer_id;
config.bounds_rect = bounds_rect;
config.damage_region = gfx::ToEnclosedRect(bounds_rect);
return config;
}
void CommitBuffer(gfx::AcceleratedWidget widget,
uint32_t frame_id,
uint32_t buffer_id,
gfx::FrameData data,
const gfx::Rect& bounds_rect,
const gfx::RoundedCornersF& corners,
float surface_scale_factor,
const gfx::Rect& damage_region) {
buffer_manager_gpu_->CommitBuffer(
widget, frame_id, buffer_id, data, bounds_rect, /*enable_blend=*/false,
corners, surface_scale_factor, damage_region);
// Let the mojo message to be processed.
base::RunLoop().RunUntilIdle();
}
MockTerminateGpuCallback callback_;
raw_ptr<WaylandBufferManagerHost> manager_host_;
// Error message that is received when the manager_host destroys the channel.
std::string channel_destroyed_error_message_;
uint32_t surface_id_ = 0u;
};
TEST_P(WaylandBufferManagerTest, CreateDmabufBasedBuffers) {
constexpr uint32_t kDmabufBufferId = 1;
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kDmabufBufferId);
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, VerifyModifiers) {
constexpr uint32_t kDmabufBufferId = 1;
constexpr uint32_t kFourccFormatR8 = DRM_FORMAT_R8;
constexpr uint64_t kFormatModiferLinear = DRM_FORMAT_MOD_LINEAR;
const std::vector<uint64_t> kFormatModifiers{DRM_FORMAT_MOD_INVALID,
kFormatModiferLinear};
// Tests that fourcc format is added, but invalid modifier is ignored first.
// Then, when valid modifier comes, it is stored.
for (const auto& modifier : kFormatModifiers) {
PostToServerAndWait([modifier](wl::TestWaylandServerThread* server) {
uint32_t modifier_hi = modifier >> 32;
uint32_t modifier_lo = modifier & UINT32_MAX;
zwp_linux_dmabuf_v1_send_modifier(
server->zwp_linux_dmabuf_v1()->resource(), kFourccFormatR8,
modifier_hi, modifier_lo);
});
auto buffer_formats =
connection_->buffer_factory()->GetSupportedBufferFormats();
ASSERT_EQ(buffer_formats.size(), 1u);
ASSERT_EQ(buffer_formats.begin()->first,
GetBufferFormatFromFourCCFormat(kFourccFormatR8));
auto modifiers = buffer_formats.begin()->second;
if (modifier == DRM_FORMAT_MOD_INVALID) {
ASSERT_EQ(modifiers.size(), 0u);
} else {
ASSERT_EQ(modifiers.size(), 1u);
ASSERT_EQ(modifiers[0], modifier);
}
}
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(
false /*fail*/, kDmabufBufferId, base::ScopedFD(), kDefaultSize, {1}, {2},
{kFormatModiferLinear}, kFourccFormatR8, 1);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto params_vector = server->zwp_linux_dmabuf_v1()->buffer_params();
EXPECT_EQ(params_vector.size(), 1u);
EXPECT_EQ(params_vector[0]->modifier_hi_, kFormatModiferLinear >> 32);
EXPECT_EQ(params_vector[0]->modifier_lo_,
kFormatModiferLinear & UINT32_MAX);
});
// Clean up.
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, CreateShmBasedBuffers) {
constexpr uint32_t kShmBufferId = 1;
CreateShmBasedBufferAndSetTerminateExpecation(false /*fail*/, kShmBufferId);
DestroyBufferAndSetTerminateExpectation(kShmBufferId, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) {
const InputData kBadInputs[] = {
// All zeros.
{},
// Valid file but zeros everywhereelse.
{true},
// Valid file, invalid size, zeros elsewhere.
{true, {kDefaultSize.width(), 0}},
{true, {0, kDefaultSize.height()}},
// Valid file and size but zeros in other fields.
{true, kDefaultSize},
// Vectors have different lengths.
{true, kDefaultSize, 1, {1}, {2, 3}, {4, 5, 6}},
// Vectors have same lengths but strides have a zero.
{true, kDefaultSize, 1, {0}, {2}, {6}},
// Vectors are valid but buffer format is not.
{true, kDefaultSize, 1, {1}, {2}, {6}},
// Everything is correct but the buffer ID is zero.
{true, kDefaultSize, 1, {1}, {2}, {6}, DRM_FORMAT_R8},
};
for (const auto& bad : kBadInputs) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0);
base::ScopedFD dummy;
CreateDmabufBasedBufferAndSetTerminateExpectation(
true /*fail*/, bad.buffer_id,
bad.has_file ? MakeFD() : std::move(dummy), bad.size, bad.strides,
bad.offsets, bad.modifiers, bad.format, bad.planes_count);
}
}
TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
const uint32_t kBufferId1 = 1;
const uint32_t kBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
// This section tests that it is impossible to create buffers with the same
// id if they haven't been assigned to any surfaces yet.
{
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(2);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId2);
// Can't create buffer with existing id.
CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/,
kBufferId2);
}
// ... impossible to create buffers with the same id if one of them
// has already been attached to a surface.
{
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/,
kBufferId1);
}
// ... impossible to destroy non-existing buffer.
{
// Either it is attached...
DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/);
// Or not attached.
DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/);
}
// Can destroy the buffer without specifying the widget.
{
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
}
// Still can destroy the buffer even if it has not been attached to any
// widgets.
{
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
}
// ... impossible to destroy buffers twice.
{
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
// Attach to a surface.
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
// Created non-attached buffer as well.
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId2);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
// Can't destroy the buffer with non-existing id (the manager cleared the
// state after the previous failure).
DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/);
// Non-attached buffer must have been also destroyed (we can't destroy it
// twice) if there was a failure.
DestroyBufferAndSetTerminateExpectation(kBufferId2, true /*fail*/);
// Create and destroy non-attached buffer twice.
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId2);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, true /*fail*/);
}
}
TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) {
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// Can't commit for non-existing buffer id.
constexpr uint32_t kNumberOfCommits = 0;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
CommitBuffer(window_->GetWidget(), 1u, 5u,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
// Let the mojo call to go through.
base::RunLoop().RunUntilIdle();
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
testing::Mock::VerifyAndClearExpectations(
server->GetObject<wl::MockSurface>(id));
});
}
TEST_P(WaylandBufferManagerTest, CommitOverlaysNonExistingBufferId) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// Can't commit for non-existing buffer id.
constexpr uint32_t kNumberOfCommits = 0;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
std::vector<wl::WaylandOverlayConfig> overlay_configs;
if (connection_->ShouldUseOverlayDelegation()) {
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
INT32_MIN, 1u, window_->GetBoundsInPixels()));
}
// Non-existing buffer id
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(0, 2u, window_->GetBoundsInPixels()));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
// Let the mojo call to go through.
base::RunLoop().RunUntilIdle();
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
testing::Mock::VerifyAndClearExpectations(
server->GetObject<wl::MockSurface>(id));
});
}
TEST_P(WaylandBufferManagerTest, CommitOverlaysWithSameBufferId) {
if (!connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
const size_t expected_number_of_buffers =
connection_->linux_explicit_synchronization_v1() ? 1 : 2;
PostToServerAndWait(
[expected_number_of_buffers](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(expected_number_of_buffers);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u);
// Re-using the same buffer id across multiple surfaces is allowed.
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, false /*fail*/);
std::vector<wl::WaylandOverlayConfig> overlay_configs;
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(0, 1u, window_->GetBoundsInPixels()));
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(1, 1u, window_->GetBoundsInPixels()));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
// Let the mojo call to go through.
base::RunLoop().RunUntilIdle();
ProcessCreatedBufferResourcesWithExpectation(
expected_number_of_buffers /* expected size */, false /* fail */);
}
TEST_P(WaylandBufferManagerTest, CommitBufferNullWidget) {
constexpr uint32_t kBufferId = 1;
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
// Can't commit for non-existing widget.
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
CommitBuffer(gfx::kNullAcceleratedWidget, 1u, kBufferId,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
// Let the mojo call to go through.
base::RunLoop().RunUntilIdle();
}
// Tests that committing overlays with bounds_rect containing NaN or infinity
// values is illegal - the host terminates the gpu process.
TEST_P(WaylandBufferManagerTest, CommitOverlaysNonsensicalBoundsRect) {
if (!connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
const std::vector<gfx::RectF> bounds_rect_test_data = {
gfx::RectF(std::nanf(""), window_->GetBoundsInPixels().y(), std::nanf(""),
window_->GetBoundsInPixels().height()),
gfx::RectF(window_->GetBoundsInPixels().x(),
std::numeric_limits<float>::infinity(),
window_->GetBoundsInPixels().width(),
std::numeric_limits<float>::infinity())};
constexpr bool config[2] = {/*root_has_nan_bounds=*/true,
/*non_root_overlay_has_nan_bounds=*/false};
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
for (bool should_root_have_nan_bounds : config) {
for (const auto& faulty_bounds_rect : bounds_rect_test_data) {
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
// Can't commit for bounds rect containing NaN
SetTerminateCallbackExpectationAndDestroyChannel(&callback_,
true /*fail*/);
size_t z_order = 0;
std::vector<wl::WaylandOverlayConfig> overlay_configs;
if (should_root_have_nan_bounds) {
// The root surface has nan bounds.
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
INT32_MIN, kBufferId1, faulty_bounds_rect));
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order++, kBufferId2, window_->GetBoundsInPixels()));
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order++, kBufferId3, window_->GetBoundsInPixels()));
} else {
// Overlays have nan bounds. Given playback starts with the biggest
// z-order number, add two more overlays around the faulty overlay
// config so that the test ensures no further playback happens and it
// doesn't crash.
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
INT32_MIN, kBufferId1, window_->GetBoundsInPixels()));
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order++, kBufferId2, window_->GetBoundsInPixels()));
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order++, kBufferId3, faulty_bounds_rect));
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order++, kBufferId2, window_->GetBoundsInPixels()));
}
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
base::RunLoop().RunUntilIdle();
if (!should_root_have_nan_bounds &&
!connection_->linux_explicit_synchronization_v1()) {
// This case submits kBufferId2 twice. So, a second handle is requested
// during a frame playback if explicit sync is unavailable.
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
}
EXPECT_EQ("Overlay bounds_rect is invalid (NaN or infinity).",
channel_destroyed_error_message_);
std::vector<uint32_t> ids_of_subsurfaces;
ids_of_subsurfaces.reserve(window_->wayland_subsurfaces_.size());
for (auto& subsurface : window_->wayland_subsurfaces_) {
ids_of_subsurfaces.emplace_back(
subsurface->wayland_surface()->get_surface_id());
}
PostToServerAndWait([id = surface_id_, ids_of_subsurfaces](
wl::TestWaylandServerThread* server) {
// Clear all the possible frame and release callbacks.
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
for (auto subsurface_id : ids_of_subsurfaces) {
auto* mock_surface_of_subsurface =
server->GetObject<wl::MockSurface>(subsurface_id);
EXPECT_TRUE(mock_surface_of_subsurface);
mock_surface_of_subsurface->SendFrameCallback();
mock_surface_of_subsurface->ClearBufferReleases();
}
mock_surface->SendFrameCallback();
mock_surface->ClearBufferReleases();
});
}
}
}
// A similar test to the above with the only difference that a background
// dummy buffer is not used as delegation is disabled.
TEST_P(WaylandBufferManagerTest,
CommitOverlaysNonsensicalBoundsRectSingleOverlay) {
if (connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
const std::vector<gfx::RectF> bounds_rect_test_data = {
gfx::RectF(std::nanf(""), window_->GetBoundsInPixels().y(), std::nanf(""),
window_->GetBoundsInPixels().height()),
gfx::RectF(window_->GetBoundsInPixels().x(),
std::numeric_limits<float>::infinity(),
window_->GetBoundsInPixels().width(),
std::numeric_limits<float>::infinity())};
for (const auto& faulty_bounds_rect : bounds_rect_test_data) {
constexpr uint32_t kBufferId1 = 1;
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
// Can't commit for bounds rect containing NaN
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
size_t z_order = 0;
std::vector<wl::WaylandOverlayConfig> overlay_configs;
overlay_configs.emplace_back(CreateBasicWaylandOverlayConfig(
z_order, kBufferId1, faulty_bounds_rect));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
base::RunLoop().RunUntilIdle();
EXPECT_EQ("Overlay bounds_rect is invalid (NaN or infinity).",
channel_destroyed_error_message_);
}
}
TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
window_->SetBoundsInDIP(bounds);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
// All the other expectations must come in order.
::testing::InSequence sequence;
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
// wp_presentation must not exist now. This means that the buffer
// manager must send synthetized presentation feedbacks.
ASSERT_TRUE(!connection_->presentation());
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
base::RunLoop().RunUntilIdle();
// As long as there hasn't any previous buffer attached (nothing to release
// yet), it must be enough to just send a frame callback back.
SendFrameCallbackForSurface(surface_id_);
// Commit second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
base::RunLoop().RunUntilIdle();
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
});
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// wp_presentation is available now.
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
ASSERT_TRUE(mock_wp_presentation);
EXPECT_CALL(*mock_wp_presentation,
Feedback(_, _, mock_surface->resource(), _))
.Times(1);
});
// Now, the wp_presentation object exists and there must be a real feedback
// sent. Ensure the order now.
ASSERT_TRUE(connection_->presentation());
// Commit second buffer now.
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
base::RunLoop().RunUntilIdle();
// Even though, the server send the presentation feedback, the host manager
// must make sure the order of the submission and presentation callbacks is
// correct. Thus, no callbacks must be received by the MockSurfaceGpu.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
server->EnsureAndGetWpPresentation()->SendPresentationCallback();
});
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// Now, send the release callback. The host manager must send the submission
// and presentation callbacks in correct order.
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
});
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest,
DestroyedBuffersGeneratePresentationFeedbackFailure) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
window_->SetBoundsInDIP(bounds);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
ASSERT_TRUE(mock_surface);
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
ASSERT_TRUE(mock_wp_presentation);
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
EXPECT_CALL(*mock_wp_presentation,
Feedback(_, _, mock_surface->resource(), _))
.Times(3);
});
::testing::InSequence s;
// wp_presentation_feedback should work now.
ASSERT_TRUE(connection_->presentation());
// Commit the first buffer and expect OnSubmission immediately.
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
// Deliberately drop the presentation feedback for the first buffer,
// since we will destroy it.
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
EXPECT_EQ(1u, mock_wp_presentation->num_of_presentation_callbacks());
mock_wp_presentation->DropPresentationCallback();
});
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Commit second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Destroy the first buffer, which should trigger submission for the second
// buffer.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
DestroyBufferAndSetTerminateExpectation(kBufferId1, /*fail=*/false);
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->DestroyPrevAttachedBuffer();
mock_surface->SendFrameCallback();
});
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
// Deliberately drop the presentation feedback for the second buffer,
// since we will destroy it.
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
EXPECT_EQ(1u, mock_wp_presentation->num_of_presentation_callbacks());
mock_wp_presentation->DropPresentationCallback();
});
// Commit buffer 3 then send the presentation callback for it. This should
// not call OnPresentation as OnSubmission hasn't been called yet. Though,
// the previously submitted buffers will have their presentation feedback
// discarded and sent to the |mock_surface_gpu| as those buffers have already
// been acked.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(
mock_surface_gpu,
OnPresentation(
kBufferId1,
::testing::Field(
&gfx::PresentationFeedback::flags,
::testing::Eq(gfx::PresentationFeedback::Flags::kFailure))))
.Times(1);
EXPECT_CALL(
mock_surface_gpu,
OnPresentation(
kBufferId2,
::testing::Field(
&gfx::PresentationFeedback::flags,
::testing::Eq(gfx::PresentationFeedback::Flags::kFailure))))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
CommitBuffer(widget, kBufferId3, kBufferId3,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(id)->SendFrameCallback();
server->EnsureAndGetWpPresentation()->SendPresentationCallback();
});
// Ensure that presentation feedback is flushed.
task_environment_.FastForwardBy(
WaylandFrameManager::GetPresentationFlushTimerDurationForTesting());
// Verify our expecations.
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Destroy buffer 2, which should trigger OnSubmission for buffer 3, and
// finally OnPresentation for buffer 3.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(1);
DestroyBufferAndSetTerminateExpectation(kBufferId2, /*fail=*/false);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->DestroyPrevAttachedBuffer();
mock_surface->SendFrameCallback();
server->EnsureAndGetWpPresentation()->SendPresentationCallback();
});
// Let the mojo messages to be processed.
// No need to fast forward to ensure the presentation flush timer is fired,
// because in this case the OnSubmission should piggyback the feedback.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/);
}
// This test ensures that a discarded presentation feedback sent prior receiving
// results for the previous presentation feedback does not make them
// automatically failed.
TEST_P(WaylandBufferManagerTest,
EnsureDiscardedPresentationDoesNotMakePreviousFeedbacksFailed) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
// Enable wp_presentation support.
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
ASSERT_TRUE(mock_wp_presentation);
});
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
window_->SetBoundsInDIP(bounds);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
// All the other expectations must come in order.
::testing::InSequence sequence;
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
// Commit first buffer
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let the mojo message for OnSubmission go back.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
// Verify we have a presentation callback now. This will be sent later.
EXPECT_EQ(
1u,
server->EnsureAndGetWpPresentation()->num_of_presentation_callbacks());
server->GetObject<wl::MockSurface>(id)->SendFrameCallback();
});
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
// Commit second buffer
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
// Verify we have a presentation callback now. This will be sent later.
EXPECT_EQ(
2u,
server->EnsureAndGetWpPresentation()->num_of_presentation_callbacks());
// Release previous buffer and commit third buffer.
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
});
// Let the mojo message for OnSubmission go back.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
// Commit third buffer
CommitBuffer(widget, kBufferId3, kBufferId3,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
});
// Let the mojo messages to be processed.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Even though WaylandBufferManagerHost stores the previous stores
// presentation feedbacks and waits for their value, the current last one
// mustn't result in the previous marked as failed. Thus, no feedback must be
// actually sent to the MockSurfaceGpu as it's required to send feedbacks in
// order.
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
EXPECT_EQ(3u, mock_wp_presentation->num_of_presentation_callbacks());
mock_wp_presentation->SendPresentationCallbackDiscarded(/*last=*/true);
});
// Let the mojo messages to be processed if any to ensure there are no pending
// messages.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Now, start to send all the previous callbacks.
EXPECT_CALL(mock_surface_gpu,
OnPresentation(
kBufferId1,
::testing::Field(
&gfx::PresentationFeedback::flags,
::testing::Eq(gfx::PresentationFeedback::Flags::kVSync))))
.Times(1);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
EXPECT_EQ(2u, mock_wp_presentation->num_of_presentation_callbacks());
mock_wp_presentation->SendPresentationCallback();
});
// Ensure that presentation feedback is flushed.
task_environment_.FastForwardBy(
WaylandFrameManager::GetPresentationFlushTimerDurationForTesting());
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Now, send the second presentation feedback. It will send both second and
// third feedback that was discarded.
EXPECT_CALL(mock_surface_gpu,
OnPresentation(
kBufferId2,
::testing::Field(
&gfx::PresentationFeedback::flags,
::testing::Eq(gfx::PresentationFeedback::Flags::kVSync))))
.Times(1);
EXPECT_CALL(
mock_surface_gpu,
OnPresentation(
kBufferId3,
::testing::Field(
&gfx::PresentationFeedback::flags,
::testing::Eq(gfx::PresentationFeedback::Flags::kFailure))))
.Times(1);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
auto* mock_wp_presentation = server->EnsureAndGetWpPresentation();
EXPECT_EQ(1u, mock_wp_presentation->num_of_presentation_callbacks());
mock_wp_presentation->SendPresentationCallback();
});
// Ensure that presentation feedback is flushed.
task_environment_.FastForwardBy(
WaylandFrameManager::GetPresentationFlushTimerDurationForTesting());
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
constexpr uint32_t kDmabufBufferId = 1;
constexpr uint32_t kDmabufBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kDmabufBufferId);
// Part 1: the surface mustn't have a buffer attached until
// zwp_linux_buffer_params_v1_send_created is called. Instead, the buffer must
// be set as pending buffer.
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
});
CommitBuffer(widget, kDmabufBufferId, kDmabufBufferId,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* linux_dmabuf = server->zwp_linux_dmabuf_v1();
// Once the client receives a "...send_created" call, it must destroy the
// params resource.
EXPECT_TRUE(linux_dmabuf->buffer_params().empty());
// Part 2: the surface mustn't have a buffer attached until frame callback
// is sent by the server.
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
testing::Mock::VerifyAndClearExpectations(
server->GetObject<wl::MockSurface>(id));
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kDmabufBufferId2);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
});
CommitBuffer(widget, kDmabufBufferId2, kDmabufBufferId2,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, gfx::Rect(window_->applied_state().size_px));
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
// After the frame callback is sent, the pending buffer will be committed.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
mock_surface->SendFrameCallback();
});
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId2, false /*fail*/);
}
// Tests the surface does not have buffers attached until it's configured at
// least once.
TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) {
constexpr uint32_t kDmabufBufferId = 1;
// Exercise two window types that create different windows - toplevel, menu.
// subsurface windows do not create xdg_surfaces so no need to configure them.
std::vector<PlatformWindowType> window_types{PlatformWindowType::kWindow,
PlatformWindowType::kTooltip};
for (const auto& type : window_types) {
// If the type is not kWindow, provide default created window as parent of
// the newly created window.
auto temp_window = CreateWindow(type, type != PlatformWindowType::kWindow
? widget_
: gfx::kNullAcceleratedWidget);
auto widget = temp_window->GetWidget();
temp_window->Show(false);
const uint32_t temp_window_surface_id =
temp_window->root_surface()->get_surface_id();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _))
.Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kDmabufBufferId);
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface =
server->GetObject<wl::MockSurface>(temp_window_surface_id);
auto* xdg_surface = mock_surface->xdg_surface();
ASSERT_TRUE(xdg_surface);
});
ASSERT_FALSE(temp_window->IsSurfaceConfigured());
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface =
server->GetObject<wl::MockSurface>(temp_window_surface_id);
auto* xdg_surface = mock_surface->xdg_surface();
EXPECT_CALL(*xdg_surface, SetWindowGeometry(_)).Times(0);
EXPECT_CALL(*xdg_surface, AckConfigure(_)).Times(0);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
});
CommitBuffer(widget, kDmabufBufferId, kDmabufBufferId,
gfx::FrameData(delegate_.viz_seq()),
window_->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, window_->GetBoundsInPixels());
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface =
server->GetObject<wl::MockSurface>(temp_window_surface_id);
auto* xdg_surface = mock_surface->xdg_surface();
testing::Mock::VerifyAndClearExpectations(mock_surface);
EXPECT_CALL(*xdg_surface, SetWindowGeometry(_)).Times(0);
EXPECT_CALL(*xdg_surface, AckConfigure(_)).Times(1);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
// Commit() can be called a second time as part of the configure->ack
// sequence.
EXPECT_CALL(*mock_surface, Commit()).Times(testing::Between(1, 2));
});
ActivateSurface(temp_window_surface_id);
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
testing::Mock::VerifyAndClearExpectations(
server->GetObject<wl::MockSurface>(temp_window_surface_id));
});
SetPointerFocusedWindow(nullptr);
temp_window.reset();
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/);
}
}
// Verifies toplevel surfaces do not have buffers attached until configured,
// even when the initial configure sequence is not acked in response to
// xdg_surface.configure event, i.e: done asynchronously when OnSequencePoint()
// is called by they FrameManager).
//
// Regression test for https://crbug.com/1313023.
TEST_P(WaylandBufferManagerTest,
CommitBufferConditionsWithDeferredAckConfigure) {
constexpr gfx::Rect kNormalBounds{800, 800};
constexpr gfx::Rect kRestoredBounds{500, 500};
constexpr uint32_t kDmabufBufferId = 1;
testing::Mock::VerifyAndClearExpectations(&delegate_);
PlatformWindowInitProperties properties;
properties.type = PlatformWindowType::kWindow;
properties.bounds = kNormalBounds;
gfx::Insets insets;
EXPECT_CALL(delegate_, CalculateInsetsInDIP(PlatformWindowState::kNormal))
.WillRepeatedly(testing::Return(insets));
auto window = WaylandWindow::Create(&delegate_, connection_.get(),
std::move(properties));
ASSERT_TRUE(window);
ASSERT_NE(window->GetWidget(), gfx::kNullAcceleratedWidget);
auto widget = window->GetWidget();
// Set restored bounds to a value different from the initial window bounds in
// order to force WaylandWindow::ProcessPendingConfigureState() to defer the
// very first configure ack to be done in the subsequent OnSequencePoint()
// call.
window->SetRestoredBoundsInDIP(kRestoredBounds);
WaylandTestBase::SyncDisplay();
window->Show(false);
const uint32_t surface_id = window->root_surface()->get_surface_id();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
kDmabufBufferId);
PostToServerAndWait([surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(surface_id);
auto* xdg_surface = mock_surface->xdg_surface();
ASSERT_TRUE(xdg_surface);
});
ASSERT_FALSE(window->IsSurfaceConfigured());
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
// Emulate the following steps:
//
// 1. A CommitBuffer request coming from the GPU service, with frame
// bounds that do not match the one stored in |pending_configures_| at Host
// side (filled when processing 0x0 initial configure sequence sent by the
// Wayland compositor.
// 2. The initial configure sequence (i.e: with 0x0 size which means the
// client must suggest the initial geometry of the surface.
// 3. And then a CommitBuffer with the expected bounds (ie: suggested to the
// Wayland compositor through a set_geometry/ack_configure sequence when
// processing (2).
//
// And ensures the xdg and wl_surface objects received the correct requests
// amount. I.e: No buffer attaches before setting geometry + acking initial
// configure sequence, etc.
constexpr uint32_t kActivateSerial = 1u;
PostToServerAndWait([surface_id, bounds = kRestoredBounds](
wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(surface_id);
auto* xdg_surface = mock_surface->xdg_surface();
EXPECT_CALL(*xdg_surface, SetWindowGeometry(bounds)).Times(1);
EXPECT_CALL(*xdg_surface, AckConfigure(kActivateSerial)).Times(1);
EXPECT_CALL(*mock_surface, Attach(_, 0, 0)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
CommitBuffer(widget, kDmabufBufferId, kDmabufBufferId,
gfx::FrameData(delegate_.viz_seq() - 1), gfx::Rect{55, 55},
gfx::RoundedCornersF(), kDefaultScale, gfx::Rect{55, 55});
ActivateSurface(surface_id, kActivateSerial);
CommitBuffer(widget, kDmabufBufferId, kDmabufBufferId,
gfx::FrameData(delegate_.viz_seq()), kRestoredBounds,
gfx::RoundedCornersF(), kDefaultScale, kRestoredBounds);
SetPointerFocusedWindow(nullptr);
window.reset();
DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/);
}
// The buffer that is not originally attached to any of the surfaces,
// must be attached when a commit request comes. Also, it must setup a buffer
// release listener and OnSubmission must be called for that buffer if it is
// released.
TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
window_->SetBoundsInDIP(bounds);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
// All the other expectations must come in order.
::testing::InSequence sequence;
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
// Commit second buffer now.
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
// Now synchronously create a second buffer and commit it. The release
// callback must be setup and OnSubmission must be called.
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
// Commit second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
});
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Now asynchronously create another buffer so that a commit request
// comes earlier than it is created by the Wayland compositor, but it can
// released once the buffer is committed and processed (that is, it must be
// able to setup a buffer release callback).
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK, _))
.Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(0);
CommitBuffer(widget, kBufferId3, kBufferId3,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be processed from host to gpu (if any).
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(1);
// Now, create the buffer from the Wayland compositor side and let the buffer
// manager complete the commit request.
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
});
// Let mojo messages to be processed.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) {
constexpr uint32_t kBufferId = 1;
auto temp_window = CreateWindow();
auto widget = temp_window->GetWidget();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
CommitBuffer(widget, kBufferId, kBufferId,
gfx::FrameData(delegate_.viz_seq()),
temp_window->GetBoundsInPixels(), gfx::RoundedCornersF(),
kDefaultScale, temp_window->GetBoundsInPixels());
temp_window.reset();
DestroyBufferAndSetTerminateExpectation(kBufferId, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) {
constexpr uint32_t kBufferId = 1;
auto temp_window = CreateWindow();
auto widget = temp_window->GetWidget();
auto bounds = temp_window->GetBoundsInPixels();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
// All the other expectations must come in order.
::testing::InSequence sequence;
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
temp_window.reset();
CommitBuffer(widget, kBufferId, kBufferId,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
DestroyBufferAndSetTerminateExpectation(kBufferId, false /*fail*/);
}
TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
auto temp_window = CreateWindow();
temp_window->Show(false);
auto widget = temp_window->GetWidget();
auto bounds = temp_window->GetBoundsInPixels();
const uint32_t temp_window_surface_id =
temp_window->root_surface()->get_surface_id();
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
ASSERT_TRUE(server->GetObject<wl::MockSurface>(temp_window_surface_id));
});
ActivateSurface(temp_window_surface_id);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
// All the other expectations must come in order.
::testing::InSequence sequence;
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
PostToServerAndWait([temp_window_surface_id](
wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(temp_window_surface_id)
->SendFrameCallback();
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
PostToServerAndWait(
[temp_window_surface_id](wl::TestWaylandServerThread* server) {
auto* mock_surface =
server->GetObject<wl::MockSurface>(temp_window_surface_id);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
});
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
temp_window.reset();
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
}
// Tests that OnSubmission and OnPresentation are properly triggered if a buffer
// is committed twice in a row and those buffers are destroyed.
TEST_P(WaylandBufferManagerTest, DestroyBufferCommittedTwiceInARow) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Can't call OnSubmission until there is a release.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Destroying buffer2 should do nothing yet.
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Destroying buffer1 should give us two acks for buffer2.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(2);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(2);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
}
// Tests that OnSubmission and OnPresentation are properly triggered if a buffer
// is committed twice in a row and then released.
TEST_P(WaylandBufferManagerTest, ReleaseBufferCommittedTwiceInARow) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Even though, this is stored on the client thread, but must only be used on
// the server thread.
wl_resource* wl_buffer1 = GetSurfaceAttachedBuffer(surface_id_);
ASSERT_TRUE(wl_buffer1);
// Can't call OnSubmission until there is a release.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Releasing buffer1 should trigger two acks for buffer2.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(2);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(2);
PostToServerAndWait(
[wl_buffer1, id = surface_id_](wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(id)->ReleaseBuffer(wl_buffer1);
});
// Let mojo messages to be processed back to the gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
}
// Tests that OnSubmission and OnPresentation callbacks are properly called
// even if buffers are not released in the same order they were committed.
TEST_P(WaylandBufferManagerTest, ReleaseOrderDifferentToCommitOrder) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->GetObject<wl::MockSurface>(id), Attach(_, _, _))
.Times(1);
});
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed and passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Even though, this is stored on the client thread, but must only be used on
// the server thread.
wl_resource* wl_buffer1 = GetSurfaceAttachedBuffer(surface_id_);
ASSERT_TRUE(wl_buffer1);
// Can't call OnSubmission until there is a release.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->GetObject<wl::MockSurface>(id), Attach(_, _, _))
.Times(2);
});
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Even though, this is stored on the client thread, but must only be used on
// the server thread.
wl_resource* wl_buffer2 = GetSurfaceAttachedBuffer(surface_id_);
ASSERT_TRUE(wl_buffer2);
CommitBuffer(widget, kBufferId3, kBufferId3,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Let mojo messages to be processed and passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Releasing buffer2 can't trigger OnSubmission for buffer3, because
// OnSubmission for buffer2 has not been sent yet.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait(
[wl_buffer2, id = surface_id_](wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(id)->ReleaseBuffer(wl_buffer2);
});
// Let mojo messages to be processed and passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Releasing buffer1 should trigger acks for both.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(1);
PostToServerAndWait(
[wl_buffer1, id = surface_id_](wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(id)->ReleaseBuffer(wl_buffer1);
});
// Let mojo messages to be processed and passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/);
}
// This test verifies that submitting the buffer more than once results in
// OnSubmission callback as Wayland compositor is not supposed to release the
// buffer committed twice.
TEST_P(WaylandBufferManagerTest,
OnSubmissionCalledForBufferCommitedTwiceInARow) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
ASSERT_TRUE(!connection_->presentation());
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
PostToServerAndWait(
[id = surface_id_, bounds](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface,
Damage(0, 0, bounds.width(), bounds.height()))
.Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait(
[id = surface_id_, bounds](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
mock_surface->SendFrameCallback();
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface,
Damage(0, 0, bounds.width(), bounds.height()))
.Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
// Commit second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Damage(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
});
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Now, commit the buffer with the |kBufferId2| again. The manager does not
// sends the submission callback, the compositor is not going to release a
// buffer as it was the same buffer submitted more than once.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
PostToServerAndWait(
[id = surface_id_, bounds](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface,
Damage(0, 0, bounds.width(), bounds.height()))
.Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
// Commit second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Damage(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
mock_surface->SendFrameCallback();
});
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// If we commit another buffer now, the manager host must not automatically
// trigger OnSubmission and OnPresentation callbacks.
EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _, _)).Times(0);
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
PostToServerAndWait(
[id = surface_id_, bounds](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface,
Damage(0, 0, bounds.width(), bounds.height()))
.Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Now, they must be triggered once the buffer is released.
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
});
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
}
// Tests that submitting a single buffer only receives an OnSubmission. This is
// required behaviour to make sure that submitting buffers in a quiescent state
// will be immediately acked.
TEST_P(WaylandBufferManagerTest, OnSubmissionCalledForSingleBuffer) {
constexpr uint32_t kBufferId1 = 1;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
EXPECT_CALL(mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages to be passed from host to gpu.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
}
// Tests that when CommitOverlays(), root_surface can only be committed once all
// overlays in the frame are committed.
TEST_P(WaylandBufferManagerTest, RootSurfaceIsCommittedLast) {
if (!connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* linux_dmabuf = server->zwp_linux_dmabuf_v1();
// Ack creation for only the first 2 wl_buffers.
zwp_linux_buffer_params_v1_send_created(
linux_dmabuf->buffer_params()[0]->resource(),
linux_dmabuf->buffer_params()[0]->buffer_resource());
zwp_linux_buffer_params_v1_send_created(
linux_dmabuf->buffer_params()[1]->resource(),
linux_dmabuf->buffer_params()[1]->buffer_resource());
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// root_surface shall not be committed as one of its subsurface is not
// committed yet due to pending wl_buffer creation.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
});
std::vector<wl::WaylandOverlayConfig> overlay_configs;
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(INT32_MIN, kBufferId1, bounds));
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(0, kBufferId2, bounds));
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(1, kBufferId3, bounds));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
// Let mojo messages from gpu to host to go through.
base::RunLoop().RunUntilIdle();
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
testing::Mock::VerifyAndClearExpectations(mock_surface);
auto* linux_dmabuf = server->zwp_linux_dmabuf_v1();
// Once wl_buffer is created, all subsurfaces are committed, hence
// root_surface can be committed.
zwp_linux_buffer_params_v1_send_created(
linux_dmabuf->buffer_params()[0]->resource(),
linux_dmabuf->buffer_params()[0]->buffer_resource());
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
}
// TODO(crbug.com/367623923) Decouple this test from the older explicit sync
// protocol and/or add coverage for the new linux-drm-syncobj protocol.
TEST_P(WaylandBufferManagerTest, FencedRelease) {
if (!connection_->linux_explicit_synchronization_v1())
GTEST_SKIP();
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
window_->SetBoundsInDIP(bounds);
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
::testing::InSequence s;
// Commit the first buffer and expect the OnSubmission immediately.
EXPECT_CALL(
mock_surface_gpu,
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK,
Truly([](const auto& fence) { return fence.is_null(); })))
.Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages from gpu to host to go through.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
SendFrameCallbackForSurface(surface_id_);
// Commit the second buffer now.
CommitBuffer(widget, kBufferId2, kBufferId2,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
EXPECT_CALL(
mock_surface_gpu,
OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK,
Truly([](const auto& fence) { return !fence.is_null(); })))
.Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
// Release the first buffer via fenced release. This should trigger
// OnSubmission for the second buffer with a non-null fence.
gfx::GpuFenceHandle handle;
const int32_t kFenceFD = dup(1);
handle.Adopt(base::ScopedFD(kFenceFD));
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBufferFenced(mock_surface->prev_attached_buffer(),
std::move(handle));
mock_surface->SendFrameCallback();
});
// Let mojo messages from gpu to host to go through.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
// Commit the third buffer now.
CommitBuffer(widget, kBufferId3, kBufferId3,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
SendFrameCallbackForSurface(surface_id_);
// Release the second buffer via immediate explicit release. This should
// trigger OnSubmission for the second buffer with a null fence.
EXPECT_CALL(
mock_surface_gpu,
OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK,
Truly([](const auto& fence) { return fence.is_null(); })))
.Times(1);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
mock_surface->ReleaseBufferFenced(mock_surface->prev_attached_buffer(),
gfx::GpuFenceHandle());
mock_surface->SendFrameCallback();
});
// Let mojo messages from gpu to host to go through.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/);
}
// Tests that destroying a channel doesn't result in resetting surface state
// and buffers can be attached after the channel has been reinitialized.
TEST_P(WaylandBufferManagerTest,
CanSubmitBufferAfterChannelDestroyedAndInitialized) {
constexpr uint32_t kBufferId1 = 1;
const gfx::AcceleratedWidget widget = window_->GetWidget();
const gfx::Rect bounds = window_->GetBoundsInPixels();
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
auto mock_surface_gpu =
std::make_unique<MockSurfaceGpu>(buffer_manager_gpu_.get(), widget);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
EXPECT_CALL(*mock_surface_gpu.get(),
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(*mock_surface_gpu.get(), OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages from host to gpu to go through.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// The root surface shouldn't get null buffer attached.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
mock_surface->SendFrameCallback();
});
// After the channel has been destroyed and surface state has been reset, the
// interface should bind again and it still should be possible to attach
// buffers as WaylandBufferManagerHost::Surface::ResetSurfaceContents mustn't
// reset the state of |configured|.
manager_host_->OnChannelDestroyed();
manager_host_ = connection_->buffer_manager_host();
// Let mojo messages from host to gpu go through.
base::RunLoop().RunUntilIdle();
// The surface must has the buffer detached and all the buffers are destroyed.
// Release the fence as there is no further need to hold that as the client
// no longer expects that. Moreover, its next attach may result in a DCHECK,
// as the next buffer resource can be allocated on the same memory address
// resulting in a DCHECK when set_linux_buffer_release is called. The reason
// is that wl_resource_create calls internally calls malloc, which may reuse
// that memory.
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
server->GetObject<wl::MockSurface>(id)->ClearBufferReleases();
});
auto interface_ptr = manager_host_->BindInterface();
buffer_manager_gpu_->Initialize(std::move(interface_ptr), {},
/*supports_dma_buf=*/false,
/*supports_viewporter=*/true,
/*supports_acquire_fence=*/false,
/*supports_overlays=*/true,
/*supports_single_pixel_buffer=*/true);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
// The buffer must be attached.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
});
EXPECT_CALL(*mock_surface_gpu.get(),
OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK, _))
.Times(1);
EXPECT_CALL(*mock_surface_gpu.get(), OnPresentation(kBufferId1, _)).Times(1);
CommitBuffer(widget, kBufferId1, kBufferId1,
gfx::FrameData(delegate_.viz_seq()), bounds,
gfx::RoundedCornersF(), kDefaultScale, bounds);
// Let mojo messages from host to gpu to go through.
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/);
}
// Tests that destroying a channel results in attaching null buffers to the root
// surface, and hiding primary subsurface and overlay surfaces. This is required
// to make it possible for a GPU service to switch from hw acceleration to sw
// compositing. Otherwise, there will be frozen graphics represented by a
// primary subsurface as sw compositing uses the root surface to draw new
// frames. Verifies the fix for https://crbug.com/1201314
TEST_P(WaylandBufferManagerTest, HidesSubsurfacesOnChannelDestroyed) {
if (!connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
constexpr uint32_t kBufferId3 = 3;
const gfx::Rect bounds = window_->GetBoundsInPixels();
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
// Prepare a frame with one background buffer, one primary plane and one
// additional overlay plane. This will simulate hw accelerated compositing.
std::vector<wl::WaylandOverlayConfig> overlay_configs;
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(INT32_MIN, kBufferId1, bounds));
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(0, kBufferId2, bounds));
overlay_configs.emplace_back(
CreateBasicWaylandOverlayConfig(1, kBufferId3, bounds));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 1u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs));
// Let mojo messages from gpu to host to go through.
base::RunLoop().RunUntilIdle();
// 3 surfaces must exist - root surface, the primary subsurface and one
// additional overlay surface. All of them must have buffers attached.
EXPECT_EQ(1u, window_->wayland_subsurfaces().size());
PostToServerAndWait(
[id = surface_id_,
primary_subsurface_id =
window_->primary_subsurface()->wayland_surface()->get_surface_id(),
overlay_surface_id =
window_->wayland_subsurfaces()
.begin()
->get()
->wayland_surface()
->get_surface_id()](wl::TestWaylandServerThread* server) {
EXPECT_TRUE(server->GetObject<wl::MockSurface>(id)->attached_buffer());
EXPECT_TRUE(server->GetObject<wl::MockSurface>(primary_subsurface_id)
->attached_buffer());
EXPECT_TRUE(server->GetObject<wl::MockSurface>(overlay_surface_id)
->attached_buffer());
});
// Pretend that the channel gets destroyed because of some internal reason.
manager_host_->OnChannelDestroyed();
manager_host_ = connection_->buffer_manager_host();
// Let mojo messages from host to gpu go through.
base::RunLoop().RunUntilIdle();
// The root surface should still have the buffer attached....
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
EXPECT_TRUE(server->GetObject<wl::MockSurface>(id)->attached_buffer());
});
// ... and the primary and secondary subsurfaces must be hidden.
EXPECT_FALSE(window_->primary_subsurface()->IsVisible());
EXPECT_EQ(1u, window_->wayland_subsurfaces().size());
EXPECT_FALSE(window_->wayland_subsurfaces().begin()->get()->IsVisible());
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* root_surface = server->GetObject<wl::MockSurface>(id);
root_surface->ClearBufferReleases();
root_surface->SendFrameCallback();
});
auto interface_ptr = manager_host_->BindInterface();
buffer_manager_gpu_->Initialize(std::move(interface_ptr), {},
/*supports_dma_buf=*/false,
/*supports_viewporter=*/true,
/*supports_acquire_fence=*/false,
/*supports_overlays=*/true,
/*supports_single_pixel_buffer=*/true);
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
// Now, create only one buffer and attach that to the root surface. The
// primary subsurface and secondary subsurface must remain invisible.
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
});
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
std::vector<wl::WaylandOverlayConfig> overlay_configs2;
overlay_configs2.push_back(
CreateBasicWaylandOverlayConfig(INT32_MIN, kBufferId1, bounds));
buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), 2u,
gfx::FrameData(delegate_.viz_seq()),
std::move(overlay_configs2));
base::RunLoop().RunUntilIdle();
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
// The root surface should have the buffer detached.
EXPECT_TRUE(server->GetObject<wl::MockSurface>(id)->attached_buffer());
});
// The primary and secondary subsurfaces must remain hidden.
EXPECT_FALSE(window_->primary_subsurface()->IsVisible());
EXPECT_EQ(1u, window_->wayland_subsurfaces().size());
EXPECT_FALSE(window_->wayland_subsurfaces().begin()->get()->IsVisible());
}
TEST_P(WaylandBufferManagerTest,
DoesNotAttachAndCommitOnHideIfNoBuffersAttached) {
EXPECT_TRUE(window_->IsVisible());
PostToServerAndWait([id = surface_id_](wl::TestWaylandServerThread* server) {
auto* mock_surface = server->GetObject<wl::MockSurface>(id);
constexpr uint32_t kNumberOfCommits = 0;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
});
window_->Hide();
}
TEST_P(WaylandBufferManagerTest, HasOverlayPrioritizer) {
EXPECT_TRUE(connection_->overlay_prioritizer());
}
TEST_P(WaylandBufferManagerTest, CanSubmitOverlayPriority) {
if (!connection_->ShouldUseOverlayDelegation()) {
GTEST_SKIP();
}
std::vector<uint32_t> kBufferIds = {1, 2, 3};
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(),
window_->GetWidget());
PostToServerAndWait([](wl::TestWaylandServerThread* server) {
EXPECT_CALL(*server->zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
});
for (auto id : kBufferIds)
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, id);
PostToServerAndWait(
[size = kBufferIds.size()](wl::TestWaylandServerThread* server) {
auto* linux_dmabuf = server->zwp_linux_dmabuf_v1();
for (size_t i = 0; i < size; i++) {
zwp_linux_buffer_params_v1_send_created(
linux_dmabuf->buffer_params()[i]->resource(),
linux_dmabuf->buffer_params()[i]->buffer_resource());
}
});
std::vector<std::pair<gfx::OverlayPriorityHint, uint32_t>> priorities = {
{gfx::OverlayPriorityHint::kNone,
OVERLAY_PRIORITIZED_SURFACE_OVERLAY_PRIORITY_NONE},
{gfx::OverlayPriorityHint::kRegular,
OVERLAY_PRIORITIZED_SURFACE_OVERLAY_PRIORITY_REGULAR},
{gfx::OverlayPriorityHint::kVideo,