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