Skip to content

Commit

Permalink
Emulate VK_EXT_surface_maintenance1 if not supported by a driver
Browse files Browse the repository at this point in the history
The VK_EXT_surface_maintance1 extension is an instance level extension,
which means the loader will advertise support for it when at least one
driver supports it. However, there is presently no way for an application
to know which VkPhysicalDevice's will fill out the structs the extension
defines and which VkPhysicalDevice's will ignore the structs.

Add support for testing the surface_maintenance1 implementation - making
sure that a driver which supports the extension isn't affected and
correctly filling out the structs for drivers which do and do not support
vkGetPhysicalDeviceSurfaceCapabilities2KHR as well.
  • Loading branch information
charles-lunarg committed Sep 27, 2024
1 parent 4eaa6df commit 2fbbb63
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 21 deletions.
16 changes: 10 additions & 6 deletions loader/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -5468,14 +5468,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI
#endif // LOADER_ENABLE_LINUX_SORT

// Determine if vkGetPhysicalDeviceProperties2 is available to this Instance
// Also determine if VK_EXT_surface_maintenance1 is available on the ICD
if (icd_term->scanned_icd->api_version >= VK_API_VERSION_1_1) {
icd_term->supports_get_dev_prop_2 = true;
} else {
for (uint32_t j = 0; j < icd_create_info.enabledExtensionCount; j++) {
if (!strcmp(filtered_extension_names[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
icd_term->supports_get_dev_prop_2 = true;
break;
}
}
for (uint32_t j = 0; j < icd_create_info.enabledExtensionCount; j++) {
if (!strcmp(filtered_extension_names[j], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
icd_term->supports_get_dev_prop_2 = true;
continue;
}
if (!strcmp(filtered_extension_names[j], VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME)) {
icd_term->supports_ext_surface_maintenance_1 = true;
continue;
}
}

Expand Down
1 change: 1 addition & 0 deletions loader/loader_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ struct loader_icd_term {

PFN_PhysDevExt phys_dev_ext[MAX_NUM_UNKNOWN_EXTS];
bool supports_get_dev_prop_2;
bool supports_ext_surface_maintenance_1;

uint32_t physical_device_count;

Expand Down
89 changes: 74 additions & 15 deletions loader/wsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,65 @@ vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, cons
return disp->GetPhysicalDeviceSurfaceCapabilities2KHR(unwrapped_phys_dev, pSurfaceInfo, pSurfaceCapabilities);
}

void emulate_VK_EXT_surface_maintenance1(struct loader_icd_term *icd_term, const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
VkSurfaceCapabilities2KHR *pSurfaceCapabilities) {
// Because VK_EXT_surface_maintenance1 is an instance extension, applications will use it to query info on drivers which do
// not support the extension. Thus we need to emulate the driver filling out the structs in that case.
if (!icd_term->supports_ext_surface_maintenance_1) {
VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAX_ENUM_KHR;
const void *void_pNext = pSurfaceInfo->pNext;
while (void_pNext) {
VkBaseOutStructure out_structure = {0};
memcpy(&out_structure, void_pNext, sizeof(VkBaseOutStructure));
if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT) {
VkSurfacePresentModeEXT *surface_present_mode = (VkSurfacePresentModeEXT *)void_pNext;
present_mode = surface_present_mode->presentMode;
} else {
loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in "
"pSurfaceInfo->pNext - this struct will be ignored");
}
void_pNext = out_structure.pNext;
}
// If no VkSurfacePresentModeEXT was present, return
if (present_mode == VK_PRESENT_MODE_MAX_ENUM_KHR) {
return;
}

void_pNext = pSurfaceCapabilities->pNext;
while (void_pNext) {
VkBaseOutStructure out_structure = {0};
memcpy(&out_structure, void_pNext, sizeof(VkBaseOutStructure));
if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT) {
VkSurfacePresentModeCompatibilityEXT *surface_present_mode_compatibility =
(VkSurfacePresentModeCompatibilityEXT *)void_pNext;
if (surface_present_mode_compatibility->pPresentModes) {
surface_present_mode_compatibility->pPresentModes[0] = present_mode;
}
surface_present_mode_compatibility->presentModeCount = 1;

} else if (out_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT) {
// Because there is no way to fill out the information faithfully, set scaled max/min image extent to the
// surface capabilities max/min extent and the rest to zero.
VkSurfacePresentScalingCapabilitiesEXT *surface_present_scaling_capabilities =
(VkSurfacePresentScalingCapabilitiesEXT *)void_pNext;
surface_present_scaling_capabilities->supportedPresentScaling = 0;
surface_present_scaling_capabilities->supportedPresentGravityX = 0;
surface_present_scaling_capabilities->supportedPresentGravityY = 0;
surface_present_scaling_capabilities->maxScaledImageExtent =
pSurfaceCapabilities->surfaceCapabilities.maxImageExtent;
surface_present_scaling_capabilities->minScaledImageExtent =
pSurfaceCapabilities->surfaceCapabilities.minImageExtent;
} else {
loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in "
"pSurfaceCapabilities->pNext - this struct will be ignored");
}
void_pNext = out_structure.pNext;
}
}
}

VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2KHR(
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
VkSurfaceCapabilities2KHR *pSurfaceCapabilities) {
Expand All @@ -2465,31 +2524,35 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2K
pNext = (VkBaseOutStructure *)pNext->pNext;
}

VkResult res = VK_SUCCESS;

// Pass the call to the driver, possibly unwrapping the ICD surface
if (NULL != icd_term->surface_list.list &&
icd_term->surface_list.capacity > icd_surface->surface_index * sizeof(VkSurfaceKHR) &&
icd_term->surface_list.list[icd_surface->surface_index]) {
VkPhysicalDeviceSurfaceInfo2KHR info_copy = *pSurfaceInfo;
info_copy.surface = icd_term->surface_list.list[icd_surface->surface_index];
return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, &info_copy,
pSurfaceCapabilities);
res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, &info_copy,
pSurfaceCapabilities);
} else {
return icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, pSurfaceInfo,
pSurfaceCapabilities);
res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev_term->phys_dev, pSurfaceInfo,
pSurfaceCapabilities);
}

// Because VK_EXT_surface_maintenance1 is an instance extension, applications will use it to query info on drivers which do
// not support the extension. Thus we need to emulate the driver filling out the structs in that case.
if (!icd_term->supports_ext_surface_maintenance_1) {
emulate_VK_EXT_surface_maintenance1(icd_term, pSurfaceInfo, pSurfaceCapabilities);
}

return res;
} else {
// Emulate the call
loader_log(icd_term->this_instance, VULKAN_LOADER_INFO_BIT, 0,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulating call in ICD \"%s\" using "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
icd_term->scanned_icd->lib_name);

if (pSurfaceInfo->pNext != NULL) {
loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in "
"pSurfaceInfo->pNext - this struct will be ignored");
}

// Write to the VkSurfaceCapabilities2KHR struct
VkSurfaceKHR surface = pSurfaceInfo->surface;
if (NULL != icd_term->surface_list.list &&
Expand All @@ -2508,11 +2571,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceSurfaceCapabilities2K
VkResult res = icd_term->dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev_term->phys_dev, surface,
&pSurfaceCapabilities->surfaceCapabilities);

