Skip to content

Commit

Permalink
Dynamic rendering (#92)
Browse files Browse the repository at this point in the history
* Modified graphics pipeline so that it can now be created with dynamic rendering

* Use KHR types to fix build issues with Vulkan 1.2

* Added begin and end rendering commands

* Fix bugs in begin_dynamic_rendering()

* Apply suggestions from code review

Changing local variables naming to lowerCamelCase

Co-authored-by: Johannes Unterguggenberger <[email protected]>

* Add fixes to issues raised in pr review:
- removed dynamic_rendering_attachment
- added declare_dynamic(_for) functions to avk::attachment which create dynamic rendering attachments
- changed graphics_pipeline_t::renderpass_pointer() back to renderpass_reference() which now returns a reference wrapper

* PR proposed fixes (mostly cosmetic) + inital draft of MSAA support (needs to be discussed further)

* Modified dynamic rendering attachments to take in subpass_usages and modified the dynamic functionality to conform to these usages

* Added View mask to begin dynamic rendering parameters

* Trying to fix GitHub workflows

* Trying to fix GitHub workflows

* Using Invoke-WebRequest instead of curl

* Reverted the last change

* I have no idea what I'm doing

* Moving two vulkan-hpp includes a bit down, namely:
#include <vulkan/vulkan_enums.hpp>
#include <vulkan/vulkan_structs.hpp>

* Testing ["latest", "1.3.216.0", "1.2.198.1"] also in windows-latest

* Using vk::ImageLayout::eDepthStencilAttachmentOptimal instead of vk::ImageLayout::eAttachmentOptimal

* Disable VMA=ON on Linux with 1.2 SDK

---------

Co-authored-by: MatejSakmary <[email protected]>
  • Loading branch information
johannesugb and MatejSakmary authored Apr 12, 2024
1 parent a260d0b commit 2e437e0
Show file tree
Hide file tree
Showing 9 changed files with 523 additions and 74 deletions.
49 changes: 26 additions & 23 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ jobs:
#}
- {
name: "linux: gcc",
cc: "gcc-10",
cxx: "g++-10"
cc: "gcc-13",
cxx: "g++-13"
}
# 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.3.204", "1.3.216"]
vulkan-sdk: ["latest", "1.3.216", "1.2.198"]
vma: ["ON", "OFF"]
exclude: # exclude combinations that are known to fail
- vulkan-sdk: "1.3.204"
- vulkan-sdk: "1.2.198"
vma: "ON"

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Create Build Environment
shell: bash
Expand All @@ -55,13 +55,18 @@ jobs:
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-${{ matrix.vulkan-sdk }}-focal.list https://packages.lunarg.com/vulkan/${{ matrix.vulkan-sdk }}/lunarg-vulkan-${{ matrix.vulkan-sdk }}-focal.list
fi
# For GCC-13
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/ppa
# Update package lists
sudo apt-get update -qq
# Install dependencies
sudo apt-get install -y \
vulkan-sdk
vulkan-sdk \
g++-13 \
gcc-13
# clang does not yet (<= 12) support "Down with typename" (P0634R3), which is used in libstdc++ >= 11 in the ranges-header
if [ "${{ matrix.config.cc }}" = "clang" ]; then
sudo apt remove libgcc-11-dev gcc-11
Expand All @@ -86,7 +91,7 @@ jobs:
shell: bash
working-directory: ${{ runner.workspace }}/build
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE
run: cmake --build .

windows:
name: ${{ matrix.config.name }}, windows-2019, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
Expand All @@ -103,18 +108,18 @@ jobs:
cxx: "cl"
}
# note: if a specific vulkan version needs testing, you can add it here:
vulkan-sdk: ["latest", "1.3.204.1", "1.3.216.0"]
vulkan-sdk: ["latest", "1.3.216.0", "1.2.198.1"]
vma: ["ON", "OFF"]
exclude: # exclude combinations that are known to fail
- vulkan-sdk: "1.2.198"
vma: "ON"

steps:
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
- uses: actions/checkout@v2
# apparently checkout@v3 pulls to Auto-Vk/Auto-Vk on windows
- uses: actions/checkout@v3

- name: Create Build Environment
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
# apparently checkout@v3 pulls to Auto-Vk/Auto-Vk on windows
working-directory: ${{ runner.workspace }}/${{ github.event.repository.name }}
shell: pwsh
# Some projects don't allow in-source building, so create a separate build directory
Expand Down Expand Up @@ -155,10 +160,10 @@ jobs:
- name: Build
shell: bash
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
# apparently checkout@v3 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 . --config $BUILD_TYPE
run: VULKAN_SDK=${{ env.vulkan_sdk }} cmake --build . --config $BUILD_TYPE

