Rendering with vulkan fails on 144hz Monitor
This is using latest winit from github master. On Linux, with X11 (no wayland at all) - window manager is KDE.
It appears due to some sort of changes I don't understand, I now receive ERROR_OUT_OF_DATE_KHR from vulkan when rendering a winit window to my 144hz monitor.
If I initialize my window on any 60hz monitor, it works fine.
Seemed I've narrowed it down some at least:
It appears a Resize event is always fired when starting your application.
However, it appears on a 144hz monitor this invalidates the swapchain, while on a 60hz monitor it does not.
Pardon my ignorance, but I know almost nothing about vulkan (and I don't have any hardware that supports it). I'm going to need some help in understanding how winit's Resize events interact with vulkan and how this issue can be addressed.
@goddessfreya Perhaps you can educate me or take over addressing this issue?
Honestly not sure how this would be a winit bug. VK_ERROR_OUT_OF_DATE_KHR is issued when the swapchain becomes out of date. The most common reason is that a window's size changes, although drivers are not required to always issue it. Some drivers only issue VK_SUBOPTIMAL_KHR. Some drivers do nothing. Drivers can issue VK_ERROR_OUT_OF_DATE_KHR and VK_SUBOPTIMAL_KHR any time they want, not just when the window resizes.
Now, I got no familiarity with vulkan, either, but if I was to throw a passing guess it would go something like this: WMs (or at least my WM, i3) issue a ffi::ConfigureNotify at the start to give the window a initial size. If you make your swapchain before that ffi::ConfigureNotify is issued I wouldn't be surprised if the driver makes the swapchain on a window of the wrong size or you pass in to vulkan the wrong size somewhere. I'm not at all familiar with Vulkan, but someone somewhere could be getting the size wrong sometimes :D.
Maybe the driver uses a completely different mechanism to acquire the window's size internally? If the timings are just right, on the 60hz monitor maybe it might end up using the right size but fail on the 144hz monitor? Idk. There simply isn't enough info here in this issue to figure out what's happening and this strikes me as highly timing dependent thing.
For example, this code here seams to be polling XCB for the new window size when presenting the surface (I think?) and if it notices a change it returns an error: https://gitlab.freedesktop.org/mesa/mesa/merge_requests/1341/diffs#481ae7c40d2dd384540fabbaca03d48fdc5c7b54_989_993 It wouldn't surprise me if other parts of the code do that too.
The gfx-rs examples seem to always pass in the same size, irrespective of resizes, see here, which leads me to believe that a swapchain can have a different size than the backing window's, but if the backing window's size changes the swapchain's invalidated.
Good chance you'll have to build mesa w/debug symbols and start inserting printfs everywhere to figure out what's up. I got a collection of scripts for building mesa-git and it's dependencies for archlinux. I've talked about them here if interested.
Either way, as I mentioned, drivers can issue VK_ERROR_OUT_OF_DATE_KHR and VK_SUBOPTIMAL_KHR any time they want. Your program should be recreating the swapchain when the former (and optionally when the latter) is issued. As far as I can tell, most drivers tend to avoid issuing VK_ERROR_OUT_OF_DATE_KHR on the acquire, instead preferring to return VK_SUBOPTIMAL_KHR, but I can make no guarantees :D.
@goddessfreya
After investigating further, I can confirm winit is invalidating the swapchain immediately on start.
Yes, I know I should be handling swapchain invalidation, and it can technically occur at any time, however - right now, on a 144hz monitor, for some reason winit invalidates it immediately which although "allowed", is not consistent or ideal.
Using the gfx-hal default quad example exhibits the same problem on this display. The majority of vulkan examples do not handle swapchain recreation, as they arn't necessary for general example usage. A swapchain should not become immediately invalid on creation (Although you are correct, technically it can, but this is pretty bad practice on winit part to just brush it off).
So before we dive into drivers fault, or WM fault, or my fault: this behave began with winit 0.20 and did not exist prior, and winit somehow invalidates the swapchain on this monitor immediately on initialization
I don't know how to make it any more clear that this isn't a "I'm dumb" or not handling my events correctly issue; Although technically correct, winit should not be immediately invalidating the swapchain on creation.
I don't know how to make it any more clear that this isn't a "I'm dumb" or not handling my events correctly issue;
Apologies, but I'm not calling you dumb or anything. Didn't mean for it to come off that way.
So before we dive into drivers fault, or WM fault, or my fault: this behave began with winit 0.20 and did not exist prior, and winit somehow invalidates the swapchain on this monitor immediately on initialization
Yes, I know I should be handling swapchain invalidation, and it can technically occur at any time, however - right now, on a 144hz monitor, for some reason winit invalidates it immediately which although "allowed", is not consistent or ideal.
As I mentioned, there simply isn't enough info here in this issue to figure out what's happening and this issue strikes me as highly timing + driver dependent thing. It's perfectly possible that winit is triggering some edge case in the underlying drivers/WM/whatever, or maybe winit messed up, who knows?
If versions before winit 0.20 work then I think the first step is to git-bisect winit so we can track down which set of changes broke it. I can't do this myself as I don't have a 144hz monitor.
I apologize if I cam across snippy @goddessfreya. It was a bad day, so I sincerely apologize for coming across that way.
After some further investigation, heres what I have determined:
:55 PM] jaynus: @Osspial So I've narrowed it down a fair bit (sorry btw if I seemed snippy in the ticket). It appears that I am running into a very strange scenario (or I'm just dumb).
[4:55 PM] jaynus: the surface is getting invalidated on that monitor due to somehow the extent limitations changing with the return value from vkGetPhysicalDeviceSurfaceCapabilitiesKHR
[4:56 PM] jaynus: My initial winit window and vkGetPhysicalDeviceSurfaceCapabilitiesKHR both are 1280x1024, however on the swapchain invalidation, I am receiving vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): currentExtent = (1280,1020), minImageExtent = (1280,1020)
[4:57 PM] jaynus: It appears winit is resizing my window by 4 pixels
Some ghetto logs here:
caps = SurfaceCapabilitiesKHR { min_image_count: 2, max_image_count: 8, current_extent: Extent2D { width: 1280, height: 1024 }, min_image_extent: Extent2D { width: 1280, height: 1024 }, max_image_extent: Extent2D { width: 1280, height: 1024 }, max_image_array_layers: 1, supported_transforms: IDENTITY, current_transform: IDENTITY, supported_composite_alpha: OPAQUE, supported_usage_flags: TRANSFER_SRC | TRANSFER_DST | SAMPLED | STORAGE | COLOR_ATTACHMENT | INPUT_ATTACHMENT }
select_surface: Size = winit::PhysicalSize { width: 1280, height: 1024 }
Returning resolution: Extent2D { width: 1280, height: 1024 }
...
recreate_swapchain: Size = winit::PhysicalSize { width: 1280, height: 1020 }
caps = SurfaceCapabilitiesKHR { min_image_count: 2, max_image_count: 8, current_extent: Extent2D { width: 1280, height: 1024 }, min_image_extent: Extent2D { width: 1280, height: 1024 }, max_image_extent: Extent2D { width: 1280, height: 1024 }, max_image_array_layers: 1, supported_transforms: IDENTITY, current_transform: IDENTITY, supported_composite_alpha: OPAQUE, supported_usage_flags: TRANSFER_SRC | TRANSFER_DST | SAMPLED | STORAGE | COLOR_ATTACHMENT | INPUT_ATTACHMENT }
select_surface: Size = PhysicalSize { width: 1280, height: 1020 }
Returning resolution: Extent2D { width: 1280, height: 1024 }
" [ VUID-VkSwapchainCreateInfoKHR-imageExtent-01274 ] Object: 0x55b4f9c57780 (Type = 3) | vkCreateSwapchainKHR() called with imageExtent = (1280,1024), which is outside the bounds returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): currentExtent = (1280,1020), minImageExtent = (1280,1020), maxImageExtent = (1280,1020). The Vulkan spec states: imageExtent must be between minImageExtent and maxImageExtent, inclusive, where minImageExtent and maxImageExtent are members of the VkSurfaceCapabilitiesKHR structure returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the surface (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkSwapchainCreateInfoKHR-imageExtent-01274)"
In a nutshell, what this shows is that:
- On initial creation of my swapchain and window, I specify a size of 1280x1024, and both my winit window inner_size and my
vkGetPhysicalDeviceSurfaceCapabilitiesKHRcalls match this size. - However, the swapchain is almost immediately invalidated, and it winit::init_size is resized to 1280x1020
It appears for some reason, my window is getting immediately resized by winit by 4 pixels on the Y axis, triggering an immediate resize event and swapchain invalidation.
Can you log all calls to set_inner_size_physical and try to track down who's setting the size to be 4 pixels off. If you can't find anything, can you try the following:
1- Install a copy of xscope via your distro's package manager. Here's a PKGBUILD a fixed for arch users.
2- In one terminal, run xscope -v4 -d0 -o0 -i1 > xscopelog
3- In another,
$ xhost +local:
$ export DISPLAY=:1.0
$ <your program>
4- Upload the xscopelog. We're hunting for ConfigureWindow, Expose, and ConfigureNotify events, and the values 500, 1280, 1024, 400, 3fc.
I've tested on 120Hz android display and 144Hz computer display (x11). Did not find such problem.