#include "XCEngine/RHI/OpenGL/OpenGLShader.h" #include #include #include #include namespace XCEngine { namespace RHI { OpenGLShader::OpenGLShader() : m_program(0), m_uniformsCached(false) { } OpenGLShader::~OpenGLShader() { Shutdown(); } bool OpenGLShader::CompileFromFile(const char* vertexPath, const char* fragmentPath) { std::string vertexCode, fragmentCode; std::ifstream vShaderFile(vertexPath), fShaderFile(fragmentPath); if (!vShaderFile.is_open() || !fShaderFile.is_open()) { return false; } std::stringstream vShaderStream, fShaderStream; vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); vShaderFile.close(); fShaderFile.close(); vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); return Compile(vertexCode.c_str(), fragmentCode.c_str()); } bool OpenGLShader::CompileFromFile(const char* vertexPath, const char* fragmentPath, const char* geometryPath) { std::string vertexCode, fragmentCode, geometryCode; std::ifstream vShaderFile(vertexPath), fShaderFile(fragmentPath), gShaderFile(geometryPath); if (!vShaderFile.is_open() || !fShaderFile.is_open() || !gShaderFile.is_open()) { return false; } std::stringstream vShaderStream, fShaderStream, gShaderStream; vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); gShaderStream << gShaderFile.rdbuf(); vShaderFile.close(); fShaderFile.close(); gShaderFile.close(); vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); geometryCode = gShaderStream.str(); return Compile(vertexCode.c_str(), fragmentCode.c_str(), geometryCode.c_str()); } bool OpenGLShader::Compile(const char* vertexSource, const char* fragmentSource) { unsigned int vertex, fragment; vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vertexSource, nullptr); glCompileShader(vertex); if (!CheckCompileErrors(vertex, "VERTEX")) { return false; } fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fragmentSource, nullptr); glCompileShader(fragment); if (!CheckCompileErrors(fragment, "FRAGMENT")) { return false; } m_program = glCreateProgram(); glAttachShader(m_program, vertex); glAttachShader(m_program, fragment); glLinkProgram(m_program); if (!CheckLinkErrors(m_program)) { return false; } glDeleteShader(vertex); glDeleteShader(fragment); m_uniformsCached = false; return true; } bool OpenGLShader::Compile(const char* vertexSource, const char* fragmentSource, const char* geometrySource) { unsigned int vertex, fragment, geometry; vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vertexSource, nullptr); glCompileShader(vertex); if (!CheckCompileErrors(vertex, "VERTEX")) { return false; } fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fragmentSource, nullptr); glCompileShader(fragment); if (!CheckCompileErrors(fragment, "FRAGMENT")) { return false; } geometry = glCreateShader(GL_GEOMETRY_SHADER); glShaderSource(geometry, 1, &geometrySource, nullptr); glCompileShader(geometry); if (!CheckCompileErrors(geometry, "GEOMETRY")) { return false; } m_program = glCreateProgram(); glAttachShader(m_program, vertex); glAttachShader(m_program, fragment); glAttachShader(m_program, geometry); glLinkProgram(m_program); if (!CheckLinkErrors(m_program)) { return false; } glDeleteShader(vertex); glDeleteShader(fragment); glDeleteShader(geometry); m_uniformsCached = false; return true; } bool OpenGLShader::CompileCompute(const char* computeSource) { unsigned int compute = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(compute, 1, &computeSource, nullptr); glCompileShader(compute); if (!CheckCompileErrors(compute, "COMPUTE")) { return false; } m_program = glCreateProgram(); glAttachShader(m_program, compute); glLinkProgram(m_program); if (!CheckLinkErrors(m_program)) { return false; } glDeleteShader(compute); m_type = ShaderType::Compute; m_uniformsCached = false; return true; } bool OpenGLShader::Compile(const char* source, ShaderType type) { unsigned int shader = 0; switch (type) { case ShaderType::Vertex: shader = glCreateShader(GL_VERTEX_SHADER); break; case ShaderType::Fragment: shader = glCreateShader(GL_FRAGMENT_SHADER); break; case ShaderType::Geometry: shader = glCreateShader(GL_GEOMETRY_SHADER); break; case ShaderType::Compute: shader = glCreateShader(GL_COMPUTE_SHADER); break; case ShaderType::TessControl: shader = glCreateShader(GL_TESS_CONTROL_SHADER); break; case ShaderType::TessEvaluation: shader = glCreateShader(GL_TESS_EVALUATION_SHADER); break; default: return false; } glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); const char* typeName[] = { "VERTEX", "FRAGMENT", "GEOMETRY", "COMPUTE", "TESS_CONTROL", "TESS_EVALUATION" }; if (!CheckCompileErrors(shader, typeName[(int)type])) { return false; } if (m_program == 0) { m_program = glCreateProgram(); } glAttachShader(m_program, shader); glLinkProgram(m_program); if (!CheckLinkErrors(m_program)) { return false; } glDeleteShader(shader); m_uniformsCached = false; return true; } bool OpenGLShader::CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) { std::wstring ws(filePath); std::string path(ws.begin(), ws.end()); return CompileFromFile(path.c_str(), nullptr); } bool OpenGLShader::Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) { if (!sourceData || sourceSize == 0) { return false; } ShaderType type = ShaderType::Fragment; if (target) { if (strstr(target, "vs_")) { type = ShaderType::Vertex; } else if (strstr(target, "ps_")) { type = ShaderType::Fragment; } else if (strstr(target, "gs_")) { type = ShaderType::Geometry; } else if (strstr(target, "cs_")) { type = ShaderType::Compute; } } return Compile(static_cast(sourceData), type); } void OpenGLShader::Shutdown() { if (m_program) { glDeleteProgram(m_program); m_program = 0; } m_uniformInfos.clear(); m_uniformsCached = false; } void OpenGLShader::Use() const { glUseProgram(m_program); } void OpenGLShader::CacheUniformInfos() const { if (m_uniformsCached || m_program == 0) { return; } m_uniformInfos.clear(); GLint numUniforms = 0; glGetProgramInterfaceiv(m_program, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms); for (GLint i = 0; i < numUniforms; ++i) { GLenum props[] = { GL_NAME_LENGTH, GL_TYPE, GL_OFFSET, GL_ARRAY_SIZE }; GLint values[4] = { 0 }; glGetProgramResourceiv(m_program, GL_UNIFORM, i, 4, props, 4, nullptr, values); std::vector nameBuffer(values[0]); glGetProgramResourceName(m_program, GL_UNIFORM, i, values[0], nullptr, nameBuffer.data()); UniformInfo info; info.name = nameBuffer.data(); info.bindPoint = static_cast(i); info.type = static_cast(values[1]); info.offset = static_cast(values[2]); info.arraySize = static_cast(values[3]); GLint size = values[3]; switch (values[1]) { case GL_FLOAT: info.size = sizeof(GLfloat) * size; break; case GL_FLOAT_VEC2: info.size = sizeof(GLfloat) * 2 * size; break; case GL_FLOAT_VEC3: info.size = sizeof(GLfloat) * 3 * size; break; case GL_FLOAT_VEC4: info.size = sizeof(GLfloat) * 4 * size; break; case GL_INT: info.size = sizeof(GLint) * size; break; case GL_BOOL: info.size = sizeof(GLboolean) * size; break; case GL_FLOAT_MAT4: info.size = sizeof(GLfloat) * 16 * size; break; case GL_FLOAT_MAT3: info.size = sizeof(GLfloat) * 9 * size; break; case GL_FLOAT_MAT2: info.size = sizeof(GLfloat) * 4 * size; break; default: info.size = 0; break; } m_uniformInfos.push_back(info); } m_uniformsCached = true; } const std::vector& OpenGLShader::GetUniformInfos() const { CacheUniformInfos(); return m_uniformInfos; } const RHIShader::UniformInfo* OpenGLShader::GetUniformInfo(const char* name) const { CacheUniformInfos(); for (const auto& info : m_uniformInfos) { if (info.name == name) { return &info; } } return nullptr; } int OpenGLShader::GetUniformLocation(const char* name) const { return glGetUniformLocation(m_program, name); } bool OpenGLShader::CheckCompileErrors(unsigned int shader, const char* type) { int success; char infoLog[1024]; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, nullptr, infoLog); std::cout << "ERROR::SHADER_COMPILATION_ERROR: " << type << "\n" << infoLog << std::endl; return false; } return true; } bool OpenGLShader::CheckLinkErrors(unsigned int program) { int success; char infoLog[1024]; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(program, 1024, nullptr, infoLog); std::cout << "ERROR::PROGRAM_LINKING_ERROR\n" << infoLog << std::endl; return false; } return true; } } // namespace RHI } // namespace XCEngine