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..9f609b48472386cd7628ff40b5fa4b90bc91649a 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,51 @@ 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{
+        friend struct VertexBinding;
+        /**
+         * 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.
+         * The offset is calculated when a collection of attachments forms a binding, hence the friend declaration.
+         */
+        VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format) 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..426cc3a56033cea1e468c5612f4e32981d18f8c1 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,41 @@ 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] };
+    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
+    
+    std::vector<vkcv::VertexBinding> bindings;
+    for (size_t i = 0; i < vertexAttachments.size(); i++) {
+		bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
+    }
+    
+    const vkcv::VertexLayout firstMeshLayout (bindings);
+
+	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 +176,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 +190,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 +286,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 d585fa9606acb13795d299c1f4b47602c463a61a..d3be8c990ac581ec1b056926ce95c5ed9d5c2a31 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -74,43 +74,48 @@ 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"));
 	
 	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);
 	});
 
+    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
+	std::vector<vkcv::VertexBinding> bindings;
+	for (size_t i = 0; i < vertexAttachments.size(); i++) {
+		bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
+	}
+	
+	const vkcv::VertexLayout firstMeshLayout (bindings);
+
 	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);
-
-
-	if (!trianglePipeline) {
+	vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig);
+	
+	if (!firstMeshPipeline) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
@@ -126,10 +131,9 @@ int main(int argc, const char** argv) {
 	);
 
 	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>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
+			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
+			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
 
 	vkcv::DescriptorWrites setWrites;
 	setWrites.sampledImageWrites    = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
@@ -177,8 +181,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..81b7525b160374915b1918c30870b05e619a30a4 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 % vertexBinding.stride);
+
+            }
         }
 
         // Handover Containers to PipelineVertexInputStateCreateIngo Struct
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index a33fa7127dc6071439b5b350a440f995b1440ab7..aa945bb18e7cf04513b41510f1ea993a02e1f46d 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,31 @@ 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)
+		{
+			// 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);
+            }
 		}
 
 		//reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image)
@@ -202,14 +204,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..fa079a3264ae47b32461bda26485adb97b0be280 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,33 @@ 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) noexcept:
+            inputLocation{inputLocation},
             name{name},
             format{format},
-            offset{offset}
-            {}
-
-    VertexLayout::VertexLayout() noexcept :
-    stride{0},
-    attachmentMap()
+            offset{0}
     {}
 
-    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}
+    {
+        uint32_t offset = 0;
+        for (auto &attachment : vertexAttachments)
+        {
+            offset += getFormatSize(attachment.format);
+            attachment.offset = offset;
         }
+        stride = offset;
     }
 
+    VertexLayout::VertexLayout() noexcept :
+    vertexBindings{}
+    {}
+
+    VertexLayout::VertexLayout(const std::vector<VertexBinding> &bindings) noexcept :
+    vertexBindings{bindings}
+    {}
 }
\ No newline at end of file