- Removed the slot and property_slot classes, consolidating their functionality into a new modifier system. - Introduced align_modifier, padding_modifier, and stretch_modifier to handle layout properties. - Updated overlay and stack classes to utilize the new modifier system for layout management. - Simplified event handling by integrating layout modifiers directly into the event target retrieval process. - Removed unused includes and cleaned up code for better readability and maintainability.
603 lines
17 KiB
C++
603 lines
17 KiB
C++
#include "window_manager.h"
|
||
#include "desktop/glfw_window.h"
|
||
#include "vulkan/context.h"
|
||
#include "vulkan/device_manager.h"
|
||
#include "vulkan/logical_device.h"
|
||
#include "vulkan/swapchain.h"
|
||
#include "vulkan/resource_manager.h"
|
||
#include "pipeline/render_pipeline.h"
|
||
#include "render_command.h"
|
||
|
||
#include <iostream>
|
||
#include <memory>
|
||
#include <chrono>
|
||
#include <cmath>
|
||
|
||
#include "button.h"
|
||
#include "layout/fixed_box.h"
|
||
#include "imager.h"
|
||
#include "layout/overlay.h"
|
||
#include "post_effect.h"
|
||
#include "layout/stack.h"
|
||
#include "fill_box.h"
|
||
#include "layout/modifiers/modifiers.h" // 新的布局修饰器
|
||
#include "image/texture_manager.h"
|
||
|
||
using namespace mirage;
|
||
|
||
/// @brief 新渲染管线示例应用
|
||
///
|
||
/// 展示新的渲染管线系统的使用方式,包括:
|
||
/// 1. 基本几何渲染(多个矩形)
|
||
/// 2. z_order 排序验证
|
||
/// 3. 后效应用(模糊效果)
|
||
/// 4. 窗口 resize 处理
|
||
class NewPipelineExample {
|
||
public:
|
||
NewPipelineExample() = default;
|
||
|
||
void run() {
|
||
std::cout << "=== Mirage 新渲染管线示例 ===\n\n";
|
||
|
||
if (!init_window())
|
||
return;
|
||
if (!init_vulkan())
|
||
return;
|
||
if (!init_pipeline())
|
||
return;
|
||
if (!load_texture())
|
||
return;
|
||
|
||
main_loop();
|
||
cleanup();
|
||
}
|
||
|
||
private:
|
||
// ========================================================================
|
||
// 窗口和 Vulkan 资源
|
||
// ========================================================================
|
||
window_manager wm_;
|
||
window_interface* window_ = nullptr;
|
||
glfw_window* glfw_win_ = nullptr;
|
||
|
||
std::optional<render_context> context_;
|
||
vk::SurfaceKHR surface_;
|
||
vk::PhysicalDevice physical_device_;
|
||
std::optional<logical_device> logical_;
|
||
std::optional<resource_manager> res_mgr_;
|
||
std::optional<swapchain> swap_;
|
||
std::optional<texture_manager> texture_manager_;
|
||
|
||
// 新的渲染管线
|
||
std::unique_ptr<render_pipeline> pipeline_;
|
||
|
||
// 窗口尺寸
|
||
uint32_t window_width_ = 1280;
|
||
uint32_t window_height_ = 720;
|
||
|
||
// 动画时间
|
||
float time_ = 0.0f;
|
||
std::chrono::steady_clock::time_point start_time_;
|
||
|
||
texture_id_t texture_id_;
|
||
vec2i_t texture_size_;
|
||
|
||
// ========================================================================
|
||
// 初始化方法
|
||
// ========================================================================
|
||
|
||
/// 初始化窗口
|
||
bool init_window() {
|
||
std::cout << "1. 创建窗口...\n";
|
||
|
||
window_create_info window_info{
|
||
.title = "Mirage - 新渲染管线示例",
|
||
.width = static_cast<int>(window_width_),
|
||
.height = static_cast<int>(window_height_),
|
||
.mode = window_mode::WINDOWED,
|
||
.flags = window_flag::RESIZABLE | window_flag::VISIBLE |
|
||
window_flag::DECORATED | window_flag::FOCUSED
|
||
};
|
||
|
||
auto window_id_result = wm_.create_window(window_info);
|
||
if (!window_id_result) {
|
||
std::cerr << " ✗ 创建窗口失败: " << window_id_result.error() << "\n";
|
||
return false;
|
||
}
|
||
|
||
window_ = wm_.get_window(window_id_result.value());
|
||
if (!window_) {
|
||
std::cerr << " ✗ 获取窗口失败\n";
|
||
return false;
|
||
}
|
||
|
||
glfw_win_ = dynamic_cast<glfw_window*>(window_);
|
||
if (!glfw_win_) {
|
||
std::cerr << " ✗ 窗口类型错误\n";
|
||
return false;
|
||
}
|
||
|
||
std::cout << " ✓ 窗口创建成功\n\n";
|
||
return true;
|
||
}
|
||
|
||
/// 初始化 Vulkan
|
||
bool init_vulkan() {
|
||
std::cout << "2. 初始化 Vulkan...\n";
|
||
|
||
// 创建 Vulkan 上下文
|
||
auto required_extensions = glfw_window::get_required_instance_extensions();
|
||
render_context::create_info ctx_info{
|
||
.app_name = "Mirage 新管线示例",
|
||
.app_version = VK_MAKE_VERSION(1, 0, 0),
|
||
.engine_name = "Mirage Engine",
|
||
.engine_version = VK_MAKE_VERSION(1, 0, 0),
|
||
.enable_validation = true,
|
||
.required_extensions = required_extensions
|
||
};
|
||
|
||
auto context_result = render_context::create(ctx_info);
|
||
if (!context_result) {
|
||
std::cerr << " ✗ 创建 Vulkan 上下文失败: "
|
||
<< vk::to_string(context_result.error()) << "\n";
|
||
return false;
|
||
}
|
||
context_ = std::move(context_result.value());
|
||
std::cout << " ✓ Vulkan 上下文创建成功\n";
|
||
|
||
// 创建 Surface
|
||
std::cout << "3. 创建 Vulkan Surface...\n";
|
||
surface_ = glfw_win_->create_vulkan_surface(context_->get_instance());
|
||
if (!surface_) {
|
||
std::cerr << " ✗ 创建 Surface 失败\n";
|
||
return false;
|
||
}
|
||
std::cout << " ✓ Surface 创建成功\n";
|
||
|
||
// 选择物理设备
|
||
std::cout << "4. 选择物理设备...\n";
|
||
device_manager dev_mgr(context_->get_instance());
|
||
auto physical_device_result = dev_mgr.select_primary_device(surface_);
|
||
if (!physical_device_result) {
|
||
std::cerr << " ✗ 选择物理设备失败: "
|
||
<< vk::to_string(physical_device_result.error()) << "\n";
|
||
return false;
|
||
}
|
||
physical_device_ = physical_device_result.value();
|
||
auto device_props = physical_device_.getProperties();
|
||
std::cout << " ✓ 已选择: " << device_props.deviceName << "\n";
|
||
|
||
// 创建逻辑设备
|
||
std::cout << "5. 创建逻辑设备...\n";
|
||
auto logical_device_result = logical_device::create(physical_device_, surface_);
|
||
if (!logical_device_result) {
|
||
std::cerr << " ✗ 创建逻辑设备失败: "
|
||
<< vk::to_string(logical_device_result.error()) << "\n";
|
||
return false;
|
||
}
|
||
logical_ = std::move(logical_device_result.value());
|
||
std::cout << " ✓ 逻辑设备创建成功\n";
|
||
|
||
// 创建资源管理器
|
||
res_mgr_.emplace(*logical_, physical_device_, context_->get_instance());
|
||
|
||
// 创建纹理管理器
|
||
texture_manager_.emplace(*res_mgr_, *logical_);
|
||
|
||
// 创建 Swapchain
|
||
std::cout << "6. 创建 Swapchain...\n";
|
||
auto size = window_->get_framebuffer_size();
|
||
window_width_ = static_cast<uint32_t>(size.x());
|
||
window_height_ = static_cast<uint32_t>(size.y());
|
||
|
||
auto swapchain_result = swapchain::create(
|
||
*logical_, physical_device_, surface_,
|
||
window_width_, window_height_,
|
||
true, // 启用 VSync
|
||
*res_mgr_);
|
||
|
||
if (!swapchain_result) {
|
||
std::cerr << " ✗ 创建 Swapchain 失败: "
|
||
<< vk::to_string(swapchain_result.error()) << "\n";
|
||
return false;
|
||
}
|
||
swap_ = std::move(swapchain_result.value());
|
||
std::cout << " ✓ Swapchain 创建成功 (图像数: "
|
||
<< swap_->get_image_count() << ")\n\n";
|
||
|
||
return true;
|
||
}
|
||
|
||
/// 初始化渲染管线
|
||
bool init_pipeline() {
|
||
std::cout << "7. 创建新渲染管线...\n";
|
||
|
||
try {
|
||
// 配置管线选项
|
||
render_pipeline::config cfg{
|
||
.frames_in_flight = 2 // 双缓冲
|
||
};
|
||
|
||
// 创建管线实例
|
||
pipeline_ = std::make_unique<render_pipeline>(
|
||
*logical_,
|
||
*swap_,
|
||
*res_mgr_,
|
||
*texture_manager_,
|
||
cfg);
|
||
|
||
// 初始化管线(创建所有子组件)
|
||
pipeline_->initialize();
|
||
|
||
std::cout << " ✓ 新渲染管线创建成功\n";
|
||
std::cout << " - 离屏渲染目标: 已创建\n";
|
||
std::cout << " - 命令处理器: 已创建\n";
|
||
std::cout << " - 几何渲染器: 已创建\n";
|
||
std::cout << " - 后效应用器: 已创建\n";
|
||
std::cout << " - 帧调度器: 已创建\n\n";
|
||
|
||
return true;
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << " ✗ 创建渲染管线失败: " << e.what() << "\n";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool load_texture() {
|
||
texture_id_ = texture_manager_->load_texture("D:\\G2uY1fJa8AAOucs.jpg").value();
|
||
texture_size_ = texture_manager_->get_texture(texture_id_)->size();
|
||
return true;
|
||
}
|
||
|
||
// ========================================================================
|
||
// 渲染命令生成
|
||
// ========================================================================
|
||
|
||
/// 创建测试渲染命令
|
||
/// 包含多个测试场景:基本几何、z_order排序、后效应用
|
||
auto create_test_commands() -> std::vector<render_command> {
|
||
std::vector<render_command> commands;
|
||
|
||
// ====================================================================
|
||
// 场景 1: 基本几何渲染 - 多个矩形
|
||
// ====================================================================
|
||
|
||
// 背景矩形(大的深蓝色)
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(50.0f, 50.0f),
|
||
vec2f_t(300.0f, 200.0f),
|
||
color::from_rgba(30, 60, 120, 255),
|
||
10.0f // 圆角
|
||
);
|
||
cmd.order.z_order = 0; // 最底层
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// 前景矩形(较小的浅蓝色,应该在背景上面)
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(100.0f, 100.0f),
|
||
vec2f_t(200.0f, 150.0f),
|
||
color::from_rgba(100, 150, 200, 255),
|
||
5.0f);
|
||
cmd.order.z_order = 1; // 在背景上面
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// ====================================================================
|
||
// 场景 2: z_order 排序测试
|
||
// ====================================================================
|
||
|
||
// 红色矩形 (z_order = 2)
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(400.0f, 50.0f),
|
||
vec2f_t(150.0f, 150.0f),
|
||
color::from_rgba(200, 50, 50, 255),
|
||
8.0f);
|
||
cmd.order.z_order = 2;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// 绿色矩形 (z_order = 3,应该在红色上面)
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(450.0f, 100.0f),
|
||
vec2f_t(150.0f, 150.0f),
|
||
color::from_rgba(50, 200, 50, 255),
|
||
8.0f);
|
||
cmd.order.z_order = 3;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// 黄色矩形 (z_order = 1,应该在红色和绿色下面)
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(425.0f, 75.0f),
|
||
vec2f_t(150.0f, 150.0f),
|
||
color::from_rgba(200, 200, 50, 255),
|
||
8.0f);
|
||
cmd.order.z_order = 1;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// ====================================================================
|
||
// 场景 3: 后效测试 - 模糊效果
|
||
// ====================================================================
|
||
|
||
// 紫色背景矩形
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(700.0f, 50.0f),
|
||
vec2f_t(250.0f, 200.0f),
|
||
color::from_rgba(150, 50, 150, 255),
|
||
10.0f);
|
||
cmd.order.z_order = 0;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// 应用模糊效果到紫色区域
|
||
{
|
||
auto blur = blur_effect(
|
||
vec2f_t(700.0f, 50.0f),
|
||
vec2f_t(250.0f, 200.0f),
|
||
8.0f, // 模糊半径
|
||
1.0f // 强度
|
||
);
|
||
blur.order.z_order = 10; // 后效通常在几何之后
|
||
commands.emplace_back(post_effect_command{blur});
|
||
}
|
||
|
||
// ====================================================================
|
||
// 场景 4: 动画矩形(测试持续渲染)
|
||
// ====================================================================
|
||
|
||
// 使用时间创建动画效果
|
||
float offset_x = 50.0f + std::sin(time_) * 30.0f;
|
||
float offset_y = 300.0f + std::cos(time_) * 30.0f;
|
||
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(offset_x, offset_y),
|
||
vec2f_t(100.0f, 100.0f),
|
||
color::from_rgba(255, 180, 50, 255),
|
||
15.0f);
|
||
cmd.order.z_order = 5;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// ====================================================================
|
||
// 场景 5: 暗角效果测试
|
||
// ====================================================================
|
||
|
||
// 橙色矩形
|
||
{
|
||
auto cmd = rectangle_command(
|
||
vec2f_t(400.0f, 300.0f),
|
||
vec2f_t(200.0f, 180.0f),
|
||
color::from_rgba(200, 150, 50, 255),
|
||
5.0f);
|
||
cmd.order.z_order = 0;
|
||
commands.emplace_back(cmd);
|
||
}
|
||
|
||
// 应用暗角效果
|
||
{
|
||
auto vignette = vignette_effect(
|
||
vec2f_t(400.0f, 300.0f),
|
||
vec2f_t(200.0f, 180.0f),
|
||
0.5f, // 半径
|
||
0.5f, // 柔和度
|
||
0.8f // 强度
|
||
);
|
||
vignette.order.z_order = 10;
|
||
commands.emplace_back(post_effect_command{vignette});
|
||
}
|
||
|
||
return commands;
|
||
}
|
||
|
||
/// 创建widget测试渲染命令
|
||
auto create_widget_test_commands(render_command_builder& builder) -> std::vector<render_command> {
|
||
static uint32_t widget_build_count = 0;
|
||
widget_build_count++;
|
||
|
||
auto on_click = []() {
|
||
std::cout << "clicked" << std::endl;
|
||
};
|
||
|
||
// ===================================================================
|
||
// 布局修饰器管道语法示例
|
||
// ===================================================================
|
||
//
|
||
// ===== 旧写法(已弃用)=====
|
||
// slot{
|
||
// imager{}.texture_id(id_)
|
||
// }.align(alignment::CENTER).padding(10.f)
|
||
//
|
||
// slot{
|
||
// overlay{...}
|
||
// } | stretch()
|
||
//
|
||
// ===== 新写法(推荐)=====
|
||
// imager{}.texture_id(id_)
|
||
// | align(alignment::CENTER)
|
||
// | padding(10.f)
|
||
//
|
||
// overlay{...} | stretch()
|
||
//
|
||
// ===================================================================
|
||
|
||
// 使用新的布局修饰器管道语法
|
||
v_stack v{
|
||
// 普通方式:直接放置控件
|
||
button{
|
||
"Hello",
|
||
on_click
|
||
},
|
||
// 新的管道语法:通过修饰器添加布局属性
|
||
// 不再需要 slot{} 包装,直接使用 | 管道操作符
|
||
overlay{
|
||
// 使用管道语法设置图片居中对齐和边距
|
||
imager{}
|
||
.texture_id(texture_id_)
|
||
.fit(image_fit::contain)
|
||
.set_texture_size(texture_size_)
|
||
| align(alignment::CENTER)
|
||
| padding(10.f),
|
||
// 普通后效控件
|
||
post_effect_widget{
|
||
fill_box{},
|
||
blur_effect{20.f}
|
||
},
|
||
} | stretch() // 直接对 overlay 应用 stretch,无需 slot 包装
|
||
};
|
||
|
||
layout_state state;
|
||
state.set_root(transform2d_t::Identity(), window_->get_framebuffer_size().cast<float>());
|
||
|
||
v.arrange(state);
|
||
|
||
v.build_render_commands(builder);
|
||
|
||
auto commands = builder.get_sorted_commands();
|
||
return commands;
|
||
}
|
||
|
||
// ========================================================================
|
||
// 主循环
|
||
// ========================================================================
|
||
|
||
void main_loop() {
|
||
std::cout << "=== 进入主循环 ===\n";
|
||
std::cout << "按 ESC 或关闭窗口退出\n";
|
||
std::cout << "调整窗口大小测试 resize 处理\n\n";
|
||
|
||
start_time_ = std::chrono::steady_clock::now();
|
||
uint32_t frame_count = 0;
|
||
|
||
// 添加调试计数器
|
||
static uint32_t build_commands_call_count = 0;
|
||
|
||
// 设置窗口事件回调(处理 resize)
|
||
window_->set_event_callback([this](const event& e) {
|
||
if (e.type == event_type::WINDOW_RESIZE) {
|
||
pipeline_->on_resize(e.window.width, e.window.height);
|
||
window_width_ = e.window.width;
|
||
window_height_ = e.window.height;
|
||
}
|
||
});
|
||
|
||
// 主渲染循环
|
||
while (!wm_.all_windows_closed()) {
|
||
// 处理窗口事件
|
||
wm_.poll_all_events();
|
||
|
||
// 更新动画时间
|
||
auto now = std::chrono::steady_clock::now();
|
||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||
now - start_time_)
|
||
.count();
|
||
time_ = elapsed / 1000.0f;
|
||
|
||
// 生成渲染命令
|
||
render_command_builder builder;
|
||
|
||
// 调试:记录 build_render_commands 调用
|
||
build_commands_call_count++;
|
||
|
||
// auto commands = create_test_commands();
|
||
auto commands = create_widget_test_commands(builder);
|
||
|
||
// 调试:打印每帧的命令数量
|
||
if (frame_count % 60 == 0) {
|
||
std::cout << "[DEBUG] 帧 " << frame_count
|
||
<< ": build_render_commands 调用次数=" << build_commands_call_count
|
||
<< ", 生成命令数=" << commands.size() << std::endl;
|
||
}
|
||
|
||
// 使用新管线渲染帧
|
||
if (!pipeline_->render_frame(commands)) {
|
||
// 渲染失败,通常是 swapchain 过期
|
||
// on_resize 已经在事件回调中处理
|
||
std::cerr << "渲染帧失败,跳过..." << std::endl;
|
||
continue;
|
||
}
|
||
|
||
frame_count++;
|
||
|
||
// 每 60 帧输出一次信息
|
||
if (frame_count % 60 == 0) {
|
||
std::cout << "帧数: " << frame_count
|
||
<< ", 时间: " << time_ << "s" << std::endl;
|
||
}
|
||
}
|
||
|
||
std::cout << "\n=== 清理资源 ===\n";
|
||
pipeline_->wait_idle();
|
||
std::cout << "总帧数: " << frame_count << "\n";
|
||
std::cout << "总 build_render_commands 调用次数: " << build_commands_call_count << "\n";
|
||
}
|
||
|
||
// ========================================================================
|
||
// 清理
|
||
// ========================================================================
|
||
|
||
void cleanup() {
|
||
// 1. 清理管线(必须在 device 之前)
|
||
if (pipeline_) {
|
||
pipeline_->cleanup();
|
||
pipeline_.reset();
|
||
}
|
||
|
||
// 2. 等待设备空闲
|
||
if (logical_) {
|
||
auto result = logical_->get_handle().waitIdle();
|
||
if (result != vk::Result::eSuccess) {
|
||
std::cerr << "警告: waitIdle 失败: " << vk::to_string(result) << "\n";
|
||
}
|
||
}
|
||
|
||
// 3. 清理 Swapchain(必须在 logical device 销毁前)
|
||
swap_.reset();
|
||
|
||
// 4. 清理纹理管理器(必须在 resource_manager 之前!)
|
||
texture_manager_.reset();
|
||
|
||
// 5. 清理资源管理器
|
||
res_mgr_.reset();
|
||
|
||
// 6. 清理逻辑设备
|
||
logical_.reset();
|
||
|
||
// 7. 销毁 Surface(必须在 Instance 销毁前)
|
||
if (context_ && surface_) {
|
||
context_->get_instance().destroySurfaceKHR(surface_);
|
||
surface_ = nullptr;
|
||
}
|
||
|
||
// 8. 清理 Context(最后销毁,包含 Instance)
|
||
context_.reset();
|
||
|
||
std::cout << "\n=== 程序成功完成 ===\n";
|
||
}
|
||
};
|
||
|
||
// ============================================================================
|
||
// 主函数
|
||
// ============================================================================
|
||
|
||
int main() {
|
||
try {
|
||
NewPipelineExample app;
|
||
app.run();
|
||
return 0;
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << "异常: " << e.what() << "\n";
|
||
return 1;
|
||
}
|
||
}
|