使用电脑端体验更佳
定位一个{{s:Device Lost}}
发布于:
# 定位一个Device Lost 显卡驱动的质量方差{{s:大的离谱}}。但一般来说,无论多糟糕的事情,你所能得到也就是某个物体画不出来,或者画出来很奇怪,或者算出来很奇怪 但偶尔的偶尔,这受苦受累惯了的老黄牛也会有忍受不了的问题。这时候驱动就会决定{{g:自行了断,撒手人寰}},留下坐在屏幕前的你对着卡死的电脑目瞪口呆 这就是DeviceLost,而我在做的渲染器每次都能稳定复现一个 我花了{{s:差不多一个月}}的时间才定位到问题。 这篇文章讲一下我是如何去排查这个DeviceLost的 ## Bug表现 在我往场景中加了500盏灯光后,飞到场景最高处,俯视所有灯光,然后摇晃相机,就能触发 在VkQueueSubmit后的第一个有返回值的API调用会返回`VK_ERROR_DEVICE_LOST`。 同时,电脑会卡死几秒,然后画面卡住不动。所有图形api调用都会失效 ## 结论 目前我所知道的会导致DeviceLost的原因只有一个:{{w:GPU上踩内存}} 但结论不重要,我的渲染器目前代码量超过20k行,光是VulkanRHI层目前就有接近7k行代码,更别提架在上面的其他应用部分。我完全没有头绪。 ## 渲染流程  目前难以确定是什么时候引入的这个Bug,我姑且猜测是在实现完成CLusterLights后发生的。 计算ClusterLights比较复杂,要两个Compute。 第一个负责用HiZ剔除灯光,第二个负责把灯光放进TileBox里。 在画物体的时候,为了知道灯光数量和灯光信息,需要进行好几次GPU上索引SSBO。 这是疑点最大的地方。 ## GPU挂在哪里了? 我没办法靠猜来知道这一切啊...我需要一个提示 Vulkan有个Device上的拓展[VkPhysicalDeviceFaultFeaturesEXT](https://docs.vulkan.org/refpages/latest/refpages/source/VkPhysicalDeviceFaultFeaturesEXT.html),能够在DeviceLost发生后得到一些信息。 于是: ```cpp void GPUPostMortem(rs_context_vk* ctx) { if (!DeviceFault) { return; } VkDeviceFaultCountsEXT deviceFaultCnts = {VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT}; vkGetDeviceFaultInfoEXT(ctx->device, &deviceFaultCnts, nullptr); VkDeviceFaultInfoEXT deviceFaultInfo = { VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT }; std::vector<VkDeviceFaultAddressInfoEXT> deviceFaultAddrInfo{}; deviceFaultAddrInfo.resize(deviceFaultCnts.addressInfoCount); deviceFaultInfo.pAddressInfos = deviceFaultAddrInfo.data(); std::vector<VkDeviceFaultVendorInfoEXT> deviceFaultVendorInfo{}; deviceFaultVendorInfo.resize(deviceFaultCnts.vendorInfoCount); deviceFaultInfo.pVendorInfos = deviceFaultVendorInfo.data(); char* vendorData = new char[deviceFaultCnts.vendorBinarySize + 1]; deviceFaultInfo.pVendorBinaryData = vendorData; vkGetDeviceFaultInfoEXT(ctx->device, &deviceFaultCnts, &deviceFaultInfo); std::stringstream ss; ss << "=================== GPU PostMortem Begin ===================\n" << "Backend: Vulkan\n" << "Device: " << ctx->choosenPhysicalDeviceInfo << "\n" << (BindlessAvailable ? std::string("Bindless Mode") : "No Bindless") << "\n" << (BufferDeviceAddressEnable ? "BDA Enabled" : "No BDA") << "\n" << "Description: " << deviceFaultInfo.description << "\n\n"; ss << "================== Address fault Begin ==================\n"; for (int i = 0; i < deviceFaultCnts.addressInfoCount;++i) { auto addressInfo = deviceFaultInfo.pAddressInfos[i]; ss << "Fault - " << i + 1 << "/" << deviceFaultCnts.addressInfoCount << "\n"; ss << "Address Type: " << (int)(addressInfo.addressType) << "\n"; ss << "Buffer Address: " << std::hex << addressInfo.reportedAddress << std::dec << "\n"; ss << "Buffer Address Precision: " << std::hex << addressInfo.addressPrecision << std::dec << "\n\n"; } ss << "================== Address fault End ==================\n\n"; ss << "================== Vendor info Begin ==================\n"; for (int i = 0; i < deviceFaultCnts.vendorInfoCount;++i) { auto vendorInfo = deviceFaultInfo.pVendorInfos[i]; ss << "Vendor Fault - " << i + 1 << "/" << deviceFaultCnts.vendorInfoCount << "\n"; ss << "Description: " << (int)(vendorInfo.description) << "\n"; ss << "Fault Code: " << std::hex << vendorInfo.vendorFaultCode << std::dec << "\n"; ss << "Fault Data: " << std::hex << vendorInfo.vendorFaultData << std::dec << "\n\n"; } ss << "================== Vendor info End ==================\n\n"; ss << "================== GPU PostMortem End ==================\n"; //Print all log info Log::error( ss.str() ); delete[] vendorData; } ``` ## 崩溃后的信息 ``` [ERROR] =================== GPU PostMortem Begin =================== Backend: Vulkan Device: NVIDIA GeForce RTX 4060 Laptop GPU (Driver: 616.368.0) Bindless Mode BDA Enabled Description: ================== Address fault Begin ================== Fault - 1/2 Address Type: 6 Buffer Address: 20002ba90 Buffer Address Precision: 10 Fault - 2/2 Address Type: 1 Buffer Address: 3cafc000 Buffer Address Precision: 1000 ================== Address fault End ================== ================== Vendor info Begin ================== ================== Vendor info End ================== ================== GPU PostMortem End ================== ``` 第一个Fault提示: Address Type: 6 ---> 这是指令指针的地址,也就是指向了shader二进制中某一行...但是我不知道是哪个shader,更没有shader的二进制.... 第二个fault: Address Type: 1 ---> 这是个读错误。意味着和我猜想的一模一样,GPU上的踩内存。 没有任何实质进展 ## 找到所有UAV Buffer的地址 我把所有的创建的SSBO的GPU地址打印了出来,扔给AI去看。 如果是内存越界问题,那么这个地址应该是和现有的Buffer有关,至少和现有创建的Buffer在地址上相差不大 我把Log扔给了AI去看,但是并没有发现任何似乎和`3cafc000`有关的地址...问题再次陷入了僵局 ## DebugShaderPrint!