refactor(renderer): 移除实例化渲染功能及相关代码
移除不再需要的实例化渲染功能,包括着色器、材质、渲染命令和测试代码 优化实例缓冲区实现,添加脏标记和增量更新功能
This commit is contained in:
parent
97be1b746a
commit
e0b0a7883d
|
|
@ -1,5 +1,4 @@
|
|||
#include "game_scene.h"
|
||||
#include "instanced_test.h"
|
||||
#include <cmath>
|
||||
|
||||
// ========================================
|
||||
|
|
@ -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<InstancedTestNode>();
|
||||
if (instancedTest->initialize(1000)) {
|
||||
instancedTest_ = instancedTest;
|
||||
addChild(instancedTest);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d.h>
|
||||
#include "instanced_test.h"
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
|
|
@ -135,13 +134,7 @@ private:
|
|||
*/
|
||||
void createCamera();
|
||||
|
||||
/**
|
||||
* @brief 创建实例化渲染测试
|
||||
*/
|
||||
void createInstancedTest();
|
||||
|
||||
Ptr<PlayerNode> player_;
|
||||
std::vector<Ptr<RotatingDecoration>> decorations_;
|
||||
Ptr<InstancedTestNode> instancedTest_;
|
||||
float sceneTime_ = 0.0f;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -176,31 +176,6 @@ public:
|
|||
Material *getDefaultMaterialPtr();
|
||||
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<Mesh> defaultQuad_;
|
||||
|
||||
// 实例化渲染资源
|
||||
Handle<Shader> instancedShader_;
|
||||
Handle<Material> instancedMaterial_;
|
||||
|
||||
// 热重载
|
||||
bool hotReloadEnabled_ = false;
|
||||
float hotReloadInterval_ = 1.0f;
|
||||
|
|
|
|||
|
|
@ -65,29 +65,23 @@ struct DrawCommand {
|
|||
BufferHandle indexBuffer; // 索引缓冲区(可选)
|
||||
uint32_t vertexCount; // 顶点数量
|
||||
uint32_t indexCount; // 索引数量
|
||||
uint32_t instanceCount; // 实例数量(1表示非实例化)
|
||||
std::array<TextureHandle, 8> 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<Mat4>()), color(Color::White) {}
|
||||
|
||||
// 检查是否使用索引绘制
|
||||
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,
|
||||
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 提交清除命令
|
||||
* @param color 清除颜色
|
||||
|
|
|
|||
|
|
@ -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<uint32_t>(dirtyRanges_.size()); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有脏数据
|
||||
* @return 是否有脏数据
|
||||
*/
|
||||
bool hasDirtyData() const { return !dirtyRanges_.empty(); }
|
||||
|
||||
private:
|
||||
BufferHandle bufferHandle_; // RHI 缓冲区句柄
|
||||
uint32_t maxInstances_ = 0; // 最大实例数量
|
||||
uint32_t instanceCount_ = 0; // 当前实例数量
|
||||
std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区
|
||||
BufferHandle bufferHandle_; // RHI 缓冲区句柄
|
||||
uint32_t maxInstances_ = 0; // 最大实例数量
|
||||
uint32_t instanceCount_ = 0; // 当前实例数量
|
||||
std::vector<InstanceData> cpuBuffer_; // CPU 端缓冲区
|
||||
std::vector<DirtyRange> dirtyRanges_; // 脏区域列表
|
||||
|
||||
// 最大脏区域数量,超过则合并为全量更新
|
||||
static constexpr uint32_t MAX_DIRTY_RANGES = 8;
|
||||
|
||||
/**
|
||||
* @brief 添加脏区域,自动合并重叠区域
|
||||
* @param range 脏区域
|
||||
*/
|
||||
void addDirtyRange(const DirtyRange& range);
|
||||
|
||||
/**
|
||||
* @brief 合并所有脏区域为一个
|
||||
*/
|
||||
void mergeAllDirtyRanges();
|
||||
|
||||
/**
|
||||
* @brief 清空脏区域列表
|
||||
*/
|
||||
void clearDirtyRanges();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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> mesh; // 网格句柄
|
||||
Handle<Material> 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
|
||||
|
|
|
|||
|
|
@ -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 着色器句柄
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// 热重载
|
||||
//===========================================================================
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <renderer/command_queue.h>
|
||||
#include <renderer/instance_buffer.h>
|
||||
#include <renderer/material.h>
|
||||
#include <renderer/mesh.h>
|
||||
#include <renderer/rhi_module.h>
|
||||
|
|
@ -406,59 +405,6 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
|||
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) {
|
||||
// 清除命令直接执行,不加入排序队列
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <renderer/rhi/rhi_device.h>
|
||||
#include <utils/logger.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
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<const uint8_t*>(cpuBuffer_.data());
|
||||
uint32_t offset = range.start * sizeof(InstanceData);
|
||||
uint32_t size = range.count * sizeof(InstanceData);
|
||||
|
||||
rhiBuffer->update(data + offset, size, offset);
|
||||
}
|
||||
|
||||
// 清空脏区域列表
|
||||
clearDirtyRanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstanceBuffer::clear() {
|
||||
instanceCount_ = 0;
|
||||
cpuBuffer_.clear();
|
||||
clearDirtyRanges();
|
||||
}
|
||||
|
||||
void InstanceBuffer::addDirtyRange(const DirtyRange& range) {
|
||||
if (!range.isValid()) return;
|
||||
|
||||
// 检查是否可以与现有区域合并
|
||||
for (auto& existing : dirtyRanges_) {
|
||||
if (existing.canMerge(range)) {
|
||||
existing.merge(range);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新区域
|
||||
dirtyRanges_.push_back(range);
|
||||
|
||||
// 如果脏区域太多,合并所有区域
|
||||
if (dirtyRanges_.size() > MAX_DIRTY_RANGES) {
|
||||
mergeAllDirtyRanges();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceBuffer::mergeAllDirtyRanges() {
|
||||
if (dirtyRanges_.empty()) return;
|
||||
|
||||
// 找到最小起始和最大结束
|
||||
uint32_t minStart = dirtyRanges_[0].start;
|
||||
uint32_t maxEnd = dirtyRanges_[0].end();
|
||||
|
||||
for (const auto& range : dirtyRanges_) {
|
||||
if (range.start < minStart) minStart = range.start;
|
||||
if (range.end() > maxEnd) maxEnd = range.end();
|
||||
}
|
||||
|
||||
dirtyRanges_.clear();
|
||||
dirtyRanges_.push_back(DirtyRange(minStart, maxEnd - minStart));
|
||||
}
|
||||
|
||||
void InstanceBuffer::clearDirtyRanges() {
|
||||
dirtyRanges_.clear();
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <assets/assets_module.h>
|
||||
#include <platform/window_module.h>
|
||||
#include <renderer/instance_buffer.h>
|
||||
#include <renderer/render_graph.h>
|
||||
#include <renderer/renderer_module.h>
|
||||
#include <renderer/rhi_module.h>
|
||||
|
|
@ -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<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: {
|
||||
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
|
||||
cmd.viewport.height);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue