Introduce CameraRenderRequest scheduling and fix Vulkan build

This commit is contained in:
2026-03-27 16:57:04 +08:00
parent a72f9f7f05
commit dbec62608c
13 changed files with 605 additions and 21 deletions

View File

@@ -0,0 +1,115 @@
#include "XCEngine/RHI/Vulkan/VulkanFramebuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
#include <vector>
namespace XCEngine {
namespace RHI {
VulkanFramebuffer::~VulkanFramebuffer() {
Shutdown();
}
bool VulkanFramebuffer::Initialize(VkDevice device, RHIRenderPass* renderPass, uint32_t width, uint32_t height,
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
RHIResourceView* depthStencilAttachment) {
if (device == VK_NULL_HANDLE || renderPass == nullptr || width == 0 || height == 0) {
return false;
}
if (colorAttachmentCount > 0 && colorAttachments == nullptr) {
return false;
}
auto* vulkanRenderPass = static_cast<VulkanRenderPass*>(renderPass);
if (vulkanRenderPass == nullptr || vulkanRenderPass->GetRenderPass() == VK_NULL_HANDLE) {
return false;
}
Shutdown();
m_device = device;
m_renderPass = vulkanRenderPass;
m_width = width;
m_height = height;
m_colorAttachmentViews.reserve(colorAttachmentCount);
std::vector<VkImageView> attachments;
attachments.reserve(colorAttachmentCount + (depthStencilAttachment != nullptr ? 1u : 0u));
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
auto* view = static_cast<VulkanResourceView*>(colorAttachments[i]);
if (view == nullptr || view->GetImageView() == VK_NULL_HANDLE) {
Shutdown();
return false;
}
m_colorAttachmentViews.push_back(view);
attachments.push_back(view->GetImageView());
}
m_depthStencilView = static_cast<VulkanResourceView*>(depthStencilAttachment);
if (m_depthStencilView != nullptr) {
if (m_depthStencilView->GetImageView() == VK_NULL_HANDLE) {
Shutdown();
return false;
}
attachments.push_back(m_depthStencilView->GetImageView());
}
VkFramebufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
createInfo.renderPass = vulkanRenderPass->GetRenderPass();
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
createInfo.pAttachments = attachments.empty() ? nullptr : attachments.data();
createInfo.width = width;
createInfo.height = height;
createInfo.layers = 1;
if (vkCreateFramebuffer(device, &createInfo, nullptr, &m_framebuffer) != VK_SUCCESS) {
Shutdown();
return false;
}
return true;
}
bool VulkanFramebuffer::Initialize(RHIRenderPass* renderPass, uint32_t width, uint32_t height,
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
RHIResourceView* depthStencilAttachment) {
return Initialize(m_device, renderPass, width, height, colorAttachmentCount, colorAttachments, depthStencilAttachment);
}
void VulkanFramebuffer::Shutdown() {
if (m_framebuffer != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyFramebuffer(m_device, m_framebuffer, nullptr);
}
m_framebuffer = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_renderPass = nullptr;
m_width = 0;
m_height = 0;
m_colorAttachmentViews.clear();
m_depthStencilView = nullptr;
}
VulkanResourceView* VulkanFramebuffer::GetColorAttachmentView(uint32_t index) const {
if (index >= m_colorAttachmentViews.size()) {
return nullptr;
}
return m_colorAttachmentViews[index];
}
VulkanTexture* VulkanFramebuffer::GetColorAttachmentTexture(uint32_t index) const {
VulkanResourceView* view = GetColorAttachmentView(index);
return view != nullptr ? view->GetTexture() : nullptr;
}
VulkanTexture* VulkanFramebuffer::GetDepthStencilTexture() const {
return m_depthStencilView != nullptr ? m_depthStencilView->GetTexture() : nullptr;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,156 @@
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
#include <vector>
namespace XCEngine {
namespace RHI {
namespace {
VkAttachmentLoadOp ToVulkanLoadOp(LoadAction loadAction) {
switch (loadAction) {
case LoadAction::Clear:
return VK_ATTACHMENT_LOAD_OP_CLEAR;
case LoadAction::Load:
return VK_ATTACHMENT_LOAD_OP_LOAD;
case LoadAction::Undefined:
default:
return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
}
VkAttachmentStoreOp ToVulkanStoreOp(StoreAction storeAction) {
switch (storeAction) {
case StoreAction::Store:
case StoreAction::Resolve:
case StoreAction::StoreAndResolve:
return VK_ATTACHMENT_STORE_OP_STORE;
case StoreAction::Discard:
case StoreAction::Undefined:
default:
return VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
}
} // namespace
VulkanRenderPass::~VulkanRenderPass() {
Shutdown();
}
bool VulkanRenderPass::Initialize(VkDevice device, uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
const AttachmentDesc* depthStencilAttachment) {
if (device == VK_NULL_HANDLE) {
return false;
}
if (colorAttachmentCount > 0 && colorAttachments == nullptr) {
return false;
}
Shutdown();
m_device = device;
m_colorAttachmentCount = colorAttachmentCount;
m_colorAttachments.assign(colorAttachments, colorAttachments + colorAttachmentCount);
if (depthStencilAttachment != nullptr) {
m_depthStencilAttachment = *depthStencilAttachment;
m_hasDepthStencil = true;
}
std::vector<VkAttachmentDescription> attachments;
attachments.reserve(colorAttachmentCount + (m_hasDepthStencil ? 1u : 0u));
std::vector<VkAttachmentReference> colorAttachmentRefs;
colorAttachmentRefs.reserve(colorAttachmentCount);
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
const AttachmentDesc& attachmentDesc = m_colorAttachments[i];
const VkFormat format = ToVulkanFormat(attachmentDesc.format);
if (format == VK_FORMAT_UNDEFINED) {
Shutdown();
return false;
}
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = ToVulkanLoadOp(attachmentDesc.loadOp);
attachment.storeOp = ToVulkanStoreOp(attachmentDesc.storeOp);
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(attachment);
VkAttachmentReference attachmentRef = {};
attachmentRef.attachment = i;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachmentRefs.push_back(attachmentRef);
}
VkAttachmentReference depthAttachmentRef = {};
if (m_hasDepthStencil) {
const VkFormat format = ToVulkanFormat(m_depthStencilAttachment.format);
if (format == VK_FORMAT_UNDEFINED) {
Shutdown();
return false;
}
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = ToVulkanLoadOp(m_depthStencilAttachment.loadOp);
attachment.storeOp = ToVulkanStoreOp(m_depthStencilAttachment.storeOp);
attachment.stencilLoadOp = ToVulkanLoadOp(m_depthStencilAttachment.stencilLoadOp);
attachment.stencilStoreOp = ToVulkanStoreOp(m_depthStencilAttachment.stencilStoreOp);
attachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments.push_back(attachment);
depthAttachmentRef.attachment = colorAttachmentCount;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
subpass.pColorAttachments = colorAttachmentRefs.empty() ? nullptr : colorAttachmentRefs.data();
if (m_hasDepthStencil) {
subpass.pDepthStencilAttachment = &depthAttachmentRef;
}
VkRenderPassCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
createInfo.pAttachments = attachments.empty() ? nullptr : attachments.data();
createInfo.subpassCount = 1;
createInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(device, &createInfo, nullptr, &m_renderPass) != VK_SUCCESS) {
Shutdown();
return false;
}
return true;
}
bool VulkanRenderPass::Initialize(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
const AttachmentDesc* depthStencilAttachment) {
return Initialize(m_device, colorAttachmentCount, colorAttachments, depthStencilAttachment);
}
void VulkanRenderPass::Shutdown() {
if (m_renderPass != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, m_renderPass, nullptr);
}
m_renderPass = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_colorAttachmentCount = 0;
m_colorAttachments.clear();
m_depthStencilAttachment = {};
m_hasDepthStencil = false;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -36,24 +36,21 @@ void CameraRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
}
bool CameraRenderer::Render(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) {
if (!context.IsValid() || m_pipeline == nullptr) {
const CameraRenderRequest& request) {
if (!request.IsValid() || m_pipeline == nullptr) {
return false;
}
RenderSceneData sceneData = m_sceneExtractor.Extract(
scene,
overrideCamera,
surface.GetWidth(),
surface.GetHeight());
RenderSceneData sceneData = m_sceneExtractor.ExtractForCamera(
*request.scene,
*request.camera,
request.surface.GetWidth(),
request.surface.GetHeight());
if (!sceneData.HasCamera()) {
return false;
}
return m_pipeline->Render(context, surface, sceneData);
return m_pipeline->Render(request.context, request.surface, sceneData);
}
} // namespace Rendering

View File

@@ -70,6 +70,33 @@ RenderSceneData RenderSceneExtractor::Extract(
return sceneData;
}
RenderSceneData RenderSceneExtractor::ExtractForCamera(
const Components::Scene& scene,
Components::CameraComponent& camera,
uint32_t viewportWidth,
uint32_t viewportHeight) const {
RenderSceneData sceneData;
if (!IsUsableCamera(&camera)) {
return sceneData;
}
sceneData.camera = &camera;
sceneData.cameraData = BuildCameraData(camera, viewportWidth, viewportHeight);
const Math::Vector3 cameraPosition = sceneData.cameraData.worldPosition;
const std::vector<Components::GameObject*> rootGameObjects = scene.GetRootGameObjects();
for (Components::GameObject* rootGameObject : rootGameObjects) {
ExtractVisibleItems(rootGameObject, cameraPosition, sceneData.visibleItems);
}
std::stable_sort(
sceneData.visibleItems.begin(),
sceneData.visibleItems.end(),
CompareVisibleItems);
return sceneData;
}
Components::CameraComponent* RenderSceneExtractor::SelectCamera(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const {

View File

@@ -1,8 +1,24 @@
#include "Rendering/SceneRenderer.h"
#include "Components/CameraComponent.h"
#include <algorithm>
namespace XCEngine {
namespace Rendering {
namespace {
bool CompareCameraRenderRequest(const CameraRenderRequest& lhs, const CameraRenderRequest& rhs) {
if (lhs.cameraDepth != rhs.cameraDepth) {
return lhs.cameraDepth < rhs.cameraDepth;
}
return lhs.camera < rhs.camera;
}
} // namespace
SceneRenderer::SceneRenderer() = default;
SceneRenderer::SceneRenderer(std::unique_ptr<RenderPipeline> pipeline)
@@ -13,12 +29,66 @@ void SceneRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
m_cameraRenderer.SetPipeline(std::move(pipeline));
}
std::vector<CameraRenderRequest> SceneRenderer::BuildRenderRequests(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) const {
std::vector<CameraRenderRequest> requests;
Components::CameraComponent* camera = m_sceneExtractor.SelectCamera(scene, overrideCamera);
if (camera == nullptr) {
return requests;
}
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = surface;
request.cameraDepth = camera->GetDepth();
requests.push_back(request);
return requests;
}
bool SceneRenderer::Render(const CameraRenderRequest& request) {
return m_cameraRenderer.Render(request);
}
bool SceneRenderer::Render(const std::vector<CameraRenderRequest>& requests) {
if (requests.empty()) {
return false;
}
for (const CameraRenderRequest& request : requests) {
if (!request.IsValid()) {
return false;
}
}
std::vector<CameraRenderRequest> sortedRequests = requests;
std::stable_sort(
sortedRequests.begin(),
sortedRequests.end(),
CompareCameraRenderRequest);
bool rendered = false;
for (const CameraRenderRequest& request : sortedRequests) {
if (!m_cameraRenderer.Render(request)) {
return false;
}
rendered = true;
}
return rendered;
}
bool SceneRenderer::Render(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) {
return m_cameraRenderer.Render(scene, overrideCamera, context, surface);
return Render(BuildRenderRequests(scene, overrideCamera, context, surface));
}
} // namespace Rendering