Compare commits

..

10 Commits

Author SHA1 Message Date
ChestnutYueyue 44e0d65f10 feat(assets): 添加资源热重载和智能卸载功能
实现纹理和着色器的热重载功能,支持文件修改后自动重新加载
添加资源引用计数和LRU缓存机制,支持智能卸载长时间未使用的资源
重构文件监控系统,减少重复代码
扩展AssetStorage功能,增加加载状态管理和访问时间追踪
2026-03-03 19:32:23 +08:00
ChestnutYueyue e0b0a7883d refactor(renderer): 移除实例化渲染功能及相关代码
移除不再需要的实例化渲染功能,包括着色器、材质、渲染命令和测试代码
优化实例缓冲区实现,添加脏标记和增量更新功能
2026-03-03 12:18:32 +08:00
ChestnutYueyue 97be1b746a chore: 删除未使用的实例化顶点着色器文件
该着色器文件未被实际使用,为保持代码库整洁故移除
2026-03-03 11:42:13 +08:00
ChestnutYueyue 717112c437 perf(渲染): 优化渲染性能并添加统计功能
- 实现全局UBO双缓冲机制减少CPU-GPU等待
- 添加渲染统计功能,每60帧输出一次性能数据
- 优化命令列表状态缓存减少冗余OpenGL调用
- 改进材质UBO管理,支持批量更新和CPU缓冲区
- 重构命令队列执行逻辑,支持双缓冲和统计收集
2026-03-03 11:25:43 +08:00
ChestnutYueyue ec6ced9db2 refactor(模块系统): 调整模块初始化优先级并重构文件加载逻辑
重构模块初始化顺序,确保文件模块优先加载
移除Shader类中直接文件操作,统一通过FileModule进行文件访问
添加FileModule实现跨平台文件系统操作,支持RomFS
重构资源加载逻辑,统一使用FileModule进行文件操作
2026-03-03 05:53:40 +08:00
ChestnutYueyue 8cd883ede7 feat(着色器): 添加默认和实例化着色器到场景图示例
添加默认和实例化着色器文件到场景图示例,包括顶点和片段着色器
修改构建脚本自动复制着色器文件到romfs目录
2026-03-03 03:57:19 +08:00
ChestnutYueyue 91e3e8fe57 feat(渲染): 实现实例化渲染功能并优化渲染管线
- 添加实例化渲染支持,包括InstanceBuffer、InstanceBufferManager和相关着色器
- 重构渲染命令队列以支持实例化绘制
- 优化材质系统,使用vector保持参数顺序并添加快速查找
- 改进顶点布局系统,支持实例属性
- 添加全局UBO管理,优化uniform数据传递
- 实现新的实例化测试场景节点
- 更新着色器以支持实例化渲染和UBO
- 改进GL命令列表,支持实例属性绑定
- 添加AssetsModule对实例化资源的支持
- 修复Director在没有主相机时的警告日志
2026-03-03 03:48:55 +08:00
ChestnutYueyue 9041833430 refactor(rhi): 重构渲染硬件接口模块
feat(rhi): 新增RHI模块抽象层
feat(opengl): 实现OpenGL后端支持
refactor(renderer): 重构材质、纹理和网格类以使用RHI接口
refactor(scene): 移除移动构造函数并禁止移动操作
fix(shader): 修复默认片段着色器纹理采样问题
refactor(module): 改进模块注册表以支持实例存储
refactor(window): 移除OpenGL上下文管理交由RHI处理
feat(assets): 为默认材质添加纹理支持
refactor(timer): 移除移动操作并简化实现
refactor(input): 移除移动操作并简化实现
2026-03-03 02:16:29 +08:00
ChestnutYueyue b4a3d6b14b refactor(assets): 移除未使用的chrono头文件 2026-03-02 22:58:03 +08:00
ChestnutYueyue d387532738 refactor(renderer): 移除冗余的<vector>头文件引入
清理不再需要的<vector>头文件,保持头文件简洁性
2026-03-02 22:57:00 +08:00
75 changed files with 9542 additions and 2311 deletions

View File

@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(git diff:*)"
]
}
}

View File

