746 lines
24 KiB
C++
746 lines
24 KiB
C++
/**
|
|
* @file renderer.cpp
|
|
* @brief MIRAI 框架渲染器主类实现
|
|
* @author MIRAI Team
|
|
* @version 0.1.0
|
|
*/
|
|
|
|
#include "renderer.hpp"
|
|
#include "logger.hpp"
|
|
|
|
#include <SDL3/SDL_vulkan.h>
|
|
|
|
namespace mirai {
|
|
|
|
renderer::renderer(const renderer_config& config)
|
|
: config_(config)
|
|
, frame_time_tracker_(60) // 60 帧采样
|
|
{
|
|
}
|
|
|
|
renderer::~renderer() {
|
|
shutdown();
|
|
}
|
|
|
|
bool renderer::initialize(SDL_Window* window) {
|
|
if (state_ != render_state::uninitialized) {
|
|
MIRAI_LOG_WARN("Renderer already initialized");
|
|
return true;
|
|
}
|
|
|
|
if (!window) {
|
|
MIRAI_LOG_ERROR("Invalid window pointer");
|
|
return false;
|
|
}
|
|
|
|
window_ = window;
|
|
|
|
// 获取窗口大小
|
|
int width, height;
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
config_.viewport_width = static_cast<u32>(width);
|
|
config_.viewport_height = static_cast<u32>(height);
|
|
|
|
// 1. 创建 Vulkan 实例
|
|
vulkan_instance_config instance_config{};
|
|
instance_config.app_name = config_.app_name;
|
|
instance_config.app_version = config_.app_version;
|
|
instance_config.enable_validation = config_.enable_validation;
|
|
|
|
// SDL 扩展由 vulkan_instance 自动获取,无需手动添加
|
|
|
|
instance_ = std::make_shared<vulkan_instance>(instance_config);
|
|
if (!instance_->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to create Vulkan instance");
|
|
return false;
|
|
}
|
|
|
|
// 2. 创建表面
|
|
if (!create_surface(window)) {
|
|
MIRAI_LOG_ERROR("Failed to create Vulkan surface");
|
|
return false;
|
|
}
|
|
|
|
// 3. 创建设备
|
|
vulkan_device_config device_config{};
|
|
device_config.extra_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
|
device_config.require_dynamic_rendering = true;
|
|
device_config.require_synchronization2 = true;
|
|
|
|
device_ = std::make_shared<vulkan_device>(instance_, surface_, device_config);
|
|
if (!device_->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to create Vulkan device");
|
|
return false;
|
|
}
|
|
|
|
// 4. 创建 VMA 分配器
|
|
if (!create_allocator()) {
|
|
MIRAI_LOG_ERROR("Failed to create VMA allocator");
|
|
return false;
|
|
}
|
|
|
|
// 5. 创建交换链
|
|
if (!create_swapchain(config_.viewport_width, config_.viewport_height)) {
|
|
MIRAI_LOG_ERROR("Failed to create swapchain");
|
|
return false;
|
|
}
|
|
|
|
// 6. 创建深度资源
|
|
if (!create_depth_resources()) {
|
|
MIRAI_LOG_ERROR("Failed to create depth resources");
|
|
return false;
|
|
}
|
|
|
|
// 7. 创建命令池
|
|
if (!create_command_pools()) {
|
|
MIRAI_LOG_ERROR("Failed to create command pools");
|
|
return false;
|
|
}
|
|
|
|
// 8. 创建帧同步管理器
|
|
frame_sync_config sync_config{};
|
|
sync_config.frames_in_flight = config_.frames_in_flight;
|
|
|
|
frame_sync_ = std::make_unique<frame_sync>(device_, swapchain_, sync_config);
|
|
if (!frame_sync_->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to create frame sync");
|
|
return false;
|
|
}
|
|
|
|
// 9. 创建批处理渲染器
|
|
batch_renderer_config batch_config{};
|
|
batch_config.max_quads = config_.max_batch_quads;
|
|
batch_config.viewport_width = config_.viewport_width;
|
|
batch_config.viewport_height = config_.viewport_height;
|
|
|
|
batch_renderer_ = std::make_unique<batch_renderer>(device_, allocator_, batch_config);
|
|
if (!batch_renderer_->initialize(swapchain_->get_format(), depth_format_)) {
|
|
MIRAI_LOG_WARN("Batch renderer initialization incomplete (needs shaders)");
|
|
// 不视为致命错误,因为着色器可能稍后加载
|
|
}
|
|
|
|
state_ = render_state::initialized;
|
|
MIRAI_LOG_INFO("Renderer initialized successfully");
|
|
MIRAI_LOG_INFO(" - Vulkan Instance: {}", (void*)static_cast<VkInstance>(instance_->get_instance()));
|
|
MIRAI_LOG_INFO(" - Physical Device: {}", device_->get_device_name());
|
|
MIRAI_LOG_INFO(" - Swapchain: {}x{}", swapchain_->get_extent().width, swapchain_->get_extent().height);
|
|
MIRAI_LOG_INFO(" - Frames in Flight: {}", config_.frames_in_flight);
|
|
|
|
return true;
|
|
}
|
|
|
|
void renderer::shutdown() {
|
|
if (state_ == render_state::uninitialized) {
|
|
return;
|
|
}
|
|
|
|
// 等待设备空闲
|
|
if (device_ && device_->is_valid()) {
|
|
device_->wait_idle();
|
|
}
|
|
|
|
// 销毁资源(按创建的逆序)
|
|
destroy_resources();
|
|
|
|
state_ = render_state::uninitialized;
|
|
MIRAI_LOG_INFO("Renderer shutdown");
|
|
}
|
|
|
|
bool renderer::begin_frame() {
|
|
if (state_ != render_state::initialized) {
|
|
if (state_ == render_state::frame_in_progress) {
|
|
MIRAI_LOG_WARN("Frame already in progress");
|
|
return true;
|
|
}
|
|
MIRAI_LOG_ERROR("Cannot begin frame: renderer not in valid state");
|
|
return false;
|
|
}
|
|
|
|
// 检查窗口是否最小化
|
|
const auto window_flags = SDL_GetWindowFlags(window_);
|
|
window_minimized_ = (window_flags & SDL_WINDOW_MINIMIZED) != 0;
|
|
|
|
if (window_minimized_) {
|
|
// 窗口最小化时不渲染
|
|
return false;
|
|
}
|
|
|
|
// 开始帧计时
|
|
frame_time_tracker_.begin_frame();
|
|
|
|
// 开始帧同步
|
|
auto [result, image_index] = frame_sync_->begin_frame();
|
|
|
|
if (result == frame_begin_result::swapchain_out_of_date) {
|
|
// 需要重建交换链
|
|
int width, height;
|
|
SDL_GetWindowSize(window_, &width, &height);
|
|
|
|
if (!recreate_swapchain(static_cast<u32>(width), static_cast<u32>(height))) {
|
|
MIRAI_LOG_ERROR("Failed to recreate swapchain");
|
|
state_ = render_state::error;
|
|
return false;
|
|
}
|
|
|
|
// 重试
|
|
auto [retry_result, retry_index] = frame_sync_->begin_frame();
|
|
if (retry_result != frame_begin_result::success) {
|
|
MIRAI_LOG_ERROR("Failed to begin frame after swapchain recreation");
|
|
return false;
|
|
}
|
|
image_index = retry_index;
|
|
} else if (result != frame_begin_result::success) {
|
|
MIRAI_LOG_ERROR("Failed to begin frame");
|
|
return false;
|
|
}
|
|
|
|
current_frame_index_ = frame_sync_->get_current_frame_index();
|
|
current_image_index_ = image_index;
|
|
|
|
// 重置并开始命令缓冲录制
|
|
vk::CommandBuffer cmd_buffer = frame_sync_->get_current_command_buffer();
|
|
|
|
vk::CommandBufferBeginInfo begin_info{};
|
|
begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
|
|
|
auto vk_result = cmd_buffer.begin(&begin_info);
|
|
if (vk_result != vk::Result::eSuccess) {
|
|
MIRAI_LOG_ERROR("Failed to begin command buffer: {}", vk_result_to_string(vk_result));
|
|
return false;
|
|
}
|
|
|
|
state_ = render_state::frame_in_progress;
|
|
|
|
// 调用帧开始回调
|
|
if (on_frame_begin_) {
|
|
on_frame_begin_();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool renderer::end_frame() {
|
|
if (state_ != render_state::frame_in_progress &&
|
|
state_ != render_state::rendering_in_progress) {
|
|
MIRAI_LOG_ERROR("Cannot end frame: frame not in progress");
|
|
return false;
|
|
}
|
|
|
|
// 如果正在渲染中,先结束渲染
|
|
if (state_ == render_state::rendering_in_progress) {
|
|
end_rendering();
|
|
}
|
|
|
|
vk::CommandBuffer cmd_buffer = frame_sync_->get_current_command_buffer();
|
|
|
|
// 结束命令缓冲录制
|
|
auto vk_result = cmd_buffer.end();
|
|
if (vk_result != vk::Result::eSuccess) {
|
|
MIRAI_LOG_ERROR("Failed to end command buffer: {}", vk_result_to_string(vk_result));
|
|
return false;
|
|
}
|
|
|
|
// 提交并呈现
|
|
auto result = frame_sync_->end_frame(cmd_buffer);
|
|
|
|
if (result == frame_end_result::swapchain_out_of_date ||
|
|
result == frame_end_result::swapchain_suboptimal) {
|
|
swapchain_needs_recreation_ = true;
|
|
} else if (result != frame_end_result::success) {
|
|
MIRAI_LOG_ERROR("Failed to end frame");
|
|
return false;
|
|
}
|
|
|
|
// 如果需要重建交换链,在下一帧开始时处理
|
|
if (swapchain_needs_recreation_ && config_.auto_recreate_swapchain) {
|
|
int width, height;
|
|
SDL_GetWindowSize(window_, &width, &height);
|
|
|
|
if (width > 0 && height > 0) {
|
|
recreate_swapchain(static_cast<u32>(width), static_cast<u32>(height));
|
|
}
|
|
swapchain_needs_recreation_ = false;
|
|
}
|
|
|
|
state_ = render_state::initialized;
|
|
|
|
// 结束帧计时
|
|
frame_time_tracker_.end_frame();
|
|
++frame_count_;
|
|
|
|
// 调用帧结束回调
|
|
if (on_frame_end_) {
|
|
on_frame_end_();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void renderer::begin_rendering(const render_target& target) {
|
|
if (state_ != render_state::frame_in_progress) {
|
|
MIRAI_LOG_ERROR("Cannot begin rendering: frame not in progress");
|
|
return;
|
|
}
|
|
|
|
vk::CommandBuffer cmd_buffer = frame_sync_->get_current_command_buffer();
|
|
|
|
// 转换交换链图像布局到颜色附件
|
|
vk::Image swapchain_image = swapchain_->get_images()[current_image_index_];
|
|
transition_image_layout(cmd_buffer, swapchain_image,
|
|
vk::ImageLayout::eUndefined,
|
|
vk::ImageLayout::eColorAttachmentOptimal);
|
|
|
|
// 构建渲染信息
|
|
rendering_info render_info(target);
|
|
|
|
// 开始动态渲染
|
|
cmd_buffer.beginRendering(render_info.get_vulkan_rendering_info());
|
|
|
|
// 设置视口和裁剪
|
|
vk::Viewport viewport{};
|
|
viewport.x = 0.0f;
|
|
viewport.y = 0.0f;
|
|
viewport.width = static_cast<f32>(target.render_area.extent.width);
|
|
viewport.height = static_cast<f32>(target.render_area.extent.height);
|
|
viewport.minDepth = 0.0f;
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
cmd_buffer.setViewport(0, 1, &viewport);
|
|
|
|
vk::Rect2D scissor = target.render_area;
|
|
cmd_buffer.setScissor(0, 1, &scissor);
|
|
|
|
state_ = render_state::rendering_in_progress;
|
|
}
|
|
|
|
void renderer::begin_rendering(const clear_color& clear) {
|
|
// 创建默认渲染目标(使用当前交换链图像)
|
|
render_attachment color_attachment{};
|
|
color_attachment.image_view = swapchain_->get_image_views()[current_image_index_];
|
|
color_attachment.image_layout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
color_attachment.load_op = attachment_load_op::clear;
|
|
color_attachment.store_op = attachment_store_op::store;
|
|
color_attachment.clear_value = clear;
|
|
|
|
render_target target;
|
|
target.color_attachments.push_back(color_attachment);
|
|
target.render_area.offset = vk::Offset2D{0, 0};
|
|
target.render_area.extent = swapchain_->get_extent();
|
|
|
|
// 添加深度附件(如果有)
|
|
if (depth_image_view_) {
|
|
render_attachment depth_attachment{};
|
|
depth_attachment.image_view = depth_image_view_;
|
|
depth_attachment.image_layout = vk::ImageLayout::eDepthAttachmentOptimal;
|
|
depth_attachment.load_op = attachment_load_op::clear;
|
|
depth_attachment.store_op = attachment_store_op::store;
|
|
depth_attachment.clear_depth_stencil_value = clear_depth_stencil(1.0f, 0);
|
|
target.depth_attachment = depth_attachment;
|
|
}
|
|
|
|
begin_rendering(target);
|
|
}
|
|
|
|
void renderer::end_rendering() {
|
|
if (state_ != render_state::rendering_in_progress) {
|
|
MIRAI_LOG_ERROR("Cannot end rendering: not in rendering state");
|
|
return;
|
|
}
|
|
|
|
vk::CommandBuffer cmd_buffer = frame_sync_->get_current_command_buffer();
|
|
|
|
// 结束动态渲染
|
|
cmd_buffer.endRendering();
|
|
|
|
// 转换交换链图像布局到呈现
|
|
vk::Image swapchain_image = swapchain_->get_images()[current_image_index_];
|
|
transition_image_layout(cmd_buffer, swapchain_image,
|
|
vk::ImageLayout::eColorAttachmentOptimal,
|
|
vk::ImageLayout::ePresentSrcKHR);
|
|
|
|
state_ = render_state::frame_in_progress;
|
|
}
|
|
|
|
vk::CommandBuffer renderer::get_command_buffer() const {
|
|
if (frame_sync_) {
|
|
return frame_sync_->get_current_command_buffer();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
command_buffer* renderer::get_current_command_buffer() {
|
|
if (current_frame_index_ < command_buffers_.size()) {
|
|
return command_buffers_[current_frame_index_].get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool renderer::submit(vk::CommandBuffer cmd_buffer) {
|
|
// 直接提交到图形队列
|
|
vk::SubmitInfo submit_info{};
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &cmd_buffer;
|
|
|
|
auto result = device_->get_graphics_queue().submit(1, &submit_info, nullptr);
|
|
return result == vk::Result::eSuccess;
|
|
}
|
|
|
|
bool renderer::present() {
|
|
// 呈现由 end_frame 自动处理
|
|
MIRAI_LOG_WARN("present() called directly; use end_frame() instead");
|
|
return true;
|
|
}
|
|
|
|
void renderer::wait_idle() {
|
|
if (device_ && device_->is_valid()) {
|
|
device_->wait_idle();
|
|
}
|
|
}
|
|
|
|
bool renderer::on_resize(u32 width, u32 height) {
|
|
if (width == 0 || height == 0) {
|
|
window_minimized_ = true;
|
|
return true;
|
|
}
|
|
|
|
window_minimized_ = false;
|
|
swapchain_needs_recreation_ = true;
|
|
|
|
return recreate_swapchain(width, height);
|
|
}
|
|
|
|
u32 renderer::get_current_frame_index() const {
|
|
return current_frame_index_;
|
|
}
|
|
|
|
u32 renderer::get_current_image_index() const {
|
|
return current_image_index_;
|
|
}
|
|
|
|
vk::Extent2D renderer::get_swapchain_extent() const {
|
|
return swapchain_ ? swapchain_->get_extent() : vk::Extent2D{0, 0};
|
|
}
|
|
|
|
vk::Format renderer::get_swapchain_format() const {
|
|
return swapchain_ ? swapchain_->get_format() : vk::Format::eUndefined;
|
|
}
|
|
|
|
vk::Instance renderer::get_vulkan_instance() const {
|
|
return instance_ ? instance_->get_instance() : nullptr;
|
|
}
|
|
|
|
vk::Device renderer::get_vulkan_device() const {
|
|
return device_ ? device_->get_device() : nullptr;
|
|
}
|
|
|
|
vk::PhysicalDevice renderer::get_vulkan_physical_device() const {
|
|
return device_ ? device_->get_physical_device() : nullptr;
|
|
}
|
|
|
|
vk::Queue renderer::get_graphics_queue() const {
|
|
return device_ ? device_->get_graphics_queue() : nullptr;
|
|
}
|
|
|
|
vk::Queue renderer::get_present_queue() const {
|
|
return device_ ? device_->get_present_queue() : nullptr;
|
|
}
|
|
|
|
void renderer::on_created() {
|
|
MIRAI_LOG_DEBUG("Renderer created");
|
|
}
|
|
|
|
void renderer::on_destroying() {
|
|
MIRAI_LOG_DEBUG("Renderer destroying");
|
|
}
|
|
|
|
bool renderer::create_allocator() {
|
|
VmaAllocatorCreateInfo allocator_info{};
|
|
allocator_info.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
|
|
allocator_info.vulkanApiVersion = VK_API_VERSION_1_3;
|
|
allocator_info.physicalDevice = device_->get_physical_device();
|
|
allocator_info.device = device_->get_device();
|
|
allocator_info.instance = instance_->get_instance();
|
|
|
|
VkResult result = vmaCreateAllocator(&allocator_info, &allocator_);
|
|
if (result != VK_SUCCESS) {
|
|
MIRAI_LOG_ERROR("Failed to create VMA allocator: {}", vk_result_to_string(static_cast<vk::Result>(result)));
|
|
return false;
|
|
}
|
|
|
|
MIRAI_LOG_DEBUG("VMA allocator created");
|
|
return true;
|
|
}
|
|
|
|
bool renderer::create_surface(SDL_Window* window) {
|
|
VkSurfaceKHR c_surface = VK_NULL_HANDLE;
|
|
if (!SDL_Vulkan_CreateSurface(window, static_cast<VkInstance>(instance_->get_instance()), nullptr, &c_surface)) {
|
|
MIRAI_LOG_ERROR("Failed to create Vulkan surface: {}", SDL_GetError());
|
|
return false;
|
|
}
|
|
surface_ = c_surface;
|
|
|
|
MIRAI_LOG_DEBUG("Vulkan surface created");
|
|
return true;
|
|
}
|
|
|
|
bool renderer::create_swapchain(u32 width, u32 height) {
|
|
swapchain_config swap_config{};
|
|
if (config_.enable_vsync) {
|
|
swap_config.preferred_present_mode = vk::PresentModeKHR::eFifo;
|
|
} else {
|
|
swap_config.preferred_present_mode = vk::PresentModeKHR::eMailbox;
|
|
}
|
|
|
|
swapchain_ = std::make_shared<swapchain>(device_, surface_, width, height, swap_config);
|
|
if (!swapchain_->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to create swapchain");
|
|
return false;
|
|
}
|
|
|
|
MIRAI_LOG_DEBUG("Swapchain created: {}x{}", width, height);
|
|
return true;
|
|
}
|
|
|
|
bool renderer::create_command_pools() {
|
|
// 创建主命令池
|
|
command_pool_ = std::make_unique<command_pool>(
|
|
device_,
|
|
device_->get_graphics_queue_family(),
|
|
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
|
);
|
|
|
|
if (!command_pool_->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to create command pool");
|
|
return false;
|
|
}
|
|
|
|
// 分配命令缓冲(每帧一个)
|
|
command_buffers_.resize(config_.frames_in_flight);
|
|
for (u32 i = 0; i < config_.frames_in_flight; ++i) {
|
|
command_buffers_[i] = command_pool_->allocate_buffer();
|
|
if (!command_buffers_[i] || !command_buffers_[i]->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to allocate command buffer {}", i);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MIRAI_LOG_DEBUG("Command pools and buffers created");
|
|
return true;
|
|
}
|
|
|
|
bool renderer::create_depth_resources() {
|
|
// 选择深度格式
|
|
vk::Format candidates[] = {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint};
|
|
|
|
for (vk::Format format : candidates) {
|
|
vk::FormatProperties props = device_->get_physical_device().getFormatProperties(format);
|
|
|
|
if (props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment) {
|
|
depth_format_ = format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
vk::Extent2D extent = swapchain_->get_extent();
|
|
|
|
// 创建深度图像
|
|
VkImageCreateInfo image_info{};
|
|
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
image_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_info.extent.width = extent.width;
|
|
image_info.extent.height = extent.height;
|
|
image_info.extent.depth = 1;
|
|
image_info.mipLevels = 1;
|
|
image_info.arrayLayers = 1;
|
|
image_info.format = static_cast<VkFormat>(depth_format_);
|
|
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
VmaAllocationCreateInfo alloc_info{};
|
|
alloc_info.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
|
|
|
VkImage depth_image_raw = VK_NULL_HANDLE;
|
|
VkResult result = vmaCreateImage(allocator_, &image_info, &alloc_info,
|
|
&depth_image_raw, &depth_allocation_, nullptr);
|
|
if (result != VK_SUCCESS) {
|
|
MIRAI_LOG_ERROR("Failed to create depth image: {}", vk_result_to_string(static_cast<vk::Result>(result)));
|
|
return false;
|
|
}
|
|
depth_image_ = depth_image_raw;
|
|
|
|
// 创建深度图像视图
|
|
vk::ImageViewCreateInfo view_info{};
|
|
view_info.image = depth_image_;
|
|
view_info.viewType = vk::ImageViewType::e2D;
|
|
view_info.format = depth_format_;
|
|
view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
|
view_info.subresourceRange.baseMipLevel = 0;
|
|
view_info.subresourceRange.levelCount = 1;
|
|
view_info.subresourceRange.baseArrayLayer = 0;
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
auto [view_result, image_view] = device_->get_device().createImageView(view_info);
|
|
if (view_result != vk::Result::eSuccess) {
|
|
MIRAI_LOG_ERROR("Failed to create depth image view: {}", vk_result_to_string(view_result));
|
|
return false;
|
|
}
|
|
depth_image_view_ = image_view;
|
|
|
|
MIRAI_LOG_DEBUG("Depth resources created: format={}", static_cast<int>(depth_format_));
|
|
return true;
|
|
}
|
|
|
|
bool renderer::recreate_swapchain(u32 width, u32 height) {
|
|
MIRAI_LOG_DEBUG("Recreating swapchain: {}x{}", width, height);
|
|
|
|
// 等待设备空闲
|
|
device_->wait_idle();
|
|
|
|
// 销毁旧的深度资源
|
|
if (depth_image_view_) {
|
|
device_->get_device().destroyImageView(depth_image_view_);
|
|
depth_image_view_ = nullptr;
|
|
}
|
|
if (depth_image_) {
|
|
vmaDestroyImage(allocator_, static_cast<VkImage>(depth_image_), depth_allocation_);
|
|
depth_image_ = nullptr;
|
|
depth_allocation_ = nullptr;
|
|
}
|
|
|
|
// 重建交换链
|
|
swapchain_config swap_config{};
|
|
if (config_.enable_vsync) {
|
|
swap_config.preferred_present_mode = vk::PresentModeKHR::eFifo;
|
|
} else {
|
|
swap_config.preferred_present_mode = vk::PresentModeKHR::eMailbox;
|
|
}
|
|
swap_config.old_swapchain = swapchain_ ? swapchain_->get_swapchain() : nullptr;
|
|
|
|
auto new_swapchain = std::make_shared<swapchain>(device_, surface_, width, height, swap_config);
|
|
if (!new_swapchain->is_valid()) {
|
|
MIRAI_LOG_ERROR("Failed to recreate swapchain");
|
|
return false;
|
|
}
|
|
|
|
// 替换交换链
|
|
swapchain_ = new_swapchain;
|
|
|
|
// 重建深度资源
|
|
if (!create_depth_resources()) {
|
|
MIRAI_LOG_ERROR("Failed to recreate depth resources");
|
|
return false;
|
|
}
|
|
|
|
// 更新帧同步
|
|
if (frame_sync_) {
|
|
frame_sync_->recreate(swapchain_);
|
|
}
|
|
|
|
// 更新批处理渲染器的视口
|
|
if (batch_renderer_) {
|
|
batch_renderer_->set_viewport_orthographic(static_cast<f32>(width),
|
|
static_cast<f32>(height));
|
|
}
|
|
|
|
// 调用回调
|
|
if (on_swapchain_recreated_) {
|
|
on_swapchain_recreated_();
|
|
}
|
|
|
|
MIRAI_LOG_INFO("Swapchain recreated: {}x{}", width, height);
|
|
return true;
|
|
}
|
|
|
|
void renderer::destroy_resources() {
|
|
// 销毁批处理渲染器
|
|
batch_renderer_.reset();
|
|
|
|
// 销毁命令缓冲和命令池
|
|
command_buffers_.clear();
|
|
command_pool_.reset();
|
|
|
|
// 销毁帧同步
|
|
frame_sync_.reset();
|
|
|
|
// 销毁深度资源
|
|
if (depth_image_view_ && device_) {
|
|
device_->get_device().destroyImageView(depth_image_view_);
|
|
depth_image_view_ = nullptr;
|
|
}
|
|
if (depth_image_ && allocator_) {
|
|
vmaDestroyImage(allocator_, static_cast<VkImage>(depth_image_), depth_allocation_);
|
|
depth_image_ = nullptr;
|
|
depth_allocation_ = nullptr;
|
|
}
|
|
|
|
// 销毁交换链
|
|
swapchain_.reset();
|
|
|
|
// 销毁 VMA 分配器
|
|
if (allocator_ != VK_NULL_HANDLE) {
|
|
vmaDestroyAllocator(allocator_);
|
|
allocator_ = VK_NULL_HANDLE;
|
|
}
|
|
|
|
// 销毁表面
|
|
if (surface_ && instance_) {
|
|
instance_->get_instance().destroySurfaceKHR(surface_);
|
|
surface_ = nullptr;
|
|
}
|
|
|
|
// 销毁设备
|
|
device_.reset();
|
|
|
|
// 销毁实例
|
|
instance_.reset();
|
|
|
|
window_ = nullptr;
|
|
}
|
|
|
|
void renderer::transition_image_layout(vk::CommandBuffer cmd_buffer, vk::Image image,
|
|
vk::ImageLayout old_layout, vk::ImageLayout new_layout)
|
|
{
|
|
vk::ImageMemoryBarrier2 barrier{};
|
|
barrier.oldLayout = old_layout;
|
|
barrier.newLayout = new_layout;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = image;
|
|
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
|
|
// 设置源和目标阶段/访问掩码
|
|
if (old_layout == vk::ImageLayout::eUndefined &&
|
|
new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
|
|
barrier.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe;
|
|
barrier.srcAccessMask = vk::AccessFlagBits2::eNone;
|
|
barrier.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput;
|
|
barrier.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite;
|
|
} else if (old_layout == vk::ImageLayout::eColorAttachmentOptimal &&
|
|
new_layout == vk::ImageLayout::ePresentSrcKHR) {
|
|
barrier.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput;
|
|
barrier.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite;
|
|
barrier.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe;
|
|
barrier.dstAccessMask = vk::AccessFlagBits2::eNone;
|
|
} else {
|
|
// 通用转换
|
|
barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
|
barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite;
|
|
barrier.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
|
barrier.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite;
|
|
}
|
|
|
|
vk::DependencyInfo dependency_info{};
|
|
dependency_info.imageMemoryBarrierCount = 1;
|
|
dependency_info.pImageMemoryBarriers = &barrier;
|
|
|
|
cmd_buffer.pipelineBarrier2(&dependency_info);
|
|
}
|
|
|
|
} // namespace mirai
|