diff --git a/shadermeh.c b/shadermeh.c index 7d1fdc6..79bfd45 100644 --- a/shadermeh.c +++ b/shadermeh.c @@ -21,14 +21,55 @@ static double diff_timespec(const struct timespec *time1, + (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[] = { { "width", required_argument, NULL, 'w' }, { "height", required_argument, NULL, 'h' }, { "shader", required_argument, NULL, 's' }, + { "to-stdout", no_argument, NULL, 'S' }, { 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 = "shadermeh OPTIONS...\n" @@ -38,6 +79,8 @@ static const char *usage_str = " --width, -w \n" " --height, -h \n" "\n" +" --to-stdout, -S\n" +"\n" " --shader, -s \n" "\n"; @@ -46,10 +89,12 @@ int main(int argc, char **argv) GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame; struct timespec start, frame_start, frame_end; unsigned int width, height, iFrame = 0; + void *fb32 = NULL, *fb24 = NULL; const char *shader_file = NULL; + GLuint vao, vbo, fbo, fbo_tex; GLint major, minor, prog; float iTime, iTimeDelta; - GLuint vao, vbo; + bool to_stdout = false; window *wnd; int i; @@ -72,6 +117,9 @@ int main(int argc, char **argv) case 's': shader_file = optarg; break; + case 'S': + to_stdout = true; + break; default: fputs(usage_str, stderr); return EXIT_FAILURE; @@ -84,11 +132,28 @@ int main(int argc, char **argv) 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 **********/ wnd = window_create(width, height, "shader meh..."); if (!wnd) { fputs("failed to create window", stderr); + free(fb32); + free(fb24); return EXIT_FAILURE; } @@ -101,6 +166,8 @@ int main(int argc, char **argv) if (glewInit() != GLEW_OK) { fputs("glewInit() error", stderr); window_destroy(wnd); + free(fb32); + free(fb24); return EXIT_FAILURE; } @@ -148,12 +215,34 @@ int main(int argc, char **argv) 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 ********************/ while (window_handle_events()) { + /* render image to FBO */ + if (to_stdout) + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - window_swap_buffers(wnd); + + if (!to_stdout) + window_swap_buffers(wnd); + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end); iFrame += 1; @@ -163,6 +252,23 @@ int main(int argc, char **argv) glUniform1f(u_iTimeDelta, iTimeDelta); glUniform1f(u_iTime, iTime); 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 ********************/ @@ -173,7 +279,15 @@ int main(int argc, char **argv) glDeleteVertexArrays(1, &vao); glDeleteProgram(prog); + if (to_stdout) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &fbo_tex); + } + window_make_current(NULL); + free(fb32); + free(fb24); window_destroy(wnd); return EXIT_SUCCESS; } diff --git a/shadermeh.h b/shadermeh.h index 419bc47..d568af4 100644 --- a/shadermeh.h +++ b/shadermeh.h @@ -10,11 +10,13 @@ #include #include +#include #include #include #include #include #include +#include #include typedef struct {