Skip to content

Commit

Permalink
Modified graphics pipeline so that it can now be created with dynamic…
Browse files Browse the repository at this point in the history
… rendering
  • Loading branch information
MatejSakmary committed Oct 27, 2023
1 parent c904add commit dcdd084
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 47 deletions.
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_pointer().value(), framebuffer.as_reference(), {
avk::command::bind_pipeline(graphicsPipeline.as_reference()),
avk::command::draw(3u, 1u, 0u, 0u)
})
Expand Down
33 changes: 33 additions & 0 deletions include/avk/attachment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@

namespace avk
{

/** Describes a dynamic rendering attachment. It can only be used with pipeline that has dynamic rendering enabled.
* It has fewever 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.
* It can describe color attachments as well as depth/stencil attachments
* and holds some additional config parameters for these attachments.
*/
struct dynamic_rendering_attachment
{
/** Declare multisampled format of an attachment for a dynamic rendering pipeline
* @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.
*/
static dynamic_rendering_attachment declare(std::tuple<vk::Format, vk::SampleCountFlagBits> aFormatAndSamples);
/** Declare format of an attachment for a dynamic rendering pipeline
* @param aFormat The format of the attachment
*/
static dynamic_rendering_attachment declare(vk::Format aFormat);

/** Declare format of an attachment for a dynamic rendering pipeline
* @param aImageView The format of the attachment is copied from the given image view.
*/
static dynamic_rendering_attachment declare_for(const image_view_t& aImageView);

/** The color/depth/stencil format of the attachment */
auto format() const { return mFormat; }
/** True if the sample count is greater than 1 */
bool is_multisampled() const { return mSampleCount != vk::SampleCountFlagBits::e1; }
/** The sample count for this attachment. */
auto sample_count() const { return mSampleCount; }

vk::Format mFormat;
vk::SampleCountFlagBits mSampleCount;
};
/** Describes an attachment to a framebuffer or a renderpass.
* It can describe color attachments as well as depth/stencil attachments
* and holds some additional config parameters for these attachments.
Expand Down
37 changes: 26 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 @@ -760,15 +763,27 @@ namespace avk
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!");
}
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 hasRenderPassAttachments = renderPassAttachments.size() > 0;
const bool isDynamicRenderingSet = config.mDynamicRendering == avk::cfg::dynamic_rendering::enabled;
// .has_value() should be enough since I don't fill in the optional unless there was dynamic_rendering_attachment provided
const bool hasDynamicRenderingAttachments = config.mDynamicRenderingAttachments.has_value();
// 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("Usage of avk::attachment is not allowed when dynamic rendering is enabled! Use avk::dynamic_rendering_attachment instead!"); }
if(!hasDynamicRenderingAttachments) { throw avk::runtime_error("Dynamic rendering enabled but no avk::dynamic_rendering_attachments 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)));
}

Expand Down
28 changes: 23 additions & 5 deletions include/avk/graphics_pipeline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,25 @@ namespace avk
graphics_pipeline_t& operator=(const graphics_pipeline_t&) = delete;
~graphics_pipeline_t() = default;

[[nodiscard]] avk::renderpass renderpass() const { return mRenderPass; }
[[nodiscard]] const avk::renderpass_t& renderpass_reference() const { return mRenderPass.get(); }
auto renderpass_handle() const { return mRenderPass->handle(); }
auto subpass_id() const { return mSubpassIndex; }
[[nodiscard]] auto renderpass() const { return mRenderPass; }
// TODO(msakmary) I can also keep the consistent naming aka renderpass_reference() return std::optional<std::reference_wrapper>> I feel like this is way cleaner?
[[nodiscard]] auto renderpass_pointer() const -> std::optional<const avk::renderpass_t *>
{
if(mRenderPass.has_value()) { return &(mRenderPass.value().get()); }
else { return std::nullopt; }
}
// TODO(msakmary) Perhaps I just return std::optional<vk::RenderPass> here? It would probably be more readable then declval
auto renderpass_handle() const -> std::optional<decltype(std::declval<avk::renderpass_t>().handle())>
{
if(mRenderPass.has_value()) {return mRenderPass.value()->handle();}
else {return std::nullopt;}
}
auto subpass_id() const -> std::optional<uint32_t>
{
if(mRenderPass.has_value()) {return mSubpassIndex;}
// TODO(msakmary) change subpass index to int and make -1 invalid value or perhaps add on optional here?
else {return -1;}
};
auto& vertex_input_binding_descriptions() { return mOrderedVertexInputBindingDescriptions; }
auto& vertex_input_attribute_descriptions() { return mVertexInputAttributeDescriptions; }
auto& vertex_input_state_create_info() { return mPipelineVertexInputStateCreateInfo; }
Expand Down Expand Up @@ -69,7 +84,7 @@ namespace avk
const auto& handle() const { return mPipeline.get(); }

private:
avk::renderpass mRenderPass;
std::optional<avk::renderpass> mRenderPass;
uint32_t mSubpassIndex;
// The vertex input data:
std::vector<vk::VertexInputBindingDescription> mOrderedVertexInputBindingDescriptions;
Expand All @@ -85,6 +100,9 @@ namespace avk
std::vector<vk::Viewport> mViewports;
std::vector<vk::Rect2D> mScissors;
vk::PipelineViewportStateCreateInfo mViewportStateCreateInfo;
// Dynamic rendering state
std::vector<vk::Format> mDynamicRenderingColorFormats;
std::optional<vk::PipelineRenderingCreateInfo> mRenderingCreateInfo;
// Rasterization state:
vk::PipelineRasterizationStateCreateInfo mRasterizationStateCreateInfo;
// Depth stencil config:
Expand Down
32 changes: 31 additions & 1 deletion include/avk/graphics_pipeline_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ namespace avk
bool mDynamicScissorEnabled;
};

/** Dyanmic rendering*/
enum struct dynamic_rendering
{
disabled,
enabled
};

/** Pipeline configuration data: Culling Mode */
enum struct culling_mode
{
Expand Down Expand Up @@ -608,6 +615,7 @@ namespace avk
~graphics_pipeline_config() = default;

cfg::pipeline_settings mPipelineSettings; // TODO: Handle settings!
std::optional<std::vector<avk::dynamic_rendering_attachment>> mDynamicRenderingAttachments;
std::optional<std::tuple<renderpass, uint32_t>> mRenderPassSubpass;
std::vector<input_binding_to_location_mapping> mInputBindingLocations;
cfg::primitive_topology mPrimitiveTopology;
Expand All @@ -616,6 +624,7 @@ namespace avk
cfg::rasterizer_geometry_mode mRasterizerGeometryMode;
cfg::polygon_drawing mPolygonDrawingModeAndConfig;
cfg::culling_mode mCullingMode;
cfg::dynamic_rendering mDynamicRendering;
cfg::front_face mFrontFaceWindingOrder;
cfg::depth_clamp_bias mDepthClampBiasConfig;
cfg::depth_test mDepthTestConfig;
Expand Down Expand Up @@ -672,7 +681,20 @@ namespace avk
add_config(aConfig, aAttachments, aFunc, std::move(args)...);
}

// Add a renderpass attachment to the (temporary) attachments vector and build renderpass afterwards
// Add a dynamic rendering attachment which are used when dynamic_rendering is enabled for this pipeline
template <typename... Ts>
void add_config(graphics_pipeline_config& aConfig, std::vector<avk::attachment>& aAttachments, std::function<void(graphics_pipeline_t&)>& aFunc, avk::dynamic_rendering_attachment aDynAttachment, Ts... args)
{
if(aConfig.mDynamicRenderingAttachments.has_value())
{
aConfig.mDynamicRenderingAttachments.value().push_back(aDynAttachment);
} else {
aConfig.mDynamicRenderingAttachments = std::vector{aDynAttachment};
}
add_config(aConfig, aAttachments, aFunc, std::move(args)...);
}

// Add a renderpass attachment to the (temporary) attachments vector and later build renderpass from them
template <typename... Ts>
void add_config(graphics_pipeline_config& aConfig, std::vector<avk::attachment>& aAttachments, std::function<void(graphics_pipeline_t&)>& aFunc, avk::attachment aAttachment, Ts... args)
{
Expand Down Expand Up @@ -752,6 +774,14 @@ namespace avk
add_config(aConfig, aAttachments, aFunc, std::move(args)...);
}

// Set dynamic rendering
template <typename... Ts>
void add_config(graphics_pipeline_config& aConfig, std::vector<avk::attachment>& aAttachments, std::function<void(graphics_pipeline_t&)>& aFunc, cfg::dynamic_rendering aDynamicRendering, Ts... args)
{
aConfig.mDynamicRendering = aDynamicRendering;
add_config(aConfig, aAttachments, aFunc, std::move(args)...);
}

// Set the definition of front faces in the pipeline config
template <typename... Ts>
void add_config(graphics_pipeline_config& aConfig, std::vector<avk::attachment>& aAttachments, std::function<void(graphics_pipeline_t&)>& aFunc, cfg::front_face aFrontFace, Ts... args)
Expand Down
Loading

0 comments on commit dcdd084

Please sign in to comment.