feat(渲染): 实现实例化渲染功能并优化渲染管线

- 添加实例化渲染支持,包括InstanceBuffer、InstanceBufferManager和相关着色器
- 重构渲染命令队列以支持实例化绘制
- 优化材质系统,使用vector保持参数顺序并添加快速查找
- 改进顶点布局系统,支持实例属性
- 添加全局UBO管理,优化uniform数据传递
- 实现新的实例化测试场景节点
- 更新着色器以支持实例化渲染和UBO
- 改进GL命令列表,支持实例属性绑定
- 添加AssetsModule对实例化资源的支持
- 修复Director在没有主相机时的警告日志
This commit is contained in:
ChestnutYueyue 2026-03-03 03:48:55 +08:00
parent 9041833430
commit 91e3e8fe57
32 changed files with 2475 additions and 580 deletions

View File

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

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <extra2d.h> #include <extra2d.h>
#include "instanced_test.h"
using namespace extra2d; using namespace extra2d;
@ -134,7 +135,13 @@ private:
*/ */
void createCamera(); void createCamera();
/**
* @brief
*/
void createInstancedTest();
Ptr<PlayerNode> player_; Ptr<PlayerNode> player_;
std::vector<Ptr<RotatingDecoration>> decorations_; std::vector<Ptr<RotatingDecoration>> decorations_;
Ptr<InstancedTestNode> instancedTest_;
float sceneTime_ = 0.0f; float sceneTime_ = 0.0f;
}; };

View File

@ -0,0 +1,139 @@
#include "instanced_test.h"
#include <assets/assets_module.h>
#include <event/events.h>
#include <module/module_registry.h>
#include <renderer/renderer_module.h>
#include <utils/logger.h>
#include <cmath>
namespace extra2d {
InstancedTestNode::InstancedTestNode() {
setName("InstancedTest");
}
InstancedTestNode::~InstancedTestNode() {
instanceBuffer_.shutdown();
}
bool InstancedTestNode::initialize(uint32_t instanceCount) {
instanceCount_ = instanceCount;
// 获取资源模块
auto* assets = getModule<AssetsModule>();
if (!assets) {
E2D_LOG_ERROR("InstancedTestNode: AssetsModule not available");
return false;
}
// 使用默认网格(四边形)
mesh_ = assets->getDefaultQuad();
if (!mesh_.isValid()) {
E2D_LOG_ERROR("InstancedTestNode: Failed to get default quad mesh");
return false;
}
// 使用默认纹理
texture_ = assets->getDefaultTexture();
// 获取实例化渲染材质
material_ = assets->getInstancedMaterial();
if (!material_.isValid()) {
E2D_LOG_ERROR("InstancedTestNode: Failed to get instanced material");
return false;
}
// 初始化实例缓冲区
if (!instanceBuffer_.initialize(instanceCount)) {
E2D_LOG_ERROR("InstancedTestNode: Failed to initialize instance buffer");
return false;
}
// 预分配实例数据
instanceData_.resize(instanceCount);
// 初始化实例数据
updateInstances();
E2D_LOG_INFO("InstancedTestNode initialized with {} instances", instanceCount);
return true;
}
void InstancedTestNode::update(float dt) {
time_ += dt;
// 更新实例变换
updateInstances();
// 更新GPU缓冲区
if (!instanceData_.empty()) {
instanceBuffer_.updateInstances(instanceData_.data(), instanceCount_);
}
}
void InstancedTestNode::updateInstances() {
// 创建螺旋分布的实例
float radius = 200.0f;
float centerX = 640.0f;
float centerY = 360.0f;
for (uint32_t i = 0; i < instanceCount_; ++i) {
float t = static_cast<float>(i) / instanceCount_;
float angle = t * 6.28318f * 3.0f + time_; // 3圈螺旋
float r = radius * (0.2f + 0.8f * t);
// 位置
instanceData_[i].position.x = centerX + r * std::cos(angle);
instanceData_[i].position.y = centerY + r * std::sin(angle);
// 旋转(朝向中心)
instanceData_[i].rotation = angle + 1.5708f;
// 缩放(随距离变化)
float scale = 0.5f + 0.5f * t;
instanceData_[i].scale.x = scale * 32.0f;
instanceData_[i].scale.y = scale * 32.0f;
// 颜色(彩虹色)
float hue = t + time_ * 0.1f;
float r_color = std::abs(std::fmod(hue * 6.0f, 2.0f) - 1.0f);
float g_color = std::abs(std::fmod(hue * 6.0f + 2.0f, 2.0f) - 1.0f);
float b_color = std::abs(std::fmod(hue * 6.0f + 4.0f, 2.0f) - 1.0f);
instanceData_[i].color.r = r_color;
instanceData_[i].color.g = g_color;
instanceData_[i].color.b = b_color;
instanceData_[i].color.a = 1.0f;
// UV坐标使用完整纹理
instanceData_[i].uvX = 0.0f;
instanceData_[i].uvY = 0.0f;
instanceData_[i].uvWidth = 1.0f;
instanceData_[i].uvHeight = 1.0f;
}
}
void InstancedTestNode::render() {
if (!isVisible() || instanceCount_ == 0) {
return;
}
// 检查资源有效性
if (!mesh_.isValid() || !material_.isValid() || !instanceBuffer_.isValid()) {
return;
}
// 提交实例化渲染命令
RenderCommand cmd;
cmd.type = RenderCommandType::DrawMeshInstanced;
cmd.sortKey = 0;
cmd.drawInstanced.mesh = mesh_;
cmd.drawInstanced.material = material_;
cmd.drawInstanced.instanceBuffer = &instanceBuffer_;
cmd.drawInstanced.instanceCount = instanceCount_;
cmd.drawInstanced.instanceOffset = 0;
events::OnRenderSubmit::emit(cmd);
}
} // namespace extra2d

View File

@ -0,0 +1,58 @@
#pragma once
#include <scene/node.h>
#include <renderer/instance_buffer.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/texture.h>
#include <assets/handle.h>
#include <vector>
namespace extra2d {
/**
* @brief
*
* 使
*/
class InstancedTestNode : public Node {
public:
InstancedTestNode();
~InstancedTestNode() override;
/**
* @brief
* @param instanceCount
* @return
*/
bool initialize(uint32_t instanceCount = 1000);
/**
* @brief
* @param dt
*/
void update(float dt);
/**
* @brief
*/
void render() override;
private:
InstanceBuffer instanceBuffer_; // 实例缓冲区
std::vector<InstanceData> instanceData_; // CPU端实例数据
uint32_t instanceCount_ = 0; // 实例数量
float time_ = 0.0f; // 时间累积
// 资源句柄
Handle<Material> material_;
Handle<Mesh> mesh_;
Handle<Texture> texture_;
/**
* @brief
*/
void updateInstances();
};
} // namespace extra2d

View File

@ -10,7 +10,7 @@ local example_dir = os.scriptdir()
-- 可执行文件目标 -- 可执行文件目标
target("scene_graph_demo") target("scene_graph_demo")
set_kind("binary") set_kind("binary")
add_files("main.cpp", "game_scene.cpp") add_files("main.cpp", "game_scene.cpp", "instanced_test.cpp")
add_includedirs("../../include", ".") add_includedirs("../../include", ".")
add_deps("extra2d") add_deps("extra2d")

View File

@ -175,6 +175,31 @@ public:
Material *getDefaultMaterialPtr(); Material *getDefaultMaterialPtr();
Mesh *getDefaultQuadPtr(); Mesh *getDefaultQuadPtr();
//===========================================================================
// 实例化渲染资源
//===========================================================================
/**
* @brief
* @return
*/
Handle<Shader> getInstancedShader();
/**
* @brief
* @return
*/
Handle<Material> getInstancedMaterial();
Shader *getInstancedShaderPtr();
Material *getInstancedMaterialPtr();
/**
* @brief
* @return
*/
bool createInstancedResources();
//=========================================================================== //===========================================================================
// 热重载 // 热重载
//=========================================================================== //===========================================================================
@ -259,6 +284,10 @@ private:
Handle<Material> defaultMaterial_; Handle<Material> defaultMaterial_;
Handle<Mesh> defaultQuad_; Handle<Mesh> defaultQuad_;
// 实例化渲染资源
Handle<Shader> instancedShader_;
Handle<Material> instancedMaterial_;
// 热重载 // 热重载
bool hotReloadEnabled_ = false; bool hotReloadEnabled_ = false;
float hotReloadInterval_ = 1.0f; float hotReloadInterval_ = 1.0f;

View File

