mirror of https://github.com/mosra/magnum.git
Browse Source
Next up is *the unthinkable*, a Vk::Mesh. After that I'll finally have enough APIs exposed to document everything including command buffer recording and submission.pull/494/head
13 changed files with 995 additions and 156 deletions
@ -0,0 +1,210 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include "MeshLayout.h" |
||||
|
||||
#include <Corrade/Containers/GrowableArray.h> |
||||
|
||||
#include "Magnum/Mesh.h" |
||||
#include "Magnum/Vk/VertexFormat.h" |
||||
#include "Magnum/Vk/Implementation/structureHelpers.h" |
||||
|
||||
namespace Magnum { namespace Vk { |
||||
|
||||
namespace { |
||||
|
||||
constexpr MeshPrimitive MeshPrimitiveMapping[]{ |
||||
MeshPrimitive::Points, |
||||
MeshPrimitive::Lines, |
||||
MeshPrimitive(~Int{}), /* LineLoop */ |
||||
MeshPrimitive::LineStrip, |
||||
MeshPrimitive::Triangles, |
||||
MeshPrimitive::TriangleStrip, |
||||
MeshPrimitive::TriangleFan, |
||||
MeshPrimitive(~Int{}), /* Instances */ |
||||
MeshPrimitive(~Int{}), /* Faces */ |
||||
MeshPrimitive(~Int{}) /* Edges */ |
||||
}; |
||||
|
||||
} |
||||
|
||||
bool hasMeshPrimitive(const Magnum::MeshPrimitive primitive) { |
||||
if(isMeshPrimitiveImplementationSpecific(primitive)) |
||||
return true; |
||||
|
||||
CORRADE_ASSERT(UnsignedInt(primitive) - 1 < Containers::arraySize(MeshPrimitiveMapping), |
||||
"Vk::hasMeshPrimitive(): invalid primitive" << primitive, {}); |
||||
return UnsignedInt(MeshPrimitiveMapping[UnsignedInt(primitive) - 1]) != ~UnsignedInt{}; |
||||
} |
||||
|
||||
MeshPrimitive meshPrimitive(const Magnum::MeshPrimitive primitive) { |
||||
if(isMeshPrimitiveImplementationSpecific(primitive)) |
||||
return meshPrimitiveUnwrap<MeshPrimitive>(primitive); |
||||
|
||||
CORRADE_ASSERT(UnsignedInt(primitive) - 1 < Containers::arraySize(MeshPrimitiveMapping), |
||||
"Vk::meshPrimitive(): invalid primitive" << primitive, {}); |
||||
const MeshPrimitive out = MeshPrimitiveMapping[UnsignedInt(primitive) - 1]; |
||||
CORRADE_ASSERT(out != MeshPrimitive(~UnsignedInt{}), |
||||
"Vk::meshPrimitive(): unsupported primitive" << primitive, {}); |
||||
return out; |
||||
} |
||||
|
||||
struct MeshLayout::State { |
||||
Containers::Array<VkVertexInputBindingDescription> bindings; |
||||
Containers::Array<VkVertexInputBindingDivisorDescriptionEXT> bindingDivisors; |
||||
Containers::Array<VkVertexInputAttributeDescription> attributes; |
||||
VkPipelineVertexInputDivisorStateCreateInfoEXT vertexDivisorInfo{}; |
||||
}; |
||||
|
||||
MeshLayout::MeshLayout(const MeshPrimitive primitive): _vertexInfo{}, _assemblyInfo{} { |
||||
_vertexInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
||||
|
||||
_assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
||||
_assemblyInfo.topology = VkPrimitiveTopology(primitive); |
||||
} |
||||
|
||||
MeshLayout::MeshLayout(const Magnum::MeshPrimitive primitive): MeshLayout{meshPrimitive(primitive)} {} |
||||
|
||||
MeshLayout::MeshLayout(NoInitT) noexcept {} |
||||
|
||||
MeshLayout::MeshLayout(const VkPipelineVertexInputStateCreateInfo& vertexInfo, const VkPipelineInputAssemblyStateCreateInfo& assemblyInfo): |
||||
/* Can't use {} with GCC 4.8 here because it tries to initialize the first
|
||||
member instead of doing a copy */ |
||||
_vertexInfo(vertexInfo), _assemblyInfo(assemblyInfo) {} |
||||
|
||||
MeshLayout::MeshLayout(MeshLayout&& other) noexcept: |
||||
/* Can't use {} with GCC 4.8 here because it tries to initialize the first
|
||||
member instead of doing a copy */ |
||||
_vertexInfo(other._vertexInfo), |
||||
_assemblyInfo(other._assemblyInfo), |
||||
_state{std::move(other._state)} |
||||
{ |
||||
/* Ensure the previous instance doesn't reference state that's now ours */ |
||||
/** @todo this is now more like a destructible move, do it more selectively
|
||||
and clear only what's really ours and not external? */ |
||||
other._vertexInfo.pNext = nullptr; |
||||
other._vertexInfo.vertexBindingDescriptionCount = 0; |
||||
other._vertexInfo.pVertexBindingDescriptions = nullptr; |
||||
other._vertexInfo.vertexAttributeDescriptionCount = 0; |
||||
other._vertexInfo.pVertexAttributeDescriptions = nullptr; |
||||
other._assemblyInfo.pNext = nullptr; |
||||
} |
||||
|
||||
MeshLayout::~MeshLayout() = default; |
||||
|
||||
MeshLayout& MeshLayout::operator=(MeshLayout&& other) noexcept { |
||||
using std::swap; |
||||
swap(other._vertexInfo, _vertexInfo); |
||||
swap(other._assemblyInfo, _assemblyInfo); |
||||
swap(other._state, _state); |
||||
return *this; |
||||
} |
||||
|
||||
MeshLayout& MeshLayout::addBinding(const UnsignedInt binding, const UnsignedInt stride) { |
||||
if(!_state) _state.emplace(); |
||||
|
||||
VkVertexInputBindingDescription description{}; |
||||
description.binding = binding; |
||||
description.stride = stride; |
||||
description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; |
||||
arrayAppend(_state->bindings, description); |
||||
_vertexInfo.vertexBindingDescriptionCount = _state->bindings.size(); |
||||
_vertexInfo.pVertexBindingDescriptions = _state->bindings; |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
MeshLayout& MeshLayout::addInstancedBinding(const UnsignedInt binding, const UnsignedInt stride, const UnsignedInt divisor) { |
||||
if(!_state) _state.emplace(); |
||||
|
||||
VkVertexInputBindingDescription description{}; |
||||
description.binding = binding; |
||||
description.stride = stride; |
||||
description.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; |
||||
arrayAppend(_state->bindings, description); |
||||
_vertexInfo.vertexBindingDescriptionCount = _state->bindings.size(); |
||||
_vertexInfo.pVertexBindingDescriptions = _state->bindings; |
||||
|
||||
if(divisor != 1) { |
||||
VkVertexInputBindingDivisorDescriptionEXT divisorDescription{}; |
||||
divisorDescription.binding = binding; |
||||
divisorDescription.divisor = divisor; |
||||
arrayAppend(_state->bindingDivisors, divisorDescription); |
||||
_state->vertexDivisorInfo.vertexBindingDivisorCount = _state->bindingDivisors.size(); |
||||
_state->vertexDivisorInfo.pVertexBindingDivisors = _state->bindingDivisors; |
||||
|
||||
/* Attach the structure if not already */ |
||||
if(!_state->vertexDivisorInfo.sType) |
||||
Implementation::structureConnectOne(_vertexInfo.pNext, _state->vertexDivisorInfo, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT); |
||||
} |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const VertexFormat format, const UnsignedInt offset) { |
||||
if(!_state) _state.emplace(); |
||||
|
||||
VkVertexInputAttributeDescription description{}; |
||||
description.location = location; |
||||
description.binding = binding; |
||||
description.format = VkFormat(format); |
||||
description.offset = offset; |
||||
arrayAppend(_state->attributes, description); |
||||
_vertexInfo.vertexAttributeDescriptionCount = _state->attributes.size(); |
||||
_vertexInfo.pVertexAttributeDescriptions = _state->attributes; |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
MeshLayout& MeshLayout::addAttribute(const UnsignedInt location, const UnsignedInt binding, const Magnum::VertexFormat format, const UnsignedInt offset) { |
||||
return addAttribute(location, binding, vertexFormat(format), offset); |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const MeshPrimitive value) { |
||||
debug << "Vk::MeshPrimitive" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(value) case Vk::MeshPrimitive::value: return debug << "::" << Debug::nospace << #value; |
||||
_c(Points) |
||||
_c(Lines) |
||||
_c(LineStrip) |
||||
_c(Triangles) |
||||
_c(TriangleStrip) |
||||
_c(TriangleFan) |
||||
_c(LinesAdjacency) |
||||
_c(LineStripAdjacency) |
||||
_c(TrianglesAdjacency) |
||||
_c(TriangleStripAdjacency) |
||||
_c(Patches) |
||||
#undef _c |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
/* Vulkan docs have the values in decimal, so not converting to hex */ |
||||
return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,373 @@
|
||||
#ifndef Magnum_Vk_MeshLayout_h |
||||
#define Magnum_Vk_MeshLayout_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Class @ref Magnum::Vk::MeshLayout, enum @ref Magnum::Vk::MeshPrimitive, function @ref Magnum::Vk::hasMeshPrimitive(), @ref Magnum::Vk::meshPrimitive() |
||||
* @m_since_latest |
||||
*/ |
||||
|
||||
#include <Corrade/Containers/Pointer.h> |
||||
|
||||
#include "Magnum/Tags.h" |
||||
#include "Magnum/Vk/Vk.h" |
||||
#include "Magnum/Vk/Vulkan.h" |
||||
#include "Magnum/Vk/visibility.h" |
||||
|
||||
namespace Magnum { namespace Vk { |
||||
|
||||
/* About naming -- I wonder why Vulkan tries *so hard* to avoid naming anything
|
||||
a "mesh". It would so nicely group things togehter BUT NO, there's primitive |
||||
topology, and vertex input state, and input assembly and ugh. */ |
||||
|
||||
/**
|
||||
@brief Mesh primitive |
||||
@m_since_latest |
||||
|
||||
Wraps a @type_vk_keyword{PrimitiveTopology}. |
||||
|
||||
@m_enum_values_as_keywords |
||||
|
||||
@see @ref Magnum::MeshPrimitive, @ref hasMeshPrimitive(), @ref meshPrimitive(), |
||||
@ref MeshLayout |
||||
*/ |
||||
enum class MeshPrimitive: Int { |
||||
/* The _LIST seems too verbose and looks like Vulkan naming got inspired by
|
||||
D3D here. I'm omitting those since it's unnecessary verbosity, Metal |
||||
doesn't have those either. GL had the naming right. */ |
||||
|
||||
/** Single points. */ |
||||
Points = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, |
||||
|
||||
/**
|
||||
* Each pair of vertices defines a single line, lines aren't |
||||
* connected together. |
||||
*/ |
||||
Lines = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, |
||||
|
||||
/**
|
||||
* First two vertices define first line segment, each following |
||||
* vertex defines another segment. |
||||
*/ |
||||
LineStrip = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, |
||||
|
||||
/** Each three vertices define one triangle. */ |
||||
Triangles = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
||||
|
||||
/**
|
||||
* First three vertices define first triangle, each following |
||||
* vertex defines another triangle. |
||||
*/ |
||||
TriangleStrip = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, |
||||
|
||||
/**
|
||||
* First vertex is center, each following vertex is connected to |
||||
* previous and center vertex. |
||||
* @requires_vk_feature @ref DeviceFeature::TriangleFans if the |
||||
* @vk_extension{KHR,portability_subset} extension is present |
||||
*/ |
||||
TriangleFan = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, |
||||
|
||||
/**
|
||||
* Lines with adjacency information. |
||||
* @requires_vk_feature @ref DeviceFeature::GeometryShader |
||||
*/ |
||||
LinesAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, |
||||
|
||||
/**
|
||||
* Line strip with adjacency information. |
||||
* @requires_vk_feature @ref DeviceFeature::GeometryShader |
||||
*/ |
||||
LineStripAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, |
||||
|
||||
/**
|
||||
* Triangles with adjacency information. |
||||
* @requires_vk_feature @ref DeviceFeature::GeometryShader |
||||
*/ |
||||
TrianglesAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, |
||||
|
||||
/**
|
||||
* Triangle strip with adjacency information. |
||||
* @requires_vk_feature @ref DeviceFeature::GeometryShader |
||||
*/ |
||||
TriangleStripAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, |
||||
|
||||
/**
|
||||
* Patches. |
||||
* @requires_vk_feature @ref DeviceFeature::TessellationShader |
||||
*/ |
||||
Patches = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST |
||||
}; |
||||
|
||||
/**
|
||||
@debugoperatorenum{MeshPrimitive} |
||||
@m_since_latest |
||||
*/ |
||||
MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MeshPrimitive value); |
||||
|
||||
/**
|
||||
@brief Check availability of a generic mesh primitive |
||||
@m_since_latest |
||||
|
||||
In particular, Vulkan doesn't support the @ref MeshPrimitive::LineLoop |
||||
primitive. Returns @cpp false @ce if Vulkan doesn't support such primitive, |
||||
@cpp true @ce otherwise. Moreover, returns @cpp true @ce also for all types |
||||
that are @ref isMeshPrimitiveImplementationSpecific(). The @p primitive value |
||||
is expected to be valid. |
||||
|
||||
@note Support of some types depends on presence of a particular Vulkan |
||||
extension. Such check is outside of the scope of this function and you are |
||||
expected to verify extension availability before using such type. |
||||
|
||||
@see @ref meshPrimitive() |
||||
*/ |
||||
MAGNUM_VK_EXPORT bool hasMeshPrimitive(Magnum::MeshPrimitive primitive); |
||||
|
||||
/**
|
||||
@brief Convert generic mesh primitive to Vulkan mesh primitive |
||||
@m_since_latest |
||||
|
||||
In case @ref isMeshPrimitiveImplementationSpecific() returns @cpp false @ce for |
||||
@p primitive, maps it to a corresponding Vulkan primitive topology. In case |
||||
@ref isMeshPrimitiveImplementationSpecific() returns @cpp true @ce, assumes |
||||
@p primitive stores a Vulkan-specific primitive topology and returns |
||||
@ref meshPrimitiveUnwrap() cast to @type_vk{PrimitiveTopology}. |
||||
|
||||
Not all generic mesh primitives have a Vulkan equivalent and this function |
||||
expects that given primitive is available. Use @ref hasMeshPrimitive() to query |
||||
availability of given primitive. |
||||
@see @ref vkIndexType() |
||||
*/ |
||||
MAGNUM_VK_EXPORT MeshPrimitive meshPrimitive(Magnum::MeshPrimitive primitive); |
||||
|
||||
/**
|
||||
@brief Mesh layout |
||||
@m_since_latest |
||||
|
||||
Wraps a |
||||
|
||||
- @type_vk_keyword{VertexInputBindingDescription}, |
||||
- @type_vk_keyword{VertexInputAttributeDescription}, |
||||
- @type_vk_keyword{PipelineVertexInputStateCreateInfo}, |
||||
- @type_vk_keyword{PipelineInputAssemblyStateCreateInfo}, |
||||
- @type_vk_keyword{VertexInputBindingDivisorDescriptionEXT} and |
||||
- @type_vk_keyword{PipelineVertexInputDivisorStateCreateInfoEXT}, |
||||
|
||||
@m_class{m-noindent} |
||||
|
||||
describing how vertex attributes are organized in buffers and what's the layout |
||||
of each attribute. |
||||
|
||||
@section Vk-MeshLayout-usage Usage |
||||
|
||||
As an example let's assume a shader expects positions, texture coordinates and |
||||
normals in locations @cpp 0 @ce, @cpp 1 @ce and @cpp 5 @ce, respectively. If |
||||
we'd have them stored interleaved in a single buffer, the layout description |
||||
could look like this: |
||||
|
||||
@snippet MagnumVk.cpp MeshLayout-usage |
||||
|
||||
The `BufferBinding` is then subsequently used as a binding index for a concrete |
||||
vertex buffer when drawing. |
||||
*/ |
||||
class MAGNUM_VK_EXPORT MeshLayout { |
||||
public: |
||||
/**
|
||||
* @brief Constructor |
||||
* @param primitive Mesh primitive |
||||
* |
||||
* The following @type_vk{PipelineVertexInputStateCreateInfo} fields |
||||
* are pre-filled in addition to `sType`, everything else is |
||||
* zero-filled: |
||||
* |
||||
* - <em>(none)</em> |
||||
* |
||||
* The following @type_vk{PipelineInputAssemblyStateCreateInfo} fields |
||||
* are pre-filled in addition to `sType`, everything else is |
||||
* zero-filled: |
||||
* |
||||
* - `topology` to @p primitive |
||||
* |
||||
* @see @ref vkPipelineVertexInputStateCreateInfo(), |
||||
* @ref vkPipelineInputAssemblyStateCreateInfo() |
||||
*/ |
||||
explicit MeshLayout(MeshPrimitive primitive); |
||||
/** @overload */ |
||||
explicit MeshLayout(Magnum::MeshPrimitive primitive); |
||||
|
||||
/**
|
||||
* @brief Construct without initializing the contents |
||||
* |
||||
* Note that not even the `sType` fields are set --- the structures |
||||
* have to be fully initialized afterwards in order to be usable. |
||||
*/ |
||||
explicit MeshLayout(NoInitT) noexcept; |
||||
|
||||
/**
|
||||
* @brief Construct from existing data |
||||
* |
||||
* Copies the existing values verbatim, pointers are kept unchanged |
||||
* without taking over the ownership. Modifying the newly created |
||||
* instance will not modify the original data nor the pointed-to data. |
||||
*/ |
||||
explicit MeshLayout(const VkPipelineVertexInputStateCreateInfo& vertexInfo, const VkPipelineInputAssemblyStateCreateInfo& assemblyInfo); |
||||
|
||||
/** @brief Copying is not allowed */ |
||||
MeshLayout(const MeshLayout&) = delete; |
||||
|
||||
/** @brief Move constructor */ |
||||
MeshLayout(MeshLayout&& other) noexcept; |
||||
|
||||
~MeshLayout(); |
||||
|
||||
/** @brief Copying is not allowed */ |
||||
MeshLayout& operator=(const MeshLayout&) = delete; |
||||
|
||||
/** @brief Move assignment */ |
||||
MeshLayout& operator=(MeshLayout&& other) noexcept; |
||||
|
||||
/**
|
||||
* @brief Add a buffer binding |
||||
* @param binding Binding index, to which a buffer subrange will be |
||||
* bound when drawing the mesh. Has to be unique among all |
||||
* @ref addBinding() and @ref addInstancedBinding() calls. |
||||
* @param stride Binding stride, in bytes |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Adds a new @type_vk{VertexInputBindingDescription} structure to |
||||
* @ref vkPipelineVertexInputStateCreateInfo() with the following |
||||
* fields set: |
||||
* |
||||
* - `binding` |
||||
* - `stride` |
||||
* - `inputRate` to @val_vk{VERTEX_INPUT_RATE_VERTEX,VertexInputRate} |
||||
* |
||||
* @see @ref addInstancedBinding() |
||||
*/ |
||||
MeshLayout& addBinding(UnsignedInt binding, UnsignedInt stride); |
||||
|
||||
/**
|
||||
* @brief Add an instanced buffer binding |
||||
* @param binding Binding index, to which a buffer subrange will be |
||||
* bound when drawing the mesh. Has to be unique among all |
||||
* @ref addBinding() and @ref addInstancedBinding() calls. |
||||
* @param stride Binding stride, in bytes |
||||
* @param divisor Attribute divisor. @cpp 1 @ce means the attribute |
||||
* will be advanced for each instance, larger values will mean |
||||
* `n` instances will be drawn using the same attribute, |
||||
* @cpp 0 @ce will make all instances use a single attribute |
||||
* (which is effectively the same as setting @p divisor to |
||||
* instance count). |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Compared to @ref addBinding(), sets `inputRate` to |
||||
* @val_vk{VERTEX_INPUT_RATE_INSTANCE,VertexInputRate}. If @p divisor |
||||
* is not @cpp 1 @ce, a new |
||||
* @type_vk{VertexInputBindingDivisorDescriptionEXT} structure is added |
||||
* to @type_vk{PipelineVertexInputDivisorStateCreateInfoEXT} which is |
||||
* then referenced from the `pNext` chain of |
||||
* @ref vkPipelineVertexInputStateCreateInfo(), with the following |
||||
* fields set: |
||||
* |
||||
* - `binding` |
||||
* - `divisor` |
||||
* |
||||
* <b></b> |
||||
* |
||||
* @requires_vk_feature @ref DeviceFeature::VertexAttributeInstanceRateDivisor |
||||
* if @p divisor isn't `1` |
||||
* @requires_vk_feature @ref DeviceFeature::VertexAttributeInstanceRateZeroDivisor |
||||
* if @p divisor is `0` |
||||
*/ |
||||
MeshLayout& addInstancedBinding(UnsignedInt binding, UnsignedInt stride, UnsignedInt divisor = 1); |
||||
|
||||
/**
|
||||
* @brief Add an attribute |
||||
* @param location Attribute location, matching a shader input |
||||
* @param binding Binding index, corresponding to the @p binding |
||||
* parameter of one of the @ref addBinding() / |
||||
* @ref addInstancedBinding() calls. |
||||
* @param format Vertex format |
||||
* @param offset Attribute offset in bytes inside @p stride of |
||||
* given @p binding |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Adds a new @type_vk{VertexInputAttributeDescription} structure to |
||||
* @ref vkPipelineVertexInputStateCreateInfo() with the following |
||||
* fields set: |
||||
* |
||||
* - `location` |
||||
* - `binding` |
||||
* - `format` |
||||
* - `offset` |
||||
*/ |
||||
MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, VertexFormat format, UnsignedInt offset); |
||||
/** @overload */ |
||||
MeshLayout& addAttribute(UnsignedInt location, UnsignedInt binding, Magnum::VertexFormat format, UnsignedInt offset); |
||||
|
||||
/** @brief Underlying @type_vk{PipelineVertexInputStateCreateInfo} structure */ |
||||
VkPipelineVertexInputStateCreateInfo& vkPipelineVertexInputStateCreateInfo() { |
||||
return _vertexInfo; |
||||
} |
||||
/** @overload */ |
||||
const VkPipelineVertexInputStateCreateInfo& vkPipelineVertexInputStateCreateInfo() const { |
||||
return _vertexInfo; |
||||
} |
||||
/** @overload */ |
||||
operator const VkPipelineVertexInputStateCreateInfo*() const { |
||||
return &_vertexInfo; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Underlying @type_vk{PipelineInputAssemblyStateCreateInfo} structure |
||||
* |
||||
* If @ref addInstancedBinding() was called with @p divisor different |
||||
* than @cpp 0 @ce, the `pNext` chain contains the |
||||
* @type_vk{PipelineVertexInputDivisorStateCreateInfoEXT} structure. |
||||
*/ |
||||
VkPipelineInputAssemblyStateCreateInfo& vkPipelineInputAssemblyStateCreateInfo() { |
||||
return _assemblyInfo; |
||||
} |
||||
/** @overload */ |
||||
const VkPipelineInputAssemblyStateCreateInfo& vkPipelineInputAssemblyStateCreateInfo() const { |
||||
return _assemblyInfo; |
||||
} |
||||
/** @overload */ |
||||
operator const VkPipelineInputAssemblyStateCreateInfo*() const { |
||||
return &_assemblyInfo; |
||||
} |
||||
|
||||
private: |
||||
VkPipelineVertexInputStateCreateInfo _vertexInfo; |
||||
VkPipelineInputAssemblyStateCreateInfo _assemblyInfo; |
||||
|
||||
struct State; |
||||
Containers::Pointer<State> _state; |
||||
}; |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,352 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include <new> |
||||
#include <sstream> |
||||
#include <Corrade/Containers/ArrayView.h> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/Utility/DebugStl.h> |
||||
|
||||
#include "Magnum/Mesh.h" |
||||
#include "Magnum/VertexFormat.h" |
||||
#include "Magnum/Vk/MeshLayout.h" |
||||
#include "Magnum/Vk/VertexFormat.h" |
||||
|
||||
namespace Magnum { namespace Vk { namespace Test { namespace { |
||||
|
||||
struct MeshLayoutTest: TestSuite::Tester { |
||||
explicit MeshLayoutTest(); |
||||
|
||||
void mapMeshPrimitive(); |
||||
void mapMeshPrimitiveImplementationSpecific(); |
||||
void mapMeshPrimitiveUnsupported(); |
||||
void mapMeshPrimitiveInvalid(); |
||||
|
||||
template<class T> void construct(); |
||||
void constructNoInit(); |
||||
void constructFromVk(); |
||||
void constructCopy(); |
||||
void constructMove(); |
||||
|
||||
void addBinding(); |
||||
void addInstancedBinding(); |
||||
void addInstancedBindingDivisor(); |
||||
template<class T> void addAttribute(); |
||||
|
||||
void debugMeshPrimitive(); |
||||
}; |
||||
|
||||
MeshLayoutTest::MeshLayoutTest() { |
||||
addTests({&MeshLayoutTest::mapMeshPrimitive, |
||||
&MeshLayoutTest::mapMeshPrimitiveImplementationSpecific, |
||||
&MeshLayoutTest::mapMeshPrimitiveUnsupported, |
||||
&MeshLayoutTest::mapMeshPrimitiveInvalid, |
||||
|
||||
&MeshLayoutTest::construct<MeshPrimitive>, |
||||
&MeshLayoutTest::construct<Magnum::MeshPrimitive>, |
||||
&MeshLayoutTest::constructNoInit, |
||||
&MeshLayoutTest::constructFromVk, |
||||
&MeshLayoutTest::constructCopy, |
||||
&MeshLayoutTest::constructMove, |
||||
|
||||
&MeshLayoutTest::addBinding, |
||||
&MeshLayoutTest::addInstancedBinding, |
||||
&MeshLayoutTest::addInstancedBindingDivisor, |
||||
&MeshLayoutTest::addAttribute<VertexFormat>, |
||||
&MeshLayoutTest::addAttribute<Magnum::VertexFormat>, |
||||
|
||||
&MeshLayoutTest::debugMeshPrimitive}); |
||||
} |
||||
|
||||
template<class> struct VertexFormatTraits; |
||||
template<> struct VertexFormatTraits<VertexFormat> { |
||||
static const char* name() { return "VertexFormat"; } |
||||
}; |
||||
template<> struct VertexFormatTraits<Magnum::VertexFormat> { |
||||
static const char* name() { return "Magnum::VertexFormat"; } |
||||
}; |
||||
|
||||
void MeshLayoutTest::mapMeshPrimitive() { |
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::Points)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::Points), MeshPrimitive::Points); |
||||
|
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::Lines)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::Lines), MeshPrimitive::Lines); |
||||
|
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::LineStrip)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::LineStrip), MeshPrimitive::LineStrip); |
||||
|
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::Triangles)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::Triangles), MeshPrimitive::Triangles); |
||||
|
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::TriangleStrip)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::TriangleStrip), MeshPrimitive::TriangleStrip); |
||||
|
||||
CORRADE_VERIFY(hasMeshPrimitive(Magnum::MeshPrimitive::TriangleFan)); |
||||
CORRADE_COMPARE(meshPrimitive(Magnum::MeshPrimitive::TriangleFan), MeshPrimitive::TriangleFan); |
||||
|
||||
/* Ensure all generic primitives are handled. This goes through the first
|
||||
16 bits, which should be enough. Going through 32 bits takes 8 seconds, |
||||
too much. */ |
||||
for(UnsignedInt i = 1; i <= 0xffff; ++i) { |
||||
const auto primitive = Magnum::MeshPrimitive(i); |
||||
#ifdef __GNUC__ |
||||
#pragma GCC diagnostic push |
||||
#pragma GCC diagnostic error "-Wswitch" |
||||
#endif |
||||
switch(primitive) { |
||||
#define _c(primitive) \ |
||||
case Magnum::MeshPrimitive::primitive: \
|
||||
if(hasMeshPrimitive(Magnum::MeshPrimitive::primitive)) \
|
||||
CORRADE_VERIFY(Int(meshPrimitive(Magnum::MeshPrimitive::primitive)) >= 0); \
|
||||
break; |
||||
#include "Magnum/Implementation/meshPrimitiveMapping.hpp" |
||||
#undef _c |
||||
} |
||||
#ifdef __GNUC__ |
||||
#pragma GCC diagnostic pop |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
void MeshLayoutTest::mapMeshPrimitiveImplementationSpecific() { |
||||
CORRADE_VERIFY(hasMeshPrimitive(meshPrimitiveWrap(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY))); |
||||
CORRADE_COMPARE(meshPrimitive(meshPrimitiveWrap(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY)), |
||||
MeshPrimitive(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY)); |
||||
} |
||||
|
||||
void MeshLayoutTest::mapMeshPrimitiveUnsupported() { |
||||
#ifdef CORRADE_NO_ASSERT |
||||
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||
#endif |
||||
|
||||
CORRADE_VERIFY(!hasMeshPrimitive(Magnum::MeshPrimitive::LineLoop)); |
||||
|
||||
std::ostringstream out; |
||||
{ |
||||
Error redirectError{&out}; |
||||
meshPrimitive(Magnum::MeshPrimitive::LineLoop); |
||||
} |
||||
CORRADE_COMPARE(out.str(), |
||||
"Vk::meshPrimitive(): unsupported primitive MeshPrimitive::LineLoop\n"); |
||||
} |
||||
|
||||
void MeshLayoutTest::mapMeshPrimitiveInvalid() { |
||||
#ifdef CORRADE_NO_ASSERT |
||||
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||
#endif |
||||
|
||||
std::ostringstream out; |
||||
Error redirectError{&out}; |
||||
|
||||
hasMeshPrimitive(Magnum::MeshPrimitive{}); |
||||
hasMeshPrimitive(Magnum::MeshPrimitive(0x12)); |
||||
meshPrimitive(Magnum::MeshPrimitive{}); |
||||
meshPrimitive(Magnum::MeshPrimitive(0x12)); |
||||
CORRADE_COMPARE(out.str(), |
||||
"Vk::hasMeshPrimitive(): invalid primitive MeshPrimitive(0x0)\n" |
||||
"Vk::hasMeshPrimitive(): invalid primitive MeshPrimitive(0x12)\n" |
||||
"Vk::meshPrimitive(): invalid primitive MeshPrimitive(0x0)\n" |
||||
"Vk::meshPrimitive(): invalid primitive MeshPrimitive(0x12)\n"); |
||||
} |
||||
|
||||
template<class T> void MeshLayoutTest::construct() { |
||||
setTestCaseTemplateName(std::is_same<T, MeshPrimitive>::value ? "MeshPrimitive" : "Magnum::MeshPrimitive"); |
||||
|
||||
MeshLayout layout{T::TriangleFan}; |
||||
CORRADE_COMPARE(layout.vkPipelineInputAssemblyStateCreateInfo().topology, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 0); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 0); |
||||
CORRADE_VERIFY(!layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_VERIFY(!layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
} |
||||
|
||||
void MeshLayoutTest::constructNoInit() { |
||||
MeshLayout layout{NoInit}; |
||||
layout.vkPipelineVertexInputStateCreateInfo().sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; |
||||
layout.vkPipelineInputAssemblyStateCreateInfo().sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES; |
||||
new(&layout) MeshLayout{NoInit}; |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); |
||||
CORRADE_COMPARE(layout.vkPipelineInputAssemblyStateCreateInfo().sType, VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES); |
||||
|
||||
CORRADE_VERIFY((std::is_nothrow_constructible<MeshLayout, NoInitT>::value)); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!(std::is_convertible<NoInitT, MeshLayout>::value)); |
||||
} |
||||
|
||||
void MeshLayoutTest::constructFromVk() { |
||||
VkPipelineVertexInputStateCreateInfo vertexInfo; |
||||
vertexInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; |
||||
VkPipelineInputAssemblyStateCreateInfo assemblyInfo; |
||||
assemblyInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES; |
||||
|
||||
MeshLayout layout{vertexInfo, assemblyInfo}; |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); |
||||
CORRADE_COMPARE(layout.vkPipelineInputAssemblyStateCreateInfo().sType, VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES); |
||||
} |
||||
|
||||
void MeshLayoutTest::constructCopy() { |
||||
CORRADE_VERIFY(!std::is_copy_constructible<MeshLayout>{}); |
||||
CORRADE_VERIFY(!std::is_copy_assignable<MeshLayout>{}); |
||||
} |
||||
|
||||
void MeshLayoutTest::constructMove() { |
||||
MeshLayout a{MeshPrimitive::Patches}; |
||||
a.addInstancedBinding(3, 5, 555) |
||||
.addAttribute(15, 23, VertexFormat::UnsignedShort, 11); |
||||
|
||||
MeshLayout b = std::move(a); |
||||
CORRADE_VERIFY(!a.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
CORRADE_COMPARE(a.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 0); |
||||
CORRADE_VERIFY(!a.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(a.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 0); |
||||
CORRADE_VERIFY(!a.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
CORRADE_VERIFY(b.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
CORRADE_COMPARE(static_cast<const VkPipelineVertexInputDivisorStateCreateInfoEXT*>(b.vkPipelineVertexInputStateCreateInfo().pNext)->sType, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 1); |
||||
CORRADE_VERIFY(b.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].stride, 5); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 1); |
||||
CORRADE_VERIFY(b.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].format, VK_FORMAT_R16_UINT); |
||||
|
||||
MeshLayout c{{}, {}}; |
||||
c = std::move(b); |
||||
CORRADE_VERIFY(!b.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 0); |
||||
CORRADE_VERIFY(!b.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(b.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 0); |
||||
CORRADE_VERIFY(!b.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
CORRADE_VERIFY(c.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
CORRADE_COMPARE(static_cast<const VkPipelineVertexInputDivisorStateCreateInfoEXT*>(c.vkPipelineVertexInputStateCreateInfo().pNext)->sType, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT); |
||||
CORRADE_COMPARE(c.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 1); |
||||
CORRADE_VERIFY(c.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(c.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].stride, 5); |
||||
CORRADE_COMPARE(c.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 1); |
||||
CORRADE_VERIFY(c.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
CORRADE_COMPARE(c.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].format, VK_FORMAT_R16_UINT); |
||||
|
||||
CORRADE_VERIFY(std::is_nothrow_move_constructible<MeshLayout>::value); |
||||
CORRADE_VERIFY(std::is_nothrow_move_assignable<MeshLayout>::value); |
||||
} |
||||
|
||||
void MeshLayoutTest::addBinding() { |
||||
MeshLayout layout{MeshPrimitive::Triangles}; |
||||
layout.addBinding(35, 2) |
||||
.addBinding(36, 17); |
||||
|
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 2); |
||||
CORRADE_VERIFY(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].binding, 35); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].stride, 2); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].inputRate, VK_VERTEX_INPUT_RATE_VERTEX); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].binding, 36); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].stride, 17); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].inputRate, VK_VERTEX_INPUT_RATE_VERTEX); |
||||
} |
||||
|
||||
void MeshLayoutTest::addInstancedBinding() { |
||||
MeshLayout layout{MeshPrimitive::Triangles}; |
||||
layout.addInstancedBinding(35, 17) |
||||
.addBinding(36, 2); |
||||
|
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 2); |
||||
CORRADE_VERIFY(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].binding, 35); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].stride, 17); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].inputRate, VK_VERTEX_INPUT_RATE_INSTANCE); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].binding, 36); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].stride, 2); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].inputRate, VK_VERTEX_INPUT_RATE_VERTEX); |
||||
} |
||||
|
||||
void MeshLayoutTest::addInstancedBindingDivisor() { |
||||
MeshLayout layout{MeshPrimitive::Triangles}; |
||||
|
||||
/* Set the pNext pointer to something to verify it's preserved */ |
||||
VkPhysicalDeviceVariablePointersFeatures variableFeatures{}; |
||||
layout.vkPipelineVertexInputStateCreateInfo().pNext = &variableFeatures; |
||||
|
||||
layout.addBinding(35, 2) |
||||
.addInstancedBinding(36, 17, 555) |
||||
.addInstancedBinding(37, 22, 0); |
||||
|
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexBindingDescriptionCount, 3); |
||||
CORRADE_VERIFY(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].binding, 35); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].stride, 2); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[0].inputRate, VK_VERTEX_INPUT_RATE_VERTEX); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].binding, 36); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].stride, 17); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[1].inputRate, VK_VERTEX_INPUT_RATE_INSTANCE); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[2].binding, 37); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[2].stride, 22); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexBindingDescriptions[2].inputRate, VK_VERTEX_INPUT_RATE_INSTANCE); |
||||
|
||||
/* Verify the extra structure is connected properly */ |
||||
CORRADE_VERIFY(layout.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
auto& vertexDivisorInfo = *static_cast<const VkPipelineVertexInputDivisorStateCreateInfoEXT*>(layout.vkPipelineVertexInputStateCreateInfo().pNext); |
||||
CORRADE_VERIFY(&vertexDivisorInfo != static_cast<const void*>(&variableFeatures)); |
||||
CORRADE_COMPARE(vertexDivisorInfo.sType, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT); |
||||
/* The original chain should be preserved */ |
||||
CORRADE_COMPARE(vertexDivisorInfo.pNext, &variableFeatures); |
||||
|
||||
CORRADE_COMPARE(vertexDivisorInfo.vertexBindingDivisorCount, 2); |
||||
CORRADE_VERIFY(vertexDivisorInfo.pVertexBindingDivisors); |
||||
CORRADE_COMPARE(vertexDivisorInfo.pVertexBindingDivisors[0].binding, 36); |
||||
CORRADE_COMPARE(vertexDivisorInfo.pVertexBindingDivisors[0].divisor, 555); |
||||
CORRADE_COMPARE(vertexDivisorInfo.pVertexBindingDivisors[1].binding, 37); |
||||
CORRADE_COMPARE(vertexDivisorInfo.pVertexBindingDivisors[1].divisor, 0); |
||||
} |
||||
|
||||
template<class T> void MeshLayoutTest::addAttribute() { |
||||
setTestCaseTemplateName(VertexFormatTraits<T>::name()); |
||||
|
||||
MeshLayout layout{MeshPrimitive::Triangles}; |
||||
layout.addAttribute(1, 35, T::Vector2ui, 17) |
||||
.addAttribute(2, 36, T::Double, 22); |
||||
|
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().vertexAttributeDescriptionCount, 2); |
||||
CORRADE_VERIFY(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].location, 1); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].binding, 35); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].format, VK_FORMAT_R32G32_UINT); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[0].offset, 17); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[1].location, 2); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[1].binding, 36); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[1].format, VK_FORMAT_R64_SFLOAT); |
||||
CORRADE_COMPARE(layout.vkPipelineVertexInputStateCreateInfo().pVertexAttributeDescriptions[1].offset, 22); |
||||
} |
||||
|
||||
void MeshLayoutTest::debugMeshPrimitive() { |
||||
std::ostringstream out; |
||||
Debug{&out} << MeshPrimitive::TriangleFan << MeshPrimitive(-10007655); |
||||
CORRADE_COMPARE(out.str(), "Vk::MeshPrimitive::TriangleFan Vk::MeshPrimitive(-10007655)\n"); |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Vk::Test::MeshLayoutTest) |
||||
Loading…
Reference in new issue