Compare commits
No commits in common. "44e0d65f10360829eafdbde6a581b87e823a339f" and "f7e4f89ccad0f11c96dd35846291f572a88596d1" have entirely different histories.
44e0d65f10
...
f7e4f89cca
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(git diff:*)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -31,8 +31,8 @@ void PlayerNode::onUpdate(float dt) {
|
||||||
|
|
||||||
setPosition(x, y);
|
setPosition(x, y);
|
||||||
|
|
||||||
// 根据移动方向旋转(顺时针,使用负角度)
|
// 根据移动方向旋转
|
||||||
float angle = -time_ * 0.5f * 57.2958f + 90.0f;
|
float angle = time_ * 0.5f * 57.2958f + 90.0f;
|
||||||
setRotation(angle);
|
setRotation(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,8 +56,8 @@ RotatingDecoration::RotatingDecoration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RotatingDecoration::onUpdate(float dt) {
|
void RotatingDecoration::onUpdate(float dt) {
|
||||||
// 自转(顺时针,使用负角度)
|
// 自转
|
||||||
setRotation(getRotation() - rotationSpeed_ * dt);
|
setRotation(getRotation() + rotationSpeed_ * dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
@ -89,7 +89,6 @@ void GameScene::onExit() {
|
||||||
Scene::onExit();
|
Scene::onExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameScene::update(float dt) {
|
void GameScene::update(float dt) {
|
||||||
Scene::update(dt);
|
Scene::update(dt);
|
||||||
|
|
||||||
|
|
@ -136,9 +135,12 @@ void GameScene::createDecorations() {
|
||||||
float r = std::abs(hue * 6.0f - 3.0f) - 1.0f;
|
float r = std::abs(hue * 6.0f - 3.0f) - 1.0f;
|
||||||
float g = 2.0f - std::abs(hue * 6.0f - 2.0f);
|
float g = 2.0f - std::abs(hue * 6.0f - 2.0f);
|
||||||
float b = 2.0f - std::abs(hue * 6.0f - 4.0f);
|
float b = 2.0f - std::abs(hue * 6.0f - 4.0f);
|
||||||
sprite->setColor(Color(std::max(0.0f, std::min(1.0f, r)),
|
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, g)),
|
||||||
std::max(0.0f, std::min(1.0f, b)), 1.0f));
|
std::max(0.0f, std::min(1.0f, b)),
|
||||||
|
1.0f
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
decorations_.push_back(decoration);
|
decorations_.push_back(decoration);
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
@ -28,25 +28,9 @@ target("scene_graph_demo")
|
||||||
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
||||||
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.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
|
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
||||||
os.vrunv(nacptool, {"--create", "Scene Graph Demo", "Extra2D Team", "1.0.0", nacp_file})
|
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
|
if os.isdir(romfs) then
|
||||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
|
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -4,42 +4,24 @@
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <chrono>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 资源加载状态
|
|
||||||
*/
|
|
||||||
enum class AssetLoadState {
|
|
||||||
Unloaded, ///< 未加载
|
|
||||||
Loading, ///< 加载中
|
|
||||||
Loaded, ///< 已加载
|
|
||||||
Failed ///< 加载失败
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 密集存储的资源仓库
|
* @brief 密集存储的资源仓库
|
||||||
*
|
*
|
||||||
* 使用 Dense Vec 存储,类似 ECS 的组件存储。
|
* 使用 Dense Vec 存储,类似 ECS 的组件存储。
|
||||||
* 提供 O(1) 的插入、删除、访问。
|
* 提供 O(1) 的插入、删除、访问。
|
||||||
* 支持引用统计和 LRU(最近最少使用)缓存策略。
|
|
||||||
*
|
*
|
||||||
* @tparam T 资源类型
|
* @tparam T 资源类型
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class AssetStorage {
|
class AssetStorage {
|
||||||
public:
|
public:
|
||||||
using TimePoint = std::chrono::steady_clock::time_point;
|
|
||||||
|
|
||||||
struct Slot {
|
struct Slot {
|
||||||
Ptr<T> asset;
|
Ptr<T> asset;
|
||||||
typename Handle<T>::Generation generation = 0;
|
typename Handle<T>::Generation generation = 0;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
AssetLoadState loadState = AssetLoadState::Unloaded;
|
|
||||||
TimePoint lastAccessTime; ///< 最后访问时间(用于 LRU)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AssetStorage() = default;
|
AssetStorage() = default;
|
||||||
|
|
@ -55,23 +37,18 @@ public:
|
||||||
*/
|
*/
|
||||||
Handle<T> insert(Ptr<T> asset) {
|
Handle<T> insert(Ptr<T> asset) {
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
auto now = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
if (!freeIndices_.empty()) {
|
if (!freeIndices_.empty()) {
|
||||||
index = freeIndices_.back();
|
index = freeIndices_.back();
|
||||||
freeIndices_.pop_back();
|
freeIndices_.pop_back();
|
||||||
slots_[index].asset = std::move(asset);
|
slots_[index].asset = std::move(asset);
|
||||||
slots_[index].active = true;
|
slots_[index].active = true;
|
||||||
slots_[index].loadState = AssetLoadState::Loaded;
|
|
||||||
slots_[index].lastAccessTime = now;
|
|
||||||
} else {
|
} else {
|
||||||
index = static_cast<uint32_t>(slots_.size());
|
index = static_cast<uint32_t>(slots_.size());
|
||||||
Slot slot;
|
Slot slot;
|
||||||
slot.asset = std::move(asset);
|
slot.asset = std::move(asset);
|
||||||
slot.generation = nextGeneration_++;
|
slot.generation = nextGeneration_++;
|
||||||
slot.active = true;
|
slot.active = true;
|
||||||
slot.loadState = AssetLoadState::Loaded;
|
|
||||||
slot.lastAccessTime = now;
|
|
||||||
slots_.push_back(std::move(slot));
|
slots_.push_back(std::move(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,18 +76,7 @@ public:
|
||||||
* @param handle 资源句柄
|
* @param handle 资源句柄
|
||||||
* @return 资源指针
|
* @return 资源指针
|
||||||
*/
|
*/
|
||||||
T* get(Handle<T> handle) {
|
T* get(Handle<T> handle) const {
|
||||||
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;
|
if (!isValid(handle)) return nullptr;
|
||||||
return slots_[handle.index()].asset.get();
|
return slots_[handle.index()].asset.get();
|
||||||
}
|
}
|
||||||
|
|
@ -120,17 +86,6 @@ public:
|
||||||
* @param handle 资源句柄
|
* @param handle 资源句柄
|
||||||
* @return 资源智能指针
|
* @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 {
|
Ptr<T> getPtr(Handle<T> handle) const {
|
||||||
if (!isValid(handle)) return Ptr<T>();
|
if (!isValid(handle)) return Ptr<T>();
|
||||||
return slots_[handle.index()].asset;
|
return slots_[handle.index()].asset;
|
||||||
|
|
@ -149,44 +104,6 @@ public:
|
||||||
return slot.active && slot.generation == handle.generation();
|
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 遍历所有资源
|
* @brief 遍历所有资源
|
||||||
* @param func 回调函数,签名 void(Handle<T>, T*)
|
* @param func 回调函数,签名 void(Handle<T>, T*)
|
||||||
|
|
@ -220,111 +137,6 @@ public:
|
||||||
*/
|
*/
|
||||||
size_t capacity() const { return slots_.size(); }
|
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 查找可以卸载的资源(引用计数为1的资源,只有AssetStorage自己持有引用)
|
|
||||||
* @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:
|
private:
|
||||||
std::vector<Slot> slots_;
|
std::vector<Slot> slots_;
|
||||||
std::vector<uint32_t> freeIndices_;
|
std::vector<uint32_t> freeIndices_;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <assets/asset_storage.h>
|
|
||||||
#include <assets/asset_loader.h>
|
#include <assets/asset_loader.h>
|
||||||
|
#include <assets/asset_storage.h>
|
||||||
#include <assets/handle.h>
|
#include <assets/handle.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
@ -33,8 +34,7 @@ namespace extra2d {
|
||||||
* - Handle<T>: 轻量级句柄
|
* - Handle<T>: 轻量级句柄
|
||||||
*/
|
*/
|
||||||
class AssetsModule : public Module {
|
class AssetsModule : public Module {
|
||||||
// 优先级为 3,在 FileModule (优先级 2) 之后初始化
|
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2)
|
||||||
E2D_REGISTER_MODULE(AssetsModule, "Assets", 3)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AssetsModule();
|
AssetsModule();
|
||||||
|
|
@ -103,52 +103,11 @@ public:
|
||||||
*/
|
*/
|
||||||
template <typename T> bool isValid(Handle<T> handle) const;
|
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 移除资源
|
* @brief 移除资源
|
||||||
*/
|
*/
|
||||||
template <typename T> void remove(Handle<T> handle);
|
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);
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 批量加载(目录/包)
|
// 批量加载(目录/包)
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -256,12 +215,6 @@ private:
|
||||||
void addFileWatch(const std::string &path, Handle<Texture> handle);
|
void addFileWatch(const std::string &path, Handle<Texture> handle);
|
||||||
void addFileWatch(const std::string &path, Handle<Shader> handle);
|
void addFileWatch(const std::string &path, Handle<Shader> handle);
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief 内部实现:添加文件监控(减少重复代码)
|
|
||||||
*/
|
|
||||||
void addFileWatchInternal(const std::string &path, FileWatchInfo &&info);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重新加载纹理
|
* @brief 重新加载纹理
|
||||||
*/
|
*/
|
||||||
|
|
@ -517,75 +470,6 @@ template <> inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
|
||||||
return meshes_.isValid(handle);
|
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 <typename T> void AssetsModule::remove(Handle<T> handle) {}
|
||||||
|
|
||||||
template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
|
template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
|
||||||
|
|
@ -605,108 +489,6 @@ template <> inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
|
||||||
meshes_.remove(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>
|
template <typename T>
|
||||||
std::vector<Handle<T>> AssetsModule::loadDir(const std::string &directory,
|
std::vector<Handle<T>> AssetsModule::loadDir(const std::string &directory,
|
||||||
const std::string &pattern,
|
const std::string &pattern,
|
||||||
|
|
@ -727,10 +509,6 @@ template <>
|
||||||
void AssetsModule::loadAsync<Texture>(
|
void AssetsModule::loadAsync<Texture>(
|
||||||
const std::string &path, std::function<void(Handle<Texture>)> callback);
|
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>
|
template <typename T>
|
||||||
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {}
|
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#include <module/module.h>
|
#include <module/module.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -68,35 +67,9 @@ public:
|
||||||
return registrations_;
|
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:
|
private:
|
||||||
ModuleRegistry() = default;
|
ModuleRegistry() = default;
|
||||||
std::vector<ModuleInfo> registrations_;
|
std::vector<ModuleInfo> registrations_;
|
||||||
std::unordered_map<std::type_index, Module*> instances_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -135,14 +108,4 @@ public: \
|
||||||
#define E2D_REGISTER_MODULE_SIMPLE(ClassName) \
|
#define E2D_REGISTER_MODULE_SIMPLE(ClassName) \
|
||||||
E2D_REGISTER_MODULE(ClassName, #ClassName, 100)
|
E2D_REGISTER_MODULE(ClassName, #ClassName, 100)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取模块实例的辅助函数
|
|
||||||
* @tparam T 模块类型
|
|
||||||
* @return 模块实例指针
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
T* getModule() {
|
|
||||||
return ModuleRegistry::instance().getModule<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#include <module/module.h>
|
#include <module/module.h>
|
||||||
#include <module/module_registry.h>
|
#include <module/module_registry.h>
|
||||||
#include <string>
|
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -38,8 +38,8 @@ struct FileData {
|
||||||
* 非单例设计,通过 Context 管理生命周期
|
* 非单例设计,通过 Context 管理生命周期
|
||||||
*/
|
*/
|
||||||
class FileModule : public Module {
|
class FileModule : public Module {
|
||||||
// 优先级为 2,系统级模块,在 TimerModule (优先级 4) 之前初始化
|
// 自动注册到模块系统,优先级为 20(系统模块)
|
||||||
E2D_REGISTER_MODULE(FileModule, "File", 2)
|
E2D_REGISTER_MODULE(FileModule, "File", 20)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileModule();
|
FileModule();
|
||||||
|
|
@ -152,28 +152,9 @@ public:
|
||||||
*/
|
*/
|
||||||
std::string assetPath(const std::string& relPath) const;
|
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:
|
private:
|
||||||
std::string assetRoot_;
|
std::string assetRoot_;
|
||||||
std::string writableDir_;
|
std::string writableDir_;
|
||||||
std::string romfsRoot_ = "romfs:/";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -172,18 +172,20 @@ using TouchCb = std::function<void(const TouchPoint &)>;
|
||||||
* 使用新的 Module 基类,支持自动注册
|
* 使用新的 Module 基类,支持自动注册
|
||||||
*/
|
*/
|
||||||
class InputModule : public Module {
|
class InputModule : public Module {
|
||||||
// 优先级为 7,在 TimerModule (优先级 6) 之后初始化
|
// 自动注册到模块系统,优先级为 10
|
||||||
E2D_REGISTER_MODULE(InputModule, "Input", 7)
|
E2D_REGISTER_MODULE(InputModule, "Input", 10)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InputModule();
|
InputModule();
|
||||||
~InputModule() override;
|
~InputModule() override;
|
||||||
|
|
||||||
// 禁止拷贝和移动
|
// 禁止拷贝
|
||||||
InputModule(const InputModule &) = delete;
|
InputModule(const InputModule &) = delete;
|
||||||
InputModule &operator=(const InputModule &) = delete;
|
InputModule &operator=(const InputModule &) = delete;
|
||||||
InputModule(InputModule &&) = delete;
|
|
||||||
InputModule &operator=(InputModule &&) = delete;
|
// 允许移动
|
||||||
|
InputModule(InputModule &&) noexcept;
|
||||||
|
InputModule &operator=(InputModule &&) noexcept;
|
||||||
|
|
||||||
// Module 接口实现
|
// Module 接口实现
|
||||||
bool init() override;
|
bool init() override;
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,13 @@ public:
|
||||||
WindowModule();
|
WindowModule();
|
||||||
~WindowModule() override;
|
~WindowModule() override;
|
||||||
|
|
||||||
// 禁止拷贝和移动
|
// 禁止拷贝
|
||||||
WindowModule(const WindowModule &) = delete;
|
WindowModule(const WindowModule &) = delete;
|
||||||
WindowModule &operator=(const WindowModule &) = delete;
|
WindowModule &operator=(const WindowModule &) = delete;
|
||||||
WindowModule(WindowModule &&) = delete;
|
|
||||||
WindowModule &operator=(WindowModule &&) = delete;
|
// 允许移动
|
||||||
|
WindowModule(WindowModule &&) noexcept;
|
||||||
|
WindowModule &operator=(WindowModule &&) noexcept;
|
||||||
|
|
||||||
// Module 接口实现
|
// Module 接口实现
|
||||||
bool init() override;
|
bool init() override;
|
||||||
|
|
@ -119,6 +121,28 @@ public:
|
||||||
*/
|
*/
|
||||||
bool shouldClose() const { return shouldClose_; }
|
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:
|
private:
|
||||||
void handleWindowEvent(const SDL_WindowEvent &evt);
|
void handleWindowEvent(const SDL_WindowEvent &evt);
|
||||||
|
|
||||||
|
|
@ -138,6 +162,7 @@ private:
|
||||||
void onRenderPresent();
|
void onRenderPresent();
|
||||||
|
|
||||||
SDL_Window *window_ = nullptr;
|
SDL_Window *window_ = nullptr;
|
||||||
|
SDL_GLContext glContext_ = nullptr;
|
||||||
bool shouldClose_ = false;
|
bool shouldClose_ = false;
|
||||||
|
|
||||||
CloseCb onClose_;
|
CloseCb onClose_;
|
||||||
|
|
|
||||||
|
|
@ -1,424 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,321 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <renderer/rhi/rhi.h>
|
|
||||||
#include <renderer/shader.h>
|
|
||||||
#include <renderer/texture.h>
|
|
||||||
#include <types/ptr/ref_counted.h>
|
#include <types/ptr/ref_counted.h>
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
#include <types/math/vec2.h>
|
#include <types/math/vec2.h>
|
||||||
#include <types/math/color.h>
|
#include <types/math/color.h>
|
||||||
|
#include <renderer/render_types.h>
|
||||||
|
#include <renderer/shader.h>
|
||||||
|
#include <renderer/uniform_buffer.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -15,37 +15,7 @@ namespace extra2d {
|
||||||
|
|
||||||
// 前向声明
|
// 前向声明
|
||||||
class Material;
|
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 材质参数信息
|
* @brief 材质参数信息
|
||||||
|
|
@ -60,7 +30,7 @@ struct MaterialParamInfo {
|
||||||
* @brief 纹理槽位信息
|
* @brief 纹理槽位信息
|
||||||
*/
|
*/
|
||||||
struct TextureSlot {
|
struct TextureSlot {
|
||||||
Ptr<Texture> texture;
|
Handle<Texture> handle;
|
||||||
uint32_t slot;
|
uint32_t slot;
|
||||||
std::string uniformName;
|
std::string uniformName;
|
||||||
};
|
};
|
||||||
|
|
@ -109,9 +79,7 @@ public:
|
||||||
bool isFinalized() const { return finalized_; }
|
bool isFinalized() const { return finalized_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 使用vector保持参数添加顺序,确保与着色器中的声明顺序一致
|
std::unordered_map<std::string, MaterialParamInfo> params_;
|
||||||
std::vector<std::pair<std::string, MaterialParamInfo>> params_;
|
|
||||||
std::unordered_map<std::string, size_t> paramIndexMap_; // 用于快速查找
|
|
||||||
uint32_t bufferSize_ = 0;
|
uint32_t bufferSize_ = 0;
|
||||||
bool finalized_ = false;
|
bool finalized_ = false;
|
||||||
};
|
};
|
||||||
|
|
@ -119,7 +87,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief 材质类
|
* @brief 材质类
|
||||||
*
|
*
|
||||||
* 基于 RHI 的材质包装类,管理:
|
* 作为着色器和纹理的中间层,管理:
|
||||||
* - 着色器程序
|
* - 着色器程序
|
||||||
* - 材质参数(通过 UBO 上传)
|
* - 材质参数(通过 UBO 上传)
|
||||||
* - 纹理绑定
|
* - 纹理绑定
|
||||||
|
|
@ -144,7 +112,10 @@ private:
|
||||||
* material->setFloat("uOpacity", 1.0f);
|
* material->setFloat("uOpacity", 1.0f);
|
||||||
*
|
*
|
||||||
* // 设置纹理
|
* // 设置纹理
|
||||||
* material->setTexture("uTexture", texture, 0);
|
* material->setTexture("uTexture", textureHandle, 0);
|
||||||
|
*
|
||||||
|
* // 注册到渲染器
|
||||||
|
* MaterialHandle handle = renderer->registerMaterial(material);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
class Material : public RefCounted {
|
class Material : public RefCounted {
|
||||||
|
|
@ -176,12 +147,6 @@ public:
|
||||||
*/
|
*/
|
||||||
Ptr<Shader> getShader() const { return shader_; }
|
Ptr<Shader> getShader() const { return shader_; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 RHI 管线句柄
|
|
||||||
* @return RHI 管线句柄
|
|
||||||
*/
|
|
||||||
PipelineHandle getPipeline() const;
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 参数设置
|
// 参数设置
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
@ -203,10 +168,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 设置 vec4 参数
|
* @brief 设置 vec4 参数
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param x X 分量
|
* @param value 值
|
||||||
* @param y Y 分量
|
|
||||||
* @param z Z 分量
|
|
||||||
* @param w W 分量
|
|
||||||
*/
|
*/
|
||||||
void setVec4(const std::string& name, float x, float y, float z, float w);
|
void setVec4(const std::string& name, float x, float y, float z, float w);
|
||||||
|
|
||||||
|
|
@ -231,10 +193,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 设置纹理
|
* @brief 设置纹理
|
||||||
* @param uniformName 着色器中的采样器 uniform 名称
|
* @param uniformName 着色器中的采样器 uniform 名称
|
||||||
* @param texture 纹理
|
* @param texture 纹理句柄
|
||||||
* @param slot 纹理槽位(0-15)
|
* @param slot 纹理槽位(0-15)
|
||||||
*/
|
*/
|
||||||
void setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot);
|
void setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取所有纹理槽位
|
* @brief 获取所有纹理槽位
|
||||||
|
|
@ -263,11 +225,17 @@ public:
|
||||||
*/
|
*/
|
||||||
uint32_t getDataSize() const { return static_cast<uint32_t>(data_.size()); }
|
uint32_t getDataSize() const { return static_cast<uint32_t>(data_.size()); }
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 应用材质(渲染时调用)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取材质布局
|
* @brief 应用材质
|
||||||
* @return 材质布局
|
*
|
||||||
|
* 绑定着色器、上传 UBO 数据
|
||||||
|
* @param uboManager UBO 管理器
|
||||||
*/
|
*/
|
||||||
Ptr<MaterialLayout> getLayout() const { return layout_; }
|
void apply(UniformBufferManager& uboManager);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ptr<MaterialLayout> layout_; // 材质布局
|
Ptr<MaterialLayout> layout_; // 材质布局
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <renderer/rhi/rhi.h>
|
|
||||||
#include <types/math/color.h>
|
#include <types/math/color.h>
|
||||||
#include <types/math/rect.h>
|
#include <types/math/rect.h>
|
||||||
#include <types/math/vec2.h>
|
#include <types/math/vec2.h>
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
#include <types/ptr/ref_counted.h>
|
#include <types/ptr/ref_counted.h>
|
||||||
|
|
||||||
|
// 前向声明 OpenGL 类型
|
||||||
|
typedef unsigned int GLuint;
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,7 +27,7 @@ struct Vertex {
|
||||||
/**
|
/**
|
||||||
* @brief 网格类
|
* @brief 网格类
|
||||||
*
|
*
|
||||||
* 基于 RHI 的网格包装类,管理顶点缓冲区和索引缓冲区
|
* 管理 OpenGL 顶点数组对象和缓冲区
|
||||||
* 支持静态和动态顶点数据
|
* 支持静态和动态顶点数据
|
||||||
*/
|
*/
|
||||||
class Mesh : public RefCounted {
|
class Mesh : public RefCounted {
|
||||||
|
|
@ -62,6 +64,27 @@ public:
|
||||||
*/
|
*/
|
||||||
void updateVertices(const Vertex *vertices, uint32_t count, uint32_t offset);
|
void updateVertices(const Vertex *vertices, uint32_t count, uint32_t offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定网格
|
||||||
|
*/
|
||||||
|
void bind() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑网格
|
||||||
|
*/
|
||||||
|
void unbind() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制网格
|
||||||
|
*/
|
||||||
|
void draw() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 实例化绘制
|
||||||
|
* @param instanceCount 实例数量
|
||||||
|
*/
|
||||||
|
void drawInstanced(uint32_t instanceCount) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取顶点数量
|
* @brief 获取顶点数量
|
||||||
* @return 顶点数量
|
* @return 顶点数量
|
||||||
|
|
@ -74,24 +97,6 @@ public:
|
||||||
*/
|
*/
|
||||||
uint32_t getIndexCount() const { return indexCount_; }
|
uint32_t getIndexCount() const { return indexCount_; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取顶点缓冲区句柄
|
|
||||||
* @return 顶点缓冲区句柄
|
|
||||||
*/
|
|
||||||
BufferHandle getVertexBuffer() const { return vertexBuffer_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取索引缓冲区句柄
|
|
||||||
* @return 索引缓冲区句柄
|
|
||||||
*/
|
|
||||||
BufferHandle getIndexBuffer() const { return indexBuffer_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否有索引数据
|
|
||||||
* @return 是否有索引数据
|
|
||||||
*/
|
|
||||||
bool hasIndices() const { return indexCount_ > 0; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建标准四边形
|
* @brief 创建标准四边形
|
||||||
* @param size 四边形大小
|
* @param size 四边形大小
|
||||||
|
|
@ -102,8 +107,9 @@ public:
|
||||||
const Rect &uv = Rect(0, 0, 1, 1));
|
const Rect &uv = Rect(0, 0, 1, 1));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferHandle vertexBuffer_; // 顶点缓冲区句柄
|
GLuint vao_ = 0; // 顶点数组对象
|
||||||
BufferHandle indexBuffer_; // 索引缓冲区句柄
|
GLuint vbo_ = 0; // 顶点缓冲区
|
||||||
|
GLuint ibo_ = 0; // 索引缓冲区
|
||||||
uint32_t vertexCount_ = 0; // 顶点数量
|
uint32_t vertexCount_ = 0; // 顶点数量
|
||||||
uint32_t indexCount_ = 0; // 索引数量
|
uint32_t indexCount_ = 0; // 索引数量
|
||||||
uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量
|
uint32_t vertexCapacity_ = 0; // 顶点缓冲区容量
|
||||||
|
|
|
||||||
|
|
@ -1,366 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <assets/handle.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
|
#include <types/math/vec2.h>
|
||||||
#include <types/math/color.h>
|
#include <types/math/color.h>
|
||||||
#include <types/math/transform.h>
|
#include <types/math/transform.h>
|
||||||
#include <types/math/vec2.h>
|
#include <assets/handle.h>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -14,17 +15,79 @@ class Material;
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// 注意:资源句柄直接使用 Handle<T>,与 RHI 的 TextureHandle 等区分
|
// 资源句柄类型(使用新的 Handle<T>)
|
||||||
|
using MaterialHandle = Handle<Material>;
|
||||||
|
using MeshHandle = Handle<Mesh>;
|
||||||
|
using TextureHandle = Handle<Texture>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 渲染命令类型
|
* @brief 渲染命令类型
|
||||||
*/
|
*/
|
||||||
enum class RenderCommandType : uint8_t {
|
enum class RenderCommandType : uint8_t {
|
||||||
DrawMesh, // 绘制网格
|
DrawMesh, // 绘制网格
|
||||||
|
DrawMeshInstanced, // 实例化绘制
|
||||||
SetViewport, // 设置视口
|
SetViewport, // 设置视口
|
||||||
Clear // 清除缓冲区
|
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 渲染命令结构
|
* @brief 渲染命令结构
|
||||||
*
|
*
|
||||||
|
|
@ -37,14 +100,22 @@ struct RenderCommand {
|
||||||
|
|
||||||
// 绘制网格命令数据
|
// 绘制网格命令数据
|
||||||
struct DrawMeshData {
|
struct DrawMeshData {
|
||||||
Handle<Mesh> mesh; // 网格句柄
|
MeshHandle mesh; // 网格句柄
|
||||||
Handle<Material> material; // 材质句柄
|
MaterialHandle material; // 材质句柄
|
||||||
Vec2 pos; // 位置
|
Vec2 pos; // 位置
|
||||||
Vec2 scale; // 缩放
|
Vec2 scale; // 缩放
|
||||||
float rot; // 旋转角度
|
float rot; // 旋转角度
|
||||||
Color color; // 顶点颜色
|
Color color; // 顶点颜色
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 实例化绘制命令数据
|
||||||
|
struct DrawInstancedData {
|
||||||
|
MeshHandle mesh; // 网格句柄
|
||||||
|
MaterialHandle material; // 材质句柄
|
||||||
|
uint32_t instanceCount; // 实例数量
|
||||||
|
uint32_t instanceOffset; // 实例数据偏移
|
||||||
|
};
|
||||||
|
|
||||||
// 设置视口命令数据
|
// 设置视口命令数据
|
||||||
struct ViewportData {
|
struct ViewportData {
|
||||||
int32_t x, y; // 视口位置
|
int32_t x, y; // 视口位置
|
||||||
|
|
@ -59,6 +130,7 @@ struct RenderCommand {
|
||||||
|
|
||||||
union {
|
union {
|
||||||
DrawMeshData drawMesh;
|
DrawMeshData drawMesh;
|
||||||
|
DrawInstancedData drawInstanced;
|
||||||
ViewportData viewport;
|
ViewportData viewport;
|
||||||
ClearData clear;
|
ClearData clear;
|
||||||
};
|
};
|
||||||
|
|
@ -67,8 +139,8 @@ struct RenderCommand {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
||||||
drawMesh.mesh = Handle<Mesh>::invalid();
|
drawMesh.mesh = MeshHandle::invalid();
|
||||||
drawMesh.material = Handle<Material>::invalid();
|
drawMesh.material = MaterialHandle::invalid();
|
||||||
drawMesh.pos = Vec2(0.0f, 0.0f);
|
drawMesh.pos = Vec2(0.0f, 0.0f);
|
||||||
drawMesh.scale = Vec2(1.0f, 1.0f);
|
drawMesh.scale = Vec2(1.0f, 1.0f);
|
||||||
drawMesh.rot = 0.0f;
|
drawMesh.rot = 0.0f;
|
||||||
|
|
@ -87,7 +159,9 @@ struct RenderCommand {
|
||||||
/**
|
/**
|
||||||
* @brief 设置顶点颜色
|
* @brief 设置顶点颜色
|
||||||
*/
|
*/
|
||||||
void setColor(const Color &c) { drawMesh.color = c; }
|
void setColor(const Color& c) {
|
||||||
|
drawMesh.color = c;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 Transform
|
* @brief 获取 Transform
|
||||||
|
|
@ -99,21 +173,26 @@ struct RenderCommand {
|
||||||
/**
|
/**
|
||||||
* @brief 获取顶点颜色
|
* @brief 获取顶点颜色
|
||||||
*/
|
*/
|
||||||
Color getColor() const { return drawMesh.color; }
|
Color getColor() const {
|
||||||
|
return drawMesh.color;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 清除标志
|
// 清除标志
|
||||||
constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0;
|
constexpr uint32_t CLEAR_COLOR_FLAG = 1 << 0;
|
||||||
constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1;
|
constexpr uint32_t CLEAR_DEPTH_FLAG = 1 << 1;
|
||||||
constexpr uint32_t CLEAR_STENCIL_FLAG = 1 << 2;
|
constexpr uint32_t CLEAR_STENCIL_FLAG = 1 << 2;
|
||||||
constexpr uint32_t CLEAR_ALL_FLAG =
|
constexpr uint32_t CLEAR_ALL_FLAG = CLEAR_COLOR_FLAG | CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG;
|
||||||
CLEAR_COLOR_FLAG | CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG;
|
|
||||||
|
|
||||||
// 最大渲染命令数
|
// 最大渲染命令数
|
||||||
constexpr uint32_t MAX_RENDER_COMMANDS = 10000;
|
constexpr uint32_t MAX_RENDER_COMMANDS = 10000;
|
||||||
|
|
||||||
// UBO 绑定槽位(已移至 RHI)
|
// 最大实例数
|
||||||
|
constexpr uint32_t MAX_INSTANCES = 256;
|
||||||
|
|
||||||
|
// UBO 绑定槽位
|
||||||
constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO
|
constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO
|
||||||
constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO
|
constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO
|
||||||
|
constexpr uint32_t INSTANCE_UBO_BINDING = 2; // 实例 UBO
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <module/module.h>
|
#include <module/module.h>
|
||||||
#include <module/module_registry.h>
|
#include <module/module_registry.h>
|
||||||
#include <renderer/command_queue.h>
|
#include <renderer/material.h>
|
||||||
#include <renderer/render_graph.h>
|
#include <renderer/mesh.h>
|
||||||
#include <renderer/render_types.h>
|
#include <renderer/render_types.h>
|
||||||
#include <renderer/rhi/rhi.h>
|
#include <renderer/shader.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
|
#include <renderer/uniform_buffer.h>
|
||||||
#include <renderer/viewport_adapter.h>
|
#include <renderer/viewport_adapter.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -17,15 +21,15 @@ class AssetsModule;
|
||||||
/**
|
/**
|
||||||
* @brief 渲染器模块
|
* @brief 渲染器模块
|
||||||
*
|
*
|
||||||
* 基于新 RHI 架构的核心渲染系统模块,负责:
|
* 核心渲染系统模块,负责:
|
||||||
* - 通过事件接收渲染命令
|
* - 通过事件接收渲染命令
|
||||||
* - 使用 RenderGraph 管理渲染流程
|
* - 自动批处理和排序
|
||||||
* - 使用 CommandQueue 进行命令排序和批处理
|
|
||||||
* - 执行实际渲染
|
* - 执行实际渲染
|
||||||
|
*
|
||||||
|
* 资源管理已迁移到 AssetsModule
|
||||||
*/
|
*/
|
||||||
class RendererModule : public Module {
|
class RendererModule : public Module {
|
||||||
// 优先级为 4,在 AssetsModule (优先级 3) 之后初始化
|
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3)
|
||||||
E2D_REGISTER_MODULE(RendererModule, "Renderer", 4)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,16 +42,18 @@ public:
|
||||||
*/
|
*/
|
||||||
~RendererModule() override;
|
~RendererModule() override;
|
||||||
|
|
||||||
// 禁止拷贝和移动
|
// 禁止拷贝
|
||||||
RendererModule(const RendererModule &) = delete;
|
RendererModule(const RendererModule &) = delete;
|
||||||
RendererModule &operator=(const RendererModule &) = delete;
|
RendererModule &operator=(const RendererModule &) = delete;
|
||||||
RendererModule(RendererModule &&) = delete;
|
|
||||||
RendererModule &operator=(RendererModule &&) = delete;
|
// 允许移动
|
||||||
|
RendererModule(RendererModule &&) noexcept;
|
||||||
|
RendererModule &operator=(RendererModule &&) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化模块
|
* @brief 初始化模块
|
||||||
*
|
*
|
||||||
* 绑定事件监听器,初始化 RenderGraph 和 CommandQueue
|
* 绑定事件监听器,等待窗口显示事件进行 GL 初始化
|
||||||
*
|
*
|
||||||
* @return 初始化是否成功
|
* @return 初始化是否成功
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,7 +67,29 @@ public:
|
||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 渲染接口
|
// 默认资源(通过 AssetsModule 获取)
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认材质句柄
|
||||||
|
* @return 默认材质句柄
|
||||||
|
*/
|
||||||
|
MaterialHandle getDefaultMaterialHandle() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认四边形网格句柄
|
||||||
|
* @return 默认四边形网格句柄
|
||||||
|
*/
|
||||||
|
MeshHandle getDefaultQuadHandle() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认纹理句柄(1x1 白色纹理)
|
||||||
|
* @return 默认纹理句柄
|
||||||
|
*/
|
||||||
|
TextureHandle getDefaultTextureHandle() const;
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 渲染状态设置
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,42 +121,6 @@ public:
|
||||||
*/
|
*/
|
||||||
const ViewportAdapter &getViewportAdapter() const { return viewportAdapter_; }
|
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:
|
private:
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 事件处理器
|
// 事件处理器
|
||||||
|
|
@ -137,7 +129,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief 渲染开始事件处理
|
* @brief 渲染开始事件处理
|
||||||
*
|
*
|
||||||
* 开始渲染帧,初始化 RenderGraph
|
* 清空命令缓冲区,重置统计信息
|
||||||
*/
|
*/
|
||||||
void onRenderBegin();
|
void onRenderBegin();
|
||||||
|
|
||||||
|
|
@ -156,7 +148,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief 渲染结束事件处理
|
* @brief 渲染结束事件处理
|
||||||
*
|
*
|
||||||
* 执行 RenderGraph 渲染
|
* 排序命令,批处理并执行绘制
|
||||||
*/
|
*/
|
||||||
void onRenderEnd();
|
void onRenderEnd();
|
||||||
|
|
||||||
|
|
@ -170,7 +162,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief 窗口显示事件处理
|
* @brief 窗口显示事件处理
|
||||||
*
|
*
|
||||||
* 延迟初始化到窗口显示时
|
* 延迟 GL 初始化到窗口显示时
|
||||||
*/
|
*/
|
||||||
void onWindowShow();
|
void onWindowShow();
|
||||||
|
|
||||||
|
|
@ -178,6 +170,30 @@ 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 执行单个渲染命令
|
* @brief 执行单个渲染命令
|
||||||
* @param cmd 渲染命令
|
* @param cmd 渲染命令
|
||||||
|
|
@ -185,11 +201,18 @@ private:
|
||||||
void executeCommand(const RenderCommand &cmd);
|
void executeCommand(const RenderCommand &cmd);
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 渲染图和命令队列
|
// 命令缓冲区
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
RenderGraph renderGraph_; // 渲染图
|
std::array<RenderCommand, MAX_RENDER_COMMANDS>
|
||||||
CommandQueue *commandQueue_ = nullptr; // 命令队列(指向 renderGraph_ 内部)
|
commandBuffer_; // 预分配命令缓冲区
|
||||||
|
uint32 commandCount_ = 0; // 当前命令数量
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// UBO 管理器
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
UniformBufferManager uniformManager_;
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 事件监听器
|
// 事件监听器
|
||||||
|
|
@ -206,7 +229,7 @@ private:
|
||||||
// 状态标志
|
// 状态标志
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
bool initialized_ = false; // 是否已初始化
|
bool glInitialized_ = false; // GL 是否已初始化
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 渲染统计
|
// 渲染统计
|
||||||
|
|
@ -214,6 +237,7 @@ private:
|
||||||
|
|
||||||
struct Stats {
|
struct Stats {
|
||||||
uint32 commandsSubmitted = 0; // 提交的命令数
|
uint32 commandsSubmitted = 0; // 提交的命令数
|
||||||
|
uint32 commandsExecuted = 0; // 执行的命令数
|
||||||
uint32 drawCalls = 0; // 绘制调用次数
|
uint32 drawCalls = 0; // 绘制调用次数
|
||||||
uint32 batches = 0; // 批次数
|
uint32 batches = 0; // 批次数
|
||||||
} stats_;
|
} stats_;
|
||||||
|
|
@ -223,7 +247,7 @@ private:
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
int32 viewportX_ = 0, viewportY_ = 0;
|
int32 viewportX_ = 0, viewportY_ = 0;
|
||||||
int32 viewportWidth_ = 1280, viewportHeight_ = 720;
|
int32 viewportWidth_ = 0, viewportHeight_ = 0;
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 视口适配器
|
// 视口适配器
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,307 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#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>
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,501 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#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 <types/ptr/ref_counted.h>
|
||||||
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
|
#include <types/base/types.h>
|
||||||
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// 前向声明 OpenGL 类型
|
||||||
|
typedef unsigned int GLuint;
|
||||||
|
typedef int GLint;
|
||||||
|
typedef unsigned int GLenum;
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 着色器类
|
* @brief 着色器类
|
||||||
*
|
*
|
||||||
* 基于 RHI 的着色器包装类,管理着色器资源的创建和编译
|
* 管理 OpenGL 着色器程序的编译、链接和使用
|
||||||
* 支持从文件或源码加载
|
* 支持从文件或源码加载
|
||||||
*/
|
*/
|
||||||
class Shader : public RefCounted {
|
class Shader : public RefCounted {
|
||||||
|
|
@ -27,6 +31,14 @@ public:
|
||||||
*/
|
*/
|
||||||
~Shader() override;
|
~Shader() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载着色器
|
||||||
|
* @param vsPath 顶点着色器文件路径
|
||||||
|
* @param fsPath 片段着色器文件路径
|
||||||
|
* @return 加载是否成功
|
||||||
|
*/
|
||||||
|
bool loadFromFile(const std::string& vsPath, const std::string& fsPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 从源码加载着色器
|
* @brief 从源码加载着色器
|
||||||
* @param vsSource 顶点着色器源码
|
* @param vsSource 顶点着色器源码
|
||||||
|
|
@ -36,33 +48,14 @@ public:
|
||||||
bool loadFromSource(const std::string& vsSource, const std::string& fsSource);
|
bool loadFromSource(const std::string& vsSource, const std::string& fsSource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 RHI 着色器句柄
|
* @brief 绑定着色器程序
|
||||||
* @return RHI 着色器句柄
|
|
||||||
*/
|
*/
|
||||||
ShaderHandle getHandle() const { return handle_; }
|
void bind() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 RHI 管线句柄
|
* @brief 解绑着色器程序
|
||||||
* @return RHI 管线句柄
|
|
||||||
*/
|
*/
|
||||||
PipelineHandle getPipeline() const { return pipeline_; }
|
void unbind() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 绑定槽位
|
* @brief 设置 Uniform Block 绑定槽位
|
||||||
|
|
@ -72,46 +65,91 @@ public:
|
||||||
void setUniformBlock(const std::string& name, uint32_t binding);
|
void setUniformBlock(const std::string& name, uint32_t binding);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 Uniform Block 绑定槽位
|
* @brief 设置 int 类型 uniform
|
||||||
* @param name Uniform Block 名称
|
* @param name uniform 名称
|
||||||
* @return 绑定槽位,如果未找到返回 UINT32_MAX
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
uint32_t getUniformBlockBinding(const std::string& name) const;
|
void setInt(const std::string& name, int value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 从源码重新加载着色器
|
* @brief 设置 float 类型 uniform
|
||||||
* @param vsSource 顶点着色器源码
|
* @param name uniform 名称
|
||||||
* @param fsSource 片段着色器源码
|
* @param value 值
|
||||||
* @return 重新加载是否成功
|
|
||||||
*/
|
*/
|
||||||
bool reloadFromSource(const std::string &vsSource, const std::string &fsSource);
|
void setFloat(const std::string& name, float value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 使用自定义顶点布局从源码重新加载着色器
|
* @brief 设置 vec2 类型 uniform
|
||||||
* @param vsSource 顶点着色器源码
|
* @param name uniform 名称
|
||||||
* @param fsSource 片段着色器源码
|
* @param x X 分量
|
||||||
* @param vertexLayout 顶点布局
|
* @param y Y 分量
|
||||||
* @return 重新加载是否成功
|
|
||||||
*/
|
*/
|
||||||
bool reloadFromSourceWithLayout(const std::string &vsSource,
|
void setVec2(const std::string& name, float x, float y);
|
||||||
const std::string &fsSource,
|
|
||||||
const VertexLayout &vertexLayout);
|
/**
|
||||||
|
* @brief 设置 vec4 类型 uniform
|
||||||
|
* @param name uniform 名称
|
||||||
|
* @param x X 分量
|
||||||
|
* @param y Y 分量
|
||||||
|
* @param z Z 分量
|
||||||
|
* @param w W 分量
|
||||||
|
*/
|
||||||
|
void setVec4(const std::string& name, float x, float y, float z, float w);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 mat4 类型 uniform
|
||||||
|
* @param name uniform 名称
|
||||||
|
* @param value 矩阵数据指针(16个float)
|
||||||
|
*/
|
||||||
|
void setMat4(const std::string& name, const float* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取着色器程序 ID
|
||||||
|
* @return OpenGL 程序 ID
|
||||||
|
*/
|
||||||
|
GLuint getProgram() const { return program_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否已加载
|
||||||
|
* @return 是否已加载
|
||||||
|
*/
|
||||||
|
bool isLoaded() const { return program_ != 0; }
|
||||||
|
|
||||||
private:
|
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 添加版本声明(如果不存在)
|
* @brief 添加版本声明(如果不存在)
|
||||||
* @param source 源码
|
* @param source 源码
|
||||||
* @param isVertex 是否为顶点着色器
|
* @param type 着色器类型
|
||||||
* @return 处理后的源码
|
* @return 处理后的源码
|
||||||
*/
|
*/
|
||||||
std::string addVersionIfNeeded(const std::string &source, bool isVertex);
|
std::string addVersionIfNeeded(const std::string& source, GLenum type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ShaderHandle handle_; // RHI 着色器句柄
|
GLuint program_ = 0; // OpenGL 程序对象
|
||||||
PipelineHandle pipeline_; // RHI 管线句柄
|
std::unordered_map<std::string, GLint> uniformCache_; // Uniform 位置缓存
|
||||||
|
|
||||||
// Uniform Block 绑定映射
|
|
||||||
std::unordered_map<std::string, uint32_t> uniformBlockBindings_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <renderer/rhi/rhi.h>
|
|
||||||
#include <types/ptr/ref_counted.h>
|
#include <types/ptr/ref_counted.h>
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
|
#include <renderer/render_types.h>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
// 前向声明 OpenGL 类型
|
||||||
|
typedef unsigned int GLuint;
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 纹理类
|
* @brief 纹理类
|
||||||
*
|
*
|
||||||
* 基于 RHI 的纹理包装类,管理纹理资源的创建和加载
|
* 管理 OpenGL 纹理的创建、加载和绑定
|
||||||
* 支持从文件或内存加载
|
* 支持从文件或内存加载
|
||||||
*/
|
*/
|
||||||
class Texture : public RefCounted {
|
class Texture : public RefCounted {
|
||||||
|
|
@ -52,6 +55,17 @@ public:
|
||||||
*/
|
*/
|
||||||
bool create(int width, int height, TextureFormat format);
|
bool create(int width, int height, TextureFormat format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定纹理到指定槽位
|
||||||
|
* @param slot 纹理槽位
|
||||||
|
*/
|
||||||
|
void bind(uint32_t slot = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解绑纹理
|
||||||
|
*/
|
||||||
|
void unbind() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取纹理宽度
|
* @brief 获取纹理宽度
|
||||||
* @return 宽度
|
* @return 宽度
|
||||||
|
|
@ -71,42 +85,19 @@ public:
|
||||||
TextureFormat getFormat() const { return format_; }
|
TextureFormat getFormat() const { return format_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 RHI 纹理句柄
|
* @brief 获取 OpenGL 纹理对象
|
||||||
* @return RHI 纹理句柄
|
* @return OpenGL 纹理对象
|
||||||
*/
|
*/
|
||||||
TextureHandle getHandle() const { return handle_; }
|
GLuint getHandle() const { return texture_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否已加载
|
* @brief 检查是否已加载
|
||||||
* @return 是否已加载
|
* @return 是否已加载
|
||||||
*/
|
*/
|
||||||
bool isLoaded() const { return handle_.isValid(); }
|
bool isLoaded() const { return texture_ != 0; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @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:
|
private:
|
||||||
/**
|
GLuint texture_ = 0; // OpenGL 纹理对象
|
||||||
* @brief 获取每个像素的字节数
|
|
||||||
* @param format 纹理格式
|
|
||||||
* @return 字节数
|
|
||||||
*/
|
|
||||||
static uint32_t getBytesPerPixel(TextureFormat format);
|
|
||||||
TextureHandle handle_; // RHI 纹理句柄
|
|
||||||
int width_ = 0; // 纹理宽度
|
int width_ = 0; // 纹理宽度
|
||||||
int height_ = 0; // 纹理高度
|
int height_ = 0; // 纹理高度
|
||||||
TextureFormat format_ = TextureFormat::RGBA8; // 像素格式
|
TextureFormat format_ = TextureFormat::RGBA8; // 像素格式
|
||||||
|
|
|
||||||
|
|
@ -1,247 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <renderer/rhi/rhi.h>
|
#include <glad/glad.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <renderer/render_types.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -9,7 +10,7 @@ namespace extra2d {
|
||||||
/**
|
/**
|
||||||
* @brief 统一缓冲区对象 (UBO)
|
* @brief 统一缓冲区对象 (UBO)
|
||||||
*
|
*
|
||||||
* 基于 RHI 的 UBO 包装类,用于高效地传递 uniform 数据到 GPU
|
* 用于高效地传递 uniform 数据到 GPU
|
||||||
* 支持 std140 布局标准
|
* 支持 std140 布局标准
|
||||||
*/
|
*/
|
||||||
class UniformBuffer {
|
class UniformBuffer {
|
||||||
|
|
@ -58,25 +59,19 @@ public:
|
||||||
uint32_t getSize() const { return size_; }
|
uint32_t getSize() const { return size_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 RHI 缓冲区句柄
|
* @brief 获取 OpenGL 缓冲区对象
|
||||||
* @return RHI 缓冲区句柄
|
* @return OpenGL 缓冲区对象
|
||||||
*/
|
*/
|
||||||
BufferHandle getHandle() const { return handle_; }
|
GLuint getHandle() const { return ubo_; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取底层 RHI 缓冲区指针
|
|
||||||
* @return RHI 缓冲区指针
|
|
||||||
*/
|
|
||||||
RHIBuffer* getRHIBuffer() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否有效
|
* @brief 检查是否有效
|
||||||
* @return 是否有效
|
* @return 是否有效
|
||||||
*/
|
*/
|
||||||
bool isValid() const { return handle_.isValid(); }
|
bool isValid() const { return ubo_ != 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferHandle handle_; // RHI 缓冲区句柄
|
GLuint ubo_ = 0; // OpenGL 缓冲区对象
|
||||||
uint32_t size_ = 0; // 缓冲区大小
|
uint32_t size_ = 0; // 缓冲区大小
|
||||||
uint32_t binding_ = 0; // 绑定槽位
|
uint32_t binding_ = 0; // 绑定槽位
|
||||||
};
|
};
|
||||||
|
|
@ -86,7 +81,6 @@ private:
|
||||||
*
|
*
|
||||||
* 管理多个 UBO 的分配和回收
|
* 管理多个 UBO 的分配和回收
|
||||||
* 支持每帧重置和对象池复用
|
* 支持每帧重置和对象池复用
|
||||||
* 支持双缓冲机制减少 CPU-GPU 等待
|
|
||||||
*/
|
*/
|
||||||
class UniformBufferManager {
|
class UniformBufferManager {
|
||||||
public:
|
public:
|
||||||
|
|
@ -120,11 +114,10 @@ public:
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前帧的全局 UBO(双缓冲)
|
* @brief 获取全局 UBO
|
||||||
* @param frameIndex 当前帧索引
|
|
||||||
* @return 全局 UBO 指针
|
* @return 全局 UBO 指针
|
||||||
*/
|
*/
|
||||||
UniformBuffer* getGlobalUBO(uint32_t frameIndex);
|
UniformBuffer *getGlobalUBO();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取或创建材质 UBO
|
* @brief 获取或创建材质 UBO
|
||||||
|
|
@ -141,57 +134,24 @@ public:
|
||||||
void resetMaterialUBOs();
|
void resetMaterialUBOs();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 更新全局 UBO 数据(双缓冲)
|
* @brief 更新全局 UBO 数据
|
||||||
* @param data 数据指针
|
* @param data 数据指针
|
||||||
* @param size 数据大小
|
* @param size 数据大小
|
||||||
* @param frameIndex 当前帧索引
|
|
||||||
*/
|
*/
|
||||||
void updateGlobalUBO(const void* data, uint32_t size, uint32_t frameIndex);
|
void updateGlobalUBO(const void *data, uint32_t size);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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:
|
private:
|
||||||
// 全局 UBO 双缓冲(用于 viewProjection、time 等全局数据)
|
// 全局 UBO(用于 viewProjection、time 等全局数据)
|
||||||
std::array<std::unique_ptr<UniformBuffer>, 2> globalUBOs_;
|
std::unique_ptr<UniformBuffer> globalUBO_;
|
||||||
|
|
||||||
// 材质 UBO 池
|
// 材质 UBO 池
|
||||||
std::vector<std::unique_ptr<UniformBuffer>> materialUBOPool_;
|
std::vector<std::unique_ptr<UniformBuffer>> materialUBOPool_;
|
||||||
uint32_t currentUBOIndex_ = 0;
|
uint32_t currentUBOIndex_ = 0;
|
||||||
|
|
||||||
// 材质 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 INITIAL_UBO_POOL_SIZE = 16;
|
||||||
static constexpr uint32_t GLOBAL_UBO_SIZE = 256; // 足够存储 viewProjection + time + screenSize
|
static constexpr uint32_t GLOBAL_UBO_SIZE =
|
||||||
static constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO 绑定槽位
|
256; // 足够存储 viewProjection + time + screenSize
|
||||||
static constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO 绑定槽位
|
|
||||||
static constexpr uint32_t MATERIAL_UBO_BUFFER_SIZE = 1024 * 1024; // 1MB 材质 UBO 缓冲区
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 渲染(收集渲染命令)
|
* @brief 渲染(收集渲染命令)
|
||||||
*/
|
*/
|
||||||
virtual void render();
|
void render();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,20 @@ namespace extra2d {
|
||||||
* 将导演系统集成到引擎模块系统中
|
* 将导演系统集成到引擎模块系统中
|
||||||
*/
|
*/
|
||||||
class SceneModule : public Module {
|
class SceneModule : public Module {
|
||||||
// 优先级为 5,在 RendererModule (优先级 4) 之后初始化
|
// 自动注册到模块系统,优先级为 4(在 Renderer 之后)
|
||||||
E2D_REGISTER_MODULE(SceneModule, "Scene", 5)
|
E2D_REGISTER_MODULE(SceneModule, "Scene", 4)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SceneModule();
|
SceneModule();
|
||||||
~SceneModule() override;
|
~SceneModule() override;
|
||||||
|
|
||||||
// 禁止拷贝和移动
|
// 禁止拷贝
|
||||||
SceneModule(const SceneModule &) = delete;
|
SceneModule(const SceneModule &) = delete;
|
||||||
SceneModule &operator=(const SceneModule &) = delete;
|
SceneModule &operator=(const SceneModule &) = delete;
|
||||||
SceneModule(SceneModule &&) = delete;
|
|
||||||
SceneModule &operator=(SceneModule &&) = delete;
|
// 允许移动
|
||||||
|
SceneModule(SceneModule &&) noexcept;
|
||||||
|
SceneModule &operator=(SceneModule &&) noexcept;
|
||||||
|
|
||||||
// Module 接口实现
|
// Module 接口实现
|
||||||
bool init() override;
|
bool init() override;
|
||||||
|
|
|
||||||
|
|
@ -44,18 +44,20 @@ struct TimerInfo {
|
||||||
* 通过事件总线接收更新事件,无需直接依赖
|
* 通过事件总线接收更新事件,无需直接依赖
|
||||||
*/
|
*/
|
||||||
class TimerModule : public Module {
|
class TimerModule : public Module {
|
||||||
// 优先级为 6,在 SceneModule (优先级 5) 之后初始化
|
// 自动注册到模块系统,优先级为 5(核心模块)
|
||||||
E2D_REGISTER_MODULE(TimerModule, "Timer", 6)
|
E2D_REGISTER_MODULE(TimerModule, "Timer", 5)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimerModule();
|
TimerModule();
|
||||||
~TimerModule() override;
|
~TimerModule() override;
|
||||||
|
|
||||||
// 禁止拷贝和移动
|
// 禁止拷贝
|
||||||
TimerModule(const TimerModule &) = delete;
|
TimerModule(const TimerModule &) = delete;
|
||||||
TimerModule &operator=(const TimerModule &) = delete;
|
TimerModule &operator=(const TimerModule &) = delete;
|
||||||
TimerModule(TimerModule &&) = delete;
|
|
||||||
TimerModule &operator=(TimerModule &&) = delete;
|
// 允许移动
|
||||||
|
TimerModule(TimerModule &&) noexcept;
|
||||||
|
TimerModule &operator=(TimerModule &&) noexcept;
|
||||||
|
|
||||||
// Module 接口实现
|
// Module 接口实现
|
||||||
bool init() override;
|
bool init() override;
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ precision highp float;
|
||||||
// 从顶点着色器输入
|
// 从顶点着色器输入
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
in vec4 vColor;
|
in vec4 vColor;
|
||||||
in vec4 vTintColor;
|
|
||||||
in float vOpacity;
|
|
||||||
|
|
||||||
// 纹理采样器
|
// 纹理采样器
|
||||||
uniform sampler2D uTexture;
|
uniform sampler2D uTexture;
|
||||||
|
|
||||||
|
// 材质参数
|
||||||
|
uniform vec4 uTintColor;
|
||||||
|
uniform float uOpacity;
|
||||||
|
|
||||||
// 输出颜色
|
// 输出颜色
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
|
@ -22,16 +24,11 @@ void main() {
|
||||||
// 采样纹理
|
// 采样纹理
|
||||||
vec4 texColor = texture(uTexture, vTexCoord);
|
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 = texColor * vColor * uTintColor;
|
||||||
|
|
||||||
// 应用透明度
|
// 应用透明度
|
||||||
fragColor.a *= vOpacity;
|
fragColor.a *= uOpacity;
|
||||||
|
|
||||||
// Alpha 测试:丢弃几乎透明的像素
|
// Alpha 测试:丢弃几乎透明的像素
|
||||||
if (fragColor.a < 0.01) {
|
if (fragColor.a < 0.01) {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,15 @@
|
||||||
#version 320 es
|
#version 320 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
// 全局 UBO (binding = 0) - 每帧更新一次
|
// 视图投影矩阵
|
||||||
layout(std140, binding = 0) uniform GlobalUBO {
|
uniform mat4 uViewProjection;
|
||||||
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 mat4 uModelMatrix;
|
||||||
|
|
||||||
|
// 顶点颜色(覆盖顶点属性中的颜色)
|
||||||
|
uniform vec4 uColor;
|
||||||
|
|
||||||
// 顶点属性
|
// 顶点属性
|
||||||
layout(location = 0) in vec2 aPosition;
|
layout(location = 0) in vec2 aPosition;
|
||||||
layout(location = 1) in vec2 aTexCoord;
|
layout(location = 1) in vec2 aTexCoord;
|
||||||
|
|
@ -29,20 +18,16 @@ layout(location = 2) in vec4 aColor;
|
||||||
// 输出到片段着色器
|
// 输出到片段着色器
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
out vec4 vTintColor;
|
|
||||||
out float vOpacity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 顶点着色器入口
|
* @brief 顶点着色器入口
|
||||||
*
|
*
|
||||||
* 计算顶点在裁剪空间中的位置,
|
* 计算顶点在裁剪空间中的位置,
|
||||||
* 并传递纹理坐标和颜色到片段着色器
|
* 并传递纹理坐标和顶点颜色到片段着色器
|
||||||
*/
|
*/
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
|
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
|
||||||
vTexCoord = aTexCoord;
|
vTexCoord = aTexCoord;
|
||||||
// 混合顶点颜色和材质 UBO 中的颜色
|
// 使用 uniform 颜色覆盖顶点属性颜色
|
||||||
vColor = aColor * uColor;
|
vColor = uColor;
|
||||||
vTintColor = uTintColor;
|
|
||||||
vOpacity = uOpacity;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -63,11 +63,6 @@ void Application::initModules() {
|
||||||
// 从注册表自动创建所有模块
|
// 从注册表自动创建所有模块
|
||||||
modules_ = ModuleRegistry::instance().createModules();
|
modules_ = ModuleRegistry::instance().createModules();
|
||||||
|
|
||||||
// 存储模块实例到注册表,以便其他模块可以通过 getModule<T>() 获取
|
|
||||||
for (auto &module : modules_) {
|
|
||||||
ModuleRegistry::instance().storeModuleInstance(module.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化所有模块
|
// 初始化所有模块
|
||||||
for (auto &module : modules_) {
|
for (auto &module : modules_) {
|
||||||
if (!module->init()) {
|
if (!module->init()) {
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,14 @@
|
||||||
#include <algorithm>
|
|
||||||
#include <assets/assets_module.h>
|
#include <assets/assets_module.h>
|
||||||
#include <assets/loaders/shader_loader.h>
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <assets/loaders/texture_loader.h>
|
#include <assets/loaders/texture_loader.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <module/module_registry.h>
|
|
||||||
#include <platform/file_module.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 FileModule 实例
|
|
||||||
*/
|
|
||||||
FileModule *getFileModule() { return getModule<FileModule>(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查路径是否为 RomFS 路径
|
|
||||||
*/
|
|
||||||
bool isRomfsPath(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->isRomfsPath(path);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取文件最后写入时间(支持 RomFS 路径)
|
|
||||||
*/
|
|
||||||
std::filesystem::file_time_type getLastWriteTime(const std::string &path) {
|
|
||||||
if (isRomfsPath(path)) {
|
|
||||||
// RomFS 文件是只读的,返回一个固定时间
|
|
||||||
return std::filesystem::file_time_type::min();
|
|
||||||
}
|
|
||||||
return std::filesystem::last_write_time(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
AssetsModule *g_assetsModule = nullptr;
|
AssetsModule *g_assetsModule = nullptr;
|
||||||
|
|
||||||
AssetsModule::AssetsModule() { g_assetsModule = this; }
|
AssetsModule::AssetsModule() { g_assetsModule = this; }
|
||||||
|
|
@ -232,7 +177,8 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
|
||||||
// Shader 加载特化
|
// Shader 加载特化
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
template <>
|
||||||
|
Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
// 先检查缓存(读锁)
|
// 先检查缓存(读锁)
|
||||||
{
|
{
|
||||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
|
@ -347,7 +293,7 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::filesystem::path dirPath(directory);
|
std::filesystem::path dirPath(directory);
|
||||||
if (!fileExists(directory)) {
|
if (!std::filesystem::exists(dirPath)) {
|
||||||
E2D_LOG_WARN("Directory not found: {}", directory);
|
E2D_LOG_WARN("Directory not found: {}", directory);
|
||||||
return handles;
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
@ -396,38 +342,12 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
template <>
|
template <>
|
||||||
void AssetsModule::loadAsync<Texture>(
|
void AssetsModule::loadAsync<Texture>(
|
||||||
const std::string &path, std::function<void(Handle<Texture>)> callback) {
|
const std::string &path, std::function<void(Handle<Texture>)> callback) {
|
||||||
// 确保异步加载系统已初始化
|
std::thread([this, path, callback]() {
|
||||||
if (!asyncLoaderRunning_) {
|
Handle<Texture> handle = load<Texture>(path);
|
||||||
initAsyncLoader();
|
if (callback) {
|
||||||
|
callback(handle);
|
||||||
}
|
}
|
||||||
|
}).detach();
|
||||||
// 创建并提交加载任务
|
|
||||||
LoadTask task;
|
|
||||||
task.type = LoadTask::Type::Texture;
|
|
||||||
task.priority = LoadTask::Priority::Normal;
|
|
||||||
task.path = path;
|
|
||||||
task.textureCallback = callback;
|
|
||||||
|
|
||||||
submitLoadTask(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void AssetsModule::loadAsync<Shader>(
|
|
||||||
const std::string &path, std::function<void(Handle<Shader>)> callback) {
|
|
||||||
// 确保异步加载系统已初始化
|
|
||||||
if (!asyncLoaderRunning_) {
|
|
||||||
initAsyncLoader();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建并提交加载任务
|
|
||||||
LoadTask task;
|
|
||||||
task.type = LoadTask::Type::Shader;
|
|
||||||
task.priority = LoadTask::Priority::Normal;
|
|
||||||
task.path = path;
|
|
||||||
task.secondaryPath = "";
|
|
||||||
task.shaderCallback = callback;
|
|
||||||
|
|
||||||
submitLoadTask(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -464,70 +384,34 @@ bool AssetsModule::createDefaultResources() {
|
||||||
|
|
||||||
{
|
{
|
||||||
// 从文件加载默认着色器
|
// 从文件加载默认着色器
|
||||||
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS)
|
std::filesystem::path vertPath = "shader/default.vert";
|
||||||
FileModule *fileModule = getFileModule();
|
std::filesystem::path fragPath = "shader/default.frag";
|
||||||
std::string vertPath = fileModule
|
|
||||||
? fileModule->assetPath("shader/default.vert")
|
|
||||||
: "shader/default.vert";
|
|
||||||
std::string fragPath = fileModule
|
|
||||||
? fileModule->assetPath("shader/default.frag")
|
|
||||||
: "shader/default.frag";
|
|
||||||
|
|
||||||
if (!fileExists(vertPath) || !fileExists(fragPath)) {
|
|
||||||
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath,
|
|
||||||
fragPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取着色器文件内容
|
|
||||||
std::string vsSource = readFileToString(vertPath);
|
|
||||||
std::string fsSource = readFileToString(fragPath);
|
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
|
||||||
E2D_LOG_ERROR("Failed to read default shader files: {}, {}", vertPath,
|
|
||||||
fragPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从源码加载着色器
|
|
||||||
Ptr<Shader> shader = makePtr<Shader>();
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
|
||||||
E2D_LOG_ERROR("Failed to compile default shader: {}, {}", vertPath,
|
if (!std::filesystem::exists(vertPath) ||
|
||||||
fragPath);
|
!std::filesystem::exists(fragPath)) {
|
||||||
|
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath.string(),
|
||||||
|
fragPath.string());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shader->loadFromFile(vertPath.string(), fragPath.string())) {
|
||||||
|
E2D_LOG_ERROR("Failed to load default shader from files: {}, {}",
|
||||||
|
vertPath.string(), fragPath.string());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultShader_ = shaders_.insert(shader);
|
defaultShader_ = shaders_.insert(shader);
|
||||||
E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath,
|
E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath.string(),
|
||||||
fragPath);
|
fragPath.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// 创建材质布局(与着色器中的 MaterialUBO 匹配)
|
|
||||||
// layout(std140, binding = 1) uniform MaterialUBO {
|
|
||||||
// vec4 uColor; // 16 bytes
|
|
||||||
// vec4 uTintColor; // 16 bytes
|
|
||||||
// float uOpacity; // 4 bytes
|
|
||||||
// float uPadding[3]; // 12 bytes
|
|
||||||
// };
|
|
||||||
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
|
|
||||||
layout->addParam("uColor", MaterialParamType::Color);
|
|
||||||
layout->addParam("uTintColor", MaterialParamType::Color);
|
|
||||||
layout->addParam("uOpacity", MaterialParamType::Float);
|
|
||||||
layout->finalize();
|
|
||||||
|
|
||||||
Ptr<Material> material = makePtr<Material>();
|
Ptr<Material> material = makePtr<Material>();
|
||||||
material->setShader(getPtr(defaultShader_));
|
material->setShader(getPtr(defaultShader_));
|
||||||
material->setLayout(layout);
|
|
||||||
|
|
||||||
// 设置默认材质参数
|
|
||||||
material->setColor("uColor", Color::White);
|
|
||||||
material->setColor("uTintColor", Color::White);
|
|
||||||
material->setFloat("uOpacity", 1.0f);
|
|
||||||
|
|
||||||
// 添加默认纹理到材质
|
|
||||||
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
|
|
||||||
defaultMaterial_ = materials_.insert(material);
|
defaultMaterial_ = materials_.insert(material);
|
||||||
E2D_LOG_DEBUG("Created default material with default texture and layout");
|
E2D_LOG_DEBUG("Created default material");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -594,36 +478,44 @@ void AssetsModule::setHotReloadInterval(float interval) {
|
||||||
hotReloadInterval_ = interval;
|
hotReloadInterval_ = interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &&info) {
|
void AssetsModule::addFileWatch(const std::string& path, Handle<Texture> handle) {
|
||||||
try {
|
try {
|
||||||
if (!fileExists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileWatchInfo info;
|
||||||
info.path = path;
|
info.path = path;
|
||||||
info.lastWriteTime = getLastWriteTime(path);
|
info.lastWriteTime = std::filesystem::last_write_time(path);
|
||||||
|
info.textureHandle = handle;
|
||||||
|
info.isTexture = true;
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
fileWatchList_.push_back(std::move(info));
|
fileWatchList_.push_back(info);
|
||||||
const char *type = fileWatchList_.back().isTexture ? "texture" : "shader";
|
E2D_LOG_DEBUG("Watching texture file: {}", path);
|
||||||
E2D_LOG_DEBUG("Watching {} file: {}", type, path);
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
|
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::addFileWatch(const std::string &path, Handle<Texture> handle) {
|
void AssetsModule::addFileWatch(const std::string& path, Handle<Shader> handle) {
|
||||||
FileWatchInfo info;
|
try {
|
||||||
info.textureHandle = handle;
|
if (!std::filesystem::exists(path)) {
|
||||||
info.isTexture = true;
|
return;
|
||||||
addFileWatchInternal(path, std::move(info));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::addFileWatch(const std::string &path, Handle<Shader> handle) {
|
|
||||||
FileWatchInfo info;
|
FileWatchInfo info;
|
||||||
|
info.path = path;
|
||||||
|
info.lastWriteTime = std::filesystem::last_write_time(path);
|
||||||
info.shaderHandle = handle;
|
info.shaderHandle = handle;
|
||||||
info.isTexture = false;
|
info.isTexture = false;
|
||||||
addFileWatchInternal(path, std::move(info));
|
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
fileWatchList_.push_back(info);
|
||||||
|
E2D_LOG_DEBUG("Watching shader file: {}", path);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::reloadTexture(const FileWatchInfo& info) {
|
void AssetsModule::reloadTexture(const FileWatchInfo& info) {
|
||||||
|
|
@ -633,22 +525,21 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) {
|
||||||
|
|
||||||
E2D_LOG_INFO("Reloading texture: {}", info.path);
|
E2D_LOG_INFO("Reloading texture: {}", info.path);
|
||||||
|
|
||||||
// 获取旧纹理指针
|
Ptr<Texture> newTexture = textureLoader_->load(info.path);
|
||||||
Texture *oldTexture = textures_.get(info.textureHandle);
|
if (!newTexture) {
|
||||||
if (!oldTexture) {
|
|
||||||
E2D_LOG_ERROR("Old texture not found for reload: {}", info.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直接在原有纹理对象上重新加载
|
|
||||||
if (!oldTexture->reloadFromFile(info.path)) {
|
|
||||||
E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
|
E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("Texture reloaded successfully: {}", info.path);
|
// 替换旧纹理的数据
|
||||||
|
Texture* oldTexture = textures_.get(info.textureHandle);
|
||||||
|
if (oldTexture) {
|
||||||
|
// 这里假设 Texture 类有更新数据的方法
|
||||||
|
// 如果没有,可能需要重新设计
|
||||||
|
E2D_LOG_INFO("Texture reloaded: {}", info.path);
|
||||||
notifyTextureReloaded(info.textureHandle);
|
notifyTextureReloaded(info.textureHandle);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AssetsModule::reloadShader(const FileWatchInfo& info) {
|
void AssetsModule::reloadShader(const FileWatchInfo& info) {
|
||||||
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
|
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
|
||||||
|
|
@ -657,13 +548,6 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
|
|
||||||
E2D_LOG_INFO("Reloading shader: {}", info.path);
|
E2D_LOG_INFO("Reloading shader: {}", info.path);
|
||||||
|
|
||||||
// 获取旧着色器指针
|
|
||||||
Shader *oldShader = shaders_.get(info.shaderHandle);
|
|
||||||
if (!oldShader) {
|
|
||||||
E2D_LOG_ERROR("Old shader not found for reload: {}", info.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找缓存键
|
// 查找缓存键
|
||||||
std::string cacheKey;
|
std::string cacheKey;
|
||||||
{
|
{
|
||||||
|
|
@ -681,32 +565,29 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析顶点/片段着色器路径并重新加载
|
// 解析顶点/片段着色器路径
|
||||||
size_t sepPos = cacheKey.find('|');
|
size_t sepPos = cacheKey.find('|');
|
||||||
if (sepPos == std::string::npos) {
|
if (sepPos == std::string::npos) {
|
||||||
// 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
|
// 单文件模式
|
||||||
E2D_LOG_WARN("Single-file shader reload not supported: {}", info.path);
|
Ptr<Shader> newShader = shaderLoader_->load(info.path);
|
||||||
|
if (!newShader) {
|
||||||
|
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 双文件模式
|
// 双文件模式
|
||||||
std::string vertPath = cacheKey.substr(0, sepPos);
|
std::string vertPath = cacheKey.substr(0, sepPos);
|
||||||
std::string fragPath = cacheKey.substr(sepPos + 1);
|
std::string fragPath = cacheKey.substr(sepPos + 1);
|
||||||
|
|
||||||
// 读取着色器文件内容
|
ShaderLoader* loader = static_cast<ShaderLoader*>(shaderLoader_.get());
|
||||||
std::string vsSource = readFileToString(vertPath);
|
Ptr<Shader> newShader = loader->load(vertPath, fragPath);
|
||||||
std::string fsSource = readFileToString(fragPath);
|
if (!newShader) {
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
|
||||||
E2D_LOG_ERROR("Failed to read shader files for reload: {}, {}", vertPath, fragPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直接在原有着色器对象上重新加载
|
|
||||||
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
|
|
||||||
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
|
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("Shader reloaded successfully: {}", info.path);
|
E2D_LOG_INFO("Shader reloaded: {}", info.path);
|
||||||
notifyShaderReloaded(info.shaderHandle);
|
notifyShaderReloaded(info.shaderHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -719,16 +600,11 @@ void AssetsModule::checkForChanges() {
|
||||||
|
|
||||||
for (auto& info : fileWatchList_) {
|
for (auto& info : fileWatchList_) {
|
||||||
try {
|
try {
|
||||||
// 跳过 RomFS 路径(只读文件系统)
|
if (!std::filesystem::exists(info.path)) {
|
||||||
if (isRomfsPath(info.path)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileExists(info.path)) {
|
auto currentTime = std::filesystem::last_write_time(info.path);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto currentTime = getLastWriteTime(info.path);
|
|
||||||
if (currentTime != info.lastWriteTime) {
|
if (currentTime != info.lastWriteTime) {
|
||||||
info.lastWriteTime = currentTime;
|
info.lastWriteTime = currentTime;
|
||||||
|
|
||||||
|
|
@ -801,8 +677,7 @@ void AssetsModule::submitLoadTask(const LoadTask &task) {
|
||||||
// 按优先级排序
|
// 按优先级排序
|
||||||
std::sort(loadQueue_.begin(), loadQueue_.end(),
|
std::sort(loadQueue_.begin(), loadQueue_.end(),
|
||||||
[](const LoadTask& a, const LoadTask& b) {
|
[](const LoadTask& a, const LoadTask& b) {
|
||||||
return static_cast<int>(a.priority) >
|
return static_cast<int>(a.priority) > static_cast<int>(b.priority);
|
||||||
static_cast<int>(b.priority);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
queueCV_.notify_one();
|
queueCV_.notify_one();
|
||||||
|
|
@ -814,8 +689,9 @@ void AssetsModule::workerThreadLoop() {
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queueMutex_);
|
std::unique_lock<std::mutex> lock(queueMutex_);
|
||||||
queueCV_.wait(
|
queueCV_.wait(lock, [this] {
|
||||||
lock, [this] { return !loadQueue_.empty() || !asyncLoaderRunning_; });
|
return !loadQueue_.empty() || !asyncLoaderRunning_;
|
||||||
|
});
|
||||||
|
|
||||||
if (!asyncLoaderRunning_) {
|
if (!asyncLoaderRunning_) {
|
||||||
break;
|
break;
|
||||||
|
|
@ -835,8 +711,9 @@ void AssetsModule::workerThreadLoop() {
|
||||||
|
|
||||||
if (task.textureCallback) {
|
if (task.textureCallback) {
|
||||||
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
||||||
completedCallbacks_.push_back(
|
completedCallbacks_.push_back([handle, callback = task.textureCallback]() {
|
||||||
[handle, callback = task.textureCallback]() { callback(handle); });
|
callback(handle);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (task.type == LoadTask::Type::Shader) {
|
} else if (task.type == LoadTask::Type::Shader) {
|
||||||
Handle<Shader> handle;
|
Handle<Shader> handle;
|
||||||
|
|
@ -848,8 +725,9 @@ void AssetsModule::workerThreadLoop() {
|
||||||
|
|
||||||
if (task.shaderCallback) {
|
if (task.shaderCallback) {
|
||||||
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
||||||
completedCallbacks_.push_back(
|
completedCallbacks_.push_back([handle, callback = task.shaderCallback]() {
|
||||||
[handle, callback = task.shaderCallback]() { callback(handle); });
|
callback(handle);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -873,8 +751,7 @@ void AssetsModule::processAsyncCallbacks() {
|
||||||
// 资源依赖跟踪
|
// 资源依赖跟踪
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
void AssetsModule::registerMaterialDependency(Handle<Material> material, Handle<Texture> texture) {
|
||||||
Handle<Texture> texture) {
|
|
||||||
if (!material.isValid() || !texture.isValid()) {
|
if (!material.isValid() || !texture.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -886,8 +763,7 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
info.texture = texture;
|
info.texture = texture;
|
||||||
|
|
||||||
// 检查是否已存在
|
// 检查是否已存在
|
||||||
auto it = std::find(info.dependentMaterials.begin(),
|
auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material);
|
||||||
info.dependentMaterials.end(), material);
|
|
||||||
if (it == info.dependentMaterials.end()) {
|
if (it == info.dependentMaterials.end()) {
|
||||||
info.dependentMaterials.push_back(material);
|
info.dependentMaterials.push_back(material);
|
||||||
E2D_LOG_DEBUG("Registered material {} dependency on texture {}",
|
E2D_LOG_DEBUG("Registered material {} dependency on texture {}",
|
||||||
|
|
@ -895,8 +771,7 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
void AssetsModule::registerMaterialDependency(Handle<Material> material, Handle<Shader> shader) {
|
||||||
Handle<Shader> shader) {
|
|
||||||
if (!material.isValid() || !shader.isValid()) {
|
if (!material.isValid() || !shader.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -908,8 +783,7 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
info.shader = shader;
|
info.shader = shader;
|
||||||
|
|
||||||
// 检查是否已存在
|
// 检查是否已存在
|
||||||
auto it = std::find(info.dependentMaterials.begin(),
|
auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material);
|
||||||
info.dependentMaterials.end(), material);
|
|
||||||
if (it == info.dependentMaterials.end()) {
|
if (it == info.dependentMaterials.end()) {
|
||||||
info.dependentMaterials.push_back(material);
|
info.dependentMaterials.push_back(material);
|
||||||
E2D_LOG_DEBUG("Registered material {} dependency on shader {}",
|
E2D_LOG_DEBUG("Registered material {} dependency on shader {}",
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,10 @@
|
||||||
#include <assets/loaders/shader_loader.h>
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <module/module_registry.h>
|
#include <filesystem>
|
||||||
#include <platform/file_module.h>
|
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
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) {
|
Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
std::string basePath = path;
|
std::string basePath = path;
|
||||||
|
|
||||||
|
|
@ -50,10 +16,10 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
std::string vertPath = basePath + ".vert";
|
std::string vertPath = basePath + ".vert";
|
||||||
std::string fragPath = basePath + ".frag";
|
std::string fragPath = basePath + ".frag";
|
||||||
|
|
||||||
if (!fileExists(vertPath)) {
|
if (!std::filesystem::exists(vertPath)) {
|
||||||
vertPath = basePath + ".vs";
|
vertPath = basePath + ".vs";
|
||||||
}
|
}
|
||||||
if (!fileExists(fragPath)) {
|
if (!std::filesystem::exists(fragPath)) {
|
||||||
fragPath = basePath + ".fs";
|
fragPath = basePath + ".fs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,24 +28,10 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
|
|
||||||
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
||||||
const std::string &fragPath) {
|
const std::string &fragPath) {
|
||||||
// 读取顶点着色器文件
|
|
||||||
std::string vsSource = readFileToString(vertPath);
|
|
||||||
if (vsSource.empty()) {
|
|
||||||
E2D_LOG_ERROR("ShaderLoader: Failed to read vertex shader: {}", vertPath);
|
|
||||||
return Ptr<Shader>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取片段着色器文件
|
|
||||||
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>();
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
|
||||||
E2D_LOG_ERROR("ShaderLoader: Failed to compile shader {} + {}", vertPath,
|
if (!shader->loadFromFile(vertPath, fragPath)) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader: Failed to load shader {} + {}", vertPath,
|
||||||
fragPath);
|
fragPath);
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,4 @@ std::vector<std::unique_ptr<Module>> ModuleRegistry::createModules() {
|
||||||
return modules;
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,31 @@
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <platform/file_module.h>
|
#include <platform/file_module.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <utils/logger.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__
|
#ifdef __SWITCH__
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
FileModule::FileModule() = default;
|
FileModule::FileModule() = default;
|
||||||
|
|
||||||
FileModule::~FileModule() = default;
|
FileModule::~FileModule() = default;
|
||||||
|
|
@ -31,19 +44,11 @@ bool FileModule::init() {
|
||||||
Result rc = romfsInit();
|
Result rc = romfsInit();
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
E2D_LOG_INFO("RomFS initialized successfully");
|
E2D_LOG_INFO("RomFS initialized successfully");
|
||||||
// 在 Switch 上设置资源根目录为 RomFS
|
|
||||||
assetRoot_ = "romfs:/";
|
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
|
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
|
||||||
// RomFS 初始化失败,使用当前目录
|
|
||||||
assetRoot_ = "./";
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
// 非 Switch 平台,使用当前目录作为资源根目录
|
|
||||||
assetRoot_ = "./";
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
E2D_LOG_INFO("Asset root set to: {}", assetRoot_);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,16 +59,16 @@ void FileModule::shutdown() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileModule::isRomfsPath(const std::string &path) const {
|
|
||||||
return path.find(romfsRoot_) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileModule::exists(const std::string &path) const {
|
bool FileModule::exists(const std::string &path) const {
|
||||||
return fs::exists(path);
|
struct stat st;
|
||||||
|
return stat(path.c_str(), &st) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileModule::isDir(const std::string &path) const {
|
bool FileModule::isDir(const std::string &path) const {
|
||||||
return fs::is_directory(path);
|
struct stat st;
|
||||||
|
if (stat(path.c_str(), &st) != 0)
|
||||||
|
return false;
|
||||||
|
return (st.st_mode & S_IFDIR) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData FileModule::read(const std::string &path) const {
|
FileData FileModule::read(const std::string &path) const {
|
||||||
|
|
@ -100,12 +105,6 @@ std::string FileModule::readString(const std::string &path) const {
|
||||||
|
|
||||||
bool FileModule::write(const std::string &path, const void *data,
|
bool FileModule::write(const std::string &path, const void *data,
|
||||||
size_t size) const {
|
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);
|
std::ofstream file(path, std::ios::binary);
|
||||||
if (!file.is_open())
|
if (!file.is_open())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -122,12 +121,6 @@ bool FileModule::writeString(const std::string &path,
|
||||||
|
|
||||||
bool FileModule::append(const std::string &path, const void *data,
|
bool FileModule::append(const std::string &path, const void *data,
|
||||||
size_t size) const {
|
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);
|
std::ofstream file(path, std::ios::binary | std::ios::app);
|
||||||
if (!file.is_open())
|
if (!file.is_open())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -138,69 +131,105 @@ bool FileModule::append(const std::string &path, const void *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileModule::remove(const std::string &path) const {
|
bool FileModule::remove(const std::string &path) const {
|
||||||
// RomFS 是只读的,不允许删除
|
return std::remove(path.c_str()) == 0;
|
||||||
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 {
|
bool FileModule::mkdir(const std::string &path) const {
|
||||||
// RomFS 是只读的,不允许创建目录
|
#ifdef _WIN32
|
||||||
if (isRomfsPath(path)) {
|
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
|
||||||
E2D_LOG_WARN("Cannot create directory in RomFS: {}", path);
|
#else
|
||||||
return false;
|
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
|
||||||
}
|
#endif
|
||||||
|
|
||||||
return fs::create_directories(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<FileInfo> FileModule::listDir(const std::string &path) const {
|
std::vector<FileInfo> FileModule::listDir(const std::string &path) const {
|
||||||
std::vector<FileInfo> result;
|
std::vector<FileInfo> result;
|
||||||
|
|
||||||
if (!fs::exists(path) || !fs::is_directory(path)) {
|
#ifdef _WIN32
|
||||||
return result;
|
WIN32_FIND_DATAA findData;
|
||||||
}
|
std::string searchPath = path + "\\*";
|
||||||
|
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
|
||||||
|
|
||||||
for (const auto &entry : fs::directory_iterator(path)) {
|
if (hFind == INVALID_HANDLE_VALUE)
|
||||||
std::string name = entry.path().filename().string();
|
return result;
|
||||||
|
|
||||||
|
do {
|
||||||
|
std::string name = findData.cFileName;
|
||||||
if (name == "." || name == "..")
|
if (name == "." || name == "..")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
info.name = name;
|
info.name = name;
|
||||||
info.path = entry.path().string();
|
info.path = join(path, name);
|
||||||
info.isDir = entry.is_directory();
|
info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
if (!info.isDir && entry.is_regular_file()) {
|
if (!info.isDir) {
|
||||||
info.size = static_cast<int64>(entry.file_size());
|
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);
|
||||||
}
|
}
|
||||||
result.push_back(info);
|
result.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64 FileModule::fileSize(const std::string &path) const {
|
int64 FileModule::fileSize(const std::string &path) const {
|
||||||
if (!fs::exists(path) || !fs::is_regular_file(path)) {
|
struct stat st;
|
||||||
|
if (stat(path.c_str(), &st) != 0)
|
||||||
return -1;
|
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 {
|
std::string FileModule::ext(const std::string &path) const {
|
||||||
fs::path p(path);
|
size_t pos = path.find_last_of('.');
|
||||||
return p.extension().string();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileModule::fileName(const std::string &path) const {
|
std::string FileModule::fileName(const std::string &path) const {
|
||||||
fs::path p(path);
|
size_t pos = path.find_last_of("/\\");
|
||||||
return p.filename().string();
|
if (pos == std::string::npos)
|
||||||
|
return path;
|
||||||
|
return path.substr(pos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileModule::dirName(const std::string &path) const {
|
std::string FileModule::dirName(const std::string &path) const {
|
||||||
fs::path p(path);
|
size_t pos = path.find_last_of("/\\");
|
||||||
return p.parent_path().string();
|
if (pos == std::string::npos)
|
||||||
|
return ".";
|
||||||
|
if (pos == 0)
|
||||||
|
return "/";
|
||||||
|
return path.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileModule::join(const std::string &a, const std::string &b) const {
|
std::string FileModule::join(const std::string &a, const std::string &b) const {
|
||||||
|
|
@ -209,9 +238,11 @@ std::string FileModule::join(const std::string &a, const std::string &b) const {
|
||||||
if (b.empty())
|
if (b.empty())
|
||||||
return a;
|
return a;
|
||||||
|
|
||||||
fs::path base(a);
|
char last = a.back();
|
||||||
fs::path sub(b);
|
if (last == '/' || last == '\\') {
|
||||||
return (base / sub).string();
|
return a + b;
|
||||||
|
}
|
||||||
|
return a + "/" + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileModule::writableDir() const { return writableDir_; }
|
std::string FileModule::writableDir() const { return writableDir_; }
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,64 @@ InputModule::~InputModule() {
|
||||||
shutdown();
|
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() {
|
bool InputModule::init() {
|
||||||
std::memset(keyState_.data(), 0, KEY_COUNT);
|
std::memset(keyState_.data(), 0, KEY_COUNT);
|
||||||
std::memset(keyPrev_.data(), 0, KEY_COUNT);
|
std::memset(keyPrev_.data(), 0, KEY_COUNT);
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,39 @@
|
||||||
#include <platform/window_module.h>
|
#include <platform/window_module.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
// OpenGL ES 3.2 with GLAD
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
WindowModule::WindowModule() = default;
|
WindowModule::WindowModule() = default;
|
||||||
|
|
||||||
WindowModule::~WindowModule() { shutdown(); }
|
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() {
|
bool WindowModule::init() {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||||
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
|
E2D_LOG_ERROR("Failed to initialize SDL video: {}", SDL_GetError());
|
||||||
|
|
@ -31,10 +58,7 @@ bool WindowModule::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowModule::shutdown() {
|
void WindowModule::shutdown() {
|
||||||
// 清理事件监听器
|
destroyGLContext();
|
||||||
configListener_.reset();
|
|
||||||
renderPresentListener_.reset();
|
|
||||||
|
|
||||||
if (window_) {
|
if (window_) {
|
||||||
SDL_DestroyWindow(window_);
|
SDL_DestroyWindow(window_);
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
|
|
@ -139,8 +163,14 @@ void WindowModule::onModuleConfig(const AppConfig &config) {
|
||||||
cfg.resizable = config.resizable;
|
cfg.resizable = config.resizable;
|
||||||
|
|
||||||
if (create(cfg)) {
|
if (create(cfg)) {
|
||||||
// 发送窗口显示事件
|
// 创建 OpenGL 上下文
|
||||||
// RHI 模块会监听此事件并创建 OpenGL 上下文
|
if (!createGLContext()) {
|
||||||
|
E2D_LOG_ERROR("Failed to create OpenGL context");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL 上下文创建完成后,再发送窗口显示事件
|
||||||
|
// 这样渲染模块可以在收到事件时安全地使用 GL 函数
|
||||||
events::OnShow::emit();
|
events::OnShow::emit();
|
||||||
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
|
@ -210,9 +240,56 @@ bool WindowModule::isVisible() const {
|
||||||
return (flags & SDL_WINDOW_SHOWN) != 0;
|
return (flags & SDL_WINDOW_SHOWN) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowModule::onRenderPresent() {
|
bool WindowModule::createGLContext() {
|
||||||
// 交换缓冲区由 RHI 模块处理
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *WindowModule::getGLContext() const { return glContext_; }
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,553 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,358 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -21,20 +21,12 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已存在
|
|
||||||
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
|
|
||||||
E2D_LOG_WARN("Param '{}' already exists in MaterialLayout", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialParamInfo info;
|
MaterialParamInfo info;
|
||||||
info.type = type;
|
info.type = type;
|
||||||
info.size = getMaterialParamSize(type);
|
info.size = getMaterialParamSize(type);
|
||||||
info.offset = 0; // 将在 finalize 时计算
|
info.offset = 0; // 将在 finalize 时计算
|
||||||
|
|
||||||
size_t index = params_.size();
|
params_[name] = info;
|
||||||
params_.emplace_back(name, info);
|
|
||||||
paramIndexMap_[name] = index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,7 +36,6 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
|
||||||
* - 标量和向量:偏移必须是其大小的倍数
|
* - 标量和向量:偏移必须是其大小的倍数
|
||||||
* - 数组和结构体:偏移必须是 16 的倍数
|
* - 数组和结构体:偏移必须是 16 的倍数
|
||||||
* - vec3 后面需要填充到 16 字节边界
|
* - vec3 后面需要填充到 16 字节边界
|
||||||
* - 整个结构体大小必须是16的倍数
|
|
||||||
*/
|
*/
|
||||||
void MaterialLayout::finalize() {
|
void MaterialLayout::finalize() {
|
||||||
if (finalized_) return;
|
if (finalized_) return;
|
||||||
|
|
@ -53,7 +44,7 @@ void MaterialLayout::finalize() {
|
||||||
for (auto& pair : params_) {
|
for (auto& pair : params_) {
|
||||||
auto& info = pair.second;
|
auto& info = pair.second;
|
||||||
|
|
||||||
// 计算对齐要求 - std140规则
|
// 计算对齐要求
|
||||||
uint32_t alignment = 4; // 默认 4 字节对齐
|
uint32_t alignment = 4; // 默认 4 字节对齐
|
||||||
|
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
|
|
@ -69,21 +60,21 @@ void MaterialLayout::finalize() {
|
||||||
alignment = 16; // vec3/vec4/color: 16 字节对齐(std140 规则)
|
alignment = 16; // vec3/vec4/color: 16 字节对齐(std140 规则)
|
||||||
break;
|
break;
|
||||||
case MaterialParamType::Mat4:
|
case MaterialParamType::Mat4:
|
||||||
alignment = 16; // mat4: 16 字节对齐,每列vec4对齐到16字节
|
alignment = 16; // mat4: 16 字节对齐
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
alignment = 4;
|
alignment = 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对齐偏移到alignment的倍数
|
// 对齐偏移
|
||||||
offset = (offset + alignment - 1) & ~(alignment - 1);
|
offset = (offset + alignment - 1) & ~(alignment - 1);
|
||||||
|
|
||||||
info.offset = offset;
|
info.offset = offset;
|
||||||
offset += info.size;
|
offset += info.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最终大小对齐到 16 字节(std140要求结构体总大小是16的倍数)
|
// 最终大小对齐到 16 字节
|
||||||
bufferSize_ = (offset + 15) & ~15;
|
bufferSize_ = (offset + 15) & ~15;
|
||||||
finalized_ = true;
|
finalized_ = true;
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +85,9 @@ void MaterialLayout::finalize() {
|
||||||
* @return 参数信息指针,不存在返回 nullptr
|
* @return 参数信息指针,不存在返回 nullptr
|
||||||
*/
|
*/
|
||||||
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
|
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
|
||||||
auto mapIt = paramIndexMap_.find(name);
|
auto it = params_.find(name);
|
||||||
if (mapIt != paramIndexMap_.end()) {
|
if (it != params_.end()) {
|
||||||
return ¶ms_[mapIt->second].second;
|
return &it->second;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -126,17 +117,6 @@ void Material::setShader(Ptr<Shader> shader) {
|
||||||
shader_ = shader;
|
shader_ = shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 RHI 管线句柄
|
|
||||||
* @return RHI 管线句柄
|
|
||||||
*/
|
|
||||||
PipelineHandle Material::getPipeline() const {
|
|
||||||
if (shader_) {
|
|
||||||
return shader_->getPipeline();
|
|
||||||
}
|
|
||||||
return PipelineHandle{};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置 float 参数
|
* @brief 设置 float 参数
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
|
|
@ -214,14 +194,14 @@ void Material::setMat4(const std::string& name, const float* value) {
|
||||||
/**
|
/**
|
||||||
* @brief 设置纹理
|
* @brief 设置纹理
|
||||||
* @param uniformName 着色器中的采样器 uniform 名称
|
* @param uniformName 着色器中的采样器 uniform 名称
|
||||||
* @param texture 纹理
|
* @param texture 纹理句柄
|
||||||
* @param slot 纹理槽位(0-15)
|
* @param slot 纹理槽位(0-15)
|
||||||
*/
|
*/
|
||||||
void Material::setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot) {
|
void Material::setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot) {
|
||||||
// 查找是否已存在相同名称的纹理
|
// 查找是否已存在相同名称的纹理
|
||||||
for (auto& texSlot : textures_) {
|
for (auto& texSlot : textures_) {
|
||||||
if (texSlot.uniformName == uniformName) {
|
if (texSlot.uniformName == uniformName) {
|
||||||
texSlot.texture = texture;
|
texSlot.handle = texture;
|
||||||
texSlot.slot = slot;
|
texSlot.slot = slot;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -238,4 +218,31 @@ void Material::clearTextures() {
|
||||||
textures_.clear();
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,147 @@
|
||||||
#include <renderer/mesh.h>
|
#include <renderer/mesh.h>
|
||||||
#include <renderer/rhi_module.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
Mesh::Mesh() = default;
|
Mesh::Mesh() = default;
|
||||||
|
|
||||||
Mesh::~Mesh() {
|
Mesh::~Mesh() {
|
||||||
// BufferHandle 是轻量级句柄,不需要显式释放
|
if (vao_ != 0) {
|
||||||
// 实际的缓冲区资源由 RHI 设备管理
|
glDeleteVertexArrays(1, &vao_);
|
||||||
vertexBuffer_ = BufferHandle();
|
vao_ = 0;
|
||||||
indexBuffer_ = BufferHandle();
|
}
|
||||||
|
if (vbo_ != 0) {
|
||||||
|
glDeleteBuffers(1, &vbo_);
|
||||||
|
vbo_ = 0;
|
||||||
|
}
|
||||||
|
if (ibo_ != 0) {
|
||||||
|
glDeleteBuffers(1, &ibo_);
|
||||||
|
ibo_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::setVertices(const Vertex* vertices, uint32_t count) {
|
void Mesh::setVertices(const Vertex* vertices, uint32_t count) {
|
||||||
if (!vertices || count == 0)
|
if (vao_ == 0) {
|
||||||
return;
|
glGenVertexArrays(1, &vao_);
|
||||||
|
glGenBuffers(1, &vbo_);
|
||||||
|
|
||||||
// 获取 RHI 模块
|
E2D_LOG_INFO("Created VAO: {}, VBO: {}", vao_, vbo_);
|
||||||
auto *rhiModule = RHIModule::get();
|
|
||||||
if (!rhiModule) {
|
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
glBindVertexArray(vao_);
|
||||||
if (!device) {
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取缓冲区的句柄
|
|
||||||
// 注意:createBuffer 返回的是 unique_ptr,我们需要存储句柄
|
|
||||||
// 这里直接使用缓冲区指针作为句柄,然后释放所有权让 RHI 内部管理
|
|
||||||
vertexBuffer_ = BufferHandle(buffer.release());
|
|
||||||
|
|
||||||
|
// 如果容量不足,重新分配
|
||||||
|
if (count > vertexCapacity_) {
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, count * sizeof(Vertex), vertices, GL_DYNAMIC_DRAW);
|
||||||
vertexCapacity_ = count;
|
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;
|
vertexCount_ = count;
|
||||||
E2D_LOG_INFO("Set {} vertices", count);
|
|
||||||
|
E2D_LOG_INFO("Set {} vertices for VAO {}", count, vao_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::setIndices(const uint16_t* indices, uint32_t count) {
|
void Mesh::setIndices(const uint16_t* indices, uint32_t count) {
|
||||||
if (!indices || count == 0)
|
if (vao_ == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// 获取 RHI 模块
|
if (ibo_ == 0) {
|
||||||
auto *rhiModule = RHIModule::get();
|
glGenBuffers(1, &ibo_);
|
||||||
if (!rhiModule) {
|
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
glBindVertexArray(vao_);
|
||||||
if (!device) {
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取缓冲区的句柄
|
|
||||||
indexBuffer_ = BufferHandle(buffer.release());
|
|
||||||
|
|
||||||
|
// 如果容量不足,重新分配
|
||||||
|
if (count > indexCapacity_) {
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint16_t), indices, GL_STATIC_DRAW);
|
||||||
indexCapacity_ = count;
|
indexCapacity_ = count;
|
||||||
|
} else {
|
||||||
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, count * sizeof(uint16_t), indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
indexCount_ = count;
|
indexCount_ = count;
|
||||||
E2D_LOG_INFO("Set {} indices", count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
|
void Mesh::updateVertices(const Vertex* vertices, uint32_t count, uint32_t offset) {
|
||||||
uint32_t offset) {
|
if (vao_ == 0 || vbo_ == 0 || vertices == nullptr) return;
|
||||||
if (!vertices || count == 0 || !vertexBuffer_.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (offset + count > vertexCount_) {
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||||
E2D_LOG_WARN("Vertex update out of bounds");
|
glBufferSubData(GL_ARRAY_BUFFER, offset * sizeof(Vertex),
|
||||||
return;
|
count * sizeof(Vertex), vertices);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 RHI 模块
|
void Mesh::bind() const {
|
||||||
auto *rhiModule = RHIModule::get();
|
if (vao_ != 0) {
|
||||||
if (!rhiModule)
|
glBindVertexArray(vao_);
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
void Mesh::unbind() const {
|
||||||
if (!device)
|
glBindVertexArray(0);
|
||||||
return;
|
}
|
||||||
|
|
||||||
// 通过句柄获取缓冲区并更新数据
|
void Mesh::draw() const {
|
||||||
// 注意:这里需要 RHI 提供更新缓冲区的方法
|
if (vao_ == 0) return;
|
||||||
// 暂时记录为未实现
|
|
||||||
E2D_LOG_WARN("updateVertices not fully implemented yet");
|
// 检查 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::drawInstanced(uint32_t instanceCount) const {
|
||||||
|
if (vao_ == 0) return;
|
||||||
|
|
||||||
|
if (indexCount_ > 0) {
|
||||||
|
glDrawElementsInstanced(GL_TRIANGLES, indexCount_, GL_UNSIGNED_SHORT,
|
||||||
|
nullptr, instanceCount);
|
||||||
|
} else {
|
||||||
|
glDrawArraysInstanced(GL_TRIANGLES, 0, vertexCount_, instanceCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Mesh> Mesh::createQuad(const Vec2& size, const Rect& uv) {
|
Ptr<Mesh> Mesh::createQuad(const Vec2& size, const Rect& uv) {
|
||||||
|
|
@ -145,8 +152,7 @@ Ptr<Mesh> Mesh::createQuad(const Vec2 &size, const Rect &uv) {
|
||||||
|
|
||||||
Vertex vertices[4] = {
|
Vertex vertices[4] = {
|
||||||
{Vec2(-halfW, -halfH), Vec2(uv.x, uv.y + uv.h), Color::White}, // 左下
|
{Vec2(-halfW, -halfH), Vec2(uv.x, uv.y + uv.h), Color::White}, // 左下
|
||||||
{Vec2(halfW, -halfH), Vec2(uv.x + uv.w, uv.y + uv.h),
|
{Vec2( halfW, -halfH), Vec2(uv.x + uv.w, uv.y + uv.h), Color::White}, // 右下
|
||||||
Color::White}, // 右下
|
|
||||||
{Vec2( halfW, halfH), Vec2(uv.x + uv.w, uv.y), Color::White}, // 右上
|
{Vec2( halfW, halfH), Vec2(uv.x + uv.w, uv.y), Color::White}, // 右上
|
||||||
{Vec2(-halfW, halfH), Vec2(uv.x, uv.y), Color::White} // 左上
|
{Vec2(-halfW, halfH), Vec2(uv.x, uv.y), Color::White} // 左上
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,20 +1,82 @@
|
||||||
|
#include "glad/glad.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <assets/assets_module.h>
|
#include <assets/assets_module.h>
|
||||||
#include <platform/window_module.h>
|
#include <event/events.h>
|
||||||
#include <renderer/render_graph.h>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <renderer/renderer_module.h>
|
#include <renderer/renderer_module.h>
|
||||||
#include <renderer/rhi_module.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
RendererModule::RendererModule() = default;
|
RendererModule::RendererModule() = default;
|
||||||
|
|
||||||
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() {
|
bool RendererModule::init() {
|
||||||
E2D_LOG_INFO("Initializing RendererModule...");
|
E2D_LOG_INFO("Initializing RendererModule...");
|
||||||
|
|
||||||
// 绑定事件监听器
|
|
||||||
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
||||||
onRenderSubmitListener_.bind(
|
onRenderSubmitListener_.bind(
|
||||||
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
||||||
|
|
@ -22,92 +84,69 @@ bool RendererModule::init() {
|
||||||
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
||||||
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
||||||
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
||||||
|
|
||||||
onShowListener_.bind([this]() { onWindowShow(); });
|
onShowListener_.bind([this]() { onWindowShow(); });
|
||||||
|
|
||||||
E2D_LOG_INFO("RendererModule initialized (waiting for window show)");
|
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onWindowShow() {
|
void RendererModule::onWindowShow() {
|
||||||
if (initialized_) {
|
if (glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("Initializing RendererModule context...");
|
E2D_LOG_INFO("Initializing OpenGL context...");
|
||||||
|
|
||||||
// 初始化 RHI 模块
|
if (!uniformManager_.initialize()) {
|
||||||
auto *rhiModule = RHIModule::get();
|
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
||||||
if (!rhiModule) {
|
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rhiModule->getDevice()) {
|
int windowWidth = 800, windowHeight = 600;
|
||||||
// RHI 设备尚未初始化,这是正常的时序问题
|
SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow();
|
||||||
// RHIModule 会在窗口显示后初始化设备,然后再次触发此函数
|
if (sdlWindow) {
|
||||||
E2D_LOG_INFO("RHIDevice not ready yet, waiting for RHI initialization...");
|
SDL_GetWindowSize(sdlWindow, &windowWidth, &windowHeight);
|
||||||
return;
|
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");
|
||||||
}
|
}
|
||||||
|
setViewport(0, 0, static_cast<int32>(windowWidth),
|
||||||
|
static_cast<int32>(windowHeight));
|
||||||
|
|
||||||
// 初始化渲染图
|
glEnable(GL_BLEND);
|
||||||
if (!renderGraph_.initialize()) {
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
E2D_LOG_ERROR("Failed to initialize RenderGraph");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
commandQueue_ = renderGraph_.getCommandQueue();
|
glInitialized_ = true;
|
||||||
|
E2D_LOG_INFO("OpenGL context initialized successfully");
|
||||||
// 添加默认的几何渲染通道
|
|
||||||
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() {
|
void RendererModule::shutdown() {
|
||||||
E2D_LOG_INFO("Shutting down RendererModule...");
|
E2D_LOG_INFO("Shutting down RendererModule...");
|
||||||
|
|
||||||
renderGraph_.shutdown();
|
if (glInitialized_) {
|
||||||
commandQueue_ = nullptr;
|
uniformManager_.shutdown();
|
||||||
initialized_ = false;
|
}
|
||||||
|
|
||||||
// 注意:事件监听器是值类型,会在析构时自动清理
|
glInitialized_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("RendererModule shutdown complete");
|
E2D_LOG_INFO("RendererModule shutdown complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
RHIContext *RendererModule::getRHIContext() const {
|
MaterialHandle RendererModule::getDefaultMaterialHandle() const {
|
||||||
auto *rhiModule = RHIModule::get();
|
auto* assets = getAssets();
|
||||||
if (rhiModule && rhiModule->getDevice()) {
|
return assets ? assets->getDefaultMaterial() : MaterialHandle::invalid();
|
||||||
return rhiModule->getDevice()->getContext();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<Material> RendererModule::getDefaultMaterialHandle() const {
|
MeshHandle RendererModule::getDefaultQuadHandle() const {
|
||||||
auto* assets = getAssets();
|
auto* assets = getAssets();
|
||||||
return assets ? assets->getDefaultMaterial() : Handle<Material>::invalid();
|
return assets ? assets->getDefaultQuad() : MeshHandle::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<Mesh> RendererModule::getDefaultQuadHandle() const {
|
TextureHandle RendererModule::getDefaultTextureHandle() const {
|
||||||
auto* assets = getAssets();
|
auto* assets = getAssets();
|
||||||
return assets ? assets->getDefaultQuad() : Handle<Mesh>::invalid();
|
return assets ? assets->getDefaultTexture() : TextureHandle::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) {
|
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||||
|
|
@ -118,146 +157,216 @@ void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||||
|
|
||||||
viewportAdapter_.update(width, height);
|
viewportAdapter_.update(width, height);
|
||||||
|
|
||||||
// 更新渲染图输出尺寸
|
|
||||||
renderGraph_.setOutputSize(static_cast<uint32_t>(width),
|
|
||||||
static_cast<uint32_t>(height));
|
|
||||||
|
|
||||||
// 通过 RHI 设置视口
|
|
||||||
if (initialized_) {
|
|
||||||
auto *context = getRHIContext();
|
|
||||||
if (context) {
|
|
||||||
auto result = viewportAdapter_.getResult();
|
auto result = viewportAdapter_.getResult();
|
||||||
context->setViewport(static_cast<int32_t>(result.viewport.x),
|
glViewport(static_cast<GLint>(result.viewport.x),
|
||||||
static_cast<int32_t>(result.viewport.y),
|
static_cast<GLint>(result.viewport.y),
|
||||||
static_cast<uint32_t>(result.viewport.w),
|
static_cast<GLsizei>(result.viewport.w),
|
||||||
static_cast<uint32_t>(result.viewport.h));
|
static_cast<GLsizei>(result.viewport.h));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::clear(const Color &color, uint32 flags) {
|
void RendererModule::clear(const Color &color, uint32 flags) {
|
||||||
if (!initialized_)
|
GLbitfield mask = 0;
|
||||||
return;
|
|
||||||
|
|
||||||
if (commandQueue_) {
|
if (flags & CLEAR_COLOR_FLAG) {
|
||||||
commandQueue_->submitClear(color, flags);
|
glClearColor(color.r, color.g, color.b, color.a);
|
||||||
|
mask |= GL_COLOR_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & CLEAR_DEPTH_FLAG) {
|
||||||
|
mask |= GL_DEPTH_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & CLEAR_STENCIL_FLAG) {
|
||||||
|
mask |= GL_STENCIL_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask != 0) {
|
||||||
|
glClear(mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderBegin() {
|
void RendererModule::onRenderBegin() {
|
||||||
if (!initialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置统计
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
commandCount_ = 0;
|
||||||
|
|
||||||
stats_ = {};
|
stats_ = {};
|
||||||
|
|
||||||
// 开始渲染帧 - 清空命令队列准备接收新的渲染命令
|
uniformManager_.resetMaterialUBOs();
|
||||||
if (commandQueue_) {
|
|
||||||
commandQueue_->beginFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 帧开始
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
if (!initialized_ || !commandQueue_) {
|
if (!glInitialized_) {
|
||||||
E2D_LOG_WARN(
|
|
||||||
"onRenderSubmit: RendererModule not initialized or no command queue");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)cmd; // 避免未使用警告
|
if (commandCount_ >= MAX_RENDER_COMMANDS) {
|
||||||
|
E2D_LOG_WARN("Render command buffer full!");
|
||||||
// 将渲染命令转换为绘制命令提交到队列
|
|
||||||
switch (cmd.type) {
|
|
||||||
case RenderCommandType::DrawMesh: {
|
|
||||||
auto *assets = getAssets();
|
|
||||||
if (!assets)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 获取材质,如果没有指定则使用默认材质
|
|
||||||
Material *material = assets->get(cmd.drawMesh.material);
|
|
||||||
if (!material) {
|
|
||||||
material = assets->get(assets->getDefaultMaterial());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取网格,如果没有指定则使用默认四边形
|
commandBuffer_[commandCount_++] = cmd;
|
||||||
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++;
|
stats_.commandsSubmitted++;
|
||||||
} else {
|
|
||||||
E2D_LOG_WARN("Failed to submit draw command: material={}, mesh={}",
|
|
||||||
material ? "valid" : "null", mesh ? "valid" : "null");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
|
||||||
if (!initialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewProjectionMatrix_ = viewProj;
|
viewProjectionMatrix_ = viewProj;
|
||||||
|
|
||||||
// 更新全局 UBO(通过 RenderGraph 的命令队列)
|
uniformManager_.updateGlobalUBO(&viewProj, sizeof(Mat4));
|
||||||
// 实际的 UBO 更新会在 RenderGraph 执行时进行
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderEnd() {
|
void RendererModule::onRenderEnd() {
|
||||||
if (!initialized_) {
|
if (!glInitialized_) {
|
||||||
E2D_LOG_WARN("onRenderEnd: RendererModule not initialized");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行渲染图,传递视图投影矩阵
|
sortCommands();
|
||||||
renderGraph_.execute(0.016f, viewProjectionMatrix_);
|
|
||||||
|
|
||||||
// 获取统计信息
|
batchAndDraw();
|
||||||
if (commandQueue_) {
|
|
||||||
const auto& stats = commandQueue_->getStats();
|
|
||||||
stats_.drawCalls = stats.drawCalls;
|
|
||||||
|
|
||||||
// 每60帧输出一次统计信息
|
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
|
||||||
static uint32_t frameCount = 0;
|
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
|
||||||
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) {
|
void RendererModule::onResize(int32 width, int32 height) {
|
||||||
setViewport(0, 0, width, 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) {
|
void RendererModule::executeCommand(const RenderCommand &cmd) {
|
||||||
// 此方法保留用于直接执行特定命令
|
|
||||||
// 大部分命令现在通过 RenderGraph 处理
|
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case RenderCommandType::SetViewport:
|
case RenderCommandType::SetViewport:
|
||||||
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
|
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,200 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include <renderer/rhi_module.h>
|
#include <fstream>
|
||||||
|
#include <glad/glad.h>
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
|
#include <sstream>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -7,181 +9,203 @@ namespace extra2d {
|
||||||
Shader::Shader() = default;
|
Shader::Shader() = default;
|
||||||
|
|
||||||
Shader::~Shader() {
|
Shader::~Shader() {
|
||||||
// RHIHandle 是轻量级句柄,不需要显式释放
|
if (program_ != 0) {
|
||||||
// 实际的资源由 RHI 设备管理
|
glDeleteProgram(program_);
|
||||||
pipeline_ = PipelineHandle();
|
program_ = 0;
|
||||||
handle_ = ShaderHandle();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shader::loadFromFile(const std::string &vsPath,
|
||||||
|
const std::string &fsPath) {
|
||||||
|
// 读取顶点着色器
|
||||||
|
std::ifstream vsFile(vsPath);
|
||||||
|
if (!vsFile.is_open()) {
|
||||||
|
E2D_LOG_ERROR("Failed to open vertex shader: {}", vsPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::stringstream vsStream;
|
||||||
|
vsStream << vsFile.rdbuf();
|
||||||
|
std::string vsSource = vsStream.str();
|
||||||
|
|
||||||
|
// 读取片段着色器
|
||||||
|
std::ifstream fsFile(fsPath);
|
||||||
|
if (!fsFile.is_open()) {
|
||||||
|
E2D_LOG_ERROR("Failed to open fragment shader: {}", fsPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::stringstream fsStream;
|
||||||
|
fsStream << fsFile.rdbuf();
|
||||||
|
std::string fsSource = fsStream.str();
|
||||||
|
|
||||||
|
return loadFromSource(vsSource, fsSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shader::loadFromSource(const std::string &vsSource,
|
bool Shader::loadFromSource(const std::string &vsSource,
|
||||||
const std::string &fsSource) {
|
const std::string &fsSource) {
|
||||||
// 创建标准2D顶点布局(位置 + 纹理坐标 + 颜色)
|
// 删除旧程序
|
||||||
VertexLayout vertexLayout;
|
if (program_ != 0) {
|
||||||
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
|
glDeleteProgram(program_);
|
||||||
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
|
program_ = 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,
|
uniformCache_.clear();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
|
||||||
if (!device) {
|
|
||||||
E2D_LOG_ERROR("RHIDevice not available");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理源码(添加版本声明)
|
// 处理源码(添加版本声明)
|
||||||
std::string processedVS = addVersionIfNeeded(vsSource, true);
|
std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER);
|
||||||
std::string processedFS = addVersionIfNeeded(fsSource, false);
|
std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER);
|
||||||
|
|
||||||
// 创建着色器描述
|
E2D_LOG_INFO("Compiling vertex shader...");
|
||||||
ShaderDesc shaderDesc;
|
// 编译顶点着色器
|
||||||
shaderDesc.vertexSource = processedVS;
|
GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS);
|
||||||
shaderDesc.fragmentSource = processedFS;
|
if (vs == 0) {
|
||||||
|
E2D_LOG_ERROR("Vertex shader compilation failed");
|
||||||
// 创建着色器
|
|
||||||
auto shader = device->createShader(shaderDesc);
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to create shader");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取着色器句柄
|
E2D_LOG_INFO("Compiling fragment shader...");
|
||||||
handle_ = ShaderHandle(shader.release());
|
// 编译片段着色器
|
||||||
|
GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS);
|
||||||
// 创建管线描述
|
if (fs == 0) {
|
||||||
PipelineDesc pipelineDesc;
|
E2D_LOG_ERROR("Fragment shader compilation failed");
|
||||||
pipelineDesc.vertexShader = handle_;
|
glDeleteShader(vs);
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取管线句柄
|
E2D_LOG_INFO("Linking shader program...");
|
||||||
pipeline_ = PipelineHandle(pipeline.release());
|
// 链接程序
|
||||||
|
if (!linkProgram(vs, fs)) {
|
||||||
|
E2D_LOG_ERROR("Shader program linking failed");
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("Shader created successfully");
|
// 清理着色器对象
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
|
||||||
|
E2D_LOG_INFO("Shader program created successfully, program ID: {}", program_);
|
||||||
return true;
|
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) {
|
void Shader::setUniformBlock(const std::string &name, uint32_t binding) {
|
||||||
// 存储 uniform block 绑定信息
|
if (program_ == 0)
|
||||||
uniformBlockBindings_[name] = binding;
|
return;
|
||||||
|
|
||||||
// 注意:实际的 uniform block 绑定需要在渲染时通过 RHI 命令列表设置
|
GLuint index = glGetUniformBlockIndex(program_, name.c_str());
|
||||||
// 这里仅存储绑定信息,供后续渲染使用
|
if (index != GL_INVALID_INDEX) {
|
||||||
// 例如:commandList->setUniformBlock(binding, buffer);
|
glUniformBlockBinding(program_, index, binding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Shader::getUniformBlockBinding(const std::string &name) const {
|
void Shader::setInt(const std::string &name, int value) {
|
||||||
auto it = uniformBlockBindings_.find(name);
|
GLint location = getUniformLocation(name);
|
||||||
if (it != uniformBlockBindings_.end()) {
|
if (location != -1) {
|
||||||
return it->second;
|
glUniform1i(location, value);
|
||||||
}
|
}
|
||||||
return UINT32_MAX; // 未找到
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shader::reloadFromSource(const std::string &vsSource,
|
void Shader::setFloat(const std::string &name, float value) {
|
||||||
const std::string &fsSource) {
|
GLint location = getUniformLocation(name);
|
||||||
// 创建标准2D顶点布局(位置 + 纹理坐标 + 颜色)
|
if (location != -1) {
|
||||||
VertexLayout vertexLayout;
|
glUniform1f(location, value);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
|
void Shader::setVec2(const std::string &name, float x, float y) {
|
||||||
const std::string &fsSource,
|
GLint location = getUniformLocation(name);
|
||||||
const VertexLayout &vertexLayout) {
|
if (location != -1) {
|
||||||
// 释放旧资源
|
glUniform2f(location, x, y);
|
||||||
pipeline_ = PipelineHandle();
|
}
|
||||||
handle_ = ShaderHandle();
|
}
|
||||||
|
|
||||||
// 获取 RHI 设备
|
void Shader::setVec4(const std::string &name, float x, float y, float z,
|
||||||
auto *rhiModule = RHIModule::get();
|
float w) {
|
||||||
if (!rhiModule) {
|
GLint location = getUniformLocation(name);
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
if (location != -1) {
|
||||||
|
glUniform4f(location, x, y, z, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::setMat4(const std::string &name, const float *value) {
|
||||||
|
GLint location = getUniformLocation(name);
|
||||||
|
if (location != -1) {
|
||||||
|
glUniformMatrix4fv(location, 1, GL_FALSE, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint Shader::compileShader(GLenum type, const std::string &source) {
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
|
||||||
|
const char *src = source.c_str();
|
||||||
|
glShaderSource(shader, 1, &src, nullptr);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
// 检查编译状态
|
||||||
|
GLint success;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
char infoLog[512];
|
||||||
|
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||||
|
|
||||||
|
const char *typeStr = (type == GL_VERTEX_SHADER) ? "vertex" : "fragment";
|
||||||
|
E2D_LOG_ERROR("{} shader compilation failed: {}", typeStr, infoLog);
|
||||||
|
|
||||||
|
glDeleteShader(shader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shader::linkProgram(GLuint vertexShader, GLuint fragmentShader) {
|
||||||
|
program_ = glCreateProgram();
|
||||||
|
glAttachShader(program_, vertexShader);
|
||||||
|
glAttachShader(program_, fragmentShader);
|
||||||
|
glLinkProgram(program_);
|
||||||
|
|
||||||
|
// 检查链接状态
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(program_, GL_LINK_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
char infoLog[512];
|
||||||
|
glGetProgramInfoLog(program_, 512, nullptr, infoLog);
|
||||||
|
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
|
||||||
|
|
||||||
|
glDeleteProgram(program_);
|
||||||
|
program_ = 0;
|
||||||
return false;
|
return 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Shader::addVersionIfNeeded(const std::string &source,
|
GLint Shader::getUniformLocation(const std::string &name) {
|
||||||
bool isVertex) {
|
if (program_ == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// 检查缓存
|
||||||
|
auto it = uniformCache_.find(name);
|
||||||
|
if (it != uniformCache_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 uniform 位置
|
||||||
|
GLint location = glGetUniformLocation(program_, name.c_str());
|
||||||
|
uniformCache_[name] = location;
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Shader::addVersionIfNeeded(const std::string &source, GLenum type) {
|
||||||
// 如果已经包含版本声明,直接返回
|
// 如果已经包含版本声明,直接返回
|
||||||
if (source.find("#version") != std::string::npos) {
|
if (source.find("#version") != std::string::npos) {
|
||||||
return source;
|
return source;
|
||||||
|
|
@ -191,7 +215,7 @@ std::string Shader::addVersionIfNeeded(const std::string &source,
|
||||||
std::string result = "#version 320 es\n";
|
std::string result = "#version 320 es\n";
|
||||||
|
|
||||||
// 片段着色器需要添加精度声明
|
// 片段着色器需要添加精度声明
|
||||||
if (!isVertex) {
|
if (type == GL_FRAGMENT_SHADER) {
|
||||||
result += "precision mediump float;\n";
|
result += "precision mediump float;\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,46 @@
|
||||||
#include <renderer/texture.h>
|
#include <renderer/texture.h>
|
||||||
#include <renderer/rhi_module.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
|
|
||||||
namespace extra2d {
|
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() = default;
|
||||||
|
|
||||||
Texture::~Texture() {
|
Texture::~Texture() {
|
||||||
// RHI 纹理句柄是轻量级句柄,不需要显式释放
|
if (texture_ != 0) {
|
||||||
// 实际的纹理资源由 RHI 设备管理
|
glDeleteTextures(1, &texture_);
|
||||||
handle_ = TextureHandle();
|
texture_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::loadFromFile(const std::string& path) {
|
bool Texture::loadFromFile(const std::string& path) {
|
||||||
|
|
@ -46,186 +75,81 @@ bool Texture::loadFromFile(const std::string& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
||||||
// 释放旧纹理
|
if (texture_ != 0) {
|
||||||
handle_ = TextureHandle();
|
glDeleteTextures(1, &texture_);
|
||||||
|
texture_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
// 获取 RHI 设备
|
// 创建纹理
|
||||||
auto* rhiModule = RHIModule::get();
|
glGenTextures(1, &texture_);
|
||||||
if (!rhiModule) {
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* device = rhiModule->getDevice();
|
// 设置纹理参数
|
||||||
if (!device) {
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
E2D_LOG_ERROR("RHIDevice not available");
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
return false;
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
}
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
// 创建纹理描述
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传纹理数据
|
// 上传纹理数据
|
||||||
if (data) {
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
getTextureInternalFormat(format_),
|
||||||
// 这里假设 update 接受数据指针
|
width_, height_, 0,
|
||||||
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
getTextureFormat(format_),
|
||||||
// 生成 mipmap
|
GL_UNSIGNED_BYTE, data);
|
||||||
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取纹理句柄
|
// 生成 mipmap
|
||||||
handle_ = TextureHandle(texture.release());
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::create(int width, int height, TextureFormat format) {
|
bool Texture::create(int width, int height, TextureFormat format) {
|
||||||
// 释放旧纹理
|
if (texture_ != 0) {
|
||||||
handle_ = TextureHandle();
|
glDeleteTextures(1, &texture_);
|
||||||
|
texture_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
// 获取 RHI 设备
|
// 创建纹理
|
||||||
auto* rhiModule = RHIModule::get();
|
glGenTextures(1, &texture_);
|
||||||
if (!rhiModule) {
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* device = rhiModule->getDevice();
|
// 设置纹理参数
|
||||||
if (!device) {
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
E2D_LOG_ERROR("RHIDevice not available");
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
return false;
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
}
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
// 创建纹理描述
|
// 分配纹理存储(无数据)
|
||||||
TextureDesc desc;
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
desc.width = static_cast<uint32_t>(width);
|
getTextureInternalFormat(format_),
|
||||||
desc.height = static_cast<uint32_t>(height);
|
width_, height_, 0,
|
||||||
desc.format = format;
|
getTextureFormat(format_),
|
||||||
desc.mipLevels = 1; // 不生成 mipmap
|
GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
// 创建 RHI 纹理
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
auto texture = device->createTexture(desc);
|
|
||||||
if (!texture) {
|
|
||||||
E2D_LOG_ERROR("Failed to create RHI texture");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取纹理句柄
|
|
||||||
handle_ = TextureHandle(texture.release());
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::reloadFromFile(const std::string& path) {
|
void Texture::bind(uint32_t slot) const {
|
||||||
// 加载图片
|
if (texture_ != 0) {
|
||||||
int channels;
|
glActiveTexture(GL_TEXTURE0 + slot);
|
||||||
stbi_set_flip_vertically_on_load(true);
|
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||||
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
}
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
E2D_LOG_ERROR("Failed to reload texture: {}", path);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据通道数确定格式
|
void Texture::unbind() const {
|
||||||
TextureFormat format;
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#include <renderer/rhi/rhi_buffer.h>
|
|
||||||
#include <renderer/rhi_module.h>
|
|
||||||
#include <renderer/uniform_buffer.h>
|
#include <renderer/uniform_buffer.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
|
@ -7,54 +5,43 @@ namespace extra2d {
|
||||||
|
|
||||||
UniformBuffer::UniformBuffer() = default;
|
UniformBuffer::UniformBuffer() = default;
|
||||||
|
|
||||||
UniformBuffer::~UniformBuffer() { destroy(); }
|
UniformBuffer::~UniformBuffer() {
|
||||||
|
|
||||||
bool UniformBuffer::create(uint32_t size, uint32_t binding) {
|
|
||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
// 获取 RHI 设备
|
|
||||||
auto *rhiModule = RHIModule::get();
|
|
||||||
if (!rhiModule) {
|
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
bool UniformBuffer::create(uint32_t size, uint32_t binding) {
|
||||||
if (!device) {
|
if (ubo_ != 0) {
|
||||||
E2D_LOG_ERROR("RHIDevice not available");
|
destroy();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_ = size;
|
size_ = size;
|
||||||
binding_ = binding;
|
binding_ = binding;
|
||||||
|
|
||||||
// 创建 uniform 缓冲区描述
|
glGenBuffers(1, &ubo_);
|
||||||
BufferDesc desc = BufferDesc::uniform(size);
|
if (ubo_ == 0) {
|
||||||
|
E2D_LOG_ERROR("Failed to generate UBO");
|
||||||
// 创建缓冲区
|
|
||||||
auto buffer = device->createBuffer(desc);
|
|
||||||
if (!buffer) {
|
|
||||||
E2D_LOG_ERROR("Failed to create uniform buffer");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取缓冲区句柄
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_);
|
||||||
handle_ = BufferHandle(buffer.release());
|
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);
|
E2D_LOG_DEBUG("UBO created: size={}, binding={}", size, binding);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformBuffer::destroy() {
|
void UniformBuffer::destroy() {
|
||||||
// RHIHandle 是轻量级句柄,不需要显式释放
|
if (ubo_ != 0) {
|
||||||
// 实际的缓冲区资源由 RHI 设备管理
|
glDeleteBuffers(1, &ubo_);
|
||||||
handle_ = BufferHandle();
|
ubo_ = 0;
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UniformBuffer::update(const void* data, uint32_t size, uint32_t offset) {
|
void UniformBuffer::update(const void* data, uint32_t size, uint32_t offset) {
|
||||||
if (!handle_.isValid() || data == nullptr)
|
if (ubo_ == 0 || data == nullptr) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (offset + size > size_) {
|
if (offset + size > size_) {
|
||||||
E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}",
|
E2D_LOG_WARN("UBO update out of bounds: offset={}, size={}, bufferSize={}",
|
||||||
|
|
@ -62,99 +49,68 @@ void UniformBuffer::update(const void *data, uint32_t size, uint32_t offset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过 RHI 缓冲区接口直接更新数据
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_);
|
||||||
RHIBuffer *buffer = handle_.get();
|
glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
|
||||||
if (buffer) {
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
buffer->update(data, size, offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformBuffer::bind(uint32_t binding) {
|
void UniformBuffer::bind(uint32_t binding) {
|
||||||
if (!handle_.isValid())
|
if (ubo_ == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// 记录绑定槽位
|
glBindBufferBase(GL_UNIFORM_BUFFER, binding, ubo_);
|
||||||
// 注意:实际的 GPU 绑定操作需要在渲染时通过 RHICommandList::setUniformBuffer
|
|
||||||
// 进行 示例:commandList->setUniformBuffer(binding, handle_.get());
|
|
||||||
binding_ = binding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RHIBuffer *UniformBuffer::getRHIBuffer() const { return handle_.get(); }
|
|
||||||
|
|
||||||
UniformBufferManager::UniformBufferManager() = default;
|
UniformBufferManager::UniformBufferManager() = default;
|
||||||
|
|
||||||
UniformBufferManager::~UniformBufferManager() { shutdown(); }
|
UniformBufferManager::~UniformBufferManager() {
|
||||||
|
shutdown();
|
||||||
UniformBufferManager::UniformBufferManager(
|
|
||||||
UniformBufferManager &&other) noexcept
|
|
||||||
: 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;
|
|
||||||
other.materialUBOBufferOffset_ = 0;
|
|
||||||
other.currentMaterialUBO_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniformBufferManager &
|
UniformBufferManager::UniformBufferManager(UniformBufferManager&& other) noexcept
|
||||||
UniformBufferManager::operator=(UniformBufferManager &&other) noexcept {
|
: globalUBO_(std::move(other.globalUBO_)),
|
||||||
|
materialUBOPool_(std::move(other.materialUBOPool_)),
|
||||||
|
currentUBOIndex_(other.currentUBOIndex_) {
|
||||||
|
other.currentUBOIndex_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformBufferManager& UniformBufferManager::operator=(UniformBufferManager&& other) noexcept {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
shutdown();
|
shutdown();
|
||||||
|
|
||||||
globalUBOs_ = std::move(other.globalUBOs_);
|
globalUBO_ = std::move(other.globalUBO_);
|
||||||
materialUBOPool_ = std::move(other.materialUBOPool_);
|
materialUBOPool_ = std::move(other.materialUBOPool_);
|
||||||
currentUBOIndex_ = other.currentUBOIndex_;
|
currentUBOIndex_ = other.currentUBOIndex_;
|
||||||
materialUBOBuffer_ = std::move(other.materialUBOBuffer_);
|
|
||||||
materialUBOBufferOffset_ = other.materialUBOBufferOffset_;
|
|
||||||
currentMaterialUBO_ = other.currentMaterialUBO_;
|
|
||||||
|
|
||||||
other.currentUBOIndex_ = 0;
|
other.currentUBOIndex_ = 0;
|
||||||
other.materialUBOBufferOffset_ = 0;
|
|
||||||
other.currentMaterialUBO_ = nullptr;
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UniformBufferManager::initialize() {
|
bool UniformBufferManager::initialize() {
|
||||||
// 创建全局 UBO 双缓冲
|
// 创建全局 UBO
|
||||||
for (size_t i = 0; i < globalUBOs_.size(); ++i) {
|
globalUBO_ = std::make_unique<UniformBuffer>();
|
||||||
globalUBOs_[i] = std::make_unique<UniformBuffer>();
|
if (!globalUBO_->create(GLOBAL_UBO_SIZE, GLOBAL_UBO_BINDING)) {
|
||||||
if (!globalUBOs_[i]->create(UniformBufferManager::GLOBAL_UBO_SIZE,
|
E2D_LOG_ERROR("Failed to create global UBO");
|
||||||
UniformBufferManager::GLOBAL_UBO_BINDING)) {
|
|
||||||
E2D_LOG_ERROR("Failed to create global UBO {}", i);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 预分配材质 UBO 池
|
// 预分配材质 UBO 池
|
||||||
materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE);
|
materialUBOPool_.reserve(INITIAL_UBO_POOL_SIZE);
|
||||||
|
|
||||||
// 预分配材质 UBO CPU 缓冲区
|
E2D_LOG_INFO("UniformBufferManager initialized");
|
||||||
materialUBOBuffer_.resize(MATERIAL_UBO_BUFFER_SIZE);
|
|
||||||
|
|
||||||
E2D_LOG_INFO("UniformBufferManager initialized with double buffering");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformBufferManager::shutdown() {
|
void UniformBufferManager::shutdown() {
|
||||||
materialUBOPool_.clear();
|
materialUBOPool_.clear();
|
||||||
for (auto& ubo : globalUBOs_) {
|
globalUBO_.reset();
|
||||||
ubo.reset();
|
|
||||||
}
|
|
||||||
currentUBOIndex_ = 0;
|
currentUBOIndex_ = 0;
|
||||||
materialUBOBuffer_.clear();
|
|
||||||
materialUBOBufferOffset_ = 0;
|
|
||||||
currentMaterialUBO_ = nullptr;
|
|
||||||
|
|
||||||
E2D_LOG_INFO("UniformBufferManager shutdown");
|
E2D_LOG_INFO("UniformBufferManager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
UniformBuffer *UniformBufferManager::getGlobalUBO(uint32_t frameIndex) {
|
UniformBuffer* UniformBufferManager::getGlobalUBO() {
|
||||||
// 使用双缓冲,根据帧索引选择 UBO
|
return globalUBO_.get();
|
||||||
size_t index = frameIndex % 2;
|
|
||||||
return globalUBOs_[index].get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniformBuffer* UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
UniformBuffer* UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
||||||
|
|
@ -168,7 +124,7 @@ UniformBuffer *UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
||||||
|
|
||||||
// 需要创建新的 UBO
|
// 需要创建新的 UBO
|
||||||
auto ubo = std::make_unique<UniformBuffer>();
|
auto ubo = std::make_unique<UniformBuffer>();
|
||||||
if (!ubo->create(size, UniformBufferManager::MATERIAL_UBO_BINDING)) {
|
if (!ubo->create(size, MATERIAL_UBO_BINDING)) {
|
||||||
E2D_LOG_ERROR("Failed to create material UBO");
|
E2D_LOG_ERROR("Failed to create material UBO");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -182,37 +138,13 @@ UniformBuffer *UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
||||||
|
|
||||||
void UniformBufferManager::resetMaterialUBOs() {
|
void UniformBufferManager::resetMaterialUBOs() {
|
||||||
currentUBOIndex_ = 0;
|
currentUBOIndex_ = 0;
|
||||||
materialUBOBufferOffset_ = 0;
|
|
||||||
currentMaterialUBO_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size, uint32_t frameIndex) {
|
void UniformBufferManager::updateGlobalUBO(const void* data, uint32_t size) {
|
||||||
// 使用双缓冲更新全局 UBO
|
if (globalUBO_) {
|
||||||
size_t index = frameIndex % 2;
|
globalUBO_->update(data, size);
|
||||||
if (globalUBOs_[index]) {
|
globalUBO_->bind(GLOBAL_UBO_BINDING);
|
||||||
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,8 @@ void SpriteRenderer::render() {
|
||||||
uint32_t textureId = texture_.index();
|
uint32_t textureId = texture_.index();
|
||||||
cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF);
|
cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF);
|
||||||
|
|
||||||
// 如果没有指定网格,使用默认的四边形网格
|
cmd.drawMesh.mesh = MeshHandle::invalid();
|
||||||
cmd.drawMesh.mesh = Handle<Mesh>::invalid(); // RendererModule 会使用默认网格
|
cmd.drawMesh.material = material_.isValid() ? material_ : MaterialHandle::invalid();
|
||||||
cmd.drawMesh.material = material_.isValid() ? material_ : Handle<Material>::invalid();
|
|
||||||
cmd.setTransform(worldTransform);
|
cmd.setTransform(worldTransform);
|
||||||
|
|
||||||
cmd.setColor(color_);
|
cmd.setColor(color_);
|
||||||
|
|
|
||||||
|
|
@ -130,9 +130,8 @@ void Director::render() {
|
||||||
CameraComponent *camera = runningScene_->getMainCamera();
|
CameraComponent *camera = runningScene_->getMainCamera();
|
||||||
if (camera) {
|
if (camera) {
|
||||||
Mat4 viewProj = camera->getViewProjectionMatrix();
|
Mat4 viewProj = camera->getViewProjectionMatrix();
|
||||||
|
|
||||||
events::OnRenderSetCamera::emit(viewProj);
|
events::OnRenderSetCamera::emit(viewProj);
|
||||||
} else {
|
|
||||||
E2D_LOG_WARN("Director::render: No main camera set!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runningScene_->render();
|
runningScene_->render();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ SceneModule::~SceneModule() {
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SceneModule::SceneModule(SceneModule &&) noexcept = default;
|
||||||
|
|
||||||
|
SceneModule &SceneModule::operator=(SceneModule &&) noexcept = default;
|
||||||
|
|
||||||
bool SceneModule::init() {
|
bool SceneModule::init() {
|
||||||
// 创建导演
|
// 创建导演
|
||||||
director_ = makePtr<Director>();
|
director_ = makePtr<Director>();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,24 @@ TimerModule::TimerModule() = default;
|
||||||
|
|
||||||
TimerModule::~TimerModule() { shutdown(); }
|
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() {
|
bool TimerModule::init() {
|
||||||
// 初始化完成,等待 OnUpdate 事件
|
// 初始化完成,等待 OnUpdate 事件
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue