Browse Source

Vk: more utilites for heavyweight structure chain processing.

Gah, these device features are *hell*.
pull/491/head
Vladimír Vondruš 5 years ago
parent
commit
a4382c6305
  1. 88
      src/Magnum/Vk/Implementation/structureHelpers.h
  2. 79
      src/Magnum/Vk/Test/StructureHelpersTest.cpp

88
src/Magnum/Vk/Implementation/structureHelpers.h

@ -27,12 +27,18 @@
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/TypeTraits.h>
#include "Magnum/Magnum.h"
#include "Magnum/Vk/Vulkan.h"
namespace Magnum { namespace Vk { namespace Implementation {
/* All those helpers are designed in a way that only allows them to work with a
"whitelisted" set of structures to avoid modifying external data by
accident. Thus no "give me the first structure of this type" or "remove any
structure of this type from the chain". */
/* Meant to be used for connecting a longer chain of structures. Anything
that was connected to the `next` pointer before is reconnected to
`structure.pNext`; the `next` reference is rebound to the `structure.pNext`
@ -54,6 +60,88 @@ template<class T> inline void structureConnect(Containers::Reference<const void*
structureConnect(reinterpret_cast<Containers::Reference<void*>&>(next), structure, type);
}
CORRADE_HAS_TYPE(IsVulkanStructure, decltype(T::pNext));
/* Returns a pointer to the pNext field that has a value of `pointer` or
nullptr if no such structure if found. It can also return the `next`
parameter, if its value is already the pointer. */
template<class T> inline void** structureFind(void*& next, const T& structure) {
static_assert(IsVulkanStructure<T>::value, "passed type is not a Vulkan structure");
/* We take a reference in order to make it possible to mutate the found
result, however we need to rebind that reference in the loop, and
assigning to `next` would mean we corrupt the original structure. */
Containers::Reference<void*> nextRebindable = next;
while(nextRebindable) {
auto& found = *reinterpret_cast<VkBaseOutStructure*>(*nextRebindable);
if(&found == static_cast<const void*>(&structure)) return &*nextRebindable;
nextRebindable = reinterpret_cast<void*&>(found.pNext);
}
return nullptr;
}
template<class T> inline const void** structureFind(const void*& next, const T& structure) {
/* Ugly. Yes. Same reasoning as in structureConnect(). */
return const_cast<const void**>(structureFind(const_cast<void*&>(next), structure));
}
class AnyStructure {
public:
template<class T, class = typename std::enable_if<IsVulkanStructure<T>::value>::type> /*implicit*/ AnyStructure(const T& structure): _structure{reinterpret_cast<const VkBaseOutStructure*>(&structure)} {}
/*implicit*/ operator const VkBaseOutStructure&() const { return *_structure; }
private:
const VkBaseOutStructure* _structure;
};
/* Given a chain of structures that are always connected in the same order (but
not necessarily all of them), in which `next` points to the first structure
of the chain, the function disconnects the chain from `next`, replacing the
pointer with the first `pNext` value that points outside of the chain. The
`structures` themselves are not touched in any way.
Given the following, where 0-9 are the `structures` and 1, 3, 4, 5, 7, 8
form a chain in which the last `pNext` points to `out`:
next ___ ___ out
\ / \ / \ /
+ + + +--+--+ + +--+ +
0 1 2 3 4 5 6 7 8 9
The result is the following --- the structures are left intact, and `next`
now points directly to `out`. After that, the structures can be cleared and
repurposed or discarded without a risk of losing access to the chain end.
next ----------------------- out
___ ___
/ \ / \ /
+ + + +--+--+ + +--+ +
0 1 2 3 4 5 6 7 8 9s
*/
inline void structureDisconnectChain(const void*& next, std::initializer_list<AnyStructure> structures) {
/* We take a reference in order to make it possible to mutate the found
result, however we need to rebind that reference in the loop, and
assigning to `next` would mean we corrupt the original structure. */
Containers::Reference<const void* const> nextRebindable = next;
for(const VkBaseOutStructure& structure: structures) {
if(nextRebindable == &structure)
nextRebindable = reinterpret_cast<const void* const&>(structure.pNext);
}
/* For safety we expect `next` actually pointed to something inside the
chain */
CORRADE_INTERNAL_ASSERT(&*nextRebindable != next);
next = *nextRebindable;
}
inline void structureDisconnectChain(void*& next, std::initializer_list<AnyStructure> structures) {
return structureDisconnectChain(const_cast<const void*&>(next), structures);
}
}}}
#endif

