From 42b44d1c8ed3ea0b3d872be5900963833e6a51f2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 3 Jan 2024 10:14:50 -0600 Subject: [PATCH 1/5] Fix capturing scaled output with wlgrab --- src/platform/linux/wayland.cpp | 25 +++++++++++++++++-------- src/platform/linux/wayland.h | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index d601ba95daa..bc1e416f75a 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -67,7 +67,14 @@ namespace wl { } inline monitor_t::monitor_t(wl_output *output): - output { output }, listener { + output { output }, + wl_listener { + &CLASS_CALL(monitor_t, wl_geometry), + &CLASS_CALL(monitor_t, wl_mode), + &CLASS_CALL(monitor_t, wl_done), + &CLASS_CALL(monitor_t, wl_scale), + }, + xdg_listener { &CLASS_CALL(monitor_t, xdg_position), &CLASS_CALL(monitor_t, xdg_size), &CLASS_CALL(monitor_t, xdg_done), @@ -99,21 +106,23 @@ namespace wl { void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) { + BOOST_LOG(info) << "Logical size: "sv << width << 'x' << height; + } + + void + monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags, + std::int32_t width, std::int32_t height, std::int32_t refresh) { viewport.width = width; viewport.height = height; BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height; } - void - monitor_t::xdg_done(zxdg_output_v1 *) { - BOOST_LOG(info) << "All info about monitor ["sv << name << "] has been send"sv; - } - void monitor_t::listen(zxdg_output_manager_v1 *output_manager) { auto xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output); - zxdg_output_v1_add_listener(xdg_output, &listener, this); + zxdg_output_v1_add_listener(xdg_output, &xdg_listener, this); + wl_output_add_listener(output, &wl_listener, this); } interface_t::interface_t() noexcept @@ -137,7 +146,7 @@ namespace wl { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; monitors.emplace_back( std::make_unique( - (wl_output *) wl_registry_bind(registry, id, &wl_output_interface, version))); + (wl_output *) wl_registry_bind(registry, id, &wl_output_interface, 2))); } else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index a4c3aef17d9..e4c1c1a9800 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -118,7 +118,19 @@ namespace wl { void xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height); void - xdg_done(zxdg_output_v1 *); + xdg_done(zxdg_output_v1 *) {} + + void + wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, + std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, + const char *make, const char *model, std::int32_t transform) {} + void + wl_mode(wl_output *wl_output, std::uint32_t flags, + std::int32_t width, std::int32_t height, std::int32_t refresh); + void + wl_done(wl_output *wl_output) {} + void + wl_scale(wl_output *wl_output, std::int32_t factor) {} void listen(zxdg_output_manager_v1 *output_manager); @@ -130,7 +142,8 @@ namespace wl { platf::touch_port_t viewport; - zxdg_output_v1_listener listener; + wl_output_listener wl_listener; + zxdg_output_v1_listener xdg_listener; }; class interface_t { From 34bff3d9733a88c10ebf510357e5b692b0105a83 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 3 Jan 2024 15:42:12 -0600 Subject: [PATCH 2/5] Fix race condition causing hangs and EGL import errors due to concurrent snapshot() calls --- src/platform/linux/graphics.cpp | 30 ++++++++++++++++++++++++++++++ src/platform/linux/graphics.h | 3 +++ src/platform/linux/vaapi.cpp | 6 +++++- src/platform/linux/wlgrab.cpp | 14 ++++---------- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/platform/linux/graphics.cpp b/src/platform/linux/graphics.cpp index b25032b393a..ccdf54d4fc2 100644 --- a/src/platform/linux/graphics.cpp +++ b/src/platform/linux/graphics.cpp @@ -558,6 +558,36 @@ namespace egl { return rgb; } + /** + * @brief Creates a black RGB texture of the specified image size. + * @param img The image to use for texture sizing. + * @return The new RGB texture. + */ + rgb_t + create_blank(platf::img_t &img) { + rgb_t rgb { + EGL_NO_DISPLAY, + EGL_NO_IMAGE, + gl::tex_t::make(1) + }; + + gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]); + gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, img.width, img.height); + gl::ctx.BindTexture(GL_TEXTURE_2D, 0); + + auto framebuf = gl::frame_buf_t::make(1); + framebuf.bind(&rgb->tex[0], &rgb->tex[0] + 1); + + GLenum attachment = GL_COLOR_ATTACHMENT0; + gl::ctx.DrawBuffers(1, &attachment); + const GLuint rgb_black[] = { 0, 0, 0, 0 }; + gl::ctx.ClearBufferuiv(GL_COLOR, 0, rgb_black); + + gl_drain_errors; + + return rgb; + } + std::optional import_target(display_t::pointer egl_display, std::array &&fds, const surface_descriptor_t &r8, const surface_descriptor_t &gr88) { EGLAttrib img_attr_planes[2][17] { diff --git a/src/platform/linux/graphics.h b/src/platform/linux/graphics.h index ee72c46e766..fa658f41a48 100644 --- a/src/platform/linux/graphics.h +++ b/src/platform/linux/graphics.h @@ -268,6 +268,9 @@ namespace egl { display_t::pointer egl_display, const surface_descriptor_t &xrgb); + rgb_t + create_blank(platf::img_t &img); + std::optional import_target( display_t::pointer egl_display, diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index f210bba4bba..085ca7bb9c5 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -436,7 +436,11 @@ namespace va { convert(platf::img_t &img) override { auto &descriptor = (egl::img_descriptor_t &) img; - if (descriptor.sequence > sequence) { + if (descriptor.sequence == 0) { + // For dummy images, use a blank RGB texture instead of importing a DMA-BUF + rgb = egl::create_blank(img); + } + else if (descriptor.sequence > sequence) { sequence = descriptor.sequence; rgb = egl::rgb_t {}; diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index b57b332d7b2..8f3ef299d42 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -313,6 +313,8 @@ namespace wl { alloc_img() override { auto img = std::make_shared(); + img->width = width; + img->height = height; img->sequence = 0; img->serial = std::numeric_limitsserial)>::max(); img->data = nullptr; @@ -334,16 +336,8 @@ namespace wl { int dummy_img(platf::img_t *img) override { - // TODO: stop cheating and give black image - if (!img) { - return -1; - }; - auto pull_dummy_img_callback = [&img](std::shared_ptr &img_out) -> bool { - img_out = img->shared_from_this(); - return true; - }; - std::shared_ptr img_out; - return snapshot(pull_dummy_img_callback, img_out, 1000ms, false) != platf::capture_e::ok; + // Empty images are recognized as dummies by the zero sequence number + return 0; } std::uint64_t sequence {}; From a8852609ea0952afe4861c5b5b708d77b5c846d8 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 3 Jan 2024 16:08:46 -0600 Subject: [PATCH 3/5] Don't busy loop during wlgrab capture --- src/platform/linux/wayland.cpp | 33 +++++++++++++++++++++++++++++++++ src/platform/linux/wayland.h | 4 ++++ src/platform/linux/wlgrab.cpp | 6 +++--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index bc1e416f75a..b762492b6ae 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -2,6 +2,7 @@ * @file src/platform/linux/wayland.cpp * @brief todo */ +#include #include #include @@ -61,6 +62,38 @@ namespace wl { wl_display_roundtrip(display_internal.get()); } + /** + * @brief Waits up to the specified timeout to dispatch new events on the wl_display. + * @param timeout The timeout in milliseconds. + * @return true if new events were dispatched or false if the timeout expired. + */ + bool + display_t::dispatch(std::chrono::milliseconds timeout) { + // Check if any events are queued already. If not, flush + // outgoing events, and prepare to wait for readability. + if (wl_display_prepare_read(display_internal.get()) == 0) { + wl_display_flush(display_internal.get()); + + // Wait for an event to come in + struct pollfd pfd = {}; + pfd.fd = wl_display_get_fd(display_internal.get()); + pfd.events = POLLIN; + if (poll(&pfd, 1, timeout.count()) == 1 && (pfd.revents & POLLIN)) { + // Read the new event(s) + wl_display_read_events(display_internal.get()); + } + else { + // We timed out, so unlock the queue now + wl_display_cancel_read(display_internal.get()); + return false; + } + } + + // Dispatch any existing or new pending events + wl_display_dispatch_pending(display_internal.get()); + return true; + } + wl_registry * display_t::registry() { return wl_display_get_registry(display_internal.get()); diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index e4c1c1a9800..062aa65f1ea 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -206,6 +206,10 @@ namespace wl { void roundtrip(); + // Wait up to the timeout to read and dispatch new events + bool + dispatch(std::chrono::milliseconds timeout); + // Get the registry associated with the display // No need to manually free the registry wl_registry * diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 8f3ef299d42..5ba0d9e24c1 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -87,11 +87,11 @@ namespace wl { snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { auto to = std::chrono::steady_clock::now() + timeout; + // Dispatch events until we get a new frame or the timeout expires dmabuf.listen(interface.dmabuf_manager, output, cursor); do { - display.roundtrip(); - - if (to < std::chrono::steady_clock::now()) { + auto remaining_time_ms = std::chrono::duration_cast(to - std::chrono::steady_clock::now()); + if (remaining_time_ms.count() < 0 || !display.dispatch(remaining_time_ms)) { return platf::capture_e::timeout; } } while (dmabuf.status == dmabuf_t::WAITING); From 54c36b80de0742be3874369eda9669afa20bd842 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 4 Jan 2024 00:20:14 -0600 Subject: [PATCH 4/5] Remove useless texture size queries for every frame in display_ram_t --- src/platform/linux/kmsgrab.cpp | 8 +------- src/platform/linux/wlgrab.cpp | 6 ------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index fd4b74d4a61..4e5d8af89be 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -819,17 +819,11 @@ namespace platf { auto &rgb = *rgb_opt; - gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]); - - int w, h; - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h; - if (!pull_free_image_cb(img_out)) { return platf::capture_e::interrupted; } + gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]); gl::ctx.GetTextureSubImage(rgb->tex[0], 0, img_offset_x, img_offset_y, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data); if (cursor_opt && cursor) { diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 5ba0d9e24c1..2162fe61a74 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -182,12 +182,6 @@ namespace wl { } gl::ctx.BindTexture(GL_TEXTURE_2D, (*rgb_opt)->tex[0]); - - int w, h; - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h; - gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data); gl::ctx.BindTexture(GL_TEXTURE_2D, 0); From d0497ab094cd97f91bd68cb37d9e4fd563513b3d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 4 Jan 2024 00:21:46 -0600 Subject: [PATCH 5/5] Use the new VAAPI dummy image support for kmsgrab too --- src/platform/linux/kmsgrab.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 4e5d8af89be..8b9780ae068 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -874,6 +874,8 @@ namespace platf { alloc_img() override { auto img = std::make_shared(); + img->width = width; + img->height = height; img->serial = std::numeric_limitsserial)>::max(); img->data = nullptr; img->pixel_pitch = 4; @@ -886,16 +888,8 @@ namespace platf { int dummy_img(platf::img_t *img) override { - // TODO: stop cheating and give black image - if (!img) { - return -1; - }; - auto pull_dummy_img_callback = [&img](std::shared_ptr &img_out) -> bool { - img_out = img->shared_from_this(); - return true; - }; - std::shared_ptr img_out; - return snapshot(pull_dummy_img_callback, img_out, 1s, false) != platf::capture_e::ok; + // Empty images are recognized as dummies by the zero sequence number + return 0; } capture_e @@ -988,12 +982,10 @@ namespace platf { return -1; } - sequence = 0; - return 0; } - std::uint64_t sequence; + std::uint64_t sequence {}; }; } // namespace kms