feat: initial DX12 foundation framework
This commit is contained in:
58
docs/架构分析/Entity组件分析.md
Normal file
58
docs/架构分析/Entity组件分析.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Entity 组件分析
|
||||
|
||||
## 组件职责
|
||||
|
||||
`Entity.*` 负责:
|
||||
|
||||
- 分配与回收 `entity_id`;
|
||||
- 维护实体到 `transform/script` 句柄的绑定;
|
||||
- 提供 `is_alive` 存活判定。
|
||||
|
||||
## 核心存储
|
||||
|
||||
位于 `Entity.cpp` 的静态容器:
|
||||
|
||||
- `transforms`:实体索引 -> 变换组件句柄
|
||||
- `scripts`:实体索引 -> 脚本组件句柄
|
||||
- `generations`:实体索引 -> 当前代数
|
||||
- `free_ids`:可复用实体 ID 队列
|
||||
|
||||
## 创建流程
|
||||
|
||||
入口:`game_entity::create(entity_info info)`。
|
||||
|
||||
主要步骤:
|
||||
|
||||
1. 校验 `info.transform`,Transform 为必需组件;
|
||||
2. 分配 `entity_id`:
|
||||
- 回收路径:从 `free_ids` 取旧 id,`new_generation`;
|
||||
- 新建路径:追加新索引并扩展并行数组;
|
||||
3. 创建 Transform 组件(强制);
|
||||
4. 若传入脚本工厂,则创建 Script 组件(可选);
|
||||
5. 返回 `entity` 轻句柄。
|
||||
|
||||
## 删除流程
|
||||
|
||||
入口:`game_entity::remove(entity_id id)`。
|
||||
|
||||
步骤:
|
||||
|
||||
1. 校验实体存活;
|
||||
2. 若存在脚本组件,先删脚本并清空句柄槽;
|
||||
3. 删除 Transform 并清空句柄槽;
|
||||
4. 把 id 放入 `free_ids`,等待复用。
|
||||
|
||||
## 存活判定规则
|
||||
|
||||
`is_alive(id)` 依赖两项:
|
||||
|
||||
- `generations[index] == id::generation(id)`
|
||||
- `transforms[index].is_valid()`
|
||||
|
||||
这让“旧代数句柄”天然失效,避免悬挂句柄误访问。
|
||||
|
||||
## 设计特点
|
||||
|
||||
- Entity 层是组件索引空间的“主时钟”;
|
||||
- Transform 与实体强 1:1 绑定;
|
||||
- Script 按需挂载,存在即可访问,不存在则为空句柄。
|
||||
18
docs/架构分析/Id与Components运行机制设计分析.md
Normal file
18
docs/架构分析/Id与Components运行机制设计分析.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Id.h 与 Components 运行机制设计索引
|
||||
|
||||
本文档改为总览索引。按你的要求,每个组件拆分到独立页面。
|
||||
|
||||
## 总览
|
||||
|
||||
- ID 机制分析:`Engine/Common/Id.h`
|
||||
- Entity 组件分析:`Engine/Components/Entity.cpp/.h`
|
||||
- Transform 组件分析:`Engine/Components/Transform.cpp/.h`
|
||||
- Script 组件分析:`Engine/Components/Script.cpp/.h`
|
||||
|
||||
## 分页文档
|
||||
|
||||
- [Id机制分析.md](./Id机制分析.md)
|
||||
- [Entity组件分析.md](./Entity组件分析.md)
|
||||
- [Transform组件分析.md](./Transform组件分析.md)
|
||||
- [Script组件分析.md](./Script组件分析.md)
|
||||
- [项目约定规范.md](./项目约定规范.md)
|
||||
46
docs/架构分析/Id机制分析.md
Normal file
46
docs/架构分析/Id机制分析.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Id 机制分析
|
||||
|
||||
## 作用定位
|
||||
|
||||
`Engine/Common/Id.h` 是整个运行时句柄系统的基础。
|
||||
核心目标是用一个 `u32` 同时表达:
|
||||
|
||||
- 资源在数组中的索引;
|
||||
- 该索引的生命周期代数(generation)。
|
||||
|
||||
## 位布局
|
||||
|
||||
- `id_type = u32`
|
||||
- 高位:generation(`generation_bits = 8`)
|
||||
- 低位:index(`index_bit = 24`)
|
||||
|
||||
对应关键常量与工具:
|
||||
|
||||
- `index_mask`:提取低位索引
|
||||
- `generation_mask`:提取高位代数
|
||||
- `invalid_id`:无效句柄哨兵值
|
||||
|
||||
## 核心函数语义
|
||||
|
||||
- `is_valid(id)`:判断是否不是无效值
|
||||
- `index(id)`:提取索引位
|
||||
- `generation(id)`:提取代数位
|
||||
- `new_generation(id)`:索引不变,代数递增
|
||||
|
||||
## 运行时价值
|
||||
|
||||
它主要解决“删除后旧句柄仍被外部持有”的问题:
|
||||
|
||||
1. 对象删除后,槽位可复用;
|
||||
2. 复用时代数 +1;
|
||||
3. 旧句柄的 generation 与当前 generation 不一致;
|
||||
4. 存活校验失败,避免误访问新对象。
|
||||
|
||||
## 强类型策略
|
||||
|
||||
`DEFINE_TYPED_ID(name)`:
|
||||
|
||||
- Debug:生成包装类型,增强类型隔离;
|
||||
- Release:退化为 `id::id_type`,零额外开销。
|
||||
|
||||
这是“开发期安全 + 发布期性能”的组合设计。
|
||||
119
docs/架构分析/Script组件分析.md
Normal file
119
docs/架构分析/Script组件分析.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Script 组件分析
|
||||
|
||||
## 组件职责
|
||||
|
||||
`Script.*` 负责:
|
||||
|
||||
- 脚本类型注册与脚本实例创建;
|
||||
- `script_id` 生命周期管理;
|
||||
- 脚本逐帧更新;
|
||||
- 脚本对 Transform 的延迟写回。
|
||||
|
||||
## 核心存储
|
||||
|
||||
`Script.cpp` 的关键结构:
|
||||
|
||||
- `entity_scripts`:活跃脚本实例池
|
||||
- `id_mapping`:`script_id` 索引到实例池下标
|
||||
- `generations + free_ids`:脚本 ID 代数与复用队列
|
||||
- `transform_cache`:脚本写变换的缓存数组
|
||||
- `cache_map`:Transform ID 到缓存下标映射
|
||||
|
||||
## 注册机制
|
||||
|
||||
脚本工厂注册表:
|
||||
|
||||
- 类型:`unordered_map<size_t, script_creator>`
|
||||
- `register_script(tag, func)` 注册工厂
|
||||
- `get_script_creator(tag)` 查询工厂
|
||||
|
||||
通常由 `REGISTER_SCRIPT` 宏在静态初始化阶段完成注册。
|
||||
|
||||
## 创建流程
|
||||
|
||||
`script::create(init_info, entity)`:
|
||||
|
||||
1. 校验实体有效与工厂函数有效;
|
||||
2. 分配 `script_id`(复用或新建);
|
||||
3. 调用工厂创建脚本实例并压入 `entity_scripts`;
|
||||
4. 写入 `id_mapping`;
|
||||
5. 返回 `script::component` 句柄。
|
||||
|
||||
## 删除流程
|
||||
|
||||
`script::remove(component c)`:
|
||||
|
||||
- 校验组件存在;
|
||||
- 用无序删除移除脚本实例;
|
||||
- 修正被交换元素的 `id_mapping`;
|
||||
- 将被删 id 的映射标记为无效。
|
||||
|
||||
## 更新与缓存提交流程
|
||||
|
||||
`script::update(dt)` 包含两段:
|
||||
|
||||
1. 遍历 `entity_scripts` 执行每个脚本的 `update(dt)`;
|
||||
2. 若 `transform_cache` 非空,统一调用 `transform::update(...)` 提交后清空。
|
||||
|
||||
这使脚本阶段不直接写 Transform 主数组,减少帧内重复写。
|
||||
|
||||
## 脚本写 Transform 的方式
|
||||
|
||||
`entity_script::set_rotation/position/...` 的行为是:
|
||||
|
||||
- 先通过 `get_cache_ptr(entity)` 找到该实体缓存槽;
|
||||
- 设置对应 `flags`;
|
||||
- 写入新值到 cache;
|
||||
- 等待帧末统一提交。
|
||||
|
||||
## 设计特点
|
||||
|
||||
- 句柄安全:`script_id` 采用 generation 机制;
|
||||
- 批处理友好:Transform 写入集中提交;
|
||||
- 解耦:脚本逻辑与 Transform 存储细节隔离。
|
||||
|
||||
## 三条主线时序图(注册、生命周期、缓存提交)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Macro as REGISTER_SCRIPT
|
||||
participant Detail as script::detail
|
||||
participant Registry as script_registry
|
||||
participant Entity as game_entity::entity
|
||||
participant ScriptSys as script::system
|
||||
participant Obj as entity_script
|
||||
participant Cache as transform_cache
|
||||
participant Trans as transform::system
|
||||
|
||||
rect rgb(236, 248, 255)
|
||||
Note over Macro,Registry: 主线一:脚本类型注册
|
||||
Macro->>Detail: register_script(tag, creator)
|
||||
Detail->>Registry: insert(tag, creator)
|
||||
Registry-->>Detail: creator 已登记
|
||||
end
|
||||
|
||||
rect rgb(239, 252, 240)
|
||||
Note over Entity,ScriptSys: 主线二:脚本实例生命周期
|
||||
Entity->>ScriptSys: create(init_info, entity)
|
||||
ScriptSys->>ScriptSys: 分配或复用 script_id
|
||||
ScriptSys->>Registry: get_script_creator(tag)
|
||||
Registry-->>ScriptSys: 返回 creator
|
||||
ScriptSys->>Obj: creator(entity)
|
||||
Obj-->>ScriptSys: 生成脚本实例
|
||||
ScriptSys->>ScriptSys: 写入 entity_scripts 与 id_mapping
|
||||
ScriptSys-->>Entity: 返回 script::component
|
||||
Entity->>ScriptSys: remove(component)
|
||||
ScriptSys->>ScriptSys: erase_unordered + 修正 id_mapping
|
||||
end
|
||||
|
||||
rect rgb(255, 246, 236)
|
||||
Note over Obj,Trans: 主线三:脚本写变换缓存并帧末提交
|
||||
ScriptSys->>Obj: update(dt)
|
||||
Obj->>Cache: set_position/rotation/scale...
|
||||
Cache-->>Obj: flags + value 已缓存
|
||||
ScriptSys->>Trans: transform::update(cache, count)
|
||||
Trans-->>ScriptSys: 写入完成
|
||||
ScriptSys->>Cache: clear()
|
||||
end
|
||||
```
|
||||
48
docs/架构分析/Transform组件分析.md
Normal file
48
docs/架构分析/Transform组件分析.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Transform 组件分析
|
||||
|
||||
## 组件职责
|
||||
|
||||
`Transform.*` 负责实体空间变换数据:
|
||||
|
||||
- 位置、旋转、朝向、缩放存储;
|
||||
- 世界矩阵与逆矩阵计算;
|
||||
- 脏标记记录与批量更新入口。
|
||||
|
||||
## 数据布局
|
||||
|
||||
`Transform.cpp` 采用并行数组(SoA):
|
||||
|
||||
- `positions / rotations / orientations / scales`
|
||||
- `to_world / inv_world`
|
||||
- `has_transform`:矩阵缓存有效位
|
||||
- `changes_from_previous_frame`:逐帧脏标记
|
||||
|
||||
## 创建与绑定
|
||||
|
||||
`transform::create(init_info, entity)` 使用 `entity_id` 的索引位定位槽位:
|
||||
|
||||
- 若槽位已存在,覆写其数据并标记脏;
|
||||
- 若槽位不存在,扩容所有并行数组;
|
||||
- 返回 `transform::component{ transform_id{ entity.get_id() } }`。
|
||||
|
||||
这说明 Transform 不维护独立 free list,而是复用实体索引空间。
|
||||
|
||||
## 读写策略
|
||||
|
||||
- 写接口(`set_rotation/position/scale`)只改数据与脏标记;
|
||||
- `get_transform_matrices` 在读取时按需计算矩阵;
|
||||
- 通过 `has_transform` 避免重复矩阵重算。
|
||||
|
||||
## 批量更新机制
|
||||
|
||||
`transform::update(cache, count)` 接收 `component_cache` 数组:
|
||||
|
||||
- 每条 cache 带 `flags` 指示写哪些字段;
|
||||
- 逐条落地到内部并行数组;
|
||||
- 适配脚本系统“先缓存,后统一提交”的帧内流程。
|
||||
|
||||
## 设计特点
|
||||
|
||||
- 读取快:索引直达;
|
||||
- 可批处理:cache + flags;
|
||||
- 低耦合:外部通过句柄和 cache 交互,不直接暴露内部数组。
|
||||
132
docs/架构分析/项目约定规范.md
Normal file
132
docs/架构分析/项目约定规范.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# 项目约定规范(接口层 / 实现层)
|
||||
|
||||
what every programmer should know about memory
|
||||
|
||||
本文档用于统一本项目在 `EngineAPI`、`Components`、`Platform`、`Utilities` 等目录下的设计与编码约定,重点规范“接口层与实现层分离”。
|
||||
|
||||
## 1. 分层职责约定
|
||||
|
||||
### 1.1 EngineAPI 层(对外契约层)
|
||||
|
||||
- 只暴露稳定接口:强类型 ID、句柄类、对外可调用的 API。
|
||||
- 不暴露内部容器与存储细节,不暴露运行时池结构。
|
||||
- 允许声明行为,不实现复杂运行逻辑。
|
||||
- 头文件应可被上层模块直接 include,尽量减少内部依赖。
|
||||
|
||||
示例:
|
||||
- `Engine/EngineAPI/ScriptComponent.h`:仅定义 `script_id` 与 `script::component` 句柄。
|
||||
|
||||
### 1.2 Components 层(运行时实现层)
|
||||
|
||||
- 实现组件生命周期:`create/remove/update`。
|
||||
- 管理内部存储:数组、映射、空闲队列、代数校验。
|
||||
- 对外只通过 API 层句柄交互,不向外泄露内部布局。
|
||||
- 允许高性能实现细节(如 `erase_unordered`、缓存表、延迟提交)。
|
||||
|
||||
示例:
|
||||
- `Engine/Components/Script.h/.cpp`:实现脚本创建、删除、逐帧更新和注册表逻辑。
|
||||
|
||||
### 1.3 Platform 层(平台适配层)
|
||||
|
||||
- 只处理操作系统相关对象与流程(如 Win32 `HWND`、消息循环、窗口样式)。
|
||||
- 上层通过抽象类型访问,不直接依赖平台原生类型。
|
||||
- 平台特有分支必须受平台宏保护(如 `_WIN64`)。
|
||||
|
||||
### 1.4 Utilities 层(通用基础层)
|
||||
|
||||
- 提供与业务无关的数学、容器、IO、工具函数。
|
||||
- 不耦合具体业务组件,不反向依赖高层模块。
|
||||
|
||||
## 2. 文件组织约定
|
||||
|
||||
### 2.1 命名与位置
|
||||
|
||||
- 对外契约放 `EngineAPI`,运行时实现放 `Components`。
|
||||
- 头文件与实现文件成对组织:`Xxx.h` / `Xxx.cpp`。
|
||||
- 公共依赖入口统一放在模块公共头(如 `ComponentsCommon.h`)。
|
||||
|
||||
### 2.2 include 方向
|
||||
|
||||
- 允许 `Components -> EngineAPI` 依赖。
|
||||
- 禁止 `EngineAPI -> Components` 反向依赖实现细节。
|
||||
- `Utilities` 不应依赖 `Components` 或 `Platform`。
|
||||
|
||||
### 2.3 前向声明优先
|
||||
|
||||
- 能前向声明时不直接 include 重头文件。
|
||||
- 对外头文件避免引入不必要实现依赖,控制编译扇出。
|
||||
|
||||
## 3. 类型与句柄约定
|
||||
|
||||
### 3.1 强类型 ID
|
||||
|
||||
- 所有核心对象 ID 使用强类型封装(`DEFINE_TYPED_ID`)。
|
||||
- 禁止在模块边界裸传 `u32` 作为对象标识。
|
||||
- 必须通过 `is_valid` / `generation` 做有效性校验。
|
||||
|
||||
### 3.2 句柄对象
|
||||
|
||||
- 对外使用轻量句柄(如 `script::component`、`game_entity::entity`)。
|
||||
- 句柄仅保存 ID,不保存大对象数据。
|
||||
- 句柄方法可转发到实现层查询真实数据。
|
||||
|
||||
## 4. 生命周期与存储约定
|
||||
|
||||
### 4.1 创建与删除
|
||||
|
||||
- `create` 负责分配 ID、构造实例、建立映射。
|
||||
- `remove` 必须清理映射、回收槽位、更新空闲队列。
|
||||
- 必须保证删除后旧句柄不可通过代数校验。
|
||||
|
||||
### 4.2 映射一致性
|
||||
|
||||
- 使用 `id_mapping` 时必须保证:
|
||||
- `id -> 索引` 映射存在且越界受保护;
|
||||
- 交换删除后映射及时回填;
|
||||
- 无效 ID 显式写回 `invalid_id`。
|
||||
|
||||
### 4.3 批处理更新
|
||||
|
||||
- 高频写入优先使用缓存并批量提交(如脚本修改 Transform 后统一提交)。
|
||||
- 帧内缓存必须在提交后清空,避免跨帧污染。
|
||||
|
||||
## 5. 脚本系统专项约定
|
||||
|
||||
### 5.1 注册机制
|
||||
|
||||
- 脚本类型通过统一宏注册(`REGISTER_SCRIPT(TYPE)`)。
|
||||
- 注册信息至少包含:类型名哈希、工厂函数指针。
|
||||
- 编辑器模式下可额外导出脚本名列表。
|
||||
|
||||
### 5.2 工厂机制
|
||||
|
||||
- 统一使用 `script_creator` 创建脚本实例。
|
||||
- 禁止外部直接 new 并绕过组件创建流程。
|
||||
- 工厂函数签名保持稳定,避免跨模块 ABI 漂移。
|
||||
|
||||
## 6. 注释与文档约定
|
||||
|
||||
### 6.1 文件头注释
|
||||
|
||||
- 使用 `@file` + `@brief` + `@details`。
|
||||
- `@details` 需说明职责边界、数据流、关键约束。
|
||||
|
||||
### 6.2 函数注释
|
||||
|
||||
- 对核心函数标注参数、返回值、副作用。
|
||||
- 对平台参数表、结构体字段允许保留逐行行尾注释。
|
||||
- 禁止乱码注释;统一 UTF-8 编码保存。
|
||||
|
||||
## 7. 可维护性与扩展性约定
|
||||
|
||||
- 先保证接口稳定,再演进实现细节。
|
||||
- 扩展新组件时,优先复用现有 ID/句柄/生命周期模式。
|
||||
- 变更跨层关系时必须先更新本规范文档与对应模块分析文档。
|
||||
|
||||
## 8. 变更检查清单(提交前)
|
||||
|
||||
- 是否保持 `EngineAPI` 与 `Components` 的边界不反转。
|
||||
- 是否新增了不必要 include 或扩大了依赖扇出。
|
||||
- 是否破坏了 ID 代数校验与映射一致性。
|
||||
- 是否补齐了注释并确保无乱码。
|
||||
- 是否与现有目录结构与命名风格一致。
|
||||
Reference in New Issue
Block a user