diff --git a/config/Sources.cmake b/config/Sources.cmake
index a5b2ddae5e0a194e4ee887da5f37097821e41d0f..4f673e00d1e42e733534480d6085affd651a8c04 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -29,10 +29,14 @@ set(vkcv_sources
 
 		${vkcv_source}/vkcv/ImageManager.hpp
 		${vkcv_source}/vkcv/ImageManager.cpp
+		
+		${vkcv_include}/vkcv/Logger.hpp
 
 		${vkcv_include}/vkcv/SwapChain.hpp
 		${vkcv_source}/vkcv/SwapChain.cpp
-
+		
+		${vkcv_include}/vkcv/ShaderStage.hpp
+		
 		${vkcv_include}/vkcv/ShaderProgram.hpp
 		${vkcv_source}/vkcv/ShaderProgram.cpp
 
diff --git a/config/lib/SPIRV_Cross.cmake b/config/lib/SPIRV_Cross.cmake
index 751ee883c47e0eab081a13e5805ced6f2daa7e30..2e705d7d5a006e3851d14d22a57fd667c61c79f5 100644
--- a/config/lib/SPIRV_Cross.cmake
+++ b/config/lib/SPIRV_Cross.cmake
@@ -6,9 +6,20 @@ if (spirv-cross_FOUND)
     message(${vkcv_config_msg} " SPIRV Cross    - " ${SPIRV_CROSS_VERSION})
 else()
     if (EXISTS "${vkcv_lib_path}/SPIRV-Cross")
+        set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF CACHE INTERNAL "")
+        set(SPIRV_CROSS_SHARED OFF CACHE INTERNAL "")
+        set(SPIRV_CROSS_STATIC ON CACHE INTERNAL "")
         set(SPIRV_CROSS_CLI OFF CACHE INTERNAL "")
         set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE INTERNAL "")
+        
+        set(SPIRV_CROSS_ENABLE_GLSL ON CACHE INTERNAL "")
+        set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE INTERNAL "")
+        set(SPIRV_CROSS_ENABLE_MSL OFF CACHE INTERNAL "")
+        set(SPIRV_CROSS_ENABLE_CPP ON CACHE INTERNAL "")
+        set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE INTERNAL "")
         set(SPIRV_CROSS_ENABLE_C_API OFF CACHE INTERNAL "")
+        set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE INTERNAL "")
+        
         set(SPIRV_CROSS_SKIP_INSTALL ON CACHE INTERNAL "")
     
         add_subdirectory(${vkcv_lib}/SPIRV-Cross)
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 8a165adf43561b1204490a12afa00d2a3fabdbf4..4a51b24f5c978daebc5116e20b527252c8063d61 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -157,6 +157,19 @@ namespace vkcv
         [[nodiscard]]
         PipelineHandle createGraphicsPipeline(const PipelineConfig &config);
 
+        /**
+         * Creates a basic vulkan compute pipeline using @p shader program and returns it using the @p handle.
+         * Fixed Functions for pipeline are set with standard values.
+         *
+         * @param shader program that hold the compiles compute shader
+         * @param handle a handle to return the created vulkan handle
+         * @return True if pipeline creation was successful, False if not
+         */
+        [[nodiscard]]
+        PipelineHandle createComputePipeline(
+            const ShaderProgram &config, 
+            const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts);
+
         /**
          * 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.
@@ -211,7 +224,7 @@ namespace vkcv
          */
         [[nodiscard]]
         DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings);
-		void writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites& writes);
+		void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes);
 
 		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
 
@@ -228,6 +241,13 @@ namespace vkcv
 			const std::vector<DrawcallInfo> &drawcalls,
 			const std::vector<ImageHandle>  &renderTargets);
 
+		void recordComputeDispatchToCmdStream(
+			CommandStreamHandle cmdStream,
+			PipelineHandle computePipeline,
+			const uint32_t dispatchCount[3],
+			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
+			const PushConstantData& pushConstantData);
+
 		/**
 		 * @brief end recording and present image
 		*/
diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
index 86c2e20eb37633e4519749bef507161133e57425..c6d0dfd1bc60988afb8b6a9326a8d50d8a4ea32e 100644
--- a/include/vkcv/DescriptorConfig.hpp
+++ b/include/vkcv/DescriptorConfig.hpp
@@ -1,8 +1,10 @@
 #pragma once
-#include <vkcv/ShaderProgram.hpp>
-#include <vkcv/Handles.hpp>
+
 #include <vulkan/vulkan.hpp>
 
