diff --git a/config/Sources.cmake b/config/Sources.cmake
index 80fa3a09d163edf3277eb69f91e7b10e57b72c5d..b8c2dcbdf87a08225cebefa3197277715845e961 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -7,9 +7,45 @@ set(vkcv_sources
 		${vkcv_include}/vkcv/Core.hpp
 		${vkcv_source}/vkcv/Core.cpp
 
+		${vkcv_include}/vkcv/PassConfig.hpp
+		${vkcv_source}/vkcv/PassConfig.cpp
+
+		${vkcv_source}/vkcv/PassManager.hpp
+		${vkcv_source}/vkcv/PassManager.cpp
+
 		${vkcv_include}/vkcv/Handles.hpp
 		${vkcv_source}/vkcv/Handles.cpp
 
 		${vkcv_include}/vkcv/Window.hpp
 		${vkcv_source}/vkcv/Window.cpp
+
+		${vkcv_include}/vkcv/SwapChain.hpp
+		${vkcv_source}/vkcv/SwapChain.cpp
+
+		${vkcv_include}/vkcv/ShaderProgram.hpp
+		${vkcv_source}/vkcv/ShaderProgram.cpp
+
+		${vkcv_include}/vkcv/PipelineConfig.hpp
+		${vkcv_source}/vkcv/PipelineConfig.cpp
+
+		${vkcv_source}/vkcv/PipelineManager.hpp
+		${vkcv_source}/vkcv/PipelineManager.cpp
+        
+        ${vkcv_include}/vkcv/CommandResources.hpp
+        ${vkcv_source}/vkcv/CommandResources.cpp
+        
+        ${vkcv_include}/vkcv/SyncResources.hpp
+        ${vkcv_source}/vkcv/SyncResources.cpp
+        
+        ${vkcv_include}/vkcv/QueueManager.hpp
+        ${vkcv_source}/vkcv/QueueManager.cpp
+        
+        ${vkcv_source}/vkcv/Surface.hpp
+        ${vkcv_source}/vkcv/Surface.cpp
+        
+        ${vkcv_source}/vkcv/ImageLayoutTransitions.hpp
+        ${vkcv_source}/vkcv/ImageLayoutTransitions.cpp
+        
+        ${vkcv_source}/vkcv/Framebuffer.hpp
+        ${vkcv_source}/vkcv/Framebuffer.cpp
 )
diff --git a/include/vkcv/CommandResources.hpp b/include/vkcv/CommandResources.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..05e848294935bcf3642e1712072acf607d153611
--- /dev/null
+++ b/include/vkcv/CommandResources.hpp
@@ -0,0 +1,12 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	struct CommandResources {
+		vk::CommandPool commandPool;
+		vk::CommandBuffer commandBuffer;
+	};
+
+	CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex);
+	void destroyCommandResources(const vk::Device& device, const CommandResources& resources);
+}
\ No newline at end of file
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 7a7f3be7d61995462da1f65bd58af098b1dd1f4e..506e72f2363a791bff9aba59348ced3b6ac4c798 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -4,16 +4,27 @@
  * @brief Handling of global states regarding dependencies
  */
 
+#include <memory>
+
 #include <vulkan/vulkan.hpp>
 #include "vkcv/Context.hpp"
+#include "vkcv/SwapChain.hpp"
+#include "vkcv/Window.hpp"
+#include "vkcv/PassConfig.hpp"
 #include "vkcv/Handles.hpp"
+#include "vkcv/PipelineConfig.hpp"
+#include "CommandResources.hpp"
+#include "SyncResources.hpp"
+#include "vkcv/QueueManager.hpp"
 
 namespace vkcv
 {
     // TODO:
     class Buffer;
-    class Renderpass;
-    class Pipeline;
+
+    // forward declarations
+    class PassManager;
+    class PipelineManager;
 
     class Core final
     {
@@ -24,16 +35,32 @@ namespace vkcv
          *
          * @param context encapsulates various Vulkan objects
          */
-        explicit Core(Context &&context) noexcept;
+        Core(Context &&context, const Window &window, SwapChain swapChain,  std::vector<vk::ImageView> imageViews, 
+			const CommandResources& commandResources, const SyncResources& syncResources, const QueueManager &queues) noexcept;
         // explicit destruction of default constructor
         Core() = delete;
 
+		uint32_t acquireSwapchainImage();
+		void destroyTemporaryFramebuffers();
+
         Context m_Context;
+
+        SwapChain m_swapchain;
+        std::vector<vk::ImageView> m_swapchainImageViews;
+        const Window& m_window;
+
+        std::unique_ptr<PassManager> m_PassManager;
+        std::unique_ptr<PipelineManager> m_PipelineManager;
+		CommandResources m_CommandResources;
+		SyncResources m_SyncResources;
+		QueueManager m_QueueManager;
+		uint32_t m_currentSwapchainImageIndex;
+		std::vector<vk::Framebuffer> m_TemporaryFramebuffers;
     public:
         /**
          * Destructor of #Core destroys the Vulkan objects contained in the core's context.
          */
-        ~Core() noexcept = default;
+        ~Core() noexcept;
 
         /**
          * Copy-constructor of #Core is deleted!
@@ -78,23 +105,59 @@ namespace vkcv
              *
              * @param[in] applicationName Name of the application
              * @param[in] applicationVersion Version of the application
-             * @param[in] queueCount (optional) Amount of queues which is requested
              * @param[in] queueFlags (optional) Requested flags of queues
              * @param[in] instanceExtensions (optional) Requested instance extensions
              * @param[in] deviceExtensions (optional) Requested device extensions
              * @return New instance of #Context
              */
-        static Core create(const char *applicationName,
+        static Core create(const Window &window,
+                           const char *applicationName,
                            uint32_t applicationVersion,
-                           uint32_t queueCount,
                            std::vector<vk::QueueFlagBits> queueFlags    = {},
                            std::vector<const char*> instanceExtensions  = {},
                            std::vector<const char*> deviceExtensions    = {});
 
+        /**
+         * Creates a basic vulkan graphics pipeline using @p config from the pipeline config class and returns it using the @p handle.
+         * Fixed Functions for pipeline are set with standard values.
+         *
+         * @param config a pipeline config object from the pipeline config class
+         * @param handle a handle to return the created vulkan handle
+         * @return True if pipeline creation was successful, False if not
+         */
+        [[nodiscard]]
+        PipelineHandle createGraphicsPipeline(const PipelineConfig &config);
+
+        /**
+         * Creates a basic vulkan render pass using @p config from the render pass config class and returns it using the @p handle.
+         * Fixed Functions for pipeline are set with standard values.
+         *
+         * @param config a render pass config object from the render pass config class
+         * @param handle a handle to return the created vulkan handle
+         * @return True if render pass creation was successful, False if not
+         */
+        [[nodiscard]]
+        PassHandle createPass(const PassConfig &config);
+
         // TODO:
         BufferHandle createBuffer(const Buffer &buf);
-        PassHandle createRenderPass(const Renderpass &pass) ;
-        PipelineHandle createPipeline(const Pipeline &pipeline);
 
+		/**
+		 * @brief start recording command buffers and increment frame index
+		*/
+		void beginFrame();
+
+		/**
+		 * @brief render a beautiful triangle
+		*/
+		void renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle,
+			const int width, const int height);
+
+		/**
+		 * @brief end recording and present image
+		*/
+		void endFrame();
+
+		vk::Format getSwapchainImageFormat();
     };
 }
diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b8e80c67c2b70e3a0e6e2732b950ccaed38da3bf
--- /dev/null
+++ b/include/vkcv/PassConfig.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv
+{
+    enum class AttachmentLayout
+    {
+        UNDEFINED,
+        GENERAL,
+
+        COLOR_ATTACHMENT,
+        SHADER_READ_ONLY,
+
+        DEPTH_STENCIL_ATTACHMENT,
+        DEPTH_STENCIL_READ_ONLY,
+
+        TRANSFER_SRC,
+        TRANSFER_DST,
+
+        PRESENTATION
+    };
+
+    enum class AttachmentOperation
+    {
+        LOAD,
+        CLEAR,
+        STORE,
+        DONT_CARE
+    };
+
+    struct AttachmentDescription
+    {
+        AttachmentDescription() = delete;
+        AttachmentDescription(
+			AttachmentLayout initial,
+			AttachmentLayout in_pass,
+			AttachmentLayout final,
+			AttachmentOperation store_op,
+			AttachmentOperation load_op,
+			vk::Format format) noexcept;
+
+        AttachmentLayout layout_initial;
+        AttachmentLayout layout_in_pass;
+        AttachmentLayout layout_final;
+
+        AttachmentOperation store_operation;
+        AttachmentOperation load_operation;
+
+		vk::Format format;
+    };
+
+    struct PassConfig
+    {
+        PassConfig() = delete;
+        explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept;
+        std::vector<AttachmentDescription> attachments{};
+    };
+}
\ No newline at end of file
diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ad6be8d1979c8a89f7de9dbe24ff13b5f5bb3fa
--- /dev/null
+++ b/include/vkcv/PipelineConfig.hpp
@@ -0,0 +1,43 @@
+/**
+ * @authors Mara Vogt, Mark Mints
+ * @file src/vkcv/Pipeline.hpp
+ * @brief Pipeline class to handle shader stages
+ */
+
+#ifndef VKCV_PIPELINECONFIG_HPP
+#define VKCV_PIPELINECONFIG_HPP
+
+#include <vector>
+#include <cstdint>
+#include "vkcv/Handles.hpp"
+#include "ShaderProgram.hpp"
+
+namespace vkcv {
+
+    class PipelineConfig {
+
+    public:
+        /**
+         *  Default constructer is deleted!
+         */
+        PipelineConfig() = delete;
+
+        /**
+         *  Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the
+         *  dimensions of the application window @p width and @p height. A handle for the Render Pass is also needed, @p passHandle.
+         *
+         * @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
+         */
+        PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle);
+
+		ShaderProgram m_ShaderProgram;
+        uint32_t m_Height;
+        uint32_t m_Width;
+        PassHandle m_PassHandle;
+    };
+
+}
+#endif //VKCV_PIPELINECONFIG_HPP
diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1779fb669eadb67682bdca6610402ce0dcbacff5
--- /dev/null
+++ b/include/vkcv/QueueManager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	class QueueManager {
+	public:
+		static QueueManager create(vk::Device device,
+                            std::vector<std::pair<int, int>> &queuePairsGraphics,
+                            std::vector<std::pair<int, int>> &queuePairsCompute,
+                            std::vector<std::pair<int, int>> &queuePairsTransfer);
+
+        const vk::Queue &getPresentQueue() const;
+
+		const std::vector<vk::Queue> &getGraphicsQueues() const;
+
+        const std::vector<vk::Queue> &getComputeQueues() const;
+
+        const std::vector<vk::Queue> &getTransferQueues() const;
+
+        static void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice,
+                std::vector<float> &queuePriorities,
+                std::vector<vk::QueueFlagBits> &queueFlags,
+                std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos,
+                std::vector<std::pair<int, int>> &queuePairsGraphics,
+                std::vector<std::pair<int, int>> &queuePairsCompute,
+                std::vector<std::pair<int, int>> &queuePairsTransfer);
+
+    private:
+        vk::Queue m_presentQueue;
+        std::vector<vk::Queue> m_graphicsQueues;
+        std::vector<vk::Queue> m_computeQueues;
+        std::vector<vk::Queue> m_transferQueues;
+
+        QueueManager(std::vector<vk::Queue> graphicsQueues, std::vector<vk::Queue> computeQueues, std::vector<vk::Queue> transferQueues, vk::Queue presentQueue);
+	};
+}
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..172e906fca457c6245855639275054514958b69d
--- /dev/null
+++ b/include/vkcv/ShaderProgram.hpp
@@ -0,0 +1,60 @@
+#pragma once
+/**
+ * @authors Simeon Hermann, Leonie Franken
+ * @file src/vkcv/ShaderProgram.hpp
+ * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline
+ */
+
+#include <unordered_map>
+#include <fstream>
+#include <iostream>
+#include <filesystem>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+
+    enum class ShaderStage
+    {
+        VERTEX,
+        TESS_CONTROL,
+        TESS_EVAL,
+        GEOMETRY,
+        FRAGMENT,
+        COMPUTE
+    };
+
+    struct Shader
+    {
+        std::vector<char> shaderCode;
+        ShaderStage shaderStage;
+    };
+
+	class ShaderProgram
+	{
+    public:
+        ShaderProgram() noexcept; // ctor
+        ~ShaderProgram() = default; // dtor
+
+        /**
+        * Adds a shader into the shader program.
+        * The shader is only added if the shader program does not contain the particular shader stage already.
+        * Contains: (1) reading of the code, (2) creation of a shader module, (3) creation of a shader stage, (4) adding to the shader stage list, (5) destroying of the shader module
+        * @param[in] flag that signals the respective shaderStage (e.g. VK_SHADER_STAGE_VERTEX_BIT)
+        * @param[in] relative path to the shader code (e.g. "../../../../../shaders/vert.spv")
+        */
+        bool addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath);
+
+        /**
+        * Returns the shader program's shader of the specified shader.
+        * Needed for the transfer to the pipeline.
+        * @return Shader object consisting of buffer with shader code and shader stage enum
+        */
+        const Shader &getShader(ShaderStage shaderStage) const;
+
+        bool existsShader(ShaderStage shaderStage) const;
+
+	private:
+        std::unordered_map<ShaderStage, Shader> m_Shaders;
+
+	};
+}
diff --git a/include/vkcv/SwapChain.hpp b/include/vkcv/SwapChain.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1087d6364f6f811b741904d4e2b31fcfeb450901
--- /dev/null
+++ b/include/vkcv/SwapChain.hpp
@@ -0,0 +1,69 @@
+#pragma once
+#include "vulkan/vulkan.hpp"
+#include "Context.hpp"
+#include "vkcv/Window.hpp"
+
+namespace vkcv {
+    class SwapChain final {
+    private:
+
+        vk::SurfaceKHR m_surface;
+        vk::SwapchainKHR m_swapchain;
+        vk::SurfaceFormatKHR m_format;
+
+		uint32_t m_ImageCount;
+
+        /**
+         * Constructor of a SwapChain object
+         * glfw is not initialized in this class because ist must be sure that there exists a context first
+         * glfw is already initialized by the window class
+         * @param surface used by the swapchain
+         * @param swapchain to show images in the window
+         * @param format
+         */
+        SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount);
+
+    public:
+        SwapChain(const SwapChain &other) = default;
+        SwapChain(SwapChain &&other) = default;
+
+        /**
+         * @return The swapchain linked with the #SwapChain class
+         * @note The reference to our Swapchain variable is needed for the recreation step
+         */
+        [[nodiscard]]
+        const vk::SwapchainKHR& getSwapchain() const;
+
+        /**
+         * gets the current surface object
+         * @return current surface
+         */
+        [[nodiscard]]
+        vk::SurfaceKHR getSurface();
+        /**
+         * gets the current surface format
+         * @return gets the surface format
+         */
+        [[nodiscard]]
+        vk::SurfaceFormatKHR getSurfaceFormat();
+
+        /**
+         * creates a swap chain object out of the given window and the given context
+         * @param window a wrapper that represents a glfw window
+         * @param context of the application
+         * @return returns an object of swapChain
+         */
+        static SwapChain create(const Window &window, const Context &context, const vk::SurfaceKHR surface);
+
+        /**
+         * Destructor of SwapChain
+         */
+        virtual ~SwapChain();
+
+		/**
+		 * @return number of images in swapchain
+		*/
+		uint32_t getImageCount();
+    };
+
+}
diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4456594722a30f73128a864714bf4690b2902525
--- /dev/null
+++ b/include/vkcv/SyncResources.hpp
@@ -0,0 +1,13 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	struct SyncResources {
+		vk::Semaphore renderFinished;
+		vk::Fence swapchainImageAcquired;
+		vk::Fence presentFinished;
+	};
+
+	SyncResources createDefaultSyncResources(const vk::Device& device);
+	void destroySyncResources(const vk::Device& device, const SyncResources& resources);
+}
\ No newline at end of file
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index 080e55350ba82ae58da8afcc8758da3fda77f19f..ef5e35cc10a9983440d3d33f7d8dd93a6aef5199 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -4,19 +4,17 @@
  * @file src/vkcv/Window.hpp
  * @brief Window class to handle a basic rendering surface and input
  */
-
-#define GLFW_INCLUDE_VULKAN
 #include <GLFW/glfw3.h>
 
 #define NOMINMAX
 #include <algorithm>
 
 namespace vkcv {
-
     class Window final {
     private:
         GLFWwindow *m_window;
 
+
         /**
          *
          * @param GLFWwindow of the class
@@ -32,8 +30,7 @@ namespace vkcv {
          * @param[in] resizable resize ability of the window (optional)
          * @return Window class
          */
-        static Window create(const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
-
+        static Window create( const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
         /**
          * checks if the window is still open, or the close event was called
          * This function should be changed/removed later on
@@ -54,20 +51,6 @@ namespace vkcv {
         [[nodiscard]]
         GLFWwindow *getWindow() const;
 
-        /**
-         * gets the current window width
-         * @return int with window width
-         */
-        [[nodiscard]]
-        int getWidth() const;
-
-        /**
-         * gets the current window height
-         * @return int with window height
-         */
-        [[nodiscard]]
-        int getHeight() const;
-
         /**
          * Copy-operator of #Window is deleted!
          *
@@ -84,10 +67,25 @@ namespace vkcv {
          */
         Window &operator=(Window &&other) = default;
 
+        /**
+         * gets the window width
+         * @param window glfwWindow
+         * @return int with window width
+         */
+        [[nodiscard]]
+        int getWidth() const;
+
+        /**
+         * gets the window height
+         * @param window glfwWindow
+         * @return int with window height
+         */
+        [[nodiscard]]
+        int getHeight() const;
+
         /**
          * Destructor of #Window, terminates GLFW
          */
         virtual ~Window();
-
     };
 }
\ No newline at end of file
diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt
index 40e016a71cd3e20690f3ace9ec0260aa37449a0c..b7a2b52fcde1ec4481bb450da233aa319709c829 100644
--- a/projects/first_triangle/CMakeLists.txt
+++ b/projects/first_triangle/CMakeLists.txt
@@ -5,6 +5,9 @@ project(first_triangle)
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders/vert.spv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/shaders)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders/frag.spv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/shaders)
+
 # adding source files to the project
 add_executable(first_triangle src/main.cpp)
 
