Commit a489cfcd authored by Kramer Ge's avatar Kramer Ge Committed by Chromium LUCI CQ

[ozone/wayland] Ensure root_surface is committed last in a frame

wl_surface.commits of a frame can happen asynchronously due to wl_buffer
import delay, frame callbacks, etc. This asynchrony can cause visual
glitches when the root_surface commits before its subsurfaces commit.

This CL ensures that a root_surface commit always happens after other
commits in a frame.

Change-Id: I7c83e122c4cdee42e6b8aca74875fa71b6c1ab5a
Bug: 1144179
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2547395
Commit-Queue: Kramer Ge <fangzhoug@chromium.org>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarMaksim Sisov (GMT+2) <msisov@igalia.com>
Cr-Commit-Position: refs/heads/master@{#834327}
parent 9ea4170b
...@@ -8,14 +8,8 @@ ...@@ -8,14 +8,8 @@
namespace ui { namespace ui {
const base::Feature kWaylandOverlayDelegation { const base::Feature kWaylandOverlayDelegation{"WaylandOverlayDelegation",
"WaylandOverlayDelegation", base::FEATURE_ENABLED_BY_DEFAULT};
#if BUILDFLAG(IS_CHROMEOS_LACROS)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
#endif
};
bool IsWaylandOverlayDelegationEnabled() { bool IsWaylandOverlayDelegationEnabled() {
return base::FeatureList::IsEnabled(kWaylandOverlayDelegation); return base::FeatureList::IsEnabled(kWaylandOverlayDelegation);
......
...@@ -203,222 +203,11 @@ class WaylandSurfaceFactoryTest : public WaylandTest { ...@@ -203,222 +203,11 @@ class WaylandSurfaceFactoryTest : public WaylandTest {
DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactoryTest); DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactoryTest);
}; };
TEST_P(WaylandSurfaceFactoryTest,
GbmSurfacelessWaylandCheckOrderOfCallbacksTest) {
gl::SetGLImplementation(gl::kGLImplementationEGLGLES2);
buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>());
auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2);
auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
EXPECT_TRUE(gl_surface);
gl_surface->SetRelyOnImplicitSync();
static_cast<ui::GbmSurfacelessWayland*>(gl_surface.get())
->SetNoGLFlushForTests();
// Expect to create 3 buffers.
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
// Create buffers and FakeGlImageNativePixmap.
std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image;
for (int i = 0; i < 3; ++i) {
auto native_pixmap = surface_factory_->CreateNativePixmap(
widget_, nullptr, window_->GetBounds().size(),
gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT);
fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>(
native_pixmap, window_->GetBounds().size()));
Sync();
// Create one buffer at a time.
auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params();
DCHECK_EQ(params_vector.size(), 1u);
zwp_linux_buffer_params_v1_send_created(
params_vector.front()->resource(),
params_vector.front()->buffer_resource());
Sync();
}
// Now, schedule 3 buffers for swap.
auto* mock_surface = server_.GetObject<wl::MockSurface>(
window_->root_surface()->GetSurfaceId());
CallbacksHelper cbs_helper;
// Submit all the available buffers.
for (const auto& gl_image : fake_gl_image) {
// Associate each image with swap id so that we could track released
// buffers.
auto swap_id = cbs_helper.GetNextLocalSwapId();
// Associate the image with the next swap id so that we can easily track if
// it became free to reuse.
gl_image->AssociateWithSwapId(swap_id);
// And set it to be busy...
gl_image->SetBusy(true);
// Prepare overlay plane.
gl_surface->ScheduleOverlayPlane(
INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL,
gl_image.get(), window_->GetBounds(), {}, false, nullptr);
std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images;
gl_images.push_back(gl_image);
// And submit each image. They will be executed in FIFO manner.
gl_surface->SwapBuffersAsync(
base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync,
base::Unretained(&cbs_helper), swap_id, gl_images),
base::BindOnce(&CallbacksHelper::BufferPresented,
base::Unretained(&cbs_helper), swap_id));
}
// Let's sync so that 1) GbmSurfacelessWayland submits the buffer according to
// internal queue and fake server processes the request.
// Also, we expect only one buffer to be committed.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
Sync();
testing::Mock::VerifyAndClearExpectations(&mock_surface);
// Give mojo the chance to pass the callbacks.
base::RunLoop().RunUntilIdle();
// We have just received Attach/DamageBuffer/Commit for buffer with swap
// id=0u. The SwapCompletionCallback must be executed automatically as long as
// we didn't have any buffers attached to the surface before.
EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 0u);
cbs_helper.ResetLastFinishedSwapId();
for (const auto& gl_image : fake_gl_image) {
// All the images except the first one, which was associated with swap
// id=0u, must be busy and not displayed. The first one must be displayed.
if (gl_image->GetAssociateWithSwapId() == 0u) {
EXPECT_FALSE(gl_image->busy());
EXPECT_TRUE(gl_image->displayed());
} else {
EXPECT_TRUE(gl_image->busy());
EXPECT_FALSE(gl_image->displayed());
}
}
// Expect buffer for swap with id=1u to be committed.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
// Send the frame callback so that pending buffer for swap id=1u is processed
// and swapped.
mock_surface->SendFrameCallback();
Sync();
// Give mojo the chance to pass the callbacks.
base::RunLoop().RunUntilIdle();
// Even though the second buffer was submitted, we mustn't receive
// SwapCompletionCallback until the previous buffer is released.
EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(),
std::numeric_limits<uint32_t>::max());
// This will result in Wayland server releasing previously attached buffer for
// swap id=0u and calling OnSubmission for buffer with swap id=1u.
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
// Give mojo the chance to pass the callbacks.
base::RunLoop().RunUntilIdle();
// We expect only one buffer to be released. Thus, the last swap id must be
// 0 as we waited until next buffer was attached to the surface.
EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 1u);
// Reset to test further swap ids.
cbs_helper.ResetLastFinishedSwapId();
for (const auto& gl_image : fake_gl_image) {
// The first image is not displayed and not busy, the second is displayed
// and not busy. And others are not display and busy.
if (gl_image->GetAssociateWithSwapId() == 0u) {
EXPECT_FALSE(gl_image->busy());
EXPECT_FALSE(gl_image->displayed());
} else if (gl_image->GetAssociateWithSwapId() == 1u) {
EXPECT_FALSE(gl_image->busy());
EXPECT_TRUE(gl_image->displayed());
} else {
EXPECT_TRUE(gl_image->busy());
EXPECT_FALSE(gl_image->displayed());
}
}
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Commit()).Times(1);
// Send the frame callback, so that the pending buffer with swap id=2u can
// be processed.
mock_surface->SendFrameCallback();
Sync();
// Give mojo the chance to pass the callbacks.
base::RunLoop().RunUntilIdle();
// Even though the second buffer was submitted, we mustn't receive
// SwapCompletionCallback until the previous buffer is released.
EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(),
std::numeric_limits<uint32_t>::max());
// This will result in Wayland server releasing previously attached buffer for
// swap id=1u and calling OnSubmission for buffer with swap id=2u.
mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
// Give mojo the chance to pass the callbacks.
base::RunLoop().RunUntilIdle();
// We should receive next callbacks for the next swap id.
EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 2u);
cbs_helper.ResetLastFinishedSwapId();
// All images must be free now and the last one is displayed.
for (const auto& gl_image : fake_gl_image) {
if (gl_image->GetAssociateWithSwapId() == 2u) {
EXPECT_TRUE(gl_image->displayed());
EXPECT_FALSE(gl_image->busy());
} else {
EXPECT_FALSE(gl_image->displayed());
EXPECT_FALSE(gl_image->busy());
}
}
// There are no buffers left. Send last frame callback and verify that.
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
// Send a frame callback so that the WaylandBufferManagerHost processes the
// pending buffers if any exist.
mock_surface->SendFrameCallback();
}
TEST_P(WaylandSurfaceFactoryTest, TEST_P(WaylandSurfaceFactoryTest,
GbmSurfacelessWaylandCommitOverlaysCallbacksTest) { GbmSurfacelessWaylandCommitOverlaysCallbacksTest) {
// GbmSurfacelessWaylandCheckOrderOfCallbacksTest tests with one buffer per // This tests multiple buffers per-frame and order of SwapCompletionCallbacks.
// frame. This tests multiple buffers per-frame and order of // Even when all OnSubmission from later frames are called, their
// SwapCompletionCallbacks. Even when all OnSubmission from later frames are // SwapCompletionCallbacks should not run until previous frames'
// called, their SwapCompletionCallbacks should not run until previous frames'
// SwapCompletionCallbacks run. // SwapCompletionCallbacks run.
gl::SetGLImplementation(gl::kGLImplementationEGLGLES2); gl::SetGLImplementation(gl::kGLImplementationEGLGLES2);
...@@ -493,10 +282,10 @@ TEST_P(WaylandSurfaceFactoryTest, ...@@ -493,10 +282,10 @@ TEST_P(WaylandSurfaceFactoryTest,
// Also, we expect only one buffer to be committed. // Also, we expect only one buffer to be committed.
EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1);
EXPECT_CALL(*root_surface, Frame(_)).Times(1); EXPECT_CALL(*root_surface, Frame(_)).Times(0);
EXPECT_CALL(*root_surface, Commit()).Times(1); EXPECT_CALL(*root_surface, Commit()).Times(1);
Sync(); Sync();
...@@ -553,15 +342,15 @@ TEST_P(WaylandSurfaceFactoryTest, ...@@ -553,15 +342,15 @@ TEST_P(WaylandSurfaceFactoryTest,
// Expect one buffer to be committed. // Expect one buffer to be committed.
EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1);
EXPECT_CALL(*root_surface, Frame(_)).Times(1); EXPECT_CALL(*root_surface, Frame(_)).Times(0);
EXPECT_CALL(*root_surface, Commit()).Times(1); EXPECT_CALL(*root_surface, Commit()).Times(1);
// Send the frame callback so that pending buffer for swap id=1u is processed // Send the frame callback so that pending buffer for swap id=1u is processed
// and swapped. // and swapped.
root_surface->SendFrameCallback(); mock_primary_surface->SendFrameCallback();
Sync(); Sync();
...@@ -620,12 +409,12 @@ TEST_P(WaylandSurfaceFactoryTest, ...@@ -620,12 +409,12 @@ TEST_P(WaylandSurfaceFactoryTest,
EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(0); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_primary_surface, Commit()).Times(0); EXPECT_CALL(*mock_primary_surface, Commit()).Times(0);
// Expect root surface to be committed. // Expect root surface to be committed.
EXPECT_CALL(*root_surface, Frame(_)).Times(1); EXPECT_CALL(*root_surface, Frame(_)).Times(0);
EXPECT_CALL(*root_surface, Commit()).Times(1); EXPECT_CALL(*root_surface, Commit()).Times(1);
// Send the frame callback so that pending buffer for swap id=2u is processed // Send the frame callback so that pending buffer for swap id=2u is processed
// and swapped. // and swapped.
root_surface->SendFrameCallback(); mock_primary_surface->SendFrameCallback();
Sync(); Sync();
...@@ -754,10 +543,10 @@ TEST_P(WaylandSurfaceFactoryTest, ...@@ -754,10 +543,10 @@ TEST_P(WaylandSurfaceFactoryTest,
// Also, we expect primary buffer to be committed. // Also, we expect primary buffer to be committed.
EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1);
EXPECT_CALL(*root_surface, Frame(_)).Times(1); EXPECT_CALL(*root_surface, Frame(_)).Times(0);
EXPECT_CALL(*root_surface, Commit()).Times(1); EXPECT_CALL(*root_surface, Commit()).Times(1);
Sync(); Sync();
...@@ -829,23 +618,24 @@ TEST_P(WaylandSurfaceFactoryTest, ...@@ -829,23 +618,24 @@ TEST_P(WaylandSurfaceFactoryTest,
// Expect primary buffer to be committed. // Expect primary buffer to be committed.
EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1);
// Expect overlay buffer to be committed. // Expect overlay buffer to be committed.
EXPECT_CALL(*mock_overlay_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_overlay_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_overlay_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_overlay_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_overlay_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_overlay_surface, DamageBuffer(_, _, _, _)).Times(1);
EXPECT_CALL(*mock_overlay_surface, Commit()).Times(1); EXPECT_CALL(*mock_overlay_surface, Commit()).Times(1);
// Expect root surface to be committed without buffer. // Expect root surface to be committed without buffer.
EXPECT_CALL(*root_surface, Frame(_)).Times(1); EXPECT_CALL(*root_surface, Frame(_)).Times(0);
EXPECT_CALL(*root_surface, Commit()).Times(1); EXPECT_CALL(*root_surface, Commit()).Times(1);
// Send the frame callback so that pending buffer for swap id=1u is processed // Send the frame callback so that pending buffer for swap id=1u is processed
// and swapped. // and swapped.
root_surface->SendFrameCallback(); mock_overlay_surface->SendFrameCallback();
mock_primary_surface->SendFrameCallback();
Sync(); Sync();
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
#include <presentation-time-client-protocol.h> #include <presentation-time-client-protocol.h>
#include <memory> #include <memory>
#include "base/bind.h"
#include "base/i18n/number_formatting.h" #include "base/i18n/number_formatting.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h" #include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h" #include "base/task/current_thread.h"
...@@ -59,6 +61,34 @@ std::string NumberToString(uint32_t number) { ...@@ -59,6 +61,34 @@ std::string NumberToString(uint32_t number) {
} // namespace } // namespace
struct WaylandBufferManagerHost::Frame {
explicit Frame(WaylandSurface* root_surface)
: root_surface(root_surface), weak_factory(this) {}
void IncrementPendingActions() { ++pending_actions; }
void PendingActionComplete() {
CHECK_GT(pending_actions, 0u);
if (!--pending_actions)
std::move(frame_commit_cb).Run();
}
// |root_surface| and |buffer_id| are saved so this Frame can be destroyed to
// prevent running |frame_commit_cb| in case the corresponding surface/buffer
// is removed.
WaylandSurface* root_surface;
uint32_t buffer_id = 0u;
// Number of actions to be completed before |frame_commit_cb| is run
// automatically. Such actions include:
// 1) End Frame;
// 2) Commit of a subsurface in this frame;
size_t pending_actions = 0u;
// This runs WaylandBufferManagerHost::Surface::CommitBuffer() of
// |root_surface| with |buffer_id|.
base::OnceCallback<bool()> frame_commit_cb;
base::WeakPtrFactory<WaylandBufferManagerHost::Frame> weak_factory;
};
class WaylandBufferManagerHost::Surface { class WaylandBufferManagerHost::Surface {
public: public:
Surface(WaylandSurface* wayland_surface, Surface(WaylandSurface* wayland_surface,
...@@ -73,7 +103,9 @@ class WaylandBufferManagerHost::Surface { ...@@ -73,7 +103,9 @@ class WaylandBufferManagerHost::Surface {
uint32_t buffer_id, uint32_t buffer_id,
const gfx::Rect& damage_region, const gfx::Rect& damage_region,
bool wait_for_frame_callback, bool wait_for_frame_callback,
base::OnceClosure post_commit_cb,
gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle()) { gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle()) {
DCHECK(!post_commit_cb.is_null());
// The window has already been destroyed. // The window has already been destroyed.
if (!wayland_surface_) if (!wayland_surface_)
return true; return true;
...@@ -81,7 +113,8 @@ class WaylandBufferManagerHost::Surface { ...@@ -81,7 +113,8 @@ class WaylandBufferManagerHost::Surface {
// This is a buffer-less commit, do not lookup buffers. // This is a buffer-less commit, do not lookup buffers.
if (buffer_id == kInvalidBufferId) { if (buffer_id == kInvalidBufferId) {
DCHECK(access_fence_handle.is_null()); DCHECK(access_fence_handle.is_null());
pending_commits_.push_back({nullptr, wait_for_frame_callback, nullptr}); pending_commits_.push_back({nullptr, wait_for_frame_callback, nullptr,
std::move(post_commit_cb)});
MaybeProcessPendingBuffer(); MaybeProcessPendingBuffer();
return true; return true;
} }
...@@ -113,7 +146,8 @@ class WaylandBufferManagerHost::Surface { ...@@ -113,7 +146,8 @@ class WaylandBufferManagerHost::Surface {
pending_commits_.push_back( pending_commits_.push_back(
{buffer, wait_for_frame_callback, {buffer, wait_for_frame_callback,
std::make_unique<gfx::GpuFence>(std::move(access_fence_handle))}); std::make_unique<gfx::GpuFence>(std::move(access_fence_handle)),
std::move(post_commit_cb)});
MaybeProcessPendingBuffer(); MaybeProcessPendingBuffer();
return true; return true;
} }
...@@ -128,8 +162,10 @@ class WaylandBufferManagerHost::Surface { ...@@ -128,8 +162,10 @@ class WaylandBufferManagerHost::Surface {
MaybeProcessSubmittedBuffers(); MaybeProcessSubmittedBuffers();
for (auto it = pending_commits_.begin(); it != pending_commits_.end(); for (auto it = pending_commits_.begin(); it != pending_commits_.end();
++it) { ++it) {
if (it->buffer == buffer) if (it->buffer == buffer) {
std::move(it->post_commit_cb).Run();
pending_commits_.erase(it++); pending_commits_.erase(it++);
}
} }
} }
...@@ -161,6 +197,8 @@ class WaylandBufferManagerHost::Surface { ...@@ -161,6 +197,8 @@ class WaylandBufferManagerHost::Surface {
ResetSurfaceContents(); ResetSurfaceContents();
submitted_buffers_.clear(); submitted_buffers_.clear();
for (auto& pending_commit : pending_commits_)
std::move(pending_commit.post_commit_cb).Run();
pending_commits_.clear(); pending_commits_.clear();
connection_->ScheduleFlush(); connection_->ScheduleFlush();
...@@ -236,6 +274,8 @@ class WaylandBufferManagerHost::Surface { ...@@ -236,6 +274,8 @@ class WaylandBufferManagerHost::Surface {
// Fence to wait on before the |buffer| content is available to read by // Fence to wait on before the |buffer| content is available to read by
// Wayland host. // Wayland host.
std::unique_ptr<gfx::GpuFence> access_fence; std::unique_ptr<gfx::GpuFence> access_fence;
// Callback to run once this commit is applied.
base::OnceClosure post_commit_cb;
}; };
bool CommitBufferInternal(WaylandBuffer* buffer, bool CommitBufferInternal(WaylandBuffer* buffer,
...@@ -581,6 +621,7 @@ class WaylandBufferManagerHost::Surface { ...@@ -581,6 +621,7 @@ class WaylandBufferManagerHost::Surface {
if (commit.wait_for_callback) if (commit.wait_for_callback)
SetupFrameCallback(); SetupFrameCallback();
CommitSurface(); CommitSurface();
std::move(commit.post_commit_cb).Run();
connection_->ScheduleFlush(); connection_->ScheduleFlush();
MaybeProcessSubmittedBuffers(); MaybeProcessSubmittedBuffers();
return; return;
...@@ -588,6 +629,7 @@ class WaylandBufferManagerHost::Surface { ...@@ -588,6 +629,7 @@ class WaylandBufferManagerHost::Surface {
CommitBufferInternal(commit.buffer, commit.wait_for_callback, CommitBufferInternal(commit.buffer, commit.wait_for_callback,
commit.access_fence->GetGpuFenceHandle()); commit.access_fence->GetGpuFenceHandle());
std::move(commit.post_commit_cb).Run();
} }
// Widget this helper surface backs and has 1:1 relationship with the // Widget this helper surface backs and has 1:1 relationship with the
...@@ -660,6 +702,8 @@ void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) { ...@@ -660,6 +702,8 @@ void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) {
surface_graveyard_.emplace_back(std::move(it->second)); surface_graveyard_.emplace_back(std::move(it->second));
} }
surfaces_.erase(it); surfaces_.erase(it);
RemovePendingFrames(window->root_surface(), 0u);
} }
void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) { void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) {
...@@ -690,6 +734,8 @@ void WaylandBufferManagerHost::OnSubsurfaceRemoved( ...@@ -690,6 +734,8 @@ void WaylandBufferManagerHost::OnSubsurfaceRemoved(
surface_graveyard_.emplace_back(std::move(it->second)); surface_graveyard_.emplace_back(std::move(it->second));
} }
surfaces_.erase(it); surfaces_.erase(it);
RemovePendingFrames(subsurface->wayland_surface(), 0u);
} }
void WaylandBufferManagerHost::SetSurfaceConfigured(WaylandSurface* surface) { void WaylandBufferManagerHost::SetSurfaceConfigured(WaylandSurface* surface) {
...@@ -724,6 +770,7 @@ void WaylandBufferManagerHost::OnChannelDestroyed() { ...@@ -724,6 +770,7 @@ void WaylandBufferManagerHost::OnChannelDestroyed() {
surface_pair.second->ClearState(); surface_pair.second->ClearState();
anonymous_buffers_.clear(); anonymous_buffers_.clear();
pending_frames_.clear();
} }
wl::BufferFormatsWithModifiersMap wl::BufferFormatsWithModifiersMap
...@@ -824,11 +871,50 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, ...@@ -824,11 +871,50 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd,
connection_->ScheduleFlush(); connection_->ScheduleFlush();
} }
void WaylandBufferManagerHost::StartFrame(WaylandSurface* root_surface) {
RemovePendingFrames(nullptr, 0u);
DCHECK_LE(pending_frames_.size(), 10u);
pending_frames_.push_back(
std::make_unique<WaylandBufferManagerHost::Frame>(root_surface));
pending_frames_.back()->IncrementPendingActions();
}
void WaylandBufferManagerHost::EndFrame(uint32_t buffer_id) {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK(!pending_frames_.empty());
pending_frames_.back()->buffer_id = buffer_id;
Surface* surface = GetSurface(pending_frames_.back()->root_surface);
if (!surface) {
pending_frames_.erase(--pending_frames_.end());
return;
}
base::OnceClosure post_commit_cb = base::DoNothing();
pending_frames_.back()->frame_commit_cb =
base::BindOnce(&WaylandBufferManagerHost::Surface::CommitBuffer,
base::Unretained(surface), buffer_id, gfx::Rect(), false,
std::move(post_commit_cb), gfx::GpuFenceHandle());
pending_frames_.back()->PendingActionComplete();
}
void WaylandBufferManagerHost::RemovePendingFrames(WaylandSurface* root_surface,
uint32_t buffer_id) {
base::EraseIf(pending_frames_,
[buffer_id, root_surface](const std::unique_ptr<Frame>& frame) {
return !frame->pending_actions ||
(frame->buffer_id == buffer_id && buffer_id) ||
(frame->root_surface == root_surface && root_surface);
});
}
bool WaylandBufferManagerHost::CommitBufferInternal( bool WaylandBufferManagerHost::CommitBufferInternal(
WaylandSurface* wayland_surface, WaylandSurface* wayland_surface,
uint32_t buffer_id, uint32_t buffer_id,
const gfx::Rect& damage_region, const gfx::Rect& damage_region,
bool wait_for_frame_callback, bool wait_for_frame_callback,
bool commit_synced_subsurface,
gfx::GpuFenceHandle access_fence_handle) { gfx::GpuFenceHandle access_fence_handle) {
DCHECK(base::CurrentUIThread::IsSet()); DCHECK(base::CurrentUIThread::IsSet());
...@@ -836,7 +922,16 @@ bool WaylandBufferManagerHost::CommitBufferInternal( ...@@ -836,7 +922,16 @@ bool WaylandBufferManagerHost::CommitBufferInternal(
if (!surface || !ValidateBufferIdFromGpu(buffer_id)) if (!surface || !ValidateBufferIdFromGpu(buffer_id))
return false; return false;
base::OnceClosure subsurface_committed_cb = base::DoNothing();
if (!pending_frames_.empty() && commit_synced_subsurface) {
pending_frames_.back()->IncrementPendingActions();
subsurface_committed_cb.Reset();
subsurface_committed_cb =
base::BindOnce(&WaylandBufferManagerHost::Frame::PendingActionComplete,
pending_frames_.back()->weak_factory.GetWeakPtr());
}
if (!surface->CommitBuffer(buffer_id, damage_region, wait_for_frame_callback, if (!surface->CommitBuffer(buffer_id, damage_region, wait_for_frame_callback,
std::move(subsurface_committed_cb),
std::move(access_fence_handle))) { std::move(access_fence_handle))) {
error_message_ = error_message_ =
base::StrCat({"Buffer with ", NumberToString(buffer_id), base::StrCat({"Buffer with ", NumberToString(buffer_id),
...@@ -848,24 +943,6 @@ bool WaylandBufferManagerHost::CommitBufferInternal( ...@@ -848,24 +943,6 @@ bool WaylandBufferManagerHost::CommitBufferInternal(
return true; return true;
} }
bool WaylandBufferManagerHost::CommitWithoutBufferInternal(
WaylandSurface* wayland_surface,
bool wait_for_frame_callback) {
DCHECK(base::CurrentUIThread::IsSet());
Surface* surface = GetSurface(wayland_surface);
if (!surface)
return false;
bool result = surface->CommitBuffer(kInvalidBufferId, gfx::Rect(),
wait_for_frame_callback);
DCHECK(result);
if (!error_message_.empty())
TerminateGpuProcess();
return true;
}
void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget, void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget,
uint32_t buffer_id, uint32_t buffer_id,
const gfx::Rect& damage_region) { const gfx::Rect& damage_region) {
...@@ -977,6 +1054,8 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget, ...@@ -977,6 +1054,8 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget,
} }
} }
RemovePendingFrames(nullptr, buffer_id);
// Ensure that we can't destroy more than 1 buffer. This can be 0 as well // Ensure that we can't destroy more than 1 buffer. This can be 0 as well
// if no buffers are destroyed. // if no buffers are destroyed.
DCHECK_LE(destroyed_count, 1u); DCHECK_LE(destroyed_count, 1u);
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_
#include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
...@@ -155,6 +154,14 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -155,6 +154,14 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
gfx::AcceleratedWidget widget, gfx::AcceleratedWidget widget,
std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) override; std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) override;
// Called by WaylandWindow to start recording a frame. This helps record the
// number of subsurface commits needed to finish for this frame before
// |root_surface| can be committed.
// This pairs with an EndCommitFrame(). Every CommitBufferInternal() in
// between increases the number of needed pending commits by 1.
void StartFrame(WaylandSurface* root_surface);
void EndFrame(uint32_t buffer_id = 0u);
// Called by the WaylandWindow and asks to attach a wl_buffer with a // Called by the WaylandWindow and asks to attach a wl_buffer with a
// |buffer_id| to a WaylandSurface. // |buffer_id| to a WaylandSurface.
// Calls OnSubmission and OnPresentation on successful swap and pixels // Calls OnSubmission and OnPresentation on successful swap and pixels
...@@ -173,14 +180,9 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -173,14 +180,9 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
uint32_t buffer_id, uint32_t buffer_id,
const gfx::Rect& damage_region, const gfx::Rect& damage_region,
bool wait_for_frame_callback = true, bool wait_for_frame_callback = true,
bool commit_synced_subsurface = false,
gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle()); gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle());
// Does a wl_surface commit without attaching any buffers. This commit will
// still wait for previous wl_frame_callback. Similar to above but for
// commits that do not change the root_surface.
bool CommitWithoutBufferInternal(WaylandSurface* wayland_surface,
bool wait_for_frame_callback = true);
// When a surface is hidden, the client may want to detach the buffer attached // When a surface is hidden, the client may want to detach the buffer attached
// to the surface to ensure Wayland does not present those contents and do not // to the surface to ensure Wayland does not present those contents and do not
// composite in a wrong way. Otherwise, users may see the contents of a hidden // composite in a wrong way. Otherwise, users may see the contents of a hidden
...@@ -196,10 +198,17 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -196,10 +198,17 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
// presentation callbacks for that surface. // presentation callbacks for that surface.
class Surface; class Surface;
// This represents a frame that consists of state changes to multiple
// synchronized wl_surfaces that are in the same hierarchy. It defers
// committing the root surface until all child surfaces' states are ready.
struct Frame;
bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id); bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id);
Surface* GetSurface(WaylandSurface* wayland_surface) const; Surface* GetSurface(WaylandSurface* wayland_surface) const;
void RemovePendingFrames(WaylandSurface* root_surface, uint32_t buffer_id);
// Validates data sent from GPU. If invalid, returns false and sets an error // Validates data sent from GPU. If invalid, returns false and sets an error
// message to |error_message_|. // message to |error_message_|.
bool ValidateDataFromGpu(const base::ScopedFD& file, bool ValidateDataFromGpu(const base::ScopedFD& file,
...@@ -237,6 +246,10 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -237,6 +246,10 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
base::flat_map<WaylandSurface*, std::unique_ptr<Surface>> surfaces_; base::flat_map<WaylandSurface*, std::unique_ptr<Surface>> surfaces_;
// When StartCommitFrame() is called, a Frame is pushed to
// |pending_frames_|. See StartCommitFrame().
std::vector<std::unique_ptr<Frame>> pending_frames_;
// When a WaylandWindow/WaylandSubsurface is removed, its corresponding // When a WaylandWindow/WaylandSubsurface is removed, its corresponding
// Surface may still have an un-released buffer and un-acked presentation. // Surface may still have an un-released buffer and un-acked presentation.
// Thus, we keep removed surfaces in the graveyard. It's safe to delete them // Thus, we keep removed surfaces in the graveyard. It's safe to delete them
......
...@@ -582,6 +582,9 @@ bool WaylandWindow::CommitOverlays( ...@@ -582,6 +582,9 @@ bool WaylandWindow::CommitOverlays(
if (!ArrangeSubsurfaceStack(above, below)) if (!ArrangeSubsurfaceStack(above, below))
return false; return false;
if (wayland_overlay_delegation_enabled_)
connection_->buffer_manager_host()->StartFrame(root_surface());
{ {
// Iterate through |subsurface_stack_below_|, setup subsurfaces and place // Iterate through |subsurface_stack_below_|, setup subsurfaces and place
// them in corresponding order. Commit wl_buffers once a subsurface is // them in corresponding order. Commit wl_buffers once a subsurface is
...@@ -606,7 +609,8 @@ bool WaylandWindow::CommitOverlays( ...@@ -606,7 +609,8 @@ bool WaylandWindow::CommitOverlays(
nullptr, reference_above); nullptr, reference_above);
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
(*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
/*wait_for_frame_callback=*/false, /*wait_for_frame_callback=*/true,
/*commit_synced_subsurface=*/true,
std::move((*overlay_iter)->access_fence_handle)); std::move((*overlay_iter)->access_fence_handle));
} else { } else {
// If there're more subsurfaces requested that we don't need at the // If there're more subsurfaces requested that we don't need at the
...@@ -636,7 +640,8 @@ bool WaylandWindow::CommitOverlays( ...@@ -636,7 +640,8 @@ bool WaylandWindow::CommitOverlays(
reference_below, nullptr); reference_below, nullptr);
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
(*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
/*wait_for_frame_callback=*/false, /*wait_for_frame_callback=*/true,
/*commit_synced_subsurface=*/true,
std::move((*overlay_iter)->access_fence_handle)); std::move((*overlay_iter)->access_fence_handle));
} else { } else {
// If there're more subsurfaces requested that we don't need at the // If there're more subsurfaces requested that we don't need at the
...@@ -660,7 +665,8 @@ bool WaylandWindow::CommitOverlays( ...@@ -660,7 +665,8 @@ bool WaylandWindow::CommitOverlays(
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
primary_subsurface_->wayland_surface(), (*split)->buffer_id, primary_subsurface_->wayland_surface(), (*split)->buffer_id,
(*split)->damage_region, (*split)->damage_region,
/*wait_for_frame_callback=*/false, /*wait_for_frame_callback=*/true,
/*commit_synced_subsurface=*/true,
std::move((*split)->access_fence_handle)); std::move((*split)->access_fence_handle));
} }
...@@ -672,15 +678,12 @@ bool WaylandWindow::CommitOverlays( ...@@ -672,15 +678,12 @@ bool WaylandWindow::CommitOverlays(
} }
if (should_attach_background_buffer_) { if (should_attach_background_buffer_) {
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->EndFrame(background_buffer_id_);
root_surface(), background_buffer_id_, /*damage_region=*/gfx::Rect(),
/*wait_for_frame_callback=*/true);
should_attach_background_buffer_ = false; should_attach_background_buffer_ = false;
} else { } else {
// Subsurfaces are set to sync, above surface configs will only take effect // Subsurfaces are set to sync, above surface configs will only take effect
// when root_surface is committed. // when root_surface is committed.
connection_->buffer_manager_host()->CommitWithoutBufferInternal( connection_->buffer_manager_host()->EndFrame();
root_surface(), /*wait_for_frame_callback=*/true);
} }
return true; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment