Add Vulkan quad integration path

This commit is contained in:
2026-03-27 13:52:56 +08:00
parent 4b21b5d3d1
commit 727b6ca249
23 changed files with 1574 additions and 39 deletions

View File

@@ -155,6 +155,10 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanCommon.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanBuffer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanTexture.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanSampler.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanDescriptorSet.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanPipelineLayout.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanPipelineState.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanResourceView.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanFence.h
@@ -165,6 +169,10 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanScreenshot.h
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanBuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanTexture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanSampler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDescriptorPool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDescriptorSet.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanPipelineLayout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanPipelineState.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanResourceView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanCommandQueue.cpp

View File

@@ -8,6 +8,7 @@
#include "XCEngine/RHI/RHIEnums.h"
#include <cmath>
#include <string>
namespace XCEngine {
@@ -245,6 +246,162 @@ inline VkSampleCountFlagBits ToVulkanSampleCount(uint32_t sampleCount) {
}
}
inline VkDescriptorType ToVulkanDescriptorType(DescriptorType type) {
switch (type) {
case DescriptorType::CBV:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case DescriptorType::SRV:
return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
case DescriptorType::UAV:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
case DescriptorType::Sampler:
return VK_DESCRIPTOR_TYPE_SAMPLER;
default:
return VK_DESCRIPTOR_TYPE_MAX_ENUM;
}
}
inline VkShaderStageFlags ToVulkanShaderStageFlags(uint32_t visibility) {
switch (static_cast<ShaderVisibility>(visibility)) {
case ShaderVisibility::Vertex:
return VK_SHADER_STAGE_VERTEX_BIT;
case ShaderVisibility::Geometry:
return VK_SHADER_STAGE_GEOMETRY_BIT;
case ShaderVisibility::Pixel:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case ShaderVisibility::Amplification:
case ShaderVisibility::Mesh:
case ShaderVisibility::Hull:
case ShaderVisibility::Domain:
case ShaderVisibility::All:
default:
return VK_SHADER_STAGE_ALL_GRAPHICS;
}
}
inline VkFilter ToVulkanMinFilter(FilterMode filter) {
switch (filter) {
case FilterMode::Point:
case FilterMode::ComparisonPoint:
case FilterMode::MinimumPoint:
case FilterMode::MaximumPoint:
return VK_FILTER_NEAREST;
default:
return VK_FILTER_LINEAR;
}
}
inline VkFilter ToVulkanMagFilter(FilterMode filter) {
switch (filter) {
case FilterMode::Point:
case FilterMode::ComparisonPoint:
case FilterMode::MinimumPoint:
case FilterMode::MaximumPoint:
return VK_FILTER_NEAREST;
default:
return VK_FILTER_LINEAR;
}
}
inline VkSamplerMipmapMode ToVulkanSamplerMipmapMode(FilterMode filter) {
switch (filter) {
case FilterMode::Point:
case FilterMode::ComparisonPoint:
case FilterMode::MinimumPoint:
case FilterMode::MaximumPoint:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
default:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
inline bool UsesVulkanSamplerAnisotropy(FilterMode filter) {
return filter == FilterMode::Anisotropic ||
filter == FilterMode::ComparisonAnisotropic ||
filter == FilterMode::MinimumAnisotropic ||
filter == FilterMode::MaximumAnisotropic;
}
inline bool UsesVulkanComparisonSampling(FilterMode filter) {
return filter == FilterMode::ComparisonPoint ||
filter == FilterMode::ComparisonLinear ||
filter == FilterMode::ComparisonAnisotropic;
}
inline VkSamplerAddressMode ToVulkanSamplerAddressMode(TextureAddressMode mode) {
switch (mode) {
case TextureAddressMode::Wrap:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case TextureAddressMode::Mirror:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case TextureAddressMode::Clamp:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case TextureAddressMode::Border:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case TextureAddressMode::MirrorOnce:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
inline VkBorderColor ToVulkanBorderColor(float r, float g, float b, float a) {
const bool opaqueBlack = std::fabs(r) < 0.0001f &&
std::fabs(g) < 0.0001f &&
std::fabs(b) < 0.0001f &&
std::fabs(a - 1.0f) < 0.0001f;
if (opaqueBlack) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
}
const bool opaqueWhite = std::fabs(r - 1.0f) < 0.0001f &&
std::fabs(g - 1.0f) < 0.0001f &&
std::fabs(b - 1.0f) < 0.0001f &&
std::fabs(a - 1.0f) < 0.0001f;
if (opaqueWhite) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
}
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
inline VkImageViewType ToVulkanImageViewType(ResourceViewDimension dimension, TextureType textureType) {
switch (dimension) {
case ResourceViewDimension::Texture1D:
return VK_IMAGE_VIEW_TYPE_1D;
case ResourceViewDimension::Texture1DArray:
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
case ResourceViewDimension::Texture2DArray:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case ResourceViewDimension::Texture3D:
return VK_IMAGE_VIEW_TYPE_3D;
case ResourceViewDimension::TextureCube:
return VK_IMAGE_VIEW_TYPE_CUBE;
case ResourceViewDimension::TextureCubeArray:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case ResourceViewDimension::Texture2D:
case ResourceViewDimension::Unknown:
default:
break;
}
switch (textureType) {
case TextureType::Texture1D:
return VK_IMAGE_VIEW_TYPE_1D;
case TextureType::Texture2DArray:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case TextureType::Texture3D:
return VK_IMAGE_VIEW_TYPE_3D;
case TextureType::TextureCube:
return VK_IMAGE_VIEW_TYPE_CUBE;
case TextureType::TextureCubeArray:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case TextureType::Texture2D:
default:
return VK_IMAGE_VIEW_TYPE_2D;
}
}
inline uint32_t ResolveVulkanApiMajor(uint32_t apiVersion) {
return VK_API_VERSION_MAJOR(apiVersion);
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "XCEngine/RHI/RHIDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <vector>
namespace XCEngine {
namespace RHI {
class VulkanDescriptorSet;
class VulkanDescriptorPool : public RHIDescriptorPool {
public:
VulkanDescriptorPool() = default;
~VulkanDescriptorPool() override;
bool Initialize(VkDevice device, const DescriptorPoolDesc& desc);
bool Initialize(const DescriptorPoolDesc& desc) override;
void Shutdown() override;
void* GetNativeHandle() override { return m_descriptorPool; }
uint32_t GetDescriptorCount() const override { return m_descriptorCount; }
DescriptorHeapType GetType() const override { return m_type; }
RHIDescriptorSet* AllocateSet(const DescriptorSetLayoutDesc& layout) override;
void FreeSet(RHIDescriptorSet* set) override;
VkDevice GetDevice() const { return m_device; }
VkDescriptorPool GetDescriptorPool() const { return m_descriptorPool; }
private:
VkDevice m_device = VK_NULL_HANDLE;
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
DescriptorHeapType m_type = DescriptorHeapType::CBV_SRV_UAV;
uint32_t m_descriptorCount = 0;
std::vector<VulkanDescriptorSet*> m_allocatedSets;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,63 @@
#pragma once
#include "XCEngine/RHI/RHIDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <unordered_map>
#include <vector>
namespace XCEngine {
namespace RHI {
class VulkanDescriptorPool;
class VulkanDescriptorSet : public RHIDescriptorSet {
public:
VulkanDescriptorSet() = default;
~VulkanDescriptorSet() override;
bool Initialize(
VkDevice device,
VulkanDescriptorPool* pool,
VkDescriptorSetLayout layout,
VkDescriptorSet descriptorSet,
const DescriptorSetLayoutDesc& desc);
void Shutdown() override;
void Bind() override;
void Unbind() override;
void Update(uint32_t offset, RHIResourceView* view) override;
void UpdateSampler(uint32_t offset, RHISampler* sampler) override;
void WriteConstant(uint32_t binding, const void* data, size_t size, size_t offset = 0) override;
uint32_t GetBindingCount() const override { return static_cast<uint32_t>(m_bindings.size()); }
const DescriptorSetLayoutBinding* GetBindings() const override {
return m_bindings.empty() ? nullptr : m_bindings.data();
}
void* GetConstantBufferData() override {
return m_constantBufferData.empty() ? nullptr : m_constantBufferData.data();
}
size_t GetConstantBufferSize() const override { return m_constantBufferData.size(); }
bool IsConstantDirty() const override { return m_constantDirty; }
void MarkConstantClean() override { m_constantDirty = false; }
VkDescriptorSet GetDescriptorSet() const { return m_descriptorSet; }
VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; }
private:
const DescriptorSetLayoutBinding* FindBinding(uint32_t binding) const;
VkDevice m_device = VK_NULL_HANDLE;
VulkanDescriptorPool* m_pool = nullptr;
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE;
std::vector<DescriptorSetLayoutBinding> m_bindings;
std::unordered_map<uint32_t, uint32_t> m_bindingToIndex;
std::vector<uint8_t> m_constantBufferData;
bool m_constantDirty = false;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,40 @@
#pragma once
#include "XCEngine/RHI/RHIPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
#include <vector>
namespace XCEngine {
namespace RHI {
class VulkanPipelineLayout : public RHIPipelineLayout {
public:
VulkanPipelineLayout() = default;
~VulkanPipelineLayout() override;
bool Initialize(VkDevice device, const RHIPipelineLayoutDesc& desc);
bool Initialize(const RHIPipelineLayoutDesc& desc) override;
void Shutdown() override;
void* GetNativeHandle() override { return m_pipelineLayout; }
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
const RHIPipelineLayoutDesc& GetDesc() const { return m_desc; }
bool UsesSetLayouts() const { return m_desc.setLayoutCount > 0 && m_desc.setLayouts != nullptr; }
private:
void PrepareExplicitLayouts(const RHIPipelineLayoutDesc& desc);
void PrepareFlatLayouts(const RHIPipelineLayoutDesc& desc);
bool CreateNativeSetLayouts();
VkDevice m_device = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
RHIPipelineLayoutDesc m_desc = {};
std::vector<DescriptorSetLayoutDesc> m_setLayouts;
std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings;
std::vector<VkDescriptorSetLayout> m_nativeSetLayouts;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -51,6 +51,7 @@ private:
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
VkRenderPass m_renderPass = VK_NULL_HANDLE;
bool m_ownsPipelineLayout = false;
InputLayoutDesc m_inputLayoutDesc = {};
RasterizerDesc m_rasterizerDesc = {};
BlendDesc m_blendDesc = {};

View File

@@ -15,6 +15,7 @@ public:
~VulkanResourceView() override;
bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
bool InitializeAsShaderResource(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
bool InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc);
bool InitializeAsIndexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc);

View File

@@ -0,0 +1,31 @@
#pragma once
#include "XCEngine/RHI/RHISampler.h"
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
namespace XCEngine {
namespace RHI {
class VulkanSampler : public RHISampler {
public:
VulkanSampler() = default;
~VulkanSampler() override;
bool Initialize(VkDevice device, const SamplerDesc& desc);
void Shutdown() override;
void Bind(unsigned int unit) override;
void Unbind(unsigned int unit) override;
void* GetNativeHandle() override { return m_sampler != VK_NULL_HANDLE ? &m_sampler : nullptr; }
unsigned int GetID() override { return 0; }
VkSampler GetSampler() const { return m_sampler; }
private:
VkDevice m_device = VK_NULL_HANDLE;
VkSampler m_sampler = VK_NULL_HANDLE;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -20,13 +20,23 @@ public:
uint32_t height,
Format format,
VkFormat vkFormat);
bool InitializeOwnedImage(
VkDevice device,
VkImage image,
VkDeviceMemory memory,
uint32_t width,
uint32_t height,
uint32_t mipLevels,
Format format,
TextureType textureType,
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; }
uint32_t GetMipLevels() const override { return m_mipLevels; }
Format GetFormat() const override { return m_format; }
TextureType GetTextureType() const override { return m_textureType; }
@@ -39,17 +49,21 @@ public:
void SetName(const std::string& name) override { m_name = name; }
VkImage GetImage() const { return m_image; }
VkDeviceMemory GetMemory() const { return m_memory; }
VkFormat GetVkFormat() const { return m_vkFormat; }
private:
VkDevice m_device = VK_NULL_HANDLE;
VkImage m_image = VK_NULL_HANDLE;
VkDeviceMemory m_memory = VK_NULL_HANDLE;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_mipLevels = 1;
Format m_format = Format::Unknown;
TextureType m_textureType = TextureType::Texture2D;
ResourceStates m_state = ResourceStates::Common;
VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
bool m_ownsImage = false;
std::string m_name;
};

View File

@@ -1,7 +1,9 @@
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
@@ -217,17 +219,66 @@ void VulkanCommandList::SetPipelineState(RHIPipelineState* pso) {
}
void VulkanCommandList::SetGraphicsDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)count;
(void)descriptorSets;
(void)pipelineLayout;
if (count == 0 || descriptorSets == nullptr || m_commandBuffer == VK_NULL_HANDLE) {
return;
}
VkPipelineLayout nativePipelineLayout = VK_NULL_HANDLE;
if (pipelineLayout != nullptr) {
nativePipelineLayout = static_cast<VulkanPipelineLayout*>(pipelineLayout)->GetPipelineLayout();
} else if (m_currentPipelineState != nullptr) {
nativePipelineLayout = m_currentPipelineState->GetPipelineLayout();
}
if (nativePipelineLayout == VK_NULL_HANDLE) {
return;
}
std::vector<VkDescriptorSet> nativeSets;
nativeSets.reserve(count);
for (uint32_t i = 0; i < count; ++i) {
auto* descriptorSet = static_cast<VulkanDescriptorSet*>(descriptorSets[i]);
if (descriptorSet == nullptr || descriptorSet->GetDescriptorSet() == VK_NULL_HANDLE) {
return;
}
nativeSets.push_back(descriptorSet->GetDescriptorSet());
}
vkCmdBindDescriptorSets(
m_commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
nativePipelineLayout,
firstSet,
static_cast<uint32_t>(nativeSets.size()),
nativeSets.data(),
0,
nullptr);
}
void VulkanCommandList::SetComputeDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)count;
(void)descriptorSets;
(void)pipelineLayout;
if (count == 0 || descriptorSets == nullptr || m_commandBuffer == VK_NULL_HANDLE || pipelineLayout == nullptr) {
return;
}
std::vector<VkDescriptorSet> nativeSets;
nativeSets.reserve(count);
for (uint32_t i = 0; i < count; ++i) {
auto* descriptorSet = static_cast<VulkanDescriptorSet*>(descriptorSets[i]);
if (descriptorSet == nullptr || descriptorSet->GetDescriptorSet() == VK_NULL_HANDLE) {
return;
}
nativeSets.push_back(descriptorSet->GetDescriptorSet());
}
vkCmdBindDescriptorSets(
m_commandBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE,
static_cast<VulkanPipelineLayout*>(pipelineLayout)->GetPipelineLayout(),
firstSet,
static_cast<uint32_t>(nativeSets.size()),
nativeSets.data(),
0,
nullptr);
}
void VulkanCommandList::SetPrimitiveTopology(PrimitiveTopology topology) {

View File

@@ -0,0 +1,140 @@
#include "XCEngine/RHI/Vulkan/VulkanDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include <algorithm>
namespace XCEngine {
namespace RHI {
namespace {
std::vector<VkDescriptorSetLayoutBinding> BuildVulkanLayoutBindings(const DescriptorSetLayoutDesc& layout) {
std::vector<VkDescriptorSetLayoutBinding> bindings;
bindings.reserve(layout.bindingCount);
for (uint32_t i = 0; i < layout.bindingCount; ++i) {
const DescriptorSetLayoutBinding& binding = layout.bindings[i];
VkDescriptorSetLayoutBinding vkBinding = {};
vkBinding.binding = binding.binding;
vkBinding.descriptorType = ToVulkanDescriptorType(static_cast<DescriptorType>(binding.type));
vkBinding.descriptorCount = binding.count > 0 ? binding.count : 1u;
vkBinding.stageFlags = ToVulkanShaderStageFlags(binding.visibility);
bindings.push_back(vkBinding);
}
return bindings;
}
} // namespace
VulkanDescriptorPool::~VulkanDescriptorPool() {
Shutdown();
}
bool VulkanDescriptorPool::Initialize(VkDevice device, const DescriptorPoolDesc& desc) {
if (device == VK_NULL_HANDLE || desc.descriptorCount == 0) {
return false;
}
m_device = device;
m_type = desc.type;
m_descriptorCount = desc.descriptorCount;
std::vector<VkDescriptorPoolSize> poolSizes;
switch (desc.type) {
case DescriptorHeapType::CBV_SRV_UAV:
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, desc.descriptorCount });
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, desc.descriptorCount });
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, desc.descriptorCount });
break;
case DescriptorHeapType::Sampler:
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_SAMPLER, desc.descriptorCount });
break;
default:
return false;
}
VkDescriptorPoolCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
createInfo.maxSets = desc.descriptorCount;
createInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
createInfo.pPoolSizes = poolSizes.data();
return vkCreateDescriptorPool(device, &createInfo, nullptr, &m_descriptorPool) == VK_SUCCESS;
}
bool VulkanDescriptorPool::Initialize(const DescriptorPoolDesc& desc) {
return Initialize(static_cast<VkDevice>(desc.device), desc);
}
void VulkanDescriptorPool::Shutdown() {
m_allocatedSets.clear();
if (m_descriptorPool != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);
}
m_descriptorPool = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_type = DescriptorHeapType::CBV_SRV_UAV;
m_descriptorCount = 0;
}
RHIDescriptorSet* VulkanDescriptorPool::AllocateSet(const DescriptorSetLayoutDesc& layout) {
if (m_device == VK_NULL_HANDLE || m_descriptorPool == VK_NULL_HANDLE) {
return nullptr;
}
const std::vector<VkDescriptorSetLayoutBinding> vkBindings = BuildVulkanLayoutBindings(layout);
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(vkBindings.size());
layoutInfo.pBindings = vkBindings.empty() ? nullptr : vkBindings.data();
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
if (vkCreateDescriptorSetLayout(m_device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
return nullptr;
}
VkDescriptorSetAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocateInfo.descriptorPool = m_descriptorPool;
allocateInfo.descriptorSetCount = 1;
allocateInfo.pSetLayouts = &descriptorSetLayout;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
if (vkAllocateDescriptorSets(m_device, &allocateInfo, &descriptorSet) != VK_SUCCESS) {
vkDestroyDescriptorSetLayout(m_device, descriptorSetLayout, nullptr);
return nullptr;
}
auto* set = new VulkanDescriptorSet();
if (!set->Initialize(m_device, this, descriptorSetLayout, descriptorSet, layout)) {
set->Shutdown();
delete set;
return nullptr;
}
m_allocatedSets.push_back(set);
return set;
}
void VulkanDescriptorPool::FreeSet(RHIDescriptorSet* set) {
if (set == nullptr) {
return;
}
auto* vulkanSet = static_cast<VulkanDescriptorSet*>(set);
auto it = std::find(m_allocatedSets.begin(), m_allocatedSets.end(), vulkanSet);
if (it != m_allocatedSets.end()) {
m_allocatedSets.erase(it);
}
delete vulkanSet;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,155 @@
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
#include <cstring>
namespace XCEngine {
namespace RHI {
VulkanDescriptorSet::~VulkanDescriptorSet() {
Shutdown();
}
bool VulkanDescriptorSet::Initialize(
VkDevice device,
VulkanDescriptorPool* pool,
VkDescriptorSetLayout layout,
VkDescriptorSet descriptorSet,
const DescriptorSetLayoutDesc& desc) {
if (device == VK_NULL_HANDLE || pool == nullptr || layout == VK_NULL_HANDLE || descriptorSet == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_pool = pool;
m_descriptorSetLayout = layout;
m_descriptorSet = descriptorSet;
if (desc.bindingCount > 0 && desc.bindings != nullptr) {
m_bindings.assign(desc.bindings, desc.bindings + desc.bindingCount);
}
for (uint32_t i = 0; i < m_bindings.size(); ++i) {
m_bindingToIndex[m_bindings[i].binding] = i;
}
return true;
}
void VulkanDescriptorSet::Shutdown() {
if (m_descriptorSet != VK_NULL_HANDLE &&
m_pool != nullptr &&
m_pool->GetDevice() != VK_NULL_HANDLE &&
m_pool->GetDescriptorPool() != VK_NULL_HANDLE) {
vkFreeDescriptorSets(m_pool->GetDevice(), m_pool->GetDescriptorPool(), 1, &m_descriptorSet);
}
if (m_descriptorSetLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr);
}
m_device = VK_NULL_HANDLE;
m_pool = nullptr;
m_descriptorSetLayout = VK_NULL_HANDLE;
m_descriptorSet = VK_NULL_HANDLE;
m_bindings.clear();
m_bindingToIndex.clear();
m_constantBufferData.clear();
m_constantDirty = false;
}
void VulkanDescriptorSet::Bind() {
}
void VulkanDescriptorSet::Unbind() {
}
void VulkanDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
if (m_device == VK_NULL_HANDLE || m_descriptorSet == VK_NULL_HANDLE || view == nullptr) {
return;
}
const DescriptorSetLayoutBinding* binding = FindBinding(offset);
if (binding == nullptr ||
static_cast<DescriptorType>(binding->type) != DescriptorType::SRV) {
return;
}
auto* vulkanView = static_cast<VulkanResourceView*>(view);
if (vulkanView == nullptr || vulkanView->GetImageView() == VK_NULL_HANDLE) {
return;
}
VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = vulkanView->GetImageView();
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = m_descriptorSet;
write.dstBinding = offset;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
write.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
}
void VulkanDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
if (m_device == VK_NULL_HANDLE || m_descriptorSet == VK_NULL_HANDLE || sampler == nullptr) {
return;
}
const DescriptorSetLayoutBinding* binding = FindBinding(offset);
if (binding == nullptr ||
static_cast<DescriptorType>(binding->type) != DescriptorType::Sampler) {
return;
}
auto* vulkanSampler = static_cast<VulkanSampler*>(sampler);
if (vulkanSampler == nullptr || vulkanSampler->GetSampler() == VK_NULL_HANDLE) {
return;
}
VkDescriptorImageInfo imageInfo = {};
imageInfo.sampler = vulkanSampler->GetSampler();
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = m_descriptorSet;
write.dstBinding = offset;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
write.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
}
void VulkanDescriptorSet::WriteConstant(uint32_t binding, const void* data, size_t size, size_t offset) {
(void)binding;
if (data == nullptr || size == 0) {
return;
}
if (m_constantBufferData.size() < offset + size) {
m_constantBufferData.resize(offset + size);
}
std::memcpy(m_constantBufferData.data() + offset, data, size);
m_constantDirty = true;
}
const DescriptorSetLayoutBinding* VulkanDescriptorSet::FindBinding(uint32_t binding) const {
auto it = m_bindingToIndex.find(binding);
if (it == m_bindingToIndex.end()) {
return nullptr;
}
return &m_bindings[it->second];
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -3,14 +3,19 @@
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandQueue.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorPool.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanFence.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
namespace XCEngine {
@@ -29,6 +34,261 @@ std::wstring ResolveVendorName(uint32_t vendorId) {
}
}
VkImageType ToVulkanImageType(TextureType type) {
switch (type) {
case TextureType::Texture1D:
return VK_IMAGE_TYPE_1D;
case TextureType::Texture3D:
return VK_IMAGE_TYPE_3D;
case TextureType::Texture2D:
case TextureType::Texture2DArray:
case TextureType::TextureCube:
case TextureType::TextureCubeArray:
default:
return VK_IMAGE_TYPE_2D;
}
}
VkImageLayout ToImageLayout(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_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 ToAccessMask(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
return VK_ACCESS_COLOR_ATTACHMENT_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 ToStageMask(ResourceStates state) {
switch (state) {
case ResourceStates::RenderTarget:
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
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;
}
}
VkImageUsageFlags ResolveTextureUsageFlags(const TextureDesc& desc) {
const Format format = static_cast<Format>(desc.format);
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if ((GetImageAspectMask(format) & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
} else {
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
return usage;
}
bool BeginSingleUseCommands(VkDevice device, uint32_t queueFamilyIndex, VkCommandPool& commandPool, VkCommandBuffer& commandBuffer) {
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndex;
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
return false;
}
VkCommandBufferAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = commandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandBufferCount = 1;
if (vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer) != VK_SUCCESS) {
vkDestroyCommandPool(device, commandPool, nullptr);
commandPool = VK_NULL_HANDLE;
return false;
}
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
vkDestroyCommandPool(device, commandPool, nullptr);
commandPool = VK_NULL_HANDLE;
commandBuffer = VK_NULL_HANDLE;
return false;
}
return true;
}
bool EndSingleUseCommands(VkDevice device, VkQueue queue, VkCommandPool commandPool, VkCommandBuffer commandBuffer) {
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
vkDestroyCommandPool(device, commandPool, nullptr);
return false;
}
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
const bool success = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE) == VK_SUCCESS &&
vkQueueWaitIdle(queue) == VK_SUCCESS;
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
vkDestroyCommandPool(device, commandPool, nullptr);
return success;
}
void TransitionImage(
VkCommandBuffer commandBuffer,
VkImage image,
Format format,
ResourceStates before,
ResourceStates after,
uint32_t mipLevels,
uint32_t arraySize) {
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = ToImageLayout(before);
barrier.newLayout = ToImageLayout(after);
barrier.srcAccessMask = ToAccessMask(before);
barrier.dstAccessMask = ToAccessMask(after);
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = GetImageAspectMask(format);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mipLevels > 0 ? mipLevels : 1u;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = arraySize > 0 ? arraySize : 1u;
vkCmdPipelineBarrier(
commandBuffer,
ToStageMask(before),
ToStageMask(after),
0,
0, nullptr,
0, nullptr,
1, &barrier);
}
bool UploadTextureData(
VulkanDevice* device,
VulkanTexture* texture,
const TextureDesc& desc,
const void* initialData,
size_t initialDataSize,
uint32_t rowPitch) {
if (device == nullptr || texture == nullptr || initialData == nullptr || initialDataSize == 0) {
return false;
}
const Format format = static_cast<Format>(desc.format);
const uint32_t pixelSize = GetFormatSize(format);
if (pixelSize == 0) {
return false;
}
BufferDesc stagingDesc = {};
stagingDesc.size = initialDataSize;
stagingDesc.stride = 1;
stagingDesc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
VulkanBuffer stagingBuffer;
if (!stagingBuffer.Initialize(
device,
stagingDesc,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
return false;
}
stagingBuffer.SetData(initialData, initialDataSize);
VkCommandPool commandPool = VK_NULL_HANDLE;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
if (!BeginSingleUseCommands(device->GetDevice(), device->GetGraphicsQueueFamilyIndex(), commandPool, commandBuffer)) {
stagingBuffer.Shutdown();
return false;
}
TransitionImage(
commandBuffer,
texture->GetImage(),
format,
ResourceStates::Common,
ResourceStates::CopyDst,
desc.mipLevels,
desc.arraySize);
VkBufferImageCopy copyRegion = {};
copyRegion.bufferOffset = 0;
copyRegion.bufferRowLength = rowPitch > 0 ? rowPitch / pixelSize : 0;
copyRegion.bufferImageHeight = 0;
copyRegion.imageSubresource.aspectMask = GetImageAspectMask(format);
copyRegion.imageSubresource.mipLevel = 0;
copyRegion.imageSubresource.baseArrayLayer = 0;
copyRegion.imageSubresource.layerCount = desc.arraySize > 0 ? desc.arraySize : 1u;
copyRegion.imageExtent.width = desc.width;
copyRegion.imageExtent.height = desc.height;
copyRegion.imageExtent.depth = desc.depth > 0 ? desc.depth : 1u;
vkCmdCopyBufferToImage(
commandBuffer,
stagingBuffer.GetBuffer(),
texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion);
TransitionImage(
commandBuffer,
texture->GetImage(),
format,
ResourceStates::CopyDst,
ResourceStates::PixelShaderResource,
desc.mipLevels,
desc.arraySize);
const bool submitted = EndSingleUseCommands(device->GetDevice(), device->GetGraphicsQueue(), commandPool, commandBuffer);
stagingBuffer.Shutdown();
return submitted;
}
} // namespace
VulkanDevice::~VulkanDevice() {
@@ -233,16 +493,91 @@ RHIBuffer* VulkanDevice::CreateBuffer(const BufferDesc& desc) {
}
RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc) {
(void)desc;
return nullptr;
const Format format = static_cast<Format>(desc.format);
const VkFormat vkFormat = ToVulkanFormat(format);
if (m_device == VK_NULL_HANDLE ||
vkFormat == VK_FORMAT_UNDEFINED ||
desc.width == 0 ||
desc.height == 0) {
return nullptr;
}
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = ToVulkanImageType(static_cast<TextureType>(desc.textureType));
imageInfo.extent.width = desc.width;
imageInfo.extent.height = desc.height;
imageInfo.extent.depth = desc.depth > 0 ? desc.depth : 1u;
imageInfo.mipLevels = desc.mipLevels > 0 ? desc.mipLevels : 1u;
imageInfo.arrayLayers = desc.arraySize > 0 ? desc.arraySize : 1u;
imageInfo.format = vkFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = ResolveTextureUsageFlags(desc);
imageInfo.samples = ToVulkanSampleCount(desc.sampleCount > 0 ? desc.sampleCount : 1u);
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkImage image = VK_NULL_HANDLE;
if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
return nullptr;
}
VkMemoryRequirements memoryRequirements = {};
vkGetImageMemoryRequirements(m_device, image, &memoryRequirements);
VkMemoryAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (allocateInfo.memoryTypeIndex == UINT32_MAX) {
vkDestroyImage(m_device, image, nullptr);
return nullptr;
}
VkDeviceMemory memory = VK_NULL_HANDLE;
if (vkAllocateMemory(m_device, &allocateInfo, nullptr, &memory) != VK_SUCCESS) {
vkDestroyImage(m_device, image, nullptr);
return nullptr;
}
if (vkBindImageMemory(m_device, image, memory, 0) != VK_SUCCESS) {
vkFreeMemory(m_device, memory, nullptr);
vkDestroyImage(m_device, image, nullptr);
return nullptr;
}
auto* texture = new VulkanTexture();
if (!texture->InitializeOwnedImage(
m_device,
image,
memory,
desc.width,
desc.height,
imageInfo.mipLevels,
format,
static_cast<TextureType>(desc.textureType),
vkFormat)) {
delete texture;
return nullptr;
}
return texture;
}
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;
auto texture = std::unique_ptr<VulkanTexture>(static_cast<VulkanTexture*>(CreateTexture(desc)));
if (!texture) {
return nullptr;
}
if (initialData != nullptr && initialDataSize > 0) {
if (!UploadTextureData(this, texture.get(), desc, initialData, initialDataSize, rowPitch)) {
return nullptr;
}
texture->SetState(ResourceStates::PixelShaderResource);
}
return texture.release();
}
RHISwapChain* VulkanDevice::CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) {
@@ -289,7 +624,12 @@ RHIPipelineState* VulkanDevice::CreatePipelineState(const GraphicsPipelineDesc&
}
RHIPipelineLayout* VulkanDevice::CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) {
(void)desc;
auto* pipelineLayout = new VulkanPipelineLayout();
if (pipelineLayout->Initialize(m_device, desc)) {
return pipelineLayout;
}
delete pipelineLayout;
return nullptr;
}
@@ -298,7 +638,12 @@ RHIFence* VulkanDevice::CreateFence(const FenceDesc& desc) {
}
RHISampler* VulkanDevice::CreateSampler(const SamplerDesc& desc) {
(void)desc;
auto* sampler = new VulkanSampler();
if (sampler->Initialize(m_device, desc)) {
return sampler;
}
delete sampler;
return nullptr;
}
@@ -320,14 +665,17 @@ RHIFramebuffer* VulkanDevice::CreateFramebuffer(class RHIRenderPass* renderPass,
}
RHIDescriptorPool* VulkanDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
(void)desc;
auto* pool = new VulkanDescriptorPool();
if (pool->Initialize(m_device, desc)) {
return pool;
}
delete pool;
return nullptr;
}
RHIDescriptorSet* VulkanDevice::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) {
(void)pool;
(void)layout;
return nullptr;
return pool != nullptr ? pool->AllocateSet(layout) : nullptr;
}
RHIResourceView* VulkanDevice::CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) {
@@ -366,8 +714,12 @@ RHIResourceView* VulkanDevice::CreateDepthStencilView(RHITexture* texture, const
}
RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture;
(void)desc;
auto* view = new VulkanResourceView();
if (view->InitializeAsShaderResource(m_device, static_cast<VulkanTexture*>(texture), desc)) {
return view;
}
delete view;
return nullptr;
}

