From 630dc0cee2891703b8571f33e2a32c79d46c3348 Mon Sep 17 00:00:00 2001
From: Johannes Unterguggenberger <johannes.unterguggenberger@gmail.com>
Date: Thu, 14 Mar 2024 12:09:18 +0100
Subject: [PATCH 1/3] Further draw indirect commands added

* Added further draw_indirect commands

* Bugfix: Post execution handler was not invoked in command_buffer_t::prepare_for_reuse()

* Attempt to fix build.yml

* trying to fix windows-latest builds
---
 .github/workflows/build.yml |  28 +++++---
 include/avk/commands.hpp    | 136 ++++++++++++++++++++++++++++++++++++
 src/avk.cpp                 |   7 +-
 3 files changed, 158 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 23a690f..24f4244 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,10 +33,10 @@ jobs:
           }
         # note: if a specific vulkan version (e.g. 1.1.x, or < 1.2.135) needs testing, you can add it here:
         # (not that ubuntu-latest (20.04) only supports >= v1.2.148 via apt)
-        vulkan-sdk: ["latest", "1.2.176"]
+        vulkan-sdk: ["latest", "1.3.204", "1.3.216"]
         vma: ["ON", "OFF"]
         exclude: # exclude combinations that are known to fail
-          - vulkan-sdk: "1.2.176"
+          - vulkan-sdk: "1.3.204"
             vma: "ON"
 
     steps:
@@ -86,10 +86,10 @@ jobs:
         shell: bash
         working-directory: ${{ runner.workspace }}/build
         # Execute the build.  You can specify a specific target with "--target <NAME>"
-        run: cmake --build .
+        run: cmake --build . --config $BUILD_TYPE
 
   windows:
-    name: ${{ matrix.config.name }}, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
+    name: ${{ matrix.config.name }}, windows-2019, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
     runs-on: windows-2019
     env:
       vulkan_sdk: "$GITHUB_WORKSPACE/vulkan_sdk/"
@@ -102,8 +102,8 @@ jobs:
             cc: "cl",
             cxx: "cl"
           }
-        # note: if a specific vulkan version (e.g. 1.1.x, or < 1.2.135) needs testing, you can add it here:
-        vulkan-sdk: ["latest", "1.3.216.0", "1.2.198.1"]
+        # note: if a specific vulkan version needs testing, you can add it here:
+        vulkan-sdk: ["latest", "1.3.204.1", "1.3.216.0"]
         vma: ["ON", "OFF"]
         exclude: # exclude combinations that are known to fail
           - vulkan-sdk: "1.2.198"
@@ -146,6 +146,9 @@ jobs:
           $env:CC="${{ matrix.config.cc }}"
           $env:CXX="${{ matrix.config.cxx }}"
           $env:Path += ";${{ env.vulkan_sdk }}\;${{ env.vulkan_sdk }}\Bin\"
+          $env:VULKAN_SDK="${{ env.vulkan_sdk }}"
+          $env:Vulkan_LIBRARY="${{ env.vulkan_sdk }}/Bin"
+          $env:Vulkan_INCLUDE_DIR="${{ env.vulkan_sdk }}/Include"
 
           # apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
           cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Davk_LibraryType=STATIC ${{ runner.workspace }}/${{ github.event.repository.name }} -Davk_UseVMA=${{ matrix.vma }} -G "Visual Studio 16 2019" -A x64
@@ -155,10 +158,10 @@ jobs:
         # apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
         working-directory: ${{ runner.workspace }}/${{ github.event.repository.name }}/build
         # Execute the build.  You can specify a specific target with "--target <NAME>"
-        run: cmake --build .
+        run: cmake --build . --config $BUILD_TYPE
 
   windows-latest:
-    name: ${{ matrix.config.name }}, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
+    name: ${{ matrix.config.name }}, windows-latest, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
     runs-on: windows-latest
     env:
       vulkan_sdk: "$GITHUB_WORKSPACE/vulkan_sdk/"
@@ -171,8 +174,8 @@ jobs:
             cc: "cl",
             cxx: "cl"
           }
-        # note: if a specific vulkan version (e.g. 1.1.x, or < 1.2.135) needs testing, you can add it here:
-        vulkan-sdk: ["latest", "1.3.216.0", "1.2.198.1"]
+        # note: if a specific vulkan version needs testing, you can add it here:
+        vulkan-sdk: ["latest", "1.3.204.1", "1.3.216.0"]
         vma: ["ON", "OFF"]
         exclude: # exclude combinations that are known to fail
           - vulkan-sdk: "1.2.198"
