summaryrefslogblamecommitdiffstats
path: root/src/GalOgl.cpp
blob: 7a662e850111ee8bf8093d13034e7dfd9509dd57 (plain) (tree)










































































































































































































































































                                                                                                                  
                                            

























                                                                 
                                

                                                                                       

                                                                                












































































































                                                                                                                                                                               







                                                                                                      









































































































































































                                                                                                                                                                  











                                                                                                       














                                                                                                                       
                          




























                                                                                                                                                             
                                                       
 



                                                                                                     

                       
                                        














































































































































































































                                                                                                                                        
#include "Gal.hpp"

#include <easylogging++.h>
#include <GL/glew.h>

#include "Utility.hpp"

using namespace Gal;

class ImplOgl;
class FramebufferDefaultOgl;
class ShaderOgl;

std::unique_ptr<ImplOgl> impl;
std::shared_ptr<FramebufferDefaultOgl> fbDefault;

size_t GalTypeGetComponents(Gal::Type type) {
    switch (type) {
    case Type::Float:
    case Type::Double:
    case Type::Uint8:
    case Type::Uint16:
    case Type::Uint32:
    case Type::Int8:
    case Type::Int16:
    case Type::Int32:
        return 1;
    case Type::Vec2:
    case Type::Vec2u8:
    case Type::Vec2u16:
    case Type::Vec2u32:
    case Type::Vec2i8:
    case Type::Vec2i16:
    case Type::Vec2i32:
        return 2;
    case Type::Vec3:
    case Type::Vec3u8:
    case Type::Vec3u16:
    case Type::Vec3u32:
    case Type::Vec3i8:
    case Type::Vec3i16:
    case Type::Vec3i32:
        return 3;
    case Type::Vec4:
    case Type::Vec4u8:
    case Type::Vec4u16:
    case Type::Vec4u32:
    case Type::Vec4i8:
    case Type::Vec4i16:
    case Type::Vec4i32:
    case Type::Mat2:
        return 4;
    case Type::Mat3:
        return 9;
    case Type::Mat4:
        return 16;
    default:
        return 0;
    }
    return 0;
}

size_t GalTypeGetComponentSize(Gal::Type type) {
    switch (type) {	
    case Type::Uint8:
    case Type::Int8:
    case Type::Vec2u8:
    case Type::Vec2i8:
    case Type::Vec3u8:
    case Type::Vec3i8:
    case Type::Vec4u8:
    case Type::Vec4i8:
        return 1;
    case Type::Uint16:
    case Type::Int16:
    case Type::Vec2u16:
    case Type::Vec2i16:
    case Type::Vec3u16:
    case Type::Vec3i16:
    case Type::Vec4u16:
    case Type::Vec4i16:
        return 2;
    case Type::Float:
    case Type::Uint32:
    case Type::Int32:
    case Type::Vec2:
    case Type::Vec2u32:
    case Type::Vec2i32:
    case Type::Vec3:
    case Type::Vec3u32:
    case Type::Vec3i32:
    case Type::Vec4:
    case Type::Vec4u32:
    case Type::Vec4i32:
    case Type::Mat2:
    case Type::Mat3:
    case Type::Mat4:
        return 4;
    case Type::Double:
        return 8;
    default:
        return 0;
    }
}

size_t GalTypeGetSize(Gal::Type type) {
    return GalTypeGetComponents(type) * GalTypeGetComponentSize(type);
}

GLenum GalTypeGetComponentGlType(Gal::Type type) {
    switch (type) {
    case Type::Float:
    case Type::Vec2:
    case Type::Vec3:
    case Type::Vec4:
    case Type::Mat2:
    case Type::Mat3:
    case Type::Mat4:
        return GL_FLOAT;
    case Type::Double:
        return GL_DOUBLE;
    case Type::Uint8:
    case Type::Vec2u8:
    case Type::Vec3u8:
    case Type::Vec4u8:
        return GL_UNSIGNED_BYTE;
    case Type::Uint16:
    case Type::Vec2u16:
    case Type::Vec3u16:
    case Type::Vec4u16:
        return GL_UNSIGNED_SHORT;
    case Type::Uint32:
    case Type::Vec2u32:
    case Type::Vec3u32:
    case Type::Vec4u32:
        return GL_UNSIGNED_INT;
    case Type::Int8:
    case Type::Vec2i8:
    case Type::Vec3i8:
    case Type::Vec4i8:
        return GL_BYTE;
    case Type::Int16:
    case Type::Vec2i16:
    case Type::Vec3i16:
    case Type::Vec4i16:
        return GL_SHORT;
    case Type::Int32:
    case Type::Vec2i32:
    case Type::Vec3i32:
    case Type::Vec4i32:
        return GL_INT;
    default:
        return 0;
    }
    return 0;
}