diff --git a/projects/first_triangle/shaders/compile.bat b/projects/first_triangle/shaders/compile.bat
new file mode 100644
index 0000000000000000000000000000000000000000..b4521235c40fe5fb163bab874560c2f219b7517f
--- /dev/null
+++ b/projects/first_triangle/shaders/compile.bat
@@ -0,0 +1,3 @@
+%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv
+%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv
+pause
\ No newline at end of file
diff --git a/projects/first_triangle/shaders/frag.spv b/projects/first_triangle/shaders/frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..cb13e606fc0041e24ff6a63c0ec7dcca466732aa
Binary files /dev/null and b/projects/first_triangle/shaders/frag.spv differ
diff --git a/projects/first_triangle/shaders/shader.frag b/projects/first_triangle/shaders/shader.frag
new file mode 100644
index 0000000000000000000000000000000000000000..d26446a73020111695aa2c86166205796dfa5e44
--- /dev/null
+++ b/projects/first_triangle/shaders/shader.frag
@@ -0,0 +1,9 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 fragColor;
+layout(location = 0) out vec4 outColor;
+
+void main()	{
+	outColor = vec4(fragColor, 1.0);
+}
\ No newline at end of file
diff --git a/projects/first_triangle/shaders/shader.vert b/projects/first_triangle/shaders/shader.vert
new file mode 100644
index 0000000000000000000000000000000000000000..1d278d5f41f803bb657a303dcc95ffcd2a92fd6e
--- /dev/null
+++ b/projects/first_triangle/shaders/shader.vert
@@ -0,0 +1,21 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) out vec3 fragColor;
+
+void main()	{
+    vec3 positions[3] = {
+        vec3(-0.5, 0.5, 0),
+        vec3( 0.5, 0.5, 0),
+        vec3(0, -0.5, 0)
+    };
+    
+    vec3 colors[3] = {
+        vec3(1, 0, 0),
+        vec3(0, 1, 0),
+        vec3(0, 0, 1)
+    };
+
+	gl_Position = vec4(positions[gl_VertexIndex], 1.0);
+	fragColor = colors[gl_VertexIndex];
+}
\ No newline at end of file
diff --git a/projects/first_triangle/shaders/vert.spv b/projects/first_triangle/shaders/vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..bd1e0e682c52e6e38a5f5aba4eeaf8e73a70d741
Binary files /dev/null and b/projects/first_triangle/shaders/vert.spv differ
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 0c981a25dc52db4363d2bc7835df3c3c9d7c49fa..fb9d764c6f130c3436f9b76c903e39bcd68d3ac6 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -1,20 +1,27 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
 #include <vkcv/Window.hpp>
