Files
Vivid2DRenderer/Vivid2DRenderer/systems/RenderSystem.h
lyxyz5223 e3b42e4f70 feat(Logger): Add a universal logger
- Add a Logger framework
- Add a spdlog logger adapter
- Update README and vcpkg.json
- fix some language error
2025-11-16 15:10:59 +08:00

569 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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, &currentProgram);
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;
};