Сравнение двух структур одного типа из внешнего API - PullRequest
2 голосов
/ 25 апреля 2020

Итак, в основном я пытаюсь сравнить два VkPhysicalDeviceFeatures от Vulkan, один из VkPhysicalDevice, на который я смотрю, и другой, который соответствует набору функций, которые мне действительно нужны. VkPhysicalDeviceFeatures struct только содержит 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 член по имени провалил сравнение, но я думаю, что это невозможно без размышления), но я не думаю, что C ++ гарантирует такое строгое выравнивание? Есть ли кросс-платформенный способ для меня выполнить это?

1 Ответ

2 голосов
/ 25 апреля 2020

В вашем подходе есть большая проблема, которая связана не столько с С ++, сколько с Вулканом. В частности:

минорная версия вулкана может добавить неизвестное число этих структур

Это говорит о том, что вы намереваетесь применить такую ​​технологию к VkPhysicalDeviceFeatures2, а также любая структура объекта, которая может появиться в его цепочке pNext. Отсюда и «неизвестное число этих структур».

Ну, вот в чем дело: VkPhysicalDeviceFeatures2 - это не просто группа VkBool с. Это расширяемая структура Vulkan, что означает, что она начинается с полей sType и pNext, общих для таких структур. То же самое можно сказать и обо всех структурах устройств Vulkan, выпущенных после 1.0.

Код, который может выполнять то, что вы заявили в структурах функций после 1.0. должен иметь возможность принимать все pNext. цепочка конструктивных элементов и их тестирование. И чтобы сделать это, вы должны знать, что они . Невозможно из простого указателя на произвольные данные запросить, что эти данные содержат число X VkBool с.

Чтобы это работало, вам нужно будет иметь возможность сопоставить значение sType к размеру этой структуры. И поэтому, он не может быть автоматически расширяемым (и нет, отражение C ++ не может это исправить; он не может знать, на что указывает структура void *pNext); это потребует некоторого уровня обслуживания вручную.

К счастью, Vulkan XML файлы описания спецификации четко указывают, какие структуры могут существовать в определенной цепочке pNext. Таким образом, вы можете написать инструмент для загрузки XML, поиска VkPhysicalDeviceFeatures2 и обработки всех структур, появляющихся в его цепочке pNext (эту часть легче сказать, чем сделать, поскольку формат XML был создан только для обрабатываться собственными инструментами Khronos), чтобы определить, какие структуры доступны, и сгенерировать необходимую вам информацию C ++. Я уверен, что вы можете относительно легко написать такую ​​вещь на любом языке сценариев.

Но так как у вас есть определения структуры (квази-разумно) XML, и у вас есть этот инструмент, который в любом случае, мы будем генерировать некоторый код C ++ ... вы можете просто сгенерировать фактические логи сравнения c. То есть, вместо того, чтобы писать вручную сравнение между членами, просто генерируйте сравнения между членами. Вы даже можете получить имена членов, которые не совпадают.

Если вы собираетесь обрабатывать произвольные функции pNext -цепи, то вам понадобится какой-нибудь инструмент генератора. И если вам все равно понадобится генератор, просто используйте его для решения всей проблемы.

Теперь важно понимать, что код генерации для гипотетической hasRequiredFeatures реализации должен будет быть полусложным. Если вы разрешаете полную цепочку структур pNext, то вам нужно создать собственную соответствующую цепочку эквивалентных структур, чтобы использовать ее для запроса из Vulkan. И это не совсем тривиально.

Вам нужно будет перебрать цепочку pNext и проверить поле sType каждой структуры. Но, конечно, pNext - это void*, поэтому вам придется немного лгать / обманывать правила C ++, чтобы прочитать поле sType. По сути, вам нужно будет reinterpret_cast void* до VkStructureType*, прочитать значение, сравнить его со всеми возможностями, с которыми вы работаете, и перейти оттуда. Вам следует пропустить все sType, о которых вы не знаете, что потребует дополнительных хитростей в C ++.

Но вы используете низкоуровневый API; Нарушение правил C ++ - это просто вещь, к которой вам нужно привыкнуть.

Для каждой такой структуры вам нужно выделить соответствующую структуру, заполнить ее sType соответствующим образом, а затем добавить в цепочка pNext, которую вы строите.

После того, как вы построили все это, вы можете сделать свой вызов Vulkan, выполнить сравнения, собрать данные и, наконец, удалить всю цепочку структур.


Если ваша цель на самом деле придерживаться только VkPhysicalDeviceFeatures, а не расширяемых структур, и вы просто хотите C ++ - переносимый способ сравнения таких структур, то просто memcpy их в массив VkBool и сравните два массива для несоответствий. Эти два типа легко копируемы, поэтому это не просто так * * * * * * * * * * * * * * * * *

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

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;
}
...