TypesVk.h#

Contains generic functions used in multiple places through PVRVk.

Detailed Description#

Contains framework types.

PowerVR by Imagination, Developer Technology Team

Copyright (c) Imagination Technologies Limited.

Includes#

Included By#

Namespaces#

Classes#

Enums#

Functions#

Defines#

Variables#

Source Code#

#pragma once
#include "PVRVk/HeadersVk.h"
#include "CommonHelpers.h"
#include <memory>
#include <vector>
#include <algorithm>
#include <cassert>
#include <map>

namespace pvrvk {
template<typename ElementType, size_t staticSize>
class ArrayOrVector
{
private:
    std::array<ElementType, staticSize> _array;
    ElementType* _ptr;

public:
    ElementType& operator[](size_t idx) { return _ptr[idx]; }

    const ElementType& operator[](size_t idx) const { return _ptr[idx]; }

    ArrayOrVector(size_t numItemsRequired) : _array{}
    {
        if (numItemsRequired > staticSize) { _ptr = new ElementType[numItemsRequired](); }
        else
        {
            _ptr = _array.data();
        }
    }

    ~ArrayOrVector()
    {
        if (_ptr != _array.data()) { delete[] _ptr; }
    }

    ElementType* get() { return _ptr; }

    const ElementType* get() const { return _ptr; }
};

namespace FrameworkCaps {
enum Enum
{
    MaxColorAttachments = 8,
    MaxDepthStencilAttachments = 4,
    MaxInputAttachments = 8,
    MaxResolveAttachments = 8,
    MaxPreserveAttachments = 8,
    MaxDescriptorSets = 8,
    MaxDescriptorSetBindings = 4,
    MaxDescriptorDynamicOffsets = 32,
    MaxScissorRegions = 8,
    MaxViewportRegions = 8,
    MaxScissorViewports = 8,
    MaxSpecialisationInfos = 7,
    MaxSpecialisationInfoDataSize = 1024,
    MaxVertexAttributes = 8,
    MaxVertexBindings = 8,
    MaxSafetyCriticalOfflineSwapchainNumber = 8,
};
} // namespace FrameworkCaps

// clang-format off
#define DEFINE_ENUM_OPERATORS(type_) \
inline type_ operator | (type_ lhs, type_ rhs) \
{ \
    return (type_)(static_cast<std::underlying_type<type_>::type>(lhs) | static_cast<std::underlying_type<type_>::type>(rhs)); \
} \
inline void operator |= (type_& lhs, type_ rhs) \
{ \
    lhs = (type_)(static_cast<std::underlying_type<type_>::type>(lhs) | static_cast<std::underlying_type<type_>::type>(rhs)); \
} \
inline type_ operator & (type_ lhs, type_ rhs) \
{ \
    return (type_)(static_cast<std::underlying_type<type_>::type>(lhs) & static_cast<std::underlying_type<type_>::type>(rhs)); \
} \
inline void operator &= (type_& lhs, type_ rhs) \
{ \
    lhs = (type_)(static_cast<std::underlying_type<type_>::type>(lhs) & static_cast<std::underlying_type<type_>::type>(rhs)); \
}
// clang-format on

#define DECLARE_NO_COPY_SEMANTICS(TYPE) \
    TYPE(const TYPE&) = delete; \
    const TYPE& operator=(const TYPE&) = delete;

namespace internal {
template<typename container, typename val, typename cmp>
size_t insertSorted_overwrite(container& cont, typename container::iterator begin, typename container::iterator end, const val& item, const cmp& compare)
{
    auto it = std::lower_bound(begin, end, item, compare);
    auto offset = static_cast<int64_t>(it - begin);
    if (it != end && !(compare(*it, item) || compare(item, *it))) { *it = item; }
    else
    {
        cont.insert(it, item);
    }
    return static_cast<size_t>(offset);
}

template<typename container, typename val>
size_t insertSorted_overwrite(container& cont, typename container::iterator begin, typename container::iterator end, const val& item)
{
    return insertSorted_overwrite(cont, begin, end, item, std::less<val>());
}

template<typename container, typename val>
size_t insertSorted_overwrite(container& cont, const val& item)
{
    return insertSorted_overwrite(cont, cont.begin(), cont.end(), item);
}

template<typename container, typename val, typename cmp>
size_t insertSorted_overwrite(container& cont, const val& item, const cmp& compare)
{
    return insertSorted_overwrite(cont, cont.begin(), cont.end(), item, compare);
}
} // namespace internal

const uint32_t SubpassExternal = ~0u;

namespace GpuDatatypesHelper {
enum class BaseType
{
    Integer = 0,
    Float = 1
};

enum class VectorWidth
{
    Scalar = 0,
    Vec2 = 1,
    Vec3 = 2,
    Vec4 = 3,
};

enum class MatrixColumns
{
    OneCol = 0,
    Mat2x = 1,
    Mat3x = 2,
    Mat4x = 3
};

// clang-format off
enum class Bits : uint64_t
{
    Integer = 0, Float = 1,
    BitScalar = 0, BitVec2 = 2, BitVec3 = 4, BitVec4 = 6,
    BitOneCol = 0, BitMat2x = 8, BitMat3x = 16, BitMat4x = 24,
    ShiftType = 0, MaskType = 1, NotMaskType = static_cast<uint64_t>(~MaskType),
    ShiftVec = 1, MaskVec = (3 << ShiftVec), NotMaskVec = static_cast<uint64_t>(~MaskVec),
    ShiftCols = 3, MaskCols = (3 << ShiftCols), NotMaskCols = static_cast<uint64_t>(~MaskCols)
};
// clang-format on
DEFINE_ENUM_OPERATORS(Bits)
} // namespace GpuDatatypesHelper

// clang-format off
enum class GpuDatatypes : uint64_t
{
    Integer = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Integer) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitScalar) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol), uinteger = Integer, boolean = Integer,
    Float = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitScalar) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol),
    ivec2 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Integer) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec2) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol), uvec2 = ivec2, bvec2 = ivec2,
    ivec3 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Integer) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec3) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol), uvec3 = ivec3, bvec3 = ivec3,
    ivec4 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Integer) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec4) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol), uvec4 = ivec4, bvec4 = ivec4,
    vec2 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec2) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol),
    vec3 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec3) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol),
    vec4 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec4) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitOneCol),
    mat2x2 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec2) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat2x),
    mat2x3 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec3) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat2x),
    mat2x4 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec4) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat2x),
    mat3x2 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec2) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat3x),
    mat3x3 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec3) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat3x),
    mat3x4 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec4) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat3x),
    mat4x2 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec2) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat4x),
    mat4x3 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec3) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat4x),
    mat4x4 = static_cast<uint64_t>(GpuDatatypesHelper::Bits::Float) | static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitVec4) |
    static_cast<uint64_t>(GpuDatatypesHelper::Bits::BitMat4x),
    none = 0xFFFFFFFF,
    structure = none
};
// clang-format on

enum class DataType
{
    None,
    Float32,
    Int32,
    UInt16,
    RGBA,
    ARGB,
    D3DCOLOR,
    UBYTE4,
    DEC3N,
    Fixed16_16,
    UInt8,
    Int16,
    Int16Norm,
    Int8,
    Int8Norm,
    UInt8Norm,
    UInt16Norm,
    UInt32,
    ABGR,
    Float16,
    Custom = 1000
};

