Commit e495b5f4 authored by Kramer Ge's avatar Kramer Ge Committed by Commit Bot

[ozone/wayland] Wait for buffer.release in gpu process collectively.

Multiple buffer acks arrive from Wayland server for a frame.
BufferQueue only expects one swap completion callback and one
presentation feedback per frame. This CL groups buffers acks in the same
frame and run callbacks when buffers in 1 frame are all ack'ed.

This is 5/? CL for overlay forwarding using wl_subsurface.

Bug: 1063865
Change-Id: I026eb1aba38288c5436c672af4101d61495031b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2343545Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarMaksim Sisov (GMT+3) <msisov@igalia.com>
Commit-Queue: Kramer Ge <fangzhoug@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810373}
parent 145877dc
...@@ -46,8 +46,10 @@ GbmSurfacelessWayland::GbmSurfacelessWayland( ...@@ -46,8 +46,10 @@ GbmSurfacelessWayland::GbmSurfacelessWayland(
} }
void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane, void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane,
uint32_t buffer_id) { BufferId buffer_id) {
unsubmitted_frames_.back()->planes.push_back({std::move(plane), buffer_id}); auto result =
unsubmitted_frames_.back()->planes.emplace(buffer_id, std::move(plane));
DCHECK(result.second);
} }
bool GbmSurfacelessWayland::ScheduleOverlayPlane( bool GbmSurfacelessWayland::ScheduleOverlayPlane(
...@@ -100,7 +102,7 @@ void GbmSurfacelessWayland::SwapBuffersAsync( ...@@ -100,7 +102,7 @@ void GbmSurfacelessWayland::SwapBuffersAsync(
return; return;
} }
// TODO(dcastagna): remove glFlush since eglImageFlushExternalEXT called on // TODO(fangzhoug): remove glFlush since eglImageFlushExternalEXT called on
// the image should be enough (https://crbug.com/720045). // the image should be enough (https://crbug.com/720045).
if (!no_gl_flush_for_tests_) if (!no_gl_flush_for_tests_)
glFlush(); glFlush();
...@@ -123,16 +125,16 @@ void GbmSurfacelessWayland::SwapBuffersAsync( ...@@ -123,16 +125,16 @@ void GbmSurfacelessWayland::SwapBuffersAsync(
// Uset in-fences provided in the overlays. If there are none, we insert our // Uset in-fences provided in the overlays. If there are none, we insert our
// own fence and wait. // own fence and wait.
for (auto& plane : frame->planes) { for (auto& plane : frame->planes) {
if (plane.plane.gpu_fence) if (plane.second.gpu_fence)
fences.push_back(std::move(plane.plane.gpu_fence)); fences.push_back(std::move(plane.second.gpu_fence));
} }
base::OnceClosure fence_wait_task; base::OnceClosure fence_wait_task;
if (!fences.empty()) { if (!fences.empty()) {
fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences)); fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences));
} else { } else {
// TODO: the following should be replaced by a per surface flush as it gets // TODO(fangzhoug): the following should be replaced by a per surface flush
// implemented in GL drivers. // as it gets implemented in GL drivers.
EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed"; CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
...@@ -238,18 +240,14 @@ void GbmSurfacelessWayland::MaybeSubmitFrames() { ...@@ -238,18 +240,14 @@ void GbmSurfacelessWayland::MaybeSubmitFrames() {
std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs;
for (const auto& plane : submitted_frame->planes) { for (const auto& plane : submitted_frame->planes) {
overlay_configs.push_back( overlay_configs.push_back(
ui::ozone::mojom::WaylandOverlayConfig::From(plane.plane)); ui::ozone::mojom::WaylandOverlayConfig::From(plane.second));
overlay_configs.back()->buffer_id = plane.buffer_id; overlay_configs.back()->buffer_id = plane.first;
if (plane.plane.z_order == 0) { if (plane.second.z_order == 0) {
overlay_configs.back()->damage_region = submitted_frame->damage_region_; overlay_configs.back()->damage_region = submitted_frame->damage_region_;
submitted_frame->buffer_id = plane.buffer_id; submitted_frame->buffer_id = plane.first;
} }
} }
buffer_manager_->CommitOverlays(widget_, std::move(overlay_configs)); buffer_manager_->CommitOverlays(widget_, std::move(overlay_configs));
submitted_frame->unacked_submissions = submitted_frame->planes.size();
submitted_frame->unacked_presentations = submitted_frame->planes.size();
submitted_frame->planes.clear();
submitted_frames_.push_back(std::move(submitted_frame)); submitted_frames_.push_back(std::move(submitted_frame));
} }
} }
...@@ -271,22 +269,46 @@ void GbmSurfacelessWayland::SetNoGLFlushForTests() { ...@@ -271,22 +269,46 @@ void GbmSurfacelessWayland::SetNoGLFlushForTests() {
no_gl_flush_for_tests_ = true; no_gl_flush_for_tests_ = true;
} }
void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id, void GbmSurfacelessWayland::OnSubmission(BufferId buffer_id,
const gfx::SwapResult& swap_result) { const gfx::SwapResult& swap_result) {
// submitted_frames_ may temporarily have more than one buffer in it if // submitted_frames_ may temporarily have more than one buffer in it if
// buffers are released out of order by the Wayland server. // buffers are released out of order by the Wayland server.
DCHECK(!submitted_frames_.empty()); DCHECK(!submitted_frames_.empty());
if (--submitted_frames_.front()->unacked_submissions)
return;
size_t erased = 0;
for (auto& submitted_frame : submitted_frames_) {
if ((erased = submitted_frame->planes.erase(buffer_id)) > 0) {
// |completion_callback| only takes 1 SwapResult. It's possible that only
// one of the buffers in a frame gets a SWAP_FAILED or
// SWAP_NAK_RECREATE_BUFFERS. Don't replace a failed swap_result with
// SWAP_ACK. If both SWAP_FAILED and SWAP_NAK_RECREATE_BUFFERS happens,
// this swap is treated as SWAP_FAILED.
if (submitted_frame->swap_result == gfx::SwapResult::SWAP_ACK ||
swap_result == gfx::SwapResult::SWAP_FAILED) {
submitted_frame->swap_result = swap_result;
}
submitted_frame->pending_presentation_buffers.insert(buffer_id);
break;
}
}
DCHECK(erased);
// Following while loop covers below scenario:
// frame_1 submitted a buffer_1 for overlay; frame_2 submitted a buffer_2
// for primary plane. This can happen at the end of a single-on-top overlay.
// buffer_1 is not attached immediately due to unack'ed wl_frame_callback.
// buffer_2 is attached immediately Onsubmission() of buffer_2 runs.
while (!submitted_frames_.empty() &&
submitted_frames_.front()->planes.empty()) {
auto submitted_frame = std::move(submitted_frames_.front()); auto submitted_frame = std::move(submitted_frames_.front());
submitted_frames_.erase(submitted_frames_.begin()); submitted_frames_.erase(submitted_frames_.begin());
submitted_frame->overlays.clear(); submitted_frame->overlays.clear();
std::move(submitted_frame->completion_callback) std::move(submitted_frame->completion_callback)
.Run(gfx::SwapCompletionResult(swap_result)); .Run(gfx::SwapCompletionResult(submitted_frame->swap_result));
pending_presentation_frames_.push_back(std::move(submitted_frame)); pending_presentation_frames_.push_back(std::move(submitted_frame));
}
if (swap_result != gfx::SwapResult::SWAP_ACK) { if (swap_result != gfx::SwapResult::SWAP_ACK) {
last_swap_buffers_result_ = false; last_swap_buffers_result_ = false;
...@@ -297,10 +319,20 @@ void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id, ...@@ -297,10 +319,20 @@ void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id,
} }
void GbmSurfacelessWayland::OnPresentation( void GbmSurfacelessWayland::OnPresentation(
uint32_t buffer_id, BufferId buffer_id,
const gfx::PresentationFeedback& feedback) { const gfx::PresentationFeedback& feedback) {
DCHECK(!submitted_frames_.empty() || !pending_presentation_frames_.empty());
size_t erased = 0;
for (auto& frame : pending_presentation_frames_) {
if ((erased = frame->pending_presentation_buffers.erase(buffer_id)) > 0) {
frame->feedback = feedback;
break;
}
}
// Items in |submitted_frames_| will not be moved to // Items in |submitted_frames_| will not be moved to
// |pending_presentation_frames_| until |unacked_submissions| decrements to 0. // |pending_presentation_frames_| until |planes| is empty.
// Example: // Example:
// A SwapBuffers that submitted 2 buffers (buffer_1 and buffer_2) will push // A SwapBuffers that submitted 2 buffers (buffer_1 and buffer_2) will push
// a submitted_frame expecting 2 submission feedbacks and 2 presentation // a submitted_frame expecting 2 submission feedbacks and 2 presentation
...@@ -312,24 +344,26 @@ void GbmSurfacelessWayland::OnPresentation( ...@@ -312,24 +344,26 @@ void GbmSurfacelessWayland::OnPresentation(
// buffer_1:submission > buffer_1:presentation > buffer_2:submission > // buffer_1:submission > buffer_1:presentation > buffer_2:submission >
// buffer_2:presentation // buffer_2:presentation
// In this case, we have to find the item in |submitted_frames_| and // In this case, we have to find the item in |submitted_frames_| and
// decrement |unacked_presentations| there. // remove from |pending_presentation_buffers| there.
// TODO(fangzhoug): This solution is sub-optimal and confusing. It increases if (!erased) {
// the number of IPCs from browser to gpu. The barrier logic should be in the for (auto& frame : submitted_frames_) {
// browser process. if ((erased = frame->pending_presentation_buffers.erase(buffer_id)) > 0) {
if (pending_presentation_frames_.empty()) { frame->feedback = feedback;
auto it = submitted_frames_.begin(); break;
for (; !(*it)->unacked_presentations; ++it) }
; }
--(*it)->unacked_presentations;
return;
} }
auto* frame = pending_presentation_frames_.front().get(); DCHECK(erased);
if (--frame->unacked_presentations)
return;
std::move(frame->presentation_callback).Run(feedback); while (!pending_presentation_frames_.empty() &&
pending_presentation_frames_.front()
->pending_presentation_buffers.empty()) {
auto* frame = pending_presentation_frames_.front().get();
DCHECK(frame->planes.empty());
std::move(frame->presentation_callback).Run(frame->feedback);
pending_presentation_frames_.erase(pending_presentation_frames_.begin()); pending_presentation_frames_.erase(pending_presentation_frames_.begin());
}
} }
} // namespace ui } // namespace ui
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/containers/small_map.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "ui/gfx/native_widget_types.h" #include "ui/gfx/native_widget_types.h"
...@@ -19,6 +20,8 @@ namespace ui { ...@@ -19,6 +20,8 @@ namespace ui {
class WaylandBufferManagerGpu; class WaylandBufferManagerGpu;
using BufferId = uint32_t;
// A GLSurface for Wayland Ozone platform that uses surfaceless drawing. Drawing // A GLSurface for Wayland Ozone platform that uses surfaceless drawing. Drawing
// and displaying happens directly through NativePixmap buffers. CC would call // and displaying happens directly through NativePixmap buffers. CC would call
// into SurfaceFactoryOzone to allocate the buffers and then call // into SurfaceFactoryOzone to allocate the buffers and then call
...@@ -29,7 +32,7 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, ...@@ -29,7 +32,7 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
GbmSurfacelessWayland(WaylandBufferManagerGpu* buffer_manager, GbmSurfacelessWayland(WaylandBufferManagerGpu* buffer_manager,
gfx::AcceleratedWidget widget); gfx::AcceleratedWidget widget);
void QueueOverlayPlane(OverlayPlane plane, uint32_t buffer_id); void QueueOverlayPlane(OverlayPlane plane, BufferId buffer_id);
// gl::GLSurface: // gl::GLSurface:
bool ScheduleOverlayPlane(int z_order, bool ScheduleOverlayPlane(int z_order,
...@@ -62,22 +65,19 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, ...@@ -62,22 +65,19 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
private: private:
FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest, FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest,
GbmSurfacelessWaylandCheckOrderOfCallbacksTest); GbmSurfacelessWaylandCheckOrderOfCallbacksTest);
FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest,
GbmSurfacelessWaylandCommitOverlaysCallbacksTest);
FRIEND_TEST_ALL_PREFIXES(WaylandSurfaceFactoryTest,
GbmSurfacelessWaylandGroupOnSubmissionCallbacksTest);
~GbmSurfacelessWayland() override; ~GbmSurfacelessWayland() override;
// WaylandSurfaceGpu overrides: // WaylandSurfaceGpu overrides:
void OnSubmission(uint32_t buffer_id, void OnSubmission(BufferId buffer_id,
const gfx::SwapResult& swap_result) override; const gfx::SwapResult& swap_result) override;
void OnPresentation(uint32_t buffer_id, void OnPresentation(BufferId buffer_id,
const gfx::PresentationFeedback& feedback) override; const gfx::PresentationFeedback& feedback) override;
struct PlaneData {
OverlayPlane plane;
// The id of the buffer, which represents buffer that backs this overlay
// plane.
const uint32_t buffer_id;
};
struct PendingFrame { struct PendingFrame {
PendingFrame(); PendingFrame();
~PendingFrame(); ~PendingFrame();
...@@ -89,24 +89,25 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, ...@@ -89,24 +89,25 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
bool ready = false; bool ready = false;
// The id of the buffer, which represents this frame. // The id of the buffer, which represents this frame.
uint32_t buffer_id = 0; BufferId buffer_id = 0;
// A region of the updated content in a corresponding frame. It's used to // A region of the updated content in a corresponding frame. It's used to
// advice Wayland which part of a buffer is going to be updated. Passing {0, // advice Wayland which part of a buffer is going to be updated. Passing {0,
// 0, 0, 0} results in a whole buffer update on the Wayland compositor side. // 0, 0, 0} results in a whole buffer update on the Wayland compositor side.
gfx::Rect damage_region_ = gfx::Rect(); gfx::Rect damage_region_ = gfx::Rect();
// TODO(fangzhoug): This should be changed to support Vulkan.
std::vector<gl::GLSurfaceOverlay> overlays; std::vector<gl::GLSurfaceOverlay> overlays;
SwapCompletionCallback completion_callback; SwapCompletionCallback completion_callback;
PresentationCallback presentation_callback; PresentationCallback presentation_callback;
bool schedule_planes_succeeded = false; bool schedule_planes_succeeded = false;
std::vector<PlaneData> planes;
// TODO(fangzhoug): This is a temporary solution to barrier swap/present // Maps |buffer_id| to an OverlayPlane, used for committing overlays and
// acks of a frame that contains multiple buffer commits. Next step is to // wait for OnSubmission's.
// barrier in browser process to avoid extra IPC hops. base::small_map<std::map<BufferId, OverlayPlane>> planes;
size_t unacked_submissions; base::flat_set<BufferId> pending_presentation_buffers;
size_t unacked_presentations; gfx::SwapResult swap_result = gfx::SwapResult::SWAP_ACK;
gfx::PresentationFeedback feedback;
}; };
void MaybeSubmitFrames(); void MaybeSubmitFrames();
......
...@@ -25,6 +25,9 @@ namespace ui { ...@@ -25,6 +25,9 @@ namespace ui {
namespace { namespace {
// Use |kInvalidBufferId| to commit surface state without updating wl_buffer.
constexpr uint32_t kInvalidBufferId = 0u;
uint32_t GetPresentationKindFlags(uint32_t flags) { uint32_t GetPresentationKindFlags(uint32_t flags) {
// Wayland spec has different meaning of VSync. In Chromium, VSync means to // Wayland spec has different meaning of VSync. In Chromium, VSync means to
// update the begin frame vsync timing based on presentation feedback. // update the begin frame vsync timing based on presentation feedback.
...@@ -65,11 +68,20 @@ class WaylandBufferManagerHost::Surface { ...@@ -65,11 +68,20 @@ class WaylandBufferManagerHost::Surface {
buffer_manager_(buffer_manager) {} buffer_manager_(buffer_manager) {}
~Surface() = default; ~Surface() = default;
bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) { bool CommitBuffer(uint32_t buffer_id,
const gfx::Rect& damage_region,
bool wait_for_frame_callback) {
// The window has already been destroyed. // The window has already been destroyed.
if (!wayland_surface_) if (!wayland_surface_)
return true; return true;
// This is a buffer-less commit, do not lookup buffers.
if (buffer_id == kInvalidBufferId) {
pending_commits_.push_back({nullptr, wait_for_frame_callback});
MaybeProcessPendingBuffer();
return true;
}
WaylandBuffer* buffer = GetBuffer(buffer_id); WaylandBuffer* buffer = GetBuffer(buffer_id);
if (!buffer) { if (!buffer) {
// Get the anonymous_wl_buffer aka the buffer that has not been attached // Get the anonymous_wl_buffer aka the buffer that has not been attached
...@@ -95,7 +107,7 @@ class WaylandBufferManagerHost::Surface { ...@@ -95,7 +107,7 @@ class WaylandBufferManagerHost::Surface {
if (buffer->attached && !buffer->wl_buffer) if (buffer->attached && !buffer->wl_buffer)
return false; return false;
pending_buffers_.push_back(buffer); pending_commits_.push_back({buffer, wait_for_frame_callback});
MaybeProcessPendingBuffer(); MaybeProcessPendingBuffer();
return true; return true;
} }
...@@ -108,7 +120,11 @@ class WaylandBufferManagerHost::Surface { ...@@ -108,7 +120,11 @@ class WaylandBufferManagerHost::Surface {
if (buffer) { if (buffer) {
buffer->released = true; buffer->released = true;
MaybeProcessSubmittedBuffers(); MaybeProcessSubmittedBuffers();
base::Erase(pending_buffers_, buffer); for (auto it = pending_commits_.begin(); it != pending_commits_.end();
++it) {
if (it->buffer == buffer)
pending_commits_.erase(it++);
}
} }
return buffers_.erase(buffer_id); return buffers_.erase(buffer_id);
...@@ -139,7 +155,7 @@ class WaylandBufferManagerHost::Surface { ...@@ -139,7 +155,7 @@ class WaylandBufferManagerHost::Surface {
ResetSurfaceContents(); ResetSurfaceContents();
submitted_buffers_.clear(); submitted_buffers_.clear();
pending_buffers_.clear(); pending_commits_.clear();
connection_->ScheduleFlush(); connection_->ScheduleFlush();
} }
...@@ -204,7 +220,16 @@ class WaylandBufferManagerHost::Surface { ...@@ -204,7 +220,16 @@ class WaylandBufferManagerHost::Surface {
bool acked; bool acked;
}; };
bool CommitBufferInternal(WaylandBuffer* buffer) { // Represents a pending surface commit.
struct PendingCommit {
// If null, means this commit will not attach buffer.
WaylandBuffer* buffer = nullptr;
// Whether this commit must wait for a wl_frame_callback and setup another
// wl_frame_callback.
bool wait_for_callback = false;
};
bool CommitBufferInternal(WaylandBuffer* buffer, bool wait_for_callback) {
DCHECK(buffer && wayland_surface_); DCHECK(buffer && wayland_surface_);
// If the same buffer has been submitted again right after the client // If the same buffer has been submitted again right after the client
...@@ -225,6 +250,7 @@ class WaylandBufferManagerHost::Surface { ...@@ -225,6 +250,7 @@ class WaylandBufferManagerHost::Surface {
DamageBuffer(buffer); DamageBuffer(buffer);
if (wait_for_callback)
SetupFrameCallback(); SetupFrameCallback();
SetupPresentationFeedback(buffer->buffer_id); SetupPresentationFeedback(buffer->buffer_id);
...@@ -504,10 +530,10 @@ class WaylandBufferManagerHost::Surface { ...@@ -504,10 +530,10 @@ class WaylandBufferManagerHost::Surface {
} }
void MaybeProcessPendingBuffer() { void MaybeProcessPendingBuffer() {
DCHECK_LE(pending_buffers_.size(), 6u); DCHECK_LE(pending_commits_.size(), 6u);
// There is nothing to process if there is no pending buffer or the window // There is nothing to process if there is no pending buffer or the window
// has been destroyed. // has been destroyed.
if (pending_buffers_.empty() || !wayland_surface_) if (pending_commits_.empty() || !wayland_surface_)
return; return;
// This request may come earlier than the Wayland compositor has imported a // This request may come earlier than the Wayland compositor has imported a
...@@ -524,12 +550,27 @@ class WaylandBufferManagerHost::Surface { ...@@ -524,12 +550,27 @@ class WaylandBufferManagerHost::Surface {
// //
// The third case happens if the window hasn't been configured until a // The third case happens if the window hasn't been configured until a
// request to attach a buffer to its surface is sent. // request to attach a buffer to its surface is sent.
auto* pending_buffer = pending_buffers_.front(); auto pending_commit = std::move(pending_commits_.front());
if (!pending_buffer->wl_buffer || wl_frame_callback_ || !configured_) if ((pending_commit.buffer && !pending_commit.buffer->wl_buffer) ||
(wl_frame_callback_ && pending_commit.wait_for_callback) ||
!configured_) {
return;
}
// A Commit without attaching buffers only needs to setup wl_frame_callback.
if (!pending_commit.buffer) {
pending_commits_.erase(pending_commits_.begin());
if (pending_commit.wait_for_callback)
SetupFrameCallback();
CommitSurface();
connection_->ScheduleFlush();
MaybeProcessSubmittedBuffers();
return; return;
}
pending_buffers_.erase(pending_buffers_.begin()); pending_commits_.erase(pending_commits_.begin());
CommitBufferInternal(pending_buffer); CommitBufferInternal(pending_commit.buffer,
pending_commit.wait_for_callback);
} }
// Widget this helper surface backs and has 1:1 relationship with the // Widget this helper surface backs and has 1:1 relationship with the
...@@ -552,9 +593,9 @@ class WaylandBufferManagerHost::Surface { ...@@ -552,9 +593,9 @@ class WaylandBufferManagerHost::Surface {
// operation. // operation.
wl::Object<wl_callback> wl_frame_callback_; wl::Object<wl_callback> wl_frame_callback_;
// Queue of buffers which are pending to be submitted (look the comment // Queue of commits which are pending to be submitted (look the comment
// in the CommitBuffer method). // in the CommitBuffer method).
std::vector<WaylandBuffer*> pending_buffers_; std::list<PendingCommit> pending_commits_;
// Queue of buffers which have been submitted and are waiting to be // Queue of buffers which have been submitted and are waiting to be
// acked (send OnSubmission) // acked (send OnSubmission)
...@@ -763,14 +804,16 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, ...@@ -763,14 +804,16 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd,
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) {
DCHECK(base::CurrentUIThread::IsSet()); DCHECK(base::CurrentUIThread::IsSet());
Surface* surface = GetSurface(wayland_surface); Surface* surface = GetSurface(wayland_surface);
if (!surface || !ValidateBufferIdFromGpu(buffer_id)) if (!surface || !ValidateBufferIdFromGpu(buffer_id))
return false; return false;
if (!surface->CommitBuffer(buffer_id, damage_region)) { if (!surface->CommitBuffer(buffer_id, damage_region,
wait_for_frame_callback)) {
error_message_ = error_message_ =
base::StrCat({"Buffer with ", NumberToString(buffer_id), base::StrCat({"Buffer with ", NumberToString(buffer_id),
" id does not exist or failed to be created."}); " id does not exist or failed to be created."});
...@@ -781,6 +824,24 @@ bool WaylandBufferManagerHost::CommitBufferInternal( ...@@ -781,6 +824,24 @@ 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) {
......
...@@ -157,9 +157,22 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -157,9 +157,22 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
// |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
// presented. // presented.
// |wait_for_frame_callback| instructs that a surface should wait for previous
// wl_frame_callback. This is primarily used for sync wl_subsurfaces case
// where buffer updates within a frame should be seen together. A root_surface
// commit will move an entire wl_surface tree from pending state to ready
// state. This root_surface commit must wait for wl_frame_callback, such that
// in effect all other surface updates wait for this wl_frame_callback, too.
bool CommitBufferInternal(WaylandSurface* wayland_surface, bool CommitBufferInternal(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 = true);
// 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
...@@ -239,8 +252,13 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, ...@@ -239,8 +252,13 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
base::OnceCallback<void(std::string)> terminate_gpu_cb_; base::OnceCallback<void(std::string)> terminate_gpu_cb_;
// Contains anonymous buffers aka buffers that are not attached to any of the // Contains anonymous buffers aka buffers that are not attached to any of the
// existing surfaces and that will be mapped to surfaces later. Typically // existing surfaces and that will be mapped to surfaces later.
// created when CreateAnonymousImage is called on the gpu process side. // Typically created when CreateAnonymousImage is called on the gpu process
// side.
// We assume that a buffer_id/wl_buffer will never be used on multiple
// wl_surfaces so we never re-map buffers to surfaces. If we ever need to use
// the same buffer for 2 surfaces at the same time, create multiple wl_buffers
// referencing the same dmabuf or underlying storage.
base::flat_map<uint32_t, std::unique_ptr<WaylandBuffer>> anonymous_buffers_; base::flat_map<uint32_t, std::unique_ptr<WaylandBuffer>> anonymous_buffers_;
base::WeakPtrFactory<WaylandBufferManagerHost> weak_factory_; base::WeakPtrFactory<WaylandBufferManagerHost> weak_factory_;
......
...@@ -541,8 +541,9 @@ bool WaylandWindow::CommitOverlays( ...@@ -541,8 +541,9 @@ bool WaylandWindow::CommitOverlays(
ozone::mojom::WaylandOverlayConfig::New(); ozone::mojom::WaylandOverlayConfig::New();
auto split = std::lower_bound(overlays.begin(), overlays.end(), value, auto split = std::lower_bound(overlays.begin(), overlays.end(), value,
OverlayStackOrderCompare); OverlayStackOrderCompare);
CHECK((*split)->z_order >= 0); CHECK(split == overlays.end() || (*split)->z_order >= 0);
size_t num_primary_planes = (*split)->z_order == 0 ? 1 : 0; size_t num_primary_planes =
(split != overlays.end() && (*split)->z_order == 0) ? 1 : 0;
size_t above = (overlays.end() - split) - num_primary_planes; size_t above = (overlays.end() - split) - num_primary_planes;
size_t below = split - overlays.begin(); size_t below = split - overlays.begin();
...@@ -572,8 +573,8 @@ bool WaylandWindow::CommitOverlays( ...@@ -572,8 +573,8 @@ bool WaylandWindow::CommitOverlays(
(*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend,
nullptr, reference_above); nullptr, reference_above);
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
(*iter)->wayland_surface(), (*overlay_iter)->buffer_id, (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
gfx::Rect()); /*wait_for_frame_callback=*/false);
} 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
// moment, hide them. // moment, hide them.
...@@ -601,8 +602,8 @@ bool WaylandWindow::CommitOverlays( ...@@ -601,8 +602,8 @@ bool WaylandWindow::CommitOverlays(
(*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend,
reference_below, nullptr); reference_below, nullptr);
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
(*iter)->wayland_surface(), (*overlay_iter)->buffer_id, (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
gfx::Rect()); /*wait_for_frame_callback=*/false);
} 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
// moment, hide them. // moment, hide them.
...@@ -612,16 +613,16 @@ bool WaylandWindow::CommitOverlays( ...@@ -612,16 +613,16 @@ bool WaylandWindow::CommitOverlays(
} }
if (num_primary_planes) { if (num_primary_planes) {
// TODO: forward fence.
connection_->buffer_manager_host()->CommitBufferInternal( connection_->buffer_manager_host()->CommitBufferInternal(
root_surface(), (*split)->buffer_id, (*split)->damage_region); root_surface(), (*split)->buffer_id, (*split)->damage_region,
/*wait_for_frame_callback=*/true);
} else { } else {
// Subsurfaces are set to desync, above operations will only take effects // Subsurfaces are set to sync, above operations will only take effects
// when root_surface is committed. // when root_surface is committed.
root_surface()->Commit(); connection_->buffer_manager_host()->CommitWithoutBufferInternal(
root_surface(), /*wait_for_frame_callback=*/true);
} }
// commit all;
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