refactor(editor): Complete architecture refactoring
- SceneManager: remove singleton, use dependency injection via EditorContext - SelectionManager: already interface-based via ISelectionManager - Panel: now receives IEditorContext for accessing managers - HierarchyPanel: migrated to use IEditorContext instead of singletons - Add ISceneManager interface and SceneManagerImpl - EditorContextImpl: holds all editor subsystems Architecture now follows dependency injection pattern: Application -> EditorContext -> SceneManager/SelectionManager EditorLayer -> Panels (receive context via SetContext) All Manager singletons removed: EditorSceneManager::Get(), SelectionManager::Get()
This commit is contained in:
466
.trae/documents/RHI模块审查报告.md
Normal file
466
.trae/documents/RHI模块审查报告.md
Normal file
@@ -0,0 +1,466 @@
|
||||
# RHI 模块审查报告
|
||||
|
||||
## 审查范围
|
||||
|
||||
基于设计文档 `RHI模块总览.md` 中定义的设计理念,对当前 RHI 模块的设计和实现进行全面审查。
|
||||
|
||||
---
|
||||
|
||||
## 一、设计问题
|
||||
|
||||
### 1. 接口一致性问题
|
||||
|
||||
#### 1.1 RHIResource 基类接口过于简单
|
||||
|
||||
**当前实现** ([RHIResource.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIResource.h)):
|
||||
```cpp
|
||||
class RHIResource {
|
||||
public:
|
||||
virtual ~RHIResource() = default;
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
virtual ResourceStates GetState() const = 0;
|
||||
virtual void SetState(ResourceStates state) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 缺少资源命名接口 (`SetName`/`GetName`)
|
||||
- 缺少资源类型查询接口
|
||||
- 缺少资源大小查询接口
|
||||
|
||||
**影响**: RHITexture 自行添加了 `SetName`/`GetName`/`GetWidth` 等方法,导致接口不统一。
|
||||
|
||||
**建议**: 扩展 RHIResource 基类接口:
|
||||
```cpp
|
||||
class RHIResource {
|
||||
public:
|
||||
virtual ~RHIResource() = default;
|
||||
|
||||
// 现有接口
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
virtual ResourceStates GetState() const = 0;
|
||||
virtual void SetState(ResourceStates state) = 0;
|
||||
|
||||
// 建议新增
|
||||
virtual void SetName(const char* name) = 0;
|
||||
virtual const char* GetName() const = 0;
|
||||
virtual ResourceType GetResourceType() const = 0;
|
||||
virtual uint64_t GetGpuVirtualAddress() const = 0; // 对于 Buffer
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 1.2 资源生命周期管理不一致
|
||||
|
||||
**当前问题**:
|
||||
- 所有资源使用 `Shutdown()` + `delete` 的手动管理方式
|
||||
- 容易导致资源泄漏或重复释放
|
||||
- 与引擎 Core 模块的智能指针体系 (`Ref`, `UniqueRef`) 不统一
|
||||
|
||||
**建议**:
|
||||
1. 考虑使用引用计数管理 RHI 资源
|
||||
2. 或者提供 RAII 包装器类
|
||||
3. 至少应该提供统一的资源销毁接口(如 `RHIDevice::DestroyResource()`)
|
||||
|
||||
---
|
||||
|
||||
### 2. 描述符系统设计问题
|
||||
|
||||
#### 2.1 描述符接口偏向 D3D12
|
||||
|
||||
**当前实现** ([RHIDescriptorPool.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIDescriptorPool.h)):
|
||||
```cpp
|
||||
struct DescriptorPoolDesc {
|
||||
uint32_t maxDescriptorCount; // D3D12 风格
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- OpenGL 没有原生的描述符池概念
|
||||
- OpenGL 后端需要完全模拟,可能效率低下
|
||||
- 缺少 DescriptorSetLayout 的概念
|
||||
|
||||
**建议**: 参考 Vulkan 的描述符系统设计:
|
||||
```cpp
|
||||
// 1. 先创建 DescriptorSetLayout
|
||||
struct DescriptorSetLayoutDesc {
|
||||
std::vector<DescriptorBinding> bindings;
|
||||
};
|
||||
class RHIDescriptorSetLayout { ... };
|
||||
|
||||
// 2. 从 Layout 创建 Pool
|
||||
struct DescriptorPoolDesc {
|
||||
std::vector<DescriptorPoolSize> poolSizes; // 按类型指定数量
|
||||
uint32_t maxSets;
|
||||
};
|
||||
|
||||
// 3. 从 Pool 分配 Set
|
||||
class RHIDescriptorSet {
|
||||
virtual void Update(uint32_t binding, RHIResource* resource) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2.2 缺少描述符集绑定机制
|
||||
|
||||
**当前问题**:
|
||||
- RHICommandList 直接绑定资源 (`SetVertexBuffer`, `SetTexture`)
|
||||
- 缺少批量绑定描述符集的能力
|
||||
|
||||
**建议**: 添加描述符集绑定接口:
|
||||
```cpp
|
||||
virtual void BindDescriptorSet(
|
||||
uint32_t setIndex,
|
||||
RHIDescriptorSet* descriptorSet,
|
||||
RHIPipelineLayout* layout
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 命令列表设计问题
|
||||
|
||||
#### 3.1 命令列表生命周期管理
|
||||
|
||||
**当前问题**:
|
||||
- 缺少命令列表池化管理
|
||||
- 每帧可能需要重新创建命令列表
|
||||
|
||||
**建议**: 添加命令分配器概念:
|
||||
```cpp
|
||||
class RHICommandAllocator {
|
||||
public:
|
||||
virtual void Reset() = 0; // 重用命令列表内存
|
||||
};
|
||||
|
||||
// Device 接口
|
||||
virtual RHICommandAllocator* CreateCommandAllocator(
|
||||
CommandListType type
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3.2 屏障接口不够清晰
|
||||
|
||||
**当前实现**:
|
||||
```cpp
|
||||
virtual void ResourceBarrier(
|
||||
uint32_t numBarriers,
|
||||
ResourceBarrierDesc* barriers
|
||||
) = 0;
|
||||
|
||||
virtual void ExecuteBarriers() = 0; // 这个方法名容易误解
|
||||
```
|
||||
|
||||
**问题**: `ExecuteBarriers` 方法名暗示有延迟执行,但实际行为不明确。
|
||||
|
||||
**建议**:
|
||||
- 明确屏障是立即执行还是批量执行
|
||||
- 考虑添加 `FlushBarriers()` 方法名替代 `ExecuteBarriers()`
|
||||
|
||||
---
|
||||
|
||||
### 4. 着色器系统问题
|
||||
|
||||
#### 4.1 编译接口职责混乱
|
||||
|
||||
**当前实现** ([RHIShader.h](file:///d:/Xuanchi/Main/XCEngine/engine/include/XCEngine/RHI/RHIShader.h)):
|
||||
```cpp
|
||||
class RHIShader {
|
||||
public:
|
||||
// RHIShader 自己有编译方法
|
||||
virtual bool CompileFromFile(...) = 0;
|
||||
virtual bool Compile(...) = 0;
|
||||
};
|
||||
|
||||
// RHIDevice 也有编译方法
|
||||
virtual RHIShader* CompileShader(const ShaderCompileDesc& desc) = 0;
|
||||
```
|
||||
|
||||
**问题**: 编译职责分散,用户不知道该用哪个接口。
|
||||
|
||||
**建议**: 统一着色器创建流程:
|
||||
```cpp
|
||||
// 方案1: 只在 Device 中创建
|
||||
class RHIDevice {
|
||||
virtual RHIShader* CreateShader(const ShaderDesc& desc) = 0;
|
||||
};
|
||||
|
||||
// 方案2: 使用 ShaderCompiler 工具类
|
||||
class RHIShaderCompiler {
|
||||
virtual RHIShader* CompileFromFile(...) = 0;
|
||||
virtual RHIShader* CompileFromSource(...) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4.2 缺少着色器反射接口
|
||||
|
||||
**当前问题**:
|
||||
- 只有 `GetUniformInfos()` 方法
|
||||
- 缺少完整的着色器反射(输入/输出语义、资源绑定等)
|
||||
|
||||
**建议**: 添加完整的着色器反射接口:
|
||||
```cpp
|
||||
struct ShaderReflection {
|
||||
std::vector<VertexInputAttribute> vertexInputs;
|
||||
std::vector<FragmentOutput> fragmentOutputs;
|
||||
std::vector<ResourceBinding> resourceBindings;
|
||||
std::vector<PushConstantRange> pushConstantRanges;
|
||||
};
|
||||
|
||||
virtual ShaderReflection GetReflection() const = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 管线状态对象问题
|
||||
|
||||
#### 5.1 缺少管线缓存
|
||||
|
||||
**当前问题**: 每次创建 PSO 都需要完整编译,没有缓存机制。
|
||||
|
||||
**建议**: 添加管线缓存接口:
|
||||
```cpp
|
||||
class RHIPipelineCache {
|
||||
public:
|
||||
virtual void Save(const char* filePath) = 0;
|
||||
virtual void Load(const char* filePath) = 0;
|
||||
virtual size_t GetDataSize() const = 0;
|
||||
virtual void* GetData() const = 0;
|
||||
};
|
||||
|
||||
// Device 接口
|
||||
virtual RHIPipelineCache* CreatePipelineCache() = 0;
|
||||
virtual RHIPipelineState* CreatePipelineState(
|
||||
const PipelineStateDesc& desc,
|
||||
RHIPipelineCache* cache = nullptr
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 缺失的关键功能
|
||||
|
||||
#### 6.1 缺少资源别名 (Aliasing) 支持
|
||||
|
||||
**设计文档提到**: "求同存异"
|
||||
|
||||
**问题**: D3D12 和 Vulkan 都支持资源别名(多个资源共享同一块内存),但当前 RHI 没有暴露。
|
||||
|
||||
**建议**: 添加资源别名支持:
|
||||
```cpp
|
||||
struct ResourceAliasingDesc {
|
||||
RHIResource* overlappingResources;
|
||||
uint32_t resourceCount;
|
||||
};
|
||||
|
||||
virtual void AliasingResources(
|
||||
const ResourceAliasingDesc& desc
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6.2 缺少多队列支持
|
||||
|
||||
**当前问题**:
|
||||
- RHICommandQueue 只支持单一类型
|
||||
- 缺少多队列同步机制
|
||||
|
||||
**建议**: 添加跨队列同步支持:
|
||||
```cpp
|
||||
struct QueueSyncDesc {
|
||||
RHICommandQueue* waitQueue;
|
||||
RHIFence* fence;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
virtual void QueueSubmit(
|
||||
const QueueSubmitDesc& desc,
|
||||
const QueueSyncDesc* waitSyncs = nullptr,
|
||||
uint32_t waitSyncCount = 0,
|
||||
const QueueSyncDesc* signalSyncs = nullptr,
|
||||
uint32_t signalSyncCount = 0
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6.3 缺少 RenderGraph 支持
|
||||
|
||||
**设计文档提到**: "渲染管线层(SRP/Render Graph)"
|
||||
|
||||
**问题**: 当前 RHI 接口没有为 RenderGraph 提供必要的支持:
|
||||
- 缺少资源传递机制
|
||||
- 缺少自动屏障生成
|
||||
- 缺少资源生命周期跟踪
|
||||
|
||||
**建议**: 添加 RenderGraph 友好的接口:
|
||||
```cpp
|
||||
struct ResourceTransition {
|
||||
RHIResource* resource;
|
||||
ResourceStates fromState;
|
||||
ResourceStates toState;
|
||||
uint32_t queueFamily; // 支持跨队列传递
|
||||
};
|
||||
|
||||
virtual void TransitionResources(
|
||||
const ResourceTransition* transitions,
|
||||
uint32_t count
|
||||
) = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、实现问题
|
||||
|
||||
### 1. OpenGL 后端状态管理
|
||||
|
||||
**问题**: OpenGL 是状态机模型,需要仔细管理状态。
|
||||
|
||||
**建议检查点**:
|
||||
- [ ] 是否正确跟踪当前绑定的 VAO/VBO/纹理
|
||||
- [ ] 是否正确跟踪当前激活的着色器程序
|
||||
- [ ] 是否正确跟踪混合/深度/模板状态
|
||||
- [ ] 是否有不必要的冗余状态切换
|
||||
|
||||
**建议**: 实现状态跟踪器:
|
||||
```cpp
|
||||
class OpenGLStateTracker {
|
||||
public:
|
||||
void BindTexture(uint32_t unit, uint32_t texture);
|
||||
void BindVertexArray(uint32_t vao);
|
||||
void UseProgram(uint32_t program);
|
||||
// ... 避免重复绑定
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 资源状态跟踪不一致
|
||||
|
||||
**问题**:
|
||||
- D3D12 需要精确的资源状态跟踪
|
||||
- OpenGL 没有显式的资源状态概念
|
||||
- 当前实现在 OpenGL 后端如何处理状态?
|
||||
|
||||
**建议**:
|
||||
- OpenGL 后端可以忽略资源状态(或仅用于调试)
|
||||
- 或者实现软件状态跟踪用于验证
|
||||
|
||||
---
|
||||
|
||||
### 3. 内存管理缺失
|
||||
|
||||
**问题**:
|
||||
- 缺少显式的 GPU 内存分配接口
|
||||
- D3D12 有 Heap 概念,OpenGL 没有
|
||||
|
||||
**建议**: 添加内存分配抽象:
|
||||
```cpp
|
||||
struct HeapDesc {
|
||||
HeapType type; // Default, Upload, Readback
|
||||
uint64_t size;
|
||||
HeapFlags flags;
|
||||
};
|
||||
|
||||
class RHIHeap {
|
||||
public:
|
||||
virtual void* Map() = 0;
|
||||
virtual void Unmap() = 0;
|
||||
};
|
||||
|
||||
// 资源创建时指定堆
|
||||
struct BufferDesc {
|
||||
// ...
|
||||
RHIHeap* heap; // 可选
|
||||
uint64_t heapOffset; // 可选
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 错误处理机制
|
||||
|
||||
**当前问题**:
|
||||
- 大部分方法返回 `void` 或 `bool`
|
||||
- 缺少详细的错误信息
|
||||
|
||||
**建议**: 添加结果类型:
|
||||
```cpp
|
||||
template<typename T>
|
||||
struct RHIResult {
|
||||
T value;
|
||||
RHIErrorCode error;
|
||||
const char* errorMessage;
|
||||
};
|
||||
|
||||
// 使用示例
|
||||
RHIResult<RHITexture*> result = device->CreateTexture(desc);
|
||||
if (result.error != RHIErrorCode::Success) {
|
||||
LogError(result.errorMessage);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、与设计理念的对比
|
||||
|
||||
### 设计理念 vs 实际实现
|
||||
|
||||
| 设计理念 | 设计文档要求 | 当前实现状态 | 问题 |
|
||||
|---------|------------|------------|------|
|
||||
| **求同存异** | 提取 API 共性 | ✅ 部分实现 | 描述符系统偏向 D3D12 |
|
||||
| **分层抽象** | 清晰的层级结构 | ✅ 已实现 | 层级清晰 |
|
||||
| **特性降级** | 能力检测和替代方案 | ⚠️ 部分实现 | RHICapabilities 存在但使用不明确 |
|
||||
| **底层逃逸** | 允许访问原生 API | ✅ 已实现 | GetNativeHandle() 接口存在 |
|
||||
|
||||
---
|
||||
|
||||
## 四、优先级建议
|
||||
|
||||
### 高优先级(影响功能正确性)
|
||||
|
||||
1. **统一资源生命周期管理** - 防止内存泄漏
|
||||
2. **完善错误处理机制** - 便于调试
|
||||
3. **OpenGL 状态跟踪** - 避免渲染错误
|
||||
|
||||
### 中优先级(影响性能和可维护性)
|
||||
|
||||
4. **重构描述符系统** - 提高 OpenGL 效率
|
||||
5. **统一着色器编译接口** - 提高易用性
|
||||
6. **添加命令分配器** - 提高性能
|
||||
|
||||
### 低优先级(增强功能)
|
||||
|
||||
7. **添加管线缓存** - 加速启动
|
||||
8. **添加资源别名支持** - 优化内存
|
||||
9. **RenderGraph 支持** - 高级功能
|
||||
|
||||
---
|
||||
|
||||
## 五、总结
|
||||
|
||||
### 优点
|
||||
|
||||
1. ✅ 分层架构清晰,符合设计文档
|
||||
2. ✅ 核心抽象接口基本完整
|
||||
3. ✅ D3D12 后端实现较为完善
|
||||
4. ✅ 测试覆盖较好
|
||||
|
||||
### 主要问题
|
||||
|
||||
1. ❌ 描述符系统设计偏向 D3D12,OpenGL 后端效率可能受影响
|
||||
2. ❌ 资源生命周期管理不一致,存在泄漏风险
|
||||
3. ❌ 着色器编译接口职责混乱
|
||||
4. ⚠️ 缺少部分高级功能(资源别名、多队列同步等)
|
||||
|
||||
### 建议
|
||||
|
||||
建议按照优先级逐步改进,首先解决高优先级问题,确保引擎的稳定性和正确性。
|
||||
335
.trae/documents/XCEngine项目熟悉计划.md
Normal file
335
.trae/documents/XCEngine项目熟悉计划.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# XCEngine 项目熟悉计划
|
||||
|
||||
## 项目概述
|
||||
|
||||
XCEngine 是一个正在开发中的模块化 C++ 游戏引擎,采用 RHI(Render Hardware Interface)抽象层设计,支持 DirectX 12 和 OpenGL 4.6+ 多种渲染 API。
|
||||
|
||||
### 技术栈
|
||||
- **语言**: C++17
|
||||
- **渲染 API**: DirectX 12, OpenGL 4.6+
|
||||
- **构建系统**: CMake 3.15+
|
||||
- **测试框架**: Google Test
|
||||
- **UI**: ImGui(用于编辑器)
|
||||
|
||||
---
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
XCEngine/
|
||||
├── engine/ # 核心引擎库(静态库)
|
||||
│ ├── include/XCEngine/ # 头文件
|
||||
│ │ ├── Audio/ # 音频系统
|
||||
│ │ ├── Components/ # 组件系统
|
||||
│ │ ├── Core/ # 核心基础模块
|
||||
│ │ ├── Debug/ # 调试与日志
|
||||
│ │ ├── Input/ # 输入模块
|
||||
│ │ ├── Memory/ # 内存管理
|
||||
│ │ ├── Platform/ # 平台抽象层
|
||||
│ │ ├── RHI/ # 渲染硬件接口
|
||||
│ │ ├── Resources/ # 资源管理
|
||||
│ │ ├── Scene/ # 场景管理
|
||||
│ │ └── Threading/ # 线程系统
|
||||
│ ├── src/ # 实现文件
|
||||
│ └── third_party/ # 第三方库
|
||||
│
|
||||
├── editor/ # 编辑器 UI 应用程序
|
||||
│ └── src/ # 编辑器源代码
|
||||
│
|
||||
├── tests/ # 单元测试
|
||||
│ ├── math/ # 数学库测试
|
||||
│ ├── containers/ # 容器测试
|
||||
│ ├── memory/ # 内存管理测试
|
||||
│ ├── threading/ # 线程模块测试
|
||||
│ ├── debug/ # 调试模块测试
|
||||
│ ├── core/ # 核心模块测试
|
||||
│ ├── Scene/ # 场景测试
|
||||
│ ├── Resources/ # 资源管理测试
|
||||
│ ├── Input/ # 输入模块测试
|
||||
│ └── RHI/ # RHI 抽象层测试
|
||||
│
|
||||
├── mvs/ # 示例程序
|
||||
│ ├── D3D12/ # DirectX 12 渲染示例
|
||||
│ ├── OpenGL/ # OpenGL 渲染示例
|
||||
│ ├── VolumeRenderer/ # 体积渲染器
|
||||
│ ├── RenderDoc/ # RenderDoc 集成示例
|
||||
│ └── ui/ # 编辑器 UI 示例
|
||||
│
|
||||
└── docs/ # 文档
|
||||
├── api/ # API 文档
|
||||
└── plan/ # 开发计划与架构设计
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心模块详解
|
||||
|
||||
### 1. Core(核心基础)
|
||||
|
||||
位置: `engine/include/XCEngine/Core/`
|
||||
|
||||
| 子模块 | 说明 |
|
||||
|--------|------|
|
||||
| Types.h | 基础类型别名(int8, uint32 等) |
|
||||
| RefCounted.h | 引用计数基类 |
|
||||
| SmartPtr.h | 智能指针(Ref, UniqueRef) |
|
||||
| Event.h | 事件系统,支持订阅/发布模式 |
|
||||
| FileWriter.h | 文件写入工具 |
|
||||
| Layer.h/LayerStack.h | 层级与层级栈管理 |
|
||||
|
||||
**Core/Math 数学库**:
|
||||
- Vector2/3/4 - 向量运算
|
||||
- Matrix3/4 - 矩阵运算
|
||||
- Quaternion - 四元数
|
||||
- Transform - 变换
|
||||
- Color - 颜色
|
||||
- Rect - 矩形
|
||||
- Sphere/Box/Plane/Ray - 几何体
|
||||
- AABB/Bounds/Frustum - 包围体
|
||||
|
||||
**Core/Containers 容器**:
|
||||
- Array.h - 动态数组
|
||||
- String.h - 字符串类
|
||||
- HashMap.h - 哈希表
|
||||
|
||||
**Core/Asset 资源系统核心**:
|
||||
- IResource.h - 资源接口基类
|
||||
- ResourceTypes.h - 资源类型定义
|
||||
- ResourceHandle.h - 资源句柄
|
||||
- ResourceManager.h - 全局资源管理器
|
||||
- ResourceCache.h - 资源缓存
|
||||
- AsyncLoader.h - 异步资源加载器
|
||||
|
||||
**Core/IO 资源 IO 系统**:
|
||||
- IResourceLoader.h - 资源加载器接口
|
||||
- ResourceFileSystem.h - 资源文件系统
|
||||
- FileArchive.h - 文件归档
|
||||
|
||||
### 2. RHI(渲染硬件接口)
|
||||
|
||||
位置: `engine/include/XCEngine/RHI/`
|
||||
|
||||
这是引擎的核心抽象层,借鉴 Unity SRP 架构设计。
|
||||
|
||||
**设计原则**:
|
||||
- 求同存异:提取各 API 共同特性
|
||||
- 分层抽象:核心抽象层 → 后端实现层 → 平台适配层
|
||||
- 特性降级:后端不支持的特性优雅降级
|
||||
|
||||
**抽象接口**:
|
||||
| 接口 | 说明 |
|
||||
|------|------|
|
||||
| RHIDevice | 渲染设备抽象 |
|
||||
| RHICommandQueue | 命令队列抽象 |
|
||||
| RHICommandList | 命令列表抽象 |
|
||||
| RHISwapChain | 交换链抽象 |
|
||||
| RHIPipelineState | 渲染管线状态抽象 |
|
||||
| RHIBuffer/RHITexture | 资源抽象 |
|
||||
| RHIShader | 着色器抽象 |
|
||||
| RHISampler | 采样器抽象 |
|
||||
| RHIFence | 同步栅栏抽象 |
|
||||
| RHIDescriptorPool | 描述符池抽象 |
|
||||
| RHIRenderPass | 渲染通道抽象 |
|
||||
| RHIFramebuffer | 帧缓冲抽象 |
|
||||
|
||||
**后端实现**:
|
||||
- `RHI/D3D12/` - DirectX 12 后端
|
||||
- `RHI/OpenGL/` - OpenGL 4.6+ 后端
|
||||
|
||||
### 3. Components(组件系统)
|
||||
|
||||
位置: `engine/include/XCEngine/Components/`
|
||||
|
||||
采用类似 Unity 的组件架构:
|
||||
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| Component.h | 组件基类,提供生命周期管理 |
|
||||
| GameObject.h | 游戏对象,支持层级结构 |
|
||||
| TransformComponent.h | 变换组件 |
|
||||
| AudioSourceComponent.h | 音频源组件 |
|
||||
| AudioListenerComponent.h | 音频监听器组件 |
|
||||
|
||||
### 4. Scene(场景管理)
|
||||
|
||||
位置: `engine/include/XCEngine/Scene/`
|
||||
|
||||
- **Scene.h** - 场景类,管理场景内的所有游戏对象
|
||||
- **SceneManager.h** - 场景管理器,处理场景切换
|
||||
|
||||
### 5. Audio(音频系统)
|
||||
|
||||
位置: `engine/include/XCEngine/Audio/`
|
||||
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| AudioSystem.h | 音频系统主类 |
|
||||
| AudioMixer.h | 音频混音器 |
|
||||
| HRTF.h | 头部相关传输函数,空间音频 |
|
||||
| FFTFilter.h | 快速傅里叶变换滤波器 |
|
||||
| Reverbation.h | 混响效果 |
|
||||
| Equalizer.h | 音频均衡器 |
|
||||
| IAudioBackend.h | 音频后端接口 |
|
||||
| WindowsAudioBackend.h | Windows WASAPI 后端 |
|
||||
|
||||
### 6. Memory(内存管理)
|
||||
|
||||
位置: `engine/include/XCEngine/Memory/`
|
||||
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| Allocator.h | 内存分配器接口 |
|
||||
| LinearAllocator.h | 线性分配器,适合帧分配 |
|
||||
| PoolAllocator.h | 内存池分配器,适合固定大小对象 |
|
||||
| ProxyAllocator.h | 代理分配器,跟踪内存使用统计 |
|
||||
| MemoryManager.h | 全局内存管理器 |
|
||||
|
||||
### 7. Threading(线程系统)
|
||||
|
||||
位置: `engine/include/XCEngine/Threading/`
|
||||
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| Thread.h | 线程封装类 |
|
||||
| Mutex.h | 互斥锁 |
|
||||
| SpinLock.h | 自旋锁 |
|
||||
| ReadWriteLock.h | 读写锁 |
|
||||
| TaskSystem.h | 多线程任务系统 |
|
||||
| Task.h/TaskGroup.h | 任务和任务组管理 |
|
||||
|
||||
### 8. Debug(调试与日志)
|
||||
|
||||
位置: `engine/include/XCEngine/Debug/`
|
||||
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| Logger.h | 分级日志系统 |
|
||||
| ConsoleLogSink.h | 控制台日志输出 |
|
||||
| FileLogSink.h | 文件日志输出 |
|
||||
| Profiler.h | 性能分析工具 |
|
||||
| RenderDocCapture.h | RenderDoc 帧捕获集成 |
|
||||
|
||||
### 9. Resources(资源管理)
|
||||
|
||||
位置: `engine/include/XCEngine/Resources/`
|
||||
|
||||
按类型分目录管理:
|
||||
- **Texture/** - 纹理资源
|
||||
- **Mesh/** - 网格资源
|
||||
- **Shader/** - 着色器资源
|
||||
- **Material/** - 材质资源
|
||||
- **AudioClip/** - 音频资源
|
||||
|
||||
### 10. Platform(平台抽象层)
|
||||
|
||||
位置: `engine/include/XCEngine/Platform/`
|
||||
|
||||
跨平台抽象接口:
|
||||
- IPlatform - 平台接口
|
||||
- IWindow - 窗口接口
|
||||
- IFileSystem - 文件系统接口
|
||||
- IClock - 时钟接口
|
||||
- Windows/ - Windows 平台实现
|
||||
|
||||
### 11. Input(输入模块)
|
||||
|
||||
位置: `engine/include/XCEngine/Input/`
|
||||
|
||||
- InputManager - 输入管理器
|
||||
- InputModule - 输入模块基类
|
||||
- InputEvent - 输入事件定义
|
||||
- InputAxis - 输入轴配置
|
||||
|
||||
---
|
||||
|
||||
## 渲染架构
|
||||
|
||||
### 渲染流程
|
||||
|
||||
```
|
||||
Scene → CullingSystem → RenderQueue → Renderer → GPU
|
||||
↓ ↓
|
||||
CullingResults CommandList
|
||||
```
|
||||
|
||||
### 关键概念(借鉴 Unity SRP)
|
||||
|
||||
1. **CullingResults** - 剔除结果
|
||||
2. **RenderPipeline** - 渲染管线
|
||||
3. **RenderQueue** - 渲染队列
|
||||
4. **ScriptableRenderContext** - 渲染上下文
|
||||
5. **LightManager** - 光照管理
|
||||
6. **ShadowAtlas** - 阴影图集
|
||||
|
||||
---
|
||||
|
||||
## 构建与测试
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake .. -A x64
|
||||
cmake --build . --config Debug
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
cd build/tests
|
||||
ctest -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 学习路径建议
|
||||
|
||||
### 第一阶段:核心基础
|
||||
1. 阅读 `README.md` 了解项目概述
|
||||
2. 学习 Core 模块(Types, Event, SmartPtr)
|
||||
3. 学习 Math 数学库
|
||||
4. 学习 Containers 容器
|
||||
|
||||
### 第二阶段:系统模块
|
||||
1. 学习 Memory 内存管理
|
||||
2. 学习 Threading 线程系统
|
||||
3. 学习 Debug 日志系统
|
||||
4. 学习 Platform 平台抽象
|
||||
|
||||
### 第三阶段:渲染系统
|
||||
1. 学习 RHI 抽象层设计
|
||||
2. 学习 D3D12 后端实现
|
||||
3. 学习 OpenGL 后端实现
|
||||
4. 学习渲染管线架构
|
||||
|
||||
### 第四阶段:高级功能
|
||||
1. 学习 Scene 场景管理
|
||||
2. 学习 Components 组件系统
|
||||
3. 学习 Resources 资源管理
|
||||
4. 学习 Audio 音频系统
|
||||
|
||||
### 第五阶段:实践
|
||||
1. 运行 mvs 中的示例程序
|
||||
2. 阅读测试用例理解 API 用法
|
||||
3. 尝试修改或扩展现有功能
|
||||
|
||||
---
|
||||
|
||||
## 关键文件索引
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| README.md | 项目说明文档 |
|
||||
| CMakeLists.txt | 根构建配置 |
|
||||
| engine/CMakeLists.txt | 引擎库构建配置 |
|
||||
| docs/plan/XCEngine渲染引擎架构设计.md | 渲染架构详细设计 |
|
||||
| docs/api/main.md | API 文档入口 |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 项目目前仅支持 Windows 平台
|
||||
2. 需要 Visual Studio 2019 或更高版本
|
||||
3. D3D12 后端需要 Windows 10/11
|
||||
4. OpenGL 后端需要 OpenGL 4.6+ 支持
|
||||
189
D3D12_RHI_Test_Issue.md
Normal file
189
D3D12_RHI_Test_Issue.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# D3D12 RHI 测试失败问题报告
|
||||
|
||||
## 问题描述
|
||||
|
||||
5个D3D12测试在调用 `CreateCommandList` 时失败,错误码 `DXGI_ERROR_NOT_CURRENTLY_AVAILABLE` (0x887A0005)
|
||||
|
||||
## 失败的测试
|
||||
|
||||
| 测试名称 | 错误位置 |
|
||||
|---------|---------|
|
||||
| `CommandList_ClearRenderTarget_WithRealView` | `CreateCommandList` 返回 nullptr |
|
||||
| `CommandList_ClearDepthStencil_WithRealView` | `CreateTexture` 返回 nullptr (D24_S8_UInt格式) |
|
||||
| `CommandList_SetRenderTargets_WithRealViews` | `CreateCommandList` 返回 nullptr |
|
||||
| `CommandList_BeginEndRenderPass_Basic` | `CreateCommandList` 返回 nullptr |
|
||||
| `CommandList_BeginEndRenderPass_WithClear` | `CreateCommandList` 返回 nullptr |
|
||||
|
||||
## 错误码分析
|
||||
|
||||
```
|
||||
DXGI_ERROR_NOT_CURRENTLY_AVAILABLE (0x887A0005)
|
||||
```
|
||||
|
||||
根据 Microsoft 文档,此错误表示:
|
||||
> "The resource or operation is not available at the current time. This can be returned when a command is submitted to a queue that is not in a state to process that command."
|
||||
|
||||
## 测试代码(最小复现)
|
||||
|
||||
```cpp
|
||||
TEST_P(RHITestFixture, CommandList_ClearRenderTarget_WithRealView) {
|
||||
// 1. 创建纹理 - 成功
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr); // ✅ 通过
|
||||
|
||||
// 2. 创建RTV - 成功
|
||||
RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
|
||||
ASSERT_NE(rtv, nullptr); // ✅ 通过
|
||||
|
||||
// 3. 创建命令列表 - 失败
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr); // ❌ 失败!cmdList == nullptr
|
||||
}
|
||||
```
|
||||
|
||||
## 调试日志
|
||||
|
||||
```
|
||||
CreateTexture: start
|
||||
CreateRenderTargetView: start
|
||||
CreateCommandList: start
|
||||
m_commandQueue=00000180F8F722E0
|
||||
m_device=00000180F22B5B30
|
||||
CreateCommandAllocator hr=887A0005
|
||||
CreateCommandList: allocator init failed
|
||||
```
|
||||
|
||||
## 关键发现
|
||||
|
||||
### 1. 通过的测试(关键差异)
|
||||
|
||||
以下测试**通过**,它们不创建纹理或只创建SRV:
|
||||
|
||||
```cpp
|
||||
// ✅ 通过 - 只创建纹理,不创建RTV
|
||||
TEST_P(RHITestFixture, CommandList_Reset_Close) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr); // ✅ 通过
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
// ✅ 通过 - 创建纹理+SRV,但不创建RTV
|
||||
TEST_P(RHITestFixture, CommandList_TransitionBarrier_WithRealResource) {
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc); // ✅
|
||||
RHIResourceView* srv = GetDevice()->CreateShaderResourceView(texture, {}); // ✅
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); // ✅ 通过
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 失败的测试(共同点)
|
||||
|
||||
所有失败的测试都**先创建纹理,再创建RTV,然后创建命令列表**。
|
||||
|
||||
### 3. OpenGL vs D3D12
|
||||
|
||||
- **OpenGL**: 全部117个测试通过 ✅
|
||||
- **D3D12 (RHITestFixture)**: 112/117 通过,5个失败 ❌
|
||||
- **D3D12TestFixture**: 全部53个测试通过 ✅
|
||||
|
||||
## 代码分析
|
||||
|
||||
### CreateCommandList 实现
|
||||
|
||||
```cpp
|
||||
// engine/src/RHI/D3D12/D3D12Device.cpp:409
|
||||
RHICommandList* D3D12Device::CreateCommandList(const CommandListDesc& desc) {
|
||||
auto* allocator = new D3D12CommandAllocator();
|
||||
|
||||
// 失败发生在这里
|
||||
if (!allocator->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType))) {
|
||||
delete allocator;
|
||||
return nullptr; // 返回 nullptr
|
||||
}
|
||||
|
||||
auto* cmdList = new D3D12CommandList();
|
||||
if (!cmdList->Initialize(m_device.Get(), ...)) {
|
||||
delete allocator;
|
||||
delete cmdList;
|
||||
return nullptr;
|
||||
}
|
||||
return cmdList;
|
||||
}
|
||||
```
|
||||
|
||||
### CreateCommandAllocator 实现
|
||||
|
||||
```cpp
|
||||
// engine/src/RHI/D3D12/D3D12CommandAllocator.cpp:15
|
||||
bool D3D12CommandAllocator::Initialize(ID3D12Device* device, CommandQueueType type) {
|
||||
m_type = type;
|
||||
HRESULT hResult = device->CreateCommandAllocator(
|
||||
ToD3D12(type), // D3D12_COMMAND_LIST_TYPE_DIRECT
|
||||
IID_PPV_ARGS(&m_commandAllocator));
|
||||
return SUCCEEDED(hResult); // 返回 false,hr=887A0005
|
||||
}
|
||||
```
|
||||
|
||||
### CreateRenderTargetView 实现
|
||||
|
||||
```cpp
|
||||
// engine/src/RHI/D3D12/D3D12Device.cpp:455
|
||||
RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
|
||||
auto* view = new D3D12ResourceView();
|
||||
auto* d3d12Texture = static_cast<D3D12Texture*>(texture);
|
||||
ID3D12Resource* resource = d3d12Texture->GetResource();
|
||||
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
||||
rtvDesc.Format = static_cast<DXGI_FORMAT>(desc.format); // desc.format = 0 (Unknown!)
|
||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
|
||||
// 创建非 shader-visible 的 descriptor heap
|
||||
DescriptorHeapDesc heapDesc = {};
|
||||
heapDesc.descriptorCount = 1;
|
||||
heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::RTV);
|
||||
heapDesc.shaderVisible = false; // 非 shader-visible
|
||||
auto* heapPool = CreateDescriptorHeap(heapDesc);
|
||||
|
||||
view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap, 0);
|
||||
return view;
|
||||
}
|
||||
```
|
||||
|
||||
## 已排除的原因
|
||||
|
||||
1. ❌ **调试层问题** - 禁用后仍然失败
|
||||
2. ❌ **命令队列类型不匹配** - 使用相同的 `CommandQueueType::Direct`
|
||||
3. ❌ **静态全局状态污染** - 每个测试创建独立设备
|
||||
4. ❌ **内存泄漏导致descriptor heap耗尽** - 内存问题不会导致此错误码
|
||||
5. ❌ **测试顺序依赖** - 每个测试有独立的 SetUp/TearDown
|
||||
|
||||
## 疑点
|
||||
|
||||
1. 为什么 `CreateCommandAllocator` 在创建 RTV(非shader-visible descriptor heap)之后会失败?
|
||||
2. D3D12TestFixture(使用相同设备创建方式)为什么全部通过?
|
||||
3. 为什么 SRV(shader-visible descriptor heap)不会触发这个问题,只有 RTV 会?
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `engine/src/RHI/D3D12/D3D12Device.cpp` - CreateCommandList, CreateTexture, CreateRenderTargetView
|
||||
- `engine/src/RHI/D3D12/D3D12CommandAllocator.cpp` - CreateCommandAllocator
|
||||
- `engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp` - CreateDescriptorHeap
|
||||
- `engine/src/RHI/D3D12/D3D12ResourceView.cpp` - InitializeAsRenderTarget
|
||||
- `tests/RHI/unit/test_command_list.cpp` - 失败的测试
|
||||
- `tests/RHI/unit/fixtures/RHITestFixture.cpp` - 测试fixture
|
||||
|
||||
## 测试环境
|
||||
|
||||
- 平台: Windows
|
||||
- RHI后端: D3D12
|
||||
- 测试框架: Google Test
|
||||
- 编译配置: Debug
|
||||
@@ -1 +0,0 @@
|
||||
---
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "IEditorContext.h"
|
||||
#include "EventBus.h"
|
||||
#include "SelectionManagerImpl.h"
|
||||
#include "Managers/SceneManager.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
@@ -13,7 +14,9 @@ class EditorContextImpl : public IEditorContext {
|
||||
public:
|
||||
EditorContextImpl()
|
||||
: m_eventBus(std::make_unique<EventBus>())
|
||||
, m_selectionManager(std::make_unique<SelectionManagerImpl>(*m_eventBus)) {
|
||||
, m_selectionManager(std::make_unique<SelectionManagerImpl>(*m_eventBus))
|
||||
, m_sceneManager(std::make_unique<SceneManager>()) {
|
||||
m_sceneManager->SetSelectionManager(m_selectionManager.get());
|
||||
}
|
||||
|
||||
EventBus& GetEventBus() override {
|
||||
@@ -24,6 +27,14 @@ public:
|
||||
return *m_selectionManager;
|
||||
}
|
||||
|
||||
void* GetSceneManager() override {
|
||||
return m_sceneManager.get();
|
||||
}
|
||||
|
||||
SceneManager& GetSceneManagerConcrete() {
|
||||
return *m_sceneManager;
|
||||
}
|
||||
|
||||
void SetProjectPath(const std::string& path) override {
|
||||
m_projectPath = path;
|
||||
}
|
||||
@@ -35,6 +46,7 @@ public:
|
||||
private:
|
||||
std::unique_ptr<EventBus> m_eventBus;
|
||||
std::unique_ptr<SelectionManagerImpl> m_selectionManager;
|
||||
std::unique_ptr<SceneManager> m_sceneManager;
|
||||
std::string m_projectPath;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
|
||||
virtual EventBus& GetEventBus() = 0;
|
||||
virtual ISelectionManager& GetSelectionManager() = 0;
|
||||
virtual void* GetSceneManager() = 0;
|
||||
|
||||
virtual void SetProjectPath(const std::string& path) = 0;
|
||||
virtual const std::string& GetProjectPath() const = 0;
|
||||
|
||||
44
editor/src/Core/ISceneManager.h
Normal file
44
editor/src/Core/ISceneManager.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
using GameObjectID = uint64_t;
|
||||
|
||||
class IGameObject {
|
||||
public:
|
||||
virtual ~IGameObject() = default;
|
||||
virtual GameObjectID GetID() const = 0;
|
||||
virtual const std::string& GetName() const = 0;
|
||||
virtual void SetName(const std::string& name) = 0;
|
||||
virtual IGameObject* GetParent() const = 0;
|
||||
virtual void SetParent(IGameObject* parent) = 0;
|
||||
virtual size_t GetChildCount() const = 0;
|
||||
virtual IGameObject* GetChild(size_t index) const = 0;
|
||||
virtual void DetachFromParent() = 0;
|
||||
virtual const std::vector<IGameObject*>& GetChildren() const = 0;
|
||||
};
|
||||
|
||||
class ISceneManager {
|
||||
public:
|
||||
virtual ~ISceneManager() = default;
|
||||
|
||||
virtual IGameObject* CreateEntity(const std::string& name, IGameObject* parent = nullptr) = 0;
|
||||
virtual void DeleteEntity(GameObjectID id) = 0;
|
||||
virtual void RenameEntity(GameObjectID id, const std::string& newName) = 0;
|
||||
virtual void CopyEntity(GameObjectID id) = 0;
|
||||
virtual GameObjectID PasteEntity(GameObjectID parent = 0) = 0;
|
||||
virtual GameObjectID DuplicateEntity(GameObjectID id) = 0;
|
||||
virtual void MoveEntity(GameObjectID id, GameObjectID newParent) = 0;
|
||||
virtual IGameObject* GetEntity(GameObjectID id) = 0;
|
||||
virtual const std::vector<IGameObject*>& GetRootEntities() const = 0;
|
||||
virtual bool HasClipboardData() const = 0;
|
||||
virtual void CreateDemoScene() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
59
editor/src/Core/SceneManagerImpl.h
Normal file
59
editor/src/Core/SceneManagerImpl.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "ISceneManager.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class MathVector3 {
|
||||
public:
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
class MathQuaternion {
|
||||
public:
|
||||
float x, y, z, w;
|
||||
};
|
||||
|
||||
class SceneManagerImpl : public ISceneManager {
|
||||
public:
|
||||
SceneManagerImpl();
|
||||
|
||||
IGameObject* CreateEntity(const std::string& name, IGameObject* parent = nullptr) override;
|
||||
void DeleteEntity(GameObjectID id) override;
|
||||
void RenameEntity(GameObjectID id, const std::string& newName) override;
|
||||
void CopyEntity(GameObjectID id) override;
|
||||
GameObjectID PasteEntity(GameObjectID parent = 0) override;
|
||||
GameObjectID DuplicateEntity(GameObjectID id) override;
|
||||
void MoveEntity(GameObjectID id, GameObjectID newParent) override;
|
||||
IGameObject* GetEntity(GameObjectID id) override;
|
||||
const std::vector<IGameObject*>& GetRootEntities() const override;
|
||||
bool HasClipboardData() const override;
|
||||
void CreateDemoScene() override;
|
||||
|
||||
private:
|
||||
struct ClipboardData {
|
||||
std::string name;
|
||||
MathVector3 localPosition = {0, 0, 0};
|
||||
MathQuaternion localRotation = {0, 0, 0, 1};
|
||||
MathVector3 localScale = {1, 1, 1};
|
||||
std::vector<ClipboardData> children;
|
||||
};
|
||||
|
||||
ClipboardData CopyEntityRecursive(GameObjectID id);
|
||||
GameObjectID PasteEntityRecursive(const ClipboardData& data, GameObjectID parent);
|
||||
IGameObject* CastToIGameObject(GameObject* go);
|
||||
|
||||
GameObjectID m_nextId = 1;
|
||||
std::vector<IGameObject*> m_rootEntities;
|
||||
std::optional<ClipboardData> m_clipboard;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,14 @@ void EditorLayer::onAttach() {
|
||||
m_consolePanel = std::make_unique<ConsolePanel>();
|
||||
m_projectPanel = std::make_unique<ProjectPanel>();
|
||||
|
||||
m_menuBar->SetContext(m_context.get());
|
||||
m_hierarchyPanel->SetContext(m_context.get());
|
||||
m_sceneViewPanel->SetContext(m_context.get());
|
||||
m_gameViewPanel->SetContext(m_context.get());
|
||||
m_inspectorPanel->SetContext(m_context.get());
|
||||
m_consolePanel->SetContext(m_context.get());
|
||||
m_projectPanel->SetContext(m_context.get());
|
||||
|
||||
m_menuBar->OnAttach();
|
||||
m_hierarchyPanel->OnAttach();
|
||||
m_sceneViewPanel->OnAttach();
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
::XCEngine::Components::GameObject* EditorSceneManager::CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent) {
|
||||
SceneManager::SceneManager() = default;
|
||||
|
||||
void SceneManager::SetSelectionManager(ISelectionManager* selectionManager) {
|
||||
m_selectionManager = selectionManager;
|
||||
}
|
||||
|
||||
::XCEngine::Components::GameObject* SceneManager::CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent) {
|
||||
if (!m_scene) {
|
||||
m_scene = new ::XCEngine::Components::Scene("EditorScene");
|
||||
}
|
||||
@@ -20,7 +26,7 @@ namespace Editor {
|
||||
return entity;
|
||||
}
|
||||
|
||||
void EditorSceneManager::DeleteEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
void SceneManager::DeleteEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return;
|
||||
|
||||
::XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
@@ -35,15 +41,15 @@ void EditorSceneManager::DeleteEntity(::XCEngine::Components::GameObject::ID id)
|
||||
m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), entity), m_rootEntities.end());
|
||||
}
|
||||
|
||||
if (SelectionManager::Get().GetSelectedEntity() == entity) {
|
||||
SelectionManager::Get().ClearSelection();
|
||||
if (m_selectionManager && m_selectionManager->GetSelectedEntity() == entity->GetID()) {
|
||||
m_selectionManager->ClearSelection();
|
||||
}
|
||||
|
||||
m_scene->DestroyGameObject(entity);
|
||||
OnEntityDeleted.Invoke(id);
|
||||
}
|
||||
|
||||
EditorSceneManager::ClipboardData EditorSceneManager::CopyEntityRecursive(const ::XCEngine::Components::GameObject* entity) {
|
||||
SceneManager::ClipboardData SceneManager::CopyEntityRecursive(const ::XCEngine::Components::GameObject* entity) {
|
||||
ClipboardData data;
|
||||
data.name = entity->GetName();
|
||||
|
||||
@@ -60,7 +66,7 @@ EditorSceneManager::ClipboardData EditorSceneManager::CopyEntityRecursive(const
|
||||
return data;
|
||||
}
|
||||
|
||||
void EditorSceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
void SceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return;
|
||||
|
||||
::XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
@@ -69,7 +75,7 @@ void EditorSceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
m_clipboard = CopyEntityRecursive(entity);
|
||||
}
|
||||
|
||||
::XCEngine::Components::GameObject::ID EditorSceneManager::PasteEntityRecursive(const ClipboardData& data, ::XCEngine::Components::GameObject::ID parent) {
|
||||
::XCEngine::Components::GameObject::ID SceneManager::PasteEntityRecursive(const ClipboardData& data, ::XCEngine::Components::GameObject::ID parent) {
|
||||
::XCEngine::Components::GameObject* parentObj = nullptr;
|
||||
if (parent != 0) {
|
||||
parentObj = m_scene->Find(std::to_string(parent));
|
||||
@@ -94,12 +100,12 @@ void EditorSceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
return newEntity->GetID();
|
||||
}
|
||||
|
||||
::XCEngine::Components::GameObject::ID EditorSceneManager::PasteEntity(::XCEngine::Components::GameObject::ID parent) {
|
||||
::XCEngine::Components::GameObject::ID SceneManager::PasteEntity(::XCEngine::Components::GameObject::ID parent) {
|
||||
if (!m_clipboard || !m_scene) return 0;
|
||||
return PasteEntityRecursive(*m_clipboard, parent);
|
||||
}
|
||||
|
||||
::XCEngine::Components::GameObject::ID EditorSceneManager::DuplicateEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
::XCEngine::Components::GameObject::ID SceneManager::DuplicateEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return 0;
|
||||
|
||||
::XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
@@ -113,22 +119,32 @@ void EditorSceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
return PasteEntity(parentId);
|
||||
}
|
||||
|
||||
void EditorSceneManager::MoveEntity(::XCEngine::Components::GameObject::ID id, ::XCEngine::Components::GameObject::ID newParentId) {
|
||||
void SceneManager::RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName) {
|
||||
if (!m_scene) return;
|
||||
|
||||
::XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
if (!entity) return;
|
||||
::XCEngine::Components::GameObject* obj = m_scene->Find(std::to_string(id));
|
||||
if (!obj) return;
|
||||
|
||||
obj->SetName(newName);
|
||||
OnEntityChanged.Invoke(id);
|
||||
}
|
||||
|
||||
void SceneManager::MoveEntity(::XCEngine::Components::GameObject::ID id, ::XCEngine::Components::GameObject::ID newParentId) {
|
||||
if (!m_scene) return;
|
||||
|
||||
::XCEngine::Components::GameObject* obj = m_scene->Find(std::to_string(id));
|
||||
if (!obj) return;
|
||||
|
||||
::XCEngine::Components::GameObject* newParent = nullptr;
|
||||
if (newParentId != 0) {
|
||||
newParent = m_scene->Find(std::to_string(newParentId));
|
||||
}
|
||||
|
||||
entity->SetParent(newParent);
|
||||
obj->SetParent(newParent);
|
||||
OnEntityChanged.Invoke(id);
|
||||
}
|
||||
|
||||
void EditorSceneManager::CreateDemoScene() {
|
||||
void SceneManager::CreateDemoScene() {
|
||||
if (m_scene) {
|
||||
delete m_scene;
|
||||
}
|
||||
@@ -143,7 +159,6 @@ void EditorSceneManager::CreateDemoScene() {
|
||||
|
||||
::XCEngine::Components::GameObject* cube = CreateEntity("Cube", nullptr);
|
||||
cube->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
// MeshRendererComponent 需要添加到 Engine
|
||||
|
||||
::XCEngine::Components::GameObject* sphere = CreateEntity("Sphere", nullptr);
|
||||
sphere->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
#include <XCEngine/Core/Event.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
@@ -11,16 +13,17 @@
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "Core/ISelectionManager.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EditorSceneManager {
|
||||
public:
|
||||
static EditorSceneManager& Get() {
|
||||
static EditorSceneManager instance;
|
||||
return instance;
|
||||
}
|
||||
class ISelectionManager;
|
||||
|
||||
class SceneManager {
|
||||
public:
|
||||
SceneManager();
|
||||
|
||||
::XCEngine::Components::GameObject* CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent = nullptr);
|
||||
|
||||
::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) {
|
||||
@@ -37,12 +40,7 @@ public:
|
||||
|
||||
void DeleteEntity(::XCEngine::Components::GameObject::ID id);
|
||||
|
||||
void RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName) {
|
||||
if (auto* entity = GetEntity(id)) {
|
||||
entity->SetName(newName);
|
||||
OnEntityChanged.Invoke(id);
|
||||
}
|
||||
}
|
||||
void RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName);
|
||||
|
||||
void CopyEntity(::XCEngine::Components::GameObject::ID id);
|
||||
::XCEngine::Components::GameObject::ID PasteEntity(::XCEngine::Components::GameObject::ID parent = 0);
|
||||
@@ -52,6 +50,8 @@ public:
|
||||
void CreateDemoScene();
|
||||
|
||||
bool HasClipboardData() const { return m_clipboard.has_value(); }
|
||||
|
||||
void SetSelectionManager(ISelectionManager* selectionManager);
|
||||
|
||||
::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityCreated;
|
||||
::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityDeleted;
|
||||
@@ -59,8 +59,6 @@ public:
|
||||
::XCEngine::Core::Event<> OnSceneChanged;
|
||||
|
||||
private:
|
||||
EditorSceneManager() = default;
|
||||
|
||||
struct ClipboardData {
|
||||
std::string name;
|
||||
Math::Vector3 localPosition = Math::Vector3::Zero();
|
||||
@@ -75,6 +73,7 @@ private:
|
||||
::XCEngine::Components::Scene* m_scene = nullptr;
|
||||
std::vector<::XCEngine::Components::GameObject*> m_rootEntities;
|
||||
std::optional<ClipboardData> m_clipboard;
|
||||
ISelectionManager* m_selectionManager = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "HierarchyPanel.h"
|
||||
#include "Managers/SceneManager.h"
|
||||
#include "Managers/SelectionManager.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <imgui.h>
|
||||
@@ -10,14 +9,14 @@ namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
|
||||
EditorSceneManager::Get().CreateDemoScene();
|
||||
|
||||
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](uint64_t) {
|
||||
});
|
||||
}
|
||||
|
||||
HierarchyPanel::~HierarchyPanel() {
|
||||
SelectionManager::Get().OnSelectionChanged.Unsubscribe(m_selectionHandlerId);
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnAttach() {
|
||||
auto* sceneManager = static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
sceneManager->CreateDemoScene();
|
||||
}
|
||||
|
||||
void HierarchyPanel::Render() {
|
||||
@@ -33,7 +32,8 @@ void HierarchyPanel::Render() {
|
||||
|
||||
ImGui::BeginChild("EntityList");
|
||||
|
||||
auto rootEntities = EditorSceneManager::Get().GetRootEntities();
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto rootEntities = sceneManager.GetRootEntities();
|
||||
SortEntities(const_cast<std::vector<::XCEngine::Components::GameObject*>&>(rootEntities));
|
||||
|
||||
for (auto* gameObject : rootEntities) {
|
||||
@@ -42,7 +42,7 @@ void HierarchyPanel::Render() {
|
||||
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
|
||||
if (!m_renaming) {
|
||||
SelectionManager::Get().ClearSelection();
|
||||
m_context->GetSelectionManager().ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ void HierarchyPanel::Render() {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
|
||||
::XCEngine::Components::GameObject* sourceGameObject = *(::XCEngine::Components::GameObject**)payload->Data;
|
||||
if (sourceGameObject && sourceGameObject->GetParent() != nullptr) {
|
||||
EditorSceneManager::Get().MoveEntity(sourceGameObject->GetID(), 0);
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
sceneManager.MoveEntity(sourceGameObject->GetID(), 0);
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
@@ -94,7 +95,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
}
|
||||
|
||||
if (SelectionManager::Get().IsSelected(gameObject)) {
|
||||
if (m_context->GetSelectionManager().IsSelected(gameObject->GetID())) {
|
||||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
EditorSceneManager::Get().RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
static_cast<SceneManager*>(m_context->GetSceneManager())->RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = nullptr;
|
||||
@@ -115,7 +116,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
|
||||
if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
EditorSceneManager::Get().RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
static_cast<SceneManager*>(m_context->GetSceneManager())->RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = nullptr;
|
||||
@@ -126,11 +127,11 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.KeyCtrl) {
|
||||
if (!SelectionManager::Get().IsSelected(gameObject)) {
|
||||
SelectionManager::Get().AddToSelection(gameObject);
|
||||
if (!m_context->GetSelectionManager().IsSelected(gameObject->GetID())) {
|
||||
m_context->GetSelectionManager().AddToSelection(gameObject->GetID());
|
||||
}
|
||||
} else {
|
||||
SelectionManager::Get().SetSelectedEntity(gameObject);
|
||||
m_context->GetSelectionManager().SetSelectedEntity(gameObject->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +161,8 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameObject) {
|
||||
auto& sceneManager = EditorSceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
if (ImGui::BeginMenu("Create")) {
|
||||
RenderCreateMenu(gameObject);
|
||||
@@ -170,7 +171,7 @@ void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameO
|
||||
|
||||
if (gameObject != nullptr && ImGui::MenuItem("Create Child")) {
|
||||
auto* child = sceneManager.CreateEntity("GameObject", gameObject);
|
||||
selectionManager.SetSelectedEntity(child);
|
||||
selectionManager.SetSelectedEntity(child->GetID());
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -212,12 +213,12 @@ void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameO
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent) {
|
||||
auto& sceneManager = EditorSceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
if (ImGui::MenuItem("Empty Object")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("GameObject", parent);
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -225,12 +226,12 @@ void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent
|
||||
if (ImGui::MenuItem("Camera")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("Camera", parent);
|
||||
newEntity->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Light")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("Light", parent);
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -238,24 +239,24 @@ void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent
|
||||
if (ImGui::MenuItem("Cube")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("Cube", parent);
|
||||
newEntity->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Sphere")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("Sphere", parent);
|
||||
newEntity->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Plane")) {
|
||||
auto* newEntity = sceneManager.CreateEntity("Plane", parent);
|
||||
newEntity->AddComponent<::XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
selectionManager.SetSelectedEntity(newEntity->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) {
|
||||
auto& sceneManager = EditorSceneManager::Get();
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
ImGui::SetDragDropPayload("ENTITY_PTR", &gameObject, sizeof(::XCEngine::Components::GameObject*));
|
||||
@@ -296,10 +297,10 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObje
|
||||
}
|
||||
|
||||
void HierarchyPanel::HandleKeyboardShortcuts() {
|
||||
auto& sceneManager = EditorSceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
::XCEngine::Components::GameObject* selectedGameObject = selectionManager.GetSelectedEntity();
|
||||
::XCEngine::Components::GameObject* selectedGameObject = sceneManager.GetEntity(selectionManager.GetSelectedEntity());
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Panel.h"
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include "Managers/SceneManager.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
@@ -13,6 +14,7 @@ public:
|
||||
HierarchyPanel();
|
||||
~HierarchyPanel();
|
||||
|
||||
void OnAttach() override;
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
@@ -25,8 +27,6 @@ private:
|
||||
bool PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter);
|
||||
void SortEntities(std::vector<::XCEngine::Components::GameObject*>& entities);
|
||||
|
||||
uint64_t m_selectionHandlerId = 0;
|
||||
|
||||
char m_searchBuffer[256] = "";
|
||||
bool m_renaming = false;
|
||||
::XCEngine::Components::GameObject* m_renamingEntity = nullptr;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class IEditorContext;
|
||||
|
||||
class Panel {
|
||||
public:
|
||||
Panel(const std::string& name);
|
||||
@@ -21,9 +23,13 @@ public:
|
||||
void SetOpen(bool open) { m_isOpen = open; }
|
||||
void Toggle() { m_isOpen = !m_isOpen; }
|
||||
|
||||
void SetContext(IEditorContext* context) { m_context = context; }
|
||||
IEditorContext* GetContext() const { return m_context; }
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
bool m_isOpen;
|
||||
IEditorContext* m_context = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void Update(uint32_t offset, RHIResourceView* view) = 0;
|
||||
virtual void UpdateSampler(uint32_t offset, RHISampler* sampler) = 0;
|
||||
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
|
||||
virtual uint32_t GetBindingCount() const = 0;
|
||||
|
||||
@@ -276,9 +276,24 @@ void D3D12CommandList::AliasBarrierInternal(ID3D12Resource* beforeResource, ID3D
|
||||
|
||||
void D3D12CommandList::BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffer* framebuffer,
|
||||
const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) {
|
||||
(void)renderArea;
|
||||
if (!framebuffer || !renderPass) return;
|
||||
|
||||
D3D12_VIEWPORT d3dViewport = {};
|
||||
d3dViewport.TopLeftX = static_cast<float>(renderArea.left);
|
||||
d3dViewport.TopLeftY = static_cast<float>(renderArea.top);
|
||||
d3dViewport.Width = static_cast<float>(renderArea.right - renderArea.left);
|
||||
d3dViewport.Height = static_cast<float>(renderArea.bottom - renderArea.top);
|
||||
d3dViewport.MinDepth = 0.0f;
|
||||
d3dViewport.MaxDepth = 1.0f;
|
||||
m_commandList->RSSetViewports(1, &d3dViewport);
|
||||
|
||||
D3D12_RECT d3dScissor = {};
|
||||
d3dScissor.left = renderArea.left;
|
||||
d3dScissor.top = renderArea.top;
|
||||
d3dScissor.right = renderArea.right;
|
||||
d3dScissor.bottom = renderArea.bottom;
|
||||
m_commandList->RSSetScissorRects(1, &d3dScissor);
|
||||
|
||||
D3D12Framebuffer* d3d12Framebuffer = static_cast<D3D12Framebuffer*>(framebuffer);
|
||||
|
||||
uint32_t rtCount = d3d12Framebuffer->GetRenderTargetCount();
|
||||
|
||||
@@ -489,6 +489,8 @@ void OpenGLCommandList::BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffe
|
||||
OpenGLFramebuffer* glFramebuffer = static_cast<OpenGLFramebuffer*>(framebuffer);
|
||||
glFramebuffer->Bind();
|
||||
|
||||
if (!renderPass) return;
|
||||
|
||||
OpenGLRenderPass* glRenderPass = static_cast<OpenGLRenderPass*>(renderPass);
|
||||
if (glRenderPass) {
|
||||
const AttachmentDesc* colorAttachments = glRenderPass->GetColorAttachments();
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#include "fixtures/RHITestFixture.h"
|
||||
#include "XCEngine/RHI/RHICommandList.h"
|
||||
#include "XCEngine/RHI/RHITexture.h"
|
||||
#include "XCEngine/RHI/RHIBuffer.h"
|
||||
#include "XCEngine/RHI/RHIShader.h"
|
||||
#include "XCEngine/RHI/RHIPipelineState.h"
|
||||
#include "XCEngine/RHI/RHIRenderPass.h"
|
||||
#include "XCEngine/RHI/RHIFramebuffer.h"
|
||||
|
||||
using namespace XCEngine::RHI;
|
||||
|
||||
@@ -87,6 +92,26 @@ TEST_P(RHITestFixture, CommandList_SetScissorRect) {
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetScissorRects_Multiple) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
Rect rects[2] = {
|
||||
{ 0, 0, 400, 300 },
|
||||
{ 400, 300, 800, 600 }
|
||||
};
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->SetScissorRects(2, rects);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_Draw) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
@@ -119,34 +144,6 @@ TEST_P(RHITestFixture, CommandList_DrawIndexed) {
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_ClearRenderTarget) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
float color[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->ClearRenderTarget(static_cast<RHIResourceView*>(nullptr), color);
|
||||
cmdList->Close();
|
||||
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetStencilRef) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
@@ -162,136 +159,297 @@ TEST_P(RHITestFixture, CommandList_SetStencilRef) {
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_TransitionBarrier) {
|
||||
TEST_P(RHITestFixture, CommandList_SetBlendFactor) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
float blendFactor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->SetBlendFactor(blendFactor);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_ClearRenderTarget_WithRealView) {
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
|
||||
ASSERT_NE(rtv, nullptr);
|
||||
|
||||
float color[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->ClearRenderTarget(rtv, color);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
delete rtv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_ClearDepthStencil_WithRealView) {
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* dsv = GetDevice()->CreateDepthStencilView(texture, {});
|
||||
ASSERT_NE(dsv, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->ClearDepthStencil(dsv, 1.0f, 0);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
delete dsv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_TransitionBarrier_WithRealResource) {
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* srv = GetDevice()->CreateShaderResourceView(texture, {});
|
||||
ASSERT_NE(srv, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->TransitionBarrier(srv, ResourceStates::Common, ResourceStates::PixelShaderResource);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
delete srv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetRenderTargets_WithRealViews) {
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
|
||||
ASSERT_NE(rtv, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->SetRenderTargets(1, &rtv, nullptr);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
delete rtv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_CopyResource_WithRealResources) {
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
|
||||
RHITexture* srcTexture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(srcTexture, nullptr);
|
||||
|
||||
RHITexture* dstTexture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(dstTexture, nullptr);
|
||||
|
||||
RHIResourceView* srcView = GetDevice()->CreateShaderResourceView(srcTexture, {});
|
||||
RHIResourceView* dstView = GetDevice()->CreateShaderResourceView(dstTexture, {});
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->CopyResource(dstView, srcView);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
delete srcView;
|
||||
delete dstView;
|
||||
srcTexture->Shutdown();
|
||||
delete srcTexture;
|
||||
dstTexture->Shutdown();
|
||||
delete dstTexture;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_BeginEndRenderPass_Basic) {
|
||||
AttachmentDesc colorDesc = {};
|
||||
colorDesc.format = Format::R8G8B8A8_UNorm;
|
||||
colorDesc.loadOp = LoadAction::Clear;
|
||||
colorDesc.storeOp = StoreAction::Store;
|
||||
|
||||
RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorDesc, nullptr);
|
||||
ASSERT_NE(renderPass, nullptr);
|
||||
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
|
||||
ASSERT_NE(rtv, nullptr);
|
||||
|
||||
RHIFramebuffer* fb = GetDevice()->CreateFramebuffer(renderPass, 256, 256, 1, &rtv, nullptr);
|
||||
ASSERT_NE(fb, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
Rect renderArea = { 0, 0, 256, 256 };
|
||||
ClearValue clearValue = { { 0.0f, 0.0f, 0.0f, 1.0f }, 1.0f, 0 };
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->TransitionBarrier(static_cast<RHIResourceView*>(nullptr), ResourceStates::Common, ResourceStates::RenderTarget);
|
||||
cmdList->BeginRenderPass(renderPass, fb, renderArea, 1, &clearValue);
|
||||
cmdList->EndRenderPass();
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
fb->Shutdown();
|
||||
delete fb;
|
||||
delete rtv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
renderPass->Shutdown();
|
||||
delete renderPass;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_TransitionBarrier_WithResourceView) {
|
||||
TEST_P(RHITestFixture, CommandList_BeginEndRenderPass_WithClear) {
|
||||
AttachmentDesc colorDesc = {};
|
||||
colorDesc.format = Format::R8G8B8A8_UNorm;
|
||||
colorDesc.loadOp = LoadAction::Clear;
|
||||
colorDesc.storeOp = StoreAction::Store;
|
||||
|
||||
RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorDesc, nullptr);
|
||||
ASSERT_NE(renderPass, nullptr);
|
||||
|
||||
TextureDesc texDesc = {};
|
||||
texDesc.width = 256;
|
||||
texDesc.height = 256;
|
||||
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
|
||||
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
|
||||
ASSERT_NE(texture, nullptr);
|
||||
|
||||
RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
|
||||
ASSERT_NE(rtv, nullptr);
|
||||
|
||||
RHIFramebuffer* fb = GetDevice()->CreateFramebuffer(renderPass, 256, 256, 1, &rtv, nullptr);
|
||||
ASSERT_NE(fb, nullptr);
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
Rect renderArea = { 0, 0, 256, 256 };
|
||||
ClearValue clearValue = { { 1.0f, 0.0f, 0.0f, 1.0f }, 1.0f, 0 };
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->TransitionBarrier(static_cast<RHIResourceView*>(nullptr), ResourceStates::Common, ResourceStates::RenderTarget);
|
||||
cmdList->BeginRenderPass(renderPass, fb, renderArea, 1, &clearValue);
|
||||
cmdList->EndRenderPass();
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
fb->Shutdown();
|
||||
delete fb;
|
||||
delete rtv;
|
||||
texture->Shutdown();
|
||||
delete texture;
|
||||
renderPass->Shutdown();
|
||||
delete renderPass;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetRenderTargets_WithResourceView) {
|
||||
TEST_P(RHITestFixture, CommandList_SetShader) {
|
||||
ShaderCompileDesc shaderDesc = {};
|
||||
if (GetBackendType() == RHIType::D3D12) {
|
||||
shaderDesc.fileName = L"tests/RHI/D3D12/integration/quad/Res/Shader/quad.hlsl";
|
||||
shaderDesc.entryPoint = L"MainVS";
|
||||
shaderDesc.profile = L"vs_5_0";
|
||||
} else {
|
||||
shaderDesc.sourceLanguage = ShaderLanguage::GLSL;
|
||||
static const char* vs = "#version 430\nin vec4 aPosition;\nvoid main() { gl_Position = aPosition; }";
|
||||
shaderDesc.source.assign(vs, vs + strlen(vs));
|
||||
}
|
||||
|
||||
RHIShader* shader = GetDevice()->CompileShader(shaderDesc);
|
||||
if (shader == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->SetRenderTargets(0, static_cast<RHIResourceView**>(nullptr), static_cast<RHIResourceView*>(nullptr));
|
||||
cmdList->SetShader(shader);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
shader->Shutdown();
|
||||
delete shader;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetVertexBuffer_WithResourceView) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
RHIResourceView* buffer = nullptr;
|
||||
cmdList->SetVertexBuffers(0, 1, &buffer, nullptr, nullptr);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_SetIndexBuffer_WithResourceView) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->SetIndexBuffer(static_cast<RHIResourceView*>(nullptr), 0);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_ClearRenderTarget_WithResourceView) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
float color[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->ClearRenderTarget(static_cast<RHIResourceView*>(nullptr), color);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_ClearDepthStencil_WithResourceView) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->ClearDepthStencil(static_cast<RHIResourceView*>(nullptr), 1.0f, 0);
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
TEST_P(RHITestFixture, CommandList_CopyResource_WithResourceView) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||
ASSERT_NE(cmdList, nullptr);
|
||||
|
||||
cmdList->Reset();
|
||||
cmdList->CopyResource(static_cast<RHIResourceView*>(nullptr), static_cast<RHIResourceView*>(nullptr));
|
||||
cmdList->Close();
|
||||
|
||||
cmdList->Shutdown();
|
||||
delete cmdList;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user