diff --git a/sunshine/platform/linux/graphics.h b/sunshine/platform/linux/graphics.h index d565f243..7e1ba805 100644 --- a/sunshine/platform/linux/graphics.h +++ b/sunshine/platform/linux/graphics.h @@ -50,6 +50,17 @@ public: static frame_buf_t make(std::size_t count); + inline void bind(std::nullptr_t, std::nullptr_t) { + int x = 0; + for(auto fb : (*this)) { + ctx.BindFramebuffer(GL_FRAMEBUFFER, fb); + ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0); + + ++x; + } + return; + } + template void bind(It it_begin, It it_end) { using namespace std::literals; @@ -230,6 +241,22 @@ public: std::vector buffer; }; +// Allow cursor and the underlying image to be kept together +class img_descriptor_t : public cursor_t { +public: + std::uint32_t format; + std::uint32_t img_width, img_height; + std::uint32_t obj_count; + std::uint32_t strides[4]; + // std::uint32_t sizes[4]; + std::int32_t fds[4]; + std::uint32_t offsets[4]; + // std::uint32_t plane_indices[4]; + + // Increment sequence when new rgb_t needs to be created + std::uint64_t sequence; +}; + class sws_t { public: static std::optional make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex); diff --git a/sunshine/platform/linux/kmsgrab.cpp b/sunshine/platform/linux/kmsgrab.cpp index d3b7616e..c059342b 100644 --- a/sunshine/platform/linux/kmsgrab.cpp +++ b/sunshine/platform/linux/kmsgrab.cpp @@ -397,7 +397,7 @@ public: return 0; } - // When the framebuffer is reinitialized, this id can no longer be found + // Keep track of what framebuffer we're using. std::uint32_t framebuffer_index; capture_e status; @@ -500,7 +500,7 @@ public: std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override { if(mem_type == mem_type_e::vaapi) { - return va::make_hwdevice(width, height); + return va::make_hwdevice(width, height, false); } return std::make_shared(); @@ -546,14 +546,7 @@ public: std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override { if(mem_type == mem_type_e::vaapi) { - return va::make_hwdevice(width, height, dup(card.fd.el), offset_x, offset_y, - { - fb_fd.el, - img_width, - img_height, - 0, - pitch, - }); + return va::make_hwdevice(width, height, dup(card.fd.el), offset_x, offset_y, true); } BOOST_LOG(error) << "Unsupported pixel format for egl::display_vram_t: "sv << platf::from_pix_fmt(pix_fmt); @@ -561,12 +554,21 @@ public: } std::shared_ptr alloc_img() override { - auto img = std::make_shared(); + auto img = std::make_shared(); img->serial = std::numeric_limitsserial)>::max(); img->data = nullptr; img->pixel_pitch = 4; + img->sequence = 1; + + img->obj_count = 1; + img->img_width = img_width; + img->img_height = img_height; + img->fds[0] = fb_fd.el; + img->offsets[0] = 0; + img->strides[0] = pitch; + return img; } diff --git a/sunshine/platform/linux/vaapi.cpp b/sunshine/platform/linux/vaapi.cpp index c9071696..d3fe74b1 100644 --- a/sunshine/platform/linux/vaapi.cpp +++ b/sunshine/platform/linux/vaapi.cpp @@ -412,26 +412,45 @@ public: class va_vram_t : public va_t { public: int convert(platf::img_t &img) override { - sws.load_vram((egl::cursor_t &)img, offset_x, offset_y, framebuffer[0]); + auto &descriptor = (egl::img_descriptor_t &)img; + + if(descriptor.sequence > sequence) { + sequence = descriptor.sequence; + + framebuffer.bind(nullptr, nullptr); + + auto rgb_opt = egl::import_source(display.get(), + { + descriptor.fds[0], + (int)descriptor.img_width, + (int)descriptor.img_height, + (int)descriptor.offsets[0], + (int)descriptor.strides[0], + }); + + if(!rgb_opt) { + return -1; + } + + rgb = std::move(*rgb_opt); + + framebuffer.bind(std::begin(rgb->tex), std::end(rgb->tex)); + } + + sws.load_vram(descriptor, offset_x, offset_y, framebuffer[0]); sws.convert(nv12); return 0; } - int init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y, const egl::surface_descriptor_t &sd) { + int init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) { if(va_t::init(in_width, in_height, std::move(render_device))) { return -1; } - auto rgb_opt = egl::import_source(display.get(), sd); - if(!rgb_opt) { - return -1; - } - - rgb = std::move(*rgb_opt); - framebuffer = gl::frame_buf_t::make(1); - framebuffer.bind(std::begin(rgb->tex), std::end(rgb->tex)); + + sequence = 0; this->offset_x = offset_x; this->offset_y = offset_y; @@ -439,8 +458,7 @@ public: return 0; } - file_t fb_fd; - + std::uint64_t sequence; egl::rgb_t rgb; gl::frame_buf_t framebuffer; @@ -607,7 +625,27 @@ bool validate(int fd) { return true; } -std::shared_ptr make_hwdevice(int width, int height) { +std::shared_ptr make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) { + if(vram) { + auto egl = std::make_shared(); + if(egl->init(width, height, std::move(card), offset_x, offset_y)) { + return nullptr; + } + + return egl; + } + + else { + auto egl = std::make_shared(); + if(egl->init(width, height, std::move(card))) { + return nullptr; + } + + return egl; + } +} + +std::shared_ptr make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram) { auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); file_t file = open(render_device, O_RDWR); @@ -618,20 +656,10 @@ std::shared_ptr make_hwdevice(int width, int height) { return nullptr; } - auto egl = std::make_shared(); - if(egl->init(width, height, std::move(file))) { - return nullptr; - } - - return egl; + return make_hwdevice(width, height, std::move(file), offset_x, offset_y, vram); } -std::shared_ptr make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, const egl::surface_descriptor_t &sd) { - auto egl = std::make_shared(); - if(egl->init(width, height, std::move(card), offset_x, offset_y, sd)) { - return nullptr; - } - - return egl; +std::shared_ptr make_hwdevice(int width, int height, bool vram) { + return make_hwdevice(width, height, 0, 0, vram); } } // namespace va \ No newline at end of file diff --git a/sunshine/platform/linux/vaapi.h b/sunshine/platform/linux/vaapi.h index a7165166..ddb5c61e 100644 --- a/sunshine/platform/linux/vaapi.h +++ b/sunshine/platform/linux/vaapi.h @@ -8,10 +8,18 @@ namespace egl { struct surface_descriptor_t; } namespace va { -std::shared_ptr make_hwdevice(int width, int height); -std::shared_ptr make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, const egl::surface_descriptor_t &sd); +/** + * Width --> Width of the image + * Height --> Height of the image + * offset_x --> Horizontal offset of the image in the texture + * offset_y --> Vertical offset of the image in the texture + * file_t card --> The file descriptor of the render device used for encoding + */ +std::shared_ptr make_hwdevice(int width, int height, bool vram); +std::shared_ptr make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram); +std::shared_ptr make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram); -// Ensure the render device pointed to by fd is capable of encoding h264 +// Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured bool validate(int fd); int init(); diff --git a/sunshine/platform/linux/wayland.cpp b/sunshine/platform/linux/wayland.cpp index 63bea5a8..338a2115 100644 --- a/sunshine/platform/linux/wayland.cpp +++ b/sunshine/platform/linux/wayland.cpp @@ -133,7 +133,7 @@ void interface_t::del_interface(wl_registry *registry, uint32_t id) { } dmabuf_t::dmabuf_t() - : status { REINIT }, frames {}, current_frame { &frames[0] }, listener { + : status { READY }, frames {}, current_frame { &frames[0] }, listener { (decltype(zwlr_export_dmabuf_frame_v1_listener::frame))&dmabuf_t::frame, (decltype(zwlr_export_dmabuf_frame_v1_listener::object))&dmabuf_t::object, (decltype(zwlr_export_dmabuf_frame_v1_listener::ready))&dmabuf_t::ready, @@ -141,26 +141,6 @@ dmabuf_t::dmabuf_t() } { } -int dmabuf_t::init(wl_display *display_p) { - display = egl::make_display(display_p); - - if(!display) { - return -1; - } - - auto ctx_opt = egl::make_ctx(display.get()); - - if(!ctx_opt) { - return -1; - } - - ctx = std::move(*ctx_opt); - - status = READY; - - return 0; -} - void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) { auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output); zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this); @@ -211,27 +191,9 @@ void dmabuf_t::object( void dmabuf_t::ready( zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec) { - auto next_frame = get_next_frame(); - - auto rgb_opt = egl::import_source(display.get(), - { - next_frame->fds[0], - (int)next_frame->width, - (int)next_frame->height, - (int)next_frame->offsets[0], - (int)next_frame->strides[0], - }); - - if(!rgb_opt) { - status = REINIT; - - return; - } - - next_frame->rgb = std::move(*rgb_opt); current_frame->destroy(); - current_frame = next_frame; + current_frame = get_next_frame(); status = READY; } @@ -254,8 +216,6 @@ void frame_t::destroy() { close(fds[x]); } - rgb = egl::rgb_t {}; - frame = nullptr; obj_count = 0; } diff --git a/sunshine/platform/linux/wayland.h b/sunshine/platform/linux/wayland.h index c55b3fab..31905f60 100644 --- a/sunshine/platform/linux/wayland.h +++ b/sunshine/platform/linux/wayland.h @@ -22,8 +22,6 @@ public: std::uint32_t offsets[4]; std::uint32_t plane_indices[4]; - egl::rgb_t rgb; - zwlr_export_dmabuf_frame_v1 *frame; void destroy(); @@ -45,7 +43,6 @@ public: dmabuf_t(); - int init(wl_display *display); void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false); ~dmabuf_t(); @@ -82,9 +79,6 @@ public: status_e status; - egl::display_t display; - egl::ctx_t ctx; - std::array frames; frame_t *current_frame; diff --git a/sunshine/platform/linux/wlgrab.cpp b/sunshine/platform/linux/wlgrab.cpp index 7dc4d6bd..ae5f4815 100644 --- a/sunshine/platform/linux/wlgrab.cpp +++ b/sunshine/platform/linux/wlgrab.cpp @@ -22,7 +22,7 @@ public: delay = std::chrono::nanoseconds { 1s } / framerate; mem_type = hwdevice_type; - if(display.init() || dmabuf.init(display.get())) { + if(display.init()) { return -1; } @@ -72,6 +72,48 @@ public: return 0; } + int dummy_img(platf::img_t *img) override { + return 0; + } + + inline platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) { + auto to = std::chrono::steady_clock::now() + timeout; + + dmabuf.listen(interface.dmabuf_manager, output, cursor); + do { + display.roundtrip(); + + if(to < std::chrono::steady_clock::now()) { + return platf::capture_e::timeout; + } + } while(dmabuf.status == dmabuf_t::WAITING); + + auto current_frame = dmabuf.current_frame; + + if( + dmabuf.status == dmabuf_t::REINIT || + current_frame->width != width || + current_frame->height != height) { + + return platf::capture_e::reinit; + } + + return platf::capture_e::ok; + } + + platf::mem_type_e mem_type; + + std::chrono::nanoseconds delay; + + wl::display_t display; + interface_t interface; + dmabuf_t dmabuf; + + wl_output *output; +}; + +class wlr_ram_t : public wlr_t { +public: platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr img, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); @@ -106,36 +148,61 @@ public: } platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) { - auto to = std::chrono::steady_clock::now() + timeout; - - dmabuf.listen(interface.dmabuf_manager, output, cursor); - do { - display.roundtrip(); - - if(to < std::chrono::steady_clock::now()) { - return platf::capture_e::timeout; - } - } while(dmabuf.status == dmabuf_t::WAITING); + auto status = wlr_t::snapshot(img_out_base, timeout, cursor); + if(status != platf::capture_e::ok) { + return status; + } auto current_frame = dmabuf.current_frame; - if( - dmabuf.status == dmabuf_t::REINIT || - current_frame->width != width || - current_frame->height != height) { + auto rgb_opt = egl::import_source(egl_display.get(), + { + current_frame->fds[0], + (int)current_frame->width, + (int)current_frame->height, + (int)current_frame->offsets[0], + (int)current_frame->strides[0], + }); + if(!rgb_opt) { return platf::capture_e::reinit; } - auto &rgb = current_frame->rgb; - - gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]); - gl::ctx.GetTextureSubImage(rgb->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out_base->height * img_out_base->row_pitch, img_out_base->data); + gl::ctx.BindTexture(GL_TEXTURE_2D, (*rgb_opt)->tex[0]); + gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out_base->height * img_out_base->row_pitch, img_out_base->data); gl::ctx.BindTexture(GL_TEXTURE_2D, 0); return platf::capture_e::ok; } + int init(platf::mem_type_e hwdevice_type, const std::string &display_name, int framerate) { + if(wlr_t::init(hwdevice_type, display_name, framerate)) { + return -1; + } + + egl_display = egl::make_display(display.get()); + if(!egl_display) { + return -1; + } + + auto ctx_opt = egl::make_ctx(egl_display.get()); + if(!ctx_opt) { + return -1; + } + + ctx = std::move(*ctx_opt); + + return 0; + } + + std::shared_ptr make_hwdevice(platf::pix_fmt_e pix_fmt) override { + if(mem_type == platf::mem_type_e::vaapi) { + return va::make_hwdevice(width, height, false); + } + + return std::make_shared(); + } + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; @@ -147,27 +214,91 @@ public: return img; } - int dummy_img(platf::img_t *img) override { - return 0; + egl::display_t egl_display; + egl::ctx_t ctx; +}; + +class wlr_vram_t : public wlr_t { +public: + platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr img, bool *cursor) override { + auto next_frame = std::chrono::steady_clock::now(); + + while(img) { + auto now = std::chrono::steady_clock::now(); + + if(next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); + } + while(next_frame > now) { + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; + + auto status = snapshot(img.get(), 1000ms, *cursor); + switch(status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + return status; + case platf::capture_e::timeout: + continue; + case platf::capture_e::ok: + img = snapshot_cb(img); + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; + return status; + } + } + + return platf::capture_e::ok; + } + + platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) { + auto status = wlr_t::snapshot(img_out_base, timeout, cursor); + if(status != platf::capture_e::ok) { + return status; + } + + auto img = (egl::img_descriptor_t *)img_out_base; + + auto current_frame = dmabuf.current_frame; + + ++sequence; + img->sequence = sequence; + + img->obj_count = 1; + img->img_width = current_frame->width; + img->img_height = current_frame->height; + img->obj_count = current_frame->obj_count; + + std::copy_n(std::begin(current_frame->fds), current_frame->obj_count, img->fds); + std::copy_n(std::begin(current_frame->offsets), current_frame->obj_count, img->offsets); + std::copy_n(std::begin(current_frame->strides), current_frame->obj_count, img->strides); + + return platf::capture_e::ok; + } + + std::shared_ptr alloc_img() override { + auto img = std::make_shared(); + + img->img_width = width; + img->img_height = height; + img->sequence = 0; + img->serial = std::numeric_limitsserial)>::max(); + img->data = nullptr; + + return img; } std::shared_ptr make_hwdevice(platf::pix_fmt_e pix_fmt) override { if(mem_type == platf::mem_type_e::vaapi) { - return va::make_hwdevice(width, height); + return va::make_hwdevice(width, height, 0, 0, true); } return std::make_shared(); } - platf::mem_type_e mem_type; - - std::chrono::nanoseconds delay; - - wl::display_t display; - interface_t interface; - dmabuf_t dmabuf; - - wl_output *output; + std::uint64_t sequence {}; }; } // namespace wl @@ -179,7 +310,16 @@ std::shared_ptr wl_display(mem_type_e hwdevice_type, const std::strin return nullptr; } - auto wlr = std::make_shared(); + if(hwdevice_type == platf::mem_type_e::vaapi) { + auto wlr = std::make_shared(); + if(wlr->init(hwdevice_type, display_name, framerate)) { + return nullptr; + } + + return wlr; + } + + auto wlr = std::make_shared(); if(wlr->init(hwdevice_type, display_name, framerate)) { return nullptr; } diff --git a/sunshine/platform/linux/x11grab.cpp b/sunshine/platform/linux/x11grab.cpp index 8d4f2b4f..de12f62b 100644 --- a/sunshine/platform/linux/x11grab.cpp +++ b/sunshine/platform/linux/x11grab.cpp @@ -513,7 +513,7 @@ struct x11_attr_t : public display_t { std::shared_ptr make_hwdevice(pix_fmt_e pix_fmt) override { if(mem_type == mem_type_e::vaapi) { - return va::make_hwdevice(width, height); + return va::make_hwdevice(width, height, false); } return std::make_shared();