79
src/Magnum/Vk/Test/StructureHelpersTest.cpp

@ -33,12 +33,18 @@ struct StructureHelpersTest: TestSuite::Tester {
explicit StructureHelpersTest();
template<class T> void connect();
template<class T> void find();
template<class T> void disconnectChain();
};
StructureHelpersTest::StructureHelpersTest() {
addTests<StructureHelpersTest>({
&StructureHelpersTest::connect<VkDeviceCreateInfo>,
&StructureHelpersTest::connect<VkPhysicalDeviceFeatures2>});
&StructureHelpersTest::connect<VkPhysicalDeviceFeatures2>,
&StructureHelpersTest::find<VkDeviceCreateInfo>,
&StructureHelpersTest::find<VkPhysicalDeviceFeatures2>,
&StructureHelpersTest::disconnectChain<VkDeviceCreateInfo>,
&StructureHelpersTest::disconnectChain<VkPhysicalDeviceFeatures2>});
}
template<class> struct Type;
@ -77,6 +83,77 @@ template<class T> void StructureHelpersTest::connect() {
CORRADE_COMPARE(*next, &variableFeatures);
}
template<class T> void StructureHelpersTest::find() {
typedef typename std::remove_reference<decltype(T{}.pNext)>::type NextType;
setTestCaseTemplateName(Type<NextType>::name());
VkPhysicalDeviceVariablePointersFeatures variableFeatures{};
VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeatures{};
ycbcrFeatures.pNext = &variableFeatures;
VkPhysicalDeviceMultiviewFeatures multiviewFeatures{};
multiviewFeatures.pNext = &ycbcrFeatures;
T info{};
info.pNext = &multiviewFeatures;
CORRADE_COMPARE(const_cast<const void**>(Implementation::structureFind(info.pNext, variableFeatures)), const_cast<const void**>(&ycbcrFeatures.pNext));
/* It shouldn't be modifying the input in any way (heh, what the hell did
I manage to do here) */
CORRADE_COMPARE(info.pNext, &multiviewFeatures);
CORRADE_COMPARE(const_cast<const void**>(Implementation::structureFind(info.pNext, ycbcrFeatures)), const_cast<const void**>(&multiviewFeatures.pNext));
CORRADE_COMPARE(info.pNext, &multiviewFeatures);
CORRADE_COMPARE(const_cast<const void**>(Implementation::structureFind(info.pNext, multiviewFeatures)), const_cast<const void**>(&info.pNext));
CORRADE_COMPARE(info.pNext, &multiviewFeatures);
CORRADE_COMPARE(Implementation::structureFind(info.pNext, info), nullptr);
CORRADE_COMPARE(info.pNext, &multiviewFeatures);
}
template<class T> void StructureHelpersTest::disconnectChain() {
typedef typename std::remove_reference<decltype(T{}.pNext)>::type NextType;
setTestCaseTemplateName(Type<NextType>::name());
VkPhysicalDeviceFeatures2 a0{}, a1{}, a2{}, a3{}, a4{}, a5{}, a6{}, a7{}, a8{}, a9{};
VkDeviceGroupSubmitInfo out{};
int error{};
T info;
info.pNext = &a1;
a0.pNext = &error;
a1.pNext = &a3;
a2.pNext = &error;
a3.pNext = &a4;
a4.pNext = &a5;
a5.pNext = &a7;
a6.pNext = &error;
a7.pNext = &a8;
a8.pNext = &out;
a9.pNext = &error;
Implementation::structureDisconnectChain(info.pNext, {
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9
});
CORRADE_COMPARE(info.pNext, &out);
/* Everything else should stay as it was before */
CORRADE_COMPARE(a0.pNext, &error);
CORRADE_COMPARE(a1.pNext, &a3);
CORRADE_COMPARE(a2.pNext, &error);
CORRADE_COMPARE(a3.pNext, &a4);
CORRADE_COMPARE(a4.pNext, &a5);
CORRADE_COMPARE(a5.pNext, &a7);
CORRADE_COMPARE(a6.pNext, &error);
CORRADE_COMPARE(a7.pNext, &a8);
CORRADE_COMPARE(a8.pNext, &out);
CORRADE_COMPARE(a9.pNext, &error);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::StructureHelpersTest)

Loading…
Cancel
Save