Browse Source

Vk: add a wrapper for mesh layout setup.

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
Vladimír Vondruš 5 years ago
parent
commit
bbb066664b
  1. 5
      doc/changelog.dox
  2. 24
      doc/snippets/MagnumVk.cpp
  3. 16
      doc/vulkan-mapping.dox
  4. 2
      doc/vulkan-support.dox
  5. 2
      src/Magnum/Vk/CMakeLists.txt
  6. 33
      src/Magnum/Vk/Enums.cpp
  7. 30
      src/Magnum/Vk/Enums.h
  8. 210
      src/Magnum/Vk/MeshLayout.cpp
  9. 373
      src/Magnum/Vk/MeshLayout.h
  10. 2
      src/Magnum/Vk/Test/CMakeLists.txt
  11. 96
      src/Magnum/Vk/Test/EnumsTest.cpp
  12. 352
      src/Magnum/Vk/Test/MeshLayoutTest.cpp
  13. 2
      src/Magnum/Vk/Vk.h

5
doc/changelog.dox

@ -373,6 +373,11 @@ See also:
@ref Vk::pixelFormat(Magnum::CompressedPixelFormat) that return the new
@ref Vk::PixelFormat enum that contains only values suitable for a pixel
format
- @cpp Vk::hasVkPrimitiveTopology() @ce and
@cpp Vk::vkPrimitiveTopology() @ce returning a raw
@type_vk{PrimitiveToplogy} are deprecated in favor of
@ref Vk::hasMeshPrimitive() and @ref Vk::meshPrimitive() that return the
new @ref Vk::MeshPrimitive enum
@subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs

24
doc/snippets/MagnumVk.cpp

