Skip to content

Commit d3fee72

Browse files
msisovChromium LUCI CQ
authored and
Chromium LUCI CQ
committed
ozone/wayland: discard presentation feedbacks after the threshold.
The frame management mechanism expects that there are no more than 25 submitted frames at the same time. However, this number can grow bigger if Wayland compositor doesn't send feedbacks (although the protocol is supported). One of such examples is Sway that runs on top of gnome/mutter, which doesn't expose wp_presentation if the underlying system doesn't support that. See bug reported to wlroots - https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3340 PS also the number of the threshold has been lowered to avoid FATAL:compositor_frame_sink_support.cc(734)] Check failed: pending_received_frame_times_.size() <= 25u (26 vs. 25) errors. (cherry picked from commit c0d0144) Bug: 1253566, 1187077, 1243133, 1254273 Change-Id: Ib2b222f40588212342dd88b2cb880647024788f4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3322619 Reviewed-by: Robert Kroeger <rjkroege@chromium.org> Reviewed-by: Kramer Ge <fangzhoug@chromium.org> Commit-Queue: Maksim Sisov <msisov@igalia.com> Cr-Original-Commit-Position: refs/heads/main@{#950548} Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3338605 Auto-Submit: Kramer Ge <fangzhoug@chromium.org> Commit-Queue: Robert Kroeger <rjkroege@chromium.org> Cr-Commit-Position: refs/branch-heads/4758@{#44} Cr-Branched-From: 4a2cf4b-refs/heads/main@{#950365}
1 parent d2bdf6f commit d3fee72

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

ui/ozone/platform/wayland/host/wayland_frame_manager.cc

