blob: 804b14d819d011cf0303ca93d8bd83c4a3c8ddd4 [file] [log] [blame]
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2015-2025 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
*/
#include <cmath>
#include <vector>
#include "layer_validation_tests.h"
#include "utils/convert_utils.h"
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include "wayland-client.h"
#endif
// Global list of sType,size identifiers
std::vector<std::pair<uint32_t, uint32_t>> custom_stype_info{};
VkFormat FindSupportedDepthOnlyFormat(VkPhysicalDevice gpu) {
constexpr std::array depth_formats = {VK_FORMAT_D16_UNORM, VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D32_SFLOAT};
for (VkFormat depth_format : depth_formats) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, depth_format, &format_props);
if (format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
return depth_format;
}
}
assert(false); // Vulkan drivers are guaranteed to have at least one supported format
return VK_FORMAT_UNDEFINED;
}
VkFormat FindSupportedStencilOnlyFormat(VkPhysicalDevice gpu) {
constexpr std::array stencil_formats = {VK_FORMAT_S8_UINT};
for (VkFormat stencil_format : stencil_formats) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, stencil_format, &format_props);
if (format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
return stencil_format;
}
}
return VK_FORMAT_UNDEFINED;
}
VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice gpu) {
const VkFormat ds_formats[] = {VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT};
for (uint32_t i = 0; i < size32(ds_formats); ++i) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, ds_formats[i], &format_props);
if (format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
return ds_formats[i];
}
}
assert(false); // Vulkan drivers are guaranteed to have at least one supported format
return VK_FORMAT_UNDEFINED;
}
bool FormatIsSupported(VkPhysicalDevice gpu, VkFormat format, VkImageTiling tiling, VkFormatFeatureFlags features) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, format, &format_props);
VkFormatFeatureFlags gpu_features =
(VK_IMAGE_TILING_OPTIMAL == tiling ? format_props.optimalTilingFeatures : format_props.linearTilingFeatures);
return (0 != (gpu_features & features));
}
bool FormatFeaturesAreSupported(VkPhysicalDevice gpu, VkFormat format, VkImageTiling tiling, VkFormatFeatureFlags features) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, format, &format_props);
VkFormatFeatureFlags gpu_features =
(VK_IMAGE_TILING_OPTIMAL == tiling ? format_props.optimalTilingFeatures : format_props.linearTilingFeatures);
return (features == (gpu_features & features));
}
bool FormatFeatures2AreSupported(VkPhysicalDevice phy, VkFormat format, VkImageTiling tiling, VkFormatFeatureFlags2 features) {
VkFormatProperties3 fmt_props_3 = vku::InitStructHelper();
VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_props_3);
vk::GetPhysicalDeviceFormatProperties2(phy, format, &fmt_props_2);
VkFormatFeatureFlags2 phy_features =
(VK_IMAGE_TILING_OPTIMAL == tiling ? fmt_props_3.optimalTilingFeatures : fmt_props_3.linearTilingFeatures);
return (features == (phy_features & features));
}
VkResult GetImageFormatProps(VkPhysicalDevice gpu, const VkImageCreateInfo &ci, VkImageFormatProperties &out_limits) {
return vk::GetPhysicalDeviceImageFormatProperties(gpu, ci.format, ci.imageType, ci.tiling, ci.usage, ci.flags, &out_limits);
}
bool IsImageFormatSupported(const VkPhysicalDevice gpu, const VkImageCreateInfo &ci, const VkFormatFeatureFlags features) {
// Verify physical device support of format features
if (!FormatFeaturesAreSupported(gpu, ci.format, ci.tiling, features)) {
return false;
}
// Verify that PhysDevImageFormatProp() also claims support for the specific usage
VkImageFormatProperties props;
VkResult err = GetImageFormatProps(gpu, ci, props);
if (VK_SUCCESS != err) {
return false;
}
if (ci.arrayLayers > props.maxArrayLayers) {
return false;
}
if ((ci.samples & props.sampleCounts) == 0) {
return false;
}
return true;
}
bool BufferFormatAndFeaturesSupported(VkPhysicalDevice gpu, VkFormat format, VkFormatFeatureFlags features) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, format, &format_props);
VkFormatFeatureFlags gpu_features = format_props.bufferFeatures;
return (features == (gpu_features & features));
}
bool operator==(const VkDebugUtilsLabelEXT &rhs, const VkDebugUtilsLabelEXT &lhs) {
bool is_equal = (rhs.color[0] == lhs.color[0]) && (rhs.color[1] == lhs.color[1]) && (rhs.color[2] == lhs.color[2]) &&
(rhs.color[3] == lhs.color[3]);
if (is_equal) {
if (rhs.pLabelName && lhs.pLabelName) {
is_equal = (0 == strcmp(rhs.pLabelName, lhs.pLabelName));
} else {
is_equal = (rhs.pLabelName == nullptr) && (lhs.pLabelName == nullptr);
}
}
return is_equal;
}
VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) {
auto *data = reinterpret_cast<DebugUtilsLabelCheckData *>(pUserData);
data->callback(pCallbackData, data);
return VK_FALSE;
}
VkFormat FindFormatWithoutFeatures(VkPhysicalDevice gpu, VkImageTiling tiling, VkFormatFeatureFlags undesired_features) {
const VkFormat first_vk_format = static_cast<VkFormat>(1);
const VkFormat last_vk_format = static_cast<VkFormat>(130); // avoid compressed/feature protected, otherwise 184
VkFormat return_format = VK_FORMAT_UNDEFINED;
for (VkFormat format = first_vk_format; format <= last_vk_format; format = static_cast<VkFormat>(format + 1)) {
VkFormatProperties format_props;
vk::GetPhysicalDeviceFormatProperties(gpu, format, &format_props);
const auto features =
(tiling == VK_IMAGE_TILING_LINEAR) ? format_props.linearTilingFeatures : format_props.optimalTilingFeatures;
if ((features & undesired_features) == 0) {
return_format = format;
break;
}
}
return return_format;
}
VkFormat FindFormatWithoutFeatures2(VkPhysicalDevice gpu, VkImageTiling tiling, VkFormatFeatureFlags2 undesired_features) {
const VkFormat first_compressed_format = VK_FORMAT_BC1_RGB_UNORM_BLOCK; // avoid compressed/feature protected, otherwise 184
const VkFormat first_vk_format = VK_FORMAT_R4G4_UNORM_PACK8;
VkFormat return_format = VK_FORMAT_UNDEFINED;
for (VkFormat format = first_vk_format; format < first_compressed_format; format = static_cast<VkFormat>(format + 1)) {
VkFormatProperties3 fmt_props_3 = vku::InitStructHelper();
VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_props_3);
vk::GetPhysicalDeviceFormatProperties2(gpu, format, &fmt_props_2);
auto features = (tiling == VK_IMAGE_TILING_LINEAR) ? fmt_props_3.linearTilingFeatures : fmt_props_3.optimalTilingFeatures;
if ((features & undesired_features) == 0) {
return_format = format;
break;
}
}
return return_format;
}
void VkLayerTest::CreateSamplerTest(const VkSamplerCreateInfo &create_info, const char *vuid) {
Monitor().SetDesiredError(vuid);
vkt::Sampler sampler(*m_device, create_info);
Monitor().VerifyFound();
}
void VkLayerTest::CreateBufferTest(const VkBufferCreateInfo &create_info, const char *vuid) {
Monitor().SetDesiredError(vuid);
vkt::Buffer buffer(*m_device, create_info, vkt::no_mem);
Monitor().VerifyFound();
}
void VkLayerTest::CreateImageTest(const VkImageCreateInfo &create_info, const char *vuid) {
Monitor().SetDesiredError(vuid);
vkt::Image image(*m_device, create_info, vkt::no_mem);
Monitor().VerifyFound();
}
void VkLayerTest::CreateBufferViewTest(const VkBufferViewCreateInfo &create_info, const char *vuid) {
Monitor().SetDesiredError(vuid);
vkt::BufferView view(*m_device, create_info);
Monitor().VerifyFound();
}
void VkLayerTest::CreateImageViewTest(const VkImageViewCreateInfo &create_info, const char *vuid) {
Monitor().SetDesiredError(vuid);
vkt::ImageView view(*m_device, create_info);
Monitor().VerifyFound();
}
VkResolveModeFlagBits VkLayerTest::FindSupportedDepthResolveMode() {
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(depth_stencil_resolve_props);
VkResolveModeFlagBits depth_resolve_mode = VK_RESOLVE_MODE_NONE;
if (depth_stencil_resolve_props.supportedDepthResolveModes & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) {
depth_resolve_mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
} else if (depth_stencil_resolve_props.supportedDepthResolveModes & VK_RESOLVE_MODE_AVERAGE_BIT) {
depth_resolve_mode = VK_RESOLVE_MODE_AVERAGE_BIT;
} else if (depth_stencil_resolve_props.supportedDepthResolveModes & VK_RESOLVE_MODE_MIN_BIT) {
depth_resolve_mode = VK_RESOLVE_MODE_MIN_BIT;
} else if (depth_stencil_resolve_props.supportedDepthResolveModes & VK_RESOLVE_MODE_MAX_BIT) {
depth_resolve_mode = VK_RESOLVE_MODE_MAX_BIT;
}
return depth_resolve_mode;
}
VkResolveModeFlagBits VkLayerTest::FindSupportedStencilResolveMode() {
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(depth_stencil_resolve_props);
VkResolveModeFlagBits stencil_resolve_mode = VK_RESOLVE_MODE_NONE;
if (depth_stencil_resolve_props.supportedStencilResolveModes & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) {
stencil_resolve_mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
} else if (depth_stencil_resolve_props.supportedStencilResolveModes & VK_RESOLVE_MODE_AVERAGE_BIT) {
stencil_resolve_mode = VK_RESOLVE_MODE_AVERAGE_BIT;
} else if (depth_stencil_resolve_props.supportedStencilResolveModes & VK_RESOLVE_MODE_MIN_BIT) {
stencil_resolve_mode = VK_RESOLVE_MODE_MIN_BIT;
} else if (depth_stencil_resolve_props.supportedStencilResolveModes & VK_RESOLVE_MODE_MAX_BIT) {
stencil_resolve_mode = VK_RESOLVE_MODE_MAX_BIT;
}
return stencil_resolve_mode;
}
void VkLayerTest::CreateRenderPassTest(const VkRenderPassCreateInfo &create_info, bool rp2_supported, const char *rp1_vuid,
const char *rp2_vuid) {
if (rp1_vuid) {
// If the second VUID is not provided, set it equal to the first VUID. In this way,
// we can check both vkCreateRenderPass and vkCreateRenderPass2 with the same VUID
// if rp2_supported is true;
if (rp2_supported && !rp2_vuid) {
rp2_vuid = rp1_vuid;
}
Monitor().SetDesiredError(rp1_vuid);
vkt::RenderPass rp(*m_device, create_info);
Monitor().VerifyFound();
}
if (rp2_supported && rp2_vuid) {
auto create_info2 = ConvertVkRenderPassCreateInfoToV2KHR(create_info);
Monitor().SetDesiredError(rp2_vuid);
vkt::RenderPass rp2(*m_device, *create_info2.ptr());
Monitor().VerifyFound();
}
}
void VkLayerTest::CreateRenderPassBeginTest(const VkCommandBuffer command_buffer, const VkRenderPassBeginInfo *begin_info,
bool rp2_supported, const char *rp1_vuid, const char *rp2_vuid) {
VkCommandBufferBeginInfo cmd_begin_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr};
if (rp1_vuid) {
vk::BeginCommandBuffer(command_buffer, &cmd_begin_info);
Monitor().SetDesiredError(rp1_vuid);
vk::CmdBeginRenderPass(command_buffer, begin_info, VK_SUBPASS_CONTENTS_INLINE);
Monitor().VerifyFound();
vk::ResetCommandBuffer(command_buffer, 0);
}
if (rp2_supported && rp2_vuid) {
VkSubpassBeginInfo subpass_begin_info = {VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR, nullptr, VK_SUBPASS_CONTENTS_INLINE};
vk::BeginCommandBuffer(command_buffer, &cmd_begin_info);
Monitor().SetDesiredError(rp2_vuid);
vk::CmdBeginRenderPass2KHR(command_buffer, begin_info, &subpass_begin_info);
Monitor().VerifyFound();
vk::ResetCommandBuffer(command_buffer, 0);
// For api version >= 1.2, try core entrypoint
PFN_vkCmdBeginRenderPass2KHR vkCmdBeginRenderPass2 =
(PFN_vkCmdBeginRenderPass2KHR)vk::GetDeviceProcAddr(*m_device, "vkCmdBeginRenderPass2");
if (vkCmdBeginRenderPass2) {
vk::BeginCommandBuffer(command_buffer, &cmd_begin_info);
Monitor().SetDesiredError(rp2_vuid);
vkCmdBeginRenderPass2(command_buffer, begin_info, &subpass_begin_info);
Monitor().VerifyFound();
vk::ResetCommandBuffer(command_buffer, 0);
}
}
}
VkSamplerCreateInfo SafeSaneSamplerCreateInfo(void *p_next) {
VkSamplerCreateInfo sampler_create_info = vku::InitStructHelper(p_next);
sampler_create_info.magFilter = VK_FILTER_NEAREST;
sampler_create_info.minFilter = VK_FILTER_NEAREST;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.mipLodBias = 0.0;
sampler_create_info.anisotropyEnable = VK_FALSE;
sampler_create_info.maxAnisotropy = 1.0;
sampler_create_info.compareEnable = VK_FALSE;
sampler_create_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_create_info.minLod = 0.0;
sampler_create_info.maxLod = 16.0;
sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
sampler_create_info.unnormalizedCoordinates = VK_FALSE;
return sampler_create_info;
}
float NearestGreater(const float from) {
using Lim = std::numeric_limits<float>;
const auto positive_direction = Lim::has_infinity ? Lim::infinity() : Lim::max();
return std::nextafter(from, positive_direction);
}
float NearestSmaller(const float from) {
using Lim = std::numeric_limits<float>;
const auto negative_direction = Lim::has_infinity ? -Lim::infinity() : Lim::lowest();
return std::nextafter(from, negative_direction);
}
void VkLayerTest::Init(VkPhysicalDeviceFeatures *features, VkPhysicalDeviceFeatures2 *features2, void *instance_pnext) {
RETURN_IF_SKIP(InitFramework(instance_pnext));
RETURN_IF_SKIP(InitState(features, features2));
}
VkLayerTest::VkLayerTest() {
m_instance_extension_names.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
instance_layers_.push_back(kValidationLayerName);
if (InstanceLayerSupported("VK_LAYER_LUNARG_device_profile_api")) {
instance_layers_.push_back("VK_LAYER_LUNARG_device_profile_api");
}
if (InstanceLayerSupported(kSynchronization2LayerName)) {
instance_layers_.push_back(kSynchronization2LayerName);
}
// If self validation is detected (ex. in CI) then we will add it.
// It is VERY important this is the last layer.
if (InstanceLayerSupported("VK_LAYER_DEV_self_validation")) {
instance_layers_.push_back("VK_LAYER_DEV_self_validation");
}
app_info_ = vku::InitStructHelper();
app_info_.pApplicationName = "layer_tests";
app_info_.applicationVersion = 1;
app_info_.pEngineName = "unittest";
app_info_.engineVersion = 1;
app_info_.apiVersion = VK_API_VERSION_1_0;
// Find out what version the instance supports and record the default target instance
auto enumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion)vk::GetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion");
if (enumerateInstanceVersion) {
uint32_t instance_api_version;
enumerateInstanceVersion(&instance_api_version);
m_instance_api_version = instance_api_version;
} else {
m_instance_api_version = VK_API_VERSION_1_0;
}
m_target_api_version = app_info_.apiVersion;
}
void VkLayerTest::AddSurfaceExtension() {
AddRequiredExtensions(VK_KHR_SURFACE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
#if defined(VK_USE_PLATFORM_WIN32_KHR)
AddWsiExtensions(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
AddWsiExtensions(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
AddWsiExtensions(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
AddWsiExtensions(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
AddWsiExtensions(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
AddWsiExtensions(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif
}
void VkLayerTest::SetTargetApiVersion(APIVersion target_api_version) {
if (target_api_version == 0) target_api_version = VK_API_VERSION_1_0;
// If we set target twice, make sure higest version always wins
if (target_api_version < m_attempted_api_version) return;
m_attempted_api_version = target_api_version; // used to know if request failed
m_target_api_version = target_api_version;
app_info_.apiVersion = m_target_api_version.Value();
}
APIVersion VkLayerTest::DeviceValidationVersion() const {
// The validation layers assume the version we are validating to is the apiVersion unless the device apiVersion is lower
return std::min(m_target_api_version, APIVersion(PhysicalDeviceProps().apiVersion));
}
template <>
VkPhysicalDeviceFeatures2 VkLayerTest::GetPhysicalDeviceFeatures2(VkPhysicalDeviceFeatures2 &features2) {
if (DeviceValidationVersion() >= VK_API_VERSION_1_1) {
vk::GetPhysicalDeviceFeatures2(Gpu(), &features2);
} else {
auto vkGetPhysicalDeviceFeatures2KHR = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR"));
assert(vkGetPhysicalDeviceFeatures2KHR);
vkGetPhysicalDeviceFeatures2KHR(Gpu(), &features2);
}
return features2;
}
template <>
VkPhysicalDeviceProperties2 VkLayerTest::GetPhysicalDeviceProperties2(VkPhysicalDeviceProperties2 &props2) {
if (DeviceValidationVersion() >= VK_API_VERSION_1_1) {
vk::GetPhysicalDeviceProperties2(Gpu(), &props2);
} else {
auto vkGetPhysicalDeviceProperties2KHR = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceProperties2KHR"));
assert(vkGetPhysicalDeviceProperties2KHR);
vkGetPhysicalDeviceProperties2KHR(Gpu(), &props2);
}
return props2;
}
bool VkLayerTest::LoadDeviceProfileLayer(
PFN_vkSetPhysicalDeviceFormatPropertiesEXT &fpvkSetPhysicalDeviceFormatPropertiesEXT,
PFN_vkGetOriginalPhysicalDeviceFormatPropertiesEXT &fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT) {
if (IsPlatformMockICD()) {
printf("Device Profile layer is for real GPU, if using MockICD with profiles, just adjust the profile json file instead\n");
return false;
}
// Load required functions
fpvkSetPhysicalDeviceFormatPropertiesEXT =
(PFN_vkSetPhysicalDeviceFormatPropertiesEXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceFormatPropertiesEXT");
fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT = (PFN_vkGetOriginalPhysicalDeviceFormatPropertiesEXT)vk::GetInstanceProcAddr(
instance(), "vkGetOriginalPhysicalDeviceFormatPropertiesEXT");
if (!(fpvkSetPhysicalDeviceFormatPropertiesEXT) || !(fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT)) {
printf(
"Can't find device_profile_api functions; make sure VK_LAYER_PATH is set correctly to where the validation layers "
"are built, the device profile layer should be in the same directory.\n");
return false;
}
return true;
}
bool VkLayerTest::LoadDeviceProfileLayer(
PFN_vkSetPhysicalDeviceFormatProperties2EXT &fpvkSetPhysicalDeviceFormatProperties2EXT,
PFN_vkGetOriginalPhysicalDeviceFormatProperties2EXT &fpvkGetOriginalPhysicalDeviceFormatProperties2EXT) {
if (IsPlatformMockICD()) {
printf("Device Profile layer is for real GPU, if using MockICD with profiles, just adjust the profile json file instead\n");
return false;
}
// Load required functions
fpvkSetPhysicalDeviceFormatProperties2EXT =
(PFN_vkSetPhysicalDeviceFormatProperties2EXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceFormatProperties2EXT");
fpvkGetOriginalPhysicalDeviceFormatProperties2EXT =
(PFN_vkGetOriginalPhysicalDeviceFormatProperties2EXT)vk::GetInstanceProcAddr(
instance(), "vkGetOriginalPhysicalDeviceFormatProperties2EXT");
if (!(fpvkSetPhysicalDeviceFormatProperties2EXT) || !(fpvkGetOriginalPhysicalDeviceFormatProperties2EXT)) {
printf(
"Can't find device_profile_api functions; make sure VK_LAYER_PATH is set correctly to where the validation layers "
"are built, the device profile layer should be in the same directory.\n");
return false;
}
return true;
}
bool VkLayerTest::LoadDeviceProfileLayer(PFN_vkSetPhysicalDeviceLimitsEXT &fpvkSetPhysicalDeviceLimitsEXT,
PFN_vkGetOriginalPhysicalDeviceLimitsEXT &fpvkGetOriginalPhysicalDeviceLimitsEXT) {
if (IsPlatformMockICD()) {
printf("Device Profile layer is for real GPU, if using MockICD with profiles, just adjust the profile json file instead\n");
return false;
}
// Load required functions
fpvkSetPhysicalDeviceLimitsEXT =
(PFN_vkSetPhysicalDeviceLimitsEXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceLimitsEXT");
fpvkGetOriginalPhysicalDeviceLimitsEXT =
(PFN_vkGetOriginalPhysicalDeviceLimitsEXT)vk::GetInstanceProcAddr(instance(), "vkGetOriginalPhysicalDeviceLimitsEXT");
if (!(fpvkSetPhysicalDeviceLimitsEXT) || !(fpvkGetOriginalPhysicalDeviceLimitsEXT)) {
printf(
"Can't find device_profile_api functions; make sure VK_LAYER_PATH is set correctly to where the validation layers "
"are built, the device profile layer should be in the same directory.\n");
return false;
}
return true;
}
bool VkLayerTest::LoadDeviceProfileLayer(PFN_vkSetPhysicalDeviceFeaturesEXT &fpvkSetPhysicalDeviceFeaturesEXT,
PFN_vkGetOriginalPhysicalDeviceFeaturesEXT &fpvkGetOriginalPhysicalDeviceFeaturesEXT) {
if (IsPlatformMockICD()) {
printf("Device Profile layer is for real GPU, if using MockICD with profiles, just adjust the profile json file instead\n");
return false;
}
// Load required functions
fpvkSetPhysicalDeviceFeaturesEXT =
(PFN_vkSetPhysicalDeviceFeaturesEXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceFeaturesEXT");
fpvkGetOriginalPhysicalDeviceFeaturesEXT =
(PFN_vkGetOriginalPhysicalDeviceFeaturesEXT)vk::GetInstanceProcAddr(instance(), "vkGetOriginalPhysicalDeviceFeaturesEXT");
if (!(fpvkSetPhysicalDeviceFeaturesEXT) || !(fpvkGetOriginalPhysicalDeviceFeaturesEXT)) {
printf(
"Can't find device_profile_api functions; make sure VK_LAYER_PATH is set correctly to where the validation layers "
"are built, the device profile layer should be in the same directory.\n");
return false;
}
return true;
}
bool VkLayerTest::LoadDeviceProfileLayer(PFN_VkSetPhysicalDeviceProperties2EXT &fpvkSetPhysicalDeviceProperties2EXT) {
if (IsPlatformMockICD()) {
printf("Device Profile layer is for real GPU, if using MockICD with profiles, just adjust the profile json file instead\n");
return false;
}
// Load required functions
fpvkSetPhysicalDeviceProperties2EXT =
(PFN_VkSetPhysicalDeviceProperties2EXT)vk::GetInstanceProcAddr(instance(), "vkSetPhysicalDeviceProperties2EXT");
if (!fpvkSetPhysicalDeviceProperties2EXT) {
printf(
"Can't find device_profile_api functions; make sure VK_LAYER_PATH is set correctly to where the validation layers "
"are built, the device profile layer should be in the same directory.\n");
return false;
}
return true;
}
void PrintAndroid(const char *c) {
(void)c;
#ifdef VK_USE_PLATFORM_ANDROID_KHR
__android_log_print(ANDROID_LOG_INFO, "VulkanLayerValidationTests", "%s", c);
#endif // VK_USE_PLATFORM_ANDROID_KHR
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR) && !defined(VVL_MOCK_ANDROID)
const char *appTag = "VulkanLayerValidationTests";
static bool initialized = false;
static bool active = false;
// Convert Intents to argv
// Ported from Hologram sample, only difference is flexible key
std::vector<std::string> get_args(android_app &app, const char *intent_extra_data_key) {
std::vector<std::string> args;
JavaVM &vm = *app.activity->vm;
JNIEnv *p_env;
if (vm.AttachCurrentThread(&p_env, nullptr) != JNI_OK) return args;
JNIEnv &env = *p_env;
jobject activity = app.activity->clazz;
jmethodID get_intent_method = env.GetMethodID(env.GetObjectClass(activity), "getIntent", "()Landroid/content/Intent;");
jobject intent = env.CallObjectMethod(activity, get_intent_method);
jmethodID get_string_extra_method =
env.GetMethodID(env.GetObjectClass(intent), "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
jvalue get_string_extra_args;
get_string_extra_args.l = env.NewStringUTF(intent_extra_data_key);
jstring extra_str = static_cast<jstring>(env.CallObjectMethodA(intent, get_string_extra_method, &get_string_extra_args));
std::string args_str;
if (extra_str) {
const char *extra_utf = env.GetStringUTFChars(extra_str, nullptr);
args_str = extra_utf;
env.ReleaseStringUTFChars(extra_str, extra_utf);
env.DeleteLocalRef(extra_str);
}
env.DeleteLocalRef(get_string_extra_args.l);
env.DeleteLocalRef(intent);
vm.DetachCurrentThread();
// split args_str
std::istringstream ss(args_str);
std::string arg;
while (std::getline(ss, arg, ' ')) {
if (!arg.empty()) args.emplace_back(arg);
}
return args;
}
void addFullTestCommentIfPresent(const ::testing::TestInfo &test_info, std::string &error_message) {
const char *const type_param = test_info.type_param();
const char *const value_param = test_info.value_param();
if (type_param != NULL || value_param != NULL) {
error_message.append(", where ");
if (type_param != NULL) {
error_message.append("TypeParam = ").append(type_param);
if (value_param != NULL) error_message.append(" and ");
}
if (value_param != NULL) {
error_message.append("GetParam() = ").append(value_param);
}
}
}
class LogcatPrinter : public ::testing::EmptyTestEventListener {
// Called before a test starts.
virtual void OnTestStart(const ::testing::TestInfo &test_info) {
__android_log_print(ANDROID_LOG_INFO, appTag, "[ RUN ] %s.%s", test_info.test_case_name(), test_info.name());
}
// Called after a failed assertion or a SUCCEED() invocation.
virtual void OnTestPartResult(const ::testing::TestPartResult &result) {
// If the test part succeeded, we don't need to do anything.
if (result.type() == ::testing::TestPartResult::kSuccess) return;
__android_log_print(ANDROID_LOG_INFO, appTag, "%s in %s:%d %s", result.failed() ? "*** Failure" : "Success",
result.file_name(), result.line_number(), result.summary());
}
// Called after a test ends.
virtual void OnTestEnd(const ::testing::TestInfo &info) {
std::string result;
if (info.result()->Passed()) {
result.append("[ OK ]");
} else if (info.result()->Skipped()) {
result.append("[ SKIPPED ]");
} else {
result.append("[ FAILED ]");
}
result.append(info.test_case_name()).append(".").append(info.name());
if (info.result()->Failed()) addFullTestCommentIfPresent(info, result);
if (::testing::GTEST_FLAG(print_time)) {
std::ostringstream os;
os << info.result()->elapsed_time();
result.append(" (").append(os.str()).append(" ms)");
}
__android_log_print(ANDROID_LOG_INFO, appTag, "%s", result.c_str());
};
};
static int32_t processInput(struct android_app *, AInputEvent *) { return 0; }
static void processCommand(struct android_app *app, int32_t cmd) {
switch (cmd) {
case APP_CMD_INIT_WINDOW: {
if (app->window) {
initialized = true;
VkTestFramework::window = app->window;
}
break;
}
case APP_CMD_GAINED_FOCUS: {
active = true;
break;
}
case APP_CMD_LOST_FOCUS: {
active = false;
break;
}
}
}
static void destroyActivity(struct android_app *app) {
ANativeActivity_finish(app->activity);
// Wait for APP_CMD_DESTROY
while (app->destroyRequested == 0) {
struct android_poll_source *source = nullptr;
int result = ALooper_pollOnce(-1, nullptr, nullptr, reinterpret_cast<void **>(&source));
if (result == ALOOPER_POLL_ERROR) {
__android_log_print(ANDROID_LOG_ERROR, appTag, "ALooper_pollOnce returned an error");
}
if ((result >= 0) && (source)) {
source->process(app, source);
} else {
break;
}
}
}
void android_main(struct android_app *app) {
app->onAppCmd = processCommand;
app->onInputEvent = processInput;
while (1) {
struct android_poll_source *source;
int result = ALooper_pollOnce(-1, nullptr, nullptr, reinterpret_cast<void **>(&source));
if (result == ALOOPER_POLL_ERROR) {
__android_log_print(ANDROID_LOG_ERROR, appTag, "ALooper_pollOnce returned an error");
VkTestFramework::Finish();
return;
}
if (result >= 0) {
if (source) {
source->process(app, source);
}
if (app->destroyRequested != 0) {
VkTestFramework::Finish();
return;
}
}
if (initialized && active) {
// Use the following key to send arguments to gtest, i.e.
// --es args "--gtest_filter=-VkLayerTest.foo"
const char key[] = "args";
std::vector<std::string> args = get_args(*app, key);
std::string filter = "";
if (args.size() > 0) {
__android_log_print(ANDROID_LOG_INFO, appTag, "Intent args = %s", args[0].c_str());
filter += args[0];
} else {
__android_log_print(ANDROID_LOG_INFO, appTag, "No Intent args detected");
}
int argc = 2;
char *argv[] = {(char *)"foo", (char *)filter.c_str()};
__android_log_print(ANDROID_LOG_DEBUG, appTag, "filter = %s", argv[1]);
// Route output to files until we can override the gtest output
freopen("/sdcard/Android/data/com.example.VulkanLayerValidationTests/files/out.txt", "w", stdout);
freopen("/sdcard/Android/data/com.example.VulkanLayerValidationTests/files/err.txt", "w", stderr);
::testing::InitGoogleTest(&argc, argv);
::testing::TestEventListeners &listeners = ::testing::UnitTest::GetInstance()->listeners();
listeners.Append(new LogcatPrinter);
VkTestFramework::InitArgs(&argc, argv);
::testing::AddGlobalTestEnvironment(new TestEnvironment);
int result = RUN_ALL_TESTS();
if (result != 0) {
__android_log_print(ANDROID_LOG_INFO, appTag, "==== Tests FAILED ====");
} else {
__android_log_print(ANDROID_LOG_INFO, appTag, "==== Tests PASSED ====");
}
VkTestFramework::Finish();
fclose(stdout);
fclose(stderr);
destroyActivity(app);
raise(SIGTERM);
return;
}
}
}
#endif
#if defined(_WIN32) && !defined(NDEBUG)
#include <crtdbg.h>
#endif
// Makes any failed assertion throw, allowing for graceful cleanup of resources instead of hard aborts
class ThrowListener : public testing::EmptyTestEventListener {
void OnTestPartResult(const testing::TestPartResult &result) override {
if (result.type() == testing::TestPartResult::kFatalFailure) {
// We need to make sure an exception wasn't already thrown so we dont throw another exception at the same time
std::exception_ptr ex = std::current_exception();
if (ex) {
return;
}
throw testing::AssertionException(result);
}
}
};
// Defining VVL_TESTS_USE_CUSTOM_TEST_FRAMEWORK allows downstream users
// to inject custom test framework changes. This includes the ability
// to override the main entry point of the test executable in order to
// add custom command line arguments and use a custom test environment
// class. This #ifndef thus makes sure that when the definition is
// present we do not include the default main entry point.
#ifndef VVL_TESTS_USE_CUSTOM_TEST_FRAMEWORK
int main(int argc, char **argv) {
int result;
#if defined(_WIN32)
// --gtest_break_on_failure disables gtest suppression of debug message boxes.
// If this flag is set, then limit the VVL test framework in how it configures CRT
// in order not to change expected gtest behavior (with regard to --gtest_break_on_failure).
bool break_on_failure = false;
for (int i = 1; i < argc; i++) {
if (std::string_view(argv[i]) == "--gtest_break_on_failure") {
break_on_failure = true;
break;
}
}
if (!break_on_failure) {
// Disable message box for: "Errors, unrecoverable problems, and issues that require immediate attention."
// This does not include asserts. GTest does similar configuration for asserts.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
}
#endif
::testing::InitGoogleTest(&argc, argv);
VkTestFramework::InitArgs(&argc, argv);
::testing::AddGlobalTestEnvironment(new TestEnvironment);
::testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener);
result = RUN_ALL_TESTS();
VkTestFramework::Finish();
return result;
}
#endif