@ -29,7 +29,9 @@
#include <Corrade/Utility/Directory.h>
#include "Magnum/Magnum.h"
#include "Magnum/Mesh.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/VertexFormat.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Vk/Assert.h"
#include "Magnum/Vk/BufferCreateInfo.h"
@ -48,6 +50,7 @@
#include "Magnum/Vk/ImageViewCreateInfo.h"
#include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/MemoryAllocateInfo.h"
#include "Magnum/Vk/MeshLayout.h"
#include "Magnum/Vk/Pipeline.h"
#include "Magnum/Vk/PixelFormat.h"
#include "Magnum/Vk/Queue.h"
@ -765,6 +768,27 @@ indices.bindMemory(memory, indicesOffset);
/* [Memory-mapping] */
}
{
/* [MeshLayout-usage] */
constexpr UnsignedInt BufferBinding = 0;
constexpr UnsignedInt PositionLocation = 0;
constexpr UnsignedInt TextureCoordinateLocation = 1;
constexpr UnsignedInt NormalLocation = 5;
Vk::MeshLayout meshLayout{MeshPrimitive::Triangles};
meshLayout
.addBinding(BufferBinding,
sizeof(Vector3) + sizeof(Vector2) + sizeof(Vector3))
.addAttribute(PositionLocation, BufferBinding, VertexFormat::Vector3,
0)
.addAttribute(TextureCoordinateLocation, BufferBinding, VertexFormat::Vector2,
sizeof(Vector3))
.addAttribute(NormalLocation, BufferBinding, VertexFormat::Vector3,
sizeof(Vector3) + sizeof(Vector2));
/* [MeshLayout-usage] */
}
{
Vk::Device device{NoCreate};
/* The include should be a no-op here since it was already included above */

16
doc/vulkan-mapping.dox

@ -647,7 +647,7 @@ Vulkan structure | Matching API
@type_vk{PipelineColorBlendStateCreateInfo} | |
@type_vk{PipelineDepthStencilStateCreateInfo} | |
@type_vk{PipelineDynamicStateCreateInfo} | |
@type_vk{PipelineInputAssemblyStateCreateInfo} | |
@type_vk{PipelineInputAssemblyStateCreateInfo} | @ref MeshLayout
@type_vk{PipelineLayoutCreateInfo} | |
@type_vk{PipelineLibraryCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | |
@type_vk{PipelineMultisampleStateCreateInfo} | |
@ -655,8 +655,8 @@ Vulkan structure | Matching API
@type_vk{PipelineShaderStageCreateInfo} | |
@type_vk{PipelineTessellationStateCreateInfo} | |
@type_vk{PipelineTessellationDomainOriginStateCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@type_vk{PipelineVertexInputDivisorStateCreateInfoEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@type_vk{PipelineVertexInputStateCreateInfo} | |
@type_vk{PipelineVertexInputDivisorStateCreateInfoEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref MeshLayout
@type_vk{PipelineVertexInputStateCreateInfo} | @ref MeshLayout
@type_vk{PipelineViewportStateCreateInfo} | |
@type_vk{ProtectedSubmitInfo} | |
@type_vk{PushConstantRange} | |
@ -739,9 +739,9 @@ Vulkan structure | Matching API
Vulkan structure | Matching API
--------------------------------------- | ------------
@type_vk{ValidationFeaturesEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@type_vk{VertexInputBindingDescription} | |
@type_vk{VertexInputBindingDivisorDescriptionEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@type_vk{VertexInputAttributeDescription} | |
@type_vk{VertexInputBindingDescription} | @ref MeshLayout
@type_vk{VertexInputBindingDivisorDescriptionEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref MeshLayout
@type_vk{VertexInputAttributeDescription} | @ref MeshLayout
@type_vk{Viewport} | convertible from/to @ref Range3D using @ref Magnum/Vk/Integration.h
@subsection vulkan-mapping-structures-w W
@ -914,7 +914,7 @@ Vulkan enum | Matching API
@type_vk{PipelineStageFlagBits}, \n @type_vk{PipelineStageFlags} | @ref PipelineStage, \n @ref PipelineStages
@type_vk{PointClippingBehavior} @m_class{m-label m-flat m-success} **KHR, 1.1** | |
@type_vk{PolygonMode} | |
@type_vk{PrimitveTopology} | only @ref vkPrimitiveTopology()
@type_vk{PrimitveTopology} | @ref MeshPrimitive
@subsection vulkan-mapping-enums-q Q
@ -985,7 +985,7 @@ Vulkan enum | Matching API
@type_vk{ValidationFeaturesDisableEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@type_vk{ValidationFeaturesEnableEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@type_vk{VendorId} | |
@type_vk{VertexInputRate} | |
@type_vk{VertexInputRate} | internal to @ref MeshLayout
*/

2
doc/vulkan-support.dox

@ -120,7 +120,7 @@ Extension | Status
@vk_extension{EXT,texture_compression_astc_hdr} | done
@vk_extension{EXT,debug_utils} @m_class{m-label m-info} **instance** | |
@vk_extension{EXT,validation_features} @m_class{m-label m-info} **instance** | |
@vk_extension{EXT,vertex_attribute_divisor} | |
@vk_extension{EXT,vertex_attribute_divisor} | done
@vk_extension{EXT,index_type_uint8} | @ref Vk::vkIndexType() only
@vk_extension{KHR,acceleration_structure} | |
@vk_extension{KHR,portability_subset} | done except properties

2
src/Magnum/Vk/CMakeLists.txt

@ -55,6 +55,7 @@ set(MagnumVk_GracefulAssert_SRCS
ImageView.cpp
Instance.cpp
LayerProperties.cpp
MeshLayout.cpp
Memory.cpp
PixelFormat.cpp
RenderPass.cpp
@ -89,6 +90,7 @@ set(MagnumVk_HEADERS
LayerProperties.h
Memory.h
MemoryAllocateInfo.h
MeshLayout.h
Pipeline.h
PixelFormat.h
Queue.h

33
src/Magnum/Vk/Enums.cpp

@ -31,6 +31,7 @@
#include "Magnum/Sampler.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include "Magnum/Vk/MeshLayout.h"
#include "Magnum/Vk/PixelFormat.h"
#include "Magnum/Vk/VertexFormat.h"
#endif
@ -39,19 +40,6 @@ namespace Magnum { namespace Vk {
namespace {
constexpr VkPrimitiveTopology PrimitiveTopologyMapping[]{
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
VkPrimitiveTopology(~UnsignedInt{}),
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
VkPrimitiveTopology(~UnsignedInt{}), /* Instances */
VkPrimitiveTopology(~UnsignedInt{}), /* Faces */
VkPrimitiveTopology(~UnsignedInt{}) /* Edges */
};
constexpr VkIndexType IndexTypeMapping[]{
VK_INDEX_TYPE_UINT8_EXT,
VK_INDEX_TYPE_UINT16,
@ -79,26 +67,15 @@ constexpr VkSamplerAddressMode SamplerAddressModeMapping[]{
}
#ifdef MAGNUM_BUILD_DEPRECATED
bool hasVkPrimitiveTopology(const Magnum::MeshPrimitive primitive) {
if(isMeshPrimitiveImplementationSpecific(primitive))
return true;
CORRADE_ASSERT(UnsignedInt(primitive) - 1 < Containers::arraySize(PrimitiveTopologyMapping),
"Vk::hasVkPrimitiveTopology(): invalid primitive" << primitive, {});
return UnsignedInt(PrimitiveTopologyMapping[UnsignedInt(primitive) - 1]) != ~UnsignedInt{};
return hasMeshPrimitive(primitive);
}
VkPrimitiveTopology vkPrimitiveTopology(const Magnum::MeshPrimitive primitive) {
if(isMeshPrimitiveImplementationSpecific(primitive))
return meshPrimitiveUnwrap<VkPrimitiveTopology>(primitive);
CORRADE_ASSERT(UnsignedInt(primitive) - 1 < Containers::arraySize(PrimitiveTopologyMapping),
"Vk::vkPrimitiveTopology(): invalid primitive" << primitive, {});
const VkPrimitiveTopology out = PrimitiveTopologyMapping[UnsignedInt(primitive) - 1];
CORRADE_ASSERT(out != VkPrimitiveTopology(~UnsignedInt{}),
"Vk::vkPrimitiveTopology(): unsupported primitive" << primitive, {});
return out;
return VkPrimitiveTopology(meshPrimitive(primitive));
}
#endif
bool hasVkIndexType(const Magnum::MeshIndexType type) {
CORRADE_ASSERT(UnsignedInt(type) - 1 < Containers::arraySize(IndexTypeMapping),

30
src/Magnum/Vk/Enums.h

@ -39,33 +39,19 @@
namespace Magnum { namespace Vk {
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief Check availability of a generic mesh primitive
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.
@see @ref vkPrimitiveTopology()
* @brief @copybrief hasMeshPrimitive()
* @m_deprecated_since_latest Use @ref hasMeshPrimitive() instead.
*/
MAGNUM_VK_EXPORT bool hasVkPrimitiveTopology(Magnum::MeshPrimitive primitive);
CORRADE_DEPRECATED("use hasMeshPrimitive() instead") MAGNUM_VK_EXPORT bool hasVkPrimitiveTopology(Magnum::MeshPrimitive primitive);
/**
@brief Convert generic mesh primitive to Vulkan primitive topology
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 hasVkPrimitiveTopology() to
query availability of given primitive.
@see @ref vkIndexType()
* @brief @copybrief meshPrimitive()
* @m_deprecated_since_latest Use @ref meshPrimitive() instead.
*/
MAGNUM_VK_EXPORT VkPrimitiveTopology vkPrimitiveTopology(Magnum::MeshPrimitive primitive);
CORRADE_DEPRECATED("use meshPrimitive() instead") MAGNUM_VK_EXPORT VkPrimitiveTopology vkPrimitiveTopology(Magnum::MeshPrimitive primitive);
#endif
/**
@brief Check availability of a generic index type

210
src/Magnum/Vk/MeshLayout.cpp

@ -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 << ")";
}
}}

373
src/Magnum/Vk/MeshLayout.h

@ -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

2
src/Magnum/Vk/Test/CMakeLists.txt

@ -42,6 +42,7 @@ corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkMeshLayoutTest MeshLayoutTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkPipelineTest PipelineTest.cpp LIBRARIES MagnumVk)
corrade_add_test(VkPixelFormatTest PixelFormatTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkQueueTest QueueTest.cpp LIBRARIES MagnumVk)
@ -137,6 +138,7 @@ set_target_properties(
VkIntegrationTest
VkLayerPropertiesTest
VkMemoryTest
VkMeshLayoutTest
VkPipelineTest
VkPixelFormatTest
VkQueueTest

96
src/Magnum/Vk/Test/EnumsTest.cpp

@ -38,11 +38,6 @@ namespace Magnum { namespace Vk { namespace Test { namespace {
struct EnumsTest: TestSuite::Tester {
explicit EnumsTest();
void mapVkPrimitiveTopology();
void mapVkPrimitiveTopologyImplementationSpecific();
void mapVkPrimitiveTopologyUnsupported();
void mapVkPrimitiveTopologyInvalid();
void mapVkIndexType();
void mapVkIndexTypeUnsupported();
void mapVkIndexTypeInvalid();
@ -60,12 +55,7 @@ struct EnumsTest: TestSuite::Tester {
};
EnumsTest::EnumsTest() {
addTests({&EnumsTest::mapVkPrimitiveTopology,
&EnumsTest::mapVkPrimitiveTopologyImplementationSpecific,
&EnumsTest::mapVkPrimitiveTopologyUnsupported,
&EnumsTest::mapVkPrimitiveTopologyInvalid,
&EnumsTest::mapVkIndexType,
addTests({&EnumsTest::mapVkIndexType,
&EnumsTest::mapVkIndexTypeUnsupported,
&EnumsTest::mapVkIndexTypeInvalid,
@ -81,90 +71,6 @@ EnumsTest::EnumsTest() {
&EnumsTest::mapVkSamplerAddressModeInvalid});
}
void EnumsTest::mapVkPrimitiveTopology() {
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::Points));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::Points), VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::Lines));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::Lines), VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::LineStrip));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::LineStrip), VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::Triangles));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::Triangles), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::TriangleStrip));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::TriangleStrip), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
CORRADE_VERIFY(hasVkPrimitiveTopology(Magnum::MeshPrimitive::TriangleFan));
CORRADE_COMPARE(vkPrimitiveTopology(Magnum::MeshPrimitive::TriangleFan), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN);
/* 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(hasVkPrimitiveTopology(Magnum::MeshPrimitive::primitive)) \
CORRADE_VERIFY(UnsignedInt(vkPrimitiveTopology(Magnum::MeshPrimitive::primitive)) >= 0); \
break;
#include "Magnum/Implementation/meshPrimitiveMapping.hpp"
#undef _c
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
void EnumsTest::mapVkPrimitiveTopologyImplementationSpecific() {
CORRADE_VERIFY(hasVkPrimitiveTopology(meshPrimitiveWrap(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY)));
CORRADE_COMPARE(vkPrimitiveTopology(meshPrimitiveWrap(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY)),
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY);
}
void EnumsTest::mapVkPrimitiveTopologyUnsupported() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
CORRADE_VERIFY(!hasVkPrimitiveTopology(Magnum::MeshPrimitive::LineLoop));
std::ostringstream out;
{
Error redirectError{&out};
vkPrimitiveTopology(Magnum::MeshPrimitive::LineLoop);
}
CORRADE_COMPARE(out.str(),
"Vk::vkPrimitiveTopology(): unsupported primitive MeshPrimitive::LineLoop\n");
}
void EnumsTest::mapVkPrimitiveTopologyInvalid() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
hasVkPrimitiveTopology(Magnum::MeshPrimitive{});
hasVkPrimitiveTopology(Magnum::MeshPrimitive(0x12));
vkPrimitiveTopology(Magnum::MeshPrimitive{});
vkPrimitiveTopology(Magnum::MeshPrimitive(0x12));
CORRADE_COMPARE(out.str(),
"Vk::hasVkPrimitiveTopology(): invalid primitive MeshPrimitive(0x0)\n"
"Vk::hasVkPrimitiveTopology(): invalid primitive MeshPrimitive(0x12)\n"
"Vk::vkPrimitiveTopology(): invalid primitive MeshPrimitive(0x0)\n"
"Vk::vkPrimitiveTopology(): invalid primitive MeshPrimitive(0x12)\n");
}
void EnumsTest::mapVkIndexType() {
CORRADE_VERIFY(hasVkIndexType(Magnum::MeshIndexType::UnsignedShort));
CORRADE_COMPARE(vkIndexType(Magnum::MeshIndexType::UnsignedShort), VK_INDEX_TYPE_UINT16);

352
src/Magnum/Vk/Test/MeshLayoutTest.cpp

@ -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)

2
src/Magnum/Vk/Vk.h

@ -94,6 +94,8 @@ enum class MemoryFlag: UnsignedInt;
typedef Containers::EnumSet<MemoryFlag> MemoryFlags;
enum class MemoryHeapFlag: UnsignedInt;
typedef Containers::EnumSet<MemoryHeapFlag> MemoryHeapFlags;
class MeshLayout;
enum class MeshPrimitive: Int;
enum class PipelineStage: UnsignedInt;
typedef Containers::EnumSet<PipelineStage> PipelineStages;
enum class PixelFormat: Int;

Loading…
Cancel
Save