size_t GalFormatGetSize(Format format) {
    switch (format) {
    case Format::R8G8B8:
        return 3;
    case Format::R8G8B8A8:
        return 4;
    default:
        return 0;
    }
    return 0;
}

GLenum GalFormatGetGlInternalFormat(Format format) {
    switch (format) {
    case Format::R8G8B8:
        return GL_RGB8;
    case Format::R8G8B8A8:
        return GL_RGBA8;
    default:
        return 0;
    }
    return 0;
}

GLenum GalFormatGetGlFormat(Format format) {
    switch (format) {
    case Format::R8G8B8:
        return GL_RGB;
    case Format::R8G8B8A8:
        return GL_RGBA;
    default:
        return 0;
    }
    return 0;
}

GLenum GalFormatGetGlType(Format format) {
    switch (format) {
    case Format::R8G8B8:
        return GL_UNSIGNED_BYTE;
    case Format::R8G8B8A8:
        return GL_UNSIGNED_BYTE;
    default:
        return 0;
    }
    return 0;
}

GLenum GalFilteringGetGlType(Filtering filtering) {
    switch (filtering) {
    case Filtering::Nearest:
        return GL_NEAREST;
    case Filtering::Bilinear:
        return GL_LINEAR;
    case Filtering::Trilinear:
        return GL_LINEAR_MIPMAP_LINEAR;
    case Filtering::Anisotropy:
        return GL_LINEAR;
    default:
        return 0;
    }
    return 0;
}

GLenum GalWrappingGetGlType(Wrapping wrapping) {
    switch (wrapping) {
    case Wrapping::Repeat:
        return GL_REPEAT;
    case Wrapping::Clamp:
        return GL_CLAMP_TO_EDGE;
    case Wrapping::Mirror:
        return GL_MIRRORED_REPEAT;
    default:
        return 0;
    }
    return 0;
}


class ShaderOgl : public Shader {
public:
    bool isVertex = true;
    std::string code;
};

class BufferBindingOgl : public BufferBinding {
public:
    BufferBindingOgl(size_t id) : bufferId(id) {}

    const size_t bufferId;

    static constexpr size_t indexValue = (std::numeric_limits<size_t>::max)(); //parenthess for windows' max macro
};

class BufferOgl : public Buffer {
public:
    GLuint vbo;

    virtual void SetData(std::vector<std::byte>&& data) override {
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, data.size(), data.data(), GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glCheckError();
    }
};

class TextureConfigOgl : public TextureConfig {
public:

    Format format;
    size_t width = 1, height = 1, depth = 1;
    bool interpolateLayers = false;

    Filtering min = Filtering::Nearest, max = Filtering::Nearest;
    Wrapping wrap = Wrapping::Clamp;
    

    virtual void SetMinFilter(Filtering filter) override {
        min = filter;
    }

    virtual void SetMaxFilter(Filtering filter) override {
        max = filter;
    }

    virtual void SetWrapping(Wrapping wrapping) override {
        wrap = wrapping;
    }

};

class TextureOgl : public Texture {
public:

    GLenum type;
    GLuint texture;
    Format format;
    size_t width, height, depth;