struct Color
{
private:
    float color[4];

public:
    Color(float r = 0.f, float g = 0.f, float b = 0.f, float a = 1.f)
    {
        color[0] = r;
        color[1] = g;
        color[2] = b;
        color[3] = a;
    }

    Color(const float rgba[4])
    {
        color[0] = rgba[0];
        color[1] = rgba[1];
        color[2] = rgba[2];
        color[3] = rgba[3];
    }

    float getR() const { return color[0]; }

    float getG() const { return color[1]; }

    float getB() const { return color[2]; }

    float getA() const { return color[3]; }
};

inline void appendPNext(VkBaseInStructure* baseStructure, const void* newPNext)
{
    auto currentStructure = baseStructure;
    while (currentStructure->pNext != nullptr) { currentStructure = const_cast<VkBaseInStructure*>(currentStructure->pNext); }
    currentStructure->pNext = (VkBaseInStructure*)newPNext;
}

struct ClearColorValue
{
private:
    VkClearColorValue color;

public:
    ClearColorValue()
    {
        color.float32[0] = color.float32[1] = color.float32[2] = 0;
        color.float32[3] = 1;
    }

    ClearColorValue(float r, float g, float b, float a)
    {
        color.float32[0] = r;
        color.float32[1] = g;
        color.float32[2] = b;
        color.float32[3] = a;
    }

    ClearColorValue(int32_t r, int32_t g, int32_t b, int32_t a)
    {
        color.int32[0] = r;
        color.int32[1] = g;
        color.int32[2] = b;
        color.int32[3] = a;
    }

    ClearColorValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
    {
        color.uint32[0] = r;
        color.uint32[1] = g;
        color.uint32[2] = b;
        color.uint32[3] = a;
    }

    float getR() const { return color.float32[0]; }

    void setR(const float r) { this->color.float32[0] = r; }

    float getG() const { return color.float32[1]; }

    void setG(const float g) { this->color.float32[1] = g; }

    float getB() const { return color.float32[2]; }

    void setB(const float b) { this->color.float32[2] = b; }

    float getA() const { return color.float32[3]; }

    void setA(const float a) { this->color.float32[3] = a; }

    int32_t getRInt() const { return color.int32[0]; }

    void setR(const int32_t r) { this->color.int32[0] = r; }

    int32_t getGInt() const { return color.int32[1]; }

    void setG(const int32_t g) { this->color.int32[1] = g; }

    int32_t getBInt() const { return color.int32[2]; }

    void setB(const int32_t b) { this->color.int32[2] = b; }

    int32_t getAInt() const { return color.int32[3]; }

    void setA(const int32_t a) { this->color.int32[3] = a; }

    uint32_t getRUint() const { return color.uint32[0]; }

    void setRUint(const uint32_t r) { this->color.uint32[0] = r; }

    uint32_t getGUint() const { return color.uint32[1]; }

    void setGUint(const uint32_t g) { this->color.uint32[1] = g; }

    uint32_t getBUint() const { return color.uint32[2]; }

    void setBUint(const uint32_t b) { this->color.uint32[2] = b; }

    uint32_t getAUint() const { return color.uint32[3]; }

    void setAUint(const uint32_t a) { this->color.uint32[3] = a; }

    const VkClearColorValue& getColor() const { return color; }
};

struct ImageLayersSize
{
private:
    uint32_t numArrayLevels;
    uint32_t numMipLevels;

public:
    ImageLayersSize() : numArrayLevels(1), numMipLevels(1) {}

    ImageLayersSize(uint32_t numArrayLevels, uint32_t numMipLevels) : numArrayLevels(numArrayLevels), numMipLevels(numMipLevels) {}

    inline uint32_t getNumArrayLevels() const { return numArrayLevels; }

    inline void setNumArrayLevels(const uint32_t& inNumArrayLevels) { this->numArrayLevels = inNumArrayLevels; }

    inline uint32_t getNumMipLevels() const { return numMipLevels; }

    inline void setNumMipLevels(const uint32_t& inNumMipLevels) { this->numMipLevels = inNumMipLevels; }
};

struct ImageAreaSize : public Extent3D, public ImageLayersSize
{
    ImageAreaSize() = default;

    ImageAreaSize(const ImageLayersSize& layersSize, const Extent3D& extents) : Extent3D(extents), ImageLayersSize(layersSize) {}

    ImageAreaSize(const ImageLayersSize& layersSize, const Extent2D& extents) : Extent3D(extents.getWidth(), extents.getHeight(), 1), ImageLayersSize(layersSize) {}
};

struct ImageAreaOffset : public ImageSubresource, public Offset3D
{
    ImageAreaOffset() = default;

    ImageAreaOffset(const ImageSubresource& baseLayers, const Offset3D& offset) : ImageSubresource(baseLayers), Offset3D(offset) {}
};

struct Offset2Df
{
private:
    float x;
    float y;

public:
    Offset2Df()
    {
        setX(float());
        setY(float());
    }
    Offset2Df(float x, float y)
    {
        setX(x);
        setY(y);
    }
    inline float getX() const { return x; }
    inline void setX(float inX) { this->x = inX; }
    inline float getY() const { return y; }
    inline void setY(float inY) { this->y = inY; }
};

struct Extent2Df
{
private:
    float width;
    float height;

public:
    Extent2Df()
    {
        setWidth(float());
        setHeight(float());
    }
    Extent2Df(float width, float height)
    {
        setWidth(width);
        setHeight(height);
    }
    inline float getWidth() const { return width; }
    inline void setWidth(float inWidth) { this->width = inWidth; }
    inline float getHeight() const { return height; }
    inline void setHeight(float inHeight) { this->height = inHeight; }
};

struct Rect2Df
{
private:
    Offset2Df offset;
    Extent2Df extent;

public:
    Rect2Df(float myx, float myy, float mywidth, float myheight) : offset(myx, myy), extent(mywidth, myheight) {}

    Rect2Df() : offset(0.0f, 0.0f), extent(1.0f, 1.0f) {}

    inline const Offset2Df& getOffset() const { return offset; }

    inline void setOffset(const Offset2Df& inOffset) { this->offset = inOffset; }

    inline const Extent2Df& getExtent() const { return extent; }

    inline void setExtent(const Extent2Df& inExtent) { this->extent = inExtent; }
};

struct ApplicationInfo
{
private:
    std::string applicationName;
    uint32_t applicationVersion;
    std::string engineName;
    uint32_t engineVersion;
    uint32_t apiVersion;

public:
    ApplicationInfo() : applicationName("PVRVkExample"), applicationVersion(0), engineName("PVRVk"), engineVersion(0), apiVersion(VK_MAKE_VERSION(1, 0, 0)) {}

    ApplicationInfo(const std::string& applicationName, uint32_t applicationVersion = 0, const std::string& engineName = nullptr, uint32_t engineVersion = 0, uint32_t apiVersion = 0)
    {
        setApplicationName(applicationName);
        setApplicationVersion(applicationVersion);
        setEngineName(engineName);
        setEngineVersion(engineVersion);
        setApiVersion(apiVersion);
    }