if (pSurfaceCapabilities->pNext != NULL) {
loader_log(icd_term->this_instance, VULKAN_LOADER_WARN_BIT, 0,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR: Emulation found unrecognized structure type in "
"pSurfaceCapabilities->pNext - this struct will be ignored");
}
emulate_VK_EXT_surface_maintenance1(icd_term, pSurfaceInfo, pSurfaceCapabilities);
return res;
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/framework/icd/physical_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ struct PhysicalDevice {
BUILDER_VALUE(PhysicalDevice, VkSurfaceCapabilitiesKHR, surface_capabilities, {})
BUILDER_VECTOR(PhysicalDevice, VkSurfaceFormatKHR, surface_formats, surface_format)
BUILDER_VECTOR(PhysicalDevice, VkPresentModeKHR, surface_present_modes, surface_present_mode)
BUILDER_VALUE(PhysicalDevice, VkSurfacePresentScalingCapabilitiesEXT, surface_present_scaling_capabilities, {})
// No good way to make this a builder value. Each std::vector<VkPresentModeKHR> corresponds to each surface_present_modes
// element
std::vector<std::vector<VkPresentModeKHR>> surface_present_mode_compatibility{};

BUILDER_VECTOR(PhysicalDevice, VkDisplayPropertiesKHR, display_properties, display_properties)
BUILDER_VECTOR(PhysicalDevice, VkDisplayPlanePropertiesKHR, display_plane_properties, display_plane_properties)
Expand Down
56 changes: 56 additions & 0 deletions tests/framework/icd/test_icd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,62 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceCapabilities2KHR(V
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
if (nullptr != pSurfaceInfo && nullptr != pSurfaceCapabilities) {
if (IsInstanceExtensionSupported("VK_EXT_surface_maintenance1") &&
IsInstanceExtensionEnabled("VK_EXT_surface_maintenance1")) {
auto& phys_dev = icd.GetPhysDevice(physicalDevice);
void* pNext = pSurfaceCapabilities->pNext;
while (pNext) {
VkBaseOutStructure pNext_base_structure{};
std::memcpy(&pNext_base_structure, pNext, sizeof(VkBaseInStructure));
if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT) {
// First must find the present mode that is being queried
VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAX_ENUM_KHR;
const void* pSurfaceInfo_pNext = pSurfaceInfo->pNext;
while (pSurfaceInfo_pNext) {
VkBaseInStructure pSurfaceInfo_pNext_base_structure{};
std::memcpy(&pSurfaceInfo_pNext_base_structure, pSurfaceInfo_pNext, sizeof(VkBaseInStructure));
if (pSurfaceInfo_pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT) {
present_mode = reinterpret_cast<const VkSurfacePresentModeEXT*>(pSurfaceInfo_pNext)->presentMode;
}
pSurfaceInfo_pNext = pSurfaceInfo_pNext_base_structure.pNext;
}

VkSurfacePresentModeCompatibilityEXT* present_mode_compatibility =
reinterpret_cast<VkSurfacePresentModeCompatibilityEXT*>(pNext);
if (present_mode == VK_PRESENT_MODE_MAX_ENUM_KHR) {
present_mode_compatibility->presentModeCount = 0;
} else {
auto it =
std::find(phys_dev.surface_present_modes.begin(), phys_dev.surface_present_modes.end(), present_mode);
if (it != phys_dev.surface_present_modes.end()) {
size_t index = it - phys_dev.surface_present_modes.begin();
present_mode_compatibility->presentModeCount =
static_cast<uint32_t>(phys_dev.surface_present_mode_compatibility[index].size());
if (present_mode_compatibility->pPresentModes) {
for (size_t i = 0; i < phys_dev.surface_present_mode_compatibility[index].size(); i++) {
present_mode_compatibility->pPresentModes[i] =
phys_dev.surface_present_mode_compatibility[index][i];
}
}
}
}
} else if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT) {
VkSurfacePresentScalingCapabilitiesEXT* present_scaling_capabilities =
reinterpret_cast<VkSurfacePresentScalingCapabilitiesEXT*>(pNext);
present_scaling_capabilities->minScaledImageExtent =
phys_dev.surface_present_scaling_capabilities.minScaledImageExtent;
present_scaling_capabilities->maxScaledImageExtent =
phys_dev.surface_present_scaling_capabilities.maxScaledImageExtent;
present_scaling_capabilities->supportedPresentScaling =
phys_dev.surface_present_scaling_capabilities.supportedPresentScaling;
present_scaling_capabilities->supportedPresentGravityX =
phys_dev.surface_present_scaling_capabilities.supportedPresentGravityX;
present_scaling_capabilities->supportedPresentGravityY =
phys_dev.surface_present_scaling_capabilities.supportedPresentGravityY;
}
pNext = pNext_base_structure.pNext;
}
}
return test_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, pSurfaceInfo->surface,
&pSurfaceCapabilities->surfaceCapabilities);
}
Expand Down
12 changes: 12 additions & 0 deletions tests/framework/test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,15 @@ inline bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCa
props1.currentTransform == props2.currentTransform && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha &&
props1.supportedUsageFlags == props2.supportedUsageFlags;
}
inline bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2) {
return caps1.supportedPresentScaling == caps2.supportedPresentScaling &&
caps1.supportedPresentGravityX == caps2.supportedPresentGravityX &&
caps1.supportedPresentGravityY == caps2.supportedPresentGravityY &&
caps1.minScaledImageExtent.width == caps2.minScaledImageExtent.width &&
caps1.minScaledImageExtent.height == caps2.minScaledImageExtent.height &&
caps1.maxScaledImageExtent.width == caps2.maxScaledImageExtent.width &&
caps1.maxScaledImageExtent.height == caps2.maxScaledImageExtent.height;
}
inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2) {
return format1.format == format2.format && format1.colorSpace == format2.colorSpace;
}
Expand Down Expand Up @@ -846,6 +855,9 @@ inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDispla
inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2) {
return props1 == props2.displayPlaneProperties;
}
inline bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2) {
return ext1.height == ext2.height && ext1.width == ext2.width;
}
// Allow comparison of vectors of different types as long as their elements are comparable (just has to make sure to only apply when
// T != U)
template <typename T, typename U, typename = std::enable_if_t<!std::is_same_v<T, U>>>
Expand Down
Loading

0 comments on commit 2fbbb63

Please sign in to comment.