+31-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace ui {
1818

1919
namespace {
2020

21+
constexpr uint32_t kMaxNumberOfFrames = 20u;
22+
2123
uint32_t GetPresentationKindFlags(uint32_t flags) {
2224
// Wayland spec has different meaning of VSync. In Chromium, VSync means to
2325
// update the begin frame vsync timing based on presentation feedback.
@@ -208,8 +210,8 @@ void WaylandFrameManager::PlayBackFrame(std::unique_ptr<WaylandFrame> frame) {
208210
if (!empty_frame)
209211
submitted_frames_.push_back(std::move(frame));
210212

211-
// This queue should be small - if not it's likely a bug.
212-
DCHECK_LE(submitted_frames_.size(), 25u);
213+
VerifyNumberOfSubmittedFrames();
214+
213215
MaybeProcessSubmittedFrames();
214216
}
215217

@@ -382,6 +384,31 @@ void WaylandFrameManager::OnPresentation(
382384
MaybeProcessSubmittedFrames();
383385
}
384386

387+
void WaylandFrameManager::VerifyNumberOfSubmittedFrames() {
388+
static constexpr uint32_t kLastThreeFrames = 3u;
389+
390+
// This queue should be small - if not it's likely a bug.
391+
//
392+
// Ideally this should be a DCHECK, but if the feedbacks are never sent (a bug
393+
// in the server, everything crashes).
394+
if (submitted_frames_.size() >= kMaxNumberOfFrames) {
395+
LOG(ERROR)
396+
<< "The server has buggy presentation feedback. Discarding all "
397+
"presentation feedback requests in all frames except the last "
398+
<< kLastThreeFrames << ".";
399+
// Leave three last frames in case if the server restores its behavior
400+
// (unlikely).
401+
for (auto it = submitted_frames_.begin();
402+
it < (submitted_frames_.end() - kLastThreeFrames); it++) {
403+
if (!(*it)->submission_acked || !(*it)->pending_feedback)
404+
break;
405+
DCHECK(!(*it)->feedback.has_value());
406+
(*it)->feedback = gfx::PresentationFeedback::Failure();
407+
(*it)->pending_feedback.reset();
408+
}
409+
}
410+
}
411+
385412
void WaylandFrameManager::OnExplicitBufferRelease(
386413
WaylandSurface* surface,
387414
struct wl_buffer* wl_buffer,
@@ -455,8 +482,6 @@ void WaylandFrameManager::MaybeProcessSubmittedFrames() {
455482
gfx::GpuFenceHandle());
456483
}
457484

458-
CHECK_LT(submitted_frames_.size(), 25u);
459-
460485
// Buffers may be released out of order, but we need to provide the
461486
// guarantee that OnSubmission will be called in order of buffer submission.
462487
for (auto iter = submitted_frames_.begin();
@@ -504,6 +529,8 @@ void WaylandFrameManager::MaybeProcessSubmittedFrames() {
504529
DCHECK(submitted_frames_.front()->submission_acked);
505530
submitted_frames_.pop_front();
506531
}
532+
533+
DCHECK_LE(submitted_frames_.size(), kMaxNumberOfFrames);
507534
}
508535

509536
void WaylandFrameManager::ProcessOldSubmittedFrame(

ui/ozone/platform/wayland/host/wayland_frame_manager.h

+4
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ class WaylandFrameManager {
156156
const gfx::PresentationFeedback& feedback,
157157
bool discarded = false);
158158

159+
// Verifies the number of submitted frames and discards pending presentation
160+
// feedbacks if the number is too big.
161+
void VerifyNumberOfSubmittedFrames();
162+
159163
WaylandWindow* const window_;
160164

161165
// When RecordFrame() is called, a Frame is pushed to |pending_frames_|. See

ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc

+96
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "ui/gfx/gpu_fence_handle.h"
2020
#include "ui/gfx/linux/drm_util_linux.h"
2121
#include "ui/gfx/overlay_priority_hint.h"
22+
#include "ui/gfx/presentation_feedback.h"
2223
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
2324
#include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
2425
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
@@ -2113,6 +2114,101 @@ TEST_P(WaylandBufferManagerTest, CanSetRoundedCorners) {
21132114
}
21142115
}
21152116

2117+
// Verifies that there are no more than certain number of submitted frames that
2118+
// wait presentation feedbacks. If the number of pending frames hit the
2119+
// threshold, the feedbacks are marked as failed and discarded. See the comments
2120+
// below in the test.
2121+
TEST_P(WaylandBufferManagerTest, FeedbacksAreDiscardedIfClientMisbehaves) {
2122+
auto* mock_wp_presentation = server_.EnsureWpPresentation();
2123+
ASSERT_TRUE(mock_wp_presentation);
2124+
2125+
// 2 buffers are enough.
2126+
constexpr uint32_t kBufferId1 = 1;
2127+
constexpr uint32_t kBufferId2 = 2;
2128+
2129+
const gfx::AcceleratedWidget widget = window_->GetWidget();
2130+
const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
2131+
window_->SetBounds(bounds);
2132+
2133+
MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
2134+
2135+
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
2136+
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
2137+
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
2138+
CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
2139+
2140+
Sync();
2141+
2142+
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
2143+
false /* fail */);
2144+
2145+
auto* mock_surface = server_.GetObject<wl::MockSurface>(
2146+
window_->root_surface()->GetSurfaceId());
2147+
2148+
// There will be 235 frames/commits.
2149+
constexpr uint32_t kNumberOfCommits = 235u;
2150+
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
2151+
EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
2152+
EXPECT_CALL(*mock_wp_presentation, Feedback(_, _, _, _))
2153+
.Times(kNumberOfCommits);
2154+
EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
2155+
2156+
// The presentation feedbacks should fail after first 20 commits (that's the
2157+
// threshold that WaylandFrameManager maintains). Next, the presentation
2158+
// feedbacks will fail every consequent 17 commits as 3 frames out of 20
2159+
// previous frames that WaylandFrameManager stores are always preserved in
2160+
// case if the client restores the behavior (which is very unlikely).
2161+
uint32_t expect_presentation_failure_on_commit_seq = 20u;
2162+
// Chooses the next buffer id that should be committed.
2163+
uint32_t next_buffer_id_commit = 0;
2164+
// Specifies the expected number of failing feedbacks if the client
2165+
// misbehaves.
2166+
constexpr uint32_t kExpectedFailedFeedbacks = 17u;
2167+
for (auto commit_seq = 1u; commit_seq <= kNumberOfCommits; commit_seq++) {
2168+
// All the other expectations must come in order.
2169+
if (next_buffer_id_commit == kBufferId1)
2170+
next_buffer_id_commit = kBufferId2;
2171+
else
2172+
next_buffer_id_commit = kBufferId1;
2173+
2174+
EXPECT_CALL(mock_surface_gpu, OnSubmission(next_buffer_id_commit,
2175+
gfx::SwapResult::SWAP_ACK, _))
2176+
.Times(1);
2177+
2178+
if (commit_seq % expect_presentation_failure_on_commit_seq == 0) {
2179+
EXPECT_CALL(mock_surface_gpu,
2180+
OnPresentation(_, gfx::PresentationFeedback::Failure()))
2181+
.Times(kExpectedFailedFeedbacks);
2182+
// See comment near |expect_presentation_failure_on_commit_seq|.
2183+
expect_presentation_failure_on_commit_seq += kExpectedFailedFeedbacks;
2184+
} else {
2185+
// The client misbehaves and doesn't send presentation feedbacks. The
2186+
// frame manager doesn't mark the feedbacks as failed until the threshold
2187+
// is hit.
2188+
EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
2189+
}
2190+
2191+
buffer_manager_gpu_->CommitBuffer(widget, next_buffer_id_commit, bounds,
2192+
kDefaultScale, bounds);
2193+
2194+
Sync();
2195+
2196+
if (auto* buffer = mock_surface->prev_attached_buffer())
2197+
mock_surface->ReleaseBuffer(buffer);
2198+
2199+
wl_resource_destroy(mock_wp_presentation->ReleasePresentationCallback());
2200+
2201+
mock_surface->SendFrameCallback();
2202+
2203+
Sync();
2204+
2205+
testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
2206+
}
2207+
2208+
DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
2209+
DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
2210+
}
2211+
21162212
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
21172213
WaylandBufferManagerTest,
21182214
Values(wl::ServerConfig{

0 commit comments

Comments
 (0)