/** * @file renderer.cpp * @brief MIRAI 框架渲染器主类实现 * @author MIRAI Team * @version 0.1.0 */ #include "renderer.hpp" #include "logger.hpp" #include 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(width); config_.viewport_height = static_cast(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(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(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(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(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(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(width), static_cast(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(width), static_cast(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(target.render_area.extent.width); viewport.height = static_cast(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(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(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(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( 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(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(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(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(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(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(width), static_cast(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(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