VK_EXT_swapchain_maintenance1 not observing VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT

I’m trying to make use of VK_EXT_swapchain_maintenance1 by setting the swapchain present scaling mode to VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT

The scaling mode is reported to be available, however it appears to have no effect. I continue to observe the swapchain being scaled as if VK_PRESENT_SCALING_STRETCH_BIT_EXT were set

Possible driver issue?

Windows 10
nVidia Geforce GTX 970
driverVersion 552.12.0.0
Vulkan API version 1.3.277

My usage of this extension appears to work for other vendors (MoltenVk) but that doesn’t mean I’m not doing something wrong

FWIW, the linux driver (550.67) reports all available present scaling modes for XLIB and XCB, though it reports none for Wayland.

Setting the scaling mode on Linux for XLIB and XCB also appears to have no effect, but would appear to be defaulting to ONE_TO_ONE (while the Windows driver seems to be defaulting to STRETCH)

Further issues…
For XLIB and XCB it seems vkAcquireNextImage() never returns VK_SUBOPTIMAL_KHR, even when VK_EXT_swapchain_maintenance1 is enabled

When the surface dimensions do not agree with the swapchain, it will instead always return VK_OUT_OF_DATE_KHR

Ultimately this enforces that the swapchain extent must always align with the window dimensions, which I would think defeats the purpose of these scaling features

Hi Tim, would you be willing to share the code you used to enable the extension + feature, or perhaps even a sample app?

Also, it would be worthwhile to confirm no validation errors are being thrown.

Thanks Dave, I’m sure I can work up a minimal repro however I’m currently travelling and won’t be back at my desktop for a few weeks yet.

Hi Tim,

Regarding the issues reported on Linux, it is expected that no scaling modes are reported on Wayland since scaling does not apply there.

For XCB and Xlib, I verified locally and the extension appears to work as expected.

Does your application manually handle native resize events, by any chance?

Are you able to run a recent Vulkan CTS build and check if the “dEQP-VK.wsi.xcb.maintenance1.scaling.*” tests are passing for you?

Regarding the issue observed on Windows (SCALING_ONE_TO_ONE not being observed)…

I’m able to repro this with the following patch applied to the swapchain_recreation example from the Vulkan samples repo (GitHub - KhronosGroup/Vulkan-Samples: One stop solution for all Vulkan samples)

I’ve not observed any validation errors, in either my personal project or with the Vulkan sample repro below

diff --git a/samples/api/swapchain_recreation/swapchain_recreation.cpp b/samples/api/swapchain_recreation/swapchain_recreation.cpp
index 03b6560..f145696 100644
--- a/samples/api/swapchain_recreation/swapchain_recreation.cpp
+++ b/samples/api/swapchain_recreation/swapchain_recreation.cpp
@@ -298,6 +298,21 @@ void SwapchainRecreation::init_swapchain()
                }
        }

+
+       VkSwapchainPresentScalingCreateInfoEXT scalingInfo{ VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT };
+       scalingInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT;
+       scalingInfo.pNext = nullptr;
+       scalingInfo.scalingBehavior = VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT;
+       scalingInfo.presentGravityX = VK_PRESENT_GRAVITY_MIN_BIT_EXT;
+       scalingInfo.presentGravityY = VK_PRESENT_GRAVITY_MIN_BIT_EXT;
+
+
+       const void** nextPnext = &(info.pNext);
+       while (*nextPnext != nullptr) {
+               nextPnext = const_cast<const void**>(&((const VkSwapchainCreateInfoKHR*)*nextPnext)->pNext);
+       }
+       *nextPnext = (void*)&scalingInfo;
+
        LOGI("Creating new swapchain");
        VK_CHECK(vkCreateSwapchainKHR(get_device_handle(), &info, nullptr, &swapchain));
        ++swapchain_creation_count;

With this patch, I’d expect that during resize events the display should not be scaled - but I continue to observe stretch behaviour.

Edit: tagging @DaveNV