@@ -215,6 +218,9 @@ jobs:
           $env:CC="${{ matrix.config.cc }}"
           $env:CXX="${{ matrix.config.cxx }}"
           $env:Path += ";${{ env.vulkan_sdk }}\;${{ env.vulkan_sdk }}\Bin\"
+          $env:VULKAN_SDK="${{ env.vulkan_sdk }}"
+          $env:Vulkan_LIBRARY="${{ env.vulkan_sdk }}/Bin"
+          $env:Vulkan_INCLUDE_DIR="${{ env.vulkan_sdk }}/Include"
 
           # apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
           cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Davk_LibraryType=STATIC ${{ runner.workspace }}/${{ github.event.repository.name }} -Davk_UseVMA=${{ matrix.vma }} -G "Visual Studio 17 2022" -A x64
@@ -224,4 +230,4 @@ jobs:
         # apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
         working-directory: ${{ runner.workspace }}/${{ github.event.repository.name }}/build
         # Execute the build.  You can specify a specific target with "--target <NAME>"
-        run: cmake --build .
+        run: cmake --build . --config $BUILD_TYPE
diff --git a/include/avk/commands.hpp b/include/avk/commands.hpp
index b5c723c..4ffa2b9 100644
--- a/include/avk/commands.hpp
+++ b/include/avk/commands.hpp
@@ -604,6 +604,142 @@ namespace avk
 			bind_vertex_buffer(aHandlePtr + 1, aOffsetPtr + 1, aRest...);
 		}
 
