Add Vulkan shader, UAV, and compute coverage
This commit is contained in:
@@ -156,6 +156,7 @@ add_library(XCEngine STATIC
|
|||||||
${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/VulkanSampler.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanShader.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanDescriptorPool.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/VulkanDescriptorSet.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanPipelineLayout.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/RHI/Vulkan/VulkanPipelineLayout.h
|
||||||
@@ -172,6 +173,7 @@ add_library(XCEngine STATIC
|
|||||||
${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/VulkanSampler.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanShader.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanDescriptorPool.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/VulkanDescriptorSet.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanPipelineLayout.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/Vulkan/VulkanPipelineLayout.cpp
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ inline VkShaderStageFlags ToVulkanShaderStageFlags(uint32_t visibility) {
|
|||||||
case ShaderVisibility::Domain:
|
case ShaderVisibility::Domain:
|
||||||
case ShaderVisibility::All:
|
case ShaderVisibility::All:
|
||||||
default:
|
default:
|
||||||
return VK_SHADER_STAGE_ALL_GRAPHICS;
|
return VK_SHADER_STAGE_ALL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace XCEngine {
|
|||||||
namespace RHI {
|
namespace RHI {
|
||||||
|
|
||||||
class VulkanDevice;
|
class VulkanDevice;
|
||||||
|
class VulkanShader;
|
||||||
|
|
||||||
class VulkanPipelineState : public RHIPipelineState {
|
class VulkanPipelineState : public RHIPipelineState {
|
||||||
public:
|
public:
|
||||||
@@ -29,17 +30,17 @@ public:
|
|||||||
const DepthStencilStateDesc& GetDepthStencilState() const override { return m_depthStencilDesc; }
|
const DepthStencilStateDesc& GetDepthStencilState() const override { return m_depthStencilDesc; }
|
||||||
const InputLayoutDesc& GetInputLayout() const override { return m_inputLayoutDesc; }
|
const InputLayoutDesc& GetInputLayout() const override { return m_inputLayoutDesc; }
|
||||||
PipelineStateHash GetHash() const override;
|
PipelineStateHash GetHash() const override;
|
||||||
RHIShader* GetComputeShader() const override { return nullptr; }
|
RHIShader* GetComputeShader() const override { return m_computeShader; }
|
||||||
bool HasComputeShader() const override { return false; }
|
bool HasComputeShader() const override { return m_computeShader != nullptr; }
|
||||||
|
|
||||||
bool IsValid() const override { return m_pipeline != VK_NULL_HANDLE; }
|
bool IsValid() const override { return m_pipeline != VK_NULL_HANDLE; }
|
||||||
void EnsureValid() override {}
|
void EnsureValid() override;
|
||||||
|
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
void Bind() override {}
|
void Bind() override {}
|
||||||
void Unbind() override {}
|
void Unbind() override {}
|
||||||
void* GetNativeHandle() override { return m_pipeline; }
|
void* GetNativeHandle() override { return m_pipeline; }
|
||||||
PipelineType GetType() const override { return PipelineType::Graphics; }
|
PipelineType GetType() const override { return HasComputeShader() ? PipelineType::Compute : PipelineType::Graphics; }
|
||||||
|
|
||||||
VkPipeline GetPipeline() const { return m_pipeline; }
|
VkPipeline GetPipeline() const { return m_pipeline; }
|
||||||
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
|
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
|
||||||
@@ -49,6 +50,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool EnsurePipelineLayout(const GraphicsPipelineDesc& desc);
|
||||||
|
bool CreateGraphicsPipeline(const GraphicsPipelineDesc& desc);
|
||||||
|
bool CreateComputePipeline();
|
||||||
|
|
||||||
VulkanDevice* m_deviceOwner = nullptr;
|
VulkanDevice* m_deviceOwner = nullptr;
|
||||||
VkDevice m_device = VK_NULL_HANDLE;
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||||
@@ -64,6 +69,7 @@ private:
|
|||||||
uint32_t m_renderTargetFormats[8] = { 0 };
|
uint32_t m_renderTargetFormats[8] = { 0 };
|
||||||
uint32_t m_depthStencilFormat = 0;
|
uint32_t m_depthStencilFormat = 0;
|
||||||
uint32_t m_sampleCount = 1;
|
uint32_t m_sampleCount = 1;
|
||||||
|
RHIShader* m_computeShader = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace RHI
|
} // namespace RHI
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public:
|
|||||||
bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
bool InitializeAsRenderTarget(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
||||||
bool InitializeAsDepthStencil(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
bool InitializeAsDepthStencil(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
||||||
bool InitializeAsShaderResource(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
bool InitializeAsShaderResource(VkDevice device, VulkanTexture* texture, const ResourceViewDesc& desc);
|
||||||
|
bool InitializeAsUnorderedAccess(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);
|
||||||
|
|
||||||
|
|||||||
47
engine/include/XCEngine/RHI/Vulkan/VulkanShader.h
Normal file
47
engine/include/XCEngine/RHI/Vulkan/VulkanShader.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "XCEngine/RHI/RHIShader.h"
|
||||||
|
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace RHI {
|
||||||
|
|
||||||
|
class VulkanShader : public RHIShader {
|
||||||
|
public:
|
||||||
|
explicit VulkanShader(VkDevice device = VK_NULL_HANDLE)
|
||||||
|
: m_device(device) {}
|
||||||
|
~VulkanShader() override;
|
||||||
|
|
||||||
|
bool CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) override;
|
||||||
|
bool Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
|
ShaderType GetType() const override { return m_type; }
|
||||||
|
bool IsValid() const override { return m_shaderModule != VK_NULL_HANDLE; }
|
||||||
|
void* GetNativeHandle() override { return m_shaderModule; }
|
||||||
|
|
||||||
|
const std::vector<UniformInfo>& GetUniformInfos() const override { return m_uniformInfos; }
|
||||||
|
const UniformInfo* GetUniformInfo(const char* name) const override;
|
||||||
|
|
||||||
|
void SetDevice(VkDevice device) { m_device = device; }
|
||||||
|
|
||||||
|
VkShaderModule GetShaderModule() const { return m_shaderModule; }
|
||||||
|
const char* GetEntryPoint() const { return m_entryPoint.empty() ? "main" : m_entryPoint.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool InitializeFromSpirvWords(const uint32_t* words, size_t wordCount, const char* entryPointHint, const char* targetHint);
|
||||||
|
static bool ResolveShaderTypeFromTarget(const char* target, ShaderType& type);
|
||||||
|
static bool ResolveShaderTypeFromSpirv(const uint32_t* words, size_t wordCount, ShaderType& type, std::string& entryPoint);
|
||||||
|
|
||||||
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
|
VkShaderModule m_shaderModule = VK_NULL_HANDLE;
|
||||||
|
ShaderType m_type = ShaderType::Vertex;
|
||||||
|
std::string m_entryPoint;
|
||||||
|
std::vector<UniformInfo> m_uniformInfos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace RHI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -762,9 +762,22 @@ void VulkanCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommandList::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
void VulkanCommandList::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
||||||
(void)x;
|
EndActiveRenderPass();
|
||||||
(void)y;
|
|
||||||
(void)z;
|
if (m_commandBuffer == VK_NULL_HANDLE || m_currentPipelineState == nullptr || !m_currentPipelineState->HasComputeShader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentPipelineState->EnsureValid();
|
||||||
|
if (!m_currentPipelineState->IsValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdBindPipeline(
|
||||||
|
m_commandBuffer,
|
||||||
|
VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||||
|
m_currentPipelineState->GetPipeline());
|
||||||
|
vkCmdDispatch(m_commandBuffer, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanCommandList::EnsureGraphicsRenderPass() {
|
bool VulkanCommandList::EnsureGraphicsRenderPass() {
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ void VulkanDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DescriptorSetLayoutBinding* binding = FindBinding(offset);
|
const DescriptorSetLayoutBinding* binding = FindBinding(offset);
|
||||||
if (binding == nullptr ||
|
if (binding == nullptr) {
|
||||||
static_cast<DescriptorType>(binding->type) != DescriptorType::SRV) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ void VulkanDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkDescriptorImageInfo imageInfo = {};
|
VkDescriptorImageInfo imageInfo = {};
|
||||||
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
||||||
imageInfo.imageView = vulkanView->GetImageView();
|
imageInfo.imageView = vulkanView->GetImageView();
|
||||||
|
|
||||||
VkWriteDescriptorSet write = {};
|
VkWriteDescriptorSet write = {};
|
||||||
@@ -94,8 +92,21 @@ void VulkanDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
|
|||||||
write.dstSet = m_descriptorSet;
|
write.dstSet = m_descriptorSet;
|
||||||
write.dstBinding = offset;
|
write.dstBinding = offset;
|
||||||
write.descriptorCount = 1;
|
write.descriptorCount = 1;
|
||||||
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
||||||
write.pImageInfo = &imageInfo;
|
switch (static_cast<DescriptorType>(binding->type)) {
|
||||||
|
case DescriptorType::SRV:
|
||||||
|
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||||
|
write.pImageInfo = &imageInfo;
|
||||||
|
break;
|
||||||
|
case DescriptorType::UAV:
|
||||||
|
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||||
|
write.pImageInfo = &imageInfo;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
|
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
|
#include "XCEngine/RHI/Vulkan/VulkanRenderPass.h"
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
|
#include "XCEngine/RHI/Vulkan/VulkanResourceView.h"
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
|
#include "XCEngine/RHI/Vulkan/VulkanSampler.h"
|
||||||
|
#include "XCEngine/RHI/Vulkan/VulkanShader.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 <filesystem>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -25,6 +27,15 @@ namespace RHI {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
std::string NarrowAscii(const std::wstring& value) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(value.size());
|
||||||
|
for (wchar_t ch : value) {
|
||||||
|
result.push_back(static_cast<char>(ch));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring ResolveVendorName(uint32_t vendorId) {
|
std::wstring ResolveVendorName(uint32_t vendorId) {
|
||||||
switch (vendorId) {
|
switch (vendorId) {
|
||||||
case 0x10DE: return L"NVIDIA";
|
case 0x10DE: return L"NVIDIA";
|
||||||
@@ -62,10 +73,18 @@ VkImageLayout ToImageLayout(ResourceStates state) {
|
|||||||
case ResourceStates::Present:
|
case ResourceStates::Present:
|
||||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
case ResourceStates::PixelShaderResource:
|
case ResourceStates::PixelShaderResource:
|
||||||
|
case ResourceStates::NonPixelShaderResource:
|
||||||
|
case ResourceStates::GenericRead:
|
||||||
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
case ResourceStates::DepthWrite:
|
case ResourceStates::DepthWrite:
|
||||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
|
case ResourceStates::DepthRead:
|
||||||
|
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
||||||
|
case ResourceStates::UnorderedAccess:
|
||||||
|
return VK_IMAGE_LAYOUT_GENERAL;
|
||||||
case ResourceStates::Common:
|
case ResourceStates::Common:
|
||||||
|
case ResourceStates::VertexAndConstantBuffer:
|
||||||
|
case ResourceStates::IndexBuffer:
|
||||||
default:
|
default:
|
||||||
return VK_IMAGE_LAYOUT_UNDEFINED;
|
return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
}
|
}
|
||||||
@@ -75,14 +94,21 @@ VkAccessFlags ToAccessMask(ResourceStates state) {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case ResourceStates::RenderTarget:
|
case ResourceStates::RenderTarget:
|
||||||
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
case ResourceStates::UnorderedAccess:
|
||||||
|
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||||
case ResourceStates::CopySrc:
|
case ResourceStates::CopySrc:
|
||||||
return VK_ACCESS_TRANSFER_READ_BIT;
|
return VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
case ResourceStates::CopyDst:
|
case ResourceStates::CopyDst:
|
||||||
return VK_ACCESS_TRANSFER_WRITE_BIT;
|
return VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
case ResourceStates::PixelShaderResource:
|
case ResourceStates::PixelShaderResource:
|
||||||
|
case ResourceStates::NonPixelShaderResource:
|
||||||
return VK_ACCESS_SHADER_READ_BIT;
|
return VK_ACCESS_SHADER_READ_BIT;
|
||||||
case ResourceStates::DepthWrite:
|
case ResourceStates::DepthWrite:
|
||||||
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||||
|
case ResourceStates::DepthRead:
|
||||||
|
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||||
|
case ResourceStates::GenericRead:
|
||||||
|
return VK_ACCESS_MEMORY_READ_BIT;
|
||||||
case ResourceStates::Present:
|
case ResourceStates::Present:
|
||||||
case ResourceStates::Common:
|
case ResourceStates::Common:
|
||||||
default:
|
default:
|
||||||
@@ -94,13 +120,20 @@ VkPipelineStageFlags ToStageMask(ResourceStates state) {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case ResourceStates::RenderTarget:
|
case ResourceStates::RenderTarget:
|
||||||
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
case ResourceStates::UnorderedAccess:
|
||||||
|
return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||||
case ResourceStates::CopySrc:
|
case ResourceStates::CopySrc:
|
||||||
case ResourceStates::CopyDst:
|
case ResourceStates::CopyDst:
|
||||||
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
case ResourceStates::PixelShaderResource:
|
case ResourceStates::PixelShaderResource:
|
||||||
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||||
|
case ResourceStates::NonPixelShaderResource:
|
||||||
|
return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||||
case ResourceStates::DepthWrite:
|
case ResourceStates::DepthWrite:
|
||||||
|
case ResourceStates::DepthRead:
|
||||||
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||||
|
case ResourceStates::GenericRead:
|
||||||
|
return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||||
case ResourceStates::Present:
|
case ResourceStates::Present:
|
||||||
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||||
case ResourceStates::Common:
|
case ResourceStates::Common:
|
||||||
@@ -115,7 +148,7 @@ VkImageUsageFlags ResolveTextureUsageFlags(const TextureDesc& desc) {
|
|||||||
if ((GetImageAspectMask(format) & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
|
if ((GetImageAspectMask(format) & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
|
||||||
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||||
} else {
|
} else {
|
||||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||||
}
|
}
|
||||||
return usage;
|
return usage;
|
||||||
}
|
}
|
||||||
@@ -611,7 +644,34 @@ RHICommandQueue* VulkanDevice::CreateCommandQueue(const CommandQueueDesc& desc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHIShader* VulkanDevice::CreateShader(const ShaderCompileDesc& desc) {
|
RHIShader* VulkanDevice::CreateShader(const ShaderCompileDesc& desc) {
|
||||||
(void)desc;
|
auto* shader = new VulkanShader(m_device);
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
const std::string entryPoint = NarrowAscii(desc.entryPoint);
|
||||||
|
const std::string profile = NarrowAscii(desc.profile);
|
||||||
|
|
||||||
|
const bool isSpirvSource = desc.sourceLanguage == ShaderLanguage::SPIRV;
|
||||||
|
const bool isSpirvFile = !desc.fileName.empty() &&
|
||||||
|
std::filesystem::path(desc.fileName).extension() == L".spv";
|
||||||
|
|
||||||
|
if (!desc.source.empty() && isSpirvSource) {
|
||||||
|
success = shader->Compile(
|
||||||
|
desc.source.data(),
|
||||||
|
desc.source.size(),
|
||||||
|
entryPoint.empty() ? nullptr : entryPoint.c_str(),
|
||||||
|
profile.empty() ? nullptr : profile.c_str());
|
||||||
|
} else if (!desc.fileName.empty() && (isSpirvSource || isSpirvFile)) {
|
||||||
|
success = shader->CompileFromFile(
|
||||||
|
desc.fileName.c_str(),
|
||||||
|
entryPoint.empty() ? nullptr : entryPoint.c_str(),
|
||||||
|
profile.empty() ? nullptr : profile.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete shader;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,8 +794,12 @@ RHIResourceView* VulkanDevice::CreateShaderResourceView(RHITexture* texture, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHIResourceView* VulkanDevice::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) {
|
RHIResourceView* VulkanDevice::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) {
|
||||||
(void)texture;
|
auto* view = new VulkanResourceView();
|
||||||
(void)desc;
|
if (view->InitializeAsUnorderedAccess(m_device, static_cast<VulkanTexture*>(texture), desc)) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete view;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
|
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
|
#include "XCEngine/RHI/Vulkan/VulkanPipelineLayout.h"
|
||||||
|
#include "XCEngine/RHI/Vulkan/VulkanShader.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -66,6 +67,10 @@ bool LoadSpirvBytes(const ShaderCompileDesc& desc, std::vector<uint32_t>& words,
|
|||||||
return !words.empty();
|
return !words.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasShaderPayload(const ShaderCompileDesc& desc) {
|
||||||
|
return !desc.source.empty() || !desc.fileName.empty();
|
||||||
|
}
|
||||||
|
|
||||||
VkShaderModule CreateShaderModule(VkDevice device, const std::vector<uint32_t>& words) {
|
VkShaderModule CreateShaderModule(VkDevice device, const std::vector<uint32_t>& words) {
|
||||||
VkShaderModuleCreateInfo createInfo = {};
|
VkShaderModuleCreateInfo createInfo = {};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
@@ -91,6 +96,8 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
m_deviceOwner = device;
|
m_deviceOwner = device;
|
||||||
m_device = device->GetDevice();
|
m_device = device->GetDevice();
|
||||||
m_inputLayoutDesc = desc.inputLayout;
|
m_inputLayoutDesc = desc.inputLayout;
|
||||||
@@ -105,10 +112,48 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
|
|||||||
m_renderTargetFormats[i] = desc.renderTargetFormats[i];
|
m_renderTargetFormats[i] = desc.renderTargetFormats[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_renderTargetCount != 1 || m_renderTargetFormats[0] == 0) {
|
if (!EnsurePipelineLayout(desc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool hasVertexShader = HasShaderPayload(desc.vertexShader);
|
||||||
|
const bool hasFragmentShader = HasShaderPayload(desc.fragmentShader);
|
||||||
|
if (!hasVertexShader && !hasFragmentShader) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_renderTargetCount != 1 || m_renderTargetFormats[0] == 0 || !hasVertexShader || !hasFragmentShader) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateGraphicsPipeline(desc)) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanPipelineState::EnsurePipelineLayout(const GraphicsPipelineDesc& desc) {
|
||||||
|
auto* externalPipelineLayout = static_cast<VulkanPipelineLayout*>(desc.pipelineLayout);
|
||||||
|
if (externalPipelineLayout != nullptr) {
|
||||||
|
m_pipelineLayout = externalPipelineLayout->GetPipelineLayout();
|
||||||
|
m_ownsPipelineLayout = false;
|
||||||
|
return m_pipelineLayout != VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
|
||||||
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
|
if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ownsPipelineLayout = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& desc) {
|
||||||
std::vector<uint32_t> vertexWords;
|
std::vector<uint32_t> vertexWords;
|
||||||
std::vector<uint32_t> fragmentWords;
|
std::vector<uint32_t> fragmentWords;
|
||||||
std::string vertexEntryPoint;
|
std::string vertexEntryPoint;
|
||||||
@@ -130,21 +175,6 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
|
|||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkAttachmentDescription> attachments;
|
std::vector<VkAttachmentDescription> attachments;
|
||||||
attachments.reserve(2);
|
attachments.reserve(2);
|
||||||
|
|
||||||
@@ -197,11 +227,6 @@ 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) {
|
||||||
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, fragmentModule, nullptr);
|
||||||
vkDestroyShaderModule(m_device, vertexModule, nullptr);
|
vkDestroyShaderModule(m_device, vertexModule, nullptr);
|
||||||
return false;
|
return false;
|
||||||
@@ -346,7 +371,6 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
|
|||||||
vkDestroyShaderModule(m_device, vertexModule, nullptr);
|
vkDestroyShaderModule(m_device, vertexModule, nullptr);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Shutdown();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +410,11 @@ void VulkanPipelineState::SetSampleCount(uint32_t count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPipelineState::SetComputeShader(RHIShader* shader) {
|
void VulkanPipelineState::SetComputeShader(RHIShader* shader) {
|
||||||
(void)shader;
|
if (m_pipeline != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyPipeline(m_device, m_pipeline, nullptr);
|
||||||
|
m_pipeline = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
m_computeShader = shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineStateHash VulkanPipelineState::GetHash() const {
|
PipelineStateHash VulkanPipelineState::GetHash() const {
|
||||||
@@ -396,6 +424,38 @@ PipelineStateHash VulkanPipelineState::GetHash() const {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanPipelineState::EnsureValid() {
|
||||||
|
if (m_pipeline != VK_NULL_HANDLE || !HasComputeShader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateComputePipeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanPipelineState::CreateComputePipeline() {
|
||||||
|
if (m_device == VK_NULL_HANDLE || m_pipelineLayout == VK_NULL_HANDLE || m_computeShader == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* vulkanShader = static_cast<VulkanShader*>(m_computeShader);
|
||||||
|
if (vulkanShader->GetShaderModule() == VK_NULL_HANDLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo shaderStage = {};
|
||||||
|
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
shaderStage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
shaderStage.module = vulkanShader->GetShaderModule();
|
||||||
|
shaderStage.pName = vulkanShader->GetEntryPoint();
|
||||||
|
|
||||||
|
VkComputePipelineCreateInfo pipelineInfo = {};
|
||||||
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||||
|
pipelineInfo.stage = shaderStage;
|
||||||
|
pipelineInfo.layout = m_pipelineLayout;
|
||||||
|
|
||||||
|
return vkCreateComputePipelines(m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_pipeline) == VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanPipelineState::Shutdown() {
|
void VulkanPipelineState::Shutdown() {
|
||||||
if (m_pipeline != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
|
if (m_pipeline != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
|
||||||
vkDestroyPipeline(m_device, m_pipeline, nullptr);
|
vkDestroyPipeline(m_device, m_pipeline, nullptr);
|
||||||
@@ -417,6 +477,7 @@ void VulkanPipelineState::Shutdown() {
|
|||||||
m_deviceOwner = nullptr;
|
m_deviceOwner = nullptr;
|
||||||
m_device = VK_NULL_HANDLE;
|
m_device = VK_NULL_HANDLE;
|
||||||
m_ownsPipelineLayout = false;
|
m_ownsPipelineLayout = false;
|
||||||
|
m_computeShader = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RHI
|
} // namespace RHI
|
||||||
|
|||||||
@@ -85,6 +85,31 @@ bool VulkanResourceView::InitializeAsShaderResource(VkDevice device, VulkanTextu
|
|||||||
return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS;
|
return vkCreateImageView(device, &viewInfo, nullptr, &m_imageView) == VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanResourceView::InitializeAsUnorderedAccess(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::UnorderedAccess;
|
||||||
|
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 = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
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::InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc) {
|
bool VulkanResourceView::InitializeAsVertexBuffer(VulkanBuffer* buffer, const ResourceViewDesc& desc) {
|
||||||
if (buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE) {
|
if (buffer == nullptr || buffer->GetBuffer() == VK_NULL_HANDLE) {
|
||||||
return false;
|
return false;
|
||||||
@@ -131,6 +156,7 @@ bool VulkanResourceView::IsValid() const {
|
|||||||
case ResourceViewType::RenderTarget:
|
case ResourceViewType::RenderTarget:
|
||||||
case ResourceViewType::DepthStencil:
|
case ResourceViewType::DepthStencil:
|
||||||
case ResourceViewType::ShaderResource:
|
case ResourceViewType::ShaderResource:
|
||||||
|
case ResourceViewType::UnorderedAccess:
|
||||||
return m_imageView != VK_NULL_HANDLE;
|
return m_imageView != VK_NULL_HANDLE;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
190
engine/src/RHI/Vulkan/VulkanShader.cpp
Normal file
190
engine/src/RHI/Vulkan/VulkanShader.cpp
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#include "XCEngine/RHI/Vulkan/VulkanShader.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace RHI {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint16_t kSpirvOpEntryPoint = 15;
|
||||||
|
|
||||||
|
ShaderType ToShaderType(uint32_t executionModel) {
|
||||||
|
switch (executionModel) {
|
||||||
|
case 0:
|
||||||
|
return ShaderType::Vertex;
|
||||||
|
case 1:
|
||||||
|
return ShaderType::TessControl;
|
||||||
|
case 2:
|
||||||
|
return ShaderType::TessEvaluation;
|
||||||
|
case 3:
|
||||||
|
return ShaderType::Geometry;
|
||||||
|
case 4:
|
||||||
|
return ShaderType::Fragment;
|
||||||
|
case 5:
|
||||||
|
return ShaderType::Compute;
|
||||||
|
default:
|
||||||
|
return ShaderType::Vertex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
VulkanShader::~VulkanShader() {
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanShader::CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) {
|
||||||
|
if (filePath == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::streamsize fileSize = file.tellg();
|
||||||
|
if (fileSize <= 0 || (fileSize % static_cast<std::streamsize>(sizeof(uint32_t))) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> bytes(static_cast<size_t>(fileSize));
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(bytes.data()), fileSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Compile(bytes.data(), bytes.size(), entryPoint, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanShader::Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) {
|
||||||
|
if (sourceData == nullptr || sourceSize == 0 || (sourceSize % sizeof(uint32_t)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InitializeFromSpirvWords(
|
||||||
|
static_cast<const uint32_t*>(sourceData),
|
||||||
|
sourceSize / sizeof(uint32_t),
|
||||||
|
entryPoint,
|
||||||
|
target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanShader::Shutdown() {
|
||||||
|
if (m_shaderModule != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyShaderModule(m_device, m_shaderModule, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_shaderModule = VK_NULL_HANDLE;
|
||||||
|
m_type = ShaderType::Vertex;
|
||||||
|
m_entryPoint.clear();
|
||||||
|
m_uniformInfos.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RHIShader::UniformInfo* VulkanShader::GetUniformInfo(const char* name) const {
|
||||||
|
if (name == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const UniformInfo& uniformInfo : m_uniformInfos) {
|
||||||
|
if (uniformInfo.name == name) {
|
||||||
|
return &uniformInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanShader::InitializeFromSpirvWords(const uint32_t* words, size_t wordCount, const char* entryPointHint, const char* targetHint) {
|
||||||
|
if (m_device == VK_NULL_HANDLE || words == nullptr || wordCount < 5 || words[0] != 0x07230203u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderType shaderType = ShaderType::Vertex;
|
||||||
|
std::string moduleEntryPoint;
|
||||||
|
if (!ResolveShaderTypeFromSpirv(words, wordCount, shaderType, moduleEntryPoint) &&
|
||||||
|
!ResolveShaderTypeFromTarget(targetHint, shaderType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
createInfo.codeSize = wordCount * sizeof(uint32_t);
|
||||||
|
createInfo.pCode = words;
|
||||||
|
|
||||||
|
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
||||||
|
if (vkCreateShaderModule(m_device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
m_shaderModule = shaderModule;
|
||||||
|
m_type = shaderType;
|
||||||
|
if (entryPointHint != nullptr && entryPointHint[0] != '\0') {
|
||||||
|
m_entryPoint = entryPointHint;
|
||||||
|
} else if (!moduleEntryPoint.empty()) {
|
||||||
|
m_entryPoint = moduleEntryPoint;
|
||||||
|
} else {
|
||||||
|
m_entryPoint = "main";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanShader::ResolveShaderTypeFromTarget(const char* target, ShaderType& type) {
|
||||||
|
if (target == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::strstr(target, "vs_") != nullptr || std::strcmp(target, "vs") == 0) {
|
||||||
|
type = ShaderType::Vertex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (std::strstr(target, "ps_") != nullptr || std::strstr(target, "fs_") != nullptr || std::strcmp(target, "ps") == 0) {
|
||||||
|
type = ShaderType::Fragment;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (std::strstr(target, "gs_") != nullptr || std::strcmp(target, "gs") == 0) {
|
||||||
|
type = ShaderType::Geometry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (std::strstr(target, "cs_") != nullptr || std::strcmp(target, "cs") == 0) {
|
||||||
|
type = ShaderType::Compute;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanShader::ResolveShaderTypeFromSpirv(const uint32_t* words, size_t wordCount, ShaderType& type, std::string& entryPoint) {
|
||||||
|
if (words == nullptr || wordCount < 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 5;
|
||||||
|
while (index < wordCount) {
|
||||||
|
const uint32_t instruction = words[index];
|
||||||
|
const uint16_t wordCountInInstruction = static_cast<uint16_t>(instruction >> 16);
|
||||||
|
const uint16_t opcode = static_cast<uint16_t>(instruction & 0xFFFFu);
|
||||||
|
if (wordCountInInstruction == 0 || index + wordCountInInstruction > wordCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode == kSpirvOpEntryPoint && wordCountInInstruction >= 4) {
|
||||||
|
type = ToShaderType(words[index + 1]);
|
||||||
|
const char* nameBytes = reinterpret_cast<const char*>(&words[index + 3]);
|
||||||
|
entryPoint = nameBytes;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += wordCountInInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RHI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -4,15 +4,21 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "XCEngine/RHI/RHICommandList.h"
|
#include "XCEngine/RHI/RHICommandList.h"
|
||||||
#include "XCEngine/RHI/RHICommandQueue.h"
|
#include "XCEngine/RHI/RHICommandQueue.h"
|
||||||
|
#include "XCEngine/RHI/RHIDescriptorPool.h"
|
||||||
|
#include "XCEngine/RHI/RHIDescriptorSet.h"
|
||||||
#include "XCEngine/RHI/RHIDevice.h"
|
#include "XCEngine/RHI/RHIDevice.h"
|
||||||
#include "XCEngine/RHI/RHIFramebuffer.h"
|
#include "XCEngine/RHI/RHIFramebuffer.h"
|
||||||
#include "XCEngine/RHI/RHIFactory.h"
|
#include "XCEngine/RHI/RHIFactory.h"
|
||||||
|
#include "XCEngine/RHI/RHIPipelineLayout.h"
|
||||||
|
#include "XCEngine/RHI/RHIPipelineState.h"
|
||||||
#include "XCEngine/RHI/RHIRenderPass.h"
|
#include "XCEngine/RHI/RHIRenderPass.h"
|
||||||
#include "XCEngine/RHI/RHIResourceView.h"
|
#include "XCEngine/RHI/RHIResourceView.h"
|
||||||
|
#include "XCEngine/RHI/RHIShader.h"
|
||||||
#include "XCEngine/RHI/RHITexture.h"
|
#include "XCEngine/RHI/RHITexture.h"
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
|
#include "XCEngine/RHI/Vulkan/VulkanCommon.h"
|
||||||
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
|
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
|
||||||
@@ -106,6 +112,28 @@ VkPipelineStageFlags ToStageMask(ResourceStates state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t kWriteRedComputeSpirv[] = {
|
||||||
|
0x07230203, 0x00010000, 0x0008000B, 0x00000017, 0x00000000, 0x00020011, 0x00000001, 0x0006000B,
|
||||||
|
0x00000001, 0x4C534C47, 0x6474732E, 0x3035342E, 0x00000000, 0x0003000E, 0x00000000, 0x00000001,
|
||||||
|
0x0005000F, 0x00000005, 0x00000004, 0x6E69616D, 0x00000000, 0x00060010, 0x00000004, 0x00000011,
|
||||||
|
0x00000001, 0x00000001, 0x00000001, 0x00030003, 0x00000002, 0x000001C2, 0x00040005, 0x00000004,
|
||||||
|
0x6E69616D, 0x00000000, 0x00040005, 0x00000009, 0x616D4975, 0x00006567, 0x00030047, 0x00000009,
|
||||||
|
0x00000019, 0x00040047, 0x00000009, 0x00000021, 0x00000000, 0x00040047, 0x00000009, 0x00000022,
|
||||||
|
0x00000000, 0x00040047, 0x00000016, 0x0000000B, 0x00000019, 0x00020013, 0x00000002, 0x00030021,
|
||||||
|
0x00000003, 0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00090019, 0x00000007, 0x00000006,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000004, 0x00040020, 0x00000008,
|
||||||
|
0x00000000, 0x00000007, 0x0004003B, 0x00000008, 0x00000009, 0x00000000, 0x00040015, 0x0000000B,
|
||||||
|
0x00000020, 0x00000001, 0x00040017, 0x0000000C, 0x0000000B, 0x00000002, 0x0004002B, 0x0000000B,
|
||||||
|
0x0000000D, 0x00000000, 0x0005002C, 0x0000000C, 0x0000000E, 0x0000000D, 0x0000000D, 0x00040017,
|
||||||
|
0x0000000F, 0x00000006, 0x00000004, 0x0004002B, 0x00000006, 0x00000010, 0x3F800000, 0x0004002B,
|
||||||
|
0x00000006, 0x00000011, 0x00000000, 0x0007002C, 0x0000000F, 0x00000012, 0x00000010, 0x00000011,
|
||||||
|
0x00000011, 0x00000010, 0x00040015, 0x00000013, 0x00000020, 0x00000000, 0x00040017, 0x00000014,
|
||||||
|
0x00000013, 0x00000003, 0x0004002B, 0x00000013, 0x00000015, 0x00000001, 0x0006002C, 0x00000014,
|
||||||
|
0x00000016, 0x00000015, 0x00000015, 0x00000015, 0x00050036, 0x00000002, 0x00000004, 0x00000000,
|
||||||
|
0x00000003, 0x000200F8, 0x00000005, 0x0004003D, 0x00000007, 0x0000000A, 0x00000009, 0x00040063,
|
||||||
|
0x0000000A, 0x0000000E, 0x00000012, 0x000100FD, 0x00010038
|
||||||
|
};
|
||||||
|
|
||||||
class VulkanGraphicsFixture : public ::testing::Test {
|
class VulkanGraphicsFixture : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
@@ -169,6 +197,15 @@ protected:
|
|||||||
return m_device->CreateCommandList(desc);
|
return m_device->CreateCommandList(desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RHIShader* CreateWriteRedComputeShader() const {
|
||||||
|
ShaderCompileDesc shaderDesc = {};
|
||||||
|
shaderDesc.sourceLanguage = ShaderLanguage::SPIRV;
|
||||||
|
shaderDesc.profile = L"cs_6_0";
|
||||||
|
shaderDesc.source.resize(sizeof(kWriteRedComputeSpirv));
|
||||||
|
std::memcpy(shaderDesc.source.data(), kWriteRedComputeSpirv, sizeof(kWriteRedComputeSpirv));
|
||||||
|
return m_device->CreateShader(shaderDesc);
|
||||||
|
}
|
||||||
|
|
||||||
void SubmitAndWait(RHICommandList* commandList) {
|
void SubmitAndWait(RHICommandList* commandList) {
|
||||||
ASSERT_NE(commandList, nullptr);
|
ASSERT_NE(commandList, nullptr);
|
||||||
commandList->Close();
|
commandList->Close();
|
||||||
@@ -417,6 +454,119 @@ TEST_F(VulkanGraphicsFixture, CopyResourceCopiesTexturePixels) {
|
|||||||
delete sourceTexture;
|
delete sourceTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(VulkanGraphicsFixture, CreateShaderFromSpirvProducesValidComputeShader) {
|
||||||
|
RHIShader* shader = CreateWriteRedComputeShader();
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
EXPECT_TRUE(shader->IsValid());
|
||||||
|
EXPECT_EQ(shader->GetType(), ShaderType::Compute);
|
||||||
|
EXPECT_NE(shader->GetNativeHandle(), nullptr);
|
||||||
|
shader->Shutdown();
|
||||||
|
delete shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(VulkanGraphicsFixture, CreateUnorderedAccessViewProducesValidView) {
|
||||||
|
RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4));
|
||||||
|
ASSERT_NE(texture, nullptr);
|
||||||
|
|
||||||
|
ResourceViewDesc viewDesc = {};
|
||||||
|
viewDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
viewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||||
|
|
||||||
|
RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, viewDesc);
|
||||||
|
ASSERT_NE(uav, nullptr);
|
||||||
|
EXPECT_TRUE(uav->IsValid());
|
||||||
|
EXPECT_EQ(uav->GetViewType(), ResourceViewType::UnorderedAccess);
|
||||||
|
|
||||||
|
uav->Shutdown();
|
||||||
|
delete uav;
|
||||||
|
texture->Shutdown();
|
||||||
|
delete texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(VulkanGraphicsFixture, DispatchWritesUavTexture) {
|
||||||
|
RHITexture* texture = m_device->CreateTexture(CreateColorTextureDesc(4, 4));
|
||||||
|
ASSERT_NE(texture, nullptr);
|
||||||
|
|
||||||
|
ResourceViewDesc uavDesc = {};
|
||||||
|
uavDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
uavDesc.dimension = ResourceViewDimension::Texture2D;
|
||||||
|
RHIResourceView* uav = m_device->CreateUnorderedAccessView(texture, uavDesc);
|
||||||
|
ASSERT_NE(uav, nullptr);
|
||||||
|
|
||||||
|
DescriptorPoolDesc poolDesc = {};
|
||||||
|
poolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
|
||||||
|
poolDesc.descriptorCount = 1;
|
||||||
|
poolDesc.shaderVisible = true;
|
||||||
|
RHIDescriptorPool* pool = m_device->CreateDescriptorPool(poolDesc);
|
||||||
|
ASSERT_NE(pool, nullptr);
|
||||||
|
|
||||||
|
DescriptorSetLayoutBinding uavBinding = {};
|
||||||
|
uavBinding.binding = 0;
|
||||||
|
uavBinding.type = static_cast<uint32_t>(DescriptorType::UAV);
|
||||||
|
uavBinding.count = 1;
|
||||||
|
uavBinding.visibility = static_cast<uint32_t>(ShaderVisibility::All);
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc setLayout = {};
|
||||||
|
setLayout.bindings = &uavBinding;
|
||||||
|
setLayout.bindingCount = 1;
|
||||||
|
|
||||||
|
RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||||
|
pipelineLayoutDesc.setLayouts = &setLayout;
|
||||||
|
pipelineLayoutDesc.setLayoutCount = 1;
|
||||||
|
RHIPipelineLayout* pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||||
|
ASSERT_NE(pipelineLayout, nullptr);
|
||||||
|
|
||||||
|
RHIDescriptorSet* descriptorSet = pool->AllocateSet(setLayout);
|
||||||
|
ASSERT_NE(descriptorSet, nullptr);
|
||||||
|
descriptorSet->Update(0, uav);
|
||||||
|
|
||||||
|
GraphicsPipelineDesc pipelineDesc = {};
|
||||||
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||||
|
RHIPipelineState* pipelineState = m_device->CreatePipelineState(pipelineDesc);
|
||||||
|
ASSERT_NE(pipelineState, nullptr);
|
||||||
|
|
||||||
|
RHIShader* shader = CreateWriteRedComputeShader();
|
||||||
|
ASSERT_NE(shader, nullptr);
|
||||||
|
pipelineState->SetComputeShader(shader);
|
||||||
|
EXPECT_TRUE(pipelineState->HasComputeShader());
|
||||||
|
EXPECT_EQ(pipelineState->GetType(), PipelineType::Compute);
|
||||||
|
|
||||||
|
RHICommandList* commandList = CreateCommandList();
|
||||||
|
ASSERT_NE(commandList, nullptr);
|
||||||
|
|
||||||
|
commandList->Reset();
|
||||||
|
commandList->TransitionBarrier(uav, ResourceStates::Common, ResourceStates::UnorderedAccess);
|
||||||
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
RHIDescriptorSet* descriptorSets[] = { descriptorSet };
|
||||||
|
commandList->SetComputeDescriptorSets(0, 1, descriptorSets, pipelineLayout);
|
||||||
|
commandList->Dispatch(1, 1, 1);
|
||||||
|
SubmitAndWait(commandList);
|
||||||
|
|
||||||
|
const std::vector<uint8_t> pixels = ReadTextureRgba8(static_cast<VulkanTexture*>(texture));
|
||||||
|
ASSERT_GE(pixels.size(), 4u);
|
||||||
|
EXPECT_EQ(pixels[0], 255u);
|
||||||
|
EXPECT_EQ(pixels[1], 0u);
|
||||||
|
EXPECT_EQ(pixels[2], 0u);
|
||||||
|
EXPECT_EQ(pixels[3], 255u);
|
||||||
|
|
||||||
|
commandList->Shutdown();
|
||||||
|
delete commandList;
|
||||||
|
shader->Shutdown();
|
||||||
|
delete shader;
|
||||||
|
pipelineState->Shutdown();
|
||||||
|
delete pipelineState;
|
||||||
|
descriptorSet->Shutdown();
|
||||||
|
delete descriptorSet;
|
||||||
|
pipelineLayout->Shutdown();
|
||||||
|
delete pipelineLayout;
|
||||||
|
pool->Shutdown();
|
||||||
|
delete pool;
|
||||||
|
uav->Shutdown();
|
||||||
|
delete uav;
|
||||||
|
texture->Shutdown();
|
||||||
|
delete texture;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user