Files
mirage/src/render/pipeline/mask_renderer.cpp
daiqingshuang b01cf247ec Refactor mask widget and renderer for improved functionality and performance
- 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.
2025-12-01 13:17:38 +08:00

647 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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