diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index 7dc6f2200db7efc1abdddab81145daec27540c49..729330fcaf7eeac2acfdd1816b86ac29c7d9e30b 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -7,9 +7,9 @@ #include <vector> #include <cstdint> -#include "vkcv/Handles.hpp" +#include "Handles.hpp" #include "ShaderProgram.hpp" -#include <vkcv/VertexLayout.hpp> +#include "VertexLayout.hpp" namespace vkcv { @@ -21,24 +21,25 @@ namespace vkcv { * @param shaderProgram shaders of the pipeline * @param height height of the application window * @param width width of the application window - * @param passHandle handle for Render Pass + * @param passHandle handle for render pass + * @param vertexLayout layout of vertex buffer, comprised of its bindings and the bindings' attachments */ PipelineConfig( const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, const PassHandle &passHandle, - const std::vector<VertexAttribute> &vertexAttributes, + const VertexLayout &vertexLayouts, const std::vector<vk::DescriptorSetLayout> &descriptorLayouts, bool useDynamicViewport); - ShaderProgram m_ShaderProgram; - uint32_t m_Height; - uint32_t m_Width; - PassHandle m_PassHandle; - std::vector<VertexAttribute> m_VertexAttributes; - std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; - bool m_UseDynamicViewport; + ShaderProgram m_ShaderProgram; + uint32_t m_Height; + uint32_t m_Width; + PassHandle m_PassHandle; + VertexLayout m_VertexLayout; + std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; + bool m_UseDynamicViewport; }; diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index ce28cccf07e22dda21fd14d0bddd0ba6e9842328..28f746d78477062eae9b0ad88f8c5de71e11efd0 100644 --- a/include/vkcv/ShaderProgram.hpp +++ b/include/vkcv/ShaderProgram.hpp @@ -12,9 +12,9 @@ #include <filesystem> #include <vulkan/vulkan.hpp> #include <spirv_cross.hpp> -#include "vkcv/VertexLayout.hpp" -#include "vkcv/ShaderStage.hpp" -#include "vkcv/DescriptorConfig.hpp" +#include "VertexLayout.hpp" +#include "ShaderStage.hpp" +#include "DescriptorConfig.hpp" namespace vkcv { @@ -48,17 +48,23 @@ namespace vkcv { bool existsShader(ShaderStage shaderStage) const; - void reflectShader(ShaderStage shaderStage); - - const VertexLayout &getVertexLayout() const; + const std::vector<VertexAttachment> &getVertexAttachments() const; size_t getPushConstantSize() const; - const std::vector<std::vector<DescriptorBinding>> getReflectedDescriptors() const; + const std::vector<std::vector<DescriptorBinding>> &getReflectedDescriptors() const; private: + /** + * Called after successfully adding a shader to the program. + * Fills vertex input attachments and descriptor sets (if present). + * @param shaderStage the stage to reflect data from + */ + void reflectShader(ShaderStage shaderStage); + std::unordered_map<ShaderStage, Shader> m_Shaders; - VertexLayout m_VertexLayout; + // contains all vertex input attachments used in the vertex buffer + std::vector<VertexAttachment> m_VertexAttachments; std::vector<std::vector<DescriptorBinding>> m_DescriptorSets; size_t m_pushConstantSize = 0; }; diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index aae43910a09f221874813733b2ef72d36467a750..67687f64c2fcbf777b55f15b3c7cb0adaa939ebc 100644 --- a/include/vkcv/VertexLayout.hpp +++ b/include/vkcv/VertexLayout.hpp @@ -1,30 +1,11 @@ #pragma once -#include <unordered_map> #include <vector> #include <iostream> #include <string> namespace vkcv{ - - /* With these enums, 0 is reserved to signal uninitialized or invalid data. */ - enum class PrimitiveType : uint32_t { - UNDEFINED = 0, - POSITION = 1, - NORMAL = 2, - TEXCOORD_0 = 3 - }; - /* This struct describes one vertex attribute of a vertex buffer. */ - typedef struct { - PrimitiveType type; // POSITION, NORMAL, ... - uint32_t offset; // offset in bytes - uint32_t length; // length of ... in bytes - uint32_t stride; // stride in bytes - uint16_t componentType; // eg. 5126 for float - uint8_t componentCount; // eg. 3 for vec3 - } VertexAttribute; - - enum class VertexFormat{ + enum class VertexAttachmentFormat{ FLOAT, FLOAT2, FLOAT3, @@ -35,23 +16,50 @@ namespace vkcv{ INT4 }; - uint32_t getFormatSize(VertexFormat format); + uint32_t getFormatSize(VertexAttachmentFormat format); - struct VertexInputAttachment{ - VertexInputAttachment() = delete; - VertexInputAttachment(uint32_t location, uint32_t binding, std::string name, VertexFormat format, uint32_t offset) noexcept; + struct VertexAttachment{ + /** + * Describes an individual vertex input attribute/attachment. + * @param inputLocation its location in the vertex shader. + * @param name the name referred to in the shader. + * @param format the format (and therefore, the size) this attachment is in. + * @param offset the attachment's byte offset within a vertex. + */ + VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format, uint32_t offset) noexcept; + VertexAttachment() = delete; - uint32_t location; - uint32_t binding; - std::string name; - VertexFormat format; - uint32_t offset; + uint32_t inputLocation; + std::string name; + VertexAttachmentFormat format; + uint32_t offset; + }; + + struct VertexBinding{ + /** + * Describes all vertex input attachments _one_ buffer contains to create a vertex buffer binding. + * NOTE: multiple vertex layouts may contain various (mutually exclusive) vertex input attachments + * to form one complete vertex buffer binding! + * @param bindingLocation its entry in the buffers that make up the whole vertex buffer. + * @param attachments the vertex input attachments this specific buffer layout contains. + */ + VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept; + VertexBinding() = delete; + + uint32_t bindingLocation; + uint32_t stride; + std::vector<VertexAttachment> vertexAttachments; }; struct VertexLayout{ + /** + * Describes the complete layout of one vertex, e.g. all of the vertex input attachments used, + * and all of the buffer bindings that refer to the attachments (for when multiple buffers are used). + * @param bindings bindings the complete vertex buffer is comprised of. + */ VertexLayout() noexcept; - VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept; - std::unordered_map<uint32_t, VertexInputAttachment> attachmentMap; - uint32_t stride; + VertexLayout(const std::vector<VertexBinding> &bindings) noexcept; + + std::vector<VertexBinding> vertexBindings; }; } \ No newline at end of file diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 24687e846ff9eae3275de357331a825f0b4ed2c3..d687bbf60a03a59eee8c29f9b676f10cc7f2f2b3 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -8,7 +8,6 @@ #include <string> #include <vector> #include <cstdint> -#include <vkcv/VertexLayout.hpp> /* These macros define limits of the following structs. Implementations can * test against these limits when performing sanity checks. The main constraint @@ -53,6 +52,23 @@ enum PrimitiveMode { /* The indices in the index buffer can be of different bit width. */ enum IndexType { UINT32=0, UINT16=1, UINT8=2 }; +/* With these enums, 0 is reserved to signal uninitialized or invalid data. */ +enum class PrimitiveType : uint32_t { + UNDEFINED = 0, + POSITION = 1, + NORMAL = 2, + TEXCOORD_0 = 3 +}; +/* This struct describes one vertex attribute of a vertex buffer. */ +typedef struct { + PrimitiveType type; // POSITION, NORMAL, ... + uint32_t offset; // offset in bytes + uint32_t length; // length of ... in bytes + uint32_t stride; // stride in bytes + uint16_t componentType; // eg. 5126 for float + uint8_t componentCount; // eg. 3 for vec3 +} VertexAttribute; + typedef struct { // TODO not yet needed for the first (unlit) triangle } Material; @@ -71,7 +87,7 @@ typedef struct { } indexBuffer; struct { std::vector<uint8_t> data; // binary data of the vertex buffer - std::vector<VertexAttribute> attributes; + std::vector<VertexAttribute> attributes; // description of one } vertexBuffer; struct { float x, y, z; } min; // bounding box lower left struct { float x, y, z; } max; // bounding box upper right diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp index 2494793f3b4aff3dfa412ff9bbe27e4550ca8fe0..1b1605fb409ed3b8f6b4e26f8f37299852f6833b 100644 --- a/projects/cmd_sync_test/src/main.cpp +++ b/projects/cmd_sync_test/src/main.cpp @@ -65,7 +65,7 @@ int main(int argc, const char** argv) { auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; - std::sort(attributes.begin(), attributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) { + std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); }); @@ -89,34 +89,45 @@ int main(int argc, const char** argv) { vk::Format::eD32Sfloat ); - vkcv::PassConfig trianglePassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); + vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); - if (!trianglePass) { + if (!firstMeshPass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; } - vkcv::ShaderProgram triangleShaderProgram{}; - triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); - triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); + vkcv::ShaderProgram firstMeshProgram{}; + firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); - std::vector<vkcv::DescriptorBinding> descriptorBindings = { triangleShaderProgram.getReflectedDescriptors()[0] }; + /** + * TODO: + * Since the framework's vertex layout specification is now separate + * from that defined in the asset loader module, there needs to be a smarter way to translate the asset loader's + * specific layout into "our" uniform vertex layout spec. + * + * This is just a quick hack. + */ + + const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); + const vkcv::VertexBinding vertexBinding(0, vertexAttachments); + const vkcv::VertexLayout firstMeshLayout({vertexBinding}); + + std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[0] }; vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); - const vkcv::PipelineConfig trianglePipelineDefinition( - triangleShaderProgram, + const vkcv::PipelineConfig firstMeshPipelineConfig( + firstMeshProgram, windowWidth, windowHeight, - trianglePass, - attributes, + firstMeshPass, + {firstMeshLayout}, { core.getDescriptorSet(descriptorSet).layout }, true); - vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); - if (!trianglePipeline) { + if (!firstMeshPipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } @@ -169,8 +180,6 @@ int main(int argc, const char** argv) { vkcv::ShaderProgram shadowShader; shadowShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv"); shadowShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv"); - shadowShader.reflectShader(vkcv::ShaderStage::VERTEX); - shadowShader.reflectShader(vkcv::ShaderStage::FRAGMENT); const vk::Format shadowMapFormat = vk::Format::eD16Unorm; const std::vector<vkcv::AttachmentDescription> shadowAttachments = { @@ -185,8 +194,8 @@ int main(int argc, const char** argv) { shadowShader, shadowMapResolution, shadowMapResolution, - shadowPass, - attributes, + shadowPass, + {firstMeshLayout}, {}, false); const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); @@ -281,8 +290,8 @@ int main(int argc, const char** argv) { core.recordDrawcallsToCmdStream( cmdStream, - trianglePass, - trianglePipeline, + firstMeshPass, + firstMeshPipeline, pushConstantData, drawcalls, renderTargets); diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 15bb872ce21d9042a6e606e791daab54344d128d..d84adcd20425ff1e94738f71f1d9239528e3308b 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -74,41 +74,46 @@ int main(int argc, const char** argv) { vk::Format::eD32Sfloat ); - vkcv::PassConfig trianglePassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); + vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); - if (!trianglePass) { + if (!firstMeshPass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; } - vkcv::ShaderProgram triangleShaderProgram{}; - triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); - triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); - - auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; - - std::sort(attributes.begin(), attributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); + vkcv::ShaderProgram firstMeshProgram{}; + firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + + /** + * TODO: + * Since the framework's vertex layout specification is now separate + * from that defined in the asset loader module, there needs to be a smarter way to translate the asset loader's + * specific layout into "our" uniform vertex layout spec. + * + */ + + const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); + + const vkcv::VertexBinding vertexBinding(0, vertexAttachments); + const vkcv::VertexLayout firstMeshLayout({vertexBinding}); uint32_t setID = 0; - std::vector<vkcv::DescriptorBinding> descriptorBindings = { triangleShaderProgram.getReflectedDescriptors()[setID] }; + std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[setID] }; vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); - const vkcv::PipelineConfig trianglePipelineDefinition( - triangleShaderProgram, + const vkcv::PipelineConfig firstMeshPipelineConfig( + firstMeshProgram, UINT32_MAX, UINT32_MAX, - trianglePass, - mesh.vertexGroups[0].vertexBuffer.attributes, + firstMeshPass, + {firstMeshLayout}, { core.getDescriptorSet(descriptorSet).layout }, true); - vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); - if (!trianglePipeline) { + if (!firstMeshPipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } @@ -123,11 +128,22 @@ int main(int argc, const char** argv) { vkcv::SamplerAddressMode::REPEAT ); + auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; + + std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { + return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); + }); + + /* const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { - vkcv::VertexBufferBinding( mesh.vertexGroups[0].vertexBuffer.attributes[0].offset, vertexBuffer.getVulkanHandle() ), - vkcv::VertexBufferBinding( mesh.vertexGroups[0].vertexBuffer.attributes[1].offset, vertexBuffer.getVulkanHandle() ), - vkcv::VertexBufferBinding( mesh.vertexGroups[0].vertexBuffer.attributes[2].offset, vertexBuffer.getVulkanHandle() ) + vkcv::VertexBufferBinding( static_cast<vk::DeviceSize>(mesh.vertexGroups[0].vertexBuffer.attributes[0].offset), vertexBuffer.getVulkanHandle() ), + vkcv::VertexBufferBinding( static_cast<vk::DeviceSize>(mesh.vertexGroups[0].vertexBuffer.attributes[1].offset), vertexBuffer.getVulkanHandle() ), + vkcv::VertexBufferBinding( static_cast<vk::DeviceSize>(mesh.vertexGroups[0].vertexBuffer.attributes[2].offset), vertexBuffer.getVulkanHandle() ) }; + */ + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::VertexBufferBinding( 0, vertexBuffer.getVulkanHandle() ), + }; vkcv::DescriptorWrites setWrites; setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; @@ -175,8 +191,8 @@ int main(int argc, const char** argv) { core.recordDrawcallsToCmdStream( cmdStream, - trianglePass, - trianglePipeline, + firstMeshPass, + firstMeshPipeline, pushConstantData, { drawcall }, renderTargets); diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index ca10434bb9e486649411bcaac54c432744a914bb..b6b75bca82a8479f3d6b09577337503424ce7a83 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -96,8 +96,6 @@ 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, @@ -119,7 +117,6 @@ int main(int argc, const char** argv) { // Compute Pipeline vkcv::ShaderProgram computeShaderProgram{}; computeShaderProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("shaders/comp.spv")); - computeShaderProgram.reflectShader(vkcv::ShaderStage::COMPUTE); // take care, assuming shader has exactly one descriptor set vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]); diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp index ad8437ca2a6c07862f66485c74c89ccba0d69ebe..3bd2a68cb86f167afecc551dbd664dee8a63eb08 100644 --- a/src/vkcv/PipelineConfig.cpp +++ b/src/vkcv/PipelineConfig.cpp @@ -13,7 +13,7 @@ namespace vkcv { uint32_t width, uint32_t height, const PassHandle &passHandle, - const std::vector<VertexAttribute> &vertexAttributes, + const VertexLayout &vertexLayout, const std::vector<vk::DescriptorSetLayout> &descriptorLayouts, bool useDynamicViewport) : @@ -21,7 +21,7 @@ namespace vkcv { m_Height(height), m_Width(width), m_PassHandle(passHandle), - m_VertexAttributes(vertexAttributes), + m_VertexLayout(vertexLayout), m_DescriptorLayouts(descriptorLayouts), m_UseDynamicViewport(useDynamicViewport) {} diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 24ac7970d739d46ab7cecca7bb783bb0037c43cb..452414f24cc5717bc1dc5c4d4f558e8d37366bcc 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -19,23 +19,23 @@ namespace vkcv } // currently assuming default 32 bit formats, no lower precision or normalized variants supported - vk::Format vertexFormatToVulkanFormat(const VertexFormat format) { + vk::Format vertexFormatToVulkanFormat(const VertexAttachmentFormat format) { switch (format) { - case VertexFormat::FLOAT: + case VertexAttachmentFormat::FLOAT: return vk::Format::eR32Sfloat; - case VertexFormat::FLOAT2: + case VertexAttachmentFormat::FLOAT2: return vk::Format::eR32G32Sfloat; - case VertexFormat::FLOAT3: + case VertexAttachmentFormat::FLOAT3: return vk::Format::eR32G32B32Sfloat; - case VertexFormat::FLOAT4: + case VertexAttachmentFormat::FLOAT4: return vk::Format::eR32G32B32A32Sfloat; - case VertexFormat::INT: + case VertexAttachmentFormat::INT: return vk::Format::eR32Sint; - case VertexFormat::INT2: + case VertexAttachmentFormat::INT2: return vk::Format::eR32G32Sint; - case VertexFormat::INT3: + case VertexAttachmentFormat::INT3: return vk::Format::eR32G32B32Sint; - case VertexFormat::INT4: + case VertexAttachmentFormat::INT4: return vk::Format::eR32G32B32A32Sint; default: vkcv_log(LogLevel::WARNING, "Unknown vertex format"); @@ -94,24 +94,24 @@ namespace vkcv std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; - VertexLayout layout = config.m_ShaderProgram.getVertexLayout(); - std::unordered_map<uint32_t, VertexInputAttachment> attachments = layout.attachmentMap; + const VertexLayout &layout = config.m_VertexLayout; - for (int i = 0; i < attachments.size(); i++) { - VertexInputAttachment &attachment = attachments.at(i); - - uint32_t location = attachment.location; - uint32_t binding = i; - vk::Format vertexFormat = vertexFormatToVulkanFormat(attachment.format); - - //FIXME: hoping that order is the same and compatible: add explicit mapping and validation - const VertexAttribute attribute = config.m_VertexAttributes[i]; - - vertexAttributeDescriptions.emplace_back(location, binding, vertexFormatToVulkanFormat(attachment.format), 0); - vertexBindingDescriptions.emplace_back(vk::VertexInputBindingDescription( - binding, - attribute.stride + getFormatSize(attachment.format), - vk::VertexInputRate::eVertex)); + // iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer + for (const auto &vertexBinding : layout.vertexBindings) + { + vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, + vertexBinding.stride, + vk::VertexInputRate::eVertex); + + // iterate over the bindings' specified, mutually exclusive vertex input attachments that make up a vertex + for(const auto &vertexAttachment: vertexBinding.vertexAttachments) + { + vertexAttributeDescriptions.emplace_back(vertexAttachment.inputLocation, + vertexBinding.bindingLocation, + vertexFormatToVulkanFormat(vertexAttachment.format), + vertexAttachment.offset); + + } } // Handover Containers to PipelineVertexInputStateCreateIngo Struct diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 7c54c301d1301127273303128b0c10a9c2c53942..96a3c4a991549ada4a2a3d6581757244be5feddf 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -28,18 +28,18 @@ namespace vkcv { return buffer; } - VertexFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ + VertexAttachmentFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ switch (basetype) { case spirv_cross::SPIRType::Int: switch (vecsize) { case 1: - return VertexFormat::INT; + return VertexAttachmentFormat::INT; case 2: - return VertexFormat::INT2; + return VertexAttachmentFormat::INT2; case 3: - return VertexFormat::INT3; + return VertexAttachmentFormat::INT3; case 4: - return VertexFormat::INT4; + return VertexAttachmentFormat::INT4; default: break; } @@ -47,13 +47,13 @@ namespace vkcv { case spirv_cross::SPIRType::Float: switch (vecsize) { case 1: - return VertexFormat::FLOAT; + return VertexAttachmentFormat::FLOAT; case 2: - return VertexFormat::FLOAT2; + return VertexAttachmentFormat::FLOAT2; case 3: - return VertexFormat::FLOAT3; + return VertexAttachmentFormat::FLOAT3; case 4: - return VertexFormat::FLOAT4; + return VertexAttachmentFormat::FLOAT4; default: break; } @@ -63,12 +63,12 @@ namespace vkcv { } vkcv_log(LogLevel::WARNING, "Unknown vertex format"); - return VertexFormat::FLOAT; + return VertexAttachmentFormat::FLOAT; } ShaderProgram::ShaderProgram() noexcept : m_Shaders{}, - m_VertexLayout{}, + m_VertexAttachments{}, m_DescriptorSets{} {} @@ -85,6 +85,7 @@ namespace vkcv { } else { Shader shader{shaderCode, shaderStage}; m_Shaders.insert(std::make_pair(shaderStage, shader)); + reflectShader(shaderStage); return true; } } @@ -107,30 +108,39 @@ namespace vkcv { auto shaderCodeChar = m_Shaders.at(shaderStage).shaderCode; std::vector<uint32_t> shaderCode; - for (uint32_t i = 0; i < shaderCodeChar.size()/4; i++) { + 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(); //reflect vertex input - if (shaderStage == ShaderStage::VERTEX) { - 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, - u.name, - convertFormat(base_type.basetype, base_type.vecsize), - offset); - inputVec.push_back(input); - offset += base_type.vecsize * base_type.width / 8; - } - m_VertexLayout = VertexLayout(inputVec); + if (shaderStage == ShaderStage::VERTEX) + { + uint32_t attachment_offset = 0; + + // spirv-cross API (hopefully) returns the stage_inputs in order + for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) + { + // spirv-cross specific objects + auto& stage_input = resources.stage_inputs[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(stage_input.base_type_id); + + // vertex input location + const uint32_t attachment_loc = comp.get_decoration(stage_input.id, spv::DecorationLocation); + // vertex input name + const std::string attachment_name = stage_input.name; + // vertex input format (implies its size) + const VertexAttachmentFormat attachment_format = convertFormat(base_type.basetype, base_type.vecsize); + + m_VertexAttachments.emplace_back(attachment_loc, + attachment_name, + attachment_format, + attachment_offset); + + // calculate offset for the next vertex attachment input in line + attachment_offset += base_type.vecsize * base_type.width / 8; + } } //reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image) @@ -201,14 +211,18 @@ namespace vkcv { } } - const VertexLayout& ShaderProgram::getVertexLayout() const{ - return m_VertexLayout; + const std::vector<VertexAttachment> &ShaderProgram::getVertexAttachments() const + { + return m_VertexAttachments; } - const std::vector<std::vector<DescriptorBinding>> ShaderProgram::getReflectedDescriptors() const { + const std::vector<std::vector<DescriptorBinding>> &ShaderProgram::getReflectedDescriptors() const + { return m_DescriptorSets; } - size_t ShaderProgram::getPushConstantSize() const { + + size_t ShaderProgram::getPushConstantSize() const + { return m_pushConstantSize; } } diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp index 3c39ad0d39c4b458526ceed54422d320ee6d0e0d..23d0184023e09f8e3b1d6768800c4bb53f91a9fb 100644 --- a/src/vkcv/VertexLayout.cpp +++ b/src/vkcv/VertexLayout.cpp @@ -6,23 +6,23 @@ #include "vkcv/Logger.hpp" namespace vkcv { - uint32_t getFormatSize(VertexFormat format) { + uint32_t getFormatSize(VertexAttachmentFormat format) { switch (format) { - case VertexFormat::FLOAT: + case VertexAttachmentFormat::FLOAT: return 4; - case VertexFormat::FLOAT2: + case VertexAttachmentFormat::FLOAT2: return 8; - case VertexFormat::FLOAT3: + case VertexAttachmentFormat::FLOAT3: return 12; - case VertexFormat::FLOAT4: + case VertexAttachmentFormat::FLOAT4: return 16; - case VertexFormat::INT: + case VertexAttachmentFormat::INT: return 4; - case VertexFormat::INT2: + case VertexAttachmentFormat::INT2: return 8; - case VertexFormat::INT3: + case VertexAttachmentFormat::INT3: return 12; - case VertexFormat::INT4: + case VertexAttachmentFormat::INT4: return 16; default: vkcv_log(LogLevel::WARNING, "No format given"); @@ -30,25 +30,28 @@ namespace vkcv { } } - VertexInputAttachment::VertexInputAttachment(uint32_t location, uint32_t binding, std::string name, VertexFormat format, uint32_t offset) noexcept: - location{location}, - binding{binding}, + VertexAttachment::VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format, uint32_t offset) noexcept: + inputLocation{inputLocation}, name{name}, 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); - } + + VertexBinding::VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept : + bindingLocation{bindingLocation}, + stride{0}, + vertexAttachments{attachments} + { + for (const auto &attachment : attachments) + stride += getFormatSize(attachment.format); } + VertexLayout::VertexLayout() noexcept : + vertexBindings{} + {} + + VertexLayout::VertexLayout(const std::vector<VertexBinding> &bindings) noexcept : + vertexBindings{bindings} + {} } \ No newline at end of file