diff options
Diffstat (limited to 'src/gui/rhi/qrhivulkan.cpp')
| -rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 181 |
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); |