@ -6,34 +6,34 @@
// ========================================
PlayerNode::PlayerNode() {
setName("Player");
setTag(1);
setName("Player");
setTag(1);
// 设置玩家尺寸
setSize(64.0f, 64.0f);
// 设置玩家尺寸
setSize(64.0f, 64.0f);
// 设置锚点为中心点
setAnchor(0.5f, 0.5f);
// 设置锚点为中心点
setAnchor(0.5f, 0.5f);
// 添加精灵渲染组件
auto sprite = makePtr<SpriteRenderer>();
sprite->setColor(Color::Blue);
addComponent(sprite);
// 添加精灵渲染组件
auto sprite = makePtr<SpriteRenderer>();
sprite->setColor(Color::Blue);
addComponent(sprite);
}
void PlayerNode::onUpdate(float dt) {
time_ += dt;
time_ += dt;
// 简单的圆周运动
float radius = 200.0f;
float x = 640.0f + radius * std::cos(time_ * 0.5f);
float y = 360.0f + radius * std::sin(time_ * 0.5f);
// 简单的圆周运动
float radius = 200.0f;
float x = 640.0f + radius * std::cos(time_ * 0.5f);
float y = 360.0f + radius * std::sin(time_ * 0.5f);
setPosition(x, y);
setPosition(x, y);
// 根据移动方向旋转
float angle = time_ * 0.5f * 57.2958f + 90.0f;
setRotation(angle);
// 根据移动方向旋转(顺时针,使用负角度)
float angle = -time_ * 0.5f * 57.2958f + 90.0f;
setRotation(angle);
}
// ========================================
@ -41,23 +41,23 @@ void PlayerNode::onUpdate(float dt) {
// ========================================
RotatingDecoration::RotatingDecoration() {
setName("Decoration");
setName("Decoration");
// 设置尺寸
setSize(32.0f, 32.0f);
// 设置尺寸
setSize(32.0f, 32.0f);
// 设置锚点为中心
setAnchor(0.5f, 0.5f);
// 设置锚点为中心
setAnchor(0.5f, 0.5f);
// 添加精灵渲染组件
auto sprite = makePtr<SpriteRenderer>();
sprite->setColor(Color::Yellow);
addComponent(sprite);
// 添加精灵渲染组件
auto sprite = makePtr<SpriteRenderer>();
sprite->setColor(Color::Yellow);
addComponent(sprite);
}
void RotatingDecoration::onUpdate(float dt) {
// 自转
setRotation(getRotation() + rotationSpeed_ * dt);
// 自转(顺时针,使用负角度)
setRotation(getRotation() - rotationSpeed_ * dt);
}
// ========================================
@ -65,106 +65,104 @@ void RotatingDecoration::onUpdate(float dt) {
// ========================================
GameScene::GameScene() {
// 场景初始化
// 场景初始化
}
void GameScene::onEnter() {
Scene::onEnter();
Scene::onEnter();
// 创建相机
createCamera();
// 创建相机
createCamera();
// 创建玩家
createPlayer();
// 创建玩家
createPlayer();
// 创建装饰物
createDecorations();
// 创建装饰物
createDecorations();
}
void GameScene::onExit() {
// 清理资源
player_.reset();
decorations_.clear();
// 清理资源
player_.reset();
decorations_.clear();
Scene::onExit();
Scene::onExit();
}
void GameScene::update(float dt) {
Scene::update(dt);
Scene::update(dt);
sceneTime_ += dt;
sceneTime_ += dt;
// 更新玩家
if (player_) {
player_->onUpdate(dt);
}
// 更新玩家
if (player_) {
player_->onUpdate(dt);
}
// 更新装饰物
for (auto& decoration : decorations_) {
decoration->onUpdate(dt);
}
// 更新装饰物
for (auto &decoration : decorations_) {
decoration->onUpdate(dt);
}
}
void GameScene::createPlayer() {
player_ = makePtr<PlayerNode>();
player_->setPosition(640.0f, 360.0f);
addChild(player_);
player_ = makePtr<PlayerNode>();
player_->setPosition(640.0f, 360.0f);
addChild(player_);
}
void GameScene::createDecorations() {
// 创建围绕玩家旋转的装饰物
int count = 8;
float radius = 150.0f;
// 创建围绕玩家旋转的装饰物
int count = 8;
float radius = 150.0f;
for (int i = 0; i < count; ++i) {
auto decoration = makePtr<RotatingDecoration>();
for (int i = 0; i < count; ++i) {
auto decoration = makePtr<RotatingDecoration>();
// 计算位置(圆形分布)
float angle = (2.0f * 3.14159f * i) / count;
float x = 640.0f + radius * std::cos(angle);
float y = 360.0f + radius * std::sin(angle);
// 计算位置(圆形分布)
float angle = (2.0f * 3.14159f * i) / count;
float x = 640.0f + radius * std::cos(angle);
float y = 360.0f + radius * std::sin(angle);
decoration->setPosition(x, y);
decoration->setRotationSpeed(45.0f + i * 10.0f);
decoration->setPosition(x, y);
decoration->setRotationSpeed(45.0f + i * 10.0f);
// 设置不同颜色
auto sprite = decoration->getComponent<SpriteRenderer>();
if (sprite) {
float hue = static_cast<float>(i) / count;
// 简单的HSV到RGB转换
float r = std::abs(hue * 6.0f - 3.0f) - 1.0f;
float g = 2.0f - std::abs(hue * 6.0f - 2.0f);
float b = 2.0f - std::abs(hue * 6.0f - 4.0f);
sprite->setColor(Color(
std::max(0.0f, std::min(1.0f, r)),
std::max(0.0f, std::min(1.0f, g)),
std::max(0.0f, std::min(1.0f, b)),
1.0f
));
}
decorations_.push_back(decoration);
addChild(decoration);
// 设置不同颜色
auto sprite = decoration->getComponent<SpriteRenderer>();
if (sprite) {
float hue = static_cast<float>(i) / count;
// 简单的HSV到RGB转换
float r = std::abs(hue * 6.0f - 3.0f) - 1.0f;
float g = 2.0f - std::abs(hue * 6.0f - 2.0f);
float b = 2.0f - std::abs(hue * 6.0f - 4.0f);
sprite->setColor(Color(std::max(0.0f, std::min(1.0f, r)),
std::max(0.0f, std::min(1.0f, g)),
std::max(0.0f, std::min(1.0f, b)), 1.0f));
}
decorations_.push_back(decoration);
addChild(decoration);
}
}
void GameScene::createCamera() {
// 创建相机节点
auto cameraNode = makePtr<Node>();
cameraNode->setName("MainCamera");
// 相机位置在(0, 0),这样世界坐标直接映射到屏幕
cameraNode->setPosition(0.0f, 0.0f);
// 创建相机节点
auto cameraNode = makePtr<Node>();
cameraNode->setName("MainCamera");
// 相机位置在(0, 0),这样世界坐标直接映射到屏幕
cameraNode->setPosition(0.0f, 0.0f);
// 添加相机组件
auto camera = makePtr<CameraComponent>();
// 使用标准的2D投影左上角为(0, 0),右下角为(1280, 720)
// Y轴向下bottom=720, top=0
camera->setOrtho(0.0f, 1280.0f, 720.0f, 0.0f, -1.0f, 1.0f);
cameraNode->addComponent(camera);
// 添加相机组件
auto camera = makePtr<CameraComponent>();
// 使用标准的2D投影左上角为(0, 0),右下角为(1280, 720)
// Y轴向下bottom=720, top=0
camera->setOrtho(0.0f, 1280.0f, 720.0f, 0.0f, -1.0f, 1.0f);
cameraNode->addComponent(camera);
// 设置为主相机
setMainCamera(camera);
// 设置为主相机
setMainCamera(camera);
// 添加相机节点到场景
addChild(cameraNode);
// 添加相机节点到场景
addChild(cameraNode);
}

View File

@ -0,0 +1,40 @@
#version 320 es
precision highp float;
// 从顶点着色器输入
in vec2 vTexCoord;
in vec4 vColor;
in vec4 vTintColor;
in float vOpacity;
// 纹理采样器
uniform sampler2D uTexture;
// 输出颜色
out vec4 fragColor;
/**
* @brief
*
*
*/
void main() {
// 采样纹理
vec4 texColor = texture(uTexture, vTexCoord);
// 如果纹理采样结果是黑色或透明,使用白色作为默认值
if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) {
texColor = vec4(1.0, 1.0, 1.0, 1.0);
}
// 混合:纹理 * 顶点颜色 * 色调
fragColor = texColor * vColor * vTintColor;
// 应用透明度
fragColor.a *= vOpacity;
// Alpha 测试:丢弃几乎透明的像素
if (fragColor.a < 0.01) {
discard;
}
}

View File

@ -0,0 +1,48 @@
#version 320 es
precision highp float;
// 全局 UBO (binding = 0) - 每帧更新一次
layout(std140, binding = 0) uniform GlobalUBO {
mat4 uViewProjection;
vec4 uCameraPosition;
float uTime;
float uDeltaTime;
vec2 uScreenSize;
};
// 材质 UBO (binding = 1) - 每物体更新
layout(std140, binding = 1) uniform MaterialUBO {
vec4 uColor;
vec4 uTintColor;
float uOpacity;
float uPadding[3]; // std140 对齐填充
};
// 模型矩阵作为单独的统一变量(每个物体设置)
uniform mat4 uModelMatrix;
// 顶点属性
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
layout(location = 2) in vec4 aColor;
// 输出到片段着色器
out vec2 vTexCoord;
out vec4 vColor;
out vec4 vTintColor;
out float vOpacity;
/**
* @brief
*
*
*
*/
void main() {
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
// 混合顶点颜色和材质 UBO 中的颜色
vColor = aColor * uColor;
vTintColor = uTintColor;
vOpacity = uOpacity;
}

View File

@ -28,9 +28,25 @@ target("scene_graph_demo")
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
-- 确保 romfs 目录存在
local romfs = path.join(example_dir, "romfs")
if not os.isdir(romfs) then
os.mkdir(romfs)
end
-- 复制着色器文件到 romfs/shader 目录
local shader_source = path.join(example_dir, "../../shader")
local shader_target = path.join(romfs, "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 to romfs: " .. shader_target)
end
if os.isfile(nacptool) and os.isfile(elf2nro) then
os.vrunv(nacptool, {"--create", "Scene Graph Demo", "Extra2D Team", "1.0.0", nacp_file})
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
else

View File

@ -4,24 +4,42 @@
#include <types/ptr/intrusive_ptr.h>
#include <vector>
#include <cstdint>
#include <chrono>
#include <algorithm>
#include <unordered_map>
namespace extra2d {
/**
* @brief
*/
enum class AssetLoadState {
Unloaded, ///< 未加载
Loading, ///< 加载中
Loaded, ///< 已加载
Failed ///< 加载失败
};
/**
* @brief
*
* 使 Dense Vec ECS
* O(1) 访
* LRU使
*
* @tparam T
*/
template<typename T>
class AssetStorage {
public:
using TimePoint = std::chrono::steady_clock::time_point;
struct Slot {
Ptr<T> asset;
typename Handle<T>::Generation generation = 0;
bool active = false;
AssetLoadState loadState = AssetLoadState::Unloaded;
TimePoint lastAccessTime; ///< 最后访问时间(用于 LRU
};
AssetStorage() = default;
@ -37,18 +55,23 @@ public:
*/
Handle<T> insert(Ptr<T> asset) {
uint32_t index;
auto now = std::chrono::steady_clock::now();
if (!freeIndices_.empty()) {
index = freeIndices_.back();
freeIndices_.pop_back();
slots_[index].asset = std::move(asset);
slots_[index].active = true;
slots_[index].loadState = AssetLoadState::Loaded;
slots_[index].lastAccessTime = now;
} else {
index = static_cast<uint32_t>(slots_.size());
Slot slot;
slot.asset = std::move(asset);
slot.generation = nextGeneration_++;
slot.active = true;
slot.loadState = AssetLoadState::Loaded;
slot.lastAccessTime = now;
slots_.push_back(std::move(slot));
}
@ -76,7 +99,18 @@ public:
* @param handle
* @return
*/
T* get(Handle<T> handle) const {
T* get(Handle<T> handle) {
if (!isValid(handle)) return nullptr;
updateAccessTime(handle);
return slots_[handle.index()].asset.get();
}
/**
* @brief nullptr - 访
* @param handle
* @return
*/
const T* get(Handle<T> handle) const {
if (!isValid(handle)) return nullptr;
return slots_[handle.index()].asset.get();
}
@ -86,6 +120,17 @@ public:
* @param handle
* @return
*/
Ptr<T> getPtr(Handle<T> handle) {
if (!isValid(handle)) return Ptr<T>();
updateAccessTime(handle);
return slots_[handle.index()].asset;
}
/**
* @brief - 访
* @param handle
* @return
*/
Ptr<T> getPtr(Handle<T> handle) const {
if (!isValid(handle)) return Ptr<T>();
return slots_[handle.index()].asset;
@ -104,6 +149,44 @@ public:
return slot.active && slot.generation == handle.generation();
}
/**
* @brief
* @param handle
* @return
*/
AssetLoadState getLoadState(Handle<T> handle) const {
if (!isValid(handle)) return AssetLoadState::Unloaded;
return slots_[handle.index()].loadState;
}
/**
* @brief
* @param handle
* @param state
*/
void setLoadState(Handle<T> handle, AssetLoadState state) {
if (!isValid(handle)) return;
slots_[handle.index()].loadState = state;
}
/**
* @brief
* @param handle
* @return
*/
bool isLoading(Handle<T> handle) const {
return getLoadState(handle) == AssetLoadState::Loading;
}
/**
* @brief
* @param handle
* @return
*/
bool isLoaded(Handle<T> handle) const {
return getLoadState(handle) == AssetLoadState::Loaded;
}
/**
* @brief
* @param func void(Handle<T>, T*)
@ -137,6 +220,111 @@ public:
*/
size_t capacity() const { return slots_.size(); }
/**
* @brief
* @param handle
* @return
*/
uint32_t getRefCount(Handle<T> handle) const {
if (!isValid(handle)) return 0;
const auto& slot = slots_[handle.index()];
return slot.asset ? slot.asset.refCount() : 0;
}
/**
* @brief 访
* @param handle
* @return 访
*/
TimePoint getLastAccessTime(Handle<T> handle) const {
if (!isValid(handle)) return TimePoint();
return slots_[handle.index()].lastAccessTime;
}
/**
* @brief 访
* @param handle
*/
void updateAccessTime(Handle<T> handle) {
if (!isValid(handle)) return;
slots_[handle.index()].lastAccessTime = std::chrono::steady_clock::now();
}
/**
* @brief 1AssetStorage自己持有引用
* @param maxAgeSeconds 300
* @return
*/
std::vector<Handle<T>> findUnloadableResources(int maxAgeSeconds = 300) const {
std::vector<Handle<T>> result;
auto now = std::chrono::steady_clock::now();
for (size_t i = 0; i < slots_.size(); ++i) {
const Slot& slot = slots_[i];
if (slot.active && slot.asset) {
// 引用计数为1表示只有 AssetStorage 自己持有引用
if (slot.asset.refCount() == 1) {
auto age = std::chrono::duration_cast<std::chrono::seconds>(
now - slot.lastAccessTime).count();
if (age >= maxAgeSeconds) {
result.emplace_back(static_cast<uint32_t>(i), slot.generation);
}
}
}
}
// 按访问时间排序,最久未使用的排在前面
std::sort(result.begin(), result.end(),
[this](const Handle<T>& a, const Handle<T>& b) {
return slots_[a.index()].lastAccessTime < slots_[b.index()].lastAccessTime;
});
return result;
}
/**
* @brief 1
* @param handle
* @return
*/
bool unloadResource(Handle<T> handle) {
if (!isValid(handle)) return false;
auto& slot = slots_[handle.index()];
if (!slot.asset) return false;
// 只有引用计数为1时才可以卸载只有AssetStorage自己持有
if (slot.asset.refCount() > 1) {
return false;
}
slot.asset.reset();
slot.loadState = AssetLoadState::Unloaded;
return true;
}
/**
* @brief
* @param maxCount -1
* @param maxAgeSeconds
* @return
*/
size_t unloadOldResources(int maxCount = -1, int maxAgeSeconds = 300) {
auto unloadable = findUnloadableResources(maxAgeSeconds);
size_t unloaded = 0;
for (const auto& handle : unloadable) {
if (maxCount >= 0 && unloaded >= static_cast<size_t>(maxCount)) {
break;
}
if (unloadResource(handle)) {
++unloaded;
}
}
return unloaded;
}
private:
std::vector<Slot> slots_;
std::vector<uint32_t> freeIndices_;

View File

@ -1,10 +1,9 @@
#pragma once
#include <assets/asset_loader.h>
#include <assets/asset_storage.h>
#include <assets/asset_loader.h>
#include <assets/handle.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <event/events.h>
#include <filesystem>
@ -34,7 +33,8 @@ namespace extra2d {
* - Handle<T>:
*/
class AssetsModule : public Module {
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2)
// 优先级为 3在 FileModule (优先级 2) 之后初始化
E2D_REGISTER_MODULE(AssetsModule, "Assets", 3)
public:
AssetsModule();
@ -103,11 +103,52 @@ public:
*/
template <typename T> bool isValid(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> bool isLoading(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> bool isLoaded(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> AssetLoadState getLoadState(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> void remove(Handle<T> handle);
//===========================================================================
// 资源引用统计与智能卸载
//===========================================================================
/**
* @brief
*/
template <typename T> uint32_t getRefCount(Handle<T> handle) const;
/**
* @brief
*/
template <typename T>
std::vector<Handle<T>> findUnloadableResources(int maxAgeSeconds = 300) const;
/**
* @brief
*/
template <typename T> bool unloadResource(Handle<T> handle);
/**
* @brief
*/
template <typename T>
size_t unloadOldResources(int maxCount = -1, int maxAgeSeconds = 300);
//===========================================================================
// 批量加载(目录/包)
//===========================================================================
@ -215,6 +256,12 @@ private:
void addFileWatch(const std::string &path, Handle<Texture> handle);
void addFileWatch(const std::string &path, Handle<Shader> handle);
private:
/**
* @brief
*/
void addFileWatchInternal(const std::string &path, FileWatchInfo &&info);
/**
* @brief
*/
@ -470,6 +517,75 @@ template <> inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
return meshes_.isValid(handle);
}
template <typename T> bool AssetsModule::isLoading(Handle<T> handle) const {
return false;
}
template <>
inline bool AssetsModule::isLoading<Texture>(Handle<Texture> handle) const {
return textures_.isLoading(handle);
}
template <>
inline bool AssetsModule::isLoading<Shader>(Handle<Shader> handle) const {
return shaders_.isLoading(handle);
}
template <>
inline bool AssetsModule::isLoading<Material>(Handle<Material> handle) const {
return materials_.isLoading(handle);
}
template <> inline bool AssetsModule::isLoading<Mesh>(Handle<Mesh> handle) const {
return meshes_.isLoading(handle);
}
template <typename T> bool AssetsModule::isLoaded(Handle<T> handle) const {
return false;
}
template <>
inline bool AssetsModule::isLoaded<Texture>(Handle<Texture> handle) const {
return textures_.isLoaded(handle);
}
template <>
inline bool AssetsModule::isLoaded<Shader>(Handle<Shader> handle) const {
return shaders_.isLoaded(handle);
}
template <>
inline bool AssetsModule::isLoaded<Material>(Handle<Material> handle) const {
return materials_.isLoaded(handle);
}
template <> inline bool AssetsModule::isLoaded<Mesh>(Handle<Mesh> handle) const {
return meshes_.isLoaded(handle);
}
template <typename T> AssetLoadState AssetsModule::getLoadState(Handle<T> handle) const {
return AssetLoadState::Unloaded;
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Texture>(Handle<Texture> handle) const {
return textures_.getLoadState(handle);
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Shader>(Handle<Shader> handle) const {
return shaders_.getLoadState(handle);
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Material>(Handle<Material> handle) const {
return materials_.getLoadState(handle);
}
template <> inline AssetLoadState AssetsModule::getLoadState<Mesh>(Handle<Mesh> handle) const {
return meshes_.getLoadState(handle);
}
template <typename T> void AssetsModule::remove(Handle<T> handle) {}
template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
@ -489,6 +605,108 @@ template <> inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
meshes_.remove(handle);
}
template <typename T> uint32_t AssetsModule::getRefCount(Handle<T> handle) const {
return 0;
}
template <>
inline uint32_t AssetsModule::getRefCount<Texture>(Handle<Texture> handle) const {
return textures_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Shader>(Handle<Shader> handle) const {
return shaders_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Material>(Handle<Material> handle) const {
return materials_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Mesh>(Handle<Mesh> handle) const {
return meshes_.getRefCount(handle);
}
template <typename T>
std::vector<Handle<T>> AssetsModule::findUnloadableResources(int maxAgeSeconds) const {
return {};
}
template <>
inline std::vector<Handle<Texture>>
AssetsModule::findUnloadableResources<Texture>(int maxAgeSeconds) const {
return textures_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Shader>>
AssetsModule::findUnloadableResources<Shader>(int maxAgeSeconds) const {
return shaders_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Material>>
AssetsModule::findUnloadableResources<Material>(int maxAgeSeconds) const {
return materials_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Mesh>>
AssetsModule::findUnloadableResources<Mesh>(int maxAgeSeconds) const {
return meshes_.findUnloadableResources(maxAgeSeconds);
}
template <typename T> bool AssetsModule::unloadResource(Handle<T> handle) {
return false;
}
template <>
inline bool AssetsModule::unloadResource<Texture>(Handle<Texture> handle) {
return textures_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Shader>(Handle<Shader> handle) {
return shaders_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Material>(Handle<Material> handle) {
return materials_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Mesh>(Handle<Mesh> handle) {
return meshes_.unloadResource(handle);
}
template <typename T>
size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) {
return 0;
}
template <>
inline size_t AssetsModule::unloadOldResources<Texture>(int maxCount, int maxAgeSeconds) {
return textures_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Shader>(int maxCount, int maxAgeSeconds) {
return shaders_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Material>(int maxCount, int maxAgeSeconds) {
return materials_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Mesh>(int maxCount, int maxAgeSeconds) {
return meshes_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <typename T>
std::vector<Handle<T>> AssetsModule::loadDir(const std::string &directory,
const std::string &pattern,
@ -509,6 +727,10 @@ template <>
void AssetsModule::loadAsync<Texture>(
const std::string &path, std::function<void(Handle<Texture>)> callback);
template <>
void AssetsModule::loadAsync<Shader>(
const std::string &path, std::function<void(Handle<Shader>)> callback);
template <typename T>
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {}

View File

@ -5,6 +5,7 @@
#include <module/module.h>
#include <string>
#include <typeindex>
#include <unordered_map>
#include <vector>
namespace extra2d {
@ -67,9 +68,35 @@ public:
return registrations_;
}
/**
* @brief
* @param module
*/
void storeModuleInstance(Module* module);
/**
* @brief
* @tparam T
* @return nullptr
*/
template <typename T>
T* getModule() {
auto it = instances_.find(std::type_index(typeid(T)));
if (it != instances_.end()) {
return static_cast<T*>(it->second);
}
return nullptr;
}
/**
* @brief
*/
void clearInstances();
private:
ModuleRegistry() = default;
std::vector<ModuleInfo> registrations_;
std::unordered_map<std::type_index, Module*> instances_;
};
/**
@ -108,4 +135,14 @@ public: \
#define E2D_REGISTER_MODULE_SIMPLE(ClassName) \
E2D_REGISTER_MODULE(ClassName, #ClassName, 100)
/**
* @brief
* @tparam T
* @return
*/
template <typename T>
T* getModule() {
return ModuleRegistry::instance().getModule<T>();
}
} // namespace extra2d

View File

@ -2,8 +2,8 @@
#include <module/module.h>
#include <module/module_registry.h>
#include <types/base/types.h>
#include <string>
#include <types/base/types.h>
#include <vector>
namespace extra2d {
@ -12,23 +12,23 @@ namespace extra2d {
* @brief
*/
struct FileInfo {
std::string path;
std::string name;
bool isDir = false;
int64 size = 0;
std::string path;
std::string name;
bool isDir = false;
int64 size = 0;
};
/**
* @brief
*/
struct FileData {
bool ok = false;
std::vector<uint8> data;
std::string error;
bool ok = false;
std::vector<uint8> data;
std::string error;
operator bool() const { return ok; }
const uint8* ptr() const { return data.data(); }
size_t size() const { return data.size(); }
operator bool() const { return ok; }
const uint8 *ptr() const { return data.data(); }
size_t size() const { return data.size(); }
};
/**
@ -38,123 +38,142 @@ struct FileData {
* Context
*/
class FileModule : public Module {
// 自动注册到模块系统,优先级为 20系统模块
E2D_REGISTER_MODULE(FileModule, "File", 20)
// 优先级为 2系统级模块在 TimerModule (优先级 4) 之前初始化
E2D_REGISTER_MODULE(FileModule, "File", 2)
public:
FileModule();
~FileModule() override;
FileModule();
~FileModule() override;
// 禁止拷贝
FileModule(const FileModule&) = delete;
FileModule& operator=(const FileModule&) = delete;
// 禁止拷贝
FileModule(const FileModule &) = delete;
FileModule &operator=(const FileModule &) = delete;
// 允许移动
FileModule(FileModule&&) noexcept;
FileModule& operator=(FileModule&&) noexcept;
// 允许移动
FileModule(FileModule &&) noexcept;
FileModule &operator=(FileModule &&) noexcept;
// Module 接口实现
bool init() override;
void shutdown() override;
// Module 接口实现
bool init() override;
void shutdown() override;
/**
* @brief
*/
bool exists(const std::string& path) const;
/**
* @brief
*/
bool exists(const std::string &path) const;
/**
* @brief
*/
bool isDir(const std::string& path) const;
/**
* @brief
*/
bool isDir(const std::string &path) const;
/**
* @brief
*/
FileData read(const std::string& path) const;
/**
* @brief
*/
FileData read(const std::string &path) const;
/**
* @brief
*/
std::string readString(const std::string& path) const;
/**
* @brief
*/
std::string readString(const std::string &path) const;
/**
* @brief
*/
bool write(const std::string& path, const void* data, size_t size) const;
/**
* @brief
*/
bool write(const std::string &path, const void *data, size_t size) const;
/**
* @brief
*/
bool writeString(const std::string& path, const std::string& content) const;
/**
* @brief
*/
bool writeString(const std::string &path, const std::string &content) const;
/**
* @brief
*/
bool append(const std::string& path, const void* data, size_t size) const;
/**
* @brief
*/
bool append(const std::string &path, const void *data, size_t size) const;
/**
* @brief
*/
bool remove(const std::string& path) const;
/**
* @brief
*/
bool remove(const std::string &path) const;
/**
* @brief
*/
bool mkdir(const std::string& path) const;
/**
* @brief
*/
bool mkdir(const std::string &path) const;
/**
* @brief
*/
std::vector<FileInfo> listDir(const std::string& path) const;
/**
* @brief
*/
std::vector<FileInfo> listDir(const std::string &path) const;
/**
* @brief
*/
int64 fileSize(const std::string& path) const;
/**
* @brief
*/
int64 fileSize(const std::string &path) const;
/**
* @brief
*/
std::string ext(const std::string& path) const;
/**
* @brief
*/
std::string ext(const std::string &path) const;
/**
* @brief
*/
std::string fileName(const std::string& path) const;
/**
* @brief
*/
std::string fileName(const std::string &path) const;
/**
* @brief
*/
std::string dirName(const std::string& path) const;
/**
* @brief
*/
std::string dirName(const std::string &path) const;
/**
* @brief
*/
std::string join(const std::string& a, const std::string& b) const;
/**
* @brief
*/
std::string join(const std::string &a, const std::string &b) const;
/**
* @brief
*/
std::string writableDir() const;
/**
* @brief
*/
std::string writableDir() const;
/**
* @brief
*/
void setAssetRoot(const std::string& root) { assetRoot_ = root; }
/**
* @brief
*/
void setAssetRoot(const std::string &root) { assetRoot_ = root; }
/**
* @brief
*/
const std::string& assetRoot() const { return assetRoot_; }
/**
* @brief
*/
const std::string &assetRoot() const { return assetRoot_; }
/**
* @brief
*/
std::string assetPath(const std::string& relPath) const;
/**
* @brief
*/
std::string assetPath(const std::string &relPath) const;
/**
* @brief RomFS
* @param path
* @return "romfs:/"
*/
bool isRomfsPath(const std::string &path) const;
/**
* @brief RomFS Switch
* @param root RomFS "romfs:/"
*/
void setRomfsRoot(const std::string &root) { romfsRoot_ = root; }
/**
* @brief RomFS
*/
const std::string &romfsRoot() const { return romfsRoot_; }
private:
std::string assetRoot_;
std::string writableDir_;
std::string assetRoot_;
std::string writableDir_;
std::string romfsRoot_ = "romfs:/";
};
} // namespace extra2d

View File

@ -172,20 +172,18 @@ using TouchCb = std::function<void(const TouchPoint &)>;
* 使 Module
*/
class InputModule : public Module {
// 自动注册到模块系统,优先级为 10
E2D_REGISTER_MODULE(InputModule, "Input", 10)
// 优先级为 7在 TimerModule (优先级 6) 之后初始化
E2D_REGISTER_MODULE(InputModule, "Input", 7)
public:
InputModule();
~InputModule() override;
// 禁止拷贝
// 禁止拷贝和移动
InputModule(const InputModule &) = delete;
InputModule &operator=(const InputModule &) = delete;
// 允许移动
InputModule(InputModule &&) noexcept;
InputModule &operator=(InputModule &&) noexcept;
InputModule(InputModule &&) = delete;
InputModule &operator=(InputModule &&) = delete;
// Module 接口实现
bool init() override;

View File

@ -33,13 +33,11 @@ public:
WindowModule();
~WindowModule() override;
// 禁止拷贝
// 禁止拷贝和移动
WindowModule(const WindowModule &) = delete;
WindowModule &operator=(const WindowModule &) = delete;
// 允许移动
WindowModule(WindowModule &&) noexcept;
WindowModule &operator=(WindowModule &&) noexcept;
WindowModule(WindowModule &&) = delete;
WindowModule &operator=(WindowModule &&) = delete;
// Module 接口实现
bool init() override;
@ -121,28 +119,6 @@ public:
*/
bool shouldClose() const { return shouldClose_; }
/**
* @brief OpenGL ES
* @return
*/
bool createGLContext();
/**
* @brief OpenGL ES
*/
void destroyGLContext();
/**
* @brief
*/
void swapBuffers();
/**
* @brief OpenGL ES
* @return SDL_GLContext
*/
void* getGLContext() const;
private:
void handleWindowEvent(const SDL_WindowEvent &evt);
@ -162,7 +138,6 @@ private:
void onRenderPresent();
SDL_Window *window_ = nullptr;
SDL_GLContext glContext_ = nullptr;
bool shouldClose_ = false;
CloseCb onClose_;

View File

@ -0,0 +1,424 @@
#pragma once
#include <array>
#include <memory>
#include <renderer/rhi/rhi.h>
#include <renderer/rhi/rhi_command_list.h>
#include <renderer/uniform_buffer.h>
#include <types/math/color.h>
#include <types/math/transform.h>
#include <unordered_map>
#include <vector>
namespace extra2d {
// 前向声明
class CommandQueue;
class Material;
class Mesh;
class UniformBufferManager;
template <typename T> class Ptr;
/**
* @brief
*
* 64
* | ID (32) | (16) | (8) | (8) |
*/
struct DrawKey {
uint64_t value;
DrawKey() : value(0) {}
explicit DrawKey(uint64_t v) : value(v) {}
// 构建排序键
static DrawKey make(uint32_t materialId, uint16_t depth, uint8_t layer,
uint8_t flags = 0) {
DrawKey key;
key.value = (static_cast<uint64_t>(materialId) << 32) |
(static_cast<uint64_t>(depth) << 16) |
(static_cast<uint64_t>(layer) << 8) |
static_cast<uint64_t>(flags);
return key;
}
uint32_t getMaterialId() const { return static_cast<uint32_t>(value >> 32); }
uint16_t getDepth() const {
return static_cast<uint16_t>((value >> 16) & 0xFFFF);
}
uint8_t getLayer() const { return static_cast<uint8_t>((value >> 8) & 0xFF); }
uint8_t getFlags() const { return static_cast<uint8_t>(value & 0xFF); }
bool operator<(const DrawKey &other) const { return value < other.value; }
bool operator>(const DrawKey &other) const { return value > other.value; }
};
/**
* @brief
*
*
*/
struct DrawCommand {
DrawKey key; // 排序键
PipelineHandle pipeline; // 管线状态
BufferHandle vertexBuffer; // 顶点缓冲区
BufferHandle indexBuffer; // 索引缓冲区(可选)
uint32_t vertexCount; // 顶点数量
uint32_t indexCount; // 索引数量
std::array<TextureHandle, 8> textures; // 纹理数组
uint32_t textureCount; // 纹理数量
BufferHandle materialUBO; // 材质 UBO
uint32_t materialUBOSize; // 材质 UBO 大小
uint32_t materialUBOOffset; // 材质 UBO 在全局缓冲区中的偏移
// 变换和颜色数据(用于设置 shader uniform
Mat4 modelMatrix; // 模型矩阵
Color color; // 颜色
DrawCommand()
: vertexCount(0), indexCount(0), textureCount(0),
materialUBOSize(0), materialUBOOffset(0),
modelMatrix(glm::identity<Mat4>()), color(Color::White) {}
// 检查是否使用索引绘制
bool isIndexed() const { return indexCount > 0; }
};
/**
* @brief
*
* 线
*/
struct CommandBatch {
PipelineHandle pipeline; // 共享管线
std::array<TextureHandle, 8> textures; // 共享纹理
uint32_t textureCount;
uint32_t startIndex; // 批次起始命令索引
uint32_t count; // 批次命令数量
CommandBatch() : textureCount(0), startIndex(0), count(0) {}
// 检查是否与命令兼容
bool isCompatibleWith(const DrawCommand &cmd) const {
if (pipeline != cmd.pipeline)
return false;
if (textureCount != cmd.textureCount)
return false;
for (uint32_t i = 0; i < textureCount; ++i) {
if (textures[i] != cmd.textures[i])
return false;
}
return true;
}
};
/**
* @brief
*
*
* 线> >
* 使
*/
class CommandSorter {
public:
/**
* @brief
*/
CommandSorter();
/**
* @brief
*/
~CommandSorter();
// 禁止拷贝
CommandSorter(const CommandSorter&) = delete;
CommandSorter& operator=(const CommandSorter&) = delete;
// 允许移动
CommandSorter(CommandSorter&&) noexcept;
CommandSorter& operator=(CommandSorter&&) noexcept;
/**
* @brief
* @param cmd
* @return
*/
uint32_t addCommand(const DrawCommand &cmd);
/**
* @brief
*/
void sort();
/**
* @brief
* @param index
* @return
*/
const DrawCommand &getCommand(uint32_t index) const {
return commands_[sortedIndices_[index]];
}
/**
* @brief
* @return
*/
uint32_t getCount() const { return commandCount_; }
/**
* @brief
*/
void clear();
/**
* @brief
* @param capacity
*/
void reserve(uint32_t capacity);
private:
std::vector<DrawCommand> commands_; // 命令缓冲区(复用)
std::vector<uint32_t> sortedIndices_; // 排序索引缓冲区(复用)
uint32_t commandCount_ = 0; // 当前命令数量
static constexpr uint32_t INITIAL_CAPACITY = 1024; // 初始容量
};
/**
* @brief
*
*
*
*/
class CommandBatcher {
public:
/**
* @brief
* @param sorter
*/
void process(const CommandSorter &sorter);
/**
* @brief
* @return
*/
uint32_t getBatchCount() const {
return static_cast<uint32_t>(batches_.size());
}
/**
* @brief
* @param index
* @return
*/
const CommandBatch &getBatch(uint32_t index) const { return batches_[index]; }
/**
* @brief
* @param batchIndex
* @param cmdIndex
* @return
*/
const DrawCommand &getCommand(uint32_t batchIndex, uint32_t cmdIndex) const;
/**
* @brief
*/
void clear();
private:
std::vector<CommandBatch> batches_;
const CommandSorter *sorter_ = nullptr;
};
/**
* @brief
*
*
*
*/
class CommandQueue {
public:
/**
* @brief
*/
CommandQueue();
/**
* @brief
*/
~CommandQueue();
// 禁止拷贝
CommandQueue(const CommandQueue&) = delete;
CommandQueue& operator=(const CommandQueue&) = delete;
// 允许移动
CommandQueue(CommandQueue&&) noexcept;
CommandQueue& operator=(CommandQueue&&) noexcept;
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
void beginFrame();
/**
* @brief
*/
void endFrame();
/**
* @brief
* @param material
* @param mesh
* @param transform
* @param color
*/
void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
const struct Transform &transform, const Color &color);
/**
* @brief
* @param color
* @param flags
*/
void submitClear(const Color &color, uint32_t flags);
/**
* @brief
* @param x X
* @param y Y
* @param width
* @param height
*/
void setViewport(int32_t x, int32_t y, int32_t width, int32_t height);
/**
* @brief
*
*
* @param frameIndex
*/
void execute(uint32_t frameIndex);
/**
* @brief UBO
* @param viewProjection
* @param deltaTime
* @param screenWidth
* @param screenHeight
* @param frameIndex
*/
void updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex);
/**
* @brief
* @return
*/
uint32_t getCommandCount() const { return sorter_.getCount(); }
/**
* @brief
* @return
*/
RHICommandList* getCommandList() const { return commandList_.get(); }
/**
* @brief
* @return
*/
const RenderStats& getStats() const { return stats_; }
/**
* @brief
*/
void resetStats() { stats_ = {}; }
private:
CommandSorter sorter_;
CommandBatcher batcher_;
RHIContext *context_ = nullptr;
std::unique_ptr<RHICommandList> commandList_;
// UBO 管理器
std::unique_ptr<UniformBufferManager> uboManager_;
// 渲染统计
RenderStats stats_;
// 全局 UBO 数据 - 必须与着色器中的 std140 布局完全匹配
// layout(std140, binding = 0) uniform GlobalUBO {
// mat4 uViewProjection; // 64 bytes, offset 0
// vec4 uCameraPosition; // 16 bytes, offset 64
// float uTime; // 4 bytes, offset 80
// float uDeltaTime; // 4 bytes, offset 84
// vec2 uScreenSize; // 8 bytes, offset 88
// }; // 总大小: 96 bytes (std140 对齐)
struct alignas(16) GlobalUBOData {
float viewProjection[16]; // 64 bytes, offset 0
float cameraPosition[4]; // 16 bytes, offset 64
float time; // 4 bytes, offset 80
float deltaTime; // 4 bytes, offset 84
float screenSize[2]; // 8 bytes, offset 88
} globalUBOData_;
// 材质 UBO 数据缓冲区
std::vector<uint8_t> materialUBOData_;
// 当前材质 ID 计数器
uint32_t nextMaterialId_ = 1;
// 材质到 ID 的映射
std::unordered_map<Material *, uint32_t> materialIds_;
// 当前材质 UBO 缓冲区
UniformBuffer* currentMaterialUBO_ = nullptr;
uint32_t materialUBOBufferOffset_ = 0;
/**
* @brief ID
* @param material
* @return ID
*/
uint32_t getMaterialId(Material *material);
/**
* @brief
* @param batchIndex
* @param batch
* @param frameIndex
*/
void executeBatch(uint32_t batchIndex, const CommandBatch &batch, uint32_t frameIndex);
/**
* @brief UBO 使 CPU
* @param size
* @return UBO nullptr 使 CPU
*/
std::pair<UniformBuffer*, uint32_t> allocateMaterialUBO(uint32_t size);
/**
* @brief UBO CPU GPU
*/
void flushMaterialUBOToGPU();
/**
* @brief UBO
* @return UBO
*/
UniformBuffer* getCurrentMaterialUBO() const;
};
} // namespace extra2d

View File

@ -0,0 +1,321 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <renderer/rhi/rhi_types.h>
#include <types/math/vec2.h>
#include <types/math/color.h>
#include <types/math/mat4.h>
#include <vector>
#include <memory>
namespace extra2d {
/**
* @brief
*
*
* std140
*
*
* - 使 cos/sin
* - 便GPU访问
*/
struct InstanceData {
// 第一组 16 字节 - 变换数据
Vec2 position; // 位置偏移 (8 bytes)
float rotationCos; // 旋转角度余弦值 (4 bytes) - 预计算避免着色器三角函数
float rotationSin; // 旋转角度正弦值 (4 bytes) - 预计算避免着色器三角函数
// 第二组 16 字节 - 缩放和预留
Vec2 scale; // 缩放 (8 bytes)
float padding1[2]; // 填充到 16 字节对齐 (8 bytes)
// 第三组 16 字节 - 颜色
Color color; // 颜色 (16 bytes) - r, g, b, a
// 第四组 16 字节 - UV坐标
float uvX; // UV 起始 X (4 bytes)
float uvY; // UV 起始 Y (4 bytes)
float uvWidth; // UV 宽度 (4 bytes)
float uvHeight; // UV 高度 (4 bytes)
InstanceData()
: position(0.0f, 0.0f)
, rotationCos(1.0f) // cos(0) = 1
, rotationSin(0.0f) // sin(0) = 0
, scale(1.0f, 1.0f)
, padding1{0.0f, 0.0f}
, color(Color::White)
, uvX(0.0f)
, uvY(0.0f)
, uvWidth(1.0f)
, uvHeight(1.0f) {}
/**
* @brief cos/sin
* @param angle
*/
void setRotation(float angle) {
rotationCos = std::cos(angle);
rotationSin = std::sin(angle);
}
/**
* @brief atan2计算
* @return
*/
float getRotation() const {
return std::atan2(rotationSin, rotationCos);
}
};
static_assert(sizeof(InstanceData) == 64, "InstanceData size should be 64 bytes for std140 alignment");
/**
* @brief
*
* GPU缓冲区
*/
struct DirtyRange {
uint32_t start; // 起始实例索引
uint32_t count; // 实例数量
DirtyRange(uint32_t s = 0, uint32_t c = 0) : start(s), count(c) {}
bool isValid() const { return count > 0; }
uint32_t end() const { return start + count; }
// 检查是否与另一个区域重叠或相邻
bool canMerge(const DirtyRange& other) const {
return (start <= other.end() && other.start <= end()) ||
(end() + 1 >= other.start && other.end() + 1 >= start);
}
// 合并两个区域
void merge(const DirtyRange& other) {
uint32_t newStart = std::min(start, other.start);
uint32_t newEnd = std::max(end(), other.end());
start = newStart;
count = newEnd - newStart;
}
};
/**
* @brief
*
*
*
*
*
* - PCIe带宽占用
* - GPU上传次数
*/
class InstanceBuffer {
public:
/**
* @brief
*/
InstanceBuffer();
/**
* @brief
*/
~InstanceBuffer();
// 禁止拷贝
InstanceBuffer(const InstanceBuffer&) = delete;
InstanceBuffer& operator=(const InstanceBuffer&) = delete;
// 允许移动
InstanceBuffer(InstanceBuffer&& other) noexcept;
InstanceBuffer& operator=(InstanceBuffer&& other) noexcept;
/**
* @brief
* @param maxInstances
* @return
*/
bool initialize(uint32_t maxInstances);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param instances
* @param count
* @return
*/
bool updateInstances(const InstanceData* instances, uint32_t count);
/**
* @brief
* @param instances
* @param start
* @param count
* @return
*/
bool updateInstancesRange(const InstanceData* instances, uint32_t start, uint32_t count);
/**
* @brief
* @param instance
* @return
*/
uint32_t addInstance(const InstanceData& instance);
/**
* @brief updateGPU时更新
* @param start
* @param count
*/
void markDirty(uint32_t start, uint32_t count);
/**
* @brief
*/
void markAllDirty();
/**
* @brief GPU
* @return
*/
bool updateGPU();
/**
* @brief
*/
void clear();
/**
* @brief
* @return
*/
uint32_t getInstanceCount() const { return instanceCount_; }
/**
* @brief
* @return
*/
uint32_t getMaxInstances() const { return maxInstances_; }
/**
* @brief RHI
* @return
*/
BufferHandle getBufferHandle() const { return bufferHandle_; }
/**
* @brief RHI
* @return
*/
RHIBuffer* getRHIBuffer() const { return bufferHandle_.get(); }
/**
* @brief
* @return
*/
bool isValid() const { return bufferHandle_.isValid(); }
/**
* @brief
* @return
*/
uint32_t getDirtyRangeCount() const { return static_cast<uint32_t>(dirtyRanges_.size()); }
/**
* @brief
* @return
*/
bool hasDirtyData() const { return !dirtyRanges_.empty(); }
private:
BufferHandle bufferHandle_; // RHI 缓冲区句柄
uint32_t maxInstances_ = 0; // 最大实例数量
uint32_t instanceCount_ = 0; // 当前实例数量
std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区
std::vector<DirtyRange> dirtyRanges_; // 脏区域列表
// 最大脏区域数量,超过则合并为全量更新
static constexpr uint32_t MAX_DIRTY_RANGES = 8;
/**
* @brief
* @param range
*/
void addDirtyRange(const DirtyRange& range);
/**
* @brief
*/
void mergeAllDirtyRanges();
/**
* @brief
*/
void clearDirtyRanges();
};
/**
* @brief
*
*
*/
class InstanceBufferManager {
public:
/**
* @brief
*/
InstanceBufferManager();
/**
* @brief
*/
~InstanceBufferManager();
// 禁止拷贝
InstanceBufferManager(const InstanceBufferManager&) = delete;
InstanceBufferManager& operator=(const InstanceBufferManager&) = delete;
// 允许移动
InstanceBufferManager(InstanceBufferManager&& other) noexcept;
InstanceBufferManager& operator=(InstanceBufferManager&& other) noexcept;
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param minSize
* @return
*/
InstanceBuffer* acquireBuffer(uint32_t minSize);
/**
* @brief
* @param buffer
*/
void releaseBuffer(InstanceBuffer* buffer);
/**
* @brief
*/
void reset();
private:
std::vector<std::unique_ptr<InstanceBuffer>> bufferPool_;
uint32_t currentBufferIndex_ = 0;
static constexpr uint32_t DEFAULT_BUFFER_SIZE = 1024;
};
} // namespace extra2d

View File

@ -1,12 +1,12 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <renderer/shader.h>
#include <renderer/texture.h>
#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>
@ -15,7 +15,37 @@ namespace extra2d {
// 前向声明
class Material;
class Texture;
/**
* @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(uint32_t);
default: return 0;
}
}
/**
* @brief
@ -30,7 +60,7 @@ struct MaterialParamInfo {
* @brief
*/
struct TextureSlot {
Handle<Texture> handle;
Ptr<Texture> texture;
uint32_t slot;
std::string uniformName;
};
@ -79,7 +109,9 @@ public:
bool isFinalized() const { return finalized_; }
private:
std::unordered_map<std::string, MaterialParamInfo> params_;
// 使用vector保持参数添加顺序确保与着色器中的声明顺序一致
std::vector<std::pair<std::string, MaterialParamInfo>> params_;
std::unordered_map<std::string, size_t> paramIndexMap_; // 用于快速查找
uint32_t bufferSize_ = 0;
bool finalized_ = false;
};
@ -87,7 +119,7 @@ private:
/**
* @brief
*
*
* RHI
* -
* - UBO
* -
@ -112,10 +144,7 @@ private:
* material->setFloat("uOpacity", 1.0f);
*
* // 设置纹理
* material->setTexture("uTexture", textureHandle, 0);
*
* // 注册到渲染器
* MaterialHandle handle = renderer->registerMaterial(material);
* material->setTexture("uTexture", texture, 0);
* @endcode
*/
class Material : public RefCounted {
@ -147,6 +176,12 @@ public:
*/
Ptr<Shader> getShader() const { return shader_; }
/**
* @brief RHI 线
* @return RHI 线
*/
PipelineHandle getPipeline() const;
// ========================================
// 参数设置
// ========================================
@ -168,7 +203,10 @@ public:
/**
* @brief vec4
* @param name
* @param value
* @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);
@ -193,10 +231,10 @@ public:
/**
* @brief
* @param uniformName uniform
* @param texture
* @param texture
* @param slot 0-15
*/
void setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot);
void setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot);
/**
* @brief
@ -225,17 +263,11 @@ public:
*/
uint32_t getDataSize() const { return static_cast<uint32_t>(data_.size()); }
// ========================================
// 应用材质(渲染时调用)
// ========================================
/**
* @brief
*
* UBO
* @param uboManager UBO
* @brief
* @return
*/
void apply(UniformBufferManager& uboManager);
Ptr<MaterialLayout> getLayout() const { return layout_; }
private:
Ptr<MaterialLayout> layout_; // 材质布局

View File

@ -1,119 +1,113 @@
#pragma once
#include <renderer/rhi/rhi.h>
#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; // 颜色
Vec2 position; // 位置
Vec2 texCoord; // 纹理坐标
Color color; // 颜色
Vertex() = default;
Vertex(const Vec2 &pos, const Vec2 &uv, const Color &col)
: position(pos), texCoord(uv), color(col) {}
Vertex() = default;
Vertex(const Vec2& pos, const Vec2& uv, const Color& col)
: position(pos), texCoord(uv), color(col) {}
};
/**
* @brief
*
* OpenGL
* RHI
*
*/
class Mesh : public RefCounted {
public:
/**
* @brief
*/
Mesh();
/**
* @brief
*/
Mesh();
/**
* @brief
*/
~Mesh() override;
/**
* @brief
*/
~Mesh() override;
/**
* @brief
* @param vertices
* @param count
*/
void setVertices(const Vertex *vertices, uint32_t count);
/**
* @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 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
* @param vertices
* @param count
* @param offset
*/
void updateVertices(const Vertex* vertices, uint32_t count, uint32_t offset);
/**
* @brief
*/
void bind() const;
/**
* @brief
* @return
*/
uint32_t getVertexCount() const { return vertexCount_; }
/**
* @brief
*/
void unbind() const;
/**
* @brief
* @return
*/
uint32_t getIndexCount() const { return indexCount_; }
/**
* @brief
*/
void draw() const;
/**
* @brief
* @return
*/
BufferHandle getVertexBuffer() const { return vertexBuffer_; }
/**
* @brief
* @param instanceCount
*/
void drawInstanced(uint32_t instanceCount) const;
/**
* @brief
* @return
*/
BufferHandle getIndexBuffer() const { return indexBuffer_; }
/**
* @brief
* @return
*/
uint32_t getVertexCount() const { return vertexCount_; }
/**
* @brief
* @return
*/
bool hasIndices() const { return indexCount_ > 0; }
/**
* @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));
/**
* @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; // 索引缓冲区容量
BufferHandle vertexBuffer_; // 顶点缓冲区句柄
BufferHandle indexBuffer_; // 索引缓冲区句柄
uint32_t vertexCount_ = 0; // 顶点数量
uint32_t indexCount_ = 0; // 索引数量
uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量
uint32_t indexCapacity_ = 0; // 索引缓冲区容量
};
} // namespace extra2d

View File

@ -0,0 +1,366 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <renderer/command_queue.h>
#include <types/math/color.h>
#include <types/math/rect.h>
#include <string>
#include <vector>
#include <functional>
#include <memory>
#include <unordered_map>
namespace extra2d {
// 前向声明
class RenderGraph;
class RenderPass;
class RenderGraphBuilder;
/**
* @brief
*/
enum class RenderGraphResourceType : uint8_t {
Texture, // 纹理
Buffer, // 缓冲区
RenderTarget // 渲染目标
};
/**
* @brief
*/
struct RenderGraphResourceHandle {
uint32_t index = static_cast<uint32_t>(-1);
RenderGraphResourceType type = RenderGraphResourceType::Texture;
bool isValid() const { return index != static_cast<uint32_t>(-1); }
static RenderGraphResourceHandle invalid() { return RenderGraphResourceHandle{}; }
};
/**
* @brief
*/
struct RenderGraphTextureDesc {
uint32_t width = 0;
uint32_t height = 0;
TextureFormat format = TextureFormat::RGBA8;
TextureUsage usage = TextureUsage::Sampled;
bool isRenderTarget = false;
std::string name;
static RenderGraphTextureDesc create2D(uint32_t w, uint32_t h, TextureFormat fmt,
const std::string& name = "") {
RenderGraphTextureDesc desc;
desc.width = w;
desc.height = h;
desc.format = fmt;
desc.name = name;
return desc;
}
};
/**
* @brief
*/
struct RenderGraphRenderTargetDesc {
RenderGraphResourceHandle colorTarget;
RenderGraphResourceHandle depthTarget;
Rect clearRect;
Color clearColor = Color::Black;
float clearDepth = 1.0f;
uint8_t clearStencil = 0;
bool clearColorFlag = true;
bool clearDepthFlag = false;
bool clearStencilFlag = false;
};
/**
* @brief
*/
enum class RenderPassType : uint8_t {
Graphics, // 图形渲染
Compute, // 计算
Copy // 拷贝/传输
};
/**
* @brief
*/
struct RenderPassContext {
RHIContext* context = nullptr;
CommandQueue* commandQueue = nullptr;
RenderGraph* graph = nullptr;
float deltaTime = 0.0f;
uint32_t frameIndex = 0;
// 获取纹理句柄
TextureHandle getTexture(RenderGraphResourceHandle handle) const;
};
/**
* @brief
*/
class RenderPass {
public:
virtual ~RenderPass() = default;
/**
* @brief
*/
virtual const char* getName() const = 0;
/**
* @brief
*/
virtual RenderPassType getType() const { return RenderPassType::Graphics; }
/**
* @brief
* @param builder
*/
virtual void declareResources(RenderGraphBuilder& builder) {}
/**
* @brief
* @param ctx
*/
virtual void execute(const RenderPassContext& ctx) = 0;
/**
* @brief
*/
void setEnabled(bool enabled) { enabled_ = enabled; }
/**
* @brief
*/
bool isEnabled() const { return enabled_; }
private:
bool enabled_ = true;
};
/**
* @brief
*
*
*/
class RenderGraphBuilder {
public:
/**
* @brief
* @param desc
* @return
*/
RenderGraphResourceHandle createTexture(const RenderGraphTextureDesc& desc);
/**
* @brief
* @param handle
*/
void readResource(RenderGraphResourceHandle handle);
/**
* @brief
* @param handle
*/
void writeResource(RenderGraphResourceHandle handle);
/**
* @brief
* @param desc
*/
void setRenderTarget(const RenderGraphRenderTargetDesc& desc);
// 内部使用
void setCurrentPass(RenderPass* pass) { currentPass_ = pass; }
private:
RenderPass* currentPass_ = nullptr;
std::vector<RenderGraphTextureDesc> textureDescs_;
};
/**
* @brief
*
*
*
*/
class RenderGraph {
public:
RenderGraph();
~RenderGraph();
// 禁止拷贝
RenderGraph(const RenderGraph&) = delete;
RenderGraph& operator=(const RenderGraph&) = delete;
// 允许移动
RenderGraph(RenderGraph&&) noexcept;
RenderGraph& operator=(RenderGraph&&) noexcept;
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param pass
* @return
*/
RenderPass* addPass(std::unique_ptr<RenderPass> pass);
/**
* @brief 便
* @param name
* @param executeFunc
* @return
*/
RenderPass* addLambdaPass(const std::string& name,
std::function<void(const RenderPassContext&)> executeFunc);
/**
* @brief
*
*
*/
bool compile();
/**
* @brief
* @param deltaTime
* @param viewProjection
*/
void execute(float deltaTime, const Mat4& viewProjection = Mat4(1.0f));
/**
* @brief
* @param handle
* @return
*/
TextureHandle getTexture(RenderGraphResourceHandle handle) const;
/**
* @brief
* @param width
* @param height
*/
void setOutputSize(uint32_t width, uint32_t height);
/**
* @brief
*/
uint32_t getOutputWidth() const { return outputWidth_; }
/**
* @brief
*/
uint32_t getOutputHeight() const { return outputHeight_; }
/**
* @brief
*/
CommandQueue* getCommandQueue() { return &commandQueue_; }
private:
// 渲染通道列表
std::vector<std::unique_ptr<RenderPass>> passes_;
// 编译后的执行顺序
std::vector<RenderPass*> executionOrder_;
// 资源管理
std::unordered_map<uint32_t, TextureHandle> textures_;
uint32_t nextResourceId_ = 1;
// 构建器
RenderGraphBuilder builder_;
// 命令队列
CommandQueue commandQueue_;
// 输出尺寸
uint32_t outputWidth_ = 1280;
uint32_t outputHeight_ = 720;
// 帧计数
uint32_t frameIndex_ = 0;
// 编译状态
bool compiled_ = false;
/**
* @brief
*/
void createResources();
/**
* @brief
*/
void destroyResources();
};
/**
* @brief
*
* 2D
*/
class GeometryRenderPass : public RenderPass {
public:
GeometryRenderPass(const std::string& name);
const char* getName() const override { return name_.c_str(); }
void declareResources(RenderGraphBuilder& builder) override;
void execute(const RenderPassContext& ctx) override;
/**
* @brief
*/
void setColorTarget(RenderGraphResourceHandle handle) { colorTarget_ = handle; }
/**
* @brief
*/
void setDepthTarget(RenderGraphResourceHandle handle) { depthTarget_ = handle; }
private:
std::string name_;
RenderGraphResourceHandle colorTarget_;
RenderGraphResourceHandle depthTarget_;
};
/**
* @brief
*/
class PostProcessRenderPass : public RenderPass {
public:
PostProcessRenderPass(const std::string& name);
const char* getName() const override { return name_.c_str(); }
void declareResources(RenderGraphBuilder& builder) override;
void execute(const RenderPassContext& ctx) override;
/**
* @brief
*/
void setInputTexture(RenderGraphResourceHandle handle) { inputTexture_ = handle; }
/**
* @brief
*/
void setOutputTarget(RenderGraphResourceHandle handle) { outputTarget_ = handle; }
private:
std::string name_;
RenderGraphResourceHandle inputTexture_;
RenderGraphResourceHandle outputTarget_;
};
} // namespace extra2d

View File

@ -1,12 +1,11 @@
#pragma once
#include <assets/handle.h>
#include <cstdint>
#include <types/base/types.h>
#include <types/math/vec2.h>
#include <types/math/color.h>
#include <types/math/transform.h>
#include <assets/handle.h>
#include <array>
#include <cstdint>
#include <types/math/vec2.h>
namespace extra2d {
@ -15,184 +14,106 @@ class Material;
class Mesh;
class Texture;
// 资源句柄类型(使用新的 Handle<T>
using MaterialHandle = Handle<Material>;
using MeshHandle = Handle<Mesh>;
using TextureHandle = Handle<Texture>;
// 注意:资源句柄直接使用 Handle<T>,与 RHI 的 TextureHandle 等区分
/**
* @brief
*/
enum class RenderCommandType : uint8_t {
DrawMesh, // 绘制网格
DrawMeshInstanced, // 实例化绘制
SetViewport, // 设置视口
Clear // 清除缓冲区
DrawMesh, // 绘制网格
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(uint32_t) * 2; // Handle size
default: return 0;
}
}
/**
* @brief
*
*
* 使
*
*/
struct RenderCommand {
RenderCommandType type; // 命令类型
uint32_t sortKey; // 排序键材质ID + 层)
// 绘制网格命令数据
struct DrawMeshData {
MeshHandle mesh; // 网格句柄
MaterialHandle material; // 材质句柄
Vec2 pos; // 位置
Vec2 scale; // 缩放
float rot; // 旋转角度
Color color; // 顶点颜色
};
// 实例化绘制命令数据
struct DrawInstancedData {
MeshHandle mesh; // 网格句柄
MaterialHandle material; // 材质句柄
uint32_t instanceCount; // 实例数量
uint32_t instanceOffset; // 实例数据偏移
};
// 设置视口命令数据
struct ViewportData {
int32_t x, y; // 视口位置
int32_t width, height; // 视口大小
};
// 清除缓冲区命令数据
struct ClearData {
Color color; // 清除颜色
uint32_t flags; // 清除标志(颜色/深度/模板)
};
union {
DrawMeshData drawMesh;
DrawInstancedData drawInstanced;
ViewportData viewport;
ClearData clear;
};
/**
* @brief
*/
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
drawMesh.mesh = MeshHandle::invalid();
drawMesh.material = MaterialHandle::invalid();
drawMesh.pos = Vec2(0.0f, 0.0f);
drawMesh.scale = Vec2(1.0f, 1.0f);
drawMesh.rot = 0.0f;
drawMesh.color = Color::White;
}
/**
* @brief Transform
*/
void setTransform(const Transform& t) {
drawMesh.pos = t.pos;
drawMesh.scale = t.scale;
drawMesh.rot = t.rot;
}
/**
* @brief
*/
void setColor(const Color& c) {
drawMesh.color = c;
}
/**
* @brief Transform
*/
Transform getTransform() const {
return Transform(drawMesh.pos, drawMesh.scale, drawMesh.rot);
}
/**
* @brief
*/
Color getColor() const {
return drawMesh.color;
}
RenderCommandType type; // 命令类型
uint32_t sortKey; // 排序键材质ID + 层)
// 绘制网格命令数据
struct DrawMeshData {
Handle<Mesh> mesh; // 网格句柄
Handle<Material> material; // 材质句柄
Vec2 pos; // 位置
Vec2 scale; // 缩放
float rot; // 旋转角度
Color color; // 顶点颜色
};
// 设置视口命令数据
struct ViewportData {
int32_t x, y; // 视口位置
int32_t width, height; // 视口大小
};
// 清除缓冲区命令数据
struct ClearData {
Color color; // 清除颜色
uint32_t flags; // 清除标志(颜色/深度/模板)
};
union {
DrawMeshData drawMesh;
ViewportData viewport;
ClearData clear;
};
/**
* @brief
*/
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
drawMesh.mesh = Handle<Mesh>::invalid();
drawMesh.material = Handle<Material>::invalid();
drawMesh.pos = Vec2(0.0f, 0.0f);
drawMesh.scale = Vec2(1.0f, 1.0f);
drawMesh.rot = 0.0f;
drawMesh.color = Color::White;
}
/**
* @brief Transform
*/
void setTransform(const Transform &t) {
drawMesh.pos = t.pos;
drawMesh.scale = t.scale;
drawMesh.rot = t.rot;
}
/**
* @brief
*/
void setColor(const Color &c) { drawMesh.color = c; }
/**
* @brief Transform
*/
Transform getTransform() const {
return Transform(drawMesh.pos, drawMesh.scale, drawMesh.rot);
}
/**
* @brief
*/
Color getColor() const { return drawMesh.color; }
};
// 清除标志
constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0;
constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1;
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 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
// UBO 绑定槽位(已移至 RHI
constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO
constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO
} // namespace extra2d

View File

@ -1,17 +1,13 @@
#pragma once
#include <array>
#include <event/events.h>
#include <module/module.h>
#include <module/module_registry.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/command_queue.h>
#include <renderer/render_graph.h>
#include <renderer/render_types.h>
#include <renderer/shader.h>
#include <renderer/texture.h>
#include <renderer/uniform_buffer.h>
#include <renderer/rhi/rhi.h>
#include <renderer/viewport_adapter.h>
#include <vector>
namespace extra2d {
@ -21,15 +17,15 @@ class AssetsModule;
/**
* @brief
*
*
* RHI
* -
* -
* - 使 RenderGraph
* - 使 CommandQueue
* -
*
* AssetsModule
*/
class RendererModule : public Module {
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3)
// 优先级为 4在 AssetsModule (优先级 3) 之后初始化
E2D_REGISTER_MODULE(RendererModule, "Renderer", 4)
public:
/**
@ -42,18 +38,16 @@ public:
*/
~RendererModule() override;
// 禁止拷贝
// 禁止拷贝和移动
RendererModule(const RendererModule &) = delete;
RendererModule &operator=(const RendererModule &) = delete;
// 允许移动
RendererModule(RendererModule &&) noexcept;
RendererModule &operator=(RendererModule &&) noexcept;
RendererModule(RendererModule &&) = delete;
RendererModule &operator=(RendererModule &&) = delete;
/**
* @brief
*
* GL
* RenderGraph CommandQueue
*
* @return
*/
@ -67,29 +61,7 @@ public:
void shutdown() override;
//===========================================================================
// 默认资源(通过 AssetsModule 获取)
//===========================================================================
/**
* @brief
* @return
*/
MaterialHandle getDefaultMaterialHandle() const;
/**
* @brief
* @return
*/
MeshHandle getDefaultQuadHandle() const;
/**
* @brief 1x1
* @return
*/
TextureHandle getDefaultTextureHandle() const;
//===========================================================================
// 渲染状态设置
// 渲染接口
//===========================================================================
/**
@ -121,6 +93,42 @@ public:
*/
const ViewportAdapter &getViewportAdapter() const { return viewportAdapter_; }
/**
* @brief
* @return
*/
RenderGraph *getRenderGraph() { return &renderGraph_; }
/**
* @brief
* @return
*/
CommandQueue *getCommandQueue() { return renderGraph_.getCommandQueue(); }
/**
* @brief RHI
* @return RHI
*/
RHIContext *getRHIContext() const;
/**
* @brief
* @return
*/
Handle<Material> getDefaultMaterialHandle() const;
/**
* @brief
* @return
*/
Handle<Mesh> getDefaultQuadHandle() const;
/**
* @brief 1x1
* @return
*/
Handle<Texture> getDefaultTextureHandle() const;
private:
//===========================================================================
// 事件处理器
@ -129,7 +137,7 @@ private:
/**
* @brief
*
*
* RenderGraph
*/
void onRenderBegin();
@ -148,7 +156,7 @@ private:
/**
* @brief
*
*
* RenderGraph
*/
void onRenderEnd();
@ -162,7 +170,7 @@ private:
/**
* @brief
*
* GL
*
*/
void onWindowShow();
@ -170,30 +178,6 @@ private:
// 渲染执行
//===========================================================================
/**
* @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
@ -201,18 +185,11 @@ private:
void executeCommand(const RenderCommand &cmd);
//===========================================================================
// 命令缓冲区
// 渲染图和命令队列
//===========================================================================
std::array<RenderCommand, MAX_RENDER_COMMANDS>
commandBuffer_; // 预分配命令缓冲区
uint32 commandCount_ = 0; // 当前命令数量
//===========================================================================
// UBO 管理器
//===========================================================================
UniformBufferManager uniformManager_;
RenderGraph renderGraph_; // 渲染图
CommandQueue *commandQueue_ = nullptr; // 命令队列(指向 renderGraph_ 内部)
//===========================================================================
// 事件监听器
@ -229,7 +206,7 @@ private:
// 状态标志
//===========================================================================
bool glInitialized_ = false; // GL 是否已初始化
bool initialized_ = false; // 是否已初始化
//===========================================================================
// 渲染统计
@ -237,7 +214,6 @@ private:
struct Stats {
uint32 commandsSubmitted = 0; // 提交的命令数
uint32 commandsExecuted = 0; // 执行的命令数
uint32 drawCalls = 0; // 绘制调用次数
uint32 batches = 0; // 批次数
} stats_;
@ -247,7 +223,7 @@ private:
//===========================================================================
int32 viewportX_ = 0, viewportY_ = 0;
int32 viewportWidth_ = 0, viewportHeight_ = 0;
int32 viewportWidth_ = 1280, viewportHeight_ = 720;
//===========================================================================
// 视口适配器

View File

@ -0,0 +1,35 @@
#pragma once
#include <glad/glad.h>
#include <renderer/rhi/rhi.h>
namespace extra2d {
/**
* @brief OpenGL
*/
class GLBuffer : public RHIBuffer {
public:
explicit GLBuffer(const BufferDesc &desc);
~GLBuffer() override;
bool create();
void destroy();
bool update(const void *data, uint32_t size, uint32_t offset) override;
void *map() override;
void unmap() override;
const BufferDesc &getDesc() const { return desc_; }
uint32_t getSize() const override { return desc_.size; }
BufferType getType() const override { return desc_.type; }
bool isValid() const override { return buffer_ != 0; }
GLuint getGLBuffer() const { return buffer_; }
private:
BufferDesc desc_;
GLuint buffer_;
};
} // namespace extra2d

View File

@ -0,0 +1,176 @@
#pragma once
#include <glad/glad.h>
#include <renderer/rhi/rhi.h>
namespace extra2d {
// 前向声明
class GLPipeline;
class GLBuffer;
class GLTexture;
class GLFramebuffer;
/**
* @brief OpenGL
*/
class GLCommandList : public RHICommandList {
public:
GLCommandList();
~GLCommandList() override;
//===========================================================================
// 命令列表生命周期
//===========================================================================
void begin() override;
void end() override;
void submit() override;
//===========================================================================
// 渲染通道
//===========================================================================
void beginRenderPass(RHIFramebuffer *framebuffer,
ClearFlags clearFlags = ClearFlags::Color,
const Color &clearColor = Color::Black,
float clearDepth = 1.0f,
uint8_t clearStencil = 0) override;
void endRenderPass() override;
//===========================================================================
// 状态设置
//===========================================================================
void setViewport(const Viewport &viewport) override;
void setScissor(const ScissorRect &scissor) override;
void setPipeline(RHIPipeline *pipeline) override;
//===========================================================================
// 资源绑定
//===========================================================================
void setVertexBuffer(uint32_t slot, RHIBuffer *buffer,
uint32_t offset = 0, uint32_t stride = 0) override;
void setIndexBuffer(RHIBuffer *buffer, IndexType type,
uint32_t offset = 0) override;
void setUniformBuffer(uint32_t slot, RHIBuffer *buffer) override;
void setUniformBuffer(uint32_t slot, RHIBuffer *buffer, uint32_t offset, uint32_t size = 0) override;
void setTexture(uint32_t slot, RHITexture *texture) override;
void setSampler(uint32_t slot, TextureFilter minFilter,
TextureFilter magFilter, TextureWrap wrapS,
TextureWrap wrapT) override;
//===========================================================================
// 绘制命令
//===========================================================================
void draw(uint32_t vertexCount, uint32_t firstVertex = 0,
uint32_t instanceCount = 1, uint32_t firstInstance = 0) override;
void drawIndexed(uint32_t indexCount, uint32_t firstIndex = 0,
int32_t vertexOffset = 0, uint32_t instanceCount = 1,
uint32_t firstInstance = 0) override;
//===========================================================================
// 工具方法
//===========================================================================
void clear(ClearFlags flags, const Color &color = Color::Black,
float depth = 1.0f, uint8_t stencil = 0) override;
bool isRecording() const override;
// 设置 uniform 变量
void setUniform(const char* name, float value) override;
void setUniform(const char* name, const Vec2& value) override;
void setUniform(const char* name, const Vec3& value) override;
void setUniform(const char* name, const Color& value) override;
void setUniform(const char* name, const Mat4& value) override;
private:
// 状态缓存机制 - 避免冗余的 OpenGL 状态切换
struct StateCache {
// 管线状态
GLPipeline* pipeline = nullptr;
GLuint shaderProgram = 0;
// 视口状态
Viewport viewport;
bool viewportValid = false;
// 裁剪状态
ScissorRect scissor{0, 0, 0, 0};
bool scissorValid = false;
bool scissorEnabled = false;
// 缓冲区状态
GLBuffer* vertexBuffers[4] = {nullptr, nullptr, nullptr, nullptr}; // 支持最多4个顶点缓冲区槽
uint32_t vertexBufferOffsets[4] = {0, 0, 0, 0};
GLBuffer* indexBuffer = nullptr;
// Uniform 缓冲区状态
struct UBOBinding {
GLBuffer* buffer = nullptr;
uint32_t offset = 0;
uint32_t size = 0;
bool valid = false;
};
UBOBinding uniformBuffers[4]; // 支持最多4个 UBO 槽
// 纹理状态
struct TextureBinding {
GLTexture* texture = nullptr;
bool valid = false;
};
TextureBinding textures[8]; // 支持最多8个纹理槽
// 帧缓冲状态
GLFramebuffer* framebuffer = nullptr;
// 重置所有状态缓存
void reset() {
pipeline = nullptr;
shaderProgram = 0;
viewportValid = false;
scissorValid = false;
scissorEnabled = false;
for (int i = 0; i < 4; ++i) {
vertexBuffers[i] = nullptr;
vertexBufferOffsets[i] = 0;
uniformBuffers[i] = {};
}
indexBuffer = nullptr;
for (int i = 0; i < 8; ++i) {
textures[i] = {};
}
framebuffer = nullptr;
}
};
bool recording_ = false;
StateCache stateCache_;
// 统计信息(调试用)
struct Stats {
uint32_t pipelineChanges = 0;
uint32_t textureChanges = 0;
uint32_t bufferChanges = 0;
uint32_t viewportChanges = 0;
uint32_t redundantPipelineChanges = 0;
uint32_t redundantTextureChanges = 0;
uint32_t redundantBufferChanges = 0;
void reset() {
pipelineChanges = 0;
textureChanges = 0;
bufferChanges = 0;
viewportChanges = 0;
redundantPipelineChanges = 0;
redundantTextureChanges = 0;
redundantBufferChanges = 0;
}
} stats_;
};
} // namespace extra2d

View File

@ -0,0 +1,38 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
#include <memory>
namespace extra2d {
// 前向声明
class GLDevice;
class GLFramebuffer;
/**
* @brief OpenGL
*/
class GLContext : public RHIContext {
public:
explicit GLContext(GLDevice* device);
~GLContext() override;
bool initialize() override;
void shutdown() override;
void beginFrame() override;
void endFrame() override;
void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) override;
void bindDefaultFramebuffer() override;
RHIFramebuffer* getDefaultFramebuffer() override;
bool isFeatureSupported(const char* feature) const override;
private:
GLDevice* device_;
std::unique_ptr<GLFramebuffer> defaultFramebuffer_;
};
} // namespace extra2d

View File

@ -0,0 +1,53 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
#include <vector>
namespace extra2d {
// 前向声明
class GLTexture;
/**
* @brief OpenGL
*/
class GLFramebuffer : public RHIFramebuffer {
public:
GLFramebuffer();
explicit GLFramebuffer(const RenderPassDesc& desc);
~GLFramebuffer() override;
bool create();
void destroy();
void bind() override;
void unbind() override;
uint32_t getColorAttachmentCount() const override;
TextureHandle getColorAttachment(uint32_t index) const override;
TextureHandle getDepthStencilAttachment() const override;
uint32_t getWidth() const override { return width_; }
uint32_t getHeight() const override { return height_; }
bool hasDepthStencil() const override;
bool isValid() const override;
bool isDefault() const override;
void setSize(uint32_t width, uint32_t height);
GLuint getGLFramebuffer() const { return framebuffer_; }
const RenderPassDesc& getDesc() const { return desc_; }
void clear(ClearFlags flags, const Color& color, float depth, uint8_t stencil);
private:
RenderPassDesc desc_;
GLuint framebuffer_;
uint32_t width_;
uint32_t height_;
std::vector<std::unique_ptr<GLTexture>> colorAttachments_;
std::unique_ptr<GLTexture> depthStencilAttachment_;
};
} // namespace extra2d

View File

@ -0,0 +1,45 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
namespace extra2d {
// 前向声明
class GLShader;
/**
* @brief OpenGL 线
*/
class GLPipeline : public RHIPipeline {
public:
explicit GLPipeline(const PipelineDesc& desc);
~GLPipeline() override;
bool create();
void destroy();
void bind() override;
void unbind() override;
ShaderHandle getVertexShader() const override;
ShaderHandle getFragmentShader() const override;
const VertexLayout& getVertexLayout() const override;
bool isValid() const override;
const PipelineDesc& getDesc() const { return desc_; }
GLuint getGLVAO() const { return vao_; }
// 获取 shader program ID用于设置 uniform
GLuint getGLProgram() const { return shaderProgram_; }
// 设置 shader program ID在创建时调用
void setGLProgram(GLuint program) { shaderProgram_ = program; }
private:
PipelineDesc desc_;
GLuint vao_;
GLuint shaderProgram_ = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,45 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
#include <string>
namespace extra2d {
/**
* @brief OpenGL
*/
class GLShader : public RHIShader {
public:
explicit GLShader(const ShaderDesc& desc);
~GLShader() override;
bool compile();
void destroy();
ShaderType getType() const override { return ShaderType::Vertex; } // 返回 Vertex 表示这是一个程序着色器
bool isCompiled() const override;
std::string getCompileLog() const override;
bool isValid() const override;
void bind() const;
void unbind() const;
void setUniform(const std::string& name, float value);
void setUniform(const std::string& name, const Vec2& value);
void setUniform(const std::string& name, const Vec3& value);
void setUniform(const std::string& name, const Color& value);
void setUniform(const std::string& name, const Mat4& value);
const ShaderDesc& getDesc() const { return desc_; }
GLuint getGLProgram() const { return program_; }
private:
ShaderDesc desc_;
GLuint shader_;
GLuint program_;
std::string compileLog_;
bool compiled_;
};
} // namespace extra2d

View File

@ -0,0 +1,40 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
namespace extra2d {
/**
* @brief OpenGL
*/
class GLTexture : public RHITexture {
public:
explicit GLTexture(const TextureDesc& desc);
~GLTexture() override;
bool create();
void destroy();
TextureType getType() const override { return desc_.type; }
TextureFormat getFormat() const override { return desc_.format; }
uint32_t getWidth() const override { return desc_.width; }
uint32_t getHeight() const override { return desc_.height; }
uint32_t getMipLevels() const override { return desc_.mipLevels; }
bool update(const void* data, uint32_t mipLevel = 0) override;
void generateMipmap() override;
void bind(uint32_t slot) override;
void unbind() override;
bool isValid() const override;
bool isRenderTarget() const override;
GLuint getGLTexture() const { return texture_; }
private:
TextureDesc desc_;
GLuint texture_;
};
} // namespace extra2d

View File

@ -0,0 +1,307 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
#include <utils/logger.h>
namespace extra2d {
/**
* @brief OpenGL
*/
#define GL_CHECK(call) do { \
call; \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) { \
E2D_LOG_ERROR("OpenGL error in {}: {} ({})", #call, getGLErrorString(err), err); \
} \
} while(0)
/**
* @brief OpenGL
* @param error OpenGL
* @return
*/
inline const char* getGLErrorString(GLenum error) {
switch (error) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
default: return "Unknown Error";
}
}
/**
* @brief BufferType OpenGL
*/
inline GLenum bufferTypeToGL(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;
case BufferType::Storage: return GL_SHADER_STORAGE_BUFFER;
case BufferType::Staging: return GL_ARRAY_BUFFER;
default: return GL_ARRAY_BUFFER;
}
}
/**
* @brief BufferUsage OpenGL 使
*/
inline GLenum bufferUsageToGL(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;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLInternal(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return GL_R8;
case TextureFormat::RG8: return GL_RG8;
case TextureFormat::RGB8: return GL_RGB8;
case TextureFormat::RGBA8: return GL_RGBA8;
case TextureFormat::RGBA8_SRGB: return GL_SRGB8_ALPHA8;
case TextureFormat::Depth16: return GL_DEPTH_COMPONENT16;
case TextureFormat::Depth24: return GL_DEPTH_COMPONENT24;
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT32F;
case TextureFormat::Depth24Stencil8: return GL_DEPTH24_STENCIL8;
case TextureFormat::Depth32FStencil8:return GL_DEPTH32F_STENCIL8;
case TextureFormat::BC1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
case TextureFormat::BC3: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case TextureFormat::BC5: return 0x8DBD; // GL_COMPRESSED_RG_RGTC2
default: return GL_RGBA8;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLFormat(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return GL_RED;
case TextureFormat::RG8: return GL_RG;
case TextureFormat::RGB8: return GL_RGB;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return GL_RGBA;
case TextureFormat::Depth16:
case TextureFormat::Depth24:
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT;
case TextureFormat::Depth24Stencil8:
case TextureFormat::Depth32FStencil8:return GL_DEPTH_STENCIL;
default: return GL_RGBA;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLType(TextureFormat format) {
switch (format) {
case TextureFormat::R8:
case TextureFormat::RG8:
case TextureFormat::RGB8:
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return GL_UNSIGNED_BYTE;
case TextureFormat::Depth16: return GL_UNSIGNED_SHORT;
case TextureFormat::Depth24: return GL_UNSIGNED_INT;
case TextureFormat::Depth32F: return GL_FLOAT;
case TextureFormat::Depth24Stencil8: return GL_UNSIGNED_INT_24_8;
case TextureFormat::Depth32FStencil8:return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
default: return GL_UNSIGNED_BYTE;
}
}
/**
* @brief TextureFilter OpenGL
*/
inline GLenum textureFilterToGLMin(TextureFilter filter) {
switch (filter) {
case TextureFilter::Nearest: return GL_NEAREST;
case TextureFilter::Linear: return GL_LINEAR;
case TextureFilter::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST;
case TextureFilter::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST;
case TextureFilter::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR;
case TextureFilter::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR;
default: return GL_LINEAR;
}
}
inline GLenum textureFilterToGLMag(TextureFilter filter) {
// Mag filter doesn't support mipmaps
switch (filter) {
case TextureFilter::Nearest:
case TextureFilter::NearestMipmapNearest:
case TextureFilter::NearestMipmapLinear:
return GL_NEAREST;
case TextureFilter::Linear:
case TextureFilter::LinearMipmapNearest:
case TextureFilter::LinearMipmapLinear:
default:
return GL_LINEAR;
}
}
/**
* @brief TextureWrap OpenGL
*/
inline GLenum textureWrapToGL(TextureWrap wrap) {
switch (wrap) {
case TextureWrap::Repeat: return GL_REPEAT;
case TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE;
case TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER;
case TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT;
default: return GL_REPEAT;
}
}
/**
* @brief VertexFormat OpenGL
*/
inline void vertexFormatToGL(VertexFormat format, GLint& components, GLenum& type, GLboolean& normalized) {
switch (format) {
case VertexFormat::Float1: components = 1; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float2: components = 2; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float3: components = 3; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float4: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Int1: components = 1; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int2: components = 2; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int3: components = 3; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int4: components = 4; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt1: components = 1; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt2: components = 2; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt3: components = 3; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt4: components = 4; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::Byte4: components = 4; type = GL_BYTE; normalized = GL_FALSE; break;
case VertexFormat::Byte4Normalized: components = 4; type = GL_BYTE; normalized = GL_TRUE; break;
case VertexFormat::UByte4: components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_FALSE; break;
case VertexFormat::UByte4Normalized:components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_TRUE; break;
default: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
}
}
/**
* @brief IndexType OpenGL
*/
inline GLenum indexTypeToGL(IndexType type) {
switch (type) {
case IndexType::UInt16: return GL_UNSIGNED_SHORT;
case IndexType::UInt32: return GL_UNSIGNED_INT;
default: return GL_UNSIGNED_SHORT;
}
}
/**
* @brief PrimitiveType OpenGL
*/
inline GLenum primitiveTypeToGL(PrimitiveType type) {
switch (type) {
case PrimitiveType::Points: return GL_POINTS;
case PrimitiveType::Lines: return GL_LINES;
case PrimitiveType::LineStrip: return GL_LINE_STRIP;
case PrimitiveType::Triangles: return GL_TRIANGLES;
case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP;
case PrimitiveType::TriangleFan: return GL_TRIANGLE_FAN;
default: return GL_TRIANGLES;
}
}
/**
* @brief BlendFactor OpenGL
*/
inline GLenum blendFactorToGL(BlendFactor factor) {
switch (factor) {
case BlendFactor::Zero: return GL_ZERO;
case BlendFactor::One: return GL_ONE;
case BlendFactor::SrcColor: return GL_SRC_COLOR;
case BlendFactor::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor: return GL_DST_COLOR;
case BlendFactor::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha: return GL_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha: return GL_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA;
default: return GL_ONE;
}
}
/**
* @brief BlendOp OpenGL
*/
inline GLenum blendOpToGL(BlendOp op) {
switch (op) {
case BlendOp::Add: return GL_FUNC_ADD;
case BlendOp::Subtract: return GL_FUNC_SUBTRACT;
case BlendOp::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT;
case BlendOp::Min: return GL_MIN;
case BlendOp::Max: return GL_MAX;
default: return GL_FUNC_ADD;
}
}
/**
* @brief CompareFunc OpenGL
*/
inline GLenum compareFuncToGL(CompareFunc func) {
switch (func) {
case CompareFunc::Never: return GL_NEVER;
case CompareFunc::Less: return GL_LESS;
case CompareFunc::Equal: return GL_EQUAL;
case CompareFunc::LessEqual: return GL_LEQUAL;
case CompareFunc::Greater: return GL_GREATER;
case CompareFunc::NotEqual: return GL_NOTEQUAL;
case CompareFunc::GreaterEqual: return GL_GEQUAL;
case CompareFunc::Always: return GL_ALWAYS;
default: return GL_LESS;
}
}
/**
* @brief ShaderType OpenGL
*/
inline GLenum shaderTypeToGL(ShaderType type) {
switch (type) {
case ShaderType::Vertex: return GL_VERTEX_SHADER;
case ShaderType::Fragment: return GL_FRAGMENT_SHADER;
case ShaderType::Geometry: return GL_GEOMETRY_SHADER;
case ShaderType::Compute: return GL_COMPUTE_SHADER;
default: return GL_VERTEX_SHADER;
}
}
/**
* @brief VertexFormat
*/
inline uint32_t getVertexFormatSize(VertexFormat format) {
switch (format) {
case VertexFormat::Float1: return sizeof(float) * 1;
case VertexFormat::Float2: return sizeof(float) * 2;
case VertexFormat::Float3: return sizeof(float) * 3;
case VertexFormat::Float4: return sizeof(float) * 4;
case VertexFormat::Int1: return sizeof(int32_t) * 1;
case VertexFormat::Int2: return sizeof(int32_t) * 2;
case VertexFormat::Int3: return sizeof(int32_t) * 3;
case VertexFormat::Int4: return sizeof(int32_t) * 4;
case VertexFormat::UInt1: return sizeof(uint32_t) * 1;
case VertexFormat::UInt2: return sizeof(uint32_t) * 2;
case VertexFormat::UInt3: return sizeof(uint32_t) * 3;
case VertexFormat::UInt4: return sizeof(uint32_t) * 4;
case VertexFormat::Byte4:
case VertexFormat::Byte4Normalized: return sizeof(int8_t) * 4;
case VertexFormat::UByte4:
case VertexFormat::UByte4Normalized: return sizeof(uint8_t) * 4;
default: return sizeof(float) * 4;
}
}
} // namespace extra2d

View File

@ -0,0 +1,14 @@
#pragma once
/**
* @brief RHI (Render Hardware Interface)
*
* RHI
*/
#include <renderer/rhi/rhi_types.h>
#include <renderer/rhi/rhi_device.h>
#include <renderer/rhi/rhi_buffer.h>
#include <renderer/rhi/rhi_shader.h>
#include <renderer/rhi/rhi_framebuffer.h>
#include <renderer/rhi/rhi_command_list.h>

View File

@ -0,0 +1,131 @@
#pragma once
#include <renderer/rhi/rhi_types.h>
namespace extra2d {
/**
* @brief RHI
*
* GPU
*/
class RHIBuffer {
public:
virtual ~RHIBuffer() = default;
/**
* @brief
* @return
*/
virtual BufferType getType() const = 0;
/**
* @brief
* @return
*/
virtual uint32_t getSize() const = 0;
/**
* @brief
* @param data
* @param size
* @param offset
* @return
*/
virtual bool update(const void* data, uint32_t size, uint32_t offset = 0) = 0;
/**
* @brief CPU
* @return nullptr
*/
virtual void* map() = 0;
/**
* @brief
*/
virtual void unmap() = 0;
/**
* @brief
* @return
*/
virtual bool isValid() const = 0;
};
/**
* @brief RHI
*
* GPU
*/
class RHITexture {
public:
virtual ~RHITexture() = default;
/**
* @brief
* @return
*/
virtual TextureType getType() const = 0;
/**
* @brief
* @return
*/
virtual TextureFormat getFormat() const = 0;
/**
* @brief
* @return
*/
virtual uint32_t getWidth() const = 0;
/**
* @brief
* @return
*/
virtual uint32_t getHeight() const = 0;
/**
* @brief Mipmap
* @return Mipmap
*/
virtual uint32_t getMipLevels() const = 0;
/**
* @brief
* @param data
* @param mipLevel Mipmap
* @return
*/
virtual bool update(const void* data, uint32_t mipLevel = 0) = 0;
/**
* @brief Mipmap
*/
virtual void generateMipmap() = 0;
/**
* @brief
* @param slot
*/
virtual void bind(uint32_t slot) = 0;
/**
* @brief
*/
virtual void unbind() = 0;
/**
* @brief
* @return
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual bool isRenderTarget() const = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,229 @@
#pragma once
#include <renderer/rhi/rhi_types.h>
#include <renderer/rhi/rhi_buffer.h>
#include <renderer/rhi/rhi_shader.h>
#include <renderer/rhi/rhi_framebuffer.h>
namespace extra2d {
/**
* @brief RHI
*
* GPU
*/
class RHICommandList {
public:
virtual ~RHICommandList() = default;
//===========================================================================
// 命令列表生命周期
//===========================================================================
/**
* @brief
*/
virtual void begin() = 0;
/**
* @brief
*/
virtual void end() = 0;
/**
* @brief GPU
*/
virtual void submit() = 0;
//===========================================================================
// 渲染通道
//===========================================================================
/**
* @brief
* @param framebuffer
* @param clearFlags
* @param clearColor Color
* @param clearDepth Depth
* @param clearStencil Stencil
*/
virtual void beginRenderPass(RHIFramebuffer* framebuffer,
ClearFlags clearFlags = ClearFlags::Color,
const Color& clearColor = Color::Black,
float clearDepth = 1.0f,
uint8_t clearStencil = 0) = 0;
/**
* @brief
*/
virtual void endRenderPass() = 0;
//===========================================================================
// 状态设置
//===========================================================================
/**
* @brief
* @param viewport
*/
virtual void setViewport(const Viewport& viewport) = 0;
/**
* @brief
* @param scissor
*/
virtual void setScissor(const ScissorRect& scissor) = 0;
/**
* @brief 线
* @param pipeline 线
*/
virtual void setPipeline(RHIPipeline* pipeline) = 0;
//===========================================================================
// 资源绑定
//===========================================================================
/**
* @brief
* @param slot
* @param buffer
* @param offset
* @param stride 0使
*/
virtual void setVertexBuffer(uint32_t slot, RHIBuffer* buffer, uint32_t offset = 0, uint32_t stride = 0) = 0;
/**
* @brief
* @param buffer
* @param type
* @param offset
*/
virtual void setIndexBuffer(RHIBuffer* buffer, IndexType type, uint32_t offset = 0) = 0;
/**
* @brief Uniform
* @param slot
* @param buffer
*/
virtual void setUniformBuffer(uint32_t slot, RHIBuffer* buffer) = 0;
/**
* @brief Uniform
* @param slot
* @param buffer
* @param offset
* @param size 0
*/
virtual void setUniformBuffer(uint32_t slot, RHIBuffer* buffer, uint32_t offset, uint32_t size = 0) = 0;
/**
* @brief
* @param slot
* @param texture
*/
virtual void setTexture(uint32_t slot, RHITexture* texture) = 0;
/**
* @brief
* @param slot
* @param minFilter
* @param magFilter
* @param wrapS S
* @param wrapT T
*/
virtual void setSampler(uint32_t slot,
TextureFilter minFilter,
TextureFilter magFilter,
TextureWrap wrapS,
TextureWrap wrapT) = 0;
/**
* @brief float uniform
* @param name
* @param value
*/
virtual void setUniform(const char* name, float value) = 0;
/**
* @brief vec2 uniform
* @param name
* @param value
*/
virtual void setUniform(const char* name, const Vec2& value) = 0;
/**
* @brief vec3 uniform
* @param name
* @param value
*/
virtual void setUniform(const char* name, const Vec3& value) = 0;
/**
* @brief vec4/Color uniform
* @param name
* @param value
*/
virtual void setUniform(const char* name, const Color& value) = 0;
/**
* @brief mat4 uniform
* @param name
* @param value
*/
virtual void setUniform(const char* name, const Mat4& value) = 0;
//===========================================================================
// 绘制命令
//===========================================================================
/**
* @brief
* @param vertexCount
* @param firstVertex
* @param instanceCount
* @param firstInstance
*/
virtual void draw(uint32_t vertexCount,
uint32_t firstVertex = 0,
uint32_t instanceCount = 1,
uint32_t firstInstance = 0) = 0;
/**
* @brief
* @param indexCount
* @param firstIndex
* @param vertexOffset
* @param instanceCount
* @param firstInstance
*/
virtual void drawIndexed(uint32_t indexCount,
uint32_t firstIndex = 0,
int32_t vertexOffset = 0,
uint32_t instanceCount = 1,
uint32_t firstInstance = 0) = 0;
//===========================================================================
// 工具方法
//===========================================================================
/**
* @brief
* @param flags
* @param color
* @param depth
* @param stencil
*/
virtual void clear(ClearFlags flags,
const Color& color = Color::Black,
float depth = 1.0f,
uint8_t stencil = 0) = 0;
/**
* @brief
* @return
*/
virtual bool isRecording() const = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,169 @@
#pragma once
#include <memory>
#include <renderer/rhi/rhi_types.h>
namespace extra2d {
/**
* @brief RHI
*
*
*/
class RHIDevice {
public:
virtual ~RHIDevice() = default;
/**
* @brief
* @param nativeWindow SDL_Window*
* @return
*/
virtual bool initialize(void* nativeWindow) = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
//===========================================================================
// 资源创建
//===========================================================================
/**
* @brief
* @param desc
* @return
*/
virtual std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc &desc) = 0;
/**
* @brief
* @param desc
* @return
*/
virtual std::unique_ptr<RHITexture>
createTexture(const TextureDesc &desc) = 0;
/**
* @brief
* @param desc
* @return
*/
virtual std::unique_ptr<RHIShader> createShader(const ShaderDesc &desc) = 0;
/**
* @brief 线
* @param desc 线
* @return 线
*/
virtual std::unique_ptr<RHIPipeline>
createPipeline(const PipelineDesc &desc) = 0;
/**
* @brief
* @param desc
* @return
*/
virtual std::unique_ptr<RHIFramebuffer>
createFramebuffer(const RenderPassDesc &desc) = 0;
/**
* @brief
* @return
*/
virtual std::unique_ptr<RHICommandList> createCommandList() = 0;
//===========================================================================
// 上下文管理
//===========================================================================
/**
* @brief
* @return
*/
virtual RHIContext *getContext() = 0;
/**
* @brief
* @return "OpenGL", "Vulkan"
*/
virtual const char *getBackendName() const = 0;
/**
* @brief GPU
*/
virtual void waitIdle() = 0;
/**
* @brief
* @return
*/
virtual const RenderStats &getStats() const = 0;
/**
* @brief
*/
virtual void resetStats() = 0;
};
/**
* @brief RHI
*
*
*/
class RHIContext {
public:
virtual ~RHIContext() = default;
/**
* @brief
* @return
*/
virtual bool initialize() = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
*/
virtual void beginFrame() = 0;
/**
* @brief
*/
virtual void endFrame() = 0;
/**
* @brief
* @param x X
* @param y Y
* @param width
* @param height
*/
virtual void setViewport(int32_t x, int32_t y, uint32_t width,
uint32_t height) = 0;
/**
* @brief
*/
virtual void bindDefaultFramebuffer() = 0;
/**
* @brief
* @return
*/
virtual RHIFramebuffer *getDefaultFramebuffer() = 0;
/**
* @brief
* @param feature
* @return
*/
virtual bool isFeatureSupported(const char *feature) const = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,76 @@
#pragma once
#include <renderer/rhi/rhi_types.h>
namespace extra2d {
/**
* @brief RHI
*
* GPU (FBO)
*/
class RHIFramebuffer {
public:
virtual ~RHIFramebuffer() = default;
/**
* @brief
*/
virtual void bind() = 0;
/**
* @brief
*/
virtual void unbind() = 0;
/**
* @brief
* @return
*/
virtual uint32_t getColorAttachmentCount() const = 0;
/**
* @brief
* @param index
* @return
*/
virtual TextureHandle getColorAttachment(uint32_t index) const = 0;
/**
* @brief /
* @return
*/
virtual TextureHandle getDepthStencilAttachment() const = 0;
/**
* @brief
* @return
*/
virtual uint32_t getWidth() const = 0;
/**
* @brief
* @return
*/
virtual uint32_t getHeight() const = 0;
/**
* @brief /
* @return /
*/
virtual bool hasDepthStencil() const = 0;
/**
* @brief
* @return
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual bool isDefault() const = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,86 @@
#pragma once
#include <renderer/rhi/rhi_types.h>
#include <string>
namespace extra2d {
/**
* @brief RHI
*
* GPU
*/
class RHIShader {
public:
virtual ~RHIShader() = default;
/**
* @brief
* @return
*/
virtual ShaderType getType() const = 0;
/**
* @brief
* @return
*/
virtual bool isCompiled() const = 0;
/**
* @brief
* @return
*/
virtual std::string getCompileLog() const = 0;
/**
* @brief
* @return
*/
virtual bool isValid() const = 0;
};
/**
* @brief RHI 线
*
* GPU 线 (PSO)
*/
class RHIPipeline {
public:
virtual ~RHIPipeline() = default;
/**
* @brief 线
*/
virtual void bind() = 0;
/**
* @brief 线
*/
virtual void unbind() = 0;
/**
* @brief
* @return
*/
virtual ShaderHandle getVertexShader() const = 0;
/**
* @brief
* @return
*/
virtual ShaderHandle getFragmentShader() const = 0;
/**
* @brief
* @return
*/
virtual const VertexLayout& getVertexLayout() const = 0;
/**
* @brief
* @return
*/
virtual bool isValid() const = 0;
};
} // namespace extra2d

View File

@ -0,0 +1,501 @@
#pragma once
#include <string>
#include <types/base/types.h>
#include <types/math/color.h>
#include <types/math/mat4.h>
#include <types/math/vec2.h>
#include <types/math/vec3.h>
#include <vector>
namespace extra2d {
// 前向声明
class RHIDevice;
class RHIContext;
class RHICommandList;
class RHIBuffer;
class RHITexture;
class RHIShader;
class RHIPipeline;
class RHIFramebuffer;
/**
* @brief RHI
*/
template <typename T> class RHIHandle {
public:
RHIHandle() = default;
explicit RHIHandle(T *ptr) : ptr_(ptr) {}
T *get() const { return ptr_; }
bool isValid() const { return ptr_ != nullptr; }
explicit operator bool() const { return isValid(); }
bool operator==(const RHIHandle &other) const { return ptr_ == other.ptr_; }
bool operator!=(const RHIHandle &other) const { return ptr_ != other.ptr_; }
private:
T *ptr_ = nullptr;
};
using BufferHandle = RHIHandle<RHIBuffer>;
using TextureHandle = RHIHandle<RHITexture>;
using ShaderHandle = RHIHandle<RHIShader>;
using PipelineHandle = RHIHandle<RHIPipeline>;
using FramebufferHandle = RHIHandle<RHIFramebuffer>;
/**
* @brief
*/
enum class BufferType : uint8_t {
Vertex, // 顶点缓冲区
Index, // 索引缓冲区
Uniform, // Uniform 缓冲区
Storage, // 存储缓冲区 (SSBO)
Staging // 暂存缓冲区
};
/**
* @brief 使
*/
enum class BufferUsage : uint8_t {
Static, // 静态数据,很少修改
Dynamic, // 动态数据,每帧修改
Stream // 流数据,每帧多次修改
};
/**
* @brief
*/
struct BufferDesc {
BufferType type = BufferType::Vertex;
BufferUsage usage = BufferUsage::Static;
uint32_t size = 0; // 缓冲区大小(字节)
const void *initialData = nullptr; // 初始数据
static BufferDesc vertex(uint32_t size,
BufferUsage usage = BufferUsage::Static);
static BufferDesc index(uint32_t size,
BufferUsage usage = BufferUsage::Static);
static BufferDesc uniform(uint32_t size);
};
/**
* @brief
*/
enum class TextureType : uint8_t {
Texture2D, // 2D 纹理
TextureCube, // 立方体贴图
TextureArray, // 纹理数组
RenderTarget // 渲染目标
};
/**
* @brief
*/
enum class TextureUsage : uint8_t {
Sampled, // 采样纹理(只读)
Storage, // 存储纹理(读写)
RenderTarget, // 渲染目标
DepthStencil // 深度/模板目标
};
/**
* @brief
*/
enum class TextureFormat : uint8_t {
// 颜色格式
R8, // 8位 红
RG8, // 8位 RG
RGB8, // 8位 RGB
RGBA8, // 8位 RGBA
RGBA8_SRGB, // sRGB 颜色空间
// 深度/模板格式
Depth16,
Depth24,
Depth32F,
Depth24Stencil8,
Depth32FStencil8,
// 压缩格式
BC1, // DXT1
BC3, // DXT5
BC5 // 3Dc
};
/**
* @brief
*/
enum class TextureFilter : uint8_t {
Nearest, // 最近邻
Linear, // 线性
NearestMipmapNearest,
LinearMipmapNearest,
NearestMipmapLinear,
LinearMipmapLinear
};
/**
* @brief
*/
enum class TextureWrap : uint8_t {
Repeat, // 重复
ClampToEdge, // 边缘钳制
ClampToBorder, // 边界钳制
MirroredRepeat // 镜像重复
};
/**
* @brief
*/
struct TextureDesc {
TextureType type = TextureType::Texture2D;
TextureFormat format = TextureFormat::RGBA8;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1; // 对于 3D 纹理或数组层数
uint32_t mipLevels = 1;
TextureFilter minFilter = TextureFilter::Linear;
TextureFilter magFilter = TextureFilter::Linear;
TextureWrap wrapS = TextureWrap::Repeat;
TextureWrap wrapT = TextureWrap::Repeat;
bool renderTarget = false; // 是否可作为渲染目标
static TextureDesc texture2D(uint32_t width, uint32_t height,
TextureFormat format = TextureFormat::RGBA8);
static TextureDesc renderTarget2D(uint32_t width, uint32_t height,
TextureFormat format);
static TextureDesc depthStencil(uint32_t width, uint32_t height);
};
/**
* @brief
*/
enum class ShaderType : uint8_t {
Vertex, // 顶点着色器
Fragment, // 片段着色器
Geometry, // 几何着色器
Compute // 计算着色器
};
/**
* @brief
*
*
*/
struct ShaderDesc {
std::string vertexSource; // 顶点着色器源码
std::string fragmentSource; // 片段着色器源码
std::string entryPoint = "main"; // 入口函数名
static ShaderDesc fromSource(const std::string &vertexSrc,
const std::string &fragmentSrc) {
ShaderDesc desc;
desc.vertexSource = vertexSrc;
desc.fragmentSource = fragmentSrc;
return desc;
}
};
/**
* @brief
*/
enum class VertexFormat : uint8_t {
Float1,
Float2,
Float3,
Float4,
Int1,
Int2,
Int3,
Int4,
UInt1,
UInt2,
UInt3,
UInt4,
Byte4,
Byte4Normalized,
UByte4,
UByte4Normalized
};
/**
* @brief
*/
struct VertexAttribute {
uint32_t location = 0; // 属性位置
VertexFormat format = VertexFormat::Float3;
uint32_t offset = 0; // 在顶点结构中的偏移
uint32_t bufferIndex = 0; // 绑定的顶点缓冲区索引
uint32_t divisor = 0; // 实例化除数0=每顶点1=每实例N=每N个实例
static uint32_t getSize(VertexFormat format);
// 创建实例化属性
static VertexAttribute perInstance(uint32_t location, VertexFormat format,
uint32_t offset, uint32_t bufferIndex = 1) {
VertexAttribute attr;
attr.location = location;
attr.format = format;
attr.offset = offset;
attr.bufferIndex = bufferIndex;
attr.divisor = 1;
return attr;
}
};
/**
* @brief
*/
struct VertexLayout {
std::vector<VertexAttribute> attributes;
uint32_t stride = 0; // 顶点大小(字节)
void addAttribute(uint32_t location, VertexFormat format, uint32_t offset,
uint32_t bufferIndex = 0);
/**
* @brief VertexAttribute
* @param attr
*/
void addAttribute(const VertexAttribute& attr) {
attributes.push_back(attr);
}
};
/**
* @brief
*/
enum class IndexType : uint8_t { UInt16, UInt32 };
/**
* @brief
*/
enum class PrimitiveType : uint8_t {
Points,
Lines,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan
};
/**
* @brief
*/
enum class BlendFactor : uint8_t {
Zero,
One,
SrcColor,
OneMinusSrcColor,
DstColor,
OneMinusDstColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha
};
/**
* @brief
*/
enum class BlendOp : uint8_t { Add, Subtract, ReverseSubtract, Min, Max };
/**
* @brief
*/
struct BlendState {
bool enabled = false;
BlendFactor srcFactor = BlendFactor::One;
BlendFactor dstFactor = BlendFactor::Zero;
BlendOp op = BlendOp::Add;
BlendFactor srcAlphaFactor = BlendFactor::One;
BlendFactor dstAlphaFactor = BlendFactor::Zero;
BlendOp alphaOp = BlendOp::Add;
static BlendState opaque();
static BlendState alphaBlend();
static BlendState additive();
};
/**
* @brief
*/
enum class CompareFunc : uint8_t {
Never,
Less,
Equal,
LessEqual,
Greater,
NotEqual,
GreaterEqual,
Always
};
/**
* @brief /
*/
struct DepthStencilState {
bool depthTestEnabled = true;
bool depthWriteEnabled = true;
CompareFunc depthCompare = CompareFunc::Less;
bool stencilEnabled = false;
// 模板测试配置(简化版)
uint8_t stencilReadMask = 0xFF;
uint8_t stencilWriteMask = 0xFF;
uint8_t stencilRef = 0;
static DepthStencilState depthTest();
static DepthStencilState depthTestWrite();
static DepthStencilState depthTestNoWrite();
static DepthStencilState noDepthTest();
};
/**
* @brief
*/
struct RasterizerState {
bool cullEnabled = false;
bool cullFrontFace = false; // true = 剔除正面, false = 剔除背面
bool frontCCW = false; // 正面是否为逆时针
bool scissorEnabled = false;
bool wireframe = false;
static RasterizerState cullBack();
static RasterizerState cullFront();
static RasterizerState noCull();
};
/**
* @brief 线
*/
struct PipelineDesc {
ShaderHandle vertexShader;
ShaderHandle fragmentShader;
VertexLayout vertexLayout;
PrimitiveType primitiveType = PrimitiveType::Triangles;
BlendState blendState;
DepthStencilState depthStencilState;
RasterizerState rasterizerState;
static PipelineDesc create(ShaderHandle vs, ShaderHandle fs,
const VertexLayout &layout);
};
/**
* @brief /
*/
enum class LoadOp : uint8_t {
Load, // 加载现有内容
Clear, // 清除为指定值
DontCare // 不关心现有内容
};
enum class StoreOp : uint8_t {
Store, // 保存结果
DontCare // 不关心结果
};
/**
* @brief
*/
struct RenderPassAttachment {
TextureHandle texture;
LoadOp loadOp = LoadOp::Clear;
StoreOp storeOp = StoreOp::Store;
Color clearColor = Color::Black;
float clearDepth = 1.0f;
uint8_t clearStencil = 0;
};
/**
* @brief
*/
struct RenderPassDesc {
std::vector<RenderPassAttachment> colorAttachments;
RenderPassAttachment depthStencilAttachment;
bool hasDepthStencil = false;
};
/**
* @brief
*/
struct Viewport {
float x = 0.0f;
float y = 0.0f;
float width = 0.0f;
float height = 0.0f;
float minDepth = 0.0f;
float maxDepth = 1.0f;
Viewport() = default;
Viewport(float x, float y, float w, float h)
: x(x), y(y), width(w), height(h) {}
};
/**
* @brief
*/
struct ScissorRect {
int32_t x = 0;
int32_t y = 0;
int32_t width = 0;
int32_t height = 0;
ScissorRect() = default;
ScissorRect(int32_t x, int32_t y, int32_t w, int32_t h)
: x(x), y(y), width(w), height(h) {}
};
/**
* @brief
*/
enum class ClearFlags : uint32_t {
None = 0,
Color = 1 << 0,
Depth = 1 << 1,
Stencil = 1 << 2,
All = Color | Depth | Stencil
};
inline ClearFlags operator|(ClearFlags a, ClearFlags b) {
return static_cast<ClearFlags>(static_cast<uint32_t>(a) |
static_cast<uint32_t>(b));
}
inline ClearFlags operator&(ClearFlags a, ClearFlags b) {
return static_cast<ClearFlags>(static_cast<uint32_t>(a) &
static_cast<uint32_t>(b));
}
inline bool hasFlag(ClearFlags flags, ClearFlags flag) {
return (static_cast<uint32_t>(flags) & static_cast<uint32_t>(flag)) != 0;
}
/**
* @brief
*/
struct RenderStats {
uint32_t drawCalls = 0;
uint32_t triangles = 0;
uint32_t vertices = 0;
uint32_t textureBinds = 0;
uint32_t bufferBinds = 0;
uint32_t pipelineBinds = 0;
uint32_t renderPassSwitches = 0;
void reset() {
drawCalls = 0;
triangles = 0;
vertices = 0;
textureBinds = 0;
bufferBinds = 0;
pipelineBinds = 0;
renderPassSwitches = 0;
}
};
} // namespace extra2d

View File

@ -0,0 +1,113 @@
#pragma once
#include <event/events.h>
#include <module/module.h>
#include <module/module_registry.h>
#include <renderer/rhi/rhi.h>
#include <memory>
namespace extra2d {
/**
* @brief RHI
*
* RHI API
* 1 WindowModule
*/
class RHIModule : public Module {
E2D_REGISTER_MODULE(RHIModule, "RHI", 1)
public:
RHIModule();
~RHIModule() override;
/**
* @brief RHI
* @return
*/
bool init() override;
/**
* @brief RHI
*/
void shutdown() override;
/**
* @brief RHI
* @return RHI
*/
RHIDevice* getDevice() const { return device_.get(); }
/**
* @brief RHI
* @return RHI
*/
RHIContext* getContext() const { return context_; }
/**
* @brief RHIModule
* @return RHIModule
*/
static RHIModule* get();
/**
* @brief RHI
* @return
*/
bool isInitialized() const { return initialized_; }
/**
* @brief
* @param desc
* @return
*/
std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc& desc);
/**
* @brief
* @param desc
* @return
*/
std::unique_ptr<RHITexture> createTexture(const TextureDesc& desc);
/**
* @brief
* @param desc
* @return
*/
std::unique_ptr<RHIShader> createShader(const ShaderDesc& desc);
/**
* @brief 线
* @param desc 线
* @return 线
*/
std::unique_ptr<RHIPipeline> createPipeline(const PipelineDesc& desc);
/**
* @brief
* @param desc
* @return
*/
std::unique_ptr<RHIFramebuffer> createFramebuffer(const RenderPassDesc& desc);
/**
* @brief
* @return
*/
std::unique_ptr<RHICommandList> createCommandList();
private:
/**
* @brief - OpenGL
*/
void onWindowShow();
private:
std::unique_ptr<RHIDevice> device_;
RHIContext* context_;
bool initialized_ = false;
std::unique_ptr<events::OnShow::Listener> onShowListener_;
};
} // namespace extra2d

View File

@ -1,155 +1,117 @@
#pragma once
#include <types/ptr/ref_counted.h>
#include <types/ptr/intrusive_ptr.h>
#include <types/base/types.h>
#include <renderer/rhi/rhi.h>
#include <string>
#include <types/base/types.h>
#include <types/ptr/intrusive_ptr.h>
#include <types/ptr/ref_counted.h>
#include <unordered_map>
// 前向声明 OpenGL 类型
typedef unsigned int GLuint;
typedef int GLint;
typedef unsigned int GLenum;
namespace extra2d {
/**
* @brief
*
* OpenGL 使
*
* RHI
*
*/
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 16float
*/
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; }
/**
* @brief
*/
Shader();
/**
* @brief
*/
~Shader() override;
/**
* @brief
* @param vsSource
* @param fsSource
* @return
*/
bool loadFromSource(const std::string &vsSource, const std::string &fsSource);
/**
* @brief RHI
* @return RHI
*/
ShaderHandle getHandle() const { return handle_; }
/**
* @brief RHI 线
* @return RHI 线
*/
PipelineHandle getPipeline() const { return pipeline_; }
/**
* @brief
* @return
*/
bool isLoaded() const { return handle_.isValid() && pipeline_.isValid(); }
/**
* @brief 使
* @param vsSource
* @param fsSource
* @param vertexLayout
* @return
*/
bool loadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout);
/**
* @brief Uniform Block
* @param name Uniform Block
* @param binding
*/
void setUniformBlock(const std::string &name, uint32_t binding);
/**
* @brief Uniform Block
* @param name Uniform Block
* @return UINT32_MAX
*/
uint32_t getUniformBlockBinding(const std::string& name) const;
/**
* @brief
* @param vsSource
* @param fsSource
* @return
*/
bool reloadFromSource(const std::string &vsSource, const std::string &fsSource);
/**
* @brief 使
* @param vsSource
* @param fsSource
* @param vertexLayout
* @return
*/
bool reloadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout);
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);
/**
* @brief
* @param source
* @param isVertex
* @return
*/
std::string addVersionIfNeeded(const std::string &source, bool isVertex);
private:
GLuint program_ = 0; // OpenGL 程序对象
std::unordered_map<std::string, GLint> uniformCache_; // Uniform 位置缓存
ShaderHandle handle_; // RHI 着色器句柄
PipelineHandle pipeline_; // RHI 管线句柄
// Uniform Block 绑定映射
std::unordered_map<std::string, uint32_t> uniformBlockBindings_;
};
} // namespace extra2d

View File

@ -1,20 +1,17 @@
#pragma once
#include <renderer/rhi/rhi.h>
#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
* RHI
*
*/
class Texture : public RefCounted {
@ -55,17 +52,6 @@ public:
*/
bool create(int width, int height, TextureFormat format);
/**
* @brief
* @param slot
*/
void bind(uint32_t slot = 0) const;
/**
* @brief
*/
void unbind() const;
/**
* @brief
* @return
@ -85,19 +71,42 @@ public:
TextureFormat getFormat() const { return format_; }
/**
* @brief OpenGL
* @return OpenGL
* @brief RHI
* @return RHI
*/
GLuint getHandle() const { return texture_; }
TextureHandle getHandle() const { return handle_; }
/**
* @brief
* @return
*/
bool isLoaded() const { return texture_ != 0; }
bool isLoaded() const { return handle_.isValid(); }
/**
* @brief
* @param path
* @return
*/
bool reloadFromFile(const std::string& path);
/**
* @brief
* @param data
* @param width
* @param height
* @param format
* @return
*/
bool reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format);
private:
GLuint texture_ = 0; // OpenGL 纹理对象
/**
* @brief
* @param format
* @return
*/
static uint32_t getBytesPerPixel(TextureFormat format);
TextureHandle handle_; // RHI 纹理句柄
int width_ = 0; // 纹理宽度
int height_ = 0; // 纹理高度
TextureFormat format_ = TextureFormat::RGBA8; // 像素格式

View File

@ -0,0 +1,247 @@
#pragma once
#include <renderer/texture.h>
#include <types/math/rect.h>
#include <types/ptr/intrusive_ptr.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
// 使用 stb_rect_pack 进行矩形打包
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb/stb_rect_pack.h>
namespace extra2d {
/**
* @brief
*
* UV
*/
struct AtlasRegion {
int x; // 在图集中的 X 坐标(像素)
int y; // 在图集中的 Y 坐标(像素)
int width; // 宽度(像素)
int height; // 高度(像素)
/**
* @brief UV
* @param atlasWidth
* @param atlasHeight
* @return UV (x, y, width, height)
*/
Rect getUVRect(int atlasWidth, int atlasHeight) const {
return Rect(
static_cast<float>(x) / atlasWidth,
static_cast<float>(y) / atlasHeight,
static_cast<float>(width) / atlasWidth,
static_cast<float>(height) / atlasHeight
);
}
/**
* @brief UV Y
* @param atlasWidth
* @param atlasHeight
* @return UV (x, y, width, height)Y
*/
Rect getUVRectFlipped(int atlasWidth, int atlasHeight) const {
float u = static_cast<float>(x) / atlasWidth;
float v = static_cast<float>(atlasHeight - y - height) / atlasHeight;
float w = static_cast<float>(width) / atlasWidth;
float h = static_cast<float>(height) / atlasHeight;
return Rect(u, v, w, h);
}
};
/**
* @brief
*
* 使 stb_rect_pack
*
*/
class TextureAtlas : public RefCounted {
public:
/**
* @brief
*/
TextureAtlas();
/**
* @brief
*/
~TextureAtlas();
// 禁止拷贝
TextureAtlas(const TextureAtlas&) = delete;
TextureAtlas& operator=(const TextureAtlas&) = delete;
// 允许移动
TextureAtlas(TextureAtlas&& other) noexcept;
TextureAtlas& operator=(TextureAtlas&& other) noexcept;
/**
* @brief
* @param width 2
* @param height 2
* @return
*/
bool initialize(int width, int height);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param name
* @param texture
* @return
*/
bool addTexture(const std::string& name, Ptr<Texture> texture);
/**
* @brief
* @param name
* @param data
* @param width
* @param height
* @param format
* @return
*/
bool addTextureData(const std::string& name, const uint8_t* data,
int width, int height, TextureFormat format);
/**
* @brief
* @return
*/
bool finalize();
/**
* @brief
* @return
*/
Ptr<Texture> getAtlasTexture() const { return atlasTexture_; }
/**
* @brief
* @param name
* @return nullptr
*/
const AtlasRegion* getRegion(const std::string& name) const;
/**
* @brief UV
* @param name
* @return UV (0,0,1,1)
*/
Rect getUVRect(const std::string& name) const;
/**
* @brief
* @param name
* @return
*/
bool hasTexture(const std::string& name) const;
/**
* @brief
*/
int getWidth() const { return width_; }
/**
* @brief
*/
int getHeight() const { return height_; }
/**
* @brief
*/
float getUsageRatio() const;
/**
* @brief
*/
size_t getTextureCount() const { return regions_.size(); }
/**
* @brief
*/
bool isFinalized() const { return finalized_; }
private:
// 待打包的矩形信息
struct PendingTexture {
std::string name;
int width;
int height;
std::vector<uint8_t> data;
TextureFormat format;
};
int width_ = 0;
int height_ = 0;
Ptr<Texture> atlasTexture_;
std::unordered_map<std::string, AtlasRegion> regions_;
std::vector<PendingTexture> pendingTextures_;
// stb_rect_pack 上下文
std::unique_ptr<stbrp_context> packContext_;
std::vector<stbrp_node> packNodes_;
bool finalized_ = false;
/**
* @brief
*/
void copyTextureData(const PendingTexture& tex, const AtlasRegion& region);
};
/**
* @brief
*
*
*/
class AtlasBuilder {
public:
/**
* @brief
* @param width
* @param height
*/
void setSize(int width, int height) {
width_ = width;
height_ = height;
}
/**
* @brief
* @param name
* @param texture
*/
void addTexture(const std::string& name, Ptr<Texture> texture) {
textures_.push_back({name, texture});
}
/**
* @brief
* @return nullptr
*/
Ptr<TextureAtlas> build();
/**
* @brief
* @return nullptr
*/
Ptr<TextureAtlas> buildAuto();
private:
int width_ = 2048;
int height_ = 2048;
std::vector<std::pair<std::string, Ptr<Texture>>> textures_;
};
} // namespace extra2d

View File

@ -1,8 +1,7 @@
#pragma once
#include <glad/glad.h>
#include <renderer/rhi/rhi.h>
#include <memory>
#include <renderer/render_types.h>
#include <vector>
namespace extra2d {
@ -10,70 +9,76 @@ namespace extra2d {
/**
* @brief (UBO)
*
* uniform GPU
* RHI UBO uniform GPU
* std140
*/
class UniformBuffer {
public:
/**
* @brief
*/
UniformBuffer();
/**
* @brief
*/
UniformBuffer();
/**
* @brief
*/
~UniformBuffer();
/**
* @brief
*/
~UniformBuffer();
/**
* @brief UBO
* @param size
* @param binding
* @return
*/
bool create(uint32_t size, uint32_t binding);
/**
* @brief UBO
* @param size
* @param binding
* @return
*/
bool create(uint32_t size, uint32_t binding);
/**
* @brief UBO
*/
void destroy();
/**
* @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 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
* @param binding
*/
void bind(uint32_t binding);
/**
* @brief
* @return
*/
uint32_t getSize() const { return size_; }
/**
* @brief
* @return
*/
uint32_t getSize() const { return size_; }
/**
* @brief OpenGL
* @return OpenGL
*/
GLuint getHandle() const { return ubo_; }
/**
* @brief RHI
* @return RHI
*/
BufferHandle getHandle() const { return handle_; }
/**
* @brief
* @return
*/
bool isValid() const { return ubo_ != 0; }
/**
* @brief RHI
* @return RHI
*/
RHIBuffer* getRHIBuffer() const;
/**
* @brief
* @return
*/
bool isValid() const { return handle_.isValid(); }
private:
GLuint ubo_ = 0; // OpenGL 缓冲区对象
uint32_t size_ = 0; // 缓冲区大小
uint32_t binding_ = 0; // 绑定槽位
BufferHandle handle_; // RHI 缓冲区句柄
uint32_t size_ = 0; // 缓冲区大小
uint32_t binding_ = 0; // 绑定槽位
};
/**
@ -81,77 +86,112 @@ private:
*
* UBO
*
* CPU-GPU
*/
class UniformBufferManager {
public:
/**
* @brief
*/
UniformBufferManager();
/**
* @brief
*/
UniformBufferManager();
/**
* @brief
*/
~UniformBufferManager();
/**
* @brief
*/
~UniformBufferManager();
// 禁止拷贝
UniformBufferManager(const UniformBufferManager &) = delete;
UniformBufferManager &operator=(const UniformBufferManager &) = delete;
// 禁止拷贝
UniformBufferManager(const UniformBufferManager&) = delete;
UniformBufferManager& operator=(const UniformBufferManager&) = delete;
// 允许移动
UniformBufferManager(UniformBufferManager &&) noexcept;
UniformBufferManager &operator=(UniformBufferManager &&) noexcept;
// 允许移动
UniformBufferManager(UniformBufferManager&&) noexcept;
UniformBufferManager& operator=(UniformBufferManager&&) noexcept;
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
void shutdown();
/**
* @brief UBO
* @return UBO
*/
UniformBuffer *getGlobalUBO();
/**
* @brief UBO
* @param frameIndex
* @return UBO
*/
UniformBuffer* getGlobalUBO(uint32_t frameIndex);
/**
* @brief UBO
* @param size
* @return UBO
*/
UniformBuffer *acquireMaterialUBO(uint32_t size);
/**
* @brief UBO
* @param size
* @return UBO
*/
UniformBuffer* acquireMaterialUBO(uint32_t size);
/**
* @brief UBO
*
* 便 UBO
*/
void resetMaterialUBOs();
/**
* @brief UBO
*
* 便 UBO
*/
void resetMaterialUBOs();
/**
* @brief UBO
* @param data
* @param size
*/
void updateGlobalUBO(const void *data, uint32_t size);
/**
* @brief UBO
* @param data
* @param size
* @param frameIndex
*/
void updateGlobalUBO(const void* data, uint32_t size, uint32_t frameIndex);
/**
* @brief UBO
* @param data
* @param size
* @param offset
*/
void batchUpdateMaterialUBO(const void* data, uint32_t size, uint32_t offset);
/**
* @brief UBO CPU
* @return CPU
*/
uint8_t* getMaterialUBOBuffer() { return materialUBOBuffer_.data(); }
/**
* @brief UBO
* @return
*/
uint32_t getMaterialUBOBufferSize() const { return static_cast<uint32_t>(materialUBOBuffer_.size()); }
/**
* @brief UBO GPU
*/
void flushMaterialUBO();
private:
// 全局 UBO用于 viewProjection、time 等全局数据)
std::unique_ptr<UniformBuffer> globalUBO_;
// 全局 UBO 双缓冲(用于 viewProjection、time 等全局数据)
std::array<std::unique_ptr<UniformBuffer>, 2> globalUBOs_;
// 材质 UBO 池
std::vector<std::unique_ptr<UniformBuffer>> materialUBOPool_;
uint32_t currentUBOIndex_ = 0;
// 材质 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
// 材质 UBO CPU 缓冲区(用于批量更新)
std::vector<uint8_t> materialUBOBuffer_;
uint32_t materialUBOBufferOffset_ = 0;
UniformBuffer* currentMaterialUBO_ = nullptr;
// 常量
static constexpr uint32_t INITIAL_UBO_POOL_SIZE = 16;
static constexpr uint32_t GLOBAL_UBO_SIZE = 256; // 足够存储 viewProjection + time + screenSize
static constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO 绑定槽位
static constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO 绑定槽位
static constexpr uint32_t MATERIAL_UBO_BUFFER_SIZE = 1024 * 1024; // 1MB 材质 UBO 缓冲区
};
} // namespace extra2d

View File

@ -307,7 +307,7 @@ public:
/**
* @brief
*/
void render();
virtual void render();
private:
std::string name_;

View File

@ -13,20 +13,18 @@ namespace extra2d {
*
*/
class SceneModule : public Module {
// 自动注册到模块系统,优先级为 4在 Renderer 之后)
E2D_REGISTER_MODULE(SceneModule, "Scene", 4)
// 优先级为 5在 RendererModule (优先级 4) 之后初始化
E2D_REGISTER_MODULE(SceneModule, "Scene", 5)
public:
SceneModule();
~SceneModule() override;
// 禁止拷贝
// 禁止拷贝和移动
SceneModule(const SceneModule &) = delete;
SceneModule &operator=(const SceneModule &) = delete;
// 允许移动
SceneModule(SceneModule &&) noexcept;
SceneModule &operator=(SceneModule &&) noexcept;
SceneModule(SceneModule &&) = delete;
SceneModule &operator=(SceneModule &&) = delete;
// Module 接口实现
bool init() override;

View File

@ -44,20 +44,18 @@ struct TimerInfo {
* 线
*/
class TimerModule : public Module {
// 自动注册到模块系统,优先级为 5核心模块
E2D_REGISTER_MODULE(TimerModule, "Timer", 5)
// 优先级为 6在 SceneModule (优先级 5) 之后初始化
E2D_REGISTER_MODULE(TimerModule, "Timer", 6)
public:
TimerModule();
~TimerModule() override;
// 禁止拷贝
// 禁止拷贝和移动
TimerModule(const TimerModule &) = delete;
TimerModule &operator=(const TimerModule &) = delete;
// 允许移动
TimerModule(TimerModule &&) noexcept;
TimerModule &operator=(TimerModule &&) noexcept;
TimerModule(TimerModule &&) = delete;
TimerModule &operator=(TimerModule &&) = delete;
// Module 接口实现
bool init() override;

View File

@ -4,14 +4,12 @@ precision highp float;
// 从顶点着色器输入
in vec2 vTexCoord;
in vec4 vColor;
in vec4 vTintColor;
in float vOpacity;
// 纹理采样器
uniform sampler2D uTexture;
// 材质参数
uniform vec4 uTintColor;
uniform float uOpacity;
// 输出颜色
out vec4 fragColor;
@ -24,11 +22,16 @@ void main() {
// 采样纹理
vec4 texColor = texture(uTexture, vTexCoord);
// 如果纹理采样结果是黑色或透明,使用白色作为默认值
if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) {
texColor = vec4(1.0, 1.0, 1.0, 1.0);
}
// 混合:纹理 * 顶点颜色 * 色调
fragColor = texColor * vColor * uTintColor;
fragColor = texColor * vColor * vTintColor;
// 应用透明度
fragColor.a *= uOpacity;
fragColor.a *= vOpacity;
// Alpha 测试:丢弃几乎透明的像素
if (fragColor.a < 0.01) {

View File

@ -1,15 +1,26 @@
#version 320 es
precision highp float;
// 视图投影矩阵
uniform mat4 uViewProjection;
// 全局 UBO (binding = 0) - 每帧更新一次
layout(std140, binding = 0) uniform GlobalUBO {
mat4 uViewProjection;
vec4 uCameraPosition;
float uTime;
float uDeltaTime;
vec2 uScreenSize;
};
// 模型矩阵
// 材质 UBO (binding = 1) - 每物体更新
layout(std140, binding = 1) uniform MaterialUBO {
vec4 uColor;
vec4 uTintColor;
float uOpacity;
float uPadding[3]; // std140 对齐填充
};
// 模型矩阵作为单独的统一变量(每个物体设置)
uniform mat4 uModelMatrix;
// 顶点颜色(覆盖顶点属性中的颜色)
uniform vec4 uColor;
// 顶点属性
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
@ -18,16 +29,20 @@ layout(location = 2) in vec4 aColor;
// 输出到片段着色器
out vec2 vTexCoord;
out vec4 vColor;
out vec4 vTintColor;
out float vOpacity;
/**
* @brief
*
*
*
*
*/
void main() {
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
// 使用 uniform 颜色覆盖顶点属性颜色
vColor = uColor;
// 混合顶点颜色和材质 UBO 中的颜色
vColor = aColor * uColor;
vTintColor = uTintColor;
vOpacity = uOpacity;
}

73
shader/instanced.vert Normal file
View File

@ -0,0 +1,73 @@
#version 320 es
precision highp float;
// 全局 UBO (binding = 0) - 每帧更新一次
layout(std140, binding = 0) uniform GlobalUBO {
mat4 uViewProjection;
vec4 uCameraPosition;
float uTime;
float uDeltaTime;
vec2 uScreenSize;
};
// 材质 UBO (binding = 1) - 每批次更新
layout(std140, binding = 1) uniform MaterialUBO {
vec4 uColor;
vec4 uTintColor;
float uOpacity;
float uPadding[3]; // std140 对齐填充
};
// 顶点属性 (每个顶点)
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
layout(location = 2) in vec4 aColor;
// 实例属性 (每个实例) - 使用 location 3-6
layout(location = 3) in vec2 iPosition; // 实例位置
layout(location = 4) in float iRotation; // 实例旋转
layout(location = 5) in vec2 iScale; // 实例缩放
layout(location = 6) in vec4 iColor; // 实例颜色
// 输出到片段着色器
out vec2 vTexCoord;
out vec4 vColor;
out vec4 vTintColor;
out float vOpacity;
/**
* @brief 2D变换矩阵
* @param angle
* @return 2x2旋转矩阵
*/
mat2 rotate2D(float angle) {
float c = cos(angle);
float s = sin(angle);
return mat2(c, -s, s, c);
}
/**
* @brief
*
*
*
*
*/
void main() {
// 应用实例缩放和旋转
vec2 localPos = rotate2D(iRotation) * (aPosition * iScale);
// 应用实例位置偏移
vec2 worldPos = localPos + iPosition;
// 变换到裁剪空间
gl_Position = uViewProjection * vec4(worldPos, 0.0, 1.0);
vTexCoord = aTexCoord;
// 混合顶点颜色、实例颜色和材质颜色
vColor = aColor * iColor * uColor;
vTintColor = uTintColor;
vOpacity = uOpacity;
}

View File

@ -0,0 +1,35 @@
#version 320 es
precision highp float;
// 从顶点着色器输入
in vec2 vTexCoord;
in vec4 vColor;
// 纹理采样器
uniform sampler2D uTexture;
// 输出颜色
out vec4 fragColor;
/**
* @brief
*
*
*/
void main() {
// 采样纹理
vec4 texColor = texture(uTexture, vTexCoord);
// 如果纹理采样结果是黑色或透明,使用白色作为默认值
if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) {
texColor = vec4(1.0, 1.0, 1.0, 1.0);
}
// 混合:纹理 * 顶点颜色
fragColor = texColor * vColor;
// Alpha 测试:丢弃几乎透明的像素
if (fragColor.a < 0.01) {
discard;
}
}

View File

@ -63,6 +63,11 @@ void Application::initModules() {
// 从注册表自动创建所有模块
modules_ = ModuleRegistry::instance().createModules();
// 存储模块实例到注册表,以便其他模块可以通过 getModule<T>() 获取
for (auto &module : modules_) {
ModuleRegistry::instance().storeModuleInstance(module.get());
}
// 初始化所有模块
for (auto &module : modules_) {
if (!module->init()) {

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,44 @@
#include <assets/loaders/shader_loader.h>
#include <filesystem>
#include <module/module_registry.h>
#include <platform/file_module.h>
#include <renderer/shader.h>
#include <utils/logger.h>
namespace extra2d {
namespace {
/**
* @brief FileModule
*/
FileModule *getFileModule() {
return getModule<FileModule>();
}
/**
* @brief 使 FileModule
*/
bool fileExists(const std::string &path) {
FileModule *fileModule = getFileModule();
if (fileModule) {
return fileModule->exists(path);
}
return false;
}
/**
* @brief 使 FileModule RomFS
*/
std::string readFileToString(const std::string &path) {
FileModule *fileModule = getFileModule();
if (fileModule) {
return fileModule->readString(path);
}
return "";
}
} // anonymous namespace
Ptr<Shader> ShaderLoader::load(const std::string &path) {
std::string basePath = path;
@ -16,10 +50,10 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
std::string vertPath = basePath + ".vert";
std::string fragPath = basePath + ".frag";
if (!std::filesystem::exists(vertPath)) {
if (!fileExists(vertPath)) {
vertPath = basePath + ".vs";
}
if (!std::filesystem::exists(fragPath)) {
if (!fileExists(fragPath)) {
fragPath = basePath + ".fs";
}
@ -28,10 +62,24 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
const std::string &fragPath) {
Ptr<Shader> shader = makePtr<Shader>();
// 读取顶点着色器文件
std::string vsSource = readFileToString(vertPath);
if (vsSource.empty()) {
E2D_LOG_ERROR("ShaderLoader: Failed to read vertex shader: {}", vertPath);
return Ptr<Shader>();
}
if (!shader->loadFromFile(vertPath, fragPath)) {
E2D_LOG_ERROR("ShaderLoader: Failed to load shader {} + {}", vertPath,
// 读取片段着色器文件
std::string fsSource = readFileToString(fragPath);
if (fsSource.empty()) {
E2D_LOG_ERROR("ShaderLoader: Failed to read fragment shader: {}", fragPath);
return Ptr<Shader>();
}
// 从源码加载着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("ShaderLoader: Failed to compile shader {} + {}", vertPath,
fragPath);
return Ptr<Shader>();
}

View File

@ -11,7 +11,7 @@ ModuleRegistry& ModuleRegistry::instance() {
std::vector<std::unique_ptr<Module>> ModuleRegistry::createModules() {
// 按优先级排序(值小的先初始化)
std::vector<ModuleInfo> sorted = registrations_;
std::sort(sorted.begin(), sorted.end(),
std::sort(sorted.begin(), sorted.end(),
[](const ModuleInfo& a, const ModuleInfo& b) {
return a.priority < b.priority;
});
@ -26,4 +26,21 @@ std::vector<std::unique_ptr<Module>> ModuleRegistry::createModules() {
return modules;
}
void ModuleRegistry::storeModuleInstance(Module* module) {
if (!module) return;
// 通过模块名称查找对应的注册信息
for (const auto& info : registrations_) {
if (info.name == module->getName()) {
// 使用注册时存储的 type 作为键
instances_[info.type] = module;
return;
}
}
}
void ModuleRegistry::clearInstances() {
instances_.clear();
}
} // namespace extra2d

View File

@ -1,31 +1,18 @@
#include <SDL.h>
#include <filesystem>
#include <fstream>
#include <platform/file_module.h>
#include <sstream>
#include <sys/stat.h>
#include <utils/logger.h>
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#define mkdir_impl(path, mode) _mkdir(path)
#elif defined(__SWITCH__)
#include <dirent.h>
#include <unistd.h>
// Switch 使用 ::mkdir 避免与类成员函数冲突
#define mkdir_impl(path, mode) ::mkdir(path, mode)
#else
#include <dirent.h>
#include <unistd.h>
#define mkdir_impl(path, mode) mkdir(path, mode)
#endif
#ifdef __SWITCH__
#include <switch.h>
#endif
namespace extra2d {
namespace fs = std::filesystem;
FileModule::FileModule() = default;
FileModule::~FileModule() = default;
@ -44,11 +31,19 @@ bool FileModule::init() {
Result rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
// 在 Switch 上设置资源根目录为 RomFS
assetRoot_ = "romfs:/";
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
// RomFS 初始化失败,使用当前目录
assetRoot_ = "./";
}
#else
// 非 Switch 平台,使用当前目录作为资源根目录
assetRoot_ = "./";
#endif
E2D_LOG_INFO("Asset root set to: {}", assetRoot_);
return true;
}
@ -59,16 +54,16 @@ void FileModule::shutdown() {
#endif
}
bool FileModule::isRomfsPath(const std::string &path) const {
return path.find(romfsRoot_) == 0;
}
bool FileModule::exists(const std::string &path) const {
struct stat st;
return stat(path.c_str(), &st) == 0;
return fs::exists(path);
}
bool FileModule::isDir(const std::string &path) const {
struct stat st;
if (stat(path.c_str(), &st) != 0)
return false;
return (st.st_mode & S_IFDIR) != 0;
return fs::is_directory(path);
}
FileData FileModule::read(const std::string &path) const {
@ -105,6 +100,12 @@ std::string FileModule::readString(const std::string &path) const {
bool FileModule::write(const std::string &path, const void *data,
size_t size) const {
// RomFS 是只读的,不允许写入
if (isRomfsPath(path)) {
E2D_LOG_WARN("Cannot write to RomFS path: {}", path);
return false;
}
std::ofstream file(path, std::ios::binary);
if (!file.is_open())
return false;
@ -121,6 +122,12 @@ bool FileModule::writeString(const std::string &path,
bool FileModule::append(const std::string &path, const void *data,
size_t size) const {
// RomFS 是只读的,不允许写入
if (isRomfsPath(path)) {
E2D_LOG_WARN("Cannot append to RomFS path: {}", path);
return false;
}
std::ofstream file(path, std::ios::binary | std::ios::app);
if (!file.is_open())
return false;
@ -131,105 +138,69 @@ bool FileModule::append(const std::string &path, const void *data,
}
bool FileModule::remove(const std::string &path) const {
return std::remove(path.c_str()) == 0;
// RomFS 是只读的,不允许删除
if (isRomfsPath(path)) {
E2D_LOG_WARN("Cannot remove RomFS path: {}", path);
return false;
}
return fs::remove(path);
}
bool FileModule::mkdir(const std::string &path) const {
#ifdef _WIN32
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
#else
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
#endif
// RomFS 是只读的,不允许创建目录
if (isRomfsPath(path)) {
E2D_LOG_WARN("Cannot create directory in RomFS: {}", path);
return false;
}
return fs::create_directories(path);
}
std::vector<FileInfo> FileModule::listDir(const std::string &path) const {
std::vector<FileInfo> result;
#ifdef _WIN32
WIN32_FIND_DATAA findData;
std::string searchPath = path + "\\*";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE)
if (!fs::exists(path) || !fs::is_directory(path)) {
return result;
}
do {
std::string name = findData.cFileName;
for (const auto &entry : fs::directory_iterator(path)) {
std::string name = entry.path().filename().string();
if (name == "." || name == "..")
continue;
FileInfo info;
info.name = name;
info.path = join(path, name);
info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (!info.isDir) {
info.size = static_cast<int64>(findData.nFileSizeLow) |
(static_cast<int64>(findData.nFileSizeHigh) << 32);
}
result.push_back(info);
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
#else
DIR *dir = opendir(path.c_str());
if (!dir)
return result;
struct dirent *entry;
while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name;
if (name == "." || name == "..")
continue;
FileInfo info;
info.name = name;
info.path = join(path, name);
info.isDir = isDir(info.path);
if (!info.isDir) {
info.size = fileSize(info.path);
info.path = entry.path().string();
info.isDir = entry.is_directory();
if (!info.isDir && entry.is_regular_file()) {
info.size = static_cast<int64>(entry.file_size());
}
result.push_back(info);
}
closedir(dir);
#endif
return result;
}
int64 FileModule::fileSize(const std::string &path) const {
struct stat st;
if (stat(path.c_str(), &st) != 0)
if (!fs::exists(path) || !fs::is_regular_file(path)) {
return -1;
return static_cast<int64>(st.st_size);
}
return static_cast<int64>(fs::file_size(path));
}
std::string FileModule::ext(const std::string &path) const {
size_t pos = path.find_last_of('.');
if (pos == std::string::npos || pos == 0)
return "";
size_t lastSep = path.find_last_of("/\\");
if (lastSep != std::string::npos && pos < lastSep)
return "";
return path.substr(pos + 1);
fs::path p(path);
return p.extension().string();
}
std::string FileModule::fileName(const std::string &path) const {
size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos)
return path;
return path.substr(pos + 1);
fs::path p(path);
return p.filename().string();
}
std::string FileModule::dirName(const std::string &path) const {
size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos)
return ".";
if (pos == 0)
return "/";
return path.substr(0, pos);
fs::path p(path);
return p.parent_path().string();
}
std::string FileModule::join(const std::string &a, const std::string &b) const {
@ -238,11 +209,9 @@ std::string FileModule::join(const std::string &a, const std::string &b) const {
if (b.empty())
return a;
char last = a.back();
if (last == '/' || last == '\\') {
return a + b;
}
return a + "/" + b;
fs::path base(a);
fs::path sub(b);
return (base / sub).string();
}
std::string FileModule::writableDir() const { return writableDir_; }

View File

@ -12,64 +12,6 @@ InputModule::~InputModule() {
shutdown();
}
InputModule::InputModule(InputModule&& other) noexcept
: mouseX_(other.mouseX_)
, mouseY_(other.mouseY_)
, mouseWheel_(other.mouseWheel_)
, activeTouches_(std::move(other.activeTouches_))
, endedTouches_(std::move(other.endedTouches_))
, onKeyDown_(std::move(other.onKeyDown_))
, onKeyUp_(std::move(other.onKeyUp_))
, onMouseDown_(std::move(other.onMouseDown_))
, onMouseUp_(std::move(other.onMouseUp_))
, onTouchBegan_(std::move(other.onTouchBegan_))
, onTouchMoved_(std::move(other.onTouchMoved_))
, onTouchEnded_(std::move(other.onTouchEnded_)) {
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
std::memcpy(padState_, other.padState_, sizeof(padState_));
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
other.gamepads_[i] = nullptr;
}
}
InputModule& InputModule::operator=(InputModule&& other) noexcept {
if (this != &other) {
shutdown();
mouseX_ = other.mouseX_;
mouseY_ = other.mouseY_;
mouseWheel_ = other.mouseWheel_;
activeTouches_ = std::move(other.activeTouches_);
endedTouches_ = std::move(other.endedTouches_);
onKeyDown_ = std::move(other.onKeyDown_);
onKeyUp_ = std::move(other.onKeyUp_);
onMouseDown_ = std::move(other.onMouseDown_);
onMouseUp_ = std::move(other.onMouseUp_);
onTouchBegan_ = std::move(other.onTouchBegan_);
onTouchMoved_ = std::move(other.onTouchMoved_);
onTouchEnded_ = std::move(other.onTouchEnded_);
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
std::memcpy(padState_, other.padState_, sizeof(padState_));
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
other.gamepads_[i] = nullptr;
}
}
return *this;
}
bool InputModule::init() {
std::memset(keyState_.data(), 0, KEY_COUNT);
std::memset(keyPrev_.data(), 0, KEY_COUNT);

View File

@ -4,39 +4,12 @@
#include <platform/window_module.h>
#include <utils/logger.h>
// OpenGL ES 3.2 with GLAD
#include <glad/glad.h>
namespace extra2d {
WindowModule::WindowModule() = default;
WindowModule::~WindowModule() { shutdown(); }
WindowModule::WindowModule(WindowModule &&other) noexcept
: window_(other.window_), glContext_(other.glContext_),
shouldClose_(other.shouldClose_), onClose_(std::move(other.onClose_)),
onResize_(std::move(other.onResize_)),
configListener_(std::move(other.configListener_)) {
other.window_ = nullptr;
other.glContext_ = nullptr;
}
WindowModule &WindowModule::operator=(WindowModule &&other) noexcept {
if (this != &other) {
shutdown();
window_ = other.window_;
glContext_ = other.glContext_;
shouldClose_ = other.shouldClose_;
onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_);
configListener_ = std::move(other.configListener_);
other.window_ = nullptr;
other.glContext_ = nullptr;
}
return *this;
}
bool WindowModule::init() {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
@ -58,7 +31,10 @@ bool WindowModule::init() {
}
void WindowModule::shutdown() {
destroyGLContext();
// 清理事件监听器
configListener_.reset();
renderPresentListener_.reset();
if (window_) {
SDL_DestroyWindow(window_);
window_ = nullptr;
@ -163,16 +139,10 @@ void WindowModule::onModuleConfig(const AppConfig &config) {
cfg.resizable = config.resizable;
if (create(cfg)) {
// 创建 OpenGL 上下文
if (!createGLContext()) {
E2D_LOG_ERROR("Failed to create OpenGL context");
return;
}
// GL 上下文创建完成后,再发送窗口显示事件
// 这样渲染模块可以在收到事件时安全地使用 GL 函数
// 发送窗口显示事件
// RHI 模块会监听此事件并创建 OpenGL 上下文
events::OnShow::emit();
setVisible(true);
}
}
@ -240,56 +210,9 @@ bool WindowModule::isVisible() const {
return (flags & SDL_WINDOW_SHOWN) != 0;
}
bool WindowModule::createGLContext() {
if (!window_) {
return false;
}
// 创建 OpenGL ES 3.2 上下文
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_LOG_ERROR("Failed to create OpenGL ES context: {}", SDL_GetError());
return false;
}
// 初始化 GLAD
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("Failed to initialize GLAD");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL ES context created: {}.{}", GLVersion.major,
GLVersion.minor);
return true;
}
void WindowModule::destroyGLContext() {
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
}
void WindowModule::swapBuffers() {
if (window_) {
SDL_GL_SwapWindow(window_);
}
}
void WindowModule::onRenderPresent() {
swapBuffers();
// 交换缓冲区由 RHI 模块处理
// 这里可以触发呈现事件
}
void *WindowModule::getGLContext() const { return glContext_; }
} // namespace extra2d

View File

@ -0,0 +1,553 @@
#include <algorithm>
#include <cstring>
#include <vector>
#include <renderer/command_queue.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/rhi_module.h>
#include <renderer/rhi/opengl/gl_command_list.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <types/math/transform.h>
#include <types/ptr/intrusive_ptr.h>
#include <utils/logger.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace extra2d {
// ========================================
// CommandSorter 实现
// ========================================
CommandSorter::CommandSorter() {
// 预分配初始容量
commands_.reserve(INITIAL_CAPACITY);
sortedIndices_.reserve(INITIAL_CAPACITY);
}
CommandSorter::~CommandSorter() = default;
CommandSorter::CommandSorter(CommandSorter&& other) noexcept
: commands_(std::move(other.commands_)),
sortedIndices_(std::move(other.sortedIndices_)),
commandCount_(other.commandCount_) {
other.commandCount_ = 0;
}
CommandSorter& CommandSorter::operator=(CommandSorter&& other) noexcept {
if (this != &other) {
commands_ = std::move(other.commands_);
sortedIndices_ = std::move(other.sortedIndices_);
commandCount_ = other.commandCount_;
other.commandCount_ = 0;
}
return *this;
}
uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
uint32_t index = commandCount_;
// 如果缓冲区不够大,扩展它
if (index >= commands_.size()) {
commands_.push_back(cmd);
sortedIndices_.push_back(index);
} else {
commands_[index] = cmd;
sortedIndices_[index] = index;
}
commandCount_++;
return index;
}
void CommandSorter::sort() {
if (commandCount_ == 0) return;
// 只排序有效范围的索引
auto indicesBegin = sortedIndices_.begin();
auto indicesEnd = sortedIndices_.begin() + commandCount_;
// 使用稳定排序保持相同键的命令顺序
std::stable_sort(indicesBegin, indicesEnd,
[this](uint32_t a, uint32_t b) {
return commands_[a].key < commands_[b].key;
});
}
void CommandSorter::clear() {
// 重置计数器,保留内存
commandCount_ = 0;
}
void CommandSorter::reserve(uint32_t capacity) {
commands_.reserve(capacity);
sortedIndices_.reserve(capacity);
}
// ========================================
// CommandBatcher 实现
// ========================================
void CommandBatcher::process(const CommandSorter &sorter) {
clear();
sorter_ = &sorter;
if (sorter.getCount() == 0) {
return;
}
CommandBatch currentBatch;
currentBatch.startIndex = 0;
currentBatch.count = 1;
const auto &firstCmd = sorter.getCommand(0);
currentBatch.pipeline = firstCmd.pipeline;
currentBatch.textureCount = firstCmd.textureCount;
for (uint32_t i = 0; i < firstCmd.textureCount; ++i) {
currentBatch.textures[i] = firstCmd.textures[i];
}
// 遍历所有命令,合并兼容的命令
for (uint32_t i = 1; i < sorter.getCount(); ++i) {
const auto &cmd = sorter.getCommand(i);
if (currentBatch.isCompatibleWith(cmd)) {
// 兼容,加入当前批次
currentBatch.count++;
} else {
// 不兼容,保存当前批次并创建新批次
batches_.push_back(currentBatch);
currentBatch.startIndex = i;
currentBatch.count = 1;
currentBatch.pipeline = cmd.pipeline;
currentBatch.textureCount = cmd.textureCount;
for (uint32_t j = 0; j < cmd.textureCount; ++j) {
currentBatch.textures[j] = cmd.textures[j];
}
}
}
// 保存最后一个批次
batches_.push_back(currentBatch);
}
const DrawCommand &CommandBatcher::getCommand(uint32_t batchIndex,
uint32_t cmdIndex) const {
const auto &batch = batches_[batchIndex];
return sorter_->getCommand(batch.startIndex + cmdIndex);
}
void CommandBatcher::clear() {
batches_.clear();
sorter_ = nullptr;
}
// ========================================
// CommandQueue 实现
// ========================================
CommandQueue::CommandQueue() = default;
CommandQueue::~CommandQueue() { shutdown(); }
CommandQueue::CommandQueue(CommandQueue &&other) noexcept
: sorter_(std::move(other.sorter_)), batcher_(std::move(other.batcher_)),
context_(other.context_), commandList_(std::move(other.commandList_)),
uboManager_(std::move(other.uboManager_)),
globalUBOData_(other.globalUBOData_),
materialUBOData_(std::move(other.materialUBOData_)),
nextMaterialId_(other.nextMaterialId_),
materialIds_(std::move(other.materialIds_)),
currentMaterialUBO_(other.currentMaterialUBO_),
materialUBOBufferOffset_(other.materialUBOBufferOffset_) {
other.context_ = nullptr;
other.globalUBOData_ = {};
other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.materialUBOBufferOffset_ = 0;
}
CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept {
if (this != &other) {
shutdown();
sorter_ = std::move(other.sorter_);
batcher_ = std::move(other.batcher_);
context_ = other.context_;
commandList_ = std::move(other.commandList_);
uboManager_ = std::move(other.uboManager_);
globalUBOData_ = other.globalUBOData_;
materialUBOData_ = std::move(other.materialUBOData_);
nextMaterialId_ = other.nextMaterialId_;
materialIds_ = std::move(other.materialIds_);
currentMaterialUBO_ = other.currentMaterialUBO_;
materialUBOBufferOffset_ = other.materialUBOBufferOffset_;
other.context_ = nullptr;
other.globalUBOData_ = {};
other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.materialUBOBufferOffset_ = 0;
}
return *this;
}
bool CommandQueue::initialize() {
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
context_ = rhiModule->getContext();
if (!context_) {
E2D_LOG_ERROR("RHIContext not available");
return false;
}
// 创建命令列表
commandList_ = device->createCommandList();
if (!commandList_) {
E2D_LOG_ERROR("Failed to create command list");
return false;
}
// 初始化 UBO 管理器
uboManager_ = std::make_unique<UniformBufferManager>();
if (!uboManager_->initialize()) {
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
return false;
}
// 预分配材质 UBO 数据缓冲区
materialUBOData_.reserve(1024 * 1024); // 1MB
E2D_LOG_INFO("CommandQueue initialized");
return true;
}
void CommandQueue::shutdown() {
uboManager_.reset();
commandList_.reset();
context_ = nullptr;
materialUBOData_.clear();
E2D_LOG_INFO("CommandQueue shutdown");
}
void CommandQueue::beginFrame() {
sorter_.clear();
batcher_.clear();
materialIds_.clear();
nextMaterialId_ = 1;
// 重置材质 UBO 缓冲区(保留容量,避免重新分配)
materialUBOBufferOffset_ = 0;
currentMaterialUBO_ = nullptr;
// 重置 UBO 管理器的材质 UBO 池
if (uboManager_) {
uboManager_->resetMaterialUBOs();
}
// 重置统计
stats_ = {};
// 开始录制命令
if (commandList_) {
commandList_->begin();
}
}
void CommandQueue::endFrame() {
// 结束录制
if (commandList_) {
commandList_->end();
}
}
uint32_t CommandQueue::getMaterialId(Material *material) {
auto it = materialIds_.find(material);
if (it != materialIds_.end()) {
return it->second;
}
uint32_t id = nextMaterialId_++;
materialIds_[material] = id;
return id;
}
std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t size) {
if (!uboManager_ || size == 0) {
return {nullptr, 0};
}
// OpenGL 要求 UBO 偏移对齐到 GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT (通常是 256)
static constexpr uint32_t UBO_ALIGNMENT = 256;
// 使用批量更新机制:先分配到 CPU 缓冲区
uint32_t offset = materialUBOBufferOffset_;
// 对齐到 UBO_ALIGNMENT 字节
offset = (offset + UBO_ALIGNMENT - 1) & ~(UBO_ALIGNMENT - 1);
uint32_t alignedSize = (size + UBO_ALIGNMENT - 1) & ~(UBO_ALIGNMENT - 1);
// 检查 CPU 缓冲区是否有足够空间
if (offset + alignedSize > materialUBOData_.capacity()) {
// 如果缓冲区已满,先刷新到 GPU
flushMaterialUBOToGPU();
offset = 0; // 重置偏移量
}
// 确保缓冲区足够大
if (offset + alignedSize > materialUBOData_.size()) {
materialUBOData_.resize(offset + alignedSize);
}
materialUBOBufferOffset_ = offset + alignedSize;
return {nullptr, offset}; // 返回 nullptr 表示使用 CPU 缓冲区offset 是 CPU 缓冲区偏移
}
void CommandQueue::flushMaterialUBOToGPU() {
if (materialUBOBufferOffset_ == 0) {
return;
}
// 获取一个足够大的 UBO
currentMaterialUBO_ = uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
if (!currentMaterialUBO_) {
E2D_LOG_ERROR("Failed to acquire material UBO for flush");
return;
}
// 批量更新到 GPU
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_, 0);
// 重置 CPU 缓冲区偏移
materialUBOBufferOffset_ = 0;
}
UniformBuffer* CommandQueue::getCurrentMaterialUBO() const {
return currentMaterialUBO_;
}
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
const struct Transform &transform,
const Color &color) {
if (!material || !mesh || !material->getShader()) {
return;
}
DrawCommand cmd;
// 构建排序键
uint32_t materialId = getMaterialId(material.get());
cmd.key = DrawKey::make(materialId, 0, 0);
// 设置管线
cmd.pipeline = material->getPipeline();
// 设置网格数据
cmd.vertexBuffer = mesh->getVertexBuffer();
cmd.indexBuffer = mesh->getIndexBuffer();
cmd.vertexCount = mesh->getVertexCount();
cmd.indexCount = mesh->getIndexCount();
// 设置变换和颜色
float matrixData[16];
transform.toMatrix(matrixData);
cmd.modelMatrix = glm::make_mat4(matrixData);
cmd.color = color;
// 设置纹理
const auto &textures = material->getTextures();
cmd.textureCount =
static_cast<uint32_t>(std::min(textures.size(), size_t(8)));
for (uint32_t i = 0; i < cmd.textureCount; ++i) {
if (textures[i].texture) {
cmd.textures[i] = textures[i].texture->getHandle();
}
}
// 分配材质 UBO 空间(使用 CPU 缓冲区)
uint32_t materialDataSize = material->getDataSize();
if (materialDataSize > 0) {
auto [ubo, offset] = allocateMaterialUBO(materialDataSize);
(void)ubo; // ubo 为 nullptr使用 CPU 缓冲区
// 复制材质数据到 CPU 缓冲区,并修改颜色
if (offset + materialDataSize <= materialUBOData_.size()) {
std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize);
// 将实例颜色应用到 UBO 数据中的 uColor 参数
auto layout = material->getLayout();
if (layout) {
const auto* param = layout->getParam("uColor");
if (param && param->type == MaterialParamType::Color) {
std::memcpy(materialUBOData_.data() + offset + param->offset, &color.r, sizeof(float) * 4);
}
}
cmd.materialUBOOffset = offset;
cmd.materialUBOSize = materialDataSize;
}
}
sorter_.addCommand(cmd);
}
void CommandQueue::submitClear(const Color &color, uint32_t flags) {
// 清除命令直接执行,不加入排序队列
if (!commandList_) {
return;
}
commandList_->clear(static_cast<ClearFlags>(flags), color, 1.0f, 0);
}
void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
int32_t height) {
if (!commandList_) {
return;
}
Viewport viewport;
viewport.x = static_cast<float>(x);
viewport.y = static_cast<float>(y);
viewport.width = static_cast<float>(width);
viewport.height = static_cast<float>(height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
commandList_->setViewport(viewport);
}
void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex) {
if (!uboManager_) {
E2D_LOG_WARN("CommandQueue::updateGlobalUBO: uboManager is null");
return;
}
// 填充全局 UBO 数据
std::memcpy(globalUBOData_.viewProjection, glm::value_ptr(viewProjection), sizeof(float) * 16);
globalUBOData_.cameraPosition[0] = 0.0f;
globalUBOData_.cameraPosition[1] = 0.0f;
globalUBOData_.cameraPosition[2] = 0.0f;
globalUBOData_.cameraPosition[3] = 1.0f;
globalUBOData_.time = 0.0f; // TODO: 传递实际时间
globalUBOData_.deltaTime = deltaTime;
globalUBOData_.screenSize[0] = static_cast<float>(screenWidth);
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
// 使用双缓冲更新 UBO
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_), frameIndex);
}
void CommandQueue::execute(uint32_t frameIndex) {
if (!commandList_) {
E2D_LOG_ERROR("CommandQueue::execute: commandList is null");
return;
}
// 在排序前刷新材质 UBO 数据到 GPU
flushMaterialUBOToGPU();
// 排序命令
sorter_.sort();
// 批处理
batcher_.process(sorter_);
// 执行批次
for (uint32_t i = 0; i < batcher_.getBatchCount(); ++i) {
executeBatch(i, batcher_.getBatch(i), frameIndex);
}
// 提交命令到 GPU
commandList_->submit();
}
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch, uint32_t frameIndex) {
if (!commandList_) {
return;
}
// 绑定管线
if (batch.pipeline.isValid()) {
commandList_->setPipeline(batch.pipeline.get());
stats_.pipelineBinds++;
} else {
E2D_LOG_WARN("Batch has no valid pipeline!");
}
// 绑定全局 UBO (binding = 0) - 使用双缓冲
if (uboManager_) {
UniformBuffer* globalUBO = uboManager_->getGlobalUBO(frameIndex);
if (globalUBO) {
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
stats_.bufferBinds++;
}
}
// 绑定纹理
if (batch.textureCount > 0) {
for (uint32_t i = 0; i < batch.textureCount; ++i) {
if (batch.textures[i].isValid()) {
commandList_->setTexture(i, batch.textures[i].get());
stats_.textureBinds++;
}
}
}
// 执行批次中的所有命令
for (uint32_t i = 0; i < batch.count; ++i) {
const auto &cmd = batcher_.getCommand(batchIndex, i);
// 绑定顶点缓冲区
if (cmd.vertexBuffer.isValid()) {
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
stats_.bufferBinds++;
} else {
E2D_LOG_WARN("Draw command has no valid vertex buffer!");
}
// 绑定索引缓冲区(如果有)
if (cmd.isIndexed() && cmd.indexBuffer.isValid()) {
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
stats_.bufferBinds++;
}
// 绑定材质 UBO (binding = 1)
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(), cmd.materialUBOOffset, cmd.materialUBOSize);
stats_.bufferBinds++;
}
// 设置模型矩阵
commandList_->setUniform("uModelMatrix", cmd.modelMatrix);
// 绘制
if (cmd.isIndexed()) {
commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0);
stats_.drawCalls++;
stats_.triangles += cmd.indexCount / 3;
} else {
commandList_->draw(cmd.vertexCount, 0, 1, 0);
stats_.drawCalls++;
stats_.triangles += cmd.vertexCount / 3;
}
stats_.vertices += cmd.vertexCount;
}
}
} // namespace extra2d

View File

@ -0,0 +1,358 @@
#include <renderer/instance_buffer.h>
#include <renderer/rhi_module.h>
#include <renderer/rhi/rhi_device.h>
#include <utils/logger.h>
#include <cstring>
#include <algorithm>
namespace extra2d {
// ========================================
// InstanceBuffer 实现
// ========================================
InstanceBuffer::InstanceBuffer() = default;
InstanceBuffer::~InstanceBuffer() {
shutdown();
}
InstanceBuffer::InstanceBuffer(InstanceBuffer&& other) noexcept
: bufferHandle_(std::move(other.bufferHandle_))
, maxInstances_(other.maxInstances_)
, instanceCount_(other.instanceCount_)
, cpuBuffer_(std::move(other.cpuBuffer_))
, dirtyRanges_(std::move(other.dirtyRanges_)) {
other.maxInstances_ = 0;
other.instanceCount_ = 0;
}
InstanceBuffer& InstanceBuffer::operator=(InstanceBuffer&& other) noexcept {
if (this != &other) {
shutdown();
bufferHandle_ = std::move(other.bufferHandle_);
maxInstances_ = other.maxInstances_;
instanceCount_ = other.instanceCount_;
cpuBuffer_ = std::move(other.cpuBuffer_);
dirtyRanges_ = std::move(other.dirtyRanges_);
other.maxInstances_ = 0;
other.instanceCount_ = 0;
}
return *this;
}
bool InstanceBuffer::initialize(uint32_t maxInstances) {
shutdown();
if (maxInstances == 0) {
E2D_LOG_ERROR("InstanceBuffer::initialize: maxInstances must be > 0");
return false;
}
auto* rhiModule = RHIModule::get();
if (!rhiModule || !rhiModule->getDevice()) {
E2D_LOG_ERROR("InstanceBuffer::initialize: RHIModule not available");
return false;
}
auto* device = rhiModule->getDevice();
// 创建实例数据缓冲区
BufferDesc desc;
desc.type = BufferType::Vertex;
desc.usage = BufferUsage::Dynamic;
desc.size = maxInstances * sizeof(InstanceData);
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("InstanceBuffer::initialize: Failed to create buffer");
return false;
}
bufferHandle_ = BufferHandle(buffer.release());
maxInstances_ = maxInstances;
instanceCount_ = 0;
cpuBuffer_.reserve(maxInstances);
dirtyRanges_.clear();
dirtyRanges_.reserve(MAX_DIRTY_RANGES);
E2D_LOG_DEBUG("InstanceBuffer initialized with capacity for {} instances", maxInstances);
return true;
}
void InstanceBuffer::shutdown() {
bufferHandle_ = BufferHandle();
maxInstances_ = 0;
instanceCount_ = 0;
cpuBuffer_.clear();
cpuBuffer_.shrink_to_fit();
dirtyRanges_.clear();
dirtyRanges_.shrink_to_fit();
}
bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t count) {
if (!bufferHandle_.isValid() || !instances) {
return false;
}
if (count > maxInstances_) {
E2D_LOG_WARN("InstanceBuffer::updateInstances: count {} exceeds maxInstances {}",
count, maxInstances_);
count = maxInstances_;
}
// 复制到CPU缓冲区
if (count > 0) {
if (cpuBuffer_.size() < count) {
cpuBuffer_.resize(count);
}
std::memcpy(cpuBuffer_.data(), instances, count * sizeof(InstanceData));
}
// 标记整个范围为脏
markAllDirty();
// 立即上传到GPU
bool result = updateGPU();
instanceCount_ = count;
return result;
}
bool InstanceBuffer::updateInstancesRange(const InstanceData* instances, uint32_t start, uint32_t count) {
if (!bufferHandle_.isValid() || !instances || count == 0) {
return false;
}
if (start >= maxInstances_) {
E2D_LOG_WARN("InstanceBuffer::updateInstancesRange: start {} exceeds maxInstances {}",
start, maxInstances_);
return false;
}
if (start + count > maxInstances_) {
E2D_LOG_WARN("InstanceBuffer::updateInstancesRange: range {}-{} exceeds maxInstances {}",
start, start + count, maxInstances_);
count = maxInstances_ - start;
}
// 确保CPU缓冲区足够大
if (cpuBuffer_.size() < start + count) {
cpuBuffer_.resize(start + count);
}
// 复制到CPU缓冲区
std::memcpy(cpuBuffer_.data() + start, instances, count * sizeof(InstanceData));
// 标记为脏
markDirty(start, count);
// 更新实例数量
if (start + count > instanceCount_) {
instanceCount_ = start + count;
}
return true;
}
uint32_t InstanceBuffer::addInstance(const InstanceData& instance) {
if (instanceCount_ >= maxInstances_) {
E2D_LOG_WARN("InstanceBuffer::addInstance: buffer full (max {})", maxInstances_);
return UINT32_MAX;
}
uint32_t index = instanceCount_++;
// 添加到 CPU 缓冲区
if (index >= cpuBuffer_.size()) {
cpuBuffer_.push_back(instance);
} else {
cpuBuffer_[index] = instance;
}
// 标记为脏(单个实例)
markDirty(index, 1);
return index;
}
void InstanceBuffer::markDirty(uint32_t start, uint32_t count) {
if (count == 0 || start >= maxInstances_) {
return;
}
// 限制范围
if (start + count > maxInstances_) {
count = maxInstances_ - start;
}
addDirtyRange(DirtyRange(start, count));
}
void InstanceBuffer::markAllDirty() {
dirtyRanges_.clear();
if (instanceCount_ > 0) {
dirtyRanges_.push_back(DirtyRange(0, instanceCount_));
}
}
bool InstanceBuffer::updateGPU() {
if (!bufferHandle_.isValid() || dirtyRanges_.empty()) {
return true; // 没有脏数据,无需更新
}
RHIBuffer* rhiBuffer = bufferHandle_.get();
if (!rhiBuffer) {
return false;
}
// 如果脏区域太多,合并为全量更新
if (dirtyRanges_.size() > MAX_DIRTY_RANGES) {
mergeAllDirtyRanges();
}
// 上传所有脏区域
for (const auto& range : dirtyRanges_) {
if (!range.isValid()) continue;
const uint8_t* data = reinterpret_cast<const uint8_t*>(cpuBuffer_.data());
uint32_t offset = range.start * sizeof(InstanceData);
uint32_t size = range.count * sizeof(InstanceData);
rhiBuffer->update(data + offset, size, offset);
}
// 清空脏区域列表
clearDirtyRanges();
return true;
}
void InstanceBuffer::clear() {
instanceCount_ = 0;
cpuBuffer_.clear();
clearDirtyRanges();
}
void InstanceBuffer::addDirtyRange(const DirtyRange& range) {
if (!range.isValid()) return;
// 检查是否可以与现有区域合并
for (auto& existing : dirtyRanges_) {
if (existing.canMerge(range)) {
existing.merge(range);
return;
}
}
// 添加新区域
dirtyRanges_.push_back(range);
// 如果脏区域太多,合并所有区域
if (dirtyRanges_.size() > MAX_DIRTY_RANGES) {
mergeAllDirtyRanges();
}
}
void InstanceBuffer::mergeAllDirtyRanges() {
if (dirtyRanges_.empty()) return;
// 找到最小起始和最大结束
uint32_t minStart = dirtyRanges_[0].start;
uint32_t maxEnd = dirtyRanges_[0].end();
for (const auto& range : dirtyRanges_) {
if (range.start < minStart) minStart = range.start;
if (range.end() > maxEnd) maxEnd = range.end();
}
dirtyRanges_.clear();
dirtyRanges_.push_back(DirtyRange(minStart, maxEnd - minStart));
}
void InstanceBuffer::clearDirtyRanges() {
dirtyRanges_.clear();
}
// ========================================
// InstanceBufferManager 实现
// ========================================
InstanceBufferManager::InstanceBufferManager() = default;
InstanceBufferManager::~InstanceBufferManager() {
shutdown();
}
InstanceBufferManager::InstanceBufferManager(InstanceBufferManager&& other) noexcept
: bufferPool_(std::move(other.bufferPool_))
, currentBufferIndex_(other.currentBufferIndex_) {
other.currentBufferIndex_ = 0;
}
InstanceBufferManager& InstanceBufferManager::operator=(InstanceBufferManager&& other) noexcept {
if (this != &other) {
shutdown();
bufferPool_ = std::move(other.bufferPool_);
currentBufferIndex_ = other.currentBufferIndex_;
other.currentBufferIndex_ = 0;
}
return *this;
}
bool InstanceBufferManager::initialize() {
// 预分配一些缓冲区
bufferPool_.reserve(4);
E2D_LOG_INFO("InstanceBufferManager initialized");
return true;
}
void InstanceBufferManager::shutdown() {
bufferPool_.clear();
currentBufferIndex_ = 0;
}
InstanceBuffer* InstanceBufferManager::acquireBuffer(uint32_t minSize) {
// 查找现有缓冲区
for (uint32_t i = currentBufferIndex_; i < bufferPool_.size(); ++i) {
if (bufferPool_[i]->getMaxInstances() >= minSize) {
currentBufferIndex_ = i + 1;
bufferPool_[i]->clear();
return bufferPool_[i].get();
}
}
// 创建新缓冲区
auto buffer = std::make_unique<InstanceBuffer>();
uint32_t allocSize = std::max(minSize, DEFAULT_BUFFER_SIZE);
if (!buffer->initialize(allocSize)) {
E2D_LOG_ERROR("InstanceBufferManager::acquireBuffer: Failed to create buffer");
return nullptr;
}
InstanceBuffer* ptr = buffer.get();
bufferPool_.push_back(std::move(buffer));
currentBufferIndex_ = bufferPool_.size();
return ptr;
}
void InstanceBufferManager::releaseBuffer(InstanceBuffer* buffer) {
// 简单实现:不清除缓冲区,只是重置索引
// 实际实现可能需要更复杂的池管理
(void)buffer;
}
void InstanceBufferManager::reset() {
currentBufferIndex_ = 0;
for (auto& buffer : bufferPool_) {
buffer->clear();
}
}
} // namespace extra2d

View File

@ -21,12 +21,20 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
return;
}
// 检查是否已存在
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
E2D_LOG_WARN("Param '{}' already exists in MaterialLayout", name);
return;
}
MaterialParamInfo info;
info.type = type;
info.size = getMaterialParamSize(type);
info.offset = 0; // 将在 finalize 时计算
params_[name] = info;
size_t index = params_.size();
params_.emplace_back(name, info);
paramIndexMap_[name] = index;
}
/**
@ -36,6 +44,7 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
* -
* - 16
* - vec3 16
* - 16
*/
void MaterialLayout::finalize() {
if (finalized_) return;
@ -44,7 +53,7 @@ void MaterialLayout::finalize() {
for (auto& pair : params_) {
auto& info = pair.second;
// 计算对齐要求
// 计算对齐要求 - std140规则
uint32_t alignment = 4; // 默认 4 字节对齐
switch (info.type) {
@ -60,21 +69,21 @@ void MaterialLayout::finalize() {
alignment = 16; // vec3/vec4/color: 16 字节对齐std140 规则)
break;
case MaterialParamType::Mat4:
alignment = 16; // mat4: 16 字节对齐
alignment = 16; // mat4: 16 字节对齐每列vec4对齐到16字节
break;
default:
alignment = 4;
break;
}
// 对齐偏移
// 对齐偏移到alignment的倍数
offset = (offset + alignment - 1) & ~(alignment - 1);
info.offset = offset;
offset += info.size;
}
// 最终大小对齐到 16 字节
// 最终大小对齐到 16 字节std140要求结构体总大小是16的倍数
bufferSize_ = (offset + 15) & ~15;
finalized_ = true;
}
@ -85,9 +94,9 @@ void MaterialLayout::finalize() {
* @return nullptr
*/
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
auto it = params_.find(name);
if (it != params_.end()) {
return &it->second;
auto mapIt = paramIndexMap_.find(name);
if (mapIt != paramIndexMap_.end()) {
return &params_[mapIt->second].second;
}
return nullptr;
}
@ -117,6 +126,17 @@ void Material::setShader(Ptr<Shader> shader) {
shader_ = shader;
}
/**
* @brief RHI 线
* @return RHI 线
*/
PipelineHandle Material::getPipeline() const {
if (shader_) {
return shader_->getPipeline();
}
return PipelineHandle{};
}
/**
* @brief float
* @param name
@ -194,14 +214,14 @@ void Material::setMat4(const std::string& name, const float* value) {
/**
* @brief
* @param uniformName uniform
* @param texture
* @param texture
* @param slot 0-15
*/
void Material::setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot) {
void Material::setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot) {
// 查找是否已存在相同名称的纹理
for (auto& texSlot : textures_) {
if (texSlot.uniformName == uniformName) {
texSlot.handle = texture;
texSlot.texture = texture;
texSlot.slot = slot;
return;
}
@ -218,31 +238,4 @@ void Material::clearTextures() {
textures_.clear();
}
/**
* @brief
*
* UBO
* @param uboManager UBO
*/
void Material::apply(UniformBufferManager& uboManager) {
if (!shader_) return;
// 绑定着色器
shader_->bind();
// 设置 Uniform Block 绑定
shader_->setUniformBlock("GlobalUBO", GLOBAL_UBO_BINDING);
shader_->setUniformBlock("MaterialUBO", MATERIAL_UBO_BINDING);
shader_->setUniformBlock("InstanceUBO", INSTANCE_UBO_BINDING);
// 上传材质数据到 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);
}
}
}
} // namespace extra2d

View File

@ -1,171 +1,165 @@
#include <renderer/mesh.h>
#include <renderer/rhi_module.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;
}
// BufferHandle 是轻量级句柄,不需要显式释放
// 实际的缓冲区资源由 RHI 设备管理
vertexBuffer_ = BufferHandle();
indexBuffer_ = BufferHandle();
}
void Mesh::setVertices(const Vertex* vertices, uint32_t count) {
if (vao_ == 0) {
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &vbo_);
E2D_LOG_INFO("Created VAO: {}, VBO: {}", vao_, vbo_);
void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
if (!vertices || count == 0)
return;
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return;
}
uint32_t dataSize = count * sizeof(Vertex);
// 如果缓冲区已存在且容量足够,更新数据
if (vertexBuffer_.isValid() && vertexCapacity_ >= count) {
// 通过句柄获取缓冲区指针并更新数据
// 注意:这里假设可以通过某种方式获取缓冲区指针
// 实际实现可能需要通过 RHI 命令列表来更新
E2D_LOG_WARN("Vertex buffer update not implemented yet");
} else {
// 需要创建新缓冲区
vertexBuffer_ = BufferHandle();
// 创建顶点缓冲区描述
BufferDesc desc = BufferDesc::vertex(dataSize, BufferUsage::Dynamic);
desc.initialData = vertices;
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("Failed to create vertex buffer");
return;
}
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;
E2D_LOG_INFO("Set {} vertices for VAO {}", count, vao_);
// 获取缓冲区的句柄
// 注意createBuffer 返回的是 unique_ptr我们需要存储句柄
// 这里直接使用缓冲区指针作为句柄,然后释放所有权让 RHI 内部管理
vertexBuffer_ = BufferHandle(buffer.release());
vertexCapacity_ = count;
}
vertexCount_ = count;
E2D_LOG_INFO("Set {} vertices", count);
}
void Mesh::setIndices(const uint16_t* indices, uint32_t count) {
if (vao_ == 0) return;
if (ibo_ == 0) {
glGenBuffers(1, &ibo_);
void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
if (!indices || count == 0)
return;
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return;
}
uint32_t dataSize = count * sizeof(uint16_t);
// 如果缓冲区已存在且容量足够,更新数据
if (indexBuffer_.isValid() && indexCapacity_ >= count) {
E2D_LOG_WARN("Index buffer update not implemented yet");
} else {
// 需要创建新缓冲区
indexBuffer_ = BufferHandle();
// 创建索引缓冲区描述
BufferDesc desc = BufferDesc::index(dataSize, BufferUsage::Static);
desc.initialData = indices;
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("Failed to create index buffer");
return;
}
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;
// 获取缓冲区的句柄
indexBuffer_ = BufferHandle(buffer.release());
indexCapacity_ = count;
}
indexCount_ = count;
E2D_LOG_INFO("Set {} indices", 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::updateVertices(const Vertex *vertices, uint32_t count,
uint32_t offset) {
if (!vertices || count == 0 || !vertexBuffer_.isValid())
return;
if (offset + count > vertexCount_) {
E2D_LOG_WARN("Vertex update out of bounds");
return;
}
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule)
return;
auto *device = rhiModule->getDevice();
if (!device)
return;
// 通过句柄获取缓冲区并更新数据
// 注意:这里需要 RHI 提供更新缓冲区的方法
// 暂时记录为未实现
E2D_LOG_WARN("updateVertices not fully implemented yet");
}
void Mesh::bind() const {
if (vao_ != 0) {
glBindVertexArray(vao_);
}
}
Ptr<Mesh> Mesh::createQuad(const Vec2 &size, const Rect &uv) {
Ptr<Mesh> mesh = makePtr<Mesh>();
void Mesh::unbind() const {
glBindVertexArray(0);
}
float halfW = size.x * 0.5f;
float halfH = size.y * 0.5f;
void Mesh::draw() const {
if (vao_ == 0) return;
// 检查 OpenGL 错误
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
static bool loggedOnce = false;
if (!loggedOnce) {
E2D_LOG_ERROR("OpenGL error before draw: {}", err);
loggedOnce = true;
}
}
if (indexCount_ > 0) {
glDrawElements(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT, nullptr);
} else {
glDrawArrays(GL_TRIANGLES, 0, vertexCount_);
}
// 检查绘制后的错误
err = glGetError();
if (err != GL_NO_ERROR) {
static bool loggedOnce2 = false;
if (!loggedOnce2) {
E2D_LOG_ERROR("OpenGL error after draw: {}", err);
loggedOnce2 = true;
}
}
}
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} // 左上
};
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);
}
}
uint16_t indices[6] = {
0, 1, 2, // 第一个三角形
0, 2, 3 // 第二个三角形
};
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;
mesh->setVertices(vertices, 4);
mesh->setIndices(indices, 6);
return mesh;
}
} // namespace extra2d

View File

@ -0,0 +1,333 @@
#include <renderer/render_graph.h>
#include <renderer/rhi_module.h>
#include <utils/logger.h>
namespace extra2d {
// ========================================
// RenderPassContext 实现
// ========================================
TextureHandle
RenderPassContext::getTexture(RenderGraphResourceHandle handle) const {
if (graph) {
return graph->getTexture(handle);
}
return TextureHandle{};
}
// ========================================
// RenderGraphBuilder 实现
// ========================================
RenderGraphResourceHandle
RenderGraphBuilder::createTexture(const RenderGraphTextureDesc &desc) {
// 实际的资源创建由 RenderGraph 管理
// 这里只记录描述,返回临时句柄
textureDescs_.push_back(desc);
RenderGraphResourceHandle handle;
handle.index = static_cast<uint32_t>(textureDescs_.size()) - 1;
handle.type = RenderGraphResourceType::Texture;
return handle;
}
void RenderGraphBuilder::readResource(RenderGraphResourceHandle handle) {
// 记录资源读取依赖
// 用于后续的依赖分析和资源生命周期管理
}
void RenderGraphBuilder::writeResource(RenderGraphResourceHandle handle) {
// 记录资源写入依赖
}
void RenderGraphBuilder::setRenderTarget(
const RenderGraphRenderTargetDesc &desc) {
// 设置渲染目标
}
// ========================================
// RenderGraph 实现
// ========================================
RenderGraph::RenderGraph() = default;
RenderGraph::~RenderGraph() { shutdown(); }
RenderGraph::RenderGraph(RenderGraph &&other) noexcept
: passes_(std::move(other.passes_)),
executionOrder_(std::move(other.executionOrder_)),
textures_(std::move(other.textures_)),
nextResourceId_(other.nextResourceId_),
builder_(std::move(other.builder_)),
commandQueue_(std::move(other.commandQueue_)),
outputWidth_(other.outputWidth_), outputHeight_(other.outputHeight_),
frameIndex_(other.frameIndex_), compiled_(other.compiled_) {
other.nextResourceId_ = 1;
other.outputWidth_ = 1280;
other.outputHeight_ = 720;
other.frameIndex_ = 0;
other.compiled_ = false;
}
RenderGraph &RenderGraph::operator=(RenderGraph &&other) noexcept {
if (this != &other) {
shutdown();
passes_ = std::move(other.passes_);
executionOrder_ = std::move(other.executionOrder_);
textures_ = std::move(other.textures_);
nextResourceId_ = other.nextResourceId_;
builder_ = std::move(other.builder_);
commandQueue_ = std::move(other.commandQueue_);
outputWidth_ = other.outputWidth_;
outputHeight_ = other.outputHeight_;
frameIndex_ = other.frameIndex_;
compiled_ = other.compiled_;
other.nextResourceId_ = 1;
other.outputWidth_ = 1280;
other.outputHeight_ = 720;
other.frameIndex_ = 0;
other.compiled_ = false;
}
return *this;
}
bool RenderGraph::initialize() {
if (!commandQueue_.initialize()) {
E2D_LOG_ERROR("Failed to initialize CommandQueue");
return false;
}
E2D_LOG_INFO("RenderGraph initialized");
return true;
}
void RenderGraph::shutdown() {
destroyResources();
passes_.clear();
executionOrder_.clear();
commandQueue_.shutdown();
compiled_ = false;
E2D_LOG_INFO("RenderGraph shutdown");
}
RenderPass *RenderGraph::addPass(std::unique_ptr<RenderPass> pass) {
if (!pass)
return nullptr;
RenderPass *ptr = pass.get();
passes_.push_back(std::move(pass));
compiled_ = false; // 添加新通道需要重新编译
return ptr;
}
RenderPass *RenderGraph::addLambdaPass(
const std::string &name,
std::function<void(const RenderPassContext &)> executeFunc) {
// 创建一个简单的 lambda 渲染通道
class LambdaRenderPass : public RenderPass {
public:
std::string name;
std::function<void(const RenderPassContext &)> func;
LambdaRenderPass(const std::string &n,
std::function<void(const RenderPassContext &)> f)
: name(n), func(f) {}
const char *getName() const override { return name.c_str(); }
void execute(const RenderPassContext &ctx) override {
if (func) {
func(ctx);
}
}
};
return addPass(std::make_unique<LambdaRenderPass>(name, executeFunc));
}
bool RenderGraph::compile() {
if (compiled_)
return true;
// 清空之前的编译结果
executionOrder_.clear();
// 简单的编译策略:按添加顺序执行
// 实际实现中应该进行依赖分析和拓扑排序
for (auto &pass : passes_) {
if (pass->isEnabled()) {
executionOrder_.push_back(pass.get());
}
}
// 声明资源
for (auto *pass : executionOrder_) {
builder_.setCurrentPass(pass);
pass->declareResources(builder_);
}
// 创建资源
createResources();
compiled_ = true;
E2D_LOG_INFO("RenderGraph compiled with {} passes", executionOrder_.size());
return true;
}
void RenderGraph::execute(float deltaTime, const Mat4& viewProjection) {
if (!compiled_) {
if (!compile()) {
E2D_LOG_ERROR("Failed to compile RenderGraph");
return;
}
}
// 更新全局 UBO使用双缓冲
commandQueue_.updateGlobalUBO(viewProjection, deltaTime, outputWidth_,
outputHeight_, frameIndex_);
// 获取 RHI 上下文
auto *rhiModule = RHIModule::get();
RHIContext *context = nullptr;
if (rhiModule && rhiModule->getDevice()) {
context = rhiModule->getDevice()->getContext();
}
// 创建执行上下文
RenderPassContext ctx;
ctx.context = context;
ctx.commandQueue = &commandQueue_;
ctx.graph = this;
ctx.deltaTime = deltaTime;
ctx.frameIndex = frameIndex_;
// 执行所有通道
for (auto *pass : executionOrder_) {
if (pass->isEnabled()) {
pass->execute(ctx);
}
}
// 执行命令队列(传递帧索引用于双缓冲)
commandQueue_.execute(frameIndex_);
// 结束帧
commandQueue_.endFrame();
// 调用 RHI 上下文的 endFrame 进行缓冲区交换
if (context) {
context->endFrame();
}
frameIndex_++;
}
TextureHandle RenderGraph::getTexture(RenderGraphResourceHandle handle) const {
auto it = textures_.find(handle.index);
if (it != textures_.end()) {
return it->second;
}
return TextureHandle{};
}
void RenderGraph::setOutputSize(uint32_t width, uint32_t height) {
outputWidth_ = width;
outputHeight_ = height;
}
void RenderGraph::createResources() {
auto *rhiModule = RHIModule::get();
if (!rhiModule || !rhiModule->getDevice())
return;
auto *device = rhiModule->getDevice();
// 创建渲染图内部资源
// 这里可以根据需要创建默认的后缓冲等
}
void RenderGraph::destroyResources() {
// 释放所有渲染图管理的资源
textures_.clear();
}
// ========================================
// GeometryRenderPass 实现
// ========================================
GeometryRenderPass::GeometryRenderPass(const std::string &name) : name_(name) {}
void GeometryRenderPass::declareResources(RenderGraphBuilder &builder) {
// 声明几何渲染通道的资源依赖
if (colorTarget_.isValid()) {
builder.writeResource(colorTarget_);
}
if (depthTarget_.isValid()) {
builder.writeResource(depthTarget_);
}
}
void GeometryRenderPass::execute(const RenderPassContext &ctx) {
// 几何渲染通道的执行逻辑
// 通常由外部的渲染系统填充具体的绘制命令
if (!ctx.commandQueue)
return;
// 清除颜色缓冲区
ctx.commandQueue->submitClear(Color::Black, CLEAR_COLOR_FLAG);
// 设置视口
if (ctx.graph) {
ctx.commandQueue->setViewport(
0, 0, static_cast<int32_t>(ctx.graph->getOutputWidth()),
static_cast<int32_t>(ctx.graph->getOutputHeight()));
}
}
// ========================================
// PostProcessRenderPass 实现
// ========================================
PostProcessRenderPass::PostProcessRenderPass(const std::string &name)
: name_(name) {}
void PostProcessRenderPass::declareResources(RenderGraphBuilder &builder) {
// 声明后期处理通道的资源依赖
if (inputTexture_.isValid()) {
builder.readResource(inputTexture_);
}
if (outputTarget_.isValid()) {
builder.writeResource(outputTarget_);
}
}
void PostProcessRenderPass::execute(const RenderPassContext &ctx) {
// 后期处理通道的执行逻辑
// 这里可以实现全屏四边形绘制等后期处理效果
if (!ctx.context || !ctx.commandQueue)
return;
// 获取命令列表
RHICommandList *cmdList = ctx.commandQueue->getCommandList();
if (!cmdList)
return;
// 绑定输入纹理
if (inputTexture_.isValid()) {
TextureHandle tex = ctx.getTexture(inputTexture_);
if (tex.isValid()) {
cmdList->setTexture(0, tex.get());
}
}
// 后期处理的具体实现由子类或外部提供
}
} // namespace extra2d

View File

@ -1,82 +1,20 @@
#include "glad/glad.h"
#include <algorithm>
#include <assets/assets_module.h>
#include <event/events.h>
#include <glm/gtc/type_ptr.hpp>
#include <platform/window_module.h>
#include <renderer/render_graph.h>
#include <renderer/renderer_module.h>
#include <renderer/rhi_module.h>
#include <utils/logger.h>
#include <SDL.h>
namespace extra2d {
RendererModule::RendererModule() = default;
RendererModule::~RendererModule() = default;
RendererModule::RendererModule(RendererModule &&other) noexcept
: commandBuffer_(std::move(other.commandBuffer_)),
commandCount_(other.commandCount_),
uniformManager_(std::move(other.uniformManager_)),
onRenderBeginListener_(std::move(other.onRenderBeginListener_)),
onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)),
onRenderSetCameraListener_(std::move(other.onRenderSetCameraListener_)),
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_),
viewportAdapter_(std::move(other.viewportAdapter_)),
viewProjectionMatrix_(std::move(other.viewProjectionMatrix_)) {
other.commandCount_ = 0;
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_) {
uniformManager_.shutdown();
}
commandBuffer_ = std::move(other.commandBuffer_);
commandCount_ = other.commandCount_;
uniformManager_ = std::move(other.uniformManager_);
onRenderBeginListener_ = std::move(other.onRenderBeginListener_);
onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_);
onRenderSetCameraListener_ = std::move(other.onRenderSetCameraListener_);
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_;
viewportAdapter_ = std::move(other.viewportAdapter_);
viewProjectionMatrix_ = std::move(other.viewProjectionMatrix_);
other.commandCount_ = 0;
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); });
@ -84,69 +22,92 @@ bool RendererModule::init() {
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
onRenderEndListener_.bind([this]() { onRenderEnd(); });
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
onShowListener_.bind([this]() { onWindowShow(); });
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
E2D_LOG_INFO("RendererModule initialized (waiting for window show)");
return true;
}
void RendererModule::onWindowShow() {
if (glInitialized_) {
if (initialized_) {
return;
}
E2D_LOG_INFO("Initializing OpenGL context...");
E2D_LOG_INFO("Initializing RendererModule context...");
if (!uniformManager_.initialize()) {
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
// 初始化 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return;
}
int windowWidth = 800, windowHeight = 600;
SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow();
if (sdlWindow) {
SDL_GetWindowSize(sdlWindow, &windowWidth, &windowHeight);
E2D_LOG_INFO("Setting initial viewport to window size: {}x{}", windowWidth,
windowHeight);
} else {
E2D_LOG_WARN("Could not get SDL window, using default viewport 800x600");
if (!rhiModule->getDevice()) {
// RHI 设备尚未初始化,这是正常的时序问题
// RHIModule 会在窗口显示后初始化设备,然后再次触发此函数
E2D_LOG_INFO("RHIDevice not ready yet, waiting for RHI initialization...");
return;
}
setViewport(0, 0, static_cast<int32>(windowWidth),
static_cast<int32>(windowHeight));
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 初始化渲染图
if (!renderGraph_.initialize()) {
E2D_LOG_ERROR("Failed to initialize RenderGraph");
return;
}
glInitialized_ = true;
E2D_LOG_INFO("OpenGL context initialized successfully");
commandQueue_ = renderGraph_.getCommandQueue();
// 添加默认的几何渲染通道
auto geometryPass = std::make_unique<GeometryRenderPass>("Geometry");
renderGraph_.addPass(std::move(geometryPass));
E2D_LOG_INFO("Added default geometry render pass");
auto windowModule = getModule<WindowModule>();
if (!windowModule) {
E2D_LOG_ERROR("WindowModule not available");
return;
}
setViewport(0, 0, static_cast<int32>(windowModule->getSize().w),
static_cast<int32>(windowModule->getSize().h));
initialized_ = true;
E2D_LOG_INFO("RendererModule context initialized successfully");
}
void RendererModule::shutdown() {
E2D_LOG_INFO("Shutting down RendererModule...");
if (glInitialized_) {
uniformManager_.shutdown();
}
renderGraph_.shutdown();
commandQueue_ = nullptr;
initialized_ = false;
glInitialized_ = false;
// 注意:事件监听器是值类型,会在析构时自动清理
E2D_LOG_INFO("RendererModule shutdown complete");
}
MaterialHandle RendererModule::getDefaultMaterialHandle() const {
auto* assets = getAssets();
return assets ? assets->getDefaultMaterial() : MaterialHandle::invalid();
RHIContext *RendererModule::getRHIContext() const {
auto *rhiModule = RHIModule::get();
if (rhiModule && rhiModule->getDevice()) {
return rhiModule->getDevice()->getContext();
}
return nullptr;
}
MeshHandle RendererModule::getDefaultQuadHandle() const {
auto* assets = getAssets();
return assets ? assets->getDefaultQuad() : MeshHandle::invalid();
Handle<Material> RendererModule::getDefaultMaterialHandle() const {
auto *assets = getAssets();
return assets ? assets->getDefaultMaterial() : Handle<Material>::invalid();
}
TextureHandle RendererModule::getDefaultTextureHandle() const {
auto* assets = getAssets();
return assets ? assets->getDefaultTexture() : TextureHandle::invalid();
Handle<Mesh> RendererModule::getDefaultQuadHandle() const {
auto *assets = getAssets();
return assets ? assets->getDefaultQuad() : Handle<Mesh>::invalid();
}
Handle<Texture> RendererModule::getDefaultTextureHandle() const {
auto *assets = getAssets();
return assets ? assets->getDefaultTexture() : Handle<Texture>::invalid();
}
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
@ -157,216 +118,146 @@ void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
viewportAdapter_.update(width, height);
auto result = viewportAdapter_.getResult();
glViewport(static_cast<GLint>(result.viewport.x),
static_cast<GLint>(result.viewport.y),
static_cast<GLsizei>(result.viewport.w),
static_cast<GLsizei>(result.viewport.h));
// 更新渲染图输出尺寸
renderGraph_.setOutputSize(static_cast<uint32_t>(width),
static_cast<uint32_t>(height));
// 通过 RHI 设置视口
if (initialized_) {
auto *context = getRHIContext();
if (context) {
auto result = viewportAdapter_.getResult();
context->setViewport(static_cast<int32_t>(result.viewport.x),
static_cast<int32_t>(result.viewport.y),
static_cast<uint32_t>(result.viewport.w),
static_cast<uint32_t>(result.viewport.h));
}
}
}
void RendererModule::clear(const Color &color, uint32 flags) {
GLbitfield mask = 0;
if (!initialized_)
return;
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);
if (commandQueue_) {
commandQueue_->submitClear(color, flags);
}
}
void RendererModule::onRenderBegin() {
if (!glInitialized_) {
if (!initialized_) {
return;
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
commandCount_ = 0;
// 重置统计
stats_ = {};
uniformManager_.resetMaterialUBOs();
// 开始渲染帧 - 清空命令队列准备接收新的渲染命令
if (commandQueue_) {
commandQueue_->beginFrame();
}
// 帧开始
}
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
if (!glInitialized_) {
if (!initialized_ || !commandQueue_) {
E2D_LOG_WARN(
"onRenderSubmit: RendererModule not initialized or no command queue");
return;
}
if (commandCount_ >= MAX_RENDER_COMMANDS) {
E2D_LOG_WARN("Render command buffer full!");
return;
(void)cmd; // 避免未使用警告
// 将渲染命令转换为绘制命令提交到队列
switch (cmd.type) {
case RenderCommandType::DrawMesh: {
auto *assets = getAssets();
if (!assets)
return;
// 获取材质,如果没有指定则使用默认材质
Material *material = assets->get(cmd.drawMesh.material);
if (!material) {
material = assets->get(assets->getDefaultMaterial());
}
// 获取网格,如果没有指定则使用默认四边形
Mesh *mesh = assets->get(cmd.drawMesh.mesh);
if (!mesh) {
mesh = assets->get(assets->getDefaultQuad());
}
if (material && mesh) {
Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale,
cmd.drawMesh.rot);
commandQueue_->submitDraw(Ptr<Material>(material), Ptr<Mesh>(mesh),
transform, cmd.drawMesh.color);
stats_.commandsSubmitted++;
} else {
E2D_LOG_WARN("Failed to submit draw command: material={}, mesh={}",
material ? "valid" : "null", mesh ? "valid" : "null");
}
break;
}
commandBuffer_[commandCount_++] = cmd;
stats_.commandsSubmitted++;
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;
}
}
}
void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
if (!glInitialized_) {
if (!initialized_) {
return;
}
viewProjectionMatrix_ = viewProj;
uniformManager_.updateGlobalUBO(&viewProj, sizeof(Mat4));
// 更新全局 UBO通过 RenderGraph 的命令队列)
// 实际的 UBO 更新会在 RenderGraph 执行时进行
}
void RendererModule::onRenderEnd() {
if (!glInitialized_) {
if (!initialized_) {
E2D_LOG_WARN("onRenderEnd: RendererModule not initialized");
return;
}
sortCommands();
// 执行渲染图,传递视图投影矩阵
renderGraph_.execute(0.016f, viewProjectionMatrix_);
batchAndDraw();
// 获取统计信息
if (commandQueue_) {
const auto& stats = commandQueue_->getStats();
stats_.drawCalls = stats.drawCalls;
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
// 每60帧输出一次统计信息
static uint32_t frameCount = 0;
if (++frameCount % 60 == 0) {
E2D_LOG_INFO("Render Stats: DrawCalls={}, Triangles={}, Vertices={}, PipelineBinds={}, TextureBinds={}, BufferBinds={}",
stats.drawCalls, stats.triangles, stats.vertices,
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
}
}
// 渲染完成
}
void RendererModule::onResize(int32 width, int32 height) {
setViewport(0, 0, width, height);
}
void RendererModule::sortCommands() {
std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_,
[](const RenderCommand &a, const RenderCommand &b) {
return a.sortKey < b.sortKey;
});
}
void RendererModule::batchAndDraw() {
auto* assets = getAssets();
if (!assets) {
E2D_LOG_ERROR("AssetsModule not available");
return;
}
MaterialHandle lastMaterial = MaterialHandle::invalid();
MeshHandle lastMesh = MeshHandle::invalid();
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* assets = getAssets();
if (!assets) return;
Material* material = assets->get(materialHandle);
if (!material) {
material = assets->getDefaultMaterialPtr();
}
Mesh* mesh = assets->get(meshHandle);
if (!mesh) {
mesh = assets->getDefaultQuadPtr();
}
if (!material || !mesh)
return;
Shader* shader = material->getShader().get();
if (!shader)
return;
shader->bind();
shader->setMat4("uViewProjection", glm::value_ptr(viewProjectionMatrix_));
shader->setVec4("uTintColor", 1.0f, 1.0f, 1.0f, 1.0f);
shader->setFloat("uOpacity", 1.0f);
const auto &textureSlots = material->getTextures();
bool hasMaterialTexture = false;
for (const auto &slot : textureSlots) {
if (slot.handle.isValid()) {
Texture* texture = assets->get(slot.handle);
if (texture) {
texture->bind(slot.slot);
shader->setInt(slot.uniformName, static_cast<int>(slot.slot));
hasMaterialTexture = true;
}
}
}
if (!hasMaterialTexture) {
Texture* defaultTexture = assets->getDefaultTexturePtr();
if (defaultTexture) {
defaultTexture->bind(0);
shader->setInt("uTexture", 0);
}
}
mesh->bind();
for (uint32_t i = 0; i < count; ++i) {
Transform transform = commandBuffer_[start + i].getTransform();
float matrix[16];
transform.toMatrix(matrix);
shader->setMat4("uModelMatrix", matrix);
Color color = commandBuffer_[start + i].getColor();
shader->setVec4("uColor", color.r, color.g, color.b, color.a);
mesh->draw();
stats_.drawCalls++;
}
}
void RendererModule::executeCommand(const RenderCommand &cmd) {
// 此方法保留用于直接执行特定命令
// 大部分命令现在通过 RenderGraph 处理
switch (cmd.type) {
case RenderCommandType::SetViewport:
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,

View File

@ -0,0 +1,65 @@
#include <renderer/rhi/opengl/gl_buffer.h>
#include <renderer/rhi/opengl/gl_utils.h>
namespace extra2d {
GLBuffer::GLBuffer(const BufferDesc &desc) : desc_(desc), buffer_(0) {}
GLBuffer::~GLBuffer() { destroy(); }
bool GLBuffer::create() {
glGenBuffers(1, &buffer_);
if (buffer_ == 0) {
return false;
}
GLenum target = bufferTypeToGL(desc_.type);
glBindBuffer(target, buffer_);
GLenum usage = bufferUsageToGL(desc_.usage);
glBufferData(target, desc_.size, desc_.initialData, usage);
glBindBuffer(target, 0);
return true;
}
void GLBuffer::destroy() {
if (buffer_ != 0) {
glDeleteBuffers(1, &buffer_);
buffer_ = 0;
}
}
bool GLBuffer::update(const void *data, uint32_t size, uint32_t offset) {
if (buffer_ == 0 || data == nullptr)
return false;
GLenum target = bufferTypeToGL(desc_.type);
glBindBuffer(target, buffer_);
glBufferSubData(target, offset, size, data);
glBindBuffer(target, 0);
return true;
}
void *GLBuffer::map() {
// OpenGL ES 3.2 不支持 glMapBuffer使用 glMapBufferRange
if (buffer_ == 0)
return nullptr;
GLenum target = bufferTypeToGL(desc_.type);
glBindBuffer(target, buffer_);
void *ptr = glMapBufferRange(target, 0, desc_.size, GL_MAP_WRITE_BIT);
return ptr;
}
void GLBuffer::unmap() {
if (buffer_ == 0)
return;
GLenum target = bufferTypeToGL(desc_.type);
glBindBuffer(target, buffer_);
glUnmapBuffer(target);
glBindBuffer(target, 0);
}
} // namespace extra2d

View File

@ -0,0 +1,367 @@
#include <renderer/rhi/opengl/gl_command_list.h>
#include <renderer/rhi/opengl/gl_pipeline.h>
#include <renderer/rhi/opengl/gl_buffer.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi/opengl/gl_framebuffer.h>
#include <glm/gtc/type_ptr.hpp>
namespace extra2d {
GLCommandList::GLCommandList() = default;
GLCommandList::~GLCommandList() = default;
void GLCommandList::begin() {
// 开始记录命令
recording_ = true;
// 重置状态缓存
stateCache_.reset();
// 重置统计
stats_.reset();
}
void GLCommandList::end() {
// 结束记录命令
recording_ = false;
}
void GLCommandList::submit() {
// 提交命令列表OpenGL 是立即模式,这里不需要特殊处理)
}
void GLCommandList::beginRenderPass(RHIFramebuffer *framebuffer,
ClearFlags clearFlags,
const Color &clearColor,
float clearDepth,
uint8_t clearStencil) {
if (framebuffer) {
auto *glFramebuffer = static_cast<GLFramebuffer *>(framebuffer);
glFramebuffer->bind();
stateCache_.framebuffer = glFramebuffer;
} else {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
stateCache_.framebuffer = nullptr;
}
// 禁用2D渲染不需要的OpenGL状态
glDisable(GL_DEPTH_TEST); // 禁用深度测试
glDisable(GL_CULL_FACE); // 禁用面剔除
glDisable(GL_STENCIL_TEST); // 禁用模板测试
// 启用混合2D渲染通常需要透明度混合
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 禁用裁剪测试(除非需要)
glDisable(GL_SCISSOR_TEST);
// 执行清除操作
if (clearFlags != ClearFlags::None) {
clear(clearFlags, clearColor, clearDepth, clearStencil);
}
}
void GLCommandList::endRenderPass() {
// 结束渲染通道
stateCache_.framebuffer = nullptr;
}
void GLCommandList::setViewport(const Viewport &viewport) {
// 状态缓存检查
if (stateCache_.viewportValid &&
stateCache_.viewport.x == viewport.x &&
stateCache_.viewport.y == viewport.y &&
stateCache_.viewport.width == viewport.width &&
stateCache_.viewport.height == viewport.height) {
stats_.redundantPipelineChanges++;
return;
}
glViewport(static_cast<GLint>(viewport.x), static_cast<GLint>(viewport.y),
static_cast<GLsizei>(viewport.width),
static_cast<GLsizei>(viewport.height));
stateCache_.viewport = viewport;
stateCache_.viewportValid = true;
stats_.viewportChanges++;
}
void GLCommandList::setScissor(const ScissorRect &scissor) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
void GLCommandList::setPipeline(RHIPipeline *pipeline) {
if (!pipeline) return;
auto *glPipeline = static_cast<GLPipeline *>(pipeline);
// 状态缓存检查
if (stateCache_.pipeline == glPipeline) {
stats_.redundantPipelineChanges++;
return;
}
glPipeline->bind();
stateCache_.pipeline = glPipeline;
stateCache_.shaderProgram = glPipeline->getGLProgram();
stats_.pipelineChanges++;
}
void GLCommandList::setVertexBuffer(uint32_t slot, RHIBuffer *buffer,
uint32_t offset, uint32_t stride) {
if (!buffer || slot >= 4) return;
auto *glBuffer = static_cast<GLBuffer *>(buffer);
// 状态缓存检查
if (stateCache_.vertexBuffers[slot] == glBuffer &&
stateCache_.vertexBufferOffsets[slot] == offset) {
stats_.redundantBufferChanges++;
return;
}
glBindBuffer(GL_ARRAY_BUFFER, glBuffer->getGLBuffer());
// 配置属于当前 slot 的属性
// 注意:虽然 VAO 缓存了部分配置,但我们需要绑定具体的缓冲区
const auto &layout = stateCache_.pipeline ? stateCache_.pipeline->getVertexLayout() : VertexLayout{};
uint32_t actualStride = stride > 0 ? stride : layout.stride;
for (const auto &attr : layout.attributes) {
if (attr.bufferIndex != slot) {
continue;
}
glEnableVertexAttribArray(attr.location);
GLenum type = GL_FLOAT;
GLint size = 1;
GLboolean normalized = GL_FALSE;
switch (attr.format) {
case VertexFormat::Float1:
size = 1;
break;
case VertexFormat::Float2:
size = 2;
break;
case VertexFormat::Float3:
size = 3;
break;
case VertexFormat::Float4:
size = 4;
break;
default:
break;
}
glVertexAttribPointer(attr.location, size, type, normalized, actualStride,
reinterpret_cast<const void *>(attr.offset + offset));
// 设置实例化除数
glVertexAttribDivisor(attr.location, attr.divisor);
}
stateCache_.vertexBuffers[slot] = glBuffer;
stateCache_.vertexBufferOffsets[slot] = offset;
stats_.bufferChanges++;
}
void GLCommandList::setIndexBuffer(RHIBuffer *buffer, IndexType type,
uint32_t offset) {
if (!buffer) return;
auto *glBuffer = static_cast<GLBuffer *>(buffer);
// 状态缓存检查
if (stateCache_.indexBuffer == glBuffer) {
stats_.redundantBufferChanges++;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glBuffer->getGLBuffer());
stateCache_.indexBuffer = glBuffer;
stats_.bufferChanges++;
}
void GLCommandList::setUniformBuffer(uint32_t slot, RHIBuffer *buffer) {
if (!buffer || slot >= 4) return;
auto *glBuffer = static_cast<GLBuffer *>(buffer);
// 状态缓存检查
if (stateCache_.uniformBuffers[slot].valid &&
stateCache_.uniformBuffers[slot].buffer == glBuffer &&
stateCache_.uniformBuffers[slot].offset == 0) {
return;
}
glBindBufferBase(GL_UNIFORM_BUFFER, slot, glBuffer->getGLBuffer());
stateCache_.uniformBuffers[slot].buffer = glBuffer;
stateCache_.uniformBuffers[slot].offset = 0;
stateCache_.uniformBuffers[slot].size = 0;
stateCache_.uniformBuffers[slot].valid = true;
}
void GLCommandList::setUniformBuffer(uint32_t slot, RHIBuffer *buffer, uint32_t offset, uint32_t size) {
if (!buffer || slot >= 4) return;
auto *glBuffer = static_cast<GLBuffer *>(buffer);
GLuint glBufferId = glBuffer->getGLBuffer();
// 状态缓存检查
if (stateCache_.uniformBuffers[slot].valid &&
stateCache_.uniformBuffers[slot].buffer == glBuffer &&
stateCache_.uniformBuffers[slot].offset == offset &&
stateCache_.uniformBuffers[slot].size == size) {
return;
}
if (size == 0) {
// 绑定整个缓冲区
glBindBufferBase(GL_UNIFORM_BUFFER, slot, glBufferId);
} else {
// 绑定缓冲区范围
glBindBufferRange(GL_UNIFORM_BUFFER, slot, glBufferId, offset, size);
}
stateCache_.uniformBuffers[slot].buffer = glBuffer;
stateCache_.uniformBuffers[slot].offset = offset;
stateCache_.uniformBuffers[slot].size = size;
stateCache_.uniformBuffers[slot].valid = true;
}
void GLCommandList::setTexture(uint32_t slot, RHITexture *texture) {
if (!texture || slot >= 8) return;
auto *glTexture = static_cast<GLTexture *>(texture);
// 状态缓存检查
if (stateCache_.textures[slot].valid &&
stateCache_.textures[slot].texture == glTexture) {
stats_.redundantTextureChanges++;
return;
}
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, glTexture->getGLTexture());
stateCache_.textures[slot].texture = glTexture;
stateCache_.textures[slot].valid = true;
stats_.textureChanges++;
}
void GLCommandList::setSampler(uint32_t slot, TextureFilter minFilter,
TextureFilter magFilter, TextureWrap wrapS,
TextureWrap wrapT) {
// OpenGL 的采样器状态是纹理对象的一部分
// 这里可以设置当前绑定纹理的采样参数
}
void GLCommandList::draw(uint32_t vertexCount, uint32_t firstVertex,
uint32_t instanceCount, uint32_t firstInstance) {
if (instanceCount > 1) {
glDrawArraysInstanced(GL_TRIANGLES, firstVertex, vertexCount, instanceCount);
} else {
glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount);
}
}
void GLCommandList::drawIndexed(uint32_t indexCount, uint32_t firstIndex,
int32_t vertexOffset, uint32_t instanceCount,
uint32_t firstInstance) {
GLenum indexType = GL_UNSIGNED_SHORT;
if (stateCache_.indexBuffer) {
// 根据缓冲区类型确定索引类型
// 这里简化处理,使用 GL_UNSIGNED_SHORT
}
if (instanceCount > 1) {
glDrawElementsInstanced(
GL_TRIANGLES, indexCount, indexType,
reinterpret_cast<const void *>(firstIndex * sizeof(uint16_t)),
instanceCount);
} else {
glDrawElements(GL_TRIANGLES, indexCount, indexType,
reinterpret_cast<const void *>(firstIndex * sizeof(uint16_t)));
}
}
void GLCommandList::clear(ClearFlags flags, const Color &color, float depth,
uint8_t stencil) {
GLbitfield mask = 0;
if (hasFlag(flags, ClearFlags::Color)) {
glClearColor(color.r, color.g, color.b, color.a);
mask |= GL_COLOR_BUFFER_BIT;
}
if (hasFlag(flags, ClearFlags::Depth)) {
glClearDepthf(depth);
mask |= GL_DEPTH_BUFFER_BIT;
}
if (hasFlag(flags, ClearFlags::Stencil)) {
glClearStencil(stencil);
mask |= GL_STENCIL_BUFFER_BIT;
}
if (mask != 0) {
glClear(mask);
}
}
bool GLCommandList::isRecording() const {
return recording_;
}
void GLCommandList::setUniform(const char* name, float value) {
if (stateCache_.shaderProgram != 0) {
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
if (location != -1) {
glUniform1f(location, value);
}
}
}
void GLCommandList::setUniform(const char* name, const Vec2& value) {
if (stateCache_.shaderProgram != 0) {
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
if (location != -1) {
glUniform2f(location, value.x, value.y);
}
}
}
void GLCommandList::setUniform(const char* name, const Vec3& value) {
if (stateCache_.shaderProgram != 0) {
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
if (location != -1) {
glUniform3f(location, value.x, value.y, value.z);
}
}
}
void GLCommandList::setUniform(const char* name, const Color& value) {
if (stateCache_.shaderProgram != 0) {
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
if (location != -1) {
glUniform4f(location, value.r, value.g, value.b, value.a);
}
}
}
void GLCommandList::setUniform(const char* name, const Mat4& value) {
if (stateCache_.shaderProgram != 0) {
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
if (location != -1) {
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
}
}
}
} // namespace extra2d

View File

@ -0,0 +1,84 @@
#include <cstring>
#include <renderer/rhi/opengl/gl_context.h>
#include <renderer/rhi/opengl/gl_framebuffer.h>
#include <utils/logger.h>
namespace extra2d {
GLContext::GLContext(GLDevice *device)
: device_(device), defaultFramebuffer_(nullptr) {}
GLContext::~GLContext() = default;
bool GLContext::initialize() {
E2D_LOG_INFO("Initializing OpenGL context...");
// 创建默认帧缓冲(窗口帧缓冲)
defaultFramebuffer_ = std::make_unique<GLFramebuffer>();
defaultFramebuffer_->setSize(800, 600); // 默认大小,会被更新
E2D_LOG_INFO("OpenGL context initialized");
return true;
}
void GLContext::shutdown() {
E2D_LOG_INFO("Shutting down OpenGL context...");
defaultFramebuffer_.reset();
E2D_LOG_INFO("OpenGL context shutdown complete");
}
void GLContext::beginFrame() {
// 重置每帧的统计
// device_->resetStats(); // 需要在 GLDevice 中添加此方法
}
void GLContext::endFrame() {
// 交换缓冲区
// 通过 SDL 获取当前窗口并交换缓冲区
SDL_Window* window = SDL_GL_GetCurrentWindow();
if (window) {
SDL_GL_SwapWindow(window);
}
}
void GLContext::setViewport(int32_t x, int32_t y, uint32_t width,
uint32_t height) {
glViewport(x, y, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
// 更新默认帧缓冲大小
if (defaultFramebuffer_) {
defaultFramebuffer_->setSize(width, height);
}
}
void GLContext::bindDefaultFramebuffer() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
RHIFramebuffer *GLContext::getDefaultFramebuffer() {
return defaultFramebuffer_.get();
}
bool GLContext::isFeatureSupported(const char *feature) const {
// 检查 OpenGL 扩展或版本
if (std::strcmp(feature, "instancing") == 0) {
return glad_glDrawArraysInstanced != nullptr;
}
if (std::strcmp(feature, "compute") == 0) {
return glad_glDispatchCompute != nullptr;
}
if (std::strcmp(feature, "sRGB") == 0) {
return GLAD_GL_EXT_texture_sRGB_R8;
}
if (std::strcmp(feature, "anisotropic") == 0) {
return GLAD_GL_EXT_texture_filter_anisotropic;
}
if (std::strcmp(feature, "debug") == 0) {
return GLAD_GL_KHR_debug;
}
return false;
}
} // namespace extra2d

View File

@ -0,0 +1,200 @@
#include <renderer/rhi/opengl/gl_buffer.h>
#include <renderer/rhi/opengl/gl_command_list.h>
#include <renderer/rhi/opengl/gl_context.h>
#include <renderer/rhi/opengl/gl_framebuffer.h>
#include <renderer/rhi/opengl/gl_pipeline.h>
#include <renderer/rhi/opengl/gl_shader.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi/opengl/gl_utils.h>
#include <glad/glad.h>
#include <SDL.h>
#include <utils/logger.h>
namespace extra2d {
// 前向声明
class GLDevice;
/**
* @brief OpenGL
*/
class GLDevice : public RHIDevice {
public:
GLDevice() : window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {}
~GLDevice() override { shutdown(); }
bool initialize(void* nativeWindow) override {
E2D_LOG_INFO("Initializing OpenGL device...");
if (!nativeWindow) {
E2D_LOG_ERROR("Native window handle is null");
return false;
}
window_ = static_cast<SDL_Window*>(nativeWindow);
// 创建 OpenGL ES 3.2 上下文
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_LOG_ERROR("Failed to create OpenGL ES context: {}", SDL_GetError());
return false;
}
// 初始化 GLAD
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("Failed to initialize GLAD");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL ES context created: {}.{}", GLVersion.major, GLVersion.minor);
const char* version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
if (!version) {
E2D_LOG_ERROR("Failed to get OpenGL version");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL Version: {}", version);
E2D_LOG_INFO("OpenGL Vendor: {}",
reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
E2D_LOG_INFO("OpenGL Renderer: {}",
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
// 创建上下文管理器
context_ = std::make_unique<GLContext>(this);
if (!context_->initialize()) {
E2D_LOG_ERROR("Failed to initialize GL context");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
// 设置默认状态
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
E2D_LOG_INFO("OpenGL device initialized successfully");
return true;
}
void shutdown() override {
E2D_LOG_INFO("Shutting down OpenGL device...");
if (context_) {
context_->shutdown();
context_.reset();
}
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
window_ = nullptr;
E2D_LOG_INFO("OpenGL device shutdown complete");
}
std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc &desc) override {
auto buffer = std::make_unique<GLBuffer>(desc);
if (!buffer->create()) {
return nullptr;
}
return buffer;
}
std::unique_ptr<RHITexture> createTexture(const TextureDesc &desc) override {
auto texture = std::make_unique<GLTexture>(desc);
if (!texture->create()) {
return nullptr;
}
return texture;
}
std::unique_ptr<RHIShader> createShader(const ShaderDesc &desc) override {
auto shader = std::make_unique<GLShader>(desc);
if (!shader->compile()) {
E2D_LOG_ERROR("Shader compilation failed: {}", shader->getCompileLog());
return nullptr;
}
return shader;
}
std::unique_ptr<RHIPipeline>
createPipeline(const PipelineDesc &desc) override {
auto pipeline = std::make_unique<GLPipeline>(desc);
if (!pipeline->create()) {
return nullptr;
}
// 获取 vertex shader 的 program ID
if (desc.vertexShader.isValid()) {
RHIShader* shader = desc.vertexShader.get();
if (shader) {
auto* glShader = static_cast<GLShader*>(shader);
pipeline->setGLProgram(glShader->getGLProgram());
}
}
return pipeline;
}
std::unique_ptr<RHIFramebuffer>
createFramebuffer(const RenderPassDesc &desc) override {
auto framebuffer = std::make_unique<GLFramebuffer>(desc);
if (!framebuffer->create()) {
return nullptr;
}
return framebuffer;
}
std::unique_ptr<RHICommandList> createCommandList() override {
return std::make_unique<GLCommandList>();
}
RHIContext *getContext() override { return context_.get(); }
const char *getBackendName() const override { return "OpenGL"; }
void *getNativeWindow() const { return window_; }
void waitIdle() override { glFinish(); }
const RenderStats &getStats() const override { return stats_; }
void resetStats() override { stats_.reset(); }
void incrementDrawCalls() { stats_.drawCalls++; }
void incrementTriangles(uint32_t count) { stats_.triangles += count; }
void incrementVertices(uint32_t count) { stats_.vertices += count; }
void incrementTextureBinds() { stats_.textureBinds++; }
void incrementBufferBinds() { stats_.bufferBinds++; }
void incrementPipelineBinds() { stats_.pipelineBinds++; }
void incrementRenderPassSwitches() { stats_.renderPassSwitches++; }
private:
SDL_Window* window_;
SDL_GLContext glContext_;
std::unique_ptr<GLContext> context_;
RenderStats stats_;
};
// 创建设备的工厂函数
std::unique_ptr<RHIDevice> CreateGLDevice() {
return std::make_unique<GLDevice>();
}
} // namespace extra2d

View File

@ -0,0 +1,151 @@
#include <renderer/rhi/opengl/gl_framebuffer.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi/opengl/gl_utils.h>
namespace extra2d {
/**
* @brief
* @param format
* @return /
*/
static inline bool isStencilFormat(TextureFormat format) {
switch (format) {
case TextureFormat::Depth24Stencil8:
case TextureFormat::Depth32FStencil8:
return true;
default:
return false;
}
}
GLFramebuffer::GLFramebuffer()
: desc_(), framebuffer_(0), width_(0), height_(0) {}
GLFramebuffer::GLFramebuffer(const RenderPassDesc &desc)
: desc_(desc), framebuffer_(0), width_(0), height_(0) {}
GLFramebuffer::~GLFramebuffer() { destroy(); }
bool GLFramebuffer::create() {
glGenFramebuffers(1, &framebuffer_);
if (framebuffer_ == 0) {
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
// 附加颜色附件
std::vector<GLenum> drawBuffers;
for (size_t i = 0; i < desc_.colorAttachments.size(); ++i) {
const auto &attachment = desc_.colorAttachments[i];
if (attachment.texture.isValid()) {
auto *texture = static_cast<GLTexture *>(attachment.texture.get());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
GL_TEXTURE_2D, texture->getGLTexture(), 0);
drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i);
if (i == 0) {
width_ = texture->getWidth();
height_ = texture->getHeight();
}
}
}
// 附加深度/模板附件
if (desc_.hasDepthStencil && desc_.depthStencilAttachment.texture.isValid()) {
auto *texture =
static_cast<GLTexture *>(desc_.depthStencilAttachment.texture.get());
GLenum attachment = isStencilFormat(texture->getFormat())
? GL_DEPTH_STENCIL_ATTACHMENT
: GL_DEPTH_ATTACHMENT;
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D,
texture->getGLTexture(), 0);
}
if (!drawBuffers.empty()) {
glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), drawBuffers.data());
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return status == GL_FRAMEBUFFER_COMPLETE;
}
void GLFramebuffer::destroy() {
if (framebuffer_ != 0) {
glDeleteFramebuffers(1, &framebuffer_);
framebuffer_ = 0;
}
}
void GLFramebuffer::bind() {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
glViewport(0, 0, width_, height_);
}
void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
void GLFramebuffer::clear(ClearFlags flags, const Color &color, float depth,
uint8_t stencil) {
bind();
GLbitfield mask = 0;
if (hasFlag(flags, ClearFlags::Color)) {
glClearColor(color.r, color.g, color.b, color.a);
mask |= GL_COLOR_BUFFER_BIT;
}
if (hasFlag(flags, ClearFlags::Depth)) {
glClearDepthf(depth);
mask |= GL_DEPTH_BUFFER_BIT;
}
if (hasFlag(flags, ClearFlags::Stencil)) {
glClearStencil(stencil);
mask |= GL_STENCIL_BUFFER_BIT;
}
if (mask != 0) {
glClear(mask);
}
}
void GLFramebuffer::setSize(uint32_t width, uint32_t height) {
width_ = width;
height_ = height;
}
uint32_t GLFramebuffer::getColorAttachmentCount() const {
return static_cast<uint32_t>(desc_.colorAttachments.size());
}
TextureHandle GLFramebuffer::getColorAttachment(uint32_t index) const {
if (index < desc_.colorAttachments.size()) {
return desc_.colorAttachments[index].texture;
}
return TextureHandle();
}
TextureHandle GLFramebuffer::getDepthStencilAttachment() const {
if (desc_.hasDepthStencil) {
return desc_.depthStencilAttachment.texture;
}
return TextureHandle();
}
bool GLFramebuffer::hasDepthStencil() const {
return desc_.hasDepthStencil;
}
bool GLFramebuffer::isValid() const {
return framebuffer_ != 0;
}
bool GLFramebuffer::isDefault() const {
return framebuffer_ == 0;
}
} // namespace extra2d

View File

@ -0,0 +1,116 @@
#include <renderer/rhi/opengl/gl_pipeline.h>
#include <renderer/rhi/opengl/gl_utils.h>
namespace extra2d {
GLPipeline::GLPipeline(const PipelineDesc &desc) : desc_(desc), vao_(0) {}
GLPipeline::~GLPipeline() { destroy(); }
bool GLPipeline::create() {
// 创建 VAO
glGenVertexArrays(1, &vao_);
if (vao_ == 0) {
return false;
}
glBindVertexArray(vao_);
// 配置顶点属性
const auto &layout = desc_.vertexLayout;
for (const auto &attr : layout.attributes) {
glEnableVertexAttribArray(attr.location);
GLenum type = GL_FLOAT;
GLint size = 1;
GLboolean normalized = GL_FALSE;
switch (attr.format) {
case VertexFormat::Float1:
size = 1;
break;
case VertexFormat::Float2:
size = 2;
break;
case VertexFormat::Float3:
size = 3;
break;
case VertexFormat::Float4:
size = 4;
break;
default:
break;
}
glVertexAttribPointer(attr.location, size, type, normalized, layout.stride,
reinterpret_cast<const void *>(attr.offset));
}
glBindVertexArray(0);
return true;
}
void GLPipeline::destroy() {
if (vao_ != 0) {
glDeleteVertexArrays(1, &vao_);
vao_ = 0;
}
}
void GLPipeline::bind() {
// 绑定着色器程序
if (shaderProgram_ != 0) {
glUseProgram(shaderProgram_);
}
// 注意VAO 需要与具体的顶点缓冲区绑定才能工作
// 所以我们不在此处绑定 VAO而是在渲染时动态配置顶点属性
// 这样可以支持不同的 Mesh 使用相同的 Pipeline
// 应用混合状态
if (desc_.blendState.enabled) {
glEnable(GL_BLEND);
glBlendFunc(blendFactorToGL(desc_.blendState.srcFactor),
blendFactorToGL(desc_.blendState.dstFactor));
} else {
glDisable(GL_BLEND);
}
// 应用深度状态
if (desc_.depthStencilState.depthTestEnabled) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(compareFuncToGL(desc_.depthStencilState.depthCompare));
glDepthMask(desc_.depthStencilState.depthWriteEnabled ? GL_TRUE : GL_FALSE);
} else {
glDisable(GL_DEPTH_TEST);
}
// 应用光栅化状态
if (desc_.rasterizerState.cullEnabled) {
glEnable(GL_CULL_FACE);
glCullFace(desc_.rasterizerState.cullFrontFace ? GL_FRONT : GL_BACK);
glFrontFace(desc_.rasterizerState.frontCCW ? GL_CCW : GL_CW);
} else {
glDisable(GL_CULL_FACE);
}
}
void GLPipeline::unbind() { glBindVertexArray(0); }
ShaderHandle GLPipeline::getVertexShader() const {
return desc_.vertexShader;
}
ShaderHandle GLPipeline::getFragmentShader() const {
return desc_.fragmentShader;
}
const VertexLayout& GLPipeline::getVertexLayout() const {
return desc_.vertexLayout;
}
bool GLPipeline::isValid() const {
return vao_ != 0;
}
} // namespace extra2d

View File

@ -0,0 +1,137 @@
#include <glm/gtc/type_ptr.hpp>
#include <renderer/rhi/opengl/gl_shader.h>
namespace extra2d {
GLShader::GLShader(const ShaderDesc &desc)
: desc_(desc), shader_(0), program_(0) {}
GLShader::~GLShader() { destroy(); }
bool GLShader::compile() {
// 创建程序
program_ = glCreateProgram();
if (program_ == 0) {
compileLog_ = "Failed to create shader program";
return false;
}
// 编译顶点着色器
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
const char *vsSource = desc_.vertexSource.c_str();
glShaderSource(vs, 1, &vsSource, nullptr);
glCompileShader(vs);
GLint success;
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(vs, 512, nullptr, infoLog);
compileLog_ = std::string("Vertex shader compilation failed: ") + infoLog;
glDeleteShader(vs);
return false;
}
// 编译片段着色器
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
const char *fsSource = desc_.fragmentSource.c_str();
glShaderSource(fs, 1, &fsSource, nullptr);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(fs, 512, nullptr, infoLog);
compileLog_ = std::string("Fragment shader compilation failed: ") + infoLog;
glDeleteShader(vs);
glDeleteShader(fs);
return false;
}
// 链接着色器程序
glAttachShader(program_, vs);
glAttachShader(program_, fs);
glLinkProgram(program_);
glGetProgramiv(program_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(program_, 512, nullptr, infoLog);
compileLog_ = std::string("Shader program linking failed: ") + infoLog;
glDeleteShader(vs);
glDeleteShader(fs);
glDeleteProgram(program_);
program_ = 0;
return false;
}
// 清理着色器对象
glDeleteShader(vs);
glDeleteShader(fs);
return true;
}
void GLShader::destroy() {
if (program_ != 0) {
glDeleteProgram(program_);
program_ = 0;
}
}
void GLShader::bind() const {
if (program_ != 0) {
glUseProgram(program_);
}
}
void GLShader::unbind() const { glUseProgram(0); }
void GLShader::setUniform(const std::string &name, float value) {
GLint location = glGetUniformLocation(program_, name.c_str());
if (location != -1) {
glUniform1f(location, value);
}
}
void GLShader::setUniform(const std::string &name, const Vec2 &value) {
GLint location = glGetUniformLocation(program_, name.c_str());
if (location != -1) {
glUniform2f(location, value.x, value.y);
}
}
void GLShader::setUniform(const std::string &name, const Vec3 &value) {
GLint location = glGetUniformLocation(program_, name.c_str());
if (location != -1) {
glUniform3f(location, value.x, value.y, value.z);
}
}
void GLShader::setUniform(const std::string &name, const Color &value) {
GLint location = glGetUniformLocation(program_, name.c_str());
if (location != -1) {
glUniform4f(location, value.r, value.g, value.b, value.a);
}
}
void GLShader::setUniform(const std::string &name, const Mat4 &value) {
GLint location = glGetUniformLocation(program_, name.c_str());
if (location != -1) {
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
}
}
bool GLShader::isCompiled() const {
return program_ != 0;
}
std::string GLShader::getCompileLog() const {
return compileLog_;
}
bool GLShader::isValid() const {
return program_ != 0;
}
} // namespace extra2d

View File

@ -0,0 +1,120 @@
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi/opengl/gl_utils.h>
namespace extra2d {
/**
* @brief
* @param format
* @return
*/
static inline bool isDepthFormat(TextureFormat format) {
switch (format) {
case TextureFormat::Depth16:
case TextureFormat::Depth24:
case TextureFormat::Depth32F:
case TextureFormat::Depth24Stencil8:
case TextureFormat::Depth32FStencil8:
return true;
default:
return false;
}
}
GLTexture::GLTexture(const TextureDesc &desc) : desc_(desc), texture_(0) {}
GLTexture::~GLTexture() { destroy(); }
bool GLTexture::create() {
glGenTextures(1, &texture_);
if (texture_ == 0) {
return false;
}
GLenum target = GL_TEXTURE_2D;
glBindTexture(target, texture_);
// 设置纹理参数
glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
textureFilterToGLMin(desc_.minFilter));
glTexParameteri(target, GL_TEXTURE_MAG_FILTER,
textureFilterToGLMag(desc_.magFilter));
glTexParameteri(target, GL_TEXTURE_WRAP_S, textureWrapToGL(desc_.wrapS));
glTexParameteri(target, GL_TEXTURE_WRAP_T, textureWrapToGL(desc_.wrapT));
// 分配纹理存储
GLenum format = textureFormatToGLFormat(desc_.format);
GLenum internalFormat = textureFormatToGLInternal(desc_.format);
GLenum type = GL_UNSIGNED_BYTE;
if (isDepthFormat(desc_.format)) {
type = GL_FLOAT;
}
glTexImage2D(target, 0, internalFormat, desc_.width, desc_.height, 0, format,
type, nullptr);
// 生成 mipmap如果需要
if (desc_.mipLevels > 1) {
glGenerateMipmap(target);
}
glBindTexture(target, 0);
return true;
}
void GLTexture::destroy() {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
}
bool GLTexture::update(const void *data, uint32_t mipLevel) {
if (texture_ == 0 || data == nullptr)
return false;
glBindTexture(GL_TEXTURE_2D, texture_);
GLenum format = textureFormatToGLFormat(desc_.format);
GLenum type = isDepthFormat(desc_.format) ? GL_FLOAT : GL_UNSIGNED_BYTE;
// 计算当前 mipmap 层级的尺寸
uint32_t width = desc_.width >> mipLevel;
uint32_t height = desc_.height >> mipLevel;
if (width == 0)
width = 1;
if (height == 0)
height = 1;
glTexSubImage2D(GL_TEXTURE_2D, mipLevel, 0, 0, width, height, format, type,
data);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void GLTexture::generateMipmap() {
if (texture_ == 0)
return;
glBindTexture(GL_TEXTURE_2D, texture_);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
void GLTexture::bind(uint32_t slot) {
if (texture_ == 0)
return;
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, texture_);
}
void GLTexture::unbind() { glBindTexture(GL_TEXTURE_2D, 0); }
bool GLTexture::isValid() const {
return texture_ != 0;
}
bool GLTexture::isRenderTarget() const {
return desc_.renderTarget;
}
} // namespace extra2d

352
src/renderer/rhi_module.cpp Normal file
View File

@ -0,0 +1,352 @@
#include <module/module_registry.h>
#include <platform/window_module.h>
#include <renderer/rhi_module.h>
#include <utils/logger.h>
// 包含 OpenGL 设备实现
// 注意GLDevice 定义在 gl_device.cpp 中,需要确保链接时包含
namespace extra2d {
// 前向声明 GLDevice 类
class GLDevice;
} // namespace extra2d
namespace extra2d {
// 全局实例指针
static RHIModule *g_rhiModule = nullptr;
// GLDevice 创建函数声明 - 在 gl_device.cpp 中定义
std::unique_ptr<RHIDevice> CreateGLDevice();
RHIModule::RHIModule() : context_(nullptr) { g_rhiModule = this; }
RHIModule::~RHIModule() {
shutdown();
g_rhiModule = nullptr;
}
bool RHIModule::init() {
E2D_LOG_INFO("Initializing RHIModule...");
// 注册窗口显示事件监听,在窗口显示时创建 OpenGL 上下文
onShowListener_ = std::make_unique<events::OnShow::Listener>();
onShowListener_->bind([this]() { this->onWindowShow(); });
E2D_LOG_INFO("RHIModule initialized (waiting for window show)");
return true;
}
void RHIModule::onWindowShow() {
if (initialized_) {
return;
}
E2D_LOG_INFO("RHIModule: Creating OpenGL device...");
// 获取窗口模块
auto *windowModule = getModule<WindowModule>();
if (!windowModule) {
E2D_LOG_ERROR("WindowModule not available");
return;
}
// 等待窗口创建完成
if (!windowModule->handle()) {
E2D_LOG_ERROR("Window not created yet");
return;
}
// 创建 OpenGL 设备并传入窗口句柄
device_ = CreateGLDevice();
if (!device_) {
E2D_LOG_ERROR("Failed to create RHI device");
return;
}
if (!device_->initialize(windowModule->handle())) {
E2D_LOG_ERROR("Failed to initialize RHI device");
device_.reset();
return;
}
context_ = device_->getContext();
if (!context_) {
E2D_LOG_ERROR("Failed to get RHI context");
device_->shutdown();
device_.reset();
return;
}
// 初始化上下文
if (!context_->initialize()) {
E2D_LOG_ERROR("Failed to initialize RHI context");
device_->shutdown();
device_.reset();
context_ = nullptr;
return;
}
initialized_ = true;
E2D_LOG_INFO("RHIModule initialized successfully (backend: {})",
device_->getBackendName());
// RHI 初始化完成,再次触发 OnShow 事件通知其他模块
// 这样 RendererModule 等依赖 RHI 的模块可以完成初始化
events::OnShow::emit();
}
void RHIModule::shutdown() {
E2D_LOG_INFO("Shutting down RHIModule...");
// 清理事件监听器
onShowListener_.reset();
if (context_) {
context_->shutdown();
context_ = nullptr;
}
if (device_) {
device_->shutdown();
device_.reset();
}
initialized_ = false;
E2D_LOG_INFO("RHIModule shutdown complete");
}
RHIModule *RHIModule::get() { return g_rhiModule; }
std::unique_ptr<RHIBuffer> RHIModule::createBuffer(const BufferDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create buffer: RHI device not initialized");
return nullptr;
}
return device_->createBuffer(desc);
}
std::unique_ptr<RHITexture> RHIModule::createTexture(const TextureDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create texture: RHI device not initialized");
return nullptr;
}
return device_->createTexture(desc);
}
std::unique_ptr<RHIShader> RHIModule::createShader(const ShaderDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create shader: RHI device not initialized");
return nullptr;
}
return device_->createShader(desc);
}
std::unique_ptr<RHIPipeline>
RHIModule::createPipeline(const PipelineDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create pipeline: RHI device not initialized");
return nullptr;
}
return device_->createPipeline(desc);
}
std::unique_ptr<RHIFramebuffer>
RHIModule::createFramebuffer(const RenderPassDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create framebuffer: RHI device not initialized");
return nullptr;
}
return device_->createFramebuffer(desc);
}
std::unique_ptr<RHICommandList> RHIModule::createCommandList() {
if (!device_) {
E2D_LOG_ERROR("Cannot create command list: RHI device not initialized");
return nullptr;
}
return device_->createCommandList();
}
// BufferDesc 静态方法实现
BufferDesc BufferDesc::vertex(uint32_t size, BufferUsage usage) {
BufferDesc desc;
desc.type = BufferType::Vertex;
desc.usage = usage;
desc.size = size;
return desc;
}
BufferDesc BufferDesc::index(uint32_t size, BufferUsage usage) {
BufferDesc desc;
desc.type = BufferType::Index;
desc.usage = usage;
desc.size = size;
return desc;
}
BufferDesc BufferDesc::uniform(uint32_t size) {
BufferDesc desc;
desc.type = BufferType::Uniform;
desc.usage = BufferUsage::Dynamic;
desc.size = size;
return desc;
}
// BlendState 静态方法实现
BlendState BlendState::opaque() {
BlendState state;
state.enabled = false;
state.srcFactor = BlendFactor::One;
state.dstFactor = BlendFactor::Zero;
state.op = BlendOp::Add;
state.srcAlphaFactor = BlendFactor::One;
state.dstAlphaFactor = BlendFactor::Zero;
state.alphaOp = BlendOp::Add;
return state;
}
BlendState BlendState::alphaBlend() {
BlendState state;
state.enabled = true;
state.srcFactor = BlendFactor::SrcAlpha;
state.dstFactor = BlendFactor::OneMinusSrcAlpha;
state.op = BlendOp::Add;
state.srcAlphaFactor = BlendFactor::One;
state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha;
state.alphaOp = BlendOp::Add;
return state;
}
BlendState BlendState::additive() {
BlendState state;
state.enabled = true;
state.srcFactor = BlendFactor::SrcAlpha;
state.dstFactor = BlendFactor::One;
state.op = BlendOp::Add;
state.srcAlphaFactor = BlendFactor::SrcAlpha;
state.dstAlphaFactor = BlendFactor::One;
state.alphaOp = BlendOp::Add;
return state;
}
// DepthStencilState 静态方法实现
DepthStencilState DepthStencilState::depthTest() {
DepthStencilState state;
state.depthTestEnabled = true;
state.depthWriteEnabled = true;
state.depthCompare = CompareFunc::Less;
state.stencilEnabled = false;
return state;
}
DepthStencilState DepthStencilState::depthTestWrite() {
DepthStencilState state;
state.depthTestEnabled = true;
state.depthWriteEnabled = true;
state.depthCompare = CompareFunc::Less;
state.stencilEnabled = false;
return state;
}
DepthStencilState DepthStencilState::depthTestNoWrite() {
DepthStencilState state;
state.depthTestEnabled = true;
state.depthWriteEnabled = false;
state.depthCompare = CompareFunc::Less;
state.stencilEnabled = false;
return state;
}
DepthStencilState DepthStencilState::noDepthTest() {
DepthStencilState state;
state.depthTestEnabled = false;
state.depthWriteEnabled = false;
state.depthCompare = CompareFunc::Always;
state.stencilEnabled = false;
return state;
}
// RasterizerState 静态方法实现
RasterizerState RasterizerState::cullBack() {
RasterizerState state;
state.cullEnabled = true;
state.cullFrontFace = false;
state.frontCCW = false;
state.scissorEnabled = false;
state.wireframe = false;
return state;
}
RasterizerState RasterizerState::cullFront() {
RasterizerState state;
state.cullEnabled = true;
state.cullFrontFace = true;
state.frontCCW = false;
state.scissorEnabled = false;
state.wireframe = false;
return state;
}
RasterizerState RasterizerState::noCull() {
RasterizerState state;
state.cullEnabled = false;
state.cullFrontFace = false;
state.frontCCW = false;
state.scissorEnabled = false;
state.wireframe = false;
return state;
}
// VertexLayout 方法实现
void VertexLayout::addAttribute(uint32_t location, VertexFormat format,
uint32_t offset, uint32_t bufferIndex) {
VertexAttribute attr;
attr.location = location;
attr.format = format;
attr.offset = offset;
attr.bufferIndex = bufferIndex;
attributes.push_back(attr);
}
// VertexAttribute 方法实现
uint32_t VertexAttribute::getSize(VertexFormat format) {
switch (format) {
case VertexFormat::Float1:
return 4;
case VertexFormat::Float2:
return 8;
case VertexFormat::Float3:
return 12;
case VertexFormat::Float4:
return 16;
case VertexFormat::Int1:
return 4;
case VertexFormat::Int2:
return 8;
case VertexFormat::Int3:
return 12;
case VertexFormat::Int4:
return 16;
case VertexFormat::UInt1:
return 4;
case VertexFormat::UInt2:
return 8;
case VertexFormat::UInt3:
return 12;
case VertexFormat::UInt4:
return 16;
case VertexFormat::Byte4:
return 4;
case VertexFormat::Byte4Normalized:
return 4;
case VertexFormat::UByte4:
return 4;
case VertexFormat::UByte4Normalized:
return 4;
default:
return 0;
}
}
} // namespace extra2d

