Files
mirai/src/demo/widgets/gradient_widget.cpp
2026-01-04 09:58:08 +08:00

351 lines
9.7 KiB
C++

/**
* @file gradient_widget.cpp
* @brief MIRAI 框架渐变效果 Widget 实现
*/
#include "gradient_widget.hpp"
#include "vulkan_device.hpp"
#include "allocator.hpp"
#include "descriptor_pool.hpp"
#include "command_buffer.hpp"
#include "buffer.hpp"
#include "pipeline.hpp"
#include "logger.hpp"
#include <cmath>
#include <algorithm>
namespace mirai {
namespace demo {
// ================================================================================================
// gradient_widget 实现
// ================================================================================================
gradient_widget::gradient_widget() {
// 默认渐变:从蓝色到紫色
push_constants_.color_start[0] = 0.2f;
push_constants_.color_start[1] = 0.4f;
push_constants_.color_start[2] = 0.8f;
push_constants_.color_start[3] = 1.0f;
push_constants_.color_end[0] = 0.8f;
push_constants_.color_end[1] = 0.2f;
push_constants_.color_end[2] = 0.6f;
push_constants_.color_end[3] = 1.0f;
push_constants_.angle = 0.0f;
}
gradient_widget::~gradient_widget() {
destroy();
}
bool gradient_widget::initialize(
std::shared_ptr<vulkan_device> device,
std::shared_ptr<gpu_allocator> allocator,
std::shared_ptr<descriptor_pool> pool,
vk::Format color_format) {
if (initialized_) {
MIRAI_LOG_WARN("gradient_widget already initialized");
return true;
}
device_ = std::move(device);
allocator_ = std::move(allocator);
pool_ = std::move(pool);
color_format_ = color_format;
// 创建资源
if (!create_uniform_buffer()) {
MIRAI_LOG_ERROR("Failed to create uniform buffer for gradient_widget");
return false;
}
if (!create_descriptor_sets()) {
MIRAI_LOG_ERROR("Failed to create descriptor sets for gradient_widget");
return false;
}
if (!create_pipeline()) {
MIRAI_LOG_ERROR("Failed to create pipeline for gradient_widget");
return false;
}
initialized_ = true;
MIRAI_LOG_INFO("gradient_widget initialized successfully");
return true;
}
void gradient_widget::destroy() {
if (!initialized_) {
return;
}
// 等待设备空闲
if (device_) {
device_->get_device().waitIdle();
}
// 销毁描述符集布局
if (descriptor_set_layout_ && device_) {
device_->get_device().destroyDescriptorSetLayout(descriptor_set_layout_);
descriptor_set_layout_ = nullptr;
}
// 销毁管线
pipeline_.reset();
pipeline_layout_.reset();
// 销毁缓冲区
uniform_buffer_.reset();
vertex_buffer_.reset();
// 清理引用
device_.reset();
allocator_.reset();
pool_.reset();
initialized_ = false;
MIRAI_LOG_INFO("gradient_widget destroyed");
}
void gradient_widget::set_bounds(f32 x, f32 y, f32 width, f32 height) {
bounds_.bounds[0] = x;
bounds_.bounds[1] = y;
bounds_.bounds[2] = width;
bounds_.bounds[3] = height;
ubo_dirty_ = true;
}
void gradient_widget::set_opacity(f32 opacity) {
bounds_.opacity = std::clamp(opacity, 0.0f, 1.0f);
ubo_dirty_ = true;
}
void gradient_widget::set_colors(const std::array<f32, 4>& start, const std::array<f32, 4>& end) {
push_constants_.color_start = start;
push_constants_.color_end = end;
}
void gradient_widget::set_angle(f32 angle_radians) {
push_constants_.angle = angle_radians;
}
void gradient_widget::set_angle_degrees(f32 angle_degrees) {
push_constants_.angle = angle_degrees * 3.14159265358979f / 180.0f;
}
void gradient_widget::update(command_buffer& /*cmd*/) {
if (!initialized_) {
return;
}
// 更新 UBO
if (ubo_dirty_ && uniform_buffer_) {
uniform_buffer_->write(&bounds_, sizeof(bounds_), 0);
ubo_dirty_ = false;
}
}
void gradient_widget::render(command_buffer& cmd) {
if (!initialized_ || !pipeline_ || !pipeline_->is_valid()) {
return;
}
auto vk_cmd = cmd.get_vulkan_buffer();
auto vk_pipeline = pipeline_->get_vulkan_pipeline();
auto vk_layout = pipeline_->get_layout();
// 绑定管线
vk_cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, vk_pipeline);
// 绑定描述符集
vk_cmd.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
vk_layout,
0,
1,
&descriptor_set_,
0,
nullptr
);
// 推送常量
vk_cmd.pushConstants(
vk_layout,
vk::ShaderStageFlagBits::eFragment,
0,
sizeof(push_constants_),
&push_constants_
);
// 绘制全屏四边形(使用顶点着色器生成顶点)
vk_cmd.draw(6, 1, 0, 0);
}
bool gradient_widget::create_pipeline() {
auto vk_device = device_->get_device();
// 获取着色器 SPIR-V
auto vert_spirv = shaders::quad::vertex::spirv();
auto frag_spirv = shaders::gradient::fragment::spirv();
if (vert_spirv.empty() || frag_spirv.empty()) {
MIRAI_LOG_ERROR("Failed to get shader SPIR-V data");
return false;
}
// 创建着色器模块
vk::ShaderModuleCreateInfo vert_info{{}, vert_spirv.size() * sizeof(u32), vert_spirv.data()};
auto [vert_result, vert_module] = vk_device.createShaderModule(vert_info);
if (vert_result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to create vertex shader module");
return false;
}
vk::ShaderModuleCreateInfo frag_info{{}, frag_spirv.size() * sizeof(u32), frag_spirv.data()};
auto [frag_result, frag_module] = vk_device.createShaderModule(frag_info);
if (frag_result != vk::Result::eSuccess) {
vk_device.destroyShaderModule(vert_module);
MIRAI_LOG_ERROR("Failed to create fragment shader module");
return false;
}
// 创建管线布局
vk::PushConstantRange push_constant_range{
vk::ShaderStageFlagBits::eFragment,
0,
sizeof(gradient_push_constants)
};
pipeline_layout_config layout_config;
layout_config.descriptor_set_layouts = {descriptor_set_layout_};
layout_config.push_constant_ranges = {push_constant_range};
pipeline_layout_ = std::make_unique<pipeline_layout>(device_, layout_config);
if (!pipeline_layout_->is_valid()) {
vk_device.destroyShaderModule(vert_module);
vk_device.destroyShaderModule(frag_module);
MIRAI_LOG_ERROR("Failed to create pipeline layout");
return false;
}
// 使用构建器创建管线
graphics_pipeline_builder builder(device_);
pipeline_ = builder
.set_vertex_shader(vert_module, "main")
.set_fragment_shader(frag_module, "main")
// 不需要顶点输入,使用 gl_VertexIndex 生成顶点
.set_topology(vk::PrimitiveTopology::eTriangleList)
.set_polygon_mode(vk::PolygonMode::eFill)
.set_cull_mode(vk::CullModeFlagBits::eNone)
.set_front_face(vk::FrontFace::eCounterClockwise)
.enable_depth_test(false)
.enable_depth_write(false)
.enable_blending(true)
.set_blend_factors(
vk::BlendFactor::eSrcAlpha,
vk::BlendFactor::eOneMinusSrcAlpha,
vk::BlendFactor::eOne,
vk::BlendFactor::eOneMinusSrcAlpha)
.set_color_format(color_format_)
.add_dynamic_state(vk::DynamicState::eViewport)
.add_dynamic_state(vk::DynamicState::eScissor)
.set_layout(*pipeline_layout_)
.build();
// 销毁着色器模块(管线创建后不再需要)
vk_device.destroyShaderModule(vert_module);
vk_device.destroyShaderModule(frag_module);
if (!pipeline_ || !pipeline_->is_valid()) {
MIRAI_LOG_ERROR("Failed to create graphics pipeline");
return false;
}
return true;
}
bool gradient_widget::create_descriptor_sets() {
auto vk_device = device_->get_device();
// 创建描述符集布局
vk::DescriptorSetLayoutBinding binding{
0,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
nullptr
};
vk::DescriptorSetLayoutCreateInfo layout_info{{}, 1, &binding};
auto [layout_result, layout] = vk_device.createDescriptorSetLayout(layout_info);
if (layout_result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to create descriptor set layout");
return false;
}
descriptor_set_layout_ = layout;
// 分配描述符集
auto set_result = pool_->allocate(descriptor_set_layout_);
if (!set_result) {
MIRAI_LOG_ERROR("Failed to allocate descriptor set");
return false;
}
descriptor_set_ = set_result.value()->get_vulkan_set();
// 更新描述符集
vk::DescriptorBufferInfo buffer_info{
uniform_buffer_->get_vulkan_buffer(),
0,
sizeof(widget_bounds_ubo)
};
vk::WriteDescriptorSet write{
descriptor_set_,
0,
0,
1,
vk::DescriptorType::eUniformBuffer,
nullptr,
&buffer_info,
nullptr
};
vk_device.updateDescriptorSets(1, &write, 0, nullptr);
return true;
}
bool gradient_widget::create_vertex_buffer() {
// 不需要顶点缓冲区,使用 gl_VertexIndex 生成顶点
return true;
}
bool gradient_widget::create_uniform_buffer() {
buffer_create_info info;
info.size = sizeof(widget_bounds_ubo);
info.usage = buffer_usage::uniform;
info.mem_usage = memory_usage::cpu_to_gpu;
info.persistent_mapped = true;
info.debug_name = "gradient_widget_uniform_buffer";
uniform_buffer_ = std::make_unique<buffer>(allocator_, info);
if (!uniform_buffer_->is_valid()) {
MIRAI_LOG_ERROR("Failed to create uniform buffer");
return false;
}
// 初始化 UBO 数据
uniform_buffer_->write(&bounds_, sizeof(bounds_), 0);
return true;
}
} // namespace demo
} // namespace mirai