351 lines
9.7 KiB
C++
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
|