    inline const std::string& getApplicationName() const { return applicationName; }
    inline void setApplicationName(const std::string& inApplicationName) { this->applicationName = inApplicationName; }
    inline uint32_t getApplicationVersion() const { return applicationVersion; }
    inline void setApplicationVersion(uint32_t inApplicationVersion) { this->applicationVersion = inApplicationVersion; }
    inline const std::string& getEngineName() const { return engineName; }
    inline void setEngineName(const std::string& inEngineName) { this->engineName = inEngineName; }
    inline uint32_t getEngineVersion() const { return engineVersion; }
    inline void setEngineVersion(uint32_t inEngineVersion) { this->engineVersion = inEngineVersion; }
    inline uint32_t getApiVersion() const { return apiVersion; }
    inline void setApiVersion(uint32_t inApiVersion) { this->apiVersion = inApiVersion; }
};

struct DeviceQueueCreateInfo
{
private:
    uint32_t queueFamilyIndex;
    std::vector<float> queuePriorities;

public:
    DeviceQueueCreateInfo() { setQueueFamilyIndex(static_cast<uint32_t>(-1)); }
    DeviceQueueCreateInfo(uint32_t queueFamilyIndex, uint32_t queueCount)
    {
        setQueueFamilyIndex(queueFamilyIndex);
        for (uint32_t i = 0; i < queueCount; i++) { addQueue(); }
    }

    DeviceQueueCreateInfo(uint32_t queueFamilyIndex, const std::vector<float>& queuePriorities)
    {
        setQueueFamilyIndex(queueFamilyIndex);
        this->queuePriorities.resize(queuePriorities.size());
        std::copy(queuePriorities.begin(), queuePriorities.end(), this->queuePriorities.begin());
    }

    inline uint32_t getQueueFamilyIndex() const { return queueFamilyIndex; }
    inline void setQueueFamilyIndex(uint32_t inQueueFamilyIndex) { this->queueFamilyIndex = inQueueFamilyIndex; }
    inline const uint32_t getNumQueues() const { return static_cast<uint32_t>(queuePriorities.size()); }
    inline const std::vector<float>& getQueuePriorities() const { return queuePriorities; }
    inline std::vector<float>& getQueuePriorities() { return queuePriorities; }
    inline float getQueuePriority(uint32_t index) const { return queuePriorities[index]; }
    inline void addQueue(float priority = 1.0f) { queuePriorities.emplace_back(priority); }
    inline void addQueueAtIndex(uint32_t index, float priority = 1.0f) { queuePriorities[index] = priority; }
    inline void clearQueues() { queuePriorities.clear(); }
};

struct VulkanExtension
{
public:
    VulkanExtension(std::string name = "", uint32_t specVersion = -1) : _name(name), _specVersion(specVersion) {}

    inline const std::string& getName() const { return _name; }

    inline void setName(std::string name) { this->_name = name; }

    inline uint32_t getSpecVersion() const { return _specVersion; }

    inline void setSpecVersion(uint32_t specVersion) { this->_specVersion = specVersion; }

    bool operator==(const VulkanExtension& rhs) const { return getName() == rhs.getName() && getSpecVersion() == rhs.getSpecVersion(); }

private:
    std::string _name;
    uint32_t _specVersion;
};

struct VulkanLayer
{
public:
    VulkanLayer(const std::string& name = "", uint32_t specVersion = -1, uint32_t implementationVersion = -1, const std::string& description = "")
        : _name(name), _specVersion(specVersion), _implementationVersion(implementationVersion), _description(description)
    {}

    inline const std::string& getName() const { return _name; }

    inline void setName(std::string name) { this->_name = name; }

    inline uint32_t getSpecVersion() const { return _specVersion; }

    inline void setSpecVersion(uint32_t specVersion) { this->_specVersion = specVersion; }

    inline uint32_t getImplementationVersion() const { return _implementationVersion; }

    inline void setImplementationVersion(uint32_t implementationVersion) { this->_implementationVersion = implementationVersion; }

    inline const std::string& getDescription() const { return _description; }

    inline void setDescription(std::string description) { this->_description = description; }

    bool operator==(const VulkanLayer& rhs) const { return getName() == rhs.getName() && getSpecVersion() == rhs.getSpecVersion(); }

private:
    std::string _name;
    uint32_t _specVersion;
    uint32_t _implementationVersion;
    std::string _description;
};

struct VulkanExtensionList
{
public:
    VulkanExtensionList() = default;

    inline uint32_t getNumExtensions() const { return static_cast<uint32_t>(_extensions.size()); }
    inline const std::vector<VulkanExtension>& getExtensions() const { return _extensions; }
    inline const VulkanExtension& getExtension(uint32_t index) const { return _extensions[index]; }
    inline void setExtensions(const std::vector<VulkanExtension>& extensions)
    {
        this->_extensions.resize(extensions.size());
        std::copy(extensions.begin(), extensions.end(), this->_extensions.begin());
    }
    inline void addExtension(const VulkanExtension& extension) { this->_extensions.emplace_back(extension); }
    inline void addExtension(const std::string& extensionName) { this->_extensions.emplace_back(VulkanExtension(extensionName)); }
    inline void removeExtension(const VulkanExtension& extension)
    {
        this->_extensions.erase(std::remove(this->_extensions.begin(), this->_extensions.end(), extension), this->_extensions.end());
    }
    inline void removeExtension(const std::string& extensionName)
    {
        auto new_end =
            std::remove_if(this->_extensions.begin(), this->_extensions.end(), [extensionName](const VulkanExtension& extension) { return extension.getName() == extensionName; });
        this->_extensions.erase(new_end, this->_extensions.end());
    }

    inline void removeAllExtensions() { this->_extensions.clear(); }

    bool containsExtension(const std::string& extensionName) const
    {
        return std::find_if(_extensions.begin(), _extensions.end(), [extensionName](VulkanExtension const& extension) { return extension.getName() == extensionName; }) !=
            _extensions.end();
    }

    bool containsExtension(const VulkanExtension& extension) const { return std::find(_extensions.begin(), _extensions.end(), extension) != _extensions.end(); }

    inline void* getLastRequestedExtensionFeature() const { return _lastRequestedExtensionFeature; }

    template<class T>
    void addExtensionFeatureVk(VkStructureType type, T* extension)
    {
        // Insert the extension feature into the extension feature map so its ownership is held
        _extensionFeatures.insert({ type, (void*)extension });

        // Pull out the dereferenced void pointer, we can assume its type based on the template
        T* extensionPtr = static_cast<T*>(_extensionFeatures.find(type)->second);

        // If an extension feature has already been requested, we shift the linked list down by one
        // Making this current extension the new base pointer
        if (_lastRequestedExtensionFeature) { extensionPtr->pNext = _lastRequestedExtensionFeature; }
        _lastRequestedExtensionFeature = extensionPtr;
    }

    template<class T>
    void addExtensionFeatureVk(T* extension)
    {
        // Get the structure type from the sType member
        VkStructureType type = static_cast<VkStructureType>(extension->sType);

        // Use this structure type to make a call to the above function
        addExtensionFeatureVk(type, extension);
    }

protected:
    std::vector<pvrvk::VulkanExtension> _extensions;
    std::map<VkStructureType, void*> _extensionFeatures;
    void* _lastRequestedExtensionFeature{ nullptr };
};

