5 GLFilter::GLFilter(
const std::string _kernel_name,
6 const std::string _vertex_shader,
7 const std::string _fragment_shader,
8 const std::vector<binding*> uniforms,
9 const std::vector<binding*> uniform_blocks,
10 const std::vector<binding*> attributes,
11 const replacements_t& _replacements)
12 : kernel_name(_kernel_name),
14 uniform_blocks_(uniform_blocks),
15 attributes_(attributes) {
17 if (createProgram(_vertex_shader.c_str(),
18 process_replacements(_fragment_shader, _replacements).c_str(),
20 gl_log(GL_VERBOSE,
"created program %d\n", program);
25 [&](std::stringstream& errmsg) { errmsg <<
"Problem initializing OpenGL program"; });
29 const char* shader_utils = R
"GLSL( 30 #define unpackHalf4x16(pd) vec4(unpackHalf2x16(pd.x), unpackHalf2x16(pd.y)) 31 #define packHalf4x16(pd) uvec2(packHalf2x16(pd.xy), packHalf2x16(pd.zw)) 34 const char* half_float_texture_utils = R
"GLSL( 35 precision mediump sampler2D; 37 #define TEXTURE_OUTPUT(_loc, _var) \ 38 layout(location = _loc) out mediump vec4 _var 39 #define TEXTURE_INPUT(_var) \ 40 uniform sampler2D _var 41 #define TEXTURE_LOAD(_input, _coord) \ 42 texelFetch((_input), (_coord), 0) 43 #define TEXTURE_STORE(_val) \ 47 const char* half_float_compat_texture_utils = R
"GLSL( 48 precision highp usampler2D; 50 #define TEXTURE_OUTPUT(_loc, _var) \ 51 layout(location = _loc) out highp uvec2 _var 52 #define TEXTURE_INPUT(_var) \ 53 uniform usampler2D _var 54 #define TEXTURE_LOAD(_input, _coord) \ 55 unpackHalf4x16(texelFetch((_input), (_coord), 0).xy) 56 #define TEXTURE_STORE(_val) \ 57 (uvec2(packHalf4x16((_val)))) 60 std::string GLFilter::process_replacements(std::string shader, 61 const replacements_t& replacements)
const {
62 for (
auto&& replacement : replacements) {
63 std::string tag =
"$(" + replacement.first +
")";
64 std::string value = replacement.second;
66 size_t position = shader.find(tag);
67 if (position != std::string::npos) {
68 shader.replace(position, tag.size(), value);
71 [&](std::stringstream& errmsg) { errmsg <<
"Couldn't find replacement tag: " << tag; });
76 std::string version_tag =
"#version 300 es";
77 if (GLContext::getGLContext()->halfFloatTextureSupported()) {
78 shader.insert(shader.find(version_tag) + version_tag.size(), half_float_texture_utils);
80 shader.insert(shader.find(version_tag) + version_tag.size(), half_float_compat_texture_utils);
82 shader.insert(shader.find(version_tag) + version_tag.size(), shader_utils);
87 void GLFilter::attach_uniform_buffer(
const binding* block,
89 std::function<
void(T*,
size_t)> loader) {
90 if (block->location >= 0) {
91 if (bindingPoint < kMaxUniformBlocks) {
92 if (uniformBlock[bindingPoint] == 0) {
94 glUniformBlockBinding(program, block->location, bindingPoint);
97 glGetActiveUniformBlockiv(program, block->location, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize[bindingPoint]);
100 glGenBuffers(1, &uniformBlock[bindingPoint]);
102 gl_log(GL_VERBOSE,
"created uniform buffer block %d\n", uniformBlock[bindingPoint]);
106 glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock[bindingPoint]);
107 glBufferData(GL_UNIFORM_BUFFER, blockSize[bindingPoint], NULL, GL_DYNAMIC_DRAW);
109 checkGLError([&](std::stringstream& errmsg) {
110 errmsg <<
"Unable to bind uniform buffer " << block->name <<
":" << block->location
111 <<
" at binding point " << bindingPoint;
114 T* blockData = (T*)glMapBufferRange(
115 GL_UNIFORM_BUFFER, 0, blockSize[bindingPoint], GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
116 if (blockData != NULL) {
119 loader(blockData, blockSize[bindingPoint]);
122 if (glUnmapBuffer(GL_UNIFORM_BUFFER) == GL_TRUE) {
124 glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, uniformBlock[bindingPoint]);
126 throwRuntimeError([&](std::stringstream& errmsg) { errmsg <<
"Error unmapping element buffer object"; });
129 throwRuntimeError([&](std::stringstream& errmsg) {
130 errmsg <<
"Error mapping element buffer object, blockSize: " << blockSize;
134 glBindBuffer(GL_UNIFORM_BUFFER, 0);
136 throwRuntimeError([&](std::stringstream& errmsg) {
137 errmsg <<
"Uniform block binding point out of range: " << bindingPoint <<
", should be < " 138 << kMaxUniformBlocks;
142 throwRuntimeError([&](std::stringstream& errmsg) { errmsg <<
"unbound uniform block"; });
146 template void GLFilter::attach_uniform_buffer<float16_t>(
const binding* block,
148 std::function<void(float16_t*, size_t)> loader);
150 static const GLenum unused_capability[] = {GL_CULL_FACE,
156 GL_POLYGON_OFFSET_FILL,
157 GL_SAMPLE_ALPHA_TO_COVERAGE,
160 void GLFilter::run(
const std::vector<texture_attachment>& input,
161 const std::vector<const GLTexture*>& output,
162 std::function<
void(
void)> uniforms_initializer,
165 const int first_texture_id = GL_TEXTURE0;
167 GLint defaultFramebuffer = 0;
168 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFramebuffer);
171 "GLFilter::run %s - inputs: %d, outputs: %d, width: %d, height: %d\n",
178 if (output.size() > 4) {
179 throwRuntimeError([&](std::stringstream& errmsg) {
180 errmsg <<
"Too many output textures: " << output.size() <<
", should be <= 4";
184 if (frameBuffer == 0) {
186 glGenFramebuffers(1, &frameBuffer);
187 gl_log(GL_VERBOSE,
"created frame buffer %d\n", frameBuffer);
190 glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
191 checkGLError([&](std::stringstream& errmsg) { errmsg <<
"glBindFramebuffer"; });
194 for (
int i = 0; i < output.size(); i++) {
195 GLenum target = output[i]->target();
196 GLuint texture = output[i]->name();
198 glBindTexture(target, texture);
199 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target, texture, 0);
201 checkGLError([&](std::stringstream& errmsg) {
202 errmsg <<
"Unable to connect output texture " << texture <<
" at color attachment " << i;
205 gl_log(GL_VERBOSE,
"connected output texture %d to color attachment %d\n", texture, i);
209 if (!frame_buffer_initialized) {
210 const int attachments_number = output.size();
211 const GLenum attachments[4] = {
212 GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
214 glDrawBuffers(attachments_number, attachments);
216 int fbs = glCheckFramebufferStatus(GL_FRAMEBUFFER);
218 if (fbs != GL_FRAMEBUFFER_COMPLETE) {
220 [&](std::stringstream& errmsg) { errmsg <<
"Frame buffer incomplete: " << fbs; });
223 frame_buffer_initialized =
true;
226 glUseProgram(program);
227 checkGLError([&](std::stringstream& errmsg) { errmsg <<
"glUseProgram"; });
230 GLenum texture_idx = first_texture_id;
231 for (
int i = 0; i < input.size(); i++, texture_idx++) {
232 if (input[i].uniform->location >= 0) {
233 GLenum target = input[i].texture->target();
234 GLuint texture = input[i].texture->name();
236 glActiveTexture(texture_idx);
237 glBindTexture(target, texture);
238 glUniform1i(input[i].uniform->location, texture_idx - GL_TEXTURE0);
240 checkGLError([&](std::stringstream& errmsg) {
241 errmsg <<
": Unable to attach input texture " << texture <<
" to uniform " 242 << input[i].uniform->name <<
":" << input[i].uniform->location <<
" at index " 243 << texture_idx - GL_TEXTURE0;
247 "connected input texture %d to texture unit %d\n",
249 texture_idx - GL_TEXTURE0);
251 gl_log(GL_VERBOSE,
"something wrong happened when i = %d\n", i);
256 if (uniforms_initializer) {
257 uniforms_initializer();
259 checkGLError([&](std::stringstream& errmsg) {
260 errmsg <<
"errors in the uniforms initializer callback";
265 if (check_opengl_errors && !validateProgram(program)) {
267 [&](std::stringstream& errmsg) { errmsg <<
"Couldn't validate OpenGL program"; });
270 glViewport(0, 0, width, height);
273 for (
int i = 0; i <
sizeof(unused_capability) /
sizeof(GLenum); i++) {
274 glDisable(unused_capability[i]);
276 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
280 const bool useDrawArrays =
true;
283 enum { ATTRIB_VERTEX, ATTRIB_TEXTUREPOSITON, NUM_ATTRIBUTES };
285 static const GLfloat squareVertices[] = {
296 static const float textureVertices[] = {
307 glBindBuffer(GL_ARRAY_BUFFER, 0);
308 glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
309 glEnableVertexAttribArray(ATTRIB_VERTEX);
311 [&](std::stringstream& errmsg) { errmsg <<
"glEnableVertexAttribArray(ATTRIB_VERTEX)"; });
313 glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
314 glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
315 checkGLError([&](std::stringstream& errmsg) {
316 errmsg <<
"glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON)";
319 gl_log(GL_VERBOSE,
"Calling glDrawArrays\n");
320 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
322 checkGLError([&](std::stringstream& errmsg) { errmsg <<
"glDrawArrays"; });
325 static const GLfloat vVertices[] = {
335 static const GLushort indices[] = {0, 1, 2, 0, 2, 3};
338 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 *
sizeof(GLfloat), vVertices);
340 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 *
sizeof(GLfloat), &vVertices[3]);
342 glEnableVertexAttribArray(0);
343 glEnableVertexAttribArray(1);
345 gl_log(GL_VERBOSE,
"Calling glDrawElements\n");
346 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
348 checkGLError([&](std::stringstream& errmsg) { errmsg <<
"glDrawElements"; });
356 for (
int i = texture_idx - 1; i >= first_texture_id; i--) {
357 gl_log(GL_VERBOSE,
"unbinding texture unit %d\n", i - GL_TEXTURE0);
359 glBindTexture(GL_TEXTURE_2D, 0);
361 checkGLError([&](std::stringstream& errmsg) {
362 errmsg <<
"Error unbinding texture unit " << i - GL_TEXTURE0;
366 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
369 void GLFilter::releaseBuffers() {
370 for (
int i = 0; i < kMaxUniformBlocks; i++) {
371 if (uniformBlock[i]) {
372 gl_log(GL_VERBOSE,
"deleting uniform buffer block %d\n", uniformBlock[i]);
373 glDeleteBuffers(1, &uniformBlock[i]);
378 gl_log(GL_VERBOSE,
"deleting frame buffer %d\n", frameBuffer);
379 glDeleteFramebuffers(1, &frameBuffer);
384 void GLFilter::deleteProgram() {
386 gl_log(GL_VERBOSE,
"deleting program %d\n", program);
387 glDeleteProgram(program);
392 void GLFilter::deleteBindings() {
393 for (binding* uniform : uniforms_) {
396 for (binding* uniform_block : uniform_blocks_) {
397 delete uniform_block;
399 for (binding* attribute : attributes_) {
405 const char* GLFilter::vertex_shader = R
"GLSL(#version 300 es 407 layout(location = 0) in vec4 a_position; 408 layout(location = 1) in vec2 a_texCoord; 413 gl_Position = a_position; 414 v_texCoord = a_texCoord; 418 bool GLFilter::createProgram(
const GLchar* vertSource,
419 const GLchar* fragSource,
420 GLuint* program)
const {
421 GLuint vertShader = 0, fragShader = 0, prog = 0, status = 1;
428 prog = glCreateProgram();
431 status *= compileShader(GL_VERTEX_SHADER, 1, &vertSource, &vertShader);
434 status *= compileShader(GL_FRAGMENT_SHADER, 1, &fragSource, &fragShader);
437 glAttachShader(prog, vertShader);
440 glAttachShader(prog, fragShader);
444 for (
auto&& attribute : attributes_) {
445 glBindAttribLocation(prog, attribute->location, attribute->name.c_str());
447 checkGLError([&](std::stringstream& errmsg) {
448 errmsg <<
"Couldn't bind attribute: " << attribute->name <<
" at location " 449 << attribute->location;
454 status *= linkProgram(prog);
458 for (
auto&& uniform : uniforms_) {
459 uniform->location = glGetUniformLocation(prog, uniform->name.c_str());
461 checkGLError([&](std::stringstream& errmsg) {
462 errmsg <<
"Couldn't resolve uniform: " << uniform->name;
466 for (
auto&& uniform_block : uniform_blocks_) {
467 uniform_block->location = glGetUniformBlockIndex(prog, uniform_block->name.c_str());
469 "Getting location for uniform block: %s, location: %d\n",
470 uniform_block->name.c_str(),
471 uniform_block->location);
473 checkGLError([&](std::stringstream& errmsg) {
474 errmsg <<
"Couldn't resolve uniform block: " << uniform_block->name;
483 glDetachShader(prog, vertShader);
484 glDeleteShader(vertShader);
487 glDetachShader(prog, fragShader);
488 glDeleteShader(fragShader);
497 GLint GLFilter::compileShader(GLenum target,
499 const GLchar** sources,
500 GLuint* shader)
const {
503 *shader = glCreateShader(target);
504 glShaderSource(*shader, count, sources, NULL);
505 glCompileShader(*shader);
508 glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
510 std::vector<GLchar> log(logLength);
511 glGetShaderInfoLog(*shader, logLength, &logLength, &log[0]);
512 gl_log(GL_ERR,
"Shader compile log:\n%s", &log[0]);
515 glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
519 gl_log(GL_ERR,
"Failed to compile shader:\n");
520 for (i = 0; i < count; i++)
521 gl_log(GL_ERR,
"%s", sources[i]);
528 GLint GLFilter::linkProgram(GLuint program)
const {
531 glLinkProgram(program);
534 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
536 std::vector<GLchar> log(logLength);
537 glGetProgramInfoLog(program, logLength, &logLength, &log[0]);
538 gl_log(GL_ERR,
"Program link log:\n%s", &log[0]);
541 glGetProgramiv(program, GL_LINK_STATUS, &status);
543 gl_log(GL_ERR,
"Failed to link program %d\n", program);
549 GLint GLFilter::validateProgram(GLuint program)
const {
552 glValidateProgram(program);
555 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
557 std::vector<GLchar> log(logLength);
558 glGetProgramInfoLog(program, logLength, &logLength, &log[0]);
559 gl_log(GL_ERR,
"Program validate log:\n%s", &log[0]);
562 glGetProgramiv(program, GL_VALIDATE_STATUS, &status);
564 gl_log(GL_ERR,
"Failed to validate program %d\n", program);