Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2204,7 +2204,8 @@ supported on the current platform.
<tr>
<td>wlr</td>
<td>Capture for wlroots based Wayland compositors via wlr-screencopy-unstable-v1. It is possible to capture
virtual displays in e.g. Hyprland using this method.
virtual displays in e.g. Hyprland using this method. Frames are captured only when the display changes, so
an idle display costs the compositor no redraws.
@note{Applies to Linux only.}</td>
</tr>
<tr>
Expand Down
5 changes: 3 additions & 2 deletions src/platform/linux/wayland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,9 +507,10 @@ namespace wl {
// Store for cleanup
self->current_wl_buffer = buffer;

// Start the actual copy
// Start the actual copy. With damage, the compositor completes it on the next change to the output instead of
// redrawing the whole output for it, so an unchanged screen costs nothing to capture.
zwp_linux_buffer_params_v1_destroy(params);
zwlr_screencopy_frame_v1_copy(frame, buffer);
zwlr_screencopy_frame_v1_copy_with_damage(frame, buffer);
}

// Buffer params failed callback
Expand Down
11 changes: 11 additions & 0 deletions src/platform/linux/wayland.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ namespace wl {
bool y_invert {false};
};

/**
* @brief Determine whether a capture should request a new screencopy frame.
*
* Frames are copied with damage, so the compositor holds a request until the output changes. A capture that timed
* out still has that request in flight, and requesting another would replace the buffer it is being copied into.
*
* @param status State of the current screencopy request.
* @return `true` unless a requested frame is still pending.
*/
bool should_request_frame(dmabuf_t::status_e status);

/**
* @brief Wayland output metadata used to match a configured display name.
*/
Expand Down
15 changes: 14 additions & 1 deletion src/platform/linux/wlgrab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ namespace wl {
return false;
}

bool should_request_frame(dmabuf_t::status_e status) {
return status != dmabuf_t::WAITING;
}

/**
* @brief Captured frame buffer shared between capture and encode stages.
*/
Expand Down Expand Up @@ -174,8 +178,17 @@ namespace wl {
inline platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
auto to = std::chrono::steady_clock::now() + timeout;

if (should_request_frame(dmabuf.status)) {
dmabuf.listen(
interface.screencopy_manager,
interface.dmabuf_interface,
&interface.supported_modifiers,
output,
cursor
);
}

// Dispatch events until we get a new frame or the timeout expires
dmabuf.listen(interface.screencopy_manager, interface.dmabuf_interface, &interface.supported_modifiers, output, cursor);
do {
auto remaining_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(to - std::chrono::steady_clock::now());
if (remaining_time_ms.count() < 0 || !display.dispatch(remaining_time_ms)) {
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/platform/linux/test_wayland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ TEST(WaylandCaptureTest, UsesVramForCudaOnlyWhenCudaSupportIsBuilt) {
#endif
}

TEST(WaylandCaptureTest, RequestsTheFirstFrame) {
wl::dmabuf_t dmabuf;

EXPECT_TRUE(wl::should_request_frame(dmabuf.status));
}

TEST(WaylandCaptureTest, WaitsOnAFramePendingDamage) {
EXPECT_FALSE(wl::should_request_frame(wl::dmabuf_t::WAITING));
}

TEST(WaylandCaptureTest, RequestsAFrameOnceTheLastOneCompleted) {
EXPECT_TRUE(wl::should_request_frame(wl::dmabuf_t::READY));
EXPECT_TRUE(wl::should_request_frame(wl::dmabuf_t::REINIT));
}

TEST(WaylandInterfaceTest, RecordsOnlyExplicitDmabufModifiers) {
constexpr std::uint32_t format = DRM_FORMAT_XRGB8888;
constexpr std::uint64_t explicit_modifier = 0x100000000000004;
Expand Down