Tyrolyean
51f808d12f
This reverts commit be8f7d1716
.
This was reverted as with this commit the program would consume large amounts
of cpu time just looping and calling the write function, which would not block.
306 lines
6.9 KiB
C
306 lines
6.9 KiB
C
/* SPDX-License-Identifier: ISC */
|
|
/*
|
|
* shadermeh.c
|
|
*
|
|
* Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
|
|
*/
|
|
#include "shadermeh.h"
|
|
|
|
static GLfloat vertex_buffer[] = {
|
|
-1.0f, -1.0f, 0.0f, /* lower left corner */
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
+1.0f, -1.0f, 0.0f, /* lower right corner */
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
-1.0f, 1.0f, 0.0f, /* uper left corner */
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
+1.0f, +1.0f, 0.0f, /* upper right corner */
|
|
1.0f, 1.0f, 1.0f,
|
|
};
|
|
|
|
static double diff_timespec(const struct timespec *time1,
|
|
const struct timespec *time0)
|
|
{
|
|
return (time1->tv_sec - time0->tv_sec)
|
|
+ (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' },
|
|
{ "sampling-rate", required_argument, NULL, 'r' },
|
|
{ "to-stdout", no_argument, NULL, 'S' },
|
|
{ NULL, 0, NULL, 0 },
|
|
};
|
|
|
|
static const char *short_opts = "w:h:s:S:r";
|
|
|
|
static const char *usage_str =
|
|
"shadermeh OPTIONS...\n"
|
|
"\n"
|
|
"Possible options:\n"
|
|
"\n"
|
|
" --width, -w <pixels>\n"
|
|
" --height, -h <pixels>\n"
|
|
"\n"
|
|
" --to-stdout, -S\n"
|
|
"\n"
|
|
" --shader, -s <shader file>\n"
|
|
"\n";
|
|
|
|
#define SND_BUFFER_SIZE 512
|
|
|
|
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;
|
|
bool to_stdout = false;
|
|
window *wnd;
|
|
int i;
|
|
unsigned int sampling_rate = 0;
|
|
float in_samples[SND_BUFFER_SIZE]; /* Raw input floats from -1...1 */
|
|
float norm_samples[SND_BUFFER_SIZE]; /* Normalized samples from 0...1 */
|
|
|
|
/******************** parse options ************************/
|
|
width = 800;
|
|
height = 450;
|
|
|
|
for (;;) {
|
|
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
|
if (i == -1)
|
|
break;
|
|
|
|
switch (i) {
|
|
case 'w':
|
|
width = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 'h':
|
|
height = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 's':
|
|
shader_file = optarg;
|
|
break;
|
|
case 'r':
|
|
sampling_rate = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 'S':
|
|
to_stdout = true;
|
|
break;
|
|
default:
|
|
fputs(usage_str, stderr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (!shader_file) {
|
|
fputs(usage_str, stderr);
|
|
fputs("No shader file specified!\n", stderr);
|
|
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;
|
|
}
|
|
|
|
window_make_current(wnd);
|
|
window_set_vsync(wnd, 1);
|
|
|
|
/******************** load entry points ********************/
|
|
glewExperimental = GL_TRUE;
|
|
|
|
if (glewInit() != GLEW_OK) {
|
|
fputs("glewInit() error", stderr);
|
|
window_destroy(wnd);
|
|
free(fb32);
|
|
free(fb24);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
printf("OpenGL version %d.%d\n", major, minor);
|
|
|
|
/******************** initialization ********************/
|
|
glViewport(0, 0, width, height);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
/* vertex buffer object & vertex array object */
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
|
|
glGenBuffers(1, &vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer),
|
|
vertex_buffer, GL_STATIC_DRAW);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glEnableVertexAttribArray(1);
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6,
|
|
(GLvoid *)(3 * sizeof(GLfloat)));
|
|
|
|
/* shader */
|
|
prog = shader_program_load(shader_file);
|
|
|
|
if (!shader_program_get_build_status(prog)) {
|
|
shader_program_print_info_log(prog);
|
|
}
|
|
|
|
glUseProgram(prog);
|
|
|
|
/* uniforms */
|
|
u_iResolution = glGetUniformLocation(prog, "iResolution");
|
|
u_iTime = glGetUniformLocation(prog, "iTime");
|
|
u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta");
|
|
u_iFrame = glGetUniformLocation(prog, "iFrame;");
|
|
|
|
glUniform3f(u_iResolution, width, height, 0.0f);
|
|
|
|
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);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
fbo_tex, 0);
|
|
} else {
|
|
window_show(wnd);
|
|
}
|
|
|
|
/******************** drawing loop ********************/
|
|
while (to_stdout || window_handle_events()) {
|
|
/* render image to FBO */
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
if (to_stdout) {
|
|
glFlush();
|
|
} else {
|
|
window_swap_buffers(wnd);
|
|
}
|
|
|
|
/* get image from FBO, dump to stdout */
|
|
if (to_stdout) {
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, fb32);
|
|
|
|
convert_for_ffmpeg(fb32, fb24, width, height);
|
|
|
|
if (write_retry(STDOUT_FILENO, fb24,
|
|
width * height * 3)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* update timers */
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end);
|
|
|
|
iFrame += 1;
|
|
iTimeDelta = diff_timespec(&frame_end, &frame_start);
|
|
iTime = diff_timespec(&frame_end, &start);
|
|
|
|
glUniform1f(u_iTimeDelta, iTimeDelta);
|
|
glUniform1f(u_iTime, iTime);
|
|
glUniform1ui(u_iFrame, iFrame);
|
|
}
|
|
|
|
/******************** cleanup ********************/
|
|
glUseProgram(0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glDeleteBuffers(1, &vbo);
|
|
glBindVertexArray(0);
|
|
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;
|
|
}
|