Let's pick up where we left off in the first article. An example of a Qt Quick application running on Linux on top of OpenGL and Vulkan was considered. We also saw the Vulkan frame capture in RenderDoc, which is not only an invaluable tool when developing Qt, but can also be useful to anyone who wants to dig deeper and better understand how Qt Quick renders a frame (or troubleshoot issues in application rendering). Now in this article, let's focus on what Qt 5.14 offers for macOS and Windows.
Not surprisingly, running the qt5-cinematic-experience demo with QSG_RHI=1 (and QSG_INFO=1) on macOS 10.13 or 10.14 results in:
qt.scenegraph.general: Using QRhi with backend Metal graphics API debug/validation layers: 0 QRhi profiling and debug markers: 0 qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.67 ms qt.rhi.general: Metal device: Intel(R) HD Graphics 615 qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no. qt.scenegraph.general: rhi texture atlas dimensions: 4096x2048 qt.rhi.general: got CAMetalLayer, size 2560x1440
In line with Qt 6's goal to default to the core, most supported platform graphics API (while still allowing applications to set their own preferences if they so choose), by enabling a QRhi-based rendering path in Qt Quick on macOS, Metal is the default.
Just as the Vulkan-based rendering path builds on the Vulkan sample and surface support introduced to the QtGui module, QPA, and some platform plugins, the Metal QRhi backend relies on the Metal support for the cocoa platform plugin that appeared around Qt 5.12. QWindow with QSurface::MetalSurface gets an NSView backed by CAMetalLayer under the hood. This is exactly what the Metal QRhi backend needs (this all works because QQuickWindow gets the appropriate QSurface::SurfaceType set whenever a QRhi-based code path is active).
One important feature here is the ability to work with Qt Quick's multi-threaded render loop. This is very welcome because due to multithreading issues with some NSOpenGLContext and related APIs in macOS 10.14, Qt disables multithreaded looping with OpenGL on macOS, defaulting to the base loop instead. This results in somewhat reduced animation smoothness in Qt Quick. As for Metal, developers don't (according to current knowledge) have any problem setting threads, so one can once again use the default multi-threaded render loop (the default selection for the render loop can be changed by setting the QSG_RENDER_LOOP environment variable, this variable is supported also in combination with QSG_RHI)
RenderDoc has already been mentioned as a tool for debugging Qt application frame rendering when running on OpenGL, Vulkan, or Direct3D. For Metal, you can use XCode and the built-in GPU frame capture.
One useful way to quickly open a Qt project in XCode ready for debugging is to run make xcodeproj && open .xcodeproj (or qmake -spec macx-xcode* if qmake has not already been started) from a terminal. Pressing Cmd-R immediately launches the debugger. Developers also often use this during Qt development. In case Metal GPU capture is not available, even if Qt Quick is configured to render via Metal, select Product -> Scheme -> Edit scheme ... and change GPU Frame Capture to Metal.
When you run a debug build of your application, XCode will have Metal checking enabled, which means that XCode will (hopefully) issue a useful warning and abort program execution if the Metal API is used in any way that is not correct. Unlike other platforms, Metal API validation cannot be enabled unless debugging through XCode (so, unlike Vulkan and D3D, setting QSG_RHI_DEBUG_LAYER will have no effect).
What about iOS or tvOS?
Well, at the time of writing, there is no Metal related plugin in the platform plugin, which also means that the Metal QRhi backend has not yet been tested on those platforms. This is why the Qt 5.14 New Features page only mentions macOS in the Qt Quick section. Support is expected to be added in the near future, possibly in version 5.15.
How about Vulkan via MoltenVK?
As mentioned in Part 1, requesting to render through Vulkan and then using MoltenVK to translate SPIR-V API calls and shaders at runtime into Metal and Metal Shading Language is also possible. This requires building Qt with Vulkan support, which cannot be said about ready-made solutions on Apple platforms. The key is to pass -I and to set up and make sure libraries can be found at runtime, perhaps by setting QT_VULKAN_LIB.
It is important to note that this approach will only be supported on a best effort basis. The primary supported rendering on Apple platforms will be via Metal.
Running the demo application with QSG_RHI_BACKEND=vulkan (using an appropriately configured Qt 5.14 build) yields:
qt.scenegraph.general: Using QRhi with backend Vulkan graphics API debug/validation layers: 0 QRhi profiling and debug markers: 0 qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.67 ms qt.rhi.general: Physical device 0: 'Intel(R) UHD Graphics 630' 0.2.1835 (api 1.0.92 vendor 0x8086 device 0x3E9B type 1) qt.rhi.general: using this physical device qt.rhi.general: queue family 0: flags=0x7 count=1 qt.rhi.general: 17 device extensions available qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no. qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024 qt.rhi.general: Creating new swapchain of 2 buffers, size 1280x720, presentation mode 2
It's worth noting that there were issues running this on macOS 10.13, blocking and crashing when using a multi-threaded render loop. Upgrading to the newer (1.0.121) Vulkan SDK (including MoltenVK) resulted in a new set of issues and an inability to launch the application.
Windows is the platform with the most features. Of the 4 main QRhi backends (Vulkan, Metal, D3D11, OpenGL), at least 3 can be used on Windows: Direct3D 11, Vulkan and OpenGL.
This begs the obvious question: why only 3? Shouldn't it be 4 with Direct3D 12?
There is currently no D3D12 backend. This may change later as it is in the plans but not at this time. It's worth noting that the experimental D3D12 backend for Qt Quick added in Qt 5.8 is now architecturally deprecated (because the developers handle the problem of multiple graphics APIs in a completely new way), and is due to be removed in Qt 6.
On Windows, by default setting QSG_RHI=1 uses Direct3D 11. As usual, override with QSG_RHI_BACKEND if Vulkan or OpenGL is required.
qt.scenegraph.general: Using QRhi with backend D3D11 graphics API debug/validation layers: 0 QRhi profiling and debug markers: 0 qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.67 ms qt.rhi.general: DXGI 1.2 = true, FLIP_DISCARD swapchain supported = true qt.rhi.general: Adapter 0: 'NVIDIA GeForce RTX 2060' (flags 0x0) qt.rhi.general: using this adapter qt.rhi.general: Adapter 1: 'Microsoft Basic Render Driver' (flags 0x2) qt.scenegraph.general: MSAA sample count for the swapchain is 1 qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024
It's worth noting that you need Direct3D 11.1, not 11.0. This is mainly because VSSetConstantBuffers1 is needed
(and related PS and CS variants). This shouldn't be a problem if someone doesn't want to run plain Windows 7 without a platform upgrade. Speaking of Windows 7, it's important to note that D3D11 based rendering does not currently work correctly on Windows 7 with Qt 5.14, so the 5.14 new features page only mentions Windows 10. This will be fixed later (but only if Windows 7 remains a supported platform). for Qt 6).
To enable the Direct3D debug layer, set the QSG_RHI_DEBUG_LAYER environment variable to 1. This also works for Vulkan if inspection layers are available (for example, from the installed Vulkan SDK). Qt conveniently routes messages to debug output (as if they were output via qDebug).
Vulkan and OpenGL should work as expected on Windows.
As with Vulkan and Metal, the OpenGL QRhi backend relies on some (but not all) of the existing OpenGL activators and platform tracking, such as QOpenGLContext, QOpenGLFunctions and in the Windows platform plugin (WGL, EGL). Therefore, everything related to Qt Quick applications running directly in OpenGL applies here as well (desktop (desktop) vs. ANGLE vs. software, environment variables like QT_OPENGL).
Speaking of ANGLE, it is expected that it may be removed as a direct dependency in Qt 6. This needs further study to fully ensure that support for special use cases is not lost, but for now the plan is to have it based on QRhi rendering path with support for Direct3D 11, OpenGL (via WGL itself) and Vulkan will suffice for Qt 6 applications on Windows.
The third part will look at what QRhi is and how shaders work.