Implement "dump raw frame buffer to stdout"

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
David Oberhollenzer 2022-07-17 01:58:46 +02:00
parent acaef93bd8
commit 75baf054e5
2 changed files with 119 additions and 3 deletions

View File

@ -21,14 +21,55 @@ static double diff_timespec(const struct timespec *time1,
+ (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0;
} }
static void convert_for_ffmpeg(const uint8_t *in, uint8_t *out,
size_t width, size_t height)
{
size_t x, y;
for (y = 0; y < height; ++y) {
const uint8_t *src = in + y * width * 4;
uint8_t *dst = out + (height - 1 - y) * width * 3;
for (x = 0; x < width; ++x) {
*(dst++) = *(src++);
*(dst++) = *(src++);
*(dst++) = *(src++);
++src;
}
}
}
static int write_retry(int fd, const void *buffer, size_t size)
{
while (size > 0) {
int ret = write(fd, buffer, size);
if (ret < 0) {
if (errno == EINTR)
continue;
perror("write");
return -1;
}
if (ret == 0)
return -1;
size -= ret;
buffer = (const char *)buffer + ret;
}
return 0;
}
static const struct option long_opts[] = { static const struct option long_opts[] = {
{ "width", required_argument, NULL, 'w' }, { "width", required_argument, NULL, 'w' },
{ "height", required_argument, NULL, 'h' }, { "height", required_argument, NULL, 'h' },
{ "shader", required_argument, NULL, 's' }, { "shader", required_argument, NULL, 's' },
{ "to-stdout", no_argument, NULL, 'S' },
{ NULL, 0, NULL, 0 }, { NULL, 0, NULL, 0 },
}; };
static const char *short_opts = "w:h:s:"; static const char *short_opts = "w:h:s:S";
static const char *usage_str = static const char *usage_str =
"shadermeh OPTIONS...\n" "shadermeh OPTIONS...\n"
@ -38,6 +79,8 @@ static const char *usage_str =
" --width, -w <pixels>\n" " --width, -w <pixels>\n"
" --height, -h <pixels>\n" " --height, -h <pixels>\n"
"\n" "\n"
" --to-stdout, -S\n"
"\n"
" --shader, -s <shader file>\n" " --shader, -s <shader file>\n"
"\n"; "\n";
@ -46,10 +89,12 @@ int main(int argc, char **argv)
GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame; GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame;
struct timespec start, frame_start, frame_end; struct timespec start, frame_start, frame_end;
unsigned int width, height, iFrame = 0; unsigned int width, height, iFrame = 0;
void *fb32 = NULL, *fb24 = NULL;
const char *shader_file = NULL; const char *shader_file = NULL;
GLuint vao, vbo, fbo, fbo_tex;
GLint major, minor, prog; GLint major, minor, prog;
float iTime, iTimeDelta; float iTime, iTimeDelta;
GLuint vao, vbo; bool to_stdout = false;
window *wnd; window *wnd;
int i; int i;
@ -72,6 +117,9 @@ int main(int argc, char **argv)
case 's': case 's':
shader_file = optarg; shader_file = optarg;
break; break;
case 'S':
to_stdout = true;
break;
default: default:
fputs(usage_str, stderr); fputs(usage_str, stderr);
return EXIT_FAILURE; return EXIT_FAILURE;
@ -84,11 +132,28 @@ int main(int argc, char **argv)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (to_stdout) {
fb32 = calloc(1, width * height * 4);
if (!fb32) {
perror("allocating scratch framebuffer");
return EXIT_FAILURE;
}
fb24 = calloc(1, width * height * 3);
if (!fb24) {
perror("allocating scratch framebuffer");
free(fb32);
return EXIT_FAILURE;
}
}
/********** create window and make context current **********/ /********** create window and make context current **********/
wnd = window_create(width, height, "shader meh..."); wnd = window_create(width, height, "shader meh...");
if (!wnd) { if (!wnd) {
fputs("failed to create window", stderr); fputs("failed to create window", stderr);
free(fb32);
free(fb24);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -101,6 +166,8 @@ int main(int argc, char **argv)
if (glewInit() != GLEW_OK) { if (glewInit() != GLEW_OK) {
fputs("glewInit() error", stderr); fputs("glewInit() error", stderr);
window_destroy(wnd); window_destroy(wnd);
free(fb32);
free(fb24);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -148,12 +215,34 @@ int main(int argc, char **argv)
clock_gettime(CLOCK_MONOTONIC_RAW, &start); clock_gettime(CLOCK_MONOTONIC_RAW, &start);
/******************** framebuffer object ********************/
if (to_stdout) {
glGenFramebuffers(1, &fbo);
glGenTextures(1, &fbo_tex);
glBindTexture(GL_TEXTURE_2D, fbo_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
fbo_tex, 0);
}
/******************** drawing loop ********************/ /******************** drawing loop ********************/
while (window_handle_events()) { while (window_handle_events()) {
/* render image to FBO */
if (to_stdout)
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start); clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
window_swap_buffers(wnd);
if (!to_stdout)
window_swap_buffers(wnd);
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end); clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end);
iFrame += 1; iFrame += 1;
@ -163,6 +252,23 @@ int main(int argc, char **argv)
glUniform1f(u_iTimeDelta, iTimeDelta); glUniform1f(u_iTimeDelta, iTimeDelta);
glUniform1f(u_iTime, iTime); glUniform1f(u_iTime, iTime);
glUniform1ui(u_iFrame, iFrame); glUniform1ui(u_iFrame, iFrame);
if (to_stdout) {
/* get image from FBO */
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, fbo_tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA,
GL_UNSIGNED_BYTE, fb32);
glBindTexture(GL_TEXTURE_2D, 0);
/* convert to 24 bps and dump to stdout */
convert_for_ffmpeg(fb32, fb24, width, height);
if (write_retry(STDOUT_FILENO, fb24,
width * height * 3)) {
break;
}
}
} }
/******************** cleanup ********************/ /******************** cleanup ********************/
@ -173,7 +279,15 @@ int main(int argc, char **argv)
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
glDeleteProgram(prog); glDeleteProgram(prog);
if (to_stdout) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &fbo_tex);
}
window_make_current(NULL); window_make_current(NULL);
free(fb32);
free(fb24);
window_destroy(wnd); window_destroy(wnd);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -10,11 +10,13 @@
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <getopt.h> #include <getopt.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
#include <time.h> #include <time.h>
typedef struct { typedef struct {