Magnum::Vk::Mesh class new in Git master

Mesh.

Connects MeshLayout with concrete vertex/index Buffer instances and manages related information such as vertex or instance count.

Populating a mesh

Continuing from the mesh layout setup, the Mesh gets concrete buffers bound using addVertexBuffer() and vertex count specified with setCount():

Vk::MeshLayout meshLayout{MeshPrimitive::Triangles};
meshLayout
    .addBinding(Binding, 8*sizeof(Float))
    .addAttribute(PositionLocation, Binding, VertexFormat::Vector3, 0)
    .addAttribute(TextureLocation, Binding, VertexFormat::Vector2, 3*sizeof(Float))
    .addAttribute(NormalLocation, Binding, VertexFormat::Vector3, 5*sizeof(Float));

Vk::Buffer vertices{, Vk::BufferCreateInfo{
    Vk::BufferUsage::VertexBuffer, vertexCount*8*sizeof(Float)
}, };



Vk::Mesh mesh{meshLayout};
mesh.addVertexBuffer(Binding, vertices, 0)
    .setCount(vertexCount);

For an indexed mesh, the index buffer can be specified via setIndexBuffer(). With an index buffer present, setCount() then describes count of indices, instead of vertices:

Vk::Buffer indices{, Vk::BufferCreateInfo{
    Vk::BufferUsage::IndexBuffer, indexCount*sizeof(UnsignedShort)
}, };



mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort)
    .setCount(indexCount);

Transferring buffer and layout ownership

To simplify resource management, it's possible to have the Buffer instances owned by the Mesh, as well as the MeshLayout, either using std::move() or by directly passing a r-value. If a single buffer is used for multiple bindings (for example as both a vertex and an index buffer), perform the move last:

Vk::Buffer buffer{, Vk::BufferCreateInfo{
    Vk::BufferUsage::VertexBuffer|Vk::BufferUsage::IndexBuffer, 
}, };



Vk::Mesh mesh{Vk::MeshLayout{MeshPrimitive::Triangles}
    .addBinding()
    
};
mesh.addVertexBuffer(, buffer, )
    .setIndexBuffer(std::move(buffer), )
    .setCount();

Drawing a mesh

Assuming a rasterization pipeline with the same MeshLayout was bound, a mesh can be then drawn using CommandBuffer::draw(). The function takes care of binding all buffers and executing an appropriate draw command:

Vk::Mesh mesh{};

Vk::Pipeline pipeline{, Vk::RasterizationPipelineCreateInfo{
        , mesh.layout(), 
    }
};



cmd.bindPipeline(pipeline)
   .draw(mesh);

Dynamic pipeline state

Both the MeshPrimitive set in MeshLayout constructor and binding stride set in MeshLayout::addBinding() can be set as dynamic in the pipeline using DynamicRasterizationState::MeshPrimitive and VertexInputBindingStride, assuming DeviceFeature::ExtendedDynamicState is supported and enabled. The CommandBuffer::draw() function then checks what dynamic state is enabled in the currently bound pipeline and implicitly sets all dynamic states.

Taking this to the extreme, with these two dynamic states and a dedicated binding for each attribute you can make the pipeline accept basically any mesh as long as just the attribute locations and types are the same — offsets and strided of particular attributes are then fully dynamic.

/* Use zero stride and zero offsets, as the stride gets specified dynamically
   and offsets specified in concrete buffer bindings instead */
Vk::MeshLayout dynamicMeshLayout{MeshPrimitive::Triangles};
dynamicMeshLayout
    .addBinding(0, 0)
    .addBinding(1, 0)
    .addBinding(2, 0)
    .addAttribute(PositionLocation, 0, VertexFormat::Vector3, 0)
    .addAttribute(TextureLocation, 1, VertexFormat::Vector2, 0)
    .addAttribute(NormalLocation, 2, VertexFormat::Vector3, 0);

Vk::Pipeline pipeline{, Vk::RasterizationPipelineCreateInfo{
        , dynamicMeshLayout, }
    /* Enable dynamic primitive and stride */
    .setDynamicStates(Vk::DynamicRasterizationState::MeshPrimitive|
                      Vk::DynamicRasterizationState::VertexInputBindingStride)
    
};

Vk::Buffer vertices{};