    virtual void SetData(std::vector<std::byte>&& data, size_t mipLevel = 0) override {
        size_t expectedSize = width * height * depth * GalFormatGetSize(format);
        if (data.size() != expectedSize)
            throw std::logic_error("Size of data is not valid for this texture");

        glBindTexture(type, texture);
        glCheckError();

        switch (type) {
        case GL_TEXTURE_2D:
        case GL_PROXY_TEXTURE_2D:
        case GL_TEXTURE_1D_ARRAY:
        case GL_PROXY_TEXTURE_1D_ARRAY:
        case GL_TEXTURE_RECTANGLE:
        case GL_PROXY_TEXTURE_RECTANGLE:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
        case GL_PROXY_TEXTURE_CUBE_MAP:
            glTexImage2D(type, mipLevel, GalFormatGetGlInternalFormat(format), width, height, 0, GalFormatGetGlFormat(format), GalFormatGetGlType(format), data.data());
            break;
        case GL_TEXTURE_3D:
        case GL_PROXY_TEXTURE_3D:
        case GL_TEXTURE_2D_ARRAY:
        case GL_PROXY_TEXTURE_2D_ARRAY:
            glTexImage3D(type, mipLevel, GalFormatGetGlInternalFormat(format), width, height, depth, 0, GalFormatGetGlFormat(format), GalFormatGetGlType(format), data.data());
            break;
        default:
            throw std::runtime_error("Unknown texture type");
        }

        glCheckError();

        glBindTexture(type, 0);
    }

};

class PipelineConfigOgl : public PipelineConfig {
public:
    std::shared_ptr<ShaderOgl> vertexShader, pixelShader;
    std::map<std::string, Type> shaderParameters;
    std::shared_ptr<Framebuffer> targetFb;
    std::vector<std::vector<VertexAttribute>> vertexBuffers;

public:
    virtual void SetVertexShader(std::shared_ptr<Shader> shader) override {
        vertexShader = std::static_pointer_cast<ShaderOgl,Shader>(shader);
    }

    virtual void SetPixelShader(std::shared_ptr<Shader> shader) override {
        pixelShader = std::static_pointer_cast<ShaderOgl, Shader>(shader);
    }

    virtual void AddShaderParameter(std::string_view name, Type type) override {
        shaderParameters.emplace(std::string(name), type);
    }

    virtual void SetTarget(std::shared_ptr<Framebuffer> target) override {
        targetFb = target;
    }

    virtual std::shared_ptr<BufferBinding> BindVertexBuffer(std::vector<VertexAttribute> &&bufferLayout) override {
        auto binding = std::make_shared<BufferBindingOgl>(vertexBuffers.size());
        vertexBuffers.push_back(bufferLayout);
        return std::static_pointer_cast<BufferBinding, BufferBindingOgl>(binding);
    }

    virtual std::shared_ptr<BufferBinding> BindIndexBuffer() override {
        auto binding = std::make_shared<BufferBindingOgl>(BufferBindingOgl::indexValue);
        return std::static_pointer_cast<BufferBinding, BufferBindingOgl>(binding);
    }
    
};

class PipelineInstanceOgl : public PipelineInstance {
public:
    GLuint vao;

    virtual void Activate() override {
        glBindVertexArray(vao);
        glCheckError();
    }

    virtual void Render(size_t offset = 0, size_t count = -1) override {
        glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0);
        glCheckError();
    }
};

class PipelineOgl : public Pipeline {
public:
    std::map<std::string, size_t> shaderParameters;
    GLuint program;
    struct VertexBindingCommand {
        size_t bufferId;
        size_t location;
        GLenum type;
        size_t count;
        size_t stride;
        size_t offset;
    };
    std::vector<VertexBindingCommand> vertexBindCmds;
    
    virtual void Activate() override {
        glUseProgram(program);
        glCheckError();
    }

    virtual void SetDynamicTexture(std::string_view name, std::shared_ptr<Texture> texture) override {
        Activate();
        glActiveTexture(GL_TEXTURE0);
        auto tex = std::static_pointer_cast<TextureOgl>(texture);
        glBindTexture(tex->type, tex->texture);
        SetShaderParameter(name, 0);
    }

