Files
mirai/docs/07-phase4-debugger.md
2025-12-31 02:36:08 +08:00

15 KiB
Raw Permalink Blame History

Phase 4: 调试器系统 (S12)

S12 - 调试器系统

调试模式激活方式

MIRAI 调试器提供多种激活方式:

// 调试器管理器
class debug_system {
public:
    static debug_system& instance();
    
    // 激活调试器
    void enable();
    void disable();
    [[nodiscard]] bool is_enabled() const;
    
    // 切换调试器显示状态
    void toggle_inspector();
    
    // 设置调试器根窗口
    void set_root_window(widget_base* root);
    
private:
    bool enabled_ = false;
    debug_overlay* overlay_ = nullptr;
};

激活方式:

触发方式 快捷键/命令 说明
键盘快捷键 F12 全局切换
鼠标手势 右键双击 在控件上激活
控制台命令 debug.toggle() 脚本调用
启动参数 --debug 程序启动时启用
环境变量 MIRAI_DEBUG=1 编译时配置

使用示例:

// 启动参数解析
int main(int argc, char** argv) {
    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--debug") == 0) {
            debug_system::instance().enable();
        }
    }
    
    // 运行应用
    run_application();
    return 0;
}

// 控制台命令(游戏引擎集成)
void console_debug_toggle() {
    debug_system::instance().toggle_inspector();
}

着色器热重载触发机制

// 着色器热重载管理器
class shader_hot_reloader {
public:
    static shader_hot_reloader& instance();
    
    // 启用/禁用热重载
    void set_enabled(bool enabled);
    [[nodiscard]] bool is_enabled() const;
    
    // 添加监视的着色器文件
    void watch(const std::filesystem::path& path);
    void unwatch(const std::filesystem::path& path);
    
    // 手动触发重载
    void reload(const std::filesystem::path& path);
    void reload_all();
    
    // 事件回调
    std::function<void(const std::filesystem::path&)> on_before_reload;
    std::function<void(const std::filesystem::path&)> on_after_reload;
    std::function<void(const std::filesystem::path&, const std::string&)> on_reload_failed;
    
    // 轮询检查(在主循环中调用)
    void poll();
    
    // 触发方式
    enum class trigger_mode {
        auto_reload,      // 自动检测并重载
        auto_notify,      // 自动检测,仅通知
        manual_only,      // 仅手动触发
    };
    void set_trigger_mode(trigger_mode mode);
    
private:
    bool enabled_ = false;
    trigger_mode mode_ = trigger_mode::auto_reload;
    std::unordered_map<std::string, std::filesystem::file_time_type> file_times_;
};

热重载触发流程:

sequenceDiagram
    participant S as Shader Watcher
    participant F as File System
    participant C as Shader Compiler
    participant W as Widget
    
    loop 每帧 poll()
        S->>F: 检查文件修改时间
        F-->>S: 修改时间戳
        
        alt 文件已修改
            S->>C: 重新编译着色器
            C-->>S: 编译结果
            
            alt 编译成功
                S->>S: 备份当前资源
                S->>W: on_before_reload
                S->>W: 更新着色器管线
                S->>W: on_after_reload
                Note over W: 立即反映新效果
            else 编译失败
                S->>S: 回滚到备份
                S->>W: on_reload_failed
            end
        end
    end

配置选项:

选项 默认值 说明
MIRAI_HOT_RELOAD Debug: true, Release: false 全局开关
MIRAI_RELOAD_DEBOUNCE_MS 100 防抖动延迟
MIRAI_RELOAD_BACKUP true 失败时回滚

任务 12.1: 性能监控器

前置依赖: 任务 2.1

系统: S12 - 调试器系统

目标: 实现性能指标收集

目录结构:

src/mirai/debug/
├── perf_monitor.hpp
└── perf_monitor.cpp

公共接口:

// perf_monitor.hpp
namespace mirai {

struct frame_stats {
    f64 frame_time;
    f64 update_time;
    f64 layout_time;
    f64 paint_time;
    f64 gpu_time;
    u32 draw_calls;
    u32 vertices;
    u64 memory_usage;
};

class perf_monitor {
public:
    static perf_monitor& instance();
    
    void begin_frame();
    void end_frame();
    
    void begin_section(std::string_view name);
    void end_section(std::string_view name);
    
    [[nodiscard]] const frame_stats& current_stats() const;
    [[nodiscard]] f64 average_fps(u32 frame_count = 60) const;
    
    [[nodiscard]] bool is_enabled() const;
    void set_enabled(bool enabled);
    
private:
    frame_stats current_stats_;
    std::vector<frame_stats> history_;
    std::unordered_map<std::string, f64> section_times_;
    bool enabled_ = true;
};

} // namespace mirai

预期产出物:

  • src/mirai/debug/perf_monitor.hpp
  • src/mirai/debug/perf_monitor.cpp
  • tests/debug/test_perf_monitor.cpp

任务 12.2: Widget 树视图

前置依赖: 任务 11.1

系统: S12 - 调试器系统

目标: 实现 Widget 树可视化

目录结构:

src/mirai/debug/
├── widget_tree_view.hpp
└── widget_tree_view.cpp

