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/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index fd4b74d4a61..8b9780ae068 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) { @@ -880,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; @@ -892,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 @@ -994,12 +982,10 @@ namespace platf { return -1; } - sequence = 0; - return 0; } - std::uint64_t sequence; + std::uint64_t sequence {}; }; } // namespace kms 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/wayland.cpp b/src/platform/linux/wayland.cpp index d601ba95daa..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,13 +62,52 @@ 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()); } 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 +139,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 +179,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..062aa65f1ea 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 { @@ -193,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 b57b332d7b2..2162fe61a74 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); @@ -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); @@ -313,6 +307,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 +330,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 {};