Files
mirai/src/render/renderer.cpp
2025-12-31 12:27:48 +08:00

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