@ -4,6 +4,7 @@
#include <memory> #include <memory>
#include <renderer/rhi/rhi.h> #include <renderer/rhi/rhi.h>
#include <renderer/rhi/rhi_command_list.h> #include <renderer/rhi/rhi_command_list.h>
#include <renderer/uniform_buffer.h>
#include <types/math/color.h> #include <types/math/color.h>
#include <types/math/transform.h> #include <types/math/transform.h>
#include <unordered_map> #include <unordered_map>
@ -15,6 +16,7 @@ namespace extra2d {
class CommandQueue; class CommandQueue;
class Material; class Material;
class Mesh; class Mesh;
class UniformBufferManager;
template <typename T> class Ptr; template <typename T> class Ptr;
/** /**
@ -68,6 +70,9 @@ struct DrawCommand {
uint32_t textureCount; // 纹理数量 uint32_t textureCount; // 纹理数量
BufferHandle materialUBO; // 材质 UBO BufferHandle materialUBO; // 材质 UBO
uint32_t materialUBOSize; // 材质 UBO 大小 uint32_t materialUBOSize; // 材质 UBO 大小
uint32_t materialUBOOffset; // 材质 UBO 在全局缓冲区中的偏移
BufferHandle instanceBuffer; // 实例数据缓冲区(实例化渲染使用)
uint32_t instanceBufferStride; // 实例数据步长
// 变换和颜色数据(用于设置 shader uniform // 变换和颜色数据(用于设置 shader uniform
Mat4 modelMatrix; // 模型矩阵 Mat4 modelMatrix; // 模型矩阵
@ -75,7 +80,8 @@ struct DrawCommand {
DrawCommand() DrawCommand()
: vertexCount(0), indexCount(0), instanceCount(1), textureCount(0), : vertexCount(0), indexCount(0), instanceCount(1), textureCount(0),
materialUBOSize(0), modelMatrix(glm::identity<Mat4>()), color(Color::White) {} materialUBOSize(0), materialUBOOffset(0), instanceBufferStride(0),
modelMatrix(glm::identity<Mat4>()), color(Color::White) {}
// 检查是否使用索引绘制 // 检查是否使用索引绘制
bool isIndexed() const { return indexCount > 0; } bool isIndexed() const { return indexCount > 0; }
@ -265,10 +271,11 @@ public:
* @brief * @brief
* @param material * @param material
* @param mesh * @param mesh
* @param instanceBuffer
* @param instanceCount * @param instanceCount
*/ */
void submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh, void submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh,
uint32_t instanceCount); BufferHandle instanceBuffer, uint32_t instanceCount);
/** /**
* @brief * @brief
@ -293,6 +300,16 @@ public:
*/ */
void execute(); void execute();
/**
* @brief UBO
* @param viewProjection
* @param deltaTime
* @param screenWidth
* @param screenHeight
*/
void updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight);
/** /**
* @brief * @brief
* @return * @return
@ -311,12 +328,23 @@ private:
RHIContext *context_ = nullptr; RHIContext *context_ = nullptr;
std::unique_ptr<RHICommandList> commandList_; std::unique_ptr<RHICommandList> commandList_;
// 全局 UBO 数据 // UBO 管理器
struct GlobalUBOData { std::unique_ptr<UniformBufferManager> uboManager_;
float viewProjection[16];
float time; // 全局 UBO 数据 - 必须与着色器中的 std140 布局完全匹配
float screenSize[2]; // layout(std140, binding = 0) uniform GlobalUBO {
float padding; // 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_; } globalUBOData_;
// 材质 UBO 数据缓冲区 // 材质 UBO 数据缓冲区
@ -328,6 +356,10 @@ private:
// 材质到 ID 的映射 // 材质到 ID 的映射
std::unordered_map<Material *, uint32_t> materialIds_; std::unordered_map<Material *, uint32_t> materialIds_;
// 当前材质 UBO 缓冲区
UniformBuffer* currentMaterialUBO_ = nullptr;
uint32_t currentMaterialUBOOffset_ = 0;
/** /**
* @brief ID * @brief ID
* @param material * @param material
@ -341,6 +373,13 @@ private:
* @param batch * @param batch
*/ */
void executeBatch(uint32_t batchIndex, const CommandBatch &batch); void executeBatch(uint32_t batchIndex, const CommandBatch &batch);
/**
* @brief UBO
* @param size
* @return UBO
*/
std::pair<UniformBuffer*, uint32_t> allocateMaterialUBO(uint32_t size);
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,208 @@
#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
*/
struct InstanceData {
// 第一组 16 字节
Vec2 position; // 位置偏移 (8 bytes)
float rotation; // 旋转角度 (4 bytes)
float padding0; // 填充到 16 字节对齐 (4 bytes)
// 第二组 16 字节
Vec2 scale; // 缩放 (8 bytes)
float padding1[2]; // 填充到 16 字节对齐 (8 bytes)
// 第三组 16 字节
Color color; // 颜色 (16 bytes) - r, g, b, a
// 第四组 16 字节
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)
, rotation(0.0f)
, padding0(0.0f)
, 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) {}
};
static_assert(sizeof(InstanceData) == 64, "InstanceData size should be 64 bytes for std140 alignment");
/**
* @brief
*
*
*
*/
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 instance
* @return
*/
uint32_t addInstance(const InstanceData& instance);
/**
* @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(); }
private:
BufferHandle bufferHandle_; // RHI 缓冲区句柄
uint32_t maxInstances_ = 0; // 最大实例数量
uint32_t instanceCount_ = 0; // 当前实例数量
std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区
};
/**
* @brief
*
*
*/
class InstanceBufferManager {
public:
/**
* @brief
*/
InstanceBufferManager();
/**
* @brief
*/
~InstanceBufferManager();
// 禁止拷贝
InstanceBufferManager(const InstanceBufferManager&) = delete;
InstanceBufferManager& operator=(const InstanceBufferManager&) = delete;
// 允许移动
InstanceBufferManager(InstanceBufferManager&& other) noexcept;
InstanceBufferManager& operator=(InstanceBufferManager&& other) noexcept;
/**
* @brief
* @return
*/
bool initialize();
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param minSize
* @return
*/
InstanceBuffer* acquireBuffer(uint32_t minSize);
/**
* @brief
* @param buffer
*/
void releaseBuffer(InstanceBuffer* buffer);
/**
* @brief
*/
void reset();
private:
std::vector<std::unique_ptr<InstanceBuffer>> bufferPool_;
uint32_t currentBufferIndex_ = 0;
static constexpr uint32_t DEFAULT_BUFFER_SIZE = 1024;
};
} // namespace extra2d

View File

@ -109,7 +109,9 @@ public:
bool isFinalized() const { return finalized_; } bool isFinalized() const { return finalized_; }
private: private:
std::unordered_map<std::string, MaterialParamInfo> params_; // 使用vector保持参数添加顺序确保与着色器中的声明顺序一致
std::vector<std::pair<std::string, MaterialParamInfo>> params_;
std::unordered_map<std::string, size_t> paramIndexMap_; // 用于快速查找
uint32_t bufferSize_ = 0; uint32_t bufferSize_ = 0;
bool finalized_ = false; bool finalized_ = false;
}; };

View File

@ -236,8 +236,9 @@ public:
/** /**
* @brief * @brief
* @param deltaTime * @param deltaTime
* @param viewProjection
*/ */
void execute(float deltaTime); void execute(float deltaTime, const Mat4& viewProjection = Mat4(1.0f));
/** /**
* @brief * @brief

View File

@ -46,10 +46,11 @@ struct RenderCommand {
Color color; // 顶点颜色 Color color; // 顶点颜色
}; };
// 实例化绘制命令数据 // 实例化绘制命令数据
struct DrawInstancedData { struct DrawInstancedData {
Handle<Mesh> mesh; // 网格句柄 Handle<Mesh> mesh; // 网格句柄
Handle<Material> material; // 材质句柄 Handle<Material> material; // 材质句柄
void* instanceBuffer; // 实例数据缓冲区指针 (InstanceBuffer*)
uint32_t instanceCount; // 实例数量 uint32_t instanceCount; // 实例数量
uint32_t instanceOffset; // 实例数据偏移 uint32_t instanceOffset; // 实例数据偏移
}; };

View File

@ -51,10 +51,11 @@ public:
//=========================================================================== //===========================================================================
void setVertexBuffer(uint32_t slot, RHIBuffer *buffer, void setVertexBuffer(uint32_t slot, RHIBuffer *buffer,
uint32_t offset = 0) override; uint32_t offset = 0, uint32_t stride = 0) override;
void setIndexBuffer(RHIBuffer *buffer, IndexType type, void setIndexBuffer(RHIBuffer *buffer, IndexType type,
uint32_t offset = 0) override; uint32_t offset = 0) override;
void setUniformBuffer(uint32_t slot, RHIBuffer *buffer) 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 setTexture(uint32_t slot, RHITexture *texture) override;
void setSampler(uint32_t slot, TextureFilter minFilter, void setSampler(uint32_t slot, TextureFilter minFilter,
TextureFilter magFilter, TextureWrap wrapS, TextureFilter magFilter, TextureWrap wrapS,
@ -81,11 +82,11 @@ public:
bool isRecording() const override; bool isRecording() const override;
// 设置 uniform 变量 // 设置 uniform 变量
void setUniform(const std::string& name, float value); void setUniform(const char* name, float value) override;
void setUniform(const std::string& name, const Vec2& value); void setUniform(const char* name, const Vec2& value) override;
void setUniform(const std::string& name, const Vec3& value); void setUniform(const char* name, const Vec3& value) override;
void setUniform(const std::string& name, const Color& value); void setUniform(const char* name, const Color& value) override;
void setUniform(const std::string& name, const Mat4& value); void setUniform(const char* name, const Mat4& value) override;
private: private:
bool recording_ = false; bool recording_ = false;

View File

@ -89,8 +89,9 @@ public:
* @param slot * @param slot
* @param buffer * @param buffer
* @param offset * @param offset
* @param stride 0使
*/ */
virtual void setVertexBuffer(uint32_t slot, RHIBuffer* buffer, uint32_t offset = 0) = 0; virtual void setVertexBuffer(uint32_t slot, RHIBuffer* buffer, uint32_t offset = 0, uint32_t stride = 0) = 0;
/** /**
* @brief * @brief
@ -107,6 +108,15 @@ public:
*/ */
virtual void setUniformBuffer(uint32_t slot, RHIBuffer* buffer) = 0; 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 * @brief
* @param slot * @param slot
@ -128,6 +138,41 @@ public:
TextureWrap wrapS, TextureWrap wrapS,
TextureWrap wrapT) = 0; 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;
//=========================================================================== //===========================================================================
// 绘制命令 // 绘制命令
//=========================================================================== //===========================================================================

