diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index d6e47d26e5c26dab7929ac5905938c2ac0c80125..c24d4c7cc435389c739bf47f7316586b5d6c676c 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -391,6 +391,16 @@ namespace vkcv
 		void prepareSwapchainImageForPresent(const CommandStreamHandle& handle);
 		void prepareImageForSampling(const CommandStreamHandle& cmdStream, const ImageHandle& image);
 		void prepareImageForStorage(const CommandStreamHandle& cmdStream, const ImageHandle& image);
+
+		// normally layout transitions for attachments are handled by the core
+		// however for manual vulkan use, e.g. ImGui integration, this function is exposed
+		// this is also why the command buffer is passed directly, instead of the command stream handle
+		void prepareImageForAttachmentManually(const vk::CommandBuffer& cmdBuffer, const ImageHandle& image);
+
+		// if manual vulkan work, e.g. ImGui integration, changes an image layout this function must be used
+		// to update the internal image state
+		void updateImageLayoutManual(const vkcv::ImageHandle& image, const vk::ImageLayout layout);
+
 		void recordImageMemoryBarrier(const CommandStreamHandle& cmdStream, const ImageHandle& image);
 		void recordBufferMemoryBarrier(const CommandStreamHandle& cmdStream, const BufferHandle& buffer);
 		void resolveMSAAImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst);
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 22c40d2937c69525c04ffd79f26107f829e42f4d..7ee335379603b3e21ab4d95f0738097bd954cf71 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -6,6 +6,9 @@
 
 namespace vkcv::gui {
 	
+	const static vk::ImageLayout initialImageLayout = vk::ImageLayout::eColorAttachmentOptimal;
+	const static vk::ImageLayout finalImageLayout   = vk::ImageLayout::ePresentSrcKHR;
+
 	static void checkVulkanResult(VkResult resultCode) {
 		if (resultCode == 0)
 			return;
@@ -95,8 +98,8 @@ namespace vkcv::gui {
 				vk::AttachmentStoreOp::eStore,
 				vk::AttachmentLoadOp::eDontCare,
 				vk::AttachmentStoreOp::eDontCare,
-				vk::ImageLayout::eUndefined,
-				vk::ImageLayout::ePresentSrcKHR
+				initialImageLayout,
+				finalImageLayout
 		);
 		
 		const vk::AttachmentReference attachmentReference (
@@ -199,7 +202,7 @@ namespace vkcv::gui {
 		
 		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
 		const auto extent = swapchain.getExtent();
-		
+
 		const vk::ImageView swapchainImageView = m_core.getSwapchainImageView();
 
 		const vk::FramebufferCreateInfo framebufferCreateInfo (
@@ -218,6 +221,10 @@ namespace vkcv::gui {
 		submitInfo.queueType = QueueType::Graphics;
 		
 		m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) {
+
+			assert(initialImageLayout == vk::ImageLayout::eColorAttachmentOptimal);
+			m_core.prepareImageForAttachmentManually(commandBuffer, vkcv::ImageHandle::createSwapchainImageHandle());
+
 			const vk::Rect2D renderArea (
 					vk::Offset2D(0, 0),
 					extent
@@ -230,12 +237,17 @@ namespace vkcv::gui {
 					0,
 					nullptr
 			);
-			
+
 			commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline);
 			
 			ImGui_ImplVulkan_RenderDrawData(drawData, static_cast<VkCommandBuffer>(commandBuffer));
 			
 			commandBuffer.endRenderPass();
+
+			// executing the renderpass changed the image layout without going through the image manager
+			// therefore the layout must be updated manually
+			m_core.updateImageLayoutManual(vkcv::ImageHandle::createSwapchainImageHandle(), finalImageLayout);
+
 		}, [&]() {
 			m_context.getDevice().destroyFramebuffer(framebuffer);
 		});
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index f22d56650654f66dd1fea4141a449004dcad88cc..84529dbd019e2bee6e6a2eaa142dbd25d064b06c 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -328,8 +328,8 @@ namespace vkcv {
 			buffer.m_size);
 
 		cmdBuffer.pipelineBarrier(
-			vk::PipelineStageFlagBits::eTopOfPipe,
-			vk::PipelineStageFlagBits::eBottomOfPipe,
+			vk::PipelineStageFlagBits::eAllCommands,
+			vk::PipelineStageFlagBits::eAllCommands,
 			{},
 			nullptr,
 			memoryBarrier,
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 21435c818451d1bea796b862cbb57ebeea8f0abd..00ea4a9fec2bbc49ef078074ca7e13927d146b9d 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -737,6 +737,14 @@ namespace vkcv
 		}, nullptr);
 	}
 
+	void Core::prepareImageForAttachmentManually(const vk::CommandBuffer& cmdBuffer, const ImageHandle& image) {
+		transitionRendertargetsToAttachmentLayout({ image }, *m_ImageManager, cmdBuffer);
+	}
+
+	void Core::updateImageLayoutManual(const vkcv::ImageHandle& image, const vk::ImageLayout layout) {
+		m_ImageManager->updateImageLayoutManual(image, layout);
+	}
+
 	void Core::recordImageMemoryBarrier(const CommandStreamHandle& cmdStream, const ImageHandle& image) {
 		recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
 			m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer);
diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp
index 8d31c64ccbcbf33e259714f8c581c920738190b4..14b226847a15b5e9cadffc28555e76b88b61e6a3 100644
--- a/src/vkcv/ImageLayoutTransitions.cpp
+++ b/src/vkcv/ImageLayoutTransitions.cpp
@@ -58,8 +58,8 @@ namespace vkcv {
 
 	void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier) {
 		cmdBuffer.pipelineBarrier(
-			vk::PipelineStageFlagBits::eTopOfPipe,
-			vk::PipelineStageFlagBits::eBottomOfPipe,
+			vk::PipelineStageFlagBits::eAllCommands,
+			vk::PipelineStageFlagBits::eAllCommands,
 			{},
 			nullptr,
 			nullptr,
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 4ddd7f8c44c6023a80831bc8b4b092692e84ec86..bde020498e19e3f9bf0667c7182ca13d11f9044f 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -685,4 +685,20 @@ namespace vkcv {
 		}
 	}
 
+	void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle& handle, const vk::ImageLayout layout) {
+		const uint64_t id = handle.getId();
+
+		if (handle.isSwapchainImage()) {
+			m_swapchainImages[m_currentSwapchainInputImage].m_layout = layout;
+		}
+		else {
+			if (id >= m_images.size()) {
+				vkcv_log(LogLevel::ERROR, "Invalid handle");
+				return;
+			}
+			m_swapchainImages[id].m_layout = layout;
+		}
+		
+	}
+
 }
\ No newline at end of file
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 4d99422118e8d464ea75d9f013b471f3dd40fd8c..124508d5e36711a13ad49ae289b9de98600e0d6e 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -120,5 +120,9 @@ namespace vkcv {
 		void setSwapchainImages(const std::vector<vk::Image>& images, const std::vector<vk::ImageView>& views,
 								uint32_t width, uint32_t height, vk::Format format);
 
+		// if manual vulkan work, e.g. ImGui integration, changes an image layout this function must be used
+		// to update the internal image state
+		void updateImageLayoutManual(const vkcv::ImageHandle& handle, const vk::ImageLayout layout);
+
 	};
 }
\ No newline at end of file