Сравнение двух структур одного типа из внешнего 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 их в VkBoolarray и сравните два массива на предмет несовпадений. Эти два типа легко копируются, так что делать это на первый взгляд нелегитимно.

Этот код не компилировался и не тестировался.

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;
}
Другие вопросы по тегам