+		/**	Draw vertices with vertex buffer bindings starting at BUFFER-BINDING #0 top to the number of total buffers passed -1.
+		 *	"BUFFER-BINDING" means that it corresponds to the binding specified in `input_binding_location_data::from_buffer_at_binding`.
+		 *	There can be no gaps between buffer bindings.
+		 *	@param	aFurtherBuffers		Multiple const-references to buffers, or tuples of const-references to buffers + offsets.
+		 *								First case:   Pass const buffer_t& types!
+		 *								Second case:  Pass tuples of type std::tuple<const buffer_t&, size_t>!
+		 *											  Hint:    std::forward_as_tuple might be useful to get that reference into a std::tuple.
+		 *											  Example: avk::buffer myVertexBuffer;
+		 *											           auto myTuple = std::forward_as_tuple(myVertexBuffer.get(), size_t{0});
+		 */
+		template <typename... Bfrs>
+		action_type_command draw_vertices_indirect(const buffer_t& aParametersBuffer, vk::DeviceSize aParametersOffset, uint32_t aParametersStride, uint32_t aDrawCount, const Bfrs&... aFurtherBuffers)
+		{
+			constexpr size_t N = sizeof...(aFurtherBuffers);
+
+			if constexpr (N == 0) {
+				return action_type_command{
+					avk::sync::sync_hint {
+						{{ // DESTINATION dependencies for previous commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eInputAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentRead | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}},
+						{{ // SOURCE dependencies for subsequent commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}}
+					},
+					{}, // no resource-specific sync hints
+					[
+					    lParametersBufferHandle = aParametersBuffer.handle(), aParametersOffset, aParametersStride, aDrawCount
+					](avk::command_buffer_t& cb) {
+						cb.handle().drawIndirect(lParametersBufferHandle, aParametersOffset, aDrawCount, aParametersStride);
+					}
+				};
+ 			}
+ 			else {
+				std::array<vk::Buffer, N> handles;
+				std::array<vk::DeviceSize, N> offsets;
+				bind_vertex_buffer(&handles[0], &offsets[0], aFurtherBuffers...);
+
+				return action_type_command{
+					avk::sync::sync_hint {
+						{{ // DESTINATION dependencies for previous commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eInputAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentRead | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}},
+						{{ // SOURCE dependencies for subsequent commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}}
+					},
+					{}, // no resource-specific sync hints
+					[
+						lBindingCount = static_cast<uint32_t>(N),
+					    lParametersBufferHandle = aParametersBuffer.handle(), aParametersOffset, aParametersStride, aDrawCount,
+						handles, offsets
+					](avk::command_buffer_t& cb) {
+						cb.handle().bindVertexBuffers(
+							0u, // TODO: Should the first binding really always be 0?
+							static_cast<uint32_t>(N), handles.data(), offsets.data()
+						);
+						cb.handle().drawIndirect(lParametersBufferHandle, aParametersOffset, aDrawCount, aParametersStride);
+					}
+				};
+			}
+		}
+
+		/**	Draw vertices with vertex buffer bindings starting at BUFFER-BINDING #0 top to the number of total buffers passed -1.
+		 *	"BUFFER-BINDING" means that it corresponds to the binding specified in `input_binding_location_data::from_buffer_at_binding`.
+		 *	There can be no gaps between buffer bindings.
+		 *	@param	aFurtherBuffers		Multiple const-references to buffers, or tuples of const-references to buffers + offsets.
+		 *								First case:   Pass const buffer_t& types!
+		 *								Second case:  Pass tuples of type std::tuple<const buffer_t&, size_t>!
+		 *											  Hint:    std::forward_as_tuple might be useful to get that reference into a std::tuple.
+		 *											  Example: avk::buffer myVertexBuffer;
+		 *											           auto myTuple = std::forward_as_tuple(myVertexBuffer.get(), size_t{0});
+		 */
+		template <typename... Bfrs>
+		action_type_command draw_vertices_indirect_count(const buffer_t& aParametersBuffer, vk::DeviceSize aParametersOffset, uint32_t aParametersStride, const buffer_t& aDrawCountBuffer, vk::DeviceSize aDrawCountOffset, uint32_t aMaxNumberOfDraws, const Bfrs&... aFurtherBuffers)
+		{
+			constexpr size_t N = sizeof...(aFurtherBuffers);
+
+			if constexpr (N == 0) {
+				return action_type_command{
+					avk::sync::sync_hint {
+						{{ // DESTINATION dependencies for previous commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eInputAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentRead | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}},
+						{{ // SOURCE dependencies for subsequent commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}}
+					},
+					{}, // no resource-specific sync hints
+					[
+					    lParametersBufferHandle = aParametersBuffer.handle(), aParametersOffset, aParametersStride,
+					    lDrawCountBufferHandle = aDrawCountBuffer.handle(),   aDrawCountOffset,  aMaxNumberOfDraws
+					](avk::command_buffer_t& cb) {
+						cb.handle().drawIndirectCount(lParametersBufferHandle, aParametersOffset, lDrawCountBufferHandle, aDrawCountOffset, aMaxNumberOfDraws, aParametersStride);
+					}
+				};
+ 			}
+ 			else {
+				std::array<vk::Buffer, N> handles;
+				std::array<vk::DeviceSize, N> offsets;
+				bind_vertex_buffer(&handles[0], &offsets[0], aFurtherBuffers...);
+
+				return action_type_command{
+					avk::sync::sync_hint {
+						{{ // DESTINATION dependencies for previous commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eInputAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentRead | vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentRead | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}},
+						{{ // SOURCE dependencies for subsequent commands:
+							vk::PipelineStageFlagBits2KHR::eAllGraphics,
+							vk::AccessFlagBits2KHR::eColorAttachmentWrite | vk::AccessFlagBits2KHR::eDepthStencilAttachmentWrite
+						}}
+					},
+					{}, // no resource-specific sync hints
+					[
+						lBindingCount = static_cast<uint32_t>(N),
+					    lParametersBufferHandle = aParametersBuffer.handle(), aParametersOffset, aParametersStride,
+					    lDrawCountBufferHandle = aDrawCountBuffer.handle(),   aDrawCountOffset,  aMaxNumberOfDraws,
+						handles, offsets
+					](avk::command_buffer_t& cb) {
+						cb.handle().bindVertexBuffers(
+							0u, // TODO: Should the first binding really always be 0?
+							static_cast<uint32_t>(N), handles.data(), offsets.data()
+						);
+						cb.handle().drawIndirectCount(lParametersBufferHandle, aParametersOffset, lDrawCountBufferHandle, aDrawCountOffset, aMaxNumberOfDraws, aParametersStride);
+					}
+				};
+			}
+		}
+
 		/**	Draw vertices with vertex buffer bindings starting at BUFFER-BINDING #0 top to the number of total buffers passed -1.
 		 *	"BUFFER-BINDING" means that it corresponds to the binding specified in `input_binding_location_data::from_buffer_at_binding`.
 		 *	There can be no gaps between buffer bindings.
diff --git a/src/avk.cpp b/src/avk.cpp
index d15e219..db05209 100644
--- a/src/avk.cpp
+++ b/src/avk.cpp
@@ -2856,12 +2856,15 @@ namespace avk
 	void command_buffer_t::prepare_for_reuse()
 	{
 		if (mPostExecutionHandler.has_value()) {
-			// Clear post-execution handler
+			// If there is a post-execution handler => call it now:
+			invoke_post_execution_handler();
+			// Clear post-execution handler:
 			mPostExecutionHandler.reset();
 		}
 		if (mCustomDeleter.has_value() && *mCustomDeleter) {
-			// If there is a custom deleter => call it now
+			// If there is a custom deleter => call it now:
 			(*mCustomDeleter)();
+			// Clear custom deleter:
 			mCustomDeleter.reset();
 		}
 		mLifetimeHandledResources.clear();

From eea56041034c1afdf1275370522a0a5c400f961e Mon Sep 17 00:00:00 2001
From: Johannes Unterguggenberger <johannes.unterguggenberger@gmail.com>
Date: Thu, 14 Mar 2024 12:16:22 +0100
Subject: [PATCH 2/3] Using std::filesystem for getting file size instead of
 unreliable tellg() (#94)

* Using std::filesystem to load binary files
---
 include/avk/cpp_utils.hpp | 41 ++++++++++++++-------------------------
 1 file changed, 15 insertions(+), 26 deletions(-)

diff --git a/include/avk/cpp_utils.hpp b/include/avk/cpp_utils.hpp
index f253c57..5020789 100644
--- a/include/avk/cpp_utils.hpp
+++ b/include/avk/cpp_utils.hpp
@@ -69,39 +69,28 @@ namespace avk
 	}
 
 	/**	Load a binary file into memory.
-	 *	Adapted from: http://www.cplusplus.com/reference/istream/istream/read/
+	 *	Adapted from: https://coniferproductions.com/posts/2022/10/25/reading-binary-files-cpp/
 	 */
 	static std::vector<char> load_binary_file(std::string path)
 	{
-		std::vector<char> buffer;
-		std::ifstream is(path.c_str(), std::ifstream::binary);
-		if (!is) {
-			throw avk::runtime_error("Couldn't load file '" + path + "'");
+		try {
+			std::filesystem::path inputFilePath{path};
+			auto length = std::filesystem::file_size(inputFilePath);
+			if (length == 0) {
+			    throw avk::runtime_error("Couldn't load file '" + path + "'. It appears to be empty.");
+			}
+			std::vector<char> buffer(((length + 3) / 4) * 4);
+			std::ifstream inputFile(path, std::ios_base::binary);
+			inputFile.read(reinterpret_cast<char*>(buffer.data()), length);
+			inputFile.close();
+	    	return buffer;
 		}
-
-		// get length of file:
-		is.seekg(0, is.end);
-		size_t length = is.tellg();
-		is.seekg(0, is.beg);
-
-		buffer.resize(length);
-
-		// read data as a block:
-		is.read(buffer.data(), length);
-
-		if (!is) {
-			is.close();
-			throw avk::runtime_error(
-				"Couldn't read file '" + path + "' into buffer. " +
-				"load_binary_file could only read " + std::to_string(is.gcount()) + " bytes instead of " + std::to_string(length)
-			);
+		catch (std::filesystem::filesystem_error& e)
+		{
+			throw avk::runtime_error("Couldn't load file '" + path + "'. Reason: " + e.what());
 		}
-
-		is.close();
-		return buffer;
 	}
 
-
 	static std::string trim_spaces(std::string_view s)
 	{
 		if (s.empty()) {

From a260d0bf282402603bbff50de56d452af6a4ebb1 Mon Sep 17 00:00:00 2001
From: Johannes Unterguggenberger <johannes.unterguggenberger@gmail.com>
Date: Thu, 14 Mar 2024 12:17:43 +0100
Subject: [PATCH 3/3] Just settling on Vulkan 1.3 SDK as the baseline
 requirement

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 88d6acf..01c9141 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ I.e., the big, important concepts, which make Vulkan as performant as it can be-
 # Setup
 
 _Auto-Vk_ requires
-* A Vulkan 1.2 SDK or a Vulkan 1.3 SDK
+* A Vulkan 1.3 SDK
 * [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp)
 * A C++20 compiler