Browse Source

Vk: ability to pick a memory type.

Goes in-line with APIs for device and queue selection.
pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
67ebe78a6e
  1. 41
      src/Magnum/Vk/DeviceProperties.cpp
  2. 43
      src/Magnum/Vk/DeviceProperties.h
  3. 60
      src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp

41
src/Magnum/Vk/DeviceProperties.cpp

@ -32,9 +32,11 @@
#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Debug.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/Vk/Instance.h"
#include "Magnum/Vk/ExtensionProperties.h"
#include "Magnum/Vk/LayerProperties.h"
#include "Magnum/Vk/Memory.h"
#include "Magnum/Vk/Result.h"
#include "Magnum/Vk/Implementation/Arguments.h"
#include "Magnum/Vk/Implementation/InstanceState.h"
@ -236,6 +238,45 @@ UnsignedInt DeviceProperties::memoryHeapIndex(const UnsignedInt memory) {
return properties.memoryTypes[memory].heapIndex;
}
UnsignedInt DeviceProperties::pickMemory(const MemoryFlags requiredFlags, const MemoryFlags preferredFlags, const UnsignedInt memories) {
Containers::Optional<UnsignedInt> id = tryPickMemory(requiredFlags, preferredFlags, memories);
if(id) return *id;
std::exit(1); /* LCOV_EXCL_LINE */
}
Containers::Optional<UnsignedInt> DeviceProperties::tryPickMemory(const MemoryFlags requiredFlags, const MemoryFlags preferredFlags, const UnsignedInt memories) {
const VkPhysicalDeviceMemoryProperties properties = memoryProperties().memoryProperties;
/* The picking strategy is basically equivalent to vmaFindMemoryTypeIndex()
from AMD's Vulkan Memory Allocator -- choosing the one that has the most
bits set. */
Int maxPreferredBitCount = -1;
UnsignedInt maxPreferredBitCountMemory = ~UnsignedInt{};
UnsignedInt bit = 1;
for(UnsignedInt i = 0; i != properties.memoryTypeCount; ++i, bit <<= 1) {
/* Not among considered memory types, skip */
if(!(memories & bit))
continue;
/* Not all required flags present, skip */
if(!(MemoryFlag(properties.memoryTypes[i].propertyFlags) >= requiredFlags))
continue;
/* Check how many of the preferred flags are present and use the one
with highest count */
const Int preferredBitCount = Math::popcount(properties.memoryTypes[i].propertyFlags & UnsignedInt(preferredFlags));
if(preferredBitCount > maxPreferredBitCount) {
maxPreferredBitCount = preferredBitCount;
maxPreferredBitCountMemory = i;
}
}
if(maxPreferredBitCount >= 0) return maxPreferredBitCountMemory;
Error{} << "Vk::DeviceProperties::tryPickMemory(): no" << requiredFlags << "found among" << Math::popcount(memories & ((1 << properties.memoryTypeCount) - 1)) << "considered memory types";
return {};
}
Containers::Array<DeviceProperties> enumerateDevices(Instance& instance) {
/* Retrieve total device count */
UnsignedInt count;

43
src/Magnum/Vk/DeviceProperties.h

@ -421,6 +421,49 @@ class MAGNUM_VK_EXPORT DeviceProperties {
*/
UnsignedInt memoryHeapIndex(UnsignedInt memory);
/**
* @brief Pick a memory type satisfying given flags
* @param requiredFlags Memory flags that should be present in
* picked memory type. Can be an empty set, but picking such
* memory is probably not very useful.
* @param preferredFlags If there's more than one memory type
* matching @p requiredFlags, prefer one that has most of these
* as well. Defaults to an empty set.
* @param memories Bits indicating which memory types should
* be considered (bit @cpp 0 @ce indicates memory type @cpp 0 @ce
* should be considered etc.). Expected to have at least one bit
* of the first @ref memoryCount() bits set, otherwise the
* function will always fail. Defaults to all bits set, meaning
* all memory types are considered. Corresponds to the
* `memoryTypeBits` field of @type_vk{MemoryRequirements}.
*
* Queries memory properties using @ref memoryProperties() and out of
* memory types set in @p memoryBits tries to find one that contains
* all @p requiredFlags and most of @p optionalFlags. If it is not
* found, exits. See @ref tryPickMemory() for an alternative that
* doesn't exit on failure.
*
* @m_class{m-note m-success}
*
* @par
* The @p preferredFlags can be used for example to ask for a
* @ref MemoryFlag::HostVisible bit on a
* @ref MemoryFlag::DeviceLocal memory --- on discrete GPUs this
* combination is usually not possible so you get just a
* device-only memory, but on integrated GPUs it can be used to
* avoid a need for a copy through a temporary staging buffer.
*/
UnsignedInt pickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{});
/**
* @brief Try to pick a memory type satisfying given flags
*
* Compared to @ref pickMemory() the function returns
* @ref Containers::NullOpt if a desired memory type isn't found
* instead of exiting.
*/
Containers::Optional<UnsignedInt> tryPickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{});
private:
friend Implementation::InstanceState;