struct VulkanLayerList
{
public:
    VulkanLayerList() = default;

    inline uint32_t getNumLayers() const { return static_cast<uint32_t>(_layers.size()); }
    inline const std::vector<VulkanLayer>& getLayers() const { return _layers; }
    inline const VulkanLayer& getLayer(uint32_t index) const { return _layers[index]; }
    inline void setLayers(const std::vector<VulkanLayer>& layers)
    {
        this->_layers.resize(layers.size());
        std::copy(layers.begin(), layers.end(), this->_layers.begin());
    }
    inline void addLayer(const VulkanLayer& layer) { this->_layers.emplace_back(layer); }
    inline void addLayer(const std::string& layerName) { this->_layers.emplace_back(VulkanLayer(layerName)); }
    inline void removeLayer(const VulkanLayer& layer) { this->_layers.erase(std::remove(this->_layers.begin(), this->_layers.end(), layer), this->_layers.end()); }
    inline void removeLayer(const std::string& layerName)
    {
        auto new_end = std::remove_if(this->_layers.begin(), this->_layers.end(), [layerName](const VulkanLayer& layer) { return layer.getName() == layerName; });
        this->_layers.erase(new_end, this->_layers.end());
    }

    inline void removeAllLayers() { _layers.clear(); }

    bool containsLayer(const std::string& layerName) const
    {
        return std::find_if(_layers.begin(), _layers.end(), [layerName](VulkanLayer const& layer) { return layer.getName() == layerName; }) != _layers.end();
    }

    bool containsLayer(const VulkanLayer& layer) const { return std::find(_layers.begin(), _layers.end(), layer) != _layers.end(); }

    bool containsLayerByName(const std::string& layerName) const
    {
        for (size_t i = 0; i < _layers.size(); ++i)
        {
            if (_layers[i].getName() == layerName) { return true; }
        }

        return false;
    }

private:
    std::vector<pvrvk::VulkanLayer> _layers;
};

struct DeviceCreateInfo
{
private:
    DeviceCreateFlags flags;
    std::vector<DeviceQueueCreateInfo> queueCreateInfos;
    VulkanExtensionList enabledExtensions;
    const PhysicalDeviceFeatures* enabledFeatures;
    void* lastRequestedExtensionFeature;
    bool isSafetyCritical;
    VkDeviceObjectReservationCreateInfo reservationCreateInfo;

public:
    explicit DeviceCreateInfo(const std::vector<DeviceQueueCreateInfo>& queueCreateInfos = std::vector<DeviceQueueCreateInfo>(),
        const VulkanExtensionList& enabledExtensions = VulkanExtensionList(), const PhysicalDeviceFeatures* enabledFeatures = nullptr, DeviceCreateFlags flags = DeviceCreateFlags::e_NONE)
        : flags(flags), enabledFeatures(enabledFeatures), lastRequestedExtensionFeature(nullptr), isSafetyCritical(false), reservationCreateInfo(VkDeviceObjectReservationCreateInfo{})
    {
        setDeviceQueueCreateInfos(queueCreateInfos);
        setExtensionList(enabledExtensions);
    }

    inline const DeviceCreateFlags& getFlags() const { return flags; }

    inline void setFlags(const DeviceCreateFlags& inFlags) { this->flags = inFlags; }

    inline uint32_t getNumDeviceQueueCreateInfos() const { return static_cast<uint32_t>(queueCreateInfos.size()); }

    inline const std::vector<DeviceQueueCreateInfo>& getDeviceQueueCreateInfos() const { return queueCreateInfos; }

    inline const DeviceQueueCreateInfo& getDeviceQueueCreateInfo(uint32_t index) const { return queueCreateInfos[index]; }

    inline DeviceQueueCreateInfo& getDeviceQueueCreateInfo(uint32_t index) { return queueCreateInfos[index]; }

    inline void setDeviceQueueCreateInfos(const std::vector<DeviceQueueCreateInfo>& inQueueCreateInfos)
    {
        this->queueCreateInfos.resize(inQueueCreateInfos.size());
        std::copy(inQueueCreateInfos.begin(), inQueueCreateInfos.end(), this->queueCreateInfos.begin());
    }
    inline void addDeviceQueue(DeviceQueueCreateInfo deviceQueueCreateInfo = DeviceQueueCreateInfo()) { queueCreateInfos.emplace_back(deviceQueueCreateInfo); }

    inline void addDeviceQueueAtIndex(uint32_t index, DeviceQueueCreateInfo deviceQueueCreateInfo = DeviceQueueCreateInfo()) { queueCreateInfos[index] = deviceQueueCreateInfo; }

    inline void clearDeviceQueueCreateInfos() { queueCreateInfos.clear(); }

    inline const VulkanExtensionList& getExtensionList() const { return enabledExtensions; }

    inline void setExtensionList(const VulkanExtensionList& inEnabledExtensions) { this->enabledExtensions = inEnabledExtensions; }

    inline const PhysicalDeviceFeatures* getEnabledFeatures() const { return enabledFeatures; }

    inline void setEnabledFeatures(const PhysicalDeviceFeatures* inEnabledFeatures) { this->enabledFeatures = inEnabledFeatures; }

    inline void setLastRequestedExtensionFeature(void* inLastRequestedExtensionFeature) { this->lastRequestedExtensionFeature = inLastRequestedExtensionFeature; }

    inline const void* getLastRequestedExtensionFeature() const { return lastRequestedExtensionFeature; }

    inline void setIsSafetyCritical(bool InIsSafetyCritical) { this->isSafetyCritical = InIsSafetyCritical; }

    inline const bool getIsSafetyCritical() const { return isSafetyCritical; }

    // VkDeviceObjectReservationCreateInfo reservationCreateInfo;
    inline void setDeviceObjectReservationCreateInfo(VkDeviceObjectReservationCreateInfo inReservationCreateInfo) { this->reservationCreateInfo = inReservationCreateInfo; }

    const VkDeviceObjectReservationCreateInfo& getDeviceObjectReservationCreateInfo() const { return reservationCreateInfo; }
};

struct ClearValue
{
private:
    char bytes[16];

public:
    ClearValue()
    {
        float one = 1.f;
        memset(bytes, 0, 12);
        memcpy(bytes + 12, &one, 4);
    }

    ClearValue(float depth, uint32_t stencil) { setDepthStencilValue(depth, stencil); }

    ClearValue(float r, float g, float b, float a) { setColorValue(r, g, b, a); }

    ClearValue(int32_t r, int32_t g, int32_t b, int32_t a)
    {
        memcpy(bytes, &r, sizeof(int32_t));
        memcpy(bytes + sizeof(int32_t), &g, sizeof(int32_t));
        memcpy(bytes + sizeof(int32_t) * 2, &b, sizeof(int32_t));
        memcpy(bytes + sizeof(int32_t) * 3, &a, sizeof(int32_t));
    }

    ClearValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
    {
        memcpy(bytes, &r, sizeof(uint32_t));
        memcpy(bytes + sizeof(uint32_t), &g, sizeof(uint32_t));
        memcpy(bytes + sizeof(uint32_t) * 2, &b, sizeof(uint32_t));
        memcpy(bytes + sizeof(uint32_t) * 3, &a, sizeof(uint32_t));
    }