+#include "vkcv/Handles.hpp"
+#include "vkcv/ShaderStage.hpp"
+
 namespace vkcv
 {
     struct DescriptorSet
@@ -33,11 +35,13 @@ namespace vkcv
     struct DescriptorBinding
     {
         DescriptorBinding(
+            uint32_t bindingID,
             DescriptorType descriptorType,
             uint32_t descriptorCount,
             ShaderStage shaderStage
         ) noexcept;
-
+        
+        uint32_t bindingID;
         DescriptorType descriptorType;
         uint32_t descriptorCount;
         ShaderStage shaderStage;
diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..251b6b528c45ea509dbfcd0cfb7135b77031f1ac
--- /dev/null
+++ b/include/vkcv/Logger.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <iostream>
+
+namespace vkcv {
+	
+	enum class LogLevel {
+		INFO,
+		WARNING,
+		ERROR
+	};
+	
+	constexpr auto getLogOutput(LogLevel level) {
+		switch (level) {
+			case LogLevel::INFO:
+				return stdout;
+			default:
+				return stderr;
+		}
+	}
+	
+	constexpr const char* getLogName(LogLevel level) {
+		switch (level) {
+			case LogLevel::INFO:
+				return "INFO";
+			case LogLevel::WARNING:
+				return "WARNING";
+			case LogLevel::ERROR:
+				return "ERROR";
+			default:
+				return "UNKNOWN";
+		}
+	}
+	
+#ifndef NDEBUG
+#ifndef VKCV_DEBUG_MESSAGE_LEN
+#define VKCV_DEBUG_MESSAGE_LEN 1024
+#endif
+
+#ifdef _MSC_VER
+#define __PRETTY_FUNCTION__ __FUNCSIG__
+#endif
+
+#define vkcv_log(level, ...) {      \
+  char output_message [             \
+    VKCV_DEBUG_MESSAGE_LEN          \
+  ];                                \
+  std::snprintf(                    \
+    output_message,                 \
+    VKCV_DEBUG_MESSAGE_LEN,         \
+    __VA_ARGS__                     \
+  );                                \
+  std::fprintf(                     \
+    getLogOutput(level),            \
+    "[%s]: %s [%s, line %d: %s]\n", \
+  	vkcv::getLogName(level),        \
+    output_message,                 \
+    __FILE__,                       \
+    __LINE__,                       \
+    __PRETTY_FUNCTION__             \
+  );                                \
+}
+
+#else
+#define vkcv_log(level, ...) {}
+#endif
+
+}
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index 459125bbd3208ffb40815e1e3fd4c9615ce21724..ce28cccf07e22dda21fd14d0bddd0ba6e9842328 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -8,23 +8,16 @@
 #include <unordered_map>
 #include <fstream>
 #include <iostream>
+#include <algorithm>
 #include <filesystem>
 #include <vulkan/vulkan.hpp>
 #include <spirv_cross.hpp>
 #include "vkcv/VertexLayout.hpp"
+#include "vkcv/ShaderStage.hpp"
+#include "vkcv/DescriptorConfig.hpp"
 
 namespace vkcv {
 
-    enum class ShaderStage
-    {
-        VERTEX,
-        TESS_CONTROL,
-        TESS_EVAL,
-        GEOMETRY,
-        FRAGMENT,
-        COMPUTE
-    };
-
     struct Shader
     {
         std::vector<char> shaderCode;
@@ -60,10 +53,13 @@ namespace vkcv {
         const VertexLayout &getVertexLayout() const;
 		size_t getPushConstantSize() const;
 
+        const std::vector<std::vector<DescriptorBinding>> getReflectedDescriptors() const;
+
 	private:
         std::unordered_map<ShaderStage, Shader> m_Shaders;
 
         VertexLayout m_VertexLayout;
+        std::vector<std::vector<DescriptorBinding>> m_DescriptorSets;
 		size_t m_pushConstantSize = 0;
 	};
 }
diff --git a/include/vkcv/ShaderStage.hpp b/include/vkcv/ShaderStage.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dca395bdba82a2f1cb38bb0a25196cfd3dab8019
--- /dev/null
+++ b/include/vkcv/ShaderStage.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace vkcv {
+	
+	enum class ShaderStage
+	{
+		VERTEX,
+		TESS_CONTROL,
+		TESS_EVAL,
+		GEOMETRY,
+		FRAGMENT,
+		COMPUTE
+	};
+
+}
diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp
index ee0ad8ef56d5284af2be4c81b7ea2f0d052d5a6f..aae43910a09f221874813733b2ef72d36467a750 100644
--- a/include/vkcv/VertexLayout.hpp
+++ b/include/vkcv/VertexLayout.hpp
@@ -3,6 +3,7 @@
 #include <unordered_map>
 #include <vector>
 #include <iostream>
+#include <string>
 
 namespace vkcv{
 
@@ -38,10 +39,11 @@ namespace vkcv{
 
     struct VertexInputAttachment{
         VertexInputAttachment() = delete;
-        VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept;
+        VertexInputAttachment(uint32_t location, uint32_t binding, std::string name, VertexFormat format, uint32_t offset) noexcept;
 
         uint32_t location;
         uint32_t binding;
+        std::string name;
         VertexFormat format;
         uint32_t offset;
     };
diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
index e660b442d0b9a0208f95c9d753ef19e926bcac44..f3823cc8f3fe54b53835f356dd14a086515118dd 100644
--- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
+++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
@@ -7,6 +7,8 @@
 #define STBI_ONLY_JPEG
 #include <stb_image.h>
 
+#include <vkcv/Logger.hpp>
+
 namespace vkcv::asset {
 
 /**
@@ -39,11 +41,12 @@ uint8_t convertTypeToInt(const fx::gltf::Accessor::Type type) {
  * @param path path to file that is responsible for error
  */
 void print_what (const std::exception& e, const std::string &path) {
-	fprintf(stderr, "ERROR loading file %s: %s\n", path.c_str(), e.what());
+	vkcv_log(LogLevel::ERROR, "Loading file %s: %s",
+			 path.c_str(), e.what());
+	
 	try {
 		std::rethrow_if_nested(e);
 	} catch (const std::exception& nested) {
-		std::cerr << "nested: ";
 		print_what(nested, path);
 	}
 }
@@ -121,7 +124,7 @@ int loadMesh(const std::string &path, Mesh &mesh) {
 		const size_t off = indexBufferView.byteOffset;
 		const void *const ptr = ((char*)indexBuffer.data.data()) + off;
 		if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) {
-			std::cerr << "ERROR copying index buffer data.\n";
+			vkcv_log(LogLevel::ERROR, "Copying index buffer data");
 			return 0;
 		}
 	}
@@ -136,7 +139,7 @@ int loadMesh(const std::string &path, Mesh &mesh) {
 		const size_t off = 0;
 		const void *const ptr = ((char*)vertexBuffer.data.data()) + off;
 		if (!memcpy(vertexBufferData.data(), ptr, vertexBuffer.byteLength)) {
-			std::cerr << "ERROR copying vertex buffer data.\n";
+			vkcv_log(LogLevel::ERROR, "Copying vertex buffer data");
 			return 0;
 		}
 	}
@@ -150,9 +153,8 @@ int loadMesh(const std::string &path, Mesh &mesh) {
 	case fx::gltf::Accessor::ComponentType::UnsignedInt:
 		indexType = UINT32; break;
 	default:
-		std::cerr << "ERROR: Index type not supported: " <<
-			static_cast<uint16_t>(indexAccessor.componentType) <<
-			std::endl;
+		vkcv_log(LogLevel::ERROR, "Index type (%u) not supported",
+				 static_cast<uint16_t>(indexAccessor.componentType));
 		return 0;
 	}
 
diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp
index a0fb29fafe24a4ae1279161dc8814c0d8f52765f..2494793f3b4aff3dfa412ff9bbe27e4550ca8fe0 100644
--- a/projects/cmd_sync_test/src/main.cpp
+++ b/projects/cmd_sync_test/src/main.cpp
@@ -102,13 +102,8 @@ int main(int argc, const char** argv) {
 	triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv"));
 	triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX);
 	triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT);
-	
-	std::vector<vkcv::DescriptorBinding> descriptorBindings = {
-		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,    1, vkcv::ShaderStage::FRAGMENT),
-		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,          1, vkcv::ShaderStage::FRAGMENT),
-		vkcv::DescriptorBinding(vkcv::DescriptorType::UNIFORM_BUFFER,   1, vkcv::ShaderStage::FRAGMENT),
-		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,    1, vkcv::ShaderStage::FRAGMENT) ,
-		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,          1, vkcv::ShaderStage::FRAGMENT) };
+
+	std::vector<vkcv::DescriptorBinding> descriptorBindings = { triangleShaderProgram.getReflectedDescriptors()[0] };
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings);
 
 	const vkcv::PipelineConfig trianglePipelineDefinition(
@@ -212,7 +207,7 @@ int main(int argc, const char** argv) {
         vkcv::SamplerDescriptorWrite(1, sampler), 
         vkcv::SamplerDescriptorWrite(4, shadowSampler) };
     setWrites.uniformBufferWrites   = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) };
