#include "Gal.hpp" #include #include #include #include #include "Utility.hpp" using namespace Gal; class ImplOgl; class ShaderOgl; class FramebufferOgl; std::unique_ptr impl; std::shared_ptr 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::D24S8: return GL_DEPTH24_STENCIL8; 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::D24S8: return GL_DEPTH_STENCIL; 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::D24S8: return GL_UNSIGNED_INT_24_8; 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; } GLenum glCheckError_(const char* file, int line) { OPTICK_EVENT(); GLenum errorCode; while ((errorCode = glGetError()) != GL_NO_ERROR) { std::string error; switch (errorCode) { case GL_INVALID_ENUM: error = "INVALID_ENUM"; break; case GL_INVALID_VALUE: error = "INVALID_VALUE"; break; case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break; } static int t = 0; LOG(ERROR) << "OpenGL error: " << error << " at " << file << ":" << line; } return errorCode; } #define glCheckError() glCheckError_(__FILE__, __LINE__) 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::max)(); //parenthess for windows' max macro }; class BufferOgl : public Buffer { public: GLuint vbo; virtual void SetData(std::vector&& 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; GLenum type; 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&& data, size_t mipLevel = 0) override { size_t expectedSize = width * height * depth * GalFormatGetSize(format); if (data.size() != expectedSize && !data.empty()) 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.empty() ? nullptr : 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.empty() ? nullptr : data.data()); break; default: throw std::runtime_error("Unknown texture type"); } glCheckError(); glBindTexture(type, 0); } virtual void SetSubData(size_t x, size_t y, size_t z, size_t width, size_t height, size_t depth, std::vector&& 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: glTexSubImage2D(type, mipLevel, x, y, width, height, 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: glTexSubImage3D(type, mipLevel, x, y, z, width, height, depth, GalFormatGetGlFormat(format), GalFormatGetGlType(format), data.data()); break; default: throw std::runtime_error("Unknown texture type"); } glCheckError(); glBindTexture(type, 0); } }; class FramebufferOgl : public Framebuffer { public: size_t vpX = 0, vpY = 0, vpW = 1, vpH = 1; std::shared_ptr depthStencil; std::vector> colors; GLuint fbo = 0; virtual void Clear() override { glBindFramebuffer(GL_FRAMEBUFFER, fbo); 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; } }; class FramebufferConfigOgl : public FramebufferConfig { public: std::shared_ptr depthStencil; std::map> colors; virtual void SetDepthStencil(std::shared_ptr texture) override { auto tex = std::static_pointer_cast(texture); depthStencil = tex; } virtual void SetTexture(size_t location, std::shared_ptr texture) override { auto tex = std::static_pointer_cast(texture); colors.emplace(location, tex); } }; class PipelineConfigOgl : public PipelineConfig { public: std::shared_ptr vertexShader, pixelShader; std::map> textures; std::map shaderParameters; std::shared_ptr targetFb; std::vector> vertexBuffers; Primitive vertexPrimitive = Primitive::Triangle; public: virtual void SetVertexShader(std::shared_ptr shader) override { vertexShader = std::static_pointer_cast(shader); } virtual void SetPixelShader(std::shared_ptr shader) override { pixelShader = std::static_pointer_cast(shader); } virtual void AddShaderParameter(std::string_view name, Type type) override { shaderParameters.emplace(std::string(name), type); } virtual void AddStaticTexture(std::string_view name, std::shared_ptr texture) override { auto tex = std::static_pointer_cast(texture); textures.emplace(std::string(name), tex); } virtual void SetTarget(std::shared_ptr target) override { auto fb = std::static_pointer_cast(target); targetFb = fb; } virtual void SetPrimitive(Primitive primitive) override { vertexPrimitive = primitive; } virtual std::shared_ptr BindVertexBuffer(std::vector &&bufferLayout) override { auto binding = std::make_shared(vertexBuffers.size()); vertexBuffers.push_back(bufferLayout); return std::static_pointer_cast(binding); } virtual std::shared_ptr BindIndexBuffer() override { auto binding = std::make_shared(BufferBindingOgl::indexValue); return std::static_pointer_cast(binding); } }; class PipelineInstanceOgl : public PipelineInstance { public: GLuint vao; bool useIndex = false; Primitive primitive; size_t instances = 0; virtual void Activate() override { glBindVertexArray(vao); glCheckError(); } virtual void Render(size_t offset = 0, size_t count = -1) override { GLenum vertexMode; switch (primitive) { case Primitive::Line: vertexMode = GL_LINES; break; case Primitive::Triangle: vertexMode = GL_TRIANGLES; break; case Primitive::TriangleFan: vertexMode = GL_TRIANGLE_FAN; break; case Primitive::TriangleStrip: vertexMode = GL_TRIANGLE_STRIP; break; default: vertexMode = GL_TRIANGLES; } if (useIndex) { if (instances) { glDrawElementsInstanced(vertexMode, instances, GL_UNSIGNED_INT, nullptr, instances); } else { glDrawElements(vertexMode, count, GL_UNSIGNED_INT, nullptr); } } else { if (instances) { glDrawArraysInstanced(vertexMode, offset, instances, count); } else { glDrawArrays(vertexMode, offset, count); } } glCheckError(); } virtual void SetInstancesCount(size_t count) override { instances = count; } }; class PipelineOgl : public Pipeline { public: std::map shaderParameters; std::vector> staticTextures; GLuint program; struct VertexBindingCommand { size_t bufferId; size_t location; GLenum type; size_t count; size_t stride; size_t offset; size_t instances; }; std::vector vertexBindCmds; Primitive primitive; std::shared_ptr target; virtual void Activate() override { glUseProgram(program); glBindFramebuffer(GL_FRAMEBUFFER, target->fbo); glViewport(target->vpX, target->vpY, target->vpW, target->vpH); for (size_t i = 0; i < staticTextures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(staticTextures[i]->type, staticTextures[i]->texture); } glCheckError(); } virtual void SetDynamicTexture(std::string_view name, std::shared_ptr texture) override { Activate(); glActiveTexture(GL_TEXTURE0 + staticTextures.size()); auto tex = std::static_pointer_cast(texture); glBindTexture(tex->type, tex->texture); SetShaderParameter(name, static_cast(staticTextures.size())); } virtual std::shared_ptr CreateInstance(std::vector, std::shared_ptr>>&& buffers) override { auto instance = std::make_shared(); instance->primitive = primitive; size_t indexBuffer = BufferBindingOgl::indexValue; std::map bufferBindingId; for (auto&& [binding, buffer] : buffers) { auto bind = std::static_pointer_cast(binding); auto buff = std::static_pointer_cast(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(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(cmd.stride)); break; } glCheckError(); glEnableVertexAttribArray(cmd.location); glCheckError(); if (cmd.instances) { glVertexAttribDivisor(cmd.location, cmd.instances); glCheckError(); } } if (indexBuffer != BufferBindingOgl::indexValue) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); instance->useIndex = true; } 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(); glUniformMatrix4fv(shaderParameters.at(std::string(name)), 1, GL_FALSE, glm::value_ptr(value)); 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 CreateBuffer() override { auto buff = std::make_shared(); glGenBuffers(1, &buff->vbo); buff->SetData({}); glCheckError(); return std::static_pointer_cast(buff); } virtual std::shared_ptr CreateTexture2DConfig(size_t width, size_t height, Format format) override { auto config = std::make_shared(); config->type = GL_TEXTURE_2D; config->width = width; config->height = height; config->depth = 1; config->format = format; return std::static_pointer_cast(config); } virtual std::shared_ptr CreateTexture3DConfig(size_t width, size_t height, size_t depth, bool interpolateLayers, Format format) override { auto config = std::make_shared(); config->type = interpolateLayers ? GL_TEXTURE_3D : GL_TEXTURE_2D_ARRAY; config->width = width; config->height = height; config->depth = depth; config->interpolateLayers = interpolateLayers; config->format = format; return std::static_pointer_cast(config); } virtual std::shared_ptr BuildTexture(std::shared_ptr config) override { auto texConfig = std::static_pointer_cast(config); auto texture = std::make_shared(); texture->type = texConfig->type; 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(texture->width * texture->height * texture->depth * GalFormatGetSize(texture->format))); glCheckError(); return std::static_pointer_cast(texture); } virtual std::shared_ptr CreatePipelineConfig() override { auto pipelineConfig = std::make_shared(); return std::static_pointer_cast(pipelineConfig); } virtual std::shared_ptr BuildPipeline(std::shared_ptr pipelineConfig) override { auto pipeline = std::make_shared(); auto config = std::static_pointer_cast(pipelineConfig); pipeline->primitive = config->vertexPrimitive; pipeline->target = config->targetFb; if (!pipeline->target) pipeline->target = std::static_pointer_cast(GetDefaultFramebuffer()); //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) { LOG(ERROR) << "Uniform name \"" << name << "\" not found in shader"; } pipeline->shaderParameters.insert({ name,location }); } glCheckError(); //Static textures size_t usedTextureBlocks = 0; for (auto&& [name, texture] : config->textures) { GLint location = glGetUniformLocation(program, name.c_str()); if (location < 0) { LOG(ERROR) << "Texture uniform name \"" << name << "\" not found in shader"; } glUniform1i(location, usedTextureBlocks); pipeline->staticTextures.push_back(texture); } glCheckError(); //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, count, instances] : buffer) { if (name.empty()) { vertexSize += GalTypeGetSize(type) * count; continue; } GLint location = glGetAttribLocation(program, name.c_str()); if (location < 0) { LOG(ERROR) << "Vertex attribute name \"" << name << "\" not found in shader"; } size_t attribSize = GalTypeGetSize(type); for (size_t i = 0; i < count; i++) { pipeline->vertexBindCmds.push_back({ bufferId, static_cast(location + i), GalTypeGetComponentGlType(type), GalTypeGetComponents(type), vertexSize, 0, instances, }); 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 CreateFramebufferConfig() override { auto config = std::make_shared(); return std::static_pointer_cast(config); } virtual std::shared_ptr BuildFramebuffer(std::shared_ptr config) override { auto conf = std::static_pointer_cast(config); auto fb = std::make_shared(); glGenFramebuffers(1, &fb->fbo); glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); if (conf->depthStencil) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, conf->depthStencil->type, conf->depthStencil->texture, 0); fb->depthStencil = std::move(conf->depthStencil); } for (auto&& [location, texture] : conf->colors) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + location, texture->type, texture->texture, 0); fb->colors.emplace_back(std::move(texture)); } if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { LOG(ERROR) << "Framebuffer not completed: " << glCheckFramebufferStatus(GL_FRAMEBUFFER); } glBindFramebuffer(GL_FRAMEBUFFER, 0); glCheckError(); return std::static_pointer_cast(fb); } virtual std::shared_ptr GetDefaultFramebuffer() override { if (!fbDefault) fbDefault = std::make_shared(); fbDefault->fbo = 0; return std::static_pointer_cast(fbDefault); } virtual std::shared_ptr GetGlobalShaderParameters() override { return nullptr; } virtual std::shared_ptr LoadVertexShader(std::string_view code) override { auto shader = std::make_shared(); shader->code = code; shader->isVertex = true; return std::static_pointer_cast(shader); } virtual std::shared_ptr LoadPixelShader(std::string_view code) override { auto shader = std::make_shared(); shader->code = code; shader->isVertex = false; return std::static_pointer_cast(shader); } }; Impl* Gal::GetImplementation() { if (!impl) impl = std::make_unique(); return impl.get(); }