View File

@ -229,8 +229,21 @@ struct VertexAttribute {
VertexFormat format = VertexFormat::Float3; VertexFormat format = VertexFormat::Float3;
uint32_t offset = 0; // 在顶点结构中的偏移 uint32_t offset = 0; // 在顶点结构中的偏移
uint32_t bufferIndex = 0; // 绑定的顶点缓冲区索引 uint32_t bufferIndex = 0; // 绑定的顶点缓冲区索引
uint32_t divisor = 0; // 实例化除数0=每顶点1=每实例N=每N个实例
static uint32_t getSize(VertexFormat format); 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;
}
}; };
/** /**
@ -242,6 +255,14 @@ struct VertexLayout {
void addAttribute(uint32_t location, VertexFormat format, uint32_t offset, void addAttribute(uint32_t location, VertexFormat format, uint32_t offset,
uint32_t bufferIndex = 0); uint32_t bufferIndex = 0);
/**
* @brief VertexAttribute
* @param attr
*/
void addAttribute(const VertexAttribute& attr) {
attributes.push_back(attr);
}
}; };
/** /**

View File

@ -43,6 +43,22 @@ public:
*/ */
bool loadFromSource(const std::string &vsSource, const std::string &fsSource); bool loadFromSource(const std::string &vsSource, const std::string &fsSource);
/**
* @brief
* @param vsPath
* @param fsPath
* @return
*/
bool loadInstancedFromFile(const std::string &vsPath, const std::string &fsPath);
/**
* @brief
* @param vsSource
* @param fsSource
* @return
*/
bool loadInstancedFromSource(const std::string &vsSource, const std::string &fsSource);
/** /**
* @brief RHI * @brief RHI
* @return RHI * @return RHI
@ -61,6 +77,17 @@ public:
*/ */
bool isLoaded() const { return handle_.isValid() && pipeline_.isValid(); } 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
* @param name Uniform Block * @param name Uniform Block

View File

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

View File

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

View File

@ -4,14 +4,12 @@ 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;
@ -21,20 +19,19 @@ out vec4 fragColor;
* *
*/ */
void main() { void main() {
// 采样纹理如果没有绑定纹理texture 会返回 vec4(0,0,0,1) 或 vec4(1,1,1,1) 取决于实现) // 采样纹理
vec4 texColor = texture(uTexture, vTexCoord); vec4 texColor = texture(uTexture, vTexCoord);
// 如果纹理采样结果是黑色或透明,使用白色作为默认值 // 如果纹理采样结果是黑色或透明,使用白色作为默认值
// 这样即使在没有纹理的情况下也能显示颜色
if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) {
texColor = vec4(1.0, 1.0, 1.0, 1.0); texColor = vec4(1.0, 1.0, 1.0, 1.0);
} }
// 混合:纹理 * 顶点颜色 * 色调 // 混合:纹理 * 顶点颜色 * 色调
fragColor = texColor * vColor * uTintColor; fragColor = texColor * vColor * vTintColor;
// 应用透明度 // 应用透明度
fragColor.a *= uOpacity; fragColor.a *= vOpacity;
// Alpha 测试:丢弃几乎透明的像素 // Alpha 测试:丢弃几乎透明的像素
if (fragColor.a < 0.01) { if (fragColor.a < 0.01) {

View File

@ -1,15 +1,26 @@
#version 320 es #version 320 es
precision highp float; precision highp float;
// 视图投影矩阵 // 全局 UBO (binding = 0) - 每帧更新一次
uniform mat4 uViewProjection; layout(std140, binding = 0) uniform GlobalUBO {
mat4 uViewProjection;
vec4 uCameraPosition;
float uTime;
float uDeltaTime;
vec2 uScreenSize;
};
// 模型矩阵 // 材质 UBO (binding = 1) - 每物体更新
layout(std140, binding = 1) uniform MaterialUBO {
vec4 uColor;
vec4 uTintColor;
float uOpacity;
float uPadding[3]; // std140 对齐填充
};
// 模型矩阵作为单独的统一变量(每个物体设置)
uniform mat4 uModelMatrix; uniform 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;
@ -18,16 +29,20 @@ 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;
// 使用 uniform 颜色覆盖顶点属性颜色 // 混合顶点颜色和材质 UBO 中的颜色
vColor = uColor; vColor = aColor * uColor;
vTintColor = uTintColor;
vOpacity = uOpacity;
} }

73
shader/instanced.vert Normal file
View File

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

View File

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

View File

@ -0,0 +1,58 @@
#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;
};
// 顶点属性
layout(location = 0) in vec2 aPosition; // 基础顶点位置
layout(location = 1) in vec2 aTexCoord; // 基础 UV
layout(location = 2) in vec4 aColor; // 基础颜色
// 实例属性 (每个实例)
layout(location = 3) in vec2 aInstancePos; // 实例位置偏移
layout(location = 4) in float aInstanceRot; // 实例旋转
layout(location = 5) in vec2 aInstanceScale; // 实例缩放
layout(location = 6) in vec4 aInstanceColor; // 实例颜色
layout(location = 7) in vec4 aInstanceUV; // 实例 UV 区域
// 输出到片段着色器
out vec2 vTexCoord;
out vec4 vColor;
/**
* @brief
*
*
*/
void main() {
// 构建旋转矩阵
float cosRot = cos(aInstanceRot);
float sinRot = sin(aInstanceRot);
mat2 rotation = mat2(
cosRot, -sinRot,
sinRot, cosRot
);
// 应用缩放和旋转
vec2 scaledPos = aPosition * aInstanceScale;
vec2 rotatedPos = rotation * scaledPos;
// 应用实例位置偏移
vec2 worldPos = rotatedPos + aInstancePos;
// 计算裁剪空间位置
gl_Position = uViewProjection * vec4(worldPos, 0.0, 1.0);
// 计算 UV 坐标(应用实例 UV 区域)
vTexCoord = aTexCoord * aInstanceUV.zw + aInstanceUV.xy;
// 混合顶点颜色和实例颜色
vColor = aColor * aInstanceColor;
}

View File

