diff --git a/lsfg-vk-backend/src/modules/pipeline/signature.hpp b/lsfg-vk-backend/src/modules/pipeline/signature.hpp new file mode 100644 index 0000000..8130307 --- /dev/null +++ b/lsfg-vk-backend/src/modules/pipeline/signature.hpp @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "signature/helpers.hpp" +#include "signature/image.hpp" +#include "signature/pass.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace lsfgvk::pipeline { + + /// Type of a descriptor set binding + enum class BindingType : uint8_t { + SampledImage, + StorageImage + }; + + /// Signature of a descriptor set binding + struct BindingSignature { + /// Type of binding + BindingType type{ BindingType::SampledImage }; + /// Resources attached to binding + inplace_vector resources; + }; + + /// Signature of a pipeline stage + struct StageSignature { + /// Passes executed this stage + inplace_vector passes; + }; + + /// + /// Signature of a compute pipeline + /// + struct PipelineSignature { + /// Shader names used by the pipeline (and if there are hdr variants) + inplace_vector, 32> shaders; + /// Images used by the pipeline + inplace_vector images; + /// Ordered set of bindings for the descriptor set + inplace_vector descriptors; + /// Indexable list of all passes + inplace_vector passes; + /// Ordered list of stages, excecuted in sequence + inplace_vector stages; + /// Stage index where the command buffers are split + inplace_vector splitIndices; + }; + + /// + /// The signature of a compute pipeline + /// + class PipelineSignatureBuilder { + public: + /// + /// Create a new empty signature builder + /// + explicit PipelineSignatureBuilder() = default; + + /// + /// Register an image + /// + /// @param image Image signature + /// @return Handle to the image + /// + consteval size_t registerImage(ImageSignature image) { + this->m_images.push_back(std::move(image)); + return this->m_images.size() - 1; + } + + /// + /// Append a pass + /// + /// @param pass Pass signature + /// @return Handle to the pass + /// + consteval size_t appendPass(PassSignature pass) { + this->m_passes.push_back(std::move(pass)); + return this->m_passes.size() - 1; + } + + /// + /// Split the command buffer + /// + consteval void split() { + this->m_splitIndices.emplace_back(this->m_passes.size()); + } + + /// + /// Compute a pipeline signature + /// + /// @throws const char* on failure + /// @return Pipeline siganture + /// + consteval PipelineSignature finalize() { + PipelineSignature s{}; + + /* TODO */ + + return s; + } + private: + std::vector m_images; + std::vector m_passes; + std::vector m_splitIndices; + }; + +} diff --git a/lsfg-vk-backend/src/modules/pipeline/signature/helpers.hpp b/lsfg-vk-backend/src/modules/pipeline/signature/helpers.hpp index 2674ea5..91b743e 100644 --- a/lsfg-vk-backend/src/modules/pipeline/signature/helpers.hpp +++ b/lsfg-vk-backend/src/modules/pipeline/signature/helpers.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -90,4 +91,39 @@ namespace lsfgvk::pipeline { #pragma clang diagnostic pop }; + /// Sequence of operations to apply to the base extent + class ExtentOp { + public: + /// Default constructor for no operations and no flow scaling + constexpr ExtentOp() = default; + /// Constructor for no operations aside from flow scale + constexpr ExtentOp(bool flow) : m_flow(flow) {} + /// Constructor for a single operation + constexpr ExtentOp(bool flow, uint32_t add, uint32_t shift) + : m_flow(flow), m_operations({{add, shift}}) {} + /// Constructor for a single operation starting from the flow base extent + constexpr ExtentOp(uint32_t add, uint32_t shift) + : m_flow(true), m_operations({{add, shift}}) {} + // Combine two extents + constexpr ExtentOp operator+(const ExtentOp& other) const { + ExtentOp result{*this}; + for (const auto& [add, shift] : other.m_operations) + result.m_operations.emplace_back(add, shift); + return result; + } + // Combine two extends + constexpr ExtentOp operator+=(const ExtentOp& other) { + for (const auto& [add, shift] : other.m_operations) + this->m_operations.emplace_back(add, shift); + return *this; + } + /// Get the flow value + [[nodiscard]] constexpr auto flow() const { return this->m_flow; } + /// Get the operations + [[nodiscard]] constexpr const auto& operations() const { return this->m_operations; } + private: + bool m_flow{false}; + inplace_vector, 8> m_operations; + }; + } diff --git a/lsfg-vk-backend/src/modules/pipeline/signature/image.hpp b/lsfg-vk-backend/src/modules/pipeline/signature/image.hpp new file mode 100644 index 0000000..b9aec58 --- /dev/null +++ b/lsfg-vk-backend/src/modules/pipeline/signature/image.hpp @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "helpers.hpp" + +#include +#include +#include + +namespace lsfgvk::pipeline { + + /// All supported image formats + enum class Format : char { + /// Invalid format + Invalid = 0, + /// 8-bit unsigned normalized RGBA format + RGBA8888 = 37, // VK_FORMAT_R8G8B8A8_UNORM + /// 8-bit unsigned normalized R format + R8 = 9, // VK_FORMAT_R8_UNORM + /// 16-bit signed floating point RGBA format + RGBA16161616 = 97, // VK_FORMAT_R16G16B16A16_SFLOAT + }; + + /// All supported image flags + enum class ImageFlag : char { + /// No special flags + None = 0, + /// Instead of using a single image array, create several individual images with halving + /// extends for each mip level. + /// + /// This will cause the image to show up as Texture2D[], rather than Texture2DArray + /// and must therefore not be used in full with passes where the "Aggregate" flag is set. + Mipmaps = 1 << 0, + /// Indicate that the image is pinned & not transient + Pinned = 1 << 1, + /// Indicate that this image is written to externally + ExternalInput = 1 << 2, + /// Indicate that this image is read from externally + ExternalOutput = 1 << 3, + /// Indicate that a separate format should be used for HDR + HdrVariant = 1 << 4 + }; + + /// Helper type for operating on image flags + class ImageFlags { + public: + /// Default constructor + constexpr ImageFlags() = default; + /// Create from single image flag + constexpr ImageFlags(ImageFlag flag) : m_flags(static_cast(flag)) {} + /// Check any set of flags + constexpr operator bool() const { return m_flags != 0; } + /// Combine with another flag + constexpr ImageFlags operator|(ImageFlag flag) const { + return{this->m_flags | static_cast(flag)}; + } + /// Match with another flag + constexpr ImageFlags operator&(ImageFlag flag) const { + return{this->m_flags & static_cast(flag)}; + } + /// Match with another flag instance + constexpr ImageFlags operator&(ImageFlags other) const { + return{this->m_flags & other.m_flags}; + } + private: + int m_flags{static_cast(ImageFlag::None)}; + + // Create from number + constexpr ImageFlags(int flags) : m_flags(flags) {} + }; + + /// Compine two image flags + constexpr ImageFlags operator|(ImageFlag lhs, ImageFlag rhs) { + return ImageFlags(lhs) | rhs; + } + + /// Signature for an image + struct ImageSignature { + /// Format of the image + Format format{ Format::RGBA8888 }; + /// Optional second format for HDR variants + Format hdrFormat{ Format::RGBA16161616 }; + /// Optional flags for the image + ImageFlags flags{ ImageFlag::None }; + /// Operation applied to the base extent for calculating the image extent + ExtentOp extentOp; + /// Amount of layers in the image + uint32_t count{1}; + + /// Lifetime of the image (set by pipeline builder) + std::pair lifetime; + }; + +} diff --git a/lsfg-vk-backend/src/modules/pipeline/signature/pass.hpp b/lsfg-vk-backend/src/modules/pipeline/signature/pass.hpp new file mode 100644 index 0000000..2a056eb --- /dev/null +++ b/lsfg-vk-backend/src/modules/pipeline/signature/pass.hpp @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "helpers.hpp" + +#include +#include +#include +#include + +namespace lsfgvk::pipeline { + + /// All supported pass flags + enum class PassFlag : char { + /// No special flags + None = 0, + /// Indicates the shader will be reused several times and resources must be + /// aggregated into arrays and indexed via push constants. + Aggregate = 1 << 0, + /// Indicate that the special flag is set via push constant. + Special = 1 << 1, + /// Indicate that there are two variants for 8-bit and 16-bit foramtrs + HdrVariant = 1 << 2 + }; + + /// Helper type for operating on pass flags + class PassFlags { + public: + /// Default constructor + constexpr PassFlags() = default; + /// Create from single pass flag + constexpr PassFlags(PassFlag flag) : m_flags(static_cast(flag)) {} + /// Check any set of flags + constexpr operator bool() const { return m_flags != 0; } + /// Combine with another flag + constexpr PassFlags operator|(PassFlag flag) const { + return{this->m_flags | static_cast(flag)}; + } + /// Match with another flag + constexpr PassFlags operator&(PassFlag flag) const { + return{this->m_flags & static_cast(flag)}; + } + private: + int m_flags{static_cast(PassFlag::None)}; + + // Create from number + constexpr PassFlags(int flags) : m_flags(flags) {} + }; + + /// Combine two pass flags + constexpr PassFlags operator|(PassFlag lhs, PassFlag rhs) { + return PassFlags(lhs) | rhs; + } + + /// A pointer to an image, or a specific layer inside that image + class Resource { + public: + /// Default constructor + constexpr Resource() = default; + /// Constructor for a full image + constexpr Resource(size_t idx) : m_idx(idx) {} + /// Constructor for a single layer + constexpr Resource(size_t idx, uint32_t layer) : m_idx(idx), m_layer(layer) {} + /// Get the flow value + [[nodiscard]] constexpr auto idx() const { return this->m_idx; } + /// Get the operations + [[nodiscard]] constexpr auto layer() const { return this->m_layer; } + private: + std::optional m_idx{0}; + std::optional m_layer; + }; + + /// Signature of a shader pass + struct PassSignature { + /// Name of the shader + std::string_view shader; + /// Optional flags of this pass + PassFlags flags{ PassFlag::None }; + /// Resources to read from + inplace_vector inputs; + /// Resources to write to + inplace_vector outputs; + /// Operation applied to the base extent for calculating the dispatch extent + ExtentOp dispatchOp; + }; + +}