60
src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp

@ -71,6 +71,9 @@ struct DevicePropertiesVkTest: VulkanTester {
void memoryTypes();
void memoryTypeOutOfRange();
void memoryTypesPick();
void memoryTypesPickIgnoreSomePreferred();
void memoryTypesPickFailed();
void pickDevice();
void pickDeviceIndex();
@ -114,6 +117,9 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): VulkanTester{NoCreate} {
&DevicePropertiesVkTest::memoryTypes,
&DevicePropertiesVkTest::memoryTypeOutOfRange,
&DevicePropertiesVkTest::memoryTypesPick,
&DevicePropertiesVkTest::memoryTypesPickIgnoreSomePreferred,
&DevicePropertiesVkTest::memoryTypesPickFailed,
&DevicePropertiesVkTest::pickDevice,
&DevicePropertiesVkTest::pickDeviceIndex,
@ -427,6 +433,60 @@ void DevicePropertiesVkTest::memoryTypeOutOfRange() {
"Vk::DeviceProperties::memoryHeapIndex(): index {0} out of range for {0} memory types\n", count));
}
void DevicePropertiesVkTest::memoryTypesPick() {
Containers::Array<DeviceProperties> devices = enumerateDevices(instance());
CORRADE_VERIFY(!devices.empty());
Containers::Optional<UnsignedInt> id = devices[0].tryPickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent);
CORRADE_VERIFY(id);
CORRADE_COMPARE_AS(*id, devices[0].memoryCount(), TestSuite::Compare::Less);
CORRADE_COMPARE_AS(devices[0].memoryFlags(*id),
MemoryFlag::HostVisible|MemoryFlag::HostCoherent,
TestSuite::Compare::GreaterOrEqual);
/* Pick should return the same ID, and shouldn't exit */
CORRADE_COMPARE(devices[0].pickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent), id);
/* If we put the same into preferred flags and leave the required empty, it
should pick the same (the first one as well) */
Containers::Optional<UnsignedInt> idPreferred = devices[0].tryPickMemory({}, MemoryFlag::HostVisible|MemoryFlag::HostCoherent);
CORRADE_COMPARE(idPreferred, id);
}
void DevicePropertiesVkTest::memoryTypesPickIgnoreSomePreferred() {
Containers::Array<DeviceProperties> devices = enumerateDevices(instance());
CORRADE_VERIFY(!devices.empty());
Containers::Optional<UnsignedInt> id = devices[0].tryPickMemory({}, MemoryFlag::HostVisible|MemoryFlag::HostCoherent|MemoryFlag(0xcafe0000u));
CORRADE_VERIFY(id);
CORRADE_COMPARE_AS(*id, devices[0].memoryCount(), TestSuite::Compare::Less);
/* Should pick all what makes sense and ignore what doesn't */
CORRADE_COMPARE_AS(devices[0].memoryFlags(*id),
MemoryFlag::HostVisible|MemoryFlag::HostCoherent,
TestSuite::Compare::GreaterOrEqual);
/* And should be the same as picking the same required or halfway */
CORRADE_COMPARE(id, devices[0].tryPickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent));
CORRADE_COMPARE(id, devices[0].tryPickMemory(MemoryFlag::HostVisible, MemoryFlag::HostCoherent));
}
void DevicePropertiesVkTest::memoryTypesPickFailed() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Containers::Array<DeviceProperties> devices = enumerateDevices(instance());
CORRADE_VERIFY(!devices.empty());
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!devices[0].tryPickMemory(MemoryFlag(0xc0ffeee0)));
CORRADE_VERIFY(!devices[0].tryPickMemory({}, {}, 0));
CORRADE_COMPARE(out.str(), Utility::formatString(
"Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlag(0xc0ffeee0) found among {} considered memory types\n"
"Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlags{{}} found among 0 considered memory types\n", devices[0].memoryCount()));
}
void DevicePropertiesVkTest::pickDevice() {
/* Default behavior */
Containers::Optional<DeviceProperties> device = tryPickDevice(instance());

Loading…
Cancel
Save