    virtual std::shared_ptr<PipelineInstance> CreateInstance(std::vector<std::pair<std::shared_ptr<BufferBinding>, std::shared_ptr<Buffer>>>&& buffers) override {
        auto instance = std::make_shared<PipelineInstanceOgl>();

        size_t indexBuffer = BufferBindingOgl::indexValue;
        std::map<size_t, size_t> bufferBindingId;

        for (auto&& [binding, buffer] : buffers) {
            auto bind = std::static_pointer_cast<BufferBindingOgl, BufferBinding>(binding);
            auto buff = std::static_pointer_cast<BufferOgl, Buffer>(buffer);

            if (bind->bufferId == BufferBindingOgl::indexValue)
                indexBuffer = buff->vbo;
            else
                bufferBindingId.insert({ bind->bufferId,buff->vbo });
        }

        glGenVertexArrays(1, &instance->vao);
        glBindVertexArray(instance->vao);
        glCheckError();

        for (const auto& cmd : vertexBindCmds) {
            glBindBuffer(GL_ARRAY_BUFFER, bufferBindingId.find(cmd.bufferId)->second);
            glCheckError();
            switch (cmd.type) {
            case GL_FLOAT:
            case GL_DOUBLE:
                glVertexAttribPointer(cmd.location, cmd.count, cmd.type, GL_FALSE, cmd.offset, reinterpret_cast<void*>(cmd.stride));
                break;
            case GL_UNSIGNED_BYTE:
            case GL_BYTE:
            case GL_UNSIGNED_SHORT:
            case GL_SHORT:
            case GL_UNSIGNED_INT:
            case GL_INT:
                glVertexAttribIPointer(cmd.location, cmd.count, cmd.type, cmd.offset, reinterpret_cast<void*>(cmd.stride));
                break;
            }
            
            glCheckError();
            glEnableVertexAttribArray(cmd.location);
            glCheckError();
        }

        if (indexBuffer != BufferBindingOgl::indexValue) {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        }

        glBindVertexArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glCheckError();

        return instance;
    }