windows-latest:
name: ${{ matrix.config.name }}, windows-latest, VulkanSDK ${{ matrix.vulkan-sdk }}, VMA=${{ matrix.vma }}
Expand All @@ -175,18 +180,18 @@ jobs:
cxx: "cl"
}
# note: if a specific vulkan version needs testing, you can add it here:
vulkan-sdk: ["latest", "1.3.204.1", "1.3.216.0"]
vulkan-sdk: ["latest", "1.3.216.0", "1.2.198.1"]
vma: ["ON", "OFF"]
exclude: # exclude combinations that are known to fail
- vulkan-sdk: "1.2.198"
vma: "ON"

steps:
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
- uses: actions/checkout@v2
# apparently checkout@v3 pulls to Auto-Vk/Auto-Vk on windows
- uses: actions/checkout@v3

- name: Create Build Environment
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
# apparently checkout@v3 pulls to Auto-Vk/Auto-Vk on windows
working-directory: ${{ runner.workspace }}/${{ github.event.repository.name }}
shell: pwsh
# Some projects don't allow in-source building, so create a separate build directory
Expand All @@ -202,7 +207,7 @@ jobs:
mkdir "${{ env.vulkan_sdk }}Include/vma/"
curl -LS -o "${{ env.vulkan_sdk }}Include/vma/vk_mem_alloc.h" https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/blob/master/include/vk_mem_alloc.h?raw=true
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
# apparently checkout@v3 pulls to Auto-Vk/Auto-Vk on windows
cmake -E make_directory ${{ runner.workspace }}/${{ github.event.repository.name }}/build
- name: Configure CMake
Expand All @@ -217,17 +222,15 @@ jobs:
run: |
$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
$env:Path += ";${{ env.vulkan_sdk }}\;${{ env.vulkan_sdk }}\Bin\"
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
- name: Build
shell: bash
# apparently checkout@v2 pulls to Auto-Vk/Auto-Vk on windows
# apparently checkout@v3 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 . --config $BUILD_TYPE
5 changes: 2 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.16)
project(avk)

if (MSVC)
# <ranges> support requires /std:c++latest on MSVC
set(CMAKE_CXX_STANDARD 23)
add_compile_options(/bigobj)
set(CMAKE_CXX_STANDARD 20)
else (MSVC)
set(CMAKE_CXX_STANDARD 20)
endif (MSVC)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ avk::framebuffer framebuffer = myRoot.create_framebuffer(...);
avk::semaphore imageAvailableSemaphore = ...;

