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/VulkanCommon.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanBuffer.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/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/VulkanPipelineState.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanResourceView.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/VulkanFence.h
@@ -165,6 +169,10 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanScreenshot.h ${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/VulkanBuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanTexture.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/VulkanPipelineState.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanResourceView.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/VulkanCommandQueue.cpp

View File

@@ -8,6 +8,7 @@
#include "XCEngine/RHI/RHIEnums.h" #include "XCEngine/RHI/RHIEnums.h"
#include <cmath>
#include <string> #include <string>
namespace XCEngine { 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) { inline uint32_t ResolveVulkanApiMajor(uint32_t apiVersion) {
return VK_API_VERSION_MAJOR(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; VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
VkRenderPass m_renderPass = VK_NULL_HANDLE; VkRenderPass m_renderPass = VK_NULL_HANDLE;
bool m_ownsPipelineLayout = false;
InputLayoutDesc m_inputLayoutDesc = {}; InputLayoutDesc m_inputLayoutDesc = {};
RasterizerDesc m_rasterizerDesc = {}; RasterizerDesc m_rasterizerDesc = {};
BlendDesc m_blendDesc = {}; BlendDesc m_blendDesc = {};

View File

@@ -15,6 +15,7 @@ public:
~VulkanResourceView() override; ~VulkanResourceView() override;
bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc); 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 InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc);
bool InitializeAsIndexBuffer(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, uint32_t height,
Format format, Format format,
VkFormat vkFormat); 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; void Shutdown() override;
uint32_t GetWidth() const override { return m_width; } uint32_t GetWidth() const override { return m_width; }
uint32_t GetHeight() const override { return m_height; } uint32_t GetHeight() const override { return m_height; }
uint32_t GetDepth() const override { return 1; } 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; } Format GetFormat() const override { return m_format; }
TextureType GetTextureType() const override { return m_textureType; } TextureType GetTextureType() const override { return m_textureType; }
@@ -39,17 +49,21 @@ public:
void SetName(const std::string& name) override { m_name = name; } void SetName(const std::string& name) override { m_name = name; }
VkImage GetImage() const { return m_image; } VkImage GetImage() const { return m_image; }
VkDeviceMemory GetMemory() const { return m_memory; }
VkFormat GetVkFormat() const { return m_vkFormat; } VkFormat GetVkFormat() const { return m_vkFormat; }
private: private:
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VkImage m_image = VK_NULL_HANDLE; VkImage m_image = VK_NULL_HANDLE;
VkDeviceMemory m_memory = VK_NULL_HANDLE;
uint32_t m_width = 0; uint32_t m_width = 0;
uint32_t m_height = 0; uint32_t m_height = 0;
uint32_t m_mipLevels = 1;
Format m_format = Format::Unknown; Format m_format = Format::Unknown;
TextureType m_textureType = TextureType::Texture2D; TextureType m_textureType = TextureType::Texture2D;
ResourceStates m_state = ResourceStates::Common; ResourceStates m_state = ResourceStates::Common;
VkFormat m_vkFormat = VK_FORMAT_UNDEFINED; VkFormat m_vkFormat = VK_FORMAT_UNDEFINED;
bool m_ownsImage = false;
std::string m_name; std::string m_name;
}; };

View File

