This commit is contained in:
ouczbs 2024-11-02 17:55:55 +08:00
parent fe8a28c8b0
commit 622a0936b1
15 changed files with 247 additions and 28 deletions

View File

@ -1 +1 @@
add_requires("spdlog", "lemon", "libsdl", "vulkansdk","shaderc")
add_requires("spdlog", "lemon", "libsdl", "vulkansdk","shaderc","spirv","spirv-cross")

View File

@ -1,10 +1,13 @@
#version 450
layout (location = 0) in vec3 iPos;
layout(set = 0, binding = 0) uniform ColorData {
vec4 uColor; // 从 uniform buffer 获取颜色
};
layout (location = 0) out vec4 vColor;
void main()
{
gl_Position = vec4(iPos.x, iPos.y, iPos.z, 1.0);
vColor = vec4(1.0, 0.0, 0.0, 1.0);
gl_Position = vec4(iPos, 1.0);
vColor = uColor; // 使用 uniform 颜色
}

View File

@ -1,7 +1,16 @@
#pragma once
#include "asset/asset.h"
#include "render/type.h"
namespace api {
class ShaderProgram : public Resource<ShaderProgram> {};
class ShaderProgram : public Resource<ShaderProgram> {
protected:
ShaderStage mStage;
public:
ShaderProgram(ShaderStage stage) : mStage(stage) {};
ShaderStage GetStage() {
return mStage;
}
};
class Shader : public Asset {
private:
UPROPERTY()

View File

@ -1,5 +1,6 @@
#pragma once
#include "pmr/name.h"
#include "render/asset/shader.h"
#include <optional>
#include <shaderc/shaderc.h>
namespace api
@ -11,6 +12,9 @@ namespace api
{
public:
static shaderc_shader_kind GetShaderKind(Name name);
static ShaderStage GetShaderStage(shaderc_shader_kind kind);
static optional<pmr::vector<uint32_t>> ToSpirv(const pmr::string& glsl, shaderc_shader_kind kind, string_view code_id = "unknown_shader");
static void LoadShaderLayout(ShaderProgram* program, const pmr::vector<uint32_t>& spirv);
static void GetShaderLayout(ShaderDescriptorSet& descriptorSet, pmr::vector<ShaderProgram*>& programList);
};
}

View File

@ -65,6 +65,23 @@ namespace api {
TEX_3D = 0x04,
TEX_CUBE = 0x08,
};
enum class ShaderDescriptorType : uint8_t{
UNIFORM_BUFFER,
SAMPLER,
};
enum class ShaderStage : uint32_t {
NONE = 0,
VERTEX = 0x1,
FRAGMENT = 0x2,
};
struct ShaderDescriptorLayout {
uint32_t set : 8;
uint32_t binding : 8;
uint32_t size : 16;
ShaderDescriptorType type;
ShaderStage stageFlags;
};
using ShaderDescriptorSet = pmr::vector<ShaderDescriptorLayout>;
using ImagePtr = void*;
using ImageViewPtr = void*;
using BufferPtr = void*;

View File

@ -1,8 +1,12 @@
#include "render/tool/glsl_to_spirv.h"
#include <shaderc/shaderc.hpp>
#include "render/type.h"
#include "zlog.h"
#include "meta/enum.h"
#include <shaderc/shaderc.hpp>
#include <spirv_cross/spirv_reflect.hpp>
namespace api
{
static pmr::table<Guid, ShaderDescriptorSet> ShaderLayoutTable;
optional<pmr::vector<uint32_t>> GlslToSpirv::ToSpirv(const pmr::string& glsl, shaderc_shader_kind kind, string_view code_id)
{
optional<pmr::vector<uint32_t>> spirv_out{FramePool()};
@ -34,5 +38,78 @@ namespace api
default: return shaderc_shader_kind::shaderc_miss_shader;
}
}
ShaderStage GlslToSpirv::GetShaderStage(shaderc_shader_kind kind)
{
switch (kind)
{
case shaderc_shader_kind::shaderc_vertex_shader:
return ShaderStage::VERTEX;
case shaderc_shader_kind::shaderc_fragment_shader:
return ShaderStage::FRAGMENT;
default:
return ShaderStage::NONE;
}
}
void GlslToSpirv::LoadShaderLayout(ShaderProgram* program, const pmr::vector<uint32_t>& spirv) {
ShaderStage stage = program->GetStage();
spirv_cross::Compiler compiler(spirv.data(), spirv.size());
auto resources = compiler.get_shader_resources();
ShaderDescriptorSet descriptorSet;
// 遍历 uniform buffers (UBO)
for (auto& res : resources.uniform_buffers)
{
ShaderDescriptorLayout layout{};
layout.set = compiler.get_decoration(res.id, spv::Decoration::DecorationDescriptorSet);
layout.binding = compiler.get_decoration(res.id, spv::Decoration::DecorationBinding);
auto type = compiler.get_type(res.type_id);
uint32_t size = type.width;
uint32_t i = 0;
for (auto& member_type : type.member_types)
{
auto tmp = compiler.get_type(member_type);
size += (uint32_t)(compiler.get_declared_struct_member_size(type, i++));
}
layout.size = size;
layout.stageFlags = stage;
layout.type = ShaderDescriptorType::UNIFORM_BUFFER;
descriptorSet.push_back(layout);
}
// 遍历 sampled images (采样器/纹理)
for (const auto& res : resources.sampled_images) {
ShaderDescriptorLayout layout{};
layout.set = compiler.get_decoration(res.id, spv::Decoration::DecorationDescriptorSet);
layout.binding = compiler.get_decoration(res.id, spv::Decoration::DecorationBinding);
layout.size = 0;
layout.stageFlags = stage;
layout.type = ShaderDescriptorType::SAMPLER;
descriptorSet.push_back(layout);
}
ShaderLayoutTable.emplace(program->GetGuid(), descriptorSet);
}
void GlslToSpirv::GetShaderLayout(ShaderDescriptorSet& descriptorSet, pmr::vector<ShaderProgram*>& programList)
{
table<uint32_t, uint32_t> idTable{ FramePool() };
for (auto program : programList) {
auto& itSet = ShaderLayoutTable[program->GetGuid()];
for (ShaderDescriptorLayout& layout : itSet) {
uint32_t id = (layout.set << 8) + layout.binding;
auto itId = idTable.find(id);
if (itId == idTable.end()) {
descriptorSet.emplace_back(layout);
idTable[id] = descriptorSet.size() - 1;
}
else {
using ::operator|=;
ShaderDescriptorLayout& desc = descriptorSet[itId->second];
desc.stageFlags |= layout.stageFlags;
}
}
}
std::sort(descriptorSet.begin(), descriptorSet.end(), [](auto& k1, auto& k2) {
if (k1.set != k2.set) {
return k1.set < k2.set;
}
return k1.binding < k2.binding;
});
}
}