@ -1,8 +1,8 @@
#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 <thread> #include <thread>
#include <utils/logger.h> #include <utils/logger.h>
@ -46,6 +46,13 @@ void AssetsModule::onGLContextReady() {
return; return;
} }
// 创建实例化渲染资源
if (!createInstancedResources()) {
E2D_LOG_WARN("Failed to create instanced resources, instanced rendering "
"will not be available");
// 不返回错误,实例化渲染是可选功能
}
defaultResourcesCreated_ = true; defaultResourcesCreated_ = true;
E2D_LOG_INFO("Default resources created successfully"); E2D_LOG_INFO("Default resources created successfully");
} }
@ -123,24 +130,24 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
Handle<Texture> handle; Handle<Texture> handle;
{ {
std::unique_lock<std::shared_mutex> lock(mutex_); std::unique_lock<std::shared_mutex> lock(mutex_);
// 双重检查,避免重复加载 // 双重检查,避免重复加载
auto it = texturePathCache_.find(path); auto it = texturePathCache_.find(path);
if (it != texturePathCache_.end() && textures_.isValid(it->second)) { if (it != texturePathCache_.end() && textures_.isValid(it->second)) {
return it->second; return it->second;
} }
handle = textures_.insert(texture); handle = textures_.insert(texture);
texturePathCache_[path] = handle; texturePathCache_[path] = handle;
} }
E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index()); E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控 // 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) { if (hotReloadEnabled_) {
addFileWatch(path, handle); addFileWatch(path, handle);
} }
return handle; return handle;
} }
@ -177,8 +184,7 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
// Shader 加载特化 // Shader 加载特化
//=========================================================================== //===========================================================================
template <> template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
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_);
@ -205,24 +211,24 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
Handle<Shader> handle; Handle<Shader> handle;
{ {
std::unique_lock<std::shared_mutex> lock(mutex_); std::unique_lock<std::shared_mutex> lock(mutex_);
// 双重检查 // 双重检查
auto it = shaderPathCache_.find(path); auto it = shaderPathCache_.find(path);
if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) { if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) {
return it->second; return it->second;
} }
handle = shaders_.insert(shader); handle = shaders_.insert(shader);
shaderPathCache_[path] = handle; shaderPathCache_[path] = handle;
} }
E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index()); E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控 // 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) { if (hotReloadEnabled_) {
addFileWatch(path, handle); addFileWatch(path, handle);
} }
return handle; return handle;
} }
@ -258,26 +264,26 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
Handle<Shader> handle; Handle<Shader> handle;
{ {
std::unique_lock<std::shared_mutex> lock(mutex_); std::unique_lock<std::shared_mutex> lock(mutex_);
// 双重检查 // 双重检查
auto it = shaderPathCache_.find(cacheKey); auto it = shaderPathCache_.find(cacheKey);
if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) { if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) {
return it->second; return it->second;
} }
handle = shaders_.insert(shader); handle = shaders_.insert(shader);
shaderPathCache_[cacheKey] = handle; shaderPathCache_[cacheKey] = handle;
} }
E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath, E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath,
handle.index()); handle.index());
// 如果启用了热重载,添加文件监控 // 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) { if (hotReloadEnabled_) {
addFileWatch(vertPath, handle); addFileWatch(vertPath, handle);
addFileWatch(fragPath, handle); addFileWatch(fragPath, handle);
} }
return handle; return handle;
} }
@ -408,12 +414,32 @@ bool AssetsModule::createDefaultResources() {
} }
{ {
// 创建材质布局(与着色器中的 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); material->setTexture("uTexture", getPtr(defaultTexture_), 0);
defaultMaterial_ = materials_.insert(material); defaultMaterial_ = materials_.insert(material);
E2D_LOG_DEBUG("Created default material with default texture"); E2D_LOG_DEBUG("Created default material with default texture and layout");
} }
{ {
@ -463,168 +489,231 @@ Material *AssetsModule::getDefaultMaterialPtr() {
Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); } Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
Handle<Shader> AssetsModule::getInstancedShader() { return instancedShader_; }
Handle<Material> AssetsModule::getInstancedMaterial() {
return instancedMaterial_;
}
Shader *AssetsModule::getInstancedShaderPtr() {
return shaders_.get(instancedShader_);
}
Material *AssetsModule::getInstancedMaterialPtr() {
return materials_.get(instancedMaterial_);
}
bool AssetsModule::createInstancedResources() {
// 加载实例化着色器
std::filesystem::path vertPath = "shader/sprite_instanced.vert";
std::filesystem::path fragPath = "shader/sprite_instanced.frag";
if (!std::filesystem::exists(vertPath) ||
!std::filesystem::exists(fragPath)) {
E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath.string(),
fragPath.string());
return false;
}
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadInstancedFromFile(vertPath.string(), fragPath.string())) {
E2D_LOG_ERROR("Failed to load instanced shader from files: {}, {}",
vertPath.string(), fragPath.string());
return false;
}
instancedShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath.string(),
fragPath.string());
// 创建实例化材质布局
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>();
material->setShader(getPtr(instancedShader_));
material->setLayout(layout);
// 设置默认材质参数
material->setColor("uColor", Color::White);
material->setColor("uTintColor", Color::White);
material->setFloat("uOpacity", 1.0f);
// 添加默认纹理到材质
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
instancedMaterial_ = materials_.insert(material);
E2D_LOG_DEBUG("Created instanced material with default texture and layout");
return true;
}
//=========================================================================== //===========================================================================
// 热重载 // 热重载
//=========================================================================== //===========================================================================
void AssetsModule::enableHotReload(bool enable) { void AssetsModule::enableHotReload(bool enable) {
hotReloadEnabled_ = enable; hotReloadEnabled_ = enable;
if (enable) { if (enable) {
E2D_LOG_INFO("Hot reload enabled"); E2D_LOG_INFO("Hot reload enabled");
} else { } else {
E2D_LOG_INFO("Hot reload disabled"); E2D_LOG_INFO("Hot reload disabled");
} }
} }
void AssetsModule::setHotReloadInterval(float interval) { void AssetsModule::setHotReloadInterval(float interval) {
hotReloadInterval_ = interval; hotReloadInterval_ = interval;
} }
void AssetsModule::addFileWatch(const std::string& path, Handle<Texture> handle) { void AssetsModule::addFileWatch(const std::string &path,
try { Handle<Texture> handle) {
if (!std::filesystem::exists(path)) { try {
return; if (!std::filesystem::exists(path)) {
} return;
FileWatchInfo info;
info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path);
info.textureHandle = handle;
info.isTexture = true;
std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(info);
E2D_LOG_DEBUG("Watching texture file: {}", path);
} catch (const std::exception& e) {
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
} }
FileWatchInfo info;
info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path);
info.textureHandle = handle;
info.isTexture = true;
std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(info);
E2D_LOG_DEBUG("Watching texture file: {}", path);
} catch (const std::exception &e) {
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
}
} }
void AssetsModule::addFileWatch(const std::string& path, Handle<Shader> handle) { void AssetsModule::addFileWatch(const std::string &path,
try { Handle<Shader> handle) {
if (!std::filesystem::exists(path)) { try {
return; if (!std::filesystem::exists(path)) {
} return;
FileWatchInfo info;
info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path);
info.shaderHandle = handle;
info.isTexture = false;
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());
} }
FileWatchInfo info;
info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path);
info.shaderHandle = handle;
info.isTexture = false;
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) {
if (!textureLoader_ || !info.textureHandle.isValid()) { if (!textureLoader_ || !info.textureHandle.isValid()) {
return; return;
} }
E2D_LOG_INFO("Reloading texture: {}", info.path); E2D_LOG_INFO("Reloading texture: {}", info.path);
Ptr<Texture> newTexture = textureLoader_->load(info.path); Ptr<Texture> newTexture = textureLoader_->load(info.path);
if (!newTexture) { if (!newTexture) {
E2D_LOG_ERROR("Failed to reload texture: {}", info.path); E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
return; return;
} }
// 替换旧纹理的数据 // 替换旧纹理的数据
Texture* oldTexture = textures_.get(info.textureHandle); Texture *oldTexture = textures_.get(info.textureHandle);
if (oldTexture) { if (oldTexture) {
// 这里假设 Texture 类有更新数据的方法 // 这里假设 Texture 类有更新数据的方法
// 如果没有,可能需要重新设计 // 如果没有,可能需要重新设计
E2D_LOG_INFO("Texture reloaded: {}", info.path); 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()) {
return; return;
}
E2D_LOG_INFO("Reloading shader: {}", info.path);
// 查找缓存键
std::string cacheKey;
{
std::shared_lock<std::shared_mutex> lock(mutex_);
for (const auto &pair : shaderPathCache_) {
if (pair.second == info.shaderHandle) {
cacheKey = pair.first;
break;
}
} }
}
E2D_LOG_INFO("Reloading shader: {}", info.path);
if (cacheKey.empty()) {
// 查找缓存键 E2D_LOG_WARN("Shader cache key not found for: {}", info.path);
std::string cacheKey; return;
{ }
std::shared_lock<std::shared_mutex> lock(mutex_);
for (const auto& pair : shaderPathCache_) { // 解析顶点/片段着色器路径
if (pair.second == info.shaderHandle) { size_t sepPos = cacheKey.find('|');
cacheKey = pair.first; if (sepPos == std::string::npos) {
break; // 单文件模式
} Ptr<Shader> newShader = shaderLoader_->load(info.path);
} if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
return;
} }
} else {
if (cacheKey.empty()) { // 双文件模式
E2D_LOG_WARN("Shader cache key not found for: {}", info.path); std::string vertPath = cacheKey.substr(0, sepPos);
return; std::string fragPath = cacheKey.substr(sepPos + 1);
ShaderLoader *loader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> newShader = loader->load(vertPath, fragPath);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
return;
} }
}
// 解析顶点/片段着色器路径
size_t sepPos = cacheKey.find('|'); E2D_LOG_INFO("Shader reloaded: {}", info.path);
if (sepPos == std::string::npos) { notifyShaderReloaded(info.shaderHandle);
// 单文件模式
Ptr<Shader> newShader = shaderLoader_->load(info.path);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
return;
}
} else {
// 双文件模式
std::string vertPath = cacheKey.substr(0, sepPos);
std::string fragPath = cacheKey.substr(sepPos + 1);
ShaderLoader* loader = static_cast<ShaderLoader*>(shaderLoader_.get());
Ptr<Shader> newShader = loader->load(vertPath, fragPath);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
return;
}
}
E2D_LOG_INFO("Shader reloaded: {}", info.path);
notifyShaderReloaded(info.shaderHandle);
} }
void AssetsModule::checkForChanges() { void AssetsModule::checkForChanges() {
if (!hotReloadEnabled_ || fileWatchList_.empty()) { if (!hotReloadEnabled_ || fileWatchList_.empty()) {
return; return;
} }
std::unique_lock<std::shared_mutex> lock(mutex_); std::unique_lock<std::shared_mutex> lock(mutex_);
for (auto& info : fileWatchList_) { for (auto &info : fileWatchList_) {
try { try {
if (!std::filesystem::exists(info.path)) { if (!std::filesystem::exists(info.path)) {
continue; continue;
} }
auto currentTime = std::filesystem::last_write_time(info.path); auto currentTime = std::filesystem::last_write_time(info.path);
if (currentTime != info.lastWriteTime) { if (currentTime != info.lastWriteTime) {
info.lastWriteTime = currentTime; info.lastWriteTime = currentTime;
// 解锁进行重载操作 // 解锁进行重载操作
lock.unlock(); lock.unlock();
if (info.isTexture) { if (info.isTexture) {
reloadTexture(info); reloadTexture(info);
} else { } else {
reloadShader(info); reloadShader(info);
}
lock.lock();
}
} catch (const std::exception& e) {
E2D_LOG_ERROR("Error checking file {}: {}", info.path, e.what());
} }
lock.lock();
}
} catch (const std::exception &e) {
E2D_LOG_ERROR("Error checking file {}: {}", info.path, e.what());
} }
}
} }
//=========================================================================== //===========================================================================
@ -632,216 +721,218 @@ void AssetsModule::checkForChanges() {
//=========================================================================== //===========================================================================
void AssetsModule::initAsyncLoader(uint32_t threadCount) { void AssetsModule::initAsyncLoader(uint32_t threadCount) {
if (asyncLoaderRunning_) { if (asyncLoaderRunning_) {
return; return;
} }
if (threadCount == 0) { if (threadCount == 0) {
threadCount = std::max(1u, std::thread::hardware_concurrency() / 2); threadCount = std::max(1u, std::thread::hardware_concurrency() / 2);
} }
asyncLoaderRunning_ = true; asyncLoaderRunning_ = true;
for (uint32_t i = 0; i < threadCount; ++i) { for (uint32_t i = 0; i < threadCount; ++i) {
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this); workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
} }
E2D_LOG_INFO("Async loader initialized with {} threads", threadCount); E2D_LOG_INFO("Async loader initialized with {} threads", threadCount);
} }
void AssetsModule::shutdownAsyncLoader() { void AssetsModule::shutdownAsyncLoader() {
if (!asyncLoaderRunning_) { if (!asyncLoaderRunning_) {
return; return;
}
asyncLoaderRunning_ = false;
queueCV_.notify_all();
for (auto &thread : workerThreads_) {
if (thread.joinable()) {
thread.join();
} }
}
asyncLoaderRunning_ = false;
queueCV_.notify_all(); workerThreads_.clear();
for (auto& thread : workerThreads_) { std::lock_guard<std::mutex> lock(queueMutex_);
if (thread.joinable()) { loadQueue_.clear();
thread.join();
} E2D_LOG_INFO("Async loader shutdown");
}
workerThreads_.clear();
std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.clear();
E2D_LOG_INFO("Async loader shutdown");
} }
void AssetsModule::submitLoadTask(const LoadTask& task) { void AssetsModule::submitLoadTask(const LoadTask &task) {
{ {
std::lock_guard<std::mutex> lock(queueMutex_); std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.push_back(task); loadQueue_.push_back(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) > static_cast<int>(b.priority); return static_cast<int>(a.priority) >
}); static_cast<int>(b.priority);
} });
queueCV_.notify_one(); }
queueCV_.notify_one();
} }
void AssetsModule::workerThreadLoop() { void AssetsModule::workerThreadLoop() {
while (asyncLoaderRunning_) { while (asyncLoaderRunning_) {
LoadTask task; LoadTask task;
{ {
std::unique_lock<std::mutex> lock(queueMutex_); std::unique_lock<std::mutex> lock(queueMutex_);
queueCV_.wait(lock, [this] { queueCV_.wait(
return !loadQueue_.empty() || !asyncLoaderRunning_; lock, [this] { return !loadQueue_.empty() || !asyncLoaderRunning_; });
});
if (!asyncLoaderRunning_) {
if (!asyncLoaderRunning_) { break;
break; }
}
if (loadQueue_.empty()) {
if (loadQueue_.empty()) { continue;
continue; }
}
task = std::move(loadQueue_.back());
task = std::move(loadQueue_.back()); loadQueue_.pop_back();
loadQueue_.pop_back();
}
// 执行加载任务
if (task.type == LoadTask::Type::Texture) {
Handle<Texture> handle = load<Texture>(task.path);
if (task.textureCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back([handle, callback = task.textureCallback]() {
callback(handle);
});
}
} else if (task.type == LoadTask::Type::Shader) {
Handle<Shader> handle;
if (task.secondaryPath.empty()) {
handle = load<Shader>(task.path);
} else {
handle = load<Shader>(task.path, task.secondaryPath);
}
if (task.shaderCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back([handle, callback = task.shaderCallback]() {
callback(handle);
});
}
}
} }
// 执行加载任务
if (task.type == LoadTask::Type::Texture) {
Handle<Texture> handle = load<Texture>(task.path);
if (task.textureCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back(
[handle, callback = task.textureCallback]() { callback(handle); });
}
} else if (task.type == LoadTask::Type::Shader) {
Handle<Shader> handle;
if (task.secondaryPath.empty()) {
handle = load<Shader>(task.path);
} else {
handle = load<Shader>(task.path, task.secondaryPath);
}
if (task.shaderCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back(
[handle, callback = task.shaderCallback]() { callback(handle); });
}
}
}
} }
void AssetsModule::processAsyncCallbacks() { void AssetsModule::processAsyncCallbacks() {
std::vector<std::function<void()>> callbacks; std::vector<std::function<void()>> callbacks;
{ {
std::lock_guard<std::mutex> lock(callbackMutex_); std::lock_guard<std::mutex> lock(callbackMutex_);
callbacks = std::move(completedCallbacks_); callbacks = std::move(completedCallbacks_);
completedCallbacks_.clear(); completedCallbacks_.clear();
} }
for (auto& callback : callbacks) { for (auto &callback : callbacks) {
callback(); callback();
} }
} }
//=========================================================================== //===========================================================================
// 资源依赖跟踪 // 资源依赖跟踪
//=========================================================================== //===========================================================================
void AssetsModule::registerMaterialDependency(Handle<Material> material, Handle<Texture> texture) { void AssetsModule::registerMaterialDependency(Handle<Material> material,
if (!material.isValid() || !texture.isValid()) { Handle<Texture> texture) {
return; if (!material.isValid() || !texture.isValid()) {
} return;
}
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t textureIndex = texture.index();
auto& info = textureDependencies_[textureIndex]; uint32_t textureIndex = texture.index();
info.texture = texture; auto &info = textureDependencies_[textureIndex];
info.texture = texture;
// 检查是否已存在
auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material); // 检查是否已存在
if (it == info.dependentMaterials.end()) { auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.push_back(material); info.dependentMaterials.end(), material);
E2D_LOG_DEBUG("Registered material {} dependency on texture {}", if (it == info.dependentMaterials.end()) {
material.index(), textureIndex); info.dependentMaterials.push_back(material);
} E2D_LOG_DEBUG("Registered material {} dependency on texture {}",
material.index(), textureIndex);
}
} }
void AssetsModule::registerMaterialDependency(Handle<Material> material, Handle<Shader> shader) { void AssetsModule::registerMaterialDependency(Handle<Material> material,
if (!material.isValid() || !shader.isValid()) { Handle<Shader> shader) {
return; if (!material.isValid() || !shader.isValid()) {
} return;
}
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t shaderIndex = shader.index();
auto& info = shaderDependencies_[shaderIndex]; uint32_t shaderIndex = shader.index();
info.shader = shader; auto &info = shaderDependencies_[shaderIndex];
info.shader = shader;
// 检查是否已存在
auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material); // 检查是否已存在
if (it == info.dependentMaterials.end()) { auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.push_back(material); info.dependentMaterials.end(), material);
E2D_LOG_DEBUG("Registered material {} dependency on shader {}", if (it == info.dependentMaterials.end()) {
material.index(), shaderIndex); info.dependentMaterials.push_back(material);
} E2D_LOG_DEBUG("Registered material {} dependency on shader {}",
material.index(), shaderIndex);
}
} }
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) { void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
if (!texture.isValid()) { if (!texture.isValid()) {
return; return;
} }
std::shared_lock<std::shared_mutex> lock(dependencyMutex_); std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t textureIndex = texture.index(); uint32_t textureIndex = texture.index();
auto it = textureDependencies_.find(textureIndex); auto it = textureDependencies_.find(textureIndex);
if (it != textureDependencies_.end()) { if (it != textureDependencies_.end()) {
E2D_LOG_INFO("Notifying {} materials of texture reload", E2D_LOG_INFO("Notifying {} materials of texture reload",
it->second.dependentMaterials.size()); it->second.dependentMaterials.size());
// 材质需要更新纹理引用 // 材质需要更新纹理引用
// 这里可以触发材质更新事件 // 这里可以触发材质更新事件
for (const auto& materialHandle : it->second.dependentMaterials) { for (const auto &materialHandle : it->second.dependentMaterials) {
Material* material = materials_.get(materialHandle); Material *material = materials_.get(materialHandle);
if (material) { if (material) {
// 材质自动使用新的纹理数据 // 材质自动使用新的纹理数据
E2D_LOG_DEBUG("Material {} updated with reloaded texture", E2D_LOG_DEBUG("Material {} updated with reloaded texture",
materialHandle.index()); materialHandle.index());
} }
}
} }
}
} }
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) { void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
if (!shader.isValid()) { if (!shader.isValid()) {
return; return;
} }
std::shared_lock<std::shared_mutex> lock(dependencyMutex_); std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t shaderIndex = shader.index(); uint32_t shaderIndex = shader.index();
auto it = shaderDependencies_.find(shaderIndex); auto it = shaderDependencies_.find(shaderIndex);
if (it != shaderDependencies_.end()) { if (it != shaderDependencies_.end()) {
E2D_LOG_INFO("Notifying {} materials of shader reload", E2D_LOG_INFO("Notifying {} materials of shader reload",
it->second.dependentMaterials.size()); it->second.dependentMaterials.size());
for (const auto& materialHandle : it->second.dependentMaterials) { for (const auto &materialHandle : it->second.dependentMaterials) {
Material* material = materials_.get(materialHandle); Material *material = materials_.get(materialHandle);
if (material) { if (material) {
// 更新材质的着色器引用 // 更新材质的着色器引用
material->setShader(getPtr(shader)); material->setShader(getPtr(shader));
E2D_LOG_DEBUG("Material {} updated with reloaded shader", E2D_LOG_DEBUG("Material {} updated with reloaded shader",
materialHandle.index()); materialHandle.index());
} }
}
} }
}
} }
//=========================================================================== //===========================================================================
@ -849,14 +940,14 @@ void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
//=========================================================================== //===========================================================================
AssetsModule::Stats AssetsModule::getStats() const { AssetsModule::Stats AssetsModule::getStats() const {
std::shared_lock<std::shared_mutex> lock(mutex_); std::shared_lock<std::shared_mutex> lock(mutex_);
Stats stats; Stats stats;
stats.textureCount = textures_.count(); stats.textureCount = textures_.count();
stats.shaderCount = shaders_.count(); stats.shaderCount = shaders_.count();
stats.materialCount = materials_.count(); stats.materialCount = materials_.count();
stats.meshCount = meshes_.count(); stats.meshCount = meshes_.count();
return stats; return stats;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,8 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <vector>
#include <renderer/command_queue.h> #include <renderer/command_queue.h>
#include <renderer/instance_buffer.h>
#include <renderer/material.h> #include <renderer/material.h>
#include <renderer/mesh.h> #include <renderer/mesh.h>
#include <renderer/rhi_module.h> #include <renderer/rhi_module.h>
@ -109,13 +111,18 @@ CommandQueue::~CommandQueue() { shutdown(); }
CommandQueue::CommandQueue(CommandQueue &&other) noexcept CommandQueue::CommandQueue(CommandQueue &&other) noexcept
: sorter_(std::move(other.sorter_)), batcher_(std::move(other.batcher_)), : sorter_(std::move(other.sorter_)), batcher_(std::move(other.batcher_)),
context_(other.context_), commandList_(std::move(other.commandList_)), context_(other.context_), commandList_(std::move(other.commandList_)),
uboManager_(std::move(other.uboManager_)),
globalUBOData_(other.globalUBOData_), globalUBOData_(other.globalUBOData_),
materialUBOData_(std::move(other.materialUBOData_)), materialUBOData_(std::move(other.materialUBOData_)),
nextMaterialId_(other.nextMaterialId_), nextMaterialId_(other.nextMaterialId_),
materialIds_(std::move(other.materialIds_)) { materialIds_(std::move(other.materialIds_)),
currentMaterialUBO_(other.currentMaterialUBO_),
currentMaterialUBOOffset_(other.currentMaterialUBOOffset_) {
other.context_ = nullptr; other.context_ = nullptr;
other.globalUBOData_ = {}; other.globalUBOData_ = {};
other.nextMaterialId_ = 1; other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.currentMaterialUBOOffset_ = 0;
} }
CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept { CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept {
@ -126,14 +133,19 @@ CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept {
batcher_ = std::move(other.batcher_); batcher_ = std::move(other.batcher_);
context_ = other.context_; context_ = other.context_;
commandList_ = std::move(other.commandList_); commandList_ = std::move(other.commandList_);
uboManager_ = std::move(other.uboManager_);
globalUBOData_ = other.globalUBOData_; globalUBOData_ = other.globalUBOData_;
materialUBOData_ = std::move(other.materialUBOData_); materialUBOData_ = std::move(other.materialUBOData_);
nextMaterialId_ = other.nextMaterialId_; nextMaterialId_ = other.nextMaterialId_;
materialIds_ = std::move(other.materialIds_); materialIds_ = std::move(other.materialIds_);
currentMaterialUBO_ = other.currentMaterialUBO_;
currentMaterialUBOOffset_ = other.currentMaterialUBOOffset_;
other.context_ = nullptr; other.context_ = nullptr;
other.globalUBOData_ = {}; other.globalUBOData_ = {};
other.nextMaterialId_ = 1; other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.currentMaterialUBOOffset_ = 0;
} }
return *this; return *this;
} }
@ -165,6 +177,13 @@ bool CommandQueue::initialize() {
return false; return false;
} }
// 初始化 UBO 管理器
uboManager_ = std::make_unique<UniformBufferManager>();
if (!uboManager_->initialize()) {
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
return false;
}
// 预分配材质 UBO 数据缓冲区 // 预分配材质 UBO 数据缓冲区
materialUBOData_.reserve(1024 * 1024); // 1MB materialUBOData_.reserve(1024 * 1024); // 1MB
@ -173,6 +192,7 @@ bool CommandQueue::initialize() {
} }
void CommandQueue::shutdown() { void CommandQueue::shutdown() {
uboManager_.reset();
commandList_.reset(); commandList_.reset();
context_ = nullptr; context_ = nullptr;
materialUBOData_.clear(); materialUBOData_.clear();
@ -186,6 +206,15 @@ void CommandQueue::beginFrame() {
nextMaterialId_ = 1; nextMaterialId_ = 1;
materialUBOData_.clear(); materialUBOData_.clear();
// 重置材质 UBO 分配状态
currentMaterialUBO_ = nullptr;
currentMaterialUBOOffset_ = 0;
// 重置 UBO 管理器的材质 UBO 池
if (uboManager_) {
uboManager_->resetMaterialUBOs();
}
// 开始录制命令 // 开始录制命令
if (commandList_) { if (commandList_) {
commandList_->begin(); commandList_->begin();
@ -210,6 +239,31 @@ uint32_t CommandQueue::getMaterialId(Material *material) {
return id; return id;
} }
std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t size) {
if (!uboManager_ || size == 0) {
return {nullptr, 0};
}
// 如果当前 UBO 没有足够的空间,获取一个新的
if (currentMaterialUBO_ == nullptr ||
currentMaterialUBOOffset_ + size > currentMaterialUBO_->getSize()) {
currentMaterialUBO_ = uboManager_->acquireMaterialUBO(size);
currentMaterialUBOOffset_ = 0;
}
if (!currentMaterialUBO_) {
return {nullptr, 0};
}
uint32_t offset = currentMaterialUBOOffset_;
currentMaterialUBOOffset_ += size;
// 对齐到 16 字节std140 要求)
currentMaterialUBOOffset_ = (currentMaterialUBOOffset_ + 15) & ~15;
return {currentMaterialUBO_, offset};
}
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh, void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
const struct Transform &transform, const struct Transform &transform,
const Color &color) { const Color &color) {
@ -248,22 +302,36 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
} }
} }
// 分配材质 UBO 数据 // 分配材质 UBO 空间并更新数据
uint32_t materialDataSize = material->getDataSize(); uint32_t materialDataSize = material->getDataSize();
if (materialDataSize > 0) { if (materialDataSize > 0) {
uint32_t uboOffset = static_cast<uint32_t>(materialUBOData_.size()); auto [ubo, offset] = allocateMaterialUBO(materialDataSize);
materialUBOData_.resize(uboOffset + materialDataSize); if (ubo) {
std::memcpy(materialUBOData_.data() + uboOffset, material->getData(), // 复制材质数据到临时缓冲区,以便修改颜色
materialDataSize); std::vector<uint8_t> uboData(materialDataSize);
cmd.materialUBOSize = materialDataSize; std::memcpy(uboData.data(), material->getData(), materialDataSize);
// 注意:实际的 UBO 句柄需要在执行时从 UBO 管理器获取
// 将实例颜色应用到 UBO 数据中的 uColor 参数
auto layout = material->getLayout();
if (layout) {
const auto* param = layout->getParam("uColor");
if (param && param->type == MaterialParamType::Color) {
std::memcpy(uboData.data() + param->offset, &color.r, sizeof(float) * 4);
}
}
ubo->update(uboData.data(), materialDataSize, offset);
cmd.materialUBO = BufferHandle(ubo->getRHIBuffer());
cmd.materialUBOSize = materialDataSize;
cmd.materialUBOOffset = offset;
}
} }
sorter_.addCommand(cmd); sorter_.addCommand(cmd);
} }
void CommandQueue::submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh, void CommandQueue::submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh,
uint32_t instanceCount) { BufferHandle instanceBuffer, uint32_t instanceCount) {
if (!material || !mesh || !material->getShader() || instanceCount == 0) { if (!material || !mesh || !material->getShader() || instanceCount == 0) {
return; return;
} }
@ -284,6 +352,10 @@ void CommandQueue::submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh,
cmd.indexCount = mesh->getIndexCount(); cmd.indexCount = mesh->getIndexCount();
cmd.instanceCount = instanceCount; cmd.instanceCount = instanceCount;
// 设置实例缓冲区
cmd.instanceBuffer = instanceBuffer;
cmd.instanceBufferStride = sizeof(InstanceData); // 64 bytes
// 设置纹理 // 设置纹理
const auto &textures = material->getTextures(); const auto &textures = material->getTextures();
cmd.textureCount = cmd.textureCount =
@ -294,14 +366,16 @@ void CommandQueue::submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh,
} }
} }
// 材质 UBO // 分配材质 UBO 空间并更新数据
uint32_t materialDataSize = material->getDataSize(); uint32_t materialDataSize = material->getDataSize();
if (materialDataSize > 0) { if (materialDataSize > 0) {
uint32_t uboOffset = static_cast<uint32_t>(materialUBOData_.size()); auto [ubo, offset] = allocateMaterialUBO(materialDataSize);
materialUBOData_.resize(uboOffset + materialDataSize); if (ubo) {
std::memcpy(materialUBOData_.data() + uboOffset, material->getData(), ubo->update(material->getData(), materialDataSize, offset);
materialDataSize); cmd.materialUBO = BufferHandle(ubo->getRHIBuffer());
cmd.materialUBOSize = materialDataSize; cmd.materialUBOSize = materialDataSize;
cmd.materialUBOOffset = offset;
}
} }
sorter_.addCommand(cmd); sorter_.addCommand(cmd);
@ -333,6 +407,28 @@ void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
commandList_->setViewport(viewport); commandList_->setViewport(viewport);
} }
void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight) {
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_));
}
void CommandQueue::execute() { void CommandQueue::execute() {
if (!commandList_) { if (!commandList_) {
E2D_LOG_ERROR("CommandQueue::execute: commandList is null"); E2D_LOG_ERROR("CommandQueue::execute: commandList is null");
@ -366,6 +462,14 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch)
E2D_LOG_WARN("Batch has no valid pipeline!"); E2D_LOG_WARN("Batch has no valid pipeline!");
} }
// 绑定全局 UBO (binding = 0)
if (uboManager_) {
UniformBuffer* globalUBO = uboManager_->getGlobalUBO();
if (globalUBO) {
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
}
}
// 绑定纹理 // 绑定纹理
if (batch.textureCount > 0) { if (batch.textureCount > 0) {
for (uint32_t i = 0; i < batch.textureCount; ++i) { for (uint32_t i = 0; i < batch.textureCount; ++i) {
@ -394,25 +498,24 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch)
E2D_LOG_WARN("Draw command has no valid vertex buffer!"); E2D_LOG_WARN("Draw command has no valid vertex buffer!");
} }
// 绑定实例缓冲区(实例化渲染)
if (cmd.isInstanced() && cmd.instanceBuffer.isValid()) {
commandList_->setVertexBuffer(1, cmd.instanceBuffer.get(), 0, cmd.instanceBufferStride);
}
// 绑定索引缓冲区(如果有) // 绑定索引缓冲区(如果有)
if (cmd.isIndexed() && cmd.indexBuffer.isValid()) { if (cmd.isIndexed() && cmd.indexBuffer.isValid()) {
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0); commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
} }
// 设置 shader uniform 变量 // 绑定材质 UBO (binding = 1)
auto* glCmdList = dynamic_cast<extra2d::GLCommandList*>(commandList_.get()); if (cmd.materialUBO.isValid()) {
if (glCmdList) { commandList_->setUniformBuffer(1, cmd.materialUBO.get(), cmd.materialUBOOffset, cmd.materialUBOSize);
// 设置模型矩阵 }
glCmdList->setUniform("uModelMatrix", cmd.modelMatrix);
// 设置颜色 // 设置模型矩阵(仅在非实例化渲染时使用)
glCmdList->setUniform("uColor", cmd.color); if (!cmd.isInstanced()) {
// 设置 view projection 矩阵 commandList_->setUniform("uModelMatrix", cmd.modelMatrix);
Mat4 viewProj = glm::ortho(0.0f, 1280.0f, 0.0f, 720.0f, -1.0f, 1.0f);
glCmdList->setUniform("uViewProjection", viewProj);
// 设置色调颜色
glCmdList->setUniform("uTintColor", Color::White);
// 设置透明度
glCmdList->setUniform("uOpacity", 1.0f);
} }
// 绘制 // 绘制

View File

@ -0,0 +1,216 @@
#include <renderer/instance_buffer.h>
#include <renderer/rhi_module.h>
#include <renderer/rhi/rhi_device.h>
#include <utils/logger.h>
#include <cstring>
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_)) {
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_);
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);
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();
}
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_;
}
RHIBuffer* rhiBuffer = bufferHandle_.get();
if (rhiBuffer) {
rhiBuffer->update(instances, count * sizeof(InstanceData), 0);
}
instanceCount_ = 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;
}
// 更新 GPU 缓冲区
RHIBuffer* rhiBuffer = bufferHandle_.get();
if (rhiBuffer) {
rhiBuffer->update(&instance, sizeof(InstanceData), index * sizeof(InstanceData));
}
return index;
}
void InstanceBuffer::clear() {
instanceCount_ = 0;
cpuBuffer_.clear();
}
// ========================================
// InstanceBufferManager 实现
// ========================================
InstanceBufferManager::InstanceBufferManager() = default;
InstanceBufferManager::~InstanceBufferManager() {
shutdown();
}
InstanceBufferManager::InstanceBufferManager(InstanceBufferManager&& other) noexcept
: bufferPool_(std::move(other.bufferPool_))
, currentBufferIndex_(other.currentBufferIndex_) {
other.currentBufferIndex_ = 0;
}
InstanceBufferManager& InstanceBufferManager::operator=(InstanceBufferManager&& other) noexcept {
if (this != &other) {
shutdown();
bufferPool_ = std::move(other.bufferPool_);
currentBufferIndex_ = other.currentBufferIndex_;
other.currentBufferIndex_ = 0;
}
return *this;
}
bool InstanceBufferManager::initialize() {
// 预分配一些缓冲区
bufferPool_.reserve(4);
E2D_LOG_INFO("InstanceBufferManager initialized");
return true;
}
void InstanceBufferManager::shutdown() {
bufferPool_.clear();
currentBufferIndex_ = 0;
}
InstanceBuffer* InstanceBufferManager::acquireBuffer(uint32_t minSize) {
// 查找现有缓冲区
for (uint32_t i = currentBufferIndex_; i < bufferPool_.size(); ++i) {
if (bufferPool_[i]->getMaxInstances() >= minSize) {
currentBufferIndex_ = i + 1;
bufferPool_[i]->clear();
return bufferPool_[i].get();
}
}
// 创建新缓冲区
auto buffer = std::make_unique<InstanceBuffer>();
uint32_t allocSize = std::max(minSize, DEFAULT_BUFFER_SIZE);
if (!buffer->initialize(allocSize)) {
E2D_LOG_ERROR("InstanceBufferManager::acquireBuffer: Failed to create buffer");
return nullptr;
}
InstanceBuffer* ptr = buffer.get();
bufferPool_.push_back(std::move(buffer));
currentBufferIndex_ = bufferPool_.size();
return ptr;
}
void InstanceBufferManager::releaseBuffer(InstanceBuffer* buffer) {
// 简单实现:不清除缓冲区,只是重置索引
// 实际实现可能需要更复杂的池管理
(void)buffer;
}
void InstanceBufferManager::reset() {
currentBufferIndex_ = 0;
for (auto& buffer : bufferPool_) {
buffer->clear();
}
}
} // namespace extra2d