    virtual void SetShaderParameter(std::string_view name, float value) override {
        Activate();
        glUniform1f(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, double value) override {
        Activate();
        glUniform1d(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, int8_t value) override {
        Activate();
        glUniform1i(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, int16_t value) override {
        Activate();
        glUniform1i(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, int32_t value) override {
        Activate();
        glUniform1i(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, uint8_t value) override {
        Activate();
        glUniform1ui(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, uint16_t value) override {
        Activate();
        glUniform1ui(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, uint32_t value) override {
        Activate();
        glUniform1ui(shaderParameters.at(std::string(name)), value);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, glm::vec2 value) override {
        Activate();
        glUniform2f(shaderParameters.at(std::string(name)), value.x, value.y);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, glm::uvec2 value) override {
        Activate();
        glUniform2ui(shaderParameters.at(std::string(name)), value.x, value.y);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, glm::vec3 value) override {
        Activate();
        glUniform3f(shaderParameters.at(std::string(name)), value.x, value.y, value.z);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, glm::vec4 value) override {
        Activate();
        glUniform4f(shaderParameters.at(std::string(name)), value.x, value.y, value.z, value.w);
        glCheckError();
    }

    virtual void SetShaderParameter(std::string_view name, glm::mat4 value) override {
        Activate();
        glCheckError();
    }
};

class ImplOgl : public Impl {

public:

    virtual void Init() override {
        LOG(INFO) << "Initalizing Gal:OpenGL...";
        LOG(INFO) << "Initializing GLEW";
        glewExperimental = GL_TRUE;
        GLenum glewStatus = glewInit();
        glCheckError();
        if (glewStatus != GLEW_OK) {
            LOG(FATAL) << "Failed to initialize GLEW: " << glewGetErrorString(glewStatus);
        }
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);

        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        glFrontFace(GL_CCW);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glCheckError();
        if (glActiveTexture == nullptr) {
            throw std::runtime_error("GLEW initialization failed with unknown reason");
        }
    }

    virtual void DeInit() override {
        LOG(INFO) << "Destroying Gal:OpenGL...";
    }

    virtual void Cleanup() override {

    }

    virtual void SetScissor(size_t x = 0, size_t y = 0, size_t width = 0, size_t height = 0) override {
        glEnable(GL_SCISSOR_TEST);
        glScissor(x, y, width, height);
    }

    virtual void SetScissor(bool enabled) override {
        if (enabled)
            glEnable(GL_SCISSOR_TEST);
        else
            glDisable(GL_SCISSOR_TEST);
    }


    virtual std::shared_ptr<Buffer> CreateBuffer() override {
        auto buff = std::make_shared<BufferOgl>();
        glGenBuffers(1, &buff->vbo);
        buff->SetData({});
        glCheckError();
        return std::static_pointer_cast<Buffer, BufferOgl>(buff);
    }


    virtual std::shared_ptr<TextureConfig> CreateTexture2DConfig(size_t width, size_t height, Format format) override {
        auto config = std::make_shared<TextureConfigOgl>();

        config->width = width;
        config->height = height;
        config->depth = 1;
        config->format = format;

        return std::static_pointer_cast<TextureConfig, TextureConfigOgl>(config);
    }

    virtual std::shared_ptr<TextureConfig> CreateTexture3DConfig(size_t width, size_t height, size_t depth, bool interpolateLayers, Format format) override {
        auto config = std::make_shared<TextureConfigOgl>();

        config->width = width;
        config->height = height;
        config->depth = depth;
        config->interpolateLayers = interpolateLayers;
        config->format = format;

        return std::static_pointer_cast<TextureConfig, TextureConfigOgl>(config);
    }

    virtual std::shared_ptr<Texture> BuildTexture(std::shared_ptr<TextureConfig> config) override {
        auto texConfig = std::static_pointer_cast<TextureConfigOgl, TextureConfig>(config);
        auto texture = std::make_shared<TextureOgl>();

        texture->type = GL_TEXTURE_2D;
        texture->format = texConfig->format;
        texture->width = texConfig->width;
        texture->height = texConfig->height;
        texture->depth = texConfig->depth;

        glGenTextures(1, &texture->texture);
        glCheckError();
        glBindTexture(texture->type, texture->texture);

        glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GalFilteringGetGlType(texConfig->min));
        glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GalFilteringGetGlType(texConfig->max));
        glTexParameteri(texture->type, GL_TEXTURE_WRAP_S, GalWrappingGetGlType(texConfig->wrap));
        glTexParameteri(texture->type, GL_TEXTURE_WRAP_T, GalWrappingGetGlType(texConfig->wrap));
        glCheckError();

        glBindTexture(texture->type, 0);
        texture->SetData(std::vector<std::byte>(texture->width * texture->height * texture->depth * GalFormatGetSize(texture->format)));
        glCheckError();

        return std::static_pointer_cast<Texture, TextureOgl>(texture);
    }


    virtual std::shared_ptr<PipelineConfig> CreatePipelineConfig() override {
        auto pipelineConfig = std::make_shared<PipelineConfigOgl>();
        return std::static_pointer_cast<PipelineConfig, PipelineConfigOgl>(pipelineConfig);
    }

    virtual std::shared_ptr<Pipeline> BuildPipeline(std::shared_ptr<PipelineConfig> pipelineConfig) override {
        auto pipeline = std::make_shared<PipelineOgl>();
        auto config = std::static_pointer_cast<PipelineConfigOgl, PipelineConfig>(pipelineConfig);

        //Shader compilation

        bool vertexFailed = false, pixelFailed = false, linkFailed = false;

        const GLchar* vertexSourcePtr = config->vertexShader->code.c_str();
        const GLchar* pixelSourcePtr = config->pixelShader->code.c_str();

        GLuint vertex, pixel;
        GLint success;
        GLuint program;
        GLchar infoLog[512];

        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vertexSourcePtr, NULL);
        glCompileShader(vertex);

        glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(vertex, 512, NULL, infoLog);
            LOG(ERROR) << "Vertex shader compilation failed: " << std::endl << infoLog;
            vertexFailed = true;
        };

        pixel = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(pixel, 1, &pixelSourcePtr, NULL);
        glCompileShader(pixel);

        glGetShaderiv(pixel, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(pixel, 512, NULL, infoLog);
            LOG(ERROR) << "Fragment shader compilation failed: " << std::endl << infoLog;
            pixelFailed = true;
        };

        if (vertexFailed || pixelFailed)
            throw std::runtime_error("Shaders not compiled");

        program = glCreateProgram();
        glAttachShader(program, vertex);
        glAttachShader(program, pixel);
        glLinkProgram(program);
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(program, 512, NULL, infoLog);
            LOG(ERROR) << "Shader program not linked: " << std::endl << infoLog;
            linkFailed = true;
        }

        glDeleteShader(vertex);
        glDeleteShader(pixel);

        if (linkFailed)
            throw std::runtime_error("Shader not linked");

        glUseProgram(program);

        glCheckError();

        pipeline->program = program;

        //Shader parameters

        for (auto&& [name, type] : config->shaderParameters) {
            GLint location = glGetUniformLocation(program, name.c_str());
            if (location < 0) {
                glDeleteProgram(program);
                LOG(ERROR) << "Uniform name \"" << name << "\" not found in shader";
                throw std::runtime_error("Invalid uniform");
            }
            switch (type) {
            case Type::Vec2:
                glUniform2f(location, 0.0f, 0.0f);
                break;
            case Type::Vec2u8:
            case Type::Vec2u16:
            case Type::Vec2u32:
                glUniform2ui(location, 0, 0);
                break;
            case Type::Vec4u8:
                glUniform4ui(location, 0, 0, 0, 0);
                break;
            }
            pipeline->shaderParameters.insert({ name,location });
        }

        //Vertex attributes

        size_t bufferId = 0;
        for (const auto& buffer : config->vertexBuffers) {
            size_t vertexSize = 0;
            size_t cmdOffset = pipeline->vertexBindCmds.size();
            for (const auto& [name, type] : buffer) {
                if (name.empty()) {
                    vertexSize += GalTypeGetSize(type);
                    continue;
                }

                GLint location = glGetAttribLocation(program, name.c_str());
                if (location < 0) {
                    glDeleteProgram(program);
                    LOG(ERROR) << "Vertex attribute name \"" << name << "\" not found in shader";
                    throw std::runtime_error("Invalid attribute");
                }

                size_t attribSize = GalTypeGetSize(type);

                pipeline->vertexBindCmds.push_back({
                    bufferId,
                    static_cast<size_t>(location),
                    GalTypeGetComponentGlType(type),
                    GalTypeGetComponents(type),
                    vertexSize,
                    0
                    });

                vertexSize += attribSize;
            }

            for (size_t i = cmdOffset; i < pipeline->vertexBindCmds.size(); i++)
                pipeline->vertexBindCmds[i].offset = vertexSize;

            bufferId++;
        }

        glCheckError();

        return pipeline;
    }

    virtual std::shared_ptr<FramebufferConfig> CreateFramebufferConfig() override {
        return nullptr;
    }

    virtual std::shared_ptr<Framebuffer> BuildFramebuffer(std::shared_ptr<FramebufferConfig> config) override {
        return nullptr;
    }

    virtual std::shared_ptr<Framebuffer> GetDefaultFramebuffer() override {
        if (!fbDefault)
            fbDefault = std::make_shared<FramebufferDefaultOgl>();
        return std::static_pointer_cast<Framebuffer, FramebufferDefaultOgl>(fbDefault);
    }


    virtual std::shared_ptr<ShaderParameters> GetGlobalShaderParameters() override {
        return nullptr;
    }

    virtual std::shared_ptr<Shader> LoadVertexShader(std::string_view code) override {
        auto shader = std::make_shared<ShaderOgl>();
        shader->code = code;
        shader->isVertex = true;
        return std::static_pointer_cast<Shader, ShaderOgl>(shader);
    }

    virtual std::shared_ptr<Shader> LoadPixelShader(std::string_view code) override {
        auto shader = std::make_shared<ShaderOgl>();
        shader->code = code;
        shader->isVertex = false;
        return std::static_pointer_cast<Shader, ShaderOgl>(shader);
    }
};

class FramebufferDefaultOgl : public Framebuffer {
    size_t vpX, vpY, vpW, vpH;
public:

    virtual void Clear() override {
        GLbitfield clearBits = 0;
        clearBits |= GL_COLOR_BUFFER_BIT;
        clearBits |= GL_DEPTH_BUFFER_BIT;
        clearBits |= GL_STENCIL_BUFFER_BIT;
        glClear(clearBits);
    }

    virtual void SetViewport(size_t x, size_t y, size_t w, size_t h) override {
        vpX = x;
        vpY = y;
        vpW = w;
        vpH = h;
        glViewport(x, y, w, h);
    }
};

Impl* Gal::GetImplementation()
{
    if (!impl)
        impl = std::make_unique<ImplOgl>();

    return impl.get();
}