View File

@ -7,4 +7,4 @@ static_component("render","engine")
add_files("src/**.cpp")
add_deps("asset", "zlib", "core")
add_syslinks("user32", {public = true})
add_packages("lemon", "libsdl","shaderc", {public = true})
add_packages("lemon", "libsdl","shaderc","spirv-cross", {public = true})

View File

@ -1,12 +1,15 @@
#pragma once
#include <type_traits>
namespace meta {
template<typename Enum>
concept is_enum_t = requires { std::is_enum_v<Enum>; };
concept is_enum_t = std::is_enum_v<Enum>;
}
template<typename Enum>
concept is_enum_t = std::is_enum_v<Enum>;
template<meta::is_enum_t Enum>
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
inline constexpr Enum& operator|=(Enum& lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
lhs = Enum(underlying(lhs) | underlying(rhs));
lhs = static_cast<Enum>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
return lhs;
}
template<meta::is_enum_t Enum>

View File

@ -3,14 +3,17 @@
#include "asset/resource_system.h"
#include "vkn/vulkan_api.h"
namespace vkn {
using api::ShaderDescriptorSet;
class vkShaderProgram : public api::ShaderProgram {
private:
VkShaderModule mPtr;
public:
using api::ShaderProgram::ShaderProgram;
VkShaderModule Ptr() {
return mPtr;
}
void Load(const pmr::vector<uint32_t>& spirv);
VkShaderStageFlagBits GetVkStage();
};
class VulkanGlslLoader : public api::IFileLoader
{
@ -18,5 +21,6 @@ namespace vkn {
public:
static void Init();
api::ResourceBundle LoadFile(api::PackagePath handle, const api::MetaBundle& meta) override;
static uint32_t GetShaderLayout(VkDescriptorSetLayoutList& layoutList, pmr::vector<api::ShaderProgram*>& programList);
};
}

View File

@ -29,6 +29,9 @@ namespace vkn {
constexpr uint32_t SHADER_MODULE_COUNT = 2;
constexpr uint32_t VERTEX_ATTRIBUTE_COUNT = 16;
constexpr uint32_t MAX_BUFFER_BIND_NUM = 16;
constexpr uint32_t MAX_SHADER_DESCRIPTOR_SET_COUNT = 3;
using VkDescriptorSetLayoutList = VkDescriptorSetLayout[MAX_SHADER_DESCRIPTOR_SET_COUNT];
using VkDescriptorSetList = VkDescriptorSet[MAX_SHADER_DESCRIPTOR_SET_COUNT];
struct MeshVAO
{
uint32_t indexCount = 0; // 索引数量
@ -96,10 +99,10 @@ namespace vkn {
{
Name name; // For debug
VkPipeline pipeline = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout sceneDescriptorSetLayout = VK_NULL_HANDLE; // For ray tracing
VkDescriptorSetList descList{};
VkDescriptorSetLayoutList descLayoutList{};
uint32_t descCount = 0;
bool inUse = false;
};
}

View File

@ -2,6 +2,8 @@
namespace vkn {
using api::ResourceState;
using api::TextureBarrier;
using api::ShaderDescriptorType;
using api::ShaderStage;
struct VkTextureTransitionDesc {
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
@ -20,4 +22,6 @@ namespace vkn {
VkImageCreateFlags vkApiGetImageCreateFlag(TextureDimension dimension, uint32_t arraySize);
VkSampleCountFlagBits vkApiGetSmpleCountFlag(SampleCount sample);
VkDescriptorType vkApiGetDescriptorType(ShaderDescriptorType type);
VkShaderStageFlags vkApiGetShaderStageFlags(ShaderStage stage);
}

View File

@ -11,6 +11,9 @@ namespace vkn {
public:
DescriptorPool(Device& device, pmr::vector<VkDescriptorPoolSize>& pPoolSizes,uint32_t maxSets);
VkDescriptorPool Ptr() {
return mPtr;
}
VkDescriptorSet Allocate(VkDescriptorSetLayout& descriptorSetLayout);
public:
static pmr::vector<VkDescriptorPoolSize> DefaultDescriptorPoolSize() {

View File

@ -1,6 +1,7 @@
#include "vkn/loader/vulkan_glsl_loader.h"
#include "vkn/vulkan_api.h"
#include "vkn/wrapper/device.h"
#include "vkn/vulkan_api_help.h"
#include "render/tool/glsl_to_spirv.h"
#include "render/asset/vertex.h"
#include "os/file_handle.h"
@ -19,8 +20,10 @@ namespace vkn {
ResourceBundle VulkanGlslLoader::LoadFile(PackagePath path, const MetaBundle& meta)
{
auto m = meta.FetchMeta<ShaderProgram>();
auto program = m ? ResourceSystem::Ptr()->LoadEmplaceResource<vkShaderProgram>(m->guid)
: ResourceSystem::Ptr()->LoadEmplaceResource<vkShaderProgram>();
auto shader_kind = GlslToSpirv::GetShaderKind(path.GetExtension().ToStringView());
auto shader_stage = GlslToSpirv::GetShaderStage(shader_kind);
auto program = m ? ResourceSystem::Ptr()->LoadEmplaceResource<vkShaderProgram>(m->guid, shader_stage)
: ResourceSystem::Ptr()->LoadEmplaceResource<vkShaderProgram>(shader_stage);
FileHandle handle(path);
if (!handle.Open(FILE_OP::READ, mFileFlag & FileFlag::File_Binary)) {
return program;
@ -34,7 +37,6 @@ namespace vkn {
}
else {
pmr::string glsl = handle.ReadAll<pmr::string>();
auto shader_kind = GlslToSpirv::GetShaderKind(path.GetExtension().ToStringView());
auto spirv = GlslToSpirv::ToSpirv(glsl, shader_kind, path.GetFileName());
if (spirv) {
program->Load(*spirv);
@ -43,10 +45,67 @@ namespace vkn {
}
return program;
}
VkDescriptorSetLayout CreateShaderLayout(std::span<ShaderDescriptorLayout> descList, pmr::vector<VkDescriptorSetLayoutBinding>& bindingList) {
VkDescriptorSetLayout layout;
bindingList.resize(descList.size());
uint32_t i = 0;
for (auto& desc : descList) {
VkDescriptorSetLayoutBinding binding = {};
binding.binding = desc.binding;
binding.descriptorType = vkApiGetDescriptorType(desc.type);
binding.descriptorCount = 1;
binding.stageFlags = vkApiGetShaderStageFlags(desc.stageFlags);
bindingList[i++] = binding;
}
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.pBindings = bindingList.data();
layoutInfo.bindingCount = static_cast<uint32_t>(bindingList.size());
vkCreateDescriptorSetLayout(VulkanAPI::Ptr()->GetBackend().GetDevice().Ptr(), &layoutInfo, nullptr, &layout);
return layout;
}
uint32_t VulkanGlslLoader::GetShaderLayout(VkDescriptorSetLayoutList& layoutList, pmr::vector<api::ShaderProgram*>& programList)
{
ShaderDescriptorSet descList{FramePool()};
GlslToSpirv::GetShaderLayout(descList, programList);
if (descList.empty()) {
return 0;
}
descList.push_back(ShaderDescriptorLayout{.set = 0xff});
pmr::vector<VkDescriptorSetLayoutBinding> bindingList{FramePool()};
uint32_t set = 0, binding = 0, count = 0;
for (auto it = descList.begin(), itEnd = descList.end(); it != itEnd; it++) {
if (set == it->set) {
binding++;
}
else {
if (binding > 0) {
assert(count < MAX_SHADER_DESCRIPTOR_SET_COUNT);
std::span<ShaderDescriptorLayout> spanList{ it - binding, binding };
layoutList[count++] = CreateShaderLayout(spanList, bindingList);
}
set = it->set;
binding = 0;
}
}
return count;
}
void vkShaderProgram::Load(const pmr::vector<uint32_t>& spirv)
{
VulkanAPI* API = VulkanAPI::Ptr();
mPtr = API->GetBackend().GetDevice().CreateShaderModule(spirv);
GlslToSpirv::LoadShaderLayout(this, spirv);
}
VkShaderStageFlagBits vkShaderProgram::GetVkStage()
{
switch (mStage) {
case ShaderStage::VERTEX:
return VK_SHADER_STAGE_VERTEX_BIT;
case ShaderStage::FRAGMENT:
return VK_SHADER_STAGE_FRAGMENT_BIT;
default:
return VkShaderStageFlagBits();
}
}
}

View File

@ -72,8 +72,8 @@ namespace vkn {
vkCmdBindVertexBuffers(ptr, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(ptr, vulkanVAO.indexBuffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindPipeline(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);
if(pipeline.descriptorSet)
vkCmdBindDescriptorSets(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, 0, 1, &pipeline.descriptorSet, 0, VK_NULL_HANDLE);
if(pipeline.descCount > 0)
vkCmdBindDescriptorSets(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, 0, pipeline.descCount, pipeline.descList, 0, VK_NULL_HANDLE);
vkCmdDrawIndexed(ptr, vulkanVAO.indexCount, 1, 0, 0, 0);
}
void VulkanAPI::LoadShader(Shader& shader, size_t passKey)
@ -86,10 +86,14 @@ namespace vkn {
pmr::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::map<VkShaderStageFlagBits, VkShaderModule> shaderModules;
auto& device = backend.GetDevice();
auto vertModule = shader.GetVertHandle<vkShaderProgram>()->Ptr();
shaderModules.insert(std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertModule));
auto fragModule = shader.GetFragHandle<vkShaderProgram>()->Ptr();
shaderModules.insert(std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragModule));
pmr::vector<api::ShaderProgram*> programList;
programList.push_back(shader.GetVertHandle().Ptr());
programList.push_back(shader.GetFragHandle().Ptr());
for (auto program : programList) {
vkShaderProgram* vkProgram = (vkShaderProgram*)program;
auto shaderModule = vkProgram->Ptr();
shaderModules.insert(std::make_pair(vkProgram->GetVkStage(), shaderModule));
}
for (auto& shaderModule : shaderModules)
{
VkPipelineShaderStageCreateInfo shaderStageInfo = {};
@ -205,11 +209,21 @@ namespace vkn {
depthStencilInfo.stencilTestEnable = VK_FALSE;
depthStencilInfo.front = {};
depthStencilInfo.back = {};
VkDescriptorSetLayout descriptorSetLayout{};
VulkanPipeline& vulkan_pipeline = PipelineTable[shader.GetGuid()];
vulkan_pipeline.descCount = VulkanGlslLoader::GetShaderLayout(vulkan_pipeline.descLayoutList, programList);
VkDescriptorSet descriptorSet;
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = backend.GetPool().Ptr();
allocInfo.descriptorSetCount = vulkan_pipeline.descCount;
allocInfo.pSetLayouts = vulkan_pipeline.descLayoutList;
vkAllocateDescriptorSets(device.Ptr(), &allocInfo, vulkan_pipeline.descList);
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // 没有描述符集布局时可以设置为0
pipelineLayoutInfo.pSetLayouts = nullptr;
pipelineLayoutInfo.setLayoutCount = vulkan_pipeline.descCount;
pipelineLayoutInfo.pSetLayouts = vulkan_pipeline.descLayoutList;
VkPipelineLayout pipelineLayout;
if (vkCreatePipelineLayout(device.Ptr(), &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
throw std::runtime_error("failed to create pipeline layout!");
@ -239,13 +253,10 @@ namespace vkn {
for (auto& shaderModule : shaderModules)
vkDestroyShaderModule(device.Ptr(), shaderModule.second, nullptr);
VulkanPipeline& vulkan_pipeline = PipelineTable[shader.GetGuid()];
vulkan_pipeline.name = shader.Name();
vulkan_pipeline.pipeline = pipeLine;
vulkan_pipeline.inUse = true;
vulkan_pipeline.pipelineLayout = pipelineLayout;
vulkan_pipeline.descriptorSetLayout = descriptorSetLayout;
vulkan_pipeline.descriptorSet = descriptorSetLayout ? backend.GetPool().Allocate(descriptorSetLayout) : nullptr;
}
ImagePtr VulkanAPI::CreateTexture(TextureDesc desc)
{

View File

@ -233,4 +233,26 @@ namespace vkn {
{
return (VkSampleCountFlagBits)sample;
}
VkDescriptorType vkApiGetDescriptorType(ShaderDescriptorType type)
{
switch (type) {
case ShaderDescriptorType::SAMPLER:
return VK_DESCRIPTOR_TYPE_SAMPLER;
case ShaderDescriptorType::UNIFORM_BUFFER:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
default:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
}
}
VkShaderStageFlags vkApiGetShaderStageFlags(ShaderStage stage)
{
VkShaderStageFlags flags{};
if (any(stage & ShaderStage::FRAGMENT)) {
flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (any(stage & ShaderStage::VERTEX)) {
flags |= VK_SHADER_STAGE_VERTEX_BIT;
}
return flags;
}
}