← index
cplusplus2x.Examples/C++23/SDL3/GL4/ex2/src/main.cpp
Source: cplusplus2x.Examples/C++23/SDL3/GL4/ex2/src/main.cpp
/*
    SDL3/OpenGL 4.6/C++23
*/

#include <SDL3/SDL.h>
#include <fstream>
#include <glad/glad.h>
#include <print>
#include <vector>

static std::vector<char> readFile(const char *path) {
    std::ifstream file(path, std::ios::binary | std::ios::ate);
    if (!file) {
        std::println("Failed to open: {}", path);
        return {};
    }
    size_t size = (size_t)file.tellg();
    if (size == 0) {
        std::println("Empty file: {}", path);
        return {};
    }
    std::vector<char> buffer(size);
    file.seekg(0);
    file.read(buffer.data(), (std::streamsize)size);
    return buffer;
}

static GLuint loadSPV(GLenum type, const char *path) {
    auto data = readFile(path);
    if (data.empty()) {
        std::println("SPV load failed: {}", path);
        return 0;
    }

    GLuint shader = glCreateShader(type);
    glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V,
                   data.data(), data.size());

    glSpecializeShader(shader, "main", 0, nullptr, nullptr);

    GLint ok = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
    if (!ok) {
        char log[4096];
        glGetShaderInfoLog(shader, 4096, nullptr, log);
        std::println("Shader error ({}): {}", path, log);
        glDeleteShader(shader);
        return 0;
    }

    return shader;
}

struct StaticShader {
    GLuint program = 0;
};

static StaticShader initStaticShader() {
    StaticShader s;

    GLuint vs = loadSPV(GL_VERTEX_SHADER, "shaders/static.vert.spv");
    GLuint fs = loadSPV(GL_FRAGMENT_SHADER, "shaders/static.frag.spv");

    if (!vs || !fs) {
        std::println("Shader creation failed");
        return s;
    }

    s.program = glCreateProgram();
    glAttachShader(s.program, vs);
    glAttachShader(s.program, fs);
    glLinkProgram(s.program);

    GLint linked = 0;
    glGetProgramiv(s.program, GL_LINK_STATUS, &linked);
    if (!linked) {
        char log[4096];
        glGetProgramInfoLog(s.program, 4096, nullptr, log);
        std::println("Program link error: {}", log);
    }

    glDeleteShader(vs);
    glDeleteShader(fs);

    return s;
}

int main() {
    if (!SDL_Init(SDL_INIT_VIDEO))
        return 1;

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_Window *window = SDL_CreateWindow("OpenGL SPV Static", 1280, 720, SDL_WINDOW_OPENGL);
    SDL_GLContext ctx = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, ctx);

    if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
        std::println("Failed to load GL");
        return 1;
    }

    SDL_GL_SetSwapInterval(0);

    std::println("GL_VERSION: {}", (const char *)glGetString(GL_VERSION));
    if (!SDL_GL_ExtensionSupported("GL_ARB_gl_spirv")) {
        std::println("Missing GL_ARB_gl_spirv");
    }

    GLuint vao = 0;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    StaticShader shader = initStaticShader();

    bool running = true;
    while (running) {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_EVENT_QUIT ||
                (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE))
                running = false;
        }

        int w, h;
        SDL_GetWindowSizeInPixels(window, &w, &h);

        glViewport(0, 0, w, h);
        glClearColor(0.1f, 0.0f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shader.program);
        float t = SDL_GetTicks() / 1000.0f;
        glUniform1f(0, t);
        glUniform2f(1, (float)w, (float)h);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        SDL_GL_SwapWindow(window);
    }

    SDL_Quit();
    return 0;
}