View File

@@ -0,0 +1,190 @@
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
namespace XCEngine {
namespace RHI {
namespace {
void AppendSyntheticSetLayout(
std::vector<DescriptorSetLayoutDesc>& setLayouts,
std::vector<std::vector<DescriptorSetLayoutBinding>>& setLayoutBindings,
DescriptorType type,
uint32_t count) {
if (count == 0) {
return;
}
setLayoutBindings.emplace_back();
setLayoutBindings.back().push_back({
0,
static_cast<uint32_t>(type),
count,
static_cast<uint32_t>(ShaderVisibility::All)
});
DescriptorSetLayoutDesc setLayout = {};
setLayout.bindingCount = 1;
setLayout.bindings = setLayoutBindings.back().data();
setLayouts.push_back(setLayout);
}
} // namespace
VulkanPipelineLayout::~VulkanPipelineLayout() {
Shutdown();
}
bool VulkanPipelineLayout::Initialize(VkDevice device, const RHIPipelineLayoutDesc& desc) {
if (device == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_desc = desc;
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_nativeSetLayouts.clear();
if (desc.setLayoutCount > 0 && desc.setLayouts != nullptr) {
PrepareExplicitLayouts(desc);
} else {
PrepareFlatLayouts(desc);
}
if (!CreateNativeSetLayouts()) {
Shutdown();
return false;
}
VkPipelineLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.setLayoutCount = static_cast<uint32_t>(m_nativeSetLayouts.size());
createInfo.pSetLayouts = m_nativeSetLayouts.empty() ? nullptr : m_nativeSetLayouts.data();
if (vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
Shutdown();
return false;
}
return true;
}
bool VulkanPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
(void)desc;
return false;
}
void VulkanPipelineLayout::PrepareExplicitLayouts(const RHIPipelineLayoutDesc& desc) {
m_desc.constantBufferCount = 0;
m_desc.textureCount = 0;
m_desc.samplerCount = 0;
m_desc.uavCount = 0;
m_setLayouts.resize(desc.setLayoutCount);
m_setLayoutBindings.resize(desc.setLayoutCount);
for (uint32_t setIndex = 0; setIndex < desc.setLayoutCount; ++setIndex) {
const DescriptorSetLayoutDesc& srcSetLayout = desc.setLayouts[setIndex];
auto& dstBindings = m_setLayoutBindings[setIndex];
if (srcSetLayout.bindingCount > 0 && srcSetLayout.bindings != nullptr) {
dstBindings.assign(srcSetLayout.bindings, srcSetLayout.bindings + srcSetLayout.bindingCount);
}
DescriptorSetLayoutDesc dstSetLayout = {};
dstSetLayout.bindingCount = srcSetLayout.bindingCount;
dstSetLayout.bindings = dstBindings.empty() ? nullptr : dstBindings.data();
m_setLayouts[setIndex] = dstSetLayout;
for (const DescriptorSetLayoutBinding& binding : dstBindings) {
switch (static_cast<DescriptorType>(binding.type)) {
case DescriptorType::CBV:
m_desc.constantBufferCount += binding.count;
break;
case DescriptorType::SRV:
m_desc.textureCount += binding.count;
break;
case DescriptorType::UAV:
m_desc.uavCount += binding.count;
break;
case DescriptorType::Sampler:
m_desc.samplerCount += binding.count;
break;
default:
break;
}
}
}
m_desc.setLayouts = m_setLayouts.empty() ? nullptr : m_setLayouts.data();
m_desc.setLayoutCount = static_cast<uint32_t>(m_setLayouts.size());
}
void VulkanPipelineLayout::PrepareFlatLayouts(const RHIPipelineLayoutDesc& desc) {
AppendSyntheticSetLayout(m_setLayouts, m_setLayoutBindings, DescriptorType::CBV, desc.constantBufferCount);
AppendSyntheticSetLayout(m_setLayouts, m_setLayoutBindings, DescriptorType::SRV, desc.textureCount);
AppendSyntheticSetLayout(m_setLayouts, m_setLayoutBindings, DescriptorType::UAV, desc.uavCount);
AppendSyntheticSetLayout(m_setLayouts, m_setLayoutBindings, DescriptorType::Sampler, desc.samplerCount);
for (uint32_t i = 0; i < m_setLayouts.size(); ++i) {
m_setLayouts[i].bindingCount = static_cast<uint32_t>(m_setLayoutBindings[i].size());
m_setLayouts[i].bindings = m_setLayoutBindings[i].empty() ? nullptr : m_setLayoutBindings[i].data();
}
m_desc.setLayouts = m_setLayouts.empty() ? nullptr : m_setLayouts.data();
m_desc.setLayoutCount = static_cast<uint32_t>(m_setLayouts.size());
}
bool VulkanPipelineLayout::CreateNativeSetLayouts() {
m_nativeSetLayouts.reserve(m_setLayouts.size());
for (const DescriptorSetLayoutDesc& setLayout : m_setLayouts) {
std::vector<VkDescriptorSetLayoutBinding> vkBindings;
vkBindings.reserve(setLayout.bindingCount);
for (uint32_t i = 0; i < setLayout.bindingCount; ++i) {
const DescriptorSetLayoutBinding& binding = setLayout.bindings[i];
VkDescriptorSetLayoutBinding vkBinding = {};
vkBinding.binding = binding.binding;
vkBinding.descriptorType = ToVulkanDescriptorType(static_cast<DescriptorType>(binding.type));
vkBinding.descriptorCount = binding.count > 0 ? binding.count : 1u;
vkBinding.stageFlags = ToVulkanShaderStageFlags(binding.visibility);
vkBindings.push_back(vkBinding);
}
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(vkBindings.size());
layoutInfo.pBindings = vkBindings.empty() ? nullptr : vkBindings.data();
VkDescriptorSetLayout nativeLayout = VK_NULL_HANDLE;
if (vkCreateDescriptorSetLayout(m_device, &layoutInfo, nullptr, &nativeLayout) != VK_SUCCESS) {
return false;
}
m_nativeSetLayouts.push_back(nativeLayout);
}
return true;
}
void VulkanPipelineLayout::Shutdown() {
if (m_pipelineLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
}
m_pipelineLayout = VK_NULL_HANDLE;
for (VkDescriptorSetLayout nativeSetLayout : m_nativeSetLayouts) {
if (nativeSetLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(m_device, nativeSetLayout, nullptr);
}
}
m_nativeSetLayouts.clear();
m_desc = {};
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_device = VK_NULL_HANDLE;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -1,6 +1,7 @@
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include <algorithm>
#include <cstring>
@@ -129,12 +130,19 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
return false;
}
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
vkDestroyShaderModule(m_device, fragmentModule, nullptr);
vkDestroyShaderModule(m_device, vertexModule, nullptr);
return false;
auto* externalPipelineLayout = static_cast<VulkanPipelineLayout*>(desc.pipelineLayout);
if (externalPipelineLayout != nullptr) {
m_pipelineLayout = externalPipelineLayout->GetPipelineLayout();
m_ownsPipelineLayout = false;
} else {
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
vkDestroyShaderModule(m_device, fragmentModule, nullptr);
vkDestroyShaderModule(m_device, vertexModule, nullptr);
return false;
}
m_ownsPipelineLayout = true;
}
VkAttachmentDescription colorAttachment = {};
@@ -164,8 +172,11 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
renderPassInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) {
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
if (m_ownsPipelineLayout) {
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
}
m_pipelineLayout = VK_NULL_HANDLE;
m_ownsPipelineLayout = false;
vkDestroyShaderModule(m_device, fragmentModule, nullptr);
vkDestroyShaderModule(m_device, vertexModule, nullptr);
return false;
@@ -372,12 +383,15 @@ void VulkanPipelineState::Shutdown() {
}
if (m_pipelineLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
if (m_ownsPipelineLayout) {
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
}
m_pipelineLayout = VK_NULL_HANDLE;
}
m_deviceOwner = nullptr;
m_device = VK_NULL_HANDLE;
m_ownsPipelineLayout = false;
}
} // namespace RHI