View File

@ -1,7 +1,5 @@
#include <fstream>
#include <glad/glad.h>
#include <renderer/rhi_module.h>
#include <renderer/shader.h>
#include <sstream>
#include <utils/logger.h>
namespace extra2d {
@ -9,203 +7,181 @@ 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);
// RHIHandle 是轻量级句柄,不需要显式释放
// 实际的资源由 RHI 设备管理
pipeline_ = PipelineHandle();
handle_ = ShaderHandle();
}
bool Shader::loadFromSource(const std::string &vsSource,
const std::string &fsSource) {
// 删除旧程序
if (program_ != 0) {
glDeleteProgram(program_);
program_ = 0;
// 创建标准2D顶点布局位置 + 纹理坐标 + 颜色)
VertexLayout vertexLayout;
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout);
}
bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout) {
// 释放旧资源
pipeline_ = PipelineHandle();
handle_ = ShaderHandle();
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
uniformCache_.clear();
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 处理源码(添加版本声明)
std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER);
std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER);
std::string processedVS = addVersionIfNeeded(vsSource, true);
std::string processedFS = addVersionIfNeeded(fsSource, false);
E2D_LOG_INFO("Compiling vertex shader...");
// 编译顶点着色器
GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS);
if (vs == 0) {
E2D_LOG_ERROR("Vertex shader compilation failed");
// 创建着色器描述
ShaderDesc shaderDesc;
shaderDesc.vertexSource = processedVS;
shaderDesc.fragmentSource = processedFS;
// 创建着色器
auto shader = device->createShader(shaderDesc);
if (!shader) {
E2D_LOG_ERROR("Failed to create shader");
return false;
}
E2D_LOG_INFO("Compiling fragment shader...");
// 编译片段着色器
GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS);
if (fs == 0) {
E2D_LOG_ERROR("Fragment shader compilation failed");
glDeleteShader(vs);
// 获取着色器句柄
handle_ = ShaderHandle(shader.release());
// 创建管线描述
PipelineDesc pipelineDesc;
pipelineDesc.vertexShader = handle_;
pipelineDesc.fragmentShader = handle_;
pipelineDesc.vertexLayout = vertexLayout;
pipelineDesc.blendState = BlendState::alphaBlend();
pipelineDesc.depthStencilState = DepthStencilState::noDepthTest();
pipelineDesc.rasterizerState = RasterizerState::noCull();
// 创建管线
auto pipeline = device->createPipeline(pipelineDesc);
if (!pipeline) {
E2D_LOG_ERROR("Failed to create pipeline");
handle_ = ShaderHandle();
return false;
}
E2D_LOG_INFO("Linking shader program...");
// 链接程序
if (!linkProgram(vs, fs)) {
E2D_LOG_ERROR("Shader program linking failed");
glDeleteShader(vs);
glDeleteShader(fs);
return false;
}
// 获取管线句柄
pipeline_ = PipelineHandle(pipeline.release());
// 清理着色器对象
glDeleteShader(vs);
glDeleteShader(fs);
E2D_LOG_INFO("Shader program created successfully, program ID: {}", program_);
E2D_LOG_INFO("Shader 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;
// 存储 uniform block 绑定信息
uniformBlockBindings_[name] = binding;
GLuint index = glGetUniformBlockIndex(program_, name.c_str());
if (index != GL_INVALID_INDEX) {
glUniformBlockBinding(program_, index, binding);
}
// 注意:实际的 uniform block 绑定需要在渲染时通过 RHI 命令列表设置
// 这里仅存储绑定信息,供后续渲染使用
// 例如commandList->setUniformBlock(binding, buffer);
}
void Shader::setInt(const std::string &name, int value) {
GLint location = getUniformLocation(name);
if (location != -1) {
glUniform1i(location, value);
uint32_t Shader::getUniformBlockBinding(const std::string &name) const {
auto it = uniformBlockBindings_.find(name);
if (it != uniformBlockBindings_.end()) {
return it->second;
}
return UINT32_MAX; // 未找到
}
void Shader::setFloat(const std::string &name, float value) {
GLint location = getUniformLocation(name);
if (location != -1) {
glUniform1f(location, value);
}
bool Shader::reloadFromSource(const std::string &vsSource,
const std::string &fsSource) {
// 创建标准2D顶点布局位置 + 纹理坐标 + 颜色)
VertexLayout vertexLayout;
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
return reloadFromSourceWithLayout(vsSource, fsSource, vertexLayout);
}
void Shader::setVec2(const std::string &name, float x, float y) {
GLint location = getUniformLocation(name);
if (location != -1) {
glUniform2f(location, x, y);
}
}
bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout) {
// 释放旧资源
pipeline_ = PipelineHandle();
handle_ = ShaderHandle();
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;
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 处理源码(添加版本声明)
std::string processedVS = addVersionIfNeeded(vsSource, true);
std::string processedFS = addVersionIfNeeded(fsSource, false);
// 创建着色器描述
ShaderDesc shaderDesc;
shaderDesc.vertexSource = processedVS;
shaderDesc.fragmentSource = processedFS;
// 创建着色器
auto shader = device->createShader(shaderDesc);
if (!shader) {
E2D_LOG_ERROR("Failed to create shader during reload");
return false;
}
// 获取着色器句柄
handle_ = ShaderHandle(shader.release());
// 创建管线描述
PipelineDesc pipelineDesc;
pipelineDesc.vertexShader = handle_;
pipelineDesc.fragmentShader = handle_;
pipelineDesc.vertexLayout = vertexLayout;
pipelineDesc.blendState = BlendState::alphaBlend();
pipelineDesc.depthStencilState = DepthStencilState::noDepthTest();
pipelineDesc.rasterizerState = RasterizerState::noCull();
// 创建管线
auto pipeline = device->createPipeline(pipelineDesc);
if (!pipeline) {
E2D_LOG_ERROR("Failed to create pipeline during reload");
handle_ = ShaderHandle();
return false;
}
// 获取管线句柄
pipeline_ = PipelineHandle(pipeline.release());
E2D_LOG_INFO("Shader reloaded successfully");
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) {
std::string Shader::addVersionIfNeeded(const std::string &source,
bool isVertex) {
// 如果已经包含版本声明,直接返回
if (source.find("#version") != std::string::npos) {
return source;
@ -215,7 +191,7 @@ std::string Shader::addVersionIfNeeded(const std::string &source, GLenum type) {
std::string result = "#version 320 es\n";
// 片段着色器需要添加精度声明
if (type == GL_FRAGMENT_SHADER) {
if (!isVertex) {
result += "precision mediump float;\n";
}

View File

@ -1,46 +1,17 @@
#include <renderer/texture.h>
#include <renderer/rhi_module.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;
}
// RHI 纹理句柄是轻量级句柄,不需要显式释放
// 实际的纹理资源由 RHI 设备管理
handle_ = TextureHandle();
}
bool Texture::loadFromFile(const std::string& path) {
@ -75,81 +46,186 @@ bool Texture::loadFromFile(const std::string& path) {
}
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
// 释放旧纹理
handle_ = TextureHandle();
width_ = width;
height_ = height;
format_ = format;
// 创建纹理
glGenTextures(1, &texture_);
glBindTexture(GL_TEXTURE_2D, texture_);
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
// 设置纹理参数
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);
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 初始创建 1 层,后续生成 mipmap
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("Failed to create RHI texture");
return false;
}
// 上传纹理数据
glTexImage2D(GL_TEXTURE_2D, 0,
getTextureInternalFormat(format_),
width_, height_, 0,
getTextureFormat(format_),
GL_UNSIGNED_BYTE, data);
if (data) {
// 注意update 方法的参数需要根据实际 RHI 接口调整
// 这里假设 update 接受数据指针
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
// 生成 mipmap
// 注意generateMipmap 方法需要在 RHI 接口中实现
}
// 生成 mipmap
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
bool Texture::create(int width, int height, TextureFormat format) {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
// 释放旧纹理
handle_ = TextureHandle();
width_ = width;
height_ = height;
format_ = format;
// 创建纹理
glGenTextures(1, &texture_);
glBindTexture(GL_TEXTURE_2D, texture_);
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
// 设置纹理参数
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);
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 分配纹理存储(无数据)
glTexImage2D(GL_TEXTURE_2D, 0,
getTextureInternalFormat(format_),
width_, height_, 0,
getTextureFormat(format_),
GL_UNSIGNED_BYTE, nullptr);
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 不生成 mipmap
glBindTexture(GL_TEXTURE_2D, 0);
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("Failed to create RHI texture");
return false;
}
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
void Texture::bind(uint32_t slot) const {
if (texture_ != 0) {
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, texture_);
bool Texture::reloadFromFile(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 reload 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 = reloadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_LOG_INFO("Texture reloaded: {} ({}x{})", path, width_, height_);
}
return result;
}
bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
// 更新尺寸和格式
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 创建新的纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1;
// 创建新的 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("Failed to create RHI texture during reload");
return false;
}
// 上传纹理数据
if (data) {
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
}
// 替换旧的纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
// 辅助函数:获取每个像素的字节数
uint32_t Texture::getBytesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return 1;
case TextureFormat::RG8: return 2;
case TextureFormat::RGB8: return 3;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return 4;
case TextureFormat::Depth16: return 2;
case TextureFormat::Depth24: return 3;
case TextureFormat::Depth32F: return 4;
case TextureFormat::Depth24Stencil8: return 4;
case TextureFormat::Depth32FStencil8: return 5;
default: return 4;
}
}
void Texture::unbind() const {
glBindTexture(GL_TEXTURE_2D, 0);
}
} // namespace extra2d