View File

@ -21,12 +21,20 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
return; 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 时计算
params_[name] = info; size_t index = params_.size();
params_.emplace_back(name, info);
paramIndexMap_[name] = index;
} }
/** /**
@ -36,6 +44,7 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
* - * -
* - 16 * - 16
* - vec3 16 * - vec3 16
* - 16
*/ */
void MaterialLayout::finalize() { void MaterialLayout::finalize() {
if (finalized_) return; if (finalized_) return;
@ -44,7 +53,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) {
@ -60,21 +69,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 字节对齐 alignment = 16; // mat4: 16 字节对齐每列vec4对齐到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 字节 // 最终大小对齐到 16 字节std140要求结构体总大小是16的倍数
bufferSize_ = (offset + 15) & ~15; bufferSize_ = (offset + 15) & ~15;
finalized_ = true; finalized_ = true;
} }
@ -85,9 +94,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 it = params_.find(name); auto mapIt = paramIndexMap_.find(name);
if (it != params_.end()) { if (mapIt != paramIndexMap_.end()) {
return &it->second; return &params_[mapIt->second].second;
} }
return nullptr; return nullptr;
} }

View File

@ -179,7 +179,7 @@ bool RenderGraph::compile() {
return true; return true;
} }
void RenderGraph::execute(float deltaTime) { void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection) {
if (!compiled_) { if (!compiled_) {
if (!compile()) { if (!compile()) {
E2D_LOG_ERROR("Failed to compile RenderGraph"); E2D_LOG_ERROR("Failed to compile RenderGraph");
@ -187,8 +187,9 @@ void RenderGraph::execute(float deltaTime) {
} }
} }
// 注意beginFrame 现在由 RendererModule::onRenderBegin 调用 // 更新全局 UBO
// 这里不再调用,以避免清空已经提交的渲染命令 commandQueue_.updateGlobalUBO(viewProjection, deltaTime, outputWidth_,
outputHeight_);
// 获取 RHI 上下文 // 获取 RHI 上下文
auto *rhiModule = RHIModule::get(); auto *rhiModule = RHIModule::get();
@ -212,8 +213,6 @@ void RenderGraph::execute(float deltaTime) {
} }
} }
// 执行命令队列
// 执行命令队列 // 执行命令队列
commandQueue_.execute(); commandQueue_.execute();

