- Add a Logger framework - Add a spdlog logger adapter - Update README and vcpkg.json - fix some language error
569 lines
18 KiB
C++
569 lines
18 KiB
C++
#pragma once
|
||
|
||
#include <glad/glad.h>
|
||
#include <glm/glm.hpp>
|
||
#include <glm/gtc/type_ptr.hpp>
|
||
|
||
#include <functional>
|
||
#include <queue>
|
||
#include <mutex>
|
||
#include <thread>
|
||
#include <stack>
|
||
#include <string>
|
||
#include <memory>
|
||
#include <atomic>
|
||
|
||
namespace Vivid2D::Render::Texture
|
||
{
|
||
class Texture;
|
||
}
|
||
|
||
// 向前声明 spdlog::logger 以避免在头文件中包含其完整定义
|
||
namespace spdlog {
|
||
class logger;
|
||
}
|
||
|
||
/**
|
||
* @class RenderSystem
|
||
* @brief 提供一个线程安全的、基于命令队列的 OpenGL API 封装层。
|
||
* @details
|
||
* 这是一个完全静态的类,旨在将来自任何线程的渲染调用排队,并在一个专用的渲染线程上执行它们。
|
||
* 这样做可以确保所有 OpenGL 操作的线程安全性。它还提供了对 OpenGL 状态、着色器、缓冲区和纹理的
|
||
* 高级管理功能,以及一个用于调试的错误检查机制。
|
||
* 使用方法:在主线程或其他逻辑线程中调用公开的静态方法(如 RenderSystem::drawElements),
|
||
* 这些方法会将渲染命令记录到队列中。在渲染线程的循环中,调用 RenderSystem::replayQueue()
|
||
* 来执行所有排队的命令。
|
||
*/
|
||
class VIVID_2D_MYDLL_API RenderSystem {
|
||
public:
|
||
// RenderSystem 是一个静态工具类,因此禁止实例化。
|
||
RenderSystem() = delete;
|
||
~RenderSystem() = delete;
|
||
RenderSystem(const RenderSystem&) = delete;
|
||
RenderSystem& operator=(const RenderSystem&) = delete;
|
||
using GLADloader = void* (*)(const char* name);
|
||
|
||
// ================== 日志系统管理 ==================
|
||
/**
|
||
* @brief 初始化日志系统。必须在任何日志调用之前,在主函数开始时调用。
|
||
*/
|
||
static void InitializeLogging();
|
||
|
||
/**
|
||
* @brief 关闭日志系统。应在主函数结束时调用以确保所有日志都被刷新。
|
||
*/
|
||
static void ShutdownLogging();
|
||
|
||
// ================== 初始化与线程管理 ==================
|
||
/**
|
||
* @brief 初始化并启动专用的渲染线程。
|
||
*/
|
||
static void initRenderThread();
|
||
|
||
/**
|
||
* @brief 开始渲染系统的初始化阶段。在此阶段,调用可以直接在调用线程上执行。
|
||
*/
|
||
static void beginInitialization();
|
||
|
||
/**
|
||
* @brief 结束渲染系统的初始化阶段。此后,所有调用都将被排队。
|
||
*/
|
||
static void finishInitialization();
|
||
|
||
/**
|
||
* @brief 检查当前线程是否是渲染线程。
|
||
* @return 如果是渲染线程,则返回 true。
|
||
*/
|
||
static bool isOnRenderThread();
|
||
|
||
/**
|
||
* @brief 检查当前是否处于初始化阶段。
|
||
* @return 如果是初始化阶段,则返回 true。
|
||
*/
|
||
static bool isInInitPhase();
|
||
|
||
/**
|
||
* @brief 断言当前线程必须是渲染线程,否则程序将中止。
|
||
*/
|
||
static void assertOnRenderThread();
|
||
|
||
/**
|
||
* @brief 断言当前线程必须是渲染线程或处于初始化阶段。
|
||
*/
|
||
static void assertOnRenderThreadOrInit();
|
||
|
||
/**
|
||
* @brief 使用 GLAD 加载 OpenGL 函数指针。
|
||
* @param loader 一个函数指针,用于获取 OpenGL 函数的地址(例如 glfwGetProcAddress)。
|
||
*/
|
||
static void loadGLFunctions(GLADloader loader);
|
||
|
||
// ================== 渲染命令队列 ==================
|
||
/**
|
||
* @brief 将一个渲染调用(作为 lambda 函数)记录到命令队列中,以便稍后在渲染线程上执行。
|
||
* @param renderCall 要执行的渲染操作。
|
||
*/
|
||
static void recordRenderCall(std::function<void()>&& renderCall);
|
||
|
||
/**
|
||
* @brief 在渲染线程上执行命令队列中的所有待处理渲染调用。
|
||
*/
|
||
static void replayQueue();
|
||
|
||
// ================== OpenGL 状态管理封装 ==================
|
||
/**
|
||
* @brief 启用一个 OpenGL 功能(例如 GL_BLEND)。
|
||
* @param capability 要启用的 OpenGL 功能枚举。
|
||
*/
|
||
static void enable(GLenum capability);
|
||
|
||
/**
|
||
* @brief 禁用一个 OpenGL 功能。
|
||
* @param capability 要禁用的 OpenGL 功能枚举。
|
||
*/
|
||
static void disable(GLenum capability);
|
||
|
||
/**
|
||
* @brief 将当前的渲染状态(如着色器、混合、视口等)推入状态堆栈。
|
||
*/
|
||
static void pushState();
|
||
|
||
/**
|
||
* @brief 从状态堆栈中弹出并恢复上一个渲染状态。
|
||
*/
|
||
static void popState();
|
||
|
||
/**
|
||
* @brief 获取当前状态堆栈的大小。
|
||
* @return 堆栈中的状态数量。
|
||
*/
|
||
static size_t getStateStackSize();
|
||
|
||
/**
|
||
* @brief 设置清屏颜色。
|
||
*/
|
||
static void clearColor(float r, float g, float b, float a);
|
||
|
||
/**
|
||
* @brief 清除指定的缓冲区(例如 GL_COLOR_BUFFER_BIT)。
|
||
* @param mask 要清除的缓冲区的位域掩码。
|
||
*/
|
||
static void clear(GLbitfield mask);
|
||
|
||
/**
|
||
* @brief 设置 OpenGL 视口。
|
||
*/
|
||
static void viewport(int x, int y, int width, int height);
|
||
|
||
// ================== VAO / VBO / EBO 操作 ==================
|
||
/**
|
||
* @brief 生成一个顶点数组对象 (VAO)。
|
||
* @return 新创建的 VAO 的句柄。
|
||
*/
|
||
static GLuint GenVertexArrays();
|
||
|
||
/**
|
||
* @brief 删除一个 VAO。
|
||
* @param vao 要删除的 VAO 的句柄。
|
||
*/
|
||
static void DeleteVertexArrays(GLuint vao);
|
||
|
||
/**
|
||
* @brief 绑定一个 VAO。
|
||
* @param vao 要绑定的 VAO 的句柄。
|
||
*/
|
||
static void BindVertexArray(GLuint vao);
|
||
|
||
/**
|
||
* @brief 生成一个缓冲区对象 (VBO/EBO)。
|
||
* @return 新创建的缓冲区的句柄。
|
||
*/
|
||
static GLuint GenBuffers();
|
||
|
||
/**
|
||
* @brief 删除一个缓冲区对象。
|
||
* @param buffer 要删除的缓冲区的句柄。
|
||
*/
|
||
static void DeleteBuffers(GLuint buffer);
|
||
|
||
/**
|
||
* @brief 绑定一个缓冲区对象。
|
||
* @param target 缓冲区的目标 (例如 GL_ARRAY_BUFFER)。
|
||
* @param buffer 要绑定的缓冲区的句柄。
|
||
*/
|
||
static void BindBuffer(GLenum target, GLuint buffer);
|
||
|
||
/**
|
||
* @brief 将数据上传到当前绑定的缓冲区。
|
||
* @param target 缓冲区目标。
|
||
* @param size 数据的大小(以字节为单位)。
|
||
* @param data 指向数据的指针。
|
||
* @param usage 数据的预期用途 (例如 GL_STATIC_DRAW)。
|
||
*/
|
||
static void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
||
|
||
// ================== Uniform 设置 ==================
|
||
static void uniform1i(GLint location, int value);
|
||
static void uniform1f(GLint location, float value);
|
||
static void uniform2f(GLint location, float x, float y);
|
||
static void uniform3f(GLint location, float x, float y, float z);
|
||
static void uniform4f(GLint location, float x, float y, float z, float w);
|
||
static void uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||
static void uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||
static void uniform2f(GLint location, const glm::vec2& vec);
|
||
static void uniform3f(GLint location, const glm::vec3& vec);
|
||
static void uniform4f(GLint location, const glm::vec4& vec);
|
||
static void uniformMatrix3(GLint location, const glm::mat3& matrix, bool transpose = false);
|
||
static void uniformMatrix4(GLint location, const glm::mat4& matrix, bool transpose = false);
|
||
|
||
/**
|
||
* @brief 获取着色器程序中 uniform 变量的位置。
|
||
* @param program 着色器程序的句柄。
|
||
* @param name uniform 变量的名称。
|
||
* @return uniform 变量的位置。
|
||
*/
|
||
static GLint getUniformLocation(GLuint program, const std::string& name);
|
||
|
||
// ================== 绘制命令 ==================
|
||
/**
|
||
* @brief 设置线宽。
|
||
*/
|
||
static void lineWidth(float width);
|
||
|
||
/**
|
||
* @brief 使用索引进行绘制。
|
||
*/
|
||
static void drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||
|
||
/**
|
||
* @brief 直接使用顶点数组进行绘制。
|
||
*/
|
||
static void drawArrays(GLenum mode, GLint first, GLsizei count);
|
||
|
||
// ================== 混合模式 ==================
|
||
/**
|
||
* @brief 启用混合。
|
||
*/
|
||
static void enableBlend();
|
||
|
||
/**
|
||
* @brief 禁用混合。
|
||
*/
|
||
static void disableBlend();
|
||
|
||
/**
|
||
* @brief 设置混合函数。
|
||
*/
|
||
static void blendFunc(GLenum sfactor, GLenum dfactor);
|
||
|
||
/**
|
||
* @brief 设置默认的 Alpha 混合函数 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)。
|
||
*/
|
||
static void defaultBlendFunc();
|
||
|
||
// ================== 着色器程序管理 ==================
|
||
/**
|
||
* @brief 创建一个空的着色器程序。
|
||
* @return 新程序的句柄。
|
||
*/
|
||
static GLuint createProgram();
|
||
|
||
/**
|
||
* @brief 获取当前绑定的着色器程序。
|
||
* @return 当前程序的句柄。
|
||
*/
|
||
static GLint getCurrentProgram();
|
||
|
||
/**
|
||
* @brief 将一个着色器附加到着色器程序。
|
||
*/
|
||
static void attachShader(GLuint program, GLuint shader);
|
||
|
||
/**
|
||
* @brief 链接一个着色器程序。
|
||
*/
|
||
static void linkProgram(GLuint program);
|
||
|
||
/**
|
||
* @brief 获取着色器程序的参数。
|
||
*/
|
||
static GLint getProgrami(GLuint program, GLenum pname);
|
||
|
||
/**
|
||
* @brief 获取着色器程序的日志信息。
|
||
*/
|
||
static std::string getProgramInfoLog(GLuint program);
|
||
|
||
/**
|
||
* @brief 从程序中分离一个着色器。
|
||
*/
|
||
static void detachShader(GLuint program, GLuint shader);
|
||
|
||
/**
|
||
* @brief 删除一个着色器程序。
|
||
*/
|
||
static void deleteProgram(GLuint program);
|
||
|
||
/**
|
||
* @brief 链接顶点和片段着色器以创建一个完整的程序。
|
||
*/
|
||
static GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);
|
||
|
||
/**
|
||
* @brief 链接顶点、几何和片段着色器以创建一个完整的程序。
|
||
*/
|
||
static GLuint linkProgram(GLuint vertexShader, GLuint geometryShader, GLuint fragmentShader);
|
||
|
||
/**
|
||
* @brief 激活一个着色器程序。
|
||
*/
|
||
static void useProgram(GLuint program);
|
||
|
||
// ================== 着色器管理 ==================
|
||
/**
|
||
* @brief 创建一个指定类型的着色器对象。
|
||
* @param type 着色器类型 (例如 GL_VERTEX_SHADER)。
|
||
* @return 新着色器对象的句柄。
|
||
*/
|
||
static GLuint createShader(GLenum type);
|
||
|
||
/**
|
||
* @brief 为着色器设置源代码。
|
||
*/
|
||
static void shaderSource(GLuint shader, const std::string& source);
|
||
|
||
/**
|
||
* @brief 编译一个着色器。
|
||
*/
|
||
static void compileShader(GLuint shader);
|
||
|
||
/**
|
||
* @brief 获取着色器的参数。
|
||
*/
|
||
static GLint getShaderi(GLuint shader, GLenum pname);
|
||
|
||
/**
|
||
* @brief 获取着色器的日志信息。
|
||
*/
|
||
static std::string getShaderInfoLog(GLuint shader);
|
||
|
||
/**
|
||
* @brief 删除一个着色器。
|
||
*/
|
||
static void deleteShader(GLuint shader);
|
||
|
||
/**
|
||
* @brief 从源代码编译一个指定类型的着色器。
|
||
* @return 编译后的着色器句柄。
|
||
*/
|
||
static GLuint compileShader(GLenum type, const std::string& source);
|
||
|
||
// ================== 深度测试 ==================
|
||
static void depthFunc(GLenum func);
|
||
static void depthMask(bool flag);
|
||
static void clearDepth(double depth);
|
||
static void enableDepthTest();
|
||
static void disableDepthTest();
|
||
|
||
// ================== 纹理管理 ==================
|
||
/**
|
||
* @brief 绑定一个 2D 纹理到当前活动的纹理单元。
|
||
* @param texture 要绑定的纹理的句柄。
|
||
*/
|
||
static void bindTexture(GLuint texture);
|
||
|
||
/**
|
||
* @brief 绑定一个 Texture 对象到指定的纹理单元。
|
||
* @details 这是一个便利方法,它将激活纹理单元和绑定纹理两个操作合并为一个命令,
|
||
* 并安全地提交到渲染线程。
|
||
* @param texture 要绑定的 Texture 对象的常量引用。
|
||
* @param textureUnit 要绑定的纹理单元索引 (0, 1, 2, ...)。
|
||
*/
|
||
static void bindTexture(const Vivid2D::Render::Texture::Texture& texture, int textureUnit = 0);
|
||
static void getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels);
|
||
/**
|
||
* @brief 激活一个纹理单元。
|
||
* @param texture 要激活的纹理单元 (例如 GL_TEXTURE0)。
|
||
*/
|
||
static void activeTexture(GLenum texture);
|
||
/**
|
||
* @brief 生成一个纹理对象。
|
||
* @return 新纹理的句柄。
|
||
*/
|
||
static GLuint genTextures();
|
||
|
||
/**
|
||
* @brief 删除一个纹理对象。
|
||
* @param texture 要删除的纹理的句柄。
|
||
*/
|
||
static void deleteTextures(GLuint texture);
|
||
static void texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
|
||
static void texParameteri(GLenum target, GLenum pname, GLint param);
|
||
static void setTextureMinFilter(GLenum filter);
|
||
static void setTextureMagFilter(GLenum filter);
|
||
static void setTextureWrapS(GLenum wrap);
|
||
static void setTextureWrapT(GLenum wrap);
|
||
|
||
/**
|
||
* @brief 创建一个默认的 1x1 白色纹理。
|
||
* @return 默认纹理的句柄。
|
||
*/
|
||
static GLuint createDefaultTexture();
|
||
|
||
// ================== 顶点属性 ==================
|
||
static void enableVertexAttribArray(GLuint index);
|
||
static void disableVertexAttribArray(GLuint index);
|
||
static void vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
|
||
|
||
// ================== 工具方法 ==================
|
||
static void readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels);
|
||
/**
|
||
* @brief 检查是否支持某个 OpenGL 扩展。
|
||
*/
|
||
static bool isExtensionSupported(const std::string& extension);
|
||
static void pixelStore(GLenum pname, GLint param);
|
||
static std::string getVendor();
|
||
static std::string getRenderer();
|
||
static std::string getOpenGLVersion();
|
||
static std::string getGLSLVersion();
|
||
|
||
/**
|
||
* @brief 记录详细的 OpenGL 和驱动信息。
|
||
*/
|
||
static void logDetailedGLInfo();
|
||
|
||
/**
|
||
* @brief 设置一套默认的 OpenGL 状态(例如启用混合,设置清屏色)。
|
||
*/
|
||
static void setupDefaultState();
|
||
|
||
/**
|
||
* @brief 检查并记录任何发生的 OpenGL 错误。
|
||
* @param operation 导致检查的操作的描述性名称。
|
||
*/
|
||
static void checkGLError(const std::string& operation);
|
||
|
||
// ================== 获取状态 ==================
|
||
static int getViewportWidth();
|
||
static int getViewportHeight();
|
||
static glm::vec4 getClearColor();
|
||
|
||
/**
|
||
* @brief 获取当前渲染命令队列中的命令数量。
|
||
* @return 队列大小。
|
||
*/
|
||
static size_t getQueueSize();
|
||
|
||
private:
|
||
struct RenderState;
|
||
// 私有的、直接执行GL调用的版本
|
||
static void _enable(GLenum capability);
|
||
static void _disable(GLenum capability);
|
||
static void _pushState();
|
||
static void _popState();
|
||
static void _clearColor(float r, float g, float b, float a);
|
||
static void _clear(GLbitfield mask);
|
||
static void _viewport(int x, int y, int width, int height);
|
||
static void _glDeleteVertexArrays(GLuint vao);
|
||
static void _uniform1i(GLint location, int value);
|
||
static void _uniform1f(GLint location, float value);
|
||
static void _uniform2f(GLint location, float x, float y);
|
||
static void _uniform3f(GLint location, float x, float y, float z);
|
||
static void _uniform4f(GLint location, float x, float y, float z, float w);
|
||
static void _uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||
static void _uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||
static void _lineWidth(float width);
|
||
static void _drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||
static void _drawArrays(GLenum mode, GLint first, GLsizei count);
|
||
static void _glBindVertexArray(GLuint vao);
|
||
static void _enableBlend();
|
||
static void _disableBlend();
|
||
static void _blendFunc(GLenum sfactor, GLenum dfactor);
|
||
static void _attachShader(GLuint program, GLuint shader);
|
||
static void _linkProgram(GLuint program);
|
||
static void _detachShader(GLuint program, GLuint shader);
|
||
static void _deleteProgram(GLuint program);
|
||
static void _useProgram(GLuint program);
|
||
static void _shaderSource(GLuint shader, const std::string& source);
|
||
static void _compileShader(GLuint shader);
|
||
static void _deleteShader(GLuint shader);
|
||
static void _depthFunc(GLenum func);
|
||
static void _depthMask(bool flag);
|
||
static void _clearDepth(double depth);
|
||
static void _enableDepthTest();
|
||
static void _disableDepthTest();
|
||
static void _bindTexture(GLuint texture);
|
||
static void _getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels);
|
||
static void _activeTexture(GLenum texture);
|
||
static void _deleteTextures(GLuint texture);
|
||
static void _texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
|
||
static void _texParameteri(GLenum target, GLenum pname, GLint param);
|
||
static void _glBindBuffer(GLenum target, GLuint buffer);
|
||
static void _glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
||
static void _glDeleteBuffers(GLuint buffer);
|
||
static void _enableVertexAttribArray(GLuint index);
|
||
static void _disableVertexAttribArray(GLuint index);
|
||
static void _vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
|
||
static void _readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels);
|
||
static void _pixelStore(GLenum pname, GLint param);
|
||
static void _checkGLError(const std::string& operation);
|
||
|
||
|
||
static std::string getGLErrorString(GLenum error);
|
||
|
||
// 静态成员变量
|
||
static std::thread::id s_RenderThreadId;
|
||
static std::atomic<bool> s_IsInInit;
|
||
static std::queue<std::function<void()>> s_RenderQueue;
|
||
static std::mutex s_RenderQueueMutex;
|
||
static std::atomic<bool> s_IsReplayingQueue;
|
||
|
||
// ================== RenderState 结构体和实现 ==================
|
||
struct RenderState {
|
||
GLint currentProgram = 0;
|
||
GLboolean blendEnabled = GL_FALSE;
|
||
GLboolean depthTestEnabled = GL_FALSE;
|
||
GLint blendSrcFactor = GL_SRC_ALPHA;
|
||
GLint blendDstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||
GLint activeTexture = GL_TEXTURE0;
|
||
GLint boundTexture = 0;
|
||
glm::vec4 clearColor{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||
glm::ivec4 viewport{ 0, 0, 800, 600 };
|
||
|
||
|
||
RenderState() {
|
||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
||
blendEnabled = glIsEnabled(GL_BLEND);
|
||
depthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
|
||
glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcFactor);
|
||
glGetIntegerv(GL_BLEND_DST_RGB, &blendDstFactor);
|
||
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
|
||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
|
||
glGetFloatv(GL_COLOR_CLEAR_VALUE, &clearColor[0]);
|
||
glGetIntegerv(GL_VIEWPORT, &viewport[0]);
|
||
while (glGetError() != GL_NO_ERROR) {}
|
||
}
|
||
|
||
void restore() {
|
||
glViewport(viewport.x, viewport.y, viewport.z, viewport.w);
|
||
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
||
glUseProgram(currentProgram);
|
||
glActiveTexture(activeTexture);
|
||
glBindTexture(GL_TEXTURE_2D, boundTexture);
|
||
if (blendEnabled) glEnable(GL_BLEND); else glDisable(GL_BLEND);
|
||
glBlendFunc(blendSrcFactor, blendDstFactor);
|
||
if (depthTestEnabled) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
|
||
while (glGetError() != GL_NO_ERROR) {}
|
||
}
|
||
|
||
};
|
||
|
||
static std::stack<RenderState> s_StateStack;
|
||
|
||
static int s_ViewportWidth;
|
||
static int s_ViewportHeight;
|
||
static glm::vec4 s_ClearColorValue;
|
||
|
||
static std::shared_ptr<spdlog::logger> s_Logger;
|
||
|
||
}; |