Sunshine master
Self-hosted game stream host for Moonlight.
graphics.h
Go to the documentation of this file.
1
5#pragma once
6
7// standard includes
8#include <optional>
9#include <string_view>
10
11// lib includes
12#include <glad/egl.h>
13#include <glad/gl.h>
14
15// local includes
16#include "misc.h"
17#include "src/logging.h"
18#include "src/platform/common.h"
19#include "src/utility.h"
21
26#define SUNSHINE_STRINGIFY_HELPER(x) #x
31#define SUNSHINE_STRINGIFY(x) SUNSHINE_STRINGIFY_HELPER(x)
36#define gl_drain_errors_helper(x) gl::drain_errors(x)
41#define gl_drain_errors gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__))
42
49extern "C" int close(int __fd);
50
51// X11 Display
52extern "C" struct _XDisplay;
53
54struct AVFrame;
60void free_frame(AVFrame *frame);
61
66
67namespace gl {
68 extern GladGLContext ctx;
69
70 // glEGLImageTargetTexture2DOES (GL_OES_EGL_image) is not part of desktop GL —
71 // it is a GLES extension that must be loaded manually via eglGetProcAddress.
72 // GLeglImageOES is typedef void* per the Khronos spec (gl.xml).
76 using PFNGLEGLIMAGETARGETTEXTURE2DOESPROC = void (*)(GLenum target, void *image);
83
89 void drain_errors(const std::string_view &prefix);
90
94 class tex_t: public util::buffer_t<GLuint> {
95 using util::buffer_t<GLuint>::buffer_t;
96
97 public:
101 tex_t(tex_t &&) = default;
107 tex_t &operator=(tex_t &&) = default;
108
109 ~tex_t();
110
117 static tex_t make(std::size_t count);
118 };
119
123 class frame_buf_t: public util::buffer_t<GLuint> {
124 using util::buffer_t<GLuint>::buffer_t;
125
126 public:
137
138 ~frame_buf_t();
139
146 static frame_buf_t make(std::size_t count);
147
151 inline void bind(std::nullptr_t, std::nullptr_t) {
152 int x = 0;
153 for (auto fb : (*this)) {
154 ctx.BindFramebuffer(GL_FRAMEBUFFER, fb);
155 ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0);
156
157 ++x;
158 }
159 return;
160 }
161
168 template<class It>
169 void bind(It it_begin, It it_end) {
170 using namespace std::literals;
171 if (std::distance(it_begin, it_end) > size()) {
172 BOOST_LOG(warning) << "To many elements to bind"sv;
173 return;
174 }
175
176 int x = 0;
177 std::for_each(it_begin, it_end, [&](auto tex) {
178 ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[x]);
179 ctx.BindTexture(GL_TEXTURE_2D, tex);
180
181 ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, tex, 0);
182
183 ++x;
184 });
185 }
186
197 void copy(int id, int texture, int offset_x, int offset_y, int width, int height);
198 };
199
203 class shader_t {
204 KITTY_USING_MOVE_T(shader_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
205 if (el != std::numeric_limits<GLuint>::max()) {
206 ctx.DeleteShader(el);
207 }
208 });
209
210 public:
216 std::string err_str();
217
225 static util::Either<shader_t, std::string> compile(const std::string_view &source, GLenum type);
226
232 GLuint handle() const;
233
234 private:
235 shader_internal_t _shader;
236 };
237
241 class buffer_t {
242 KITTY_USING_MOVE_T(buffer_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
243 if (el != std::numeric_limits<GLuint>::max()) {
244 ctx.DeleteBuffers(1, &el);
245 }
246 });
247
248 public:
257 static buffer_t make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data);
258
264 GLuint handle() const;
265
271 const char *block() const;
272
279 void update(const std::string_view &view, std::size_t offset = 0);
287 void update(std::string_view *members, std::size_t count, std::size_t offset = 0);
288
289 private:
290 const char *_block;
291
292 std::size_t _size;
293
294 util::buffer_t<GLint> _offsets;
295
296 buffer_internal_t _buffer;
297 };
298
302 class program_t {
303 KITTY_USING_MOVE_T(program_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
304 if (el != std::numeric_limits<GLuint>::max()) {
305 ctx.DeleteProgram(el);
306 }
307 });
308
309 public:
315 std::string err_str();
316
324 static util::Either<program_t, std::string> link(const shader_t &vert, const shader_t &frag);
325
331 void bind(const buffer_t &buffer);
332
341 std::optional<buffer_t> uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count);
342
348 GLuint handle() const;
349
350 private:
351 program_internal_t _program;
352 };
353} // namespace gl
354
355namespace gbm {
356 struct device;
360 typedef void (*device_destroy_fn)(device *gbm);
364 typedef device *(*create_device_fn)(int fd);
365
368
373
379 int init();
380
381} // namespace gbm
382
383namespace egl {
388
398
402 struct nv12_img_t {
404 EGLImage r8;
405 EGLImage bg88;
406
409
410 // sizeof(va::DRMPRIMESurfaceDescriptor::objects) / sizeof(va::DRMPRIMESurfaceDescriptor::objects[0]);
411 static constexpr std::size_t num_fds = 4;
412
413 std::array<file_t, num_fds> fds;
414 };
415
421 EGLImage r8;
422 EGLImage g8;
423 EGLImage b8;
424
427
428 static constexpr std::size_t num_fds = 4;
429
430 std::array<file_t, num_fds> fds;
431 };
432
433#ifndef DOXYGEN
434 KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , {
435 if (el.xrgb8) {
436 eglDestroyImage(el.display, el.xrgb8);
437 }
438 });
439
440 KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , {
441 if (el.r8) {
442 eglDestroyImage(el.display, el.r8);
443 }
444
445 if (el.bg88) {
446 eglDestroyImage(el.display, el.bg88);
447 }
448 });
449
450 KITTY_USING_MOVE_T(yuv444_t, yuv444_img_t, , {
451 if (el.r8) {
452 eglDestroyImage(el.display, el.r8);
453 }
454
455 if (el.g8) {
456 eglDestroyImage(el.display, el.g8);
457 }
458
459 if (el.b8) {
460 eglDestroyImage(el.display, el.b8);
461 }
462 });
463
464 KITTY_USING_MOVE_T(ctx_t, (std::tuple<display_t::pointer, EGLContext>), , {
465 TUPLE_2D_REF(disp, ctx, el);
466 if (ctx) {
467 eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
468 eglDestroyContext(disp, ctx);
469 }
470 });
471#else
475 class rgb_t;
476
480 class nv12_t;
481
485 class yuv444_t;
486
490 class ctx_t;
491#endif
492
497 int width;
498 int height;
499 int fds[4];
500 std::uint32_t fourcc;
501 std::uint64_t modifier;
502 std::uint32_t pitches[4];
503 std::uint32_t offsets[4];
504 };
505
512 display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display);
519 std::optional<ctx_t> make_ctx(display_t::pointer display);
520
526 std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb);
527
533 rgb_t create_blank(platf::img_t &img);
534
540 std::optional<nv12_t> import_target(
541 display_t::pointer egl_display,
542 std::array<file_t, nv12_img_t::num_fds> &&fds,
543 const surface_descriptor_t &y,
544 const surface_descriptor_t &uv
545 );
546
557 std::optional<yuv444_t> import_target(
558 display_t::pointer egl_display,
559 std::array<file_t, yuv444_img_t::num_fds> &&fds,
560 const surface_descriptor_t &y,
561 const surface_descriptor_t &u,
562 const surface_descriptor_t &v
563 );
564
572 std::optional<nv12_t> create_nv12_target(int width, int height, AVPixelFormat format);
573
582 std::optional<yuv444_t> create_yuv444_target(int width, int height, AVPixelFormat format);
583
587 class cursor_t: public platf::img_t {
588 public:
589 int x;
590 int y;
591 int src_w;
592 int src_h;
593
594 unsigned long serial;
595
596 std::vector<std::uint8_t> buffer;
597 };
598
599 // Allow cursor and the underlying image to be kept together
604 public:
606 reset();
607 }
608
612 void reset() {
613 for (auto x = 0; x < 4; ++x) {
614 if (sd.fds[x] >= 0) {
615 close(sd.fds[x]);
616
617 sd.fds[x] = -1;
618 }
619 }
620 }
621
623
624 // Increment sequence when new rgb_t needs to be created
625 std::uint64_t sequence;
626
627 // Frame is vertically flipped (GL convention)
628 bool y_invert {false};
629
630 // PipeWire metadata
631 std::optional<uint64_t> pts;
632 std::optional<uint64_t> seq;
633 std::optional<bool> pw_damage;
634 std::optional<uint32_t> pw_flags;
635 };
636
640 class sws_t {
641 public:
652 static std::optional<sws_t> make_nv12(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex);
663 static std::optional<sws_t> make_yuv444(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex);
664
676 static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format, bool is_yuv444);
677
678 // Convert the loaded image into the first two framebuffers
686
687 // Convert the loaded image into the first three framebuffers
695
696 // Draw loaded image by programs to frame buffers
706 int draw_programs_to_buffers(GLenum attachments[], gl::frame_buf_t &fb, int count, bool is_yuv444);
707
708 // Make an area of the image black
720 int blank(gl::frame_buf_t &fb, int offsetX_, int offsetY_, int width, int height, bool is_yuv444);
721
727 void load_ram(platf::img_t &img);
737 void load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture, bool is_yuv444);
738
745 void apply_colorspace(const video::sunshine_colorspace_t &colorspace, bool is_yuv444);
746
747 // The first texture is the monitor image.
748 // The second texture is the cursor image
750
751 // The cursor image will be blended into this framebuffer
754
755 // Y - shader, UV - shader, Cursor - shader : for nv12
756 // Y - shader, U - shader, V - shader, Cursor - shader : for yuv444
757 std::array<gl::program_t, 4> program;
759
766
767 // Pointer to the texture to be converted to nv12
769
770 // Store latest cursor for load_vram
771 std::uint64_t serial;
772 };
773
779 bool fail();
780} // namespace egl
util::safe_ptr< CodedBitstreamContext, close > ctx_t
Owning coded bitstream context pointer that calls ff_cbs_close.
Definition cbs.cpp:34
Cursor image and hotspot metadata captured from the window system.
Definition graphics.h:587
std::vector< std::uint8_t > buffer
Cursor image pixels.
Definition graphics.h:596
unsigned long serial
X11 cursor serial used to detect cursor image changes.
Definition graphics.h:594
int y
Cursor hotspot or surface Y coordinate.
Definition graphics.h:590
int x
Cursor hotspot or surface X coordinate.
Definition graphics.h:589
int src_w
Cursor source image width in pixels.
Definition graphics.h:591
int src_h
Cursor source image height in pixels.
Definition graphics.h:592
Captured image descriptor shared by EGL conversion paths.
Definition graphics.h:603
std::optional< bool > pw_damage
Whether PipeWire damage tracking should be used.
Definition graphics.h:633
std::optional< uint32_t > pw_flags
PipeWire frame flags reported with the buffer.
Definition graphics.h:634
std::optional< uint64_t > seq
PipeWire frame sequence number.
Definition graphics.h:632
std::uint64_t sequence
Monotonic value used to detect when GL resources must be recreated.
Definition graphics.h:625
surface_descriptor_t sd
DMA-BUF surface descriptor for the captured image.
Definition graphics.h:622
void reset()
Reset the object to its initial empty state.
Definition graphics.h:612
bool y_invert
Whether the shader should invert the Y axis.
Definition graphics.h:628
std::optional< uint64_t > pts
PipeWire presentation timestamp.
Definition graphics.h:631
EGL/OpenGL scaler and colorspace conversion pipeline.
Definition graphics.h:640
gl::tex_t tex
Source and cursor textures used by the conversion pipeline.
Definition graphics.h:749
int offsetY
Offset y.
Definition graphics.h:765
int convert_yuv444(gl::frame_buf_t &fb)
Convert the loaded source image into YUV444 output planes.
Definition graphics.cpp:1374
int in_width
In width.
Definition graphics.h:762
int in_height
In height.
Definition graphics.h:763
gl::frame_buf_t cursor_framebuffer
Cursor framebuffer.
Definition graphics.h:752
static std::optional< sws_t > make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format, bool is_yuv444)
Allocate the underlying object and wrap it in the owning handle.
Definition graphics.cpp:1224
int out_width
Out width.
Definition graphics.h:760
int convert_nv12(gl::frame_buf_t &fb)
Convert the loaded source image into NV12 output planes.
Definition graphics.cpp:1352
static std::optional< sws_t > make_nv12(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex)
Create a software-scaling pipeline that renders to NV12 planes.
Definition graphics.cpp:997
void apply_colorspace(const video::sunshine_colorspace_t &colorspace, bool is_yuv444)
Apply the configured colorspace metadata to the active frame.
Definition graphics.cpp:935
int blank(gl::frame_buf_t &fb, int offsetX_, int offsetY_, int width, int height, bool is_yuv444)
Clear the render target to a blank frame.
Definition graphics.cpp:1208
int offsetX
Offset x.
Definition graphics.h:764
gl::buffer_t color_matrix
Color matrix.
Definition graphics.h:758
gl::frame_buf_t copy_framebuffer
Copy framebuffer.
Definition graphics.h:753
void load_ram(platf::img_t &img)
Load ram data from the backing API or store.
Definition graphics.cpp:1261
static std::optional< sws_t > make_yuv444(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex)
Create a software-scaling pipeline that renders to YUV444 planes.
Definition graphics.cpp:1103
void load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture, bool is_yuv444)
Load vram data from the backing API or store.
Definition graphics.cpp:1268
int loaded_texture
Loaded texture.
Definition graphics.h:768
int out_height
Out height.
Definition graphics.h:761
std::uint64_t serial
Serial.
Definition graphics.h:771
int draw_programs_to_buffers(GLenum attachments[], gl::frame_buf_t &fb, int count, bool is_yuv444)
Render the loaded source texture into output framebuffers.
Definition graphics.cpp:1330
std::array< gl::program_t, 4 > program
Program.
Definition graphics.h:757
EGL image buffer with plane descriptors and imported GL textures.
Definition graphics.h:241
void update(const std::string_view &view, std::size_t offset=0)
Update one uniform member in the block buffer.
Definition graphics.cpp:180
static buffer_t make(util::buffer_t< GLint > &&offsets, const char *block, const std::string_view &data)
Allocate the underlying object and wrap it in the owning handle.
Definition graphics.cpp:159
const char * block() const
Query a uniform block index from an OpenGL program.
Definition graphics.cpp:176
GLuint handle() const
Return the native handle owned by the wrapper.
Definition graphics.cpp:172
OpenGL framebuffer handle wrapper.
Definition graphics.h:123
void bind(std::nullptr_t, std::nullptr_t)
Bind each framebuffer and clear its color attachment.
Definition graphics.h:151
void bind(It it_begin, It it_end)
Bind textures to this object's framebuffers as color attachments.
Definition graphics.h:169
static frame_buf_t make(std::size_t count)
Allocate the underlying object and wrap it in the owning handle.
Definition graphics.cpp:106
frame_buf_t(frame_buf_t &&)=default
Move ownership of OpenGL framebuffer object names.
frame_buf_t & operator=(frame_buf_t &&)=default
Assign state from another instance while preserving ownership semantics.
void copy(int id, int texture, int offset_x, int offset_y, int width, int height)
Definition graphics.cpp:114
OpenGL shader program with attached shader stages.
Definition graphics.h:302
std::optional< buffer_t > uniform(const char *block, std::pair< const char *, std::string_view > *members, std::size_t count)
Query a uniform location from an OpenGL program.
Definition graphics.cpp:245
GLuint handle() const
Return the native handle owned by the wrapper.
Definition graphics.cpp:293
std::string err_str()
Read the program linker log.
Definition graphics.cpp:197
static util::Either< program_t, std::string > link(const shader_t &vert, const shader_t &frag)
Link an OpenGL program from compiled shaders.
Definition graphics.cpp:211
void bind(const buffer_t &buffer)
Bind this program and attach a uniform buffer block.
Definition graphics.cpp:238
OpenGL shader object that compiles GLSL source.
Definition graphics.h:203
static util::Either< shader_t, std::string > compile(const std::string_view &source, GLenum type)
Compile an OpenGL shader and report compiler errors.
Definition graphics.cpp:135
std::string err_str()
Read the shader compiler log.
Definition graphics.cpp:121
GLuint handle() const
Return the native handle owned by the wrapper.
Definition graphics.cpp:155
OpenGL texture handle wrapper.
Definition graphics.h:94
tex_t(tex_t &&)=default
Move ownership of OpenGL texture object names.
static tex_t make(std::size_t count)
Allocate the underlying object and wrap it in the owning handle.
Definition graphics.cpp:81
tex_t & operator=(tex_t &&)=default
Assign state from another instance while preserving ownership semantics.
Tagged storage for one of two possible value types.
Definition utility.h:756
Owning contiguous buffer with an explicit logical element count.
Definition utility.h:1394
size_t size() const
Definition utility.h:1481
Unique pointer wrapper with customizable pointer and deleter types.
Definition utility.h:820
element_type * pointer
Pointer type stored by the unique pointer wrapper.
Definition utility.h:829
Declarations for common platform specific utilities.
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC egl_image_target_texture_2d()
Bind an EGL image to the current OpenGL texture target.
Definition graphics.cpp:61
GladGLContext ctx
Loaded OpenGL function table for the active context.
Definition graphics.cpp:54
std::optional< rgb_t > import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb)
Import the source frame texture for EGL/OpenGL conversion.
Definition graphics.cpp:640
std::optional< ctx_t > make_ctx(display_t::pointer display)
Create an EGL/OpenGL context for capture or conversion.
Definition graphics.cpp:422
bool fail()
Log EGL failure details and return an error code.
Definition graphics.cpp:340
std::optional< nv12_t > create_nv12_target(int width, int height, AVPixelFormat format)
Create biplanar YUV textures to render into.
Definition graphics.cpp:849
rgb_t create_blank(platf::img_t &img)
Create a black RGB texture of the specified image size.
Definition graphics.cpp:674
device_destroy_fn device_destroy
Device destroy.
Definition graphics.cpp:300
display_t make_display(std::variant< gbm::gbm_t::pointer, wl_display *, _XDisplay * > native_display)
Open and initialize the display connection used for capture.
Definition graphics.cpp:344
std::optional< nv12_t > import_target(display_t::pointer egl_display, std::array< file_t, nv12_img_t::num_fds > &&fds, const surface_descriptor_t &y, const surface_descriptor_t &uv)
Import the output frame target for EGL/OpenGL conversion.
Definition graphics.cpp:749
void drain_errors(const std::string_view &prefix)
Drain and log pending OpenGL errors.
Definition graphics.cpp:68
create_device_fn create_device
Create device.
Definition graphics.cpp:301
std::optional< yuv444_t > create_yuv444_target(int width, int height, AVPixelFormat format)
Create YUV444 target.
Definition graphics.cpp:890
void free_frame(AVFrame *frame)
Release an FFmpeg frame allocated by the capture or conversion backend.
Definition graphics.cpp:1403
int close(int __fd)
Release the native resource held by the RAII wrapper.
void(* device_destroy_fn)(device *gbm)
Function pointer used to destroy a GBM device.
Definition graphics.h:360
void(*)(GLenum target, void *image) PFNGLEGLIMAGETARGETTEXTURE2DOESPROC
Function pointer type for glEGLImageTargetTexture2DOES.
Definition graphics.h:76
device *(* create_device_fn)(int fd)
Function pointer used to create a GBM device from a file descriptor.
Definition graphics.h:364
Miscellaneous declarations for Linux.
bl::sources::severity_logger< int > warning
Strange events.
Definition logging.cpp:43
Declarations for logging related functions.
NV12 capture image backed by EGL and OpenGL resources.
Definition graphics.h:402
EGLImage bg88
EGL image for the NV12 interleaved chroma plane.
Definition graphics.h:405
static constexpr std::size_t num_fds
Maximum number of DMA-BUF plane file descriptors exported by VAAPI.
Definition graphics.h:411
gl::tex_t tex
Textures containing the imported Y and UV planes.
Definition graphics.h:407
display_t::pointer display
EGL display that owns the imported planes.
Definition graphics.h:403
gl::frame_buf_t buf
OpenGL framebuffer object used for rendering.
Definition graphics.h:408
std::array< file_t, num_fds > fds
DMA-BUF file descriptors for each exported plane.
Definition graphics.h:413
EGLImage r8
EGL image for the NV12 luma plane.
Definition graphics.h:404
RGB capture image backed by EGL and OpenGL resources.
Definition graphics.h:392
display_t::pointer display
EGL display that owns the imported image.
Definition graphics.h:393
EGLImage xrgb8
EGL image for the imported XRGB plane.
Definition graphics.h:394
gl::tex_t tex
Texture containing the imported RGB plane.
Definition graphics.h:396
EGL surface descriptor used to import a captured DMA-BUF.
Definition graphics.h:496
std::uint32_t fourcc
DRM fourcc pixel format for the buffer.
Definition graphics.h:500
int fds[4]
DMA-BUF file descriptors for up to four planes.
Definition graphics.h:499
std::uint32_t pitches[4]
Row stride in bytes for each DMA-BUF plane.
Definition graphics.h:502
std::uint64_t modifier
DRM format modifier describing the buffer layout.
Definition graphics.h:501
int height
Frame or display height in pixels.
Definition graphics.h:498
int width
Frame or display width in pixels.
Definition graphics.h:497
std::uint32_t offsets[4]
Byte offset to the first pixel for each DMA-BUF plane.
Definition graphics.h:503
YUV 4:4:4 capture image backed by EGL and OpenGL resources.
Definition graphics.h:419
EGLImage b8
EGL image for the V plane.
Definition graphics.h:423
display_t::pointer display
EGL display that owns the imported planes.
Definition graphics.h:420
EGLImage r8
EGL image for the Y plane.
Definition graphics.h:421
std::array< file_t, num_fds > fds
DMA-BUF file descriptors for each exported plane.
Definition graphics.h:430
EGLImage g8
EGL image for the U plane.
Definition graphics.h:422
gl::frame_buf_t buf
OpenGL framebuffer object used for rendering.
Definition graphics.h:426
static constexpr std::size_t num_fds
Num fds.
Definition graphics.h:428
gl::tex_t tex
Textures containing the imported Y, U, and V planes.
Definition graphics.h:425
Captured frame buffer shared between capture and encode stages.
Definition common.h:502
Sunshine colorimetry values derived from stream configuration.
Definition video_colorspace.h:26
Declarations for utility functions.
#define TUPLE_2D_REF(a, b, expr)
Bind both elements of an existing 2-tuple expression to local references.
Definition utility.h:162
#define KITTY_USING_MOVE_T(move_t, t, init_val, z)
Define a move-only RAII wrapper with caller-provided initial value and destructor body.
Definition utility.h:48
Declarations for colorspace functions.