Add Vulkan RHI minimal backend path

This commit is contained in:
2026-03-27 12:05:12 +08:00
parent 90961f01aa
commit c33404767e
25 changed files with 1894 additions and 16 deletions

View File

@@ -2,6 +2,22 @@ cmake_minimum_required(VERSION 3.15)
project(XCEngineLib)
set(CMAKE_CXX_STANDARD 17)
set(XCENGINE_VULKAN_SDK_HINT "$ENV{VULKAN_SDK}")
if(NOT EXISTS "${XCENGINE_VULKAN_SDK_HINT}/Lib/vulkan-1.lib")
file(GLOB XCENGINE_VULKAN_SDK_DIRS "D:/VulkanSDK/*")
if(XCENGINE_VULKAN_SDK_DIRS)
list(SORT XCENGINE_VULKAN_SDK_DIRS COMPARE NATURAL ORDER DESCENDING)
list(GET XCENGINE_VULKAN_SDK_DIRS 0 XCENGINE_VULKAN_SDK_HINT)
endif()
endif()
if(EXISTS "${XCENGINE_VULKAN_SDK_HINT}/Lib/vulkan-1.lib")
set(Vulkan_ROOT "${XCENGINE_VULKAN_SDK_HINT}")
list(APPEND CMAKE_PREFIX_PATH "${XCENGINE_VULKAN_SDK_HINT}")
endif()
find_package(Vulkan REQUIRED)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(XCEngine STATIC
@@ -133,6 +149,24 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12ResourceView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12QueryHeap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12RenderPass.cpp
# Vulkan RHI
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommon.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanTexture.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanResourceView.h
${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/VulkanSwapChain.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanDevice.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanScreenshot.h
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanTexture.cpp
${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/VulkanSwapChain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDevice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanScreenshot.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/D3D12/D3D12Framebuffer.cpp
# OpenGL RHI
@@ -372,6 +406,7 @@ target_include_directories(XCEngine PUBLIC
target_link_libraries(XCEngine PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/lib/assimp-vc143-mt.lib
Vulkan::Vulkan
)
if(MSVC)
@@ -380,4 +415,7 @@ else()
target_compile_options(XCEngine PRIVATE -Wall)
endif()
target_compile_definitions(XCEngine PRIVATE XCENGINE_SUPPORT_OPENGL)
target_compile_definitions(XCEngine PRIVATE
XCENGINE_SUPPORT_OPENGL
XCENGINE_SUPPORT_VULKAN
)

View File

@@ -0,0 +1,71 @@
#pragma once
#include "XCEngine/RHI/RHICommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
namespace XCEngine {
namespace RHI {
class VulkanDevice;
class VulkanTexture;
class VulkanCommandList : public RHICommandList {
public:
VulkanCommandList() = default;
~VulkanCommandList() override;
bool Initialize(VulkanDevice* device);
void Shutdown() override;
void Reset() override;
void Close() override;
void TransitionBarrier(RHIResourceView* resource, ResourceStates stateBefore, ResourceStates stateAfter) override;
void BeginRenderPass(class RHIRenderPass* renderPass, class RHIFramebuffer* framebuffer,
const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) override;
void EndRenderPass() override;
void SetShader(RHIShader* shader) override;
void SetPipelineState(RHIPipelineState* pso) override;
void SetGraphicsDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) override;
void SetComputeDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) override;
void SetPrimitiveTopology(PrimitiveTopology topology) override;
void SetViewport(const Viewport& viewport) override;
void SetViewports(uint32_t count, const Viewport* viewports) override;
void SetScissorRect(const Rect& rect) override;
void SetScissorRects(uint32_t count, const Rect* rects) override;
void SetRenderTargets(uint32_t count, RHIResourceView** renderTargets, RHIResourceView* depthStencil = nullptr) override;
void SetStencilRef(uint8_t ref) override;
void SetBlendFactor(const float factor[4]) override;
void SetVertexBuffers(uint32_t startSlot, uint32_t count, RHIResourceView** buffers, const uint64_t* offsets, const uint32_t* strides) override;
void SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) override;
void Draw(uint32_t vertexCount, uint32_t instanceCount = 1, uint32_t startVertex = 0, uint32_t startInstance = 0) override;
void DrawIndexed(uint32_t indexCount, uint32_t instanceCount = 1, uint32_t startIndex = 0, int32_t baseVertex = 0, uint32_t startInstance = 0) override;
void Clear(float r, float g, float b, float a, uint32_t buffers) override;
void ClearRenderTarget(RHIResourceView* renderTarget, const float color[4]) override;
void ClearDepthStencil(RHIResourceView* depthStencil, float depth, uint8_t stencil) override;
void CopyResource(RHIResourceView* dst, RHIResourceView* src) override;
void Dispatch(uint32_t x, uint32_t y, uint32_t z) override;
void* GetNativeHandle() override { return m_commandBuffer; }
VkCommandBuffer GetCommandBuffer() const { return m_commandBuffer; }
private:
void TransitionTexture(VulkanTexture* texture, ResourceStates newState);
VulkanDevice* m_device = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE;
RHIResourceView* m_currentColorTarget = nullptr;
RHIResourceView* m_currentDepthTarget = nullptr;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,43 @@
#pragma once
#include "XCEngine/RHI/RHICommandQueue.h"
#include "XCEngine/RHI/RHIFence.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
namespace XCEngine {
namespace RHI {
class VulkanDevice;
class VulkanCommandQueue : public RHICommandQueue {
public:
VulkanCommandQueue() = default;
~VulkanCommandQueue() override = default;
bool Initialize(VulkanDevice* device, CommandQueueType type);
void Shutdown() override;
void ExecuteCommandLists(uint32_t count, void** lists) override;
void Signal(RHIFence* fence, uint64_t value) override;
void Wait(RHIFence* fence, uint64_t value) override;
uint64_t GetCompletedValue() override;
void WaitForIdle() override;
CommandQueueType GetType() const override { return m_type; }
uint64_t GetTimestampFrequency() const override { return 1000000000ull; }
void* GetNativeHandle() override { return m_queue; }
void WaitForPreviousFrame() override {}
uint64_t GetCurrentFrame() const override { return m_currentFrame; }
VkQueue GetQueue() const { return m_queue; }
private:
VulkanDevice* m_device = nullptr;
VkQueue m_queue = VK_NULL_HANDLE;
CommandQueueType m_type = CommandQueueType::Direct;
uint64_t m_currentFrame = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,85 @@
#pragma once
#ifndef VK_USE_PLATFORM_WIN32_KHR
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#include <vulkan/vulkan.h>
#include "XCEngine/RHI/RHIEnums.h"
#include <string>
namespace XCEngine {
namespace RHI {
inline std::wstring WidenAscii(const char* value) {
if (value == nullptr) {
return {};
}
const std::string ascii(value);
return std::wstring(ascii.begin(), ascii.end());
}
inline VkFormat ToVulkanFormat(Format format) {
switch (format) {
case Format::R8G8B8A8_UNorm:
return VK_FORMAT_R8G8B8A8_UNORM;
case Format::D24_UNorm_S8_UInt:
return VK_FORMAT_D24_UNORM_S8_UINT;
case Format::D32_Float:
return VK_FORMAT_D32_SFLOAT;
case Format::R32_UInt:
return VK_FORMAT_R32_UINT;
case Format::R32G32_Float:
return VK_FORMAT_R32G32_SFLOAT;
case Format::R32G32B32A32_Float:
return VK_FORMAT_R32G32B32A32_SFLOAT;
default:
return VK_FORMAT_UNDEFINED;
}
}
inline Format ToRHIFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_B8G8R8A8_UNORM:
return Format::R8G8B8A8_UNorm;
case VK_FORMAT_D24_UNORM_S8_UINT:
return Format::D24_UNorm_S8_UInt;
case VK_FORMAT_D32_SFLOAT:
return Format::D32_Float;
case VK_FORMAT_R32_UINT:
return Format::R32_UInt;
case VK_FORMAT_R32G32_SFLOAT:
return Format::R32G32_Float;
case VK_FORMAT_R32G32B32A32_SFLOAT:
return Format::R32G32B32A32_Float;
default:
return Format::Unknown;
}
}
inline VkImageAspectFlags GetImageAspectMask(Format format) {
switch (format) {
case Format::D24_UNorm_S8_UInt:
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
case Format::D32_Float:
case Format::D16_UNorm:
return VK_IMAGE_ASPECT_DEPTH_BIT;
default:
return VK_IMAGE_ASPECT_COLOR_BIT;
}
}
inline uint32_t ResolveVulkanApiMajor(uint32_t apiVersion) {
return VK_API_VERSION_MAJOR(apiVersion);
}
inline uint32_t ResolveVulkanApiMinor(uint32_t apiVersion) {
return VK_API_VERSION_MINOR(apiVersion);
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,74 @@
#pragma once
#include "XCEngine/RHI/RHIDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
namespace XCEngine {
namespace RHI {
class VulkanCommandQueue;
class VulkanCommandList;
class VulkanDevice : public RHIDevice {
public:
VulkanDevice() = default;
~VulkanDevice() override;
bool Initialize(const RHIDeviceDesc& desc) override;
void Shutdown() override;
RHIBuffer* CreateBuffer(const BufferDesc& desc) override;
RHITexture* CreateTexture(const TextureDesc& desc) override;
RHITexture* CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch = 0) override;
RHISwapChain* CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) override;
RHICommandList* CreateCommandList(const CommandListDesc& desc) override;
RHICommandQueue* CreateCommandQueue(const CommandQueueDesc& desc) override;
RHIShader* CreateShader(const ShaderCompileDesc& desc) override;
RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) override;
RHIPipelineLayout* CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) override;
RHIFence* CreateFence(const FenceDesc& desc) override;
RHISampler* CreateSampler(const SamplerDesc& desc) override;
RHIRenderPass* CreateRenderPass(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments, const AttachmentDesc* depthStencilAttachment) override;
RHIFramebuffer* CreateFramebuffer(class RHIRenderPass* renderPass, uint32_t width, uint32_t height, uint32_t colorAttachmentCount, RHIResourceView** colorAttachments, RHIResourceView* depthStencilAttachment) override;
RHIDescriptorPool* CreateDescriptorPool(const DescriptorPoolDesc& desc) override;
RHIDescriptorSet* CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) override;
RHIResourceView* CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override;
RHIResourceView* CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) override;
RHIResourceView* CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) override;
RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override;
RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override;
RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override;
const RHICapabilities& GetCapabilities() const override { return m_capabilities; }
const RHIDeviceInfo& GetDeviceInfo() const override { return m_deviceInfo; }
void* GetNativeDevice() override { return m_device; }
VkInstance GetInstance() const { return m_instance; }
VkPhysicalDevice GetPhysicalDevice() const { return m_physicalDevice; }
VkDevice GetDevice() const { return m_device; }
VkQueue GetGraphicsQueue() const { return m_graphicsQueue; }
uint32_t GetGraphicsQueueFamilyIndex() const { return m_graphicsQueueFamilyIndex; }
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const;
private:
bool CreateInstance();
bool PickPhysicalDevice();
bool CreateLogicalDevice();
void QueryDeviceInfo();
RHIDeviceDesc m_deviceDesc = {};
VkInstance m_instance = VK_NULL_HANDLE;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
uint32_t m_graphicsQueueFamilyIndex = UINT32_MAX;
RHICapabilities m_capabilities = {};
RHIDeviceInfo m_deviceInfo = {};
bool m_initialized = false;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,25 @@
#pragma once
#include "XCEngine/RHI/RHIFence.h"
namespace XCEngine {
namespace RHI {
class VulkanFence : public RHIFence {
public:
explicit VulkanFence(uint64_t initialValue = 0)
: m_value(initialValue) {}
void Shutdown() override {}
void Signal() override { ++m_value; }
void Signal(uint64_t value) override { m_value = value; }
void Wait(uint64_t value) override { if (m_value < value) m_value = value; }
uint64_t GetCompletedValue() const override { return m_value; }
void* GetNativeHandle() override { return nullptr; }
private:
uint64_t m_value = 0;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,38 @@
#pragma once
#include "XCEngine/RHI/RHIResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
namespace XCEngine {
namespace RHI {
class VulkanResourceView : public RHIResourceView {
public:
VulkanResourceView() = default;
~VulkanResourceView() override;
bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
void Shutdown() override;
void* GetNativeHandle() override { return m_imageView; }
bool IsValid() const override { return m_imageView != VK_NULL_HANDLE; }
ResourceViewType GetViewType() const override { return m_viewType; }
ResourceViewDimension GetDimension() const override { return m_dimension; }
Format GetFormat() const override { return m_format; }
VulkanTexture* GetTexture() const { return m_texture; }
VkImageView GetImageView() const { return m_imageView; }
private:
VkDevice m_device = VK_NULL_HANDLE;
VkImageView m_imageView = VK_NULL_HANDLE;
VulkanTexture* m_texture = nullptr;
ResourceViewType m_viewType = ResourceViewType::RenderTarget;
ResourceViewDimension m_dimension = ResourceViewDimension::Texture2D;
Format m_format = Format::Unknown;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,15 @@
#pragma once
#include "XCEngine/RHI/RHIScreenshot.h"
namespace XCEngine {
namespace RHI {
class VulkanScreenshot : public RHIScreenshot {
public:
bool Capture(RHIDevice* device, RHISwapChain* swapChain, const char* filename) override;
void Shutdown() override {}
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,59 @@
#pragma once
#include "XCEngine/RHI/RHISwapChain.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <memory>
#include <vector>
struct HWND__;
namespace XCEngine {
namespace RHI {
class VulkanDevice;
class VulkanCommandQueue;
class VulkanTexture;
class VulkanSwapChain : public RHISwapChain {
public:
VulkanSwapChain() = default;
~VulkanSwapChain() override;
bool Initialize(VulkanDevice* device, VulkanCommandQueue* presentQueue, HWND__* window, uint32_t width, uint32_t height);
bool AcquireNextImage();
void Shutdown() override;
uint32_t GetCurrentBackBufferIndex() const override { return m_currentImageIndex; }
RHITexture* GetCurrentBackBuffer() override;
void Present(uint32_t syncInterval = 1, uint32_t flags = 0) override;
void Resize(uint32_t width, uint32_t height) override;
void* GetNativeHandle() override { return m_swapChain; }
uint32_t GetWidth() const { return m_width; }
uint32_t GetHeight() const { return m_height; }
VkSwapchainKHR GetSwapChain() const { return m_swapChain; }
VkSurfaceKHR GetSurface() const { return m_surface; }
VkFormat GetVkFormat() const { return m_surfaceFormat; }
VulkanDevice* GetDevice() const { return m_device; }
private:
bool CreateSurface(HWND__* window);
bool CreateSwapChainResources();
void DestroySwapChainResources();
VulkanDevice* m_device = nullptr;
VulkanCommandQueue* m_presentQueue = nullptr;
HWND__* m_window = nullptr;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkSwapchainKHR m_swapChain = VK_NULL_HANDLE;
VkFormat m_surfaceFormat = VK_FORMAT_UNDEFINED;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_currentImageIndex = 0;
std::vector<std::unique_ptr<VulkanTexture>> m_backBuffers;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,57 @@
#pragma once
#include "XCEngine/RHI/RHITexture.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <string>
namespace XCEngine {
namespace RHI {
class VulkanTexture : public RHITexture {
public:
VulkanTexture() = default;
~VulkanTexture() override;
bool InitializeSwapChainImage(
VkDevice device,
VkImage image,
uint32_t width,
uint32_t height,
Format format,
VkFormat vkFormat);
void Shutdown() override;
uint32_t GetWidth() const override { return m_width; }
uint32_t GetHeight() const override { return m_height; }
uint32_t GetDepth() const override { return 1; }
uint32_t GetMipLevels() const override { return 1; }
Format GetFormat() const override { return m_format; }
TextureType GetTextureType() const override { return m_textureType; }
ResourceStates GetState() const override { return m_state; }
void SetState(ResourceStates state) override { m_state = state; }
void* GetNativeHandle() override { return m_image; }
const std::string& GetName() const override { return m_name; }
void SetName(const std::string& name) override { m_name = name; }
VkImage GetImage() const { return m_image; }
VkFormat GetVkFormat() const { return m_vkFormat; }
private:
VkDevice m_device = VK_NULL_HANDLE;
VkImage m_image = VK_NULL_HANDLE;
uint32_t m_width = 0;
uint32_t m_height = 0;
Format m_format = Format::Unknown;
TextureType m_textureType = TextureType::Texture2D;
ResourceStates m_state = ResourceStates::Common;
VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
std::string m_name;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -3,6 +3,9 @@
#ifdef XCENGINE_SUPPORT_OPENGL
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
#endif
#ifdef XCENGINE_SUPPORT_VULKAN
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#endif
namespace XCEngine {
namespace RHI {
@@ -15,7 +18,10 @@ RHIDevice* RHIFactory::CreateRHIDevice(RHIType type) {
case RHIType::OpenGL:
return new OpenGLDevice();
#endif
#ifdef XCENGINE_SUPPORT_VULKAN
case RHIType::Vulkan:
return new VulkanDevice();
#endif
case RHIType::Metal:
default:
return nullptr;
@@ -28,6 +34,10 @@ RHIDevice* RHIFactory::CreateRHIDevice(const std::string& typeName) {
#ifdef XCENGINE_SUPPORT_OPENGL
} else if (typeName == "OpenGL" || typeName == "opengl" || typeName == "GL") {
return new OpenGLDevice();
#endif
#ifdef XCENGINE_SUPPORT_VULKAN
} else if (typeName == "Vulkan" || typeName == "vulkan" || typeName == "VK" || typeName == "vk") {
return new VulkanDevice();
#endif
}
return nullptr;

View File

@@ -4,6 +4,9 @@
#ifdef XCENGINE_SUPPORT_OPENGL
#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h"
#endif
#ifdef XCENGINE_SUPPORT_VULKAN
#include "XCEngine/RHI/Vulkan/VulkanScreenshot.h"
#endif
namespace XCEngine {
namespace RHI {
@@ -15,6 +18,10 @@ RHIScreenshot* RHIScreenshot::Create(RHIType type) {
#ifdef XCENGINE_SUPPORT_OPENGL
case RHIType::OpenGL:
return new OpenGLScreenshot();
#endif
#ifdef XCENGINE_SUPPORT_VULKAN
case RHIType::Vulkan:
return new VulkanScreenshot();
#endif
default:
return nullptr;
@@ -22,4 +29,4 @@ RHIScreenshot* RHIScreenshot::Create(RHIType type) {
}
} // namespace RHI
} // namespace XCEngine
} // namespace XCEngine

View File

@@ -0,0 +1,326 @@
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
namespace XCEngine {
namespace RHI {
namespace {
VkImageLayout ToVulkanImageLayout(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
case ResourceStates::CopySrc:
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
case ResourceStates::CopyDst:
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
case ResourceStates::Present:
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
case ResourceStates::PixelShaderResource:
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
case ResourceStates::DepthWrite:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case ResourceStates::Common:
default:
return VK_IMAGE_LAYOUT_UNDEFINED;
}
}
VkAccessFlags ToVulkanAccessMask(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case ResourceStates::CopySrc:
return VK_ACCESS_TRANSFER_READ_BIT;
case ResourceStates::CopyDst:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case ResourceStates::PixelShaderResource:
return VK_ACCESS_SHADER_READ_BIT;
case ResourceStates::DepthWrite:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case ResourceStates::Present:
case ResourceStates::Common:
default:
return 0;
}
}
VkPipelineStageFlags ToVulkanStageMask(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
case ResourceStates::CopySrc:
case ResourceStates::CopyDst:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case ResourceStates::PixelShaderResource:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case ResourceStates::DepthWrite:
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case ResourceStates::Present:
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
case ResourceStates::Common:
default:
return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
}
}
} // namespace
VulkanCommandList::~VulkanCommandList() {
Shutdown();
}
bool VulkanCommandList::Initialize(VulkanDevice* device) {
if (device == nullptr || device->GetDevice() == VK_NULL_HANDLE) {
return false;
}
m_device = device;
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = device->GetGraphicsQueueFamilyIndex();
if (vkCreateCommandPool(device->GetDevice(), &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) {
return false;
}
VkCommandBufferAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = m_commandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandBufferCount = 1;
return vkAllocateCommandBuffers(device->GetDevice(), &allocateInfo, &m_commandBuffer) == VK_SUCCESS;
}
void VulkanCommandList::Shutdown() {
if (m_commandPool != VK_NULL_HANDLE && m_device != nullptr) {
if (m_commandBuffer != VK_NULL_HANDLE) {
vkFreeCommandBuffers(m_device->GetDevice(), m_commandPool, 1, &m_commandBuffer);
}
vkDestroyCommandPool(m_device->GetDevice(), m_commandPool, nullptr);
}
m_commandBuffer = VK_NULL_HANDLE;
m_commandPool = VK_NULL_HANDLE;
m_currentColorTarget = nullptr;
m_currentDepthTarget = nullptr;
m_device = nullptr;
}
void VulkanCommandList::Reset() {
if (m_device == nullptr || m_commandPool == VK_NULL_HANDLE || m_commandBuffer == VK_NULL_HANDLE) {
return;
}
vkResetCommandPool(m_device->GetDevice(), m_commandPool, 0);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(m_commandBuffer, &beginInfo);
}
void VulkanCommandList::Close() {
if (m_commandBuffer != VK_NULL_HANDLE) {
vkEndCommandBuffer(m_commandBuffer);
}
}
void VulkanCommandList::TransitionTexture(VulkanTexture* texture, ResourceStates newState) {
if (texture == nullptr || m_commandBuffer == VK_NULL_HANDLE) {
return;
}
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = ToVulkanImageLayout(texture->GetState());
barrier.newLayout = ToVulkanImageLayout(newState);
barrier.srcAccessMask = ToVulkanAccessMask(texture->GetState());
barrier.dstAccessMask = ToVulkanAccessMask(newState);
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = texture->GetImage();
barrier.subresourceRange.aspectMask = GetImageAspectMask(texture->GetFormat());
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(
m_commandBuffer,
ToVulkanStageMask(texture->GetState()),
ToVulkanStageMask(newState),
0,
0, nullptr,
0, nullptr,
1, &barrier);
texture->SetState(newState);
}
void VulkanCommandList::TransitionBarrier(RHIResourceView* resource, ResourceStates stateBefore, ResourceStates stateAfter) {
(void)stateBefore;
auto* view = static_cast<VulkanResourceView*>(resource);
if (view != nullptr) {
TransitionTexture(view->GetTexture(), stateAfter);
}
}
void VulkanCommandList::BeginRenderPass(class RHIRenderPass* renderPass, class RHIFramebuffer* framebuffer, const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) {
(void)renderPass;
(void)framebuffer;
(void)renderArea;
(void)clearValueCount;
(void)clearValues;
}
void VulkanCommandList::EndRenderPass() {
}
void VulkanCommandList::SetShader(RHIShader* shader) {
(void)shader;
}
void VulkanCommandList::SetPipelineState(RHIPipelineState* pso) {
(void)pso;
}
void VulkanCommandList::SetGraphicsDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)count;
(void)descriptorSets;
(void)pipelineLayout;
}
void VulkanCommandList::SetComputeDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)count;
(void)descriptorSets;
(void)pipelineLayout;
}
void VulkanCommandList::SetPrimitiveTopology(PrimitiveTopology topology) {
(void)topology;
}
void VulkanCommandList::SetViewport(const Viewport& viewport) {
(void)viewport;
}
void VulkanCommandList::SetViewports(uint32_t count, const Viewport* viewports) {
(void)count;
(void)viewports;
}
void VulkanCommandList::SetScissorRect(const Rect& rect) {
(void)rect;
}
void VulkanCommandList::SetScissorRects(uint32_t count, const Rect* rects) {
(void)count;
(void)rects;
}
void VulkanCommandList::SetRenderTargets(uint32_t count, RHIResourceView** renderTargets, RHIResourceView* depthStencil) {
m_currentColorTarget = (count > 0 && renderTargets != nullptr) ? renderTargets[0] : nullptr;
m_currentDepthTarget = depthStencil;
}
void VulkanCommandList::SetStencilRef(uint8_t ref) {
(void)ref;
}
void VulkanCommandList::SetBlendFactor(const float factor[4]) {
(void)factor;
}
void VulkanCommandList::SetVertexBuffers(uint32_t startSlot, uint32_t count, RHIResourceView** buffers, const uint64_t* offsets, const uint32_t* strides) {
(void)startSlot;
(void)count;
(void)buffers;
(void)offsets;
(void)strides;
}
void VulkanCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) {
(void)buffer;
(void)offset;
}
void VulkanCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
(void)vertexCount;
(void)instanceCount;
(void)startVertex;
(void)startInstance;
}
void VulkanCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
(void)indexCount;
(void)instanceCount;
(void)startIndex;
(void)baseVertex;
(void)startInstance;
}
void VulkanCommandList::Clear(float r, float g, float b, float a, uint32_t buffers) {
(void)buffers;
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
if (colorView == nullptr || colorView->GetTexture() == nullptr) {
return;
}
VulkanTexture* texture = colorView->GetTexture();
TransitionTexture(texture, ResourceStates::RenderTarget);
VkClearColorValue clearColor = {};
clearColor.float32[0] = r;
clearColor.float32[1] = g;
clearColor.float32[2] = b;
clearColor.float32[3] = a;
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
vkCmdClearColorImage(
m_commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearColor,
1,
&range);
TransitionTexture(texture, ResourceStates::Present);
}
void VulkanCommandList::ClearRenderTarget(RHIResourceView* renderTarget, const float color[4]) {
m_currentColorTarget = renderTarget;
Clear(color[0], color[1], color[2], color[3], 1);
}
void VulkanCommandList::ClearDepthStencil(RHIResourceView* depthStencil, float depth, uint8_t stencil) {
(void)depthStencil;
(void)depth;
(void)stencil;
}
void VulkanCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) {
(void)dst;
(void)src;
}
void VulkanCommandList::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
(void)x;
(void)y;
(void)z;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,80 @@
#include "XCEngine/RHI/Vulkan/VulkanCommandQueue.h"
#include "XCEngine/RHI/RHIFence.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include <vector>
namespace XCEngine {
namespace RHI {
bool VulkanCommandQueue::Initialize(VulkanDevice* device, CommandQueueType type) {
if (device == nullptr || device->GetGraphicsQueue() == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_queue = device->GetGraphicsQueue();
m_type = type;
return true;
}
void VulkanCommandQueue::Shutdown() {
m_queue = VK_NULL_HANDLE;
m_device = nullptr;
m_currentFrame = 0;
}
void VulkanCommandQueue::ExecuteCommandLists(uint32_t count, void** lists) {
if (m_queue == VK_NULL_HANDLE || count == 0 || lists == nullptr) {
return;
}
std::vector<VkCommandBuffer> commandBuffers;
commandBuffers.reserve(count);
for (uint32_t i = 0; i < count; ++i) {
auto* commandList = static_cast<VulkanCommandList*>(lists[i]);
if (commandList != nullptr && commandList->GetCommandBuffer() != VK_NULL_HANDLE) {
commandBuffers.push_back(commandList->GetCommandBuffer());
}
}
if (commandBuffers.empty()) {
return;
}
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
submitInfo.pCommandBuffers = commandBuffers.data();
vkQueueSubmit(m_queue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(m_queue);
++m_currentFrame;
}
void VulkanCommandQueue::Signal(RHIFence* fence, uint64_t value) {
if (fence != nullptr) {
fence->Signal(value);
}
}
void VulkanCommandQueue::Wait(RHIFence* fence, uint64_t value) {
if (fence != nullptr) {
fence->Wait(value);
}
}
uint64_t VulkanCommandQueue::GetCompletedValue() {
return m_currentFrame;
}
void VulkanCommandQueue::WaitForIdle() {
if (m_queue != VK_NULL_HANDLE) {
vkQueueWaitIdle(m_queue);
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,342 @@
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandQueue.h"
#include "XCEngine/RHI/Vulkan/VulkanFence.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#include <algorithm>
#include <iterator>
#include <vector>
namespace XCEngine {
namespace RHI {
namespace {
std::wstring ResolveVendorName(uint32_t vendorId) {
switch (vendorId) {
case 0x10DE: return L"NVIDIA";
case 0x1002: return L"AMD";
case 0x8086: return L"Intel";
case 0x13B5: return L"ARM";
case 0x5143: return L"Qualcomm";
default: return L"Unknown";
}
}
} // namespace
VulkanDevice::~VulkanDevice() {
Shutdown();
}
bool VulkanDevice::Initialize(const RHIDeviceDesc& desc) {
if (m_initialized) {
return true;
}
m_deviceDesc = desc;
if (!CreateInstance()) {
return false;
}
if (!PickPhysicalDevice()) {
Shutdown();
return false;
}
if (!CreateLogicalDevice()) {
Shutdown();
return false;
}
QueryDeviceInfo();
m_initialized = true;
return true;
}
bool VulkanDevice::CreateInstance() {
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "XCEngine";
appInfo.pEngineName = "XCEngine";
appInfo.apiVersion = VK_API_VERSION_1_1;
const char* extensions[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.enabledExtensionCount = static_cast<uint32_t>(std::size(extensions));
createInfo.ppEnabledExtensionNames = extensions;
return vkCreateInstance(&createInfo, nullptr, &m_instance) == VK_SUCCESS;
}
bool VulkanDevice::PickPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
if (deviceCount == 0) {
return false;
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices.data());
for (VkPhysicalDevice physicalDevice : devices) {
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
for (uint32_t index = 0; index < queueFamilyCount; ++index) {
if ((queueFamilies[index].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
m_physicalDevice = physicalDevice;
m_graphicsQueueFamilyIndex = index;
return true;
}
}
}
return false;
}
bool VulkanDevice::CreateLogicalDevice() {
const float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = m_graphicsQueueFamilyIndex;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
VkPhysicalDeviceFeatures features = {};
const char* deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = 1;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.pEnabledFeatures = &features;
createInfo.enabledExtensionCount = static_cast<uint32_t>(std::size(deviceExtensions));
createInfo.ppEnabledExtensionNames = deviceExtensions;
if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) {
return false;
}
vkGetDeviceQueue(m_device, m_graphicsQueueFamilyIndex, 0, &m_graphicsQueue);
return m_graphicsQueue != VK_NULL_HANDLE;
}
void VulkanDevice::QueryDeviceInfo() {
VkPhysicalDeviceProperties properties = {};
VkPhysicalDeviceFeatures features = {};
vkGetPhysicalDeviceProperties(m_physicalDevice, &properties);
vkGetPhysicalDeviceFeatures(m_physicalDevice, &features);
m_deviceInfo.description = WidenAscii(properties.deviceName);
m_deviceInfo.vendor = ResolveVendorName(properties.vendorID);
m_deviceInfo.renderer = WidenAscii(properties.deviceName);
m_deviceInfo.version = L"Vulkan";
m_deviceInfo.majorVersion = ResolveVulkanApiMajor(properties.apiVersion);
m_deviceInfo.minorVersion = ResolveVulkanApiMinor(properties.apiVersion);
m_deviceInfo.vendorId = properties.vendorID;
m_deviceInfo.deviceId = properties.deviceID;
m_deviceInfo.isSoftware = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
m_capabilities.bSupportsGeometryShaders = features.geometryShader == VK_TRUE;
m_capabilities.bSupportsTessellation = features.tessellationShader == VK_TRUE;
m_capabilities.bSupportsComputeShaders = true;
m_capabilities.bSupportsMultiViewport = features.multiViewport == VK_TRUE;
m_capabilities.bSupportsExplicitMultiThreading = true;
m_capabilities.maxTexture2DSize = properties.limits.maxImageDimension2D;
m_capabilities.maxTexture3DSize = properties.limits.maxImageDimension3D;
m_capabilities.maxTextureCubeSize = properties.limits.maxImageDimensionCube;
m_capabilities.maxRenderTargets = properties.limits.maxColorAttachments;
m_capabilities.maxColorAttachments = properties.limits.maxColorAttachments;
m_capabilities.maxViewports = properties.limits.maxViewports;
m_capabilities.maxVertexAttribs = properties.limits.maxVertexInputAttributes;
m_capabilities.maxConstantBufferSize = properties.limits.maxUniformBufferRange;
m_capabilities.maxAnisotropy = static_cast<uint32_t>(properties.limits.maxSamplerAnisotropy);
m_capabilities.majorVersion = static_cast<int>(m_deviceInfo.majorVersion);
m_capabilities.minorVersion = static_cast<int>(m_deviceInfo.minorVersion);
m_capabilities.shaderModel = "SPIR-V";
}
void VulkanDevice::Shutdown() {
if (m_device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(m_device);
vkDestroyDevice(m_device, nullptr);
m_device = VK_NULL_HANDLE;
}
if (m_instance != VK_NULL_HANDLE) {
vkDestroyInstance(m_instance, nullptr);
m_instance = VK_NULL_HANDLE;
}
m_physicalDevice = VK_NULL_HANDLE;
m_graphicsQueue = VK_NULL_HANDLE;
m_graphicsQueueFamilyIndex = UINT32_MAX;
m_initialized = false;
}
uint32_t VulkanDevice::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) const {
VkPhysicalDeviceMemoryProperties memoryProperties = {};
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) {
if ((typeFilter & (1u << i)) != 0 &&
(memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
return UINT32_MAX;
}
RHIBuffer* VulkanDevice::CreateBuffer(const BufferDesc& desc) {
(void)desc;
return nullptr;
}
RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc) {
(void)desc;
return nullptr;
}
RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch) {
(void)desc;
(void)initialData;
(void)initialDataSize;
(void)rowPitch;
return nullptr;
}
RHISwapChain* VulkanDevice::CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) {
auto* swapChain = new VulkanSwapChain();
if (swapChain->Initialize(this, static_cast<VulkanCommandQueue*>(presentQueue), static_cast<HWND__*>(desc.windowHandle), desc.width, desc.height)) {
return swapChain;
}
delete swapChain;
return nullptr;
}
RHICommandList* VulkanDevice::CreateCommandList(const CommandListDesc& desc) {
(void)desc;
auto* commandList = new VulkanCommandList();
if (commandList->Initialize(this)) {
return commandList;
}
delete commandList;
return nullptr;
}
RHICommandQueue* VulkanDevice::CreateCommandQueue(const CommandQueueDesc& desc) {
auto* queue = new VulkanCommandQueue();
if (queue->Initialize(this, static_cast<CommandQueueType>(desc.queueType))) {
return queue;
}
delete queue;
return nullptr;
}
RHIShader* VulkanDevice::CreateShader(const ShaderCompileDesc& desc) {
(void)desc;
return nullptr;
}
RHIPipelineState* VulkanDevice::CreatePipelineState(const GraphicsPipelineDesc& desc) {
(void)desc;
return nullptr;
}
RHIPipelineLayout* VulkanDevice::CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) {
(void)desc;
return nullptr;
}
RHIFence* VulkanDevice::CreateFence(const FenceDesc& desc) {
return new VulkanFence(desc.initialValue);
}
RHISampler* VulkanDevice::CreateSampler(const SamplerDesc& desc) {
(void)desc;
return nullptr;
}
RHIRenderPass* VulkanDevice::CreateRenderPass(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments, const AttachmentDesc* depthStencilAttachment) {
(void)colorAttachmentCount;
(void)colorAttachments;
(void)depthStencilAttachment;
return nullptr;
}
RHIFramebuffer* VulkanDevice::CreateFramebuffer(class RHIRenderPass* renderPass, uint32_t width, uint32_t height, uint32_t colorAttachmentCount, RHIResourceView** colorAttachments, RHIResourceView* depthStencilAttachment) {
(void)renderPass;
(void)width;
(void)height;
(void)colorAttachmentCount;
(void)colorAttachments;
(void)depthStencilAttachment;
return nullptr;
}
RHIDescriptorPool* VulkanDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
(void)desc;
return nullptr;
}
RHIDescriptorSet* VulkanDevice::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) {
(void)pool;
(void)layout;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) {
(void)buffer;
(void)desc;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) {
(void)buffer;
(void)desc;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
auto* view = new VulkanResourceView();
if (view->InitializeAsRenderTarget(m_device, static_cast<VulkanTexture*>(texture), desc)) {
return view;
}
delete view;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture;
(void)desc;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture;
(void)desc;
return nullptr;
}
RHIResourceView* VulkanDevice::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture;
(void)desc;
return nullptr;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,47 @@
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
namespace XCEngine {
namespace RHI {
VulkanResourceView::~VulkanResourceView() {
Shutdown();
}
bool VulkanResourceView::InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc) {
if (device == VK_NULL_HANDLE || texture == nullptr || texture->GetImage() == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_texture = texture;
m_viewType = ResourceViewType::RenderTarget;
m_dimension = desc.dimension != ResourceViewDimension::Unknown ? desc.dimension : ResourceViewDimension::Texture2D;
m_format = desc.format != 0 ? static_cast<Format>(desc.format) : texture->GetFormat();
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture->GetImage();
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = texture->GetVkFormat();
viewInfo.subresourceRange.aspectMask = GetImageAspectMask(texture->GetFormat());
viewInfo.subresourceRange.baseMipLevel = desc.mipLevel;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = desc.firstArraySlice;
viewInfo.subresourceRange.layerCount = 1;
return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS;
}
void VulkanResourceView::Shutdown() {
if (m_imageView != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, m_imageView, nullptr);
}
m_imageView = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_texture = nullptr;
m_format = Format::Unknown;
m_dimension = ResourceViewDimension::Unknown;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,187 @@
#include "XCEngine/RHI/Vulkan/VulkanScreenshot.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
#include <cstdio>
#include <vector>
namespace XCEngine {
namespace RHI {
bool VulkanScreenshot::Capture(RHIDevice* device, RHISwapChain* swapChain, const char* filename) {
auto* vkDevice = static_cast<VulkanDevice*>(device);
auto* texture = static_cast<VulkanTexture*>(swapChain->GetCurrentBackBuffer());
if (vkDevice == nullptr || texture == nullptr) {
return false;
}
const uint32_t width = texture->GetWidth();
const uint32_t height = texture->GetHeight();
const VkDevice logicalDevice = vkDevice->GetDevice();
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(width) * static_cast<VkDeviceSize>(height) * 4;
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(logicalDevice, &bufferInfo, nullptr, &stagingBuffer) != VK_SUCCESS) {
return false;
}
VkMemoryRequirements memoryRequirements = {};
vkGetBufferMemoryRequirements(logicalDevice, stagingBuffer, &memoryRequirements);
VkMemoryAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = vkDevice->FindMemoryType(
memoryRequirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (allocateInfo.memoryTypeIndex == UINT32_MAX ||
vkAllocateMemory(logicalDevice, &allocateInfo, nullptr, &stagingMemory) != VK_SUCCESS) {
vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr);
return false;
}
vkBindBufferMemory(logicalDevice, stagingBuffer, stagingMemory, 0);
VkCommandPool commandPool = VK_NULL_HANDLE;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = vkDevice->GetGraphicsQueueFamilyIndex();
if (vkCreateCommandPool(logicalDevice, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
vkFreeMemory(logicalDevice, stagingMemory, nullptr);
vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr);
return false;
}
VkCommandBufferAllocateInfo commandBufferInfo = {};
commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferInfo.commandPool = commandPool;
commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
commandBufferInfo.commandBufferCount = 1;
vkAllocateCommandBuffers(logicalDevice, &commandBufferInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkImageMemoryBarrier toCopySource = {};
toCopySource.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toCopySource.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
toCopySource.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
toCopySource.srcAccessMask = 0;
toCopySource.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
toCopySource.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toCopySource.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toCopySource.image = texture->GetImage();
toCopySource.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toCopySource.subresourceRange.baseMipLevel = 0;
toCopySource.subresourceRange.levelCount = 1;
toCopySource.subresourceRange.baseArrayLayer = 0;
toCopySource.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &toCopySource);
VkBufferImageCopy copyRegion = {};
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyRegion.imageSubresource.mipLevel = 0;
copyRegion.imageSubresource.baseArrayLayer = 0;
copyRegion.imageSubresource.layerCount = 1;
copyRegion.imageExtent.width = width;
copyRegion.imageExtent.height = height;
copyRegion.imageExtent.depth = 1;
vkCmdCopyImageToBuffer(
commandBuffer,
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
stagingBuffer,
1,
&copyRegion);
VkImageMemoryBarrier backToPresent = toCopySource;
backToPresent.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
backToPresent.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
backToPresent.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
backToPresent.dstAccessMask = 0;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0, nullptr,
0, nullptr,
1, &backToPresent);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(vkDevice->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(vkDevice->GetGraphicsQueue());
void* mappedData = nullptr;
if (vkMapMemory(logicalDevice, stagingMemory, 0, bufferSize, 0, &mappedData) != VK_SUCCESS) {
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
vkFreeMemory(logicalDevice, stagingMemory, nullptr);
vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr);
return false;
}
FILE* fp = fopen(filename, "wb");
if (fp == nullptr) {
vkUnmapMemory(logicalDevice, stagingMemory);
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
vkFreeMemory(logicalDevice, stagingMemory, nullptr);
vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr);
return false;
}
fprintf(fp, "P6\n%d %d\n255\n", width, height);
const uint8_t* bytes = static_cast<const uint8_t*>(mappedData);
const bool isBGRA = texture->GetVkFormat() == VK_FORMAT_B8G8R8A8_UNORM;
for (uint32_t y = 0; y < height; ++y) {
for (uint32_t x = 0; x < width; ++x) {
const size_t index = (static_cast<size_t>(y) * width + x) * 4;
const uint8_t r = isBGRA ? bytes[index + 2] : bytes[index + 0];
const uint8_t g = bytes[index + 1];
const uint8_t b = isBGRA ? bytes[index + 0] : bytes[index + 2];
fwrite(&r, 1, 1, fp);
fwrite(&g, 1, 1, fp);
fwrite(&b, 1, 1, fp);
}
}
fclose(fp);
vkUnmapMemory(logicalDevice, stagingMemory);
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
vkFreeMemory(logicalDevice, stagingMemory, nullptr);
vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr);
return true;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,234 @@
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandQueue.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
#include <algorithm>
#include <vector>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
namespace XCEngine {
namespace RHI {
VulkanSwapChain::~VulkanSwapChain() {
Shutdown();
}
bool VulkanSwapChain::Initialize(VulkanDevice* device, VulkanCommandQueue* presentQueue, HWND__* window, uint32_t width, uint32_t height) {
if (device == nullptr || presentQueue == nullptr || window == nullptr) {
return false;
}
m_device = device;
m_presentQueue = presentQueue;
m_window = window;
m_width = width;
m_height = height;
if (!CreateSurface(window)) {
return false;
}
return CreateSwapChainResources();
}
bool VulkanSwapChain::CreateSurface(HWND__* window) {
VkWin32SurfaceCreateInfoKHR surfaceInfo = {};
surfaceInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surfaceInfo.hinstance = GetModuleHandle(nullptr);
surfaceInfo.hwnd = window;
return vkCreateWin32SurfaceKHR(m_device->GetInstance(), &surfaceInfo, nullptr, &m_surface) == VK_SUCCESS;
}
bool VulkanSwapChain::CreateSwapChainResources() {
VkSurfaceCapabilitiesKHR capabilities = {};
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_device->GetPhysicalDevice(), m_surface, &capabilities) != VK_SUCCESS) {
return false;
}
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(m_device->GetPhysicalDevice(), m_surface, &formatCount, nullptr);
if (formatCount == 0) {
return false;
}
std::vector<VkSurfaceFormatKHR> formats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(m_device->GetPhysicalDevice(), m_surface, &formatCount, formats.data());
VkSurfaceFormatKHR selectedFormat = formats[0];
for (const VkSurfaceFormatKHR& candidate : formats) {
if (candidate.format == VK_FORMAT_B8G8R8A8_UNORM) {
selectedFormat = candidate;
break;
}
}
m_surfaceFormat = selectedFormat.format;
if ((capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0 ||
(capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) == 0) {
return false;
}
uint32_t presentModeCount = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(m_device->GetPhysicalDevice(), m_surface, &presentModeCount, nullptr);
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
if (presentModeCount > 0) {
vkGetPhysicalDeviceSurfacePresentModesKHR(m_device->GetPhysicalDevice(), m_surface, &presentModeCount, presentModes.data());
}
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
for (VkPresentModeKHR candidate : presentModes) {
if (candidate == VK_PRESENT_MODE_MAILBOX_KHR) {
presentMode = candidate;
break;
}
}
const uint32_t requestedImageCount = (std::max<uint32_t>)(2, capabilities.minImageCount);
const uint32_t imageCount = capabilities.maxImageCount > 0
? std::min<uint32_t>(requestedImageCount, capabilities.maxImageCount)
: requestedImageCount;
VkExtent2D extent = capabilities.currentExtent;
if (extent.width == UINT32_MAX) {
extent.width = m_width;
extent.height = m_height;
}
m_width = extent.width;
m_height = extent.height;
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = m_surface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = selectedFormat.format;
createInfo.imageColorSpace = selectedFormat.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.preTransform = capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(m_device->GetDevice(), &createInfo, nullptr, &m_swapChain) != VK_SUCCESS) {
return false;
}
uint32_t swapChainImageCount = 0;
vkGetSwapchainImagesKHR(m_device->GetDevice(), m_swapChain, &swapChainImageCount, nullptr);
std::vector<VkImage> images(swapChainImageCount);
vkGetSwapchainImagesKHR(m_device->GetDevice(), m_swapChain, &swapChainImageCount, images.data());
m_backBuffers.clear();
m_backBuffers.reserve(swapChainImageCount);
for (VkImage image : images) {
auto texture = std::make_unique<VulkanTexture>();
texture->InitializeSwapChainImage(
m_device->GetDevice(),
image,
m_width,
m_height,
ToRHIFormat(selectedFormat.format),
selectedFormat.format);
m_backBuffers.push_back(std::move(texture));
}
return !m_backBuffers.empty();
}
void VulkanSwapChain::DestroySwapChainResources() {
m_backBuffers.clear();
if (m_swapChain != VK_NULL_HANDLE && m_device != nullptr) {
vkDestroySwapchainKHR(m_device->GetDevice(), m_swapChain, nullptr);
m_swapChain = VK_NULL_HANDLE;
}
}
bool VulkanSwapChain::AcquireNextImage() {
if (m_device == nullptr || m_swapChain == VK_NULL_HANDLE) {
return false;
}
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkFence acquireFence = VK_NULL_HANDLE;
if (vkCreateFence(m_device->GetDevice(), &fenceInfo, nullptr, &acquireFence) != VK_SUCCESS) {
return false;
}
const VkResult result = vkAcquireNextImageKHR(
m_device->GetDevice(),
m_swapChain,
UINT64_MAX,
VK_NULL_HANDLE,
acquireFence,
&m_currentImageIndex);
if (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) {
vkWaitForFences(m_device->GetDevice(), 1, &acquireFence, VK_TRUE, UINT64_MAX);
vkDestroyFence(m_device->GetDevice(), acquireFence, nullptr);
return true;
}
vkDestroyFence(m_device->GetDevice(), acquireFence, nullptr);
return false;
}
void VulkanSwapChain::Shutdown() {
DestroySwapChainResources();
if (m_surface != VK_NULL_HANDLE && m_device != nullptr) {
vkDestroySurfaceKHR(m_device->GetInstance(), m_surface, nullptr);
m_surface = VK_NULL_HANDLE;
}
m_presentQueue = nullptr;
m_window = nullptr;
m_device = nullptr;
m_width = 0;
m_height = 0;
m_currentImageIndex = 0;
}
RHITexture* VulkanSwapChain::GetCurrentBackBuffer() {
if (m_currentImageIndex >= m_backBuffers.size()) {
return nullptr;
}
return m_backBuffers[m_currentImageIndex].get();
}
void VulkanSwapChain::Present(uint32_t syncInterval, uint32_t flags) {
(void)syncInterval;
(void)flags;
if (m_presentQueue == nullptr || m_swapChain == VK_NULL_HANDLE) {
return;
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &m_swapChain;
presentInfo.pImageIndices = &m_currentImageIndex;
vkQueuePresentKHR(m_presentQueue->GetQueue(), &presentInfo);
}
void VulkanSwapChain::Resize(uint32_t width, uint32_t height) {
m_width = width;
m_height = height;
DestroySwapChainResources();
CreateSwapChainResources();
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,39 @@
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
namespace XCEngine {
namespace RHI {
VulkanTexture::~VulkanTexture() {
Shutdown();
}
bool VulkanTexture::InitializeSwapChainImage(
VkDevice device,
VkImage image,
uint32_t width,
uint32_t height,
Format format,
VkFormat vkFormat) {
m_device = device;
m_image = image;
m_width = width;
m_height = height;
m_format = format;
m_vkFormat = vkFormat;
m_textureType = TextureType::Texture2D;
m_state = ResourceStates::Common;
return m_image != VK_NULL_HANDLE;
}
void VulkanTexture::Shutdown() {
m_image = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_width = 0;
m_height = 0;
m_format = Format::Unknown;
m_vkFormat = VK_FORMAT_UNDEFINED;
m_state = ResourceStates::Common;
}
} // namespace RHI
} // namespace XCEngine