diff --git a/source/runtime/render/scene/CpuScene.cpp b/source/runtime/render/scene/CpuScene.cpp index 018cdc78d..8156cb51e 100644 --- a/source/runtime/render/scene/CpuScene.cpp +++ b/source/runtime/render/scene/CpuScene.cpp @@ -2,14 +2,17 @@ #include "LogicalComponents.h" #include "LogicalScene.h" -#include "log/LogSystem.h" +#include "config/ConfigManager.h" +#include "scene/LogicalComponents.h" #include "shaderheaders/shared/scene/SharedSceneStruct.h" #include #include namespace Moer { -CpuScene::CpuScene(ecs::LogicalScene& m_logical_scene) : m_logical_scene(m_logical_scene) { +CpuScene::CpuScene(ecs::LogicalScene& m_logical_scene) : + m_logical_scene(m_logical_scene), + m_frame_data(ConfigManager::GetInstance().GetConfig().engine.rhi.max_frame_in_flight) { /** * 注意初始化顺序: * - Materials必须在Meshes之前初始化,因为Mesh需要Material ID @@ -17,12 +20,22 @@ CpuScene::CpuScene(ecs::LogicalScene& m_logical_scene) : m_logical_scene(m_logic InitializeLights(); InitializeMaterials(); InitializeMeshes(); + + SetupObservers(); } void CpuScene::Update() { + auto& r = m_logical_scene.r(); + UpdateLights(); UpdateMaterials(); UpdateMeshes(); + + r.clear(); + r.clear(); + r.clear(); + + m_current_frame_idx = (m_current_frame_idx + 1) % m_frame_data.size(); } uint CpuScene::GetPrimitiveId(entt::entity primitive_entt) const { @@ -124,7 +137,50 @@ void CpuScene::InitializeLights() { } void CpuScene::UpdateLights() { - // TODO + auto& r = m_logical_scene.r(); + + // 查询所有被标记为 dirty 的 Light 实体 + auto view = r.view(); + view.each([&](const auto entity, const ecs::CLight& c_light) { + auto light_id_it = m_map_light_entity_to_id.find(entity); + if (light_id_it == m_map_light_entity_to_id.end()) { + LOG_ERROR("The light to update was not initialized. Adding new lights is not supported yet."); + return; + } + + uint light_id = light_id_it->second; + GLight light{}; + + if (r.all_of(entity)) { + const auto& c_light = r.get(entity); + light.color = c_light.color; + light.intensity = c_light.intensity; + light.type = static_cast(ELightType::Directional); + light.direction = m_logical_scene.GetDirectionalLightDirection(entity); + } else if (r.all_of(entity)) { + const auto& c_light = r.get(entity); + light.color = c_light.color; + light.intensity = c_light.intensity; + light.position = m_logical_scene.GetPointLightPosition(entity); + light.type = static_cast(ELightType::Point); + } else if (r.all_of(entity)) { + const auto& c_light = r.get(entity); + light.color = c_light.color; + light.intensity = c_light.intensity; + light.type = static_cast(ELightType::Ambient); + } else if (r.all_of(entity)) { + // TODO: spot light 更新待实现 + LOG_ERROR("CLightSpot is not implemented yet"); + return; + } else if (r.all_of(entity)) { + // TODO: environment light 更新待实现 + LOG_ERROR("CLightEnvironment is not implemented yet"); + return; + } + + GetCurrentFrameData().lights.emplace_back(light); + GetCurrentFrameData().dirty_light_indices.emplace_back(light_id); + }); } void CpuScene::InitializeMaterials() { @@ -170,7 +226,29 @@ void CpuScene::InitializeMaterials() { } void CpuScene::UpdateMaterials() { - // TODO + auto& r = m_logical_scene.r(); + + auto view = r.view(); + view.each([&](const auto entity, const ecs::CMaterial& c_material) { + auto mat_id_it = m_map_material_entity_to_id.find(entity); + if (mat_id_it == m_map_material_entity_to_id.end()) { + LOG_ERROR( + "The material to update was not initialized. Adding new materials is not supported yet." + ); + return; + } + + GMaterial g_material{}; + g_material.albedo_factor = c_material.albedo_factor; + g_material.emissive_factor = c_material.emissive_factor; + g_material.metallic_factor = c_material.metallic_factor; + g_material.roughness_factor = c_material.roughness_factor; + g_material.alpha_mode = static_cast(c_material.alpha_mode); + g_material.alpha_cutoff = c_material.alpha_cutoff; + + GetCurrentFrameData().materials.emplace_back(g_material); + GetCurrentFrameData().dirty_material_indices.emplace_back(mat_id_it->second); + }); } void CpuScene::InitializeMeshes() { diff --git a/source/runtime/render/scene/CpuScene.h b/source/runtime/render/scene/CpuScene.h index d48de3068..b2cce4c91 100644 --- a/source/runtime/render/scene/CpuScene.h +++ b/source/runtime/render/scene/CpuScene.h @@ -29,8 +29,10 @@ class GpuScene; * - 存储所有准备上传到GPU的场景数据; * - 实现所有 LogicalScene -> CpuScene 的逻辑 * - * TODO: LogicalScene数据变更时,增量更新CpuScene数据 - * 考虑使用 事件队列 或 观察者模式 + * 增量更新通过CTagDirty实现: + * - 当CLight/CMaterial/CTransform被修改时,观察者回调会添加CTagDirty标签 + * - UpdateLights/UpdateMaterials/UpdateMeshes通过view过滤出有CTagDirty的实体进行处理 + * - 处理完毕后清除CTagDirty标签 */ class RENDER_API CpuScene { @@ -43,6 +45,20 @@ class RENDER_API CpuScene { CpuScene(const CpuScene&) = delete; CpuScene& operator=(const CpuScene&) = delete; + // 每帧更新数据的缓冲,包含所有 Lights、Materials、Instances 的完整数据,以及增量更新的索引列表 + struct FrameData { + Array lights; + Array materials; + Array instances; + + Array dirty_light_indices; + Array dirty_material_indices; + Array dirty_instance_indices; + }; + + FrameData& GetCurrentFrameData() { return m_frame_data[m_current_frame_idx]; } + const FrameData& GetCurrentFrameData() const { return m_frame_data[m_current_frame_idx]; } + void Update(); /** @@ -91,6 +107,16 @@ class RENDER_API CpuScene { ecs::LogicalScene& m_logical_scene; private: + // frame data 的大小与 frame_in_flight 一直 + Array m_frame_data{}; + size_t m_current_frame_idx = 0; + + /** + * 设置观察者,监听 LogicalScene 中 CLight/CMaterial/CTransform 的修改 + * - 当被修改时,观察者回调会添加 CTagDirty 标签 + */ + void SetupObservers(); + /** * 下面的内容分为3类:数据、map缓存、逻辑 * - 数据:存储最终上传到Gpu的数据 diff --git a/source/runtime/render/scene/GpuScene.cpp b/source/runtime/render/scene/GpuScene.cpp index 656d2b23a..d5ff67303 100644 --- a/source/runtime/render/scene/GpuScene.cpp +++ b/source/runtime/render/scene/GpuScene.cpp @@ -1,7 +1,7 @@ #include "GpuScene.h" #include "CpuScene.h" -#include "log/LogSystem.h" +#include "config/ConfigManager.h" #include "rhi/RHI.h" #include "rhi/RHICommand.h" #include "rhi/RHICommon.h" @@ -26,6 +26,7 @@ namespace Moer::Render { GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : m_logical_scene(cpu_scene.m_logical_scene), m_cpu_scene(cpu_scene), + m_frame_in_flight(ConfigManager::GetInstance().GetConfig().engine.rhi.max_frame_in_flight), m_bindless_array(bindless_array) { m_res = Res{}; @@ -124,17 +125,23 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : * 此处按照 Res 中顺序进行创建 */ - m_res.light_buf.buf = device.CreateBuffer( - "GpuScene::LightBuffer", - m_cpu_scene.m_light_buf.size() * sizeof(GLight), - EBufferUsageFlags::UNORDERED_ACCESS - ); + m_res.light_bufs.resize(m_frame_in_flight); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_res.light_bufs[i].buf = device.CreateBuffer( + std::format("GpuScene::LightBuffer_{}", i), + m_cpu_scene.m_light_buf.size() * sizeof(GLight), + EBufferUsageFlags::UNORDERED_ACCESS + ); + } - m_res.material_buf.buf = device.CreateBuffer( - "GpuScene::MaterialBuffer", - m_cpu_scene.m_material_buf.size() * sizeof(GMaterial), - EBufferUsageFlags::UNORDERED_ACCESS - ); + m_res.material_bufs.resize(m_frame_in_flight); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_res.material_bufs[i].buf = device.CreateBuffer( + std::format("GpuScene::MaterialBuffer_{}", i), + m_cpu_scene.m_material_buf.size() * sizeof(GMaterial), + EBufferUsageFlags::UNORDERED_ACCESS + ); + } // 这里不设置为byte,是为了GeometryPass中可以直接获取 命令的数量(cpu count)、DrawIndexedCmdData的stride m_res.draw_cmd_buf.buf = device.CreateBuffer( @@ -149,11 +156,14 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : EBufferUsageFlags::UNORDERED_ACCESS ); - m_res.instance_buf.buf = device.CreateBuffer( - "GpuScene::InstanceBuffer", - m_cpu_scene.m_instance_buf.size() * sizeof(GInstance), - EBufferUsageFlags::UNORDERED_ACCESS - ); + m_res.instance_bufs.resize(m_frame_in_flight); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_res.instance_bufs[i].buf = device.CreateBuffer( + "GpuScene::InstanceBuffer", + m_cpu_scene.m_instance_buf.size() * sizeof(GInstance), + EBufferUsageFlags::UNORDERED_ACCESS + ); + } m_res.position_buf.buf = device.CreateBuffer( "GpuScene::PositionMegaBuffer", @@ -191,21 +201,26 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : * 此处按照 Res 中顺序进行上传 */ - m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( - std::span( - (byte*)m_cpu_scene.m_light_buf.data(), m_cpu_scene.m_light_buf.size() * sizeof(GLight) - ), - m_res.light_buf.buf->GetView(), - "CopyFrom GpuScene::LightBuffer" - ); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( + std::span( + (byte*)m_cpu_scene.m_light_buf.data(), m_cpu_scene.m_light_buf.size() * sizeof(GLight) + ), + m_res.light_bufs[i].buf->GetView(), + "CopyFrom GpuScene::LightBuffer" + ); + } - m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( - std::span( - (byte*)m_cpu_scene.m_material_buf.data(), m_cpu_scene.m_material_buf.size() * sizeof(GMaterial) - ), - m_res.material_buf.buf->GetView(), - "CopyFrom GpuScene::MaterialBuffer" - ); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( + std::span( + (byte*)m_cpu_scene.m_material_buf.data(), + m_cpu_scene.m_material_buf.size() * sizeof(GMaterial) + ), + m_res.material_bufs[i].buf->GetView(), + "CopyFrom GpuScene::MaterialBuffer" + ); + } m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( std::span( @@ -224,13 +239,16 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : "CopyFrom GpuScene::PrimitiveBuffer" ); - m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( - std::span( - (byte*)m_cpu_scene.m_instance_buf.data(), m_cpu_scene.m_instance_buf.size() * sizeof(GInstance) - ), - m_res.instance_buf.buf->GetView(), - "CopyFrom GpuScene::InstanceBuffer" - ); + for (uint i = 0; i < m_frame_in_flight; ++i) { + m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( + std::span( + (byte*)m_cpu_scene.m_instance_buf.data(), + m_cpu_scene.m_instance_buf.size() * sizeof(GInstance) + ), + m_res.instance_bufs[i].buf->GetView(), + "CopyFrom GpuScene::InstanceBuffer" + ); + } m_pending_cmd_lists.copy_queue_cmd_list.CopyFrom( std::span( @@ -286,11 +304,8 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : */ Array buffers = { - &m_res.light_buf, - &m_res.material_buf, &m_res.draw_cmd_buf, &m_res.primitive_buf, - &m_res.instance_buf, &m_res.position_buf, &m_res.packed_normal_buf, &m_res.packed_tangent_buf, @@ -298,6 +313,12 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : &m_res.index_buf, }; + for (uint i = 0; i < m_frame_in_flight; ++i) { + buffers.push_back(&m_res.light_bufs[i]); + buffers.push_back(&m_res.material_bufs[i]); + buffers.push_back(&m_res.instance_bufs[i]); + } + for (auto& buf_with_hdl_ptr : buffers) { BufferWithHandle& buf_with_hdl = *buf_with_hdl_ptr; @@ -362,11 +383,55 @@ GpuScene::GpuScene(CpuScene& cpu_scene, BindlessArrayRef bindless_array) : } void GpuScene::Update(const ecs::LogicalScene& m_logical_scene, CpuScene& m_cpu_scene) { - auto& device = RenderDevice::Get(); + UpdateLights(m_cpu_scene); + UpdateMaterials(m_cpu_scene); + UpdateInstances(m_cpu_scene); + + UpdateRaytracingScene(m_pending_cmd_lists.gfx_queue_cmd_list); // gfx_queue交给主线程执行 - // TODO: others + m_current_frame_idx = (m_current_frame_idx + 1) % m_frame_in_flight; +} - UpdateRaytracingScene(m_pending_cmd_lists.gfx_queue_cmd_list); // gfx_queue交给主线程执行 +void GpuScene::UpdateLights(CpuScene& cpu_scene) { + const auto& frame_data = cpu_scene.GetCurrentFrameData(); + auto& cmd_list = m_pending_cmd_lists.copy_queue_cmd_list; + BufferWithHandle& light_buf = m_res.light_bufs[m_current_frame_idx]; + + for (uint idx : frame_data.dirty_light_indices) { + cmd_list.CopyFrom( + std::span((byte*)&frame_data.lights[idx], sizeof(GLight)), + light_buf.buf->GetView(idx * sizeof(GLight), sizeof(GLight)), + std::format("UpdateLights: CopyFrom light index {}", idx) + ); + } +} + +void GpuScene::UpdateMaterials(CpuScene& cpu_scene) { + const auto& frame_data = cpu_scene.GetCurrentFrameData(); + auto& cmd_list = m_pending_cmd_lists.copy_queue_cmd_list; + BufferWithHandle& material_buf = m_res.material_bufs[m_current_frame_idx]; + + for (uint idx : frame_data.dirty_material_indices) { + cmd_list.CopyFrom( + std::span((byte*)&frame_data.materials[idx], sizeof(GMaterial)), + material_buf.buf->GetView(idx * sizeof(GMaterial), sizeof(GMaterial)), + std::format("UpdateMaterials: CopyFrom material index {}", idx) + ); + } +} + +void GpuScene::UpdateInstances(CpuScene& cpu_scene) { + const auto& frame_data = cpu_scene.GetCurrentFrameData(); + auto& cmd_list = m_pending_cmd_lists.copy_queue_cmd_list; + BufferWithHandle& instance_buf = m_res.instance_bufs[m_current_frame_idx]; + + for (uint idx : frame_data.dirty_instance_indices) { + cmd_list.CopyFrom( + std::span((byte*)&frame_data.instances[idx], sizeof(GInstance)), + instance_buf.buf->GetView(idx * sizeof(GInstance), sizeof(GInstance)), + std::format("UpdateInstances: CopyFrom instance index {}", idx) + ); + } } GpuScene::~GpuScene() noexcept { diff --git a/source/runtime/render/scene/GpuScene.h b/source/runtime/render/scene/GpuScene.h index 42454ca47..5219b0453 100644 --- a/source/runtime/render/scene/GpuScene.h +++ b/source/runtime/render/scene/GpuScene.h @@ -25,6 +25,10 @@ class RENDER_API GpuScene { void Update(const ecs::LogicalScene& logical_scene, CpuScene& cpu_scene); + void UpdateLights(CpuScene& cpu_scene); + void UpdateMaterials(CpuScene& cpu_scene); + void UpdateInstances(CpuScene& cpu_scene); + /** * 便于抛出GpuScene Res接口 */ @@ -33,15 +37,15 @@ class RENDER_API GpuScene { Array texture_array; // light - BufferWithHandle light_buf; + Array light_bufs; // material - BufferWithHandle material_buf; + Array material_bufs; // mesh - BufferWithHandle draw_cmd_buf; - BufferWithHandle primitive_buf; - BufferWithHandle instance_buf; + BufferWithHandle draw_cmd_buf; + BufferWithHandle primitive_buf; + Array instance_bufs; // mega buffers BufferWithHandle position_buf; @@ -104,6 +108,8 @@ class RENDER_API GpuScene { CpuScene& m_cpu_scene; private: + size_t m_frame_in_flight; // 初始化时从 ConfigManager 获取 + size_t m_current_frame_idx = 0; Res m_res; BindlessArrayRef m_bindless_array; // TODO: 移动到RenderDevice里? diff --git a/source/runtime/render/scene/LogicalComponents.h b/source/runtime/render/scene/LogicalComponents.h index 78062bb5d..33a77c87a 100644 --- a/source/runtime/render/scene/LogicalComponents.h +++ b/source/runtime/render/scene/LogicalComponents.h @@ -201,4 +201,19 @@ struct CtxMegaBuffers { Array index; }; +/** + * Dirty Tag Component + * + * 用于标记需要在CpuScene中重新上传到GpuScene的组件 + * 比如 Light 可能同时有 CLight 和 CTransform 组件,所以对 Dirty 的类型需要做区分 + * + * 使用方法: + * - 观察者回调中:reg.emplace(entity) + * - 更新函数中:auto view = reg.view(); + * - 处理完毕后:reg.clear() 清除所有标记 + */ +struct CTagDirtyLight {}; +struct CTagDirtyMaterial {}; +struct CTagDirtyTransform {}; + } // namespace Moer::ecs \ No newline at end of file diff --git a/source/runtime/render/scene/LogicalScene.cpp b/source/runtime/render/scene/LogicalScene.cpp index 51ca6c0f0..31a2af801 100644 --- a/source/runtime/render/scene/LogicalScene.cpp +++ b/source/runtime/render/scene/LogicalScene.cpp @@ -108,9 +108,6 @@ void LogicalScene::SUpdateAllNodeTransformAndAABB() { Array dirty_entities; dirty_entities.reserve(registry.group().size()); registry.group().each([&](auto entity_id, auto& c_node, auto& c_transform) { - if (c_transform.is_dirty == false) - return; - // 检查父节点是否 dirty,向下传递 bool is_parent_dirty = false; if (c_node.parent_entt != entt::null) { @@ -119,7 +116,7 @@ void LogicalScene::SUpdateAllNodeTransformAndAABB() { } // is_dirty会向下传递 - c_transform.is_dirty = c_transform.is_dirty || is_parent_dirty; + c_transform.is_dirty |= is_parent_dirty; if (c_transform.is_dirty) { dirty_entities.push_back(entity_id); } diff --git a/source/runtime/render/scene/Scene.cpp b/source/runtime/render/scene/Scene.cpp index f73364320..363a4b0f0 100644 --- a/source/runtime/render/scene/Scene.cpp +++ b/source/runtime/render/scene/Scene.cpp @@ -58,7 +58,11 @@ Render::GpuScene::PendingCommandList&& Scene::PopPendingCommandList() { } void Scene::Tick() { - // TODO + m_logical_scene->SUpdateAllNodeTransformAndAABB(); + + m_cpu_scene->Update(); + + m_gpu_scene->Update(*m_logical_scene, *m_cpu_scene); } void Scene::Reset() {