    void setColorValue(float r, float g, float b, float a)
    {
        memcpy(bytes, &r, sizeof(float));
        memcpy(bytes + sizeof(float), &g, sizeof(float));
        memcpy(bytes + sizeof(float) * 2, &b, sizeof(float));
        memcpy(bytes + sizeof(float) * 3, &a, sizeof(float));
    }

    void setDepthStencilValue(float depth = 1.f, uint32_t stencil = 0u)
    {
        memcpy(bytes, &depth, sizeof(float));
        memcpy(bytes + sizeof(float), &stencil, sizeof(uint32_t));
    }

    VkClearValue toVkValue()
    {
        VkClearValue value;
        static_assert(sizeof(value) == sizeof(bytes), "VkClearValue wrong alignment");
        memcpy(&value, bytes, sizeof(value));
        return value;
    }

    const VkClearValue toVkValue() const
    {
        VkClearValue value;
        static_assert(sizeof(value) == sizeof(bytes), "VkClearValue wrong alignment");
        memcpy(&value, bytes, sizeof(value));
        return value;
    }

    static ClearValue createDefaultDepthStencilClearValue() { return ClearValue(1.f, 0u); }

    static ClearValue createStencilClearValue(uint32_t stencil) { return ClearValue(1.f, stencil); }

    static ClearValue createDepthStencilClearValue(float depth, uint32_t stencil) { return ClearValue(depth, stencil); }
};

struct ClearAttachment : private VkClearAttachment
{
    ClearAttachment() = default;

    ClearAttachment(ImageAspectFlags aspectMask, uint32_t colorAttachment, const ClearValue& clearValue)
    {
        this->aspectMask = static_cast<VkImageAspectFlags>(aspectMask);
        this->colorAttachment = colorAttachment;
        this->clearValue = clearValue.toVkValue();
    }

    static ClearAttachment createStencilClearAttachment(uint32_t stencil)
    {
        return ClearAttachment(ImageAspectFlags::e_STENCIL_BIT, 0, ClearValue::createStencilClearValue(stencil));
    }

    static ClearAttachment createDepthStencilClearAttachment(float depth, uint32_t stencil)
    {
        return ClearAttachment(ImageAspectFlags::e_DEPTH_BIT | ImageAspectFlags::e_STENCIL_BIT, 0, ClearValue::createDepthStencilClearValue(depth, stencil));
    }

    static ClearAttachment createColorClearAttachment(uint32_t colorAttachment, const ClearValue& clearValue)
    {
        return ClearAttachment(ImageAspectFlags::e_COLOR_BIT, colorAttachment, clearValue);
    }
    inline ImageAspectFlags getAspectMask() const { return static_cast<ImageAspectFlags>(aspectMask); }

    inline void setAspectMask(const ImageAspectFlags& inAspectMask) { this->aspectMask = static_cast<VkImageAspectFlags>(inAspectMask); }

    inline uint32_t getColorAttachment() const { return colorAttachment; }

    inline void setColorAttachment(const uint32_t& inColorAttachment) { this->colorAttachment = inColorAttachment; }

    inline ClearValue getClearValue() const
    {
        ClearValue retval;
        memcpy(&retval, &clearValue, sizeof(ClearValue));
        return retval;
    }

    inline void setClearValue(const ClearValue& inClearValue) { memcpy(&this->clearValue, &inClearValue, sizeof(ClearValue)); }
};

struct AttachmentDescription : private VkAttachmentDescription
{
    AttachmentDescription()
    {
        this->flags = static_cast<VkAttachmentDescriptionFlags>(AttachmentDescriptionFlags::e_NONE);
        this->format = static_cast<VkFormat>(pvrvk::Format::e_UNDEFINED);
        this->initialLayout = static_cast<VkImageLayout>(pvrvk::ImageLayout::e_UNDEFINED);
        this->finalLayout = static_cast<VkImageLayout>(pvrvk::ImageLayout::e_UNDEFINED);
        this->loadOp = static_cast<VkAttachmentLoadOp>(pvrvk::AttachmentLoadOp::e_CLEAR);
        this->storeOp = static_cast<VkAttachmentStoreOp>(pvrvk::AttachmentStoreOp::e_STORE);
        this->stencilLoadOp = static_cast<VkAttachmentLoadOp>(pvrvk::AttachmentLoadOp::e_CLEAR);
        this->stencilStoreOp = static_cast<VkAttachmentStoreOp>(pvrvk::AttachmentStoreOp::e_STORE);
        this->samples = static_cast<VkSampleCountFlagBits>(pvrvk::SampleCountFlags::e_1_BIT);
    }

    AttachmentDescription(const pvrvk::Format format, const pvrvk::ImageLayout initialLayout, const pvrvk::ImageLayout finalLayout,
        const pvrvk::AttachmentLoadOp loadOp, const pvrvk::AttachmentStoreOp storeOp, const pvrvk::AttachmentLoadOp stencilLoadOp = pvrvk::AttachmentLoadOp::e_DONT_CARE,
        const pvrvk::AttachmentStoreOp stencilStoreOp = pvrvk::AttachmentStoreOp::e_DONT_CARE, const pvrvk::SampleCountFlags numSamples = pvrvk::SampleCountFlags::e_1_BIT)
    {
        this->flags = static_cast<VkAttachmentDescriptionFlags>(AttachmentDescriptionFlags::e_NONE);
        this->format = static_cast<VkFormat>(format);
        this->initialLayout = static_cast<VkImageLayout>(initialLayout);
        this->finalLayout = static_cast<VkImageLayout>(finalLayout);
        this->loadOp = static_cast<VkAttachmentLoadOp>(loadOp);
        this->storeOp = static_cast<VkAttachmentStoreOp>(storeOp);
        this->stencilLoadOp = static_cast<VkAttachmentLoadOp>(stencilLoadOp);
        this->stencilStoreOp = static_cast<VkAttachmentStoreOp>(stencilStoreOp);
        this->samples = static_cast<VkSampleCountFlagBits>(numSamples);
    }

    static AttachmentDescription createColorDescription(const pvrvk::Format format, const pvrvk::ImageLayout initialLayout = pvrvk::ImageLayout::e_COLOR_ATTACHMENT_OPTIMAL,
        const pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_COLOR_ATTACHMENT_OPTIMAL, const pvrvk::AttachmentLoadOp loadOp = pvrvk::AttachmentLoadOp::e_CLEAR,
        const pvrvk::AttachmentStoreOp storeOp = pvrvk::AttachmentStoreOp::e_STORE, const pvrvk::SampleCountFlags numSamples = pvrvk::SampleCountFlags::e_1_BIT)
    {
        return AttachmentDescription(format, initialLayout, finalLayout, loadOp, storeOp, AttachmentLoadOp::e_DONT_CARE, AttachmentStoreOp::e_DONT_CARE, numSamples);
    }

