refactor(renderer): 移除实例化渲染功能及相关代码

移除不再需要的实例化渲染功能,包括着色器、材质、渲染命令和测试代码
优化实例缓冲区实现,添加脏标记和增量更新功能
This commit is contained in:
ChestnutYueyue 2026-03-03 12:18:32 +08:00
parent 97be1b746a
commit e0b0a7883d
18 changed files with 295 additions and 699 deletions

View File

@ -1,5 +1,4 @@
#include "game_scene.h" #include "game_scene.h"
#include "instanced_test.h"
#include <cmath> #include <cmath>
// ======================================== // ========================================
@ -80,9 +79,6 @@ void GameScene::onEnter() {
// 创建装饰物 // 创建装饰物
createDecorations(); createDecorations();
// 创建实例化渲染测试1000个实例
createInstancedTest();
} }
void GameScene::onExit() { void GameScene::onExit() {
@ -93,6 +89,7 @@ void GameScene::onExit() {
Scene::onExit(); Scene::onExit();
} }
void GameScene::update(float dt) { void GameScene::update(float dt) {
Scene::update(dt); Scene::update(dt);
@ -107,11 +104,6 @@ void GameScene::update(float 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() {
@ -174,12 +166,3 @@ void GameScene::createCamera() {
// 添加相机节点到场景 // 添加相机节点到场景
addChild(cameraNode); addChild(cameraNode);
} }
void GameScene::createInstancedTest() {
// 创建实例化渲染测试节点
auto instancedTest = makePtr<InstancedTestNode>();
if (instancedTest->initialize(1000)) {
instancedTest_ = instancedTest;
addChild(instancedTest);
}
}

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <extra2d.h> #include <extra2d.h>
#include "instanced_test.h"
using namespace extra2d; using namespace extra2d;
@ -135,13 +134,7 @@ 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

@ -1,139 +0,0 @@
#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

@ -1,58 +0,0 @@
#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

@ -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;
}
}

View File

@ -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;
}

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", "instanced_test.cpp") add_files("main.cpp", "game_scene.cpp")
add_includedirs("../../include", ".") add_includedirs("../../include", ".")
add_deps("extra2d") add_deps("extra2d")

View File

@ -176,31 +176,6 @@ 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();
//=========================================================================== //===========================================================================
// 热重载 // 热重载
//=========================================================================== //===========================================================================
@ -285,10 +260,6 @@ 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

@ -65,29 +65,23 @@ struct DrawCommand {
BufferHandle indexBuffer; // 索引缓冲区(可选) BufferHandle indexBuffer; // 索引缓冲区(可选)
uint32_t vertexCount; // 顶点数量 uint32_t vertexCount; // 顶点数量
uint32_t indexCount; // 索引数量 uint32_t indexCount; // 索引数量
uint32_t instanceCount; // 实例数量1表示非实例化
std::array<TextureHandle, 8> textures; // 纹理数组 std::array<TextureHandle, 8> textures; // 纹理数组
uint32_t textureCount; // 纹理数量 uint32_t textureCount; // 纹理数量
BufferHandle materialUBO; // 材质 UBO BufferHandle materialUBO; // 材质 UBO
uint32_t materialUBOSize; // 材质 UBO 大小 uint32_t materialUBOSize; // 材质 UBO 大小
uint32_t materialUBOOffset; // 材质 UBO 在全局缓冲区中的偏移 uint32_t materialUBOOffset; // 材质 UBO 在全局缓冲区中的偏移
BufferHandle instanceBuffer; // 实例数据缓冲区(实例化渲染使用)
uint32_t instanceBufferStride; // 实例数据步长
// 变换和颜色数据(用于设置 shader uniform // 变换和颜色数据(用于设置 shader uniform
Mat4 modelMatrix; // 模型矩阵 Mat4 modelMatrix; // 模型矩阵
Color color; // 颜色 Color color; // 颜色
DrawCommand() DrawCommand()
: vertexCount(0), indexCount(0), instanceCount(1), textureCount(0), : vertexCount(0), indexCount(0), textureCount(0),
materialUBOSize(0), materialUBOOffset(0), instanceBufferStride(0), materialUBOSize(0), materialUBOOffset(0),
modelMatrix(glm::identity<Mat4>()), color(Color::White) {} modelMatrix(glm::identity<Mat4>()), color(Color::White) {}
// 检查是否使用索引绘制 // 检查是否使用索引绘制
bool isIndexed() const { return indexCount > 0; } bool isIndexed() const { return indexCount > 0; }
// 检查是否实例化绘制
bool isInstanced() const { return instanceCount > 1; }
}; };
/** /**
@ -294,16 +288,6 @@ public:
void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh, void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
const struct Transform &transform, const Color &color); const struct Transform &transform, const Color &color);
/**
* @brief
* @param material
* @param mesh
* @param instanceBuffer
* @param instanceCount
*/
void submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> mesh,
BufferHandle instanceBuffer, uint32_t instanceCount);
/** /**
* @brief * @brief
* @param color * @param color

View File

@ -15,21 +15,25 @@ namespace extra2d {
* *
* *
* std140 * std140
*
*
* - 使 cos/sin
* - 便GPU访问
*/ */
struct InstanceData { struct InstanceData {
// 第一组 16 字节 // 第一组 16 字节 - 变换数据
Vec2 position; // 位置偏移 (8 bytes) Vec2 position; // 位置偏移 (8 bytes)
float rotation; // 旋转角度 (4 bytes) float rotationCos; // 旋转角度余弦值 (4 bytes) - 预计算避免着色器三角函数
float padding0; // 填充到 16 字节对齐 (4 bytes) float rotationSin; // 旋转角度正弦值 (4 bytes) - 预计算避免着色器三角函数
// 第二组 16 字节 // 第二组 16 字节 - 缩放和预留
Vec2 scale; // 缩放 (8 bytes) Vec2 scale; // 缩放 (8 bytes)
float padding1[2]; // 填充到 16 字节对齐 (8 bytes) float padding1[2]; // 填充到 16 字节对齐 (8 bytes)
// 第三组 16 字节 // 第三组 16 字节 - 颜色
Color color; // 颜色 (16 bytes) - r, g, b, a Color color; // 颜色 (16 bytes) - r, g, b, a
// 第四组 16 字节 // 第四组 16 字节 - UV坐标
float uvX; // UV 起始 X (4 bytes) float uvX; // UV 起始 X (4 bytes)
float uvY; // UV 起始 Y (4 bytes) float uvY; // UV 起始 Y (4 bytes)
float uvWidth; // UV 宽度 (4 bytes) float uvWidth; // UV 宽度 (4 bytes)
@ -37,8 +41,8 @@ struct InstanceData {
InstanceData() InstanceData()
: position(0.0f, 0.0f) : position(0.0f, 0.0f)
, rotation(0.0f) , rotationCos(1.0f) // cos(0) = 1
, padding0(0.0f) , rotationSin(0.0f) // sin(0) = 0
, scale(1.0f, 1.0f) , scale(1.0f, 1.0f)
, padding1{0.0f, 0.0f} , padding1{0.0f, 0.0f}
, color(Color::White) , color(Color::White)
@ -46,15 +50,65 @@ struct InstanceData {
, uvY(0.0f) , uvY(0.0f)
, uvWidth(1.0f) , uvWidth(1.0f)
, uvHeight(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"); 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 * @brief
* *
* *
* *
*
*
* - PCIe带宽占用
* - GPU上传次数
*/ */
class InstanceBuffer { class InstanceBuffer {
public: public:
@ -89,13 +143,22 @@ public:
void shutdown(); void shutdown();
/** /**
* @brief * @brief
* @param instances * @param instances
* @param count * @param count
* @return * @return
*/ */
bool updateInstances(const InstanceData* instances, uint32_t count); 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 * @brief
* @param instance * @param instance
@ -103,6 +166,24 @@ public:
*/ */
uint32_t addInstance(const InstanceData& instance); 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 * @brief
*/ */
@ -138,11 +219,43 @@ public:
*/ */
bool isValid() const { return bufferHandle_.isValid(); } bool isValid() const { return bufferHandle_.isValid(); }
/**
* @brief
* @return
*/
uint32_t getDirtyRangeCount() const { return static_cast<uint32_t>(dirtyRanges_.size()); }
/**
* @brief
* @return
*/
bool hasDirtyData() const { return !dirtyRanges_.empty(); }
private: private:
BufferHandle bufferHandle_; // RHI 缓冲区句柄 BufferHandle bufferHandle_; // RHI 缓冲区句柄
uint32_t maxInstances_ = 0; // 最大实例数量 uint32_t maxInstances_ = 0; // 最大实例数量
uint32_t instanceCount_ = 0; // 当前实例数量 uint32_t instanceCount_ = 0; // 当前实例数量
std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区 std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区
std::vector<DirtyRange> dirtyRanges_; // 脏区域列表
// 最大脏区域数量,超过则合并为全量更新
static constexpr uint32_t MAX_DIRTY_RANGES = 8;
/**
* @brief
* @param range
*/
void addDirtyRange(const DirtyRange& range);
/**
* @brief
*/
void mergeAllDirtyRanges();
/**
* @brief
*/
void clearDirtyRanges();
}; };
/** /**

View File

@ -20,10 +20,9 @@ class Texture;
* @brief * @brief
*/ */
enum class RenderCommandType : uint8_t { enum class RenderCommandType : uint8_t {
DrawMesh, // 绘制网格 DrawMesh, // 绘制网格
DrawMeshInstanced, // 实例化绘制 SetViewport, // 设置视口
SetViewport, // 设置视口 Clear // 清除缓冲区
Clear // 清除缓冲区
}; };
/** /**
@ -46,15 +45,6 @@ struct RenderCommand {
Color color; // 顶点颜色 Color color; // 顶点颜色
}; };
// 实例化绘制命令数据
struct DrawInstancedData {
Handle<Mesh> mesh; // 网格句柄
Handle<Material> material; // 材质句柄
void* instanceBuffer; // 实例数据缓冲区指针 (InstanceBuffer*)
uint32_t instanceCount; // 实例数量
uint32_t instanceOffset; // 实例数据偏移
};
// 设置视口命令数据 // 设置视口命令数据
struct ViewportData { struct ViewportData {
int32_t x, y; // 视口位置 int32_t x, y; // 视口位置
@ -69,7 +59,6 @@ struct RenderCommand {
union { union {
DrawMeshData drawMesh; DrawMeshData drawMesh;
DrawInstancedData drawInstanced;
ViewportData viewport; ViewportData viewport;
ClearData clear; ClearData clear;
}; };
@ -123,12 +112,8 @@ constexpr uint32_t CLEAR_ALL_FLAG =
// 最大渲染命令数 // 最大渲染命令数
constexpr uint32_t MAX_RENDER_COMMANDS = 10000; constexpr uint32_t MAX_RENDER_COMMANDS = 10000;
// 最大实例数
constexpr uint32_t MAX_INSTANCES = 256;
// UBO 绑定槽位(已移至 RHI // UBO 绑定槽位(已移至 RHI
constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO constexpr uint32_t GLOBAL_UBO_BINDING = 0; // 全局 UBO
constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO constexpr uint32_t MATERIAL_UBO_BINDING = 1; // 材质 UBO
constexpr uint32_t INSTANCE_UBO_BINDING = 2; // 实例 UBO
} // namespace extra2d } // namespace extra2d

View File

@ -35,14 +35,6 @@ public:
*/ */
bool loadFromSource(const std::string &vsSource, const std::string &fsSource); 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 * @brief RHI
* @return RHI * @return RHI

View File

@ -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;
}

View File

@ -101,13 +101,6 @@ 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");
} }
@ -558,82 +551,6 @@ 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() {
// 加载实例化着色器
// 使用 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> shader = makePtr<Shader>();
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<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;
}
//=========================================================================== //===========================================================================
// 热重载 // 热重载
//=========================================================================== //===========================================================================

View File

@ -2,7 +2,6 @@
#include <cstring> #include <cstring>
#include <vector> #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>
@ -406,59 +405,6 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
sorter_.addCommand(cmd); sorter_.addCommand(cmd);
} }
void CommandQueue::submitDrawInstanced(Ptr<Material> material, Ptr<Mesh> 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<uint32_t>(std::min(textures.size(), size_t(8)));
for (uint32_t i = 0; i < cmd.textureCount; ++i) {
if (textures[i].texture) {
cmd.textures[i] = textures[i].texture->getHandle();
}
}
// 分配材质 UBO 空间(使用 CPU 缓冲区)
uint32_t materialDataSize = material->getDataSize();
if (materialDataSize > 0) {
auto [ubo, offset] = allocateMaterialUBO(materialDataSize);
(void)ubo; // ubo 为 nullptr使用 CPU 缓冲区
// 复制材质数据到 CPU 缓冲区
if (offset + materialDataSize <= materialUBOData_.size()) {
std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize);
cmd.materialUBOOffset = offset;
cmd.materialUBOSize = materialDataSize;
}
}
sorter_.addCommand(cmd);
}
void CommandQueue::submitClear(const Color &color, uint32_t flags) { void CommandQueue::submitClear(const Color &color, uint32_t flags) {
// 清除命令直接执行,不加入排序队列 // 清除命令直接执行,不加入排序队列
if (!commandList_) { 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!"); 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()) { if (cmd.isIndexed() && cmd.indexBuffer.isValid()) {
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0); commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
@ -593,34 +533,20 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
stats_.bufferBinds++; stats_.bufferBinds++;
} }
// 设置模型矩阵(仅在非实例化渲染时使用) // 设置模型矩阵
if (!cmd.isInstanced()) { commandList_->setUniform("uModelMatrix", cmd.modelMatrix);
commandList_->setUniform("uModelMatrix", cmd.modelMatrix);
}
// 绘制 // 绘制
if (cmd.isInstanced()) { if (cmd.isIndexed()) {
if (cmd.isIndexed()) { commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0);
commandList_->drawIndexed(cmd.indexCount, 0, 0, cmd.instanceCount, 0); stats_.drawCalls++;
stats_.drawCalls++; stats_.triangles += cmd.indexCount / 3;
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;
}
} else { } else {
if (cmd.isIndexed()) { commandList_->draw(cmd.vertexCount, 0, 1, 0);
commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0); stats_.drawCalls++;
stats_.drawCalls++; stats_.triangles += cmd.vertexCount / 3;
stats_.triangles += cmd.indexCount / 3;
} else {
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;
} }
} }

