Sunshine v2025.118.151840
Self-hosted game stream host for Moonlight.
graphics.h
Go to the documentation of this file.
1
5#pragma once
6
7#include <optional>
8#include <string_view>
9
10#include <glad/egl.h>
11#include <glad/gl.h>
12
13#include "misc.h"
14#include "src/logging.h"
15#include "src/platform/common.h"
16#include "src/utility.h"
18
19#define SUNSHINE_STRINGIFY_HELPER(x) #x
20#define SUNSHINE_STRINGIFY(x) SUNSHINE_STRINGIFY_HELPER(x)
21#define gl_drain_errors_helper(x) gl::drain_errors(x)
22#define gl_drain_errors gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__))
23
24extern "C" int
25close(int __fd);
26
27// X11 Display
28extern "C" struct _XDisplay;
29
30struct AVFrame;
31void
32free_frame(AVFrame *frame);
33
35
36namespace gl {
37 extern GladGLContext ctx;
38 void
39 drain_errors(const std::string_view &prefix);
40
41 class tex_t: public util::buffer_t<GLuint> {
42 using util::buffer_t<GLuint>::buffer_t;
43
44 public:
45 tex_t(tex_t &&) = default;
46 tex_t &
47 operator=(tex_t &&) = default;
48
49 ~tex_t();
50
51 static tex_t
52 make(std::size_t count);
53 };
54
55 class frame_buf_t: public util::buffer_t<GLuint> {
56 using util::buffer_t<GLuint>::buffer_t;
57
58 public:
59 frame_buf_t(frame_buf_t &&) = default;
61 operator=(frame_buf_t &&) = default;
62
64
65 static frame_buf_t
66 make(std::size_t count);
67
68 inline void
69 bind(std::nullptr_t, std::nullptr_t) {
70 int x = 0;
71 for (auto fb : (*this)) {
72 ctx.BindFramebuffer(GL_FRAMEBUFFER, fb);
73 ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0);
74
75 ++x;
76 }
77 return;
78 }
79
80 template <class It>
81 void
82 bind(It it_begin, It it_end) {
83 using namespace std::literals;
84 if (std::distance(it_begin, it_end) > size()) {
85 BOOST_LOG(warning) << "To many elements to bind"sv;
86 return;
87 }
88
89 int x = 0;
90 std::for_each(it_begin, it_end, [&](auto tex) {
91 ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[x]);
92 ctx.BindTexture(GL_TEXTURE_2D, tex);
93
94 ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, tex, 0);
95
96 ++x;
97 });
98 }
99
103 void
104 copy(int id, int texture, int offset_x, int offset_y, int width, int height);
105 };
106
107 class shader_t {
108 KITTY_USING_MOVE_T(shader_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
109 if (el != std::numeric_limits<GLuint>::max()) {
110 ctx.DeleteShader(el);
111 }
112 });
113
114 public:
115 std::string
116 err_str();
117
119 compile(const std::string_view &source, GLenum type);
120
121 GLuint
122 handle() const;
123
124 private:
125 shader_internal_t _shader;
126 };
127
128 class buffer_t {
129 KITTY_USING_MOVE_T(buffer_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
130 if (el != std::numeric_limits<GLuint>::max()) {
131 ctx.DeleteBuffers(1, &el);
132 }
133 });
134
135 public:
136 static buffer_t
137 make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data);
138
139 GLuint
140 handle() const;
141
142 const char *
143 block() const;
144
145 void
146 update(const std::string_view &view, std::size_t offset = 0);
147 void
148 update(std::string_view *members, std::size_t count, std::size_t offset = 0);
149
150 private:
151 const char *_block;
152
153 std::size_t _size;
154
155 util::buffer_t<GLint> _offsets;
156
157 buffer_internal_t _buffer;
158 };
159
160 class program_t {
161 KITTY_USING_MOVE_T(program_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
162 if (el != std::numeric_limits<GLuint>::max()) {
163 ctx.DeleteProgram(el);
164 }
165 });
166
167 public:
168 std::string
169 err_str();
170
172 link(const shader_t &vert, const shader_t &frag);
173
174 void
175 bind(const buffer_t &buffer);
176
177 std::optional<buffer_t>
178 uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count);
179
180 GLuint
181 handle() const;
182
183 private:
184 program_internal_t _program;
185 };
186} // namespace gl
187
188namespace gbm {
189 struct device;
190 typedef void (*device_destroy_fn)(device *gbm);
191 typedef device *(*create_device_fn)(int fd);
192
193 extern device_destroy_fn device_destroy;
194 extern create_device_fn create_device;
195
197
198 int
199 init();
200
201} // namespace gbm
202
203namespace egl {
205
206 struct rgb_img_t {
207 display_t::pointer display;
208 EGLImage xrgb8;
209
210 gl::tex_t tex;
211 };
212
213 struct nv12_img_t {
214 display_t::pointer display;
215 EGLImage r8;
216 EGLImage bg88;
217
218 gl::tex_t tex;
219 gl::frame_buf_t buf;
220
221 // sizeof(va::DRMPRIMESurfaceDescriptor::objects) / sizeof(va::DRMPRIMESurfaceDescriptor::objects[0]);
222 static constexpr std::size_t num_fds = 4;
223
224 std::array<file_t, num_fds> fds;
225 };
226
227 KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , {
228 if (el.xrgb8) {
229 eglDestroyImage(el.display, el.xrgb8);
230 }
231 });
232
233 KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , {
234 if (el.r8) {
235 eglDestroyImage(el.display, el.r8);
236 }
237
238 if (el.bg88) {
239 eglDestroyImage(el.display, el.bg88);
240 }
241 });
242
243 KITTY_USING_MOVE_T(ctx_t, (std::tuple<display_t::pointer, EGLContext>), , {
244 TUPLE_2D_REF(disp, ctx, el);
245 if (ctx) {
246 eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
247 eglDestroyContext(disp, ctx);
248 }
249 });
250
252 int width;
253 int height;
254 int fds[4];
255 std::uint32_t fourcc;
256 std::uint64_t modifier;
257 std::uint32_t pitches[4];
258 std::uint32_t offsets[4];
259 };
260
262 make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display);
263 std::optional<ctx_t>
264 make_ctx(display_t::pointer display);
265
266 std::optional<rgb_t>
267 import_source(
268 display_t::pointer egl_display,
269 const surface_descriptor_t &xrgb);
270
271 rgb_t
273
274 std::optional<nv12_t>
275 import_target(
276 display_t::pointer egl_display,
277 std::array<file_t, nv12_img_t::num_fds> &&fds,
278 const surface_descriptor_t &y, const surface_descriptor_t &uv);
279
287 std::optional<nv12_t>
288 create_target(int width, int height, AVPixelFormat format);
289
290 class cursor_t: public platf::img_t {
291 public:
292 int x, y;
293 int src_w, src_h;
294
295 unsigned long serial;
296
297 std::vector<std::uint8_t> buffer;
298 };
299
300 // Allow cursor and the underlying image to be kept together
302 public:
304 reset();
305 }
306
307 void
308 reset() {
309 for (auto x = 0; x < 4; ++x) {
310 if (sd.fds[x] >= 0) {
311 close(sd.fds[x]);
312
313 sd.fds[x] = -1;
314 }
315 }
316 }
317
319
320 // Increment sequence when new rgb_t needs to be created
321 std::uint64_t sequence;
322 };
323
324 class sws_t {
325 public:
326 static std::optional<sws_t>
327 make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex);
328 static std::optional<sws_t>
329 make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format);
330
331 // Convert the loaded image into the first two framebuffers
332 int
333 convert(gl::frame_buf_t &fb);
334
335 // Make an area of the image black
336 int
337 blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height);
338
339 void
340 load_ram(platf::img_t &img);
341 void
342 load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture);
343
344 void
345 apply_colorspace(const video::sunshine_colorspace_t &colorspace);
346
347 // The first texture is the monitor image.
348 // The second texture is the cursor image
349 gl::tex_t tex;
350
351 // The cursor image will be blended into this framebuffer
352 gl::frame_buf_t cursor_framebuffer;
353 gl::frame_buf_t copy_framebuffer;
354
355 // Y - shader, UV - shader, Cursor - shader
356 gl::program_t program[3];
357 gl::buffer_t color_matrix;
358
359 int out_width, out_height;
360 int in_width, in_height;
361 int offsetX, offsetY;
362
363 // Pointer to the texture to be converted to nv12
364 int loaded_texture;
365
366 // Store latest cursor for load_vram
367 std::uint64_t serial;
368 };
369
370 bool
371 fail();
372} // namespace egl
Definition graphics.h:290
Definition graphics.h:301
Definition graphics.h:324
Definition graphics.h:128
Definition graphics.h:55
void copy(int id, int texture, int offset_x, int offset_y, int width, int height)
Definition graphics.cpp:83
Definition graphics.h:160
Definition graphics.h:107
Definition graphics.h:41
Definition utility.h:460
Definition utility.h:814
Definition utility.h:496
Declarations for common platform specific utilities.
rgb_t create_blank(platf::img_t &img)
Create a black RGB texture of the specified image size.
Definition graphics.cpp:584
std::optional< nv12_t > create_target(int width, int height, AVPixelFormat format)
Create biplanar YUV textures to render into.
Definition graphics.cpp:665
Miscellaneous declarations for Linux.
Declarations for logging related functions.
Definition graphics.h:213
Definition graphics.h:206
Definition graphics.h:251
Definition common.h:341
Definition video_colorspace.h:20
Declarations for utility functions.
Declarations for colorspace functions.