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