blob: 090466e91a9882d2801621f8b1f51e1a0d9490a5 [file] [log] [blame]
/* Copyright (c) 2015-2023 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
* Copyright (C) 2015-2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "stateless/stateless_validation.h"
namespace {
struct ImportOperationsInfo {
const VkImportMemoryHostPointerInfoEXT *host_pointer_info_ext;
uint32_t total_import_ops;
};
ImportOperationsInfo GetNumberOfImportInfo(const VkMemoryAllocateInfo *pAllocateInfo) {
uint32_t count = 0;
#ifdef VK_USE_PLATFORM_WIN32_KHR
// VkImportMemoryWin32HandleInfoKHR with a non-zero handleType value
auto import_memory_win32_handle = vku::FindStructInPNextChain<VkImportMemoryWin32HandleInfoKHR>(pAllocateInfo->pNext);
count += (import_memory_win32_handle && import_memory_win32_handle->handleType);
#endif
// VkImportMemoryFdInfoKHR with a non-zero handleType value
auto fd_info_khr = vku::FindStructInPNextChain<VkImportMemoryFdInfoKHR>(pAllocateInfo->pNext);
count += (fd_info_khr && fd_info_khr->handleType);
// VkImportMemoryHostPointerInfoEXT with a non-zero handleType value
auto host_pointer_info_ext = vku::FindStructInPNextChain<VkImportMemoryHostPointerInfoEXT>(pAllocateInfo->pNext);
count += (host_pointer_info_ext && host_pointer_info_ext->handleType);
#ifdef VK_USE_PLATFORM_ANDROID_KHR
// VkImportAndroidHardwareBufferInfoANDROID with a non-NULL buffer value
auto import_memory_ahb = vku::FindStructInPNextChain<VkImportAndroidHardwareBufferInfoANDROID>(pAllocateInfo->pNext);
count += (import_memory_ahb && import_memory_ahb->buffer);
#endif
#ifdef VK_USE_PLATFORM_FUCHSIA
// VkImportMemoryZirconHandleInfoFUCHSIA with a non-zero handleType value
auto import_zircon_fuchsia = vku::FindStructInPNextChain<VkImportMemoryZirconHandleInfoFUCHSIA>(pAllocateInfo->pNext);
count += (import_zircon_fuchsia && import_zircon_fuchsia->handleType);
// VkImportMemoryBufferCollectionFUCHSIA
auto import_buffer_collection_fuchsia = vku::FindStructInPNextChain<VkImportMemoryBufferCollectionFUCHSIA>(pAllocateInfo->pNext);
count += static_cast<bool>(
import_buffer_collection_fuchsia); // NOTE: There's no handleType on VkImportMemoryBufferCollectionFUCHSIA, so we
// can't check that, and from the "Valid Usage (Implicit)" collection has to
// always be valid.
#endif
ImportOperationsInfo info = {};
info.total_import_ops = count;
info.host_pointer_info_ext = host_pointer_info_ext;
return info;
}
} // namespace
bool StatelessValidation::manual_PreCallValidateAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory,
const ErrorObject &error_obj) const {
bool skip = false;
if (!pAllocateInfo) {
return skip;
}
const Location allocate_info_loc = error_obj.location.dot(Field::pAllocateInfo);
auto chained_prio_struct = vku::FindStructInPNextChain<VkMemoryPriorityAllocateInfoEXT>(pAllocateInfo->pNext);
if (chained_prio_struct && (chained_prio_struct->priority < 0.0f || chained_prio_struct->priority > 1.0f)) {
skip |= LogError("VUID-VkMemoryPriorityAllocateInfoEXT-priority-02602", device,
allocate_info_loc.pNext(Struct::VkMemoryPriorityAllocateInfoEXT, Field::priority), "is %f",
chained_prio_struct->priority);
}
VkMemoryAllocateFlags flags = 0;
auto flags_info = vku::FindStructInPNextChain<VkMemoryAllocateFlagsInfo>(pAllocateInfo->pNext);
if (flags_info) {
flags = flags_info->flags;
}
const ImportOperationsInfo import_info = GetNumberOfImportInfo(pAllocateInfo);
auto opaque_alloc_info = vku::FindStructInPNextChain<VkMemoryOpaqueCaptureAddressAllocateInfo>(pAllocateInfo->pNext);
if (opaque_alloc_info && opaque_alloc_info->opaqueCaptureAddress != 0) {
const Location address_loc =
allocate_info_loc.pNext(Struct::VkMemoryOpaqueCaptureAddressAllocateInfo, Field::opaqueCaptureAddress);
if (!(flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT)) {
skip |= LogError("VUID-VkMemoryAllocateInfo-opaqueCaptureAddress-03329", device, address_loc,
"is non-zero (%" PRIu64
") so VkMemoryAllocateFlagsInfo::flags must include "
"VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT.",
opaque_alloc_info->opaqueCaptureAddress);
}
if (import_info.host_pointer_info_ext) {
skip |= LogError("VUID-VkMemoryAllocateInfo-pNext-03332", device, address_loc,
"is non-zero (%" PRIu64 ") but the pNext chain includes a VkImportMemoryHostPointerInfoEXT structure.",
opaque_alloc_info->opaqueCaptureAddress);
}
if (import_info.total_import_ops > 0) {
skip |=
LogError("VUID-VkMemoryAllocateInfo-opaqueCaptureAddress-03333", device, address_loc,
"is non-zero (%" PRIu64 ") but an import operation is defined.", opaque_alloc_info->opaqueCaptureAddress);
}
}
if (import_info.total_import_ops > 1) {
skip |= LogError("VUID-VkMemoryAllocateInfo-None-06657", device, allocate_info_loc,
"%" PRIu32 " import operations are defined", import_info.total_import_ops);
}
auto export_memory = vku::FindStructInPNextChain<VkExportMemoryAllocateInfo>(pAllocateInfo->pNext);
if (export_memory) {
auto export_memory_nv = vku::FindStructInPNextChain<VkExportMemoryAllocateInfoNV>(pAllocateInfo->pNext);
if (export_memory_nv) {
skip |= LogError("VUID-VkMemoryAllocateInfo-pNext-00640", device, allocate_info_loc,
"pNext chain includes both VkExportMemoryAllocateInfo and "
"VkExportMemoryAllocateInfoNV");
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
auto export_memory_win32_nv = vku::FindStructInPNextChain<VkExportMemoryWin32HandleInfoNV>(pAllocateInfo->pNext);
if (export_memory_win32_nv) {
skip |= LogError("VUID-VkMemoryAllocateInfo-pNext-00640", device, allocate_info_loc,
"pNext chain includes both VkExportMemoryAllocateInfo and "
"VkExportMemoryWin32HandleInfoNV");
}
#endif
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
if (vku::FindStructInPNextChain<VkImportMemoryWin32HandleInfoKHR>(pAllocateInfo->pNext) &&
vku::FindStructInPNextChain<VkImportMemoryWin32HandleInfoNV>(pAllocateInfo->pNext)) {
skip |= LogError("VUID-VkMemoryAllocateInfo-pNext-00641", device, allocate_info_loc,
"pNext chain includes both VkImportMemoryWin32HandleInfoKHR and "
"VkImportMemoryWin32HandleInfoNV");
}
#endif
if (flags) {
const Location flags_loc = allocate_info_loc.pNext(Struct::VkMemoryAllocateFlagsInfo, Field::flags);
VkBool32 capture_replay = false;
VkBool32 buffer_device_address = false;
const auto *vulkan_12_features = vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(device_createinfo_pnext);
if (vulkan_12_features) {
capture_replay = vulkan_12_features->bufferDeviceAddressCaptureReplay;
buffer_device_address = vulkan_12_features->bufferDeviceAddress;
} else {
const auto *bda_features = vku::FindStructInPNextChain<VkPhysicalDeviceBufferDeviceAddressFeatures>(device_createinfo_pnext);
if (bda_features) {
capture_replay = bda_features->bufferDeviceAddressCaptureReplay;
buffer_device_address = bda_features->bufferDeviceAddress;
}
}
if ((flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT) && !capture_replay) {
skip |= LogError("VUID-VkMemoryAllocateInfo-flags-03330", device, flags_loc,
"has VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT set, but"
"bufferDeviceAddressCaptureReplay feature is not enabled.");
}
if ((flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT) && !buffer_device_address) {
skip |= LogError("VUID-VkMemoryAllocateInfo-flags-03331", device, flags_loc,
"has VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT set, but bufferDeviceAddress feature is not enabled.");
}
}
#ifdef VK_USE_PLATFORM_METAL_EXT
skip |=
ExportMetalObjectsPNextUtil(VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT, "VUID-VkMemoryAllocateInfo-pNext-06780",
error_obj.location, "VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT", pAllocateInfo->pNext);
#endif // VK_USE_PLATFORM_METAL_EXT
return skip;
}
bool StatelessValidation::ValidateDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirementsKHR *pInfo,
const Location &loc) const {
bool skip = false;
if (!pInfo || !pInfo->pCreateInfo) {
return skip;
}
const auto &create_info = *(pInfo->pCreateInfo);
if (vku::FindStructInPNextChain<VkImageSwapchainCreateInfoKHR>(create_info.pNext)) {
skip |= LogError("VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06416", device, loc,
"pNext chain contains VkImageSwapchainCreateInfoKHR.");
}
if (vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(create_info.pNext)) {
skip |= LogError("VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06776", device, loc,
"pNext chain contains VkImageDrmFormatModifierExplicitCreateInfoEXT.");
}
if (vkuFormatIsMultiplane(create_info.format) && (create_info.flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0) {
if (pInfo->planeAspect == VK_IMAGE_ASPECT_NONE_KHR) {
skip |= LogError("VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06417", device, loc.dot(Field::planeAspect),
"is VK_IMAGE_ASPECT_NONE_KHR with a multi-planar format and disjoint flag.");
} else if ((create_info.tiling == VK_IMAGE_TILING_LINEAR || create_info.tiling == VK_IMAGE_TILING_OPTIMAL) &&
!IsOnlyOneValidPlaneAspect(create_info.format, pInfo->planeAspect)) {
skip |= LogError("VUID-VkDeviceImageMemoryRequirements-pCreateInfo-06419", device, loc.dot(Field::planeAspect),
"is %s but is invalid for %s.", string_VkImageAspectFlags(pInfo->planeAspect).c_str(),
string_VkFormat(create_info.format));
}
}
#ifdef VK_USE_PLATFORM_ANDROID_KHR
const auto *external_format = vku::FindStructInPNextChain<VkExternalFormatANDROID>(pInfo->pCreateInfo);
if (external_format && external_format->externalFormat) {
skip |= LogError("VUID-VkDeviceImageMemoryRequirements-pNext-06996", device, loc.dot(Field::pCreateInfo),
"pNext chain contains VkExternalFormatANDROID with externalFormat %" PRIu64 ".",
external_format->externalFormat);
}
#endif // VK_USE_PLATFORM_ANDROID_KHR
return skip;
}
bool StatelessValidation::manual_PreCallValidateGetDeviceImageMemoryRequirements(VkDevice device,
const VkDeviceImageMemoryRequirements *pInfo,
VkMemoryRequirements2 *pMemoryRequirements,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidateDeviceImageMemoryRequirements(device, pInfo, error_obj.location.dot(Field::pInfo));
return skip;
}
bool StatelessValidation::manual_PreCallValidateGetDeviceImageMemoryRequirementsKHR(VkDevice device,
const VkDeviceImageMemoryRequirements *pInfo,
VkMemoryRequirements2 *pMemoryRequirements,
const ErrorObject &error_obj) const {
return manual_PreCallValidateGetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements, error_obj);
}
bool StatelessValidation::manual_PreCallValidateGetDeviceImageSparseMemoryRequirements(
VkDevice device, const VkDeviceImageMemoryRequirements *pInfo, uint32_t *pSparseMemoryRequirementCount,
VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements, const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidateDeviceImageMemoryRequirements(device, pInfo, error_obj.location.dot(Field::pInfo));
return skip;
}
bool StatelessValidation::manual_PreCallValidateGetDeviceImageSparseMemoryRequirementsKHR(
VkDevice device, const VkDeviceImageMemoryRequirements *pInfo, uint32_t *pSparseMemoryRequirementCount,
VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements, const ErrorObject &error_obj) const {
return manual_PreCallValidateGetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount,
pSparseMemoryRequirements, error_obj);
}