From e0b0a7883d9cdab62c012fea6b54afabec1fa28b Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 3 Mar 2026 12:18:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor(renderer):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E5=8C=96=E6=B8=B2=E6=9F=93=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=8F=8A=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除不再需要的实例化渲染功能,包括着色器、材质、渲染命令和测试代码 优化实例缓冲区实现,添加脏标记和增量更新功能 --- examples/scene_graph_demo/game_scene.cpp | 19 +-- examples/scene_graph_demo/game_scene.h | 7 - examples/scene_graph_demo/instanced_test.cpp | 139 --------------- examples/scene_graph_demo/instanced_test.h | 58 ------- .../romfs/shader/sprite_instanced.frag | 35 ---- .../romfs/shader/sprite_instanced.vert | 58 ------- examples/scene_graph_demo/xmake.lua | 2 +- include/assets/assets_module.h | 29 ---- include/renderer/command_queue.h | 22 +-- include/renderer/instance_buffer.h | 139 +++++++++++++-- include/renderer/render_types.h | 21 +-- include/renderer/shader.h | 8 - shader/sprite_instanced.vert | 58 ------- src/assets/assets_module.cpp | 83 --------- src/renderer/command_queue.cpp | 94 ++-------- src/renderer/instance_buffer.cpp | 160 +++++++++++++++++- src/renderer/renderer_module.cpp | 21 --- src/renderer/shader.cpp | 41 ----- 18 files changed, 295 insertions(+), 699 deletions(-) delete mode 100644 examples/scene_graph_demo/instanced_test.cpp delete mode 100644 examples/scene_graph_demo/instanced_test.h delete mode 100644 examples/scene_graph_demo/romfs/shader/sprite_instanced.frag delete mode 100644 examples/scene_graph_demo/romfs/shader/sprite_instanced.vert delete mode 100644 shader/sprite_instanced.vert diff --git a/examples/scene_graph_demo/game_scene.cpp b/examples/scene_graph_demo/game_scene.cpp index 632a71f..61419ac 100644 --- a/examples/scene_graph_demo/game_scene.cpp +++ b/examples/scene_graph_demo/game_scene.cpp @@ -1,5 +1,4 @@ #include "game_scene.h" -#include "instanced_test.h" #include // ======================================== @@ -80,9 +79,6 @@ void GameScene::onEnter() { // 创建装饰物 createDecorations(); - - // 创建实例化渲染测试(1000个实例) - createInstancedTest(); } void GameScene::onExit() { @@ -93,6 +89,7 @@ void GameScene::onExit() { Scene::onExit(); } + void GameScene::update(float dt) { Scene::update(dt); @@ -107,11 +104,6 @@ void GameScene::update(float dt) { for (auto &decoration : decorations_) { decoration->onUpdate(dt); } - - // 更新实例化测试 - if (instancedTest_) { - instancedTest_->update(dt); - } } void GameScene::createPlayer() { @@ -174,12 +166,3 @@ void GameScene::createCamera() { // 添加相机节点到场景 addChild(cameraNode); } - -void GameScene::createInstancedTest() { - // 创建实例化渲染测试节点 - auto instancedTest = makePtr(); - if (instancedTest->initialize(1000)) { - instancedTest_ = instancedTest; - addChild(instancedTest); - } -} diff --git a/examples/scene_graph_demo/game_scene.h b/examples/scene_graph_demo/game_scene.h index 6deaa7e..bd8e5d2 100644 --- a/examples/scene_graph_demo/game_scene.h +++ b/examples/scene_graph_demo/game_scene.h @@ -1,7 +1,6 @@ #pragma once #include -#include "instanced_test.h" using namespace extra2d; @@ -135,13 +134,7 @@ private: */ void createCamera(); - /** - * @brief 创建实例化渲染测试 - */ - void createInstancedTest(); - Ptr player_; std::vector> decorations_; - Ptr instancedTest_; float sceneTime_ = 0.0f; }; diff --git a/examples/scene_graph_demo/instanced_test.cpp b/examples/scene_graph_demo/instanced_test.cpp deleted file mode 100644 index 6f02297..0000000 --- a/examples/scene_graph_demo/instanced_test.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "instanced_test.h" -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -InstancedTestNode::InstancedTestNode() { - setName("InstancedTest"); -} - -InstancedTestNode::~InstancedTestNode() { - instanceBuffer_.shutdown(); -} - -bool InstancedTestNode::initialize(uint32_t instanceCount) { - instanceCount_ = instanceCount; - - // 获取资源模块 - auto* assets = getModule(); - 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(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 diff --git a/examples/scene_graph_demo/instanced_test.h b/examples/scene_graph_demo/instanced_test.h deleted file mode 100644 index 4df7473..0000000 --- a/examples/scene_graph_demo/instanced_test.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -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_; // CPU端实例数据 - uint32_t instanceCount_ = 0; // 实例数量 - float time_ = 0.0f; // 时间累积 - - // 资源句柄 - Handle material_; - Handle mesh_; - Handle texture_; - - /** - * @brief 更新实例变换 - */ - void updateInstances(); -}; - -} // namespace extra2d diff --git a/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag b/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag deleted file mode 100644 index aad250d..0000000 --- a/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag +++ /dev/null @@ -1,35 +0,0 @@ -#version 320 es -precision highp float; - -// 从顶点着色器输入 -in vec2 vTexCoord; -in vec4 vColor; - -// 纹理采样器 -uniform sampler2D uTexture; - -// 输出颜色 -out vec4 fragColor; - -/** - * @brief 片段着色器入口(实例化版本) - * - * 采样纹理并与顶点颜色混合 - */ -void main() { - // 采样纹理 - vec4 texColor = texture(uTexture, vTexCoord); - - // 如果纹理采样结果是黑色或透明,使用白色作为默认值 - if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { - texColor = vec4(1.0, 1.0, 1.0, 1.0); - } - - // 混合:纹理 * 顶点颜色 - fragColor = texColor * vColor; - - // Alpha 测试:丢弃几乎透明的像素 - if (fragColor.a < 0.01) { - discard; - } -} diff --git a/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert b/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert deleted file mode 100644 index 0675904..0000000 --- a/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert +++ /dev/null @@ -1,58 +0,0 @@ -#version 320 es -precision highp float; - -// 全局 UBO (binding = 0) - 每帧更新一次 -layout(std140, binding = 0) uniform GlobalUBO { - mat4 uViewProjection; - vec4 uCameraPosition; - float uTime; - float uDeltaTime; - vec2 uScreenSize; -}; - -// 顶点属性 -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; -} diff --git a/examples/scene_graph_demo/xmake.lua b/examples/scene_graph_demo/xmake.lua index c076f92..3da5a90 100644 --- a/examples/scene_graph_demo/xmake.lua +++ b/examples/scene_graph_demo/xmake.lua @@ -10,7 +10,7 @@ local example_dir = os.scriptdir() -- 可执行文件目标 target("scene_graph_demo") set_kind("binary") - add_files("main.cpp", "game_scene.cpp", "instanced_test.cpp") + add_files("main.cpp", "game_scene.cpp") add_includedirs("../../include", ".") add_deps("extra2d") diff --git a/include/assets/assets_module.h b/include/assets/assets_module.h index 1895ca4..9f43e26 100644 --- a/include/assets/assets_module.h +++ b/include/assets/assets_module.h @@ -176,31 +176,6 @@ public: Material *getDefaultMaterialPtr(); Mesh *getDefaultQuadPtr(); - //=========================================================================== - // 实例化渲染资源 - //=========================================================================== - - /** - * @brief 获取实例化渲染着色器 - * @return 实例化着色器句柄 - */ - Handle getInstancedShader(); - - /** - * @brief 获取实例化渲染材质 - * @return 实例化材质句柄 - */ - Handle getInstancedMaterial(); - - Shader *getInstancedShaderPtr(); - Material *getInstancedMaterialPtr(); - - /** - * @brief 创建实例化渲染资源 - * @return 是否成功 - */ - bool createInstancedResources(); - //=========================================================================== // 热重载 //=========================================================================== @@ -285,10 +260,6 @@ private: Handle defaultMaterial_; Handle defaultQuad_; - // 实例化渲染资源 - Handle instancedShader_; - Handle instancedMaterial_; - // 热重载 bool hotReloadEnabled_ = false; float hotReloadInterval_ = 1.0f; diff --git a/include/renderer/command_queue.h b/include/renderer/command_queue.h index 440a118..3ef6a61 100644 --- a/include/renderer/command_queue.h +++ b/include/renderer/command_queue.h @@ -65,29 +65,23 @@ struct DrawCommand { BufferHandle indexBuffer; // 索引缓冲区(可选) uint32_t vertexCount; // 顶点数量 uint32_t indexCount; // 索引数量 - uint32_t instanceCount; // 实例数量(1表示非实例化) std::array textures; // 纹理数组 uint32_t textureCount; // 纹理数量 BufferHandle materialUBO; // 材质 UBO uint32_t materialUBOSize; // 材质 UBO 大小 uint32_t materialUBOOffset; // 材质 UBO 在全局缓冲区中的偏移 - BufferHandle instanceBuffer; // 实例数据缓冲区(实例化渲染使用) - uint32_t instanceBufferStride; // 实例数据步长 - + // 变换和颜色数据(用于设置 shader uniform) Mat4 modelMatrix; // 模型矩阵 Color color; // 颜色 DrawCommand() - : vertexCount(0), indexCount(0), instanceCount(1), textureCount(0), - materialUBOSize(0), materialUBOOffset(0), instanceBufferStride(0), + : vertexCount(0), indexCount(0), textureCount(0), + materialUBOSize(0), materialUBOOffset(0), modelMatrix(glm::identity()), color(Color::White) {} // 检查是否使用索引绘制 bool isIndexed() const { return indexCount > 0; } - - // 检查是否实例化绘制 - bool isInstanced() const { return instanceCount > 1; } }; /** @@ -294,16 +288,6 @@ public: void submitDraw(Ptr material, Ptr mesh, const struct Transform &transform, const Color &color); - /** - * @brief 提交实例化绘制命令 - * @param material 材质 - * @param mesh 网格 - * @param instanceBuffer 实例数据缓冲区 - * @param instanceCount 实例数量 - */ - void submitDrawInstanced(Ptr material, Ptr mesh, - BufferHandle instanceBuffer, uint32_t instanceCount); - /** * @brief 提交清除命令 * @param color 清除颜色 diff --git a/include/renderer/instance_buffer.h b/include/renderer/instance_buffer.h index 6fce8f4..d062950 100644 --- a/include/renderer/instance_buffer.h +++ b/include/renderer/instance_buffer.h @@ -15,21 +15,25 @@ namespace extra2d { * * 单个实例的属性数据,用于实例化渲染 * 布局遵循 std140 对齐规则 + * + * 优化说明: + * - 旋转使用预计算的 cos/sin 值,避免在着色器中进行三角函数计算 + * - 属性布局优化,便于GPU访问 */ struct InstanceData { - // 第一组 16 字节 + // 第一组 16 字节 - 变换数据 Vec2 position; // 位置偏移 (8 bytes) - float rotation; // 旋转角度 (4 bytes) - float padding0; // 填充到 16 字节对齐 (4 bytes) + float rotationCos; // 旋转角度余弦值 (4 bytes) - 预计算避免着色器三角函数 + float rotationSin; // 旋转角度正弦值 (4 bytes) - 预计算避免着色器三角函数 - // 第二组 16 字节 + // 第二组 16 字节 - 缩放和预留 Vec2 scale; // 缩放 (8 bytes) float padding1[2]; // 填充到 16 字节对齐 (8 bytes) - // 第三组 16 字节 + // 第三组 16 字节 - 颜色 Color color; // 颜色 (16 bytes) - r, g, b, a - // 第四组 16 字节 + // 第四组 16 字节 - UV坐标 float uvX; // UV 起始 X (4 bytes) float uvY; // UV 起始 Y (4 bytes) float uvWidth; // UV 宽度 (4 bytes) @@ -37,8 +41,8 @@ struct InstanceData { InstanceData() : position(0.0f, 0.0f) - , rotation(0.0f) - , padding0(0.0f) + , rotationCos(1.0f) // cos(0) = 1 + , rotationSin(0.0f) // sin(0) = 0 , scale(1.0f, 1.0f) , padding1{0.0f, 0.0f} , color(Color::White) @@ -46,15 +50,65 @@ struct InstanceData { , uvY(0.0f) , uvWidth(1.0f) , uvHeight(1.0f) {} + + /** + * @brief 设置旋转角度(自动计算cos/sin) + * @param angle 旋转角度(弧度) + */ + void setRotation(float angle) { + rotationCos = std::cos(angle); + rotationSin = std::sin(angle); + } + + /** + * @brief 获取旋转角度(通过atan2计算,仅用于调试) + * @return 旋转角度(弧度) + */ + float getRotation() const { + return std::atan2(rotationSin, rotationCos); + } }; static_assert(sizeof(InstanceData) == 64, "InstanceData size should be 64 bytes for std140 alignment"); +/** + * @brief 脏区域标记 + * + * 标记需要更新的实例数据区域,支持部分更新GPU缓冲区 + */ +struct DirtyRange { + uint32_t start; // 起始实例索引 + uint32_t count; // 实例数量 + + DirtyRange(uint32_t s = 0, uint32_t c = 0) : start(s), count(c) {} + + bool isValid() const { return count > 0; } + uint32_t end() const { return start + count; } + + // 检查是否与另一个区域重叠或相邻 + bool canMerge(const DirtyRange& other) const { + return (start <= other.end() && other.start <= end()) || + (end() + 1 >= other.start && other.end() + 1 >= start); + } + + // 合并两个区域 + void merge(const DirtyRange& other) { + uint32_t newStart = std::min(start, other.start); + uint32_t newEnd = std::max(end(), other.end()); + start = newStart; + count = newEnd - newStart; + } +}; + /** * @brief 实例缓冲区 * * 管理实例化渲染的实例数据缓冲区 * 支持动态更新和双缓冲 + * + * 优化特性: + * - 脏标记系统:只更新变化的实例数据,减少PCIe带宽占用 + * - 批量更新:合并相邻的脏区域,减少GPU上传次数 */ class InstanceBuffer { public: @@ -89,13 +143,22 @@ public: void shutdown(); /** - * @brief 更新实例数据 + * @brief 更新实例数据(全量更新,标记整个缓冲区为脏) * @param instances 实例数据数组 * @param count 实例数量 * @return 更新是否成功 */ bool updateInstances(const InstanceData* instances, uint32_t count); + /** + * @brief 更新部分实例数据(增量更新) + * @param instances 实例数据数组 + * @param start 起始实例索引 + * @param count 实例数量 + * @return 更新是否成功 + */ + bool updateInstancesRange(const InstanceData* instances, uint32_t start, uint32_t count); + /** * @brief 添加单个实例 * @param instance 实例数据 @@ -103,6 +166,24 @@ public: */ uint32_t addInstance(const InstanceData& instance); + /** + * @brief 标记实例范围为脏(下次updateGPU时更新) + * @param start 起始实例索引 + * @param count 实例数量 + */ + void markDirty(uint32_t start, uint32_t count); + + /** + * @brief 标记整个缓冲区为脏 + */ + void markAllDirty(); + + /** + * @brief 将脏数据上传到GPU + * @return 上传是否成功 + */ + bool updateGPU(); + /** * @brief 清除所有实例 */ @@ -138,11 +219,43 @@ public: */ bool isValid() const { return bufferHandle_.isValid(); } + /** + * @brief 获取脏区域数量 + * @return 脏区域数量 + */ + uint32_t getDirtyRangeCount() const { return static_cast(dirtyRanges_.size()); } + + /** + * @brief 检查是否有脏数据 + * @return 是否有脏数据 + */ + bool hasDirtyData() const { return !dirtyRanges_.empty(); } + private: - BufferHandle bufferHandle_; // RHI 缓冲区句柄 - uint32_t maxInstances_ = 0; // 最大实例数量 - uint32_t instanceCount_ = 0; // 当前实例数量 - std::vector cpuBuffer_; // CPU 端缓冲区 + BufferHandle bufferHandle_; // RHI 缓冲区句柄 + uint32_t maxInstances_ = 0; // 最大实例数量 + uint32_t instanceCount_ = 0; // 当前实例数量 + std::vector cpuBuffer_; // CPU 端缓冲区 + std::vector dirtyRanges_; // 脏区域列表 + + // 最大脏区域数量,超过则合并为全量更新 + static constexpr uint32_t MAX_DIRTY_RANGES = 8; + + /** + * @brief 添加脏区域,自动合并重叠区域 + * @param range 脏区域 + */ + void addDirtyRange(const DirtyRange& range); + + /** + * @brief 合并所有脏区域为一个 + */ + void mergeAllDirtyRanges(); + + /** + * @brief 清空脏区域列表 + */ + void clearDirtyRanges(); }; /** diff --git a/include/renderer/render_types.h b/include/renderer/render_types.h index eb64c93..6063772 100644 --- a/include/renderer/render_types.h +++ b/include/renderer/render_types.h @@ -20,10 +20,9 @@ class Texture; * @brief 渲染命令类型 */ enum class RenderCommandType : uint8_t { - DrawMesh, // 绘制网格 - DrawMeshInstanced, // 实例化绘制 - SetViewport, // 设置视口 - Clear // 清除缓冲区 + DrawMesh, // 绘制网格 + SetViewport, // 设置视口 + Clear // 清除缓冲区 }; /** @@ -46,15 +45,6 @@ struct RenderCommand { Color color; // 顶点颜色 }; -// 实例化绘制命令数据 - struct DrawInstancedData { - Handle mesh; // 网格句柄 - Handle material; // 材质句柄 - void* instanceBuffer; // 实例数据缓冲区指针 (InstanceBuffer*) - uint32_t instanceCount; // 实例数量 - uint32_t instanceOffset; // 实例数据偏移 - }; - // 设置视口命令数据 struct ViewportData { int32_t x, y; // 视口位置 @@ -69,7 +59,6 @@ struct RenderCommand { union { DrawMeshData drawMesh; - DrawInstancedData drawInstanced; ViewportData viewport; ClearData clear; }; @@ -123,12 +112,8 @@ constexpr uint32_t CLEAR_ALL_FLAG = // 最大渲染命令数 constexpr uint32_t MAX_RENDER_COMMANDS = 10000; -// 最大实例数 -constexpr uint32_t MAX_INSTANCES = 256; - // UBO 绑定槽位(已移至 RHI) constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO -constexpr uint32_t INSTANCE_UBO_BINDING = 2; // 实例 UBO } // namespace extra2d diff --git a/include/renderer/shader.h b/include/renderer/shader.h index 82badac..a20f62a 100644 --- a/include/renderer/shader.h +++ b/include/renderer/shader.h @@ -35,14 +35,6 @@ public: */ bool loadFromSource(const std::string &vsSource, const std::string &fsSource); - /** - * @brief 从源码加载实例化着色器(支持实例属性) - * @param vsSource 顶点着色器源码 - * @param fsSource 片段着色器源码 - * @return 加载是否成功 - */ - bool loadInstancedFromSource(const std::string &vsSource, const std::string &fsSource); - /** * @brief 获取 RHI 着色器句柄 * @return RHI 着色器句柄 diff --git a/shader/sprite_instanced.vert b/shader/sprite_instanced.vert deleted file mode 100644 index 0675904..0000000 --- a/shader/sprite_instanced.vert +++ /dev/null @@ -1,58 +0,0 @@ -#version 320 es -precision highp float; - -// 全局 UBO (binding = 0) - 每帧更新一次 -layout(std140, binding = 0) uniform GlobalUBO { - mat4 uViewProjection; - vec4 uCameraPosition; - float uTime; - float uDeltaTime; - vec2 uScreenSize; -}; - -// 顶点属性 -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; -} diff --git a/src/assets/assets_module.cpp b/src/assets/assets_module.cpp index e2e0342..9b6db45 100644 --- a/src/assets/assets_module.cpp +++ b/src/assets/assets_module.cpp @@ -101,13 +101,6 @@ void AssetsModule::onGLContextReady() { return; } - // 创建实例化渲染资源 - if (!createInstancedResources()) { - E2D_LOG_WARN("Failed to create instanced resources, instanced rendering " - "will not be available"); - // 不返回错误,实例化渲染是可选功能 - } - defaultResourcesCreated_ = true; E2D_LOG_INFO("Default resources created successfully"); } @@ -558,82 +551,6 @@ Material *AssetsModule::getDefaultMaterialPtr() { Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); } -Handle AssetsModule::getInstancedShader() { return instancedShader_; } - -Handle AssetsModule::getInstancedMaterial() { - return instancedMaterial_; -} - -Shader *AssetsModule::getInstancedShaderPtr() { - return shaders_.get(instancedShader_); -} - -Material *AssetsModule::getInstancedMaterialPtr() { - return materials_.get(instancedMaterial_); -} - -bool AssetsModule::createInstancedResources() { - // 加载实例化着色器 - // 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS) - FileModule *fileModule = getFileModule(); - std::string vertPath = - fileModule ? fileModule->assetPath("shader/sprite_instanced.vert") - : "shader/sprite_instanced.vert"; - std::string fragPath = - fileModule ? fileModule->assetPath("shader/sprite_instanced.frag") - : "shader/sprite_instanced.frag"; - - if (!fileExists(vertPath) || !fileExists(fragPath)) { - E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath, - fragPath); - return false; - } - - // 读取着色器文件内容 - std::string vsSource = readFileToString(vertPath); - std::string fsSource = readFileToString(fragPath); - if (vsSource.empty() || fsSource.empty()) { - E2D_LOG_ERROR("Failed to read instanced shader files: {}, {}", vertPath, - fragPath); - return false; - } - - // 从源码加载实例化着色器 - Ptr shader = makePtr(); - if (!shader->loadInstancedFromSource(vsSource, fsSource)) { - E2D_LOG_ERROR("Failed to compile instanced shader: {}, {}", vertPath, - fragPath); - return false; - } - - instancedShader_ = shaders_.insert(shader); - E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath, - fragPath); - - // 创建实例化材质布局 - Ptr layout = makePtr(); - layout->addParam("uColor", MaterialParamType::Color); - layout->addParam("uTintColor", MaterialParamType::Color); - layout->addParam("uOpacity", MaterialParamType::Float); - layout->finalize(); - - Ptr material = makePtr(); - 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; -} - //=========================================================================== // 热重载 //=========================================================================== diff --git a/src/renderer/command_queue.cpp b/src/renderer/command_queue.cpp index 4d3d7e7..bf487a1 100644 --- a/src/renderer/command_queue.cpp +++ b/src/renderer/command_queue.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -406,59 +405,6 @@ void CommandQueue::submitDraw(Ptr material, Ptr mesh, sorter_.addCommand(cmd); } -void CommandQueue::submitDrawInstanced(Ptr material, Ptr mesh, - BufferHandle instanceBuffer, uint32_t instanceCount) { - if (!material || !mesh || !material->getShader() || instanceCount == 0) { - return; - } - - DrawCommand cmd; - - // 构建排序键 - uint32_t materialId = getMaterialId(material.get()); - cmd.key = DrawKey::make(materialId, 0, 0); - - // 设置管线 - cmd.pipeline = material->getPipeline(); - - // 设置网格数据 - cmd.vertexBuffer = mesh->getVertexBuffer(); - cmd.indexBuffer = mesh->getIndexBuffer(); - cmd.vertexCount = mesh->getVertexCount(); - cmd.indexCount = mesh->getIndexCount(); - cmd.instanceCount = instanceCount; - - // 设置实例缓冲区 - cmd.instanceBuffer = instanceBuffer; - cmd.instanceBufferStride = sizeof(InstanceData); // 64 bytes - - // 设置纹理 - const auto &textures = material->getTextures(); - cmd.textureCount = - static_cast(std::min(textures.size(), size_t(8))); - for (uint32_t i = 0; i < cmd.textureCount; ++i) { - if (textures[i].texture) { - cmd.textures[i] = textures[i].texture->getHandle(); - } - } - - // 分配材质 UBO 空间(使用 CPU 缓冲区) - uint32_t materialDataSize = material->getDataSize(); - if (materialDataSize > 0) { - auto [ubo, offset] = allocateMaterialUBO(materialDataSize); - (void)ubo; // ubo 为 nullptr,使用 CPU 缓冲区 - - // 复制材质数据到 CPU 缓冲区 - if (offset + materialDataSize <= materialUBOData_.size()) { - std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize); - cmd.materialUBOOffset = offset; - cmd.materialUBOSize = materialDataSize; - } - } - - sorter_.addCommand(cmd); -} - void CommandQueue::submitClear(const Color &color, uint32_t flags) { // 清除命令直接执行,不加入排序队列 if (!commandList_) { @@ -575,12 +521,6 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch, 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); - stats_.bufferBinds++; - } - // 绑定索引缓冲区(如果有) if (cmd.isIndexed() && cmd.indexBuffer.isValid()) { commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0); @@ -593,34 +533,20 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch, stats_.bufferBinds++; } - // 设置模型矩阵(仅在非实例化渲染时使用) - if (!cmd.isInstanced()) { - commandList_->setUniform("uModelMatrix", cmd.modelMatrix); - } + // 设置模型矩阵 + commandList_->setUniform("uModelMatrix", cmd.modelMatrix); // 绘制 - if (cmd.isInstanced()) { - if (cmd.isIndexed()) { - commandList_->drawIndexed(cmd.indexCount, 0, 0, cmd.instanceCount, 0); - stats_.drawCalls++; - stats_.triangles += (cmd.indexCount / 3) * cmd.instanceCount; - } else { - commandList_->draw(cmd.vertexCount, 0, cmd.instanceCount, 0); - stats_.drawCalls++; - stats_.triangles += (cmd.vertexCount / 3) * cmd.instanceCount; - } + if (cmd.isIndexed()) { + commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0); + stats_.drawCalls++; + stats_.triangles += cmd.indexCount / 3; } else { - if (cmd.isIndexed()) { - commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0); - stats_.drawCalls++; - stats_.triangles += cmd.indexCount / 3; - } else { - commandList_->draw(cmd.vertexCount, 0, 1, 0); - stats_.drawCalls++; - stats_.triangles += cmd.vertexCount / 3; - } + commandList_->draw(cmd.vertexCount, 0, 1, 0); + stats_.drawCalls++; + stats_.triangles += cmd.vertexCount / 3; } - stats_.vertices += cmd.isInstanced() ? (cmd.vertexCount * cmd.instanceCount) : cmd.vertexCount; + stats_.vertices += cmd.vertexCount; } } diff --git a/src/renderer/instance_buffer.cpp b/src/renderer/instance_buffer.cpp index 34003e3..b295be1 100644 --- a/src/renderer/instance_buffer.cpp +++ b/src/renderer/instance_buffer.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace extra2d { @@ -20,7 +21,8 @@ InstanceBuffer::InstanceBuffer(InstanceBuffer&& other) noexcept : bufferHandle_(std::move(other.bufferHandle_)) , maxInstances_(other.maxInstances_) , instanceCount_(other.instanceCount_) - , cpuBuffer_(std::move(other.cpuBuffer_)) { + , cpuBuffer_(std::move(other.cpuBuffer_)) + , dirtyRanges_(std::move(other.dirtyRanges_)) { other.maxInstances_ = 0; other.instanceCount_ = 0; } @@ -33,6 +35,7 @@ InstanceBuffer& InstanceBuffer::operator=(InstanceBuffer&& other) noexcept { maxInstances_ = other.maxInstances_; instanceCount_ = other.instanceCount_; cpuBuffer_ = std::move(other.cpuBuffer_); + dirtyRanges_ = std::move(other.dirtyRanges_); other.maxInstances_ = 0; other.instanceCount_ = 0; @@ -72,6 +75,8 @@ bool InstanceBuffer::initialize(uint32_t maxInstances) { maxInstances_ = maxInstances; instanceCount_ = 0; cpuBuffer_.reserve(maxInstances); + dirtyRanges_.clear(); + dirtyRanges_.reserve(MAX_DIRTY_RANGES); E2D_LOG_DEBUG("InstanceBuffer initialized with capacity for {} instances", maxInstances); return true; @@ -83,6 +88,8 @@ void InstanceBuffer::shutdown() { instanceCount_ = 0; cpuBuffer_.clear(); cpuBuffer_.shrink_to_fit(); + dirtyRanges_.clear(); + dirtyRanges_.shrink_to_fit(); } bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t count) { @@ -96,12 +103,57 @@ bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t cou count = maxInstances_; } - RHIBuffer* rhiBuffer = bufferHandle_.get(); - if (rhiBuffer) { - rhiBuffer->update(instances, count * sizeof(InstanceData), 0); + // 复制到CPU缓冲区 + if (count > 0) { + if (cpuBuffer_.size() < count) { + cpuBuffer_.resize(count); + } + std::memcpy(cpuBuffer_.data(), instances, count * sizeof(InstanceData)); } + // 标记整个范围为脏 + markAllDirty(); + + // 立即上传到GPU + bool result = updateGPU(); + instanceCount_ = count; + return result; +} + +bool InstanceBuffer::updateInstancesRange(const InstanceData* instances, uint32_t start, uint32_t count) { + if (!bufferHandle_.isValid() || !instances || count == 0) { + return false; + } + + if (start >= maxInstances_) { + E2D_LOG_WARN("InstanceBuffer::updateInstancesRange: start {} exceeds maxInstances {}", + start, maxInstances_); + return false; + } + + if (start + count > maxInstances_) { + E2D_LOG_WARN("InstanceBuffer::updateInstancesRange: range {}-{} exceeds maxInstances {}", + start, start + count, maxInstances_); + count = maxInstances_ - start; + } + + // 确保CPU缓冲区足够大 + if (cpuBuffer_.size() < start + count) { + cpuBuffer_.resize(start + count); + } + + // 复制到CPU缓冲区 + std::memcpy(cpuBuffer_.data() + start, instances, count * sizeof(InstanceData)); + + // 标记为脏 + markDirty(start, count); + + // 更新实例数量 + if (start + count > instanceCount_) { + instanceCount_ = start + count; + } + return true; } @@ -120,18 +172,108 @@ uint32_t InstanceBuffer::addInstance(const InstanceData& instance) { cpuBuffer_[index] = instance; } - // 更新 GPU 缓冲区 - RHIBuffer* rhiBuffer = bufferHandle_.get(); - if (rhiBuffer) { - rhiBuffer->update(&instance, sizeof(InstanceData), index * sizeof(InstanceData)); - } + // 标记为脏(单个实例) + markDirty(index, 1); return index; } +void InstanceBuffer::markDirty(uint32_t start, uint32_t count) { + if (count == 0 || start >= maxInstances_) { + return; + } + + // 限制范围 + if (start + count > maxInstances_) { + count = maxInstances_ - start; + } + + addDirtyRange(DirtyRange(start, count)); +} + +void InstanceBuffer::markAllDirty() { + dirtyRanges_.clear(); + if (instanceCount_ > 0) { + dirtyRanges_.push_back(DirtyRange(0, instanceCount_)); + } +} + +bool InstanceBuffer::updateGPU() { + if (!bufferHandle_.isValid() || dirtyRanges_.empty()) { + return true; // 没有脏数据,无需更新 + } + + RHIBuffer* rhiBuffer = bufferHandle_.get(); + if (!rhiBuffer) { + return false; + } + + // 如果脏区域太多,合并为全量更新 + if (dirtyRanges_.size() > MAX_DIRTY_RANGES) { + mergeAllDirtyRanges(); + } + + // 上传所有脏区域 + for (const auto& range : dirtyRanges_) { + if (!range.isValid()) continue; + + const uint8_t* data = reinterpret_cast(cpuBuffer_.data()); + uint32_t offset = range.start * sizeof(InstanceData); + uint32_t size = range.count * sizeof(InstanceData); + + rhiBuffer->update(data + offset, size, offset); + } + + // 清空脏区域列表 + clearDirtyRanges(); + + return true; +} + void InstanceBuffer::clear() { instanceCount_ = 0; cpuBuffer_.clear(); + clearDirtyRanges(); +} + +void InstanceBuffer::addDirtyRange(const DirtyRange& range) { + if (!range.isValid()) return; + + // 检查是否可以与现有区域合并 + for (auto& existing : dirtyRanges_) { + if (existing.canMerge(range)) { + existing.merge(range); + return; + } + } + + // 添加新区域 + dirtyRanges_.push_back(range); + + // 如果脏区域太多,合并所有区域 + if (dirtyRanges_.size() > MAX_DIRTY_RANGES) { + mergeAllDirtyRanges(); + } +} + +void InstanceBuffer::mergeAllDirtyRanges() { + if (dirtyRanges_.empty()) return; + + // 找到最小起始和最大结束 + uint32_t minStart = dirtyRanges_[0].start; + uint32_t maxEnd = dirtyRanges_[0].end(); + + for (const auto& range : dirtyRanges_) { + if (range.start < minStart) minStart = range.start; + if (range.end() > maxEnd) maxEnd = range.end(); + } + + dirtyRanges_.clear(); + dirtyRanges_.push_back(DirtyRange(minStart, maxEnd - minStart)); +} + +void InstanceBuffer::clearDirtyRanges() { + dirtyRanges_.clear(); } // ======================================== diff --git a/src/renderer/renderer_module.cpp b/src/renderer/renderer_module.cpp index ed7fb22..6dff308 100644 --- a/src/renderer/renderer_module.cpp +++ b/src/renderer/renderer_module.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -202,26 +201,6 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) { break; } - case RenderCommandType::DrawMeshInstanced: { - auto *assets = getAssets(); - if (!assets) - return; - - Material *material = assets->get(cmd.drawInstanced.material); - Mesh *mesh = assets->get(cmd.drawInstanced.mesh); - InstanceBuffer *instanceBuffer = - static_cast(cmd.drawInstanced.instanceBuffer); - - if (material && mesh && instanceBuffer && instanceBuffer->isValid()) { - commandQueue_->submitDrawInstanced( - Ptr(material), Ptr(mesh), - BufferHandle(instanceBuffer->getRHIBuffer()), - cmd.drawInstanced.instanceCount); - stats_.commandsSubmitted++; - } - break; - } - case RenderCommandType::SetViewport: { setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width, cmd.viewport.height); diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp index 62aaef2..763a852 100644 --- a/src/renderer/shader.cpp +++ b/src/renderer/shader.cpp @@ -25,47 +25,6 @@ bool Shader::loadFromSource(const std::string &vsSource, return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout); } -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) {