feat(渲染器): 实现核心渲染系统模块
添加渲染器模块及相关组件,包括材质、网格、纹理、着色器和统一缓冲区管理。主要变更包括: - 新增渲染器模块,负责接收渲染命令、批处理和排序 - 实现材质系统支持参数和着色器管理 - 添加网格类管理顶点和索引数据 - 实现纹理加载和绑定功能 - 添加着色器编译和链接功能 - 实现统一缓冲区对象(UBO)管理系统 - 提供默认资源(材质、网格、纹理) - 支持实例化渲染和命令批处理 - 添加渲染事件系统(OnRenderBegin/Submit/End) - 完善资源句柄管理机制 - 优化GL资源初始化和清理流程 移除不再使用的IModule接口,调整窗口模块事件触发时机确保GL上下文安全
This commit is contained in:
parent
46393fd027
commit
b4be0d84f8
|
|
@ -56,9 +56,11 @@ target("hello_world")
|
|||
|
||||
-- 复制资源到输出目录
|
||||
after_build(function (target)
|
||||
local target_dir = path.directory(target:targetfile())
|
||||
|
||||
-- 复制 romfs 资源
|
||||
local romfs = path.join(example_dir, "romfs")
|
||||
if os.isdir(romfs) then
|
||||
local target_dir = path.directory(target:targetfile())
|
||||
local assets_dir = path.join(target_dir, "assets")
|
||||
|
||||
-- 创建 assets 目录
|
||||
|
|
@ -72,6 +74,17 @@ target("hello_world")
|
|||
else
|
||||
print("Warning: romfs directory not found at " .. romfs)
|
||||
end
|
||||
|
||||
-- 复制着色器文件
|
||||
local shader_source = path.join(example_dir, "../../shader")
|
||||
local shader_target = path.join(target_dir, "shader")
|
||||
if os.isdir(shader_source) then
|
||||
if not os.isdir(shader_target) then
|
||||
os.mkdir(shader_target)
|
||||
end
|
||||
os.cp(path.join(shader_source, "*"), shader_target)
|
||||
print("Copied shaders from " .. shader_source .. " to " .. shader_target)
|
||||
end
|
||||
end)
|
||||
end
|
||||
target_end()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace extra2d {
|
|||
class ModuleRegistry;
|
||||
class PluginLoader;
|
||||
class TimerModule;
|
||||
class RendererModule;
|
||||
|
||||
/**
|
||||
* @brief 引擎上下文
|
||||
|
|
@ -66,6 +67,11 @@ public:
|
|||
*/
|
||||
TimerModule& timer();
|
||||
|
||||
/**
|
||||
* @brief 获取渲染器模块
|
||||
*/
|
||||
RendererModule& renderer();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 获取总运行时间
|
||||
|
|
@ -89,7 +95,6 @@ private:
|
|||
|
||||
private:
|
||||
std::unique_ptr<PluginLoader> pluginLoader_;
|
||||
std::unique_ptr<TimerModule> timerModule_;
|
||||
|
||||
float totalTime_ = 0.0f;
|
||||
uint64 frameCount_ = 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <event/event_bus_macros.h>
|
||||
#include <renderer/render_types.h>
|
||||
#include <types/base/types.h>
|
||||
|
||||
namespace extra2d::events {
|
||||
|
|
@ -223,4 +224,30 @@ DECLARE_EVENT_0(OnEnterBackground, Engine)
|
|||
*/
|
||||
DECLARE_EVENT_0(OnEnterForeground, Engine)
|
||||
|
||||
// ============================================================================
|
||||
// 渲染事件
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 渲染开始事件
|
||||
*
|
||||
* 在渲染帧开始时触发,渲染器应清空命令缓冲区
|
||||
*/
|
||||
DECLARE_EVENT_0(OnRenderBegin, Engine)
|
||||
|
||||
/**
|
||||
* @brief 渲染提交事件
|
||||
*
|
||||
* 场景图模块通过此事件提交渲染命令
|
||||
* @param cmd 渲染命令
|
||||
*/
|
||||
DECLARE_EVENT_1(OnRenderSubmit, Engine, extra2d::RenderCommand)
|
||||
|
||||
/**
|
||||
* @brief 渲染结束事件
|
||||
*
|
||||
* 在渲染帧结束时触发,渲染器执行所有收集的命令
|
||||
*/
|
||||
DECLARE_EVENT_0(OnRenderEnd, Engine)
|
||||
|
||||
} // namespace extra2d::events
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <context/context.h>
|
||||
|
||||
// Module System (新架构 - 基于事件总线)
|
||||
#include <module/imodule.h>
|
||||
#include <module/module_registry.h>
|
||||
|
||||
// Plugin System (新架构 - 基于事件总线)
|
||||
|
|
@ -41,6 +40,15 @@
|
|||
#include <config/app_config.h>
|
||||
#include <config/window_config.h>
|
||||
|
||||
// Renderer System
|
||||
#include <renderer/render_types.h>
|
||||
#include <renderer/renderer_module.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <renderer/material.h>
|
||||
#include <renderer/texture.h>
|
||||
#include <renderer/mesh.h>
|
||||
#include <renderer/uniform_buffer.h>
|
||||
|
||||
// Application
|
||||
#include <app/application.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/base/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 模块类型枚举
|
||||
*/
|
||||
enum class ModuleType : uint8 {
|
||||
Core, // 核心模块(必须)
|
||||
System, // 系统模块(平台相关)
|
||||
Feature, // 功能模块
|
||||
Extension // 扩展模块
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块接口 - 基于事件总线
|
||||
*
|
||||
* 模块不再通过虚函数被调用,而是通过监听事件响应
|
||||
* 模块可以发送事件,但不应直接调用其他模块
|
||||
*
|
||||
* 设计理念:
|
||||
* - 模块是引擎的内置组件,在编译时确定
|
||||
* - 模块之间通过事件总线通信,零直接依赖
|
||||
* - 模块的生命周期由 ModuleRegistry 管理
|
||||
*/
|
||||
class IModule {
|
||||
public:
|
||||
virtual ~IModule() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取模块名称
|
||||
* @return 模块唯一标识名
|
||||
*/
|
||||
virtual const char* name() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块类型
|
||||
* @return 模块类型,用于分类和优先级管理
|
||||
*/
|
||||
virtual ModuleType type() const { return ModuleType::Feature; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级
|
||||
* @return 优先级数值,越小优先级越高
|
||||
*/
|
||||
virtual int priority() const { return 0; }
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
*
|
||||
* 在此注册事件监听器
|
||||
* 返回 false 表示初始化失败,引擎将停止
|
||||
*
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
virtual bool init() { return true; }
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*
|
||||
* 在此执行清理工作
|
||||
* 事件监听器会自动注销(RAII)
|
||||
*/
|
||||
virtual void shutdown() {}
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/ptr/ref_counted.h>
|
||||
#include <types/ptr/intrusive_ptr.h>
|
||||
#include <types/math/vec2.h>
|
||||
#include <types/math/color.h>
|
||||
#include <renderer/render_types.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <renderer/uniform_buffer.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Material;
|
||||
|
||||
/**
|
||||
* @brief 材质参数信息
|
||||
*/
|
||||
struct MaterialParamInfo {
|
||||
MaterialParamType type;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质布局类
|
||||
*
|
||||
* 定义材质参数的布局,可被多个材质共享
|
||||
*/
|
||||
class MaterialLayout : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
MaterialLayout();
|
||||
|
||||
/**
|
||||
* @brief 添加参数
|
||||
* @param name 参数名称
|
||||
* @param type 参数类型
|
||||
*/
|
||||
void addParam(const std::string& name, MaterialParamType type);
|
||||
|
||||
/**
|
||||
* @brief 完成布局定义,计算偏移和总大小
|
||||
*/
|
||||
void finalize();
|
||||
|
||||
/**
|
||||
* @brief 获取参数信息
|
||||
* @param name 参数名称
|
||||
* @return 参数信息指针,不存在返回 nullptr
|
||||
*/
|
||||
const MaterialParamInfo* getParam(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小
|
||||
* @return 缓冲区大小
|
||||
*/
|
||||
uint32_t getBufferSize() const { return bufferSize_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否已最终化
|
||||
* @return 是否已最终化
|
||||
*/
|
||||
bool isFinalized() const { return finalized_; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, MaterialParamInfo> params_;
|
||||
uint32_t bufferSize_ = 0;
|
||||
bool finalized_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质类
|
||||
*
|
||||
* 管理着色器和材质参数
|
||||
* 支持通过 UBO 高效上传材质数据到 GPU
|
||||
*/
|
||||
class Material : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Material();
|
||||
|
||||
/**
|
||||
* @brief 设置材质布局
|
||||
* @param layout 材质布局
|
||||
*/
|
||||
void setLayout(Ptr<MaterialLayout> layout);
|
||||
|
||||
/**
|
||||
* @brief 设置着色器
|
||||
* @param shader 着色器
|
||||
*/
|
||||
void setShader(Ptr<Shader> shader);
|
||||
|
||||
/**
|
||||
* @brief 设置 float 参数
|
||||
* @param name 参数名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setFloat(const std::string& name, float value);
|
||||
|
||||
/**
|
||||
* @brief 设置 vec2 参数
|
||||
* @param name 参数名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setVec2(const std::string& name, const Vec2& value);
|
||||
|
||||
/**
|
||||
* @brief 设置 vec4 参数
|
||||
* @param name 参数名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setVec4(const std::string& name, float x, float y, float z, float w);
|
||||
|
||||
/**
|
||||
* @brief 设置颜色参数
|
||||
* @param name 参数名称
|
||||
* @param value 颜色值
|
||||
*/
|
||||
void setColor(const std::string& name, const Color& value);
|
||||
|
||||
/**
|
||||
* @brief 设置 mat4 参数
|
||||
* @param name 参数名称
|
||||
* @param value 矩阵数据指针
|
||||
*/
|
||||
void setMat4(const std::string& name, const float* value);
|
||||
|
||||
/**
|
||||
* @brief 设置纹理
|
||||
* @param name 参数名称
|
||||
* @param texture 纹理句柄
|
||||
* @param slot 纹理槽位
|
||||
*/
|
||||
void setTexture(const std::string& name, TextureHandle texture, uint32_t slot);
|
||||
|
||||
/**
|
||||
* @brief 应用材质
|
||||
*
|
||||
* 绑定着色器、上传 UBO 数据、绑定纹理
|
||||
* @param uboManager UBO 管理器
|
||||
*/
|
||||
void apply(UniformBufferManager& uboManager);
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
* @return 着色器
|
||||
*/
|
||||
Ptr<Shader> getShader() const { return shader_; }
|
||||
|
||||
/**
|
||||
* @brief 获取材质数据指针
|
||||
* @return 数据指针
|
||||
*/
|
||||
const void* getData() const { return data_.data(); }
|
||||
|
||||
/**
|
||||
* @brief 获取材质数据大小
|
||||
* @return 数据大小
|
||||
*/
|
||||
uint32_t getDataSize() const { return static_cast<uint32_t>(data_.size()); }
|
||||
|
||||
private:
|
||||
Ptr<MaterialLayout> layout_; // 材质布局
|
||||
Ptr<Shader> shader_; // 着色器
|
||||
std::vector<uint8_t> data_; // 材质数据
|
||||
std::vector<std::pair<TextureHandle, uint32_t>> textures_; // 纹理列表
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/math/color.h>
|
||||
#include <types/math/rect.h>
|
||||
#include <types/math/vec2.h>
|
||||
#include <types/ptr/intrusive_ptr.h>
|
||||
#include <types/ptr/ref_counted.h>
|
||||
|
||||
// 前向声明 OpenGL 类型
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 顶点结构
|
||||
*/
|
||||
struct Vertex {
|
||||
Vec2 position; // 位置
|
||||
Vec2 texCoord; // 纹理坐标
|
||||
Color color; // 颜色
|
||||
|
||||
Vertex() = default;
|
||||
Vertex(const Vec2 &pos, const Vec2 &uv, const Color &col)
|
||||
: position(pos), texCoord(uv), color(col) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格类
|
||||
*
|
||||
* 管理 OpenGL 顶点数组对象和缓冲区
|
||||
* 支持静态和动态顶点数据
|
||||
*/
|
||||
class Mesh : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Mesh();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Mesh() override;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点数据
|
||||
* @param vertices 顶点数组
|
||||
* @param count 顶点数量
|
||||
*/
|
||||
void setVertices(const Vertex *vertices, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief 设置索引数据
|
||||
* @param indices 索引数组
|
||||
* @param count 索引数量
|
||||
*/
|
||||
void setIndices(const uint16_t *indices, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief 更新部分顶点数据
|
||||
* @param vertices 顶点数据
|
||||
* @param count 顶点数量
|
||||
* @param offset 偏移量
|
||||
*/
|
||||
void updateVertices(const Vertex *vertices, uint32_t count, uint32_t offset);
|
||||
|
||||
/**
|
||||
* @brief 绑定网格
|
||||
*/
|
||||
void bind() const;
|
||||
|
||||
/**
|
||||
* @brief 解绑网格
|
||||
*/
|
||||
void unbind() const;
|
||||
|
||||
/**
|
||||
* @brief 绘制网格
|
||||
*/
|
||||
void draw() const;
|
||||
|
||||
/**
|
||||
* @brief 实例化绘制
|
||||
* @param instanceCount 实例数量
|
||||
*/
|
||||
void drawInstanced(uint32_t instanceCount) const;
|
||||
|
||||
/**
|
||||
* @brief 获取顶点数量
|
||||
* @return 顶点数量
|
||||
*/
|
||||
uint32_t getVertexCount() const { return vertexCount_; }
|
||||
|
||||
/**
|
||||
* @brief 获取索引数量
|
||||
* @return 索引数量
|
||||
*/
|
||||
uint32_t getIndexCount() const { return indexCount_; }
|
||||
|
||||
/**
|
||||
* @brief 创建标准四边形
|
||||
* @param size 四边形大小
|
||||
* @param uv 纹理坐标范围
|
||||
* @return 网格对象
|
||||
*/
|
||||
static Ptr<Mesh> createQuad(const Vec2 &size,
|
||||
const Rect &uv = Rect(0, 0, 1, 1));
|
||||
|
||||
private:
|
||||
GLuint vao_ = 0; // 顶点数组对象
|
||||
GLuint vbo_ = 0; // 顶点缓冲区
|
||||
GLuint ibo_ = 0; // 索引缓冲区
|
||||
uint32_t vertexCount_ = 0; // 顶点数量
|
||||
uint32_t indexCount_ = 0; // 索引数量
|
||||
uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量
|
||||
uint32_t indexCapacity_ = 0; // 索引缓冲区容量
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/base/types.h>
|
||||
#include <types/math/vec2.h>
|
||||
#include <types/math/color.h>
|
||||
#include <types/math/transform.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Material;
|
||||
class Mesh;
|
||||
class Texture;
|
||||
|
||||
// 资源句柄类型(64位,替代智能指针)
|
||||
using MaterialHandle = uint64_t;
|
||||
using MeshHandle = uint64_t;
|
||||
using TextureHandle = uint64_t;
|
||||
|
||||
// 无效句柄值
|
||||
constexpr MaterialHandle INVALID_MATERIAL_HANDLE = 0;
|
||||
constexpr MeshHandle INVALID_MESH_HANDLE = 0;
|
||||
constexpr TextureHandle INVALID_TEXTURE_HANDLE = 0;
|
||||
|
||||
/**
|
||||
* @brief 渲染命令类型
|
||||
*/
|
||||
enum class RenderCommandType : uint8_t {
|
||||
DrawMesh, // 绘制网格
|
||||
DrawMeshInstanced, // 实例化绘制
|
||||
SetViewport, // 设置视口
|
||||
Clear // 清除缓冲区
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理格式
|
||||
*/
|
||||
enum class TextureFormat : uint8_t {
|
||||
RGBA8, // 32位 RGBA
|
||||
RGB8, // 24位 RGB
|
||||
RGBA4, // 16位 RGBA
|
||||
R8, // 8位 灰度
|
||||
RG8 // 16位 RG
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取纹理格式的像素大小(字节)
|
||||
* @param format 纹理格式
|
||||
* @return 像素大小
|
||||
*/
|
||||
inline uint32_t getTexturePixelSize(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::RGBA8: return 4;
|
||||
case TextureFormat::RGB8: return 3;
|
||||
case TextureFormat::RGBA4: return 2;
|
||||
case TextureFormat::R8: return 1;
|
||||
case TextureFormat::RG8: return 2;
|
||||
default: return 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 材质参数类型
|
||||
*/
|
||||
enum class MaterialParamType : uint8_t {
|
||||
Float, // 浮点数
|
||||
Vec2, // 二维向量
|
||||
Vec3, // 三维向量
|
||||
Vec4, // 四维向量
|
||||
Color, // 颜色
|
||||
Mat4, // 4x4 矩阵
|
||||
Texture // 纹理
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取材质参数类型的大小
|
||||
* @param type 参数类型
|
||||
* @return 参数大小(字节)
|
||||
*/
|
||||
inline uint32_t getMaterialParamSize(MaterialParamType type) {
|
||||
switch (type) {
|
||||
case MaterialParamType::Float: return sizeof(float);
|
||||
case MaterialParamType::Vec2: return sizeof(float) * 2;
|
||||
case MaterialParamType::Vec3: return sizeof(float) * 3;
|
||||
case MaterialParamType::Vec4: return sizeof(float) * 4;
|
||||
case MaterialParamType::Color: return sizeof(float) * 4;
|
||||
case MaterialParamType::Mat4: return sizeof(float) * 16;
|
||||
case MaterialParamType::Texture: return sizeof(TextureHandle);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染命令结构(32字节对齐)
|
||||
*
|
||||
* 使用值类型设计,避免智能指针开销
|
||||
* 通过资源句柄引用实际资源
|
||||
*/
|
||||
struct alignas(32) RenderCommand {
|
||||
RenderCommandType type; // 命令类型
|
||||
uint32_t sortKey; // 排序键(材质ID + 层)
|
||||
|
||||
union {
|
||||
// 绘制网格命令
|
||||
struct {
|
||||
MeshHandle mesh; // 网格句柄
|
||||
MaterialHandle material; // 材质句柄
|
||||
Transform transform; // 世界变换
|
||||
} drawMesh;
|
||||
|
||||
// 实例化绘制命令
|
||||
struct {
|
||||
MeshHandle mesh; // 网格句柄
|
||||
MaterialHandle material; // 材质句柄
|
||||
uint32_t instanceCount; // 实例数量
|
||||
uint32_t instanceOffset; // 实例数据偏移
|
||||
} drawInstanced;
|
||||
|
||||
// 设置视口命令
|
||||
struct {
|
||||
int32_t x, y; // 视口位置
|
||||
int32_t width, height; // 视口大小
|
||||
} viewport;
|
||||
|
||||
// 清除缓冲区命令
|
||||
struct {
|
||||
Color color; // 清除颜色
|
||||
uint32_t flags; // 清除标志(颜色/深度/模板)
|
||||
} clear;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
||||
drawMesh.mesh = INVALID_MESH_HANDLE;
|
||||
drawMesh.material = INVALID_MATERIAL_HANDLE;
|
||||
drawMesh.transform = Transform();
|
||||
}
|
||||
};
|
||||
|
||||
// 清除标志
|
||||
constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0;
|
||||
constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1;
|
||||
constexpr uint32_t CLEAR_STENCIL_FLAG = 1 << 2;
|
||||
constexpr uint32_t CLEAR_ALL_FLAG = CLEAR_COLOR_FLAG | CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG;
|
||||
|
||||
// 最大渲染命令数
|
||||
constexpr uint32_t MAX_RENDER_COMMANDS = 10000;
|
||||
|
||||
// 最大实例数
|
||||
constexpr uint32_t MAX_INSTANCES = 256;
|
||||
|
||||
// UBO 绑定槽位
|
||||
constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO
|
||||
constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO
|
||||
constexpr uint32_t INSTANCE_UBO_BINDING = 2; // 实例 UBO
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <event/events.h>
|
||||
#include <module/module.h>
|
||||
#include <module/module_registry.h>
|
||||
#include <queue>
|
||||
#include <renderer/material.h>
|
||||
#include <renderer/mesh.h>
|
||||
#include <renderer/render_types.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <renderer/texture.h>
|
||||
#include <renderer/uniform_buffer.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染器模块
|
||||
*
|
||||
* 核心渲染系统模块,负责:
|
||||
* - 通过事件接收渲染命令
|
||||
* - 自动批处理和排序
|
||||
* - GPU 资源管理
|
||||
* - 执行实际渲染
|
||||
*/
|
||||
class RendererModule : public Module {
|
||||
// 自动注册到模块系统,优先级为 3(在 Window、Timer、Input 之后)
|
||||
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
RendererModule();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RendererModule() override;
|
||||
|
||||
// 禁止拷贝
|
||||
RendererModule(const RendererModule &) = delete;
|
||||
RendererModule &operator=(const RendererModule &) = delete;
|
||||
|
||||
// 允许移动
|
||||
RendererModule(RendererModule &&) noexcept;
|
||||
RendererModule &operator=(RendererModule &&) noexcept;
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
*
|
||||
* 绑定事件监听器,等待窗口显示事件进行 GL 初始化
|
||||
*
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
bool init() override;
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*
|
||||
* 清理所有渲染资源
|
||||
*/
|
||||
void shutdown() override;
|
||||
|
||||
//===========================================================================
|
||||
// 资源注册接口(供其他模块使用)
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 注册材质
|
||||
* @param material 材质对象
|
||||
* @return 材质句柄
|
||||
*/
|
||||
MaterialHandle registerMaterial(Ptr<Material> material);
|
||||
|
||||
/**
|
||||
* @brief 注册网格
|
||||
* @param mesh 网格对象
|
||||
* @return 网格句柄
|
||||
*/
|
||||
MeshHandle registerMesh(Ptr<Mesh> mesh);
|
||||
|
||||
/**
|
||||
* @brief 注册纹理
|
||||
* @param texture 纹理对象
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
TextureHandle registerTexture(Ptr<Texture> texture);
|
||||
|
||||
/**
|
||||
* @brief 注销材质
|
||||
* @param handle 材质句柄
|
||||
*/
|
||||
void unregisterMaterial(MaterialHandle handle);
|
||||
|
||||
/**
|
||||
* @brief 注销网格
|
||||
* @param handle 网格句柄
|
||||
*/
|
||||
void unregisterMesh(MeshHandle handle);
|
||||
|
||||
/**
|
||||
* @brief 注销纹理
|
||||
* @param handle 纹理句柄
|
||||
*/
|
||||
void unregisterTexture(TextureHandle handle);
|
||||
|
||||
/**
|
||||
* @brief 获取材质
|
||||
* @param handle 材质句柄
|
||||
* @return 材质对象,无效句柄返回 nullptr
|
||||
*/
|
||||
Ptr<Material> getMaterial(MaterialHandle handle);
|
||||
|
||||
/**
|
||||
* @brief 获取网格
|
||||
* @param handle 网格句柄
|
||||
* @return 网格对象,无效句柄返回 nullptr
|
||||
*/
|
||||
Ptr<Mesh> getMesh(MeshHandle handle);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
* @param handle 纹理句柄
|
||||
* @return 纹理对象,无效句柄返回 nullptr
|
||||
*/
|
||||
Ptr<Texture> getTexture(TextureHandle handle);
|
||||
|
||||
//===========================================================================
|
||||
// 默认资源
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取默认材质句柄
|
||||
* @return 默认材质句柄
|
||||
*/
|
||||
MaterialHandle getDefaultMaterialHandle() const {
|
||||
return defaultMaterialHandle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取默认四边形网格句柄
|
||||
* @return 默认四边形网格句柄
|
||||
*/
|
||||
MeshHandle getDefaultQuadHandle() const { return defaultQuadHandle_; }
|
||||
|
||||
/**
|
||||
* @brief 获取默认纹理句柄(1x1 白色纹理)
|
||||
* @return 默认纹理句柄
|
||||
*/
|
||||
TextureHandle getDefaultTextureHandle() const {
|
||||
return defaultTextureHandle_;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// 渲染状态设置
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x 视口左上角 X 坐标
|
||||
* @param y 视口左上角 Y 坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
void setViewport(int32 x, int32 y, int32 width, int32 height);
|
||||
|
||||
/**
|
||||
* @brief 清除缓冲区
|
||||
* @param color 清除颜色
|
||||
* @param flags 清除标志(组合使用 CLEAR_COLOR_FLAG, CLEAR_DEPTH_FLAG, CLEAR_STENCIL_FLAG)
|
||||
*/
|
||||
void clear(const Color &color, uint32 flags = CLEAR_COLOR_FLAG);
|
||||
|
||||
private:
|
||||
//===========================================================================
|
||||
// 事件处理器
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 渲染开始事件处理
|
||||
*
|
||||
* 清空命令缓冲区,重置统计信息
|
||||
*/
|
||||
void onRenderBegin();
|
||||
|
||||
/**
|
||||
* @brief 渲染提交事件处理
|
||||
* @param cmd 渲染命令
|
||||
*/
|
||||
void onRenderSubmit(const RenderCommand &cmd);
|
||||
|
||||
/**
|
||||
* @brief 渲染结束事件处理
|
||||
*
|
||||
* 排序命令,批处理并执行绘制
|
||||
*/
|
||||
void onRenderEnd();
|
||||
|
||||
/**
|
||||
* @brief 窗口大小改变事件处理
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
*/
|
||||
void onResize(int32 width, int32 height);
|
||||
|
||||
/**
|
||||
* @brief 窗口显示事件处理
|
||||
*
|
||||
* 延迟 GL 初始化到窗口显示时
|
||||
*/
|
||||
void onWindowShow();
|
||||
|
||||
//===========================================================================
|
||||
// 渲染执行
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 排序渲染命令
|
||||
*
|
||||
* 根据 sortKey 对命令进行排序以优化绘制顺序
|
||||
*/
|
||||
void sortCommands();
|
||||
|
||||
/**
|
||||
* @brief 批处理并绘制
|
||||
*
|
||||
* 将相同材质和网格的命令合并批次绘制
|
||||
*/
|
||||
void batchAndDraw();
|
||||
|
||||
/**
|
||||
* @brief 绘制批次
|
||||
* @param start 批次起始索引
|
||||
* @param count 批次命令数量
|
||||
* @param materialHandle 材质句柄
|
||||
* @param meshHandle 网格句柄
|
||||
*/
|
||||
void drawBatch(uint32 start, uint32 count, MaterialHandle materialHandle,
|
||||
MeshHandle meshHandle);
|
||||
|
||||
/**
|
||||
* @brief 执行单个渲染命令
|
||||
* @param cmd 渲染命令
|
||||
*/
|
||||
void executeCommand(const RenderCommand &cmd);
|
||||
|
||||
//===========================================================================
|
||||
// 默认资源创建与销毁
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建默认资源
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
bool createDefaultResources();
|
||||
|
||||
/**
|
||||
* @brief 销毁默认资源
|
||||
*/
|
||||
void destroyDefaultResources();
|
||||
|
||||
//===========================================================================
|
||||
// 资源句柄管理
|
||||
//===========================================================================
|
||||
|
||||
/**
|
||||
* @brief 资源槽位结构
|
||||
*
|
||||
* 用于管理资源对象和句柄生命周期
|
||||
*/
|
||||
struct ResourceSlot {
|
||||
Ptr<Material> material;
|
||||
Ptr<Mesh> mesh;
|
||||
Ptr<Texture> texture;
|
||||
uint32 generation = 0;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 句柄池模板类
|
||||
*
|
||||
* 管理资源句柄的分配和回收
|
||||
*/
|
||||
template <typename T> struct HandlePool {
|
||||
std::vector<ResourceSlot> slots;
|
||||
std::queue<uint32> freeIndices;
|
||||
uint32 nextGeneration = 1;
|
||||
|
||||
/**
|
||||
* @brief 分配新句柄
|
||||
* @return 编码后的句柄(高32位索引,低32位世代)
|
||||
*/
|
||||
uint64 acquire() {
|
||||
uint32 index;
|
||||
if (!freeIndices.empty()) {
|
||||
index = freeIndices.front();
|
||||
freeIndices.pop();
|
||||
} else {
|
||||
index = static_cast<uint32>(slots.size());
|
||||
slots.emplace_back();
|
||||
}
|
||||
|
||||
slots[index].active = true;
|
||||
slots[index].generation = nextGeneration++;
|
||||
|
||||
// 编码句柄:高32位是索引,低32位是世代
|
||||
return (static_cast<uint64>(index) << 32) | slots[index].generation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放句柄
|
||||
* @param handle 要释放的句柄
|
||||
*/
|
||||
void release(uint64 handle) {
|
||||
uint32 index = static_cast<uint32>(handle >> 32);
|
||||
if (index < slots.size() && slots[index].active) {
|
||||
slots[index].active = false;
|
||||
slots[index].material.reset();
|
||||
slots[index].mesh.reset();
|
||||
slots[index].texture.reset();
|
||||
freeIndices.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取资源槽位
|
||||
* @param handle 资源句柄
|
||||
* @return 资源槽位指针,无效句柄返回 nullptr
|
||||
*/
|
||||
ResourceSlot *get(uint64 handle) {
|
||||
uint32 index = static_cast<uint32>(handle >> 32);
|
||||
uint32 generation = static_cast<uint32>(handle & 0xFFFFFFFF);
|
||||
|
||||
if (index < slots.size() && slots[index].active &&
|
||||
slots[index].generation == generation) {
|
||||
return &slots[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
HandlePool<Material> materialPool_; // 材质资源池
|
||||
HandlePool<Mesh> meshPool_; // 网格资源池
|
||||
HandlePool<Texture> texturePool_; // 纹理资源池
|
||||
|
||||
//===========================================================================
|
||||
// 命令缓冲区
|
||||
//===========================================================================
|
||||
|
||||
std::array<RenderCommand, MAX_RENDER_COMMANDS> commandBuffer_; // 预分配命令缓冲区
|
||||
uint32 commandCount_ = 0; // 当前命令数量
|
||||
|
||||
//===========================================================================
|
||||
// UBO 管理器
|
||||
//===========================================================================
|
||||
|
||||
UniformBufferManager uniformManager_;
|
||||
|
||||
//===========================================================================
|
||||
// 默认资源句柄
|
||||
//===========================================================================
|
||||
|
||||
MaterialHandle defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
||||
MeshHandle defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
||||
TextureHandle defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
||||
|
||||
//===========================================================================
|
||||
// 事件监听器
|
||||
//===========================================================================
|
||||
|
||||
events::OnRenderBegin::Listener onRenderBeginListener_;
|
||||
events::OnRenderSubmit::Listener onRenderSubmitListener_;
|
||||
events::OnRenderEnd::Listener onRenderEndListener_;
|
||||
events::OnResize::Listener onResizeListener_;
|
||||
events::OnShow::Listener onShowListener_;
|
||||
|
||||
//===========================================================================
|
||||
// 状态标志
|
||||
//===========================================================================
|
||||
|
||||
bool glInitialized_ = false; // GL 是否已初始化
|
||||
|
||||
//===========================================================================
|
||||
// 渲染统计
|
||||
//===========================================================================
|
||||
|
||||
struct Stats {
|
||||
uint32 commandsSubmitted = 0; // 提交的命令数
|
||||
uint32 commandsExecuted = 0; // 执行的命令数
|
||||
uint32 drawCalls = 0; // 绘制调用次数
|
||||
uint32 batches = 0; // 批次数
|
||||
} stats_;
|
||||
|
||||
//===========================================================================
|
||||
// 视口状态
|
||||
//===========================================================================
|
||||
|
||||
int32 viewportX_ = 0, viewportY_ = 0;
|
||||
int32 viewportWidth_ = 0, viewportHeight_ = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/ptr/ref_counted.h>
|
||||
#include <types/ptr/intrusive_ptr.h>
|
||||
#include <types/base/types.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// 前向声明 OpenGL 类型
|
||||
typedef unsigned int GLuint;
|
||||
typedef int GLint;
|
||||
typedef unsigned int GLenum;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 着色器类
|
||||
*
|
||||
* 管理 OpenGL 着色器程序的编译、链接和使用
|
||||
* 支持从文件或源码加载
|
||||
*/
|
||||
class Shader : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Shader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Shader() override;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载着色器
|
||||
* @param vsPath 顶点着色器文件路径
|
||||
* @param fsPath 片段着色器文件路径
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
bool loadFromFile(const std::string& vsPath, const std::string& fsPath);
|
||||
|
||||
/**
|
||||
* @brief 从源码加载着色器
|
||||
* @param vsSource 顶点着色器源码
|
||||
* @param fsSource 片段着色器源码
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
bool loadFromSource(const std::string& vsSource, const std::string& fsSource);
|
||||
|
||||
/**
|
||||
* @brief 绑定着色器程序
|
||||
*/
|
||||
void bind() const;
|
||||
|
||||
/**
|
||||
* @brief 解绑着色器程序
|
||||
*/
|
||||
void unbind() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 Uniform Block 绑定槽位
|
||||
* @param name Uniform Block 名称
|
||||
* @param binding 绑定槽位
|
||||
*/
|
||||
void setUniformBlock(const std::string& name, uint32_t binding);
|
||||
|
||||
/**
|
||||
* @brief 设置 int 类型 uniform
|
||||
* @param name uniform 名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setInt(const std::string& name, int value);
|
||||
|
||||
/**
|
||||
* @brief 设置 float 类型 uniform
|
||||
* @param name uniform 名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setFloat(const std::string& name, float value);
|
||||
|
||||
/**
|
||||
* @brief 设置 vec2 类型 uniform
|
||||
* @param name uniform 名称
|
||||
* @param x X 分量
|
||||
* @param y Y 分量
|
||||
*/
|
||||
void setVec2(const std::string& name, float x, float y);
|
||||
|
||||
/**
|
||||
* @brief 设置 vec4 类型 uniform
|
||||
* @param name uniform 名称
|
||||
* @param x X 分量
|
||||
* @param y Y 分量
|
||||
* @param z Z 分量
|
||||
* @param w W 分量
|
||||
*/
|
||||
void setVec4(const std::string& name, float x, float y, float z, float w);
|
||||
|
||||
/**
|
||||
* @brief 设置 mat4 类型 uniform
|
||||
* @param name uniform 名称
|
||||
* @param value 矩阵数据指针(16个float)
|
||||
*/
|
||||
void setMat4(const std::string& name, const float* value);
|
||||
|
||||
/**
|
||||
* @brief 获取着色器程序 ID
|
||||
* @return OpenGL 程序 ID
|
||||
*/
|
||||
GLuint getProgram() const { return program_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否已加载
|
||||
* @return 是否已加载
|
||||
*/
|
||||
bool isLoaded() const { return program_ != 0; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 编译着色器
|
||||
* @param type 着色器类型
|
||||
* @param source 源码
|
||||
* @return 着色器对象,失败返回 0
|
||||
*/
|
||||
GLuint compileShader(GLenum type, const std::string& source);
|
||||
|
||||
/**
|
||||
* @brief 链接着色器程序
|
||||
* @param vertexShader 顶点着色器
|
||||
* @param fragmentShader 片段着色器
|
||||
* @return 链接是否成功
|
||||
*/
|
||||
bool linkProgram(GLuint vertexShader, GLuint fragmentShader);
|
||||
|
||||
/**
|
||||
* @brief 获取 uniform 位置
|
||||
* @param name uniform 名称
|
||||
* @return uniform 位置,不存在返回 -1
|
||||
*/
|
||||
GLint getUniformLocation(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 添加版本声明(如果不存在)
|
||||
* @param source 源码
|
||||
* @param type 着色器类型
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string addVersionIfNeeded(const std::string& source, GLenum type);
|
||||
|
||||
private:
|
||||
GLuint program_ = 0; // OpenGL 程序对象
|
||||
std::unordered_map<std::string, GLint> uniformCache_; // Uniform 位置缓存
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include <types/ptr/ref_counted.h>
|
||||
#include <types/ptr/intrusive_ptr.h>
|
||||
#include <renderer/render_types.h>
|
||||
#include <types/base/types.h>
|
||||
#include <string>
|
||||
|
||||
// 前向声明 OpenGL 类型
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 纹理类
|
||||
*
|
||||
* 管理 OpenGL 纹理的创建、加载和绑定
|
||||
* 支持从文件或内存加载
|
||||
*/
|
||||
class Texture : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Texture();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Texture() override;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param path 文件路径
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
bool loadFromFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从内存加载纹理
|
||||
* @param data 像素数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param format 像素格式
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
bool loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format);
|
||||
|
||||
/**
|
||||
* @brief 创建空纹理
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param format 像素格式
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
bool create(int width, int height, TextureFormat format);
|
||||
|
||||
/**
|
||||
* @brief 绑定纹理到指定槽位
|
||||
* @param slot 纹理槽位
|
||||
*/
|
||||
void bind(uint32_t slot = 0) const;
|
||||
|
||||
/**
|
||||
* @brief 解绑纹理
|
||||
*/
|
||||
void unbind() const;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
int getWidth() const { return width_; }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理高度
|
||||
* @return 高度
|
||||
*/
|
||||
int getHeight() const { return height_; }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理格式
|
||||
* @return 像素格式
|
||||
*/
|
||||
TextureFormat getFormat() const { return format_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 纹理对象
|
||||
* @return OpenGL 纹理对象
|
||||
*/
|
||||
GLuint getHandle() const { return texture_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否已加载
|
||||
* @return 是否已加载
|
||||
*/
|
||||
bool isLoaded() const { return texture_ != 0; }
|
||||
|
||||
private:
|
||||
GLuint texture_ = 0; // OpenGL 纹理对象
|
||||
int width_ = 0; // 纹理宽度
|
||||
int height_ = 0; // 纹理高度
|
||||
TextureFormat format_ = TextureFormat::RGBA8; // 像素格式
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <memory>
|
||||
#include <renderer/render_types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 统一缓冲区对象 (UBO)
|
||||
*
|
||||
* 用于高效地传递 uniform 数据到 GPU
|
||||
* 支持 std140 布局标准
|
||||
*/
|
||||
class UniformBuffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
UniformBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~UniformBuffer();
|
||||
|
||||
/**
|
||||
* @brief 创建 UBO
|
||||
* @param size 缓冲区大小(字节)
|
||||
* @param binding 绑定槽位
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
bool create(uint32_t size, uint32_t binding);
|
||||
|
||||
/**
|
||||
* @brief 销毁 UBO
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区数据
|
||||
* @param data 数据源指针
|
||||
* @param size 数据大小
|
||||
* @param offset 缓冲区偏移(字节)
|
||||
*/
|
||||
void update(const void *data, uint32_t size, uint32_t offset = 0);
|
||||
|
||||
/**
|
||||
* @brief 绑定到指定槽位
|
||||
* @param binding 绑定槽位
|
||||
*/
|
||||
void bind(uint32_t binding);
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小
|
||||
* @return 缓冲区大小
|
||||
*/
|
||||
uint32_t getSize() const { return size_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区对象
|
||||
* @return OpenGL 缓冲区对象
|
||||
*/
|
||||
GLuint getHandle() const { return ubo_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
* @return 是否有效
|
||||
*/
|
||||
bool isValid() const { return ubo_ != 0; }
|
||||
|
||||
private:
|
||||
GLuint ubo_ = 0; // OpenGL 缓冲区对象
|
||||
uint32_t size_ = 0; // 缓冲区大小
|
||||
uint32_t binding_ = 0; // 绑定槽位
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief UBO 管理器
|
||||
*
|
||||
* 管理多个 UBO 的分配和回收
|
||||
* 支持每帧重置和对象池复用
|
||||
*/
|
||||
class UniformBufferManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
UniformBufferManager();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~UniformBufferManager();
|
||||
|
||||
// 禁止拷贝
|
||||
UniformBufferManager(const UniformBufferManager &) = delete;
|
||||
UniformBufferManager &operator=(const UniformBufferManager &) = delete;
|
||||
|
||||
// 允许移动
|
||||
UniformBufferManager(UniformBufferManager &&) noexcept;
|
||||
UniformBufferManager &operator=(UniformBufferManager &&) noexcept;
|
||||
|
||||
/**
|
||||
* @brief 初始化管理器
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
bool initialize();
|
||||
|
||||
/**
|
||||
* @brief 关闭管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 获取全局 UBO
|
||||
* @return 全局 UBO 指针
|
||||
*/
|
||||
UniformBuffer *getGlobalUBO();
|
||||
|
||||
/**
|
||||
* @brief 获取或创建材质 UBO
|
||||
* @param size 缓冲区大小
|
||||
* @return UBO 指针
|
||||
*/
|
||||
UniformBuffer *acquireMaterialUBO(uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief 重置材质 UBO 池
|
||||
*
|
||||
* 每帧调用,重置当前索引以便复用 UBO
|
||||
*/
|
||||
void resetMaterialUBOs();
|
||||
|
||||
/**
|
||||
* @brief 更新全局 UBO 数据
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
*/
|
||||
void updateGlobalUBO(const void *data, uint32_t size);
|
||||
|
||||
private:
|
||||
// 全局 UBO(用于 viewProjection、time 等全局数据)
|
||||
std::unique_ptr<UniformBuffer> globalUBO_;
|
||||
|
||||
// 材质 UBO 池
|
||||
std::vector<std::unique_ptr<UniformBuffer>> materialUBOPool_;
|
||||
uint32_t currentUBOIndex_ = 0;
|
||||
|
||||
// 常量
|
||||
static constexpr uint32_t INITIAL_UBO_POOL_SIZE = 16;
|
||||
static constexpr uint32_t GLOBAL_UBO_SIZE =
|
||||
256; // 足够存储 viewProjection + time + screenSize
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <types/base/types.h>
|
||||
#include <types/math/vec2.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -25,6 +26,21 @@ struct Transform {
|
|||
}
|
||||
bool operator!=(const Transform& o) const { return !(*this == o); }
|
||||
|
||||
/**
|
||||
* @brief 转换为 4x4 矩阵(列主序)
|
||||
* @param out 输出矩阵(16个float)
|
||||
*/
|
||||
void toMatrix(float* out) const {
|
||||
float c = std::cos(rot);
|
||||
float s = std::sin(rot);
|
||||
|
||||
// 列主序矩阵
|
||||
out[0] = scale.x * c; out[1] = scale.x * s; out[2] = 0.0f; out[3] = 0.0f;
|
||||
out[4] = -scale.y * s; out[5] = scale.y * c; out[6] = 0.0f; out[7] = 0.0f;
|
||||
out[8] = 0.0f; out[9] = 0.0f; out[10] = 1.0f; out[11] = 0.0f;
|
||||
out[12] = pos.x; out[13] = pos.y; out[14] = 0.0f; out[15] = 1.0f;
|
||||
}
|
||||
|
||||
static const Transform Identity;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
#version 320 es
|
||||
precision mediump float;
|
||||
|
||||
// 材质 UBO
|
||||
layout(std140, binding = 1) uniform MaterialUBO {
|
||||
vec4 tintColor;
|
||||
float opacity;
|
||||
} uMaterial;
|
||||
|
||||
// 输入从顶点着色器
|
||||
in vec2 vTexCoord;
|
||||
in vec4 vColor;
|
||||
|
||||
// 纹理采样器
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
// 输出颜色
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
// 采样纹理
|
||||
vec4 texColor = texture(uTexture, vTexCoord);
|
||||
|
||||
// 应用顶点颜色、材质颜色和透明度
|
||||
fragColor = texColor * vColor * uMaterial.tintColor;
|
||||
fragColor.a *= uMaterial.opacity;
|
||||
|
||||
// 丢弃完全透明的像素
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#version 320 es
|
||||
|
||||
// 全局 UBO
|
||||
layout(std140, binding = 0) uniform GlobalUBO {
|
||||
mat4 viewProjection;
|
||||
vec2 screenSize;
|
||||
float time;
|
||||
} uGlobal;
|
||||
|
||||
// 实例 UBO
|
||||
layout(std140, binding = 2) uniform InstanceUBO {
|
||||
mat4 transforms[256];
|
||||
} uInstances;
|
||||
|
||||
// 顶点属性
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
layout(location = 2) in vec4 aColor;
|
||||
|
||||
// 输出到片段着色器
|
||||
out vec2 vTexCoord;
|
||||
out vec4 vColor;
|
||||
|
||||
void main() {
|
||||
// 获取实例变换矩阵
|
||||
mat4 instanceTransform = uInstances.transforms[gl_InstanceID];
|
||||
|
||||
// 计算最终位置
|
||||
gl_Position = uGlobal.viewProjection * instanceTransform * vec4(aPosition, 0.0, 1.0);
|
||||
|
||||
// 传递纹理坐标和颜色
|
||||
vTexCoord = aTexCoord;
|
||||
vColor = aColor;
|
||||
}
|
||||
|
|
@ -2,13 +2,12 @@
|
|||
#include <event/events.h>
|
||||
#include <module/module_registry.h>
|
||||
#include <plugin/plugin_loader.h>
|
||||
#include <renderer/renderer_module.h>
|
||||
#include <utils/timer_module.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Context::Context()
|
||||
: pluginLoader_(std::make_unique<PluginLoader>()),
|
||||
timerModule_(std::make_unique<TimerModule>()) {}
|
||||
Context::Context() : pluginLoader_(std::make_unique<PluginLoader>()) {}
|
||||
|
||||
Context::~Context() {
|
||||
if (inited_) {
|
||||
|
|
@ -31,11 +30,6 @@ bool Context::init() {
|
|||
// 发送引擎初始化事件
|
||||
events::OnInit::emit();
|
||||
|
||||
// 初始化定时器模块
|
||||
if (!timerModule_->init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化所有插件
|
||||
if (!pluginLoader_->initAll()) {
|
||||
return false;
|
||||
|
|
@ -56,9 +50,6 @@ void Context::shutdown() {
|
|||
// 关闭所有插件
|
||||
pluginLoader_->shutdownAll();
|
||||
|
||||
// 关闭定时器模块
|
||||
timerModule_->shutdown();
|
||||
|
||||
// 发送引擎关闭事件
|
||||
events::OnShutdown::emit();
|
||||
|
||||
|
|
@ -74,20 +65,24 @@ void Context::tick(float dt) {
|
|||
totalTime_ += dt;
|
||||
frameCount_++;
|
||||
|
||||
// 更新定时器模块
|
||||
timerModule_->update(dt);
|
||||
|
||||
// 发送更新事件
|
||||
events::OnUpdate::emit(dt);
|
||||
|
||||
// 发送延迟更新事件
|
||||
events::OnLateUpdate::emit(dt);
|
||||
|
||||
// 渲染阶段
|
||||
// 1. 渲染开始
|
||||
events::OnRenderBegin::emit();
|
||||
|
||||
// 2. 场景图提交渲染命令(通过 OnRenderSubmit 事件)
|
||||
|
||||
// 3. 渲染结束并执行绘制
|
||||
events::OnRenderEnd::emit();
|
||||
}
|
||||
|
||||
ModuleRegistry &Context::modules() { return ModuleRegistry::instance(); }
|
||||
|
||||
PluginLoader &Context::plugins() { return *pluginLoader_; }
|
||||
|
||||
TimerModule &Context::timer() { return *timerModule_; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -81,9 +81,6 @@ bool WindowModule::create(const WindowCfg &cfg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 发送窗口显示事件
|
||||
events::OnShow::emit();
|
||||
|
||||
E2D_LOG_INFO("Window created: {}x{}", cfg.width, cfg.height);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -165,6 +162,11 @@ void WindowModule::onModuleConfig(const AppConfig &config) {
|
|||
E2D_LOG_ERROR("Failed to create OpenGL context");
|
||||
return;
|
||||
}
|
||||
|
||||
// GL 上下文创建完成后,再发送窗口显示事件
|
||||
// 这样渲染模块可以在收到事件时安全地使用 GL 函数
|
||||
events::OnShow::emit();
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
#include <renderer/material.h>
|
||||
#include <utils/logger.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// MaterialLayout 实现
|
||||
|
||||
MaterialLayout::MaterialLayout() = default;
|
||||
|
||||
void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
|
||||
if (finalized_) {
|
||||
E2D_LOG_WARN("Cannot add param to finalized MaterialLayout");
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialParamInfo info;
|
||||
info.type = type;
|
||||
info.size = getMaterialParamSize(type);
|
||||
info.offset = 0; // 将在 finalize 时计算
|
||||
|
||||
params_[name] = info;
|
||||
}
|
||||
|
||||
void MaterialLayout::finalize() {
|
||||
if (finalized_) return;
|
||||
|
||||
// 计算 std140 布局的偏移
|
||||
// std140 规则:
|
||||
// - 标量和向量:偏移必须是其大小的倍数
|
||||
// - 数组和结构体:偏移必须是 16 的倍数
|
||||
// - vec3 后面需要填充到 16 字节边界
|
||||
|
||||
uint32_t offset = 0;
|
||||
for (auto& pair : params_) {
|
||||
auto& info = pair.second;
|
||||
|
||||
// 对齐到参数大小的倍数
|
||||
uint32_t alignment = info.size;
|
||||
if (info.type == MaterialParamType::Mat4) {
|
||||
alignment = 16; // mat4 需要 16 字节对齐
|
||||
} else if (info.type == MaterialParamType::Vec3) {
|
||||
alignment = 16; // vec3 在 std140 中占 16 字节
|
||||
} else if (alignment < 4) {
|
||||
alignment = 4; // 最小 4 字节对齐
|
||||
}
|
||||
|
||||
// 对齐偏移
|
||||
offset = (offset + alignment - 1) & ~(alignment - 1);
|
||||
|
||||
info.offset = offset;
|
||||
offset += info.size;
|
||||
}
|
||||
|
||||
// 最终大小对齐到 16 字节
|
||||
bufferSize_ = (offset + 15) & ~15;
|
||||
finalized_ = true;
|
||||
}
|
||||
|
||||
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
|
||||
auto it = params_.find(name);
|
||||
if (it != params_.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Material 实现
|
||||
|
||||
Material::Material() = default;
|
||||
|
||||
void Material::setLayout(Ptr<MaterialLayout> layout) {
|
||||
layout_ = layout;
|
||||
if (layout_ && layout_->isFinalized()) {
|
||||
data_.resize(layout_->getBufferSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setShader(Ptr<Shader> shader) {
|
||||
shader_ = shader;
|
||||
}
|
||||
|
||||
void Material::setFloat(const std::string& name, float value) {
|
||||
if (!layout_) return;
|
||||
|
||||
const auto* param = layout_->getParam(name);
|
||||
if (param && param->type == MaterialParamType::Float) {
|
||||
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setVec2(const std::string& name, const Vec2& value) {
|
||||
if (!layout_) return;
|
||||
|
||||
const auto* param = layout_->getParam(name);
|
||||
if (param && param->type == MaterialParamType::Vec2) {
|
||||
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setVec4(const std::string& name, float x, float y, float z, float w) {
|
||||
if (!layout_) return;
|
||||
|
||||
const auto* param = layout_->getParam(name);
|
||||
if (param && param->type == MaterialParamType::Vec4) {
|
||||
float values[4] = {x, y, z, w};
|
||||
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setColor(const std::string& name, const Color& value) {
|
||||
if (!layout_) return;
|
||||
|
||||
const auto* param = layout_->getParam(name);
|
||||
if (param && param->type == MaterialParamType::Color) {
|
||||
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setMat4(const std::string& name, const float* value) {
|
||||
if (!layout_) return;
|
||||
|
||||
const auto* param = layout_->getParam(name);
|
||||
if (param && param->type == MaterialParamType::Mat4) {
|
||||
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setTexture(const std::string& name, TextureHandle texture, uint32_t slot) {
|
||||
// 纹理存储在单独的列表中
|
||||
textures_.push_back({texture, slot});
|
||||
}
|
||||
|
||||
void Material::apply(UniformBufferManager& uboManager) {
|
||||
if (!shader_) return;
|
||||
|
||||
// 绑定着色器
|
||||
shader_->bind();
|
||||
|
||||
// 上传材质数据到 UBO
|
||||
if (!data_.empty()) {
|
||||
auto* ubo = uboManager.acquireMaterialUBO(static_cast<uint32_t>(data_.size()));
|
||||
if (ubo) {
|
||||
ubo->update(data_.data(), static_cast<uint32_t>(data_.size()));
|
||||
ubo->bind(MATERIAL_UBO_BINDING);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置 Uniform Block 绑定(如果着色器支持)
|
||||
shader_->setUniformBlock("MaterialUBO", MATERIAL_UBO_BINDING);
|
||||
|
||||
// TODO: 绑定纹理
|
||||
// for (const auto& [texture, slot] : textures_) {
|
||||
// // 通过纹理句柄获取纹理并绑定
|
||||
// }
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
#include <renderer/mesh.h>
|
||||
#include <utils/logger.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Mesh::Mesh() = default;
|
||||
|
||||
Mesh::~Mesh() {
|
||||
if (vao_ != 0) {
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
vao_ = 0;
|
||||
}
|
||||
if (vbo_ != 0) {
|
||||
glDeleteBuffers(1, &vbo_);
|
||||
vbo_ = 0;
|
||||
}
|
||||
if (ibo_ != 0) {
|
||||
glDeleteBuffers(1, &ibo_);
|
||||
ibo_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertices(const Vertex* vertices, uint32_t count) {
|
||||
if (vao_ == 0) {
|
||||
glGenVertexArrays(1, &vao_);
|
||||
glGenBuffers(1, &vbo_);
|
||||
}
|
||||
|
||||
glBindVertexArray(vao_);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||
|
||||
// 如果容量不足,重新分配
|
||||
if (count > vertexCapacity_) {
|
||||
glBufferData(GL_ARRAY_BUFFER, count * sizeof(Vertex), vertices, GL_DYNAMIC_DRAW);
|
||||
vertexCapacity_ = count;
|
||||
} else {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(Vertex), vertices);
|
||||
}
|
||||
|
||||
// 设置顶点属性
|
||||
// 位置 (location = 0)
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(offsetof(Vertex, position)));
|
||||
|
||||
// 纹理坐标 (location = 1)
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(offsetof(Vertex, texCoord)));
|
||||
|
||||
// 颜色 (location = 2)
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<void*>(offsetof(Vertex, color)));
|
||||
|
||||
glBindVertexArray(0);
|
||||
vertexCount_ = count;
|
||||
}
|
||||
|
||||
void Mesh::setIndices(const uint16_t* indices, uint32_t count) {
|
||||
if (vao_ == 0) return;
|
||||
|
||||
if (ibo_ == 0) {
|
||||
glGenBuffers(1, &ibo_);
|
||||
}
|
||||
|
||||
glBindVertexArray(vao_);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
||||
|
||||
// 如果容量不足,重新分配
|
||||
if (count > indexCapacity_) {
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint16_t), indices, GL_STATIC_DRAW);
|
||||
indexCapacity_ = count;
|
||||
} else {
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, count * sizeof(uint16_t), indices);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
indexCount_ = count;
|
||||
}
|
||||
|
||||
void Mesh::updateVertices(const Vertex* vertices, uint32_t count, uint32_t offset) {
|
||||
if (vao_ == 0 || vbo_ == 0 || vertices == nullptr) return;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset * sizeof(Vertex),
|
||||
count * sizeof(Vertex), vertices);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Mesh::bind() const {
|
||||
if (vao_ != 0) {
|
||||
glBindVertexArray(vao_);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::unbind() const {
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Mesh::draw() const {
|
||||
if (vao_ == 0) return;
|
||||
|
||||
if (indexCount_ > 0) {
|
||||
glDrawElements(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT, nullptr);
|
||||
} else {
|
||||
glDrawArrays(GL_TRIANGLES, 0, vertexCount_);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::drawInstanced(uint32_t instanceCount) const {
|
||||
if (vao_ == 0) return;
|
||||
|
||||
if (indexCount_ > 0) {
|
||||
glDrawElementsInstanced(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT,
|
||||
nullptr, instanceCount);
|
||||
} else {
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, vertexCount_, instanceCount);
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<Mesh> Mesh::createQuad(const Vec2& size, const Rect& uv) {
|
||||
Ptr<Mesh> mesh = makePtr<Mesh>();
|
||||
|
||||
float halfW = size.x * 0.5f;
|
||||
float halfH = size.y * 0.5f;
|
||||
|
||||
Vertex vertices[4] = {
|
||||
{Vec2(-halfW, -halfH), Vec2(uv.x, uv.y + uv.h), Color::White}, // 左下
|
||||
{Vec2( halfW, -halfH), Vec2(uv.x + uv.w, uv.y + uv.h), Color::White}, // 右下
|
||||
{Vec2( halfW, halfH), Vec2(uv.x + uv.w, uv.y), Color::White}, // 右上
|
||||
{Vec2(-halfW, halfH), Vec2(uv.x, uv.y), Color::White} // 左上
|
||||
};
|
||||
|
||||
uint16_t indices[6] = {
|
||||
0, 1, 2, // 第一个三角形
|
||||
0, 2, 3 // 第二个三角形
|
||||
};
|
||||
|
||||
mesh->setVertices(vertices, 4);
|
||||
mesh->setIndices(indices, 6);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
#include <algorithm>
|
||||
#include <event/events.h>
|
||||
#include <renderer/renderer_module.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
RendererModule::RendererModule() = default;
|
||||
|
||||
RendererModule::~RendererModule() = default;
|
||||
|
||||
RendererModule::RendererModule(RendererModule &&other) noexcept
|
||||
: materialPool_(std::move(other.materialPool_)),
|
||||
meshPool_(std::move(other.meshPool_)),
|
||||
texturePool_(std::move(other.texturePool_)),
|
||||
commandBuffer_(std::move(other.commandBuffer_)),
|
||||
commandCount_(other.commandCount_),
|
||||
uniformManager_(std::move(other.uniformManager_)),
|
||||
defaultMaterialHandle_(other.defaultMaterialHandle_),
|
||||
defaultQuadHandle_(other.defaultQuadHandle_),
|
||||
defaultTextureHandle_(other.defaultTextureHandle_),
|
||||
onRenderBeginListener_(std::move(other.onRenderBeginListener_)),
|
||||
onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)),
|
||||
onRenderEndListener_(std::move(other.onRenderEndListener_)),
|
||||
onResizeListener_(std::move(other.onResizeListener_)),
|
||||
onShowListener_(std::move(other.onShowListener_)),
|
||||
glInitialized_(other.glInitialized_), stats_(other.stats_),
|
||||
viewportX_(other.viewportX_), viewportY_(other.viewportY_),
|
||||
viewportWidth_(other.viewportWidth_),
|
||||
viewportHeight_(other.viewportHeight_) {
|
||||
// 重置源对象状态
|
||||
other.commandCount_ = 0;
|
||||
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
||||
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
||||
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
||||
other.glInitialized_ = false;
|
||||
other.stats_ = {};
|
||||
other.viewportX_ = 0;
|
||||
other.viewportY_ = 0;
|
||||
other.viewportWidth_ = 0;
|
||||
other.viewportHeight_ = 0;
|
||||
}
|
||||
|
||||
RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
||||
if (this != &other) {
|
||||
// 清理当前资源
|
||||
if (glInitialized_) {
|
||||
destroyDefaultResources();
|
||||
uniformManager_.shutdown();
|
||||
}
|
||||
|
||||
// 移动资源
|
||||
materialPool_ = std::move(other.materialPool_);
|
||||
meshPool_ = std::move(other.meshPool_);
|
||||
texturePool_ = std::move(other.texturePool_);
|
||||
commandBuffer_ = std::move(other.commandBuffer_);
|
||||
commandCount_ = other.commandCount_;
|
||||
uniformManager_ = std::move(other.uniformManager_);
|
||||
defaultMaterialHandle_ = other.defaultMaterialHandle_;
|
||||
defaultQuadHandle_ = other.defaultQuadHandle_;
|
||||
defaultTextureHandle_ = other.defaultTextureHandle_;
|
||||
onRenderBeginListener_ = std::move(other.onRenderBeginListener_);
|
||||
onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_);
|
||||
onRenderEndListener_ = std::move(other.onRenderEndListener_);
|
||||
onResizeListener_ = std::move(other.onResizeListener_);
|
||||
onShowListener_ = std::move(other.onShowListener_);
|
||||
glInitialized_ = other.glInitialized_;
|
||||
stats_ = other.stats_;
|
||||
viewportX_ = other.viewportX_;
|
||||
viewportY_ = other.viewportY_;
|
||||
viewportWidth_ = other.viewportWidth_;
|
||||
viewportHeight_ = other.viewportHeight_;
|
||||
|
||||
// 重置源对象状态
|
||||
other.commandCount_ = 0;
|
||||
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
||||
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
||||
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
||||
other.glInitialized_ = false;
|
||||
other.stats_ = {};
|
||||
other.viewportX_ = 0;
|
||||
other.viewportY_ = 0;
|
||||
other.viewportWidth_ = 0;
|
||||
other.viewportHeight_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool RendererModule::init() {
|
||||
E2D_LOG_INFO("Initializing RendererModule...");
|
||||
|
||||
// 绑定事件监听器
|
||||
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
||||
onRenderSubmitListener_.bind(
|
||||
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
||||
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
||||
onResizeListener_.bind([this](int32_t w, int32_t h) { onResize(w, h); });
|
||||
|
||||
// 延迟 GL 初始化到窗口显示时
|
||||
onShowListener_.bind([this]() { onWindowShow(); });
|
||||
|
||||
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererModule::onWindowShow() {
|
||||
if (glInitialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Initializing OpenGL context...");
|
||||
|
||||
// 初始化 UBO 管理器(需要 GL 上下文)
|
||||
if (!uniformManager_.initialize()) {
|
||||
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建默认资源(需要 GL 上下文)
|
||||
if (!createDefaultResources()) {
|
||||
E2D_LOG_ERROR("Failed to create default resources");
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置默认视口
|
||||
setViewport(0, 0, 800, 600);
|
||||
|
||||
// 启用深度测试和混合
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glInitialized_ = true;
|
||||
E2D_LOG_INFO("OpenGL context initialized successfully");
|
||||
}
|
||||
|
||||
void RendererModule::shutdown() {
|
||||
E2D_LOG_INFO("Shutting down RendererModule...");
|
||||
|
||||
// 只有在 GL 初始化后才销毁 GL 相关资源
|
||||
if (glInitialized_) {
|
||||
// 销毁默认资源
|
||||
destroyDefaultResources();
|
||||
|
||||
// 关闭 UBO 管理器
|
||||
uniformManager_.shutdown();
|
||||
}
|
||||
|
||||
// 清理资源池
|
||||
materialPool_.slots.clear();
|
||||
while (!materialPool_.freeIndices.empty())
|
||||
materialPool_.freeIndices.pop();
|
||||
|
||||
meshPool_.slots.clear();
|
||||
while (!meshPool_.freeIndices.empty())
|
||||
meshPool_.freeIndices.pop();
|
||||
|
||||
texturePool_.slots.clear();
|
||||
while (!texturePool_.freeIndices.empty())
|
||||
texturePool_.freeIndices.pop();
|
||||
|
||||
glInitialized_ = false;
|
||||
|
||||
E2D_LOG_INFO("RendererModule shutdown complete");
|
||||
}
|
||||
|
||||
MaterialHandle RendererModule::registerMaterial(Ptr<Material> material) {
|
||||
uint64_t handle = materialPool_.acquire();
|
||||
auto *slot = materialPool_.get(handle);
|
||||
if (slot) {
|
||||
slot->material = material;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
MeshHandle RendererModule::registerMesh(Ptr<Mesh> mesh) {
|
||||
uint64_t handle = meshPool_.acquire();
|
||||
auto *slot = meshPool_.get(handle);
|
||||
if (slot) {
|
||||
slot->mesh = mesh;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
TextureHandle RendererModule::registerTexture(Ptr<Texture> texture) {
|
||||
uint64_t handle = texturePool_.acquire();
|
||||
auto *slot = texturePool_.get(handle);
|
||||
if (slot) {
|
||||
slot->texture = texture;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
void RendererModule::unregisterMaterial(MaterialHandle handle) {
|
||||
materialPool_.release(handle);
|
||||
}
|
||||
|
||||
void RendererModule::unregisterMesh(MeshHandle handle) {
|
||||
meshPool_.release(handle);
|
||||
}
|
||||
|
||||
void RendererModule::unregisterTexture(TextureHandle handle) {
|
||||
texturePool_.release(handle);
|
||||
}
|
||||
|
||||
Ptr<Material> RendererModule::getMaterial(MaterialHandle handle) {
|
||||
auto *slot = materialPool_.get(handle);
|
||||
return slot ? slot->material : nullptr;
|
||||
}
|
||||
|
||||
Ptr<Mesh> RendererModule::getMesh(MeshHandle handle) {
|
||||
auto *slot = meshPool_.get(handle);
|
||||
return slot ? slot->mesh : nullptr;
|
||||
}
|
||||
|
||||
Ptr<Texture> RendererModule::getTexture(TextureHandle handle) {
|
||||
auto *slot = texturePool_.get(handle);
|
||||
return slot ? slot->texture : nullptr;
|
||||
}
|
||||
|
||||
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||
viewportX_ = x;
|
||||
viewportY_ = y;
|
||||
viewportWidth_ = width;
|
||||
viewportHeight_ = height;
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
void RendererModule::clear(const Color &color, uint32_t flags) {
|
||||
GLbitfield mask = 0;
|
||||
|
||||
if (flags & CLEAR_COLOR_FLAG) {
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
mask |= GL_COLOR_BUFFER_BIT;
|
||||
}
|
||||
|
||||
if (flags & CLEAR_DEPTH_FLAG) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
}
|
||||
|
||||
if (flags & CLEAR_STENCIL_FLAG) {
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
glClear(mask);
|
||||
}
|
||||
}
|
||||
|
||||
void RendererModule::onRenderBegin() {
|
||||
// 如果 GL 未初始化,跳过渲染
|
||||
if (!glInitialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空命令缓冲区
|
||||
commandCount_ = 0;
|
||||
|
||||
// 重置统计
|
||||
stats_ = {};
|
||||
|
||||
// 重置 UBO 管理器
|
||||
uniformManager_.resetMaterialUBOs();
|
||||
}
|
||||
|
||||
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||
// 如果 GL 未初始化,跳过提交
|
||||
if (!glInitialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查缓冲区是否已满
|
||||
if (commandCount_ >= MAX_RENDER_COMMANDS) {
|
||||
E2D_LOG_WARN("Render command buffer full!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接存入预分配缓冲区(无动态分配)
|
||||
commandBuffer_[commandCount_++] = cmd;
|
||||
stats_.commandsSubmitted++;
|
||||
}
|
||||
|
||||
void RendererModule::onRenderEnd() {
|
||||
// 如果 GL 未初始化,跳过渲染
|
||||
if (!glInitialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 排序命令
|
||||
sortCommands();
|
||||
|
||||
// 批处理并绘制
|
||||
batchAndDraw();
|
||||
|
||||
// 输出统计
|
||||
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
|
||||
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
|
||||
}
|
||||
|
||||
void RendererModule::onResize(int32 width, int32 height) {
|
||||
setViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void RendererModule::sortCommands() {
|
||||
// 使用 std::sort 对命令进行排序
|
||||
std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_,
|
||||
[](const RenderCommand &a, const RenderCommand &b) {
|
||||
return a.sortKey < b.sortKey;
|
||||
});
|
||||
}
|
||||
|
||||
void RendererModule::batchAndDraw() {
|
||||
MaterialHandle lastMaterial = INVALID_MATERIAL_HANDLE;
|
||||
MeshHandle lastMesh = INVALID_MESH_HANDLE;
|
||||
uint32_t batchStart = 0;
|
||||
uint32_t batchCount = 0;
|
||||
|
||||
for (uint32_t i = 0; i < commandCount_; ++i) {
|
||||
const auto &cmd = commandBuffer_[i];
|
||||
|
||||
if (cmd.type != RenderCommandType::DrawMesh) {
|
||||
// 处理非绘制命令
|
||||
if (batchCount > 0) {
|
||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||
stats_.batches++;
|
||||
batchCount = 0;
|
||||
}
|
||||
executeCommand(cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否需要刷新批次
|
||||
if (cmd.drawMesh.material != lastMaterial ||
|
||||
cmd.drawMesh.mesh != lastMesh) {
|
||||
|
||||
// 刷新上一批次
|
||||
if (batchCount > 0) {
|
||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||
stats_.batches++;
|
||||
}
|
||||
|
||||
lastMaterial = cmd.drawMesh.material;
|
||||
lastMesh = cmd.drawMesh.mesh;
|
||||
batchStart = i;
|
||||
batchCount = 1;
|
||||
} else {
|
||||
++batchCount;
|
||||
}
|
||||
|
||||
stats_.commandsExecuted++;
|
||||
}
|
||||
|
||||
// 刷新最后一批
|
||||
if (batchCount > 0) {
|
||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||
stats_.batches++;
|
||||
}
|
||||
}
|
||||
|
||||
void RendererModule::drawBatch(uint32_t start, uint32_t count,
|
||||
MaterialHandle materialHandle,
|
||||
MeshHandle meshHandle) {
|
||||
auto material = getMaterial(materialHandle);
|
||||
auto mesh = getMesh(meshHandle);
|
||||
|
||||
if (!material || !mesh)
|
||||
return;
|
||||
|
||||
// 应用材质(绑定着色器、上传 UBO、绑定纹理)
|
||||
material->apply(uniformManager_);
|
||||
|
||||
// 绑定网格
|
||||
mesh->bind();
|
||||
|
||||
if (count == 1) {
|
||||
// 单绘制
|
||||
mesh->draw();
|
||||
} else {
|
||||
// 批量绘制 - 使用实例化渲染
|
||||
// 上传变换矩阵到 UBO
|
||||
std::vector<float> transforms;
|
||||
transforms.reserve(count * 16);
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const auto &transform = commandBuffer_[start + i].drawMesh.transform;
|
||||
// 将 Transform 转换为 4x4 矩阵并添加到 transforms
|
||||
// 这里简化处理,实际需要完整的矩阵转换
|
||||
float matrix[16];
|
||||
transform.toMatrix(matrix);
|
||||
transforms.insert(transforms.end(), matrix, matrix + 16);
|
||||
}
|
||||
|
||||
// 更新实例 UBO
|
||||
auto *instanceUBO = uniformManager_.acquireMaterialUBO(
|
||||
static_cast<uint32_t>(transforms.size() * sizeof(float)));
|
||||
if (instanceUBO) {
|
||||
instanceUBO->update(
|
||||
transforms.data(),
|
||||
static_cast<uint32_t>(transforms.size() * sizeof(float)));
|
||||
instanceUBO->bind(INSTANCE_UBO_BINDING);
|
||||
}
|
||||
|
||||
// 实例化渲染
|
||||
mesh->drawInstanced(count);
|
||||
}
|
||||
|
||||
stats_.drawCalls++;
|
||||
}
|
||||
|
||||
void RendererModule::executeCommand(const RenderCommand &cmd) {
|
||||
switch (cmd.type) {
|
||||
case RenderCommandType::SetViewport:
|
||||
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
|
||||
cmd.viewport.height);
|
||||
break;
|
||||
|
||||
case RenderCommandType::Clear:
|
||||
clear(cmd.clear.color, cmd.clear.flags);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool RendererModule::createDefaultResources() {
|
||||
// 创建默认着色器
|
||||
auto defaultShader = makePtr<Shader>();
|
||||
if (!defaultShader->loadFromFile("shader/default.vert",
|
||||
"shader/default.frag")) {
|
||||
E2D_LOG_ERROR("Failed to load default shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建默认材质布局
|
||||
auto defaultLayout = makePtr<MaterialLayout>();
|
||||
defaultLayout->addParam("tintColor", MaterialParamType::Color);
|
||||
defaultLayout->addParam("opacity", MaterialParamType::Float);
|
||||
defaultLayout->finalize();
|
||||
|
||||
// 创建默认材质
|
||||
auto defaultMaterial = makePtr<Material>();
|
||||
defaultMaterial->setLayout(defaultLayout);
|
||||
defaultMaterial->setShader(defaultShader);
|
||||
defaultMaterial->setColor("tintColor", Color::White);
|
||||
defaultMaterial->setFloat("opacity", 1.0f);
|
||||
|
||||
defaultMaterialHandle_ = registerMaterial(defaultMaterial);
|
||||
|
||||
// 创建默认四边形
|
||||
auto defaultQuad = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
||||
defaultQuadHandle_ = registerMesh(defaultQuad);
|
||||
|
||||
// 创建默认纹理(1x1 白色)
|
||||
auto defaultTexture = makePtr<Texture>();
|
||||
uint8_t whitePixel[4] = {255, 255, 255, 255};
|
||||
defaultTexture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8);
|
||||
defaultTextureHandle_ = registerTexture(defaultTexture);
|
||||
|
||||
E2D_LOG_INFO("Default resources created");
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererModule::destroyDefaultResources() {
|
||||
if (defaultMaterialHandle_ != INVALID_MATERIAL_HANDLE) {
|
||||
unregisterMaterial(defaultMaterialHandle_);
|
||||
defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
||||
}
|
||||
|
||||
if (defaultQuadHandle_ != INVALID_MESH_HANDLE) {
|
||||
unregisterMesh(defaultQuadHandle_);
|
||||
defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
||||
}
|
||||
|
||||
if (defaultTextureHandle_ != INVALID_TEXTURE_HANDLE) {
|
||||
unregisterTexture(defaultTextureHandle_);
|
||||
defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
#include <renderer/shader.h>
|
||||
#include <utils/logger.h>
|
||||
#include <glad/glad.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Shader::Shader() = default;
|
||||
|
||||
Shader::~Shader() {
|
||||
if (program_ != 0) {
|
||||
glDeleteProgram(program_);
|
||||
program_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::loadFromFile(const std::string& vsPath, const std::string& fsPath) {
|
||||
// 读取顶点着色器
|
||||
std::ifstream vsFile(vsPath);
|
||||
if (!vsFile.is_open()) {
|
||||
E2D_LOG_ERROR("Failed to open vertex shader: {}", vsPath);
|
||||
return false;
|
||||
}
|
||||
std::stringstream vsStream;
|
||||
vsStream << vsFile.rdbuf();
|
||||
std::string vsSource = vsStream.str();
|
||||
|
||||
// 读取片段着色器
|
||||
std::ifstream fsFile(fsPath);
|
||||
if (!fsFile.is_open()) {
|
||||
E2D_LOG_ERROR("Failed to open fragment shader: {}", fsPath);
|
||||
return false;
|
||||
}
|
||||
std::stringstream fsStream;
|
||||
fsStream << fsFile.rdbuf();
|
||||
std::string fsSource = fsStream.str();
|
||||
|
||||
return loadFromSource(vsSource, fsSource);
|
||||
}
|
||||
|
||||
bool Shader::loadFromSource(const std::string& vsSource, const std::string& fsSource) {
|
||||
// 删除旧程序
|
||||
if (program_ != 0) {
|
||||
glDeleteProgram(program_);
|
||||
program_ = 0;
|
||||
}
|
||||
|
||||
uniformCache_.clear();
|
||||
|
||||
// 处理源码(添加版本声明)
|
||||
std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER);
|
||||
std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER);
|
||||
|
||||
// 编译顶点着色器
|
||||
GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS);
|
||||
if (vs == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 编译片段着色器
|
||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS);
|
||||
if (fs == 0) {
|
||||
glDeleteShader(vs);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 链接程序
|
||||
if (!linkProgram(vs, fs)) {
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清理着色器对象
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
|
||||
E2D_LOG_DEBUG("Shader program created successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shader::bind() const {
|
||||
if (program_ != 0) {
|
||||
glUseProgram(program_);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::unbind() const {
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void Shader::setUniformBlock(const std::string& name, uint32_t binding) {
|
||||
if (program_ == 0) return;
|
||||
|
||||
GLuint index = glGetUniformBlockIndex(program_, name.c_str());
|
||||
if (index != GL_INVALID_INDEX) {
|
||||
glUniformBlockBinding(program_, index, binding);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::setInt(const std::string& name, int value) {
|
||||
GLint location = getUniformLocation(name);
|
||||
if (location != -1) {
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::setFloat(const std::string& name, float value) {
|
||||
GLint location = getUniformLocation(name);
|
||||
if (location != -1) {
|
||||
glUniform1f(location, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::setVec2(const std::string& name, float x, float y) {
|
||||
GLint location = getUniformLocation(name);
|
||||
if (location != -1) {
|
||||
glUniform2f(location, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::setVec4(const std::string& name, float x, float y, float z, float w) {
|
||||
GLint location = getUniformLocation(name);
|
||||
if (location != -1) {
|
||||
glUniform4f(location, x, y, z, w);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::setMat4(const std::string& name, const float* value) {
|
||||
GLint location = getUniformLocation(name);
|
||||
if (location != -1) {
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, value);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint Shader::compileShader(GLenum type, const std::string& source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
|
||||
const char* src = source.c_str();
|
||||
glShaderSource(shader, 1, &src, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
// 检查编译状态
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
|
||||
const char* typeStr = (type == GL_VERTEX_SHADER) ? "vertex" : "fragment";
|
||||
E2D_LOG_ERROR("{} shader compilation failed: {}", typeStr, infoLog);
|
||||
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool Shader::linkProgram(GLuint vertexShader, GLuint fragmentShader) {
|
||||
program_ = glCreateProgram();
|
||||
glAttachShader(program_, vertexShader);
|
||||
glAttachShader(program_, fragmentShader);
|
||||
glLinkProgram(program_);
|
||||
|
||||
// 检查链接状态
|
||||
GLint success;
|
||||
glGetProgramiv(program_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(program_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
|
||||
|
||||
glDeleteProgram(program_);
|
||||
program_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GLint Shader::getUniformLocation(const std::string& name) {
|
||||
if (program_ == 0) return -1;
|
||||
|
||||
// 检查缓存
|
||||
auto it = uniformCache_.find(name);
|
||||
if (it != uniformCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 查询 uniform 位置
|
||||
GLint location = glGetUniformLocation(program_, name.c_str());
|
||||
uniformCache_[name] = location;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
std::string Shader::addVersionIfNeeded(const std::string& source, GLenum type) {
|
||||
// 如果已经包含版本声明,直接返回
|
||||
if (source.find("#version") != std::string::npos) {
|
||||
return source;
|
||||
}
|
||||
|
||||
// 添加 OpenGL ES 3.2 版本声明
|
||||
std::string result = "#version 320 es\n";
|
||||
|
||||
// 片段着色器需要添加精度声明
|
||||
if (type == GL_FRAGMENT_SHADER) {
|
||||
result += "precision mediump float;\n";
|
||||
}
|
||||
|
||||
result += source;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
#include <renderer/texture.h>
|
||||
#include <utils/logger.h>
|
||||
#include <glad/glad.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// OpenGL 格式转换函数
|
||||
static GLint getTextureInternalFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::RGBA8: return GL_RGBA8;
|
||||
case TextureFormat::RGB8: return GL_RGB8;
|
||||
case TextureFormat::RGBA4: return GL_RGBA4;
|
||||
case TextureFormat::R8: return GL_R8;
|
||||
case TextureFormat::RG8: return GL_RG8;
|
||||
default: return GL_RGBA8;
|
||||
}
|
||||
}
|
||||
|
||||
static GLenum getTextureFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::RGBA4:
|
||||
return GL_RGBA;
|
||||
case TextureFormat::RGB8:
|
||||
return GL_RGB;
|
||||
case TextureFormat::R8:
|
||||
return GL_RED;
|
||||
case TextureFormat::RG8:
|
||||
return GL_RG;
|
||||
default:
|
||||
return GL_RGBA;
|
||||
}
|
||||
}
|
||||
|
||||
Texture::Texture() = default;
|
||||
|
||||
Texture::~Texture() {
|
||||
if (texture_ != 0) {
|
||||
glDeleteTextures(1, &texture_);
|
||||
texture_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::loadFromFile(const std::string& path) {
|
||||
// 加载图片
|
||||
int channels;
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
||||
|
||||
if (!data) {
|
||||
E2D_LOG_ERROR("Failed to load texture: {}", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据通道数确定格式
|
||||
TextureFormat format;
|
||||
switch (channels) {
|
||||
case 1: format = TextureFormat::R8; break;
|
||||
case 2: format = TextureFormat::RG8; break;
|
||||
case 3: format = TextureFormat::RGB8; break;
|
||||
case 4: format = TextureFormat::RGBA8; break;
|
||||
default: format = TextureFormat::RGBA8; break;
|
||||
}
|
||||
|
||||
bool result = loadFromMemory(data, width_, height_, format);
|
||||
stbi_image_free(data);
|
||||
|
||||
if (result) {
|
||||
E2D_LOG_DEBUG("Texture loaded: {} ({}x{})", path, width_, height_);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
||||
if (texture_ != 0) {
|
||||
glDeleteTextures(1, &texture_);
|
||||
texture_ = 0;
|
||||
}
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format_ = format;
|
||||
|
||||
// 创建纹理
|
||||
glGenTextures(1, &texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
|
||||
// 设置纹理参数
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// 上传纹理数据
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
getTextureInternalFormat(format_),
|
||||
width_, height_, 0,
|
||||
getTextureFormat(format_),
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
|
||||
// 生成 mipmap
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::create(int width, int height, TextureFormat format) {
|
||||
if (texture_ != 0) {
|
||||
glDeleteTextures(1, &texture_);
|
||||
texture_ = 0;
|
||||
}
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format_ = format;
|
||||
|
||||
// 创建纹理
|
||||
glGenTextures(1, &texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
|
||||
// 设置纹理参数
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// 分配纹理存储(无数据)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
getTextureInternalFormat(format_),
|
||||
width_, height_, 0,
|
||||
getTextureFormat(format_),
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::bind(uint32_t slot) const {
|
||||
if (texture_ != 0) {
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::unbind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
#include <renderer/uniform_buffer.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
UniformBuffer::UniformBuffer() = default;
|
||||
|
||||
UniformBuffer::~UniformBuffer() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool UniformBuffer::create(uint32_t size, uint32_t binding) {
|
||||
if (ubo_ != 0) {
|
||||
destroy();
|
||||
}
|
||||
|
||||
size_ = size;
|
||||
binding_ = binding;
|
||||
|
||||
glGenBuffers(1, &ubo_);
|
||||
if (ubo_ == 0) {
|
||||
E2D_LOG_ERROR("Failed to generate UBO");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo_);
|
||||
glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UniformBuffer::destroy() {
|
||||
if (ubo_ != 0) {
|
||||
glDeleteBuffers(1, &ubo_);
|
||||
ubo_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UniformBuffer::update(const void* data, uint32_t size, uint32_t offset) {
|
||||
if (ubo_ == 0 || data == nullptr) return;
|
||||
|
||||
if (offset + size > size_) {
|
||||
E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}",
|
||||
offset, size, size_);
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo_);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
void UniformBuffer::bind(uint32_t binding) {
|
||||
if (ubo_ == 0) return;
|
||||
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_);
|
||||
}
|
||||
|
||||
UniformBufferManager::UniformBufferManager() = default;
|
||||
|
||||
UniformBufferManager::~UniformBufferManager() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
UniformBufferManager::UniformBufferManager(UniformBufferManager&& other) noexcept
|
||||
: globalUBO_(std::move(other.globalUBO_)),
|
||||
materialUBOPool_(std::move(other.materialUBOPool_)),
|
||||
currentUBOIndex_(other.currentUBOIndex_) {
|
||||
other.currentUBOIndex_ = 0;
|
||||
}
|
||||
|
||||
UniformBufferManager& UniformBufferManager::operator=(UniformBufferManager&& other) noexcept {
|
||||
if (this != &other) {
|
||||
shutdown();
|
||||
|
||||
globalUBO_ = std::move(other.globalUBO_);
|
||||
materialUBOPool_ = std::move(other.materialUBOPool_);
|
||||
currentUBOIndex_ = other.currentUBOIndex_;
|
||||
|
||||
other.currentUBOIndex_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool UniformBufferManager::initialize() {
|
||||
// 创建全局 UBO
|
||||
globalUBO_ = std::make_unique<UniformBuffer>();
|
||||
if (!globalUBO_->create(GLOBAL_UBO_SIZE, GLOBAL_UBO_BINDING)) {
|
||||
E2D_LOG_ERROR("Failed to create global UBO");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 预分配材质 UBO 池
|
||||
materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE);
|
||||
|
||||
E2D_LOG_INFO("UniformBufferManager initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void UniformBufferManager::shutdown() {
|
||||
materialUBOPool_.clear();
|
||||
globalUBO_.reset();
|
||||
currentUBOIndex_ = 0;
|
||||
|
||||
E2D_LOG_INFO("UniformBufferManager shutdown");
|
||||
}
|
||||
|
||||
UniformBuffer* UniformBufferManager::getGlobalUBO() {
|
||||
return globalUBO_.get();
|
||||
}
|
||||
|
||||
UniformBuffer* UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
||||
// 查找或创建合适大小的 UBO
|
||||
for (uint32_t i = currentUBOIndex_; i < materialUBOPool_.size(); ++i) {
|
||||
if (materialUBOPool_[i]->getSize() >= size) {
|
||||
currentUBOIndex_ = i + 1;
|
||||
return materialUBOPool_[i].get();
|
||||
}
|
||||
}
|
||||
|
||||
// 需要创建新的 UBO
|
||||
auto ubo = std::make_unique<UniformBuffer>();
|
||||
if (!ubo->create(size, MATERIAL_UBO_BINDING)) {
|
||||
E2D_LOG_ERROR("Failed to create material UBO");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniformBuffer* result = ubo.get();
|
||||
materialUBOPool_.push_back(std::move(ubo));
|
||||
currentUBOIndex_ = materialUBOPool_.size();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void UniformBufferManager::resetMaterialUBOs() {
|
||||
currentUBOIndex_ = 0;
|
||||
}
|
||||
|
||||
void UniformBufferManager::updateGlobalUBO(const void* data, uint32_t size) {
|
||||
if (globalUBO_) {
|
||||
globalUBO_->update(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
Loading…
Reference in New Issue