公共接口:

// widget_tree_view.hpp
namespace mirai {

class widget_tree_view : public widget {
public:
    widget_tree_view();
    
    void set_root(widget_base* root);
    void set_selected(widget_base* widget);
    [[nodiscard]] widget_base* selected() const;
    
    std::function<void(widget_base*)> on_selection_changed;
    
    void paint(render_context& ctx) override;
    bool on_mouse_event(const mouse_event& event) override;
    
private:
    void paint_node(render_context& ctx, widget_base* node, u32 depth, f32& y);
    
    widget_base* root_ = nullptr;
    widget_base* selected_ = nullptr;
    std::unordered_set<widget_base*> expanded_;
};

} // namespace mirai

预期产出物:

  • src/mirai/debug/widget_tree_view.hpp
  • src/mirai/debug/widget_tree_view.cpp

任务 12.3: 属性检查器

前置依赖: 任务 12.2

系统: S12 - 调试器系统

目标: 实现 Widget 属性编辑器

目录结构:

src/mirai/debug/
├── inspector.hpp
└── inspector.cpp

公共接口:

// inspector.hpp
namespace mirai {

class inspector : public widget {
public:
    inspector();
    
    void set_target(widget* target);
    [[nodiscard]] widget* target() const;
    
    void paint(render_context& ctx) override;
    
private:
    void paint_property(render_context& ctx, std::string_view name, std::string_view value, f32& y);
    
    // 专门用于绘制 shader 参数的编辑器
    void paint_shader_parameter(render_context& ctx, const shader_parameter& param, f32& y);
    
    widget* target_ = nullptr;
};

class debug_overlay : public widget {
public:
    static debug_overlay& instance();
    
    void toggle();
    [[nodiscard]] bool is_visible() const;
    
    void set_root(widget_base* root);
    
private:
    widget_tree_view tree_view_;
    inspector inspector_;
    perf_monitor* perf_monitor_;
    bool visible_ = false;
};

} // namespace mirai

预期产出物:

  • src/mirai/debug/inspector.hpp
  • src/mirai/debug/inspector.cpp
  • tests/debug/test_inspector.cpp

任务 12.4: Debug 模块 CMake 配置

前置依赖: 任务 12.1 - 12.3

系统: S12 - 调试器系统

CMakeLists.txt 内容:

project(mirai_debug)
simple_library(STATIC)

target_link_libraries(${PROJECT_NAME}
    PUBLIC mirai_core mirai_ui mirai_loop
)

# 仅在 Debug 构建时启用
target_compile_definitions(${PROJECT_NAME} PUBLIC
    $<$<CONFIG:Debug>:MIRAI_DEBUG_ENABLED>
)