-	core.writeResourceDescription(descriptorSet, 0, setWrites);
+	core.writeDescriptorSet(descriptorSet, setWrites);
 
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 95c81ba0f3a44e1fc965e20e808c95b6b8b3f3e1..d585fa9606acb13795d299c1f4b47602c463a61a 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -95,9 +95,8 @@ int main(int argc, const char** argv) {
 		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
 	});
 
-	std::vector<vkcv::DescriptorBinding> descriptorBindings = {
-		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,	1, vkcv::ShaderStage::FRAGMENT),
-		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,			1, vkcv::ShaderStage::FRAGMENT) };
+	uint32_t setID = 0;
+	std::vector<vkcv::DescriptorBinding> descriptorBindings = { triangleShaderProgram.getReflectedDescriptors()[setID] };
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings);
 
 	const vkcv::PipelineConfig trianglePipelineDefinition(
@@ -133,9 +132,9 @@ int main(int argc, const char** argv) {
 	};
 
 	vkcv::DescriptorWrites setWrites;
-	setWrites.sampledImageWrites	= { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
-	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(1, sampler) };
-	core.writeResourceDescription(descriptorSet, 0, setWrites);
+	setWrites.sampledImageWrites    = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
+	setWrites.samplerWrites         = { vkcv::SamplerDescriptorWrite(1, sampler) };
+	core.writeDescriptorSet(descriptorSet, setWrites);
 
 	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle();
 
diff --git a/projects/first_triangle/shaders/comp.spv b/projects/first_triangle/shaders/comp.spv
new file mode 100644
index 0000000000000000000000000000000000000000..b414e36b2bea66dab00746298e536d029091e0fd
Binary files /dev/null and b/projects/first_triangle/shaders/comp.spv differ
diff --git a/projects/first_triangle/shaders/compile.bat b/projects/first_triangle/shaders/compile.bat
index b4521235c40fe5fb163bab874560c2f219b7517f..17743a7c49cdfc6e091c43a42a0adb755a731682 100644
--- a/projects/first_triangle/shaders/compile.bat
+++ b/projects/first_triangle/shaders/compile.bat
@@ -1,3 +1,4 @@
 %VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv
 %VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv
+%VULKAN_SDK%\Bin32\glslc.exe shader.comp -o comp.spv
 pause
\ No newline at end of file
diff --git a/projects/first_triangle/shaders/shader.comp b/projects/first_triangle/shaders/shader.comp
new file mode 100644
index 0000000000000000000000000000000000000000..fad6cd0815f2f09bf92dcc3171e2e3723f5466df
--- /dev/null
+++ b/projects/first_triangle/shaders/shader.comp
@@ -0,0 +1,25 @@
+#version 440
+
+layout(std430, binding = 0) buffer testBuffer
+{ 
+    float test1[10];
+    float test2[10];
+    float test3[10];
+};
+
+layout( push_constant ) uniform constants{
+    float pushConstant;
+};
+
+layout(local_size_x = 5) in;
+
+void main(){
+
+    if(gl_GlobalInvocationID.x >= 10){
+        return;
+    }
+
+    test1[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x;
+    test2[gl_GlobalInvocationID.x] = 69;  // nice!
+    test3[gl_GlobalInvocationID.x] = pushConstant;    
+}
\ No newline at end of file
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 2ede653ff98e19159e0155b282cab1b309a13816..ca10434bb9e486649411bcaac54c432744a914bb 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -92,6 +92,7 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
+	// Graphics Pipeline
 	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"));
@@ -115,6 +116,30 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
+	// 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]);
+
+	vkcv::PipelineHandle computePipeline = core.createComputePipeline(
+		computeShaderProgram, 
+		{ core.getDescriptorSet(computeDescriptorSet).layout });
+
+	struct ComputeTestBuffer {
+		float test1[10];
+		float test2[10];
+		float test3[10];
+	};
+
+	vkcv::Buffer computeTestBuffer = core.createBuffer<ComputeTestBuffer>(vkcv::BufferType::STORAGE, 1);
+
+	vkcv::DescriptorWrites computeDescriptorWrites;
+	computeDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, computeTestBuffer.getHandle()) };
+	core.writeDescriptorSet(computeDescriptorSet, computeDescriptorWrites);
+
 	/*
 	 * BufferHandle triangleVertices = core.createBuffer(vertices);
 	 * BufferHandle triangleIndices = core.createBuffer(indices);
@@ -164,6 +189,17 @@ int main(int argc, const char** argv) {
 			pushConstantData,
 			{ drawcall },
 			{ swapchainInput });
+
+		const uint32_t dispatchSize[3] = { 2, 1, 1 };
+		const float theMeaningOfLife = 42;
+
+		core.recordComputeDispatchToCmdStream(
+			cmdStream,
+			computePipeline,
+			dispatchSize,
+			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(computeDescriptorSet).vulkanHandle) },
+			vkcv::PushConstantData((void*)&theMeaningOfLife, sizeof(theMeaningOfLife)));
+
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 	    
diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp
index 71c990c3c222f2318c2f5744ff6295f667d9e6f8..a31e6967d85bd099fe5cbbc865b0e062212ca16e 100644
--- a/src/vkcv/CommandResources.cpp
+++ b/src/vkcv/CommandResources.cpp
@@ -1,6 +1,7 @@
 #include "vkcv/CommandResources.hpp"
 #include <iostream>
 
+#include "vkcv/Logger.hpp"
 
 namespace vkcv {
 
@@ -62,7 +63,7 @@ namespace vkcv {
 			return queueManager.getPresentQueue();
 		}
 		else {
-			std::cerr << "getQueueForSubmit error: unknown queue type" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Unknown queue type");
 			return queueManager.getGraphicsQueues().front();	// graphics is the most general queue
 		}
 	}
diff --git a/src/vkcv/CommandStreamManager.cpp b/src/vkcv/CommandStreamManager.cpp
index 9d0a236a4eaa5a166be77d143370a018b9ea7e73..5a5b359b912d9cef36e0b03379d7f0f6f0951381 100644
--- a/src/vkcv/CommandStreamManager.cpp
+++ b/src/vkcv/CommandStreamManager.cpp
@@ -1,6 +1,8 @@
 #include "vkcv/CommandStreamManager.hpp"
 #include "vkcv/Core.hpp"
 
+#include "vkcv/Logger.hpp"
+
 namespace vkcv {
 	CommandStreamManager::CommandStreamManager() noexcept : m_core(nullptr){}
 
@@ -14,7 +16,7 @@ namespace vkcv {
 
 	void CommandStreamManager::init(Core* core) {
 		if (!core) {
-			std::cerr << "Error: CommandStreamManager::init requires valid core pointer" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires valid core pointer");
 		}
 		m_core = core;
 	}
@@ -57,7 +59,7 @@ namespace vkcv {
 
 		const size_t id = handle.getId();
 		if (id >= m_commandStreams.size()) {
-			std::cerr << "Error: CommandStreamManager::recordCommandsToStream requires valid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires valid handle");
 			return;
 		}
 
@@ -71,7 +73,7 @@ namespace vkcv {
 
 		const size_t id = handle.getId();
 		if (id >= m_commandStreams.size()) {
-			std::cerr << "Error: CommandStreamManager::addFinishCallbackToStream requires valid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires valid handle");
 			return;
 		}
 
@@ -86,7 +88,7 @@ namespace vkcv {
 
 		const size_t id = handle.getId();
 		if (id >= m_commandStreams.size()) {
-			std::cerr << "Error: CommandStreamManager::submitCommandStreamSynchronous requires valid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires valid handle");
 			return;
 		}
 		CommandStream& stream = m_commandStreams[id];
@@ -111,7 +113,7 @@ namespace vkcv {
 	vk::CommandBuffer CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle handle) {
 		const size_t id = handle.getId();
 		if (id >= m_commandStreams.size()) {
-			std::cerr << "Error: CommandStreamManager::submitCommandStreamSynchronous requires valid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires valid handle");
 			return nullptr;
 		}
 		return m_commandStreams[id].cmdBuffer;
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 9ed83d2a224119bd20fcfc81c5720b425de06bb6..44e7111e1f4941ef2f0f8114ac788d7db4a13b5a 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -16,6 +16,8 @@
 #include "ImageLayoutTransitions.hpp"
 #include "vkcv/CommandStreamManager.hpp"
 
+#include "vkcv/Logger.hpp"
+
 namespace vkcv
 {
 
@@ -101,6 +103,13 @@ namespace vkcv
         return m_PipelineManager->createPipeline(config, *m_PassManager);
     }
 
+    PipelineHandle Core::createComputePipeline(
+        const ShaderProgram &shaderProgram, 
+        const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts)
+    {
+        return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts);
+    }
+
 
     PassHandle Core::createPass(const PassConfig &config)
     {
@@ -125,7 +134,7 @@ namespace vkcv
 		}
 		
 		if (result != vk::Result::eSuccess) {
-			std::cerr << vk::to_string(result) << std::endl;
+			vkcv_log(LogLevel::ERROR, "%s", vk::to_string(result).c_str());
 			return Result::ERROR;
 		}
 		
@@ -149,7 +158,7 @@ namespace vkcv
 		}
 		
     	if (acquireSwapchainImage() != Result::SUCCESS) {
-    		std::cerr << "Acquire failed!" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Acquire failed");
     		
     		m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max();
     	}
@@ -234,7 +243,7 @@ namespace vkcv
             1);
         if(m_Context.m_Device.createFramebuffer(&createInfo, nullptr, &framebuffer) != vk::Result::eSuccess)
         {
-            std::cout << "FAILED TO CREATE TEMPORARY FRAMEBUFFER!" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
             return;
         }
 
@@ -298,6 +307,40 @@ namespace vkcv
 		recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
 	}
 
+	void Core::recordComputeDispatchToCmdStream(
+		CommandStreamHandle cmdStreamHandle,
+		PipelineHandle computePipeline,
+		const uint32_t dispatchCount[3],
+		const std::vector<DescriptorSetUsage>& descriptorSetUsages,
+		const PushConstantData& pushConstantData) {
+
+		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
+
+			const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline);
+
+			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline));
+			for (const auto& usage : descriptorSetUsages) {
+				cmdBuffer.bindDescriptorSets(
+					vk::PipelineBindPoint::eCompute,
+					pipelineLayout,
+					usage.setLocation,
+					{ usage.vulkanHandle },
+					{});
+			}
+			if (pushConstantData.sizePerDrawcall > 0) {
+				cmdBuffer.pushConstants(
+					pipelineLayout,
+					vk::ShaderStageFlagBits::eCompute,
+					0,
+					pushConstantData.sizePerDrawcall,
+					pushConstantData.data);
+			}
+			cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]);
+		};
+
+		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
+	}
+
 	void Core::endFrame() {
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
@@ -325,7 +368,7 @@ namespace vkcv
 		}
 		
 		if (result != vk::Result::eSuccess) {
-			std::cout << "Error: swapchain present failed... " << vk::to_string(result) << std::endl;
+			vkcv_log(LogLevel::ERROR, "Swapchain present failed (%s)", vk::to_string(result).c_str());
 		}
 	}
 
@@ -402,10 +445,9 @@ namespace vkcv
         return m_DescriptorManager->createDescriptorSet(bindings);
     }
 
-	void Core::writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites &writes) {
-		m_DescriptorManager->writeResourceDescription(
-			handle, 
-			setIndex, 
+	void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
+		m_DescriptorManager->writeDescriptorSet(
+			handle,  
 			writes, 
 			*m_ImageManager, 
 			*m_BufferManager, 
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp
index be6cfc9b40baad6636e6bc6a2b6e803aacd7ddc0..54e879ac7e6ec7825a4c003899e3c264454c547f 100644
--- a/src/vkcv/DescriptorConfig.cpp
+++ b/src/vkcv/DescriptorConfig.cpp
@@ -2,10 +2,12 @@
 
 namespace vkcv {
 	DescriptorBinding::DescriptorBinding(
+		uint32_t bindingID,
 		DescriptorType descriptorType,
 		uint32_t descriptorCount,
 		ShaderStage shaderStage) noexcept
 		:
+		bindingID(bindingID),
 		descriptorType(descriptorType),
 		descriptorCount(descriptorCount),
 		shaderStage(shaderStage) {}
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index a2efecbe7055122d28a864b7c722a5998be460e4..f591daf90b47b57a758b2b24c7fa87b5c33e3c46 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -1,5 +1,7 @@
 #include "DescriptorManager.hpp"
 
+#include "vkcv/Logger.hpp"
+
 namespace vkcv
 {
     DescriptorManager::DescriptorManager(vk::Device device) noexcept:
@@ -40,7 +42,7 @@ namespace vkcv
         //create each set's binding
         for (uint32_t i = 0; i < bindings.size(); i++) {
             vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
-                i,
+                bindings[i].bindingID,
                 convertDescriptorTypeFlag(bindings[i].descriptorType),
                 bindings[i].descriptorCount,
                 convertShaderStageFlag(bindings[i].shaderStage));
@@ -53,7 +55,7 @@ namespace vkcv
         vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings);
         if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &set.layout) != vk::Result::eSuccess)
         {
-            std::cout << "FAILED TO CREATE DESCRIPTOR SET LAYOUT" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
             return DescriptorSetHandle();
         };
         
@@ -69,10 +71,10 @@ namespace vkcv
 				result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle);
 			}
 			if (result != vk::Result::eSuccess) {
-				std::cout << "FAILED TO ALLOCATE DESCRIPTOR SET" << std::endl;
-				std::cout << vk::to_string(result) << std::endl;
+				vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)",
+						 vk::to_string(result).c_str());
+				
 				m_Device.destroy(set.layout);
-
 				return DescriptorSetHandle();
 			}
         };
@@ -90,9 +92,8 @@ namespace vkcv
 		vk::DescriptorType type;
     };
 
-	void DescriptorManager::writeResourceDescription(
+	void DescriptorManager::writeDescriptorSet(
 		const DescriptorSetHandle	&handle,
-		size_t					setIndex,
 		const DescriptorWrites	&writes,
 		const ImageManager		&imageManager, 
 		const BufferManager		&bufferManager,
@@ -239,7 +240,7 @@ namespace vkcv
 			case DescriptorType::IMAGE_STORAGE:
 				return vk::DescriptorType::eStorageImage;
             default:
-				std::cerr << "Error: DescriptorManager::convertDescriptorTypeFlag, unknown DescriptorType" << std::endl;
+				vkcv_log(LogLevel::ERROR, "Unknown DescriptorType");
                 return vk::DescriptorType::eUniformBuffer;
         }
     }
@@ -266,7 +267,7 @@ namespace vkcv
     
     void DescriptorManager::destroyDescriptorSetById(uint64_t id) {
 		if (id >= m_DescriptorSets.size()) {
-			std::cerr << "Error: DescriptorManager::destroyResourceDescriptionById invalid id" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid id");
 			return;
 		}
 		
@@ -282,7 +283,7 @@ namespace vkcv
 		vk::DescriptorPool pool;
 		if (m_Device.createDescriptorPool(&m_PoolInfo, nullptr, &pool) != vk::Result::eSuccess)
 		{
-			std::cout << "FAILED TO ALLOCATE DESCRIPTOR POOL." << std::endl;
+			vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool");
 			pool = nullptr;
 		};
 		m_Pools.push_back(pool);
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
index d8607b9312b25e71c7eb4af009efd92b834b40ec..d18be64f3b069af68cecce68f6fa623c81f8dfa4 100644
--- a/src/vkcv/DescriptorManager.hpp
+++ b/src/vkcv/DescriptorManager.hpp
@@ -23,9 +23,8 @@ namespace vkcv
 
         DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings);
 
-		void writeResourceDescription(
+		void writeDescriptorSet(
 			const DescriptorSetHandle	&handle,
-			size_t					setIndex,
 			const DescriptorWrites  &writes,
 			const ImageManager      &imageManager,
 			const BufferManager     &bufferManager,
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index cdfd32b009a8007b606c86bf087b3f921b2bb89f..1e3d19d02d7e86546d142bb64440364407e81824 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -6,6 +6,7 @@
 #include "ImageManager.hpp"
 #include "vkcv/Core.hpp"
 #include "ImageLayoutTransitions.hpp"
+#include "vkcv/Logger.hpp"
 
 #include <algorithm>
 
@@ -206,7 +207,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getVulkanImage invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return nullptr;
 		}
 		
@@ -219,7 +220,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getVulkanDeviceMemory invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return nullptr;
 		}
 		
@@ -232,7 +233,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getVulkanImageView invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return nullptr;
 		}
 		
@@ -245,7 +246,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::switchImageLayout invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return;
 		}
 		
@@ -280,7 +281,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::switchImageLayout invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return;
 		}
 
@@ -295,7 +296,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::fillImage invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return;
 		}
 		
@@ -369,7 +370,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getImageWidth invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return 0;
 		}
 		
@@ -382,7 +383,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getImageHeight invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return 0;
 		}
 		
@@ -395,7 +396,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 		
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::getImageDepth invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return 0;
 		}
 		
@@ -407,7 +408,7 @@ namespace vkcv {
 	void ImageManager::destroyImageById(uint64_t id)
 	{
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::destroyImageById invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return;
 		}
 		
@@ -436,7 +437,7 @@ namespace vkcv {
 		const uint64_t id = handle.getId();
 
 		if (id >= m_images.size()) {
-			std::cerr << "Error: ImageManager::destroyImageById invalid handle" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
 			return vk::Format::eUndefined;
 		}
 
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
index 28a64a243b9a7a8fc9372409ef3783901219c868..24ac7970d739d46ab7cecca7bb783bb0037c43cb 100644
--- a/src/vkcv/PipelineManager.cpp
+++ b/src/vkcv/PipelineManager.cpp
@@ -1,5 +1,6 @@
 #include "PipelineManager.hpp"
 #include "vkcv/Image.hpp"
+#include "vkcv/Logger.hpp"
 
 namespace vkcv
 {
@@ -20,15 +21,25 @@ namespace vkcv
 	// currently assuming default 32 bit formats, no lower precision or normalized variants supported
 	vk::Format vertexFormatToVulkanFormat(const VertexFormat format) {
 		switch (format) {
-		case VertexFormat::FLOAT: return vk::Format::eR32Sfloat;
-		case VertexFormat::FLOAT2: return vk::Format::eR32G32Sfloat;
-		case VertexFormat::FLOAT3: return vk::Format::eR32G32B32Sfloat;
-		case VertexFormat::FLOAT4: return vk::Format::eR32G32B32A32Sfloat;
-		case VertexFormat::INT: return vk::Format::eR32Sint;
-		case VertexFormat::INT2: return vk::Format::eR32G32Sint;
-		case VertexFormat::INT3: return vk::Format::eR32G32B32Sint;
-		case VertexFormat::INT4: return vk::Format::eR32G32B32A32Sint;
-		default: std::cerr << "Warning: Unknown vertex format" << std::endl; return vk::Format::eUndefined;
+		case VertexFormat::FLOAT:
+			return vk::Format::eR32Sfloat;
+		case VertexFormat::FLOAT2:
+			return vk::Format::eR32G32Sfloat;
+		case VertexFormat::FLOAT3:
+			return vk::Format::eR32G32B32Sfloat;
+		case VertexFormat::FLOAT4:
+			return vk::Format::eR32G32B32A32Sfloat;
+		case VertexFormat::INT:
+			return vk::Format::eR32Sint;
+		case VertexFormat::INT2:
+			return vk::Format::eR32G32Sint;
+		case VertexFormat::INT3:
+			return vk::Format::eR32G32B32Sint;
+		case VertexFormat::INT4:
+			return vk::Format::eR32G32B32A32Sint;
+		default:
+			vkcv_log(LogLevel::WARNING, "Unknown vertex format");
+			return vk::Format::eUndefined;
 		}
 	}
 
@@ -40,7 +51,7 @@ namespace vkcv
         const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT);
         if (!(existsVertexShader && existsFragmentShader))
         {
-            std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl;
+			vkcv_log(LogLevel::ERROR, "Requires vertex and fragment shader code");
             return PipelineHandle();
         }
 
@@ -182,6 +193,7 @@ namespace vkcv
 			{},
 			(config.m_DescriptorLayouts),
 			(pushConstantRange));
+
         vk::PipelineLayout vkPipelineLayout{};
         if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess)
         {
@@ -314,4 +326,65 @@ namespace vkcv
         return m_Configs.at(id);
     }
 
+    PipelineHandle PipelineManager::createComputePipeline(
+        const ShaderProgram &shaderProgram, 
+        const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) {
+
+        // Temporally handing over the Shader Program instead of a pipeline config
+        vk::ShaderModule computeModule{};
+        if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) != vk::Result::eSuccess)
+            return PipelineHandle();
+
+        vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo(
+                {},
+                vk::ShaderStageFlagBits::eCompute,
+                computeModule,
+                "main",
+                nullptr
+        );
+
+        vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, descriptorSetLayouts);
+
+        const size_t pushConstantSize = shaderProgram.getPushConstantSize();
+        vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eCompute, 0, pushConstantSize);
+        if (pushConstantSize > 0) {
+            pipelineLayoutCreateInfo.setPushConstantRangeCount(1);
+            pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRange);
+        }
+
+        vk::PipelineLayout vkPipelineLayout{};
+        if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess)
+        {
+            m_Device.destroy(computeModule);
+            return PipelineHandle();
+        }
+
+        vk::ComputePipelineCreateInfo computePipelineCreateInfo{};
+        computePipelineCreateInfo.stage = pipelineComputeShaderStageInfo;
+        computePipelineCreateInfo.layout = vkPipelineLayout;
+
+        vk::Pipeline vkPipeline;
+        if (m_Device.createComputePipelines(nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline)!= vk::Result::eSuccess)
+        {
+            m_Device.destroy(computeModule);
+            return PipelineHandle();
+        }
+
+        m_Device.destroy(computeModule);
+
+        const uint64_t id = m_Pipelines.size();
+        m_Pipelines.push_back({ vkPipeline, vkPipelineLayout });
+
+        return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); });
+    }
+
+    // There is an issue for refactoring the Pipeline Manager.
+    // While including Compute Pipeline Creation, some private helper functions where introduced:
+
+    vk::Result PipelineManager::createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, const ShaderStage stage)
+    {
+        std::vector<char> code = shaderProgram.getShader(stage).shaderCode;
+        vk::ShaderModuleCreateInfo moduleInfo({}, code.size(), reinterpret_cast<uint32_t*>(code.data()));
+        return m_Device.createShaderModule(&moduleInfo, nullptr, &module);
+    }
 }
\ No newline at end of file
diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp
index e243151f7248c07fa0287bb2eaf698e5080f7f61..634f5f4e6464532306e35fd10d9a1623df6ace16 100644
--- a/src/vkcv/PipelineManager.hpp
+++ b/src/vkcv/PipelineManager.hpp
@@ -21,7 +21,9 @@ namespace vkcv
         std::vector<PipelineConfig> m_Configs;
         
         void destroyPipelineById(uint64_t id);
-        
+
+        vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, ShaderStage stage);
+
     public:
         PipelineManager() = delete; // no default ctor
         explicit PipelineManager(vk::Device device) noexcept; // ctor
@@ -35,6 +37,10 @@ namespace vkcv
 
         PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager);
 
+        PipelineHandle createComputePipeline(
+            const ShaderProgram& shaderProgram,
+            const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts);
+
         [[nodiscard]]
         vk::Pipeline getVkPipeline(const PipelineHandle &handle) const;
 
diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp
index e4d1a2d3a3302fc435c4c278322c08f51f19be6b..df6c74cccf6c4652adc6a4c78802f282ea6ae293 100644
--- a/src/vkcv/QueueManager.cpp
+++ b/src/vkcv/QueueManager.cpp
@@ -4,7 +4,7 @@
 #include <iostream>
 
 #include "vkcv/QueueManager.hpp"
-
+#include "vkcv/Logger.hpp"
 
 namespace vkcv {
 
@@ -95,7 +95,8 @@ namespace vkcv {
                                 found = true;
                             }
                         }
-                        std::cerr << "Warning: not enough \"" << vk::to_string(qFlag) << "\"-Queues." << std::endl;
+	
+						vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str());
                     }
                     break;
                 case vk::QueueFlagBits::eCompute:
@@ -116,7 +117,8 @@ namespace vkcv {
                                 found = true;
                             }
                         }
-                        std::cerr << "Warning: not enough \"" << vk::to_string(qFlag) << "\"-Queues." << std::endl;
+                        
+						vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str());
                     }
                     break;
                 case vk::QueueFlagBits::eTransfer:
@@ -137,7 +139,8 @@ namespace vkcv {
                                 found = true;
                             }
                         }
-                        std::cerr << "Warning: not enough \"" << vk::to_string(qFlag) << "\"-Queues." << std::endl;
+	
+						vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str());
                     }
                     break;
                 default:
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 3eea7ed21d99af2768d48e62af47cbdef94c7ef1..7c54c301d1301127273303128b0c10a9c2c53942 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -5,7 +5,7 @@
  */
 
 #include "vkcv/ShaderProgram.hpp"
