refactor(opengl): 重构渲染器资源管理并引入资源抽象层
- 引入资源抽象层接口(Buffer、Pipeline、Framebuffer等) - 将OpenGL资源管理重构为GLBuffer、GLPipeline、GLFramebuffer等实现类 - 使用GLBuffer替代手动管理的VBO/IBO,提供更安全的资源生命周期管理 - 新增GLPipeline管理OpenGL管线状态,减少冗余状态切换 - 新增GLFramebuffer封装帧缓冲对象功能 - 更新GLRenderer使用新的资源管理方式 - 添加详细文档说明资源抽象层设计 - 修复相关内存泄漏问题 docs: 添加资源抽象层文档说明 - 新增docs/README.md详细说明资源抽象层设计 - 文档包含各接口功能说明和实现原则
This commit is contained in:
parent
c8a6ea19e3
commit
2748e80dea
|
|
@ -0,0 +1,83 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/graphics/resources/buffer.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OpenGL 缓冲区实现
|
||||||
|
// ============================================================================
|
||||||
|
class GLBuffer : public Buffer {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
*/
|
||||||
|
GLBuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构函数
|
||||||
|
*/
|
||||||
|
~GLBuffer() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化缓冲区
|
||||||
|
* @param desc 缓冲区描述
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool init(const BufferDesc& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭缓冲区,释放资源
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
// Buffer 接口实现
|
||||||
|
void bind() override;
|
||||||
|
void unbind() override;
|
||||||
|
void setData(const void* data, size_t size) override;
|
||||||
|
void updateData(const void* data, size_t offset, size_t size) override;
|
||||||
|
void* map() override;
|
||||||
|
void unmap() override;
|
||||||
|
size_t getSize() const override { return size_; }
|
||||||
|
BufferType getType() const override { return type_; }
|
||||||
|
BufferUsage getUsage() const override { return usage_; }
|
||||||
|
bool isValid() const override { return bufferID_ != 0; }
|
||||||
|
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(bufferID_); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 缓冲区 ID
|
||||||
|
* @return 缓冲区 ID
|
||||||
|
*/
|
||||||
|
GLuint getBufferID() const { return bufferID_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 缓冲区目标类型
|
||||||
|
* @return 缓冲区目标类型
|
||||||
|
*/
|
||||||
|
GLenum getTarget() const { return target_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint bufferID_ = 0;
|
||||||
|
GLenum target_ = GL_ARRAY_BUFFER;
|
||||||
|
size_t size_ = 0;
|
||||||
|
BufferType type_ = BufferType::Vertex;
|
||||||
|
BufferUsage usage_ = BufferUsage::Static;
|
||||||
|
GLenum glUsage_ = GL_STATIC_DRAW;
|
||||||
|
bool mapped_ = false;
|
||||||
|
void* mappedPtr_ = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 转换使用模式到 OpenGL 枚举
|
||||||
|
*/
|
||||||
|
static GLenum convertUsage(BufferUsage usage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 转换缓冲区类型到 OpenGL 目标
|
||||||
|
*/
|
||||||
|
static GLenum convertType(BufferType type);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OpenGL 版本信息
|
||||||
|
// ============================================================================
|
||||||
|
struct GLVersion {
|
||||||
|
int major = 0;
|
||||||
|
int minor = 0;
|
||||||
|
bool es = false; // 是否为 ES 版本
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OpenGL 上下文管理类
|
||||||
|
// ============================================================================
|
||||||
|
class GLContext {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 获取全局 GLContext 实例
|
||||||
|
*/
|
||||||
|
static GLContext& get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化 OpenGL 上下文
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭 OpenGL 上下文
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查上下文是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
bool isValid() const { return initialized_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 版本信息
|
||||||
|
*/
|
||||||
|
const GLVersion& getVersion() const { return version_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 版本字符串
|
||||||
|
*/
|
||||||
|
std::string getVersionString() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 GPU 厂商信息
|
||||||
|
*/
|
||||||
|
std::string getVendor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 GPU 渲染器信息
|
||||||
|
*/
|
||||||
|
std::string getRenderer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否支持指定扩展
|
||||||
|
* @param extension 扩展名称
|
||||||
|
* @return 支持返回 true
|
||||||
|
*/
|
||||||
|
bool hasExtension(const std::string& extension) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取最大纹理尺寸
|
||||||
|
*/
|
||||||
|
int getMaxTextureSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取最大纹理单元数
|
||||||
|
*/
|
||||||
|
int getMaxTextureUnits() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取最大顶点属性数
|
||||||
|
*/
|
||||||
|
int getMaxVertexAttribs() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取最大 uniform 缓冲区绑定点数
|
||||||
|
*/
|
||||||
|
int getMaxUniformBufferBindings() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否为 OpenGL ES
|
||||||
|
*/
|
||||||
|
bool isGLES() const { return version_.es; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否支持 VAO
|
||||||
|
*/
|
||||||
|
bool hasVAO() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否支持 FBO
|
||||||
|
*/
|
||||||
|
bool hasFBO() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否支持 Shader
|
||||||
|
*/
|
||||||
|
bool hasShader() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLContext() = default;
|
||||||
|
~GLContext() = default;
|
||||||
|
|
||||||
|
GLContext(const GLContext&) = delete;
|
||||||
|
GLContext& operator=(const GLContext&) = delete;
|
||||||
|
|
||||||
|
bool initialized_ = false;
|
||||||
|
GLVersion version_;
|
||||||
|
|
||||||
|
// 缓存的限制值
|
||||||
|
mutable int maxTextureSize_ = -1;
|
||||||
|
mutable int maxTextureUnits_ = -1;
|
||||||
|
mutable int maxVertexAttribs_ = -1;
|
||||||
|
mutable int maxUniformBufferBindings_ = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析 OpenGL 版本
|
||||||
|
*/
|
||||||
|
void parseVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载 OpenGL 扩展
|
||||||
|
*/
|
||||||
|
bool loadExtensions();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/graphics/resources/framebuffer.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OpenGL 帧缓冲实现
|
||||||
|
// ============================================================================
|
||||||
|
class GLFramebuffer : public Framebuffer {
|
||||||
|
public:
|
||||||
|
// 最大颜色附件数
|
||||||
|
static constexpr int MAX_COLOR_ATTACHMENTS = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
*/
|
||||||
|
GLFramebuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构函数
|
||||||
|
*/
|
||||||
|
~GLFramebuffer() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化帧缓冲
|
||||||
|
* @param desc 帧缓冲描述
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool init(const FramebufferDesc& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭帧缓冲,释放资源
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
// Framebuffer 接口实现
|
||||||
|
void bind() override;
|
||||||
|
void unbind() override;
|
||||||
|
void attachColorTexture(Ptr<Texture> texture, int attachment = 0) override;
|
||||||
|
void attachDepthTexture(Ptr<Texture> texture) override;
|
||||||
|
void attachDepthStencilTexture(Ptr<Texture> texture) override;
|
||||||
|
bool isComplete() override;
|
||||||
|
Ptr<Texture> getColorTexture(int attachment = 0) const override;
|
||||||
|
Ptr<Texture> getDepthTexture() const override;
|
||||||
|
int getWidth() const override { return width_; }
|
||||||
|
int getHeight() const override { return height_; }
|
||||||
|
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||||
|
bool isValid() const override { return fboID_ != 0; }
|
||||||
|
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(fboID_); }
|
||||||
|
void clear(const Color& color, bool clearColor = true,
|
||||||
|
bool clearDepth = true, bool clearStencil = false) override;
|
||||||
|
void setViewport(int x, int y, int width, int height) override;
|
||||||
|
bool readPixels(int x, int y, int width, int height,
|
||||||
|
std::vector<uint8_t>& outData) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL FBO ID
|
||||||
|
* @return FBO ID
|
||||||
|
*/
|
||||||
|
GLuint getFboID() const { return fboID_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建带内置纹理的帧缓冲(便捷方法)
|
||||||
|
* @param width 宽度
|
||||||
|
* @param height 高度
|
||||||
|
* @param colorFormat 颜色格式
|
||||||
|
* @param depthFormat 深度格式(可选)
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool createWithTextures(int width, int height,
|
||||||
|
PixelFormat colorFormat = PixelFormat::RGBA8,
|
||||||
|
PixelFormat depthFormat = PixelFormat::Depth24);
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint fboID_ = 0;
|
||||||
|
int width_ = 0;
|
||||||
|
int height_ = 0;
|
||||||
|
int numColorAttachments_ = 1;
|
||||||
|
bool hasDepth_ = false;
|
||||||
|
bool hasStencil_ = false;
|
||||||
|
|
||||||
|
// 附件纹理
|
||||||
|
std::array<Ptr<Texture>, MAX_COLOR_ATTACHMENTS> colorTextures_;
|
||||||
|
Ptr<Texture> depthTexture_;
|
||||||
|
Ptr<Texture> depthStencilTexture_;
|
||||||
|
|
||||||
|
// 是否为内置纹理(需要自动清理)
|
||||||
|
bool hasInternalTextures_ = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查并更新完整状态
|
||||||
|
*/
|
||||||
|
bool checkStatus();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 附件枚举
|
||||||
|
*/
|
||||||
|
static GLenum getColorAttachment(int index);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/graphics/resources/pipeline.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OpenGL 管线状态实现
|
||||||
|
// ============================================================================
|
||||||
|
class GLPipeline : public Pipeline {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
*/
|
||||||
|
GLPipeline();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构函数
|
||||||
|
*/
|
||||||
|
~GLPipeline() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化管线
|
||||||
|
* @param desc 管线描述
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool init(const PipelineDesc& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭管线,释放资源
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
// Pipeline 接口实现
|
||||||
|
void bind() override;
|
||||||
|
void unbind() override;
|
||||||
|
void setBlendMode(BlendMode mode) override;
|
||||||
|
BlendMode getBlendMode() const override { return blendMode_; }
|
||||||
|
void setDepthTest(bool enabled) override;
|
||||||
|
void setDepthWrite(bool enabled) override;
|
||||||
|
void setDepthFunc(DepthFunc func) override;
|
||||||
|
void setCullMode(CullMode mode) override;
|
||||||
|
bool isValid() const override { return initialized_; }
|
||||||
|
uintptr_t getNativeHandle() const override { return 0; } // OpenGL 管线没有单一句柄
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置视口
|
||||||
|
* @param x 视口左下角X坐标
|
||||||
|
* @param y 视口左下角Y坐标
|
||||||
|
* @param width 视口宽度
|
||||||
|
* @param height 视口高度
|
||||||
|
*/
|
||||||
|
void setViewport(int x, int y, int width, int height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取当前视口
|
||||||
|
* @param x 输出X坐标
|
||||||
|
* @param y 输出Y坐标
|
||||||
|
* @param width 输出宽度
|
||||||
|
* @param height 输出高度
|
||||||
|
*/
|
||||||
|
void getViewport(int& x, int& y, int& width, int& height) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用所有状态(用于初始化或重置)
|
||||||
|
*/
|
||||||
|
void applyAllStates();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initialized_ = false;
|
||||||
|
|
||||||
|
// 当前状态
|
||||||
|
BlendMode blendMode_ = BlendMode::Alpha;
|
||||||
|
bool blendEnabled_ = true;
|
||||||
|
bool depthTest_ = false;
|
||||||
|
bool depthWrite_ = false;
|
||||||
|
DepthFunc depthFunc_ = DepthFunc::Less;
|
||||||
|
CullMode cullMode_ = CullMode::None;
|
||||||
|
|
||||||
|
// 视口
|
||||||
|
int viewportX_ = 0;
|
||||||
|
int viewportY_ = 0;
|
||||||
|
int viewportWidth_ = 0;
|
||||||
|
int viewportHeight_ = 0;
|
||||||
|
|
||||||
|
// 状态缓存(避免冗余 GL 调用)
|
||||||
|
BlendMode cachedBlendMode_ = BlendMode::None;
|
||||||
|
bool cachedBlendEnabled_ = false;
|
||||||
|
bool cachedDepthTest_ = false;
|
||||||
|
bool cachedDepthWrite_ = false;
|
||||||
|
DepthFunc cachedDepthFunc_ = DepthFunc::Less;
|
||||||
|
CullMode cachedCullMode_ = CullMode::None;
|
||||||
|
int cachedViewportX_ = -1;
|
||||||
|
int cachedViewportY_ = -1;
|
||||||
|
int cachedViewportWidth_ = -1;
|
||||||
|
int cachedViewportHeight_ = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用混合状态
|
||||||
|
*/
|
||||||
|
void applyBlendState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用深度状态
|
||||||
|
*/
|
||||||
|
void applyDepthState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用裁剪状态
|
||||||
|
*/
|
||||||
|
void applyCullState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 转换混合模式到 OpenGL 枚举
|
||||||
|
*/
|
||||||
|
static void getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 转换深度函数到 OpenGL 枚举
|
||||||
|
*/
|
||||||
|
static GLenum convertDepthFunc(DepthFunc func);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 转换裁剪模式到 OpenGL 枚举
|
||||||
|
*/
|
||||||
|
static GLenum convertCullMode(CullMode mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||||
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
||||||
#include <extra2d/graphics/core/render_backend.h>
|
#include <extra2d/graphics/core/render_backend.h>
|
||||||
#include <extra2d/graphics/shader/shader_interface.h>
|
#include <extra2d/graphics/shader/shader_interface.h>
|
||||||
|
|
@ -10,7 +13,10 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
class IWindow;
|
class IWindow;
|
||||||
|
class GLContext;
|
||||||
|
class GLFramebuffer;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// OpenGL 渲染器实现
|
// OpenGL 渲染器实现
|
||||||
|
|
@ -77,6 +83,42 @@ public:
|
||||||
Stats getStats() const override { return stats_; }
|
Stats getStats() const override { return stats_; }
|
||||||
void resetStats() override;
|
void resetStats() override;
|
||||||
|
|
||||||
|
// GLFramebuffer 相关方法
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建帧缓冲对象
|
||||||
|
* @param desc 帧缓冲描述
|
||||||
|
* @return 创建的帧缓冲智能指针
|
||||||
|
*/
|
||||||
|
Ptr<GLFramebuffer> createFramebuffer(const FramebufferDesc& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定帧缓冲(作为渲染目标)
|
||||||
|
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
|
||||||
|
*/
|
||||||
|
void bindFramebuffer(GLFramebuffer* framebuffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||||
|
*/
|
||||||
|
void unbindFramebuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认帧缓冲
|
||||||
|
* @return 默认帧缓冲智能指针
|
||||||
|
*/
|
||||||
|
Ptr<GLFramebuffer> getDefaultFramebuffer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除当前绑定的帧缓冲
|
||||||
|
* @param color 清除颜色
|
||||||
|
* @param clearColor 是否清除颜色缓冲
|
||||||
|
* @param clearDepth 是否清除深度缓冲
|
||||||
|
* @param clearStencil 是否清除模板缓冲
|
||||||
|
*/
|
||||||
|
void clearFramebuffer(const Color& color, bool clearColor = true,
|
||||||
|
bool clearDepth = true, bool clearStencil = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 形状批处理常量
|
// 形状批处理常量
|
||||||
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
|
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
|
||||||
|
|
@ -93,10 +135,10 @@ private:
|
||||||
GLSpriteBatch spriteBatch_;
|
GLSpriteBatch spriteBatch_;
|
||||||
Ptr<IShader> shapeShader_;
|
Ptr<IShader> shapeShader_;
|
||||||
|
|
||||||
GLuint shapeVao_;
|
GLuint shapeVao_; // 形状 VAO(手动管理,用于顶点属性配置)
|
||||||
GLuint shapeVbo_;
|
GLBuffer shapeBuffer_; // 形状 VBO(使用 GLBuffer 管理)
|
||||||
GLuint lineVao_; // 线条专用 VAO
|
GLuint lineVao_; // 线条 VAO(手动管理,用于顶点属性配置)
|
||||||
GLuint lineVbo_; // 线条专用 VBO
|
GLBuffer lineBuffer_; // 线条 VBO(使用 GLBuffer 管理)
|
||||||
|
|
||||||
glm::mat4 viewProjection_;
|
glm::mat4 viewProjection_;
|
||||||
std::vector<glm::mat4> transformStack_;
|
std::vector<glm::mat4> transformStack_;
|
||||||
|
|
@ -113,13 +155,8 @@ private:
|
||||||
size_t lineVertexCount_ = 0;
|
size_t lineVertexCount_ = 0;
|
||||||
float currentLineWidth_ = 1.0f;
|
float currentLineWidth_ = 1.0f;
|
||||||
|
|
||||||
// OpenGL 状态缓存
|
// OpenGL 管线状态管理
|
||||||
BlendMode cachedBlendMode_ = BlendMode::None;
|
GLPipeline pipeline_;
|
||||||
bool blendEnabled_ = false;
|
|
||||||
int cachedViewportX_ = 0;
|
|
||||||
int cachedViewportY_ = 0;
|
|
||||||
int cachedViewportWidth_ = 0;
|
|
||||||
int cachedViewportHeight_ = 0;
|
|
||||||
|
|
||||||
// 自动批处理状态
|
// 自动批处理状态
|
||||||
bool batchActive_ = false; // 批处理是否激活
|
bool batchActive_ = false; // 批处理是否激活
|
||||||
|
|
@ -128,6 +165,10 @@ private:
|
||||||
std::vector<SpriteData> pendingSprites_; // 待提交的精灵
|
std::vector<SpriteData> pendingSprites_; // 待提交的精灵
|
||||||
static constexpr size_t MAX_BATCH_SPRITES = 1000; // 最大批处理精灵数
|
static constexpr size_t MAX_BATCH_SPRITES = 1000; // 最大批处理精灵数
|
||||||
|
|
||||||
|
// 帧缓冲管理
|
||||||
|
mutable Ptr<GLFramebuffer> defaultFramebuffer_; // 默认帧缓冲(延迟创建)
|
||||||
|
GLFramebuffer* currentFramebuffer_ = nullptr; // 当前绑定的帧缓冲
|
||||||
|
|
||||||
void initShapeRendering();
|
void initShapeRendering();
|
||||||
void ensureBatchActive(); // 确保批处理已激活
|
void ensureBatchActive(); // 确保批处理已激活
|
||||||
void submitPendingSprites(); // 提交待处理的精灵
|
void submitPendingSprites(); // 提交待处理的精灵
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||||
#include <extra2d/graphics/shader/shader_interface.h>
|
#include <extra2d/graphics/shader/shader_interface.h>
|
||||||
|
|
@ -39,8 +40,8 @@ public:
|
||||||
private:
|
private:
|
||||||
// OpenGL 对象
|
// OpenGL 对象
|
||||||
GLuint vao_;
|
GLuint vao_;
|
||||||
GLuint vbo_;
|
GLBuffer vbo_; // 顶点缓冲区(动态)
|
||||||
GLuint ebo_;
|
GLBuffer ebo_; // 索引缓冲区(静态)
|
||||||
|
|
||||||
// 后端无关的批处理层
|
// 后端无关的批处理层
|
||||||
SpriteBatch batch_;
|
SpriteBatch batch_;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/math_types.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 形状顶点结构
|
||||||
|
// ============================================================================
|
||||||
|
struct ShapeVertex {
|
||||||
|
float x, y; // 位置
|
||||||
|
float r, g, b, a; // 颜色
|
||||||
|
|
||||||
|
ShapeVertex() = default;
|
||||||
|
ShapeVertex(float px, float py, const Color& c)
|
||||||
|
: x(px), y(py), r(c.r), g(c.g), b(c.b), a(c.a) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 形状批处理抽象接口 - 后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class ShapeBatch {
|
||||||
|
public:
|
||||||
|
virtual ~ShapeBatch() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化形状批处理
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
virtual bool init() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭形状批处理,释放资源
|
||||||
|
*/
|
||||||
|
virtual void shutdown() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 开始批处理
|
||||||
|
* @param viewProjection 视图投影矩阵
|
||||||
|
*/
|
||||||
|
virtual void begin(const glm::mat4& viewProjection) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结束批处理并提交绘制
|
||||||
|
*/
|
||||||
|
virtual void end() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制线段
|
||||||
|
* @param start 起点
|
||||||
|
* @param end 终点
|
||||||
|
* @param color 颜色
|
||||||
|
* @param width 线宽
|
||||||
|
*/
|
||||||
|
virtual void drawLine(const Vec2& start, const Vec2& end,
|
||||||
|
const Color& color, float width = 1.0f) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制矩形边框
|
||||||
|
* @param rect 矩形区域
|
||||||
|
* @param color 颜色
|
||||||
|
* @param width 边框宽度
|
||||||
|
*/
|
||||||
|
virtual void drawRect(const Rect& rect, const Color& color,
|
||||||
|
float width = 1.0f) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 填充矩形
|
||||||
|
* @param rect 矩形区域
|
||||||
|
* @param color 颜色
|
||||||
|
*/
|
||||||
|
virtual void fillRect(const Rect& rect, const Color& color) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制圆形边框
|
||||||
|
* @param center 圆心
|
||||||
|
* @param radius 半径
|
||||||
|
* @param color 颜色
|
||||||
|
* @param segments 分段数
|
||||||
|
* @param width 边框宽度
|
||||||
|
*/
|
||||||
|
virtual void drawCircle(const Vec2& center, float radius,
|
||||||
|
const Color& color, int segments = 32,
|
||||||
|
float width = 1.0f) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 填充圆形
|
||||||
|
* @param center 圆心
|
||||||
|
* @param radius 半径
|
||||||
|
* @param color 颜色
|
||||||
|
* @param segments 分段数
|
||||||
|
*/
|
||||||
|
virtual void fillCircle(const Vec2& center, float radius,
|
||||||
|
const Color& color, int segments = 32) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制三角形边框
|
||||||
|
* @param p1 顶点1
|
||||||
|
* @param p2 顶点2
|
||||||
|
* @param p3 顶点3
|
||||||
|
* @param color 颜色
|
||||||
|
* @param width 边框宽度
|
||||||
|
*/
|
||||||
|
virtual void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||||
|
const Color& color, float width = 1.0f) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 填充三角形
|
||||||
|
* @param p1 顶点1
|
||||||
|
* @param p2 顶点2
|
||||||
|
* @param p3 顶点3
|
||||||
|
* @param color 颜色
|
||||||
|
*/
|
||||||
|
virtual void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||||
|
const Color& color) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制多边形边框
|
||||||
|
* @param points 顶点数组
|
||||||
|
* @param color 颜色
|
||||||
|
* @param width 边框宽度
|
||||||
|
*/
|
||||||
|
virtual void drawPolygon(const std::vector<Vec2>& points,
|
||||||
|
const Color& color, float width = 1.0f) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 填充多边形
|
||||||
|
* @param points 顶点数组
|
||||||
|
* @param color 颜色
|
||||||
|
*/
|
||||||
|
virtual void fillPolygon(const std::vector<Vec2>& points,
|
||||||
|
const Color& color) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取绘制调用次数
|
||||||
|
* @return 绘制调用次数
|
||||||
|
*/
|
||||||
|
virtual uint32_t getDrawCallCount() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置绘制调用计数
|
||||||
|
*/
|
||||||
|
virtual void resetDrawCallCount() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <extra2d/core/color.h>
|
#include <extra2d/core/color.h>
|
||||||
#include <extra2d/core/math_types.h>
|
#include <extra2d/core/math_types.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
|
#include <extra2d/graphics/resources/pipeline.h>
|
||||||
#include <glm/mat4x4.hpp>
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -24,15 +25,7 @@ enum class BackendType {
|
||||||
// D3D12
|
// D3D12
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// BlendMode 定义在 pipeline.h 中
|
||||||
// 混合模式
|
|
||||||
// ============================================================================
|
|
||||||
enum class BlendMode {
|
|
||||||
None, // 不混合
|
|
||||||
Alpha, // 标准 Alpha 混合
|
|
||||||
Additive, // 加法混合
|
|
||||||
Multiply // 乘法混合
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 渲染后端抽象接口
|
// 渲染后端抽象接口
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 缓冲区类型枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class BufferType {
|
||||||
|
Vertex, // 顶点缓冲
|
||||||
|
Index, // 索引缓冲
|
||||||
|
Uniform // 统一缓冲
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 缓冲区使用模式枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class BufferUsage {
|
||||||
|
Static, // 静态数据,很少更新
|
||||||
|
Dynamic, // 动态数据,频繁更新
|
||||||
|
Stream // 流式数据,每帧更新
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 缓冲区描述结构
|
||||||
|
// ============================================================================
|
||||||
|
struct BufferDesc {
|
||||||
|
BufferType type = BufferType::Vertex;
|
||||||
|
BufferUsage usage = BufferUsage::Static;
|
||||||
|
size_t size = 0; // 缓冲区大小(字节)
|
||||||
|
const void* initialData = nullptr; // 初始数据
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 缓冲区抽象接口 - 渲染后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class Buffer {
|
||||||
|
public:
|
||||||
|
virtual ~Buffer() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定缓冲区
|
||||||
|
*/
|
||||||
|
virtual void bind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑缓冲区
|
||||||
|
*/
|
||||||
|
virtual void unbind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置缓冲区数据(完全替换)
|
||||||
|
* @param data 数据指针
|
||||||
|
* @param size 数据大小(字节)
|
||||||
|
*/
|
||||||
|
virtual void setData(const void* data, size_t size) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新缓冲区部分数据
|
||||||
|
* @param data 数据指针
|
||||||
|
* @param offset 偏移量(字节)
|
||||||
|
* @param size 数据大小(字节)
|
||||||
|
*/
|
||||||
|
virtual void updateData(const void* data, size_t offset, size_t size) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 映射缓冲区到内存(用于直接写入)
|
||||||
|
* @return 映射后的内存指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
virtual void* map() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解除缓冲区映射
|
||||||
|
*/
|
||||||
|
virtual void unmap() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓冲区大小
|
||||||
|
* @return 缓冲区大小(字节)
|
||||||
|
*/
|
||||||
|
virtual size_t getSize() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓冲区类型
|
||||||
|
* @return 缓冲区类型
|
||||||
|
*/
|
||||||
|
virtual BufferType getType() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缓冲区使用模式
|
||||||
|
* @return 使用模式
|
||||||
|
*/
|
||||||
|
virtual BufferUsage getUsage() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查缓冲区是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取原生句柄(后端特定)
|
||||||
|
* @return 原生句柄值
|
||||||
|
*/
|
||||||
|
virtual uintptr_t getNativeHandle() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/math_types.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <extra2d/graphics/texture/texture.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 字形信息结构
|
||||||
|
// ============================================================================
|
||||||
|
struct Glyph {
|
||||||
|
float width = 0; // 字形宽度
|
||||||
|
float height = 0; // 字形高度
|
||||||
|
float bearingX = 0; // 水平偏移
|
||||||
|
float bearingY = 0; // 垂直偏移(从基线到字形顶部)
|
||||||
|
float advance = 0; // 水平步进
|
||||||
|
float u0 = 0, v0 = 0; // 纹理坐标左下角
|
||||||
|
float u1 = 0, v1 = 0; // 纹理坐标右上角
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 字体图集描述结构
|
||||||
|
// ============================================================================
|
||||||
|
struct FontAtlasDesc {
|
||||||
|
std::string filepath; // 字体文件路径
|
||||||
|
int fontSize = 16; // 字体大小
|
||||||
|
bool useSDF = false; // 是否使用SDF渲染
|
||||||
|
int atlasSize = 512; // 图集大小
|
||||||
|
int padding = 2; // 字形间距
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 字体图集抽象接口 - 渲染后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class FontAtlas {
|
||||||
|
public:
|
||||||
|
virtual ~FontAtlas() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化字体图集
|
||||||
|
* @param desc 字体图集描述
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
virtual bool init(const FontAtlasDesc& desc) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭字体图集,释放资源
|
||||||
|
*/
|
||||||
|
virtual void shutdown() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取字形信息
|
||||||
|
* @param codepoint Unicode 码点
|
||||||
|
* @return 字形信息指针,未找到返回 nullptr
|
||||||
|
*/
|
||||||
|
virtual const Glyph* getGlyph(char32_t codepoint) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取纹理
|
||||||
|
* @return 纹理对象
|
||||||
|
*/
|
||||||
|
virtual Ptr<Texture> getTexture() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取字体大小
|
||||||
|
* @return 字体大小
|
||||||
|
*/
|
||||||
|
virtual int getFontSize() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取行高
|
||||||
|
* @return 行高
|
||||||
|
*/
|
||||||
|
virtual float getLineHeight() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取基线到顶部的距离
|
||||||
|
* @return 上升高度
|
||||||
|
*/
|
||||||
|
virtual float getAscent() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取基线到底部的距离
|
||||||
|
* @return 下降高度
|
||||||
|
*/
|
||||||
|
virtual float getDescent() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算文本宽度
|
||||||
|
* @param text 文本内容
|
||||||
|
* @return 文本宽度
|
||||||
|
*/
|
||||||
|
virtual float measureText(const std::string& text) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算文本尺寸
|
||||||
|
* @param text 文本内容
|
||||||
|
* @return 文本尺寸
|
||||||
|
*/
|
||||||
|
virtual Size measureTextSize(const std::string& text) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 是否使用SDF渲染
|
||||||
|
* @return 使用SDF返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isSDF() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查字体图集是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 预加载字符到图集
|
||||||
|
* @param text 需要预加载的文本
|
||||||
|
* @return 成功加载的字符数
|
||||||
|
*/
|
||||||
|
virtual int preloadGlyphs(const std::string& text) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清空已加载的字形缓存
|
||||||
|
*/
|
||||||
|
virtual void clearCache() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <extra2d/graphics/texture/texture.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 帧缓冲描述结构
|
||||||
|
// ============================================================================
|
||||||
|
struct FramebufferDesc {
|
||||||
|
int width = 0; // 帧缓冲宽度
|
||||||
|
int height = 0; // 帧缓冲高度
|
||||||
|
int colorAttachments = 1; // 颜色附件数量
|
||||||
|
bool hasDepth = false; // 是否有深度附件
|
||||||
|
bool hasStencil = false; // 是否有模板附件
|
||||||
|
bool multisample = false; // 是否多重采样
|
||||||
|
int samples = 4; // 采样数(多重采样时有效)
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 帧缓冲抽象接口 - 渲染后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class Framebuffer {
|
||||||
|
public:
|
||||||
|
virtual ~Framebuffer() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定帧缓冲(作为渲染目标)
|
||||||
|
*/
|
||||||
|
virtual void bind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||||
|
*/
|
||||||
|
virtual void unbind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 附加颜色纹理
|
||||||
|
* @param texture 纹理对象
|
||||||
|
* @param attachment 附件索引(0-7)
|
||||||
|
*/
|
||||||
|
virtual void attachColorTexture(Ptr<Texture> texture, int attachment = 0) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 附加深度纹理
|
||||||
|
* @param texture 纹理对象
|
||||||
|
*/
|
||||||
|
virtual void attachDepthTexture(Ptr<Texture> texture) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 附加深度模板纹理
|
||||||
|
* @param texture 纹理对象
|
||||||
|
*/
|
||||||
|
virtual void attachDepthStencilTexture(Ptr<Texture> texture) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查帧缓冲是否完整
|
||||||
|
* @return 完整返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isComplete() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取颜色附件纹理
|
||||||
|
* @param attachment 附件索引
|
||||||
|
* @return 纹理对象
|
||||||
|
*/
|
||||||
|
virtual Ptr<Texture> getColorTexture(int attachment = 0) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取深度附件纹理
|
||||||
|
* @return 纹理对象
|
||||||
|
*/
|
||||||
|
virtual Ptr<Texture> getDepthTexture() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取帧缓冲宽度
|
||||||
|
* @return 宽度
|
||||||
|
*/
|
||||||
|
virtual int getWidth() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取帧缓冲高度
|
||||||
|
* @return 高度
|
||||||
|
*/
|
||||||
|
virtual int getHeight() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取尺寸
|
||||||
|
* @return 尺寸
|
||||||
|
*/
|
||||||
|
virtual Size getSize() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查帧缓冲是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取原生句柄(后端特定)
|
||||||
|
* @return 原生句柄值
|
||||||
|
*/
|
||||||
|
virtual uintptr_t getNativeHandle() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除帧缓冲
|
||||||
|
* @param color 清除颜色
|
||||||
|
* @param clearColor 是否清除颜色缓冲
|
||||||
|
* @param clearDepth 是否清除深度缓冲
|
||||||
|
* @param clearStencil 是否清除模板缓冲
|
||||||
|
*/
|
||||||
|
virtual void clear(const Color& color, bool clearColor = true,
|
||||||
|
bool clearDepth = true, bool clearStencil = false) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置视口
|
||||||
|
* @param x 视口左下角X坐标
|
||||||
|
* @param y 视口左下角Y坐标
|
||||||
|
* @param width 视口宽度
|
||||||
|
* @param height 视口高度
|
||||||
|
*/
|
||||||
|
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取像素数据
|
||||||
|
* @param x 起始X坐标
|
||||||
|
* @param y 起始Y坐标
|
||||||
|
* @param width 宽度
|
||||||
|
* @param height 高度
|
||||||
|
* @param outData 输出数据缓冲区
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
virtual bool readPixels(int x, int y, int width, int height,
|
||||||
|
std::vector<uint8_t>& outData) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 混合模式枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class BlendMode {
|
||||||
|
None, // 不混合
|
||||||
|
Alpha, // 标准 Alpha 混合
|
||||||
|
Additive, // 加法混合
|
||||||
|
Multiply // 乘法混合
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 深度测试函数枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class DepthFunc {
|
||||||
|
Never, // 永不通过
|
||||||
|
Less, // 小于
|
||||||
|
Equal, // 等于
|
||||||
|
LessEqual, // 小于等于
|
||||||
|
Greater, // 大于
|
||||||
|
NotEqual, // 不等于
|
||||||
|
GreaterEqual,// 大于等于
|
||||||
|
Always // 总是通过
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 裁剪模式枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class CullMode {
|
||||||
|
None, // 不裁剪
|
||||||
|
Front, // 裁剪正面
|
||||||
|
Back, // 裁剪背面
|
||||||
|
Both // 裁剪双面
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 顶点属性格式枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class VertexFormat {
|
||||||
|
Float1, // 1个float
|
||||||
|
Float2, // 2个float
|
||||||
|
Float3, // 3个float
|
||||||
|
Float4, // 4个float
|
||||||
|
Byte4, // 4个byte
|
||||||
|
UByte4, // 4个ubyte
|
||||||
|
Short2, // 2个short
|
||||||
|
Short4 // 4个short
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 顶点属性描述
|
||||||
|
// ============================================================================
|
||||||
|
struct VertexAttribute {
|
||||||
|
uint32_t location = 0; // 属性位置
|
||||||
|
VertexFormat format = VertexFormat::Float3; // 数据格式
|
||||||
|
uint32_t offset = 0; // 在顶点结构中的偏移
|
||||||
|
uint32_t stride = 0; // 顶点结构大小
|
||||||
|
bool normalized = false; // 是否归一化
|
||||||
|
|
||||||
|
VertexAttribute() = default;
|
||||||
|
VertexAttribute(uint32_t loc, VertexFormat fmt, uint32_t off, uint32_t str, bool norm = false)
|
||||||
|
: location(loc), format(fmt), offset(off), stride(str), normalized(norm) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 管线描述结构
|
||||||
|
// ============================================================================
|
||||||
|
struct PipelineDesc {
|
||||||
|
// 混合状态
|
||||||
|
BlendMode blendMode = BlendMode::Alpha;
|
||||||
|
bool blendEnabled = true;
|
||||||
|
|
||||||
|
// 深度状态
|
||||||
|
bool depthTest = false;
|
||||||
|
bool depthWrite = false;
|
||||||
|
DepthFunc depthFunc = DepthFunc::Less;
|
||||||
|
|
||||||
|
// 裁剪状态
|
||||||
|
CullMode cullMode = CullMode::None;
|
||||||
|
|
||||||
|
// 顶点布局
|
||||||
|
std::vector<VertexAttribute> vertexAttributes;
|
||||||
|
|
||||||
|
// 着色器(由后端特定实现设置)
|
||||||
|
void* vertexShader = nullptr;
|
||||||
|
void* fragmentShader = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 渲染管线抽象接口 - 渲染后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class Pipeline {
|
||||||
|
public:
|
||||||
|
virtual ~Pipeline() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定管线
|
||||||
|
*/
|
||||||
|
virtual void bind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑管线
|
||||||
|
*/
|
||||||
|
virtual void unbind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置混合模式
|
||||||
|
* @param mode 混合模式
|
||||||
|
*/
|
||||||
|
virtual void setBlendMode(BlendMode mode) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取当前混合模式
|
||||||
|
* @return 混合模式
|
||||||
|
*/
|
||||||
|
virtual BlendMode getBlendMode() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置深度测试
|
||||||
|
* @param enabled 是否启用
|
||||||
|
*/
|
||||||
|
virtual void setDepthTest(bool enabled) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置深度写入
|
||||||
|
* @param enabled 是否启用
|
||||||
|
*/
|
||||||
|
virtual void setDepthWrite(bool enabled) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置深度测试函数
|
||||||
|
* @param func 深度测试函数
|
||||||
|
*/
|
||||||
|
virtual void setDepthFunc(DepthFunc func) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置裁剪模式
|
||||||
|
* @param mode 裁剪模式
|
||||||
|
*/
|
||||||
|
virtual void setCullMode(CullMode mode) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查管线是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取原生句柄(后端特定)
|
||||||
|
* @return 原生句柄值
|
||||||
|
*/
|
||||||
|
virtual uintptr_t getNativeHandle() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 着色器类型枚举
|
||||||
|
// ============================================================================
|
||||||
|
enum class ShaderType {
|
||||||
|
Vertex, // 顶点着色器
|
||||||
|
Fragment, // 片段着色器
|
||||||
|
Geometry, // 几何着色器
|
||||||
|
Compute // 计算着色器
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 着色器描述结构
|
||||||
|
// ============================================================================
|
||||||
|
struct ShaderDesc {
|
||||||
|
std::string name; // 着色器名称
|
||||||
|
std::string vertexSource; // 顶点着色器源码
|
||||||
|
std::string fragmentSource; // 片段着色器源码
|
||||||
|
std::string geometrySource; // 几何着色器源码(可选)
|
||||||
|
std::vector<uint8_t> binaryData; // 预编译二进制数据(可选)
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 着色器抽象接口 - 渲染后端无关
|
||||||
|
// ============================================================================
|
||||||
|
class Shader {
|
||||||
|
public:
|
||||||
|
virtual ~Shader() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定着色器程序
|
||||||
|
*/
|
||||||
|
virtual void bind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑着色器程序
|
||||||
|
*/
|
||||||
|
virtual void unbind() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置布尔类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 布尔值
|
||||||
|
*/
|
||||||
|
virtual void setBool(const std::string& name, bool value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置整数类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 整数值
|
||||||
|
*/
|
||||||
|
virtual void setInt(const std::string& name, int value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置浮点类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 浮点值
|
||||||
|
*/
|
||||||
|
virtual void setFloat(const std::string& name, float value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置二维向量类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 二维向量值
|
||||||
|
*/
|
||||||
|
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置三维向量类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 三维向量值
|
||||||
|
*/
|
||||||
|
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置四维向量类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 四维向量值
|
||||||
|
*/
|
||||||
|
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 4x4 矩阵类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param value 4x4 矩阵值
|
||||||
|
*/
|
||||||
|
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色类型 uniform 变量
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param color 颜色值
|
||||||
|
*/
|
||||||
|
virtual void setColor(const std::string& name, const Color& color) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置纹理采样器
|
||||||
|
* @param name uniform 变量名
|
||||||
|
* @param slot 纹理槽位
|
||||||
|
*/
|
||||||
|
virtual void setTexture(const std::string& name, int slot) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取着色器名称
|
||||||
|
* @return 着色器名称
|
||||||
|
*/
|
||||||
|
virtual const std::string& getName() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查着色器是否有效
|
||||||
|
* @return 有效返回 true
|
||||||
|
*/
|
||||||
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取原生句柄(后端特定)
|
||||||
|
* @return 原生句柄值
|
||||||
|
*/
|
||||||
|
virtual uintptr_t getNativeHandle() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||||
|
#include <extra2d/graphics/memory/vram_manager.h>
|
||||||
|
#include <extra2d/utils/logger.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GLBuffer 实现
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
GLBuffer::GLBuffer() = default;
|
||||||
|
|
||||||
|
GLBuffer::~GLBuffer() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLBuffer::init(const BufferDesc& desc) {
|
||||||
|
if (bufferID_ != 0) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
type_ = desc.type;
|
||||||
|
usage_ = desc.usage;
|
||||||
|
size_ = desc.size;
|
||||||
|
target_ = convertType(type_);
|
||||||
|
glUsage_ = convertUsage(usage_);
|
||||||
|
|
||||||
|
// 生成缓冲区
|
||||||
|
glGenBuffers(1, &bufferID_);
|
||||||
|
if (bufferID_ == 0) {
|
||||||
|
E2D_LOG_ERROR("Failed to generate OpenGL buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定并分配缓冲区
|
||||||
|
glBindBuffer(target_, bufferID_);
|
||||||
|
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData, glUsage_);
|
||||||
|
glBindBuffer(target_, 0);
|
||||||
|
|
||||||
|
// 追踪显存使用
|
||||||
|
VRAMMgr::get().allocBuffer(size_);
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("GLBuffer created: ID={}, Size={}, Type={}, Usage={}",
|
||||||
|
bufferID_, size_, static_cast<int>(type_), static_cast<int>(usage_));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::shutdown() {
|
||||||
|
if (bufferID_ != 0) {
|
||||||
|
if (mapped_) {
|
||||||
|
unmap();
|
||||||
|
}
|
||||||
|
// 释放显存追踪
|
||||||
|
VRAMMgr::get().freeBuffer(size_);
|
||||||
|
glDeleteBuffers(1, &bufferID_);
|
||||||
|
E2D_LOG_DEBUG("GLBuffer destroyed: ID={}", bufferID_);
|
||||||
|
bufferID_ = 0;
|
||||||
|
}
|
||||||
|
size_ = 0;
|
||||||
|
mapped_ = false;
|
||||||
|
mappedPtr_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::bind() {
|
||||||
|
if (bufferID_ != 0) {
|
||||||
|
glBindBuffer(target_, bufferID_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::unbind() {
|
||||||
|
glBindBuffer(target_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::setData(const void* data, size_t size) {
|
||||||
|
if (bufferID_ == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
// 如果大小相同,使用 glBufferSubData 更高效
|
||||||
|
if (size == size_) {
|
||||||
|
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
|
||||||
|
} else {
|
||||||
|
// 大小不同,重新分配
|
||||||
|
size_ = size;
|
||||||
|
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::updateData(const void* data, size_t offset, size_t size) {
|
||||||
|
if (bufferID_ == 0 || data == nullptr || size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + size > size_) {
|
||||||
|
E2D_LOG_WARN("GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
|
||||||
|
offset, size, size_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
glBufferSubData(target_, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), data);
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GLBuffer::map() {
|
||||||
|
if (bufferID_ == 0 || mapped_) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
// 使用 glMapBufferRange 替代 glMapBuffer,更现代且安全
|
||||||
|
GLbitfield access = GL_MAP_WRITE_BIT;
|
||||||
|
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
|
||||||
|
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
|
||||||
|
}
|
||||||
|
|
||||||
|
mappedPtr_ = glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
|
||||||
|
if (mappedPtr_) {
|
||||||
|
mapped_ = true;
|
||||||
|
} else {
|
||||||
|
E2D_LOG_ERROR("Failed to map GLBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedPtr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBuffer::unmap() {
|
||||||
|
if (!mapped_ || bufferID_ == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUnmapBuffer(target_);
|
||||||
|
mapped_ = false;
|
||||||
|
mappedPtr_ = nullptr;
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum GLBuffer::convertUsage(BufferUsage usage) {
|
||||||
|
switch (usage) {
|
||||||
|
case BufferUsage::Static:
|
||||||
|
return GL_STATIC_DRAW;
|
||||||
|
case BufferUsage::Dynamic:
|
||||||
|
return GL_DYNAMIC_DRAW;
|
||||||
|
case BufferUsage::Stream:
|
||||||
|
return GL_STREAM_DRAW;
|
||||||
|
default:
|
||||||
|
return GL_STATIC_DRAW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum GLBuffer::convertType(BufferType type) {
|
||||||
|
switch (type) {
|
||||||
|
case BufferType::Vertex:
|
||||||
|
return GL_ARRAY_BUFFER;
|
||||||
|
case BufferType::Index:
|
||||||
|
return GL_ELEMENT_ARRAY_BUFFER;
|
||||||
|
case BufferType::Uniform:
|
||||||
|
return GL_UNIFORM_BUFFER;
|
||||||
|
default:
|
||||||
|
return GL_ARRAY_BUFFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_context.h>
|
||||||
|
#include <extra2d/graphics/memory/gpu_context.h>
|
||||||
|
#include <extra2d/utils/logger.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GLContext 实现
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
GLContext& GLContext::get() {
|
||||||
|
static GLContext instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::init() {
|
||||||
|
if (initialized_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 OpenGL 版本
|
||||||
|
parseVersion();
|
||||||
|
|
||||||
|
// 加载扩展(GLAD 已在 glad.c 中完成)
|
||||||
|
if (!loadExtensions()) {
|
||||||
|
E2D_LOG_ERROR("Failed to load OpenGL extensions");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
|
||||||
|
// 标记 GPU 上下文为有效
|
||||||
|
GPUContext::get().markValid();
|
||||||
|
|
||||||
|
E2D_LOG_INFO("OpenGL Context initialized");
|
||||||
|
E2D_LOG_INFO(" Version: {}", getVersionString());
|
||||||
|
E2D_LOG_INFO(" Vendor: {}", getVendor());
|
||||||
|
E2D_LOG_INFO(" Renderer: {}", getRenderer());
|
||||||
|
E2D_LOG_INFO(" Max Texture Size: {}", getMaxTextureSize());
|
||||||
|
E2D_LOG_INFO(" Max Texture Units: {}", getMaxTextureUnits());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::shutdown() {
|
||||||
|
// 标记 GPU 上下文为无效
|
||||||
|
GPUContext::get().markInvalid();
|
||||||
|
|
||||||
|
initialized_ = false;
|
||||||
|
version_ = GLVersion{};
|
||||||
|
maxTextureSize_ = -1;
|
||||||
|
maxTextureUnits_ = -1;
|
||||||
|
maxVertexAttribs_ = -1;
|
||||||
|
maxUniformBufferBindings_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GLContext::getVersionString() const {
|
||||||
|
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||||
|
return version ? version : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GLContext::getVendor() const {
|
||||||
|
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||||
|
return vendor ? vendor : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GLContext::getRenderer() const {
|
||||||
|
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||||
|
return renderer ? renderer : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::hasExtension(const std::string& extension) const {
|
||||||
|
GLint numExtensions = 0;
|
||||||
|
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
|
||||||
|
|
||||||
|
for (GLint i = 0; i < numExtensions; ++i) {
|
||||||
|
const char* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
|
||||||
|
if (ext && extension == ext) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GLContext::getMaxTextureSize() const {
|
||||||
|
if (maxTextureSize_ < 0) {
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
|
||||||
|
}
|
||||||
|
return maxTextureSize_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GLContext::getMaxTextureUnits() const {
|
||||||
|
if (maxTextureUnits_ < 0) {
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
|
||||||
|
}
|
||||||
|
return maxTextureUnits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GLContext::getMaxVertexAttribs() const {
|
||||||
|
if (maxVertexAttribs_ < 0) {
|
||||||
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
|
||||||
|
}
|
||||||
|
return maxVertexAttribs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GLContext::getMaxUniformBufferBindings() const {
|
||||||
|
if (maxUniformBufferBindings_ < 0) {
|
||||||
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
|
||||||
|
}
|
||||||
|
return maxUniformBufferBindings_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::hasVAO() const {
|
||||||
|
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
|
||||||
|
if (version_.es) {
|
||||||
|
return version_.major >= 3;
|
||||||
|
}
|
||||||
|
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::hasFBO() const {
|
||||||
|
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
|
||||||
|
if (version_.es) {
|
||||||
|
return version_.major >= 2;
|
||||||
|
}
|
||||||
|
return version_.major >= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::hasShader() const {
|
||||||
|
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
|
||||||
|
if (version_.es) {
|
||||||
|
return version_.major >= 2;
|
||||||
|
}
|
||||||
|
return version_.major >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::parseVersion() {
|
||||||
|
const char* versionStr = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||||
|
if (!versionStr) {
|
||||||
|
version_ = GLVersion{0, 0, false};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string version(versionStr);
|
||||||
|
|
||||||
|
// 检查是否为 OpenGL ES
|
||||||
|
if (version.find("OpenGL ES") != std::string::npos) {
|
||||||
|
version_.es = true;
|
||||||
|
// 解析 ES 版本号,格式如 "OpenGL ES 3.0"
|
||||||
|
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major, &version_.minor);
|
||||||
|
} else {
|
||||||
|
version_.es = false;
|
||||||
|
// 解析桌面版本号,格式如 "3.3.0 NVIDIA"
|
||||||
|
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLContext::loadExtensions() {
|
||||||
|
// GLAD 已经在 glad.c 中加载了所有扩展
|
||||||
|
// 这里可以添加额外的扩展检查
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||||
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GLFramebuffer 实现
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
GLFramebuffer::GLFramebuffer() = default;
|
||||||
|
|
||||||
|
GLFramebuffer::~GLFramebuffer() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLFramebuffer::init(const FramebufferDesc& desc) {
|
||||||
|
if (fboID_ != 0) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
width_ = desc.width;
|
||||||
|
height_ = desc.height;
|
||||||
|
numColorAttachments_ = desc.colorAttachments;
|
||||||
|
hasDepth_ = desc.hasDepth;
|
||||||
|
hasStencil_ = desc.hasStencil;
|
||||||
|
|
||||||
|
// 限制颜色附件数
|
||||||
|
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
|
||||||
|
numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 FBO
|
||||||
|
glGenFramebuffers(1, &fboID_);
|
||||||
|
if (fboID_ == 0) {
|
||||||
|
E2D_LOG_ERROR("Failed to generate OpenGL framebuffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("GLFramebuffer created: ID={}, Size={}x{}, ColorAttachments={}",
|
||||||
|
fboID_, width_, height_, numColorAttachments_);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::shutdown() {
|
||||||
|
if (fboID_ != 0) {
|
||||||
|
glDeleteFramebuffers(1, &fboID_);
|
||||||
|
E2D_LOG_DEBUG("GLFramebuffer destroyed: ID={}", fboID_);
|
||||||
|
fboID_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理纹理引用
|
||||||
|
for (auto& tex : colorTextures_) {
|
||||||
|
tex.reset();
|
||||||
|
}
|
||||||
|
depthTexture_.reset();
|
||||||
|
depthStencilTexture_.reset();
|
||||||
|
|
||||||
|
hasInternalTextures_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::bind() {
|
||||||
|
if (fboID_ != 0) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::unbind() {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) {
|
||||||
|
if (fboID_ == 0 || !texture || attachment < 0 || attachment >= MAX_COLOR_ATTACHMENTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
// 获取 OpenGL 纹理 ID
|
||||||
|
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
|
||||||
|
GL_TEXTURE_2D, texID, 0);
|
||||||
|
|
||||||
|
colorTextures_[attachment] = texture;
|
||||||
|
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) {
|
||||||
|
if (fboID_ == 0 || !texture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
// 获取 OpenGL 纹理 ID
|
||||||
|
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||||
|
GL_TEXTURE_2D, texID, 0);
|
||||||
|
|
||||||
|
depthTexture_ = texture;
|
||||||
|
hasDepth_ = true;
|
||||||
|
hasStencil_ = false;
|
||||||
|
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) {
|
||||||
|
if (fboID_ == 0 || !texture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
// 获取 OpenGL 纹理 ID
|
||||||
|
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
|
GL_TEXTURE_2D, texID, 0);
|
||||||
|
|
||||||
|
depthStencilTexture_ = texture;
|
||||||
|
hasDepth_ = true;
|
||||||
|
hasStencil_ = true;
|
||||||
|
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLFramebuffer::isComplete() {
|
||||||
|
if (fboID_ == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
bool complete = checkStatus();
|
||||||
|
unbind();
|
||||||
|
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const {
|
||||||
|
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
|
||||||
|
return colorTextures_[attachment];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> GLFramebuffer::getDepthTexture() const {
|
||||||
|
return depthTexture_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::clear(const Color& color, bool clearColor,
|
||||||
|
bool clearDepth, bool clearStencil) {
|
||||||
|
if (fboID_ == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
GLbitfield mask = 0;
|
||||||
|
|
||||||
|
if (clearColor) {
|
||||||
|
mask |= GL_COLOR_BUFFER_BIT;
|
||||||
|
glClearColor(color.r, color.g, color.b, color.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearDepth) {
|
||||||
|
mask |= GL_DEPTH_BUFFER_BIT;
|
||||||
|
glClearDepthf(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearStencil) {
|
||||||
|
mask |= GL_STENCIL_BUFFER_BIT;
|
||||||
|
glClearStencil(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask != 0) {
|
||||||
|
glClear(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLFramebuffer::setViewport(int x, int y, int width, int height) {
|
||||||
|
glViewport(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLFramebuffer::readPixels(int x, int y, int width, int height,
|
||||||
|
std::vector<uint8_t>& outData) {
|
||||||
|
if (fboID_ == 0 || width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算需要的缓冲区大小 (RGBA8)
|
||||||
|
size_t dataSize = width * height * 4;
|
||||||
|
outData.resize(dataSize);
|
||||||
|
|
||||||
|
bind();
|
||||||
|
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
|
||||||
|
unbind();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLFramebuffer::createWithTextures(int width, int height,
|
||||||
|
PixelFormat colorFormat,
|
||||||
|
PixelFormat depthFormat) {
|
||||||
|
FramebufferDesc desc;
|
||||||
|
desc.width = width;
|
||||||
|
desc.height = height;
|
||||||
|
desc.colorAttachments = 1;
|
||||||
|
desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
|
||||||
|
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
|
||||||
|
|
||||||
|
if (!init(desc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasInternalTextures_ = true;
|
||||||
|
|
||||||
|
// 创建颜色纹理
|
||||||
|
// 注意:这里简化处理,实际应该通过纹理工厂创建
|
||||||
|
// 暂时返回 true,实际纹理创建由调用者处理
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLFramebuffer::checkStatus() {
|
||||||
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case GL_FRAMEBUFFER_COMPLETE:
|
||||||
|
return true;
|
||||||
|
case GL_FRAMEBUFFER_UNDEFINED:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNDEFINED");
|
||||||
|
break;
|
||||||
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
||||||
|
break;
|
||||||
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
||||||
|
break;
|
||||||
|
#ifndef GL_ES_VERSION_2_0
|
||||||
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
||||||
|
break;
|
||||||
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNSUPPORTED");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
E2D_LOG_ERROR("Framebuffer incomplete: Unknown error {}", status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum GLFramebuffer::getColorAttachment(int index) {
|
||||||
|
return GL_COLOR_ATTACHMENT0 + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||||
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GLPipeline 实现
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
GLPipeline::GLPipeline() = default;
|
||||||
|
|
||||||
|
GLPipeline::~GLPipeline() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLPipeline::init(const PipelineDesc& desc) {
|
||||||
|
if (initialized_) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
blendMode_ = desc.blendMode;
|
||||||
|
blendEnabled_ = desc.blendEnabled;
|
||||||
|
depthTest_ = desc.depthTest;
|
||||||
|
depthWrite_ = desc.depthWrite;
|
||||||
|
depthFunc_ = desc.depthFunc;
|
||||||
|
cullMode_ = desc.cullMode;
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
|
||||||
|
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::shutdown() {
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::bind() {
|
||||||
|
if (!initialized_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyAllStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::unbind() {
|
||||||
|
// OpenGL 不需要显式解绑管线
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setBlendMode(BlendMode mode) {
|
||||||
|
blendMode_ = mode;
|
||||||
|
applyBlendState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setDepthTest(bool enabled) {
|
||||||
|
depthTest_ = enabled;
|
||||||
|
applyDepthState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setDepthWrite(bool enabled) {
|
||||||
|
depthWrite_ = enabled;
|
||||||
|
applyDepthState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setDepthFunc(DepthFunc func) {
|
||||||
|
depthFunc_ = func;
|
||||||
|
applyDepthState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setCullMode(CullMode mode) {
|
||||||
|
cullMode_ = mode;
|
||||||
|
applyCullState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::setViewport(int x, int y, int width, int height) {
|
||||||
|
viewportX_ = x;
|
||||||
|
viewportY_ = y;
|
||||||
|
viewportWidth_ = width;
|
||||||
|
viewportHeight_ = height;
|
||||||
|
|
||||||
|
// 检查缓存,避免冗余调用
|
||||||
|
if (x != cachedViewportX_ || y != cachedViewportY_ ||
|
||||||
|
width != cachedViewportWidth_ || height != cachedViewportHeight_) {
|
||||||
|
glViewport(x, y, width, height);
|
||||||
|
cachedViewportX_ = x;
|
||||||
|
cachedViewportY_ = y;
|
||||||
|
cachedViewportWidth_ = width;
|
||||||
|
cachedViewportHeight_ = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::getViewport(int& x, int& y, int& width, int& height) const {
|
||||||
|
x = viewportX_;
|
||||||
|
y = viewportY_;
|
||||||
|
width = viewportWidth_;
|
||||||
|
height = viewportHeight_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::applyAllStates() {
|
||||||
|
applyBlendState();
|
||||||
|
applyDepthState();
|
||||||
|
applyCullState();
|
||||||
|
|
||||||
|
// 应用视口
|
||||||
|
if (viewportWidth_ > 0 && viewportHeight_ > 0) {
|
||||||
|
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::applyBlendState() {
|
||||||
|
// 检查是否需要启用/禁用混合
|
||||||
|
if (blendEnabled_ != cachedBlendEnabled_) {
|
||||||
|
if (blendEnabled_) {
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
cachedBlendEnabled_ = blendEnabled_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果禁用了混合,不需要设置混合函数
|
||||||
|
if (!blendEnabled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查混合模式是否改变
|
||||||
|
if (blendMode_ != cachedBlendMode_) {
|
||||||
|
GLenum srcFactor, dstFactor;
|
||||||
|
getBlendFactors(blendMode_, srcFactor, dstFactor);
|
||||||
|
glBlendFunc(srcFactor, dstFactor);
|
||||||
|
cachedBlendMode_ = blendMode_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::applyDepthState() {
|
||||||
|
// 深度测试
|
||||||
|
if (depthTest_ != cachedDepthTest_) {
|
||||||
|
if (depthTest_) {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
cachedDepthTest_ = depthTest_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度写入
|
||||||
|
if (depthWrite_ != cachedDepthWrite_) {
|
||||||
|
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
|
||||||
|
cachedDepthWrite_ = depthWrite_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度函数
|
||||||
|
if (depthFunc_ != cachedDepthFunc_) {
|
||||||
|
glDepthFunc(convertDepthFunc(depthFunc_));
|
||||||
|
cachedDepthFunc_ = depthFunc_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::applyCullState() {
|
||||||
|
// 检查裁剪模式是否改变
|
||||||
|
if (cullMode_ != cachedCullMode_) {
|
||||||
|
if (cullMode_ == CullMode::None) {
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
} else {
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glCullFace(convertCullMode(cullMode_));
|
||||||
|
}
|
||||||
|
cachedCullMode_ = cullMode_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPipeline::getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor) {
|
||||||
|
switch (mode) {
|
||||||
|
case BlendMode::None:
|
||||||
|
srcFactor = GL_ONE;
|
||||||
|
dstFactor = GL_ZERO;
|
||||||
|
break;
|
||||||
|
case BlendMode::Alpha:
|
||||||
|
srcFactor = GL_SRC_ALPHA;
|
||||||
|
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||||
|
break;
|
||||||
|
case BlendMode::Additive:
|
||||||
|
srcFactor = GL_SRC_ALPHA;
|
||||||
|
dstFactor = GL_ONE;
|
||||||
|
break;
|
||||||
|
case BlendMode::Multiply:
|
||||||
|
srcFactor = GL_DST_COLOR;
|
||||||
|
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
srcFactor = GL_SRC_ALPHA;
|
||||||
|
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
|
||||||
|
switch (func) {
|
||||||
|
case DepthFunc::Never: return GL_NEVER;
|
||||||
|
case DepthFunc::Less: return GL_LESS;
|
||||||
|
case DepthFunc::Equal: return GL_EQUAL;
|
||||||
|
case DepthFunc::LessEqual: return GL_LEQUAL;
|
||||||
|
case DepthFunc::Greater: return GL_GREATER;
|
||||||
|
case DepthFunc::NotEqual: return GL_NOTEQUAL;
|
||||||
|
case DepthFunc::GreaterEqual: return GL_GEQUAL;
|
||||||
|
case DepthFunc::Always: return GL_ALWAYS;
|
||||||
|
default: return GL_LESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum GLPipeline::convertCullMode(CullMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case CullMode::Front: return GL_FRONT;
|
||||||
|
case CullMode::Back: return GL_BACK;
|
||||||
|
case CullMode::Both: return GL_FRONT_AND_BACK;
|
||||||
|
default: return GL_BACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_context.h>
|
||||||
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
|
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
|
||||||
|
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||||
|
|
@ -17,30 +19,11 @@ namespace extra2d {
|
||||||
// VBO 初始大小(用于 VRAM 跟踪)
|
// VBO 初始大小(用于 VRAM 跟踪)
|
||||||
static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
|
static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// BlendMode 查找表 - 编译期构建,运行时 O(1) 查找
|
|
||||||
// ============================================================================
|
|
||||||
struct BlendState {
|
|
||||||
bool enable;
|
|
||||||
GLenum srcFactor;
|
|
||||||
GLenum dstFactor;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr BlendState BLEND_STATES[] = {
|
|
||||||
{false, 0, 0}, // BlendMode::None
|
|
||||||
{true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, // BlendMode::Alpha
|
|
||||||
{true, GL_SRC_ALPHA, GL_ONE}, // BlendMode::Additive
|
|
||||||
{true, GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA} // BlendMode::Multiply
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr size_t BLEND_STATE_COUNT =
|
|
||||||
sizeof(BLEND_STATES) / sizeof(BLEND_STATES[0]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 构造函数,初始化OpenGL渲染器成员变量
|
* @brief 构造函数,初始化OpenGL渲染器成员变量
|
||||||
*/
|
*/
|
||||||
GLRenderer::GLRenderer()
|
GLRenderer::GLRenderer()
|
||||||
: window_(nullptr), shapeVao_(0), shapeVbo_(0), lineVao_(0), lineVbo_(0),
|
: window_(nullptr), shapeVao_(0), lineVao_(0),
|
||||||
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
|
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
|
||||||
lineVertexCount_(0), currentLineWidth_(1.0f) {
|
lineVertexCount_(0), currentLineWidth_(1.0f) {
|
||||||
resetStats();
|
resetStats();
|
||||||
|
|
@ -65,7 +48,11 @@ GLRenderer::~GLRenderer() { shutdown(); }
|
||||||
bool GLRenderer::init(IWindow *window) {
|
bool GLRenderer::init(IWindow *window) {
|
||||||
window_ = window;
|
window_ = window;
|
||||||
|
|
||||||
// Switch: GL 上下文已通过 SDL2 + EGL 初始化,无需 glewInit()
|
// 初始化 OpenGL 上下文(Switch 平台已通过 SDL2 + EGL 初始化,GLContext 会处理兼容性)
|
||||||
|
if (!GLContext::get().init()) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize OpenGL context");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化精灵批渲染器
|
// 初始化精灵批渲染器
|
||||||
if (!spriteBatch_.init()) {
|
if (!spriteBatch_.init()) {
|
||||||
|
|
@ -76,16 +63,24 @@ bool GLRenderer::init(IWindow *window) {
|
||||||
// 初始化形状渲染
|
// 初始化形状渲染
|
||||||
initShapeRendering();
|
initShapeRendering();
|
||||||
|
|
||||||
// 设置 OpenGL 状态
|
// 初始化管线状态管理
|
||||||
glEnable(GL_BLEND);
|
PipelineDesc pipelineDesc;
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
pipelineDesc.blendMode = BlendMode::Alpha;
|
||||||
|
pipelineDesc.depthTest = false;
|
||||||
|
pipelineDesc.depthWrite = false;
|
||||||
|
if (!pipeline_.init(pipelineDesc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize GLPipeline");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用初始管线状态
|
||||||
|
pipeline_.applyAllStates();
|
||||||
|
|
||||||
// 标记 GPU 上下文为有效
|
// 标记 GPU 上下文为有效
|
||||||
GPUContext::get().markValid();
|
GPUContext::get().markValid();
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL Renderer initialized");
|
E2D_LOG_INFO("OpenGL Renderer initialized");
|
||||||
E2D_LOG_INFO("OpenGL Version: {}",
|
E2D_LOG_INFO("OpenGL Version: {}", GLContext::get().getVersionString());
|
||||||
reinterpret_cast<const char *>(glGetString(GL_VERSION)));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -100,24 +95,22 @@ void GLRenderer::shutdown() {
|
||||||
|
|
||||||
spriteBatch_.shutdown();
|
spriteBatch_.shutdown();
|
||||||
|
|
||||||
if (lineVbo_ != 0) {
|
// 关闭 GLBuffer(自动释放 VBO)
|
||||||
glDeleteBuffers(1, &lineVbo_);
|
lineBuffer_.shutdown();
|
||||||
VRAMMgr::get().freeBuffer(MAX_LINE_VERTICES * sizeof(ShapeVertex));
|
shapeBuffer_.shutdown();
|
||||||
lineVbo_ = 0;
|
|
||||||
}
|
// 删除 VAO(VAO 仍然手动管理)
|
||||||
if (lineVao_ != 0) {
|
if (lineVao_ != 0) {
|
||||||
glDeleteVertexArrays(1, &lineVao_);
|
glDeleteVertexArrays(1, &lineVao_);
|
||||||
lineVao_ = 0;
|
lineVao_ = 0;
|
||||||
}
|
}
|
||||||
if (shapeVbo_ != 0) {
|
|
||||||
glDeleteBuffers(1, &shapeVbo_);
|
|
||||||
VRAMMgr::get().freeBuffer(MAX_SHAPE_VERTICES * sizeof(ShapeVertex));
|
|
||||||
shapeVbo_ = 0;
|
|
||||||
}
|
|
||||||
if (shapeVao_ != 0) {
|
if (shapeVao_ != 0) {
|
||||||
glDeleteVertexArrays(1, &shapeVao_);
|
glDeleteVertexArrays(1, &shapeVao_);
|
||||||
shapeVao_ = 0;
|
shapeVao_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭 OpenGL 上下文
|
||||||
|
GLContext::get().shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,6 +118,9 @@ void GLRenderer::shutdown() {
|
||||||
* @param clearColor 清屏颜色
|
* @param clearColor 清屏颜色
|
||||||
*/
|
*/
|
||||||
void GLRenderer::beginFrame(const Color &clearColor) {
|
void GLRenderer::beginFrame(const Color &clearColor) {
|
||||||
|
// 应用管线状态
|
||||||
|
pipeline_.applyAllStates();
|
||||||
|
|
||||||
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
resetStats();
|
resetStats();
|
||||||
|
|
@ -152,7 +148,8 @@ void GLRenderer::endFrame() {
|
||||||
* @param height 视口高度
|
* @param height 视口高度
|
||||||
*/
|
*/
|
||||||
void GLRenderer::setViewport(int x, int y, int width, int height) {
|
void GLRenderer::setViewport(int x, int y, int width, int height) {
|
||||||
glViewport(x, y, width, height);
|
// 使用 GLPipeline 管理视口状态
|
||||||
|
pipeline_.setViewport(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -172,31 +169,8 @@ void GLRenderer::setVSync(bool enabled) {
|
||||||
* @param mode 混合模式枚举值
|
* @param mode 混合模式枚举值
|
||||||
*/
|
*/
|
||||||
void GLRenderer::setBlendMode(BlendMode mode) {
|
void GLRenderer::setBlendMode(BlendMode mode) {
|
||||||
// 状态缓存检查,避免冗余 GL 调用
|
// 使用 GLPipeline 管理混合状态
|
||||||
if (cachedBlendMode_ == mode) {
|
pipeline_.setBlendMode(mode);
|
||||||
return;
|
|
||||||
}
|
|
||||||
cachedBlendMode_ = mode;
|
|
||||||
|
|
||||||
// 使用查找表替代 switch
|
|
||||||
size_t index = static_cast<size_t>(mode);
|
|
||||||
if (index >= BLEND_STATE_COUNT) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BlendState &state = BLEND_STATES[index];
|
|
||||||
if (state.enable) {
|
|
||||||
if (!blendEnabled_) {
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
blendEnabled_ = true;
|
|
||||||
}
|
|
||||||
glBlendFunc(state.srcFactor, state.dstFactor);
|
|
||||||
} else {
|
|
||||||
if (blendEnabled_) {
|
|
||||||
glDisable(GL_BLEND);
|
|
||||||
blendEnabled_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -787,14 +761,19 @@ void GLRenderer::initShapeRendering() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建形状 VAO 和 VBO
|
// 初始化形状 GLBuffer(使用 Dynamic 使用模式,因为每帧都会更新)
|
||||||
|
BufferDesc shapeBufferDesc;
|
||||||
|
shapeBufferDesc.type = BufferType::Vertex;
|
||||||
|
shapeBufferDesc.usage = BufferUsage::Dynamic;
|
||||||
|
shapeBufferDesc.size = MAX_SHAPE_VERTICES * sizeof(ShapeVertex);
|
||||||
|
if (!shapeBuffer_.init(shapeBufferDesc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize shape buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建形状 VAO(手动管理,用于顶点属性配置)
|
||||||
glGenVertexArrays(1, &shapeVao_);
|
glGenVertexArrays(1, &shapeVao_);
|
||||||
glGenBuffers(1, &shapeVbo_);
|
|
||||||
|
|
||||||
glBindVertexArray(shapeVao_);
|
glBindVertexArray(shapeVao_);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_);
|
shapeBuffer_.bind();
|
||||||
glBufferData(GL_ARRAY_BUFFER, MAX_SHAPE_VERTICES * sizeof(ShapeVertex),
|
|
||||||
nullptr, GL_DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
// 位置属性 (location = 0)
|
// 位置属性 (location = 0)
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
@ -808,14 +787,19 @@ void GLRenderer::initShapeRendering() {
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
// 创建线条专用 VAO 和 VBO
|
// 初始化线条 GLBuffer(使用 Dynamic 使用模式,因为每帧都会更新)
|
||||||
|
BufferDesc lineBufferDesc;
|
||||||
|
lineBufferDesc.type = BufferType::Vertex;
|
||||||
|
lineBufferDesc.usage = BufferUsage::Dynamic;
|
||||||
|
lineBufferDesc.size = MAX_LINE_VERTICES * sizeof(ShapeVertex);
|
||||||
|
if (!lineBuffer_.init(lineBufferDesc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize line buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建线条 VAO(手动管理,用于顶点属性配置)
|
||||||
glGenVertexArrays(1, &lineVao_);
|
glGenVertexArrays(1, &lineVao_);
|
||||||
glGenBuffers(1, &lineVbo_);
|
|
||||||
|
|
||||||
glBindVertexArray(lineVao_);
|
glBindVertexArray(lineVao_);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, lineVbo_);
|
lineBuffer_.bind();
|
||||||
glBufferData(GL_ARRAY_BUFFER, MAX_LINE_VERTICES * sizeof(ShapeVertex),
|
|
||||||
nullptr, GL_DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
// 位置属性 (location = 0)
|
// 位置属性 (location = 0)
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
@ -828,10 +812,6 @@ void GLRenderer::initShapeRendering() {
|
||||||
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
|
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
// VRAM 跟踪
|
|
||||||
VRAMMgr::get().allocBuffer(MAX_SHAPE_VERTICES * sizeof(ShapeVertex));
|
|
||||||
VRAMMgr::get().allocBuffer(MAX_LINE_VERTICES * sizeof(ShapeVertex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -911,9 +891,9 @@ void GLRenderer::flushShapeBatch() {
|
||||||
shapeShader_->setMat4("u_viewProjection", viewProjection_);
|
shapeShader_->setMat4("u_viewProjection", viewProjection_);
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_);
|
// 使用 GLBuffer::updateData() 更新缓冲区数据
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, shapeVertexCount_ * sizeof(ShapeVertex),
|
shapeBuffer_.updateData(shapeVertexCache_.data(), 0,
|
||||||
shapeVertexCache_.data());
|
shapeVertexCount_ * sizeof(ShapeVertex));
|
||||||
|
|
||||||
glBindVertexArray(shapeVao_);
|
glBindVertexArray(shapeVao_);
|
||||||
glDrawArrays(currentShapeMode_, 0, static_cast<GLsizei>(shapeVertexCount_));
|
glDrawArrays(currentShapeMode_, 0, static_cast<GLsizei>(shapeVertexCount_));
|
||||||
|
|
@ -940,9 +920,9 @@ void GLRenderer::flushLineBatch() {
|
||||||
shapeShader_->setMat4("u_viewProjection", viewProjection_);
|
shapeShader_->setMat4("u_viewProjection", viewProjection_);
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, lineVbo_);
|
// 使用 GLBuffer::updateData() 更新缓冲区数据
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, lineVertexCount_ * sizeof(ShapeVertex),
|
lineBuffer_.updateData(lineVertexCache_.data(), 0,
|
||||||
lineVertexCache_.data());
|
lineVertexCount_ * sizeof(ShapeVertex));
|
||||||
|
|
||||||
glBindVertexArray(lineVao_);
|
glBindVertexArray(lineVao_);
|
||||||
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertexCount_));
|
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertexCount_));
|
||||||
|
|
@ -952,4 +932,90 @@ void GLRenderer::flushLineBatch() {
|
||||||
lineVertexCount_ = 0;
|
lineVertexCount_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建帧缓冲对象
|
||||||
|
* @param desc 帧缓冲描述
|
||||||
|
* @return 创建的帧缓冲智能指针
|
||||||
|
*/
|
||||||
|
Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc& desc) {
|
||||||
|
auto framebuffer = makePtr<GLFramebuffer>();
|
||||||
|
if (!framebuffer->init(desc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to create framebuffer");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定帧缓冲(作为渲染目标)
|
||||||
|
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
|
||||||
|
*/
|
||||||
|
void GLRenderer::bindFramebuffer(GLFramebuffer* framebuffer) {
|
||||||
|
// 先刷新所有待处理的渲染批次
|
||||||
|
flush();
|
||||||
|
flushShapeBatch();
|
||||||
|
flushLineBatch();
|
||||||
|
|
||||||
|
if (framebuffer == nullptr) {
|
||||||
|
// 绑定默认帧缓冲(ID 为 0)
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
currentFramebuffer_ = nullptr;
|
||||||
|
E2D_LOG_TRACE("Bound default framebuffer (0)");
|
||||||
|
} else {
|
||||||
|
// 绑定自定义帧缓冲
|
||||||
|
framebuffer->bind();
|
||||||
|
currentFramebuffer_ = framebuffer;
|
||||||
|
E2D_LOG_TRACE("Bound custom framebuffer (ID: {})", framebuffer->getFboID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||||
|
*/
|
||||||
|
void GLRenderer::unbindFramebuffer() {
|
||||||
|
bindFramebuffer(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认帧缓冲
|
||||||
|
* @return 默认帧缓冲智能指针
|
||||||
|
*/
|
||||||
|
Ptr<GLFramebuffer> GLRenderer::getDefaultFramebuffer() const {
|
||||||
|
if (!defaultFramebuffer_) {
|
||||||
|
// 延迟创建默认帧缓冲对象(代表系统默认帧缓冲,ID 为 0)
|
||||||
|
defaultFramebuffer_ = makePtr<GLFramebuffer>();
|
||||||
|
// 注意:默认帧缓冲不需要显式初始化,它的 FBO ID 为 0
|
||||||
|
}
|
||||||
|
return defaultFramebuffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除当前绑定的帧缓冲
|
||||||
|
* @param color 清除颜色
|
||||||
|
* @param clearColor 是否清除颜色缓冲
|
||||||
|
* @param clearDepth 是否清除深度缓冲
|
||||||
|
* @param clearStencil 是否清除模板缓冲
|
||||||
|
*/
|
||||||
|
void GLRenderer::clearFramebuffer(const Color& color, bool clearColor,
|
||||||
|
bool clearDepth, bool clearStencil) {
|
||||||
|
GLbitfield mask = 0;
|
||||||
|
|
||||||
|
if (clearColor) {
|
||||||
|
glClearColor(color.r, color.g, color.b, color.a);
|
||||||
|
mask |= GL_COLOR_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearDepth) {
|
||||||
|
mask |= GL_DEPTH_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearStencil) {
|
||||||
|
mask |= GL_STENCIL_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask != 0) {
|
||||||
|
glClear(mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
GLSpriteBatch::GLSpriteBatch()
|
GLSpriteBatch::GLSpriteBatch()
|
||||||
: vao_(0), vbo_(0), ebo_(0), currentTexture_(nullptr),
|
: vao_(0), currentTexture_(nullptr), drawCallCount_(0) {}
|
||||||
drawCallCount_(0) {}
|
|
||||||
|
|
||||||
GLSpriteBatch::~GLSpriteBatch() { shutdown(); }
|
GLSpriteBatch::~GLSpriteBatch() { shutdown(); }
|
||||||
|
|
||||||
|
|
@ -19,18 +18,21 @@ bool GLSpriteBatch::init() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 VAO, VBO, EBO
|
// 创建 VAO
|
||||||
glGenVertexArrays(1, &vao_);
|
glGenVertexArrays(1, &vao_);
|
||||||
glGenBuffers(1, &vbo_);
|
|
||||||
glGenBuffers(1, &ebo_);
|
|
||||||
|
|
||||||
glBindVertexArray(vao_);
|
glBindVertexArray(vao_);
|
||||||
|
|
||||||
// 设置 VBO
|
// 初始化 VBO(顶点缓冲区)- 动态使用模式
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
BufferDesc vboDesc;
|
||||||
glBufferData(GL_ARRAY_BUFFER,
|
vboDesc.type = BufferType::Vertex;
|
||||||
SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex), nullptr,
|
vboDesc.usage = BufferUsage::Dynamic;
|
||||||
GL_DYNAMIC_DRAW);
|
vboDesc.size = SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex);
|
||||||
|
vboDesc.initialData = nullptr;
|
||||||
|
if (!vbo_.init(vboDesc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize sprite batch VBO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vbo_.bind();
|
||||||
|
|
||||||
// 设置顶点属性
|
// 设置顶点属性
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
@ -45,11 +47,17 @@ bool GLSpriteBatch::init() {
|
||||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||||
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
|
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
|
||||||
|
|
||||||
// 设置 EBO(索引缓冲区)- 使用 batch 层生成的静态索引
|
// 初始化 EBO(索引缓冲区)- 静态使用模式
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
|
BufferDesc eboDesc;
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
eboDesc.type = BufferType::Index;
|
||||||
batch_.getIndices().size() * sizeof(uint16_t),
|
eboDesc.usage = BufferUsage::Static;
|
||||||
batch_.getIndices().data(), GL_STATIC_DRAW);
|
eboDesc.size = batch_.getIndices().size() * sizeof(uint16_t);
|
||||||
|
eboDesc.initialData = batch_.getIndices().data();
|
||||||
|
if (!ebo_.init(eboDesc)) {
|
||||||
|
E2D_LOG_ERROR("Failed to initialize sprite batch EBO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ebo_.bind();
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
|
@ -57,14 +65,10 @@ bool GLSpriteBatch::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpriteBatch::shutdown() {
|
void GLSpriteBatch::shutdown() {
|
||||||
if (ebo_ != 0) {
|
// 使用 GLBuffer::shutdown() 释放缓冲区资源
|
||||||
glDeleteBuffers(1, &ebo_);
|
vbo_.shutdown();
|
||||||
ebo_ = 0;
|
ebo_.shutdown();
|
||||||
}
|
|
||||||
if (vbo_ != 0) {
|
|
||||||
glDeleteBuffers(1, &vbo_);
|
|
||||||
vbo_ = 0;
|
|
||||||
}
|
|
||||||
if (vao_ != 0) {
|
if (vao_ != 0) {
|
||||||
glDeleteVertexArrays(1, &vao_);
|
glDeleteVertexArrays(1, &vao_);
|
||||||
vao_ = 0;
|
vao_ = 0;
|
||||||
|
|
@ -78,12 +82,22 @@ void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
|
||||||
drawCallCount_ = 0;
|
drawCallCount_ = 0;
|
||||||
// 保存 viewProjection 矩阵供后续使用
|
// 保存 viewProjection 矩阵供后续使用
|
||||||
viewProjection_ = viewProjection;
|
viewProjection_ = viewProjection;
|
||||||
|
|
||||||
|
// 绑定 VAO 和缓冲区
|
||||||
|
glBindVertexArray(vao_);
|
||||||
|
vbo_.bind();
|
||||||
|
ebo_.bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpriteBatch::end() {
|
void GLSpriteBatch::end() {
|
||||||
if (batch_.getSpriteCount() > 0) {
|
if (batch_.getSpriteCount() > 0) {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解绑缓冲区
|
||||||
|
vbo_.unbind();
|
||||||
|
ebo_.unbind();
|
||||||
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
||||||
|
|
@ -154,13 +168,12 @@ void GLSpriteBatch::submitBatch() {
|
||||||
shader_->setFloat("u_opacity", 1.0f);
|
shader_->setFloat("u_opacity", 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传顶点数据
|
// 上传顶点数据 - 使用 orphaning 策略优化动态缓冲区
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
// 通过传入 nullptr 进行 orphaning,告诉驱动器可以丢弃旧缓冲区并分配新内存
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0,
|
// 这样可以避免 GPU 等待,提高性能
|
||||||
batch_.getVertices().size() * sizeof(SpriteVertex),
|
size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
|
||||||
batch_.getVertices().data());
|
vbo_.setData(nullptr, vertexDataSize); // orphaning
|
||||||
|
vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);
|
||||||
glBindVertexArray(vao_);
|
|
||||||
|
|
||||||
// 绘制
|
// 绘制
|
||||||
currentTexture_->bind(0);
|
currentTexture_->bind(0);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <extra2d/graphics/batch/shape_batch.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ShapeBatch 基础实现(后端无关部分)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// 这里可以添加后端无关的工具函数
|
||||||
|
// 例如:计算圆形的顶点、三角化多边形等
|
||||||
|
|
||||||
|
// 计算圆形顶点
|
||||||
|
void calculateCircleVertices(std::vector<Vec2>& outVertices,
|
||||||
|
const Vec2& center, float radius,
|
||||||
|
int segments, bool fill) {
|
||||||
|
outVertices.clear();
|
||||||
|
outVertices.reserve(fill ? segments + 1 : segments);
|
||||||
|
|
||||||
|
if (fill) {
|
||||||
|
// 填充圆形:中心点 + 边缘点
|
||||||
|
outVertices.push_back(center);
|
||||||
|
for (int i = 0; i <= segments; ++i) {
|
||||||
|
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
|
||||||
|
outVertices.emplace_back(
|
||||||
|
center.x + radius * cosf(angle),
|
||||||
|
center.y + radius * sinf(angle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 圆形边框:只保留边缘点
|
||||||
|
for (int i = 0; i < segments; ++i) {
|
||||||
|
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
|
||||||
|
outVertices.emplace_back(
|
||||||
|
center.x + radius * cosf(angle),
|
||||||
|
center.y + radius * sinf(angle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算矩形顶点
|
||||||
|
void calculateRectVertices(std::vector<Vec2>& outVertices, const Rect& rect) {
|
||||||
|
outVertices.clear();
|
||||||
|
outVertices.reserve(4);
|
||||||
|
|
||||||
|
float x1 = rect.origin.x;
|
||||||
|
float y1 = rect.origin.y;
|
||||||
|
float x2 = rect.origin.x + rect.size.width;
|
||||||
|
float y2 = rect.origin.y + rect.size.height;
|
||||||
|
|
||||||
|
outVertices.emplace_back(x1, y1); // 左上
|
||||||
|
outVertices.emplace_back(x2, y1); // 右上
|
||||||
|
outVertices.emplace_back(x2, y2); // 右下
|
||||||
|
outVertices.emplace_back(x1, y2); // 左下
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的多边形三角化(扇形三角化,适用于凸多边形)
|
||||||
|
void triangulatePolygon(std::vector<uint16_t>& outIndices, int vertexCount) {
|
||||||
|
outIndices.clear();
|
||||||
|
if (vertexCount < 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扇形三角化:以第一个顶点为扇形中心
|
||||||
|
for (int i = 1; i < vertexCount - 1; ++i) {
|
||||||
|
outIndices.push_back(0);
|
||||||
|
outIndices.push_back(i);
|
||||||
|
outIndices.push_back(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# 资源抽象层 (Resources Abstraction Layer)
|
||||||
|
|
||||||
|
此目录包含渲染后端无关的资源抽象接口。
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
### 头文件 (include/extra2d/graphics/resources/)
|
||||||
|
|
||||||
|
- **buffer.h** - 缓冲区抽象接口
|
||||||
|
- 顶点缓冲、索引缓冲、统一缓冲
|
||||||
|
- 支持 Static/Dynamic/Stream 使用模式
|
||||||
|
|
||||||
|
- **pipeline.h** - 渲染管线抽象接口
|
||||||
|
- 混合模式、深度测试、裁剪状态
|
||||||
|
- 顶点属性描述
|
||||||
|
|
||||||
|
- **framebuffer.h** - 帧缓冲抽象接口
|
||||||
|
- 多颜色附件支持
|
||||||
|
- 深度/模板附件
|
||||||
|
- 多重采样支持
|
||||||
|
|
||||||
|
- **font_atlas.h** - 字体图集抽象接口
|
||||||
|
- 字形信息管理
|
||||||
|
- SDF 渲染支持
|
||||||
|
- 文本测量功能
|
||||||
|
|
||||||
|
- **shader.h** - 着色器抽象接口
|
||||||
|
- Uniform 变量设置
|
||||||
|
- 支持各种数据类型
|
||||||
|
|
||||||
|
### 实现说明
|
||||||
|
|
||||||
|
这些接口是纯虚类(抽象接口),不需要在 `src/graphics/resources/` 目录中提供实现。
|
||||||
|
|
||||||
|
实际的实现位于各个渲染后端目录中:
|
||||||
|
- OpenGL 实现: `src/graphics/backends/opengl/`
|
||||||
|
- Vulkan 实现: `src/graphics/backends/vulkan/` (未来)
|
||||||
|
|
||||||
|
## 设计原则
|
||||||
|
|
||||||
|
1. **后端无关** - 接口不依赖任何特定渲染 API
|
||||||
|
2. **工厂模式** - 通过后端工厂创建具体实现
|
||||||
|
3. **资源管理** - 使用智能指针管理资源生命周期
|
||||||
|
4. **类型安全** - 使用强类型枚举和结构体
|
||||||
Loading…
Reference in New Issue