UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
opengl_utils.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2010-2023 CESNET, z. s. p. o.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38#include <vector>
39#include <string>
40#include <stdexcept>
41#include <cassert>
42#include "opengl_utils.hpp"
44#include "video_frame.h"
45#include "video_codec.h"
46#include "color.h"
47#include "debug.h"
48#include "host.h"
49
51
52#define MOD_NAME "[Opengl utils] "
53
54static const float PI_F=3.14159265358979f;
55
56static const GLfloat rectangle[] = {
57 1.0f, 1.0f, 1.0f, 1.0f,
58 -1.0f, 1.0f, 0.0f, 1.0f,
59 -1.0f, -1.0f, 0.0f, 0.0f,
60
61 1.0f, 1.0f, 1.0f, 1.0f,
62 -1.0f, -1.0f, 0.0f, 0.0f,
63 1.0f, -1.0f, 1.0f, 0.0f
64};
65
66static unsigned char pixels[] = {
67 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
68 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
69 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
70 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255
71};
72
73static void compileShader(GLuint shaderId){
74 glCompileShader(shaderId);
75
76 GLint ret = GL_FALSE;
77 int len;
78
79 glGetShaderiv(shaderId, GL_COMPILE_STATUS, &ret);
80 glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &len);
81 if (len > 0){
82 std::vector<char> errorMsg(len+1);
83 glGetShaderInfoLog(shaderId, len, NULL, &errorMsg[0]);
84 printf("%s\n", errorMsg.data());
85 }
86}
87
88static std::vector<float> gen_sphere_vertices(int r, int latitude_n, int longtitude_n);
89static std::vector<unsigned> gen_sphere_indices(int latitude_n, int longtitude_n);
90
91bool check_gl_extension_present(const char *ext){
92 int count = 0;
93 glGetIntegerv(GL_NUM_EXTENSIONS, &count);
94
95 for(int i = 0; i < count; i++){
96 const char *item = (const char *) glGetStringi(GL_EXTENSIONS, i);
97 if(strcmp(item, ext) == 0)
98 return true;
99 }
100
101 return false;
102}
103
104GlProgram::GlProgram(const char *vert_src, const char *frag_src){
105 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
106 GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
107
108 glShaderSource(vertexShader, 1, &vert_src, NULL);
109 compileShader(vertexShader);
110 glShaderSource(fragShader, 1, &frag_src, NULL);
111 compileShader(fragShader);
112
113 program = glCreateProgram();
114 glAttachShader(program, vertexShader);
115 glAttachShader(program, fragShader);
116 glLinkProgram(program);
117 //glUseProgram(program);
118
119 glDetachShader(program, vertexShader);
120 glDetachShader(program, fragShader);
121 glDeleteShader(vertexShader);
122 glDeleteShader(fragShader);
123}
124
126 if(program == 0)
127 return;
128
129 glUseProgram(0);
130 glDeleteProgram(program);
131}
132
134 if(vao == 0)
135 return;
136
137 glDeleteBuffers(1, &vbo);
138 glDeleteBuffers(1, &elem_buf);
139 glDeleteVertexArrays(1, &vao);
140}
141
144
145 glBindVertexArray(vao);
146 if(elem_buf != 0){
147 glDrawElements(GL_TRIANGLES, indices_num, GL_UNSIGNED_INT, (void *) 0);
148 } else {
149 glDrawArrays(GL_TRIANGLES, 0, indices_num);
150 }
151
152 glBindVertexArray(0);
153}
154
156 Model model;
157 glGenVertexArrays(1, &model.vao);
158 glBindVertexArray(model.vao);
159
160 auto vertices = gen_sphere_vertices(1, 64, 64);
161 auto indices = gen_sphere_indices(64, 64);
162
163 glGenBuffers(1, &model.vbo);
164 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
165 glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
166
167 glGenBuffers(1, &model.elem_buf);
168 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.elem_buf);
169 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned), indices.data(), GL_STATIC_DRAW);
170
171 glEnableVertexAttribArray(0);
172 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
173 glVertexAttribPointer(
174 0,
175 3,
176 GL_FLOAT,
177 GL_FALSE,
178 5 * sizeof(float),
179 (void*)0
180 );
181 glEnableVertexAttribArray(1);
182 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
183 glVertexAttribPointer(
184 1,
185 2,
186 GL_FLOAT,
187 GL_FALSE,
188 5 * sizeof(float),
189 (void*)(3 * sizeof(float))
190 );
191
192 glBindVertexArray(0);
193 model.indices_num = indices.size();
194
195 return model;
196}
197
199 Model model;
200 glGenVertexArrays(1, &model.vao);
201 glBindVertexArray(model.vao);
202
203 glGenBuffers(1, &model.vbo);
204 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
205 glBufferData(GL_ARRAY_BUFFER, sizeof(rectangle), rectangle, GL_STATIC_DRAW);
206
207 glEnableVertexAttribArray(0);
208 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
209 glVertexAttribPointer(
210 0,
211 2,
212 GL_FLOAT,
213 GL_FALSE,
214 4 * sizeof(float),
215 (void*)0
216 );
217 glEnableVertexAttribArray(1);
218 glBindBuffer(GL_ARRAY_BUFFER, model.vbo);
219 glVertexAttribPointer(
220 1,
221 2,
222 GL_FLOAT,
223 GL_FALSE,
224 4 * sizeof(float),
225 (void*)(2 * sizeof(float))
226 );
227
228 glBindVertexArray(0);
229 model.indices_num = 6;
230
231 return model;
232}
233
235 if(tex_id == 0)
236 return;
237
238 glDeleteTextures(1, &tex_id);
239 glDeleteBuffers(1, &pbo);
240}
241
242void Texture::allocate(int w, int h, GLint internal_fmt) {
243 allocate();
244 if(w != width || h != height || internal_fmt != internal_format){
245 width = w;
246 height = h;
247 internal_format = internal_fmt;
248
249 glBindTexture(GL_TEXTURE_2D, tex_id);
250 /* Only the internalformat parameter is relevant here, the
251 * format and type parameters refer to the source data and we
252 * are passing nullptr here
253 */
254 glTexImage2D(GL_TEXTURE_2D, 0, internal_format,
255 width, height, 0,
256 GL_RGBA, GL_UNSIGNED_BYTE,
257 nullptr);
258 }
259}
260
261void Texture::load_frame(int w, int h,
262 GLint internal_format,
263 GLenum src_format,
264 GLenum type,
265 video_frame *f,
266 bool pbo_frame)
267{
268 char *src_data = f->tiles[0].data;
270 glBindTexture(GL_TEXTURE_2D, tex_id);
271
272 if(pbo_frame){
273 GlBuffer *pbo = static_cast<GlBuffer *>(f->callbacks.dispose_udata);
274
275 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo->get());
276 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
277 f->tiles[0].data = nullptr;
278 src_data = nullptr;
279 }
280
281 glTexImage2D(GL_TEXTURE_2D, 0, internal_format,
282 w, h, 0,
283 src_format, type,
284 src_data);
285
286 if(pbo_frame){
287 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
288 }
289}
290
292 if(tex_id != 0)
293 return;
294
295 glGenTextures(1, &tex_id);
296 glBindTexture(GL_TEXTURE_2D, tex_id);
297 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
302 glGenBuffers(1, &pbo);
303}
304
305
307 glBindTexture(GL_TEXTURE_2D, tex);
308 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
310 glBindTexture(GL_TEXTURE_2D, 0);
311
312
313 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
314 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
315 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
316 printf("Error %d\n", glGetError());
317 }
318 glBindFramebuffer(GL_FRAMEBUFFER, 0);
319}
320
321void FrameUploader::put_frame(video_frame *f, bool pbo_frame){
322 assert(tex);
323 tex->allocate(f->tiles[0].width, f->tiles[0].height, GL_RGB);
324 glBindTexture(GL_TEXTURE_2D, tex->get());
325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
327 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
328 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
329
330 if(!conv || f->color_spec != configured_codec){
332 conv->set_pbo(upload_pbo.get());
334 }
335 assert(conv && "Frame uploader for codec not A;");
336 conv->attach_texture(*tex);
337 conv->put_frame(f, pbo_frame);
338}
339
341 std::vector<codec_t> ret;
342
343 ret.push_back(RGB);
344 ret.push_back(RGBA);
345
347 ret.insert(ret.end(), cvt.begin(), cvt.end());
348
349 return ret;
350}
351
353 const char *vert_src = R"END(
354#version 330 core
355layout(location = 0) in vec2 vert_pos;
356layout(location = 1) in vec2 vert_uv;
357
358uniform vec2 scale_vec;
359
360out vec2 UV;
361
362void main(){
363 gl_Position = vec4(vert_pos * scale_vec, 0.0f, 1.0f);
364 UV = vert_uv;
365}
366)END";
367
368 const char *frag_src = R"END(
369#version 330 core
370in vec2 UV;
371out vec3 color;
372uniform sampler2D tex;
373uniform bool deinterlace = false;
374void main(){
375 color = texture(tex, UV).rgb;
376
377 if(deinterlace){
378 float lineOff = 1.0f / textureSize(tex, 0).y;
379 vec3 pix_down = texture(tex, vec2(UV.x, UV.y + lineOff)).rgb;
380 color = (color + pix_down) / 2.0f;
381 }
382}
383)END";
384
389 uploader.enable_pbo(false);
390}
391
393 auto frame_desc = video_desc_from_frame(f);
394 if(!video_desc_eq(frame_desc, current_desc)){
395 current_desc = frame_desc;
397 }
398
399 uploader.put_frame(f, false);
400}
401
403 glUseProgram(program.get());
404
405 glViewport(0, 0, screen_width, screen_height);
406 glBindTexture(GL_TEXTURE_2D, texture.get());
407 quad.render();
408}
409
410void FlatVideoScene::resize(int width, int height){
411 screen_width = width;
412 screen_height = height;
413
414 double x = 1.0;
415 double y = -1.0;
416
417 double screen_aspect = (double) width / height;
418 double video_aspect = (double) current_desc.width / current_desc.height;
419 if(screen_aspect > video_aspect) {
420 x = (double) height * video_aspect / width;
421 } else {
422 y = (double) -width / (height * video_aspect);
423 }
424
425 glUseProgram(program.get());
426
427 GLuint pvLoc;
428 pvLoc = glGetUniformLocation(program.get(), "scale_vec");
429 glUniform2f(pvLoc, x, y);
430}
431
433 glUseProgram(program.get());
434
435 GLuint pvLoc;
436 pvLoc = glGetUniformLocation(program.get(), "deinterlace");
437 glUniform1i(pvLoc, enable);
438}
439
440static std::vector<float> gen_sphere_vertices(int r, int latitude_n, int longtitude_n){
441 std::vector<float> verts;
442
443 float lat_step = PI_F / latitude_n;
444 float long_step = 2 * PI_F / longtitude_n;
445
446 for(int i = 0; i < latitude_n + 1; i++){
447 float y = std::cos(i * lat_step) * r;
448 float y_slice_r = std::sin(i * lat_step) * r;
449
450 //The first and last vertex on the y slice circle are in the same place
451 for(int j = 0; j < longtitude_n + 1; j++){
452 float x = std::sin(j * long_step) * y_slice_r;
453 float z = std::cos(j * long_step) * y_slice_r;
454 verts.push_back(x);
455 verts.push_back(y);
456 verts.push_back(z);
457
458 float u = 1 - static_cast<float>(j) / longtitude_n;
459 float v = static_cast<float>(i) / latitude_n;
460 verts.push_back(u);
461 verts.push_back(v);
462 }
463 }
464
465 return verts;
466}
467
468//Generate indices for sphere
469//Faces facing inwards have counter-clockwise vertex order
470static std::vector<unsigned> gen_sphere_indices(int latitude_n, int longtitude_n){
471 std::vector<unsigned int> indices;
472
473 for(int i = 0; i < latitude_n; i++){
474 int slice_idx = i * (latitude_n + 1);
475 int next_slice_idx = (i + 1) * (latitude_n + 1);
476
477 for(int j = 0; j < longtitude_n; j++){
478 //Since the top and bottom slices are circles with radius 0,
479 //we only need one triangle for those
480 if(i != latitude_n - 1){
481 indices.push_back(slice_idx + j + 1);
482 indices.push_back(next_slice_idx + j);
483 indices.push_back(next_slice_idx + j + 1);
484 }
485
486 if(i != 0){
487 indices.push_back(slice_idx + j + 1);
488 indices.push_back(slice_idx + j);
489 indices.push_back(next_slice_idx + j);
490 }
491 }
492 }
493
494 return indices;
495}
496
int void size_t * len
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
FrameUploader uploader
void resize(int width, int height)
void enableDeinterlacing(bool enable)
video_desc current_desc
void put_frame(video_frame *f)
std::unique_ptr< GlBuffer > upload_pbo
std::vector< codec_t > get_supported_codecs()
void enable_pbo(bool enable)
void put_frame(video_frame *f, bool pbo_frame=false)
std::unique_ptr< Frame_convertor > conv
void attach_dst_texture(Texture *tex)
codec_t configured_codec
GlProgram()=default
GLuint program
GLuint get()
GLuint vao
static Model get_quad()
GLsizei indices_num
void render()
GLuint vbo
static Model get_sphere()
GLuint elem_buf
GLuint tex_id
GLint internal_format
GLuint get() const
void allocate()
void load_frame(int w, int h, GLint internal_format, GLenum src_format, GLenum type, video_frame *f, bool pbo_frame)
void allocate(int w, int h, GLint internal_fmt)
int count
Definition hd-rum.c:44
std::vector< codec_t > get_convertor_supported_codecs()
std::unique_ptr< Frame_convertor > get_convertor_for_codec(codec_t codec)
bool check_gl_extension_present(const char *ext)
static const GLfloat rectangle[]
static void compileShader(GLuint shaderId)
static std::vector< unsigned > gen_sphere_indices(int latitude_n, int longtitude_n)
static unsigned char pixels[]
static std::vector< float > gen_sphere_vertices(int r, int latitude_n, int longtitude_n)
static const float PI_F
static const GLfloat rectangle[]
static const char * vert_src
static void compileShader(GLuint shaderId, QOpenGLFunctions_3_3_Core *f)
static unsigned char pixels[]
static const char * frag_src
#define PROFILE_FUNC
Option::OptType type
Definition settings.cpp:194
void attach_texture(GLuint tex)
Definition hd-rum.c:27
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
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
struct video_frame_callbacks callbacks
Definition types.h:324
codec_t color_spec
Definition types.h:306
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.