Introduce CameraRenderRequest scheduling and fix Vulkan build
This commit is contained in:
@@ -164,6 +164,8 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanFence.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommandQueue.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommandList.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanRenderPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanFramebuffer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanSwapChain.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanDevice.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanScreenshot.h
|
||||
@@ -177,6 +179,8 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanResourceView.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanCommandQueue.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanCommandList.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanRenderPass.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanFramebuffer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanSwapChain.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDevice.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanScreenshot.cpp
|
||||
@@ -324,6 +328,7 @@ add_library(XCEngine STATIC
|
||||
# Rendering
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderContext.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderCameraData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/CameraRenderRequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/VisibleRenderObject.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderSceneExtractor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipeline.h
|
||||
|
||||
52
engine/include/XCEngine/RHI/Vulkan/VulkanFramebuffer.h
Normal file
52
engine/include/XCEngine/RHI/Vulkan/VulkanFramebuffer.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "XCEngine/RHI/RHIFramebuffer.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
class VulkanRenderPass;
|
||||
class VulkanResourceView;
|
||||
class VulkanTexture;
|
||||
|
||||
class VulkanFramebuffer : public RHIFramebuffer {
|
||||
public:
|
||||
VulkanFramebuffer() = default;
|
||||
~VulkanFramebuffer() override;
|
||||
|
||||
bool Initialize(VkDevice device, RHIRenderPass* renderPass, uint32_t width, uint32_t height,
|
||||
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
|
||||
RHIResourceView* depthStencilAttachment);
|
||||
bool Initialize(RHIRenderPass* renderPass, uint32_t width, uint32_t height,
|
||||
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
|
||||
RHIResourceView* depthStencilAttachment) override;
|
||||
void Shutdown() override;
|
||||
|
||||
void* GetNativeHandle() override { return m_framebuffer; }
|
||||
uint32_t GetWidth() const override { return m_width; }
|
||||
uint32_t GetHeight() const override { return m_height; }
|
||||
bool IsValid() const override { return m_framebuffer != VK_NULL_HANDLE; }
|
||||
|
||||
VkFramebuffer GetFramebuffer() const { return m_framebuffer; }
|
||||
VulkanRenderPass* GetRenderPass() const { return m_renderPass; }
|
||||
uint32_t GetColorAttachmentCount() const { return static_cast<uint32_t>(m_colorAttachmentViews.size()); }
|
||||
VulkanResourceView* GetColorAttachmentView(uint32_t index) const;
|
||||
VulkanResourceView* GetDepthStencilView() const { return m_depthStencilView; }
|
||||
VulkanTexture* GetColorAttachmentTexture(uint32_t index) const;
|
||||
VulkanTexture* GetDepthStencilTexture() const;
|
||||
|
||||
private:
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkFramebuffer m_framebuffer = VK_NULL_HANDLE;
|
||||
VulkanRenderPass* m_renderPass = nullptr;
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
std::vector<VulkanResourceView*> m_colorAttachmentViews;
|
||||
VulkanResourceView* m_depthStencilView = nullptr;
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
45
engine/include/XCEngine/RHI/Vulkan/VulkanRenderPass.h
Normal file
45
engine/include/XCEngine/RHI/Vulkan/VulkanRenderPass.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "XCEngine/RHI/RHIRenderPass.h"
|
||||
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
class VulkanRenderPass : public RHIRenderPass {
|
||||
public:
|
||||
VulkanRenderPass() = default;
|
||||
~VulkanRenderPass() override;
|
||||
|
||||
bool Initialize(VkDevice device, uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
|
||||
const AttachmentDesc* depthStencilAttachment);
|
||||
bool Initialize(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
|
||||
const AttachmentDesc* depthStencilAttachment) override;
|
||||
void Shutdown() override;
|
||||
|
||||
uint32_t GetColorAttachmentCount() const override { return m_colorAttachmentCount; }
|
||||
const AttachmentDesc* GetColorAttachments() const override {
|
||||
return m_colorAttachments.empty() ? nullptr : m_colorAttachments.data();
|
||||
}
|
||||
const AttachmentDesc* GetDepthStencilAttachment() const override {
|
||||
return m_hasDepthStencil ? &m_depthStencilAttachment : nullptr;
|
||||
}
|
||||
void* GetNativeHandle() override { return nullptr; }
|
||||
|
||||
VkRenderPass GetRenderPass() const { return m_renderPass; }
|
||||
uint32_t GetAttachmentCount() const { return m_colorAttachmentCount + (m_hasDepthStencil ? 1u : 0u); }
|
||||
bool HasDepthStencil() const { return m_hasDepthStencil; }
|
||||
|
||||
private:
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkRenderPass m_renderPass = VK_NULL_HANDLE;
|
||||
uint32_t m_colorAttachmentCount = 0;
|
||||
std::vector<AttachmentDesc> m_colorAttachments;
|
||||
AttachmentDesc m_depthStencilAttachment = {};
|
||||
bool m_hasDepthStencil = false;
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
29
engine/include/XCEngine/Rendering/CameraRenderRequest.h
Normal file
29
engine/include/XCEngine/Rendering/CameraRenderRequest.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class CameraComponent;
|
||||
class Scene;
|
||||
} // namespace Components
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct CameraRenderRequest {
|
||||
const Components::Scene* scene = nullptr;
|
||||
Components::CameraComponent* camera = nullptr;
|
||||
RenderContext context;
|
||||
RenderSurface surface;
|
||||
float cameraDepth = 0.0f;
|
||||
|
||||
bool IsValid() const {
|
||||
return scene != nullptr &&
|
||||
camera != nullptr &&
|
||||
context.IsValid();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/CameraRenderRequest.h>
|
||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -23,11 +24,7 @@ public:
|
||||
void SetPipeline(std::unique_ptr<RenderPipeline> pipeline);
|
||||
RenderPipeline* GetPipeline() const { return m_pipeline.get(); }
|
||||
|
||||
bool Render(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface);
|
||||
bool Render(const CameraRenderRequest& request);
|
||||
|
||||
private:
|
||||
RenderSceneExtractor m_sceneExtractor;
|
||||
|
||||
@@ -31,11 +31,16 @@ public:
|
||||
Components::CameraComponent* overrideCamera,
|
||||
uint32_t viewportWidth,
|
||||
uint32_t viewportHeight) const;
|
||||
|
||||
private:
|
||||
RenderSceneData ExtractForCamera(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent& camera,
|
||||
uint32_t viewportWidth,
|
||||
uint32_t viewportHeight) const;
|
||||
Components::CameraComponent* SelectCamera(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera) const;
|
||||
|
||||
private:
|
||||
RenderCameraData BuildCameraData(
|
||||
const Components::CameraComponent& camera,
|
||||
uint32_t viewportWidth,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/CameraRenderRequest.h>
|
||||
#include <XCEngine/Rendering/CameraRenderer.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class CameraComponent;
|
||||
@@ -19,6 +22,14 @@ public:
|
||||
void SetPipeline(std::unique_ptr<RenderPipeline> pipeline);
|
||||
RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); }
|
||||
|
||||
std::vector<CameraRenderRequest> BuildRenderRequests(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface) const;
|
||||
|
||||
bool Render(const CameraRenderRequest& request);
|
||||
bool Render(const std::vector<CameraRenderRequest>& requests);
|
||||
bool Render(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
@@ -26,6 +37,7 @@ public:
|
||||
const RenderSurface& surface);
|
||||
|
||||
private:
|
||||
RenderSceneExtractor m_sceneExtractor;
|
||||
CameraRenderer m_cameraRenderer;
|
||||
};
|
||||
|
||||
|
||||
115
engine/src/RHI/Vulkan/VulkanFramebuffer.cpp
Normal file
115
engine/src/RHI/Vulkan/VulkanFramebuffer.cpp
Normal 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
|
||||
156
engine/src/RHI/Vulkan/VulkanRenderPass.cpp
Normal file
156
engine/src/RHI/Vulkan/VulkanRenderPass.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Components;
|
||||
using namespace XCEngine::Rendering;
|
||||
@@ -21,6 +22,7 @@ struct MockPipelineState {
|
||||
uint32_t lastSurfaceHeight = 0;
|
||||
CameraComponent* lastCamera = nullptr;
|
||||
size_t lastVisibleItemCount = 0;
|
||||
std::vector<CameraComponent*> renderedCameras;
|
||||
};
|
||||
|
||||
class MockPipeline final : public RenderPipeline {
|
||||
@@ -47,6 +49,7 @@ public:
|
||||
m_state->lastSurfaceHeight = surface.GetHeight();
|
||||
m_state->lastCamera = sceneData.camera;
|
||||
m_state->lastVisibleItemCount = sceneData.visibleItems.size();
|
||||
m_state->renderedCameras.push_back(sceneData.camera);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -80,8 +83,14 @@ TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
CameraRenderer renderer(std::make_unique<MockPipeline>(state));
|
||||
|
||||
const RenderSurface surface(640, 480);
|
||||
ASSERT_TRUE(renderer.Render(scene, overrideCamera, CreateValidContext(), surface));
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = overrideCamera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(640, 480);
|
||||
request.cameraDepth = overrideCamera->GetDepth();
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_EQ(state->renderCalls, 1);
|
||||
EXPECT_EQ(state->lastSurfaceWidth, 640u);
|
||||
EXPECT_EQ(state->lastSurfaceHeight, 480u);
|
||||
@@ -90,6 +99,37 @@ TEST(CameraRenderer_Test, UsesOverrideCameraAndSurfaceSizeWhenSubmittingScene) {
|
||||
EXPECT_EQ(state->lastVisibleItemCount, 0u);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, BuildsSingleExplicitRequestFromSelectedCamera) {
|
||||
Scene scene("SceneRendererRequestScene");
|
||||
|
||||
GameObject* lowCameraObject = scene.CreateGameObject("LowCamera");
|
||||
auto* lowCamera = lowCameraObject->AddComponent<CameraComponent>();
|
||||
lowCamera->SetPrimary(true);
|
||||
lowCamera->SetDepth(1.0f);
|
||||
|
||||
GameObject* highCameraObject = scene.CreateGameObject("HighCamera");
|
||||
auto* highCamera = highCameraObject->AddComponent<CameraComponent>();
|
||||
highCamera->SetPrimary(true);
|
||||
highCamera->SetDepth(5.0f);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const RenderContext context = CreateValidContext();
|
||||
const RenderSurface surface(320, 180);
|
||||
|
||||
const std::vector<CameraRenderRequest> defaultRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(defaultRequests.size(), 1u);
|
||||
EXPECT_EQ(defaultRequests[0].camera, highCamera);
|
||||
EXPECT_EQ(defaultRequests[0].cameraDepth, 5.0f);
|
||||
EXPECT_EQ(defaultRequests[0].surface.GetWidth(), 320u);
|
||||
EXPECT_EQ(defaultRequests[0].surface.GetHeight(), 180u);
|
||||
|
||||
const std::vector<CameraRenderRequest> overrideRequests =
|
||||
renderer.BuildRenderRequests(scene, lowCamera, context, surface);
|
||||
ASSERT_EQ(overrideRequests.size(), 1u);
|
||||
EXPECT_EQ(overrideRequests[0].camera, lowCamera);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer) {
|
||||
Scene scene("SceneRendererScene");
|
||||
|
||||
@@ -125,3 +165,37 @@ TEST(SceneRenderer_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer)
|
||||
EXPECT_EQ(initialState->shutdownCalls, 1);
|
||||
EXPECT_EQ(replacementState->shutdownCalls, 1);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, SortsManualCameraRequestsByDepthBeforeRendering) {
|
||||
Scene scene("SceneRendererManualRequests");
|
||||
|
||||
GameObject* farCameraObject = scene.CreateGameObject("FarCamera");
|
||||
auto* farCamera = farCameraObject->AddComponent<CameraComponent>();
|
||||
farCamera->SetPrimary(true);
|
||||
farCamera->SetDepth(10.0f);
|
||||
|
||||
GameObject* nearCameraObject = scene.CreateGameObject("NearCamera");
|
||||
auto* nearCamera = nearCameraObject->AddComponent<CameraComponent>();
|
||||
nearCamera->SetPrimary(false);
|
||||
nearCamera->SetDepth(1.0f);
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
SceneRenderer renderer(std::make_unique<MockPipeline>(state));
|
||||
|
||||
CameraRenderRequest farRequest;
|
||||
farRequest.scene = &scene;
|
||||
farRequest.camera = farCamera;
|
||||
farRequest.context = CreateValidContext();
|
||||
farRequest.surface = RenderSurface(800, 600);
|
||||
farRequest.cameraDepth = farCamera->GetDepth();
|
||||
|
||||
CameraRenderRequest nearRequest = farRequest;
|
||||
nearRequest.camera = nearCamera;
|
||||
nearRequest.cameraDepth = nearCamera->GetDepth();
|
||||
|
||||
const std::vector<CameraRenderRequest> requests = { farRequest, nearRequest };
|
||||
ASSERT_TRUE(renderer.Render(requests));
|
||||
ASSERT_EQ(state->renderedCameras.size(), 2u);
|
||||
EXPECT_EQ(state->renderedCameras[0], nearCamera);
|
||||
EXPECT_EQ(state->renderedCameras[1], farCamera);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user