summaryrefslogtreecommitdiffstats
path: root/src/gui/vulkan/qvulkanwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/vulkan/qvulkanwindow.cpp')
-rw-r--r--src/gui/vulkan/qvulkanwindow.cpp143
1 files changed, 111 insertions, 32 deletions
diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp
index 29d6d0d5bd..00a5c5f869 100644
--- a/src/gui/vulkan/qvulkanwindow.cpp
+++ b/src/gui/vulkan/qvulkanwindow.cpp
@@ -178,12 +178,11 @@ Q_DECLARE_LOGGING_CATEGORY(lcGuiVk)
As an exception to this rule, \c robustBufferAccess is never enabled. Use the
callback mechanism described below, if enabling that feature is desired.
- Just enabling the 1.0 core features is not always sufficient, and therefore
- full control over the VkPhysicalDeviceFeatures used for device creation is
- possible too by registering a callback function with
+ This is not always desirable, and may be insufficient with Vulkan 1.1 and
+ higher. Therefore, full control over the VkPhysicalDeviceFeatures used for
+ device creation is possible too by registering a callback function with
setEnabledFeaturesModifier(). When set, the callback function is invoked,
- letting it alter the VkPhysicalDeviceFeatures, instead of enabling only the
- 1.0 core features.
+ letting it alter the VkPhysicalDeviceFeatures or VkPhysicalDeviceFeatures2.
\sa QVulkanInstance, QWindow
*/
@@ -335,7 +334,7 @@ void QVulkanWindow::setPhysicalDeviceIndex(int idx)
qWarning("QVulkanWindow: Attempted to set physical device when already initialized");
return;
}
- const int count = availablePhysicalDevices().count();
+ const int count = availablePhysicalDevices().size();
if (idx < 0 || idx >= count) {
qWarning("QVulkanWindow: Invalid physical device index %d (total physical devices: %d)", idx, count);
return;
@@ -448,7 +447,7 @@ void QVulkanWindow::setPreferredColorFormats(const QList<VkFormat> &formats)
static struct {
VkSampleCountFlagBits mask;
int count;
-} qvk_sampleCounts[] = {
+} q_vk_sampleCounts[] = {
// keep this sorted by 'count'
{ VK_SAMPLE_COUNT_1_BIT, 1 },
{ VK_SAMPLE_COUNT_2_BIT, 2 },
@@ -488,7 +487,7 @@ QList<int> QVulkanWindow::supportedSampleCounts()
VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
- for (const auto &qvk_sampleCount : qvk_sampleCounts) {
+ for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
if ((color & qvk_sampleCount.mask)
&& (depth & qvk_sampleCount.mask)
&& (stencil & qvk_sampleCount.mask))
@@ -537,7 +536,7 @@ void QVulkanWindow::setSampleCount(int sampleCount)
return;
}
- for (const auto &qvk_sampleCount : qvk_sampleCounts) {
+ for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
if (qvk_sampleCount.count == sampleCount) {
d->sampleCount = qvk_sampleCount.mask;
return;
@@ -581,7 +580,7 @@ void QVulkanWindowPrivate::init()
return;
}
- if (physDevIndex < 0 || physDevIndex >= physDevs.count()) {
+ if (physDevIndex < 0 || physDevIndex >= physDevs.size()) {
qWarning("QVulkanWindow: Invalid physical device index; defaulting to 0");
physDevIndex = 0;
}
@@ -600,7 +599,7 @@ void QVulkanWindowPrivate::init()
f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
gfxQueueFamilyIdx = uint32_t(-1);
presQueueFamilyIdx = uint32_t(-1);
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ for (int i = 0; i < queueFamilyProps.size(); ++i) {
const bool supportsPresent = inst->supportsPresent(physDev, i, q);
qCDebug(lcGuiVk, "queue family %d: flags=0x%x count=%d supportsPresent=%d", i,
queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount, supportsPresent);
@@ -613,7 +612,7 @@ void QVulkanWindowPrivate::init()
presQueueFamilyIdx = gfxQueueFamilyIdx;
} else {
qCDebug(lcGuiVk, "No queue with graphics+present; trying separate queues");
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ for (int i = 0; i < queueFamilyProps.size(); ++i) {
if (gfxQueueFamilyIdx == uint32_t(-1) && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
gfxQueueFamilyIdx = i;
if (presQueueFamilyIdx == uint32_t(-1) && inst->supportsPresent(physDev, i, q))
@@ -657,7 +656,7 @@ void QVulkanWindowPrivate::init()
queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo);
bool foundGfxQueue = false;
bool foundPresQueue = false;
- for (const VkDeviceQueueCreateInfo& createInfo : qAsConst(queueInfo)) {
+ for (const VkDeviceQueueCreateInfo& createInfo : std::as_const(queueInfo)) {
foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx;
foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx;
}
@@ -699,20 +698,25 @@ void QVulkanWindowPrivate::init()
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = queueInfo.size();
devInfo.pQueueCreateInfos = queueInfo.constData();
- devInfo.enabledExtensionCount = devExts.count();
+ devInfo.enabledExtensionCount = devExts.size();
devInfo.ppEnabledExtensionNames = devExts.constData();
- VkPhysicalDeviceFeatures features;
- memset(&features, 0, sizeof(features));
- if (enabledFeaturesModifier) {
+ VkPhysicalDeviceFeatures features = {};
+ VkPhysicalDeviceFeatures2 features2 = {};
+ if (enabledFeatures2Modifier) {
+ features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ enabledFeatures2Modifier(features2);
+ devInfo.pNext = &features2;
+ } else if (enabledFeaturesModifier) {
enabledFeaturesModifier(features);
+ devInfo.pEnabledFeatures = &features;
} else {
// Enable all supported 1.0 core features, except ones that likely
// involve a performance penalty.
f->vkGetPhysicalDeviceFeatures(physDev, &features);
features.robustBufferAccess = VK_FALSE;
+ devInfo.pEnabledFeatures = &features;
}
- devInfo.pEnabledFeatures = &features;
// Device layers are not supported by QVulkanWindow since that's an already deprecated
// API. However, have a workaround for systems with older API and layers (f.ex. L4T
@@ -857,7 +861,7 @@ void QVulkanWindowPrivate::init()
// Try to honor the user request.
if (!formats.isEmpty() && !requestedColorFormats.isEmpty()) {
- for (VkFormat reqFmt : qAsConst(requestedColorFormats)) {
+ for (VkFormat reqFmt : std::as_const(requestedColorFormats)) {
auto r = std::find_if(formats.cbegin(), formats.cend(),
[reqFmt](const VkSurfaceFormatKHR &sfmt) { return sfmt.format == reqFmt; });
if (r != formats.cend()) {
@@ -1625,16 +1629,12 @@ void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &mo
praticular, \c robustBufferAccess is always disabled in order to avoid
unexpected performance hits.
- This however is not always sufficient when working with Vulkan 1.1 or 1.2
- features and extensions. Hence this callback mechanism.
-
The VkPhysicalDeviceFeatures reference passed in is all zeroed out at the
point when the function is invoked. It is up to the function to change
- members to true, or set up \c pNext chains as it sees fit.
+ members as it sees fit.
- \note When setting up \c pNext chains, make sure the referenced objects
- have a long enough lifetime, for example by storing them as member
- variables in the QVulkanWindow subclass.
+ \note To control Vulkan 1.1, 1.2, or 1.3 features, use
+ EnabledFeatures2Modifier instead.
\sa setEnabledFeaturesModifier()
*/
@@ -1642,9 +1642,14 @@ void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &mo
/*!
Sets the enabled device features modification function \a modifier.
- \sa EnabledFeaturesModifier
+ \note To control Vulkan 1.1, 1.2, or 1.3 features, use
+ the overload taking a EnabledFeatures2Modifier instead.
+
+ \note \a modifier is passed to the callback function with all members set
+ to false. It is up to the function to change members as it sees fit.
- \since 6.4
+ \since 6.7
+ \sa EnabledFeaturesModifier
*/
void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &modifier)
{
@@ -1653,6 +1658,45 @@ void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &mo
}
/*!
+ \typedef QVulkanWindow::EnabledFeatures2Modifier
+
+ A function that is called during graphics initialization to alter the
+ VkPhysicalDeviceFeatures2 that is changed to the VkDeviceCreateInfo.
+
+ By default QVulkanWindow enables all Vulkan 1.0 core features that the
+ physical device reports as supported, with certain exceptions. In
+ praticular, \c robustBufferAccess is always disabled in order to avoid
+ unexpected performance hits.
+
+ This however is not always sufficient when working with Vulkan 1.1, 1.2, or
+ 1.3 features and extensions. Hence this callback mechanism. If only Vulkan
+ 1.0 is relevant at run time, use setEnabledFeaturesModifier() instead.
+
+ The VkPhysicalDeviceFeatures2 reference passed to the callback function
+ with \c sType set, but the rest zeroed out. It is up to the function to
+ change members to true, or set up \c pNext chains as it sees fit.
+
+ \note When setting up \c pNext chains, make sure the referenced objects
+ have a long enough lifetime, for example by storing them as member
+ variables in the QVulkanWindow subclass.
+
+ \since 6.7
+ \sa setEnabledFeaturesModifier()
+ */
+
+/*!
+ Sets the enabled device features modification function \a modifier.
+ \overload
+ \since 6.7
+ \sa EnabledFeatures2Modifier
+*/
+void QVulkanWindow::setEnabledFeaturesModifier(EnabledFeatures2Modifier modifier)
+{
+ Q_D(QVulkanWindow);
+ d->enabledFeatures2Modifier = std::move(modifier);
+}
+
+/*!
Returns true if this window has successfully initialized all Vulkan
resources, including the swapchain.
@@ -1852,13 +1896,26 @@ void QVulkanWindowRenderer::logicalDeviceLost()
{
}
+QSize QVulkanWindowPrivate::surfacePixelSize() const
+{
+ Q_Q(const QVulkanWindow);
+ VkSurfaceCapabilitiesKHR surfaceCaps = {};
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevs.at(physDevIndex), surface, &surfaceCaps);
+ VkExtent2D bufferSize = surfaceCaps.currentExtent;
+ if (bufferSize.width == uint32_t(-1)) {
+ Q_ASSERT(bufferSize.height == uint32_t(-1));
+ return q->size() * q->devicePixelRatio();
+ }
+ return QSize(int(bufferSize.width), int(bufferSize.height));
+}
+
void QVulkanWindowPrivate::beginFrame()
{
if (!swapChain || framePending)
return;
Q_Q(QVulkanWindow);
- if (q->size() * q->devicePixelRatio() != swapChainImageSize) {
+ if (swapChainImageSize != surfacePixelSize()) {
recreateSwapChain();
if (!swapChain)
return;
@@ -1927,7 +1984,7 @@ void QVulkanWindowPrivate::beginFrame()
}
if (frameGrabbing)
- frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888);
+ frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888); // the format is as documented
if (renderer) {
framePending = true;
@@ -2273,7 +2330,7 @@ void QVulkanWindowPrivate::finishBlockingReadback()
VkPhysicalDevice QVulkanWindow::physicalDevice() const
{
Q_D(const QVulkanWindow);
- if (d->physDevIndex < d->physDevs.count())
+ if (d->physDevIndex < d->physDevs.size())
return d->physDevs[d->physDevIndex];
qWarning("QVulkanWindow: Physical device not available");
return VK_NULL_HANDLE;
@@ -2289,7 +2346,7 @@ VkPhysicalDevice QVulkanWindow::physicalDevice() const
const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties() const
{
Q_D(const QVulkanWindow);
- if (d->physDevIndex < d->physDevProps.count())
+ if (d->physDevIndex < d->physDevProps.size())
return &d->physDevProps[d->physDevIndex];
qWarning("QVulkanWindow: Physical device properties not available");
return nullptr;
@@ -2443,6 +2500,19 @@ VkFormat QVulkanWindow::depthStencilFormat() const
This usually matches the size of the window, but may also differ in case
\c vkGetPhysicalDeviceSurfaceCapabilitiesKHR reports a fixed size.
+ In addition, it has been observed on some platforms that the
+ Vulkan-reported surface size is different with high DPI scaling active,
+ meaning the QWindow-reported
+ \l{QWindow::}{size()} multiplied with the \l{QWindow::}{devicePixelRatio()}
+ was 1 pixel less or more when compared to the value returned from here,
+ presumably due to differences in rounding. Rendering code should be aware
+ of this, and any related rendering logic must be based in the value returned
+ from here, never on the QWindow-reported size. Regardless of which pixel size
+ is correct in theory, Vulkan rendering must only ever rely on the Vulkan
+ API-reported surface size. Otherwise validation errors may occur, e.g. when
+ setting the viewport, because the application-provided values may become
+ out-of-bounds from Vulkan's perspective.
+
\note Calling this function is only valid from the invocation of
QVulkanWindowRenderer::initSwapChainResources() up until
QVulkanWindowRenderer::releaseSwapChainResources().
@@ -2714,6 +2784,12 @@ bool QVulkanWindow::supportsGrab() const
incomplete image, that has the correct size but not the content yet. The
content will be delivered via the frameGrabbed() signal in the latter case.
+ The returned QImage always has a format of QImage::Format_RGBA8888. If the
+ colorFormat() is \c VK_FORMAT_B8G8R8A8_UNORM, the red and blue channels are
+ swapped automatically since this format is commonly used as the default
+ choice for swapchain color buffers. With any other color buffer format,
+ there is no conversion performed by this function.
+
\note This function should not be called when a frame is in progress
(that is, frameReady() has not yet been called back by the application).
@@ -2742,6 +2818,9 @@ QImage QVulkanWindow::grab()
d->frameGrabbing = true;
d->beginFrame();
+ if (d->colorFormat == VK_FORMAT_B8G8R8A8_UNORM)
+ d->frameGrabTargetImage = std::move(d->frameGrabTargetImage).rgbSwapped();
+
return d->frameGrabTargetImage;
}