diff --git a/.trae/documents/RHI模块审查报告.md b/.trae/documents/RHI模块审查报告.md new file mode 100644 index 00000000..ddb89918 --- /dev/null +++ b/.trae/documents/RHI模块审查报告.md @@ -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 bindings; +}; +class RHIDescriptorSetLayout { ... }; + +// 2. 从 Layout 创建 Pool +struct DescriptorPoolDesc { + std::vector 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 vertexInputs; + std::vector fragmentOutputs; + std::vector resourceBindings; + std::vector 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 +struct RHIResult { + T value; + RHIErrorCode error; + const char* errorMessage; +}; + +// 使用示例 +RHIResult 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. ⚠️ 缺少部分高级功能(资源别名、多队列同步等) + +### 建议 + +建议按照优先级逐步改进,首先解决高优先级问题,确保引擎的稳定性和正确性。 diff --git a/.trae/documents/XCEngine项目熟悉计划.md b/.trae/documents/XCEngine项目熟悉计划.md new file mode 100644 index 00000000..2cd09cdf --- /dev/null +++ b/.trae/documents/XCEngine项目熟悉计划.md @@ -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+ 支持 diff --git a/D3D12_RHI_Test_Issue.md b/D3D12_RHI_Test_Issue.md new file mode 100644 index 00000000..2dc51a02 --- /dev/null +++ b/D3D12_RHI_Test_Issue.md @@ -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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(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(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(texture); + ID3D12Resource* resource = d3d12Texture->GetResource(); + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = static_cast(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(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 diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt deleted file mode 100644 index ed97d539..00000000 --- a/Testing/Temporary/CTestCostData.txt +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/editor/src/Core/EditorContextImpl.h b/editor/src/Core/EditorContextImpl.h index de3826f9..2ea2c432 100644 --- a/editor/src/Core/EditorContextImpl.h +++ b/editor/src/Core/EditorContextImpl.h @@ -3,6 +3,7 @@ #include "IEditorContext.h" #include "EventBus.h" #include "SelectionManagerImpl.h" +#include "Managers/SceneManager.h" #include #include @@ -13,7 +14,9 @@ class EditorContextImpl : public IEditorContext { public: EditorContextImpl() : m_eventBus(std::make_unique()) - , m_selectionManager(std::make_unique(*m_eventBus)) { + , m_selectionManager(std::make_unique(*m_eventBus)) + , m_sceneManager(std::make_unique()) { + 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 m_eventBus; std::unique_ptr m_selectionManager; + std::unique_ptr m_sceneManager; std::string m_projectPath; }; diff --git a/editor/src/Core/IEditorContext.h b/editor/src/Core/IEditorContext.h index 4120026d..7f198b72 100644 --- a/editor/src/Core/IEditorContext.h +++ b/editor/src/Core/IEditorContext.h @@ -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; diff --git a/editor/src/Core/ISceneManager.h b/editor/src/Core/ISceneManager.h new file mode 100644 index 00000000..9a6bbbc3 --- /dev/null +++ b/editor/src/Core/ISceneManager.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +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& 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& GetRootEntities() const = 0; + virtual bool HasClipboardData() const = 0; + virtual void CreateDemoScene() = 0; +}; + +} +} diff --git a/editor/src/Core/SceneManagerImpl.h b/editor/src/Core/SceneManagerImpl.h new file mode 100644 index 00000000..65154d60 --- /dev/null +++ b/editor/src/Core/SceneManagerImpl.h @@ -0,0 +1,59 @@ +#pragma once + +#include "ISceneManager.h" +#include +#include +#include +#include +#include +#include + +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& 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 children; + }; + + ClipboardData CopyEntityRecursive(GameObjectID id); + GameObjectID PasteEntityRecursive(const ClipboardData& data, GameObjectID parent); + IGameObject* CastToIGameObject(GameObject* go); + + GameObjectID m_nextId = 1; + std::vector m_rootEntities; + std::optional m_clipboard; +}; + +} +} diff --git a/editor/src/Layers/EditorLayer.cpp b/editor/src/Layers/EditorLayer.cpp index 68c87a8b..7559124f 100644 --- a/editor/src/Layers/EditorLayer.cpp +++ b/editor/src/Layers/EditorLayer.cpp @@ -33,6 +33,14 @@ void EditorLayer::onAttach() { m_consolePanel = std::make_unique(); m_projectPanel = std::make_unique(); + 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(); diff --git a/editor/src/Managers/SceneManager.cpp b/editor/src/Managers/SceneManager.cpp index 83adf52a..d92c5f25 100644 --- a/editor/src/Managers/SceneManager.cpp +++ b/editor/src/Managers/SceneManager.cpp @@ -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>(); diff --git a/editor/src/Managers/SceneManager.h b/editor/src/Managers/SceneManager.h index c0086dc4..d6f68fcb 100644 --- a/editor/src/Managers/SceneManager.h +++ b/editor/src/Managers/SceneManager.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -11,16 +13,17 @@ #include #include +#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 m_clipboard; + ISelectionManager* m_selectionManager = nullptr; }; } diff --git a/editor/src/panels/HierarchyPanel.cpp b/editor/src/panels/HierarchyPanel.cpp index f359f18b..19b50f42 100644 --- a/editor/src/panels/HierarchyPanel.cpp +++ b/editor/src/panels/HierarchyPanel.cpp @@ -1,6 +1,5 @@ #include "HierarchyPanel.h" -#include "Managers/SceneManager.h" -#include "Managers/SelectionManager.h" +#include "Core/IEditorContext.h" #include #include #include @@ -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(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(m_context->GetSceneManager()); + auto rootEntities = sceneManager.GetRootEntities(); SortEntities(const_cast&>(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(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(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(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(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(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(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(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)) { diff --git a/editor/src/panels/HierarchyPanel.h b/editor/src/panels/HierarchyPanel.h index 7e0a501f..77592096 100644 --- a/editor/src/panels/HierarchyPanel.h +++ b/editor/src/panels/HierarchyPanel.h @@ -2,6 +2,7 @@ #include "Panel.h" #include +#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; diff --git a/editor/src/panels/Panel.h b/editor/src/panels/Panel.h index e0483dd2..ee83a419 100644 --- a/editor/src/panels/Panel.h +++ b/editor/src/panels/Panel.h @@ -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; }; } diff --git a/engine/include/XCEngine/RHI/RHIDescriptorSet.h b/engine/include/XCEngine/RHI/RHIDescriptorSet.h index 9abd60d2..f6242166 100644 --- a/engine/include/XCEngine/RHI/RHIDescriptorSet.h +++ b/engine/include/XCEngine/RHI/RHIDescriptorSet.h @@ -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; diff --git a/engine/src/RHI/D3D12/D3D12CommandList.cpp b/engine/src/RHI/D3D12/D3D12CommandList.cpp index cd165701..d38fc3b1 100644 --- a/engine/src/RHI/D3D12/D3D12CommandList.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandList.cpp @@ -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(renderArea.left); + d3dViewport.TopLeftY = static_cast(renderArea.top); + d3dViewport.Width = static_cast(renderArea.right - renderArea.left); + d3dViewport.Height = static_cast(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(framebuffer); uint32_t rtCount = d3d12Framebuffer->GetRenderTargetCount(); diff --git a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp index 5e905de8..ce1fa308 100644 --- a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp +++ b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp @@ -489,6 +489,8 @@ void OpenGLCommandList::BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffe OpenGLFramebuffer* glFramebuffer = static_cast(framebuffer); glFramebuffer->Bind(); + if (!renderPass) return; + OpenGLRenderPass* glRenderPass = static_cast(renderPass); if (glRenderPass) { const AttachmentDesc* colorAttachments = glRenderPass->GetColorAttachments(); diff --git a/tests/RHI/unit/test_command_list.cpp b/tests/RHI/unit/test_command_list.cpp index 99aa406a..b3604b18 100644 --- a/tests/RHI/unit/test_command_list.cpp +++ b/tests/RHI/unit/test_command_list.cpp @@ -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(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(CommandQueueType::Direct); @@ -119,34 +144,6 @@ TEST_P(RHITestFixture, CommandList_DrawIndexed) { delete cmdList; } -TEST_P(RHITestFixture, CommandList_ClearRenderTarget) { - CommandListDesc cmdDesc = {}; - cmdDesc.commandListType = static_cast(CommandQueueType::Direct); - - RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); - ASSERT_NE(cmdList, nullptr); - - TextureDesc texDesc = {}; - texDesc.width = 256; - texDesc.height = 256; - texDesc.format = static_cast(Format::R8G8B8A8_UNorm); - texDesc.textureType = static_cast(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(nullptr), color); - cmdList->Close(); - - texture->Shutdown(); - delete texture; - cmdList->Shutdown(); - delete cmdList; -} - TEST_P(RHITestFixture, CommandList_SetStencilRef) { CommandListDesc cmdDesc = {}; cmdDesc.commandListType = static_cast(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(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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(Format::D24_UNorm_S8_UInt); + texDesc.textureType = static_cast(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(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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(Format::R8G8B8A8_UNorm); texDesc.textureType = static_cast(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(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(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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(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(CommandQueueType::Direct); - RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); ASSERT_NE(cmdList, nullptr); cmdList->Reset(); - cmdList->SetRenderTargets(0, static_cast(nullptr), static_cast(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(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(CommandQueueType::Direct); - - RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); - ASSERT_NE(cmdList, nullptr); - - cmdList->Reset(); - cmdList->SetIndexBuffer(static_cast(nullptr), 0); - cmdList->Close(); - - cmdList->Shutdown(); - delete cmdList; -} - -TEST_P(RHITestFixture, CommandList_ClearRenderTarget_WithResourceView) { - CommandListDesc cmdDesc = {}; - cmdDesc.commandListType = static_cast(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(nullptr), color); - cmdList->Close(); - - cmdList->Shutdown(); - delete cmdList; -} - -TEST_P(RHITestFixture, CommandList_ClearDepthStencil_WithResourceView) { - CommandListDesc cmdDesc = {}; - cmdDesc.commandListType = static_cast(CommandQueueType::Direct); - - RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); - ASSERT_NE(cmdList, nullptr); - - cmdList->Reset(); - cmdList->ClearDepthStencil(static_cast(nullptr), 1.0f, 0); - cmdList->Close(); - - cmdList->Shutdown(); - delete cmdList; -} - -TEST_P(RHITestFixture, CommandList_CopyResource_WithResourceView) { - CommandListDesc cmdDesc = {}; - cmdDesc.commandListType = static_cast(CommandQueueType::Direct); - - RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); - ASSERT_NE(cmdList, nullptr); - - cmdList->Reset(); - cmdList->CopyResource(static_cast(nullptr), static_cast(nullptr)); - cmdList->Close(); - - cmdList->Shutdown(); - delete cmdList; -}