diff --git a/.gitmodules b/.gitmodules index b44e47387fc30a41c3f5c8d09b5ad525b354f233..323286b592292b798a8b6ca03dde3651dd36239e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/glfw"] path = lib/glfw url = https://github.com/glfw/glfw.git +[submodule "lib/SPIRV-Cross"] + path = lib/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross.git diff --git a/config/Libraries.cmake b/config/Libraries.cmake index 13667936dd100be91ab43b73e6774e7db3da876c..e04aa3575a34632eb75c929bf4640305cd93e298 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -16,6 +16,7 @@ set(vkcv_config_msg " - Library: ") # load dependencies via separate cmake file include(${vkcv_config_lib}/GLFW.cmake) # glfw-x11 / glfw-wayland # libglfw3-dev include(${vkcv_config_lib}/Vulkan.cmake) # vulkan-intel / vulkan-radeon / nvidia # libvulkan-dev +include(${vkcv_config_lib}/SPIRV_Cross.cmake) # SPIRV-Cross # libspirv_cross_c_shared # cleanup of compiler flags if (vkcv_flags) diff --git a/config/Sources.cmake b/config/Sources.cmake index cb73f57c2ca7278765ef0c8d01989c09a445c7b5..998c26723940788dc2ddd427dc1a18b979ab6967 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -61,4 +61,7 @@ set(vkcv_sources ${vkcv_include}/vkcv/DescriptorConfig.hpp ${vkcv_source}/vkcv/DescriptorConfig.cpp + + ${vkcv_include}/vkcv/VertexLayout.hpp + ${vkcv_source}/vkcv/VertexLayout.cpp ) diff --git a/config/lib/SPIRV_Cross.cmake b/config/lib/SPIRV_Cross.cmake new file mode 100644 index 0000000000000000000000000000000000000000..751ee883c47e0eab081a13e5805ced6f2daa7e30 --- /dev/null +++ b/config/lib/SPIRV_Cross.cmake @@ -0,0 +1,22 @@ +find_package(spirv_cross_c_shared QUIET) + +if (spirv-cross_FOUND) + list(APPEND vkcv_libraries spirv-cross-cpp) + + message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) +else() + if (EXISTS "${vkcv_lib_path}/SPIRV-Cross") + set(SPIRV_CROSS_CLI OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_C_API OFF CACHE INTERNAL "") + set(SPIRV_CROSS_SKIP_INSTALL ON CACHE INTERNAL "") + + add_subdirectory(${vkcv_lib}/SPIRV-Cross) + + list(APPEND vkcv_libraries spirv-cross-cpp) + + message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) + else() + message(WARNING "SPIRV-Cross is required..! Update the submodules!") + endif () +endif () \ No newline at end of file diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index 172e906fca457c6245855639275054514958b69d..af60f9fb7af196ec8576320742fc86893ddc7b8e 100644 --- a/include/vkcv/ShaderProgram.hpp +++ b/include/vkcv/ShaderProgram.hpp @@ -10,6 +10,8 @@ #include <iostream> #include <filesystem> #include <vulkan/vulkan.hpp> +#include <spirv_cross.hpp> +#include "vkcv/VertexLayout.hpp" namespace vkcv { @@ -53,8 +55,13 @@ namespace vkcv { bool existsShader(ShaderStage shaderStage) const; + void reflectShader(ShaderStage shaderStage); + + VertexLayout& getVertexLayout(); + private: std::unordered_map<ShaderStage, Shader> m_Shaders; + VertexLayout m_VertexLayout; }; } diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f9579b5d7dfa8127d592532f55fd569cacb505c9 --- /dev/null +++ b/include/vkcv/VertexLayout.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <unordered_map> +#include <vector> +#include <iostream> + +namespace vkcv{ + enum class VertexFormat{ + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4, + INT, + INT2, + INT3, + INT4 + }; + + struct VertexInputAttachment{ + VertexInputAttachment() = delete; + VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept; + + uint32_t location; + uint32_t binding; + VertexFormat format; + uint32_t offset; + }; + + struct VertexLayout{ + VertexLayout() noexcept; + VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept; + std::unordered_map<uint32_t, VertexInputAttachment> attachmentMap; + uint32_t stride; + }; + + +} \ No newline at end of file diff --git a/lib/SPIRV-Cross b/lib/SPIRV-Cross new file mode 160000 index 0000000000000000000000000000000000000000..ff61890722a91e97c44940494be5b6eed0d5ff5b --- /dev/null +++ b/lib/SPIRV-Cross @@ -0,0 +1 @@ +Subproject commit ff61890722a91e97c44940494be5b6eed0d5ff5b diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 07da9506c5546985d4852e5d7d87ecbbcfe5e788..a515005e18ddaee99959a5c97b6978a0054ee574 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -86,6 +86,8 @@ int main(int argc, const char** argv) { vkcv::ShaderProgram triangleShaderProgram{}; triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv")); triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv")); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass); vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 87ccdefbfec0b4891d3152d30aa6c9f6c8c0d5ea..69ad7c3188d3dd0ae940b8bdd1c628d4fc702c7f 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -27,8 +27,46 @@ namespace vkcv { return buffer; } + VertexFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ + switch (basetype) { + case spirv_cross::SPIRType::Int: + switch (vecsize) { + case 1: + return VertexFormat::INT; + case 2: + return VertexFormat::INT2; + case 3: + return VertexFormat::INT3; + case 4: + return VertexFormat::INT4; + default: + break; + } + break; + case spirv_cross::SPIRType::Float: + switch (vecsize) { + case 1: + return VertexFormat::FLOAT; + case 2: + return VertexFormat::FLOAT2; + case 3: + return VertexFormat::FLOAT3; + case 4: + return VertexFormat::FLOAT4; + default: + break; + } + break; + default: + break; + } + std::cout << "Shader Program Reflection: unknown Vertex Format" << std::endl; + return VertexFormat::FLOAT; + } + ShaderProgram::ShaderProgram() noexcept : - m_Shaders{} + m_Shaders{}, + m_VertexLayout{} {} bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath) @@ -59,4 +97,38 @@ namespace vkcv { else return true; } + + void ShaderProgram::reflectShader(ShaderStage shaderStage) + { + auto shaderCodeChar = m_Shaders.at(shaderStage).shaderCode; + std::vector<uint32_t> shaderCode; + + for (uint32_t i = 0; i < shaderCodeChar.size()/4; i++) { + shaderCode.push_back(((uint32_t*) shaderCodeChar.data())[i]); + } + + spirv_cross::Compiler comp(move(shaderCode)); + spirv_cross::ShaderResources resources = comp.get_shader_resources(); + + std::vector<VertexInputAttachment> inputVec; + uint32_t offset = 0; + + for (uint32_t i = 0; i < resources.stage_inputs.size() ; i++){ + auto &u = resources.stage_inputs[i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + + VertexInputAttachment input = VertexInputAttachment(comp.get_decoration(u.id,spv::DecorationLocation), + 0, + convertFormat(base_type.basetype, base_type.vecsize), + offset); + inputVec.push_back(input); + offset += base_type.vecsize * base_type.width/8; + } + + m_VertexLayout = VertexLayout(inputVec); + } + + VertexLayout& ShaderProgram::getVertexLayout(){ + return m_VertexLayout; + } } diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88c9406bc321a76b48df60dae51486e39a4160de --- /dev/null +++ b/src/vkcv/VertexLayout.cpp @@ -0,0 +1,53 @@ +// +// Created by Charlotte on 28.05.2021. +// + +#include "vkcv/VertexLayout.hpp" + +namespace vkcv { + uint32_t static getFormatSize(VertexFormat format) { + switch (format) { + case VertexFormat::FLOAT: + return 4; + case VertexFormat::FLOAT2: + return 8; + case VertexFormat::FLOAT3: + return 12; + case VertexFormat::FLOAT4: + return 16; + case VertexFormat::INT: + return 4; + case VertexFormat::INT2: + return 8; + case VertexFormat::INT3: + return 12; + case VertexFormat::INT4: + return 16; + default: + break; + } + std::cout << "VertexLayout: No format given" << std::endl; + return 0; + } + + VertexInputAttachment::VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept: + location{location}, + binding{binding}, + format{format}, + offset{offset} + {} + + VertexLayout::VertexLayout() noexcept : + stride{0}, + attachmentMap() + {} + + VertexLayout::VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept { + stride = 0; + for (const auto &input : inputs) { + attachmentMap.insert(std::make_pair(input.location, input)); + stride += getFormatSize(input.format); + } + } + +} \ No newline at end of file