View File

@@ -24,8 +24,33 @@ bool VulkanResourceView::InitializeAsRenderTarget(VkDevice device, VulkanTexture
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.viewType = ToVulkanImageViewType(m_dimension, texture->GetTextureType());
viewInfo.format = m_format != Format::Unknown ? ToVulkanFormat(m_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;
}
bool VulkanResourceView::InitializeAsShaderResource(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::ShaderResource;
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 = ToVulkanImageViewType(m_dimension, texture->GetTextureType());
viewInfo.format = m_format != Format::Unknown ? ToVulkanFormat(m_format) : texture->GetVkFormat();
viewInfo.subresourceRange.aspectMask = GetImageAspectMask(texture->GetFormat());
viewInfo.subresourceRange.baseMipLevel = desc.mipLevel;
viewInfo.subresourceRange.levelCount = 1;
@@ -80,6 +105,7 @@ bool VulkanResourceView::IsValid() const {
return m_buffer != nullptr && m_buffer->GetBuffer() != VK_NULL_HANDLE;
case ResourceViewType::RenderTarget:
case ResourceViewType::DepthStencil:
case ResourceViewType::ShaderResource:
return m_imageView != VK_NULL_HANDLE;
default:
return false;

View File

@@ -0,0 +1,56 @@
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
namespace XCEngine {
namespace RHI {
VulkanSampler::~VulkanSampler() {
Shutdown();
}
bool VulkanSampler::Initialize(VkDevice device, const SamplerDesc& desc) {
if (device == VK_NULL_HANDLE) {
return false;
}
m_device = device;
VkSamplerCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
createInfo.magFilter = ToVulkanMagFilter(static_cast<FilterMode>(desc.filter));
createInfo.minFilter = ToVulkanMinFilter(static_cast<FilterMode>(desc.filter));
createInfo.mipmapMode = ToVulkanSamplerMipmapMode(static_cast<FilterMode>(desc.filter));
createInfo.addressModeU = ToVulkanSamplerAddressMode(static_cast<TextureAddressMode>(desc.addressU));
createInfo.addressModeV = ToVulkanSamplerAddressMode(static_cast<TextureAddressMode>(desc.addressV));
createInfo.addressModeW = ToVulkanSamplerAddressMode(static_cast<TextureAddressMode>(desc.addressW));
createInfo.mipLodBias = desc.mipLodBias;
createInfo.anisotropyEnable = UsesVulkanSamplerAnisotropy(static_cast<FilterMode>(desc.filter)) ? VK_TRUE : VK_FALSE;
createInfo.maxAnisotropy = desc.maxAnisotropy > 0 ? static_cast<float>(desc.maxAnisotropy) : 1.0f;
createInfo.compareEnable = UsesVulkanComparisonSampling(static_cast<FilterMode>(desc.filter)) ? VK_TRUE : VK_FALSE;
createInfo.compareOp = ToVulkanCompareOp(static_cast<ComparisonFunc>(desc.comparisonFunc));
createInfo.minLod = desc.minLod;
createInfo.maxLod = desc.maxLod;
createInfo.borderColor = ToVulkanBorderColor(desc.borderColorR, desc.borderColorG, desc.borderColorB, desc.borderColorA);
createInfo.unnormalizedCoordinates = VK_FALSE;
return vkCreateSampler(device, &createInfo, nullptr, &m_sampler) == VK_SUCCESS;
}
void VulkanSampler::Shutdown() {
if (m_sampler != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroySampler(m_device, m_sampler, nullptr);
}
m_sampler = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
}
void VulkanSampler::Bind(unsigned int unit) {
(void)unit;
}
void VulkanSampler::Unbind(unsigned int unit) {
(void)unit;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -62,11 +62,19 @@ bool VulkanSwapChain::CreateSwapChainResources() {
VkSurfaceFormatKHR selectedFormat = formats[0];
for (const VkSurfaceFormatKHR& candidate : formats) {
if (candidate.format == VK_FORMAT_B8G8R8A8_UNORM) {
if (candidate.format == VK_FORMAT_R8G8B8A8_UNORM) {
selectedFormat = candidate;
break;
}
}
if (selectedFormat.format != VK_FORMAT_R8G8B8A8_UNORM) {
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 ||

View File

@@ -18,21 +18,62 @@ bool VulkanTexture::InitializeSwapChainImage(
m_image = image;
m_width = width;
m_height = height;
m_mipLevels = 1;
m_format = format;
m_vkFormat = vkFormat;
m_textureType = TextureType::Texture2D;
m_state = ResourceStates::Common;
m_ownsImage = false;
return m_image != VK_NULL_HANDLE;
}
bool VulkanTexture::InitializeOwnedImage(
VkDevice device,
VkImage image,
VkDeviceMemory memory,
uint32_t width,
uint32_t height,
uint32_t mipLevels,
Format format,
TextureType textureType,
VkFormat vkFormat) {
if (device == VK_NULL_HANDLE || image == VK_NULL_HANDLE || memory == VK_NULL_HANDLE) {
return false;
}
m_device = device;
m_image = image;
m_memory = memory;
m_width = width;
m_height = height;
m_mipLevels = mipLevels;
m_format = format;
m_vkFormat = vkFormat;
m_textureType = textureType;
m_state = ResourceStates::Common;
m_ownsImage = true;
return true;
}
void VulkanTexture::Shutdown() {
if (m_ownsImage && m_image != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkDestroyImage(m_device, m_image, nullptr);
}
if (m_memory != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
vkFreeMemory(m_device, m_memory, nullptr);
}
m_image = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_width = 0;
m_height = 0;
m_mipLevels = 1;
m_format = Format::Unknown;
m_vkFormat = VK_FORMAT_UNDEFINED;
m_state = ResourceStates::Common;
m_ownsImage = false;
}
} // namespace RHI