| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "target_requirements.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "common/macros.h" |
| #include "radix_sort/platforms/vk/radix_sort_vk.h" |
| #include "spinel/platforms/vk/spinel_vk.h" |
| #include "target.h" |
| #include "target_archive/target_archive.h" |
| |
| // |
| // |
| // |
| struct spinel_vk_target |
| { |
| struct target_archive_header ar_header; |
| }; |
| |
| // |
| // SPINEL TARGET REQUIREMENTS: VULKAN |
| // |
| bool |
| spinel_vk_target_get_requirements(struct spinel_vk_target const * target, |
| struct spinel_vk_target_requirements * requirements) |
| { |
| // |
| // Must not be NULL |
| // |
| if ((target == NULL) || (requirements == NULL)) |
| { |
| return false; |
| } |
| |
| // |
| // Unmarshalling assumes dword alignment. |
| // |
| assert(alignof(struct spinel_target_header) == 4); |
| |
| // |
| // Verify target archive is valid archive |
| // |
| #ifndef SPN_VK_TARGET_DISABLE_VERIFY |
| if (target->ar_header.magic != TARGET_ARCHIVE_MAGIC) |
| { |
| #ifndef NDEBUG |
| fprintf(stderr, "Error: Invalid target -- missing magic."); |
| #endif |
| return NULL; |
| } |
| #endif |
| |
| // |
| // Get the target archive header |
| // |
| struct target_archive_header const * const ar_header = &target->ar_header; |
| struct target_archive_entry const * const ar_entries = ar_header->entries; |
| uint32_t const * const ar_data = ar_entries[ar_header->count - 1].data; |
| |
| // |
| // Get the spinel_vk_target header |
| // |
| struct spinel_target_header const * spinel_header; |
| |
| // We assert `alignof(spinel_target_header) == 4` (see above) so we can |
| // memcpy() pointers. |
| memcpy(&spinel_header, &ar_data, sizeof(spinel_header)); |
| |
| // |
| // Get the embedded radix_vk_target |
| // |
| // clang-format off |
| uint32_t const * rs_target_data = ar_data + (ar_entries[ar_header->count - 1].offset >> 2); |
| struct radix_sort_vk_target const * rs_target; |
| // clang-format on |
| |
| // We assert `alignof(radix_sort_vk_target_header) == 4` in the radix sort |
| // sources so we can memcpy() pointers. |
| memcpy(&rs_target, &rs_target_data, sizeof(rs_target_data)); |
| |
| // |
| // Verify target is compatible with the library. |
| // |
| #ifndef SPN_VK_TARGET_DISABLE_VERIFY |
| if (spinel_header->magic != SPN_HEADER_MAGIC) |
| { |
| #ifndef NDEBUG |
| fprintf(stderr, "Error: Target is not compatible with library."); |
| #endif |
| return NULL; |
| } |
| #endif |
| |
| // |
| // |
| // |
| bool is_ok = true; |
| uint32_t ext_name_count = 0; |
| |
| // |
| // EXTENSIONS |
| // |
| // Compute number of required extensions |
| // |
| for (uint32_t ii = 0; ii < ARRAY_LENGTH_MACRO(spinel_header->extensions.bitmap); ii++) |
| { |
| ext_name_count += __builtin_popcount(spinel_header->extensions.bitmap[ii]); |
| } |
| |
| if (requirements->ext_names == NULL) |
| { |
| requirements->ext_name_count = ext_name_count; |
| |
| if (ext_name_count > 0) |
| { |
| is_ok = false; |
| } |
| } |
| else |
| { |
| if (requirements->ext_name_count < ext_name_count) |
| { |
| is_ok = false; |
| } |
| else |
| { |
| // |
| // FIXME(allanmac): This can be accelerated by exploiting |
| // the extension bitmap. |
| // |
| uint32_t ii = 0; |
| |
| #define SPN_TARGET_EXTENSION_STRING(ext_) "VK_" STRINGIFY_MACRO(ext_) |
| |
| #undef SPN_TARGET_EXTENSION |
| #define SPN_TARGET_EXTENSION(ext_) \ |
| if (spinel_header->extensions.named.ext_) \ |
| { \ |
| requirements->ext_names[ii++] = SPN_TARGET_EXTENSION_STRING(ext_); \ |
| } |
| |
| SPN_TARGET_EXTENSIONS() |
| } |
| } |
| |
| // |
| // Enable physical device features |
| // |
| if ((requirements->pdf == NULL) || (requirements->pdf11 == NULL) || (requirements->pdf12 == NULL)) |
| { |
| is_ok = false; |
| } |
| else |
| { |
| #undef SPN_TARGET_FEATURE_VK10 |
| #define SPN_TARGET_FEATURE_VK10(feature_) 1 + |
| |
| #undef SPN_TARGET_FEATURE_VK11 |
| #define SPN_TARGET_FEATURE_VK11(feature_) 1 + |
| |
| #undef SPN_TARGET_FEATURE_VK12 |
| #define SPN_TARGET_FEATURE_VK12(feature_) 1 + |
| |
| // |
| // Don't create the variable if it's not used |
| // |
| #if (SPN_TARGET_FEATURES_VK10() 0) |
| VkPhysicalDeviceFeatures * const pdf = requirements->pdf; |
| #endif |
| #if (SPN_TARGET_FEATURES_VK11() 0) |
| VkPhysicalDeviceVulkan11Features * const pdf11 = requirements->pdf11; |
| #endif |
| #if (SPN_TARGET_FEATURES_VK12() 0) |
| VkPhysicalDeviceVulkan12Features * const pdf12 = requirements->pdf12; |
| #endif |
| |
| // |
| // Let's always have this on during debug |
| // |
| #ifndef NDEBUG |
| pdf->robustBufferAccess = true; |
| #endif |
| |
| // |
| // VULKAN 1.0 |
| // |
| #undef SPN_TARGET_FEATURE_VK10 |
| #define SPN_TARGET_FEATURE_VK10(feature_) \ |
| if (spinel_header->features.named.feature_) \ |
| { \ |
| pdf->feature_ = true; \ |
| } |
| |
| SPN_TARGET_FEATURES_VK10() |
| |
| // |
| // VULKAN 1.1 |
| // |
| #undef SPN_TARGET_FEATURE_VK11 |
| #define SPN_TARGET_FEATURE_VK11(feature_) \ |
| if (spinel_header->features.named.feature_) \ |
| { \ |
| pdf11->feature_ = true; \ |
| } |
| |
| SPN_TARGET_FEATURES_VK11() |
| |
| // |
| // VULKAN 1.2 |
| // |
| #undef SPN_TARGET_FEATURE_VK12 |
| #define SPN_TARGET_FEATURE_VK12(feature_) \ |
| if (spinel_header->features.named.feature_) \ |
| { \ |
| pdf12->feature_ = true; \ |
| } |
| |
| SPN_TARGET_FEATURES_VK12() |
| } |
| |
| // |
| // Concatenate radix sort requirements |
| // |
| if (requirements->ext_names == NULL) |
| { |
| struct radix_sort_vk_target_requirements rs_tr = { |
| // .ext_name_count = 0, |
| // .ext_names = NULL |
| .pdf = requirements->pdf, |
| .pdf11 = requirements->pdf11, |
| .pdf12 = requirements->pdf12, |
| }; |
| |
| bool const rs_is_ok = radix_sort_vk_target_get_requirements(rs_target, &rs_tr); |
| is_ok = is_ok && rs_is_ok; |
| |
| requirements->ext_name_count += rs_tr.ext_name_count; |
| } |
| else |
| { |
| uint32_t const rs_ext_name_count = (requirements->ext_name_count > ext_name_count) |
| ? (requirements->ext_name_count - ext_name_count) |
| : 0; |
| |
| struct radix_sort_vk_target_requirements rs_tr = { |
| .ext_name_count = rs_ext_name_count, |
| .ext_names = requirements->ext_names + ext_name_count, |
| .pdf = requirements->pdf, |
| .pdf11 = requirements->pdf11, |
| .pdf12 = requirements->pdf12, |
| }; |
| |
| bool const rs_is_ok = radix_sort_vk_target_get_requirements(rs_target, &rs_tr); |
| is_ok = is_ok && rs_is_ok; |
| } |
| |
| // |
| // |
| // |
| return is_ok; |
| } |
| |
| // |
| // |
| // |