mRoot.record({
avk::command::render_pass(graphicsPipeline->renderpass_reference(), framebuffer.as_reference(), {
avk::command::render_pass(graphicsPipeline->renderpass_reference().value(), framebuffer.as_reference(), {
avk::command::bind_pipeline(graphicsPipeline.as_reference()),
avk::command::draw(3u, 1u, 0u, 0u)
})
Expand Down
32 changes: 32 additions & 0 deletions include/avk/attachment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ namespace avk
*/
static attachment declare_for(const image_view_t& aImageView, attachment_load_config aLoadOp, subpass_usages aUsageInSubpasses, attachment_store_config aStoreOp);

/** Declare multisampled format of an attachment for a dynamic rendering pipeline. This attachment can only be used with pipeline that has dynamic rendering enabled.
* It has fewer parameters than regular attachment since some of its values (load/store ops etc...) are set when starting the dynamic render pass
* as opposed to being declared beforehand..
* @param aFormatAndSamples Multisampled format definition: A tuple with the format of the attachment in its first element, and with the number of samples in its second element.
* @param aUsage How is this attachment being used in the renderpass? In contrast to non-dynamic attachments, this usage can only contain a single subpass as such
* Possible values in namespace avk::usage::
* Usages for different subpasses can be defined by concatenating them using operator>>.
* Example 1: avk::usage::color(0) // Indicates that this attachment is used as color attachment at location=0 in the renderpass
* Example 2: avk::usage::color(2) + usage::resolve_to(3) // Indicates that this attachment is used as color attachment at location=2 in the renderpass
* // Additionally, at the end of renderpass, its contents are resolved into the attachment at index 3.
* Example 3: usage::unused // Indicates that this attachment is unused in the renderpass (it will only be used as a resolve target for example)
*/
static attachment declare_dynamic(std::tuple<vk::Format, vk::SampleCountFlagBits> aFormatAndSamples, subpass_usages aUsage);

/** Declare multisampled format of an attachment for a dynamic rendering pipeline. This attachment can only be used with pipeline that has dynamic rendering enabled.
* It has fewer parameters than regular attachment since some of its values (load/store ops etc...) are set when starting the dynamic render pass
* as opposed to being declared beforehand..
* @param aFormat The format of the attachment
*/
static attachment declare_dynamic(vk::Format aFormat, subpass_usages aUsage);

/** Declare multisampled format of an attachment for a dynamic rendering pipeline. This attachment can only be used with pipeline that has dynamic rendering enabled.
* It has fewer parameters than regular attachment since some of its values (load/store ops etc...) are set when starting the dynamic render pass
* as opposed to being declared beforehand..
* @param aImageView The format of the attachment is copied from the given image view.
*/
static attachment declare_dynamic_for(const image_view_t& aImageView, subpass_usages aUsage);


attachment& set_clear_color(std::array<float, 4> aColor) { mColorClearValue = aColor; return *this; }
attachment& set_depth_clear_value(float aDepthClear) { mDepthClearValue = aDepthClear; return *this; }
attachment& set_stencil_clear_value(uint32_t aStencilClear) { mStencilClearValue = aStencilClear; return *this; }
Expand Down Expand Up @@ -88,6 +117,8 @@ namespace avk
auto sample_count() const { return mSampleCount; }
/** True if a multisample resolve pass shall be set up. */
auto is_to_be_resolved() const { return mSubpassUsages.contains_resolve(); }
/** True if this attachment is declared for dynamic rendering pipelines ie. using one of the dynamic declare functions*/
bool is_for_dynamic_rendering() const { return mDynamicRenderingAttachment; }

/** Returns the stencil load operation */
auto get_stencil_load_op() const { return mStencilLoadOperation.value_or(mLoadOperation); }
Expand All @@ -108,5 +139,6 @@ namespace avk
std::array<float, 4> mColorClearValue;
float mDepthClearValue;
uint32_t mStencilClearValue;
bool mDynamicRenderingAttachment;
};
}
47 changes: 36 additions & 11 deletions include/avk/avk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,12 @@ namespace avk
* @param aAlterConfigBeforeCreation Optional custom callback function which can be used to alter the new pipeline's config right before it is being created on the device.
* @return A new graphics pipeline instance.
*/
graphics_pipeline create_graphics_pipeline_from_template(const graphics_pipeline_t& aTemplate, renderpass aNewRenderpass, std::optional<cfg::subpass_index> aSubpassIndex = {}, std::function<void(graphics_pipeline_t&)> aAlterConfigBeforeCreation = {});
graphics_pipeline create_graphics_pipeline_from_template(const graphics_pipeline_t& aTemplate, std::optional<renderpass> aNewRenderpass, std::optional<cfg::subpass_index> aSubpassIndex = {}, std::function<void(graphics_pipeline_t&)> aAlterConfigBeforeCreation = {});

/** Creates a graphics pipeline based on another graphics pipeline, which serves as a template,
* which either uses the same renderpass (if it has shared ownership enabled) or creates a new
* renderpass internally using create_renderpass_from_template with the template's renderpass.
/** Creates a graphics pipeline based on another graphics pipeline, which serves as a template, which either:
* - uses the same renderpass (if it has shared ownership enabled)
* - creates a new renderpass internally using create_renderpass_from_template with the template's renderpass
* - uses dynamic rendering and thus does not need to create new renderpass
* @param aTemplate Another, already existing graphics pipeline, which serves as a template for the newly created graphics pipeline.
* @param aAlterConfigBeforeCreation Optional custom callback function which can be used to alter the new pipeline's config right before it is being created on the device.
* @return A new graphics pipeline instance.
Expand All @@ -728,6 +729,7 @@ namespace avk
* - cfg::pipeline_settings (flags)
* - renderpass
* - avk::attachment (use either attachments or renderpass!)
* - avk::dynamic_rendering_attachment (only use if dynamic_rendering::enabled)
* - input_binding_location_data (vertex input)
* - cfg::primitive_topology
* - shader_info
Expand All @@ -736,6 +738,7 @@ namespace avk
* - cfg::depth_write
* - cfg::viewport_depth_scissors_config
* - cfg::culling_mode
* - cfg::dynamic_rendering
* - cfg::front_face
* - cfg::polygon_drawing
* - cfg::rasterizer_geometry_mode
Expand All @@ -759,17 +762,39 @@ namespace avk
std::function<void(graphics_pipeline_t&)> alterConfigFunction;
graphics_pipeline_config config;
add_config(config, renderPassAttachments, alterConfigFunction, std::move(args)...);

// Check if render pass attachments are in renderPassAttachments XOR config => only in that case, it is clear how to proceed, fail in other cases
if (renderPassAttachments.size() > 0 == (config.mRenderPassSubpass.has_value() && static_cast<bool>(std::get<renderpass>(*config.mRenderPassSubpass)->handle()))) {
if (renderPassAttachments.size() == 0) {
throw avk::runtime_error("No renderpass config provided! Please provide a renderpass or attachments!");
bool hasRenderPassAttachments = false;
bool hasDynamicRenderingAttachments = false;
for(const auto & attachment : renderPassAttachments)
{
if(attachment.is_for_dynamic_rendering())
{
hasDynamicRenderingAttachments = true;
} else {
hasRenderPassAttachments = true;
}
throw avk::runtime_error("Ambiguous renderpass config! Either set a renderpass XOR provide attachments!");
}

const bool hasValidRenderPass = config.mRenderPassSubpass.has_value() && static_cast<bool>(std::get<renderpass>(*config.mRenderPassSubpass)->handle());
const bool isDynamicRenderingSet = config.mDynamicRendering == avk::cfg::dynamic_rendering::enabled;
// Check all invalid configurations when dynamic rendering is set
if (isDynamicRenderingSet )
{
if(hasValidRenderPass) { throw avk::runtime_error("Dynamic rendering does not accept renderpasses! They are set dynamically during rendering!"); }
if(hasRenderPassAttachments) { throw avk::runtime_error("Only avk::attachments created by declare_dynamic(_for) functions are allowed when dynamic rendering is enabled!"); }
if(!hasDynamicRenderingAttachments) { throw avk::runtime_error("Dynamic rendering enabled but no avk::attachmenst created by declare_dynamic(_for) functions provided! Please provide at least one attachment!"); }
}
// Check all invalid configurations when normal rendering (with renderpasses) is used
else
{
if(hasValidRenderPass && hasRenderPassAttachments) { throw avk::runtime_error("Ambiguous renderpass config! Either set a renderpass OR provide attachments but not both at the same time!"); }
if(!(hasValidRenderPass || hasRenderPassAttachments)) { throw avk::runtime_error("No renderpass config provided! Please provide a renderpass or attachments!"); }
}

// ^ that was the sanity check. See if we have to build the renderpass from the attachments:
if (renderPassAttachments.size() > 0) {
if (hasRenderPassAttachments) {
add_config(config, renderPassAttachments, alterConfigFunction, create_renderpass(std::move(renderPassAttachments)));
} else {
config.mDynamicRenderingAttachments = std::move(renderPassAttachments);
}

// 2. CREATE PIPELINE according to the config
Expand Down
26 changes: 26 additions & 0 deletions include/avk/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "buffer.hpp"
#include "image.hpp"
#include <vulkan/vulkan_structs.hpp>

namespace avk
{
Expand Down Expand Up @@ -506,6 +507,19 @@ namespace avk
};
}

/** Begins dynamic rendering scope
* @param aAttachemnts attachments used in during this dynamic rendering pass
* @param aImageViews image views bound for each attachment
* @param aRenderAreaOffset Render area offset (default is (0,0), i.e., no offset)
* @param aRenderAreaExtent Render area extent (default is full extent inferred from images passed in aImageViews)
* @param aLayerCount number of layers that will be used for rendering (default is 1)
*/
extern action_type_command begin_dynamic_rendering(std::vector<attachment> aAttachments, std::vector<image_view> aImageViews, vk::Offset2D aRenderAreaOffset = {0, 0}, std::optional<vk::Extent2D> aRenderAreaExtent = {}, uint32_t aLayerCount = 1, uint32_t aViewMask = 0);

/** Ends dynamic rendering scope
*/
extern action_type_command end_dynamic_rendering();

/** Begins a render pass for a given framebuffer
* @param aRenderpass Renderpass which shall begin (auto lifetime handling not supported by this command)
* @param aFramebuffer Framebuffer to use with the renderpass (auto lifetime handling not supported by this command)
Expand All @@ -515,6 +529,7 @@ namespace avk
*/
extern action_type_command begin_render_pass_for_framebuffer(const renderpass_t& aRenderpass, const framebuffer_t& aFramebuffer, vk::Offset2D aRenderAreaOffset = { 0, 0 }, std::optional<vk::Extent2D> aRenderAreaExtent = {}, bool aSubpassesInline = true);


/** Ends a render pass
*/
extern action_type_command end_render_pass();
Expand All @@ -536,6 +551,17 @@ namespace avk
bool aSubpassesInline = true
);

/** Begins dynamic rendering and supports nested commands in between
* @param aNestedCommands Nested commands to be recorded between begin and end
* @param aRenderAreaOffset Render area offset (default is (0,0), i.e., no offset)
* @param aRenderAreaExtent Render area extent (default is full extent)
*/
// extern action_type_command dynamic_renderpass(
// std::vector<recorded_commands_t> aNestedCommands = {},
// vk::Offset2D aRenderAreaOffset = {0, 0},
// std::optional<vk::Extent2D> aRenderAreaExtent = {}
// );

/** Advances to the next subpass within a render pass.
*/
extern action_type_command next_subpass(bool aSubpassesInline = true);
Expand Down
Loading

0 comments on commit 2e437e0

Please sign in to comment.