Vk::Mesh mesh{Vk::MeshLayout{MeshPrimitive::Triangles} /* Or TriangleStrip etc */
    /* Concrete stride */
    .addBinding(0, 8*sizeof(Float))
    .addBinding(1, 8*sizeof(Float))
    .addBinding(2, 8*sizeof(Float))
    /* Rest the same as in the dynamicMeshLayout */
    .addAttribute(PositionLocation, 0, VertexFormat::Vector3, 0)
    .addAttribute(TextureLocation, 1, VertexFormat::Vector2, 0)
    .addAttribute(NormalLocation, 2, VertexFormat::Vector3, 0)
};

/* Bind the same buffer to three different bindings, with concrete offsets */
mesh.addVertexBuffer(0, vertices, 0)
    .addVertexBuffer(1, vertices, 3*sizeof(Float))
    .addVertexBuffer(2, vertices, 5*sizeof(Float))
    .setCount();

cmd.bindPipeline(pipeline)
   /* Updates the dynamic primitive and stride as needed by the mesh */
   .draw(mesh);

Constructors, destructors, conversion operators

Mesh(const MeshLayout& layout) explicit
Construct with a reference to external MeshLayout.
Mesh(MeshLayout&& layout) explicit
Construct with taking over MeshLayout ownership.
Mesh(const Mesh&) deleted
Copying is not allowed.
Mesh(Mesh&&) noexcept
Move constructor.
~Mesh()
Destructor.

Public functions

auto operator=(const Mesh&) -> Mesh& deleted
Copying is not allowed.
auto operator=(Mesh&&) -> Mesh& noexcept
Move assignment.
auto count() const -> UnsignedInt
Vertex/index count.
auto setCount(UnsignedInt count) -> Mesh&
Set vertex/index count.
auto vertexOffset() const -> UnsignedInt
Vertex offset.
auto setVertexOffset(UnsignedInt offset) -> Mesh&
Set vertex offset.
auto indexOffset() const -> UnsignedInt
Index offset.
auto setIndexOffset(UnsignedInt offset) -> Mesh&
Set index offset.
auto instanceCount() const -> UnsignedInt
Instance count.
auto setInstanceCount(UnsignedInt count) -> Mesh&
Set instance count.
auto instanceOffset() const -> UnsignedInt
Instance offset.
auto setInstanceOffset(UnsignedInt offset) -> Mesh&
Set instance offset.
auto addVertexBuffer(UnsignedInt binding, VkBuffer buffer, UnsignedLong offset) -> Mesh&
Add a vertex buffer.
auto addVertexBuffer(UnsignedInt binding, Buffer&& buffer, UnsignedLong offset) -> Mesh&
Add a vertex buffer and take over its ownership.
auto setIndexBuffer(VkBuffer buffer, UnsignedLong offset, MeshIndexType indexType) -> Mesh&
Set an index buffer.
auto setIndexBuffer(VkBuffer buffer, UnsignedLong offset, Magnum::MeshIndexType indexType) -> Mesh&
auto setIndexBuffer(Buffer&& buffer, UnsignedLong offset, MeshIndexType indexType) -> Mesh&
Set an index buffer and take over its ownership.
auto setIndexBuffer(Buffer&& buffer, UnsignedLong offset, Magnum::MeshIndexType indexType) -> Mesh&
auto layout() const -> const MeshLayout&
Layout of this mesh.
auto vertexBuffers() -> Containers::ArrayView<const VkBuffer>
Vertex buffers.
auto vertexBufferOffsets() const -> Containers::ArrayView<const UnsignedLong>
Vertex buffer offsets.
auto vertexBufferStrides() const -> Containers::ArrayView<const UnsignedLong>
Vertex buffer strides.
auto isIndexed() const -> bool
Whether the mesh is indexed.
auto indexBuffer() -> VkBuffer
Index buffer.
auto indexBufferOffset() const -> UnsignedLong
Index buffer offset.
auto indexType() const -> MeshIndexType
Index type.

Function documentation

Magnum::Vk::Mesh::Mesh(const MeshLayout& layout) explicit

Construct with a reference to external MeshLayout.

Assumes layout stays in scope for the whole lifetime of the Mesh instance.

Magnum::Vk::Mesh::~Mesh()

Destructor.

If any buffers were added using addVertexBuffer(UnsignedInt, Buffer&&, UnsignedLong) or setIndexBuffer(Buffer&&, UnsignedLong, MeshIndexType), their owned instanced are destructed at this point.

Mesh& Magnum::Vk::Mesh::setCount(UnsignedInt count)

Set vertex/index count.

Returns Reference to self (for method chaining)

