181 lines
4.9 KiB
C
181 lines
4.9 KiB
C
// gl_utils.c
|
|
#include "gl_utils.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
void glfw_error_callback(int error, const char* description) {
|
|
fprintf(stderr, "GLFW error %d: %s\n", error, description);
|
|
}
|
|
|
|
char* load_text_file(const char* path) {
|
|
FILE* f = fopen(path, "rb");
|
|
if (!f) {
|
|
fprintf(stderr, "Failed to open '%s': %s\n", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
if (fseek(f, 0, SEEK_END) != 0) {
|
|
fprintf(stderr, "fseek failed on '%s'\n", path);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
long size = ftell(f);
|
|
if (size < 0) {
|
|
fprintf(stderr, "ftell failed on '%s'\n", path);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
rewind(f);
|
|
|
|
char* buffer = malloc((size_t)size + 1);
|
|
if (!buffer) {
|
|
fprintf(stderr, "Out of memory reading '%s'\n", path);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
size_t read = fread(buffer, 1, (size_t)size, f);
|
|
fclose(f);
|
|
|
|
if (read != (size_t)size) {
|
|
fprintf(stderr, "Short read on '%s'\n", path);
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
buffer[size] = '\0';
|
|
return buffer;
|
|
}
|
|
|
|
GLuint compile_shader(GLenum type, const char* source, const char* debugName) {
|
|
GLuint shader = glCreateShader(type);
|
|
glShaderSource(shader, 1, &source, NULL);
|
|
glCompileShader(shader);
|
|
|
|
GLint success = 0;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
|
if (!success) {
|
|
char log[1024];
|
|
glGetShaderInfoLog(shader, sizeof(log), NULL, log);
|
|
fprintf(stderr, "Shader compile failed (%s):\n%s\n", debugName ? debugName : "unknown", log);
|
|
glDeleteShader(shader);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
GLuint create_program_from_files(const char* vsPath, const char* fsPath) {
|
|
char* vsSource = load_text_file(vsPath);
|
|
if (!vsSource) {
|
|
fprintf(stderr, "Could not load vertex shader from '%s'\n", vsPath);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
char* fsSource = load_text_file(fsPath);
|
|
if (!fsSource) {
|
|
fprintf(stderr, "Could not load fragment shader from '%s'\n", fsPath);
|
|
free(vsSource);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
GLuint vs = compile_shader(GL_VERTEX_SHADER, vsSource, vsPath);
|
|
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fsSource, fsPath);
|
|
|
|
free(vsSource);
|
|
free(fsSource);
|
|
|
|
GLuint prog = glCreateProgram();
|
|
glAttachShader(prog, vs);
|
|
glAttachShader(prog, fs);
|
|
glLinkProgram(prog);
|
|
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
|
|
GLint linked = 0;
|
|
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
|
|
if (!linked) {
|
|
char log[1024];
|
|
glGetProgramInfoLog(prog, sizeof(log), NULL, log);
|
|
fprintf(stderr, "Program link failed:\n%s\n", log);
|
|
glDeleteProgram(prog);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return prog;
|
|
}
|
|
GLuint create_compute_program_from_file(const char* path) {
|
|
char* source = load_text_file(path);
|
|
if (!source) {
|
|
fprintf(stderr, "Could not load compute shader from '%s'\n", path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Reuse compile_shader; type is GL_COMPUTE_SHADER
|
|
GLuint cs = compile_shader(GL_COMPUTE_SHADER, source, path);
|
|
free(source);
|
|
|
|
GLuint prog = glCreateProgram();
|
|
glAttachShader(prog, cs);
|
|
glLinkProgram(prog);
|
|
glDeleteShader(cs);
|
|
|
|
GLint linked = 0;
|
|
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
|
|
if (!linked) {
|
|
char log[1024];
|
|
glGetProgramInfoLog(prog, sizeof(log), NULL, log);
|
|
fprintf(stderr, "Compute program link failed (%s):\n%s\n", path, log);
|
|
glDeleteProgram(prog);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return prog;
|
|
}
|
|
|
|
GLFWwindow* init_glfw_glad(const char* title, int width, int height) {
|
|
glfwSetErrorCallback(glfw_error_callback);
|
|
|
|
// Prefer Wayland if this GLFW build supports it
|
|
if (glfwPlatformSupported(GLFW_PLATFORM_WAYLAND)) {
|
|
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND);
|
|
fprintf(stderr, "[info] Requesting Wayland platform via glfwInitHint\n");
|
|
} else {
|
|
fprintf(stderr, "[warn] Wayland platform not supported by this GLFW build, using default\n");
|
|
}
|
|
|
|
if (!glfwInit()) {
|
|
fprintf(stderr, "[fatal] glfwInit failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
|
|
|
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
|
|
if (!window) {
|
|
fprintf(stderr, "[fatal] glfwCreateWindow failed\n");
|
|
glfwTerminate();
|
|
return NULL;
|
|
}
|
|
|
|
glfwMakeContextCurrent(window);
|
|
|
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
|
fprintf(stderr, "[fatal] Failed to initialize GLAD\n");
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
return NULL;
|
|
}
|
|
glfwSwapInterval(0);
|
|
return window;
|
|
}
|