    static AttachmentDescription createDepthStencilDescription(const pvrvk::Format format, const pvrvk::ImageLayout initialLayout = pvrvk::ImageLayout::e_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
        const pvrvk::ImageLayout finalLayout = pvrvk::ImageLayout::e_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, const pvrvk::AttachmentLoadOp loadOp = pvrvk::AttachmentLoadOp::e_CLEAR,
        const pvrvk::AttachmentStoreOp storeOp = pvrvk::AttachmentStoreOp::e_DONT_CARE, const pvrvk::AttachmentLoadOp stencilLoadOp = pvrvk::AttachmentLoadOp::e_CLEAR,
        const pvrvk::AttachmentStoreOp stencilStoreOp = pvrvk::AttachmentStoreOp::e_DONT_CARE, const pvrvk::SampleCountFlags numSamples = pvrvk::SampleCountFlags::e_1_BIT)
    {
        return AttachmentDescription(format, initialLayout, finalLayout, loadOp, storeOp, stencilLoadOp, stencilStoreOp, numSamples);
    }

    inline AttachmentDescriptionFlags getFlags() const { return static_cast<AttachmentDescriptionFlags>(flags); }
    inline void setFlags(const AttachmentDescriptionFlags& inFlags) { this->flags = static_cast<VkAttachmentDescriptionFlags>(inFlags); }
    inline Format getFormat() const { return static_cast<Format>(format); }
    inline void setFormat(const Format& inFormat) { this->format = static_cast<VkFormat>(inFormat); }
    inline SampleCountFlags getSamples() const { return static_cast<SampleCountFlags>(samples); }
    inline void setSamples(const SampleCountFlags& inSamples) { this->samples = static_cast<VkSampleCountFlagBits>(inSamples); }
    inline AttachmentLoadOp getLoadOp() const { return static_cast<AttachmentLoadOp>(loadOp); }
    inline void setLoadOp(const AttachmentLoadOp& inLoadOp) { this->loadOp = static_cast<VkAttachmentLoadOp>(inLoadOp); }
    inline AttachmentStoreOp getStoreOp() const { return static_cast<AttachmentStoreOp>(storeOp); }
    inline void setStoreOp(const AttachmentStoreOp& inStoreOp) { this->storeOp = static_cast<VkAttachmentStoreOp>(inStoreOp); }
    inline AttachmentLoadOp getStencilLoadOp() const { return static_cast<AttachmentLoadOp>(stencilLoadOp); }
    inline void setStencilLoadOp(const AttachmentLoadOp& inStencilLoadOp) { this->stencilLoadOp = static_cast<VkAttachmentLoadOp>(inStencilLoadOp); }
    inline AttachmentStoreOp getStencilStoreOp() const { return static_cast<AttachmentStoreOp>(stencilStoreOp); }
    inline void setStencilStoreOp(const AttachmentStoreOp& inStencilStoreOp) { this->stencilStoreOp = static_cast<VkAttachmentStoreOp>(inStencilStoreOp); }
    inline ImageLayout getInitialLayout() const { return static_cast<ImageLayout>(initialLayout); }
    inline void setInitialLayout(const ImageLayout& inInitialLayout) { this->initialLayout = static_cast<VkImageLayout>(inInitialLayout); }
    inline ImageLayout getFinalLayout() const { return static_cast<ImageLayout>(finalLayout); }
    inline void setFinalLayout(const ImageLayout& inFinalLayout) { this->finalLayout = static_cast<VkImageLayout>(inFinalLayout); }
};

struct AttachmentReference2 : private VkAttachmentReference2
{
    AttachmentReference2()
    {
        this->sType = static_cast<VkStructureType>(StructureType::e_ATTACHMENT_REFERENCE_2);
        this->pNext = nullptr;
        this->attachment = VK_ATTACHMENT_UNUSED;
        this->layout = static_cast<VkImageLayout>(ImageLayout::e_UNDEFINED);
        this->aspectMask = static_cast<VkImageAspectFlags>(ImageAspectFlags::e_NONE);
    }

    AttachmentReference2(const uint32_t attachment, const ImageLayout layout, const ImageAspectFlags aspectMask)
    {
        this->sType = static_cast<VkStructureType>(StructureType::e_ATTACHMENT_REFERENCE_2);
        this->pNext = nullptr;
        this->attachment = attachment;
        this->layout = static_cast<VkImageLayout>(layout);
        this->aspectMask = static_cast<VkImageAspectFlags>(aspectMask);
    }

    VkAttachmentReference2 get() { return *this; }
    VkAttachmentReference2* getVkPtr() { return dynamic_cast<VkAttachmentReference2*>(this); }

    // setters
    void setAttachment(const uint32_t inAttachment) { this->attachment = inAttachment; }
    void setLayout(const ImageLayout inLayout) { this->layout = static_cast<VkImageLayout>(inLayout); }
    void setAspectMask(const ImageAspectFlags inAspectMask) { this->aspectMask = static_cast<VkImageAspectFlags>(inAspectMask); }

    // getters
    uint32_t getAttachment() const { return attachment; }
    ImageLayout getLayout() const { return static_cast<ImageLayout>(layout); }
    ImageAspectFlags getAspectMask() const { return static_cast<ImageAspectFlags>(aspectMask); }
};

struct FragmentShadingRateAttachmentInfo
{
private:
    bool _enabled;
    AttachmentReference2 _attachment;
    Extent2D _texelSize;

public:
    FragmentShadingRateAttachmentInfo() : _enabled(false) {}

    FragmentShadingRateAttachmentInfo(bool enabled, const AttachmentReference2& attachment, const Extent2D texelSize = Extent2D(1u, 1u))
        : _enabled(enabled), _attachment(attachment), _texelSize(texelSize)
    {}

    FragmentShadingRateAttachmentInfo(
        const bool enabled, const uint32_t attachment, const ImageLayout imageLayout, const ImageAspectFlags aspectMask, const Extent2D texelSize = Extent2D(1u, 1u))
        : _enabled(enabled), _attachment(AttachmentReference2(attachment, imageLayout, aspectMask)), _texelSize(texelSize)
    {}

    // setters
    FragmentShadingRateAttachmentInfo& setEnabled(const bool enabled)
    {
        this->_enabled = enabled;
        return *this;
    }
    FragmentShadingRateAttachmentInfo& setFragmentShadingRateAttachment(const AttachmentReference2& attachment)
    {
        this->_attachment = attachment;
        return *this;
    }
    FragmentShadingRateAttachmentInfo& setTexelSize(const Extent2D texelSize)
    {
        this->_texelSize = texelSize;
        return *this;
    }

    // getters
    bool getEnabled() const { return _enabled; }
    AttachmentReference2 getAttachment() const { return _attachment; }
    Extent2D getTexelSize() const { return _texelSize; }
};

struct SubpassDescription
{
public:
    SubpassDescription(pvrvk::PipelineBindPoint pipeBindPoint = pvrvk::PipelineBindPoint::e_GRAPHICS) : _pipelineBindPoint(pipeBindPoint), _fragmentShadingRateAttachment({}) {}

    SubpassDescription& setPipelineBindPoint(pvrvk::PipelineBindPoint bindingPoint)
    {
        _pipelineBindPoint = bindingPoint;
        return *this;
    }

    SubpassDescription& setColorAttachmentReference(uint32_t bindingIndex, const AttachmentReference& attachmentReference)
    {
        setElementAtIndex<AttachmentReference>(bindingIndex, attachmentReference, _colorAttachments);
        return *this;
    }