If the mesh is indexed, the value is treated as index count, otherwise the value is vertex count. If set to 0, no draw commands are issued when calling CommandBuffer::draw().

Mesh& Magnum::Vk::Mesh::setVertexOffset(UnsignedInt offset)

Set vertex offset.

Returns Reference to self (for method chaining)

For non-indexed meshes specifies the first vertex that will be drawn, for indexed meshes specifies the offset added to each index. Default is 0.

Mesh& Magnum::Vk::Mesh::setIndexOffset(UnsignedInt offset)

Set index offset.

Returns Reference to self (for method chaining)

Expects that the mesh is indexed. Specifies the first index that will be drawn. Default is 0.

Mesh& Magnum::Vk::Mesh::setInstanceCount(UnsignedInt count)

Set instance count.

Returns Reference to self (for method chaining)

If set to 0, no draw commands are issued when calling CommandBuffer::draw(). Default is 1.

Mesh& Magnum::Vk::Mesh::setInstanceOffset(UnsignedInt offset)

Set instance offset.

Returns Reference to self (for method chaining)

Specifies the first instance that will be drawn. Default is 0.

Mesh& Magnum::Vk::Mesh::addVertexBuffer(UnsignedInt binding, VkBuffer buffer, UnsignedLong offset)

Add a vertex buffer.

Parameters
binding Binding corresponding to a particular MeshLayout::addBinding() call
buffer A Buffer instance or a raw Vulkan buffer handle. Expected to have been created with BufferUsage::VertexBuffer.
offset Offset into the buffer, in bytes
Returns Reference to self (for method chaining)

Mesh& Magnum::Vk::Mesh::addVertexBuffer(UnsignedInt binding, Buffer&& buffer, UnsignedLong offset)

Add a vertex buffer and take over its ownership.

Returns Reference to self (for method chaining)

Compared to addVertexBuffer(UnsignedInt, VkBuffer, UnsignedLong) the buffer instance ownership is transferred to the class and thus doesn't have to be managed separately.

Mesh& Magnum::Vk::Mesh::setIndexBuffer(VkBuffer buffer, UnsignedLong offset, MeshIndexType indexType)

Set an index buffer.

Parameters
buffer A Buffer instance or a raw Vulkan buffer handle. Expected to have been created with BufferUsage::IndexBuffer.
offset Offset into the buffer, in bytes
indexType Index type
Returns Reference to self (for method chaining)

Mesh& Magnum::Vk::Mesh::setIndexBuffer(VkBuffer buffer, UnsignedLong offset, Magnum::MeshIndexType indexType)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Mesh& Magnum::Vk::Mesh::setIndexBuffer(Buffer&& buffer, UnsignedLong offset, MeshIndexType indexType)

Set an index buffer and take over its ownership.

Returns Reference to self (for method chaining)

Compared to setIndexBuffer(VkBuffer, UnsignedLong, MeshIndexType) the buffer instance ownership is transferred to the class and thus doesn't have to be managed separately.

Mesh& Magnum::Vk::Mesh::setIndexBuffer(Buffer&& buffer, UnsignedLong offset, Magnum::MeshIndexType indexType)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Containers::ArrayView<const VkBuffer> Magnum::Vk::Mesh::vertexBuffers()

Vertex buffers.

Has the same length as the vertex buffer binding array in layout(), the buffers correspond to binding IDs at the same index.

Containers::ArrayView<const UnsignedLong> Magnum::Vk::Mesh::vertexBufferOffsets() const

Vertex buffer offsets.

Has the same length as the vertex buffer binding array in layout(), offsets correspond to vertexBuffers() at the same index.

Containers::ArrayView<const UnsignedLong> Magnum::Vk::Mesh::vertexBufferStrides() const

Vertex buffer strides.

Has the same length as the vertex buffer binding array in layout(). The strides are the same as strides in the layout at the same index, but here in a form that's usable by vkCmdBindVertexBuffers2() if DynamicRasterizationState::VertexInputBindingStride is enabled.

bool Magnum::Vk::Mesh::isIndexed() const

Whether the mesh is indexed.

The mesh is considered indexed if setIndexBuffer() was called.

VkBuffer Magnum::Vk::Mesh::indexBuffer()

Index buffer.

Expects that the mesh is indexed.

UnsignedLong Magnum::Vk::Mesh::indexBufferOffset() const

Index buffer offset.

Expects that the mesh is indexed.

MeshIndexType Magnum::Vk::Mesh::indexType() const

Index type.

Expects that the mesh is indexed.