View File

@ -0,0 +1,367 @@
#include <renderer/texture_atlas.h>
#include <utils/logger.h>
#include <algorithm>
#include <cstring>
namespace extra2d {
// ========================================
// TextureAtlas 实现
// ========================================
TextureAtlas::TextureAtlas() = default;
TextureAtlas::~TextureAtlas() {
shutdown();
}
TextureAtlas::TextureAtlas(TextureAtlas&& other) noexcept
: width_(other.width_)
, height_(other.height_)
, atlasTexture_(std::move(other.atlasTexture_))
, regions_(std::move(other.regions_))
, pendingTextures_(std::move(other.pendingTextures_))
, packContext_(std::move(other.packContext_))
, packNodes_(std::move(other.packNodes_))
, finalized_(other.finalized_) {
other.width_ = 0;
other.height_ = 0;
other.finalized_ = false;
}
TextureAtlas& TextureAtlas::operator=(TextureAtlas&& other) noexcept {
if (this != &other) {
shutdown();
width_ = other.width_;
height_ = other.height_;
atlasTexture_ = std::move(other.atlasTexture_);
regions_ = std::move(other.regions_);
pendingTextures_ = std::move(other.pendingTextures_);
packContext_ = std::move(other.packContext_);
packNodes_ = std::move(other.packNodes_);
finalized_ = other.finalized_;
other.width_ = 0;
other.height_ = 0;
other.finalized_ = false;
}
return *this;
}
bool TextureAtlas::initialize(int width, int height) {
shutdown();
if (width <= 0 || height <= 0) {
E2D_LOG_ERROR("TextureAtlas::initialize: Invalid size {}x{}", width, height);
return false;
}
width_ = width;
height_ = height;
// 初始化 stb_rect_pack
packContext_ = std::make_unique<stbrp_context>();
packNodes_.resize(width);
stbrp_init_target(packContext_.get(), width, height, packNodes_.data(), width);
E2D_LOG_DEBUG("TextureAtlas initialized: {}x{}", width, height);
return true;
}
void TextureAtlas::shutdown() {
atlasTexture_.reset();
regions_.clear();
pendingTextures_.clear();
packContext_.reset();
packNodes_.clear();
width_ = 0;
height_ = 0;
finalized_ = false;
}
bool TextureAtlas::addTexture(const std::string& name, Ptr<Texture> texture) {
if (finalized_) {
E2D_LOG_WARN("TextureAtlas::addTexture: Cannot add texture to finalized atlas");
return false;
}
if (!texture) {
E2D_LOG_WARN("TextureAtlas::addTexture: Texture is null");
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_LOG_WARN("TextureAtlas::addTexture: Texture '{}' already exists", name);
return false;
}
// 获取纹理尺寸
int texWidth = static_cast<int>(texture->getWidth());
int texHeight = static_cast<int>(texture->getHeight());
if (texWidth <= 0 || texHeight <= 0) {
E2D_LOG_WARN("TextureAtlas::addTexture: Invalid texture size {}x{}", texWidth, texHeight);
return false;
}
// 检查纹理是否适合图集
if (texWidth > width_ || texHeight > height_) {
E2D_LOG_WARN("TextureAtlas::addTexture: Texture {}x{} too large for atlas {}x{}",
texWidth, texHeight, width_, height_);
return false;
}
// 创建待处理纹理信息
PendingTexture pending;
pending.name = name;
pending.width = texWidth;
pending.height = texHeight;
pending.format = texture->getFormat();
// 获取纹理数据
size_t dataSize = texWidth * texHeight * 4; // 假设 RGBA
pending.data.resize(dataSize);
// TODO: 实现 Texture::getData() 方法来读取像素数据
// 暂时使用占位数据
std::fill(pending.data.begin(), pending.data.end(), 255);
pendingTextures_.push_back(std::move(pending));
E2D_LOG_DEBUG("TextureAtlas: Added texture '{}' ({}x{})", name, texWidth, texHeight);
return true;
}
bool TextureAtlas::addTextureData(const std::string& name, const uint8_t* data,
int width, int height, TextureFormat format) {
if (finalized_) {
E2D_LOG_WARN("TextureAtlas::addTextureData: Cannot add texture to finalized atlas");
return false;
}
if (!data || width <= 0 || height <= 0) {
E2D_LOG_WARN("TextureAtlas::addTextureData: Invalid parameters");
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_LOG_WARN("TextureAtlas::addTextureData: Texture '{}' already exists", name);
return false;
}
if (width > width_ || height > height_) {
E2D_LOG_WARN("TextureAtlas::addTextureData: Texture {}x{} too large for atlas {}x{}",
width, height, width_, height_);
return false;
}
PendingTexture pending;
pending.name = name;
pending.width = width;
pending.height = height;
pending.format = format;
// 复制像素数据
int channels = (format == TextureFormat::RGBA8) ? 4 : 3;
size_t dataSize = width * height * channels;
pending.data.resize(dataSize);
std::memcpy(pending.data.data(), data, dataSize);
pendingTextures_.push_back(std::move(pending));
E2D_LOG_DEBUG("TextureAtlas: Added texture data '{}' ({}x{})", name, width, height);
return true;
}
bool TextureAtlas::finalize() {
if (finalized_) {
return true;
}
if (pendingTextures_.empty()) {
E2D_LOG_WARN("TextureAtlas::finalize: No textures to pack");
return false;
}
// 准备矩形数组
std::vector<stbrp_rect> rects;
rects.reserve(pendingTextures_.size());
for (size_t i = 0; i < pendingTextures_.size(); ++i) {
stbrp_rect rect;
rect.id = static_cast<int>(i);
rect.w = pendingTextures_[i].width;
rect.h = pendingTextures_[i].height;
rect.x = 0;
rect.y = 0;
rect.was_packed = 0;
rects.push_back(rect);
}
// 执行打包
int result = stbrp_pack_rects(packContext_.get(), rects.data(), static_cast<int>(rects.size()));
if (!result) {
E2D_LOG_ERROR("TextureAtlas::finalize: Failed to pack all textures");
return false;
}
// 创建图集纹理数据
std::vector<uint8_t> atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景
// 处理打包结果
for (const auto& rect : rects) {
if (!rect.was_packed) {
E2D_LOG_WARN("TextureAtlas::finalize: Texture {} not packed", rect.id);
continue;
}
const auto& pending = pendingTextures_[rect.id];
// 创建区域信息
AtlasRegion region;
region.x = rect.x;
region.y = rect.y;
region.width = rect.w;
region.height = rect.h;
regions_[pending.name] = region;
// 复制像素数据到图集
// TODO: 实现正确的像素格式转换
// 目前假设所有纹理都是 RGBA8
for (int y = 0; y < pending.height; ++y) {
for (int x = 0; x < pending.width; ++x) {
int srcIdx = (y * pending.width + x) * 4;
int dstIdx = ((rect.y + y) * width_ + (rect.x + x)) * 4;
if (srcIdx + 3 < static_cast<int>(pending.data.size()) &&
dstIdx + 3 < static_cast<int>(atlasData.size())) {
atlasData[dstIdx + 0] = pending.data[srcIdx + 0];
atlasData[dstIdx + 1] = pending.data[srcIdx + 1];
atlasData[dstIdx + 2] = pending.data[srcIdx + 2];
atlasData[dstIdx + 3] = pending.data[srcIdx + 3];
}
}
}
E2D_LOG_DEBUG("TextureAtlas: Packed '{}' at ({}, {}) size {}x{}",
pending.name, rect.x, rect.y, rect.w, rect.h);
}
// 创建图集纹理
atlasTexture_ = makePtr<Texture>();
if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_, TextureFormat::RGBA8)) {
E2D_LOG_ERROR("TextureAtlas::finalize: Failed to create atlas texture");
return false;
}
// 清理待处理列表
pendingTextures_.clear();
finalized_ = true;
E2D_LOG_INFO("TextureAtlas finalized: {} textures packed into {}x{} atlas ({}% usage)",
regions_.size(), width_, height_,
static_cast<int>(getUsageRatio() * 100));
return true;
}
const AtlasRegion* TextureAtlas::getRegion(const std::string& name) const {
auto it = regions_.find(name);
if (it != regions_.end()) {
return &it->second;
}
return nullptr;
}
Rect TextureAtlas::getUVRect(const std::string& name) const {
const AtlasRegion* region = getRegion(name);
if (region) {
return region->getUVRect(width_, height_);
}
return Rect(0.0f, 0.0f, 1.0f, 1.0f);
}
bool TextureAtlas::hasTexture(const std::string& name) const {
return regions_.find(name) != regions_.end();
}
float TextureAtlas::getUsageRatio() const {
if (!finalized_ || regions_.empty()) {
return 0.0f;
}
int totalArea = 0;
for (const auto& pair : regions_) {
totalArea += pair.second.width * pair.second.height;
}
return static_cast<float>(totalArea) / (width_ * height_);
}
// ========================================
// AtlasBuilder 实现
// ========================================
Ptr<TextureAtlas> AtlasBuilder::build() {
if (textures_.empty()) {
E2D_LOG_WARN("AtlasBuilder::build: No textures to build");
return nullptr;
}
auto atlas = makePtr<TextureAtlas>();
if (!atlas->initialize(width_, height_)) {
return nullptr;
}
for (const auto& pair : textures_) {
atlas->addTexture(pair.first, pair.second);
}
if (!atlas->finalize()) {
return nullptr;
}
return atlas;
}
Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
if (textures_.empty()) {
E2D_LOG_WARN("AtlasBuilder::buildAuto: No textures to build");
return nullptr;
}
// 计算总面积
int totalArea = 0;
int maxWidth = 0;
int maxHeight = 0;
for (const auto& pair : textures_) {
if (pair.second) {
int w = static_cast<int>(pair.second->getWidth());
int h = static_cast<int>(pair.second->getHeight());
totalArea += w * h;
maxWidth = std::max(maxWidth, w);
maxHeight = std::max(maxHeight, h);
}
}
// 选择合适的大小2 的幂)
int atlasSize = 64;
while (atlasSize < maxWidth || atlasSize < maxHeight || atlasSize * atlasSize < totalArea * 1.5f) {
atlasSize *= 2;
if (atlasSize > 8192) {
E2D_LOG_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas");
return nullptr;
}
}
width_ = atlasSize;
height_ = atlasSize;
return build();
}
} // namespace extra2d