-#include <algorithm>
+#include "vkcv/Logger.hpp"
 
 namespace vkcv {
     /**
@@ -18,7 +18,7 @@ namespace vkcv {
 	{
 		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;
+			vkcv_log(LogLevel::ERROR, "The file could not be opened");
 			return std::vector<char>{};
 		}
 		size_t fileSize = (size_t)file.tellg();
@@ -61,25 +61,28 @@ namespace vkcv {
             default:
                 break;
         }
-        std::cout << "Shader Program Reflection: unknown Vertex Format" << std::endl;
+		
+		vkcv_log(LogLevel::WARNING, "Unknown vertex format");
         return VertexFormat::FLOAT;
 	}
 
 	ShaderProgram::ShaderProgram() noexcept :
 	m_Shaders{},
-    m_VertexLayout{}
+    m_VertexLayout{},
+    m_DescriptorSets{}
 	{}
 
 	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;
+	    if(m_Shaders.find(shaderStage) != m_Shaders.end()) {
+			vkcv_log(LogLevel::WARNING, "Overwriting existing shader stage");
+		}
 
 	    const std::vector<char> shaderCode = readShaderCode(shaderPath);
-	    if (shaderCode.empty())
-	        return false;
-	    else
-        {
+	    
+	    if (shaderCode.empty()) {
+			return false;
+		} else {
             Shader shader{shaderCode, shaderStage};
             m_Shaders.insert(std::make_pair(shaderStage, shader));
             return true;
@@ -111,6 +114,7 @@ namespace vkcv {
         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;
@@ -118,17 +122,77 @@ namespace vkcv {
 			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);
 		}
+
+		//reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image)
+        std::vector<std::pair<uint32_t, DescriptorBinding>> bindings;
+        int32_t maxSetID = -1;
+        for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) {
+            auto& u = resources.uniform_buffers[i];
+            const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
+            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
+                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::UNIFORM_BUFFER, base_type.vecsize, shaderStage));
+            bindings.push_back(descriptor);
+            if (comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+        }
+
+        for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) {
+            auto& u = resources.storage_buffers[i];
+            const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
+            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
+                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::STORAGE_BUFFER, base_type.vecsize, shaderStage));
+            bindings.push_back(descriptor);
+            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) 
+                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+        }
+
+        for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) {
+            auto& u = resources.separate_samplers[i];
+            const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
+            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
+                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::SAMPLER, base_type.vecsize, shaderStage));
+            bindings.push_back(descriptor);
+            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) 
+                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+        }
+
+        for (uint32_t i = 0; i < resources.separate_images.size(); i++) {
+            auto& u = resources.separate_images[i];
+            const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
+            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
+                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_SAMPLED, base_type.vecsize, shaderStage));
+            bindings.push_back(descriptor);
+            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID)
+                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+
+        }
+
+        for (uint32_t i = 0; i < resources.storage_images.size(); i++) {
+            auto& u = resources.storage_images[i];
+            const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
+            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
+                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_STORAGE, base_type.vecsize, shaderStage));
+            bindings.push_back(descriptor);
+            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID)
+                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+        }
+        if (maxSetID != -1) {
+            if((int32_t)m_DescriptorSets.size() <= maxSetID) m_DescriptorSets.resize(maxSetID + 1);
+            for (const auto &binding : bindings) {
+                m_DescriptorSets[binding.first].push_back(binding.second);
+            }
+        }
+
+        //reflect push constants
 		for (const auto &pushConstantBuffer : resources.push_constant_buffers) {
 			for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id)) {
 				const size_t size = range.range + range.offset;
@@ -141,6 +205,9 @@ namespace vkcv {
         return m_VertexLayout;
 	}
 
+    const std::vector<std::vector<DescriptorBinding>> ShaderProgram::getReflectedDescriptors() const {
+        return m_DescriptorSets;
+    }
 	size_t ShaderProgram::getPushConstantSize() const {
 		return m_pushConstantSize;
 	}
diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp
index b06c6743e1e19a5e282af248ab6b590eb97529fd..3c39ad0d39c4b458526ceed54422d320ee6d0e0d 100644
--- a/src/vkcv/VertexLayout.cpp
+++ b/src/vkcv/VertexLayout.cpp
@@ -3,6 +3,7 @@
 //
 
 #include "vkcv/VertexLayout.hpp"
+#include "vkcv/Logger.hpp"
 
 namespace vkcv {
     uint32_t getFormatSize(VertexFormat format) {
@@ -24,15 +25,15 @@ namespace vkcv {
             case VertexFormat::INT4:
                 return 16;
             default:
-                break;
+				vkcv_log(LogLevel::WARNING, "No format given");
+                return 0;
         }
-        std::cout << "VertexLayout: No format given" << std::endl;
-        return 0;
     }
 
-    VertexInputAttachment::VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept:
+    VertexInputAttachment::VertexInputAttachment(uint32_t location, uint32_t binding, std::string name, VertexFormat format, uint32_t offset) noexcept:
             location{location},
             binding{binding},
+            name{name},
             format{format},
             offset{offset}
             {}