Regarding the Linux issue…
I am not handling native resize requests (eg: ResizeRequest or RESIZE_REDIRECT event masks). All frame resizes are handled via ConfigureNotify events

Unfortunately I am unable to get a clean build of the Vulkan CTS at this stage

@dleone Here are the results of the CTS tests as requested
dEQP-VK.wsi.xcb.maintenance1.scaling.log (418.5 KB)

These were taken from CTS version 1.3.8.3

Overview:

Test run totals:
  Passed:        100/2076 (4.8%)
  Failed:        608/2076 (29.3%)
  Not supported: 1368/2076 (65.9%)
  Warnings:      0/2076 (0.0%)
  Waived:        0/2076 (0.0%)

Note that I have since updated to nVidia driver v550.78

Attached inxi -Fzx
inxi.txt (3.4 KB)

I don’t know why I hadn’t considered this before, but it seems these issues on Linux are limited to XWayland

Running the conformance tests on native X reveals the following results:

Test run totals:
  Passed:        1049/2076 (50.5%)
  Failed:        1/2076 (0.0%)
  Not supported: 1026/2076 (49.4%)
  Warnings:      0/2076 (0.0%)
  Waived:        0/2076 (0.0%)

The issue with only applying this patch, is that the swapchain_recreation test is still handling resize events, and explicitly triggering a swapchain recreation.

If you return false in bool SwapchainRecreation::recreate_swapchain() { ... } when you enable a scaling-mode, you should see the extension working correctly.

Having recreate_swapchain() always return false doesn’t seem to change this behaviour

Here’s what it looks like for me
nvidia

Yeah this is a bit of a quirk with this extension. What’s happening is that as you’re resizing, Presents are paused. So during the resize, the contents of the window are undefined. In your current setup, we see the contents stretch, but there are other modes where you’ll see the contents remain unchanged when stretched, and wiped to black on shrink + enlarge.

However, once the next Present hits, you can see it draw into the original position and size (ie. one-to-one) with top-left gravity. The effect is easier to see if you were to fill the screen with a solid colour, because the borders created by the scaling modes would be more clear to see.

I can confirm I do not see this behavior with this sample on Linux, even with your suggestion to disable swapchain recreation, the scaling remains ONE_TO_ONE (though, that seems to be default behaviour on Linux with or without this extension)

It was my expectation that this extension is intended to define scaling behaviour outside of swapchain recreation, and I guess I expected that to also be the case wether or not presents are occuring

The issue I have in my personal project is that I am explicitly handling presents during the window resize (using a WM_TIMER, due to Windows modal behaviour with resize operations), but that introduces observed jitter (since scaling continues to be observed between presents)

Here is an example of that behaviour, using a static swapchain:
resize_jitter

Evidently, each present is fighting against the implicit scaling behaviour

The question I have at this point… Is that scaling a function of the Windows compositor, or the Vulkan driver?

In your current setup, we see the contents stretch, but there are other modes where you’ll see the contents remain unchanged when stretched

Shouldn’t VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT indicate a mode where the contents remains unchanged when stretched, between presents?

This is the method I’ve used in MoltenVk (where I observed similar issues) at it had appeared to have the desired effect,

An additional data point…
Running the same code on my AMD GPU (same machine) does not exhibit this jitter/scaling between presents.

Granted, the AMD device does not support the swapchain_maintenance extension, but I think it demonstrates this is not a compositor issue (or that the default scaling behaviour on this device remains ONE_TO_ONE)

Ignore that… this scenario on the AMD device doesn’t suggest anything (without the extension, it’s obviously having to recreate the swapchain every frame)

The question I have at this point… Is that scaling a function of the Windows compositor, or the Vulkan driver?

Well, it’s a bit complicated, since it depends on a number of factors, but the short answer is: it can be both.

Shouldn’t VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT indicate a mode where the contents remains unchanged when stretched, between presents?

I think that’s a reasonable expectation. And the good news is that I believe I’ve found a way to make that happen, which I’ll submit to be included in a subsequent driver release.

@DaveNV very much appreciated, thank you