View File

@ -1,3 +1,5 @@
#include <renderer/rhi/rhi_buffer.h>
#include <renderer/rhi_module.h>
#include <renderer/uniform_buffer.h>
#include <utils/logger.h>
@ -5,146 +7,212 @@ namespace extra2d {
UniformBuffer::UniformBuffer() = default;
UniformBuffer::~UniformBuffer() {
destroy();
}
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);
glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding);
return true;
destroy();
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
size_ = size;
binding_ = binding;
// 创建 uniform 缓冲区描述
BufferDesc desc = BufferDesc::uniform(size);
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("Failed to create uniform buffer");
return false;
}
// 获取缓冲区句柄
handle_ = BufferHandle(buffer.release());
E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding);
return true;
}
void UniformBuffer::destroy() {
if (ubo_ != 0) {
glDeleteBuffers(1, &ubo_);
ubo_ = 0;
size_ = 0;
}
// RHIHandle 是轻量级句柄,不需要显式释放
// 实际的缓冲区资源由 RHI 设备管理
handle_ = BufferHandle();
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::update(const void *data, uint32_t size, uint32_t offset) {
if (!handle_.isValid() || data == nullptr)
return;
if (offset + size > size_) {
E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}",
offset, size, size_);
return;
}
// 通过 RHI 缓冲区接口直接更新数据
RHIBuffer *buffer = handle_.get();
if (buffer) {
buffer->update(data, size, offset);
}
}
void UniformBuffer::bind(uint32_t binding) {
if (ubo_ == 0) return;
glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_);
if (!handle_.isValid())
return;
// 记录绑定槽位
// 注意:实际的 GPU 绑定操作需要在渲染时通过 RHICommandList::setUniformBuffer
// 进行 示例commandList->setUniformBuffer(binding, handle_.get());
binding_ = binding;
}
RHIBuffer *UniformBuffer::getRHIBuffer() const { return handle_.get(); }
UniformBufferManager::UniformBufferManager() = default;
UniformBufferManager::~UniformBufferManager() {
shutdown();
}
UniformBufferManager::~UniformBufferManager() { shutdown(); }
UniformBufferManager::UniformBufferManager(UniformBufferManager&& other) noexcept
: globalUBO_(std::move(other.globalUBO_)),
UniformBufferManager::UniformBufferManager(
UniformBufferManager &&other) noexcept
: globalUBOs_(std::move(other.globalUBOs_)),
materialUBOPool_(std::move(other.materialUBOPool_)),
currentUBOIndex_(other.currentUBOIndex_) {
other.currentUBOIndex_ = 0;
currentUBOIndex_(other.currentUBOIndex_),
materialUBOBuffer_(std::move(other.materialUBOBuffer_)),
materialUBOBufferOffset_(other.materialUBOBufferOffset_),
currentMaterialUBO_(other.currentMaterialUBO_) {
other.currentUBOIndex_ = 0;
other.materialUBOBufferOffset_ = 0;
other.currentMaterialUBO_ = nullptr;
}
UniformBufferManager& UniformBufferManager::operator=(UniformBufferManager&& other) noexcept {
if (this != &other) {
shutdown();
UniformBufferManager &
UniformBufferManager::operator=(UniformBufferManager &&other) noexcept {
if (this != &other) {
shutdown();
globalUBO_ = std::move(other.globalUBO_);
materialUBOPool_ = std::move(other.materialUBOPool_);
currentUBOIndex_ = other.currentUBOIndex_;
globalUBOs_ = std::move(other.globalUBOs_);
materialUBOPool_ = std::move(other.materialUBOPool_);
currentUBOIndex_ = other.currentUBOIndex_;
materialUBOBuffer_ = std::move(other.materialUBOBuffer_);
materialUBOBufferOffset_ = other.materialUBOBufferOffset_;
currentMaterialUBO_ = other.currentMaterialUBO_;
other.currentUBOIndex_ = 0;
}
return *this;
other.currentUBOIndex_ = 0;
other.materialUBOBufferOffset_ = 0;
other.currentMaterialUBO_ = nullptr;
}
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 双缓冲
for (size_t i = 0; i < globalUBOs_.size(); ++i) {
globalUBOs_[i] = std::make_unique<UniformBuffer>();
if (!globalUBOs_[i]->create(UniformBufferManager::GLOBAL_UBO_SIZE,
UniformBufferManager::GLOBAL_UBO_BINDING)) {
E2D_LOG_ERROR("Failed to create global UBO {}", i);
return false;
}
// 预分配材质 UBO 池
materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE);
E2D_LOG_INFO("UniformBufferManager initialized");
return true;
}
// 预分配材质 UBO 池
materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE);
// 预分配材质 UBO CPU 缓冲区
materialUBOBuffer_.resize(MATERIAL_UBO_BUFFER_SIZE);
E2D_LOG_INFO("UniformBufferManager initialized with double buffering");
return true;
}
void UniformBufferManager::shutdown() {
materialUBOPool_.clear();
globalUBO_.reset();
currentUBOIndex_ = 0;
E2D_LOG_INFO("UniformBufferManager shutdown");
materialUBOPool_.clear();
for (auto& ubo : globalUBOs_) {
ubo.reset();
}
currentUBOIndex_ = 0;
materialUBOBuffer_.clear();
materialUBOBufferOffset_ = 0;
currentMaterialUBO_ = nullptr;
E2D_LOG_INFO("UniformBufferManager shutdown");
}
UniformBuffer* UniformBufferManager::getGlobalUBO() {
return globalUBO_.get();
UniformBuffer *UniformBufferManager::getGlobalUBO(uint32_t frameIndex) {
// 使用双缓冲,根据帧索引选择 UBO
size_t index = frameIndex % 2;
return globalUBOs_[index].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();
}
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;
}
// 需要创建新的 UBO
auto ubo = std::make_unique<UniformBuffer>();
if (!ubo->create(size, UniformBufferManager::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;
currentUBOIndex_ = 0;
materialUBOBufferOffset_ = 0;
currentMaterialUBO_ = nullptr;
}
void UniformBufferManager::updateGlobalUBO(const void* data, uint32_t size) {
if (globalUBO_) {
globalUBO_->update(data, size);
globalUBO_->bind(GLOBAL_UBO_BINDING);
}
void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size, uint32_t frameIndex) {
// 使用双缓冲更新全局 UBO
size_t index = frameIndex % 2;
if (globalUBOs_[index]) {
globalUBOs_[index]->update(data, size);
globalUBOs_[index]->bind(UniformBufferManager::GLOBAL_UBO_BINDING);
}
}
void UniformBufferManager::batchUpdateMaterialUBO(const void *data, uint32_t size, uint32_t offset) {
if (!data || size == 0 || offset + size > materialUBOBuffer_.size()) {
E2D_LOG_WARN("Invalid batch update parameters");
return;
}
// 复制数据到 CPU 缓冲区
std::memcpy(materialUBOBuffer_.data() + offset, data, size);
}
void UniformBufferManager::flushMaterialUBO() {
if (materialUBOBufferOffset_ == 0 || !currentMaterialUBO_) {
return;
}
// 一次性将 CPU 缓冲区数据更新到 GPU
currentMaterialUBO_->update(materialUBOBuffer_.data(), materialUBOBufferOffset_, 0);
materialUBOBufferOffset_ = 0;
}
} // namespace extra2d