@@ -1,7 +1,9 @@
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h" #include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanBuffer.h" #include "XCEngine/RHI/Vulkan/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanDescriptorSet.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h" #include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h" #include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h" #include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.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 VulkanCommandList::SetGraphicsDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet; if (count == 0 || descriptorSets == nullptr || m_commandBuffer == VK_NULL_HANDLE) {
(void)count; return;
(void)descriptorSets; }
(void)pipelineLayout;
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 VulkanCommandList::SetComputeDescriptorSets(uint32_t firstSet, uint32_t count, RHIDescriptorSet** descriptorSets, RHIPipelineLayout* pipelineLayout) {
(void)firstSet; if (count == 0 || descriptorSets == nullptr || m_commandBuffer == VK_NULL_HANDLE || pipelineLayout == nullptr) {
(void)count; return;
(void)descriptorSets; }
(void)pipelineLayout;
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) { 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/VulkanBuffer.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandList.h" #include "XCEngine/RHI/Vulkan/VulkanCommandList.h"
#include "XCEngine/RHI/Vulkan/VulkanCommandQueue.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/VulkanFence.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h" #include "XCEngine/RHI/Vulkan/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h" #include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h" #include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#include "XCEngine/RHI/Vulkan/VulkanTexture.h" #include "XCEngine/RHI/Vulkan/VulkanTexture.h"
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <memory>
#include <vector> #include <vector>
namespace XCEngine { 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 } // namespace
VulkanDevice::~VulkanDevice() { VulkanDevice::~VulkanDevice() {
@@ -233,16 +493,91 @@ RHIBuffer* VulkanDevice::CreateBuffer(const BufferDesc& desc) {
} }
RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc) { RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc) {
(void)desc; const Format format = static_cast<Format>(desc.format);
return nullptr; 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) { RHITexture* VulkanDevice::CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch) {
(void)desc; auto texture = std::unique_ptr<VulkanTexture>(static_cast<VulkanTexture*>(CreateTexture(desc)));
(void)initialData; if (!texture) {
(void)initialDataSize; return nullptr;
(void)rowPitch; }
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) { RHISwapChain* VulkanDevice::CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) {
@@ -289,7 +624,12 @@ RHIPipelineState* VulkanDevice::CreatePipelineState(const GraphicsPipelineDesc&
} }
RHIPipelineLayout* VulkanDevice::CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) { RHIPipelineLayout* VulkanDevice::CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) {
(void)desc; auto* pipelineLayout = new VulkanPipelineLayout();
if (pipelineLayout->Initialize(m_device, desc)) {
return pipelineLayout;
}
delete pipelineLayout;
return nullptr; return nullptr;
} }
@@ -298,7 +638,12 @@ RHIFence* VulkanDevice::CreateFence(const FenceDesc& desc) {
} }
RHISampler* VulkanDevice::CreateSampler(const SamplerDesc& 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; return nullptr;
} }
@@ -320,14 +665,17 @@ RHIFramebuffer* VulkanDevice::CreateFramebuffer(class RHIRenderPass* renderPass,
} }
RHIDescriptorPool* VulkanDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) { RHIDescriptorPool* VulkanDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
(void)desc; auto* pool = new VulkanDescriptorPool();
if (pool->Initialize(m_device, desc)) {
return pool;
}
delete pool;
return nullptr; return nullptr;
} }
RHIDescriptorSet* VulkanDevice::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) { RHIDescriptorSet* VulkanDevice::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) {
(void)pool; return pool != nullptr ? pool->AllocateSet(layout) : nullptr;
(void)layout;
return nullptr;
} }
RHIResourceView* VulkanDevice::CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) { 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) { RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) {
(void)texture; auto* view = new VulkanResourceView();
(void)desc; if (view->InitializeAsShaderResource(m_device, static_cast<VulkanTexture*>(texture), desc)) {
return view;
}
delete view;
return nullptr; 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/VulkanPipelineState.h"
#include "XCEngine/RHI/Vulkan/VulkanDevice.h" #include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
@@ -129,12 +130,19 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
return false; return false;
} }
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; auto* externalPipelineLayout = static_cast<VulkanPipelineLayout*>(desc.pipelineLayout);
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; if (externalPipelineLayout != nullptr) {
if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) { m_pipelineLayout = externalPipelineLayout->GetPipelineLayout();
vkDestroyShaderModule(m_device, fragmentModule, nullptr); m_ownsPipelineLayout = false;
vkDestroyShaderModule(m_device, vertexModule, nullptr); } else {
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;
}
m_ownsPipelineLayout = true;
} }
VkAttachmentDescription colorAttachment = {}; VkAttachmentDescription colorAttachment = {};
@@ -164,8 +172,11 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
renderPassInfo.pSubpasses = &subpass; renderPassInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) { 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_pipelineLayout = VK_NULL_HANDLE;
m_ownsPipelineLayout = false;
vkDestroyShaderModule(m_device, fragmentModule, nullptr); vkDestroyShaderModule(m_device, fragmentModule, nullptr);
vkDestroyShaderModule(m_device, vertexModule, nullptr); vkDestroyShaderModule(m_device, vertexModule, nullptr);
return false; return false;
@@ -372,12 +383,15 @@ void VulkanPipelineState::Shutdown() {
} }
if (m_pipelineLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) { 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_pipelineLayout = VK_NULL_HANDLE;
} }
m_deviceOwner = nullptr; m_deviceOwner = nullptr;
m_device = VK_NULL_HANDLE; m_device = VK_NULL_HANDLE;
m_ownsPipelineLayout = false;
} }
} // namespace RHI } // namespace RHI

View File

