UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
gl.cpp
Go to the documentation of this file.
1
8/*
9 * Copyright (c) 2010-2024 CESNET, z. s. p. o.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, is permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of CESNET nor the names of its contributors may be
24 * used to endorse or promote products derived from this software without
25 * specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
29 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
30 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
31 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
37 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
38 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 */
40
41#ifdef __APPLE__
42#include <OpenGL/gl.h>
43#include <OpenGL/OpenGL.h> // CGL
44#include <OpenGL/glext.h>
45#else
46#include <GL/glew.h>
47#endif /* __APPLE__ */
48#include <GLFW/glfw3.h>
49
50#include "spout_sender.h"
51#include "syphon_server.h"
52
53#include <algorithm>
54#include <cassert>
55#include <climits>
56#include <cmath>
57#include <condition_variable>
58#include <cstdint>
59#include <fstream>
60#include <iostream>
61#include <mutex>
62#include <queue>
63#include <string>
64#include <string_view>
65#include <unordered_map>
66
67#include "color.h"
68#ifdef HAVE_CONFIG_H
69#include "config.h"
70#endif
71#include "debug.h"
72#include "gl_context.h"
73#include "host.h"
74#include "keyboard_control.h"
75#include "lib_common.h"
76#include "messaging.h"
77#include "module.h"
78#include "types.h"
79#include "utils/color_out.h"
80#include "utils/macros.h" // OPTIMIZED_FOR
81#include "utils/ref_count.hpp"
82#include "video.h"
83#include "video_display.h"
84
85#define MAGIC_GL 0x1331018e
86#define MOD_NAME "[GL] "
87#define DEFAULT_WIN_NAME "Ultragrid - OpenGL Display"
88#define GL_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x38e52705
89#define GL_DISABLE_10B_OPT_PARAM_NAME "gl-disable-10b"
90#define GL_WINDOW_HINT_OPT_PARAM_NAME "glfw-window-hint"
91#define MAX_BUFFER_SIZE 1
92#define ADAPTIVE_VSYNC -1
93#define SYSTEM_VSYNC 0xFE
94#define SINGLE_BUF 0xFF // use single buffering instead of double
95
96#include "gl_vdpau.hpp"
97
98using std::begin;
99using std::condition_variable;
100using std::copy;
101using std::copy_if;
102using std::cout;
103using std::end;
104using std::find;
105using std::lock_guard;
106using std::map;
107using std::min;
108using std::mutex;
109using std::ostringstream;
110using std::pair;
111using std::queue;
112using std::stof;
113using std::stoi;
114using std::stol;
115using std::string;
116using std::string_view;
117using std::swap;
118using std::unique_lock;
119using std::unordered_map;
120using std::vector;
121using std::chrono::duration;
122using std::chrono::duration_cast;
123using std::chrono::seconds;
124using std::chrono::steady_clock;
125using namespace std::chrono_literals;
126
127static const char * deinterlace_fp = R"raw(
128#version 110
129uniform sampler2D image;
130uniform float lineOff;
131void main()
132{
133 vec4 pix;
134 vec4 pix_down;
135 pix = texture2D(image, gl_TexCoord[0].xy);
136 pix_down = texture2D(image, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + lineOff));
137 gl_FragColor = (pix + pix_down) / 2.0;
138}
139)raw";
140
141static const char * uyvy_to_rgb_fp = R"raw(
142#version 110
143uniform sampler2D image;
144uniform float imageWidth;
145void main()
146{
147 float imageWidthRaw;
148 imageWidthRaw = float((int(imageWidth) + 1) / 2 * 2);
149 vec4 yuv;
150 yuv.rgba = texture2D(image, vec2(gl_TexCoord[0].x / imageWidthRaw * imageWidth, gl_TexCoord[0].y)).grba;
151 if(gl_TexCoord[0].x * imageWidth / 2.0 - floor(gl_TexCoord[0].x * imageWidth / 2.0) > 0.5)
152 yuv.r = yuv.a;
153 yuv.r = Y_SCALED_PLACEHOLDER * (yuv.r - 0.0625);
154 yuv.g = yuv.g - 0.5;
155 yuv.b = yuv.b - 0.5;
156 gl_FragColor.r = yuv.r + R_CR_PLACEHOLDER * yuv.b;
157 gl_FragColor.g = yuv.r + G_CB_PLACEHOLDER * yuv.g + G_CR_PLACEHOLDER * yuv.b;
158 gl_FragColor.b = yuv.r + B_CB_PLACEHOLDER * yuv.g;
159 gl_FragColor.a = 1.0;
160}
161)raw";
162
163static const char * yuva_to_rgb_fp = R"raw(
164#version 110
165uniform sampler2D image;
166void main()
167{
168 vec4 yuv;
169 yuv.rgba = texture2D(image, gl_TexCoord[0].xy).grba;
170 yuv.r = Y_SCALED_PLACEHOLDER * (yuv.r - 0.0625);
171 yuv.g = yuv.g - 0.5;
172 yuv.b = yuv.b - 0.5;
173 gl_FragColor.r = yuv.r + R_CR_PLACEHOLDER * yuv.b;
174 gl_FragColor.g = yuv.r + G_CB_PLACEHOLDER * yuv.g + G_CR_PLACEHOLDER * yuv.b;
175 gl_FragColor.b = yuv.r + B_CB_PLACEHOLDER * yuv.g;
176 gl_FragColor.a = yuv.a;
177}
178)raw";
179
182static const char * v210_to_rgb_fp = R"raw(
183#version 110
184#extension GL_EXT_gpu_shader4 : enable
185uniform sampler2D image;
186uniform float imageWidth;
187
188// YUV offset
189const vec3 yuvOffset = vec3(-0.0625, -0.5, -0.5);
190
191// RGB coefficients
192const vec3 Rcoeff = vec3(Y_SCALED_PLACEHOLDER, 0.0, R_CR_PLACEHOLDER);
193const vec3 Gcoeff = vec3(Y_SCALED_PLACEHOLDER, G_CB_PLACEHOLDER, G_CR_PLACEHOLDER);
194const vec3 Bcoeff = vec3(Y_SCALED_PLACEHOLDER, B_CB_PLACEHOLDER, 0.0);
195
196// U Y V A | Y U Y A | V Y U A | Y V Y A
197
198int GROUP_FOR_INDEX(int i) {
199 return i / 4;
200}
201
202int SUBINDEX_FOR_INDEX(int i) {
203 return i % 4;
204}
205
206int _y(int i) {
207 return 2 * i + 1;
208}
209
210int _u(int i) {
211 return 4 * (i/2);
212}
213
214int _v(int i) {
215 return 4 * (i / 2) + 2;
216}
217
218int offset(int i) {
219 return i + (i / 3);
220}
221
222vec3 ycbcr2rgb(vec3 yuvToConvert) {
223 vec3 pix;
224 yuvToConvert += yuvOffset;
225 pix.r = dot(yuvToConvert, Rcoeff);
226 pix.g = dot(yuvToConvert, Gcoeff);
227 pix.b = dot(yuvToConvert, Bcoeff);
228 return pix;
229}
230
231void main(void) {
232 float imageWidthRaw; // v210 texture size
233 imageWidthRaw = float((int(imageWidth) + 47) / 48 * 32); // 720->480
234
235 // interpolate (0,1) texcoords to [0,719]
236 int texcoordDenormX;
237 texcoordDenormX = int(round(gl_TexCoord[0].x * imageWidth - .5));
238
239 // 0 1 1 2 3 3 4 5 5 6 7 7 etc.
240 int yOffset;
241 yOffset = offset(_y(texcoordDenormX));
242 int sourceColumnIndexY;
243 sourceColumnIndexY = GROUP_FOR_INDEX(yOffset);
244
245 // 0 0 1 1 2 2 4 4 5 5 6 6 etc.
246 int uOffset;
247 uOffset = offset(_u(texcoordDenormX));
248 int sourceColumnIndexU;
249 sourceColumnIndexU = GROUP_FOR_INDEX(uOffset);
250
251 // 0 0 2 2 3 3 4 4 6 6 7 7 etc.
252 int vOffset;
253 vOffset = offset(_v(texcoordDenormX));
254 int sourceColumnIndexV;
255 sourceColumnIndexV = GROUP_FOR_INDEX(vOffset);
256
257 // 1 0 2 1 0 2 1 0 2 etc.
258 int compY;
259 compY = SUBINDEX_FOR_INDEX(yOffset);
260
261 // 0 0 1 1 2 2 0 0 1 1 2 2 etc.
262 int compU;
263 compU = SUBINDEX_FOR_INDEX(uOffset);
264
265 // 2 2 0 0 1 1 2 2 0 0 1 1 etc.
266 int compV;
267 compV = SUBINDEX_FOR_INDEX(vOffset);
268
269 vec4 y;
270 vec4 u;
271 vec4 v;
272 y = texture2D(image, vec2((float(sourceColumnIndexY) + .5) / imageWidthRaw, gl_TexCoord[0].y));
273 u = texture2D(image, vec2((float(sourceColumnIndexU) + .5) / imageWidthRaw, gl_TexCoord[0].y));
274 v = texture2D(image, vec2((float(sourceColumnIndexV) + .5) / imageWidthRaw, gl_TexCoord[0].y));
275
276 vec3 outColor = ycbcr2rgb(vec3(y[compY], u[compU], v[compV]));
277
278 gl_FragColor = vec4(outColor, 1.0);
279}
280)raw";
281
282/* DXT YUV (FastDXT) related */
283static const char *fp_display_dxt1_yuv = R"raw(
284#version 110
285uniform sampler2D image;
286
287void main(void) {
288 vec4 col = texture2D(image, gl_TexCoord[0].st);
289
290 float Y = 1.1643 * (col[0] - 0.0625);
291 float U = (col[1] - 0.5);
292 float V = (col[2] - 0.5);
293
294 float R = Y + 1.7926 * V;
295 float G = Y - 0.2132 * U - 0.5328 * V;
296 float B = Y + 2.1124 * U;
297
298 gl_FragColor=vec4(R,G,B,1.0);
299}
300)raw";
301
302static const char * vert = R"raw(
303#version 110
304void main() {
305 gl_TexCoord[0] = gl_MultiTexCoord0;
306 gl_Position = ftransform();
307}
308)raw";
309
310static const char fp_display_dxt5ycocg[] = R"raw(
311#version 110
312uniform sampler2D image;
313void main()
314{
315 vec4 color;
316 float Co;
317 float Cg;
318 float Y;
319 float scale;
320 color = texture2D(image, gl_TexCoord[0].xy);
321 scale = (color.z * ( 255.0 / 8.0 )) + 1.0;
322 Co = (color.x - (0.5 * 256.0 / 255.0)) / scale;
323 Cg = (color.y - (0.5 * 256.0 / 255.0)) / scale;
324 Y = color.w;
325 gl_FragColor = vec4(Y + Co - Cg, Y + Cg, Y - Co - Cg, 1.0);
326} // main end
327)raw";
328
329unordered_map<codec_t, const char *> glsl_programs = {
330 { UYVY, uyvy_to_rgb_fp },
331 { Y416, yuva_to_rgb_fp },
332 { v210, v210_to_rgb_fp },
335};
336
337static constexpr pair<int64_t, string_view> keybindings[] = {
338 pair<int64_t, string_view>{'f', "toggle fullscreen"},
339 pair<int64_t, string_view>{'q', "quit"},
340 pair<int64_t, string_view>{K_ALT('d'), "toggle deinterlace"},
341 pair<int64_t, string_view>{K_ALT('p'), "pause video"},
342 pair<int64_t, string_view>{K_ALT('s'), "screenshot"},
343 pair<int64_t, string_view>{K_ALT('c'), "show/hide cursor"},
344 pair<int64_t, string_view>{K_CTRL_DOWN, "make window 10% smaller"},
345 pair<int64_t, string_view>{K_CTRL_UP, "make window 10% bigger"}
346};
347
348/* Prototyping */
349static bool display_gl_init_opengl(struct state_gl *s);
350static bool display_gl_putf(void *state, struct video_frame *frame, long long timeout);
351static bool display_gl_process_key(struct state_gl *s, long long int key);
352static bool display_gl_reconfigure(void *state, struct video_desc desc);
353static void gl_draw(double ratio, double bottom_offset, bool double_buf);
354static void gl_change_aspect(struct state_gl *s, int width, int height);
355static void gl_resize(GLFWwindow *win, int width, int height);
356static void gl_render_glsl(struct state_gl *s, char *data);
357static void gl_reconfigure_screen(struct state_gl *s, struct video_desc desc);
358static void gl_process_frames(struct state_gl *s);
359static void glfw_key_callback(GLFWwindow* win, int key, int scancode, int action, int mods);
360static void glfw_mouse_callback(GLFWwindow *win, double x, double y);
361static void glfw_close_callback(GLFWwindow *win);
362static void glfw_print_error(int error_code, const char* description);
363static void glfw_print_video_mode(struct state_gl *s);
364static void display_gl_set_sync_on_vblank(int value);
365static void screenshot(struct video_frame *frame);
366static void upload_compressed_texture(struct state_gl *s, char *data);
367static void upload_texture(struct state_gl *s, char *data);
368static bool check_rpi_pbo_quirks();
369static void set_gamma(struct state_gl *s);
370
371struct state_gl {
372 unordered_map<codec_t, GLuint> PHandles;
373 GLuint PHandle_deint = 0;
374 GLuint current_program = 0;
375
376 // Framebuffer
377 GLuint fbo_id = 0;
378 GLuint texture_display = 0;
379 GLuint texture_raw = 0;
380 GLuint pbo_id = 0;
381
382 /* For debugging... */
383 uint32_t magic = MAGIC_GL;
384
385 GLFWmonitor *monitor = nullptr;
386 GLFWwindow *window = nullptr;
387
388 bool fs = false;
390
391 struct video_frame *current_frame = nullptr;
392
393 queue<struct video_frame *> frame_queue;
394 queue<struct video_frame *> free_frame_queue;
397 mutex lock;
398 condition_variable new_frame_ready_cv;
399 condition_variable frame_consumed_cv;
400
401 double aspect = 0.0;
402 double video_aspect = 0.0;
403 double gamma = 0.0;
404
405 int dxt_height = 0;
406
407 int vsync = 1;
408 bool noresizable = false;
409 volatile bool paused = false;
411 steady_clock::time_point cursor_shown_from{};
413
414 double window_size_factor = 1.0;
415
416 struct module mod;
417
418 void *syphon_spout = nullptr;
419
420 bool fixed_size = false;
421 int fixed_w = 0;
422 int fixed_h = 0;
423 int pos_x = INT_MIN;
424 int pos_y = INT_MIN;
425
426 enum modeset_t { MODESET = -2, MODESET_SIZE_ONLY = GLFW_DONT_CARE, NOMODESET = 0 } modeset = NOMODESET;
427 map<int, int> init_hints;
428 map<int, int> window_hints = {
429 {GLFW_AUTO_ICONIFY, GLFW_FALSE}
430 };
431 int use_pbo = -1;
433#ifdef HWACC_VDPAU
434 struct state_vdpau vdp;
435#endif
436 bool vdp_interop = false;
437 vector<char> scratchpad;
438
444
447
449 }
450
451 static const char *deint_to_string(state_gl::deint val) {
452 switch (val) {
453 case state_gl::deint::off: return "OFF";
454 case state_gl::deint::on: return "ON";
455 case state_gl::deint::force: return "FORCE";
456 }
457 return NULL;
458 }
459};
460
461static constexpr codec_t gl_supp_codecs[] = {
462#ifdef HWACC_VDPAU
463 HW_VDPAU,
464#endif
465 UYVY,
466 v210,
467 R10k,
468 RGBA,
469 RGB,
470 RG48,
471 Y416,
472 DXT1,
473 DXT1_YUV,
474 DXT5
475};
476
477static void gl_print_monitors(bool fullhelp) {
478 if (ref_count_init_once<int>()(glfwInit, glfw_init_count).value_or(GLFW_TRUE) == GLFW_FALSE) {
479 LOG(LOG_LEVEL_ERROR) << "Cannot initialize GLFW!\n";
480 return;
481 }
482 printf("\nmonitors:\n");
483 int count = 0;
484 GLFWmonitor **mon = glfwGetMonitors(&count);
485 if (count <= 0) {
486 LOG(LOG_LEVEL_ERROR) << MOD_NAME "No monitors found!\n";
487 }
488 GLFWmonitor *primary = glfwGetPrimaryMonitor();
489 for (int i = 0; i < count; ++i) {
490 col() << "\t" << (mon[i] == primary ? "*" : " ") << TBOLD(<< i <<) << ") " << glfwGetMonitorName(mon[i]);
491#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3)
492 int xpos, ypos, width, height;
493 glfwGetMonitorWorkarea(mon[i], &xpos, &ypos, &width, &height);
494 cout << " - " << width << "x" << height << "+" << xpos << "+" << ypos << "\n";
495#else
496 cout << "\n";
497#endif
498 if (!fullhelp) {
499 continue;
500 }
501 int mod_count = 0;
502 const GLFWvidmode *modes = glfwGetVideoModes(mon[i], &mod_count);
503 for (int mod = 0; mod < mod_count; ++mod) {
504 cout << "\t\t- " << modes[mod].width << "x" << modes[mod].height << "@" << modes[mod].refreshRate << ", bits: " <<
505 modes[mod].redBits << ", " <<
506 modes[mod].greenBits << ", " <<
507 modes[mod].blueBits << "\n";
508 }
509 }
510 if (!fullhelp) {
511 cout << "(use \"fullhelp\" to see modes)\n";
512 }
513
515}
516
517#define FEATURE_PRESENT(x) (strcmp(STRINGIFY(x), "1") == 0 ? "on" : "off")
518
522static void gl_show_help(bool full) {
523 col() << "usage:\n";
524 col() << SBOLD(SRED("\t-d gl[:<options>]")
525 << (full ? " [--param " GL_DISABLE_10B_OPT_PARAM_NAME "]"
526 : ""))
527 << "\n";
528 col() << SBOLD("\t-d gl:[full]help") "\n\n";
529 col() << "options:\n";
530 col() << TBOLD("\taspect=<w>/<h>") << "\trequested video aspect (eg. 16/9). Leave unset if PAR = 1.\n";
531 col() << TBOLD("\tcursor") << "\t\tshow visible cursor\n";
532 col() << TBOLD("\td[force]") << "\tdeinterlace (optionally forcing deinterlace of progressive video)\n";
533 col() << TBOLD("\tfs[=<monitor>]") << "\tfullscreen with optional display specification\n";
534 col() << TBOLD("\tgamma[=<val>]")
535 << "\tgamma value to be added _in addition_ to the hardware "
536 "gamma correction\n";
537 col() << TBOLD("\thide-window") << "\tdo not show OpenGL window (useful with Syphon/SPOUT)\n";
538 col() << TBOLD("\tmodeset[=<fps>]")
539 << "\tset received video mode as display mode (in fullscreen)\n"
540 "\t\t\t"
541 << SUNDERLINE("fps") " - override FPS; "
542 << SUNDERLINE("modeset=size") << " - set only size\n";
543 col() << TBOLD("\tnodecorate") << "\tdisable window decorations\n";
544 col() << TBOLD("\tnovsync") << "\t\tdo not turn sync on VBlank\n";
545 col() << TBOLD("\t[no]pbo") << "\t\tWhether or not use PBO (ignore if not sure)\n";
546 col() << TBOLD("\tsingle") << "\t\tuse single buffer (instead of double-buffering)\n";
547 col() << TBOLD("\tsize=<ratio>%")
548 << "\tspecifies desired size of window relative\n"
549 "\t\t\tto native resolution (in percents)\n";
550 col() << TBOLD("\tsize=<W>x<H>")
551 << "\twindow size in pixels, with optional position; full\n"
552 << "\t\t\tsyntax: " TBOLD("[<W>x<H>][{+-}<X>[{+-}<Y>]]")
553 << (full ? " [1]" : "") << "\n";
554 col() << TBOLD("\tfixed_size") << "\tdo not resize window on new stream\n";
555 col() << TBOLD("\tnoresizable") << "\twindow won't be resizable (useful with size=)\n";
556#ifdef SPOUT
557 col() << TBOLD("\tspout") << "\t\tuse Spout (optionally with name)\n";
558#endif
559#ifdef SYPHON
560 col() << TBOLD("\tsyphon") << "\t\tuse Syphon (optionally with name)\n";
561#endif
562 col() << TBOLD("\tvsync=<x>") << "\tsets vsync to: 0 - disable; 1 - enable; -1 - adaptive vsync; D - leaves system default\n";
563 if (full) {
564 col() << TBOLD("\tinit_hint=<k>=<v>[,<k2>=<v2>]")" set GLFW "
565 "init hint key\n"
566 "\t\t\t<k> to value <v> (" TBOLD("list_hints")" to enumerate)\n";
567 col() << TBOLD("\twindow_hint=<k>=<v>[,<k2>=<v2>]")" set GLFW "
568 "window hint key\n"
569 "\t\t\t<k> to value <v>, (" TBOLD("list_hints")" to enumerate)\n";
570 col() << TBOLD("\t--param " GL_DISABLE_10B_OPT_PARAM_NAME) << "\tdo not set 10-bit framebuffer (performance issues)\n";
571 col() << "\n" TBOLD(
572 "[1]") " position doesn't work in Wayland\n";
573 }
574
575 printf("\nkeyboard shortcuts:\n");
576 for (auto const &i : keybindings) {
577 char keyname[50];
578 get_keycode_name(i.first, keyname, sizeof keyname);
579 col() << "\t" << TBOLD(<< keyname <<) << "\t\t" << i.second << "\n";
580 }
581
582 gl_print_monitors(full);
583 col() << "\nCompiled " << SBOLD("features: ") << "SPOUT - "
584 << FEATURE_PRESENT(SPOUT) << ", Syphon - "
585 << FEATURE_PRESENT(SYPHON) << ", VDPAU - "
586 << FEATURE_PRESENT(HWACC_VDPAU) << "\n";
587}
588
595
596static bool set_size(struct state_gl *s, const char *tok)
597{
598 if (strstr(tok, "fixed_size=") == tok) {
600 MOD_NAME "fixed_size with dimensions is"
601 " deprecated, use size"
602 " instead\n");
603 }
604 tok = strchr(tok, '=') + 1;
605 if (strchr(tok, '%') != NULL) {
606 s->window_size_factor = atof(tok) / 100.0;
607 } else if (strpbrk(tok, "x+-") == NULL) {
608 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong size spec: %s\n", tok);
609 return false;
610 }
611 if (strchr(tok, 'x') != NULL) {
612 s->fixed_size = true;
613 s->fixed_w = atoi(tok);
614 s->fixed_h = atoi(strchr(tok, 'x') + 1);
615 }
616 tok = strpbrk(tok, "+-");
617 if (tok != NULL) {
618 s->pos_x = atoi(tok);
619 tok = strpbrk(tok + 1, "+-");
620 }
621 if (tok != NULL) {
622 s->pos_y = atoi(tok);
623 }
624 return true;
625}
626
627static const map<string, int> hint_map = {
628 {"autoiconify", 0x20006},
629 { "platform", 0x50003},
630 { "x11", 0x60003},
631 { "wayland", 0x60004},
632};
633
635static void
636parse_hints(struct state_gl *s, bool window_hints, char *hints)
637{
638 char *tok = nullptr;
639 char *save_ptr = nullptr;
640 while ((tok = strtok_r(hints, ",", &save_ptr)) != nullptr) {
641 hints = nullptr;
642 if (strchr(tok, '=') == nullptr) {
643 MSG(ERROR, "Malformed window hint - missing value "
644 "(expected <k>=<v>).\n");
645 continue;
646 }
647 const char *key_s = tok;
648 const char *val_s = strchr(tok, '=') + 1;
649 *strchr(tok, '=') = '\0';
650 const int key = hint_map.find(key_s) != hint_map.end()
651 ? hint_map.at(key_s)
652 : stoi(key_s, nullptr, 0);
653 const int val = hint_map.find(val_s) != hint_map.end()
654 ? hint_map.at(val_s)
655 : stoi(val_s, nullptr, 0);
656 if (window_hints) {
657 s->window_hints[key] = val;
658 } else {
659 s->init_hints[key] = val;
660 }
661 }
662}
663
664static void
666{
668 "Very " TBOLD("incomplete") " list of hints and its values.\n");
669 color_printf("Keywords listed below can be passed to the options "
670 "instead of the numberic value.\n\n");
671
672 color_printf("The list contains both keys and values for init and "
673 "window hints.\n");
674 color_printf("Window hints start witn 0x20000, init with 0x50000, the "
675 "others are values.\n\n");
676
677 color_printf("For commplete list refer to glfw3.h (eg. online "
678 "<https://github.com/glfw/glfw/blob/master/include/GLFW/"
679 "glfw3.h>).\n\n");
680
681 color_printf("Available hints keys and values:\n");
682 for (const auto &h : hint_map) {
683 color_printf("\t" TBOLD("%s") " - %#x\n", h.first.c_str(),
684 h.second);
685 }
686 color_printf("\n");
687
689 "Example usage: " TBOLD("-d gl:init_hint=platform=x11") "\n\n");
690}
691
692static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) {
693 if (strstr(ptr, "help") != 0) {
694 gl_show_help(strcmp(ptr, "fullhelp") == 0);
695 return INIT_NOERR;
696 }
697
698 char *tok, *save_ptr = NULL;
699
700 while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) {
701 if (!strcmp(tok, "d") || !strcmp(tok, "dforce")) {
702 s->deinterlace = !strcmp(tok, "d") ? state_gl::deint::on : state_gl::deint::force;
703 } else if(!strncmp(tok, "fs", 2)) {
704 s->fs = true;
705 if (char *val = strchr(tok, '=')) {
706 s->req_monitor_idx = stoi(val + 1);
707 }
708 } else if(strstr(tok, "modeset") != nullptr) {
709 if (strcmp(tok, "nomodeset") != 0) {
710 if (char *val = strchr(tok, '=')) {
711 val += 1;
712 s->modeset = strcmp(val, "size") == 0 ? state_gl::MODESET_SIZE_ONLY : (enum state_gl::modeset_t) stoi(val);
713 } else {
715 }
716 }
717 } else if(!strncmp(tok, "aspect=", strlen("aspect="))) {
718 s->video_aspect = atof(tok + strlen("aspect="));
719 char *pos = strchr(tok,'/');
720 if(pos) s->video_aspect /= atof(pos + 1);
721 } else if(!strcasecmp(tok, "nodecorate")) {
722 s->window_hints[GLFW_DECORATED] = GLFW_FALSE;
723 } else if(!strcasecmp(tok, "novsync")) {
724 s->vsync = 0;
725 } else if(!strcasecmp(tok, "single")) {
726 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Single-buffering is not recommended and may not work, also GLFW discourages its use.\n";
727 s->vsync = SINGLE_BUF;
728 } else if (!strncmp(tok, "vsync=", strlen("vsync="))) {
729 if (toupper((tok + strlen("vsync="))[0]) == 'D') {
730 s->vsync = SYSTEM_VSYNC;
731 } else {
732 s->vsync = atoi(tok + strlen("vsync="));
733 }
734 } else if (!strcasecmp(tok, "cursor")) {
736 } else if (strstr(tok, "syphon") == tok || strstr(tok, "spout") == tok) {
737#if defined HAVE_SYPHON || defined HAVE_SPOUT
738 if (strchr(tok, '=')) {
739 s->syphon_spout_srv_name = strchr(tok, '=') + 1;
740 } else {
741 s->syphon_spout_srv_name = "UltraGrid";
742 }
743#else
744 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Syphon/Spout support not compiled in.\n");
745 return nullptr;
746#endif
747 } else if (strstr(tok, "gamma=") == tok) {
748 s->gamma = stof(strchr(tok, '=') + 1);
749 if (strchr(tok, '/')) {
750 s->gamma /= stof(strchr(tok, '/') + 1);
751 }
752 } else if (!strcasecmp(tok, "hide-window")) {
753 s->window_hints[GLFW_VISIBLE] = GLFW_FALSE;
754 } else if (strcasecmp(tok, "pbo") == 0 || strcasecmp(tok, "nopbo") == 0) {
755 s->use_pbo = strcasecmp(tok, "pbo") == 0 ? 1 : 0;
756 } else if (strstr(tok, "size=") == tok ||
757 strstr(tok, "fixed_size=") == tok) {
758 if (!set_size(s, tok)) {
759 return nullptr;
760 }
761 } else if (strcmp(tok, "fixed_size") == 0) {
762 s->fixed_size = true;
763 } else if (strcmp(tok, "noresizable") == 0) {
764 s->noresizable = true;
765 } else if (strstr(tok, "window_hint=") == tok) {
766 parse_hints(s, true, strchr(tok, '=') + 1);
767 } else if (strstr(tok, "init_hint=") == tok) {
768 parse_hints(s, false, strchr(tok, '=') + 1);
769 } else if (strcmp(tok, "list_hints") == 0) {
770 list_hints();
771 return nullptr;
772 } else {
773 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown option: %s\n", tok);
774 return nullptr;
775 }
776 ptr = NULL;
777 }
778
779 return s;
780}
781
782static void * display_gl_init(struct module *parent, const char *fmt, unsigned int flags) {
783 UNUSED(flags);
784
785 struct state_gl *s = nullptr;
786 try {
787 s = new state_gl(parent);
788 } catch (...) {
789 return nullptr;
790 }
791
792 if (fmt != NULL) {
793 char *tmp = strdup(fmt);
794 void *ret = nullptr;
795 try {
796 ret = display_gl_parse_fmt(s, tmp);
797 } catch (std::invalid_argument &e) {
798 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Invalid numeric value for an option!\n";
799 }
800 free(tmp);
801 if (ret != s) { // ret is either nullptr or INIT_NOERR (help requested)
802 delete s;
803 return ret;
804 }
805 }
806
807 s->use_pbo = s->use_pbo == -1 ? !check_rpi_pbo_quirks() : s->use_pbo; // don't use PBO for Raspberry Pi (better performance)
808
809 log_msg(LOG_LEVEL_INFO,"GL setup: fullscreen: %s, deinterlace: %s\n",
810 s->fs ? "ON" : "OFF", state_gl::deint_to_string(s->deinterlace));
811
813 for (auto const &i : keybindings) {
814 if (i.first == 'q') { // don't report 'q' to avoid accidental close - user can use Ctrl-c there
815 continue;
816 }
817 char msg[18];
818 snprintf(msg, sizeof msg, "%" PRIx64, i.first);
819 keycontrol_register_key(&s->mod, i.first, msg, i.second.data());
820 }
821
822#if GLFW_VERSION_MAJOR < 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR < 3)
823 if(!s->init_hints.empty()){
824 log_msg(LOG_LEVEL_WARNING, "GLFW version < 3.3 doesn't support init hints. Ignoring them.\n");
825 }
826#else
827 for (auto const &hint : s->init_hints) {
828 glfwInitHint(hint.first, hint.second);
829 }
830#endif
831
832 if (!display_gl_init_opengl(s)) {
833 delete s;
834 return nullptr;
835 }
836
837 return (void*)s;
838}
839
840#if defined HAVE_SPOUT || HAVE_SYPHON
841#ifdef HAVE_SPOUT
842#define NAME_SPOUT_SYPHON_RAW spout
843#else
844#define NAME_SPOUT_SYPHON_RAW syphon
845#endif
846#define NAME_SPOUT_SYPHON TOSTRING(NAME_SPOUT_SYPHON_RAW) " disp."
847static void * display_spout_syphon_init(struct module *parent, const char *fmt, unsigned int flags) {
848 char config_str[1024] = "hide-window:" NAME_SPOUT_SYPHON;
849 if (strcmp(fmt, "help") == 0) {
850 color_printf("Display " TBOLD(NAME_SPOUT_SYPHON) " is a tiny wrapper over OpenGL display (that may be used directly as well).\n\n");
851 color_printf("Usage:\n");
852 color_printf(TBOLD(TRED("\t-d " NAME_SPOUT_SYPHON) "[:name=<server_name]\n\n"));
853 color_printf("where:\n"
854 TBOLD("\tname") " - name of the created server\n");
855 return INIT_NOERR;
856 }
857 if (strstr(fmt, "name=") == fmt) {
858 snprintf(config_str + strlen(config_str), sizeof config_str - strlen(config_str), "=%s", strchr(fmt, '=') + 1);
859 } else if (strlen(fmt) != 0) {
860 log_msg(LOG_LEVEL_ERROR, "[" NAME_SPOUT_SYPHON "] Wrong option \"%s\"!\n", fmt);
861 return NULL;
862 }
863 return display_gl_init(parent, config_str, flags);
864}
865#endif // defined HAVE_SPOUT || HAVE_SYPHON
866
870static bool
872{
873 struct state_gl *s = (struct state_gl *) state;
874
875 assert(find(begin(gl_supp_codecs), end(gl_supp_codecs),
878 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Displaying 10+ bits - performance degradation may occur, consider '--param " GL_DISABLE_10B_OPT_PARAM_NAME "'\n";
879 }
881 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Receiving interlaced video but deinterlacing is off - suggesting toggling it on (press 'd' or pass cmdline option)\n";
882 }
883
884 s->current_desc = desc;
885
886 return true;
887}
888
889static void glfw_print_video_mode(struct state_gl *s) {
890 if (!s->fs || !s->modeset) {
891 return;
892 }
893 const GLFWvidmode* mode = glfwGetVideoMode(s->monitor);
894 LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Display mode set to: " << mode->width << "x" << mode->height << "@" << mode->refreshRate << "\n";
895}
896
897static int get_refresh_rate(enum state_gl::modeset_t modeset, GLFWmonitor *mon, double video_refresh_rate) {
898 if (!mon) {
899 return GLFW_DONT_CARE;
900 }
901 switch (modeset) {
903 return round(video_refresh_rate);
905 return GLFW_DONT_CARE;
907 const GLFWvidmode* mode = glfwGetVideoMode(mon);
908 return mode->refreshRate;
909 }
910 default:
911 return static_cast<int>(modeset);
912 }
913}
914
915static void glfw_resize_window(GLFWwindow *win, bool fs, int height, double aspect, double fps, double window_size_factor)
916{
917 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "glfw - fullscreen: %d, aspect: %lf, factor %lf\n",
919 auto *s = (struct state_gl *) glfwGetWindowUserPointer(win);
920 if (fs && s->monitor != nullptr) {
921 int width = round(height * aspect);
922 GLFWmonitor *mon = s->monitor;
923 int refresh_rate = get_refresh_rate(s->modeset, mon, fps);
924 if (s->modeset == state_gl::modeset_t::NOMODESET) {
925 const GLFWvidmode* mode = glfwGetVideoMode(mon);
926 width = mode->width;
927 height = mode->height;
928 }
929 glfwSetWindowMonitor(win, mon, GLFW_DONT_CARE, GLFW_DONT_CARE, width, height, refresh_rate);
930 } else {
931 glfwSetWindowSize(win, round(window_size_factor * height * aspect), window_size_factor * height);
932 }
933 glfwSwapBuffers(win);
934}
935
936/*
937 * Please note, that setting the value of 0 to GLX function is invalid according to
938 * documentation. However. reportedly NVidia driver does unset VSync.
939 */
940static void display_gl_set_sync_on_vblank(int value) {
941 if (value == SYSTEM_VSYNC) {
942 return;
943 }
944 glfwSwapInterval(value);
945}
946
947static void screenshot(struct video_frame *frame)
948{
949 char name[128];
950 time_t t;
951 t = time(NULL);
952 struct tm *time_ptr;
953
954#ifdef _WIN32
955 time_ptr = localtime(&t);
956#else
957 struct tm time_tmp;
958 localtime_r(&t, &time_tmp);
959 time_ptr = &time_tmp;
960#endif
961
962 strftime(name, sizeof(name), "screenshot-%a, %d %b %Y %H:%M:%S %z.pnm",
963 time_ptr);
965 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Generated screenshot \"%s\".\n", name);
966 } else {
967 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to generate screenshot!\n");
968 }
969}
970
976static void gl_reconfigure_screen(struct state_gl *s, struct video_desc desc)
977{
978 assert(s->magic == MAGIC_GL);
979
981 (desc.height + 3) / 4 * 4 : desc.height;
982
983 s->aspect = s->video_aspect ? s->video_aspect : (double) desc.width / desc.height;
984
985 log_msg(LOG_LEVEL_INFO, "Setting GL size %dx%d (%dx%d).\n", (int) round(s->aspect * desc.height),
987
988 s->current_program = 0;
989
991
993 if (desc.color_spec == DXT1) {
994 glActiveTexture(GL_TEXTURE0 + 0);
995 glBindTexture(GL_TEXTURE_2D,s->texture_display);
996 } else {
997 glActiveTexture(GL_TEXTURE0 + 2);
998 glBindTexture(GL_TEXTURE_2D,s->texture_raw);
999 }
1000 size_t data_len = ((desc.width + 3) / 4 * 4* s->dxt_height)/2;
1001 char *buffer = (char *) malloc(data_len);
1002 glCompressedTexImage2D(GL_TEXTURE_2D, 0,
1003 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
1004 (desc.width + 3) / 4 * 4, s->dxt_height, 0, data_len,
1005 /* passing NULL here isn't allowed and some rigid implementations
1006 * will crash here. We just pass some egliable buffer to fulfil
1007 * this requirement. glCompressedSubTexImage2D works as expected.
1008 */
1009 buffer);
1010 free(buffer);
1011 if(desc.color_spec == DXT1_YUV) {
1012 glActiveTexture(GL_TEXTURE0 + 0);
1013 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1014 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1015 desc.width, desc.height, 0,
1016 GL_RGBA, GL_UNSIGNED_BYTE,
1017 NULL);
1018 s->current_program = s->PHandles.at(DXT1_YUV);
1019 }
1020 } else if (desc.color_spec == UYVY) {
1021 glActiveTexture(GL_TEXTURE0 + 2);
1022 glBindTexture(GL_TEXTURE_2D,s->texture_raw);
1023 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1024 (desc.width + 1) / 2, desc.height, 0,
1025 GL_RGBA, GL_UNSIGNED_BYTE,
1026 NULL);
1027 glActiveTexture(GL_TEXTURE0 + 0);
1028 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1029 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1030 desc.width, desc.height, 0,
1031 GL_RGBA, GL_UNSIGNED_BYTE,
1032 NULL);
1033 s->current_program = s->PHandles.at(UYVY);
1034 } else if (desc.color_spec == v210) {
1035 glActiveTexture(GL_TEXTURE0 + 2);
1036 glBindTexture(GL_TEXTURE_2D,s->texture_raw);
1037 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2,
1039 GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV,
1040 NULL);
1041 glActiveTexture(GL_TEXTURE0 + 0);
1042 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1043 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1044 desc.width, desc.height, 0,
1045 GL_RGBA, GL_UNSIGNED_SHORT,
1046 NULL);
1047 s->current_program = s->PHandles.at(v210);
1048 } else if (desc.color_spec == Y416) {
1049 s->current_program = s->PHandles.at(Y416);
1050 glActiveTexture(GL_TEXTURE0 + 2);
1051 glBindTexture(GL_TEXTURE_2D,s->texture_raw);
1052 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1053 desc.width, desc.height, 0,
1054 GL_RGBA, GL_UNSIGNED_SHORT,
1055 NULL);
1056 glActiveTexture(GL_TEXTURE0 + 0);
1057 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1058 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1059 desc.width, desc.height, 0,
1060 GL_RGBA, GL_UNSIGNED_SHORT,
1061 NULL);
1062 } else if (desc.color_spec == RGBA) {
1063 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1064 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1065 desc.width, desc.height, 0,
1066 GL_RGBA, GL_UNSIGNED_BYTE,
1067 NULL);
1068 } else if (desc.color_spec == RGB) {
1069 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1070 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
1071 desc.width, desc.height, 0,
1072 GL_RGB, GL_UNSIGNED_BYTE,
1073 NULL);
1074 } else if (desc.color_spec == R10k) {
1075 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1076 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2,
1077 desc.width, desc.height, 0,
1078 GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV,
1079 nullptr);
1080 } else if (desc.color_spec == RG48) {
1081 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1082 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
1083 desc.width, desc.height, 0,
1084 GL_RGB, GL_UNSIGNED_SHORT,
1085 nullptr);
1086 } else if (desc.color_spec == DXT5) {
1087 glActiveTexture(GL_TEXTURE0 + 2);
1088 glBindTexture(GL_TEXTURE_2D,s->texture_raw);
1089 glCompressedTexImage2D(GL_TEXTURE_2D, 0,
1090 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
1091 (desc.width + 3) / 4 * 4, s->dxt_height, 0,
1092 (desc.width + 3) / 4 * 4 * s->dxt_height,
1093 NULL);
1094 glActiveTexture(GL_TEXTURE0 + 0);
1095 glBindTexture(GL_TEXTURE_2D,s->texture_display);
1096 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1097 desc.width, desc.height, 0,
1098 GL_RGBA, GL_UNSIGNED_BYTE,
1099 NULL);
1100 s->current_program = s->PHandles.at(DXT5);
1101 }
1102#ifdef HWACC_VDPAU
1103 else if (desc.color_spec == HW_VDPAU) {
1104 s->vdp.init();
1105 }
1106#endif
1107 if (s->current_program) {
1108 glUseProgram(s->current_program);
1109 if (GLint l = glGetUniformLocation(s->current_program, "image"); l != -1) {
1110 glUniform1i(l, 2);
1111 }
1112 if (GLint l = glGetUniformLocation(s->current_program, "imageWidth"); l != -1) {
1113 glUniform1f(l, (GLfloat) desc.width);
1114 }
1115 glUseProgram(0);
1116 }
1117 if (s->PHandle_deint) {
1118 glUseProgram(s->PHandle_deint);
1119 glUniform1i(glGetUniformLocation(s->PHandle_deint, "image"), 0);
1120 glUniform1f(glGetUniformLocation(s->PHandle_deint, "lineOff"), 1.0f / desc.height);
1121 glUseProgram(0);
1122 }
1124
1125 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, s->pbo_id);
1126 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, vc_get_linesize(desc.width, desc.color_spec) * desc.height, 0, GL_STREAM_DRAW_ARB);
1127 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
1128
1130
1131 if (!s->fixed_size) {
1133 //gl_resize(s->window, desc.width, desc.height);
1134 }
1135 int width, height;
1136 glfwGetFramebufferSize(s->window, &width, &height);
1137 gl_change_aspect(s, width, height);
1138
1140
1143
1144#ifdef HAVE_SYPHON
1145 if (!s->syphon_spout && !s->syphon_spout_srv_name.empty()) {
1146 s->syphon_spout = syphon_server_register(CGLGetCurrentContext(), s->syphon_spout_srv_name.c_str());
1147 }
1148#endif
1149#ifdef HAVE_SPOUT
1150 if (!s->syphon_spout_srv_name.empty()) {
1151 if (!s->syphon_spout) {
1153 }
1154 }
1155#endif
1156
1157 s->scratchpad.resize(desc.width * desc.height * 8);
1159}
1160
1161static void gl_render(struct state_gl *s, char *data)
1162{
1164
1165 if (s->current_program) {
1166 gl_render_glsl(s, data);
1167 } else {
1168 upload_texture(s, data);
1169 }
1170
1172}
1173
1175static void pop_frame(struct state_gl *s, unique_lock<mutex> &lk)
1176{
1177 s->frame_queue.pop();
1178 lk.unlock();
1179 s->frame_consumed_cv.notify_one();
1180}
1181
1183static void
1185{
1186#define BAR_WIDTH (.01)
1187#define LEFT_BAR_LEFT (-.9)
1188#define LEFT_BAR_RIGHT (LEFT_BAR_LEFT + BAR_WIDTH)
1189#define RIGHT_BAR_LEFT (LEFT_BAR_RIGHT + BAR_WIDTH)
1190#define RIGHT_BAR_RIGHT (RIGHT_BAR_LEFT + BAR_WIDTH)
1191#define BARS_TOP (.9)
1192#define BARS_BOTTOM (.8)
1193#define BAR_GREY_COL (0.9)
1194 glBindTexture(GL_TEXTURE_2D, 0);
1196 glMatrixMode(GL_MODELVIEW);
1197 glPushMatrix();
1198 glLoadIdentity();
1199 glMatrixMode(GL_PROJECTION);
1200 glPushMatrix();
1201 glLoadIdentity();
1202 glBegin(GL_QUADS);
1203 glVertex2f(LEFT_BAR_LEFT, BARS_BOTTOM); // bottom left
1204 glVertex2f(LEFT_BAR_RIGHT, BARS_BOTTOM); // bottom right
1205 glVertex2f(LEFT_BAR_RIGHT, BARS_TOP); // top right
1206 glVertex2f(LEFT_BAR_LEFT, BARS_TOP); // top left
1207 glEnd();
1208 glBegin(GL_QUADS);
1209 glVertex2f(RIGHT_BAR_LEFT, BARS_BOTTOM);
1210 glVertex2f(RIGHT_BAR_RIGHT, BARS_BOTTOM);
1211 glVertex2f(RIGHT_BAR_RIGHT, BARS_TOP);
1212 glVertex2f(RIGHT_BAR_LEFT, BARS_TOP);
1213 glEnd();
1214 glColor4f(1, 1, 1, 1);
1215 glMatrixMode(GL_PROJECTION);
1216 glPopMatrix();
1217 glMatrixMode(GL_MODELVIEW);
1218 glPopMatrix();
1220}
1221
1222static void gl_process_frames(struct state_gl *s)
1223{
1224 struct video_frame *frame;
1225
1226 struct message *msg;
1227 while ((msg = check_message(&s->mod))) {
1228 auto msg_univ = reinterpret_cast<struct msg_universal *>(msg);
1229 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Received message: %s\n", msg_univ->text);
1230 struct response *r;
1231 if (strncasecmp(msg_univ->text, "win-title ", strlen("win-title ")) == 0) {
1232 glfwSetWindowTitle(s->window, msg_univ->text + strlen("win-title "));
1233 r = new_response(RESPONSE_OK, NULL);
1234 } else {
1235 if (strlen(msg_univ->text) == 0) {
1236 r = new_response(RESPONSE_BAD_REQUEST, "Wrong message - neither win-title nor key");
1237 } else {
1238 int64_t key;
1239 int ret = sscanf(msg_univ->text, "%" SCNx64, &key);
1240 if (ret != 1 || !display_gl_process_key(s, key)) {
1241 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unknown key received: %s\n", msg_univ->text);
1242 r = new_response(RESPONSE_BAD_REQUEST, "Wrong key received");
1243 } else {
1244 r = new_response(RESPONSE_OK, NULL);
1245 }
1246 }
1247 }
1248 free_message(msg, r);
1249 }
1250
1251
1253 if (s->cursor_shown_from != steady_clock::time_point()) {
1254 const auto now = steady_clock::now();
1255 if (duration_cast<seconds>(now - s->cursor_shown_from).count() > 2) {
1256 glfwSetInputMode(s->window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
1257 s->cursor_shown_from = steady_clock::time_point();
1258 }
1259 }
1260 }
1261
1262 {
1263 unique_lock<mutex> lk(s->lock);
1264 double timeout = min(2.0 / s->current_display_desc.fps, 0.1);
1265 s->new_frame_ready_cv.wait_for(lk, duration<double>(timeout), [s] {
1266 return s->frame_queue.size() > 0;});
1267 if (s->frame_queue.size() == 0) {
1268 return;
1269 }
1270 frame = s->frame_queue.front();
1271 if (!frame) {
1272 pop_frame(s, lk);
1273 return;
1274 }
1275
1276 glfwMakeContextCurrent(s->window);
1277
1278 if (s->current_frame) {
1279 if (s->paused) {
1281 }
1283 s->free_frame_queue.push(s->current_frame);
1284 }
1285 s->current_frame = frame;
1286 }
1287
1290 }
1291 glBindTexture(GL_TEXTURE_2D, s->texture_display);
1292
1293 gl_render(s, frame->tiles[0].data);
1295 glUseProgram(s->PHandle_deint);
1296 }
1297 gl_draw(s->aspect, (s->dxt_height - s->current_display_desc.height) / (float) s->dxt_height * 2, s->vsync != SINGLE_BUF);
1298 glUseProgram(0);
1299 if (s->paused) {
1300 draw_pause();
1301 }
1302
1303 // publish to Syphon/Spout
1304 if (s->syphon_spout) {
1305#ifdef HAVE_SYPHON
1307#elif defined HAVE_SPOUT
1309 glBindTexture(GL_TEXTURE_2D, s->texture_display);
1310#endif // HAVE_SPOUT
1311 }
1312
1313 if (s->vsync == SINGLE_BUF) {
1314 glFlush();
1315 } else {
1316 glfwSwapBuffers(s->window);
1317 }
1318 log_msg(LOG_LEVEL_DEBUG, "Render buffer %dx%d\n", frame->tiles[0].width, frame->tiles[0].height);
1319 {
1320 unique_lock<mutex> lk(s->lock);
1321 pop_frame(s, lk);
1322 }
1323}
1324
1325static int64_t translate_glfw_to_ug(int key, int mods) {
1326 key = tolower(key);
1327 if (mods == GLFW_MOD_CONTROL) {
1328 switch (key) {
1329 case GLFW_KEY_UP: return K_CTRL_UP;
1330 case GLFW_KEY_DOWN: return K_CTRL_DOWN;
1331 default:
1332 return isalpha(key) ? K_CTRL(key) : -1;
1333 }
1334 } else if (mods == GLFW_MOD_ALT) {
1335 if (isalpha(key)) {
1336 return K_ALT(key);
1337 }
1338 return -1;
1339 } else if (mods == 0) {
1340 switch (key) {
1341 case GLFW_KEY_LEFT: return K_LEFT;
1342 case GLFW_KEY_UP: return K_UP;
1343 case GLFW_KEY_RIGHT: return K_RIGHT;
1344 case GLFW_KEY_DOWN: return K_DOWN;
1345 case GLFW_KEY_PAGE_UP: return K_PGUP;
1346 case GLFW_KEY_PAGE_DOWN: return K_PGDOWN;
1347 default: return key;
1348 }
1349 }
1350 return -1;
1351}
1352
1353static bool display_gl_process_key(struct state_gl *s, long long int key)
1354{
1355 verbose_msg(MOD_NAME "Key %lld pressed\n", key);
1356 switch (key) {
1357 case 'f':
1358 {
1359 s->fs = !s->fs;
1360 int width = s->current_display_desc.width;
1361 int height = s->current_display_desc.height;
1362 GLFWmonitor *mon = s->fs ? s->monitor : nullptr;
1363 if (mon && s->modeset == state_gl::modeset_t::NOMODESET) {
1364 const GLFWvidmode* mode = glfwGetVideoMode(mon);
1365 width = mode->width;
1366 height = mode->height;
1367 }
1368 int refresh_rate = get_refresh_rate(s->modeset, mon, s->current_display_desc.fps);
1369 glfwSetWindowMonitor(s->window, mon, GLFW_DONT_CARE, GLFW_DONT_CARE, width, height, refresh_rate);
1370 LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Setting fullscreen: " << (s->fs ? "ON" : "OFF") << "\n";
1371 set_gamma(s);
1373 break;
1374 }
1375 case 'q':
1376 exit_uv(0);
1377 break;
1378 case K_ALT('d'):
1380 MSG(NOTICE, "Deinterlacing: %s\n",
1382 break;
1383 case K_ALT('p'):
1384 s->paused = !s->paused;
1385 MSG(NOTICE, "%s\n",
1386 s->paused ? "Paused (press 'Alt-p' to unpause)"
1387 : "Unpaused");
1388 break;
1389 case K_ALT('s'):
1391 break;
1392 case K_ALT('m'):
1393 s->show_cursor = (state_gl::show_cursor_t) (((int) s->show_cursor + 1) % 3);
1394 LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Show cursor (0 - on, 1 - off, 2 - autohide): " << s->show_cursor << "\n";
1395 glfwSetInputMode(s->window, GLFW_CURSOR, s->show_cursor == state_gl::SC_TRUE ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
1396 break;
1397 case K_CTRL_UP:
1398 s->window_size_factor *= 1.1;
1400 break;
1401 case K_CTRL_DOWN:
1402 s->window_size_factor /= 1.1;
1404 break;
1405 default:
1406 return false;
1407 }
1408
1409 return true;
1410}
1411
1412static void glfw_key_callback(GLFWwindow* win, int key, int /* scancode */, int action, int mods)
1413{
1414 if (action != GLFW_PRESS) {
1415 return;
1416 }
1417 auto *s = (struct state_gl *) glfwGetWindowUserPointer(win);
1419 int64_t ugk = translate_glfw_to_ug(key, mods);
1420 get_keycode_name(ugk, name, sizeof name);
1421 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "%d pressed, modifiers: %d (UG name: %s)\n",
1422 key, mods,
1423 ugk > 0 ? name : "unknown");
1424 if (ugk == -1) {
1425 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Cannot translate key %d (modifiers: %d)!\n", key, mods);
1426 }
1427 if (ugk <= 0) {
1428 return;
1429 }
1430 if (!display_gl_process_key(s, ugk)) { // keybinding not found -> pass to control
1431 keycontrol_send_key(get_root_module(&s->mod), ugk);
1432 }
1433}
1434
1435static void glfw_mouse_callback(GLFWwindow *win, double /* x */, double /* y */)
1436{
1437 auto *s = (struct state_gl *) glfwGetWindowUserPointer(win);
1438 if (s->show_cursor == state_gl::SC_AUTOHIDE) {
1439 if (s->cursor_shown_from == steady_clock::time_point()) {
1440 glfwSetInputMode(s->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
1441 }
1442 s->cursor_shown_from = steady_clock::now();
1443 }
1444}
1445
1447 auto version = (const char *) glGetString(GL_VERSION);
1448 if (!version) {
1449 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get OpenGL version!\n");
1450 return false;
1451 }
1452 if (atof(version) < 2.0) {
1453 log_msg(LOG_LEVEL_ERROR, MOD_NAME "ERROR: OpenGL 2.0 is not supported, try updating your drivers...\n");
1454 return false;
1455 }
1456 log_msg(LOG_LEVEL_INFO, MOD_NAME "OpenGL 2.0 is supported...\n");
1457 MSG(VERBOSE, "Supported OpenGL version is %s.\n", version);
1458 return true;
1459}
1460
1462 int bits[3];
1463 glGetIntegerv(GL_RED_BITS, &bits[0]);
1464 glGetIntegerv(GL_GREEN_BITS, &bits[1]);
1465 glGetIntegerv(GL_BLUE_BITS, &bits[2]);
1466 LOG(LOG_LEVEL_INFO) << MOD_NAME << "Buffer depth - R: " << bits[0] << "b, G: " << bits[1] << "b, B: " << bits[2] << "b\n";
1467}
1468
1469static void display_gl_render_last(GLFWwindow *win) {
1470 auto *s = (struct state_gl *) glfwGetWindowUserPointer(win);
1471 unique_lock<mutex> lk(s->lock);
1472 auto *f = s->current_frame;
1473 s->current_frame = nullptr;
1474 lk.unlock();
1475 if (!f) {
1476 return;
1477 }
1478 // redraw last frame
1480}
1481
1482#if defined __linux__ || defined _WIN32
1483#ifndef GLEW_ERROR_NO_GLX_DISPLAY
1484#define GLEW_ERROR_NO_GLX_DISPLAY 4
1485#endif
1486static const char *glewGetError(GLenum err) {
1487 switch (err) {
1488 case GLEW_ERROR_NO_GL_VERSION: return "missing GL version";
1489 case GLEW_ERROR_GL_VERSION_10_ONLY: return "Need at least OpenGL 1.1";
1490 case GLEW_ERROR_GLX_VERSION_11_ONLY: return "Need at least GLX 1.2";
1491 case GLEW_ERROR_NO_GLX_DISPLAY: return "Need GLX display for GLX support";
1492 default: return (const char *) glewGetErrorString(err);
1493 }
1494}
1495#endif // defined __linux__ || defined _WIN32
1496
1497static void glfw_print_error(int error_code, const char* description) {
1498 LOG(LOG_LEVEL_ERROR) << "GLFW error " << error_code << ": " << description << "\n";
1499}
1500
1505static void set_mac_color_space(void) {
1506#ifdef GLFW_COCOA_NS_COLOR_SPACE
1507 const char *col = get_commandline_param("color");
1508 if (!col) {
1509 return;
1510 }
1511 glfwWindowHint(GLFW_COCOA_NS_COLOR_SPACE, stoi(col, nullptr, 16));
1512#endif // defined GLFW_COCOA_NS_COLOR_SPACE
1513}
1514
1515static GLuint gl_substitute_compile_link(const char *vprogram, const char *fprogram)
1516{
1517 char *fp = strdup(fprogram);
1518 int index = 1;
1519 const char *col = get_commandline_param("color");
1520 if (col) {
1521 int color = stol(col, nullptr, 16) >> 4; // first nibble
1522 if (color > 0 && color <= 3) {
1523 index = color;
1524 } else {
1525 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Wrong chromicities index " << color << "\n";
1526 }
1527 }
1528 double cs_coeffs[2*4] = { 0, 0, KR_709, KB_709, KR_2020, KB_2020, KR_P3, KB_P3 };
1529 double kr = cs_coeffs[2 * index];
1530 double kb = cs_coeffs[2 * index + 1];
1531 const char *placeholders[] = { "Y_SCALED_PLACEHOLDER", "R_CR_PLACEHOLDER", "G_CB_PLACEHOLDER", "G_CR_PLACEHOLDER", "B_CB_PLACEHOLDER" };
1532 double values[] = { Y_LIMIT_INV(8), R_CR(8,kr,kb), G_CB(8,kr,kb), G_CR(8,kr,kb), B_CB(8,kr,kb)};
1533
1534 for (size_t i = 0; i < sizeof placeholders / sizeof placeholders[0]; ++i) {
1535 char *tok = fp;
1536 while ((tok = strstr(fp, placeholders[i])) != nullptr) {
1537 memset(tok, ' ', strlen(placeholders[i]));
1538 int written = snprintf(tok, sizeof placeholders - 1, "%.6f", values[i]);
1539 tok[written] = ' ';
1540 }
1541 }
1542 GLuint ret = glsl_compile_link(vprogram, fp);
1543 free(fp);
1544 return ret;
1545}
1546
1547static void
1549{
1550 for (auto const &hint : s->window_hints) {
1551 glfwWindowHint(hint.first, hint.second);
1552 }
1553#ifndef _WIN32
1554 if (s->noresizable) {
1555 glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
1556 }
1557#endif
1558
1561 if (hints == nullptr) {
1562 return;
1563 }
1564 vector<char> data(strlen(hints) + 1);
1565 copy(hints, hints + strlen(hints) + 1, data.begin());
1566 char *hints_c = data.data();
1567 char *tmp = hints_c;
1568 char *tok = nullptr;
1569 char *save_ptr = nullptr;
1570 while ((tok = strtok_r(tmp, ":", &save_ptr)) != nullptr) {
1571 tmp = nullptr;
1572 if (strchr(tok, '=') == nullptr) {
1573 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Malformed window hint - missing value (expected <k>=<v>).\n";
1574 continue;
1575 }
1576 int key = stoi(tok, 0, 0);
1577 int val = stoi(strchr(tok, '=') + 1, 0, 0);
1578 glfwWindowHint(key, val);
1579 }
1580}
1581
1582static void print_gamma_ramp(GLFWmonitor *monitor) {
1583 const struct GLFWgammaramp *ramp = glfwGetGammaRamp(monitor);
1584 if (ramp == nullptr) {
1585 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Cannot get gamma ramp!\n";
1586 return;
1587 }
1588 ostringstream oss;
1589 oss << "Gamma ramp:\n";
1590 for (unsigned int i = 0; i < ramp->size; ++i) {
1591 oss << "r[" << i << "]=" << ramp->red[i] <<
1592 ", g[" << i << "]=" << ramp->green[i] <<
1593 ", b[" << i << "]=" << ramp->blue[i] << "\n";
1594
1595 }
1596 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME << oss.str();
1597}
1598
1599static void set_gamma(struct state_gl *s) {
1600 if (!s->gamma || !s->monitor) {
1601 return;
1602 }
1603 glfwSetGamma(s->monitor, s->gamma);
1606 }
1607}
1608
1609static bool
1611{
1612#ifdef HWACC_VDPAU
1613 const char *hwacc_param = get_commandline_param("use-hw-accel");
1614 if (hwacc_param != nullptr && strcmp(hwacc_param, "vdpau-copy") == 0) {
1615 return false;
1616 }
1617 if (strstr((const char *) glGetString(GL_EXTENSIONS),
1618 "GL_NV_vdpau_interop") != nullptr) {
1619 return true;
1620 }
1621 log_msg(hwacc_param == nullptr ? LOG_LEVEL_VERBOSE : LOG_LEVEL_WARNING,
1622 MOD_NAME "VDPAU interop NOT supported!\n");
1623 MSG(DEBUG, "Available extensions:%s\n", glGetString(GL_EXTENSIONS));
1624#endif
1625 return false;
1626}
1627
1630 " Disable 10 bit codec processing to improve performance\n");
1632 "* " GL_WINDOW_HINT_OPT_PARAM_NAME "=<k>=<v>[:<k2>=<v2>...]\n"
1633 " Set window hint <k> to value <v> (deprecated, use option)\n");
1638static bool display_gl_init_opengl(struct state_gl *s)
1639{
1640 glfwSetErrorCallback(glfw_print_error);
1641
1643 .value_or(GLFW_TRUE) == GLFW_FALSE) {
1644 LOG(LOG_LEVEL_ERROR) << "Cannot initialize GLFW!\n";
1645 return false;
1646 }
1647
1648 if (s->req_monitor_idx != -1) {
1649 int count = 0;
1650 GLFWmonitor **mon = glfwGetMonitors(&count);
1651 if (s->req_monitor_idx < 0 || s->req_monitor_idx >= count) {
1652 MSG(ERROR, "Wrong monitor index: %d (max %d)\n",
1653 s->req_monitor_idx, count - 1);
1654 return false;
1655 }
1656 s->monitor = mon[s->req_monitor_idx];
1657 }
1658
1659 if (s->monitor == nullptr) {
1660 s->monitor = glfwGetPrimaryMonitor();
1661 if (s->monitor == nullptr) {
1662 LOG(LOG_LEVEL_WARNING) << MOD_NAME << "No monitor found! Continuing but full-screen will be disabled.\n";
1663 }
1664 }
1665
1667 for (auto const & bits : {GLFW_RED_BITS, GLFW_GREEN_BITS, GLFW_BLUE_BITS}) {
1668 glfwWindowHint(bits, 10);
1669 }
1670 }
1671
1673 glfwWindowHint(GLFW_DOUBLEBUFFER, s->vsync == SINGLE_BUF ? GLFW_FALSE : GLFW_TRUE);
1674 struct video_frame *splash = get_splashscreen();
1675 int width = splash->tiles[0].width;
1676 int height = splash->tiles[0].height;
1677 vf_free(splash);
1678 GLFWmonitor *mon = s->fs ? s->monitor : nullptr;
1679 if (s->fixed_size && s->fixed_w && s->fixed_h) {
1680 width = s->fixed_w;
1681 height = s->fixed_h;
1682 } else if (mon != nullptr && s->modeset == state_gl::modeset_t::NOMODESET) {
1683 const GLFWvidmode* mode = glfwGetVideoMode(mon);
1684 width = mode->width;
1685 height = mode->height;
1686 glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
1687 }
1689 if ((s->window = glfwCreateWindow(width, height, IF_NOT_NULL_ELSE(get_commandline_param("window-title"), DEFAULT_WIN_NAME), nullptr, nullptr)) == nullptr) {
1690 return false;
1691 }
1692 if (s->pos_x != INT_MIN) {
1693 const int y = s->pos_y == INT_MIN ? 0 : s->pos_y;
1694 glfwSetWindowPos(s->window, s->pos_x, y);
1695 }
1696 if (s->noresizable) {
1697 glfwSetWindowSizeLimits(s->window, width, height, width,
1698 height);
1699 }
1700 if (mon != nullptr) {
1701 glfwSetWindowMonitor(s->window, mon, GLFW_DONT_CARE, GLFW_DONT_CARE, width, height, get_refresh_rate(s->modeset, mon, GLFW_DONT_CARE));
1702 }
1704 glfwSetWindowUserPointer(s->window, s);
1705 set_gamma(s);
1706 glfwSetInputMode(s->window, GLFW_CURSOR, s->show_cursor == state_gl::SC_TRUE ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
1707 glfwMakeContextCurrent(s->window);
1708 glfwSetKeyCallback(s->window, glfw_key_callback);
1709 glfwSetCursorPosCallback(s->window, glfw_mouse_callback);
1710 glfwSetWindowCloseCallback(s->window, glfw_close_callback);
1711 glfwSetFramebufferSizeCallback(s->window, gl_resize);
1712 glfwSetWindowRefreshCallback(s->window, display_gl_render_last);
1713
1714#if defined __linux__ || defined _WIN32
1715 if (GLenum err = glewInit()) {
1716 log_msg(LOG_LEVEL_ERROR, MOD_NAME "GLEW Error: %s (err %d)\n", glewGetError(err), err);
1717 if (err != GLEW_ERROR_NO_GLX_DISPLAY) { // do not fail on error 4 (on Wayland), which can be suppressed
1718 return false;
1719 }
1720 }
1721#endif
1723 glfwDestroyWindow(s->window);
1724 s->window = nullptr;
1725 return false;
1726 }
1728
1729 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
1730 glEnable( GL_TEXTURE_2D );
1731
1732 glGenTextures(1, &s->texture_display);
1733 glBindTexture(GL_TEXTURE_2D, s->texture_display);
1734 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1735 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1736 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1737 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1738
1739 glGenTextures(1, &s->texture_raw);
1740 glBindTexture(GL_TEXTURE_2D, s->texture_raw);
1741 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1742 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1743 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1744 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1745
1746 for (auto &it : glsl_programs) {
1747 GLuint prog = gl_substitute_compile_link(vert, it.second);
1748 if (prog == 0U) {
1749 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to link program for %s!\n", get_codec_name(it.first));
1750 handle_error(1);
1751 continue;
1752 }
1753 s->PHandles[it.first] = prog;
1754 }
1755
1757 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to compile deinterlace program!\n");
1758 handle_error(1);
1759 }
1760
1761 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // set row alignment to 1 byte instead of default
1762 // 4 bytes which won't work on row-unaligned RGB
1763
1764 // Create fbo
1765 glGenFramebuffersEXT(1, &s->fbo_id);
1766
1767 glGenBuffersARB(1, &s->pbo_id);
1768
1770
1771 glfwMakeContextCurrent(nullptr);
1772
1773 return true;
1774}
1775
1779 glfwMakeContextCurrent(s->window);
1780
1781 for (auto &it : s->PHandles) {
1782 glDeleteProgram(it.second);
1783 }
1784 glDeleteTextures(1, &s->texture_display);
1785 glDeleteTextures(1, &s->texture_raw);
1786 glDeleteFramebuffersEXT(1, &s->fbo_id);
1787 glDeleteBuffersARB(1, &s->pbo_id);
1788 glfwDestroyWindow(s->window);
1789
1790 if (s->syphon_spout) {
1791#ifdef HAVE_SYPHON
1793#elif defined HAVE_SPOUT
1795#endif
1796 }
1797}
1798
1799static void display_gl_run(void *arg)
1800{
1801 struct state_gl *s =
1802 (struct state_gl *) arg;
1803
1804 glfwMakeContextCurrent(s->window);
1805 while (!glfwWindowShouldClose(s->window)) {
1806 glfwPollEvents();
1808 }
1809 glfwMakeContextCurrent(nullptr);
1810}
1811
1812static void gl_change_aspect(struct state_gl *s, int width, int height)
1813{
1814 double x = 1.0,
1815 y = 1.0;
1816
1817 glViewport( 0, 0, ( GLint )width, ( GLint )height );
1818
1819 glMatrixMode( GL_PROJECTION );
1820 glLoadIdentity( );
1821
1822 double screen_ratio = (double) width / height;
1823 if(screen_ratio > s->aspect) {
1824 x = (double) height * s->aspect / width;
1825 } else {
1826 y = (double) width / (height * s->aspect);
1827 }
1828 glScalef(x, y, 1);
1829
1830 glOrtho(-1,1,-1/s->aspect,1/s->aspect,10,-10);
1831}
1832
1833static void gl_resize(GLFWwindow *win, int width, int height)
1834{
1835 auto *s = (struct state_gl *) glfwGetWindowUserPointer(win);
1836 debug_msg("Resized to: %dx%d\n", width, height);
1837
1838 gl_change_aspect(s, width, height);
1839
1840 if (s->vsync == SINGLE_BUF) {
1841 glDrawBuffer(GL_FRONT);
1842 /* Clear the screen */
1843 glClear(GL_COLOR_BUFFER_BIT);
1844 }
1845}
1846
1847static void upload_compressed_texture(struct state_gl *s, char *data) {
1848 switch (s->current_display_desc.color_spec) {
1849 case DXT1:
1850 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
1851 (s->current_display_desc.width + 3) / 4 * 4, s->dxt_height,
1852 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
1853 ((s->current_display_desc.width + 3) / 4 * 4 * s->dxt_height)/2,
1854 data);
1855 break;
1856 case DXT1_YUV:
1857 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
1858 s->current_display_desc.width, s->current_display_desc.height,
1859 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
1860 (s->current_display_desc.width * s->current_display_desc.height/16)*8,
1861 data);
1862 break;
1863 case DXT5:
1864 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
1865 (s->current_display_desc.width + 3) / 4 * 4, s->dxt_height,
1866 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
1867 (s->current_display_desc.width + 3) / 4 * 4 * s->dxt_height,
1868 data);
1869 break;
1870 default:
1871 abort();
1872 }
1873}
1874
1875static void upload_texture(struct state_gl *s, char *data)
1876{
1877#ifdef HWACC_VDPAU
1878 if (s->current_display_desc.color_spec == HW_VDPAU) {
1879 s->vdp.loadFrame(reinterpret_cast<hw_vdpau_frame *>(data));
1880 return;
1881 }
1882#endif
1883 if (s->current_display_desc.color_spec == DXT1 || s->current_display_desc.color_spec == DXT1_YUV || s->current_display_desc.color_spec == DXT5) {
1885 return;
1886 }
1887 GLuint format = s->current_display_desc.color_spec == RGB || s->current_display_desc.color_spec == RG48 ? GL_RGB : GL_RGBA;
1888 GLenum type = GL_UNSIGNED_BYTE;
1889 if (s->current_display_desc.color_spec == R10k || s->current_display_desc.color_spec == v210) {
1890 type = GL_UNSIGNED_INT_2_10_10_10_REV;
1891 } else if (s->current_display_desc.color_spec == Y416 || s->current_display_desc.color_spec == RG48) {
1892 type = GL_UNSIGNED_SHORT;
1893 }
1894 GLint width = s->current_display_desc.width;
1895 if (s->current_display_desc.color_spec == UYVY || s->current_display_desc.color_spec == v210) {
1896 width = vc_get_linesize(width, s->current_display_desc.color_spec) / 4;
1897 }
1899 auto process_r10k = [](uint32_t * __restrict out, const uint32_t *__restrict in, long width, long height) {
1900 DEBUG_TIMER_START(process_r10k);
1901 long line_padding_b = vc_get_linesize(width, R10k) - 4 * width;
1902 for (long i = 0; i < height; i += 1) {
1903 OPTIMIZED_FOR (long j = 0; j < width; j += 1) {
1904 uint32_t x = *in++;
1905 *out++ = /* output is x2b8g8r8 little-endian */
1906 (x & 0xFFU) << 2U | (x & 0xC0'00U) >> 14U | // R
1907 (x & 0x3F'00U) << 6U | (x & 0xF0'00'00) >> 10U | // G
1908 (x & 0x0F'00'00U) << 10U | (x & 0xFC'00'00'00U) >> 6U; // B
1909 }
1910 in += line_padding_b / sizeof(uint32_t);
1911 }
1912 DEBUG_TIMER_STOP(process_r10k);
1913 };
1914 int data_size = vc_get_linesize(s->current_display_desc.width, s->current_display_desc.color_spec) * s->current_display_desc.height;
1915 if (s->use_pbo) {
1916 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, s->pbo_id); // current pbo
1917 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, data_size, 0, GL_STREAM_DRAW_ARB);
1918 if (void *ptr = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB)) {
1919 // update data directly on the mapped buffer
1920 if (s->current_display_desc.color_spec == R10k) { // perform byte swap
1921 process_r10k(static_cast<uint32_t *>(ptr), reinterpret_cast<uint32_t *>(data), s->current_display_desc.width, s->current_display_desc.height);
1922 } else {
1923 memcpy(ptr, data, data_size);
1924 }
1925 glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); // release pointer to mapping buffer
1926 }
1927 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, s->current_display_desc.height, format, type, nullptr);
1928
1929 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
1930 } else {
1931 if (s->current_display_desc.color_spec == R10k) { // perform byte swap
1932 process_r10k(reinterpret_cast<uint32_t *>(s->scratchpad.data()), reinterpret_cast<uint32_t *>(data), s->current_display_desc.width, s->current_display_desc.height);
1933 data = s->scratchpad.data();
1934 }
1935 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, s->current_display_desc.height, format, type, data);
1936 }
1937}
1938
1940{
1941#if ! defined __linux__
1942 return false;
1943#else
1944 std::ifstream cpuinfo("/proc/cpuinfo");
1945 if (!cpuinfo)
1946 return false;
1947
1948 bool detected_rpi = false;
1949 bool detected_bcm2835 = false;
1950 std::string line;
1951 while (std::getline(cpuinfo, line) && !detected_rpi) {
1952 detected_bcm2835 |= line.find("BCM2835") != std::string::npos;
1953 detected_rpi |= line.find("Raspberry Pi") != std::string::npos;
1954 }
1955
1956 return detected_rpi || detected_bcm2835;
1957#endif
1958}
1959
1960static void gl_render_glsl(struct state_gl *s, char *data)
1961{
1962 int status;
1963 glBindTexture(GL_TEXTURE_2D, 0);
1964 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s->fbo_id);
1966 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, s->texture_display, 0);
1967 status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1968 assert(status == GL_FRAMEBUFFER_COMPLETE_EXT);
1969 glActiveTexture(GL_TEXTURE0 + 2);
1970 glBindTexture(GL_TEXTURE_2D, s->texture_raw);
1971
1972 glMatrixMode( GL_PROJECTION );
1973 glPushMatrix();
1974 glLoadIdentity( );
1975
1976 glMatrixMode( GL_MODELVIEW );
1977 glPushMatrix();
1978 glLoadIdentity( );
1979
1980 glPushAttrib(GL_VIEWPORT_BIT);
1981
1982 glViewport( 0, 0, s->current_display_desc.width, s->current_display_desc.height);
1983
1984 upload_texture(s, data);
1986
1987 glUseProgram(s->current_program);
1988
1989 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
1991
1992 glBegin(GL_QUADS);
1993 glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0);
1994 glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0);
1995 glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0);
1996 glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0);
1997 glEnd();
1998
1999 glPopAttrib();
2000
2001 glMatrixMode( GL_PROJECTION );
2002 glPopMatrix();
2003 glMatrixMode( GL_MODELVIEW );
2004 glPopMatrix();
2005
2006 glUseProgram(0);
2007 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
2008 glActiveTexture(GL_TEXTURE0 + 0);
2009 glBindTexture(GL_TEXTURE_2D, s->texture_display);
2010}
2011
2012static void gl_draw(double ratio, double bottom_offset, bool double_buf)
2013{
2014 float bottom;
2016
2017 glDrawBuffer(double_buf ? GL_BACK : GL_FRONT);
2018 if (double_buf) {
2019 glClear(GL_COLOR_BUFFER_BIT);
2020 }
2021
2022 glMatrixMode( GL_MODELVIEW );
2023 glLoadIdentity( );
2024 glTranslatef( 0.0f, 0.0f, -1.35f );
2025
2026 /* Reflect that we may have higher texture than actual data
2027 * if we use DXT and source height was not divisible by 4
2028 * In normal case, there would be 1.0 */
2029 bottom = 1.0f - bottom_offset;
2030
2032 glBegin(GL_QUADS);
2033 /* Front Face */
2034 /* Bottom Left Of The Texture and Quad */
2035 glTexCoord2f( 0.0f, bottom ); glVertex2f( -1.0f, -1/ratio);
2036 /* Bottom Right Of The Texture and Quad */
2037 glTexCoord2f( 1.0f, bottom ); glVertex2f( 1.0f, -1/ratio);
2038 /* Top Right Of The Texture and Quad */
2039 glTexCoord2f( 1.0f, 0.0f ); glVertex2f( 1.0f, 1/ratio);
2040 /* Top Left Of The Texture and Quad */
2041 glTexCoord2f( 0.0f, 0.0f ); glVertex2f( -1.0f, 1/ratio);
2042 glEnd( );
2043
2045}
2046
2047static void glfw_close_callback(GLFWwindow *win)
2048{
2049 glfwSetWindowShouldClose(win, GLFW_TRUE);
2050 exit_uv(0);
2051}
2052
2053static bool
2054display_gl_get_property(void *state, int property, void *val, size_t *len)
2055{
2056 auto *s = (struct state_gl *) state;
2057 enum interlacing_t supported_il_modes[] = {PROGRESSIVE, INTERLACED_MERGED, SEGMENTED_FRAME};
2058 int rgb_shift[] = {0, 8, 16};
2059
2060 switch (property) {
2062 UG_ASSERT(*len >= sizeof gl_supp_codecs);
2063 auto filter_codecs = [s](codec_t c) {
2064 if (get_bits_per_component(c) > DEPTH8 &&
2065 commandline_params.find(
2067 commandline_params.end()) { // option to disable
2068 // 10-bit processing
2069 return false;
2070 }
2071 if (glsl_programs.find(c) != glsl_programs.end() &&
2072 s->PHandles.find(c) ==
2073 s->PHandles.end()) { // GLSL shader needed but
2074 // compilation failed
2075 return false;
2076 }
2077 if (c == HW_VDPAU && !s->vdp_interop) {
2078 return false;
2079 }
2080 return true;
2081 };
2082 const codec_t *endptr =
2083 copy_if(begin(gl_supp_codecs), end(gl_supp_codecs),
2084 (codec_t *) val, filter_codecs);
2085 *len = (const char *) endptr - (char *) val;
2086 return true;
2087 }
2089 UG_ASSERT(*len >= sizeof rgb_shift);
2090 memcpy(val, rgb_shift, sizeof(rgb_shift));
2091 *len = sizeof(rgb_shift);
2092 return true;
2094 UG_ASSERT(*len >= sizeof(int));
2095 *(int *) val = PITCH_DEFAULT;
2096 *len = sizeof(int);
2097 return true;
2099 UG_ASSERT(*len >= sizeof supported_il_modes);
2100 memcpy(val, supported_il_modes, sizeof(supported_il_modes));
2101 *len = sizeof(supported_il_modes);
2102 return true;
2103 default:
2104 return false;
2105 }
2106}
2107
2108static void display_gl_done(void *state)
2109{
2110 struct state_gl *s = (struct state_gl *) state;
2111
2112 assert(s->magic == MAGIC_GL);
2113
2115
2116 while (s->free_frame_queue.size() > 0) {
2117 struct video_frame *buffer = s->free_frame_queue.front();
2118 s->free_frame_queue.pop();
2119 vf_free(buffer);
2120 }
2121
2122 while (s->frame_queue.size() > 0) {
2123 struct video_frame *buffer = s->frame_queue.front();
2124 s->frame_queue.pop();
2125 vf_free(buffer);
2126 }
2127
2129
2130 delete s;
2131}
2132
2133static struct video_frame * display_gl_getf(void *state)
2134{
2135 struct state_gl *s = (struct state_gl *) state;
2136 assert(s->magic == MAGIC_GL);
2137
2138 lock_guard<mutex> lock(s->lock);
2139
2140 while (s->free_frame_queue.size() > 0) {
2141 struct video_frame *buffer = s->free_frame_queue.front();
2142 s->free_frame_queue.pop();
2144 return buffer;
2145 }
2146 vf_free(buffer);
2147 }
2148
2149 struct video_frame *buffer = vf_alloc_desc_data(s->current_desc);
2150 vf_clear(buffer);
2151 return buffer;
2152}
2153
2154static bool display_gl_putf(void *state, struct video_frame *frame, long long timeout_ns)
2155{
2156 struct state_gl *s = (struct state_gl *) state;
2157
2158 assert(s->magic == MAGIC_GL);
2159
2160 unique_lock<mutex> lk(s->lock);
2161
2162 if(!frame) {
2163 glfwSetWindowShouldClose(s->window, GLFW_TRUE);
2164 s->frame_queue.push(frame);
2165 lk.unlock();
2166 s->new_frame_ready_cv.notify_one();
2167 return true;
2168 }
2169
2170 switch (timeout_ns) {
2171 case PUTF_DISCARD:
2173 s->free_frame_queue.push(frame);
2174 return 0;
2175 case PUTF_BLOCKING:
2176 s->frame_consumed_cv.wait(lk, [s]{return s->frame_queue.size() < MAX_BUFFER_SIZE;});
2177 break;
2178 case PUTF_NONBLOCK:
2179 break;
2180 default:
2181 s->frame_consumed_cv.wait_for(lk, timeout_ns * 1ns, [s]{return s->frame_queue.size() < MAX_BUFFER_SIZE;});
2182 break;
2183 }
2184 if (s->frame_queue.size() >= MAX_BUFFER_SIZE) {
2185 LOG(LOG_LEVEL_INFO) << MOD_NAME << "1 frame(s) dropped!\n";
2187 s->free_frame_queue.push(frame);
2188 return false;
2189 }
2190 s->frame_queue.push(frame);
2191
2192 lk.unlock();
2193 s->new_frame_ready_cv.notify_one();
2194
2195 return !s->paused;
2196}
2197
2199 [](struct device_info **available_cards, int *count, void (**deleter)(void *)) {
2200 UNUSED(deleter);
2201 *count = 1;
2202 *available_cards = (struct device_info *) calloc(1, sizeof(struct device_info));
2203 strcpy((*available_cards)[0].dev, "");
2204 strcpy((*available_cards)[0].name, "OpenGL SW display");
2205
2206 dev_add_option(&(*available_cards)[0], "Deinterlace", "Deinterlace", "deinterlace", ":d", true);
2207 dev_add_option(&(*available_cards)[0], "Fullscreen", "Launch as fullscreen", "fullscreen", ":fs", true);
2208 dev_add_option(&(*available_cards)[0], "No decorate", "Disable window decorations", "nodecorate", ":nodecorate", true);
2209 dev_add_option(&(*available_cards)[0], "Show cursor", "Show visible cursor", "cursor", ":cursor", true);
2210 dev_add_option(&(*available_cards)[0], "Disable vsync", "Disable vsync", "novsync", ":novsync", true);
2211 dev_add_option(&(*available_cards)[0], "Aspect", "Requested video aspect <w>/<h>", "aspect", ":aspect=", false);
2212
2213 (*available_cards)[0].repeatable = true;
2214 },
2222 NULL,
2223 NULL,
2224 MOD_NAME,
2225};
2226
2228
2229#if defined HAVE_SPOUT || HAVE_SYPHON
2230#if defined HAVE_SPOUT && HAVE_SYPHON
2231#error "Both SPOUT and Syphon defined, please fix!"
2232#endif // defined HAVE_SPOUT && HAVE_SYPHON
2233static const struct video_display_info display_spout_syphon_info = {
2234 [](struct device_info **available_cards, int *count, void (**deleter)(void *)) {
2235 *deleter = free;
2236 *count = 1;
2237 *available_cards = (struct device_info *) calloc(1, sizeof(struct device_info));
2238#ifdef HAVE_SPOUT
2239 strcpy((*available_cards)[0].dev, "syphon");
2240 strcpy((*available_cards)[0].name, "Syphon display");
2241#else
2242 strcpy((*available_cards)[0].dev, "spout");
2243 strcpy((*available_cards)[0].name, "SPOUT display");
2244#endif
2245 },
2246 display_spout_syphon_init,
2253 NULL,
2254 NULL,
2255 "[" NAME_SPOUT_SYPHON "] ",
2256};
2257REGISTER_MODULE(NAME_SPOUT_SYPHON_RAW, &display_spout_syphon_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION);
2258#endif // defined HAVE_SPOUT || HAVE_SYPHON
#define UNUSED(x)
void ** state
int void * val
struct video_desc desc
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
const char * get_codec_name(codec_t codec)
#define KR_709
Definition color.h:82
#define KR_P3
Definition color.h:86
#define KB_2020
Definition color.h:85
#define KB_709
Definition color.h:83
#define G_CR(in_depth, kr, kb)
Definition color.h:145
#define B_CB(in_depth, kr, kb)
Definition color.h:147
#define KR_2020
Definition color.h:84
#define KB_P3
Definition color.h:87
#define R_CR(in_depth, kr, kb)
Definition color.h:141
#define Y_LIMIT_INV(in_depth)
Definition color.h:150
#define G_CB(in_depth, kr, kb)
Definition color.h:142
int color_printf(const char *format,...)
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
#define SUNDERLINE(x)
Definition color_out.h:81
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
static __device__ void swap(T &a, T &b)
Swaps two referenced values.
Definition cuda_dxt.cu:456
static __device__ vec3 round(const vec3 &v)
Definition cuda_dxt.cu:122
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define DEBUG_TIMER_START(name)
Definition debug.h:324
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define DEBUG_TIMER_STOP(name)
Definition debug.h:325
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define MSG(l, fmt,...)
Definition debug.h:96
#define verbose_msg(...)
Definition debug.h:90
#define LOG(level)
Definition debug.h:313
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
steady_clock::duration duration
Definition echo.cpp:67
#define GL_WINDOW_HINT_OPT_PARAM_NAME
Definition gl.cpp:90
static GLuint gl_substitute_compile_link(const char *vprogram, const char *fprogram)
Definition gl.cpp:1515
#define LEFT_BAR_LEFT
static void display_gl_render_last(GLFWwindow *win)
Definition gl.cpp:1469
static void glfw_print_error(int error_code, const char *description)
Definition gl.cpp:1497
static void gl_change_aspect(struct state_gl *s, int width, int height)
Definition gl.cpp:1812
static void display_gl_set_window_hints(struct state_gl *s)
Definition gl.cpp:1548
static void upload_texture(struct state_gl *s, char *data)
Definition gl.cpp:1875
static bool check_rpi_pbo_quirks()
Definition gl.cpp:1939
static void display_gl_cleanup_opengl(struct state_gl *s)
Definition gl.cpp:1778
static void glfw_print_video_mode(struct state_gl *s)
Definition gl.cpp:889
static void gl_reconfigure_screen(struct state_gl *s, struct video_desc desc)
Definition gl.cpp:976
static bool display_gl_reconfigure(void *state, struct video_desc desc)
Definition gl.cpp:871
static void parse_hints(struct state_gl *s, bool window_hints, char *hints)
Definition gl.cpp:636
static bool display_gl_process_key(struct state_gl *s, long long int key)
Definition gl.cpp:1353
static bool display_gl_init_opengl(struct state_gl *s)
Definition gl.cpp:1638
static void glfw_close_callback(GLFWwindow *win)
Definition gl.cpp:2047
static void glfw_resize_window(GLFWwindow *win, bool fs, int height, double aspect, double fps, double window_size_factor)
Definition gl.cpp:915
#define GL_DISABLE_10B_OPT_PARAM_NAME
Definition gl.cpp:89
static void gl_render(struct state_gl *s, char *data)
Definition gl.cpp:1161
static const char * fp_display_dxt1_yuv
Definition gl.cpp:283
#define SYSTEM_VSYNC
Definition gl.cpp:93
static void gl_print_monitors(bool fullhelp)
Definition gl.cpp:477
static const char fp_display_dxt5ycocg[]
Definition gl.cpp:310
static bool display_gl_get_property(void *state, int property, void *val, size_t *len)
Definition gl.cpp:2054
static void gl_resize(GLFWwindow *win, int width, int height)
Definition gl.cpp:1833
#define BARS_BOTTOM
static void upload_compressed_texture(struct state_gl *s, char *data)
Definition gl.cpp:1847
static void set_mac_color_space(void)
Definition gl.cpp:1505
#define DEFAULT_WIN_NAME
Definition gl.cpp:87
static void display_gl_print_depth()
Definition gl.cpp:1461
static void display_gl_set_sync_on_vblank(int value)
Definition gl.cpp:940
unordered_map< codec_t, const char * > glsl_programs
Definition gl.cpp:329
#define BAR_GREY_COL
#define MAGIC_GL
Definition gl.cpp:85
static bool vdp_interop_supported()
Definition gl.cpp:1610
static void set_gamma(struct state_gl *s)
Definition gl.cpp:1599
static void gl_process_frames(struct state_gl *s)
Definition gl.cpp:1222
static int64_t translate_glfw_to_ug(int key, int mods)
Definition gl.cpp:1325
static void gl_show_help(bool full)
Definition gl.cpp:522
static void screenshot(struct video_frame *frame)
Definition gl.cpp:947
static const char * vert
Definition gl.cpp:302
static void * display_gl_parse_fmt(struct state_gl *s, char *ptr)
Definition gl.cpp:692
static void gl_load_splashscreen(struct state_gl *s)
Definition gl.cpp:589
#define SINGLE_BUF
Definition gl.cpp:94
#define LEFT_BAR_RIGHT
static void print_gamma_ramp(GLFWmonitor *monitor)
Definition gl.cpp:1582
static void gl_render_glsl(struct state_gl *s, char *data)
Definition gl.cpp:1960
static const char * v210_to_rgb_fp
Definition gl.cpp:182
static const char * uyvy_to_rgb_fp
Definition gl.cpp:141
static void display_gl_run(void *arg)
Definition gl.cpp:1799
#define MOD_NAME
Definition gl.cpp:86
static bool set_size(struct state_gl *s, const char *tok)
Definition gl.cpp:596
#define RIGHT_BAR_RIGHT
static const char * deinterlace_fp
Definition gl.cpp:127
static void glfw_mouse_callback(GLFWwindow *win, double x, double y)
Definition gl.cpp:1435
static void glfw_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods)
Definition gl.cpp:1412
static bool display_gl_putf(void *state, struct video_frame *frame, long long timeout)
Definition gl.cpp:2154
#define MAX_BUFFER_SIZE
Definition gl.cpp:91
static struct video_frame * display_gl_getf(void *state)
Definition gl.cpp:2133
static void gl_draw(double ratio, double bottom_offset, bool double_buf)
Definition gl.cpp:2012
static const char * yuva_to_rgb_fp
Definition gl.cpp:163
static void list_hints()
Definition gl.cpp:665
static const struct video_display_info display_gl_info
Definition gl.cpp:2198
static const map< string, int > hint_map
Definition gl.cpp:627
#define RIGHT_BAR_LEFT
static void draw_pause()
draw pause symbol (2 vertical bars) to the lefttop part of the frame
Definition gl.cpp:1184
#define FEATURE_PRESENT(x)
Definition gl.cpp:517
static int get_refresh_rate(enum state_gl::modeset_t modeset, GLFWmonitor *mon, double video_refresh_rate)
Definition gl.cpp:897
static constexpr codec_t gl_supp_codecs[]
Definition gl.cpp:461
static void * display_gl_init(struct module *parent, const char *fmt, unsigned int flags)
Definition gl.cpp:782
static void pop_frame(struct state_gl *s, unique_lock< mutex > &lk)
Definition gl.cpp:1175
#define BARS_TOP
static constexpr pair< int64_t, string_view > keybindings[]
Definition gl.cpp:337
static bool display_gl_check_gl_version()
Definition gl.cpp:1446
static void display_gl_done(void *state)
Definition gl.cpp:2108
GLuint glsl_compile_link(const char *vprogram, const char *fprogram)
Definition gl_context.c:147
#define gl_check_error()
Definition gl_context.h:82
#define PUTF_NONBLOCK
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define PUTF_DISCARD
#define PUTF_BLOCKING
#define VIDEO_DISPLAY_ABI_VERSION
struct video_frame * get_splashscreen()
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_SUPPORTED_IL_MODES
display supported interlacing modes - enum interlacing_t[]
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
void module_init_default(struct module *module_data)
Definition module.c:48
void module_register(struct module *module_data, struct module *parent)
Definition module.c:82
void module_done(struct module *module_data)
Definition module.c:93
struct module * get_root_module(struct module *node)
Definition module.c:203
@ MODULE_CLASS_DATA
Definition module.h:86
int count
Definition hd-rum.c:44
void handle_error(int status)
Definition host.cpp:1184
void exit_uv(int status)
Definition host.cpp:625
int glfw_init_count
0->1 - call glfwInit, 1->0 call glfwTerminate; glfw{Init,Terminate} should be called from main thr,...
Definition host.cpp:182
const char * get_commandline_param(const char *key)
Definition host.cpp:894
std::unordered_map< std::string, std::string > commandline_params
Definition host.cpp:184
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
#define INIT_NOERR
Definition host.h:73
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
volatile int log_level
Definition debug.cpp:65
bool keycontrol_register_key(struct module *receiver_mod, int64_t key, const char *message, const char *description)
Registers callback message for given key.
void get_keycode_name(int64_t ch, char *buf, size_t buflen)
void keycontrol_send_key(struct module *root, int64_t key)
#define K_CTRL_DOWN
#define K_LEFT
#define K_CTRL_UP
#define K_RIGHT
#define K_ALT(x)
#define MAX_KEYCODE_NAME_LEN
#define K_PGDOWN
#define K_DOWN
#define K_CTRL(x)
#define K_PGUP
#define K_UP
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define UG_ASSERT(cond)
unconditional alternative to assert that is not affected by NDEBUG macro
Definition macros.h:78
#define IF_NOT_NULL_ELSE(val, default_val)
Definition macros.h:51
#define OPTIMIZED_FOR
Definition macros.h:127
#define DEBUG
Definition main.c:27
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
struct message * check_message(struct module *mod)
#define RESPONSE_OK
Definition messaging.h:57
#define RESPONSE_BAD_REQUEST
Definition messaging.h:60
enum openssl_mode mode
#define min(a, b)
Definition rtp.c:99
struct msghdr msg
const char * parent
Definition settings.cpp:198
Option::OptType type
Definition settings.cpp:194
const char * name
Definition settings.cpp:193
void spout_sender_unregister(void *s)
void spout_sender_sendframe(void *s, int width, int height, unsigned int id)
void * spout_sender_register(const char *name)
char dev[1024]
Definition types.h:383
enum module_class cls
Definition module.h:115
GLFWwindow * window
Definition gl.cpp:386
int dxt_height
Definition gl.cpp:405
GLFWmonitor * monitor
Definition gl.cpp:385
show_cursor_t
Definition gl.cpp:410
@ SC_FALSE
Definition gl.cpp:410
@ SC_AUTOHIDE
Definition gl.cpp:410
@ SC_TRUE
Definition gl.cpp:410
enum state_gl::modeset_t modeset
positive vals force framerate
condition_variable frame_consumed_cv
Definition gl.cpp:399
queue< struct video_frame * > frame_queue
Definition gl.cpp:393
GLuint PHandle_deint
Definition gl.cpp:373
GLuint pbo_id
Definition gl.cpp:380
void * syphon_spout
Definition gl.cpp:418
bool fixed_size
Definition gl.cpp:420
mutex lock
Definition gl.cpp:397
GLuint current_program
Definition gl.cpp:374
int vsync
Definition gl.cpp:407
bool fs
Definition gl.cpp:388
string syphon_spout_srv_name
Definition gl.cpp:412
int fixed_h
Definition gl.cpp:422
bool vdp_interop
Definition gl.cpp:436
vector< char > scratchpad
scratchpad sized WxHx8
Definition gl.cpp:437
modeset_t
Definition gl.cpp:426
@ NOMODESET
Definition gl.cpp:426
@ MODESET
Definition gl.cpp:426
@ MODESET_SIZE_ONLY
Definition gl.cpp:426
bool noresizable
Definition gl.cpp:408
double window_size_factor
Definition gl.cpp:414
steady_clock::time_point cursor_shown_from
indicates time point from which is cursor show if show_cursor == SC_AUTOHIDE, timepoint() means curso...
Definition gl.cpp:411
int pos_y
Definition gl.cpp:424
int fixed_w
Definition gl.cpp:421
double aspect
Definition gl.cpp:401
~state_gl()
Definition gl.cpp:445
GLuint texture_raw
Definition gl.cpp:379
enum state_gl::deint deinterlace
enum state_gl::show_cursor_t show_cursor
int use_pbo
Definition gl.cpp:431
double gamma
Definition gl.cpp:403
GLuint texture_display
Definition gl.cpp:378
state_gl(struct module *parent)
Definition gl.cpp:439
static const char * deint_to_string(state_gl::deint val)
Definition gl.cpp:451
int pos_x
Definition gl.cpp:423
uint32_t magic
Definition gl.cpp:383
condition_variable new_frame_ready_cv
Definition gl.cpp:398
unordered_map< codec_t, GLuint > PHandles
Definition gl.cpp:372
struct video_desc current_desc
Definition gl.cpp:395
queue< struct video_frame * > free_frame_queue
Definition gl.cpp:394
GLuint fbo_id
Definition gl.cpp:377
volatile bool paused
Definition gl.cpp:409
double video_aspect
Definition gl.cpp:402
map< int, int > init_hints
Definition gl.cpp:427
map< int, int > window_hints
Definition gl.cpp:428
int req_monitor_idx
Definition gl.cpp:432
struct module mod
Definition gl.cpp:416
struct video_frame * current_frame
Definition gl.cpp:391
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int height
Definition types.h:281
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
static const char * vprogram
Definition swmix.c:468
void syphon_server_publish(void *s, int width, int height, unsigned int id)
void * syphon_server_register(void *context, const char *name)
void syphon_server_unregister(void *s)
@ DEPTH8
Definition types.h:126
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ SEGMENTED_FRAME
Segmented frame. Contains the same data as progressive frame.
Definition types.h:168
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ DXT1
S3 Texture Compression DXT1.
Definition types.h:77
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
Definition types.h:73
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ HW_VDPAU
VDPAU hardware surface.
Definition types.h:91
@ RG48
16-bit RGB little-endian (RlRhGl...)
Definition types.h:95
@ Y416
interleaved little-endian YCbCr 4444 16-bit - UYVA
Definition types.h:99
@ DXT5
S3 Texture Compression DXT5.
Definition types.h:79
@ DXT1_YUV
Structure same as DXT1, instead of RGB, YCbCr values are stored.
Definition types.h:78
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75
This is an umbrella header for video functions.
int get_bits_per_component(codec_t codec)
const char * description
Definition cineform.cpp:126
const char * key
Definition cineform.cpp:125
void vf_clear(struct video_frame *f)
void vf_recycle(struct video_frame *buf)
This function should be called before returning a frame back to a frame pool. Currently this is used ...
bool save_video_frame_as_pnm(struct video_frame *frame, const char *name)
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.