blob: 6f1a9e3b41622a9d66da8ced4d9335117b6d3b8c [file] [log] [blame]
/* Copyright (c) 2023-2024 Nintendo
* Copyright (c) 2023-2026 LunarG, Inc.
* Modifications Copyright (C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* 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"
#include <spirv/unified1/spirv.hpp>
#include "utils/math_utils.h"
namespace stateless {
bool Device::ValidateCreateShadersFlags(VkShaderCreateFlagsEXT flags, VkShaderStageFlagBits stage, const Location &flag_loc) const {
bool skip = false;
if ((flags & VK_SHADER_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_EXT) != 0 &&
enabled_features.attachmentFragmentShadingRate == VK_FALSE) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-08487", device, flag_loc,
"is %s, but the attachmentFragmentShadingRate feature was not enabled.",
string_VkShaderCreateFlagsEXT(flags).c_str());
}
if ((flags & VK_SHADER_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT) != 0 &&
enabled_features.fragmentDensityMap == VK_FALSE) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-08489", device, flag_loc,
"is %s, but the fragmentDensityMap feature was not enabled.", string_VkShaderCreateFlagsEXT(flags).c_str());
}
if ((flags & VK_SHADER_CREATE_64_BIT_INDEXING_BIT_EXT) != 0 && enabled_features.shader64BitIndexing == VK_FALSE) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-11758", device, flag_loc,
"is %s, but the shader64BitIndexing feature was not enabled.", string_VkShaderCreateFlagsEXT(flags).c_str());
}
if ((flags & VK_SHADER_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) != 0 && enabled_features.subgroupSizeControl == VK_FALSE) {
skip |= LogError(
"VUID-VkShaderCreateInfoEXT-flags-09404", device, flag_loc,
"contains VK_SHADER_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT, but subgroupSizeControl feature is not enabled.");
}
if ((flags & VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT) != 0 && enabled_features.computeFullSubgroups == VK_FALSE) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-09405", device, flag_loc,
"contains VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, but computeFullSubgroups feature is not enabled.");
}
if ((flags & VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT) != 0 && enabled_features.deviceGeneratedCommands == VK_FALSE) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-11005", device, flag_loc,
"contains VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT, but deviceGeneratedCommands feature is not enabled.");
}
if ((stage != VK_SHADER_STAGE_COMPUTE_BIT) && ((flags & VK_SHADER_CREATE_DISPATCH_BASE_BIT_EXT) != 0)) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-08485", device, flag_loc,
"includes VK_SHADER_CREATE_DISPATCH_BASE_BIT_EXT but the stage is %s.", string_VkShaderStageFlagBits(stage));
}
if (stage != VK_SHADER_STAGE_FRAGMENT_BIT) {
if ((flags & VK_SHADER_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_EXT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-08486", device, flag_loc,
"includes VK_SHADER_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_EXT but the stage is %s.",
string_VkShaderStageFlagBits(stage));
}
if ((flags & VK_SHADER_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-08488", device, flag_loc,
"includes VK_SHADER_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT but the stage is %s.",
string_VkShaderStageFlagBits(stage));
}
}
if (stage != VK_SHADER_STAGE_MESH_BIT_EXT && (flags & VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT) != 0) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-08414", device, flag_loc,
"includes VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT but the stage is %s.", string_VkShaderStageFlagBits(stage));
}
if (((flags & VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT) != 0) &&
((stage & (VK_SHADER_STAGE_MESH_BIT_EXT | VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_COMPUTE_BIT)) == 0)) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-08992", device, flag_loc,
"includes VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT but the stage is %s.",
string_VkShaderStageFlagBits(stage));
}
return skip;
}
bool Device::manual_PreCallValidateCreateShadersEXT(VkDevice device, uint32_t createInfoCount,
const VkShaderCreateInfoEXT *pCreateInfos,
const VkAllocationCallbacks *pAllocator, VkShaderEXT *pShaders,
const Context &context) const {
bool skip = false;
const auto &error_obj = context.error_obj;
uint32_t linked_heap_stage = createInfoCount;
uint32_t linked_non_heap_stage = createInfoCount;
for (uint32_t i = 0; i < createInfoCount; ++i) {
const Location create_info_loc = error_obj.location.dot(Field::pCreateInfos, i);
const VkShaderCreateInfoEXT& create_info = pCreateInfos[i];
skip |= ValidateCreateShadersFlags(create_info.flags, create_info.stage, create_info_loc.dot(Field::flags));
if (create_info.codeType == VK_SHADER_CODE_TYPE_SPIRV_EXT) {
if (!IsPointerAligned(create_info.pCode, 4 * sizeof(unsigned char))) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-pCode-08493", device, create_info_loc.dot(Field::codeType),
"is VK_SHADER_CODE_TYPE_SPIRV_EXT, but pCode (%p) is not aligned to 4 bytes.", create_info.pCode);
} else if (!IsIntegerMultipleOf(create_info.codeSize, 4)) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-codeSize-08735", device, create_info_loc.dot(Field::codeSize),
"(%" PRIu64 ") is not a multiple of 4. You might have forget to multiply by sizeof(uint32_t).",
static_cast<uint64_t>(create_info.codeSize));
} else {
// Can't cast this until we know it is aligned to 4 bytes or USAN will catch it
const uint32_t first_dword = ((uint32_t *)create_info.pCode)[0];
if (first_dword != spv::MagicNumber) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-pCode-08738", device, create_info_loc.dot(Field::pCode),
"doesn't point to a SPIR-V module. The first dword (0x%" PRIx32
") is not the SPIR-V MagicNumber (0x07230203).",
first_dword);
}
}
} else if (create_info.codeType == VK_SHADER_CODE_TYPE_BINARY_EXT) {
if (!IsPointerAligned(create_info.pCode, 16 * sizeof(unsigned char))) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-pCode-08492", device, create_info_loc.dot(Field::codeType),
"is VK_SHADER_CODE_TYPE_BINARY_EXT, but pCode (%p) is not aligned to 16 bytes.", create_info.pCode);
}
}
const VkShaderStageFlags linkedStages = VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_MESH_BIT_EXT |
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT |
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_GEOMETRY_BIT |
VK_SHADER_STAGE_FRAGMENT_BIT;
if ((create_info.stage & linkedStages) == 0 && (create_info.flags & VK_SHADER_CREATE_LINK_STAGE_BIT_EXT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-08412", device, create_info_loc.dot(Field::flags),
"includes VK_SHADER_CREATE_LINK_STAGE_BIT_EXT but the stage is %s.",
string_VkShaderStageFlagBits(create_info.stage));
}
if (create_info.stage == VK_SHADER_STAGE_VERTEX_BIT) {
if ((create_info.nextStage &
~(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08427", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_VERTEX_BIT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
if ((create_info.nextStage & ~(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08430", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
if ((create_info.nextStage & ~(VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08431", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_GEOMETRY_BIT) {
if ((create_info.nextStage & ~VK_SHADER_STAGE_FRAGMENT_BIT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08433", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_GEOMETRY_BIT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_FRAGMENT_BIT || create_info.stage == VK_SHADER_STAGE_COMPUTE_BIT) {
if (create_info.nextStage != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08434", device, create_info_loc.dot(Field::stage),
"is %s, but nextStage is %s.", string_VkShaderStageFlagBits(create_info.stage),
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_TASK_BIT_EXT) {
if ((create_info.nextStage & ~VK_SHADER_STAGE_MESH_BIT_EXT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08435", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_TASK_BIT_EXT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_MESH_BIT_EXT) {
if ((create_info.nextStage & ~VK_SHADER_STAGE_FRAGMENT_BIT) != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-nextStage-08436", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_MESH_BIT_EXT, but nextStage is %s.",
string_VkShaderStageFlags(create_info.nextStage).c_str());
}
} else if (create_info.stage == VK_SHADER_STAGE_ALL_GRAPHICS) {
// string_VkShaderStageFlagBits can't print these, so list manually
skip |= LogError("VUID-VkShaderCreateInfoEXT-stage-08418", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_ALL_GRAPHICS.");
} else if (create_info.stage == VK_SHADER_STAGE_ALL) {
// string_VkShaderStageFlagBits can't print these, so list manually
skip |= LogError("VUID-VkShaderCreateInfoEXT-stage-08418", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_ALL.");
} else if (create_info.stage == VK_SHADER_STAGE_SUBPASS_SHADING_BIT_HUAWEI) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-stage-08425", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_SUBPASS_SHADING_BIT_HUAWEI.");
} else if (create_info.stage == VK_SHADER_STAGE_CLUSTER_CULLING_BIT_HUAWEI) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-stage-08426", device, create_info_loc.dot(Field::stage),
"is VK_SHADER_STAGE_CLUSTER_CULLING_BIT_HUAWEI.");
}
if ((create_info.flags & VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT) != 0) {
if (create_info.setLayoutCount != 0) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-11290", device, create_info_loc.dot(Field::flags),
"includes VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT (%s), but setLayoutCount is %" PRIu32 ".",
string_VkShaderCreateFlagsEXT(create_info.flags).c_str(), create_info.setLayoutCount);
} else if (create_info.pSetLayouts) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-flags-11291", device, create_info_loc.dot(Field::flags),
"includes VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT (%s), but pSetLayouts is not NULL (0x%p).",
string_VkShaderCreateFlagsEXT(create_info.flags).c_str(), create_info.pSetLayouts);
}
if (create_info.pushConstantRangeCount != 0) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-11370", device, create_info_loc.dot(Field::flags),
"includes VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT (%s), but pushConstantRangeCount is %" PRIu32 ".",
string_VkShaderCreateFlagsEXT(create_info.flags).c_str(), create_info.pushConstantRangeCount);
} else if (create_info.pPushConstantRanges) {
skip |=
LogError("VUID-VkShaderCreateInfoEXT-flags-11371", device, create_info_loc.dot(Field::flags),
"includes VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT (%s), but pPushConstantRanges is not NULL (0x%p).",
string_VkShaderCreateFlagsEXT(create_info.flags).c_str(), create_info.pPushConstantRanges);
}
}
if ((create_info.stage & linkedStages) != 0 && (create_info.flags & VK_SHADER_CREATE_LINK_STAGE_BIT_EXT) != 0) {
if ((create_info.flags & VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT) != 0) {
linked_heap_stage = i;
} else {
linked_non_heap_stage = i;
}
}
skip |= ValidatePushConstantRange(create_info.pushConstantRangeCount, create_info.pPushConstantRanges, create_info_loc);
if (create_info.setLayoutCount > phys_dev_props.limits.maxBoundDescriptorSets) {
skip |= LogError("VUID-VkShaderCreateInfoEXT-setLayoutCount-12257", device, create_info_loc.dot(Field::setLayoutCount),
"(%" PRIu32 ") exceeds the maxBoundDescriptorSets limit (%" PRIu32 ").", create_info.setLayoutCount,
phys_dev_props.limits.maxBoundDescriptorSets);
}
if (const VkShaderDescriptorSetAndBindingMappingInfoEXT* mapping_info =
vku::FindStructInPNextChain<VkShaderDescriptorSetAndBindingMappingInfoEXT>(create_info.pNext)) {
skip |= ValidateShaderDescriptorSetAndBindingMappingInfo(*mapping_info, error_obj.location);
}
}
if (linked_heap_stage < createInfoCount && linked_non_heap_stage < createInfoCount) {
skip |= LogError(
"VUID-vkCreateShadersEXT-flags-11472", device,
error_obj.location.dot(Field::pCreateInfos, linked_heap_stage).dot(Field::flags),
"has both VK_SHADER_CREATE_LINK_STAGE_BIT_EXT and VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT, but pCreateInfos[%" PRIu32
"].flags has VK_SHADER_CREATE_LINK_STAGE_BIT_EXT, but not VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT.",
linked_non_heap_stage);
}
return skip;
}
bool Device::manual_PreCallValidateGetShaderBinaryDataEXT(VkDevice device, VkShaderEXT shader, size_t *pDataSize, void *pData,
const Context &context) const {
bool skip = false;
const auto &error_obj = context.error_obj;
if (pData) {
if (!IsPointerAligned(pData, 16)) {
skip |= LogError("VUID-vkGetShaderBinaryDataEXT-None-08499", shader, error_obj.location.dot(Field::pData),
"(%p) is not aligned to 16 bytes.", pData);
}
}
return skip;
}
} // namespace stateless