Сравнение двух структур одного типа из внешнего API
Так что в основном я пытаюсь сравнить два VkPhysicalDeviceFeatures
из Вулкана, один из VkPhysicalDevice
Я смотрю, и другой, который соответствует набору действительно необходимых мне функций. аVkPhysicalDeviceFeatures
структура содержит толькоVkBool32
члены (которые являются typedefs uint32_t
), но каждая дополнительная версия vulkan может добавлять неизвестное количество этих функций. Я хотел бы просто сравнить члены каждой структуры друг с другом, но не на равенство, а скорее на логическое сравнение. Если соответствующий член в структуре физического устройства имеет значение false, но моя структура имеет значение true для этого члена, тогда сравнение должно вернуть false.
Единственный способ, которым я могу это сделать, - это что-то вроде того, что написал этот ответ:
bool hasRequiredFeatures(VkPhysicalDevice physical_device,
VkPhysicalDeviceFeatures required_features) {
VkPhysicalDeviceFeatures physical_device_features = getSupportedFeatures(physical_device);
std::size_t struct_length = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
auto physical_device_features_bool_ptr = reinterpret_cast<VkBool32*>(&physical_device_features);
auto required_features_bool_ptr = reinterpret_cast<VkBool32*>(&required_features);
for(std::size_t i = 0; i < struct_length; ++i){
if(physical_device_features_bool_ptr[i] == VK_FALSE && required_features_bool_ptr[i] == VK_TRUE){
return false;
}
}
return true;
}
Это делает то, что я хочу (хотя было бы неплохо иметь способ увидеть, какой конкретный член по имени не прошел сравнение, но я думаю, что это невозможно без отражения), но я не думаю, что C++ гарантирует такое строгое выравнивание, как это? Есть ли у меня кроссплатформенный способ сделать это?
1 ответ
В вашем подходе есть большая проблема, которая связана не столько с C++, сколько с Vulkan. В частности:
младшая версия vulkan может добавлять неизвестное количество этих структур
Это говорит мне о том, что вы собираетесь применить такую технологию к VkPhysicalDeviceFeatures2
, а также любую структуру функции, которая может появиться в ее pNext
цепь. Отсюда "неизвестное количество этих структур".
Ну вот в чем дело: VkPhysicalDeviceFeatures2
это не просто куча VkBool
с. Это расширяемая структура Vulkan, что означает, что она начинается сsType
а также pNext
общие для таких структур поля. Так же обстоят дела со всеми структурами функций устройств Vulkan после версии 1.0.
Код, который может делать то, что вы заявили, со структурами функций после версии 1.0, должен иметь возможность занимать всеpNext
цепочку структур функций и протестируйте их. И для этого вы должны знать, что они из себя представляют. Невозможно запросить, просто указав на произвольные данные, что эти данные содержат X количествоVkBool
с.
Чтобы это сработало, вам нужно будет сопоставить sType
значение к размеру этой структуры. И поэтому он не может быть автоматически расширяемым (и нет, отражение C++ не может это исправить; он не может знать, какая структураvoid *pNext
указывает на); для этого потребуется некоторый уровень обслуживания вручную.
К счастью, файлы описания спецификации XML Vulkan четко определяют, какие структуры могут существовать в конкретномpNext
цепь. Итак, вы можете написать инструмент для загрузки XML, найтиVkPhysicalDeviceFeatures2
, и обработать все структуры, появляющиеся в его pNext
цепочка (легче сказать, чем сделать, поскольку формат XML был создан только для обработки собственными инструментами Khronos), чтобы найти доступные структуры и сгенерировать необходимую информацию C++. Я уверен, что вы сравнительно легко можете написать такое на любом языке сценариев.
Но поскольку у вас есть определения структур в (квази-разумном) XML, и у вас есть этот инструмент, который в любом случае будет генерировать некоторый код C++... вы можете просто сгенерировать фактическую логику сравнения. То есть вместо того, чтобы вручную писать сравнения членов с членами, просто сгенерируйте сравнения членов с членами. Вы даже можете получить имена участников, которые не совпадают.
Если вы собираетесь обрабатывать произвольные pNext
-chain, тогда вам понадобится какой-то инструмент-генератор. И если вам все равно понадобится генератор, просто используйте его, чтобы решить всю проблему.
Теперь важно понимать, что код генерации гипотетического hasRequiredFeatures
реализация должна быть полусложной. Если вы позволяете полныйpNext
цепочка структур, то вам нужно построить свою собственную соответствующую цепочку эквивалентных структур, чтобы использовать их для запроса из Vulkan. И это не совсем тривиально.
Вам нужно будет перебрать pNext
цепь и изучить sType
поле каждой структуры. Но конечно,pNext
это void*
, так что вам придется немного обмануть / обмануть правила C++, чтобы прочитать sType
поле. В принципе, вам придетсяreinterpret_cast
в void*
для VkStructureType*
, прочтите значение, сравните его со всеми возможностями, с которыми вы работаете, и продолжайте оттуда. Вы должны пропустить любоеsType
о которых вы не знаете, для чего потребуется еще больше уловок C++.
Но вы используете низкоуровневый API; Нарушение правил C++- это то, к чему вам здесь нужно привыкнуть.
Для каждой такой структуры вам нужно выделить соответствующую структуру, заполнить ее sType
соответствующим образом, а затем добавьте его в pNext
цепочку, которую вы строите.
После того, как вы создали все это, вы можете выполнить вызов Vulkan, провести сравнение, собрать данные и, наконец, удалить всю цепочку структур.
Если ваша цель действительно - придерживаться всего VkPhysicalDeviceFeatures
а не расширяемые структуры, и вам просто нужен переносимый на C++ способ сравнения таких структур, тогда просто memcpy
их в VkBool
array и сравните два массива на предмет несовпадений. Эти два типа легко копируются, так что делать это на первый взгляд нелегитимно.
Этот код не компилировался и не тестировался.
bool hasRequiredFeatures(VkPhysicalDevice physical_device,
VkPhysicalDeviceFeatures required_features)
{
constexpr auto feature_count = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
using FeatureArray = std::array<VkBool, feature_count>;
auto physical_device_features = getSupportedFeatures(physical_device);
FeatureArray required;
memcpy(&required, &required_features, sizeof(FeatureArray));
FeatureArray retrieved;
memcpy(&retrieved, &physical_device_features, sizeof(FeatureArray));
bool did_mismatch = false;
for(auto it_pair = std::mismatch(required.begin(), required.end(), retrieved.begin());
it_pair.first != required.end();
it_pair = std::mismatch(it_pair.first, required.end(), it_pair.second))
{
did_mismatch = true
auto mismatch_index = it_pair.first - required.begin();
//Do something with mismatch_index
}
return did_mismatch;
}