View File

@ -33,8 +33,9 @@ void SpriteRenderer::render() {
uint32_t textureId = texture_.index();
cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF);
cmd.drawMesh.mesh = MeshHandle::invalid();
cmd.drawMesh.material = material_.isValid() ? material_ : MaterialHandle::invalid();
// 如果没有指定网格,使用默认的四边形网格
cmd.drawMesh.mesh = Handle<Mesh>::invalid(); // RendererModule 会使用默认网格
cmd.drawMesh.material = material_.isValid() ? material_ : Handle<Material>::invalid();
cmd.setTransform(worldTransform);
cmd.setColor(color_);

View File

@ -130,8 +130,9 @@ void Director::render() {
CameraComponent *camera = runningScene_->getMainCamera();
if (camera) {
Mat4 viewProj = camera->getViewProjectionMatrix();
events::OnRenderSetCamera::emit(viewProj);
} else {
E2D_LOG_WARN("Director::render: No main camera set!");
}
runningScene_->render();

View File

@ -8,10 +8,6 @@ SceneModule::~SceneModule() {
shutdown();
}
SceneModule::SceneModule(SceneModule &&) noexcept = default;
SceneModule &SceneModule::operator=(SceneModule &&) noexcept = default;
bool SceneModule::init() {
// 创建导演
director_ = makePtr<Director>();

View File

@ -7,24 +7,6 @@ TimerModule::TimerModule() = default;
TimerModule::~TimerModule() { shutdown(); }
TimerModule::TimerModule(TimerModule &&other) noexcept
: timers_(std::move(other.timers_)),
pendingRemove_(std::move(other.pendingRemove_)), nextId_(other.nextId_),
timeScale_(other.timeScale_), inUpdate_(other.inUpdate_) {}
TimerModule &TimerModule::operator=(TimerModule &&other) noexcept {
if (this != &other) {
shutdown();
timers_ = std::move(other.timers_);
pendingRemove_ = std::move(other.pendingRemove_);
nextId_ = other.nextId_;
timeScale_ = other.timeScale_;
inUpdate_ = other.inUpdate_;
}
return *this;
}
bool TimerModule::init() {
// 初始化完成,等待 OnUpdate 事件
return true;