View File

@ -1,5 +1,6 @@
#include <assets/assets_module.h> #include <assets/assets_module.h>
#include <platform/window_module.h> #include <platform/window_module.h>
#include <renderer/instance_buffer.h>
#include <renderer/render_graph.h> #include <renderer/render_graph.h>
#include <renderer/renderer_module.h> #include <renderer/renderer_module.h>
#include <renderer/rhi_module.h> #include <renderer/rhi_module.h>
@ -208,11 +209,14 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
Material *material = assets->get(cmd.drawInstanced.material); Material *material = assets->get(cmd.drawInstanced.material);
Mesh *mesh = assets->get(cmd.drawInstanced.mesh); Mesh *mesh = assets->get(cmd.drawInstanced.mesh);
InstanceBuffer *instanceBuffer =
static_cast<InstanceBuffer *>(cmd.drawInstanced.instanceBuffer);
if (material && mesh) { if (material && mesh && instanceBuffer && instanceBuffer->isValid()) {
commandQueue_->submitDrawInstanced(Ptr<Material>(material), commandQueue_->submitDrawInstanced(
Ptr<Mesh>(mesh), Ptr<Material>(material), Ptr<Mesh>(mesh),
cmd.drawInstanced.instanceCount); BufferHandle(instanceBuffer->getRHIBuffer()),
cmd.drawInstanced.instanceCount);
stats_.commandsSubmitted++; stats_.commandsSubmitted++;
} }
break; break;
@ -248,11 +252,8 @@ void RendererModule::onRenderEnd() {
return; return;
} }
// 执行渲染图 // 执行渲染图,传递视图投影矩阵
renderGraph_.execute(0.016f, viewProjectionMatrix_);
// 执行渲染图
// 这里会执行所有渲染通道并提交命令
renderGraph_.execute(0.016f); // TODO: 使用实际的 deltaTime
// 获取统计信息 // 获取统计信息
if (commandQueue_) { if (commandQueue_) {

View File

@ -85,43 +85,52 @@ void GLCommandList::setPipeline(RHIPipeline *pipeline) {
} }
void GLCommandList::setVertexBuffer(uint32_t slot, RHIBuffer *buffer, void GLCommandList::setVertexBuffer(uint32_t slot, RHIBuffer *buffer,
uint32_t offset) { uint32_t offset, uint32_t stride) {
if (buffer) { if (!buffer || !currentPipeline_) {
auto *glBuffer = static_cast<GLBuffer *>(buffer); return;
glBindBuffer(GL_ARRAY_BUFFER, glBuffer->getGLBuffer()); }
currentVertexBuffer_ = glBuffer;
// 如果有当前管线,配置顶点属性 auto *glBuffer = static_cast<GLBuffer *>(buffer);
if (currentPipeline_) { glBindBuffer(GL_ARRAY_BUFFER, glBuffer->getGLBuffer());
const auto &layout = currentPipeline_->getVertexLayout();
for (const auto &attr : layout.attributes) {
glEnableVertexAttribArray(attr.location);
GLenum type = GL_FLOAT; // 只配置属于当前 slot 的属性
GLint size = 1; const auto &layout = currentPipeline_->getVertexLayout();
GLboolean normalized = GL_FALSE; // 使用传入的步长如果为0则使用布局中的步长
uint32_t actualStride = stride > 0 ? stride : layout.stride;
switch (attr.format) { for (const auto &attr : layout.attributes) {
case VertexFormat::Float1: if (attr.bufferIndex != slot) {
size = 1; continue;
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));
}
} }
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);
} }
} }
@ -141,6 +150,20 @@ void GLCommandList::setUniformBuffer(uint32_t slot, RHIBuffer *buffer) {
} }
} }
void GLCommandList::setUniformBuffer(uint32_t slot, RHIBuffer *buffer, uint32_t offset, uint32_t size) {
if (buffer) {
auto *glBuffer = static_cast<GLBuffer *>(buffer);
GLuint glBufferId = glBuffer->getGLBuffer();
if (size == 0) {
// 绑定整个缓冲区
glBindBufferBase(GL_UNIFORM_BUFFER, slot, glBufferId);
} else {
// 绑定缓冲区范围
glBindBufferRange(GL_UNIFORM_BUFFER, slot, glBufferId, offset, size);
}
}
}
void GLCommandList::setTexture(uint32_t slot, RHITexture *texture) { void GLCommandList::setTexture(uint32_t slot, RHITexture *texture) {
if (texture) { if (texture) {
auto *glTexture = static_cast<GLTexture *>(texture); auto *glTexture = static_cast<GLTexture *>(texture);
@ -213,45 +236,45 @@ bool GLCommandList::isRecording() const {
return recording_; return recording_;
} }
void GLCommandList::setUniform(const std::string& name, float value) { void GLCommandList::setUniform(const char* name, float value) {
if (currentShaderProgram_ != 0) { if (currentShaderProgram_ != 0) {
GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); GLint location = glGetUniformLocation(currentShaderProgram_, name);
if (location != -1) { if (location != -1) {
glUniform1f(location, value); glUniform1f(location, value);
} }
} }
} }
void GLCommandList::setUniform(const std::string& name, const Vec2& value) { void GLCommandList::setUniform(const char* name, const Vec2& value) {
if (currentShaderProgram_ != 0) { if (currentShaderProgram_ != 0) {
GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); GLint location = glGetUniformLocation(currentShaderProgram_, name);
if (location != -1) { if (location != -1) {
glUniform2f(location, value.x, value.y); glUniform2f(location, value.x, value.y);
} }
} }
} }
void GLCommandList::setUniform(const std::string& name, const Vec3& value) { void GLCommandList::setUniform(const char* name, const Vec3& value) {
if (currentShaderProgram_ != 0) { if (currentShaderProgram_ != 0) {
GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); GLint location = glGetUniformLocation(currentShaderProgram_, name);
if (location != -1) { if (location != -1) {
glUniform3f(location, value.x, value.y, value.z); glUniform3f(location, value.x, value.y, value.z);
} }
} }
} }
void GLCommandList::setUniform(const std::string& name, const Color& value) { void GLCommandList::setUniform(const char* name, const Color& value) {
if (currentShaderProgram_ != 0) { if (currentShaderProgram_ != 0) {
GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); GLint location = glGetUniformLocation(currentShaderProgram_, name);
if (location != -1) { if (location != -1) {
glUniform4f(location, value.r, value.g, value.b, value.a); glUniform4f(location, value.r, value.g, value.b, value.a);
} }
} }
} }
void GLCommandList::setUniform(const std::string& name, const Mat4& value) { void GLCommandList::setUniform(const char* name, const Mat4& value) {
if (currentShaderProgram_ != 0) { if (currentShaderProgram_ != 0) {
GLint location = glGetUniformLocation(currentShaderProgram_, name.c_str()); GLint location = glGetUniformLocation(currentShaderProgram_, name);
if (location != -1) { if (location != -1) {
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value)); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
} }

View File

@ -42,6 +42,80 @@ bool Shader::loadFromFile(const std::string &vsPath,
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;
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout);
}
bool Shader::loadInstancedFromFile(const std::string &vsPath,
const std::string &fsPath) {
// 读取顶点着色器
std::ifstream vsFile(vsPath);
if (!vsFile.is_open()) {
E2D_LOG_ERROR("Failed to open instanced 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 instanced fragment shader: {}", fsPath);
return false;
}
std::stringstream fsStream;
fsStream << fsFile.rdbuf();
std::string fsSource = fsStream.str();
return loadInstancedFromSource(vsSource, fsSource);
}
bool Shader::loadInstancedFromSource(const std::string &vsSource,
const std::string &fsSource) {
// 创建实例化渲染顶点布局
// 槽位0顶点属性位置、UV、颜色
// 槽位1实例属性位置、旋转、缩放、颜色、UV区域
VertexLayout vertexLayout;
// 顶点属性(每个顶点)- 槽位0
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // UV
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
// 实例属性(每个实例)- 槽位1
// InstanceData 布局:
// Vec2 position; // offset 0
// float rotation; // offset 8
// float padding0; // offset 12
// Vec2 scale; // offset 16
// float padding1[2]; // offset 24
// Color color; // offset 32
// float uvX; // offset 48
// float uvY; // offset 52
// float uvWidth; // offset 56
// float uvHeight; // offset 60
vertexLayout.addAttribute(VertexAttribute::perInstance(3, VertexFormat::Float2, 0, 1)); // iPosition
vertexLayout.addAttribute(VertexAttribute::perInstance(4, VertexFormat::Float1, 8, 1)); // iRotation
vertexLayout.addAttribute(VertexAttribute::perInstance(5, VertexFormat::Float2, 16, 1)); // iScale
vertexLayout.addAttribute(VertexAttribute::perInstance(6, VertexFormat::Float4, 32, 1)); // iColor
vertexLayout.addAttribute(VertexAttribute::perInstance(7, VertexFormat::Float4, 48, 1)); // iUVRect
// 注意stride 在实例化渲染中由 buffer 的 stride 参数控制
vertexLayout.stride = sizeof(float) * 8; // 顶点缓冲区步长
return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout);
}
bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout) {
// 释放旧资源 // 释放旧资源
pipeline_ = PipelineHandle(); pipeline_ = PipelineHandle();
handle_ = ShaderHandle(); handle_ = ShaderHandle();
@ -78,13 +152,6 @@ bool Shader::loadFromSource(const std::string &vsSource,
// 获取着色器句柄 // 获取着色器句柄
handle_ = ShaderHandle(shader.release()); handle_ = ShaderHandle(shader.release());
// 创建顶点布局2D 标准布局:位置 + 纹理坐标 + 颜色)
VertexLayout vertexLayout;
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
// 创建管线描述 // 创建管线描述
PipelineDesc pipelineDesc; PipelineDesc pipelineDesc;
pipelineDesc.vertexShader = handle_; pipelineDesc.vertexShader = handle_;

View File

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

View File

@ -130,8 +130,9 @@ 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();