- Removed the MASK_WIDGET_DESIGN.md documentation file as it is no longer needed. - Updated the example pipeline to modify the corner radius of the rounded rectangle mask. - Enhanced the mask renderer to correctly calculate UV coordinates based on viewport size for better rendering accuracy. - Changed the get_mask_params method in circle_mask, rect_mask, and rounded_rect_mask to accept bounds by reference for efficiency. - Adjusted the mask factory function to ensure correct parameter order. - Refactored corner_radius method in rounded_rect_mask to accept a rect_corner_radius structure for more flexible corner radius settings.
647 lines
24 KiB
C++
647 lines
24 KiB
C++
#include "mask_renderer.h"
|
||
#include "vulkan/pipeline/graphics_pipeline.h"
|
||
#include "renderer/uniform_types.h"
|
||
#include <stdexcept>
|
||
#include <chrono>
|
||
#include <iostream>
|
||
|
||
// 引入着色器 SPIR-V 数据
|
||
#include "mask_vert_frag.h"
|
||
|
||
namespace mirage {
|
||
// ============================================================================
|
||
// 构造函数和析构函数
|
||
// ============================================================================
|
||
|
||
mask_renderer::mask_renderer(
|
||
logical_device& device,
|
||
resource_manager& res_mgr,
|
||
const config& cfg
|
||
) : device_(device)
|
||
, res_mgr_(res_mgr)
|
||
, config_(cfg) {
|
||
}
|
||
|
||
mask_renderer::~mask_renderer() {
|
||
cleanup();
|
||
}
|
||
|
||
// ============================================================================
|
||
// 初始化
|
||
// ============================================================================
|
||
|
||
void mask_renderer::initialize(vk::RenderPass render_pass, vk::Extent2D viewport_extent) {
|
||
render_pass_ = render_pass;
|
||
viewport_extent_ = viewport_extent;
|
||
|
||
create_descriptors();
|
||
create_pipeline(render_pass);
|
||
create_frame_resources();
|
||
|
||
// 预创建一些渲染目标到池中
|
||
for (uint32_t i = 0; i < config_.initial_target_pool_size; ++i) {
|
||
auto target = std::make_unique<offscreen_target>(device_, res_mgr_);
|
||
target->initialize(viewport_extent_.width, viewport_extent_.height);
|
||
available_targets_.push_back(target.get());
|
||
target_pool_.push_back(std::move(target));
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 描述符创建
|
||
// ============================================================================
|
||
|
||
void mask_renderer::create_descriptors() {
|
||
auto device_handle = device_.get_handle();
|
||
|
||
// 1. 创建 Descriptor Set Layout
|
||
vk::DescriptorSetLayoutBinding sampler_binding{};
|
||
sampler_binding.binding = 0;
|
||
sampler_binding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
|
||
sampler_binding.descriptorCount = 1;
|
||
sampler_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
|
||
|
||
vk::DescriptorSetLayoutCreateInfo layout_info{};
|
||
layout_info.bindingCount = 1;
|
||
layout_info.pBindings = &sampler_binding;
|
||
|
||
auto [layout_result, layout] = device_handle.createDescriptorSetLayout(layout_info);
|
||
if (layout_result != vk::Result::eSuccess) {
|
||
throw std::runtime_error("Failed to create descriptor set layout for mask renderer");
|
||
}
|
||
desc_set_layout_ = layout;
|
||
|
||
// 2. 创建 Sampler(描述符池在 create_frame_resources 中为每帧单独创建)
|
||
vk::SamplerCreateInfo sampler_info{};
|
||
sampler_info.magFilter = vk::Filter::eLinear;
|
||
sampler_info.minFilter = vk::Filter::eLinear;
|
||
sampler_info.addressModeU = vk::SamplerAddressMode::eClampToEdge;
|
||
sampler_info.addressModeV = vk::SamplerAddressMode::eClampToEdge;
|
||
sampler_info.addressModeW = vk::SamplerAddressMode::eClampToEdge;
|
||
sampler_info.anisotropyEnable = VK_FALSE;
|
||
sampler_info.maxAnisotropy = 1.0f;
|
||
sampler_info.borderColor = vk::BorderColor::eFloatTransparentBlack;
|
||
sampler_info.unnormalizedCoordinates = VK_FALSE;
|
||
sampler_info.compareEnable = VK_FALSE;
|
||
sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
|
||
|
||
auto [sampler_result, sampler] = device_handle.createSampler(sampler_info);
|
||
if (sampler_result != vk::Result::eSuccess) {
|
||
device_handle.destroyDescriptorSetLayout(desc_set_layout_);
|
||
throw std::runtime_error("Failed to create sampler for mask renderer");
|
||
}
|
||
sampler_ = sampler;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 管线创建
|
||
// ============================================================================
|
||
|
||
void mask_renderer::create_pipeline(vk::RenderPass render_pass) {
|
||
auto device_handle = device_.get_handle();
|
||
|
||
// 加载着色器
|
||
auto vert_shader_result = graphics_pipeline::load_shader_module_from_memory(
|
||
device_, shaders::mask_vert_spirv);
|
||
if (!vert_shader_result) {
|
||
throw std::runtime_error("Failed to load vertex shader for mask renderer");
|
||
}
|
||
auto vert_shader = vert_shader_result.value();
|
||
|
||
auto frag_shader_result = graphics_pipeline::load_shader_module_from_memory(
|
||
device_, shaders::mask_frag_spirv);
|
||
if (!frag_shader_result) {
|
||
device_handle.destroyShaderModule(vert_shader);
|
||
throw std::runtime_error("Failed to load fragment shader for mask renderer");
|
||
}
|
||
auto frag_shader = frag_shader_result.value();
|
||
|
||
// Push Constants
|
||
vk::PushConstantRange push_constant_range{
|
||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||
0, sizeof(push_constants)
|
||
};
|
||
|
||
// Pipeline Layout
|
||
vk::PipelineLayoutCreateInfo pipeline_layout_info{
|
||
{}, 1, &desc_set_layout_, 1, &push_constant_range
|
||
};
|
||
|
||
auto [result_layout, pipeline_layout] = device_handle.createPipelineLayout(pipeline_layout_info);
|
||
if (result_layout != vk::Result::eSuccess) {
|
||
device_handle.destroyShaderModule(vert_shader);
|
||
device_handle.destroyShaderModule(frag_shader);
|
||
throw std::runtime_error("Failed to create pipeline layout for mask renderer");
|
||
}
|
||
|
||
// 顶点输入
|
||
auto binding_desc = ui_vertex::get_binding_description();
|
||
auto attribute_descs = ui_vertex::get_attribute_descriptions();
|
||
auto vertex_input_info = ui_vertex::get_vertex_input_state(binding_desc, attribute_descs);
|
||
|
||
vk::PipelineInputAssemblyStateCreateInfo input_assembly{
|
||
{}, vk::PrimitiveTopology::eTriangleList, VK_FALSE
|
||
};
|
||
|
||
vk::Viewport viewport{0.0f, 0.0f, 800.0f, 600.0f, 0.0f, 1.0f};
|
||
vk::Rect2D scissor{{0, 0}, {800, 600}};
|
||
vk::PipelineViewportStateCreateInfo viewport_state{{}, 1, &viewport, 1, &scissor};
|
||
|
||
vk::PipelineRasterizationStateCreateInfo rasterizer{
|
||
{}, VK_FALSE, VK_FALSE, vk::PolygonMode::eFill,
|
||
vk::CullModeFlagBits::eNone, vk::FrontFace::eCounterClockwise,
|
||
VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f
|
||
};
|
||
|
||
vk::PipelineMultisampleStateCreateInfo multisampling{
|
||
{}, vk::SampleCountFlagBits::e1, VK_FALSE
|
||
};
|
||
|
||
vk::PipelineColorBlendAttachmentState color_blend_attachment{
|
||
VK_TRUE, vk::BlendFactor::eSrcAlpha, vk::BlendFactor::eOneMinusSrcAlpha,
|
||
vk::BlendOp::eAdd, vk::BlendFactor::eOne, vk::BlendFactor::eOneMinusSrcAlpha,
|
||
vk::BlendOp::eAdd,
|
||
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||
};
|
||
|
||
vk::PipelineColorBlendStateCreateInfo color_blending{
|
||
{}, VK_FALSE, vk::LogicOp::eCopy, 1, &color_blend_attachment
|
||
};
|
||
|
||
vk::PipelineDepthStencilStateCreateInfo depth_stencil{
|
||
{}, VK_FALSE, VK_FALSE, vk::CompareOp::eLess, VK_FALSE, VK_FALSE
|
||
};
|
||
|
||
std::vector<vk::DynamicState> dynamic_states = {
|
||
vk::DynamicState::eViewport, vk::DynamicState::eScissor
|
||
};
|
||
vk::PipelineDynamicStateCreateInfo dynamic_state{
|
||
{}, static_cast<uint32_t>(dynamic_states.size()), dynamic_states.data()
|
||
};
|
||
|
||
std::array<vk::PipelineShaderStageCreateInfo, 2> shader_stages = {
|
||
{
|
||
{{}, vk::ShaderStageFlagBits::eVertex, vert_shader, "main"},
|
||
{{}, vk::ShaderStageFlagBits::eFragment, frag_shader, "main"}
|
||
}
|
||
};
|
||
|
||
vk::GraphicsPipelineCreateInfo pipeline_info{
|
||
{}, static_cast<uint32_t>(shader_stages.size()), shader_stages.data(),
|
||
&vertex_input_info, &input_assembly, nullptr, &viewport_state,
|
||
&rasterizer, &multisampling, &depth_stencil, &color_blending,
|
||
&dynamic_state, pipeline_layout, render_pass, 0
|
||
};
|
||
|
||
auto [result_pipeline, pipeline] = device_handle.createGraphicsPipeline(nullptr, pipeline_info);
|
||
if (result_pipeline != vk::Result::eSuccess) {
|
||
device_handle.destroyPipelineLayout(pipeline_layout);
|
||
device_handle.destroyShaderModule(vert_shader);
|
||
device_handle.destroyShaderModule(frag_shader);
|
||
throw std::runtime_error("Failed to create graphics pipeline for mask renderer");
|
||
}
|
||
|
||
pipeline_ = pipeline;
|
||
pipeline_layout_ = pipeline_layout;
|
||
|
||
device_handle.destroyShaderModule(vert_shader);
|
||
device_handle.destroyShaderModule(frag_shader);
|
||
}
|
||
|
||
// ============================================================================
|
||
// 帧资源创建
|
||
// ============================================================================
|
||
|
||
void mask_renderer::create_frame_resources() {
|
||
auto device_handle = device_.get_handle();
|
||
|
||
constexpr uint32_t vertices_per_mask = 6;
|
||
constexpr uint32_t indices_per_mask = 6;
|
||
constexpr uint32_t max_masks = 16;
|
||
|
||
frame_data_.reserve(config_.frames_in_flight);
|
||
|
||
for (uint32_t i = 0; i < config_.frames_in_flight; ++i) {
|
||
auto vb_result = res_mgr_.create_buffer(
|
||
vertices_per_mask * max_masks * sizeof(ui_vertex),
|
||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||
);
|
||
|
||
if (!vb_result) {
|
||
throw std::runtime_error("Failed to create vertex buffer for mask renderer");
|
||
}
|
||
|
||
auto ib_result = res_mgr_.create_buffer(
|
||
indices_per_mask * max_masks * sizeof(uint32_t),
|
||
vk::BufferUsageFlagBits::eIndexBuffer,
|
||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||
);
|
||
|
||
if (!ib_result) {
|
||
res_mgr_.destroy_buffer(vb_result.value());
|
||
throw std::runtime_error("Failed to create index buffer for mask renderer");
|
||
}
|
||
|
||
// 为每帧创建独立的描述符池
|
||
vk::DescriptorPoolSize pool_size{};
|
||
pool_size.type = vk::DescriptorType::eCombinedImageSampler;
|
||
pool_size.descriptorCount = max_masks;
|
||
|
||
vk::DescriptorPoolCreateInfo pool_info{};
|
||
pool_info.poolSizeCount = 1;
|
||
pool_info.pPoolSizes = &pool_size;
|
||
pool_info.maxSets = max_masks;
|
||
// 不使用 eFreeDescriptorSet,因为我们使用 resetDescriptorPool 整体重置
|
||
|
||
auto [pool_result, pool] = device_handle.createDescriptorPool(pool_info);
|
||
if (pool_result != vk::Result::eSuccess) {
|
||
res_mgr_.destroy_buffer(vb_result.value());
|
||
res_mgr_.destroy_buffer(ib_result.value());
|
||
throw std::runtime_error("Failed to create descriptor pool for mask renderer frame resources");
|
||
}
|
||
|
||
frame_data_.emplace_back(frame_data{
|
||
typed_buffer<ui_vertex>(device_handle, std::move(vb_result.value()),
|
||
vertices_per_mask * max_masks),
|
||
typed_buffer<uint32_t>(device_handle, std::move(ib_result.value()),
|
||
indices_per_mask * max_masks),
|
||
pool
|
||
});
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 帧管理
|
||
// ============================================================================
|
||
|
||
void mask_renderer::begin_frame(uint32_t frame_index, const vec2f_t& viewport_size) {
|
||
current_frame_ = frame_index % config_.frames_in_flight;
|
||
viewport_size_ = viewport_size;
|
||
|
||
auto& frame = frame_data_[current_frame_];
|
||
frame.used_vertex_count = 0;
|
||
frame.used_index_count = 0;
|
||
|
||
// 重置当前帧的描述符池,释放所有之前分配的描述符集
|
||
device_.get_handle().resetDescriptorPool(frame.desc_pool);
|
||
}
|
||
|
||
void mask_renderer::end_frame() {
|
||
// 新版 API 使用类型安全的上下文管理,无需清理栈
|
||
}
|
||
|
||
// ============================================================================
|
||
// 渲染目标池管理
|
||
// ============================================================================
|
||
|
||
auto mask_renderer::acquire_render_target() -> offscreen_target* {
|
||
if (!available_targets_.empty()) {
|
||
auto* target = available_targets_.back();
|
||
available_targets_.pop_back();
|
||
|
||
if (target->extent().width != viewport_extent_.width ||
|
||
target->extent().height != viewport_extent_.height) {
|
||
target->resize(viewport_extent_.width, viewport_extent_.height);
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
auto target = std::make_unique<offscreen_target>(device_, res_mgr_);
|
||
target->initialize(viewport_extent_.width, viewport_extent_.height);
|
||
auto* ptr = target.get();
|
||
target_pool_.push_back(std::move(target));
|
||
return ptr;
|
||
}
|
||
|
||
void mask_renderer::release_render_target(offscreen_target* target) {
|
||
if (target) {
|
||
available_targets_.push_back(target);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 类型安全的遮罩渲染接口实现 (新 API)
|
||
// ============================================================================
|
||
|
||
auto mask_renderer::begin_mask(pass_state::frame_context<pass_state::offscreen_pass_tag>&& parent_ctx,
|
||
const mask_begin_command& cmd_data)
|
||
-> std::pair<pass_state::mask_context, pass_state::frame_context<pass_state::mask_pass_tag>> {
|
||
// 获取遮罩渲染目标
|
||
auto* mask_target = acquire_render_target();
|
||
|
||
// 使用 nested_pass_context 进入遮罩 Pass
|
||
// begin_mask_pass 会:
|
||
// 1. 结束父目标的 RenderPass
|
||
// 2. 开始遮罩目标的 RenderPass
|
||
// 3. 返回嵌套上下文和遮罩帧上下文
|
||
return pass_state::begin_mask_pass(std::move(parent_ctx), *mask_target, true);
|
||
}
|
||
|
||
auto mask_renderer::begin_nested_mask(pass_state::frame_context<pass_state::mask_pass_tag>&& parent_ctx,
|
||
const mask_begin_command& cmd_data)
|
||
-> std::pair<pass_state::nested_mask_context, pass_state::frame_context<pass_state::mask_pass_tag>> {
|
||
// 获取内层遮罩渲染目标
|
||
auto* inner_mask_target = acquire_render_target();
|
||
|
||
// 使用 nested_pass_context 进入嵌套遮罩 Pass
|
||
return pass_state::begin_nested_mask_pass(std::move(parent_ctx), *inner_mask_target, true);
|
||
}
|
||
|
||
auto mask_renderer::end_mask(pass_state::mask_context&& nested_ctx,
|
||
pass_state::frame_context<pass_state::mask_pass_tag>&& mask_ctx)
|
||
-> std::pair<pass_state::frame_context<pass_state::offscreen_pass_tag>, pass_state::mask_pass_result> {
|
||
// 获取遮罩边界(从当前遮罩目标)
|
||
aabb2d_t bounds{};
|
||
|
||
// 使用 end_mask_pass 结束遮罩 Pass
|
||
// 这会:
|
||
// 1. 结束遮罩目标的 RenderPass,转换为 shader read
|
||
// 2. 重新开始父目标的 RenderPass
|
||
// 3. 返回父上下文和遮罩结果
|
||
return pass_state::end_mask_pass(std::move(nested_ctx), std::move(mask_ctx), bounds);
|
||
}
|
||
|
||
auto mask_renderer::end_nested_mask(pass_state::nested_mask_context&& nested_ctx,
|
||
pass_state::frame_context<pass_state::mask_pass_tag>&& inner_mask_ctx)
|
||
-> std::pair<pass_state::frame_context<pass_state::mask_pass_tag>, pass_state::mask_pass_result> {
|
||
// 获取遮罩边界
|
||
aabb2d_t bounds{};
|
||
|
||
// 使用 end_nested_mask_pass 结束嵌套遮罩 Pass
|
||
return pass_state::end_nested_mask_pass(std::move(nested_ctx), std::move(inner_mask_ctx), bounds);
|
||
}
|
||
|
||
auto mask_renderer::get_current_target() const -> offscreen_target* {
|
||
// 新版 API 通过 frame_context 管理当前目标
|
||
return nullptr;
|
||
}
|
||
|
||
auto mask_renderer::is_in_mask() const -> bool {
|
||
// 新版 API 通过类型状态管理遮罩状态
|
||
return false;
|
||
}
|
||
|
||
auto mask_renderer::get_mask_depth() const -> size_t {
|
||
// 新版 API 通过 nested_pass_context 管理嵌套深度
|
||
return 0;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 描述符分配
|
||
// ============================================================================
|
||
|
||
auto mask_renderer::allocate_descriptor_set(vk::ImageView image_view) -> vk::DescriptorSet {
|
||
vk::DescriptorSetAllocateInfo alloc_info{};
|
||
alloc_info.descriptorPool = frame_data_[current_frame_].desc_pool;
|
||
alloc_info.descriptorSetCount = 1;
|
||
alloc_info.pSetLayouts = &desc_set_layout_;
|
||
|
||
auto [result, sets] = device_.get_handle().allocateDescriptorSets(alloc_info);
|
||
if (result != vk::Result::eSuccess) {
|
||
throw std::runtime_error("Failed to allocate descriptor set for mask renderer");
|
||
}
|
||
vk::DescriptorSet desc_set = sets[0];
|
||
|
||
vk::DescriptorImageInfo image_info{};
|
||
image_info.sampler = sampler_;
|
||
image_info.imageView = image_view;
|
||
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||
|
||
vk::WriteDescriptorSet write{};
|
||
write.dstSet = desc_set;
|
||
write.dstBinding = 0;
|
||
write.dstArrayElement = 0;
|
||
write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
|
||
write.descriptorCount = 1;
|
||
write.pImageInfo = &image_info;
|
||
|
||
device_.get_handle().updateDescriptorSets(1, &write, 0, nullptr);
|
||
|
||
return desc_set;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 遮罩绘制实现
|
||
// ============================================================================
|
||
|
||
void mask_renderer::draw_mask_quad_impl(vk::CommandBuffer cmd,
|
||
const mask_params& params,
|
||
const aabb2d_t& bounds,
|
||
vk::ImageView mask_texture_view) {
|
||
auto& frame_res = frame_data_[current_frame_];
|
||
|
||
auto vertices = generate_mask_vertices(params, bounds);
|
||
std::array<uint32_t, 6> indices = {0, 1, 2, 2, 3, 0};
|
||
|
||
if (frame_res.used_vertex_count + vertices.size() > frame_res.vertices.size() ||
|
||
frame_res.used_index_count + indices.size() > frame_res.indices.size()) {
|
||
std::cerr << "[MASK_RENDERER] Buffer overflow" << std::endl;
|
||
return;
|
||
}
|
||
|
||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline_);
|
||
|
||
vk::Viewport viewport{0.0f, 0.0f, viewport_size_.x(), viewport_size_.y(), 0.0f, 1.0f};
|
||
cmd.setViewport(0, 1, &viewport);
|
||
|
||
vk::Rect2D scissor{
|
||
{0, 0}, {
|
||
static_cast<uint32_t>(viewport_size_.x()),
|
||
static_cast<uint32_t>(viewport_size_.y())
|
||
}
|
||
};
|
||
cmd.setScissor(0, 1, &scissor);
|
||
|
||
auto desc_set = allocate_descriptor_set(mask_texture_view);
|
||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout_,
|
||
0, 1, &desc_set, 0, nullptr);
|
||
|
||
static auto start_time = std::chrono::high_resolution_clock::now();
|
||
auto current_time = std::chrono::high_resolution_clock::now();
|
||
float time = std::chrono::duration<float>(current_time - start_time).count();
|
||
|
||
auto constants = create_push_constants(viewport_size_.x(), viewport_size_.y(), time);
|
||
cmd.pushConstants(pipeline_layout_,
|
||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||
0, sizeof(push_constants), &constants);
|
||
|
||
std::vector<ui_vertex> vertex_vec(vertices.begin(), vertices.end());
|
||
frame_res.vertices.upload(vertex_vec, frame_res.used_vertex_count);
|
||
|
||
std::vector<uint32_t> index_vec(indices.begin(), indices.end());
|
||
frame_res.indices.upload(index_vec, frame_res.used_index_count);
|
||
|
||
vk::Buffer vertex_buffers[] = {frame_res.vertices.get_buffer().buffer};
|
||
vk::DeviceSize offsets[] = {0};
|
||
cmd.bindVertexBuffers(0, 1, vertex_buffers, offsets);
|
||
cmd.bindIndexBuffer(frame_res.indices.get_buffer().buffer, 0, vk::IndexType::eUint32);
|
||
|
||
cmd.drawIndexed(static_cast<uint32_t>(indices.size()), 1,
|
||
frame_res.used_index_count,
|
||
static_cast<int32_t>(frame_res.used_vertex_count), 0);
|
||
|
||
frame_res.used_vertex_count += static_cast<uint32_t>(vertices.size());
|
||
frame_res.used_index_count += static_cast<uint32_t>(indices.size());
|
||
}
|
||
|
||
auto mask_renderer::generate_mask_vertices(const mask_params& params,
|
||
const aabb2d_t& bounds) -> std::array<ui_vertex, 6> {
|
||
float left = bounds.min().x();
|
||
float top = bounds.min().y();
|
||
float right = bounds.max().x();
|
||
float bottom = bounds.max().y();
|
||
|
||
// 计算 UV 坐标(基于视口大小)
|
||
// UV 坐标需要映射到离屏目标中子控件的实际位置
|
||
float uv_left = left / viewport_size_.x();
|
||
float uv_top = top / viewport_size_.y();
|
||
float uv_right = right / viewport_size_.x();
|
||
float uv_bottom = bottom / viewport_size_.y();
|
||
|
||
// 使用实际边界框尺寸,而不是遮罩参数中的尺寸
|
||
// 这样着色器才能正确计算非正方形区域中的圆形遮罩
|
||
float width = bounds.sizes().x();
|
||
float height = bounds.sizes().y();
|
||
|
||
float mask_type_f = static_cast<float>(static_cast<uint32_t>(params.type));
|
||
float corner_tl = params.corner_radii.x();
|
||
float corner_tr = params.corner_radii.y();
|
||
float corner_br = params.corner_radii.z();
|
||
float corner_bl = params.corner_radii.w();
|
||
|
||
std::array<ui_vertex, 6> vertices{};
|
||
|
||
// 顶点 0: 左上
|
||
vertices[0].position[0] = left;
|
||
vertices[0].position[1] = top;
|
||
vertices[0].color[0] = 1.0f;
|
||
vertices[0].color[1] = 1.0f;
|
||
vertices[0].color[2] = 1.0f;
|
||
vertices[0].color[3] = 1.0f;
|
||
vertices[0].uv[0] = uv_left;
|
||
vertices[0].uv[1] = uv_top;
|
||
vertices[0].data_a[0] = mask_type_f;
|
||
vertices[0].data_a[1] = params.softness;
|
||
vertices[0].data_a[2] = width;
|
||
vertices[0].data_a[3] = height;
|
||
vertices[0].data_b[0] = corner_tl;
|
||
vertices[0].data_b[1] = corner_tr;
|
||
vertices[0].data_b[2] = corner_br;
|
||
vertices[0].data_b[3] = corner_bl;
|
||
|
||
// 顶点 1: 右上
|
||
vertices[1].position[0] = right;
|
||
vertices[1].position[1] = top;
|
||
vertices[1].color[0] = 1.0f;
|
||
vertices[1].color[1] = 1.0f;
|
||
vertices[1].color[2] = 1.0f;
|
||
vertices[1].color[3] = 1.0f;
|
||
vertices[1].uv[0] = uv_right;
|
||
vertices[1].uv[1] = uv_top;
|
||
vertices[1].data_a[0] = mask_type_f;
|
||
vertices[1].data_a[1] = params.softness;
|
||
vertices[1].data_a[2] = width;
|
||
vertices[1].data_a[3] = height;
|
||
vertices[1].data_b[0] = corner_tl;
|
||
vertices[1].data_b[1] = corner_tr;
|
||
vertices[1].data_b[2] = corner_br;
|
||
vertices[1].data_b[3] = corner_bl;
|
||
|
||
// 顶点 2: 右下
|
||
vertices[2].position[0] = right;
|
||
vertices[2].position[1] = bottom;
|
||
vertices[2].color[0] = 1.0f;
|
||
vertices[2].color[1] = 1.0f;
|
||
vertices[2].color[2] = 1.0f;
|
||
vertices[2].color[3] = 1.0f;
|
||
vertices[2].uv[0] = uv_right;
|
||
vertices[2].uv[1] = uv_bottom;
|
||
vertices[2].data_a[0] = mask_type_f;
|
||
vertices[2].data_a[1] = params.softness;
|
||
vertices[2].data_a[2] = width;
|
||
vertices[2].data_a[3] = height;
|
||
vertices[2].data_b[0] = corner_tl;
|
||
vertices[2].data_b[1] = corner_tr;
|
||
vertices[2].data_b[2] = corner_br;
|
||
vertices[2].data_b[3] = corner_bl;
|
||
|
||
// 顶点 3: 左下
|
||
vertices[3].position[0] = left;
|
||
vertices[3].position[1] = bottom;
|
||
vertices[3].color[0] = 1.0f;
|
||
vertices[3].color[1] = 1.0f;
|
||
vertices[3].color[2] = 1.0f;
|
||
vertices[3].color[3] = 1.0f;
|
||
vertices[3].uv[0] = uv_left;
|
||
vertices[3].uv[1] = uv_bottom;
|
||
vertices[3].data_a[0] = mask_type_f;
|
||
vertices[3].data_a[1] = params.softness;
|
||
vertices[3].data_a[2] = width;
|
||
vertices[3].data_a[3] = height;
|
||
vertices[3].data_b[0] = corner_tl;
|
||
vertices[3].data_b[1] = corner_tr;
|
||
vertices[3].data_b[2] = corner_br;
|
||
vertices[3].data_b[3] = corner_bl;
|
||
|
||
// 复制顶点用于两个三角形 (0,1,2) 和 (2,3,0)
|
||
vertices[4] = vertices[2];
|
||
vertices[5] = vertices[0];
|
||
|
||
return vertices;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 清理和调整大小
|
||
// ============================================================================
|
||
|
||
void mask_renderer::cleanup() {
|
||
auto device_handle = device_.get_handle();
|
||
|
||
auto result = device_handle.waitIdle();
|
||
(void)result;
|
||
|
||
if (pipeline_) {
|
||
device_handle.destroyPipeline(pipeline_);
|
||
pipeline_ = nullptr;
|
||
}
|
||
|
||
if (pipeline_layout_) {
|
||
device_handle.destroyPipelineLayout(pipeline_layout_);
|
||
pipeline_layout_ = nullptr;
|
||
}
|
||
|
||
if (desc_set_layout_) {
|
||
device_handle.destroyDescriptorSetLayout(desc_set_layout_);
|
||
desc_set_layout_ = nullptr;
|
||
}
|
||
|
||
if (sampler_) {
|
||
device_handle.destroySampler(sampler_);
|
||
sampler_ = nullptr;
|
||
}
|
||
|
||
for (auto& frame : frame_data_) {
|
||
if (frame.desc_pool) {
|
||
device_handle.destroyDescriptorPool(frame.desc_pool);
|
||
}
|
||
res_mgr_.destroy_buffer(frame.vertices.get_buffer());
|
||
res_mgr_.destroy_buffer(frame.indices.get_buffer());
|
||
}
|
||
frame_data_.clear();
|
||
|
||
available_targets_.clear();
|
||
target_pool_.clear();
|
||
}
|
||
|
||
void mask_renderer::resize(vk::Extent2D new_extent) {
|
||
viewport_extent_ = new_extent;
|
||
|
||
for (auto& target : target_pool_) {
|
||
target->resize(new_extent.width, new_extent.height);
|
||
}
|
||
}
|
||
} // namespace mirage
|