From 120286a9309d63971314332a4881569d5248781c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 30 Jun 2021 12:30:56 +0200 Subject: [PATCH 01/10] Add Degh, Radh and Range*Dh typedefs. We have half-float vectors and matrices, so why not these as well. Not sure for what all is the angle precision usable, but at the very least it could be useful for compact meshlet occlusion cone / AABB representation or rough animations. --- doc/changelog.dox | 2 ++ src/Magnum/Magnum.h | 30 ++++++++++++++++++++++++++++++ src/Magnum/Math/Angle.h | 4 ++-- src/Magnum/Math/Range.h | 12 ++++++------ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 55a855c53..ac6332885 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -51,6 +51,8 @@ See also: including mapping to @ref GL::PixelFormat / @ref GL::PixelType, @ref GL::TextureFormat and @ref Vk::PixelFormat and (partial) support in @ref DebugTools::CompareImage +- New @ref Degh, @ref Radh, @ref Range1Dh, @ref Range2Dh and @ref Range3Dh + typedefs for half-float angles and ranges @subsubsection changelog-latest-new-debugtools DebugTools library diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 82fc79e15..12d7688cc 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -967,6 +967,36 @@ typedef Math::Matrix4x3 Matrix4x3h; */ typedef Math::Matrix4x4 Matrix4x4h; +/** +@brief Angle in half-float degrees +@m_since_latest +*/ +typedef Math::Deg Degh; + +/** +@brief Angle in half-float radians +@m_since_latest +*/ +typedef Math::Rad Radh; + +/** +@brief One-dimensional half-float range +@m_since_latest +*/ +typedef Math::Range1D Range1Dh; + +/** +@brief Two-dimensional half-float range +@m_since_latest +*/ +typedef Math::Range2D Range2Dh; + +/** +@brief Three-dimensional half-float range +@m_since_latest +*/ +typedef Math::Range3D Range3Dh; + /* Since 1.8.17, the original short-hand group closing doesn't work anymore. FFS. */ /** diff --git a/src/Magnum/Math/Angle.h b/src/Magnum/Math/Angle.h index 9edb499c7..13f4e70d8 100644 --- a/src/Magnum/Math/Angle.h +++ b/src/Magnum/Math/Angle.h @@ -94,7 +94,7 @@ These silent errors are easily avoided by requiring explicit conversions: @snippet MagnumMath.cpp Deg-usage-explicit-conversion -@see @ref Magnum::Deg, @ref Magnum::Degd +@see @ref Magnum::Deg, @ref Magnum::Degh, @ref Magnum::Degd */ template class Deg: public Unit { public: @@ -166,7 +166,7 @@ constexpr Deg operator "" _degf(long double value) { return Deg(Fl @brief Angle in radians See @ref Deg for more information. -@see @ref Magnum::Rad, @ref Magnum::Radd +@see @ref Magnum::Rad, @ref Magnum::Radh, @ref Magnum::Radd */ template class Rad: public Unit { public: diff --git a/src/Magnum/Math/Range.h b/src/Magnum/Math/Range.h index 10e2333e6..91c7d21b9 100644 --- a/src/Magnum/Math/Range.h +++ b/src/Magnum/Math/Range.h @@ -348,8 +348,8 @@ template class Range { Convenience alternative to @cpp Range<1, T> @ce. See @ref Range for more information. -@see @ref Range2D, @ref Range3D, @ref Magnum::Range1D, @ref Magnum::Range1Di, - @ref Magnum::Range1Dd +@see @ref Range2D, @ref Range3D, @ref Magnum::Range1D, @ref Magnum::Range1Dh, + @ref Magnum::Range1Dd, @ref Magnum::Range1Di */ #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */ template using Range1D = Range<1, T>; @@ -359,8 +359,8 @@ template using Range1D = Range<1, T>; @brief Two-dimensional range See @ref Range for more information. -@see @ref Range1D, @ref Range3D, @ref Magnum::Range2D, @ref Magnum::Range2Di, - @ref Magnum::Range2Dd +@see @ref Range1D, @ref Range3D, @ref Magnum::Range2D, @ref Magnum::Range2Dh, + @ref Magnum::Range2Dd, @ref Magnum::Range2Di */ template class Range2D: public Range<2, T> { public: @@ -513,8 +513,8 @@ template class Range2D: public Range<2, T> { @brief Three-dimensional range See @ref Range for more information. -@see @ref Range1D, @ref Range2D, @ref Magnum::Range3D, @ref Magnum::Range3Di, - @ref Magnum::Range3Dd +@see @ref Range1D, @ref Range2D, @ref Magnum::Range3D, @ref Magnum::Range3Dh, + @ref Magnum::Range3Dd, @ref Magnum::Range3Di */ template class Range3D: public Range<3, T> { public: From c59c48c75f1b4339eb4e82ba515adcbf4529db22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 7 Jul 2021 19:59:49 +0200 Subject: [PATCH 02/10] sceneconverter: list skins in --info. --- src/Magnum/MeshTools/sceneconverter.cpp | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index e7c814b0c..7a04a5acd 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -45,6 +45,7 @@ #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshObjectData3D.h" +#include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" #include "Magnum/Trade/AbstractSceneConverter.h" #include "Magnum/Trade/Implementation/converterUtilities.h" @@ -289,6 +290,13 @@ used.)") std::string name; }; + struct SkinInfo { + UnsignedInt skin; + UnsignedInt references; + Trade::SkinData3D data{{}, {}}; + std::string name; + }; + struct LightInfo { UnsignedInt light; UnsignedInt references; @@ -338,6 +346,7 @@ used.)") Containers::Array materialReferenceCount{importer->materialCount()}; Containers::Array lightReferenceCount{importer->lightCount()}; Containers::Array meshReferenceCount{importer->meshCount()}; + Containers::Array skinReferenceCount{importer->skin3DCount()}; for(UnsignedInt i = 0; i != importer->object3DCount(); ++i) { Containers::Pointer object = importer->object3D(i); if(!object) continue; @@ -347,6 +356,8 @@ used.)") ++meshReferenceCount[meshObject.instance()]; if(std::size_t(meshObject.material()) < materialReferenceCount.size()) ++materialReferenceCount[meshObject.material()]; + if(std::size_t(meshObject.skin()) < skinReferenceCount.size()) + ++skinReferenceCount[meshObject.skin()]; } else if(object->instanceType() == Trade::ObjectInstanceType3D::Light) { if(std::size_t(object->instance()) < lightReferenceCount.size()) ++lightReferenceCount[object->instance()]; @@ -374,6 +385,27 @@ used.)") arrayAppend(animationInfos, std::move(info)); } + /* Skin properties */ + Containers::Array skinInfos; + for(UnsignedInt i = 0; i != importer->skin3DCount(); ++i) { + Containers::Optional skin; + { + Duration d{importTime}; + if(!(skin = importer->skin3D(i))) { + error = true; + continue; + } + } + + SkinInfo info{}; + info.skin = i; + info.name = importer->skin3DName(i); + info.references = skinReferenceCount[i]; + info.data = *std::move(skin); + + arrayAppend(skinInfos, std::move(info)); + } + /* Light properties */ Containers::Array lightInfos; for(UnsignedInt i = 0; i != importer->lightCount(); ++i) { @@ -569,6 +601,19 @@ used.)") not so much for things like complex numbers or quats */ } } + for(const SkinInfo& info: skinInfos) { + Debug d; + d << "Skin" << info.skin; + /* Print reference count only if there actually is a scene, + otherwise this information is useless */ + if(importer->object3DCount()) + d << Utility::formatString("(referenced by {} objects)", info.references); + d << Debug::nospace << ":"; + if(!info.name.empty()) d << info.name; + + d << Debug::newline << " Joints:" << info.data.joints(); + } + for(const LightInfo& info: lightInfos) { Debug d; d << "Light" << info.light; From e72a440720410786c43ff75aec745c08aa696f6f Mon Sep 17 00:00:00 2001 From: Squareys Date: Sat, 10 Jul 2021 12:11:11 +0200 Subject: [PATCH 03/10] Math: typo. --- src/Magnum/Math/CubicHermite.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Math/CubicHermite.h b/src/Magnum/Math/CubicHermite.h index bb7106d07..9e74148f6 100644 --- a/src/Magnum/Math/CubicHermite.h +++ b/src/Magnum/Math/CubicHermite.h @@ -137,7 +137,7 @@ template class CubicHermite { constexpr /*implicit*/ CubicHermite(const T& inTangent, const T& point, const T& outTangent) noexcept: _inTangent{inTangent}, _point{point}, _outTangent{outTangent} {} /** - * @brief Construct subic Hermite spline point from another of different type + * @brief Construct cubic Hermite spline point from another of different type * * Performs only default casting on the values, no rounding or * anything else. From 1c3f4844e98bd90769f732e587a90f1409997c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 11 Jul 2021 00:23:22 +0200 Subject: [PATCH 04/10] Trade: doc++ --- src/Magnum/Trade/MeshData.h | 67 +++++++++++++++----------- src/Magnum/Trade/Test/MeshDataTest.cpp | 3 ++ 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 6b84034ae..40359f203 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -150,7 +150,7 @@ enum class MeshAttribute: UnsignedShort { /** * This and all higher values are for importer-specific attributes. Can be * of any type. See documentation of a particular importer for details. - * @see @ref isMeshAttributeCustom(MeshAttribute) + * @see @ref isMeshAttributeCustom(), * @ref meshAttributeCustom(MeshAttribute), * @ref meshAttributeCustom(UnsignedShort) */ @@ -294,22 +294,23 @@ or @ref MeshTools::interleave(const Trade::MeshData& data, Containers::ArrayView @section Trade-MeshAttributeData-usage Usage -The most straightforward usage is constructing an instance from a pair of +The most straightforward usage is constructing an instance from a pair of a @ref MeshAttribute and a strided view. The @ref VertexFormat gets inferred from the view type: @snippet MagnumTrade.cpp MeshAttributeData-usage -Alternatively, you can pass a typeless @cpp const void @ce view and supply -@ref VertexFormat explicitly, or a 2D view. +Alternatively, you can pass a typeless @cpp const void @ce or a 2D view and +supply @ref VertexFormat explicitly. @subsection Trade-MeshAttributeData-usage-offset-only Offset-only attribute data If the actual attribute data location is not known yet, the instance can be created as "offset-only", meaning the actual view gets created only later when passed to a @ref MeshData instance with a concrete vertex data array. This is -useful for example when vertex layout is static (and thus can be defined at -compile time), but the actual data is allocated / populated at runtime: +useful mainly to avoid pointer patching during data serialization, but also for +example when vertex layout is static (and thus can be defined at compile time), +but the actual data is allocated / populated at runtime: @snippet MagnumTrade.cpp MeshAttributeData-usage-offset-only @@ -350,8 +351,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { * attributes. * * Expects that @p data stride is large enough to fit all @p arraySize - * items of @p type, @p type corresponds to @p name and @p arraySize is - * zero for builtin attributes. + * items of @p format, @p format corresponds to @p name and + * @p arraySize is zero for builtin attributes. */ explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D& data, UnsignedShort arraySize = 0) noexcept; @@ -364,8 +365,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { * attributes. * * Expects that the second dimension of @p data is contiguous and its - * size matches @p type and @p arraSize, that @p type corresponds to - * @p name and @p arraySize is zero for builtin attributes. + * size matches @p format and @p arraySize, that @p format corresponds + * to @p name and @p arraySize is zero for builtin attributes. */ explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D& data, UnsignedShort arraySize = 0) noexcept; @@ -515,8 +516,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { * @brief Type-erased attribute data * * Expects that the attribute is not offset-only, in that case use the - * @ref data(Containers::ArrayView) const overload - * instead. + * @ref data(Containers::ArrayView) const overload instead. * @see @ref isOffsetOnly() */ constexpr Containers::StridedArrayView1D data() const { @@ -544,7 +544,9 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { private: friend MeshData; - /* nullptr first, to avoid accidental matches as much as possible */ + /* Delegated to by all ArrayView constructors, which additionally check + either stride or second dimension size. Nullptr first, to avoid + accidental matches as much as possible. */ constexpr explicit MeshAttributeData(std::nullptr_t, MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D& data, UnsignedShort arraySize) noexcept; VertexFormat _format; @@ -1301,11 +1303,13 @@ class MAGNUM_TRADE_EXPORT MeshData { * * The @p id is expected to be smaller than @ref attributeCount() const. * The second dimension represents the actual data type (its size is - * equal to format size for known @ref VertexFormat values and to - * attribute stride for implementation-specific values) and is - * guaranteed to be contiguous. Use the templated overload below to get - * the attribute in a concrete type. + * equal to format size for known @ref VertexFormat values, possibly + * multiplied by array size, and to attribute stride for + * implementation-specific values) and is guaranteed to be contiguous. + * Use the templated overload below to get the attribute in a concrete + * type. * @see @ref Corrade::Containers::StridedArrayView::isContiguous(), + * @ref vertexFormatSize(), * @ref isVertexFormatImplementationSpecific() */ Containers::StridedArrayView2D attribute(UnsignedInt id) const; @@ -1338,7 +1342,7 @@ class MAGNUM_TRADE_EXPORT MeshData { * to usual types, but note that these operations involve extra * allocation and data conversion. * @see @ref attribute(MeshAttribute, UnsignedInt) const, - * @ref mutableAttribute(MeshAttribute, UnsignedInt), + * @ref mutableAttribute(UnsignedInt), * @ref isVertexFormatImplementationSpecific(), * @ref attributeArraySize() */ @@ -1409,13 +1413,15 @@ class MAGNUM_TRADE_EXPORT MeshData { * correspond to @ref attributeFormat(MeshAttribute, UnsignedInt) const. * Expects that the vertex format is *not* implementation-specific, in * that case you can only access the attribute via the typeless - * @ref attribute(MeshAttribute, UnsignedInt) const above. You can also - * use the non-templated @ref positions2DAsArray(), - * @ref positions3DAsArray(), @ref normalsAsArray(), - * @ref textureCoordinates2DAsArray() and @ref colorsAsArray() - * accessors to get common attributes converted to usual types, but - * note that these operations involve extra data conversion and an - * allocation. + * @ref attribute(MeshAttribute, UnsignedInt) const above. The + * attribute is also expected to not be an array, in that case you need + * to use the overload below by using @cpp T[] @ce instead of + * @cpp T @ce. You can also use the non-templated + * @ref positions2DAsArray(), @ref positions3DAsArray(), + * @ref normalsAsArray(), @ref textureCoordinates2DAsArray() and + * @ref colorsAsArray() accessors to get common attributes converted to + * usual types, but note that these operations involve extra data + * conversion and an allocation. * @see @ref attribute(UnsignedInt) const, * @ref mutableAttribute(MeshAttribute, UnsignedInt), * @ref isVertexFormatImplementationSpecific() @@ -1808,7 +1814,7 @@ namespace Implementation { /* Implicit mapping from a format to enum (1:1) */ template constexpr VertexFormat vertexFormatFor() { /* C++ why there isn't an obvious way to do such a thing?! */ - static_assert(sizeof(T) == 0, "unsupported attribute type"); + static_assert(sizeof(T) == 0, "unsupported vertex format"); return {}; } #ifndef DOXYGEN_GENERATING_OUTPUT @@ -2093,7 +2099,14 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D& data) noexcept: MeshAttributeData{nullptr, name, Implementation::vertexFormatFor::type>(), data, 0} {} -template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D& data) noexcept: MeshAttributeData{(CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr), name, Implementation::vertexFormatFor::type>(), Containers::StridedArrayView1D{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, UnsignedShort(data.size()[1])} {} +template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D& data) noexcept: MeshAttributeData{ + /* Not using isContiguous<1>() as that's not constexpr */ + (CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr), + name, + Implementation::vertexFormatFor::type>(), + Containers::StridedArrayView1D{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, + UnsignedShort(data.size()[1]) +} {} template Containers::ArrayView MeshData::indices() const { CORRADE_ASSERT(isIndexed(), diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index ce9bbfa0f..1e2f8a5fd 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -605,6 +605,9 @@ void MeshDataTest::constructAttributeDefault() { } void MeshDataTest::constructAttributeCustom() { + /* Verifying it doesn't hit any assertion about disallowed type for given + attribute */ + const Short idData[3]{}; MeshAttributeData ids{meshAttributeCustom(13), Containers::arrayView(idData)}; CORRADE_COMPARE(ids.name(), meshAttributeCustom(13)); From 49a5756f708ea11bd3b88c5e541251830d07358a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 11 Jul 2021 00:23:35 +0200 Subject: [PATCH 05/10] Trade: minor. --- src/Magnum/Trade/MeshData.cpp | 4 ++-- src/Magnum/Trade/MeshData.h | 4 ++-- src/Magnum/Trade/Test/MeshDataTest.cpp | 9 ++++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Magnum/Trade/MeshData.cpp b/src/Magnum/Trade/MeshData.cpp index 080a74988..e4df296c6 100644 --- a/src/Magnum/Trade/MeshData.cpp +++ b/src/Magnum/Trade/MeshData.cpp @@ -202,10 +202,10 @@ MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags MeshData::MeshData(const MeshPrimitive primitive, const UnsignedInt vertexCount, const void* const importerState) noexcept: MeshData{primitive, {}, MeshIndexData{}, {}, {}, vertexCount, importerState} {} -MeshData::~MeshData() = default; - MeshData::MeshData(MeshData&&) noexcept = default; +MeshData::~MeshData() = default; + MeshData& MeshData::operator=(MeshData&&) noexcept = default; Containers::ArrayView MeshData::mutableIndexData() & { diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 40359f203..63e76bd68 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -940,14 +940,14 @@ class MAGNUM_TRADE_EXPORT MeshData { */ explicit MeshData(MeshPrimitive primitive, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept; - ~MeshData(); - /** @brief Copying is not allowed */ MeshData(const MeshData&) = delete; /** @brief Move constructor */ MeshData(MeshData&&) noexcept; + ~MeshData(); + /** @brief Copying is not allowed */ MeshData& operator=(const MeshData&) = delete; diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index 1e2f8a5fd..c5f5ba99f 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -576,8 +576,11 @@ void MeshDataTest::constructAttribute() { CORRADE_COMPARE(positions.offset(positionData), 0); CORRADE_COMPARE(positions.stride(), sizeof(Vector2)); CORRADE_VERIFY(positions.data().data() == positionData); - /* This is allowed too for simplicity, it just ignores the parameter */ - CORRADE_VERIFY(positions.data(positionData).data() == positionData); + + /* This is allowed too for simplicity, the parameter has to be large enough + tho */ + char someArray[3*sizeof(Vector2)]; + CORRADE_VERIFY(positions.data(someArray).data() == positionData); constexpr MeshAttributeData cpositions{MeshAttribute::Position, Containers::arrayView(Positions)}; constexpr bool isOffsetOnly = cpositions.isOffsetOnly(); @@ -889,7 +892,7 @@ void MeshDataTest::constructArrayAttribute2DNonContiguous() { void MeshDataTest::constructArrayAttributeTypeErased() { Vector2 vertexData[3*4]; Containers::StridedArrayView1D attribute{vertexData, 3, 4*sizeof(Vector2)}; - MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, attribute, 4}; + MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, Containers::arrayCast(attribute), 4}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), meshAttributeCustom(35)); CORRADE_COMPARE(data.format(), VertexFormat::Vector2); From 27b0527fc03f5cc16989b3e5dbfe130c69c58712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 15 Jul 2021 15:27:37 +0200 Subject: [PATCH 06/10] Trade: use consistent naming in an assert message. --- src/Magnum/Trade/MeshData.h | 2 +- src/Magnum/Trade/Test/MeshDataTest.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 63e76bd68..6a19f91e1 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -524,7 +524,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ {_data.pointer, ~std::size_t{}}, _vertexCount, - (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array"), _stride)}; + (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::MeshAttributeData::data(): the attribute is offset-only, supply a data array"), _stride)}; } /** diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index c5f5ba99f..1169f45c7 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -795,11 +795,13 @@ void MeshDataTest::constructAttributeWrongDataAccess() { CORRADE_VERIFY(!a.isOffsetOnly()); CORRADE_VERIFY(b.isOffsetOnly()); + a.data(positionData); /* This is fine, no asserts */ + std::ostringstream out; Error redirectError{&out}; b.data(); CORRADE_COMPARE(out.str(), - "Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array\n"); + "Trade::MeshAttributeData::data(): the attribute is offset-only, supply a data array\n"); } constexpr Vector2 ArrayVertexData[3*4] From 2bd933d3ef20c934cd49b80bcacadb4606a8b6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 17 Jul 2021 11:54:43 +0200 Subject: [PATCH 07/10] doc: add docs for GL context asserts and other troubleshooting. --- doc/compilation-speedup.dox | 18 ++-- doc/opengl-wrapping.dox | 94 +++++++++++-------- doc/platform.dox | 47 ++++++++-- doc/snippets/CMakeLists.txt | 2 +- ...amebuffer.cpp => MagnumGL-application.cpp} | 27 ++++++ doc/snippets/MagnumGL.cpp | 17 ---- doc/snippets/MagnumPlatform.cpp | 4 +- doc/troubleshooting.dox | 90 ++++++++++++++---- src/Magnum/GL/Context.h | 4 +- src/Magnum/GL/DefaultFramebuffer.h | 4 +- 10 files changed, 209 insertions(+), 98 deletions(-) rename doc/snippets/{MagnumGL-framebuffer.cpp => MagnumGL-application.cpp} (80%) diff --git a/doc/compilation-speedup.dox b/doc/compilation-speedup.dox index 688401977..53dbba44e 100644 --- a/doc/compilation-speedup.dox +++ b/doc/compilation-speedup.dox @@ -25,27 +25,27 @@ namespace Magnum { /** @page compilation-speedup Speeding up compilation -@brief Techniques for reducing compilation times. +@brief Techniques for reducing compilation times used by Magnum itself and recommended for application code as well. @tableofcontents @m_footernavigation @section compilation-forward-declarations Forward declarations instead of includes -Essential thing when speeding up compilation is reducing number of @cpp #include @ce -directives in both headers and source files. Magnum is strictly applying this -policy in all header files, so all types which are not directly used in the -header have only forward declarations. +An essential thing when speeding up compilation is reducing number of +@cpp #include @ce directives in both headers and source files. Magnum is +strictly applying this policy in all header files, so all types which are not +directly used in the header have only forward declarations. For example, when including @ref Magnum.h, you get shortcut typedefs for floating-point vectors and matrices like @ref Vector3 and @ref Matrix4, but to actually use any of them, you have to include the respective header, e.g. @ref Magnum/Math/Vector3.h. -You are encouraged to use forward declarations also in your code. However, for -some types it can be too cumbersome --- e.g. too many template parameters, -typedefs etc. In this case a header with forward declarations is usually -available, each namespace has its own: +You are encouraged to use forward declarations in your code as well. However, +for some types it can be too cumbersome --- e.g. too many template parameters, +default template arguments, typedefs etc. Instead, forward declaration headers +are available, with each namespace having its own: - @ref Corrade/Corrade.h - @ref Corrade/Containers/Containers.h diff --git a/doc/opengl-wrapping.dox b/doc/opengl-wrapping.dox index 2a3821728..d642ca725 100644 --- a/doc/opengl-wrapping.dox +++ b/doc/opengl-wrapping.dox @@ -31,9 +31,9 @@ namespace Magnum { @m_footernavigation The purpose of the @ref GL library is to simplify interaction with the OpenGL -API using type-safe C++11 features, abstracting away extension and platform -differences, tracking the state for optimum performance and selecting the best -available code path for given system. +API using type-safe C++11 features and RAII, abstracting away extension and +platform differences, tracking the state for optimum performance and selecting +the best available code path for given driver. Magnum provides wrappers for most native OpenGL objects like buffers, textures, meshes, queries, transform feedback objects, shaders etc., but makes it @@ -42,49 +42,65 @@ libraries if the user wants to. @section opengl-wrapping-instances OpenGL object wrapper instances -By default, all underlying OpenGL objects are created in wrapper class -constructor and deleted in wrapper class destructor. Constructing an object -using default constructor requires active @ref GL::Context instance. All OpenGL -objects are movable (but not copyable), although for performance reasons (and -contrary to standard C++11 practice), the moved-from instance does *not* have -any associated OpenGL object and is thus in *invalid state*. Using instance in -moved-from state may result in OpenGL errors being generated, in some cases -even application crashes. - -Besides the default behavior, it is possible to construct the object without -creating the underlying OpenGL object using the @ref NoCreate tag. -Constructing the object this way does not require any active context and its -state is then equivalent to the moved-from state. It is useful in case you need -to construct the object before creating context (such as class members) or if -you know you would overwrite it later with another object: - -@snippet MagnumGL.cpp opengl-wrapping-nocreate - - - -@m_class{m-note m-warning} - -@par - Note that calling anything on objects created this way is not defined (and - not checked or guarded in any way) and may result in crashes. If you want delayed object creation with safety checks (however with some extra - memory overhead), wrap the objects in an @ref Corrade::Containers::Optional. +By default, all wrapper classes follow RAII -- underlying OpenGL objects are +created in the class constructor and deleted in the wrapper class destructor. +All OpenGL objects are movable (but not copyable) and moves are *destructive* +--- the moved-from instance does *not* have any associated OpenGL object and +is thus in an *invalid state*. Calling anything on instances in a moved-from +state may thus result in OpenGL errors being generated, in some cases even +application crashes. If you need to preserve the underlying OpenGL object after destruction, you can call @cpp release() @ce. It returns ID of the underlying object, the instance -is then equivalent to moved-from state and you are responsible for proper +is then equivalent to the moved-from state and you are responsible for proper deletion of the returned OpenGL object (note that it is possible to just query -ID of the underlying without releasing it using `id()`). It is also possible to -do the opposite --- wrapping existing OpenGL object ID into Magnum object -instance using @cpp wrap() @ce. +ID of the underlying without releasing it using @cpp id() @ce). It is also +possible to do the opposite --- wrapping an existing OpenGL object ID into a +Magnum object instance using @cpp wrap() @ce: @snippet MagnumGL.cpp opengl-wrapping-transfer -The @cpp NoCreate @ce constructor, @cpp wrap() @ce and @cpp release() @ce -functions are available for all OpenGL classes except @ref GL::Shader, where -wrapping external instances makes less sense. - -Note that interaction with third-party OpenGL code as shown above usually needs -special attention: +The @cpp wrap() @ce and @cpp release() @ce functions are available for all +OpenGL classes except for @ref GL::Shader, instances of which are rather +short-lived and thus wrapping external instances makes less sense. + +@attention + Note that interaction with third-party OpenGL code as shown above usually + needs special attention due to the global nature of the OpenGL state + tracker. See @ref opengl-state-tracking below for more information. + +@subsection opengl-wrapping-instances-nocreate Delayed context creation and NoCreate constructors + +Constructing a Magnum GL object requires an active @ref GL::Context instance. +By default, this instance is automatically created by +@ref Platform::Sdl2Application "Platform::*Application" constructors, however +if you use @ref platform-configuration-delayed "delayed context creation" or +are directly interfacing with @ref platform-custom "custom platform toolkits", +this is not the case. If OpenGL functionality gets called before the +@ref GL::Context instance is created (or after it has been destroyed), you may +end up with the following assertion: + +@code{.shell-session} +GL::Context::current(): no current context +@endcode + +In the common case of delayed context creation, this problem can be +circumvented by constructing the OpenGL objects using the @ref NoCreate tag +first and populating them with live instances once the context is ready. For +example: + +@snippet MagnumGL-application.cpp opengl-wrapping-nocreate + +Please note that objects constructed using the @ref NoCreate tag are equivalent +to the moved-from state, and thus again calling anything on these may result in +OpenGL errors being generated or even application crashes --- you're +responsible for correctly initializing the objects before use. If you are fine +with trading some overhead for extra safety checks, wrap the objects in an +@relativeref{Corrade,Containers::Optional} instead of using the @ref NoCreate +constructor. + +The @ref NoCreate constructor is available for all OpenGL classes, including +builtin shaders. @section opengl-state-tracking State tracking and interaction with third-party code diff --git a/doc/platform.dox b/doc/platform.dox index bae1a6483..557c079ae 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -143,22 +143,49 @@ this: @snippet MagnumPlatform.cpp configuration -However, sometimes you would need to configure the application based on some -configuration file or system introspection. In that case you can pass -@ref NoCreate instead of @ref Platform::Sdl2Application::Configuration "Configuration" -instance and then specify it later with @ref Platform::Sdl2Application::create() "create()": +@subsection platform-configuration-delayed Delayed context creation + +Sometimes you may want to set up the application based on a configuration file +or system introspection, which can't really be done inside the base class +initializer. You can specify @ref NoCreate in the constructor instead and pass +the @relativeref{Platform::Sdl2Application,Configuration} later to a +@relativeref{Platform::Sdl2Application,create()} function: @snippet MagnumPlatform.cpp createcontext -If the context creation in constructor or @ref Platform::Sdl2Application::create() "create()" -fails, the application exits. However, it is also possible to negotiate the -context using @ref Platform::Sdl2Application::tryCreate() "tryCreate()". The -only difference is that this function returns `false` instead of exiting. You -can for example try enabling MSAA and if the context creation fails, fall back -to no-AA rendering: +If context creation in the constructor or in +@relativeref{Platform::Sdl2Application,create()} fails, the application prints +an error message to standard output and exits. While that frees you from having +to do explicit error handling, sometimes a more graceful behavior may be +desirable --- with @relativeref{Platform::Sdl2Application,tryCreate()} the +function returns @cpp false @ce instead of exiting and it's up to you whether +you abort the launch or retry with different configuration. You can for example +try enabling MSAA first, and if the context creation fails, fall back to no-AA +rendering: @snippet MagnumPlatform.cpp trycreatecontext + + +@m_class{m-block m-warning} + +@par Implications for GL objects as class members + Delaying GL context creation to + @relativeref{Platform::Sdl2Application,create()} / + @relativeref{Platform::Sdl2Application,tryCreate()} means that, at the + point when class members get constructed, the context isn't available yet. + If you have GL objects such as @ref GL::Mesh or @ref Shaders::PhongGL as + class members, the application may hit the following assert during startup: +@par + @code{.shell-session} + GL::Context::current(): no current context + @endcode +@par + The solution is to construct the GL objects with @ref NoCreate constructors + as well and populate them with live instances only after the context has + been created. See @ref opengl-wrapping-instances-nocreate for more + information. + @section platform-custom Using custom platform toolkits In case you want to use some not-yet-supported toolkit or you don't want to use diff --git a/doc/snippets/CMakeLists.txt b/doc/snippets/CMakeLists.txt index 9af7aca4b..f1c6299e6 100644 --- a/doc/snippets/CMakeLists.txt +++ b/doc/snippets/CMakeLists.txt @@ -203,7 +203,7 @@ if(WITH_SDL2APPLICATION AND TARGET_GL) add_library(snippets-MagnumPlatform STATIC MagnumPlatform.cpp - MagnumGL-framebuffer.cpp) + MagnumGL-application.cpp) target_link_libraries(snippets-MagnumPlatform PRIVATE MagnumSdl2Application) set_target_properties( diff --git a/doc/snippets/MagnumGL-framebuffer.cpp b/doc/snippets/MagnumGL-application.cpp similarity index 80% rename from doc/snippets/MagnumGL-framebuffer.cpp rename to doc/snippets/MagnumGL-application.cpp index 70b82db50..e17606270 100644 --- a/doc/snippets/MagnumGL-framebuffer.cpp +++ b/doc/snippets/MagnumGL-application.cpp @@ -26,11 +26,38 @@ #include "Magnum/GL/Buffer.h" #include "Magnum/GL/DefaultFramebuffer.h" #include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" #include "Magnum/Platform/Sdl2Application.h" #include "Magnum/Platform/GLContext.h" +#include "Magnum/Shaders/PhongGL.h" using namespace Magnum; +#define DOXYGEN_IGNORE(...) __VA_ARGS__ + +/* [opengl-wrapping-nocreate] */ +class MyApplication: public Platform::Application { + DOXYGEN_IGNORE(explicit MyApplication(const Arguments& arguments);) + + private: + /* Placeholders without an underlying GL object */ + GL::Mesh _mesh{NoCreate}; + Shaders::PhongGL _shader{NoCreate}; + DOXYGEN_IGNORE() +}; + +MyApplication::MyApplication(const Arguments& arguments): + Platform::Application{arguments, NoCreate} +{ + DOXYGEN_IGNORE() + create(); + + /* GL context is ready, now it's safe to populate the GL objects */ + _mesh = GL::Mesh{}; + _shader = Shaders::PhongGL{}; +} +/* [opengl-wrapping-nocreate] */ + struct A: Platform::Sdl2Application { /* [DefaultFramebuffer-usage-viewport] */ void viewportEvent(ViewportEvent& event) override { diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index e11b93612..5876a4c3d 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -120,23 +120,6 @@ carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256}) } #endif -{ -#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 -#pragma GCC diagnostic push -/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */ -#pragma GCC diagnostic ignored "-Wvexing-parse" -#endif -auto importSomeMesh() -> std::tuple; -#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 -#pragma GCC diagnostic pop -#endif -/* [opengl-wrapping-nocreate] */ -GL::Mesh mesh{NoCreate}; -GL::Buffer vertices{NoCreate}, indices{NoCreate}; -std::tie(mesh, vertices, indices) = importSomeMesh(); -/* [opengl-wrapping-nocreate] */ -} - { struct Foo { void setSomeBuffer(GLuint) {} diff --git a/doc/snippets/MagnumPlatform.cpp b/doc/snippets/MagnumPlatform.cpp index 7da163055..a1f312e97 100644 --- a/doc/snippets/MagnumPlatform.cpp +++ b/doc/snippets/MagnumPlatform.cpp @@ -89,9 +89,9 @@ struct MyApplication: Platform::Application { /* [configuration] */ MyApplication::MyApplication(const Arguments& arguments): - Platform::Application(arguments, Configuration{} + Platform::Application{arguments, Configuration{} .setTitle("My Application") - .setSize({800, 600})) + .setSize({800, 600})} { // ... } diff --git a/doc/troubleshooting.dox b/doc/troubleshooting.dox index 909788932..cc618cc8b 100644 --- a/doc/troubleshooting.dox +++ b/doc/troubleshooting.dox @@ -25,29 +25,87 @@ namespace Magnum { /** @page troubleshooting Troubleshooting -@brief Various tricks to overcome common building and rendering issues. +@brief Various tricks and solutions to common pitfalls. +@tableofcontents @m_footernavigation @section troubleshooting-building Building issues -If your project suddenly stops building after Magnum upgrade, check these +If your project suddenly stops building after a Magnum upgrade, check these things: -- If the building fails on CMake step, be sure that you have up-to-date +- If building fails on the CMake step, be sure that you have up-to-date `FindCorrade.cmake`, `FindMagnum.cmake` and other CMake modules in your - project (`FindSDL2.cmake`). They are contained in `modules/` directory of - Magnum sources (and sources of other projects) also are installed into - `share/cmake/Magnum`. -- In some cases when the changes done to build system are too drastic, - recreating the build dir or clearing CMake cache is needed, but this is - a very rare occasion. -- The library is constantly evolving, thus some API might get deprecated over - time (and later removed). Either build the libraries with `BUILD_DEPRECATED` - or switch to non-deprecated features. See @ref building for more - information. - -@section troubleshooting-rendering Rendering issues + project (`FindSDL2.cmake`, ...). They are contained in `modules/` directory + of Magnum repositories and also get installed into `share/cmake/Magnum`. +- In very rare cases the CMake build directory may not survive an upgrade. If + you get some particularly cursed errors, try recreating it from scratch. +- The library is constantly evolving, meaning that APIs may get deprecated + and removed over time. If you upgrade to a version that deprecated a + particular API you use, the code will emit a deprecation warning, + suggesting a migration path. Except for rare cases, the deprecated APIs are + guaranteed to stay in the codebase for a year at least (or two subsequent + version releases, whichever is longer) and only then they get removed. To + make sure you're no longer using any deprecated functionality, it's + possible to disable the `BUILD_DEPRECATED` @ref building-features "CMake option" + when building Magnum, although for a smoother experience it's recommended + to flip this option back on when upgrading. A list of deprecated APIs is + maintained @ref changelog-latest-deprecated "in the changelog". +- In some rare cases, it's impossible to provide a deprecated migration path + and an API gets changed in a backwards-incompatible manner, directly + leading to a compile error. Another possibility is that transitive header + dependencies got cleaned up for @ref compilation-forward-declarations "speeding up compilation" and you're now missing an @cpp #include @ce. The major + compatiblity breakages are always listed @ref changelog-latest-compatibility "in the changelog". +- If you upgrade from a really old release, it's recommended to gradually + upgrade over tagged versions of Magnum and not jumping directly to latest + version. That way you're more likely to follow the deprecation path, + instead of directly ending up with long-deprecated APIs being gone with no + direct way to know what should be used instead. If in doubt, browse the + @ref changelog "old changelogs". + +@section troubleshooting-runtime Runtime issues + +Magnum makes heavy use of assertions to catch programmer errors (as opposed to +runtime or data-dependent errors, which get handled in more graceful ways), and +because past experience showed their usefulness, majority of assertions is +@ref CORRADE_NO_ASSERT "by default kept even in release builds". + +If your application abruptly exits, it's important to know whether it was a +regular *exit*, an *abort* or a *crash*, as each of these may point to a +different problem. + +- Except for crashes, in which case the application usually won't even have a + chance to complain about anything, you should get a message on the standard + error output: + - On Unix systems the output can be seen when running from a console + - On Windows, if you don't see the console, switch the build back to a + console app @ref platform-windows-hiding-console "instead of a GUI app" + --- in case of CMake you can temporarily remove the `WIN32` part from + your @cmake add_executable() @ce call + - On Android @ref platforms-android-output-redirection "you can use logcat" + - On Emscripten the output is printed to the browser console +- A regular exit may happen during startup due to an error in arguments + passed on the command line, or when a window / context creation fails. The + error output should mention what happened. +- An abort is an assertion failure, with the error message telling you why. + It's an abort in order to trigger a debugger break (or the assertion dialog + on Windows) or create a core dump (on Unix systems) so you can see the + backtrace leading to the error. + - One of the more frequent assertion messages is + @cb{.shell-session} GL::Context::current(): no current context @ce. + This happens when you try to use OpenGL functionality when the OpenGL + context is not yet created (or no longer exists). See + @ref opengl-wrapping-instances-nocreate for more information. +- A crash is usually a memory issue. To find the root cause, by far the + easiest is to hook up AddressSanitizer (`-fsanitize=address` on GCC, Clang + and [recent MSVC](https://docs.microsoft.com/en-us/cpp/sanitizers/asan)). + It can detect out-of-bounds accesses, use-after-free, leaks and other + issues and upon discovering a problem it prints a lengthy diagnostic about + what happened, where does the memory come from and what code touched it and + how. + +@section troubleshooting-opengl OpenGL issues If you are experiencing the so-called "black screen of death", weird behavior or crashes on GL calls, you might want to try these things: @@ -99,7 +157,7 @@ or crashes on GL calls, you might want to try these things: having similar ability to reset its state tracker), otherwise you may need to save and restore GL state manually for that library to work. -@section troubleshooting-debugging Debugging rendering +@subsection troubleshooting-opengl-debugging Debugging rendering - Use @ref GL::TimeQuery to find hot spots in the rendering code. - @ref GL::DebugMessage::insert() "Mark relevant parts of code" to find them diff --git a/src/Magnum/GL/Context.h b/src/Magnum/GL/Context.h index 94d7a4337..fe01327be 100644 --- a/src/Magnum/GL/Context.h +++ b/src/Magnum/GL/Context.h @@ -204,12 +204,12 @@ instances for other OpenGL contexts, *first* you need to "unset" the current one with @ref makeCurrent() and *then* create another instance, which will then become implicitly active: -@snippet MagnumGL-framebuffer.cpp Context-makeCurrent-nullptr +@snippet MagnumGL-application.cpp Context-makeCurrent-nullptr Once all needed instances are created, switch between them right after making the underlying GL context current: -@snippet MagnumGL-framebuffer.cpp Context-makeCurrent +@snippet MagnumGL-application.cpp Context-makeCurrent @section GL-Context-multithreading Thread safety diff --git a/src/Magnum/GL/DefaultFramebuffer.h b/src/Magnum/GL/DefaultFramebuffer.h index 3f01d0ff7..27390c828 100644 --- a/src/Magnum/GL/DefaultFramebuffer.h +++ b/src/Magnum/GL/DefaultFramebuffer.h @@ -49,7 +49,7 @@ classes, pass the new size in your @ref Platform::Sdl2Application::viewportEvent() "viewportEvent()" implementation, for example: -@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-viewport +@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-viewport Next thing you probably want is to clear all used buffers before performing any drawing. Again, in case you're using one of the @@ -57,7 +57,7 @@ any drawing. Again, in case you're using one of the @ref Platform::Sdl2Application::drawEvent() "drawEvent()" implementation, for example: -@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-clear +@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-clear See documentation of particular functions and @ref Framebuffer documentation for more involved usage, usage of non-default or multiple framebuffers. From c13c61e17d6e4788cf05a3f4b63e79f93b225e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 17 Jul 2021 11:55:33 +0200 Subject: [PATCH 08/10] Trade: hint on related APIs in mutable MeshData access. --- src/Magnum/Trade/MeshData.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 6a19f91e1..0a2912549 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -646,10 +646,15 @@ cases when it's desirable to modify the data in-place, there's the @ref mutableIndexData(), @ref mutableVertexData(), @ref mutableIndices() and @ref mutableAttribute() set of functions. To use these, you need to check that the data are mutable using @ref indexDataFlags() or @ref vertexDataFlags() -first. The following snippet applies a transformation to the mesh data: +first, and if not then you may want to make a mutable copy first using +@ref MeshTools::owned(). The following snippet applies a transformation to the +mesh positions: @snippet MagnumTrade.cpp MeshData-usage-mutable +If the transformation includes a rotation or non-uniform scaling, you may want +to do a similar operation with normals and tangents as well. + @section Trade-MeshData-populating Populating an instance A @ref MeshData instance by default takes over the ownership of an From 83f18b23139ff53d74f936f00702bca029917e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 17 Jul 2021 19:53:21 +0200 Subject: [PATCH 09/10] doc: *finally* document the library organization and essential tools. --- doc/features.dox | 142 +++++++++++++++++++++++++++++++++++++++- doc/getting-started.dox | 16 +++-- doc/snippets/Magnum.cpp | 39 +++++++++++ doc/types.dox | 30 +++++---- 4 files changed, 207 insertions(+), 20 deletions(-) diff --git a/doc/features.dox b/doc/features.dox index eb8eccef4..e9328c959 100644 --- a/doc/features.dox +++ b/doc/features.dox @@ -26,10 +26,146 @@ namespace Magnum { /** @page features Feature guide -@brief Fundamental principles and design goals. +@brief High-level introduction to design of the Magnum library and basic building blocks. -Overview and tutorials of high-level feature groups in Magnum. It's not -necessary to read through everything, pick only what you need. +@tableofcontents + +@todoc FUCK OFF doxygen, why are you not able to link to Corrade, Corrade::Utility and Magnum/Math/ you dumb shit?! + +Before you continue further, here are the essential bits of knowledge to help +you around. + +@section features-naming Library organization and naming scheme + +The @ref Magnum project consists of a library with core functionality and +optional sub-libraries. Each library is in its own namespace, which corresponds +to a sub-folder on the include path -- so e.g. things from @ref Magnum::Math +are included from @m_class{m-doc} [Magnum/Math/](dir_d816e7cf853e6723911731706bcab386.html). +@ref Magnum builds upon +@m_class{m-doc-external} [Corrade](https://doc.magnum.graphics/corrade/), which +provides platform abstraction, basic utilities and containers. + +To reduce redundant verbosity, references throughout the documentation and +code in example snippets have the the root @ref Magnum and +@m_class{m-doc-external} [Corrade](https://doc.magnum.graphics/corrade/namespaceCorrade.html) +namespaces omitted. In other words, as if the following was done: + +@snippet Magnum.cpp features-using-namespace + +If Magnum is the primary library you're building your project on, it's +recommended that you do the same --- we're careful to not pollute the root +namespace with overly generic names. However, except for `Literals` namespaces, +@cpp using @ce the subnamespaces is *not recommended*, as naming in those is +deliberately picked short and may introduce conflicts (such as +@relativeref{Corrade,Containers::String} vs +@relativeref{Corrade,Utility::String} or @ref GL::PixelFormat vs +@ref Vk::PixelFormat). + +If you frown upon the thought of @cpp using namespace Magnum @ce yet don't want +to wear your fingers off by repeatedly writing @cpp Magnum:: @ce everywhere, a +common shorthand that projects go with is the following --- similarly in spirit +to @cb{.py} import numpy as np @ce: + +@snippet Magnum.cpp features-using-namespace-alias + +@section features-includes Include files and forward declarations + +Depending on where you come from, the @cpp #include @ce policy used by Magnum +might either make you happy or freak you out. In short, there's no "include the +world" file, and instead you're supposed to include dedicated headers for APIs +you want to use. The main reason is compile times, and the speed gain from +doing it this way is too great to be ignored. + +The general rule is that each top-level class has a corresponding include, so +for example @ref Math::Matrix4 is included as @ref Magnum/Math/Matrix4.h. It's +not always like that though, and to help you out, documentation of each +namespace and class tells you what to include, and if particular namespace +members are defined in different headers, then detailed docs of each has a +corresponding @cpp #include @ce directive listed as well, ready to be copied. + +In order to have the includes actually independent of each other, most Magnum +types are forward-declared, and where possible, the header only relies on +forward declarations. Which means that often the type already exists as a +declaration, and in order to actually use it, you have to include the concrete +definition of it. If you don't, the compiler will complain about use of an +incomplete type. For example: + +@snippet Magnum.cpp features-forward-declaration-use + +Of course not all headers can be written with just forward declarations, so +there still are some transitive dependencies between headers (for example, the +@ref Magnum/Math/Matrix4.h header pulls in @ref Magnum/Math/Vector4.h as well). +But from most part this is an implementation detail and as such shouldn't be +relied on. + +For happy compile times you're encouraged to rely on forward declarations in +your code as well. See @ref compilation-forward-declarations for more +information. + +@section features-debug-output Debug output + +One of the essential debugging workflows is inspection of variable contents by +printing them out. Magnum defines a lot of new math types, enums and +containers and it would be very painful if you had to loop over their contents +or perform manual enum-to-string conversion every time you want to see what's +inside. + +Because writing to standard output and printing values for debugging purposes +are *distinct* use cases with potentially conflicting requirements (should an +enum value get written as a number? or as a name? a fully qualified name?), +Magnum *doesn't* provide @cpp operator<< @ce overloads for @ref std::ostream. + +Instead, there's @relativeref{Corrade,Utility::Debug}, for your typing +convenience also aliased to just @ref Debug directly in the @ref Magnum +namespace. On its own it's able to print builtin types, all usual C++ +containers as well as their Corrade equivalents. Magnum then implements debug +printers for its own math types, basic structures and most enums, which get +printed as fully-qualified names. For example: + +@m_class{m-code-figure} + +@parblock + +@snippet Magnum.cpp features-debug-output + + + +@m_class{m-nopad} + +@code{.shell-session} +Image format is PixelFormat::RGBA8Srgb and size Vector(256, 192) +Color of the bottom-left pixel is #33b27f +@endcode + +@endparblock + +The main goal of this utility is convenience and readability --- values are +implicitly delimited by spaces and ended with a newline, container contents +written with commas etc. Check out the class documentation for advanced +features like colors, output redirection or printing file/line info. + +@section features-examples Learn by example + +Before you do a deep dive into the documentation, and if you haven't done +already, it's recommended to go through the Getting Started Guide and check out +at least the first example: + +@m_class{m-row} + +@parblock + +@m_div{m-col-m-6} @m_div{m-button m-primary} @m_div{m-big}Getting Started@m_enddiv @m_div{m-small} bootstrap a basic project structure @m_enddiv @m_enddiv @m_enddiv + +@m_div{m-col-m-6} @m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv @m_enddiv + +@endparblock + +@section features-building-blocks Learn through documentation + +Each of the following pages provides a high-level description of a certain area +of the library. It's recommended to read through these first to understand the +overall principles and only then go to documentation of each concrete class and +function. - @subpage platform --- @copybrief platform - @subpage types --- @copybrief types diff --git a/doc/getting-started.dox b/doc/getting-started.dox index 544bc2eb5..6d4f10506 100644 --- a/doc/getting-started.dox +++ b/doc/getting-started.dox @@ -345,11 +345,19 @@ See @ref platform for more information. @section getting-started-tutorials Follow tutorials and learn the principles Now that you have your first application up and running, the best way to -continue is to render your first triangle. Then you can dig deeper and try -other examples, read about @ref features "fundamental principles" in the -documentation and start experimenting on your own! +continue is to get familiar with the basic workflows and render your first +triangle. Then you can dig deeper and try other examples, read about basic +building blocks in the documentation and start experimenting on your own! -@m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv +@m_class{m-row} + +@parblock + +@m_div{m-col-m-6} @m_div{m-button m-info} @m_div{m-big}The Fundamentals@m_enddiv @m_div{m-small} make yourself comfortable first @m_enddiv @m_enddiv @m_enddiv + +@m_div{m-col-m-6} @m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv @m_enddiv + +@endparblock @section getting-started-more Additional information diff --git a/doc/snippets/Magnum.cpp b/doc/snippets/Magnum.cpp index b76710945..67b66c8de 100644 --- a/doc/snippets/Magnum.cpp +++ b/doc/snippets/Magnum.cpp @@ -26,6 +26,7 @@ #include #include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix4.h" #include "Magnum/Image.h" #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" @@ -37,6 +38,8 @@ #include "Magnum/GL/Texture.h" #endif +#define DOXYGEN_IGNORE(...) __VA_ARGS__ + using namespace Magnum; using namespace Magnum::Math::Literals; @@ -64,6 +67,42 @@ class MeshResourceLoader: public AbstractResourceLoader { int main() { +{ +/* [features-using-namespace] */ +using namespace Corrade; +using namespace Magnum; +/* [features-using-namespace] */ +} + +{ +/* [features-using-namespace-alias] */ +namespace Cr = Corrade; +namespace Mn = Magnum; +/* [features-using-namespace-alias] */ +} + +{ +/* The same #include is already above so this shouldn't hurt */ +/* [features-forward-declaration-use] */ +#include /* only a Matrix4 forward declaration */ +#include /* the actual definition */ + +DOXYGEN_IGNORE() + +Matrix4 a = Matrix4::translation({3.0f, 1.0f, 0.5f}); +/* [features-forward-declaration-use] */ +static_cast(a); +} + +{ +/* [features-debug-output] */ +Image2D image = DOXYGEN_IGNORE(Image2D{{}, {}, {}}); + +Debug{} << "Image format is" << image.format() << "and size" << image.size(); +Debug{} << "Color of the first pixel is" << image.pixels()[0][0]; +/* [features-debug-output] */ +} + { std::nullptr_t data{}; /* [Image-pixels] */ diff --git a/doc/types.dox b/doc/types.dox index 6831fec4b..6b055bb46 100644 --- a/doc/types.dox +++ b/doc/types.dox @@ -31,15 +31,19 @@ namespace Magnum { @m_footernavigation @m_keyword{Type system,,} -The root @ref Magnum namespace defines a few aliases for essential math types. -See its documentation for more information about usage. +Magnum defines a variety of scalar, vector and matrix types. Most of the +functionality is implemented using template classes in the @ref Math library, +with the most common variants brought as typedefs into the root @ref Magnum +namespace. @section types-builtin Builtin types -Magnum provides typedefs for builtin integral and floating-point arithmetic -types to ensure portability (e.g. @ref Int is *always* 32bit), maintain -consistency and reduce confusion (e.g. @ref std::int32_t, @cpp int @ce and -@cpp GLint @ce all refer to the same type). +Magnum provides its own typedefs for builtin integral and floating-point +arithmetic types to ensure portability, maintain consistency and reduce +confusion. E.g., the @ref Int typedef is guaranteed to *always* be 32-bit and +Magnum's own code and documentation prefers to use it over a wild mixture of +@ref std::int32_t, @cpp int @ce, @cpp GLint @ce and @cpp ALint @ce that all +refer to the same type. | Magnum type | Size | Equivalent GLSL type | | ------------------ | -------------- | ----------------------- | @@ -56,8 +60,8 @@ consistency and reduce confusion (e.g. @ref std::int32_t, @cpp int @ce and | @ref Double | 64bit | @glsl double @ce | Types not meant to be used in arithmetic (such as @cpp bool @ce or -@cpp std::size_t @ce) or types which cannot be directly passed to GLSL shaders -(such as @cpp long double @ce) have no typedefs. +@cpp std::size_t @ce) or types which have no use in GPU computations (such as +@cpp long double @ce) have no typedefs. Types from the above table are then used to define other types. All following types are aliases of corresponding types in @ref Math namespace. No suffix @@ -121,11 +125,11 @@ about the differences. @section types-binary Binary representation -Scalar types with GLSL equivalent are verified to be exactly the same as -corresponding `GL*` types. Matrix and vector classes have the same binary -representation as corresponding array of numeric values without any additional -data or padding (e.g. @cpp sizeof(Vector3i) == sizeof(Int[3]) @ce), all -matrices are stored in column-major order. +Scalar types with a GLSL equivalent are guaranteed to have exactly the same +binary representation. Consequently, matrix and vector classes also have the +same binary representation as corresponding array of numeric values without any +additional data or padding (e.g. @cpp sizeof(Vector3i) == sizeof(Int[3]) @ce), +all matrices are stored in column-major order. This means that all scalar, matrix and vector types can be used directly for filling GPU buffers and textures without any need for data extraction or From 2347a7f730e0eb13fd64b05344d545b793721c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 17 Jul 2021 20:12:45 +0200 Subject: [PATCH 10/10] doc: updated changelog. --- doc/changelog.dox | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/changelog.dox b/doc/changelog.dox index ac6332885..a4a315ac1 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -736,6 +736,10 @@ See also: reflected in the docs and thus hidden from users. Now it's shown in the docs as a set of @ref Debug etc typedefs instead, to make them more discoverable. +- Improved documentation about @ref opengl-wrapping-instances-nocreate "delayed OpenGL context creation" + and related pitfalls (see also [mosra/magnum-bootstrap#26](https://github.com/mosra/magnum-bootstrap/issues/26)) +- Added introductory documentation about @ref features "library layout and essential workflows" + (see [mosra/magnum#526](https://github.com/mosra/magnum/issues/526)) @section changelog-2020-06 2020.06