View File

@ -3,6 +3,7 @@
#include <renderer/rhi/rhi_device.h> #include <renderer/rhi/rhi_device.h>
#include <utils/logger.h> #include <utils/logger.h>
#include <cstring> #include <cstring>
#include <algorithm>
namespace extra2d { namespace extra2d {
@ -20,7 +21,8 @@ InstanceBuffer::InstanceBuffer(InstanceBuffer&& other) noexcept
: bufferHandle_(std::move(other.bufferHandle_)) : bufferHandle_(std::move(other.bufferHandle_))
, maxInstances_(other.maxInstances_) , maxInstances_(other.maxInstances_)
, instanceCount_(other.instanceCount_) , instanceCount_(other.instanceCount_)
, cpuBuffer_(std::move(other.cpuBuffer_)) { , cpuBuffer_(std::move(other.cpuBuffer_))
, dirtyRanges_(std::move(other.dirtyRanges_)) {
other.maxInstances_ = 0; other.maxInstances_ = 0;
other.instanceCount_ = 0; other.instanceCount_ = 0;
} }
@ -33,6 +35,7 @@ InstanceBuffer& InstanceBuffer::operator=(InstanceBuffer&& other) noexcept {
maxInstances_ = other.maxInstances_; maxInstances_ = other.maxInstances_;
instanceCount_ = other.instanceCount_; instanceCount_ = other.instanceCount_;
cpuBuffer_ = std::move(other.cpuBuffer_); cpuBuffer_ = std::move(other.cpuBuffer_);
dirtyRanges_ = std::move(other.dirtyRanges_);
other.maxInstances_ = 0; other.maxInstances_ = 0;
other.instanceCount_ = 0; other.instanceCount_ = 0;
@ -72,6 +75,8 @@ bool InstanceBuffer::initialize(uint32_t maxInstances) {
maxInstances_ = maxInstances; maxInstances_ = maxInstances;
instanceCount_ = 0; instanceCount_ = 0;
cpuBuffer_.reserve(maxInstances); cpuBuffer_.reserve(maxInstances);
dirtyRanges_.clear();
dirtyRanges_.reserve(MAX_DIRTY_RANGES);
E2D_LOG_DEBUG("InstanceBuffer initialized with capacity for {} instances", maxInstances); E2D_LOG_DEBUG("InstanceBuffer initialized with capacity for {} instances", maxInstances);
return true; return true;
@ -83,6 +88,8 @@ void InstanceBuffer::shutdown() {
instanceCount_ = 0; instanceCount_ = 0;
cpuBuffer_.clear(); cpuBuffer_.clear();
cpuBuffer_.shrink_to_fit(); cpuBuffer_.shrink_to_fit();
dirtyRanges_.clear();
dirtyRanges_.shrink_to_fit();
} }
bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t count) { bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t count) {
@ -96,12 +103,57 @@ bool InstanceBuffer::updateInstances(const InstanceData* instances, uint32_t cou
count = maxInstances_; count = maxInstances_;
} }
RHIBuffer* rhiBuffer = bufferHandle_.get(); // 复制到CPU缓冲区
if (rhiBuffer) { if (count > 0) {
rhiBuffer->update(instances, count * sizeof(InstanceData), 0); if (cpuBuffer_.size() < count) {
cpuBuffer_.resize(count);
}
std::memcpy(cpuBuffer_.data(), instances, count * sizeof(InstanceData));
} }
// 标记整个范围为脏
markAllDirty();
// 立即上传到GPU
bool result = updateGPU();
instanceCount_ = count; 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; return true;
} }
@ -120,18 +172,108 @@ uint32_t InstanceBuffer::addInstance(const InstanceData& instance) {
cpuBuffer_[index] = instance; cpuBuffer_[index] = instance;
} }
// 更新 GPU 缓冲区 // 标记为脏(单个实例)
RHIBuffer* rhiBuffer = bufferHandle_.get(); markDirty(index, 1);
if (rhiBuffer) {
rhiBuffer->update(&instance, sizeof(InstanceData), index * sizeof(InstanceData));
}
return index; return index;
} }
void InstanceBuffer::markDirty(uint32_t start, uint32_t count) {
if (count == 0 || start >= maxInstances_) {
return;
}
// 限制范围
if (start + count > maxInstances_) {
count = maxInstances_ - start;
}
addDirtyRange(DirtyRange(start, count));
}
void InstanceBuffer::markAllDirty() {
dirtyRanges_.clear();
if (instanceCount_ > 0) {
dirtyRanges_.push_back(DirtyRange(0, instanceCount_));
}
}
bool InstanceBuffer::updateGPU() {
if (!bufferHandle_.isValid() || dirtyRanges_.empty()) {
return true; // 没有脏数据,无需更新
}
RHIBuffer* rhiBuffer = bufferHandle_.get();
if (!rhiBuffer) {
return false;
}
// 如果脏区域太多,合并为全量更新
if (dirtyRanges_.size() > MAX_DIRTY_RANGES) {
mergeAllDirtyRanges();
}
// 上传所有脏区域
for (const auto& range : dirtyRanges_) {
if (!range.isValid()) continue;
const uint8_t* data = reinterpret_cast<const uint8_t*>(cpuBuffer_.data());
uint32_t offset = range.start * sizeof(InstanceData);
uint32_t size = range.count * sizeof(InstanceData);
rhiBuffer->update(data + offset, size, offset);
}
// 清空脏区域列表
clearDirtyRanges();
return true;
}
void InstanceBuffer::clear() { void InstanceBuffer::clear() {
instanceCount_ = 0; instanceCount_ = 0;
cpuBuffer_.clear(); 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();
} }
// ======================================== // ========================================

View File

@ -1,6 +1,5 @@
#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>
@ -202,26 +201,6 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
break; 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<InstanceBuffer *>(cmd.drawInstanced.instanceBuffer);
if (material && mesh && instanceBuffer && instanceBuffer->isValid()) {
commandQueue_->submitDrawInstanced(
Ptr<Material>(material), Ptr<Mesh>(mesh),
BufferHandle(instanceBuffer->getRHIBuffer()),
cmd.drawInstanced.instanceCount);
stats_.commandsSubmitted++;
}
break;
}
case RenderCommandType::SetViewport: { case RenderCommandType::SetViewport: {
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width, setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
cmd.viewport.height); cmd.viewport.height);

View File

@ -25,47 +25,6 @@ bool Shader::loadFromSource(const std::string &vsSource,
return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout); 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, bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource, const std::string &fsSource,
const VertexLayout &vertexLayout) { const VertexLayout &vertexLayout) {