    SubpassDescription& setInputAttachmentReference(uint32_t bindingIndex, const AttachmentReference& attachmentReference)
    {
        setElementAtIndex<AttachmentReference>(bindingIndex, attachmentReference, _inputAttachments);
        return *this;
    }

    SubpassDescription& setResolveAttachmentReference(uint32_t bindingIndex, const AttachmentReference& attachmentReference)
    {
        setElementAtIndex<AttachmentReference>(bindingIndex, attachmentReference, _resolveAttachments);
        return *this;
    }

    SubpassDescription& setPreserveAttachmentReference(uint32_t bindingIndex, const uint32_t preserveAttachment)
    {
        setElementAtIndex<uint32_t>(bindingIndex, preserveAttachment, _preserveAttachments);
        return *this;
    }

    SubpassDescription& setDepthStencilAttachmentReference(const AttachmentReference& attachmentReference)
    {
        _depthStencilAttachment = attachmentReference;
        return *this;
    }

    SubpassDescription& setFragmentShadingRateAttachment(const FragmentShadingRateAttachmentInfo& attachmentInfo)
    {
        _fragmentShadingRateAttachment = attachmentInfo;
        return *this;
    }

    uint8_t getNumColorAttachmentReference() const { return static_cast<uint8_t>(_colorAttachments.size()); }

    uint8_t getNumInputAttachmentReference() const { return static_cast<uint8_t>(_inputAttachments.size()); }

    uint8_t getNumResolveAttachmentReference() const { return static_cast<uint8_t>(_resolveAttachments.size()); }

    uint8_t getNumPreserveAttachmentReference() const { return static_cast<uint8_t>(_preserveAttachments.size()); }

    pvrvk::PipelineBindPoint getPipelineBindPoint() const { return _pipelineBindPoint; }

    const AttachmentReference& getInputAttachmentReference(uint8_t index) const
    {
        assert(index < getNumInputAttachmentReference() && "Invalid input attachment index");
        return _inputAttachments[index];
    }

    const AttachmentReference& getDepthStencilAttachmentReference() const { return this->_depthStencilAttachment; }

    const AttachmentReference& getColorAttachmentReference(uint8_t index) const
    {
        assert(index < getNumColorAttachmentReference() && "Invalid color attachment index");
        return _colorAttachments[index];
    }

    const AttachmentReference& getResolveAttachmentReference(uint8_t index) const
    {
        assert(index < getNumResolveAttachmentReference() && "Invalid resolve attachment index");
        return _resolveAttachments[index];
    }

    uint32_t getPreserveAttachmentReference(uint8_t index) const
    {
        assert(index < getNumPreserveAttachmentReference() && "Invalid preserve attachment index");
        return _preserveAttachments[index];
    }

    const uint32_t* getAllPreserveAttachments() const { return _preserveAttachments.data(); }

    const FragmentShadingRateAttachmentInfo& getFragmentShadingRateAttachment() const { return _fragmentShadingRateAttachment; }

    SubpassDescription& clear()
    {
        _inputAttachments.clear();
        _colorAttachments.clear();
        _resolveAttachments.clear();
        _preserveAttachments.clear();
        return *this;
    }

private:
    pvrvk::PipelineBindPoint _pipelineBindPoint;
    std::vector<AttachmentReference> _inputAttachments;
    std::vector<AttachmentReference> _colorAttachments;
    std::vector<AttachmentReference> _resolveAttachments;
    std::vector<uint32_t> _preserveAttachments;
    AttachmentReference _depthStencilAttachment;
    FragmentShadingRateAttachmentInfo _fragmentShadingRateAttachment;
};

struct PipelineCacheCreateInfo
{
public:
    explicit PipelineCacheCreateInfo(size_t initialDataSize = 0, const void* pInitialData = nullptr, PipelineCacheCreateFlags flags = PipelineCacheCreateFlags::e_NONE)
        : _initialDataSize(initialDataSize), _pInitialData(pInitialData), _flags(flags)
    {}

    inline PipelineCacheCreateFlags getFlags() const { return _flags; }
    inline void setFlags(PipelineCacheCreateFlags flags) { this->_flags = flags; }
    inline size_t getInitialDataSize() const { return _initialDataSize; }
    inline void setInitialDataSize(size_t initialDataSize) { this->_initialDataSize = initialDataSize; }
    inline const void* getInitialData() const { return _pInitialData; }
    inline void setInitialData(const void* pInitialData) { this->_pInitialData = pInitialData; }

private:
    size_t _initialDataSize;
    const void* _pInitialData;
    PipelineCacheCreateFlags _flags;
};

struct EventCreateInfo
{
public:
    explicit EventCreateInfo(EventCreateFlags flags = EventCreateFlags::e_NONE) : _flags(flags) {}

    inline EventCreateFlags getFlags() const { return _flags; }
    inline void setFlags(EventCreateFlags flags) { this->_flags = flags; }

private:
    EventCreateFlags _flags;
};

struct FenceCreateInfo
{
public:
    FenceCreateInfo(FenceCreateFlags flags = FenceCreateFlags::e_NONE) : _flags(flags) {}

    inline FenceCreateFlags getFlags() const { return _flags; }
    inline void setFlags(FenceCreateFlags flags) { this->_flags = flags; }

private:
    FenceCreateFlags _flags;
};

struct SemaphoreCreateInfo
{
public:
    SemaphoreCreateInfo(SemaphoreCreateFlags flags = SemaphoreCreateFlags::e_NONE) : _flags(flags) {}

    inline SemaphoreCreateFlags getFlags() const { return _flags; }
    inline void setFlags(SemaphoreCreateFlags flags) { this->_flags = flags; }
    inline void setSemaphoreType(SemaphoreType semaphoreType) { this->_semaphoreType  = semaphoreType; }
    inline SemaphoreType getSemaphoreType() const { return this-> _semaphoreType; }

private:
    SemaphoreCreateFlags _flags;

    SemaphoreType _semaphoreType{SemaphoreType::e_BINARY};
};

struct ValidationFeatures
{
private:
    std::vector<ValidationFeatureEnableEXT> _enabledValidationFeatures;
    std::vector<ValidationFeatureDisableEXT> _disabledValidationFeatures;

public:
    ValidationFeatures() = default;

    inline void addEnabledValidationFeature(ValidationFeatureEnableEXT enabledFeature) { _enabledValidationFeatures.emplace_back(enabledFeature); }

    inline void addDisabledValidationFeature(ValidationFeatureDisableEXT disabledFeature) { _disabledValidationFeatures.emplace_back(disabledFeature); }

    inline uint32_t getNumEnabledValidationFeatures() const { return static_cast<uint32_t>(_enabledValidationFeatures.size()); }

    inline uint32_t getNumDisabledValidationFeatures() const { return static_cast<uint32_t>(_disabledValidationFeatures.size()); }

    inline const std::vector<ValidationFeatureEnableEXT>& getEnabledValidationFeatures() const { return _enabledValidationFeatures; }

    inline std::vector<ValidationFeatureEnableEXT>& getEnabledValidationFeatures() { return _enabledValidationFeatures; }

    inline const ValidationFeatureEnableEXT& getEnabledValidationFeature(uint32_t index) const { return _enabledValidationFeatures[index]; }

