summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chroma.glsl41
-rw-r--r--compute.glsl (renamed from vertex.glsl)18
-rw-r--r--gpu.c165
-rw-r--r--luma.glsl33
-rw-r--r--makefile4
5 files changed, 52 insertions, 209 deletions
diff --git a/chroma.glsl b/chroma.glsl
deleted file mode 100644
index dd103b1..0000000
--- a/chroma.glsl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2023 Mikhail Burakov. This file is part of streamer.
- *
- * streamer is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * streamer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with streamer. If not, see <https://www.gnu.org/licenses/>.
- */
-
-uniform sampler2D img_input;
-uniform mediump vec2 sample_offsets[4];
-uniform mediump mat3 colorspace;
-uniform mediump vec3 ranges[2];
-
-varying mediump vec2 texcoord;
-
-mediump vec4 supersample() {
- return texture2D(img_input, texcoord + sample_offsets[0]) +
- texture2D(img_input, texcoord + sample_offsets[1]) +
- texture2D(img_input, texcoord + sample_offsets[2]) +
- texture2D(img_input, texcoord + sample_offsets[3]);
-}
-
-mediump vec3 rgb2yuv(in mediump vec3 rgb) {
- mediump vec3 yuv = colorspace * rgb.rgb + vec3(0.0, 0.5, 0.5);
- return ranges[0] + yuv * ranges[1];
-}
-
-void main() {
- mediump vec4 rgb = supersample() / 4.0;
- mediump vec3 yuv = rgb2yuv(rgb.rgb);
- gl_FragColor = vec4(yuv.yz, 0.0, 1.0);
-}
diff --git a/vertex.glsl b/compute.glsl
index 29c4f4a..2250626 100644
--- a/vertex.glsl
+++ b/compute.glsl
@@ -15,14 +15,16 @@
* along with streamer. If not, see <https://www.gnu.org/licenses/>.
*/
-attribute vec2 position;
+#version 310 es
-varying vec2 texcoord;
+precision mediump image2D;
-void main() {
- texcoord = position;
- mat4 transform_matrix =
- mat4(vec4(2.0, 0.0, 0.0, 0.0), vec4(0.0, 2.0, 0.0, 0.0),
- vec4(0.0, 0.0, 2.0, 0.0), vec4(-1.0, -1.0, 0.0, 1.0));
- gl_Position = transform_matrix * vec4(position, 0.0, 1.0);
+layout(local_size_x = 2, local_size_y = 2) in;
+layout(rgba8, binding = 0) uniform restrict readonly image2D img_rgba;
+layout(rgba8, binding = 1) uniform restrict writeonly image2D img_luma;
+layout(rgba8, binding = 2) uniform restrict writeonly image2D img_chroma;
+layout(location = 0) uniform mat3 colorspace;
+layout(location = 1) uniform vec2 ranges[2];
+
+void main(void) {
}
diff --git a/gpu.c b/gpu.c
index 71ef625..da746c6 100644
--- a/gpu.c
+++ b/gpu.c
@@ -45,19 +45,8 @@
return NULL; \
}
-// TODO(mburakov): It should be theoretically possible to do everything in a
-// single pass using a compute shader and GLES3. Unfortunately my test machine
-// reports the primary framebuffer as multiplane. This is probably the reason
-// why texture created from it can not be sampled using imageLoad in a compute
-// shader even though it's still RGB. Fallback to GLES2 and per-plane textures
-// for now, and figure out details later.
-
-extern const char _binary_vertex_glsl_start[];
-extern const char _binary_vertex_glsl_end[];
-extern const char _binary_luma_glsl_start[];
-extern const char _binary_luma_glsl_end[];
-extern const char _binary_chroma_glsl_start[];
-extern const char _binary_chroma_glsl_end[];
+extern const char _binary_compute_glsl_start[];
+extern const char _binary_compute_glsl_end[];
struct GpuContext {
#ifndef USE_EGL_MESA_PLATFORM_SURFACELESS
@@ -69,11 +58,8 @@ struct GpuContext {
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
- GLuint program_luma;
- GLuint program_chroma;
+ GLuint compute_program;
GLint sample_offsets;
- GLuint framebuffer;
- GLuint vertices;
};
struct GpuFrame {
@@ -149,47 +135,33 @@ static bool HasExtension(const char* haystack, const char* needle) {
return result;
}
-static GLuint CreateGlProgram(const char* vs_begin, const char* vs_end,
- const char* fs_begin, const char* fs_end) {
+static GLuint CreateGlProgram(const char* begin, const char* end) {
GLuint program = 0;
- GLuint vertex = glCreateShader(GL_VERTEX_SHADER);
- if (!vertex) {
- LOG("Failed to create vertex shader (%s)", GlErrorString(glGetError()));
+ GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
+ if (!shader) {
+ LOG("Failed to create compute shader (%s)", GlErrorString(glGetError()));
goto bail_out;
}
- GLsizei size = (GLsizei)(vs_end - vs_begin);
- glShaderSource(vertex, 1, &vs_begin, &size);
- glCompileShader(vertex);
- if (!CheckBuildableShader(vertex)) goto delete_vs;
-
- GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER);
- if (!fragment) {
- LOG("Failed to create fragment shader (%s)", GlErrorString(glGetError()));
- goto delete_vs;
- }
- size = (GLsizei)(fs_end - fs_begin);
- glShaderSource(fragment, 1, &fs_begin, &size);
- glCompileShader(fragment);
- if (!CheckBuildableShader(fragment)) goto delete_fs;
+ GLsizei size = (GLsizei)(end - begin);
+ glShaderSource(shader, 1, &begin, &size);
+ glCompileShader(shader);
+ if (!CheckBuildableShader(shader)) goto delete_shader;
program = glCreateProgram();
if (!program) {
LOG("Failed to create shader program (%s)", GlErrorString(glGetError()));
- goto delete_fs;
+ goto delete_shader;
}
- glAttachShader(program, vertex);
- glAttachShader(program, fragment);
+ glAttachShader(program, shader);
glLinkProgram(program);
if (!CheckBuildableProgram(program)) {
glDeleteProgram(program);
program = 0;
- goto delete_fs;
+ goto delete_shader;
}
-delete_fs:
- glDeleteShader(fragment);
-delete_vs:
- glDeleteShader(vertex);
+delete_shader:
+ glDeleteShader(shader);
bail_out:
return program;
}
@@ -234,13 +206,12 @@ static const GLfloat* GetRangeVectors(enum YuvRange range) {
}
}
-static bool SetupCommonUniforms(GLuint program, enum YuvColorspace colorspace,
- enum YuvRange range) {
+static bool SetupUniforms(GLuint program, enum YuvColorspace colorspace,
+ enum YuvRange range) {
struct {
const char* name;
GLint location;
} uniforms[] = {
- {.name = "img_input"},
{.name = "colorspace"},
{.name = "ranges"},
};
@@ -255,10 +226,9 @@ static bool SetupCommonUniforms(GLuint program, enum YuvColorspace colorspace,
}
glUseProgram(program);
- glUniform1i(uniforms[0].location, 0);
- glUniformMatrix3fv(uniforms[1].location, 1, GL_TRUE,
+ glUniformMatrix3fv(uniforms[0].location, 1, GL_TRUE,
GetColorspaceMatrix(colorspace));
- glUniform3fv(uniforms[2].location, 2, GetRangeVectors(range));
+ glUniform3fv(uniforms[1].location, 2, GetRangeVectors(range));
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
LOG("Failed to set img_input uniform (%s)", GlErrorString(glGetError()));
@@ -377,42 +347,20 @@ struct GpuContext* GpuContextCreate(enum YuvColorspace colorspace,
LOOKUP_FUNCTION(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC,
glEGLImageTargetTexture2DOES)
- gpu_context->program_luma =
- CreateGlProgram(_binary_vertex_glsl_start, _binary_vertex_glsl_end,
- _binary_luma_glsl_start, _binary_luma_glsl_end);
- if (!gpu_context->program_luma ||
- !SetupCommonUniforms(gpu_context->program_luma, colorspace, range)) {
- LOG("Failed to create luma program");
- return NULL;
- }
-
- gpu_context->program_chroma =
- CreateGlProgram(_binary_vertex_glsl_start, _binary_vertex_glsl_end,
- _binary_chroma_glsl_start, _binary_chroma_glsl_end);
- if (!gpu_context->program_chroma ||
- !SetupCommonUniforms(gpu_context->program_chroma, colorspace, range)) {
- LOG("Failed to create chroma program");
- return NULL;
- }
- gpu_context->sample_offsets =
- glGetUniformLocation(gpu_context->program_chroma, "sample_offsets");
- if (gpu_context->sample_offsets == -1) {
- LOG("Failed to find sample_offsets uniform (%s)",
- GlErrorString(glGetError()));
+ gpu_context->compute_program =
+ CreateGlProgram(_binary_compute_glsl_start, _binary_compute_glsl_end);
+ if (!gpu_context->compute_program) {
+ LOG("Failed to create compute program");
return NULL;
}
- glGenFramebuffers(1, &gpu_context->framebuffer);
- glBindFramebuffer(GL_FRAMEBUFFER, gpu_context->framebuffer);
- glGenBuffers(1, &gpu_context->vertices);
- glBindBuffer(GL_ARRAY_BUFFER, gpu_context->vertices);
- static const GLfloat vertices[] = {0, 0, 1, 0, 1, 1, 0, 1};
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
- glEnableVertexAttribArray(0);
+ glUseProgram(gpu_context->compute_program);
+ glUniformMatrix3fv(0, 1, GL_TRUE, GetColorspaceMatrix(colorspace));
+ glUniform3fv(1, 2, GetRangeVectors(range));
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
- LOG("Failed to create gl objects (%s)", GlErrorString(glGetError()));
+ LOG("Failed to configure program uniforms (%s)",
+ GlErrorString(glGetError()));
return NULL;
}
return RELEASE(gpu_context);
@@ -431,13 +379,8 @@ bool GpuContextSync(struct GpuContext* gpu_context) {
void GpuContextDestroy(struct GpuContext** gpu_context) {
if (!gpu_context || !*gpu_context) return;
- if ((*gpu_context)->vertices) glDeleteBuffers(1, &(*gpu_context)->vertices);
- if ((*gpu_context)->framebuffer)
- glDeleteFramebuffers(1, &(*gpu_context)->framebuffer);
- if ((*gpu_context)->program_chroma)
- glDeleteProgram((*gpu_context)->program_chroma);
- if ((*gpu_context)->program_luma)
- glDeleteProgram((*gpu_context)->program_luma);
+ if ((*gpu_context)->compute_program)
+ glDeleteProgram((*gpu_context)->compute_program);
if ((*gpu_context)->context != EGL_NO_CONTEXT) {
eglMakeCurrent((*gpu_context)->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
@@ -718,45 +661,19 @@ void GpuFrameGetSize(const struct GpuFrame* gpu_frame, uint32_t* width,
*height = gpu_frame->height;
}
-static bool GpuFrameConvertImpl(GLuint from, GLuint to) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- to, 0);
- GLenum framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
- LOG("Framebuffer is incomplete (0x%x)", framebuffer_status);
- return false;
- }
+bool GpuFrameConvert(const struct GpuFrame* from, const struct GpuFrame* to) {
+ glBindImageTexture(0, from->textures[0], 0, GL_FALSE, 0, GL_READ_ONLY,
+ GL_RGBA8);
+ glBindImageTexture(1, to->textures[0], 0, GL_FALSE, 0, GL_READ_ONLY,
+ GL_RGBA8);
+ glBindImageTexture(2, to->textures[1], 0, GL_FALSE, 0, GL_READ_ONLY,
+ GL_RGBA8);
+ glDispatchCompute(from->width / 2, from->height / 2, 1);
+ glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
- glBindTexture(GL_TEXTURE_2D, from);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
- LOG("Failed to convert plane (%s)", GlErrorString(error));
- return false;
- }
- return true;
-}
-
-bool GpuFrameConvert(const struct GpuFrame* from, const struct GpuFrame* to) {
- glUseProgram(from->gpu_context->program_luma);
- glViewport(0, 0, (GLsizei)to->width, (GLsizei)to->height);
- if (!GpuFrameConvertImpl(from->textures[0], to->textures[0])) {
- LOG("Failed to convert luma plane");
- return false;
- }
-
- const GLfloat sample_offsets[] = {
- _(0.f, 0.f),
- _(1.f / (GLfloat)from->width, 0.f),
- _(0.f, 1.f / (GLfloat)from->height),
- _(1.f / (GLfloat)from->width, 1.f / (GLfloat)from->height),
- };
-
- glUseProgram(from->gpu_context->program_chroma);
- glUniform2fv(from->gpu_context->sample_offsets, 4, sample_offsets);
- glViewport(0, 0, (GLsizei)to->width / 2, (GLsizei)to->height / 2);
- if (!GpuFrameConvertImpl(from->textures[0], to->textures[1])) {
- LOG("Failed to convert chroma plane");
+ LOG("Failed to dispatch compute (%s)", GlErrorString(glGetError()));
return false;
}
return true;
diff --git a/luma.glsl b/luma.glsl
deleted file mode 100644
index 4dac957..0000000
--- a/luma.glsl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2023 Mikhail Burakov. This file is part of streamer.
- *
- * streamer is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * streamer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with streamer. If not, see <https://www.gnu.org/licenses/>.
- */
-
-uniform sampler2D img_input;
-uniform mediump mat3 colorspace;
-uniform mediump vec3 ranges[2];
-
-varying mediump vec2 texcoord;
-
-mediump vec3 rgb2yuv(in mediump vec3 rgb) {
- mediump vec3 yuv = colorspace * rgb.rgb + vec3(0.0, 0.5, 0.5);
- return ranges[0] + yuv * ranges[1];
-}
-
-void main() {
- mediump vec4 rgb = texture2D(img_input, texcoord);
- mediump vec3 yuv = rgb2yuv(rgb.rgb);
- gl_FragColor = vec4(yuv.x, 0.0, 0.0, 1.0);
-}
diff --git a/makefile b/makefile
index 5c5dd12..22f82a1 100644
--- a/makefile
+++ b/makefile
@@ -12,9 +12,7 @@ libs:=\
libva
res:=\
- vertex.glsl \
- luma.glsl \
- chroma.glsl
+ compute.glsl
#CFLAGS+=-DUSE_EGL_MESA_PLATFORM_SURFACELESS
CFLAGS+=$(shell pkg-config --cflags $(libs))