2026-03-16 17:22:45 +08:00
|
|
|
#include "XCEngine/RHI/OpenGL/OpenGLShader.h"
|
2026-03-16 16:09:09 +08:00
|
|
|
#include <glad/glad.h>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace RHI {
|
|
|
|
|
|
|
|
|
|
OpenGLShader::OpenGLShader()
|
2026-03-24 18:38:01 +08:00
|
|
|
: m_program(0), m_uniformsCached(false) {
|
2026-03-16 16:09:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 02:27:13 +08:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:09:09 +08:00
|
|
|
bool OpenGLShader::Compile(const char* vertexSource, const char* fragmentSource) {
|
|
|
|
|
unsigned int vertex, fragment;
|
|
|
|
|
|
|
|
|
|
vertex = glCreateShader(GL_VERTEX_SHADER);
|
2026-03-17 02:27:13 +08:00
|
|
|
glShaderSource(vertex, 1, &vertexSource, nullptr);
|
2026-03-16 16:09:09 +08:00
|
|
|
glCompileShader(vertex);
|
|
|
|
|
if (!CheckCompileErrors(vertex, "VERTEX")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fragment = glCreateShader(GL_FRAGMENT_SHADER);
|
2026-03-17 02:27:13 +08:00
|
|
|
glShaderSource(fragment, 1, &fragmentSource, nullptr);
|
2026-03-16 16:09:09 +08:00
|
|
|
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);
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
m_uniformsCached = false;
|
|
|
|
|
|
2026-03-16 16:09:09 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 02:27:13 +08:00
|
|
|
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);
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
m_uniformsCached = false;
|
|
|
|
|
|
2026-03-17 02:27:13 +08:00
|
|
|
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);
|
|
|
|
|
|
2026-03-25 13:52:11 +08:00
|
|
|
m_type = ShaderType::Compute;
|
2026-03-24 18:38:01 +08:00
|
|
|
m_uniformsCached = false;
|
|
|
|
|
|
2026-03-17 02:27:13 +08:00
|
|
|
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);
|
2026-03-24 18:38:01 +08:00
|
|
|
|
|
|
|
|
m_uniformsCached = false;
|
|
|
|
|
|
2026-03-17 02:27:13 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 03:37:34 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-25 13:52:11 +08:00
|
|
|
|
|
|
|
|
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<const char*>(sourceData), type);
|
2026-03-18 03:37:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:09:09 +08:00
|
|
|
void OpenGLShader::Shutdown() {
|
|
|
|
|
if (m_program) {
|
|
|
|
|
glDeleteProgram(m_program);
|
|
|
|
|
m_program = 0;
|
|
|
|
|
}
|
2026-03-24 18:38:01 +08:00
|
|
|
m_uniformInfos.clear();
|
|
|
|
|
m_uniformsCached = false;
|
2026-03-16 16:09:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OpenGLShader::Use() const {
|
|
|
|
|
glUseProgram(m_program);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
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<char> nameBuffer(values[0]);
|
|
|
|
|
glGetProgramResourceName(m_program, GL_UNIFORM, i, values[0], nullptr, nameBuffer.data());
|
|
|
|
|
|
|
|
|
|
UniformInfo info;
|
|
|
|
|
info.name = nameBuffer.data();
|
|
|
|
|
info.bindPoint = static_cast<uint32_t>(i);
|
|
|
|
|
info.type = static_cast<uint32_t>(values[1]);
|
|
|
|
|
info.offset = static_cast<uint32_t>(values[2]);
|
|
|
|
|
info.arraySize = static_cast<uint32_t>(values[3]);
|
|
|
|
|
|
2026-03-24 19:47:22 +08:00
|
|
|
GLint size = values[3];
|
2026-03-24 18:38:01 +08:00
|
|
|
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;
|
2026-03-17 02:27:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
const std::vector<RHIShader::UniformInfo>& OpenGLShader::GetUniformInfos() const {
|
|
|
|
|
CacheUniformInfos();
|
|
|
|
|
return m_uniformInfos;
|
2026-03-17 02:27:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
const RHIShader::UniformInfo* OpenGLShader::GetUniformInfo(const char* name) const {
|
|
|
|
|
CacheUniformInfos();
|
|
|
|
|
for (const auto& info : m_uniformInfos) {
|
|
|
|
|
if (info.name == name) {
|
|
|
|
|
return &info;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2026-03-17 02:27:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-17 17:26:41 +08:00
|
|
|
int OpenGLShader::GetUniformLocation(const char* name) const {
|
|
|
|
|
return glGetUniformLocation(m_program, name);
|
2026-03-17 02:27:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:09:09 +08:00
|
|
|
bool OpenGLShader::CheckCompileErrors(unsigned int shader, const char* type) {
|
|
|
|
|
int success;
|
|
|
|
|
char infoLog[1024];
|
|
|
|
|
|
|
|
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
|
|
|
|
if (!success) {
|
2026-03-17 02:27:13 +08:00
|
|
|
glGetShaderInfoLog(shader, 1024, nullptr, infoLog);
|
2026-03-16 16:09:09 +08:00
|
|
|
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) {
|
2026-03-17 02:27:13 +08:00
|
|
|
glGetProgramInfoLog(program, 1024, nullptr, infoLog);
|
2026-03-16 16:09:09 +08:00
|
|
|
std::cout << "ERROR::PROGRAM_LINKING_ERROR\n" << infoLog << std::endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace RHI
|
2026-03-24 18:38:01 +08:00
|
|
|
} // namespace XCEngine
|