summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi/qrhivulkan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi/qrhivulkan.cpp')
-rw-r--r--src/gui/rhi/qrhivulkan.cpp181
1 files changed, 179 insertions, 2 deletions
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index a511eb854cb..202e28263c2 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -682,6 +682,12 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (devExts.contains("VK_KHR_fragment_shading_rate"))
addToChain(&physDevFeaturesChainable, &fragmentShadingRateFeatures);
#endif
+#ifdef VK_EXT_device_fault
+ VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures = {};
+ deviceFaultFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
+ if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME))
+ addToChain(&physDevFeaturesChainable, &deviceFaultFeatures);
+#endif
#endif
// Vulkan >=1.2 headers at build time, >=1.2 implementation at run time
@@ -825,6 +831,13 @@ bool QRhiVulkan::create(QRhi::Flags flags)
requestedDevExts.append(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
#endif
+#ifdef VK_EXT_device_fault
+ if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_EXT_DEVICE_FAULT_EXTENSION_NAME);
+ caps.deviceFault = true;
+ }
+#endif
+
for (const QByteArray &ext : requestedDeviceExtensions) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
if (devExts.contains(ext)) {
@@ -910,6 +923,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
// Here we have no way to tell if the extensions got enabled or not.
// Pretend it's all there and supported. If getProcAddress fails, we'll
// handle that gracefully.
+ caps.deviceFault = true;
caps.vertexAttribDivisor = true;
caps.renderPass2KHR = true;
caps.depthStencilResolveKHR = true;
@@ -1126,6 +1140,12 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
#endif
+#ifdef VK_EXT_device_fault
+ if (caps.deviceFault) {
+ vkGetDeviceFaultInfoEXT = reinterpret_cast<PFN_vkGetDeviceFaultInfoEXT>(f->vkGetDeviceProcAddr(dev, "vkGetDeviceFaultInfoEXT"));
+ }
+#endif
+
deviceLost = false;
nativeHandlesStruct.physDev = physDev;
@@ -2159,6 +2179,18 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
#endif
+ // Add self-dependency to be able to add memory barriers for writes in graphics stages
+ VkSubpassDependency selfDependency;
+ VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+ selfDependency.srcSubpass = 0;
+ selfDependency.dstSubpass = 0;
+ selfDependency.srcStageMask = stageMask;
+ selfDependency.dstStageMask = stageMask;
+ selfDependency.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ selfDependency.dstAccessMask = selfDependency.srcAccessMask;
+ selfDependency.dependencyFlags = 0;
+ rpD->subpassDeps.append(selfDependency);
+
// rpD->subpassDeps stays empty: don't yet know the correct initial/final
// access and stage stuff for the implicit deps at this point, so leave it
// to the resource tracking and activateTextureRenderTarget() to generate
@@ -2631,6 +2663,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
} else {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkAcquireNextImageKHR()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2791,6 +2824,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
} else if (err != VK_SUBOPTIMAL_KHR) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkQueuePresentKHR()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2850,6 +2884,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkAllocateCommandBuffers()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2865,6 +2900,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkBeginCommandBuffer()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2882,6 +2918,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkEndCommandBuffer()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2918,6 +2955,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkQueueSubmit()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -2939,6 +2977,7 @@ QRhi::FrameOpResult QRhiVulkan::waitCommandCompletion(int frameSlot)
if (err != VK_SUCCESS) {
if (err == VK_ERROR_DEVICE_LOST) {
qWarning("Device loss detected in vkWaitForFences()");
+ printExtraErrorInfo(err);
deviceLost = true;
return QRhi::FrameOpDeviceLost;
}
@@ -4067,10 +4106,87 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
void QRhiVulkan::printExtraErrorInfo(VkResult err)
{
+ if (err == VK_ERROR_DEVICE_LOST)
+ printDeviceLossErrorInfo();
if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
qWarning() << "Out of device memory, current allocator statistics are" << statistics();
}
+void QRhiVulkan::printDeviceLossErrorInfo() const
+{
+#ifdef VK_EXT_device_fault
+ if (!dev || !caps.deviceFault || !vkGetDeviceFaultInfoEXT)
+ return;
+
+ VkDeviceFaultCountsEXT faultCounts{};
+ faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT;
+ faultCounts.pNext = nullptr;
+
+ VkResult result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, nullptr);
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
+ return;
+ }
+ faultCounts.vendorBinarySize = 0;
+
+ QVarLengthArray<VkDeviceFaultAddressInfoEXT> addressInfos;
+ addressInfos.resize(faultCounts.addressInfoCount);
+
+ QVarLengthArray<VkDeviceFaultVendorInfoEXT> vendorInfos;
+ vendorInfos.resize(faultCounts.vendorInfoCount);
+
+ VkDeviceFaultInfoEXT info{};
+ info.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT;
+ info.pNext = nullptr;
+ info.pAddressInfos = addressInfos.isEmpty() ? nullptr : addressInfos.data();
+ info.pVendorInfos = vendorInfos.isEmpty() ? nullptr : vendorInfos.data();
+ info.pVendorBinaryData = nullptr;
+
+ result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, &info);
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ qWarning("vkGetDeviceFaultInfoEXT failed with %d", result);
+ return;
+ }
+
+ const char *desc = info.description[0] ? info.description : "n/a";
+ qWarning("VK_ERROR_DEVICE_LOST (VK_EXT_device_fault): %u address infos, %u vendor infos, %llu bytes vendor binary: %s",
+ faultCounts.addressInfoCount,
+ faultCounts.vendorInfoCount,
+ (unsigned long long)faultCounts.vendorBinarySize,
+ desc);
+
+ for (uint32_t i = 0; i < faultCounts.addressInfoCount; ++i) {
+ const auto &a = addressInfos[i];
+ auto addressTypeString = [](const VkDeviceFaultAddressTypeEXT type) {
+ switch (type) {
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT: return "NONE";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT: return "READ_INVALID";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT: return "WRITE_INVALID";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT: return "EXECUTE_INVALID";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT: return "INSTRUCTION_POINTER_UNKNOWN";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT: return "INSTRUCTION_POINTER_INVALID";
+ case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT: return "INSTRUCTION_POINTER_FAULT";
+ default: return "UNKNOWN";
+ };
+ };
+ qWarning(" AddressInfo[%02u]: type=%s addr=0x%llx precision=%llu",
+ i,
+ addressTypeString(a.addressType),
+ (unsigned long long)a.reportedAddress,
+ (unsigned long long)a.addressPrecision);
+ }
+
+ for (uint32_t i = 0; i < faultCounts.vendorInfoCount; ++i) {
+ const auto &v = vendorInfos[i];
+ qWarning(" VendorInfo[%02u]: code=%llu data=%llu desc=%s",
+ i,
+ (unsigned long long)v.vendorFaultCode,
+ (unsigned long long)v.vendorFaultData,
+ v.description);
+ }
+#endif // VK_EXT_device_fault
+}
+
void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
{
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
@@ -4864,6 +4980,17 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
: VK_SUBPASS_CONTENTS_INLINE);
break;
+ case QVkCommandBuffer::Command::MemoryBarrier: {
+ VkMemoryBarrier barrier;
+ barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+ barrier.pNext = nullptr;
+ barrier.dstAccessMask = cmd.args.memoryBarrier.dstAccessMask;
+ barrier.srcAccessMask = cmd.args.memoryBarrier.srcAccessMask;
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.memoryBarrier.srcStageMask, cmd.args.memoryBarrier.dstStageMask, cmd.args.memoryBarrier.dependencyFlags,
+ 1, &barrier,
+ 0, VK_NULL_HANDLE,
+ 0, VK_NULL_HANDLE);
+ } break;
case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb);
break;
@@ -5702,6 +5829,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
auto &descSetBd(srbD->boundResourceData[currentFrameSlot]);
bool rewriteDescSet = false;
+ bool addWriteBarrier = false;
+ VkPipelineStageFlags writeBarrierSrcStageMask = 0;
+ VkPipelineStageFlags writeBarrierDstStageMask = 0;
// Do host writes and mark referenced shader resources as in-use.
// Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
@@ -5789,9 +5919,22 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::TexStorageStore;
else
access = QRhiPassResourceTracker::TexStorageLoadStore;
+
+ const auto stage = QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage);
+ const auto prevAccess = passResTracker.textures().find(texD);
+ if (prevAccess != passResTracker.textures().end()) {
+ const QRhiPassResourceTracker::Texture &tex = prevAccess->second;
+ if (tex.access == QRhiPassResourceTracker::TexStorageStore
+ || tex.access == QRhiPassResourceTracker::TexStorageLoadStore) {
+ addWriteBarrier = true;
+ writeBarrierDstStageMask |= toVkPipelineStage(stage);
+ writeBarrierSrcStageMask |= toVkPipelineStage(tex.stage);
+ }
+ }
+
trackedRegisterTexture(&passResTracker, texD,
access,
- QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ stage);
if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
rewriteDescSet = true;
@@ -5818,9 +5961,21 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::BufStorageStore;
else
access = QRhiPassResourceTracker::BufStorageLoadStore;
+
+ const auto stage = QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage);
+ const auto prevAccess = passResTracker.buffers().find(bufD);
+ if (prevAccess != passResTracker.buffers().end()) {
+ const QRhiPassResourceTracker::Buffer &buf = prevAccess->second;
+ if (buf.access == QRhiPassResourceTracker::BufStorageStore
+ || buf.access == QRhiPassResourceTracker::BufStorageLoadStore) {
+ addWriteBarrier = true;
+ writeBarrierDstStageMask |= toVkPipelineStage(stage);
+ writeBarrierSrcStageMask |= toVkPipelineStage(buf.stage);
+ }
+ }
trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
access,
- QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
+ stage);
if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
rewriteDescSet = true;
@@ -5835,6 +5990,28 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
}
+ if (addWriteBarrier) {
+ if (cbD->passUsesSecondaryCb) {
+ VkMemoryBarrier barrier;
+ barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+ barrier.pNext = nullptr;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ barrier.srcAccessMask = barrier.dstAccessMask;
+ df->vkCmdPipelineBarrier(cbD->activeSecondaryCbStack.last(), writeBarrierSrcStageMask, writeBarrierDstStageMask, 0,
+ 1, &barrier,
+ 0, VK_NULL_HANDLE,
+ 0, VK_NULL_HANDLE);
+ } else {
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QVkCommandBuffer::Command::MemoryBarrier;
+ cmd.args.memoryBarrier.dependencyFlags = 0;
+ cmd.args.memoryBarrier.dstStageMask = writeBarrierDstStageMask;
+ cmd.args.memoryBarrier.srcStageMask = writeBarrierSrcStageMask;
+ cmd.args.memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ cmd.args.memoryBarrier.srcAccessMask = cmd.args.memoryBarrier.dstAccessMask;
+ }
+ }
+
// write descriptor sets, if needed
if (rewriteDescSet)
updateShaderResourceBindings(srb);