预期产出物:

  • `src/mirai/debug/CMakeLists.txt

附录: 文档索引

本文档拆分自原 任务分解.md共分为以下7个独立文档

文档 文件名 内容描述
1 01-project-structure.md 项目目录结构设计、命名规范
2 02-phase0-infrastructure.md Phase 0: 基础设施 (S4,S1,S9)
3 03-phase1a-rendering-core.md Phase 1A: 渲染核心 (S6,S10,S5)
4 04-phase1b-text-input.md Phase 1B: 文本与输入 (S7,S8)
5 05-phase2-reactive-system.md Phase 2: 响应式系统 (S3,S2)
6 06-phase3-widget-system.md Phase 3: 控件系统 (S11)
7 07-phase4-debugger.md Phase 4: 调试器系统 (S12)

附录 A: 性能分析功能

A.1 帧时间分解

MIRAI 的性能监控系统将帧时间分解为以下阶段:

阶段 说明 包含操作
input_processing 输入处理时间 SDL 事件处理、输入事件分发
state_updates 状态更新时间 Signal 通知、Computed 更新
layout_calculation 布局计算时间 Yoga 布局、Measure/Arrange
paint_commands 绘制命令生成时间 Widget paint、Layer 录制
batch_flush 批处理提交时间 Command Buffer 提交
gpu_execution GPU 执行时间 渲染管线执行
present 呈现时间 Swapchain 呈现
// 性能监控示例
void frame_profiler::begin_frame() {
    frame_start_ = std::chrono::high_resolution_clock::now();
    
    // 输入处理阶段
    profile_section("input_processing", [&]() {
        process_input_events();
    });
}

void frame_profiler::render_widget(widget::ptr w) {
    profile_section("paint_commands", [&]() {
        w->paint(render_context_);
    });
}

// 使用 Vulkan Timestamp Queries 测量 GPU 时间
void gpu_profiler::begin_timestamp(vk_command_buffer cmd, u32 query_id) {
    vkCmdResetQueryPool(cmd, query_pool_, query_id * 2, 2);
    vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                        query_pool_, query_id * 2);
}

void gpu_profiler::end_timestamp(vk_command_buffer cmd, u32 query_id) {
    vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                        query_pool_, query_id * 2 + 1);
}

f64 gpu_profiler::get_timestamp_duration(u32 query_id) {
    u64 begin_ts, end_ts;
    vkGetQueryPoolResults(device_, query_pool_, query_id * 2, 1,
                          sizeof(u64), &begin_ts, 0, VK_QUERY_RESULT_64_BIT);
    vkGetQueryPoolResults(device_, query_pool_, query_id * 2 + 1, 1,
                          sizeof(u64), &end_ts, 0, VK_QUERY_RESULT_64_BIT);
    
    // 转换为毫秒
    return (end_ts - begin_ts) * timestamp_period_ / 1'000'000.0;
}

A.2 控件级别性能分析

// 控件性能统计
struct widget_perf_stats {
    widget* target;
    f64 total_paint_time;      // 累计绘制时间
    u32 paint_count;           // 绘制次数
    f64 avg_paint_time;        // 平均绘制时间
    u32 child_count;           // 子控件数量
    f32 bounds_area;           // 边界面积
    bool is_dirty;             // 是否脏标记
};

// 获取控件级别的性能数据
widget_perf_stats get_widget_perf_stats(widget* w) {
    auto& tracker = widget_perf_tracker::instance();
    
    return {
        .target = w,
        .total_paint_time = tracker.get_total_time(w),
        .paint_count = tracker.get_paint_count(w),
        .avg_paint_time = tracker.get_avg_time(w),
        .child_count = w->child_count(),
        .bounds_area = w->bounds().extent.width * w->bounds().extent.height,
        .is_dirty = w->is_dirty()
    };
}

// 性能热点分析
std::vector<widget_perf_stats> find_perf_hotspots(u32 top_n = 10) {
    auto all_widgets = collect_all_widgets();
    std::vector<widget_perf_stats> stats;
    
    for (auto w : all_widgets) {
        stats.push_back(get_widget_perf_stats(w));
    }
    
    // 按平均绘制时间排序
    std::sort(stats.begin(), stats.end(),
              [](const auto& a, const auto& b) {
                  return a.avg_paint_time > b.avg_paint_time;
              });
    
    return {stats.begin(), stats.begin() + std::min(top_n, (u32)stats.size())};
}

A.3 性能覆盖层显示

// 性能覆盖层配置
struct perf_overlay_config {
    bool show_fps = true;
    bool show_frame_time = true;
    bool show_gpu_time = false;
    bool show_draw_calls = true;
    bool show_memory = true;
    bool show_widget_count = false;
    bool show_perf_graph = true;
    
    f32 graph_height = 100;
    u32 graph_history_size = 60;  // 60 帧历史
};

// 性能覆盖层渲染
void perf_overlay::paint(render_context& ctx) {
    auto& stats = perf_monitor::instance().current_stats();
    
    // FPS 显示
    if (config_.show_fps) {
        draw_text(ctx, {10, 10},
            std::format("FPS: {:.1f}", stats.fps),
            colors::white);
    }
    
    // 帧时间条形图
    if (config_.show_perf_graph) {
        draw_graph(ctx, {10, 30}, stats.history, config_.graph_height);
    }
    
    // 详细信息
    if (config_.show_frame_time) {
        draw_text(ctx, {10, 150},
            std::format("Frame: {:.2f}ms", stats.frame_time * 1000),
            colors::white);
    }
    
    if (config_.show_draw_calls) {
        draw_text(ctx, {10, 170},
            std::format("Draw Calls: {}", stats.draw_calls),
            colors::white);
    }
}

附录 B: 着色器热重载详细配置

B.1 配置文件选项

{
  "shader_hot_reload": {
    "enabled": true,
    "trigger_mode": "auto_reload",
    "poll_interval_ms": 500,
    "debounce_ms": 100,
    "backup_on_error": true,
    "show_notification": true,
    "watch_patterns": ["**/*.slang"],
    "exclude_paths": ["**/build/**", "**/.git/**"]
  }
}

B.2 API 配置选项

选项 默认值 类型 说明
enabled Debug: true bool 是否启用热重载
trigger_mode auto_reload enum 触发模式
poll_interval_ms 500 u32 文件检查间隔
debounce_ms 100 u32 防抖动延迟
backup_on_error true bool 失败时回滚
show_notification true bool 显示通知

B.3 热重载事件处理

// 监听热重载事件
shader_hot_reloader::instance().on_before_reload = [](const auto& path) {
    MIRAI_LOG_INFO("Reloading shader: {}", path.string());
};

shader_hot_reloader::instance().on_after_reload = [](const auto& path) {
    // 刷新使用该着色器的所有控件
    for (auto widget : get_widgets_using_shader(path)) {
        widget->on_shader_reloaded();
    }
    
    // 显示成功通知
    notification::show("Shader reloaded", path.filename().string());
};

shader_hot_reloader::instance().on_reload_failed = [](const auto& path, const auto& error) {
    MIRAI_LOG_ERROR("Failed to reload shader {}: {}", path.string(), error);
    
    // 显示错误通知
    notification::show_error("Shader reload failed", error);
};

文档版本信息

版本 日期 更新内容
0.1.0 2024-01-15 初始版本
0.1.1 2024-01-20 添加性能分析功能、帧时间分解、GPU 时间测量、控件级别性能分析、着色器热重载详细配置
<last_updated>2024-01-20</last_updated>
draft