@@ -24,8 +24,33 @@ bool VulkanResourceView::InitializeAsRenderTarget(VkDevice device, VulkanTexture
VkImageViewCreateInfo viewInfo = {}; VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture->GetImage(); viewInfo.image = texture->GetImage();
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.viewType = ToVulkanImageViewType(m_dimension, texture->GetTextureType());
viewInfo.format = texture->GetVkFormat(); 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.aspectMask = GetImageAspectMask(texture->GetFormat());
viewInfo.subresourceRange.baseMipLevel = desc.mipLevel; viewInfo.subresourceRange.baseMipLevel = desc.mipLevel;
viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.levelCount = 1;
@@ -80,6 +105,7 @@ bool VulkanResourceView::IsValid() const {
return m_buffer != nullptr && m_buffer->GetBuffer() != VK_NULL_HANDLE; return m_buffer != nullptr && m_buffer->GetBuffer() != VK_NULL_HANDLE;
case ResourceViewType::RenderTarget: case ResourceViewType::RenderTarget:
case ResourceViewType::DepthStencil: case ResourceViewType::DepthStencil:
case ResourceViewType::ShaderResource:
return m_imageView != VK_NULL_HANDLE; return m_imageView != VK_NULL_HANDLE;
default: default:
return false; 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]; VkSurfaceFormatKHR selectedFormat = formats[0];
for (const VkSurfaceFormatKHR& candidate : formats) { for (const VkSurfaceFormatKHR& candidate : formats) {
if (candidate.format == VK_FORMAT_B8G8R8A8_UNORM) { if (candidate.format == VK_FORMAT_R8G8B8A8_UNORM) {
selectedFormat = candidate; selectedFormat = candidate;
break; 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; m_surfaceFormat = selectedFormat.format;
if ((capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0 || if ((capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0 ||

View File

@@ -18,21 +18,62 @@ bool VulkanTexture::InitializeSwapChainImage(
m_image = image; m_image = image;
m_width = width; m_width = width;
m_height = height; m_height = height;
m_mipLevels = 1;
m_format = format; m_format = format;
m_vkFormat = vkFormat; m_vkFormat = vkFormat;
m_textureType = TextureType::Texture2D; m_textureType = TextureType::Texture2D;
m_state = ResourceStates::Common; m_state = ResourceStates::Common;
m_ownsImage = false;
return m_image != VK_NULL_HANDLE; 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() { 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_image = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE; m_device = VK_NULL_HANDLE;
m_width = 0; m_width = 0;
m_height = 0; m_height = 0;
m_mipLevels = 1;
m_format = Format::Unknown; m_format = Format::Unknown;
m_vkFormat = VK_FORMAT_UNDEFINED; m_vkFormat = VK_FORMAT_UNDEFINED;
m_state = ResourceStates::Common; m_state = ResourceStates::Common;
m_ownsImage = false;
} }
} // namespace RHI } // namespace RHI

View File

@@ -9,6 +9,8 @@ set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE) get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
find_package(Vulkan QUIET)
add_executable(rhi_integration_quad add_executable(rhi_integration_quad
main.cpp main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../fixtures/RHIIntegrationFixture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../fixtures/RHIIntegrationFixture.cpp
@@ -33,6 +35,60 @@ target_link_libraries(rhi_integration_quad PRIVATE
GTest::gtest GTest::gtest
) )
if(Vulkan_FOUND)
set(XCENGINE_GLSLANG_VALIDATOR_HINT "$ENV{VULKAN_SDK}")
find_program(
XCENGINE_GLSLANG_VALIDATOR
NAMES glslangValidator glslangValidator.exe
HINTS
"${XCENGINE_GLSLANG_VALIDATOR_HINT}/Bin"
"${Vulkan_ROOT}/Bin")
if(NOT XCENGINE_GLSLANG_VALIDATOR)
file(GLOB XCENGINE_VULKAN_BIN_DIRS "D:/VulkanSDK/*/Bin")
if(XCENGINE_VULKAN_BIN_DIRS)
list(SORT XCENGINE_VULKAN_BIN_DIRS COMPARE NATURAL ORDER DESCENDING)
foreach(XCENGINE_VULKAN_BIN_DIR IN LISTS XCENGINE_VULKAN_BIN_DIRS)
find_program(
XCENGINE_GLSLANG_VALIDATOR
NAMES glslangValidator glslangValidator.exe
PATHS "${XCENGINE_VULKAN_BIN_DIR}"
NO_DEFAULT_PATH)
if(XCENGINE_GLSLANG_VALIDATOR)
break()
endif()
endforeach()
endif()
endif()
if(NOT XCENGINE_GLSLANG_VALIDATOR)
message(FATAL_ERROR "glslangValidator not found for Vulkan quad shaders")
endif()
set(QUAD_VULKAN_VERTEX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/quad_vulkan.vert)
set(QUAD_VULKAN_FRAGMENT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/quad_vulkan.frag)
add_custom_command(TARGET rhi_integration_quad PRE_BUILD
COMMAND ${XCENGINE_GLSLANG_VALIDATOR}
-V
-S
vert
-o
$<TARGET_FILE_DIR:rhi_integration_quad>/quad_vulkan.vert.spv
${QUAD_VULKAN_VERTEX_SOURCE}
COMMAND ${XCENGINE_GLSLANG_VALIDATOR}
-V
-S
frag
-o
$<TARGET_FILE_DIR:rhi_integration_quad>/quad_vulkan.frag.spv
${QUAD_VULKAN_FRAGMENT_SOURCE}
VERBATIM)
target_link_libraries(rhi_integration_quad PRIVATE Vulkan::Vulkan)
target_compile_definitions(rhi_integration_quad PRIVATE XCENGINE_SUPPORT_VULKAN)
endif()
target_compile_definitions(rhi_integration_quad PRIVATE target_compile_definitions(rhi_integration_quad PRIVATE
UNICODE UNICODE
_UNICODE _UNICODE

View File

@@ -0,0 +1,11 @@
#version 450
layout(set = 0, binding = 0) uniform texture2D uTexture;
layout(set = 1, binding = 0) uniform sampler uSampler;
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = texture(sampler2D(uTexture, uSampler), vTexCoord);
}

View File

@@ -0,0 +1,11 @@
#version 450
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec2 aTexCoord;
layout(location = 0) out vec2 vTexCoord;
void main() {
gl_Position = aPosition;
vTexCoord = aTexCoord;
}

View File

@@ -1,5 +1,6 @@
#include <windows.h> #include <windows.h>
#include <filesystem> #include <filesystem>
#include <fstream>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -53,6 +54,27 @@ std::filesystem::path ResolveRuntimePath(const char* relativePath) {
return GetExecutableDirectory() / relativePath; return GetExecutableDirectory() / relativePath;
} }
std::vector<uint8_t> LoadBinaryFileRelative(const char* filename) {
const std::filesystem::path path = ResolveRuntimePath(filename);
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return {};
}
const std::streamsize size = file.tellg();
if (size <= 0) {
return {};
}
std::vector<uint8_t> bytes(static_cast<size_t>(size));
file.seekg(0, std::ios::beg);
if (!file.read(reinterpret_cast<char*>(bytes.data()), size)) {
return {};
}
return bytes;
}
const char kQuadHlsl[] = R"( const char kQuadHlsl[] = R"(
Texture2D gTexture : register(t0); Texture2D gTexture : register(t0);
SamplerState gSampler : register(s0); SamplerState gSampler : register(s0);
@@ -104,7 +126,16 @@ void main() {
)"; )";
const char* GetScreenshotFilename(RHIType type) { const char* GetScreenshotFilename(RHIType type) {
return type == RHIType::D3D12 ? "quad_d3d12.ppm" : "quad_opengl.ppm"; switch (type) {
case RHIType::D3D12:
return "quad_d3d12.ppm";
case RHIType::OpenGL:
return "quad_opengl.ppm";
case RHIType::Vulkan:
return "quad_vulkan.ppm";
default:
return "quad_unknown.ppm";
}
} }
RHITexture* LoadQuadTexture(RHIDevice* device) { RHITexture* LoadQuadTexture(RHIDevice* device) {
@@ -190,7 +221,7 @@ GraphicsPipelineDesc CreateQuadPipelineDesc(RHIType type, RHIPipelineLayout* pip
desc.fragmentShader.sourceLanguage = ShaderLanguage::HLSL; desc.fragmentShader.sourceLanguage = ShaderLanguage::HLSL;
desc.fragmentShader.entryPoint = L"MainPS"; desc.fragmentShader.entryPoint = L"MainPS";
desc.fragmentShader.profile = L"ps_5_0"; desc.fragmentShader.profile = L"ps_5_0";
} else { } else if (type == RHIType::OpenGL) {
desc.vertexShader.source.assign(kQuadVertexShader, kQuadVertexShader + strlen(kQuadVertexShader)); desc.vertexShader.source.assign(kQuadVertexShader, kQuadVertexShader + strlen(kQuadVertexShader));
desc.vertexShader.sourceLanguage = ShaderLanguage::GLSL; desc.vertexShader.sourceLanguage = ShaderLanguage::GLSL;
desc.vertexShader.profile = L"vs_4_30"; desc.vertexShader.profile = L"vs_4_30";
@@ -198,6 +229,14 @@ GraphicsPipelineDesc CreateQuadPipelineDesc(RHIType type, RHIPipelineLayout* pip
desc.fragmentShader.source.assign(kQuadFragmentShader, kQuadFragmentShader + strlen(kQuadFragmentShader)); desc.fragmentShader.source.assign(kQuadFragmentShader, kQuadFragmentShader + strlen(kQuadFragmentShader));
desc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL; desc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL;
desc.fragmentShader.profile = L"fs_4_30"; desc.fragmentShader.profile = L"fs_4_30";
} else if (type == RHIType::Vulkan) {
desc.vertexShader.source = LoadBinaryFileRelative("quad_vulkan.vert.spv");
desc.vertexShader.sourceLanguage = ShaderLanguage::SPIRV;
desc.vertexShader.entryPoint = L"main";
desc.fragmentShader.source = LoadBinaryFileRelative("quad_vulkan.frag.spv");
desc.fragmentShader.sourceLanguage = ShaderLanguage::SPIRV;
desc.fragmentShader.entryPoint = L"main";
} }
return desc; return desc;
@@ -342,9 +381,34 @@ void QuadTest::InitializeQuadResources() {
ASSERT_NE(mSamplerSet, nullptr); ASSERT_NE(mSamplerSet, nullptr);
mSamplerSet->UpdateSampler(0, mSampler); mSamplerSet->UpdateSampler(0, mSampler);
DescriptorSetLayoutBinding pipelineTextureBinding = {};
pipelineTextureBinding.binding = 0;
pipelineTextureBinding.type = static_cast<uint32_t>(DescriptorType::SRV);
pipelineTextureBinding.count = 1;
pipelineTextureBinding.visibility = static_cast<uint32_t>(ShaderVisibility::Pixel);
DescriptorSetLayoutDesc textureSetLayout = {};
textureSetLayout.bindings = &pipelineTextureBinding;
textureSetLayout.bindingCount = 1;
DescriptorSetLayoutBinding pipelineSamplerBinding = {};
pipelineSamplerBinding.binding = 0;
pipelineSamplerBinding.type = static_cast<uint32_t>(DescriptorType::Sampler);
pipelineSamplerBinding.count = 1;
pipelineSamplerBinding.visibility = static_cast<uint32_t>(ShaderVisibility::Pixel);
DescriptorSetLayoutDesc samplerSetLayout = {};
samplerSetLayout.bindings = &pipelineSamplerBinding;
samplerSetLayout.bindingCount = 1;
DescriptorSetLayoutDesc setLayouts[] = {
textureSetLayout,
samplerSetLayout,
};
RHIPipelineLayoutDesc pipelineLayoutDesc = {}; RHIPipelineLayoutDesc pipelineLayoutDesc = {};
pipelineLayoutDesc.textureCount = 1; pipelineLayoutDesc.setLayouts = setLayouts;
pipelineLayoutDesc.samplerCount = 1; pipelineLayoutDesc.setLayoutCount = 2;
mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc); mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc);
ASSERT_NE(mPipelineLayout, nullptr); ASSERT_NE(mPipelineLayout, nullptr);
@@ -510,6 +574,9 @@ TEST_P(QuadTest, RenderQuad) {
INSTANTIATE_TEST_SUITE_P(D3D12, QuadTest, ::testing::Values(RHIType::D3D12)); INSTANTIATE_TEST_SUITE_P(D3D12, QuadTest, ::testing::Values(RHIType::D3D12));
INSTANTIATE_TEST_SUITE_P(OpenGL, QuadTest, ::testing::Values(RHIType::OpenGL)); INSTANTIATE_TEST_SUITE_P(OpenGL, QuadTest, ::testing::Values(RHIType::OpenGL));
#if defined(XCENGINE_SUPPORT_VULKAN)
INSTANTIATE_TEST_SUITE_P(Vulkan, QuadTest, ::testing::Values(RHIType::Vulkan));
#endif
GTEST_API_ int main(int argc, char** argv) { GTEST_API_ int main(int argc, char** argv) {
Logger::Get().Initialize(); Logger::Get().Initialize();