Omit copy to RAM when possible with VAAPI
This commit is contained in:
@@ -106,10 +106,13 @@ else()
|
|||||||
add_compile_definitions(SUNSHINE_PLATFORM="linux")
|
add_compile_definitions(SUNSHINE_PLATFORM="linux")
|
||||||
list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_linux.json")
|
list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_linux.json")
|
||||||
|
|
||||||
if(NOT DEFINED SUNSHINE_DISABLE_X11)
|
option(SUNSHINE_ENABLE_DRM "Enable KMS grab if available" ON)
|
||||||
|
option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON)
|
||||||
|
|
||||||
|
if(${SUNSHINE_ENABLE_X11})
|
||||||
find_package(X11)
|
find_package(X11)
|
||||||
endif()
|
endif()
|
||||||
if(NOT DEFINED SUNSHINE_DISABLE_DRM)
|
if(${SUNSHINE_ENABLE_DRM})
|
||||||
find_package(LIBDRM)
|
find_package(LIBDRM)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
#include "sunshine/video.h"
|
#include "sunshine/video.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
// I want to have as little build dependencies as possible
|
// I want to have as little build dependencies as possible
|
||||||
@@ -460,7 +456,7 @@ std::optional<nv12_t> import_target(display_t::pointer egl_display, std::array<f
|
|||||||
return nv12;
|
return nv12;
|
||||||
}
|
}
|
||||||
|
|
||||||
void egl_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||||
video::color_t *color_p;
|
video::color_t *color_p;
|
||||||
switch(colorspace) {
|
switch(colorspace) {
|
||||||
case 5: // SWS_CS_SMPTE170M
|
case 5: // SWS_CS_SMPTE170M
|
||||||
@@ -491,31 +487,25 @@ void egl_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range)
|
|||||||
color_matrix.update(members, sizeof(members) / sizeof(decltype(members[0])));
|
color_matrix.update(members, sizeof(members) / sizeof(decltype(members[0])));
|
||||||
}
|
}
|
||||||
|
|
||||||
int egl_t::init(int in_width, int in_height, file_t &&fd) {
|
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex) {
|
||||||
file = std::move(fd);
|
sws_t sws;
|
||||||
|
|
||||||
if(!gbm::create_device) {
|
// Ensure aspect ratio is maintained
|
||||||
BOOST_LOG(warning) << "libgbm not initialized"sv;
|
auto scalar = std::fminf(out_width / (float)in_width, out_heigth / (float)in_height);
|
||||||
return -1;
|
auto out_width_f = in_width * scalar;
|
||||||
}
|
auto out_height_f = in_height * scalar;
|
||||||
|
|
||||||
gbm.reset(gbm::create_device(file.el));
|
// result is always positive
|
||||||
if(!gbm) {
|
auto offsetX_f = (out_width - out_width_f) / 2;
|
||||||
BOOST_LOG(error) << "Couldn't create GBM device: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
auto offsetY_f = (out_heigth - out_height_f) / 2;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
display = make_display(gbm.get());
|
sws.width = out_width_f;
|
||||||
if(!display) {
|
sws.height = out_height_f;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ctx_opt = make_ctx(display.get());
|
sws.offsetX = offsetX_f;
|
||||||
if(!ctx_opt) {
|
sws.offsetY = offsetY_f;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = std::move(*ctx_opt);
|
auto width_i = 1.0f / sws.width;
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *sources[] {
|
const char *sources[] {
|
||||||
@@ -549,28 +539,37 @@ int egl_t::init(int in_width, int in_height, file_t &&fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(error_flag) {
|
if(error_flag) {
|
||||||
return -1;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto program = gl::program_t::link(compiled_sources[1].left(), compiled_sources[0].left());
|
auto program = gl::program_t::link(compiled_sources[1].left(), compiled_sources[0].left());
|
||||||
if(program.has_right()) {
|
if(program.has_right()) {
|
||||||
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
||||||
return -1;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UV - shader
|
// UV - shader
|
||||||
this->program[1] = std::move(program.left());
|
sws.program[1] = std::move(program.left());
|
||||||
|
|
||||||
program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[2].left());
|
program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[2].left());
|
||||||
if(program.has_right()) {
|
if(program.has_right()) {
|
||||||
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
||||||
return -1;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y - shader
|
// Y - shader
|
||||||
this->program[0] = std::move(program.left());
|
sws.program[0] = std::move(program.left());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto loc_width_i = gl::ctx.GetUniformLocation(sws.program[1].handle(), "width_i");
|
||||||
|
if(loc_width_i < 0) {
|
||||||
|
BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::ctx.UseProgram(sws.program[1].handle());
|
||||||
|
gl::ctx.Uniform1fv(loc_width_i, 1, &width_i);
|
||||||
|
|
||||||
auto color_p = &video::colors[0];
|
auto color_p = &video::colors[0];
|
||||||
std::pair<const char *, std::string_view> members[] {
|
std::pair<const char *, std::string_view> members[] {
|
||||||
std::make_pair("color_vec_y", util::view(color_p->color_vec_y)),
|
std::make_pair("color_vec_y", util::view(color_p->color_vec_y)),
|
||||||
@@ -580,25 +579,46 @@ int egl_t::init(int in_width, int in_height, file_t &&fd) {
|
|||||||
std::make_pair("range_uv", util::view(color_p->range_uv)),
|
std::make_pair("range_uv", util::view(color_p->range_uv)),
|
||||||
};
|
};
|
||||||
|
|
||||||
auto color_matrix = program[0].uniform("ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0])));
|
auto color_matrix = sws.program[0].uniform("ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0])));
|
||||||
if(!color_matrix) {
|
if(!color_matrix) {
|
||||||
return -1;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->color_matrix = std::move(*color_matrix);
|
sws.color_matrix = std::move(*color_matrix);
|
||||||
|
|
||||||
tex_in = gl::tex_t::make(1);
|
sws.tex = std::move(tex);
|
||||||
|
|
||||||
this->in_width = in_width;
|
gl_drain_errors;
|
||||||
this->in_height = in_height;
|
|
||||||
return 0;
|
return std::move(sws);
|
||||||
}
|
}
|
||||||
|
|
||||||
int egl_t::convert(platf::img_t &img) {
|
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_heigth) {
|
||||||
auto tex = tex_in[0];
|
auto tex = gl::tex_t::make(1);
|
||||||
|
gl::ctx.BindTexture(GL_TEXTURE_2D, tex[0]);
|
||||||
|
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, in_width, in_height);
|
||||||
|
|
||||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex);
|
return make(in_width, in_height, out_width, out_heigth, std::move(tex));
|
||||||
gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, in_width, in_height, GL_BGRA, GL_UNSIGNED_BYTE, img.data);
|
}
|
||||||
|
|
||||||
|
void sws_t::load_ram(platf::img_t &img) {
|
||||||
|
gl::ctx.BindTexture(GL_TEXTURE_2D, tex[0]);
|
||||||
|
gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_BGRA, GL_UNSIGNED_BYTE, img.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sws_t::load_vram(platf::img_t &img, int offset_x, int offset_y, int framebuffer) {
|
||||||
|
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||||
|
gl::ctx.ReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
|
gl::ctx.BindTexture(GL_TEXTURE_2D, tex[0]);
|
||||||
|
gl::ctx.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offset_x, offset_y, img.width, img.height);
|
||||||
|
|
||||||
|
gl::ctx.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
int sws_t::convert(nv12_t &nv12) {
|
||||||
|
auto texture = tex[0];
|
||||||
|
|
||||||
|
gl::ctx.BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
GLenum attachments[] {
|
GLenum attachments[] {
|
||||||
GL_COLOR_ATTACHMENT0,
|
GL_COLOR_ATTACHMENT0,
|
||||||
@@ -615,61 +635,17 @@ int egl_t::convert(platf::img_t &img) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex);
|
gl::ctx.BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
gl::ctx.UseProgram(program[x].handle());
|
gl::ctx.UseProgram(program[x].handle());
|
||||||
program[x].bind(color_matrix);
|
program[x].bind(color_matrix);
|
||||||
|
|
||||||
gl::ctx.Viewport(offsetX / (x + 1), offsetY / (x + 1), out_width / (x + 1), out_height / (x + 1));
|
gl::ctx.Viewport(offsetX / (x + 1), offsetY / (x + 1), width / (x + 1), height / (x + 1));
|
||||||
gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3);
|
gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int egl_t::_set_frame(AVFrame *frame) {
|
|
||||||
this->hwframe.reset(frame);
|
|
||||||
this->frame = frame;
|
|
||||||
|
|
||||||
// Ensure aspect ratio is maintained
|
|
||||||
auto scalar = std::fminf(frame->width / (float)in_width, frame->height / (float)in_height);
|
|
||||||
auto out_width_f = in_width * scalar;
|
|
||||||
auto out_height_f = in_height * scalar;
|
|
||||||
|
|
||||||
// result is always positive
|
|
||||||
auto offsetX_f = (frame->width - out_width_f) / 2;
|
|
||||||
auto offsetY_f = (frame->height - out_height_f) / 2;
|
|
||||||
|
|
||||||
out_width = out_width_f;
|
|
||||||
out_height = out_height_f;
|
|
||||||
|
|
||||||
offsetX = offsetX_f;
|
|
||||||
offsetY = offsetY_f;
|
|
||||||
|
|
||||||
auto tex = tex_in[0];
|
|
||||||
|
|
||||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex);
|
|
||||||
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, in_width, in_height);
|
|
||||||
|
|
||||||
auto loc_width_i = gl::ctx.GetUniformLocation(program[1].handle(), "width_i");
|
|
||||||
if(loc_width_i < 0) {
|
|
||||||
BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto width_i = 1.0f / out_width;
|
|
||||||
gl::ctx.UseProgram(program[1].handle());
|
|
||||||
gl::ctx.Uniform1fv(loc_width_i, 1, &width_i);
|
|
||||||
|
|
||||||
gl_drain_errors;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_t::~egl_t() {
|
|
||||||
if(gl::ctx.GetError) {
|
|
||||||
gl_drain_errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace egl
|
} // namespace egl
|
||||||
|
|
||||||
void free_frame(AVFrame *frame) {
|
void free_frame(AVFrame *frame) {
|
||||||
|
|||||||
@@ -155,12 +155,6 @@ int init();
|
|||||||
namespace egl {
|
namespace egl {
|
||||||
using display_t = util::dyn_safe_ptr_v2<void, EGLBoolean, &eglTerminate>;
|
using display_t = util::dyn_safe_ptr_v2<void, EGLBoolean, &eglTerminate>;
|
||||||
|
|
||||||
KITTY_USING_MOVE_T(file_t, int, -1, {
|
|
||||||
if(el >= 0) {
|
|
||||||
close(el);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
struct rgb_img_t {
|
struct rgb_img_t {
|
||||||
display_t::pointer display;
|
display_t::pointer display;
|
||||||
EGLImage xrgb8;
|
EGLImage xrgb8;
|
||||||
@@ -227,37 +221,24 @@ std::optional<nv12_t> import_target(
|
|||||||
std::array<file_t, nv12_img_t::num_fds> &&fds,
|
std::array<file_t, nv12_img_t::num_fds> &&fds,
|
||||||
const surface_descriptor_t &r8, const surface_descriptor_t &gr88);
|
const surface_descriptor_t &r8, const surface_descriptor_t &gr88);
|
||||||
|
|
||||||
class egl_t : public platf::hwdevice_t {
|
class sws_t {
|
||||||
public:
|
public:
|
||||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override;
|
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex);
|
||||||
|
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_heigth);
|
||||||
|
|
||||||
int init(int in_width, int in_height, file_t &&fd);
|
int convert(nv12_t &nv12);
|
||||||
|
|
||||||
int convert(platf::img_t &img) override;
|
void load_ram(platf::img_t &img);
|
||||||
|
void load_vram(platf::img_t &img, int offset_x, int offset_y, int framebuffer);
|
||||||
|
|
||||||
/**
|
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||||
* Any specialization needs to populate nv12_t nv12
|
|
||||||
* Then call this function
|
|
||||||
*/
|
|
||||||
int _set_frame(AVFrame *frame);
|
|
||||||
|
|
||||||
~egl_t() override;
|
gl::tex_t tex;
|
||||||
|
|
||||||
int in_width, in_height;
|
|
||||||
int out_width, out_height;
|
|
||||||
int offsetX, offsetY;
|
|
||||||
|
|
||||||
frame_t hwframe;
|
|
||||||
|
|
||||||
file_t file;
|
|
||||||
gbm::gbm_t gbm;
|
|
||||||
display_t display;
|
|
||||||
ctx_t ctx;
|
|
||||||
|
|
||||||
gl::tex_t tex_in;
|
|
||||||
nv12_t nv12;
|
|
||||||
gl::program_t program[2];
|
gl::program_t program[2];
|
||||||
gl::buffer_t color_matrix;
|
gl::buffer_t color_matrix;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int offsetX, offsetY;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool fail();
|
bool fail();
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ public:
|
|||||||
return drmModeGetCrtc(fd.el, id);
|
return drmModeGetCrtc(fd.el, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
egl::file_t handleFD(std::uint32_t handle) {
|
file_t handleFD(std::uint32_t handle) {
|
||||||
egl::file_t fb_fd;
|
file_t fb_fd;
|
||||||
|
|
||||||
auto status = drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el);
|
auto status = drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el);
|
||||||
if(status) {
|
if(status) {
|
||||||
@@ -180,7 +180,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
egl::file_t fd;
|
file_t fd;
|
||||||
plane_res_t plane_res;
|
plane_res_t plane_res;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -228,12 +228,11 @@ class display_t : public platf::display_t {
|
|||||||
public:
|
public:
|
||||||
display_t(mem_type_e mem_type) : platf::display_t(), mem_type { mem_type } {}
|
display_t(mem_type_e mem_type) : platf::display_t(), mem_type { mem_type } {}
|
||||||
|
|
||||||
int init(const std::string &display_name, int framerate) {
|
mem_type_e mem_type;
|
||||||
if(!gbm::create_device) {
|
|
||||||
BOOST_LOG(warning) << "libgbm not initialized"sv;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
std::chrono::nanoseconds delay;
|
||||||
|
|
||||||
|
int init(const std::string &display_name, int framerate) {
|
||||||
delay = std::chrono::nanoseconds { 1s } / framerate;
|
delay = std::chrono::nanoseconds { 1s } / framerate;
|
||||||
|
|
||||||
constexpr auto path = "/dev/dri/card1";
|
constexpr auto path = "/dev/dri/card1";
|
||||||
@@ -244,8 +243,6 @@ public:
|
|||||||
int monitor_index = util::from_view(display_name);
|
int monitor_index = util::from_view(display_name);
|
||||||
int monitor = 0;
|
int monitor = 0;
|
||||||
|
|
||||||
int pitch;
|
|
||||||
|
|
||||||
auto end = std::end(card);
|
auto end = std::end(card);
|
||||||
for(auto plane = std::begin(card); plane != end; ++plane) {
|
for(auto plane = std::begin(card); plane != end; ++plane) {
|
||||||
if(monitor != monitor_index) {
|
if(monitor != monitor_index) {
|
||||||
@@ -299,6 +296,30 @@ public:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int img_width, img_height;
|
||||||
|
int pitch;
|
||||||
|
|
||||||
|
card_t card;
|
||||||
|
file_t fb_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
class display_ram_t : public display_t {
|
||||||
|
public:
|
||||||
|
display_ram_t(mem_type_e mem_type) : display_t(mem_type) {}
|
||||||
|
|
||||||
|
int init(const std::string &display_name, int framerate) {
|
||||||
|
if(!gbm::create_device) {
|
||||||
|
BOOST_LOG(warning) << "libgbm not initialized"sv;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(display_t::init(display_name, framerate)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
gbm.reset(gbm::create_device(card.fd.el));
|
gbm.reset(gbm::create_device(card.fd.el));
|
||||||
if(!gbm) {
|
if(!gbm) {
|
||||||
BOOST_LOG(error) << "Couldn't create GBM device: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
BOOST_LOG(error) << "Couldn't create GBM device: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||||
@@ -400,24 +421,100 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int img_width, img_height;
|
|
||||||
mem_type_e mem_type;
|
|
||||||
|
|
||||||
std::chrono::nanoseconds delay;
|
|
||||||
|
|
||||||
card_t card;
|
|
||||||
egl::file_t fb_fd;
|
|
||||||
|
|
||||||
gbm::gbm_t gbm;
|
gbm::gbm_t gbm;
|
||||||
egl::display_t display;
|
egl::display_t display;
|
||||||
egl::ctx_t ctx;
|
egl::ctx_t ctx;
|
||||||
|
|
||||||
egl::rgb_t rgb;
|
egl::rgb_t rgb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class display_vram_t : public display_t {
|
||||||
|
public:
|
||||||
|
display_vram_t(mem_type_e mem_type) : display_t(mem_type) {}
|
||||||
|
|
||||||
|
std::shared_ptr<hwdevice_t> 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG(error) << "Unsupported pixel format for egl::display_vram_t: "sv << platf::from_pix_fmt(pix_fmt);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<img_t> alloc_img() override {
|
||||||
|
auto img = std::make_shared<img_t>();
|
||||||
|
|
||||||
|
img->width = width;
|
||||||
|
img->height = height;
|
||||||
|
img->pixel_pitch = 4;
|
||||||
|
img->row_pitch = img->pixel_pitch * width;
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dummy_img(platf::img_t *img) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) {
|
||||||
|
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:
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
continue;
|
||||||
|
case platf::capture_e::ok:
|
||||||
|
img = snapshot_cb(img);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return capture_e::ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_e snapshot(img_t * /*img_out_base */, std::chrono::milliseconds /* timeout */, bool /* cursor */) {
|
||||||
|
return capture_e::ok;
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace kms
|
} // namespace kms
|
||||||
|
|
||||||
std::shared_ptr<display_t> kms_display(mem_type_e hwdevice_type, const std::string &display_name, int framerate) {
|
std::shared_ptr<display_t> kms_display(mem_type_e hwdevice_type, const std::string &display_name, int framerate) {
|
||||||
auto disp = std::make_shared<kms::display_t>(hwdevice_type);
|
if(hwdevice_type == mem_type_e::vaapi) {
|
||||||
|
auto disp = std::make_shared<kms::display_vram_t>(hwdevice_type);
|
||||||
|
|
||||||
|
if(!disp->init(display_name, framerate)) {
|
||||||
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the case of failure, attempt the old method for VAAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
auto disp = std::make_shared<kms::display_ram_t>(hwdevice_type);
|
||||||
|
|
||||||
if(disp->init(display_name, framerate)) {
|
if(disp->init(display_name, framerate)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
#ifndef SUNSHINE_PLATFORM_MISC_H
|
#ifndef SUNSHINE_PLATFORM_MISC_H
|
||||||
#define SUNSHINE_PLATFORM_MISC_H
|
#define SUNSHINE_PLATFORM_MISC_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "sunshine/utility.h"
|
||||||
|
|
||||||
|
KITTY_USING_MOVE_T(file_t, int, -1, {
|
||||||
|
if(el >= 0) {
|
||||||
|
close(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
namespace dyn {
|
namespace dyn {
|
||||||
typedef void (*apiproc)(void);
|
typedef void (*apiproc)(void);
|
||||||
|
|
||||||
|
|||||||
@@ -160,29 +160,47 @@ int init() {
|
|||||||
|
|
||||||
int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf);
|
int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf);
|
||||||
|
|
||||||
class va_t : public egl::egl_t {
|
class va_t : public platf::hwdevice_t {
|
||||||
public:
|
public:
|
||||||
int init(int in_width, int in_height, const char *render_device) {
|
int init(int in_width, int in_height, file_t &&render_device) {
|
||||||
if(!va::initialize) {
|
file = std::move(render_device);
|
||||||
BOOST_LOG(warning) << "libva not initialized"sv;
|
|
||||||
|
if(!va::initialize || !gbm::create_device) {
|
||||||
|
if(!va::initialize) BOOST_LOG(warning) << "libva not initialized"sv;
|
||||||
|
if(!gbm::create_device) BOOST_LOG(warning) << "libgbm not initialized"sv;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = (void *)vaapi_make_hwdevice_ctx;
|
this->data = (void *)vaapi_make_hwdevice_ctx;
|
||||||
|
|
||||||
egl::file_t fd = open(render_device, O_RDWR);
|
gbm.reset(gbm::create_device(file.el));
|
||||||
if(fd.el < 0) {
|
if(!gbm) {
|
||||||
char string[1024];
|
char string[1024];
|
||||||
BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " << strerror_r(errno, string, sizeof(string));
|
BOOST_LOG(error) << "Couldn't create GBM device: ["sv << strerror_r(errno, string, sizeof(string)) << ']';
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return egl::egl_t::init(in_width, in_height, std::move(fd));
|
display = egl::make_display(gbm.get());
|
||||||
|
if(!display) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ctx_opt = egl::make_ctx(display.get());
|
||||||
|
if(!ctx_opt) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = std::move(*ctx_opt);
|
||||||
|
|
||||||
|
width = in_width;
|
||||||
|
height = in_height;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_frame(AVFrame *frame) override {
|
int _set_frame(AVFrame *frame) {
|
||||||
// No deallocation necessary
|
this->hwframe.reset(frame);
|
||||||
|
this->frame = frame;
|
||||||
|
|
||||||
if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) {
|
if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) {
|
||||||
BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv;
|
BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv;
|
||||||
@@ -194,7 +212,7 @@ public:
|
|||||||
va::VASurfaceID surface = (std::uintptr_t)frame->data[3];
|
va::VASurfaceID surface = (std::uintptr_t)frame->data[3];
|
||||||
|
|
||||||
auto status = va::exportSurfaceHandle(
|
auto status = va::exportSurfaceHandle(
|
||||||
va_display,
|
this->va_display,
|
||||||
surface,
|
surface,
|
||||||
va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
||||||
va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS,
|
va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS,
|
||||||
@@ -207,7 +225,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of file descriptors
|
// Keep track of file descriptors
|
||||||
std::array<egl::file_t, egl::nv12_img_t::num_fds> fds;
|
std::array<file_t, egl::nv12_img_t::num_fds> fds;
|
||||||
for(int x = 0; x < prime.num_objects; ++x) {
|
for(int x = 0; x < prime.num_objects; ++x) {
|
||||||
fds[x] = prime.objects[x].fd;
|
fds[x] = prime.objects[x].fd;
|
||||||
}
|
}
|
||||||
@@ -234,12 +252,106 @@ public:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nv12 = std::move(*nv12_opt);
|
this->nv12 = std::move(*nv12_opt);
|
||||||
|
|
||||||
return egl::egl_t::_set_frame(frame);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||||
|
sws.set_colorspace(colorspace, color_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
va::display_t::pointer va_display;
|
va::display_t::pointer va_display;
|
||||||
|
file_t file;
|
||||||
|
|
||||||
|
frame_t hwframe;
|
||||||
|
|
||||||
|
gbm::gbm_t gbm;
|
||||||
|
egl::display_t display;
|
||||||
|
egl::ctx_t ctx;
|
||||||
|
|
||||||
|
egl::sws_t sws;
|
||||||
|
egl::nv12_t nv12;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
};
|
||||||
|
|
||||||
|
class va_ram_t : public va_t {
|
||||||
|
public:
|
||||||
|
int convert(platf::img_t &img) override {
|
||||||
|
sws.load_ram(img);
|
||||||
|
|
||||||
|
sws.convert(nv12);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_frame(AVFrame *frame) override {
|
||||||
|
if(_set_frame(frame)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sws_opt = egl::sws_t::make(width, height, frame->width, frame->height);
|
||||||
|
if(!sws_opt) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sws = std::move(*sws_opt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class va_vram_t : public va_t {
|
||||||
|
public:
|
||||||
|
int convert(platf::img_t &img) override {
|
||||||
|
sws.load_vram(img, 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) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
this->offset_x = offset_x;
|
||||||
|
this->offset_y = offset_y;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_frame(AVFrame *frame) override {
|
||||||
|
if(_set_frame(frame)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sws_opt = egl::sws_t::make(width, height, frame->width, frame->height);
|
||||||
|
if(!sws_opt) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sws = std::move(*sws_opt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_t fb_fd;
|
||||||
|
|
||||||
|
egl::rgb_t rgb;
|
||||||
|
gl::frame_buf_t framebuffer;
|
||||||
|
|
||||||
|
int offset_x, offset_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,10 +455,27 @@ int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height) {
|
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height) {
|
||||||
auto egl = std::make_shared<va::va_t>();
|
|
||||||
|
|
||||||
auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str();
|
auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str();
|
||||||
if(egl->init(width, height, render_device)) {
|
|
||||||
|
file_t file = open(render_device, O_RDWR);
|
||||||
|
if(file.el < 0) {
|
||||||
|
char string[1024];
|
||||||
|
BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " << strerror_r(errno, string, sizeof(string));
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto egl = std::make_shared<va::va_ram_t>();
|
||||||
|
if(egl->init(width, height, std::move(file))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return egl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<platf::hwdevice_t> 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<va::va_vram_t>();
|
||||||
|
if(egl->init(width, height, std::move(card), offset_x, offset_y, sd)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
#ifndef SUNSHINE_DISPLAY_H
|
#ifndef SUNSHINE_VAAPI_H
|
||||||
#define SUNSHINE_DISPLAY_H
|
#define SUNSHINE_VAAPI_H
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
#include "sunshine/platform/common.h"
|
#include "sunshine/platform/common.h"
|
||||||
|
|
||||||
|
namespace egl {
|
||||||
|
struct surface_descriptor_t;
|
||||||
|
}
|
||||||
namespace va {
|
namespace va {
|
||||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height);
|
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height);
|
||||||
|
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, const egl::surface_descriptor_t &sd);
|
||||||
|
|
||||||
int init();
|
int init();
|
||||||
} // namespace va
|
} // namespace va
|
||||||
|
|||||||
Reference in New Issue
Block a user