feat(bindless): Define constructs for pipeline building

This commit is contained in:
PancakeTAS 2026-04-25 19:44:39 +02:00
parent 602f571c1d
commit f1147c3ae4
No known key found for this signature in database
4 changed files with 338 additions and 0 deletions

View file

@ -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 <algorithm>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <numeric>
#include <optional>
#include <ranges>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
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<size_t, 16> resources;
};
/// Signature of a pipeline stage
struct StageSignature {
/// Passes executed this stage
inplace_vector<size_t, 8> passes;
};
///
/// Signature of a compute pipeline
///
struct PipelineSignature {
/// Shader names used by the pipeline (and if there are hdr variants)
inplace_vector<std::pair<std::string_view, bool>, 32> shaders;
/// Images used by the pipeline
inplace_vector<ImageSignature, 192> images;
/// Ordered set of bindings for the descriptor set
inplace_vector<BindingSignature, 192> descriptors;
/// Indexable list of all passes
inplace_vector<PassSignature, 100> passes;
/// Ordered list of stages, excecuted in sequence
inplace_vector<StageSignature, 100> stages;
/// Stage index where the command buffers are split
inplace_vector<size_t, 4> 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<ImageSignature> m_images;
std::vector<PassSignature> m_passes;
std::vector<size_t> m_splitIndices;
};
}

View file

@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <new>
#include <stdexcept>
@ -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<std::pair<uint32_t, uint32_t>, 8> m_operations;
};
}

View file

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#pragma once
#include "helpers.hpp"
#include <cstddef>
#include <cstdint>
#include <utility>
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<int>(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<int>(flag)};
}
/// Match with another flag
constexpr ImageFlags operator&(ImageFlag flag) const {
return{this->m_flags & static_cast<int>(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<int>(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<size_t, size_t> lifetime;
};
}

View file

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#pragma once
#include "helpers.hpp"
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string_view>
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<int>(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<int>(flag)};
}
/// Match with another flag
constexpr PassFlags operator&(PassFlag flag) const {
return{this->m_flags & static_cast<int>(flag)};
}
private:
int m_flags{static_cast<int>(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<size_t> m_idx{0};
std::optional<uint32_t> 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<Resource, 8> inputs;
/// Resources to write to
inplace_vector<Resource, 8> outputs;
/// Operation applied to the base extent for calculating the dispatch extent
ExtentOp dispatchOp;
};
}