Unified logging: Replace LogSystem with EditorConsoleSink
- Created EditorConsoleSink (implements ILogSink interface) - EditorConsoleSink stores logs in memory buffer (max 1000 entries) - Added to Debug::Logger in Application::Initialize() - ConsolePanel now reads from EditorConsoleSink via static GetInstance() - Removed separate LogSystem singleton - Removed editor/src/Core/LogEntry.h (no longer needed) Now Editor and Engine share the same Debug::Logger, with ConsolePanel displaying logs via EditorConsoleSink.
This commit is contained in:
174
docs/issues/RHI_PipelineState_Shader_Missing.md
Normal file
174
docs/issues/RHI_PipelineState_Shader_Missing.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
# RHI 模块严重问题:PipelineState 与 Shader 关系设计混乱
|
||||||
|
|
||||||
|
## 问题严重程度
|
||||||
|
|
||||||
|
**严重级别**: 🔴 架构级缺陷(Critical)
|
||||||
|
|
||||||
|
## 问题定位
|
||||||
|
|
||||||
|
根据核心设计理念:
|
||||||
|
> - **求同存异**:优先提取 API 共性作为核心抽象
|
||||||
|
> - **核心约束原则**:面向 D3D12/Vulkan 设计,参考 Unity RHI
|
||||||
|
|
||||||
|
当前设计存在一个**根本性的架构缺陷**。
|
||||||
|
|
||||||
|
## 问题详述
|
||||||
|
|
||||||
|
### `RHIDevice::CreatePipelineState()` 接口设计错误
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// engine/include/XCEngine/RHI/RHIDevice.h:41
|
||||||
|
virtual RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `GraphicsPipelineDesc` 缺少 Shader 字段
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// engine/include/XCEngine/RHI/RHITypes.h:309-320
|
||||||
|
struct GraphicsPipelineDesc {
|
||||||
|
InputLayoutDesc inputLayout;
|
||||||
|
RasterizerDesc rasterizerState;
|
||||||
|
BlendDesc blendState;
|
||||||
|
DepthStencilStateDesc depthStencilState;
|
||||||
|
|
||||||
|
uint32_t topologyType = 0;
|
||||||
|
uint32_t renderTargetCount = 1;
|
||||||
|
uint32_t renderTargetFormats[8] = { 0 };
|
||||||
|
uint32_t depthStencilFormat = 0;
|
||||||
|
uint32_t sampleCount = 1;
|
||||||
|
// ❌ 缺少 Shader 字段!
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 导致的严重后果
|
||||||
|
|
||||||
|
### 1. D3D12 PSO 创建后立即无效
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// engine/src/RHI/D3D12/D3D12Device.cpp:434-444
|
||||||
|
RHIPipelineState* D3D12Device::CreatePipelineState(const GraphicsPipelineDesc& desc) {
|
||||||
|
auto* pso = new D3D12PipelineState(m_device.Get());
|
||||||
|
pso->SetInputLayout(desc.inputLayout);
|
||||||
|
// ... 设置各种状态
|
||||||
|
return pso; // ❌ 此时 m_vsBytecode 和 m_psBytecode 都是空的!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
当调用 `EnsureValid()` 时:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// engine/src/RHI/D3D12/D3D12PipelineState.cpp:143-146
|
||||||
|
bool D3D12PipelineState::CreateD3D12PSO() {
|
||||||
|
if (!m_vsBytecode.pShaderBytecode || !m_psBytecode.pShaderBytecode) {
|
||||||
|
return false; // ❌ 直接失败!
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 后端行为不一致,违反"求同存异"原则
|
||||||
|
|
||||||
|
| 后端 | `CreatePipelineState()` 返回后 `IsValid()` |
|
||||||
|
|------|------------------------------------------|
|
||||||
|
| D3D12 | `false` (需要额外设置 shader) |
|
||||||
|
| OpenGL | `true` (OpenGL 不需要预编译 PSO) |
|
||||||
|
|
||||||
|
这完全违反了核心设计原则。
|
||||||
|
|
||||||
|
### 3. 运行时状态污染
|
||||||
|
|
||||||
|
Shader 通过 `SetShaderBytecodes()` 在运行时注入:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 需要外部代码手动调用
|
||||||
|
pso->SetShaderBytecodes(vsBytecode, psBytecode); // ❌ 语义混乱
|
||||||
|
pso->EnsureValid(); // ❌ 现在才能创建真正的 D3D12 PSO
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设计根源分析
|
||||||
|
|
||||||
|
### 参考 Unity RHI 的正确做法
|
||||||
|
|
||||||
|
Unity 的 `PipelineState` 是 **Shader + RenderState** 的不可变组合:
|
||||||
|
- 创建时必须提供完整的 shader 和 state
|
||||||
|
- 创建后不可修改
|
||||||
|
- `IsValid()` 的语义是确定的
|
||||||
|
|
||||||
|
### 当前设计的错误假设
|
||||||
|
|
||||||
|
把 Shader 和 PipelineState 分离,试图让 PSO 成为"可配置的 render state 容器",但这与 D3D12/Vulkan 的 PSO 概念完全冲突——在这些 API 中,**PSO 是 shader + state 的编译后不可变对象**。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
1. **无法正确实现 SRP**:上层渲染管线无法可靠地预编译 PSO
|
||||||
|
2. **资源管理混乱**:PSO 何时真正创建?何时可以释放?语义不明确
|
||||||
|
3. **测试困难**:后端行为不一致,单元测试无法统一编写
|
||||||
|
4. **扩展性差**:未来添加 Vulkan 后端会遇到同样的问题
|
||||||
|
|
||||||
|
## 建议修复方案
|
||||||
|
|
||||||
|
### 方案 A:在 `GraphicsPipelineDesc` 中添加 Shader 字段
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct GraphicsPipelineDesc {
|
||||||
|
// ... 现有字段
|
||||||
|
|
||||||
|
RHIShader* vertexShader = nullptr;
|
||||||
|
RHIShader* pixelShader = nullptr;
|
||||||
|
RHIShader* geometryShader = nullptr;
|
||||||
|
RHIShader* hullShader = nullptr;
|
||||||
|
RHIShader* domainShader = nullptr;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方案 B:创建专用的 ShaderProgram 类型
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct ShaderProgramDesc {
|
||||||
|
RHIShader* vertexShader = nullptr;
|
||||||
|
RHIShader* pixelShader = nullptr;
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GraphicsPipelineDesc {
|
||||||
|
ShaderProgramDesc shaders; // 包含所有 shader
|
||||||
|
InputLayoutDesc inputLayout;
|
||||||
|
RasterizerDesc rasterizerState;
|
||||||
|
BlendDesc blendState;
|
||||||
|
DepthStencilStateDesc depthStencilState;
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方案 C:分离 PSO 创建接口
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 独立的 Compute PSO 创建
|
||||||
|
RHIPipelineState* CreateComputePipelineState(RHIShader* computeShader);
|
||||||
|
|
||||||
|
// Graphics PSO 创建时必须提供 shader
|
||||||
|
RHIPipelineState* CreateGraphicsPipelineState(
|
||||||
|
const GraphicsPipelineDesc& desc,
|
||||||
|
RHIShader* vertexShader,
|
||||||
|
RHIShader* pixelShader
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- `engine/include/XCEngine/RHI/RHIDevice.h`
|
||||||
|
- `engine/include/XCEngine/RHI/RHITypes.h`
|
||||||
|
- `engine/include/XCEngine/RHI/RHIPipelineState.h`
|
||||||
|
- `engine/src/RHI/D3D12/D3D12Device.cpp`
|
||||||
|
- `engine/src/RHI/D3D12/D3D12PipelineState.cpp`
|
||||||
|
- `engine/src/RHI/OpenGL/OpenGLDevice.cpp`
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
**`GraphicsPipelineDesc` 缺少 Shader 字段** 导致:
|
||||||
|
|
||||||
|
1. 抽象层无法正确表达 D3D12/Vulkan 的 PSO 概念
|
||||||
|
2. 后端行为不一致,违反"求同存异"
|
||||||
|
3. PSO 生命周期管理语义混乱
|
||||||
|
|
||||||
|
这是整个 RHI 模块**最严重的架构级缺陷**,应该优先修复。
|
||||||
@@ -33,8 +33,8 @@ add_executable(${PROJECT_NAME} WIN32
|
|||||||
src/Application.cpp
|
src/Application.cpp
|
||||||
src/Theme.cpp
|
src/Theme.cpp
|
||||||
src/Managers/SceneManager.cpp
|
src/Managers/SceneManager.cpp
|
||||||
src/Managers/LogSystem.cpp
|
|
||||||
src/Managers/ProjectManager.cpp
|
src/Managers/ProjectManager.cpp
|
||||||
|
src/Core/EditorConsoleSink.cpp
|
||||||
src/panels/Panel.cpp
|
src/panels/Panel.cpp
|
||||||
src/panels/MenuBar.cpp
|
src/panels/MenuBar.cpp
|
||||||
src/panels/HierarchyPanel.cpp
|
src/panels/HierarchyPanel.cpp
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Layers/EditorLayer.h"
|
#include "Layers/EditorLayer.h"
|
||||||
#include "Core/EditorContextImpl.h"
|
#include "Core/EditorContextImpl.h"
|
||||||
|
#include "Core/EditorConsoleSink.h"
|
||||||
#include <XCEngine/Debug/Logger.h>
|
#include <XCEngine/Debug/Logger.h>
|
||||||
#include <XCEngine/Debug/FileLogSink.h>
|
#include <XCEngine/Debug/FileLogSink.h>
|
||||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||||
@@ -66,6 +67,7 @@ bool Application::Initialize(HWND hwnd) {
|
|||||||
|
|
||||||
// Initialize logging first
|
// Initialize logging first
|
||||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::ConsoleLogSink>());
|
Debug::Logger::Get().AddSink(std::make_unique<Debug::ConsoleLogSink>());
|
||||||
|
Debug::Logger::Get().AddSink(std::make_unique<Debug::EditorConsoleSink>());
|
||||||
|
|
||||||
// Get exe directory for log file path
|
// Get exe directory for log file path
|
||||||
wchar_t exePath[MAX_PATH];
|
wchar_t exePath[MAX_PATH];
|
||||||
|
|||||||
43
editor/src/Core/EditorConsoleSink.cpp
Normal file
43
editor/src/Core/EditorConsoleSink.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "Core/EditorConsoleSink.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Debug {
|
||||||
|
|
||||||
|
EditorConsoleSink* EditorConsoleSink::GetInstance() {
|
||||||
|
static EditorConsoleSink instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorConsoleSink::EditorConsoleSink() = default;
|
||||||
|
|
||||||
|
EditorConsoleSink::~EditorConsoleSink() = default;
|
||||||
|
|
||||||
|
void EditorConsoleSink::Log(const LogEntry& entry) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (m_logs.size() >= MAX_LOGS) {
|
||||||
|
m_logs.erase(m_logs.begin());
|
||||||
|
}
|
||||||
|
m_logs.push_back(entry);
|
||||||
|
if (m_callback) {
|
||||||
|
m_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorConsoleSink::Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<LogEntry>& EditorConsoleSink::GetLogs() const {
|
||||||
|
return m_logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorConsoleSink::Clear() {
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_logs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorConsoleSink::SetCallback(std::function<void()> callback) {
|
||||||
|
m_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Debug
|
||||||
|
} // namespace XCEngine
|
||||||
34
editor/src/Core/EditorConsoleSink.h
Normal file
34
editor/src/Core/EditorConsoleSink.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Debug/ILogSink.h>
|
||||||
|
#include <XCEngine/Debug/LogEntry.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Debug {
|
||||||
|
|
||||||
|
class EditorConsoleSink : public ILogSink {
|
||||||
|
public:
|
||||||
|
static EditorConsoleSink* GetInstance();
|
||||||
|
|
||||||
|
EditorConsoleSink();
|
||||||
|
~EditorConsoleSink() override;
|
||||||
|
|
||||||
|
void Log(const LogEntry& entry) override;
|
||||||
|
void Flush() override;
|
||||||
|
|
||||||
|
const std::vector<LogEntry>& GetLogs() const;
|
||||||
|
void Clear();
|
||||||
|
void SetCallback(std::function<void()> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
std::vector<LogEntry> m_logs;
|
||||||
|
std::function<void()> m_callback;
|
||||||
|
static constexpr size_t MAX_LOGS = 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Debug
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <XCEngine/Debug/LogLevel.h>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
|
||||||
namespace Editor {
|
|
||||||
|
|
||||||
struct LogEntry {
|
|
||||||
::XCEngine::Debug::LogLevel level;
|
|
||||||
std::string message;
|
|
||||||
std::string timestamp;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#include "LogSystem.h"
|
|
||||||
|
|
||||||
namespace XCEngine {
|
|
||||||
namespace Editor {
|
|
||||||
|
|
||||||
LogSystem& LogSystem::Get() {
|
|
||||||
static LogSystem instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogSystem::AddLog(::XCEngine::Debug::LogLevel level, const std::string& message) {
|
|
||||||
m_logs.push_back({level, message});
|
|
||||||
if (m_callback) m_callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogSystem::Clear() {
|
|
||||||
m_logs.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Core/LogEntry.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
|
||||||
namespace Editor {
|
|
||||||
|
|
||||||
class LogSystem {
|
|
||||||
public:
|
|
||||||
static LogSystem& Get();
|
|
||||||
|
|
||||||
void AddLog(::XCEngine::Debug::LogLevel level, const std::string& message);
|
|
||||||
void Clear();
|
|
||||||
const std::vector<LogEntry>& GetLogs() const { return m_logs; }
|
|
||||||
|
|
||||||
void SetCallback(std::function<void()> callback) { m_callback = callback; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
LogSystem() = default;
|
|
||||||
|
|
||||||
std::vector<LogEntry> m_logs;
|
|
||||||
std::function<void()> m_callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
#include "ConsolePanel.h"
|
#include "ConsolePanel.h"
|
||||||
#include "Managers/LogSystem.h"
|
#include "Core/EditorConsoleSink.h"
|
||||||
#include "Core/LogEntry.h"
|
#include <XCEngine/Debug/Logger.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
ConsolePanel::ConsolePanel() : Panel("Console") {
|
ConsolePanel::ConsolePanel() : Panel("Console") {
|
||||||
LogSystem::Get().AddLog(::XCEngine::Debug::LogLevel::Info, "Engine initialized successfully");
|
|
||||||
LogSystem::Get().AddLog(::XCEngine::Debug::LogLevel::Info, "Loading default scene...");
|
|
||||||
LogSystem::Get().AddLog(::XCEngine::Debug::LogLevel::Warning, "Missing material on object 'Cube'");
|
|
||||||
LogSystem::Get().AddLog(::XCEngine::Debug::LogLevel::Error, "Failed to load texture: 'Assets/Textures/missing.png'");
|
|
||||||
LogSystem::Get().AddLog(::XCEngine::Debug::LogLevel::Info, "Scene loaded successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsolePanel::Render() {
|
void ConsolePanel::Render() {
|
||||||
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
||||||
|
|
||||||
if (ImGui::Button("Clear")) {
|
if (ImGui::Button("Clear")) {
|
||||||
LogSystem::Get().Clear();
|
Debug::EditorConsoleSink::GetInstance()->Clear();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
@@ -57,17 +52,21 @@ void ConsolePanel::Render() {
|
|||||||
|
|
||||||
ImGui::BeginChild("LogScroll", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
ImGui::BeginChild("LogScroll", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
|
||||||
|
const auto& logs = Debug::EditorConsoleSink::GetInstance()->GetLogs();
|
||||||
size_t logIndex = 0;
|
size_t logIndex = 0;
|
||||||
for (const auto& log : LogSystem::Get().GetLogs()) {
|
for (const auto& log : logs) {
|
||||||
bool shouldShow = false;
|
bool shouldShow = false;
|
||||||
switch (log.level) {
|
switch (log.level) {
|
||||||
case ::XCEngine::Debug::LogLevel::Info:
|
case ::XCEngine::Debug::LogLevel::Info:
|
||||||
|
case ::XCEngine::Debug::LogLevel::Verbose:
|
||||||
|
case ::XCEngine::Debug::LogLevel::Debug:
|
||||||
shouldShow = m_showInfo;
|
shouldShow = m_showInfo;
|
||||||
break;
|
break;
|
||||||
case ::XCEngine::Debug::LogLevel::Warning:
|
case ::XCEngine::Debug::LogLevel::Warning:
|
||||||
shouldShow = m_showWarning;
|
shouldShow = m_showWarning;
|
||||||
break;
|
break;
|
||||||
case ::XCEngine::Debug::LogLevel::Error:
|
case ::XCEngine::Debug::LogLevel::Error:
|
||||||
|
case ::XCEngine::Debug::LogLevel::Fatal:
|
||||||
shouldShow = m_showError;
|
shouldShow = m_showError;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -80,6 +79,8 @@ void ConsolePanel::Render() {
|
|||||||
const char* prefix;
|
const char* prefix;
|
||||||
|
|
||||||
switch (log.level) {
|
switch (log.level) {
|
||||||
|
case ::XCEngine::Debug::LogLevel::Verbose:
|
||||||
|
case ::XCEngine::Debug::LogLevel::Debug:
|
||||||
case ::XCEngine::Debug::LogLevel::Info:
|
case ::XCEngine::Debug::LogLevel::Info:
|
||||||
color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
|
color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
prefix = "[INFO] ";
|
prefix = "[INFO] ";
|
||||||
@@ -89,6 +90,7 @@ void ConsolePanel::Render() {
|
|||||||
prefix = "[WARN] ";
|
prefix = "[WARN] ";
|
||||||
break;
|
break;
|
||||||
case ::XCEngine::Debug::LogLevel::Error:
|
case ::XCEngine::Debug::LogLevel::Error:
|
||||||
|
case ::XCEngine::Debug::LogLevel::Fatal:
|
||||||
color = ImVec4(1.0f, 0.3f, 0.3f, 1.0f);
|
color = ImVec4(1.0f, 0.3f, 0.3f, 1.0f);
|
||||||
prefix = "[ERROR]";
|
prefix = "[ERROR]";
|
||||||
break;
|
break;
|
||||||
@@ -96,11 +98,10 @@ void ConsolePanel::Render() {
|
|||||||
|
|
||||||
ImGui::PushID(static_cast<int>(logIndex));
|
ImGui::PushID(static_cast<int>(logIndex));
|
||||||
|
|
||||||
std::string timestampStr = "[" + log.timestamp + "] ";
|
ImGui::TextColored(ImVec4(0.4f, 0.4f, 0.4f, 1.0f), "%s", log.message.CStr());
|
||||||
ImGui::TextColored(ImVec4(0.4f, 0.4f, 0.4f, 1.0f), "%s", timestampStr.c_str());
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
std::string fullMessage = std::string(prefix) + log.message;
|
std::string fullMessage = std::string(prefix) + log.message.CStr();
|
||||||
ImGui::TextColored(color, "%s", fullMessage.c_str());
|
ImGui::TextColored(color, "%s", fullMessage.c_str());
|
||||||
|
|
||||||
if (ImGui::IsItemClicked()) {
|
if (ImGui::IsItemClicked()) {
|
||||||
@@ -111,11 +112,6 @@ void ConsolePanel::Render() {
|
|||||||
logIndex++;
|
logIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scrollToBottom && !LogSystem::Get().GetLogs().empty()) {
|
|
||||||
ImGui::SetScrollHereY(1.0f);
|
|
||||||
m_scrollToBottom = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#include <wrl/client.h>
|
#include <wrl/client.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "../RHIResourceView.h"
|
#include "../RHIResourceView.h"
|
||||||
#include "../RHIEnums.h"
|
#include "../RHIEnums.h"
|
||||||
@@ -28,6 +29,8 @@ public:
|
|||||||
ResourceViewDimension GetDimension() const override { return m_dimension; }
|
ResourceViewDimension GetDimension() const override { return m_dimension; }
|
||||||
Format GetFormat() const override { return m_format; }
|
Format GetFormat() const override { return m_format; }
|
||||||
|
|
||||||
|
void SetOwnedHeap(std::unique_ptr<class D3D12DescriptorHeap> heap);
|
||||||
|
|
||||||
void InitializeAsRenderTarget(ID3D12Device* device, ID3D12Resource* resource,
|
void InitializeAsRenderTarget(ID3D12Device* device, ID3D12Resource* resource,
|
||||||
const D3D12_RENDER_TARGET_VIEW_DESC* desc,
|
const D3D12_RENDER_TARGET_VIEW_DESC* desc,
|
||||||
D3D12DescriptorHeap* heap, uint32_t slotIndex);
|
D3D12DescriptorHeap* heap, uint32_t slotIndex);
|
||||||
@@ -63,6 +66,7 @@ private:
|
|||||||
ID3D12Resource* m_resource;
|
ID3D12Resource* m_resource;
|
||||||
D3D12DescriptorHeap* m_heap;
|
D3D12DescriptorHeap* m_heap;
|
||||||
uint32_t m_slotIndex;
|
uint32_t m_slotIndex;
|
||||||
|
std::unique_ptr<D3D12DescriptorHeap> m_ownedHeap;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace RHI
|
} // namespace RHI
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
|
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
|
||||||
#include "XCEngine/RHI/D3D12/D3D12Enums.h"
|
#include "XCEngine/RHI/D3D12/D3D12Enums.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace RHI {
|
namespace RHI {
|
||||||
@@ -17,6 +18,11 @@ bool D3D12CommandAllocator::Initialize(ID3D12Device* device, CommandQueueType ty
|
|||||||
HRESULT hResult = device->CreateCommandAllocator(
|
HRESULT hResult = device->CreateCommandAllocator(
|
||||||
ToD3D12(type),
|
ToD3D12(type),
|
||||||
IID_PPV_ARGS(&m_commandAllocator));
|
IID_PPV_ARGS(&m_commandAllocator));
|
||||||
|
if (FAILED(hResult)) {
|
||||||
|
char buf[256];
|
||||||
|
sprintf(buf, "[D3D12CommandAllocator] CreateCommandAllocator failed: hr=0x%08X\n", hResult);
|
||||||
|
OutputDebugStringA(buf);
|
||||||
|
}
|
||||||
return SUCCEEDED(hResult);
|
return SUCCEEDED(hResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ bool D3D12CommandList::Initialize(ID3D12Device* device, CommandQueueType type, I
|
|||||||
m_commandAllocator = allocator;
|
m_commandAllocator = allocator;
|
||||||
m_device = device;
|
m_device = device;
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
sprintf(buf, "[D3D12CommandList::Initialize] device=%p, allocator=%p, type=%d\n", device, allocator, (int)type);
|
||||||
|
OutputDebugStringA(buf);
|
||||||
|
|
||||||
HRESULT hResult = device->CreateCommandList(
|
HRESULT hResult = device->CreateCommandList(
|
||||||
0,
|
0,
|
||||||
listType,
|
listType,
|
||||||
@@ -39,6 +43,8 @@ bool D3D12CommandList::Initialize(ID3D12Device* device, CommandQueueType type, I
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (FAILED(hResult)) {
|
if (FAILED(hResult)) {
|
||||||
|
sprintf(buf, "[D3D12CommandList::Initialize] CreateCommandList failed: hr=0x%08X\n", hResult);
|
||||||
|
OutputDebugStringA(buf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
|
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
|
||||||
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
|
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#include <dxgidebug.h>
|
#include <dxgidebug.h>
|
||||||
@@ -407,18 +408,32 @@ RHISwapChain* D3D12Device::CreateSwapChain(const SwapChainDesc& desc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHICommandList* D3D12Device::CreateCommandList(const CommandListDesc& desc) {
|
RHICommandList* D3D12Device::CreateCommandList(const CommandListDesc& desc) {
|
||||||
auto* allocator = new D3D12CommandAllocator();
|
FILE* f = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f) { fprintf(f, "[CreateCommandList] Start, m_device=%p, m_initialized=%d\n", m_device.Get(), m_initialized); fclose(f); }
|
||||||
|
|
||||||
|
if (!m_device) {
|
||||||
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f2) { fprintf(f2, "[CreateCommandList] Error: m_device is null\n"); fclose(f2); }
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto allocator = std::make_unique<D3D12CommandAllocator>();
|
||||||
if (!allocator->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType))) {
|
if (!allocator->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType))) {
|
||||||
delete allocator;
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f2) { fprintf(f2, "[CreateCommandList] Error: allocator Initialize failed\n"); fclose(f2); }
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* cmdList = new D3D12CommandList();
|
auto* cmdList = new D3D12CommandList();
|
||||||
if (!cmdList->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType), allocator->GetCommandAllocator())) {
|
if (!cmdList->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType), allocator->GetCommandAllocator())) {
|
||||||
delete allocator;
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f2) { fprintf(f2, "[CreateCommandList] Error: cmdList Initialize failed\n"); fclose(f2); }
|
||||||
delete cmdList;
|
delete cmdList;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE* f3 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f3) { fprintf(f3, "[CreateCommandList] Success\n"); fclose(f3); }
|
||||||
return cmdList;
|
return cmdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,27 +468,42 @@ RHIPipelineLayout* D3D12Device::CreatePipelineLayout(const RHIPipelineLayoutDesc
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
|
RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
|
||||||
|
FILE* f = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f) { fprintf(f, "[CreateRenderTargetView] Start\n"); fclose(f); }
|
||||||
|
|
||||||
|
if (!texture) {
|
||||||
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f2) { fprintf(f2, "[CreateRenderTargetView] Error: texture is null\n"); fclose(f2); }
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto* view = new D3D12ResourceView();
|
auto* view = new D3D12ResourceView();
|
||||||
auto* d3d12Texture = static_cast<D3D12Texture*>(texture);
|
auto* d3d12Texture = static_cast<D3D12Texture*>(texture);
|
||||||
ID3D12Resource* resource = d3d12Texture->GetResource();
|
ID3D12Resource* resource = d3d12Texture->GetResource();
|
||||||
|
|
||||||
|
if (!resource) {
|
||||||
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f2) { fprintf(f2, "[CreateRenderTargetView] Error: resource is null\n"); fclose(f2); }
|
||||||
|
delete view;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
||||||
rtvDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
rtvDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
||||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||||
|
|
||||||
DescriptorHeapDesc heapDesc = {};
|
auto heap = std::make_unique<D3D12DescriptorHeap>();
|
||||||
heapDesc.descriptorCount = 1;
|
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::RTV, 1, false)) {
|
||||||
heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::RTV);
|
FILE* f2 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
heapDesc.shaderVisible = false;
|
if (f2) { fprintf(f2, "[CreateRenderTargetView] Error: heap Initialize failed\n"); fclose(f2); }
|
||||||
auto* heapPool = CreateDescriptorHeap(heapDesc);
|
|
||||||
auto* heap = static_cast<D3D12DescriptorHeap*>(heapPool);
|
|
||||||
|
|
||||||
if (heap == nullptr) {
|
|
||||||
delete view;
|
delete view;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap, 0);
|
view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap.get(), 0);
|
||||||
|
view->SetOwnedHeap(std::move(heap));
|
||||||
|
FILE* f3 = fopen("D:\\Xuanchi\\Main\\XCEngine\\debug_rhi.log", "a");
|
||||||
|
if (f3) { fprintf(f3, "[CreateRenderTargetView] Success\n"); fclose(f3); }
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,19 +516,14 @@ RHIResourceView* D3D12Device::CreateDepthStencilView(RHITexture* texture, const
|
|||||||
dsvDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
dsvDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
||||||
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||||
|
|
||||||
DescriptorHeapDesc heapDesc = {};
|
auto heap = std::make_unique<D3D12DescriptorHeap>();
|
||||||
heapDesc.descriptorCount = 1;
|
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::DSV, 1, false)) {
|
||||||
heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::DSV);
|
|
||||||
heapDesc.shaderVisible = false;
|
|
||||||
auto* heapPool = CreateDescriptorHeap(heapDesc);
|
|
||||||
auto* heap = static_cast<D3D12DescriptorHeap*>(heapPool);
|
|
||||||
|
|
||||||
if (heap == nullptr) {
|
|
||||||
delete view;
|
delete view;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
view->InitializeAsDepthStencil(m_device.Get(), resource, &dsvDesc, heap, 0);
|
view->InitializeAsDepthStencil(m_device.Get(), resource, &dsvDesc, heap.get(), 0);
|
||||||
|
view->SetOwnedHeap(std::move(heap));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,14 +539,14 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons
|
|||||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
|
|
||||||
DescriptorHeapDesc heapDesc = {};
|
auto heap = std::make_unique<D3D12DescriptorHeap>();
|
||||||
heapDesc.descriptorCount = 1;
|
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, true)) {
|
||||||
heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::CBV_SRV_UAV);
|
delete view;
|
||||||
heapDesc.shaderVisible = true;
|
return nullptr;
|
||||||
auto* heapPool = CreateDescriptorHeap(heapDesc);
|
}
|
||||||
auto* heap = static_cast<D3D12DescriptorHeap*>(heapPool);
|
|
||||||
|
|
||||||
view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap, 0);
|
view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap.get(), 0);
|
||||||
|
view->SetOwnedHeap(std::move(heap));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,14 +559,14 @@ RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHITexture* texture, con
|
|||||||
uavDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
uavDesc.Format = static_cast<DXGI_FORMAT>(desc.format);
|
||||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||||
|
|
||||||
DescriptorHeapDesc heapDesc = {};
|
auto heap = std::make_unique<D3D12DescriptorHeap>();
|
||||||
heapDesc.descriptorCount = 1;
|
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, true)) {
|
||||||
heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::CBV_SRV_UAV);
|
delete view;
|
||||||
heapDesc.shaderVisible = true;
|
return nullptr;
|
||||||
auto* heapPool = CreateDescriptorHeap(heapDesc);
|
}
|
||||||
auto* heap = static_cast<D3D12DescriptorHeap*>(heapPool);
|
|
||||||
|
|
||||||
view->InitializeAsUnorderedAccess(m_device.Get(), resource, &uavDesc, heap, 0);
|
view->InitializeAsUnorderedAccess(m_device.Get(), resource, &uavDesc, heap.get(), 0);
|
||||||
|
view->SetOwnedHeap(std::move(heap));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ D3D12ResourceView::D3D12ResourceView()
|
|||||||
, m_handle({0})
|
, m_handle({0})
|
||||||
, m_resource(nullptr)
|
, m_resource(nullptr)
|
||||||
, m_heap(nullptr)
|
, m_heap(nullptr)
|
||||||
, m_slotIndex(0) {
|
, m_slotIndex(0)
|
||||||
|
, m_ownedHeap(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D12ResourceView::~D3D12ResourceView() {
|
D3D12ResourceView::~D3D12ResourceView() {
|
||||||
@@ -21,6 +22,10 @@ D3D12ResourceView::~D3D12ResourceView() {
|
|||||||
void D3D12ResourceView::Shutdown() {
|
void D3D12ResourceView::Shutdown() {
|
||||||
m_handle = {};
|
m_handle = {};
|
||||||
m_resource = nullptr;
|
m_resource = nullptr;
|
||||||
|
if (m_ownedHeap) {
|
||||||
|
m_ownedHeap->Shutdown();
|
||||||
|
m_ownedHeap.reset();
|
||||||
|
}
|
||||||
m_heap = nullptr;
|
m_heap = nullptr;
|
||||||
m_slotIndex = 0;
|
m_slotIndex = 0;
|
||||||
}
|
}
|
||||||
@@ -151,5 +156,10 @@ D3D12_UNORDERED_ACCESS_VIEW_DESC D3D12ResourceView::CreateUnorderedAccessDesc(Fo
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3D12ResourceView::SetOwnedHeap(std::unique_ptr<D3D12DescriptorHeap> heap) {
|
||||||
|
m_ownedHeap = std::move(heap);
|
||||||
|
m_heap = m_ownedHeap.get();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace RHI
|
} // namespace RHI
|
||||||
} // namespace XCEngine
|
} // namespace XCEngine
|
||||||
@@ -35,7 +35,7 @@ void RHITestFixture::SetUp() {
|
|||||||
bool initResult = false;
|
bool initResult = false;
|
||||||
if (GetParam() == RHIType::D3D12) {
|
if (GetParam() == RHIType::D3D12) {
|
||||||
RHIDeviceDesc desc = {};
|
RHIDeviceDesc desc = {};
|
||||||
desc.enableDebugLayer = true;
|
desc.enableDebugLayer = false;
|
||||||
initResult = mDevice->Initialize(desc);
|
initResult = mDevice->Initialize(desc);
|
||||||
} else if (GetParam() == RHIType::OpenGL) {
|
} else if (GetParam() == RHIType::OpenGL) {
|
||||||
auto* oglDevice = static_cast<OpenGLDevice*>(mDevice);
|
auto* oglDevice = static_cast<OpenGLDevice*>(mDevice);
|
||||||
|
|||||||
Reference in New Issue
Block a user