+#include <vkcv/ShaderProgram.hpp>
 
 int main(int argc, const char** argv) {
     const char* applicationName = "First Triangle";
-	vkcv::Window window = vkcv::Window::create(
-            applicationName,
-        800,
-        600,
+
+	const int windowWidth = 800;
+	const int windowHeight = 600;
+    vkcv::Window window = vkcv::Window::create(
+		applicationName,
+		windowWidth,
+		windowHeight,
 		false
-	);
+    );
+
 	vkcv::Core core = vkcv::Core::create(
+            window,
             applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
-		20,
-		{vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer}
+            {vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
+		{},
+		{"VK_KHR_swapchain"}
 	);
 
 	const auto &context = core.getContext();
@@ -33,6 +40,36 @@ int main(int argc, const char** argv) {
 		default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl;
 	}
 
+    // an example attachment for passes that output to the window
+	const vkcv::AttachmentDescription present_color_attachment(
+		vkcv::AttachmentLayout::UNDEFINED,
+		vkcv::AttachmentLayout::COLOR_ATTACHMENT,
+		vkcv::AttachmentLayout::PRESENTATION,
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		core.getSwapchainImageFormat());
+
+	vkcv::PassConfig trianglePassDefinition({present_color_attachment});
+	vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition);
+
+	if (trianglePass.id == 0)
+	{
+		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+
+	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"));
+
+	const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass);
+	vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition);
+	if (trianglePipeline.id == 0)
+	{
+		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+
 	/*
 	 * BufferHandle triangleVertices = core.createBuffer(vertices);
 	 * BufferHandle triangleIndices = core.createBuffer(indices);
@@ -51,11 +88,9 @@ int main(int argc, const char** argv) {
 
 	while (window.isWindowOpen())
 	{
-        // core.beginFrame(); or something like that
-	    // core.execute(trianglePass, trianglePipeline, triangleModel);
-	    // core.endFrame(); or something like that
-
-	    // TBD: synchronization
+		core.beginFrame();
+	    core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight);
+	    core.endFrame();
 
 		window.pollEvents();
 	}
diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..451ec4f27b3bc68e6a787bb79d1dc12f59a086aa
--- /dev/null
+++ b/src/vkcv/CommandResources.cpp
@@ -0,0 +1,23 @@
+#include "vkcv/CommandResources.hpp"
+
+namespace vkcv {
+	CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex) {
+		CommandResources resources;
+		vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
+		vk::CommandPoolCreateInfo poolCreateInfo(flags, graphicFamilyIndex);
+		resources.commandPool = device.createCommandPool(poolCreateInfo, nullptr, {});
+
+		const int commandPoolCount = 1;
+		vk::CommandBufferAllocateInfo allocateInfo(resources.commandPool, vk::CommandBufferLevel::ePrimary, commandPoolCount);
+		const std::vector<vk::CommandBuffer> createdBuffers = device.allocateCommandBuffers(allocateInfo, {});
+		assert(createdBuffers.size() == 1);
+		resources.commandBuffer = createdBuffers[0];
+
+		return resources;
+	}
+
+	void destroyCommandResources(const vk::Device& device, const CommandResources& resources) {
+		device.freeCommandBuffers(resources.commandPool, resources.commandBuffer, {});
+		device.destroyCommandPool(resources.commandPool, {});
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 0c32f4e42d1a232a9b66d42e6a16f0c1eda06bbb..7e6d87ca71e294d4f7df9b6373a002e704c68325 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -1,13 +1,19 @@
 /**
- * @authors Sebastian Gaida
- * @file src/vkcv/CoreManager.cpp
+ * @authors Artur Wasmut
+ * @file src/vkcv/Core.cpp
  * @brief Handling of global states regarding dependencies
  */
 
 #include "vkcv/Core.hpp"
+#include "PassManager.hpp"
+#include "PipelineManager.hpp"
+#include "Surface.hpp"
+#include "ImageLayoutTransitions.hpp"
+#include "Framebuffer.hpp"
 
 namespace vkcv
 {
+
     /**
      * @brief The physical device is evaluated by three categories:
      * discrete GPU vs. integrated GPU, amount of queues and its abilities, and VRAM.physicalDevice.
@@ -76,53 +82,6 @@ namespace vkcv
         return phyDevice;
     }
 
-
-    /**
-     * @brief Creates a candidate list of queues that all meet the desired flags and then creates the maximum possible number
-     * of queues. If the number of desired queues is not sufficient, the remaining queues are created from the next
-     * candidate from the list.
-     * @param physicalDevice The physical device
-     * @param queueCount The amount of queues to be created
-     * @param qPriorities
-     * @param queueFlags The abilities which have to be supported by any created queue
-     * @return
-    */
-    std::vector<vk::DeviceQueueCreateInfo> getQueueCreateInfos(vk::PhysicalDevice& physicalDevice,
-                                                               uint32_t queueCount,
-                                                               std::vector<float> &qPriorities,
-                                                               std::vector<vk::QueueFlagBits>& queueFlags)
-    {
-        std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
-        std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
-        std::vector<vk::QueueFamilyProperties> qFamilyCandidates;
-
-        // search for queue families which support the desired queue flag bits
-        for (auto& qFamily : qFamilyProperties) {
-            bool supported = true;
-            for (auto qFlag : queueFlags) {
-                supported = supported && (static_cast<uint32_t>(qFlag & qFamily.queueFlags) != 0);
-            }
-            if (supported) {
-                qFamilyCandidates.push_back(qFamily);
-            }
-        }
-
-        uint32_t create = queueCount;
-        for (uint32_t i = 0; i < qFamilyCandidates.size() && create > 0; i++) {
-            const uint32_t maxCreatableQueues = std::min(create, qFamilyCandidates[i].queueCount);
-            vk::DeviceQueueCreateInfo qCreateInfo(
-                    vk::DeviceQueueCreateFlags(),
-                    i,
-                    maxCreatableQueues,
-                    qPriorities.data()
-            );
-            queueCreateInfos.push_back(qCreateInfo);
-            create -= maxCreatableQueues;
-        }
-
-        return queueCreateInfos;
-    }
-
     /**
      * @brief With the help of the reference "supported" all elements in "check" checked,
      * if they are supported by the physical device.
@@ -146,14 +105,26 @@ namespace vkcv
         return true;
     }
 
-    Core Core::create(const char *applicationName,
+
+    std::vector<const char*> getRequiredExtensions() {
+        uint32_t glfwExtensionCount = 0;
+        const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
+        std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
+
+#ifndef NDEBUG
+        extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+#endif
+
+        return extensions;
+    }
+
+    Core Core::create(const Window &window,
+                      const char *applicationName,
                       uint32_t applicationVersion,
-                      uint32_t queueCount,
                       std::vector<vk::QueueFlagBits> queueFlags,
                       std::vector<const char *> instanceExtensions,
                       std::vector<const char *> deviceExtensions)
     {
-
         // check for layer support
 
         const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties();
@@ -190,9 +161,9 @@ namespace vkcv
             throw std::runtime_error("The requested instance extensions are not supported!");
         }
 
-#ifndef NDEBUG
-        instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
-#endif
+        // for GLFW: get all required extensions
+        std::vector<const char*> requiredExtensions = getRequiredExtensions();
+        instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end());
 
         const vk::ApplicationInfo applicationInfo(
                 applicationName,
@@ -231,35 +202,75 @@ namespace vkcv
             throw std::runtime_error("The requested device extensions are not supported by the physical device!");
         }
 
-        //vector to define the queue priorities
-        std::vector<float> qPriorities;
-        qPriorities.resize(queueCount, 1.f); // all queues have the same priorities
+		const vk::SurfaceKHR surface = createSurface(window.getWindow(), instance, physicalDevice);
+		std::vector<vk::DeviceQueueCreateInfo> qCreateInfos;
 
         // create required queues
-        std::vector<vk::DeviceQueueCreateInfo> qCreateInfos = getQueueCreateInfos(physicalDevice, queueCount, qPriorities,queueFlags);
-
-        vk::DeviceCreateInfo deviceCreateInfo(
-                vk::DeviceCreateFlags(),
-                qCreateInfos.size(),
-                qCreateInfos.data(),
-                0,
-                nullptr,
-                deviceExtensions.size(),
-                deviceExtensions.data(),
-                nullptr		// Should our device use some features??? If yes: TODO
-        );
+        std::vector<float> qPriorities;
+        qPriorities.resize(queueFlags.size(), 1.f);
+        std::vector<std::pair<int, int>> queuePairsGraphics, queuePairsCompute, queuePairsTransfer;
+        QueueManager::queueCreateInfosQueueHandles(physicalDevice, qPriorities, queueFlags, qCreateInfos, queuePairsGraphics, queuePairsCompute, queuePairsTransfer);
+
+		vk::DeviceCreateInfo deviceCreateInfo(
+			vk::DeviceCreateFlags(),
+			qCreateInfos.size(),
+			qCreateInfos.data(),
+			0,
+			nullptr,
+			deviceExtensions.size(),
+			deviceExtensions.data(),
+			nullptr		// Should our device use some features??? If yes: TODO
+		);
 
 #ifndef NDEBUG
         deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
         deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
 #endif
 
+        // Ablauf
+        // qCreateInfos erstellen --> braucht das Device
+        // device erstellen
+        // jetzt koennen wir mit dem device die queues erstellen
 
         vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
-        // TODO: implement device.getQueue() to access the queues, if needed
-        Context context(instance, physicalDevice, device);
 
-        return Core(std::move(context));
+        QueueManager queueManager = QueueManager::create(device, queuePairsGraphics, queuePairsCompute, queuePairsTransfer);
+
+        Context context (instance, physicalDevice, device);
+
+        SwapChain swapChain = SwapChain::create(window, context, surface);
+
+        std::vector<vk::Image> swapChainImages = device.getSwapchainImagesKHR(swapChain.getSwapchain());
+        std::vector<vk::ImageView> imageViews;
+        imageViews.reserve( swapChainImages.size() );
+        //here can be swizzled with vk::ComponentSwizzle if needed
+        vk::ComponentMapping componentMapping(
+                vk::ComponentSwizzle::eR,
+                vk::ComponentSwizzle::eG,
+                vk::ComponentSwizzle::eB,
+                vk::ComponentSwizzle::eA );
+
+        vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 );
+
+        for ( auto image : swapChainImages )
+        {
+            vk::ImageViewCreateInfo imageViewCreateInfo(
+                    vk::ImageViewCreateFlags(),
+                    image,
+                    vk::ImageViewType::e2D,
+                    swapChain.getSurfaceFormat().format,
+                    componentMapping,
+                    subResourceRange
+            );
+
+            imageViews.push_back( device.createImageView( imageViewCreateInfo ));
+        }
+
+		const int graphicQueueFamilyIndex = queuePairsGraphics[0].first;
+		const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex);
+		const auto defaultSyncResources = createDefaultSyncResources(context.getDevice());
+
+        return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources, queueManager);
     }
 
     const Context &Core::getContext() const
@@ -267,7 +278,129 @@ namespace vkcv
         return m_Context;
     }
 
-    Core::Core(Context &&context) noexcept :
-            m_Context(std::move(context))
-    {}
+	Core::Core(Context &&context, const Window &window , SwapChain swapChain,  std::vector<vk::ImageView> imageViews, 
+		const CommandResources& commandResources, const SyncResources& syncResources, const QueueManager& queueManager) noexcept :
+            m_Context(std::move(context)),
+            m_window(window),
+            m_swapchain(swapChain),
+            m_swapchainImageViews(imageViews),
+            m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
+            m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)},
+            m_CommandResources(commandResources),
+            m_SyncResources(syncResources),
+            m_QueueManager(queueManager)
+	{}
+
+	Core::~Core() noexcept {
+		m_Context.getDevice().waitIdle();
+		for (auto image : m_swapchainImageViews) {
+			m_Context.m_Device.destroyImageView(image);
+		}
+
+		destroyCommandResources(m_Context.getDevice(), m_CommandResources);
+		destroySyncResources(m_Context.getDevice(), m_SyncResources);
+		destroyTemporaryFramebuffers();
+
+		m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain());
+		m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface());
+	}
+
+    PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config)
+    {
+        const vk::RenderPass &pass = m_PassManager->getVkPass(config.m_PassHandle);
+        return m_PipelineManager->createPipeline(config, pass);
+    }
+
+
+    PassHandle Core::createPass(const PassConfig &config)
+    {
+        return m_PassManager->createPass(config);
+    }
+
+	uint32_t Core::acquireSwapchainImage() {
+		uint32_t index;
+		m_Context.getDevice().acquireNextImageKHR(m_swapchain.getSwapchain(), 0, nullptr,
+			m_SyncResources.swapchainImageAcquired, &index, {});
+		const uint64_t timeoutPeriodNs = 1000;	// TODO: think if is adequate
+		const auto& result = m_Context.getDevice().waitForFences(m_SyncResources.swapchainImageAcquired, true, timeoutPeriodNs);
+		m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired);
+		
+		if (result == vk::Result::eTimeout) {
+			index = std::numeric_limits<uint32_t>::max();
+		}
+		
+		return index;
+	}
+
+	void Core::destroyTemporaryFramebuffers() {
+		for (const vk::Framebuffer f : m_TemporaryFramebuffers) {
+			m_Context.getDevice().destroyFramebuffer(f);
+		}
+		m_TemporaryFramebuffers.clear();
+	}
+
+	void Core::beginFrame() {
+		m_currentSwapchainImageIndex = acquireSwapchainImage();
+		
+		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+			std::cerr << "Drop frame!" << std::endl;
+ 			return;
+		}
+		
+		m_Context.getDevice().waitIdle();	// FIMXE: this is a sin against graphics programming, but its getting late - Alex
+		destroyTemporaryFramebuffers();
+		const vk::CommandBufferUsageFlags beginFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
+		const vk::CommandBufferBeginInfo beginInfos(beginFlags);
+		m_CommandResources.commandBuffer.begin(beginInfos);
+	}
+
+	void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, 
+		const int width, const int height) {
+		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+			return;
+		}
+  
+		const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
+		const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
+		const vk::ClearValue clearValues(clearColor);
+		const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
+		const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex];
+		const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView);
+		m_TemporaryFramebuffers.push_back(framebuffer);
+		const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
+		const vk::SubpassContents subpassContents = {};
+		m_CommandResources.commandBuffer.beginRenderPass(beginInfo, subpassContents, {});
+
+		const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
+		m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
+		m_CommandResources.commandBuffer.draw(3, 1, 0, 0, {});
+		m_CommandResources.commandBuffer.endRenderPass();
+	}
+
+	void Core::endFrame() {
+		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+			return;
+		}
+  
+		const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
+		const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex];
+
+		m_CommandResources.commandBuffer.end();
+		
+		const vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished);
+		m_QueueManager.getGraphicsQueues()[0].submit(submitInfo);
+
+		vk::Result presentResult;
+		const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
+		const vk::PresentInfoKHR presentInfo(1, &m_SyncResources.renderFinished, 1, &swapchain, 
+			&m_currentSwapchainImageIndex, &presentResult);
+        m_QueueManager.getPresentQueue().presentKHR(presentInfo);
+		if (presentResult != vk::Result::eSuccess) {
+			std::cout << "Error: swapchain present failed" << std::endl;
+		}
+	}
+
+	vk::Format Core::getSwapchainImageFormat() {
+		return m_swapchain.getSurfaceFormat().format;
+	}
 }
diff --git a/src/vkcv/Framebuffer.cpp b/src/vkcv/Framebuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b3e8000668e460d476b211984e9e12249f066c0
--- /dev/null
+++ b/src/vkcv/Framebuffer.cpp
@@ -0,0 +1,11 @@
+#include "Framebuffer.hpp"
+
+namespace vkcv {
+	vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass, 
+		const int width, const int height, const vk::ImageView imageView) {
+		const vk::FramebufferCreateFlags flags = {};
+		const uint32_t attachmentCount = 1;	// TODO: proper value
+		const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachmentCount, &imageView, width, height, 1);
+		return device.createFramebuffer(createInfo, nullptr, {});
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/Framebuffer.hpp b/src/vkcv/Framebuffer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d5d718adbde0c3f8eb8d97c539fb73f7771987f
--- /dev/null
+++ b/src/vkcv/Framebuffer.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv{
+	vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass,
+		const int width, const int height, const vk::ImageView imageView);
+}
\ No newline at end of file
diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b08819489c41c5cde3ceddbb0629a5d2ae3cd30
--- /dev/null
+++ b/src/vkcv/ImageLayoutTransitions.cpp
@@ -0,0 +1,24 @@
+#include "ImageLayoutTransitions.hpp"
+
+namespace vkcv {
+	void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image,
+		const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout) {
+
+		// TODO: proper src and dst masks
+		const vk::PipelineStageFlags srcStageMask = vk::PipelineStageFlagBits::eAllCommands;
+		const vk::PipelineStageFlags dstStageMask = vk::PipelineStageFlagBits::eAllCommands;
+		const vk::DependencyFlags dependecyFlags = {};
+
+		// TODO: proper src and dst masks
+		const vk::AccessFlags srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
+		const vk::AccessFlags dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
+
+		// TODO: proper aspect flags
+		const vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor;
+
+		const vk::ImageSubresourceRange subresourceRange(aspectFlags, 0, 1, 0, 1);
+		vk::ImageMemoryBarrier imageBarrier(srcAccessMask, dstAccessMask, oldLayout, newLayout, 0, 0, image, subresourceRange);
+
+		cmdBuffer.pipelineBarrier(srcStageMask, dstStageMask, dependecyFlags, 0, nullptr, 0, nullptr, 1, &imageBarrier, {});
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/ImageLayoutTransitions.hpp b/src/vkcv/ImageLayoutTransitions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3dbfbdf6690a0683b30a96f400e7e4b6ec25c379
--- /dev/null
+++ b/src/vkcv/ImageLayoutTransitions.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image,
+		const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout);
+}
\ No newline at end of file
diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef07d3ee8d6170ae893cd055eefcc971cd1b87a3
--- /dev/null
+++ b/src/vkcv/PassConfig.cpp
@@ -0,0 +1,25 @@
+#include "vkcv/PassConfig.hpp"
+
+#include <utility>
+
+namespace vkcv
+{
+    AttachmentDescription::AttachmentDescription(
+		AttachmentLayout initial,
+		AttachmentLayout in_pass,
+		AttachmentLayout final,
+		AttachmentOperation store_op,
+		AttachmentOperation load_op,
+		vk::Format format) noexcept :
+	layout_initial{initial},
+	layout_in_pass{in_pass},
+	layout_final{final},
+	store_operation{store_op},
+	load_operation{load_op},
+	format(format)
+    {};
+
+    PassConfig::PassConfig(std::vector<AttachmentDescription> attachments) noexcept :
+    attachments{std::move(attachments)}
+    {}
+}
\ No newline at end of file
diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..820bf3ff8a84edb9d070c22c60327cf7cb661ee7
--- /dev/null
+++ b/src/vkcv/PassManager.cpp
@@ -0,0 +1,135 @@
+#include "PassManager.hpp"
+
+namespace vkcv
+{
+    static vk::ImageLayout getVkLayoutFromAttachLayout(AttachmentLayout layout)
+    {
+        switch(layout)
+        {
+            case AttachmentLayout::GENERAL:
+                return vk::ImageLayout::eGeneral;
+            case AttachmentLayout::COLOR_ATTACHMENT:
+                return vk::ImageLayout::eColorAttachmentOptimal;
+            case AttachmentLayout::SHADER_READ_ONLY:
+                return vk::ImageLayout::eShaderReadOnlyOptimal;
+            case AttachmentLayout::DEPTH_STENCIL_ATTACHMENT:
+                return vk::ImageLayout::eDepthStencilAttachmentOptimal;
+            case AttachmentLayout::DEPTH_STENCIL_READ_ONLY:
+                return vk::ImageLayout::eDepthStencilReadOnlyOptimal;
+            case AttachmentLayout::PRESENTATION:
+                return vk::ImageLayout::ePresentSrcKHR;
+            default:
+                return vk::ImageLayout::eUndefined;
+        }
+    }
+
+    static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op)
+    {
+        switch(op)
+        {
+            case AttachmentOperation::STORE:
+                return vk::AttachmentStoreOp::eStore;
+            default:
+                return vk::AttachmentStoreOp::eDontCare;
+        }
+    }
+
+    static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op)
+    {
+        switch(op)
+        {
+            case AttachmentOperation::LOAD:
+                return vk::AttachmentLoadOp::eLoad;
+            case AttachmentOperation::CLEAR:
+                return vk::AttachmentLoadOp::eClear;
+            default:
+                return vk::AttachmentLoadOp::eDontCare;
+        }
+    }
+
+    PassManager::PassManager(vk::Device device) noexcept :
+    m_Device{device},
+    m_RenderPasses{},
+    m_NextPassId{1}
+    {}
+
+    PassManager::~PassManager() noexcept
+    {
+        for(const auto &pass : m_RenderPasses)
+            m_Device.destroy(pass);
+
+        m_RenderPasses.clear();
+        m_NextPassId = 1;
+    }
+
+    PassHandle PassManager::createPass(const PassConfig &config)
+    {
+        // description of all {color, input, depth/stencil} attachments of the render pass
+        std::vector<vk::AttachmentDescription> attachmentDescriptions{};
+
+        // individual references to color attachments (of a subpass)
+        std::vector<vk::AttachmentReference> colorAttachmentReferences{};
+        // individual reference to depth attachment (of a subpass)
+        vk::AttachmentReference depthAttachmentReference{};
+        vk::AttachmentReference *pDepthAttachment = nullptr;	//stays nullptr if no depth attachment used
+
+        for (uint32_t i = 0; i < config.attachments.size(); i++)
+        {
+            // TODO: Renderpass struct should hold proper format information
+            vk::Format format = config.attachments[i].format;
+
+            if (config.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT)
+            {
+                depthAttachmentReference.attachment = i;
+                depthAttachmentReference.layout = getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass);
+                pDepthAttachment = &depthAttachmentReference;
+            }
+            else
+            {
+                vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass));
+                colorAttachmentReferences.push_back(attachmentRef);
+            }
+
+            vk::AttachmentDescription attachmentDesc({},
+                                                     format,
+                                                     vk::SampleCountFlagBits::e1,
+                                                     getVKLoadOpFromAttachOp(config.attachments[i].load_operation),
+                                                     getVkStoreOpFromAttachOp(config.attachments[i].store_operation),
+                                                     vk::AttachmentLoadOp::eDontCare,
+                                                     vk::AttachmentStoreOp::eDontCare,
+                                                     getVkLayoutFromAttachLayout(config.attachments[i].layout_initial),
+                                                     getVkLayoutFromAttachLayout(config.attachments[i].layout_final));
+            attachmentDescriptions.push_back(attachmentDesc);
+        }
+        vk::SubpassDescription subpassDescription({},
+                                                  vk::PipelineBindPoint::eGraphics,
+                                                  0,
+                                                  {},
+                                                  static_cast<uint32_t>(colorAttachmentReferences.size()),
+                                                  colorAttachmentReferences.data(),
+                                                  {},
+                                                  pDepthAttachment,
+                                                  0,
+                                                  {});
+
+        vk::RenderPassCreateInfo passInfo({},
+                                          static_cast<uint32_t>(attachmentDescriptions.size()),
+                                          attachmentDescriptions.data(),
+                                          1,
+                                          &subpassDescription,
+                                          0,
+                                          {});
+
+        vk::RenderPass vkObject{nullptr};
+        if(m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess)
+            return PassHandle{0};
+
+        m_RenderPasses.push_back(vkObject);
+            return PassHandle{m_NextPassId++};
+    }
+
+    vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const
+    {
+        return m_RenderPasses[handle.id - 1];
+    }
+}
diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6be2cb13d8d24bdb9759f8878917f99e31afbec
--- /dev/null
+++ b/src/vkcv/PassManager.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <vulkan/vulkan.hpp>
+#include <vector>
+#include "vkcv/Handles.hpp"
+#include "vkcv/PassConfig.hpp"
+
+namespace vkcv
+{
+    class PassManager
+    {
+    private:
+        vk::Device m_Device;
+        std::vector<vk::RenderPass> m_RenderPasses;
+        uint64_t m_NextPassId;
+    public:
+        PassManager() = delete; // no default ctor
+        explicit PassManager(vk::Device device) noexcept; // ctor
+        ~PassManager() noexcept; // dtor
+
+        PassManager(const PassManager &other) = delete; // copy-ctor
+        PassManager(PassManager &&other) = delete; // move-ctor;
+
+        PassManager & operator=(const PassManager &other) = delete; // copy-assign op
+        PassManager & operator=(PassManager &&other) = delete; // move-assign op
+
+        PassHandle createPass(const PassConfig &config);
+
+        [[nodiscard]]
+        vk::RenderPass getVkPass(const PassHandle &handle) const;
+    };
+}
diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2b1415f10188f082d8eca576c2143e82f99e7fa
--- /dev/null
+++ b/src/vkcv/PipelineConfig.cpp
@@ -0,0 +1,17 @@
+/**
+ * @authors Mara Vogt, Mark Mints
+ * @file src/vkcv/Pipeline.cpp
+ * @brief Pipeline class to handle shader stages
+ */
+
+#include "vkcv/PipelineConfig.hpp"
+
+namespace vkcv {
+
+    PipelineConfig::PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle):
+		m_ShaderProgram(shaderProgram),
+		m_Height(height),
+		m_Width(width),
+		m_PassHandle(passHandle)
+		{}
+}
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6d6beea463f17c86d958c9080f34eb61e9076c2
--- /dev/null
+++ b/src/vkcv/PipelineManager.cpp
@@ -0,0 +1,206 @@
+#include "PipelineManager.hpp"
+
+namespace vkcv
+{
+
+    PipelineManager::PipelineManager(vk::Device device) noexcept :
+    m_Device{device},
+    m_Pipelines{},
+    m_PipelineLayouts{},
+    m_NextPipelineId{1}
+    {}
+
+    PipelineManager::~PipelineManager() noexcept
+    {
+        for(const auto &pipeline: m_Pipelines)
+            m_Device.destroy(pipeline);
+
+        for(const auto &layout : m_PipelineLayouts)
+            m_Device.destroy(layout);
+
+        m_Pipelines.clear();
+        m_PipelineLayouts.clear();
+        m_NextPipelineId = 1;
+    }
+
+    PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, const vk::RenderPass &pass)
+    {
+        const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX);
+        const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT);
+        if (!(existsVertexShader && existsFragmentShader))
+        {
+            std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl;
+            return PipelineHandle{0};
+        }
+
+        // vertex shader stage
+        std::vector<char> vertexCode = config.m_ShaderProgram.getShader(ShaderStage::VERTEX).shaderCode;
+        vk::ShaderModuleCreateInfo vertexModuleInfo({}, vertexCode.size(), reinterpret_cast<uint32_t*>(vertexCode.data()));
+        vk::ShaderModule vertexModule{};
+        if (m_Device.createShaderModule(&vertexModuleInfo, nullptr, &vertexModule) != vk::Result::eSuccess)
+            return PipelineHandle{0};
+
+        vk::PipelineShaderStageCreateInfo pipelineVertexShaderStageInfo(
+                {},
+                vk::ShaderStageFlagBits::eVertex,
+                vertexModule,
+                "main",
+                nullptr
+        );
+
+        // fragment shader stage
+        std::vector<char> fragCode = config.m_ShaderProgram.getShader(ShaderStage::FRAGMENT).shaderCode;
+        vk::ShaderModuleCreateInfo fragmentModuleInfo({}, fragCode.size(), reinterpret_cast<uint32_t*>(fragCode.data()));
+        vk::ShaderModule fragmentModule{};
+        if (m_Device.createShaderModule(&fragmentModuleInfo, nullptr, &fragmentModule) != vk::Result::eSuccess)
+        {
+            m_Device.destroy(vertexModule);
+            return PipelineHandle{0};
+        }
+
+        vk::PipelineShaderStageCreateInfo pipelineFragmentShaderStageInfo(
+                {},
+                vk::ShaderStageFlagBits::eFragment,
+                fragmentModule,
+                "main",
+                nullptr
+        );
+
+        // vertex input state
+        vk::VertexInputBindingDescription vertexInputBindingDescription(0, 12, vk::VertexInputRate::eVertex);
+        vk::VertexInputAttributeDescription vertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0);
+
+        vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo(
+                {},			// no vertex input until vertex buffer is implemented
+                0,			// 1,
+                nullptr,	// &vertexInputBindingDescription,
+                0,			// 1,
+                nullptr		// &vertexInputAttributeDescription
+        );
+
+        // input assembly state
+        vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
+                {},
+                vk::PrimitiveTopology::eTriangleList,
+                false
+        );
+
+        // viewport state
+        vk::Viewport viewport(0.f, 0.f, static_cast<float>(config.m_Width), static_cast<float>(config.m_Height), 0.f, 1.f);
+        vk::Rect2D scissor({ 0,0 }, { config.m_Width, config.m_Height });
+        vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo({}, 1, &viewport, 1, &scissor);
+
+        // rasterization state
+        vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
+                {},
+                false,
+                false,
+                vk::PolygonMode::eFill,
+                vk::CullModeFlagBits::eNone,
+                vk::FrontFace::eCounterClockwise,
+                false,
+                0.f,
+                0.f,
+                0.f,
+                1.f
+        );
+
+        // multisample state
+        vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
+                {},
+                vk::SampleCountFlagBits::e1,
+                false,
+                0.f,
+                nullptr,
+                false,
+                false
+        );
+
+        // color blend state
+        vk::ColorComponentFlags colorWriteMask(VK_COLOR_COMPONENT_R_BIT |
+                                               VK_COLOR_COMPONENT_G_BIT |
+                                               VK_COLOR_COMPONENT_B_BIT |
+                                               VK_COLOR_COMPONENT_A_BIT);
+        vk::PipelineColorBlendAttachmentState colorBlendAttachmentState(
+                false,
+                vk::BlendFactor::eOne,
+                vk::BlendFactor::eOne,
+                vk::BlendOp::eAdd,
+                vk::BlendFactor::eOne,
+                vk::BlendFactor::eOne,
+                vk::BlendOp::eAdd,
+                colorWriteMask
+        );
+        vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
+                {},
+                false,
+                vk::LogicOp::eClear,
+                1,	//TODO: hardcoded to one
+                &colorBlendAttachmentState,
+                { 1.f,1.f,1.f,1.f }
+        );
+
+        // pipeline layout
+        vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo(
+                {},
+                0,
+                {},
+                0,
+                {}
+        );
+        vk::PipelineLayout vkPipelineLayout{};
+        if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess)
+        {
+            m_Device.destroy(vertexModule);
+            m_Device.destroy(fragmentModule);
+            return PipelineHandle{0};
+        }
+
+        // graphics pipeline create
+        std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo };
+        vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo(
+                {},
+                static_cast<uint32_t>(shaderStages.size()),
+                shaderStages.data(),
+                &pipelineVertexInputStateCreateInfo,
+                &pipelineInputAssemblyStateCreateInfo,
+                nullptr,
+                &pipelineViewportStateCreateInfo,
+                &pipelineRasterizationStateCreateInfo,
+                &pipelineMultisampleStateCreateInfo,
+                nullptr,
+                &pipelineColorBlendStateCreateInfo,
+                nullptr,
+                vkPipelineLayout,
+                pass,
+                0,
+                {},
+                0
+        );
+
+        vk::Pipeline vkPipeline{};
+        if (m_Device.createGraphicsPipelines(nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) != vk::Result::eSuccess)
+        {
+            m_Device.destroy(vertexModule);
+            m_Device.destroy(fragmentModule);
+            return PipelineHandle{0};
+        }
+
+        m_Device.destroy(vertexModule);
+        m_Device.destroy(fragmentModule);
+
+        m_Pipelines.push_back(vkPipeline);
+        m_PipelineLayouts.push_back(vkPipelineLayout);
+        return PipelineHandle{m_NextPipelineId++};
+    }
+
+    vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const
+    {
+        return m_Pipelines.at(handle.id -1);
+    }
+
+    vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const
+    {
+        return m_PipelineLayouts.at(handle.id - 1);
+    }
+}
\ No newline at end of file
diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5c0948efa13a4021f424cc576f1403a1ec26ebe
--- /dev/null
+++ b/src/vkcv/PipelineManager.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <vulkan/vulkan.hpp>
+#include <vector>
+#include "vkcv/Handles.hpp"
+#include "vkcv/PipelineConfig.hpp"
+
+namespace vkcv
+{
+    class PipelineManager
+    {
+    private:
+        vk::Device m_Device;
+        std::vector<vk::Pipeline> m_Pipelines;
+        std::vector<vk::PipelineLayout> m_PipelineLayouts;
+        uint64_t m_NextPipelineId;
+    public:
+        PipelineManager() = delete; // no default ctor
+        explicit PipelineManager(vk::Device device) noexcept; // ctor
+        ~PipelineManager() noexcept; // dtor
+
+        PipelineManager(const PipelineManager &other) = delete; // copy-ctor
+        PipelineManager(PipelineManager &&other) = delete; // move-ctor;
+
+        PipelineManager & operator=(const PipelineManager &other) = delete; // copy-assign op
+        PipelineManager & operator=(PipelineManager &&other) = delete; // move-assign op
+
+        PipelineHandle createPipeline(const PipelineConfig &config, const vk::RenderPass &pass);
+
+        [[nodiscard]]
+        vk::Pipeline getVkPipeline(const PipelineHandle &handle) const;
+        [[nodiscard]]
+        vk::PipelineLayout getVkPipelineLayout(const PipelineHandle &handle) const;
+    };
+}
diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6c9dda42e1fdfacdde8e7174307cbbcd38ff24d
--- /dev/null
+++ b/src/vkcv/QueueManager.cpp
@@ -0,0 +1,189 @@
+#include "vkcv/QueueManager.hpp"
+#include <unordered_set>
+
+namespace vkcv {
+
+    /**
+     * Given the @p physicalDevice and the @p queuePriorities, the @p queueCreateInfos are computed. First, the requested
+     * queues are sorted by priority depending on the availability of queues in the queue families of the given
+     * @p physicalDevice. Then check, if all requested queues are creatable. If so, the @p queueCreateInfos will be computed.
+     * Furthermore, lists of index pairs (queueFamilyIndex, queueIndex) for later referencing of the separate queues will
+     * be computed.
+     * @param[in] physicalDevice The physical device
+     * @param[in] queuePriorities The queue priorities used for the computation of @p queueCreateInfos
+     * @param[in] queueFlags The queue flags requesting the queues
+     * @param[in,out] queueCreateInfos The queue create info structures to be created
+     * @param[in,out] queuePairsGraphics The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
+     *      vk::QueueFlagBits::eGraphics
+     * @param[in,out] queuePairsCompute The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
+     *      vk::QueueFlagBits::eCompute
+     * @param[in,out] queuePairsTransfer The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
+     *      vk::QueueFlagBits::eTransfer
+     * @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due to insufficient availability.
+     */
+    void QueueManager::queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice,
+                                      std::vector<float> &queuePriorities,
+                                      std::vector<vk::QueueFlagBits> &queueFlags,
+                                      std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos,
+                                      std::vector<std::pair<int, int>> &queuePairsGraphics,
+                                      std::vector<std::pair<int, int>> &queuePairsCompute,
+                                      std::vector<std::pair<int, int>> &queuePairsTransfer)
+    {
+        queueCreateInfos = {};
+        queuePairsGraphics = {};
+        queuePairsCompute = {};
+        queuePairsTransfer = {};
+        std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+
+        //check priorities of flags -> the lower prioCount the higher the priority
+        std::vector<int> prios;
+        for(auto flag: queueFlags) {
+            int prioCount = 0;
+            for (int i = 0; i < qFamilyProperties.size(); i++) {
+                prioCount += (static_cast<uint32_t>(flag & qFamilyProperties[i].queueFlags) != 0) * qFamilyProperties[i].queueCount;
+            }
+            prios.push_back(prioCount);
+        }
+        //resort flags with heighest priority before allocating the queues
+        std::vector<vk::QueueFlagBits> newFlags;
+        for(int i = 0; i < prios.size(); i++) {
+            auto minElem = std::min_element(prios.begin(), prios.end());
+            int index = minElem - prios.begin();
+            newFlags.push_back(queueFlags[index]);
+            prios[index] = std::numeric_limits<int>::max();
+        }
+
+        // create requested queues and check if more requested queues are supported
+        // herefore: create vector that updates available queues in each queue family
+        // structure: [qFamily_0, ..., qFamily_n] where
+        // - qFamily_i = [GraphicsCount, ComputeCount, TransferCount], 0 <= i <= n
+        std::vector<std::vector<int>> queueFamilyStatus, initialQueueFamilyStatus;
+
+        for (auto qFamily : qFamilyProperties) {
+            int graphicsCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0) * qFamily.queueCount;
+            int computeCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0) * qFamily.queueCount;
+            int transferCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0) * qFamily.queueCount;
+            queueFamilyStatus.push_back({graphicsCount, computeCount, transferCount});
+        }
+
+        initialQueueFamilyStatus = queueFamilyStatus;
+        // check if every queue with the specified queue flag can be created
+        // this automatically checks for queue flag support!
+        for (auto qFlag : newFlags) {
+            bool found;
+            switch (qFlag) {
+                case vk::QueueFlagBits::eGraphics:
+                    found = false;
+                    for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
+                        if (queueFamilyStatus[i][0] > 0) {
+                            queuePairsGraphics.push_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]));
+                            queueFamilyStatus[i][0]--;
+                            queueFamilyStatus[i][1]--;
+                            queueFamilyStatus[i][2]--;
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        throw std::runtime_error("Too many graphics queues were requested than being available!");
+                    }
+                    break;
+                case vk::QueueFlagBits::eCompute:
+                    found = false;
+                    for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
+                        if (queueFamilyStatus[i][1] > 0) {
+                            queuePairsCompute.push_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1]));
+                            queueFamilyStatus[i][0]--;
+                            queueFamilyStatus[i][1]--;
+                            queueFamilyStatus[i][2]--;
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        throw std::runtime_error("Too many compute queues were requested than being available!");
+                    }
+                    break;
+                case vk::QueueFlagBits::eTransfer:
+                    found = false;
+                    for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
+                        if (queueFamilyStatus[i][2] > 0) {
+                            queuePairsTransfer.push_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2]));
+                            queueFamilyStatus[i][0]--;
+                            queueFamilyStatus[i][1]--;
+                            queueFamilyStatus[i][2]--;
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        throw std::runtime_error("Too many transfer queues were requested than being available!");
+                    }
+                    break;
+                default:
+                    throw std::runtime_error("Invalid input for queue flag bits. Valid inputs are 'vk::QueueFlagBits::eGraphics', 'vk::QueueFlagBits::eCompute' and 'vk::QueueFlagBits::eTransfer'.");
+            }
+        }
+
+        // create all requested queues
+        for (int i = 0; i < qFamilyProperties.size(); i++) {
+            uint32_t create = std::abs(initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]);
+            if (create > 0) {
+                vk::DeviceQueueCreateInfo qCreateInfo(
+                        vk::DeviceQueueCreateFlags(),
+                        i,
+                        create,
+                        queuePriorities.data()
+                );
+                queueCreateInfos.push_back(qCreateInfo);
+            }
+        }
+    }
+
+    /**
+     * Computes the queue handles from @p queuePairs
+     * @param device The device
+     * @param queuePairs The queuePairs that were created separately for each queue type (e.g., vk::QueueFlagBits::eGraphics)
+     * @return An array of queue handles based on the @p queuePairs
+     */
+    std::vector<vk::Queue> getQueueHandles(const vk::Device device, const std::vector<std::pair<int, int>> queuePairs) {
+        std::vector<vk::Queue> queueHandles;
+        for (auto q : queuePairs) {
+            int queueFamilyIndex = q.first; // the queueIndex of the queue family
+            int queueIndex = q.second;   // the queueIndex within a queue family
+            queueHandles.push_back(device.getQueue(queueFamilyIndex, queueIndex));
+        }
+        return queueHandles;
+    }
+
+
+    QueueManager QueueManager::create(vk::Device device,
+                                      std::vector<std::pair<int, int>> &queuePairsGraphics,
+                                      std::vector<std::pair<int, int>> &queuePairsCompute,
+                                      std::vector<std::pair<int, int>> &queuePairsTransfer) {
+
+        std::vector<vk::Queue> graphicsQueues = getQueueHandles(device, queuePairsGraphics);
+        std::vector<vk::Queue> computeQueues = getQueueHandles(device, queuePairsCompute );
+        std::vector<vk::Queue> transferQueues = getQueueHandles(device, queuePairsTransfer);
+
+    return QueueManager( graphicsQueues, computeQueues, transferQueues, graphicsQueues[0]);
+	}
+
+	QueueManager::QueueManager(std::vector<vk::Queue> graphicsQueues, std::vector<vk::Queue> computeQueues, std::vector<vk::Queue> transferQueues,  vk::Queue presentQueue)
+	: m_graphicsQueues(graphicsQueues), m_computeQueues(computeQueues), m_transferQueues(transferQueues), m_presentQueue(presentQueue)
+    {}
+
+    const vk::Queue &QueueManager::getPresentQueue() const {
+        return m_presentQueue;
+    }
+
+    const std::vector<vk::Queue> &QueueManager::getGraphicsQueues() const {
+        return m_graphicsQueues;
+    }
+
+    const std::vector<vk::Queue> &QueueManager::getComputeQueues() const {
+        return m_computeQueues;
+    }
+
+    const std::vector<vk::Queue> &QueueManager::getTransferQueues() const {
+        return m_transferQueues;
+    }
+
+}
\ No newline at end of file
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87ccdefbfec0b4891d3152d30aa6c9f6c8c0d5ea
--- /dev/null
+++ b/src/vkcv/ShaderProgram.cpp
@@ -0,0 +1,62 @@
+/**
+ * @authors Simeon Hermann, Leonie Franken
+ * @file src/vkcv/ShaderProgram.cpp
+ * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline
+ */
+
+#include "vkcv/ShaderProgram.hpp"
+
+namespace vkcv {
+    /**
+     * Reads the file of a given shader code.
+     * Only used within the class.
+     * @param[in] relative path to the shader code
+     * @return vector of chars as a buffer for the code
+     */
+	std::vector<char> readShaderCode(const std::filesystem::path &shaderPath)
+	{
+		std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary);
+		if (!file.is_open()) {
+		    std::cout << "The file could not be opened." << std::endl;
+			return std::vector<char>{};
+		}
+		size_t fileSize = (size_t)file.tellg();
+		std::vector<char> buffer(fileSize);
+		file.seekg(0);
+		file.read(buffer.data(), fileSize);
+        return buffer;
+	}
+
+	ShaderProgram::ShaderProgram() noexcept :
+	m_Shaders{}
+	{}
+
+	bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath)
+	{
+	    if(m_Shaders.find(shaderStage) != m_Shaders.end())
+	        std::cout << "Found existing shader stage. Overwriting."  << std::endl;
+
+	    const std::vector<char> shaderCode = readShaderCode(shaderPath);
+	    if (shaderCode.empty())
+	        return false;
+	    else
+        {
+            Shader shader{shaderCode, shaderStage};
+            m_Shaders.insert(std::make_pair(shaderStage, shader));
+            return true;
+        }
+	}
+
+    const Shader &ShaderProgram::getShader(ShaderStage shaderStage) const
+    {
+	    return m_Shaders.at(shaderStage);
+	}
+
+    bool ShaderProgram::existsShader(ShaderStage shaderStage) const
+    {
+	    if(m_Shaders.find(shaderStage) == m_Shaders.end())
+	        return false;
+	    else
+	        return true;
+    }
+}
diff --git a/src/vkcv/Surface.cpp b/src/vkcv/Surface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..29b6c646dc212cba2cc31f32dca5c4fcc023cd03
--- /dev/null
+++ b/src/vkcv/Surface.cpp
@@ -0,0 +1,27 @@
+#include "Surface.hpp"
+
+#define GLFW_INCLUDE_VULKAN
+#include <GLFW/glfw3.h>
+
+namespace vkcv {
+	/**
+	* creates surface and checks availability
+	* @param window current window for the surface
+	* @param instance Vulkan-Instance
+	* @param physicalDevice Vulkan-PhysicalDevice
+	* @return created surface
+	*/
+	vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice) {
+		//create surface
+		VkSurfaceKHR surface;
+		if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) {
+			throw std::runtime_error("failed to create a window surface!");
+		}
+		vk::Bool32 surfaceSupport = false;
+		if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) {
+			throw std::runtime_error("surface is not supported by the device!");
+		}
+
+		return vk::SurfaceKHR(surface);
+	}
+}
diff --git a/src/vkcv/Surface.hpp b/src/vkcv/Surface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..74aafeba821334767ac5e13cd33e1d9674e12f5b
--- /dev/null
+++ b/src/vkcv/Surface.hpp
@@ -0,0 +1,8 @@
+#pragma once
+#include <vulkan/vulkan.hpp>
+
+struct GLFWwindow;
+
+namespace vkcv {	
+	vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice);
+}
\ No newline at end of file
diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/SwapChain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3483ae37e718453a99d56d31e025433acb7f4422
--- /dev/null
+++ b/src/vkcv/SwapChain.cpp
@@ -0,0 +1,171 @@
+#include <vkcv/SwapChain.hpp>
+
+namespace vkcv {
+
+    SwapChain::SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount)
+        : m_surface(surface), m_swapchain(swapchain), m_format( format), m_ImageCount(imageCount)
+    {}
+
+    const vk::SwapchainKHR& SwapChain::getSwapchain() const {
+        return m_swapchain;
+    }
+
+    /**
+     * gets surface of the swapchain
+     * @return current surface
+     */
+    vk::SurfaceKHR SwapChain::getSurface() {
+        return m_surface;
+    }
+
+    /**
+     * gets the surface of the swapchain
+     * @return chosen format
+     */
+    vk::SurfaceFormatKHR SwapChain::getSurfaceFormat(){
+        return m_format;
+    }
+
+    /**
+     * chooses Extent and clapms values to the available
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface of the swapchain
+     * @param window of the current application
+     * @return chosen Extent for the surface
+     */
+    vk::Extent2D chooseSwapExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){
+        vk::SurfaceCapabilitiesKHR surfaceCapabilities;
+        if(physicalDevice.getSurfaceCapabilitiesKHR(surface,&surfaceCapabilities) != vk::Result::eSuccess){
+            throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface.");
+        }
+
+        VkExtent2D extent2D = {
+                static_cast<uint32_t>(window.getWidth()),
+                static_cast<uint32_t>(window.getHeight())
+        };
+        extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width));
+        extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height));
+
+        if (extent2D.width > surfaceCapabilities.maxImageExtent.width ||
+            extent2D.width < surfaceCapabilities.minImageExtent.width ||
+            extent2D.height > surfaceCapabilities.maxImageExtent.height ||
+            extent2D.height < surfaceCapabilities.minImageExtent.height) {
+            std::printf("Surface size not matching. Resizing to allowed value.");
+        }
+        return extent2D;
+    }
+
+    /**
+     * chooses Surface Format for the current surface
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface of the swapchain
+     * @return available Format
+     */
+    vk::SurfaceFormatKHR chooseSwapSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+        uint32_t formatCount;
+        physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, nullptr);
+        std::vector<vk::SurfaceFormatKHR> availableFormats(formatCount);
+        if (physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, &availableFormats[0]) != vk::Result::eSuccess) {
+            throw std::runtime_error("Failed to get surface formats");
+        }
+
+        for (const auto& availableFormat : availableFormats) {
+            if (availableFormat.format == vk::Format::eB8G8R8A8Unorm  && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
+                return availableFormat;
+            }
+        }
+        return availableFormats[0];
+    }
+
+    /**
+     * returns vk::PresentModeKHR::eMailbox if available or vk::PresentModeKHR::eFifo otherwise
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface of the swapchain
+     * @return available PresentationMode
+     */
+    vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+        uint32_t modeCount;
+        physicalDevice.getSurfacePresentModesKHR( surface, &modeCount, nullptr );
+        std::vector<vk::PresentModeKHR> availablePresentModes(modeCount);
+        if (physicalDevice.getSurfacePresentModesKHR(surface, &modeCount, &availablePresentModes[0]) != vk::Result::eSuccess) {
+            throw std::runtime_error("Failed to get presentation modes");
+        }
+
+        for (const auto& availablePresentMode : availablePresentModes) {
+            if (availablePresentMode == vk::PresentModeKHR::eMailbox) {
+                return availablePresentMode;
+            }
+        }
+        // The FIFO present mode is guaranteed by the spec to be supported
+        return vk::PresentModeKHR::eFifo;
+    }
+
+    /**
+     * returns the minImageCount +1 for at least doublebuffering, if it's greater than maxImageCount return maxImageCount
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface of the swapchain
+     * @return available ImageCount
+     */
+    uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+        vk::SurfaceCapabilitiesKHR surfaceCapabilities;
+        if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess){
+            throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface.");
+        }
+
+        uint32_t imageCount = surfaceCapabilities.minImageCount + 1;    // minImageCount should always be at least 2; set to 3 for triple buffering
+        // check if requested image count is supported
+        if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) {
+            imageCount = surfaceCapabilities.maxImageCount;
+        }
+
+        return imageCount;
+    }
+    /**
+     * creates and returns a swapchain with default specs
+     * @param window of the current application
+     * @param context that keeps instance, physicalDevice and a device.
+     * @return swapchain
+     */
+    SwapChain SwapChain::create(const Window &window, const Context &context, const vk::SurfaceKHR surface) {
+        const vk::Instance& instance = context.getInstance();
+        const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
+        const vk::Device& device = context.getDevice();
+
+        vk::Extent2D extent2D = chooseSwapExtent(physicalDevice, surface, window);
+        vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(physicalDevice, surface);
+        vk::PresentModeKHR presentMode = choosePresentMode(physicalDevice, surface);
+        uint32_t imageCount = chooseImageCount(physicalDevice, surface);
+
+        vk::SwapchainCreateInfoKHR swapchainCreateInfo(
+                vk::SwapchainCreateFlagsKHR(),  //flags
+                surface,    // surface
+                imageCount,  // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard)
+                surfaceFormat.format,   // imageFormat
+                surfaceFormat.colorSpace,   // imageColorSpace
+                extent2D,   // imageExtent
+                1,  // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers"
+                vk::ImageUsageFlagBits::eColorAttachment,  // imageUsage TODO: what attachments? only color? depth?
+                vk::SharingMode::eExclusive,    // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues"
+                0,  // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT
+                nullptr,    // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT
+                vk::SurfaceTransformFlagBitsKHR::eIdentity, // preTransform, transformations applied onto the image before display
+                vk::CompositeAlphaFlagBitsKHR::eOpaque, // compositeAlpha, TODO: how to handle transparent pixels? do we need transparency? If no -> opaque
+                presentMode,    // presentMode
+                true,   // clipped
+                nullptr // oldSwapchain
+        );
+
+        vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo);
+
+        return SwapChain(surface, swapchain, surfaceFormat, imageCount);
+    }
+
+
+    SwapChain::~SwapChain() {
+        // needs to be destroyed by creator
+    }
+
+	uint32_t SwapChain::getImageCount() {
+		return m_ImageCount;
+	}
+}
diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10d582a20501e1cc8d0bb9a98aa95b09d699c22c
--- /dev/null
+++ b/src/vkcv/SyncResources.cpp
@@ -0,0 +1,24 @@
+#include "vkcv/SyncResources.hpp"
+
+namespace vkcv {
+	SyncResources createDefaultSyncResources(const vk::Device& device) {
+		SyncResources resources;
+
+		const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits();
+		const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags);
+		resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {});
+
+		const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
+		vk::FenceCreateInfo fenceInfo(fenceFlags);
+		resources.presentFinished = device.createFence(fenceInfo, nullptr, {});
+		resources.swapchainImageAcquired = device.createFence(fenceInfo, nullptr, {});
+
+		return resources;
+	}
+
+	void destroySyncResources(const vk::Device& device, const SyncResources& resources) {
+		device.destroySemaphore(resources.renderFinished);
+		device.destroyFence(resources.presentFinished);
+		device.destroyFence(resources.swapchainImageAcquired);
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 8814a5abcf7977386490d9783d5b121b7f986651..ce05d67cb216215c625b468acdb85be2fc2a8b2e 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -6,6 +6,7 @@
 
 #include "vkcv/Window.hpp"
 
+
 namespace vkcv {
 
     static uint32_t s_WindowCount = 0;
@@ -18,14 +19,15 @@ namespace vkcv {
         glfwDestroyWindow(m_window);
         s_WindowCount--;
 
-        if(s_WindowCount == 0)
+        if(s_WindowCount == 0) {
             glfwTerminate();
+        }
     }
 
-    Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
-        if(s_WindowCount == 0)
+    Window Window::create( const char *windowTitle, int width, int height, bool resizable) {
+        if(s_WindowCount == 0) {
             glfwInit();
-
+        }
         s_WindowCount++;
 
         width = std::max(width, 1);
@@ -35,9 +37,8 @@ namespace vkcv {
         glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
         GLFWwindow *window;
         window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
-        return Window(window);
-
 
+        return Window(window);
     }
 
     bool Window::isWindowOpen() const {
@@ -48,10 +49,6 @@ namespace vkcv {
         glfwPollEvents();
     }
 
-    GLFWwindow *Window::getWindow() const {
-        return m_window;
-    }
-
     int Window::getWidth() const {
         int width;
         glfwGetWindowSize(m_window, &width, nullptr);
@@ -63,4 +60,8 @@ namespace vkcv {
         glfwGetWindowSize(m_window, nullptr, &height);
         return height;
     }
-}
\ No newline at end of file
+
+    GLFWwindow *Window::getWindow() const {
+        return m_window;
+    }
+}