diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 9a855288ec..2615200591 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -19,6 +19,7 @@ target_sources(common PRIVATE
FastFormatString.cpp
FastJmp.cpp
GL/Context.cpp
+ GL/Program.cpp
GL/StreamBuffer.cpp
FileSystem.cpp
IniInterface.cpp
@@ -76,6 +77,7 @@ target_sources(common PRIVATE
FileSystem.h
General.h
GL/Context.h
+ GL/Program.h
GL/StreamBuffer.h
HashCombine.h
MemcpyFast.h
diff --git a/common/GL/Program.cpp b/common/GL/Program.cpp
new file mode 100644
index 0000000000..5163844a45
--- /dev/null
+++ b/common/GL/Program.cpp
@@ -0,0 +1,655 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "common/PrecompiledHeader.h"
+
+#include "common/GL/Program.h"
+#include "common/Assertions.h"
+#include "common/Console.h"
+#include "common/StringUtil.h"
+#include
+#include
+
+namespace GL
+{
+ GLuint Program::s_last_program_id = 0;
+ static GLuint s_next_bad_shader_id = 1;
+
+ Program::Program() = default;
+
+ Program::Program(Program&& prog)
+ {
+ m_program_id = prog.m_program_id;
+ prog.m_program_id = 0;
+ m_vertex_shader_id = prog.m_vertex_shader_id;
+ prog.m_vertex_shader_id = 0;
+ m_fragment_shader_id = prog.m_fragment_shader_id;
+ prog.m_fragment_shader_id = 0;
+ m_uniform_locations = std::move(prog.m_uniform_locations);
+ }
+
+ Program::~Program()
+ {
+ Destroy();
+ }
+
+ GLuint Program::CompileShader(GLenum type, const std::string_view source)
+ {
+ GLuint id = glCreateShader(type);
+
+ std::array sources = {{source.data()}};
+ std::array source_lengths = {{static_cast(source.size())}};
+ glShaderSource(id, static_cast(sources.size()), sources.data(), source_lengths.data());
+ glCompileShader(id);
+
+ GLint status = GL_FALSE;
+ glGetShaderiv(id, GL_COMPILE_STATUS, &status);
+
+ GLint info_log_length = 0;
+ glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+ if (status == GL_FALSE || info_log_length > 0)
+ {
+ std::string info_log;
+ info_log.resize(info_log_length + 1);
+ glGetShaderInfoLog(id, info_log_length, &info_log_length, &info_log[0]);
+
+ if (status == GL_TRUE)
+ {
+ Console.Warning("Shader compiled with warnings:\n%s", info_log.c_str());
+ }
+ else
+ {
+ Console.Error("Shader failed to compile:\n%s", info_log.c_str());
+
+ std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(),
+ std::ofstream::out | std::ofstream::binary);
+ if (ofs.is_open())
+ {
+ ofs.write(sources[0], source_lengths[0]);
+ ofs << "\n\nCompile failed, info log:\n";
+ ofs << info_log;
+ ofs.close();
+ }
+
+ glDeleteShader(id);
+ return 0;
+ }
+ }
+
+ return id;
+ }
+
+ void Program::ResetLastProgram()
+ {
+ s_last_program_id = 0;
+ }
+
+ bool Program::Compile(const std::string_view vertex_shader, const std::string_view geometry_shader,
+ const std::string_view fragment_shader)
+ {
+ GLuint vertex_shader_id = 0;
+ if (!vertex_shader.empty())
+ {
+ vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader);
+ if (vertex_shader_id == 0)
+ return false;
+ }
+
+ GLuint geometry_shader_id = 0;
+ if (!geometry_shader.empty())
+ {
+ geometry_shader_id = CompileShader(GL_GEOMETRY_SHADER, geometry_shader);
+ if (geometry_shader_id == 0)
+ return false;
+ }
+
+ GLuint fragment_shader_id = 0;
+ if (!fragment_shader.empty())
+ {
+ fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader);
+ if (fragment_shader_id == 0)
+ {
+ glDeleteShader(vertex_shader_id);
+ return false;
+ }
+ }
+
+ m_program_id = glCreateProgram();
+ if (vertex_shader_id != 0)
+ glAttachShader(m_program_id, vertex_shader_id);
+ if (geometry_shader_id != 0)
+ glAttachShader(m_program_id, geometry_shader_id);
+ if (fragment_shader_id != 0)
+ glAttachShader(m_program_id, fragment_shader_id);
+ return true;
+ }
+
+ bool Program::CreateFromBinary(const void* data, u32 data_length, u32 data_format)
+ {
+ GLuint prog = glCreateProgram();
+ glProgramBinary(prog, static_cast(data_format), data, data_length);
+
+ GLint link_status;
+ glGetProgramiv(prog, GL_LINK_STATUS, &link_status);
+ if (link_status != GL_TRUE)
+ {
+ Console.Error("Failed to create GL program from binary: status %d", link_status);
+ glDeleteProgram(prog);
+ return false;
+ }
+
+ m_program_id = prog;
+ return true;
+ }
+
+ bool Program::GetBinary(std::vector* out_data, u32* out_data_format)
+ {
+ GLint binary_size = 0;
+ glGetProgramiv(m_program_id, GL_PROGRAM_BINARY_LENGTH, &binary_size);
+ if (binary_size == 0)
+ {
+ Console.Warning("glGetProgramiv(GL_PROGRAM_BINARY_LENGTH) returned 0");
+ return false;
+ }
+
+ GLenum format = 0;
+ out_data->resize(static_cast(binary_size));
+ glGetProgramBinary(m_program_id, binary_size, &binary_size, &format, out_data->data());
+ if (binary_size == 0)
+ {
+ Console.Warning("glGetProgramBinary() failed");
+ return false;
+ }
+ else if (static_cast(binary_size) != out_data->size())
+ {
+ Console.Warning("Size changed from %zu to %d after glGetProgramBinary()", out_data->size(), binary_size);
+ out_data->resize(static_cast(binary_size));
+ }
+
+ *out_data_format = static_cast(format);
+ DevCon.WriteLn("Program binary retrieved, %zu bytes, format %u", out_data->size(), *out_data_format);
+ return true;
+ }
+
+ void Program::SetBinaryRetrievableHint()
+ {
+ glProgramParameteri(m_program_id, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
+ }
+
+ void Program::BindAttribute(GLuint index, const char* name)
+ {
+ glBindAttribLocation(m_program_id, index, name);
+ }
+
+ void Program::BindDefaultAttributes()
+ {
+ BindAttribute(0, "a_position");
+ BindAttribute(1, "a_texcoord");
+ BindAttribute(2, "a_color");
+ }
+
+ void Program::BindFragData(GLuint index /*= 0*/, const char* name /*= "o_col0"*/)
+ {
+ glBindFragDataLocation(m_program_id, index, name);
+ }
+
+ void Program::BindFragDataIndexed(GLuint color_number /*= 0*/, const char* name /*= "o_col0"*/)
+ {
+ if (GLAD_GL_VERSION_3_3 || GLAD_GL_ARB_blend_func_extended)
+ {
+ glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
+ return;
+ }
+ else if (GLAD_GL_EXT_blend_func_extended)
+ {
+ glBindFragDataLocationIndexedEXT(m_program_id, color_number, 0, name);
+ return;
+ }
+
+ Console.Error("BindFragDataIndexed() called without ARB or EXT extension, we'll probably crash.");
+ glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
+ }
+
+ bool Program::Link()
+ {
+ glLinkProgram(m_program_id);
+
+ if (m_vertex_shader_id != 0)
+ glDeleteShader(m_vertex_shader_id);
+ m_vertex_shader_id = 0;
+ if (m_fragment_shader_id != 0)
+ glDeleteShader(m_fragment_shader_id);
+ m_fragment_shader_id = 0;
+
+ GLint status = GL_FALSE;
+ glGetProgramiv(m_program_id, GL_LINK_STATUS, &status);
+
+ GLint info_log_length = 0;
+ glGetProgramiv(m_program_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+ if (status == GL_FALSE || info_log_length > 0)
+ {
+ std::string info_log;
+ info_log.resize(info_log_length + 1);
+ glGetProgramInfoLog(m_program_id, info_log_length, &info_log_length, &info_log[0]);
+
+ if (status == GL_TRUE)
+ {
+ Console.Error("Program linked with warnings:\n%s", info_log.c_str());
+ }
+ else
+ {
+ Console.Error("Program failed to link:\n%s", info_log.c_str());
+ glDeleteProgram(m_program_id);
+ m_program_id = 0;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Program::Bind() const
+ {
+ if (s_last_program_id == m_program_id)
+ return;
+
+ glUseProgram(m_program_id);
+ s_last_program_id = m_program_id;
+ }
+
+ void Program::Destroy()
+ {
+ if (m_vertex_shader_id != 0)
+ {
+ glDeleteShader(m_vertex_shader_id);
+ m_vertex_shader_id = 0;
+ }
+ if (m_fragment_shader_id != 0)
+ {
+ glDeleteShader(m_fragment_shader_id);
+ m_fragment_shader_id = 0;
+ }
+ if (m_program_id != 0)
+ {
+ glDeleteProgram(m_program_id);
+ m_program_id = 0;
+ }
+
+ m_uniform_locations.clear();
+ }
+
+ int Program::RegisterUniform(const char* name)
+ {
+ int id = static_cast(m_uniform_locations.size());
+ m_uniform_locations.push_back(glGetUniformLocation(m_program_id, name));
+ return id;
+ }
+
+ void Program::Uniform1ui(int index, u32 x) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform1ui(location, x);
+ }
+
+ void Program::Uniform2ui(int index, u32 x, u32 y) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2ui(location, x, y);
+ }
+
+ void Program::Uniform3ui(int index, u32 x, u32 y, u32 z) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3ui(location, x, y, z);
+ }
+
+ void Program::Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4ui(location, x, y, z, w);
+ }
+
+ void Program::Uniform1i(int index, s32 x) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform1i(location, x);
+ }
+
+ void Program::Uniform2i(int index, s32 x, s32 y) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2i(location, x, y);
+ }
+
+ void Program::Uniform3i(int index, s32 x, s32 y, s32 z) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3i(location, x, y, z);
+ }
+
+ void Program::Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4i(location, x, y, z, w);
+ }
+
+ void Program::Uniform1f(int index, float x) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform1f(location, x);
+ }
+
+ void Program::Uniform2f(int index, float x, float y) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2f(location, x, y);
+ }
+
+ void Program::Uniform3f(int index, float x, float y, float z) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3f(location, x, y, z);
+ }
+
+ void Program::Uniform4f(int index, float x, float y, float z, float w) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4f(location, x, y, z, w);
+ }
+
+ void Program::Uniform2uiv(int index, const u32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2uiv(location, 1, v);
+ }
+
+ void Program::Uniform3uiv(int index, const u32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3uiv(location, 1, v);
+ }
+
+ void Program::Uniform4uiv(int index, const u32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4uiv(location, 1, v);
+ }
+
+ void Program::Uniform2iv(int index, const s32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2iv(location, 1, v);
+ }
+
+ void Program::Uniform3iv(int index, const s32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3iv(location, 1, v);
+ }
+
+ void Program::Uniform4iv(int index, const s32* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4iv(location, 1, v);
+ }
+
+ void Program::Uniform2fv(int index, const float* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform2fv(location, 1, v);
+ }
+
+ void Program::Uniform3fv(int index, const float* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform3fv(location, 1, v);
+ }
+
+ void Program::Uniform4fv(int index, const float* v) const
+ {
+ pxAssert(static_cast(index) < m_uniform_locations.size());
+ const GLint location = m_uniform_locations[index];
+ if (location >= 0)
+ glUniform4fv(location, 1, v);
+ }
+
+ void Program::Uniform1ui(const char* name, u32 x) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform1ui(location, x);
+ }
+
+ void Program::Uniform2ui(const char* name, u32 x, u32 y) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2ui(location, x, y);
+ }
+
+ void Program::Uniform3ui(const char* name, u32 x, u32 y, u32 z) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3ui(location, x, y, z);
+ }
+
+ void Program::Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4ui(location, x, y, z, w);
+ }
+
+ void Program::Uniform1i(const char* name, s32 x) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform1i(location, x);
+ }
+
+ void Program::Uniform2i(const char* name, s32 x, s32 y) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2i(location, x, y);
+ }
+
+ void Program::Uniform3i(const char* name, s32 x, s32 y, s32 z) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3i(location, x, y, z);
+ }
+
+ void Program::Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4i(location, x, y, z, w);
+ }
+
+ void Program::Uniform1f(const char* name, float x) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform1f(location, x);
+ }
+
+ void Program::Uniform2f(const char* name, float x, float y) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2f(location, x, y);
+ }
+
+ void Program::Uniform3f(const char* name, float x, float y, float z) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3f(location, x, y, z);
+ }
+
+ void Program::Uniform4f(const char* name, float x, float y, float z, float w) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4f(location, x, y, z, w);
+ }
+
+ void Program::Uniform2uiv(const char* name, const u32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2uiv(location, 1, v);
+ }
+
+ void Program::Uniform3uiv(const char* name, const u32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3uiv(location, 1, v);
+ }
+
+ void Program::Uniform4uiv(const char* name, const u32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4uiv(location, 1, v);
+ }
+
+ void Program::Uniform2iv(const char* name, const s32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2iv(location, 1, v);
+ }
+
+ void Program::Uniform3iv(const char* name, const s32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3iv(location, 1, v);
+ }
+
+ void Program::Uniform4iv(const char* name, const s32* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4iv(location, 1, v);
+ }
+
+ void Program::Uniform2fv(const char* name, const float* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform2fv(location, 1, v);
+ }
+
+ void Program::Uniform3fv(const char* name, const float* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform3fv(location, 1, v);
+ }
+
+ void Program::Uniform4fv(const char* name, const float* v) const
+ {
+ const GLint location = glGetUniformLocation(m_program_id, name);
+ if (location >= 0)
+ glUniform4fv(location, 1, v);
+ }
+
+ void Program::BindUniformBlock(const char* name, u32 index)
+ {
+ const GLint location = glGetUniformBlockIndex(m_program_id, name);
+ if (location >= 0)
+ glUniformBlockBinding(m_program_id, location, index);
+ }
+
+ void Program::SetName(const std::string_view& name)
+ {
+ if (name.empty())
+ return;
+
+#ifdef _DEBUG
+ glObjectLabel(GL_PROGRAM, m_program_id, name.length(), name.data());
+#endif
+ }
+
+ void Program::SetFormattedName(const char* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ std::string n = StringUtil::StdStringFromFormatV(format, ap);
+ va_end(ap);
+ SetName(n);
+ }
+
+ Program& Program::operator=(Program&& prog)
+ {
+ Destroy();
+ m_program_id = prog.m_program_id;
+ prog.m_program_id = 0;
+ m_vertex_shader_id = prog.m_vertex_shader_id;
+ prog.m_vertex_shader_id = 0;
+ m_fragment_shader_id = prog.m_fragment_shader_id;
+ prog.m_fragment_shader_id = 0;
+ m_uniform_locations = std::move(prog.m_uniform_locations);
+ return *this;
+ }
+} // namespace GL
diff --git a/common/GL/Program.h b/common/GL/Program.h
new file mode 100644
index 0000000000..77563995be
--- /dev/null
+++ b/common/GL/Program.h
@@ -0,0 +1,122 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+#include "../Pcsx2Defs.h"
+#include "glad.h"
+#include
+#include
+
+namespace GL
+{
+ class Program
+ {
+ public:
+ Program();
+ Program(const Program&) = delete;
+ Program(Program&& prog);
+ ~Program();
+
+ static GLuint CompileShader(GLenum type, const std::string_view source);
+ static void ResetLastProgram();
+
+ bool IsValid() const { return m_program_id != 0; }
+
+ bool Compile(const std::string_view vertex_shader, const std::string_view geometry_shader,
+ const std::string_view fragment_shader);
+
+ bool CreateFromBinary(const void* data, u32 data_length, u32 data_format);
+
+ bool GetBinary(std::vector* out_data, u32* out_data_format);
+ void SetBinaryRetrievableHint();
+
+ void BindAttribute(GLuint index, const char* name);
+ void BindDefaultAttributes();
+
+ void BindFragData(GLuint index = 0, const char* name = "o_col0");
+ void BindFragDataIndexed(GLuint color_number = 0, const char* name = "o_col0");
+
+ bool Link();
+
+ void Bind() const;
+
+ void Destroy();
+
+ int RegisterUniform(const char* name);
+ void Uniform1ui(int index, u32 x) const;
+ void Uniform2ui(int index, u32 x, u32 y) const;
+ void Uniform3ui(int index, u32 x, u32 y, u32 z) const;
+ void Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const;
+ void Uniform1i(int index, s32 x) const;
+ void Uniform2i(int index, s32 x, s32 y) const;
+ void Uniform3i(int index, s32 x, s32 y, s32 z) const;
+ void Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const;
+ void Uniform1f(int index, float x) const;
+ void Uniform2f(int index, float x, float y) const;
+ void Uniform3f(int index, float x, float y, float z) const;
+ void Uniform4f(int index, float x, float y, float z, float w) const;
+ void Uniform2uiv(int index, const u32* v) const;
+ void Uniform3uiv(int index, const u32* v) const;
+ void Uniform4uiv(int index, const u32* v) const;
+ void Uniform2iv(int index, const s32* v) const;
+ void Uniform3iv(int index, const s32* v) const;
+ void Uniform4iv(int index, const s32* v) const;
+ void Uniform2fv(int index, const float* v) const;
+ void Uniform3fv(int index, const float* v) const;
+ void Uniform4fv(int index, const float* v) const;
+
+ void Uniform1ui(const char* name, u32 x) const;
+ void Uniform2ui(const char* name, u32 x, u32 y) const;
+ void Uniform3ui(const char* name, u32 x, u32 y, u32 z) const;
+ void Uniform4ui(const char* name, u32 x, u32 y, u32 z, u32 w) const;
+ void Uniform1i(const char* name, s32 x) const;
+ void Uniform2i(const char* name, s32 x, s32 y) const;
+ void Uniform3i(const char* name, s32 x, s32 y, s32 z) const;
+ void Uniform4i(const char* name, s32 x, s32 y, s32 z, s32 w) const;
+ void Uniform1f(const char* name, float x) const;
+ void Uniform2f(const char* name, float x, float y) const;
+ void Uniform3f(const char* name, float x, float y, float z) const;
+ void Uniform4f(const char* name, float x, float y, float z, float w) const;
+ void Uniform2uiv(const char* name, const u32* v) const;
+ void Uniform3uiv(const char* name, const u32* v) const;
+ void Uniform4uiv(const char* name, const u32* v) const;
+ void Uniform2iv(const char* name, const s32* v) const;
+ void Uniform3iv(const char* name, const s32* v) const;
+ void Uniform4iv(const char* name, const s32* v) const;
+ void Uniform2fv(const char* name, const float* v) const;
+ void Uniform3fv(const char* name, const float* v) const;
+ void Uniform4fv(const char* name, const float* v) const;
+
+ void BindUniformBlock(const char* name, u32 index);
+
+ void SetName(const std::string_view& name);
+ void SetFormattedName(const char* format, ...);
+
+ Program& operator=(const Program&) = delete;
+ Program& operator=(Program&& prog);
+
+ __fi bool operator==(const Program& rhs) const { return m_program_id == rhs.m_program_id; }
+ __fi bool operator!=(const Program& rhs) const { return m_program_id != rhs.m_program_id; }
+
+ private:
+ static u32 s_last_program_id;
+
+ GLuint m_program_id = 0;
+ GLuint m_vertex_shader_id = 0;
+ GLuint m_fragment_shader_id = 0;
+
+ std::vector m_uniform_locations;
+ };
+} // namespace GL
\ No newline at end of file
diff --git a/common/common.vcxproj b/common/common.vcxproj
index 5b1d8a9870..1f8e37ce41 100644
--- a/common/common.vcxproj
+++ b/common/common.vcxproj
@@ -51,6 +51,7 @@
+
@@ -111,6 +112,7 @@
+
diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters
index 05369d0ef6..107ab30cde 100644
--- a/common/common.vcxproj.filters
+++ b/common/common.vcxproj.filters
@@ -145,6 +145,9 @@
Source Files
+
+ Source Files\GL
+
@@ -339,6 +342,9 @@
Header Files
+
+ Header Files\GL
+