    inline const std::vector<ValidationFeatureDisableEXT>& getDisabledValidationFeatures() const { return _disabledValidationFeatures; }

    inline std::vector<ValidationFeatureDisableEXT>& getDisabledValidationFeatures() { return _disabledValidationFeatures; }

    inline const ValidationFeatureDisableEXT& getDisabledValidationFeature(uint32_t index) const { return _disabledValidationFeatures[index]; }

    inline void setEnabledValidationFeatures(const std::vector<ValidationFeatureEnableEXT>& enabledValidationFeatures)
    {
        this->_enabledValidationFeatures.resize(enabledValidationFeatures.size());
        std::copy(enabledValidationFeatures.begin(), enabledValidationFeatures.end(), this->_enabledValidationFeatures.begin());
    }

    inline void setDisabledValidationFeatures(const std::vector<ValidationFeatureDisableEXT>& disabledValidationFeatures)
    {
        this->_disabledValidationFeatures.resize(disabledValidationFeatures.size());
        std::copy(disabledValidationFeatures.begin(), disabledValidationFeatures.end(), this->_disabledValidationFeatures.begin());
    }

    inline void clearEnabledValidationFeatures() { _enabledValidationFeatures.clear(); }

    inline void clearDisabledValidationFeatures() { _disabledValidationFeatures.clear(); }
};

struct PhysicalDeviceTransformFeedbackProperties
{
private:
    uint32_t _maxTransformFeedbackStreams;
    uint32_t _maxTransformFeedbackBuffers;
    VkDeviceSize _maxTransformFeedbackBufferSize;
    uint32_t _maxTransformFeedbackStreamDataSize;
    uint32_t _maxTransformFeedbackBufferDataSize;
    uint32_t _maxTransformFeedbackBufferDataStride;
    bool _transformFeedbackQueries;
    bool _transformFeedbackStreamsLinesTriangles;
    bool _transformFeedbackRasterizationStreamSelect;
    bool _transformFeedbackDraw;

public:
    PhysicalDeviceTransformFeedbackProperties() = default;

    PhysicalDeviceTransformFeedbackProperties(VkPhysicalDeviceTransformFeedbackPropertiesEXT properties)
    {
        _maxTransformFeedbackStreams = properties.maxTransformFeedbackStreams;
        _maxTransformFeedbackBuffers = properties.maxTransformFeedbackBuffers;
        _maxTransformFeedbackBufferSize = properties.maxTransformFeedbackBufferSize;
        _maxTransformFeedbackStreamDataSize = properties.maxTransformFeedbackStreamDataSize;
        _maxTransformFeedbackBufferDataSize = properties.maxTransformFeedbackBufferDataSize;
        _maxTransformFeedbackBufferDataStride = properties.maxTransformFeedbackBufferDataStride;
        _transformFeedbackQueries = (properties.transformFeedbackQueries != 0u);
        _transformFeedbackStreamsLinesTriangles = (properties.transformFeedbackStreamsLinesTriangles != 0u);
        _transformFeedbackRasterizationStreamSelect = (properties.transformFeedbackRasterizationStreamSelect != 0u);
        _transformFeedbackDraw = (properties.transformFeedbackDraw != 0u);
    }

    inline void setMaxTransformFeedbackStreams(uint32_t maxTransformFeedbackStreams) { _maxTransformFeedbackStreams = maxTransformFeedbackStreams; }

    inline uint32_t getMaxTransformFeedbackStreams() { return _maxTransformFeedbackStreams; }

    inline void setMaxTransformFeedbackBuffers(uint32_t maxTransformFeedbackBuffers) { _maxTransformFeedbackBuffers = maxTransformFeedbackBuffers; }

    inline uint32_t getMaxTransformFeedbackBuffers() { return _maxTransformFeedbackBuffers; }

    inline void setMaxTransformFeedbackBufferSize(VkDeviceSize maxTransformFeedbackBufferSize) { _maxTransformFeedbackBufferSize = maxTransformFeedbackBufferSize; }

    inline VkDeviceSize getMaxTransformFeedbackBufferSize() { return _maxTransformFeedbackBufferSize; }

    inline void setMaxTransformFeedbackStreamDataSize(uint32_t maxTransformFeedbackStreamDataSize) { _maxTransformFeedbackStreamDataSize = maxTransformFeedbackStreamDataSize; }

    inline uint32_t getMaxTransformFeedbackStreamDataSize() { return _maxTransformFeedbackStreamDataSize; }

    inline void setMaxTransformFeedbackBufferDataSize(uint32_t maxTransformFeedbackBufferDataSize) { _maxTransformFeedbackBufferDataSize = maxTransformFeedbackBufferDataSize; }

    inline uint32_t getMaxTransformFeedbackBufferDataSize() { return _maxTransformFeedbackBufferDataSize; }

    inline void setMaxTransformFeedbackBufferDataStride(uint32_t maxTransformFeedbackBufferDataStride)
    {
        _maxTransformFeedbackBufferDataStride = maxTransformFeedbackBufferDataStride;
    }

    inline uint32_t getMaxTransformFeedbackBufferDataStride() { return _maxTransformFeedbackBufferDataStride; }

    inline void setTransformFeedbackQueries(bool transformFeedbackQueries) { _transformFeedbackQueries = transformFeedbackQueries; }

    inline bool getTransformFeedbackQueries() { return _transformFeedbackQueries; }

    inline void setTransformFeedbackStreamsLinesTriangles(bool transformFeedbackStreamsLinesTriangles)
    {
        _transformFeedbackStreamsLinesTriangles = transformFeedbackStreamsLinesTriangles;
    }

    inline bool getTransformFeedbackStreamsLinesTriangles() { return _transformFeedbackStreamsLinesTriangles; }

    inline void setTransformFeedbackRasterizationStreamSelect(bool transformFeedbackRasterizationStreamSelect)
    {
        _transformFeedbackRasterizationStreamSelect = transformFeedbackRasterizationStreamSelect;
    }

    inline bool getTransformFeedbackRasterizationStreamSelect() { return _transformFeedbackRasterizationStreamSelect; }

    inline void setTransformFeedbackDraw(bool transformFeedbackDraw) { _transformFeedbackDraw = transformFeedbackDraw; }

    inline bool getTransformFeedbackDraw() { return _transformFeedbackDraw; }
};

struct PhysicalDeviceTransformFeedbackFeatures
{
private:
    bool _transformFeedback;
    bool _geometryStreams;

public:
    PhysicalDeviceTransformFeedbackFeatures() = default;

    PhysicalDeviceTransformFeedbackFeatures(VkPhysicalDeviceTransformFeedbackFeaturesEXT features)
    {
        _transformFeedback = (features.transformFeedback != 0u);
        _geometryStreams = (features.geometryStreams != 0u);
    }

    inline void setTransformFeedback(bool transformFeedback) { _transformFeedback = transformFeedback; }

    inline bool getTransformFeedback() { return _transformFeedback; }

    inline void setGeometryStreams(bool geometryStreams) { _geometryStreams = geometryStreams; }

    inline bool getGeometryStreams() { return _geometryStreams; }
};
} // namespace pvrvk
#undef DEFINE_ENUM_OPERATORS