議 Linux 版在 NVIDIA 顯卡上看不到畫面:EGL 像素配置與窗口視覺不匹配的排查與修復(fù))
文章目錄騰訊會(huì)議 Linux 版在 NVIDIA 顯卡上看不到畫面EGL 像素配置與窗口視覺不匹配的排查與修復(fù)一、現(xiàn)象二、環(huán)境硬件軟件三、先確認(rèn)攝像頭與系統(tǒng)權(quán)限沒有問題四、從騰訊會(huì)議自己的日志定位失敗點(diǎn)五、為什么它必須走 X11六、驗(yàn)證失敗原因?qū)傩詳?shù)組與最小復(fù)現(xiàn)七、結(jié)論八、修復(fù)只讓騰訊會(huì)議使用 Mesa 的 EGL九、驗(yàn)證十、代價(jià)與遺留問題附錄 A 最小復(fù)現(xiàn)程序附錄 B 命令速查參考騰訊會(huì)議 Linux 版在 NVIDIA 顯卡上看不到畫面EGL 像素配置與窗口視覺不匹配的排查與修復(fù)記錄日期 2026-10-06。攝像頭本身工作正常采集、編碼、上行都在跑騰訊會(huì)議界面里自己的畫面卻一直停在頭像占位。查下來原因是騰訊會(huì)議的視頻渲染器向 EGL 申請(qǐng)的像素配置帶 8 位 alpha而它交給 EGL 的 X11 窗口是 24 位NVIDIA 的 EGL 實(shí)現(xiàn)嚴(yán)格校驗(yàn)這兩者是否匹配于是創(chuàng)建窗口表面時(shí)每次都返回EGL_BAD_NATIVE_WINDOW。把騰訊會(huì)議單獨(dú)固定到 Mesa 的 EGL 實(shí)現(xiàn)后畫面恢復(fù)改動(dòng)只有一個(gè)啟動(dòng)包裝腳本加一個(gè)用戶級(jí)桌面條目不需要 root隨時(shí)可以撤銷。EGL 是圖形渲染接口Linux 上由 glvnd 在多個(gè)實(shí)現(xiàn)之間分發(fā)調(diào)用騰訊會(huì)議默認(rèn)拿到的是 NVIDIA 的實(shí)現(xiàn)修復(fù)生效的關(guān)鍵就是讓這一個(gè)進(jìn)程改用 Mesa 的實(shí)現(xiàn)。一、現(xiàn)象會(huì)議進(jìn)行中界面底部的攝像頭按鈕顯示“停止視頻”說明視頻處于開啟狀態(tài)但自己的畫面位只有一個(gè)頭像。麥克風(fēng)正常能聽到對(duì)方說話對(duì)方也能看到我的畫面騰訊會(huì)議的日志顯示視頻流一直在上傳。出問題的只是我這邊的窗口里沒有畫面。第一反應(yīng)是攝像頭壞了或者被別的程序占著后面的排查否定了這個(gè)方向。適用范圍。本文的驗(yàn)證環(huán)境是 Ubuntu 26.04 配 GNOME 50 的 Wayland 會(huì)話、NVIDIA 專有驅(qū)動(dòng) 595 系列、騰訊會(huì)議 3.26 版本。同類現(xiàn)象也可能出現(xiàn)在其他 Qt 或 Electron 應(yīng)用以及別的 5xx 版驅(qū)動(dòng)上判斷依據(jù)是應(yīng)用日志里創(chuàng)建 EGL 窗口表面失敗并返回 0x3005。如果攝像頭同時(shí)被別的程序占用表現(xiàn)是設(shè)備忙而不是畫面缺失那屬于另一類問題先按第三節(jié)的步驟確認(rèn)設(shè)備本身。二、環(huán)境硬件部件型號(hào)與規(guī)格CPUAMD Ryzen Threadripper 9960X24 核 48 線程L3 緩存 128 MiB4 個(gè) CCD單 NUMA 節(jié)點(diǎn)主板ASUS Pro WS TRX50-SAGE WIFI ARev 1.xxBIOSAmerican Megatrends 0617日期 2026-02-03內(nèi)存裝機(jī)容量 64 GB系統(tǒng)可用 61 GiBMemTotal: 63858056 kB。DIMM 頻率與通道需要 root 讀dmidecode -t memory本文未采集顯卡NVIDIA GeForce RTX 3090GA102顯存 24 GiBVBIOS94.02.26.80.80顯示器DisplayPort 3840×2160HDMI 2560×1440存儲(chǔ)Predator SSD GM9 2 TBnvme0n1、ZHITAI Ti600 1 TBnvme1n1PCIea9:00.0、WDC WD10SDRW 931 GBUSBsdaUSB 控制器AMD Turin USB 3.1 xHCI 兩個(gè)21:00.4、f1:00.4、ASMedia ASM4242 USB4 兩個(gè)a7:00.0、a8:00.0、600 系芯片組 USB 3.2b0:00.0網(wǎng)絡(luò)Aquantia AQC113 10Genp173s0ad:00.0鏈路未連接、Intel I226-LMenp174s0ae:00.0鏈路未連接、Wi-Fiwlp175s0af:00.0。實(shí)際上網(wǎng)走 USB 網(wǎng)卡enx5c7daecbc30dMAC5c:7d:ae:cb:c3:0d地址 192.168.0.34默認(rèn)路由攝像頭UGREEN CameraUSB ID0c45:2283UVC 1.00接在3-1端口鏈路速率為高速USB 2.0480 Mbps。所以 1080p 只能走 MJPEG 格式Y(jié)UYV 在 1080p30 下帶寬不夠音頻UGREEN CM564 USB Audio2b89:0234麥克風(fēng)、Focusrite Scarlett Solo 3rd Gen1235:8211耳機(jī)輸出鍵鼠Logitech G102/G203攝像頭暴露的采集格式ffmpeg -f v4l2 -list_formats all -i /dev/video0mjpeg : 1920x1080 1440x1080 1280x960 1280x720 1024x768 800x600 640x480 320x240 yuyv422: 1920x1080 1440x1080 1280x960 1280x720 1024x768 800x600 640x480 320x240/dev/video0是采集節(jié)點(diǎn)/dev/video1是 UVC 的元數(shù)據(jù)節(jié)點(diǎn)。后者不支持內(nèi)存映射打不開屬于正?,F(xiàn)象。軟件組件版本操作系統(tǒng)Ubuntu 26.04.1 LTSResolute Raccoon內(nèi)核7.0.0-38-generic桌面GNOME Shell 50.1Wayland 會(huì)話XDG_SESSION_TYPEwayland音頻服務(wù)PipeWire 1.6.2NVIDIA 驅(qū)動(dòng)595.91.07Open Kernel Moduleglvnd 1.7.0Mesa26.0.8libegl-mesa0、libgl1-mesa-dri、libglx-mesa0騰訊會(huì)議wemeet3.26.10.401自帶 Qt 5.15.8運(yùn)行平臺(tái)啟動(dòng)腳本強(qiáng)制到 X11QT_QPA_PLATFORMxcb、WEMEET_XWAYLAND1、DISPLAY:0也就是 XWayland這里引用的 X11 術(shù)語 visual 指窗口的像素格式與顏色布局EGL 里對(duì)應(yīng)的屬性是EGL_NATIVE_VISUAL_ID。后文提到視覺指的都是它。騰訊會(huì)議啟動(dòng)腳本/opt/wemeet/wemeetapp.sh中這一段是理解后面所有現(xiàn)象的起點(diǎn)if[$XDG_SESSION_TYPEwayland];thenif[-f/opt/x11-wayland/x11-ext.sh];thensource/opt/x11-wayland/x11-ext.shelseexportQT_QPA_PLATFORMxcbexportXDG_SESSION_TYPEx11unsetWAYLAND_DISPLAYexportWEMEET_XWAYLAND1fifi本機(jī)沒有/opt/x11-wayland/x11-ext.sh因此騰訊會(huì)議始終以 XWayland 客戶端的身份運(yùn)行。這不是配置失誤它的視頻渲染器只支持 X11見第五節(jié)。三、先確認(rèn)攝像頭與系統(tǒng)權(quán)限沒有問題設(shè)備節(jié)點(diǎn)存在權(quán)限由 logind 按活動(dòng)會(huì)話發(fā)放 ACL當(dāng)前用戶有讀寫權(quán)限。$ ls -l /dev/video* crw-rw---- 1 root video 81, 0 Oct 6 09:42 /dev/video0 crw-rw---- 1 root video 81, 1 Oct 6 09:42 /dev/video1 $ getfacl -p /dev/video0 user::rw- user:ouyangjiahong:rw- group::rw- mask::rw- other::---內(nèi)核驅(qū)動(dòng)識(shí)別正常GNOME 的攝像頭總開關(guān)也是打開的。$ journalctl -k -b | grep -i uvc uvcvideo 3-1:1.0: Found UVC 1.00 device UGREEN Camera (0c45:2283) $ gsettings get org.gnome.desktop.privacy disable-camera false會(huì)議進(jìn)行時(shí)直接抓一幀會(huì)被拒絕因?yàn)樵O(shè)備已被會(huì)議占用。這同時(shí)也是攝像頭正在工作的旁證。$ ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -frames:v 1 out.jpg [in#0] Error opening input: Device or resource busy $ fuser -v /dev/video* /dev/video0: ouyangjiahong 2486899 F...m wemeetapp /dev/video1: ouyangjiahong 2486899 F.... wemeetapp到這一步可以確定設(shè)備、驅(qū)動(dòng)、權(quán)限、占用狀態(tài)都正常問題出在騰訊會(huì)議自身。四、從騰訊會(huì)議自己的日志定位失敗點(diǎn)騰訊會(huì)議的媒體引擎會(huì)把日志寫到~/.local/share/wemeetapp/Saas/Logs/。其中xcast_年月日時(shí).log記錄采集、編碼、渲染和網(wǎng)絡(luò)wmp_年月日時(shí).log是主程序的二進(jìn)制日志不可讀。先看采集與發(fā)送的統(tǒng)計(jì)$ grep -a Snd\[big Cap xcast_2026100615.log | tail -1 Snd[big Cap:1280x72028.5/7.49/1/5 HW:0 Enc:1280x72028.5/7.36 eType:5 eBR:1642.3/1369.0/249.9 CoBR:1554.4/1327.4/1500.0/1227.3 ... Fill:0 Mut:0 In:28.5 Skp:0.0 Drp:0.0/0Cap:1280x72028.5表示攝像頭穩(wěn)定輸出每秒 28.5 幀Drp:0.0/0表示沒有丟幀編碼與上行同樣正常。異常全部集中在渲染一側(cè)$ grep -c enter create_window_surface xcast_2026100615.log 44 $ grep -c EGL_NO_SURFACE xcast_2026100615.log 44 $ grep -c create_window_surface succeeded xcast_2026100615.log 0 $ grep -a EGL_NO_SURFACE xcast_2026100615.log | head -2 15:39:57.611|W|egl_core.cc:184|25F2E2|eglCreateWindowSurface returned EGL_NO_SURFACE error:3005 15:39:57.627|W|egl_core.cc:184|25F2E2|eglCreateWindowSurface returned EGL_NO_SURFACE error:3005 $ grep -aoE render\.avg\.fps\.[0-9.] xcast_2026100615.log | sort | uniq -c 1 render.avg.fps.0.03. 1 render.avg.fps.0.21. 1 render.avg.fps.0.18.三個(gè)數(shù)字就說明了問題。創(chuàng)建 EGL 窗口表面嘗試 44 次失敗 44 次成功 0 次。錯(cuò)誤碼0x3005是EGL_BAD_NATIVE_WINDOW。渲染幀率停在每秒 0.03 到 0.21 幀而要顯示 720p 視頻本應(yīng)接近 28 幀。渲染器拿不到任何可用表面界面只能退回頭像占位。再翻三天前10 月 3 日那一次會(huì)話的日志同樣是成功 0 次渲染幀率 0.00 到 0.50 幀。這是長期存在的環(huán)境性故障不是偶發(fā)。日志里還能看到這個(gè)渲染器屬于哪個(gè)組件UGREEN Camera: UGREEN Camera add device:device.video-renderer.default renderer-dispatcher 1 DVC|renderer-dispatcher start 1 cookie:0x3d9a72f0 run:0五、為什么它必須走 X11看一眼libxcast.so里未定義的符號(hào)就能知道這個(gè)渲染器依賴哪個(gè)平臺(tái)。$ nm -D --undefined-only /opt/wemeet/lib/libxcast.so | grep -iE egl|wl_|xcb|X[A-Z] U eglChooseConfig U eglCreateContext U eglCreatePbufferSurface U eglCreateWindowSurface U eglGetDisplay U eglMakeCurrent U eglSwapBuffers U XCreateGC U XGetWindowAttributes U XGetWindowProperty U XShmCreatePixmap符號(hào)表里只有 X11Xlib 與 XShm和 EGL沒有任何與wl_surface、wl_egl_window相關(guān)的符號(hào)。所以讓騰訊會(huì)議改用原生 Wayland 來繞開 XWayland 這條路走不通即便把 Qt 切到wayland平臺(tái)插件媒體渲染器仍然需要一個(gè) X 窗口句柄。egl_core.cc的日志格式同樣在說明它想做什么enter create_window_surface eglCreateWindowSurface returned EGL_NO_SURFACE error:%x create_window_surface succeeded, egl_surface[%p]失敗的正是由 X 窗口句柄創(chuàng)建 EGL 窗口表面這一步。六、驗(yàn)證失敗原因?qū)傩詳?shù)組與最小復(fù)現(xiàn)EGL_BAD_NATIVE_WINDOW最常見的成因是 EGL 像素配置所對(duì)應(yīng)的 visual 與窗口自身的 visual 不一致。要驗(yàn)證這一點(diǎn)先得知道騰訊會(huì)議申請(qǐng)的像素配置是什么。libxcast.so沒有被 strip直接反匯編eglChooseConfig的調(diào)用點(diǎn)可以看到屬性數(shù)組是從.rodata整塊搬移到棧上的$ objdump -d --no-show-raw-insn /opt/wemeet/lib/libxcast.so | grep -B20 call.*eglChooseConfig 2a5df0: movaps 0xecbca9(%rip),%xmm0 # 1171aa0 2a5df7: movaps %xmm0,-0x60(%rbp) ... 2a5e54: call cf120 eglChooseConfigplt把.rodata中0x1171a60起的 128 字節(jié)按整數(shù)解出來得到完整的屬性表0 0x303f CONFORMANT ... 2 0x3020 BUFFER_SIZE 32 要 32 位 4 0x3024 RED_SIZE 8 6 0x3023 GREEN_SIZE 8 8 0x3022 BLUE_SIZE 8 10 0x3021 ALPHA_SIZE 8 還要 alpha 12 0x3025 DEPTH_SIZE -1 14 0x3026 STENCIL_SIZE -1 16 0x3032 SAMPLE_BUFFERS 0 18 0x3031 SAMPLES 0 20 0x3033 SURFACE_TYPE 4 EGL_WINDOW_BIT 22 0x3040 RENDERABLE_TYPE 4 EGL_OPENGL_ES2_BIT 24 0x3038 NONE騰訊會(huì)議要的是EGL_BUFFER_SIZE32加EGL_ALPHA_SIZE8的配置也就是 ARGB 視覺。再看它交給 EGL 的那個(gè)窗口$ xdotool search --name 騰訊會(huì)議 | head -2 29360170 29360204 $ DISPLAY:0 xwininfo -id 29360170 | grep -E Depth|Visual|Map State Width: 1444 Height: 964 Depth: 24 Visual: 0x2fd Map State: IsViewableQt 創(chuàng)建的 X11 窗口深度是 24 位。32 位配置配上 24 位窗口兩者不匹配。到這里假設(shè)成立但還需要一次獨(dú)立的驗(yàn)證。寫一個(gè)最小復(fù)現(xiàn)程序只保留這條調(diào)用鏈用gcc t.c -lX11 -lEGL編譯完整源碼見附錄 AEGLint attrs[]{EGL_SURFACE_TYPE,EGL_WINDOW_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,EGL_ALPHA_SIZE,8,EGL_NONE};eglChooseConfig(dpy,attrs,cfg,1,n);Window winXCreateWindow(...,DefaultVisual(dpy,scr),...);EGLSurface seglCreateWindowSurface(dpy,cfg,(EGLNativeWindowType)win,NULL);四種組合各跑一遍EGL 實(shí)現(xiàn)配置的 visual窗口的 visual結(jié)果NVIDIA0x91alpha 為 8緩沖 32 位0x61深度 24失敗error0x3005NVIDIA0x910x91深度 32成功Mesa0x61alpha 為 80x61深度 24成功MesaLIBGL_ALWAYS_SOFTWARE10x610x61成功結(jié)論有兩條。第一NVIDIA 的 EGL 嚴(yán)格檢查配置的 visual 是否等于窗口的 visual不等就返回EGL_BAD_NATIVE_WINDOW。第二Mesa 的 EGL 對(duì)此寬松同樣輸入能順利創(chuàng)建窗口表面。這也解釋了同一個(gè)版本的騰訊會(huì)議為什么挑機(jī)器。在 Intel 與 AMD 核顯的機(jī)器上走的是 Mesa畫面一切正常唯獨(dú) NVIDIA 上黑屏差別不在攝像頭兼容性而在 EGL 實(shí)現(xiàn)的嚴(yán)格程度。七、結(jié)論把整條鏈條寫成因果順序是這樣的。啟動(dòng)腳本檢測(cè)到 Wayland 會(huì)話且找不到它的 X11 輔助腳本于是把騰訊會(huì)議放到 XWayland 上運(yùn)行。Qt 因此創(chuàng)建深度 24 位的 X11 窗口。libxcast向 EGL 申請(qǐng)帶 8 位 alpha 的 32 位配置。NVIDIA 的 EGL 認(rèn)為這個(gè)組合非法創(chuàng)建窗口表面時(shí)返回EGL_BAD_NATIVE_WINDOW44 次全部失敗。視頻渲染器沒有表面可用渲染幀率停在每秒零點(diǎn)幾幀界面只能顯示頭像。采集、編碼、上行與這條鏈路無關(guān)所以一切正常。一句話概括攝像頭把畫面交給了騰訊會(huì)議騰訊會(huì)議把它交給了 EGLEGL 因?yàn)橐粋€(gè) 8 位的 alpha 通道拒絕了它。八、修復(fù)只讓騰訊會(huì)議使用 Mesa 的 EGL__EGL_VENDOR_LIBRARY_FILENAMES和LIBGL_ALWAYS_SOFTWARE會(huì)影響所有進(jìn)程包括桌面本身所以這里用啟動(dòng)包裝腳本加用戶級(jí)桌面條目把影響限制在騰訊會(huì)議內(nèi)部不需要 root。包裝腳本~/.local/bin/wemeet-mesa-egl#!/bin/sh# 只對(duì)騰訊會(huì)議固定 Mesa EGL# NVIDIA EGL 對(duì) 32 位 alpha 配置配 24 位窗口返回 EGL_BAD_NATIVE_WINDOW(0x3005)# Mesa EGL 容忍這一不匹配視頻渲染器才能拿到窗口表面。export__EGL_VENDOR_LIBRARY_FILENAMES/usr/share/glvnd/egl_vendor.d/50_mesa.jsonexportLIBGL_ALWAYS_SOFTWARE1exec/opt/wemeet/wemeetapp.sh$$ chmod x ~/.local/bin/wemeet-mesa-egl桌面條目~/.local/share/applications/wemeetapp.desktop。文件名即桌面條目 ID與/usr/share/applications/wemeetapp.desktop保持一致用戶級(jí)目錄優(yōu)先級(jí)更高菜單、Dock 與wemeet://入會(huì)鏈接都會(huì)走包裝腳本[Desktop Entry] NameWemeetApp Name[zh_CN]騰訊會(huì)議 Exec/home/ouyangjiahong/.local/bin/wemeet-mesa-egl %u Icon/opt/wemeet/wemeet.svg TypeApplication Terminalfalse CategoriesAudioVideo; MimeTypex-scheme-handler/wemeet;$ update-desktop-database ~/.local/share/applications $ grep wemeet ~/.local/share/applications/mimeinfo.cache x-scheme-handler/wemeetwemeetapp.desktop;撤銷改動(dòng)只需刪掉這兩個(gè)文件并刷新一次數(shù)據(jù)庫$ rm ~/.local/bin/wemeet-mesa-egl ~/.local/share/applications/wemeetapp.desktop $ update-desktop-database ~/.local/share/applications被否決的做法做法結(jié)論讓騰訊會(huì)議以原生 Wayland 運(yùn)行QT_QPA_PLATFORMwayland不可行。libxcast只引用 X11 與 EGL 符號(hào)需要 X 窗口句柄見第五節(jié)只設(shè)LIBGL_ALWAYS_SOFTWARE1無效。EGL 實(shí)現(xiàn)的選擇由 glvnd 決定實(shí)測(cè)進(jìn)程內(nèi)加載的仍然是 NVIDIA 的 EGL改 GLX 相關(guān)變量如LIBGL_ALWAYS_INDIRECT、__GLX_VENDOR_LIBRARY_NAME未實(shí)測(cè)。這些變量作用于 GLX騰訊會(huì)議走的是 EGL不應(yīng)對(duì)它生效等騰訊修復(fù)這是它自身的問題但周期不可控九、驗(yàn)證修復(fù)后的環(huán)境是否真的生效可以直接讀運(yùn)行中進(jìn)程的判斷依據(jù)$ tr \0 \n /proc/2749569/environ | grep -E EGL_VENDOR|LIBGL_ALWAYS __EGL_VENDOR_LIBRARY_FILENAMES/usr/share/glvnd/egl_vendor.d/50_mesa.json LIBGL_ALWAYS_SOFTWARE1 $ grep -aoE libEGL[^ ]*\.so[^ ]* /proc/2749569/maps | sort -u /usr/lib/x86_64-linux-gnu/libEGL.so.1.1.0 /usr/lib/x86_64-linux-gnu/libEGL_mesa.so.0.0.0 /usr/lib/x86_64-linux-gnu/libgallium-26.0.8-1ubuntu0.3.so $ grep -ac libEGL_nvidia /proc/2749569/maps 0進(jìn)程只加載了 Mesa 的 EGL 與 Mesa 的 Gallium 驅(qū)動(dòng)框架libgallium軟件光柵化 llvmpipe 就在其中NVIDIA 的 EGL 一個(gè)也沒有加載。媒體引擎?zhèn)鹊淖兓? grep -c EGL_NO_SURFACE xcast_2026100616.log 0 $ grep -c create_window_surface succeeded xcast_2026100616.log 3 $ grep -a create_window_surface succeeded xcast_2026100616.log 16:22:08.629|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15ee8fe0] 16:22:08.646|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15eed720] 16:22:08.694|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15ee8fe0] $ grep -aoE render\.avg\.fps\.[0-9.] xcast_2026100616.log | sort | uniq -c 1 render.avg.fps.12.69. 1 render.avg.fps.28.64.修復(fù)前后的對(duì)比指標(biāo)修復(fù)前修復(fù)后創(chuàng)建窗口表面成功次數(shù)0共嘗試 44 次3共嘗試 3 次EGL_NO_SURFACE error:3005次數(shù)440render.avg.fps0.03、0.18、0.2112.69、28.64界面表現(xiàn)只有頭像實(shí)時(shí)畫面界面本身也可以直接取證。騰訊會(huì)議是 XWayland 客戶端它的窗口在 X 一側(cè)真實(shí)存在可以用xwd把窗口內(nèi)容摳出來。GNOME 50 已經(jīng)禁止了org.gnome.Shell.Screenshot的 D-Bus 調(diào)用返回AccessDenied: Screenshot is not allowed這條路走不通。$ DISPLAY:0 xdotool search --name 騰訊會(huì)議 29360180 $ DISPLAY:0 xwd -id 29360180 -silent -out win.xwd $ ffmpeg -i win.xwd win.png修復(fù)前抓到的窗口按鈕寫著“停止視頻”自己的畫面位是一個(gè)頭像。修復(fù)后同樣的窗口里是我坐在攝像頭前的實(shí)時(shí)畫面對(duì)方仍然是頭像因?yàn)閷?duì)方?jīng)]有開攝像頭。十、代價(jià)與遺留問題軟渲染的代價(jià)。LIBGL_ALWAYS_SOFTWARE1讓騰訊會(huì)議的視頻合成退到 Mesa 的軟件光柵化。720p 的合成量對(duì) 24 核的 9960X 可以忽略渲染幀率穩(wěn)定在 28 幀以上代價(jià)是不再吃 GPU。若日后想換回硬件路徑刪掉LIBGL_ALWAYS_SOFTWARE1只留廠商變量也可以Mesa 在 NVIDIA 上沒有對(duì)應(yīng)的 DRI 驅(qū)動(dòng)多半會(huì)自行退回軟件路徑并打印egl: failed to create dri2 screen警告。采集側(cè)的隊(duì)列饑餓。整個(gè)會(huì)話里持續(xù)出現(xiàn)下面三條警告采集期間大致每秒一到兩條本文這次會(huì)話有 225 條10 月 3 日那次會(huì)話累計(jì) 2084 條。E|v4l2.c:320|Not enough buffer on device:/dev/video0 E|v4l2.c:333|Could not requeue buffer on device:/dev/video0 W|video_capture_linux.c:416|camera[...].is.busy它不影響畫面顯示但說明采集線程的緩沖區(qū)回收存在問題。如果日后遠(yuǎn)端反饋我的畫面卡頓這是第一個(gè)需要復(fù)查的地方。給騰訊的建議。libxcast把EGL_ALPHA_SIZE8與EGL_BUFFER_SIZE32寫死在屬性數(shù)組里卻不去保證窗口的 visual 與之匹配。去掉 alpha 需求或者按EGL_NATIVE_VISUAL_ID創(chuàng)建匹配的窗口或者在創(chuàng)建窗口表面失敗時(shí)退回?zé)o alpha 的配置重試任一種改法都能讓 NVIDIA 用戶不再遇到這個(gè)問題。方法本身的適用范圍。這套排查路徑對(duì)任何 Qt 或 Electron 類應(yīng)用的黑屏、視頻不渲染都適用步驟是先確認(rèn)數(shù)據(jù)源正常再在應(yīng)用日志里找 EGL 創(chuàng)建窗口表面失敗然后用一個(gè)最小程序驗(yàn)證配置與窗口視覺是否匹配最后按應(yīng)用固定 EGL 實(shí)現(xiàn)。附錄 A 最小復(fù)現(xiàn)程序// gcc eglvis.c -o eglvis -lX11 -lEGL// ./eglvis plain alpha 24 位窗口配 32 位 alpha 配置NVIDIA 下返回 0x3005// ./eglvis match alpha 32 位窗口配 32 位 alpha 配置可以通過#includestdio.h#includestdlib.h#includestring.h#includeX11/Xlib.h#includeX11/Xutil.h#includeEGL/egl.hintmain(intargc,char**argv){intmatchargc1!strcmp(argv[1],match);intwant_alphaargc2!strcmp(argv[2],alpha);Display*dpyXOpenDisplay(NULL);if(!dpy){puts(XOpenDisplay FAILED);return1;}intscrDefaultScreen(dpy);EGLDisplay edpyeglGetDisplay((EGLNativeDisplayType)dpy);EGLint major,minor;if(edpyEGL_NO_DISPLAY||!eglInitialize(edpy,major,minor)){printf(EGL init FAILED 0x%x\n,eglGetError());return1;}EGLint attrs[]{EGL_SURFACE_TYPE,EGL_WINDOW_BIT,EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,EGL_RED_SIZE,8,EGL_GREEN_SIZE,8,EGL_BLUE_SIZE,8,want_alpha?EGL_ALPHA_SIZE:EGL_NONE,want_alpha?8:0,EGL_NONE};EGLConfig cfgs[64];EGLint n0;if(!eglChooseConfig(edpy,attrs,cfgs,64,n)||n1){printf(no config 0x%x\n,eglGetError());return1;}EGLConfig cfgcfgs[0];EGLint vis0,alpha0,bufsz0;eglGetConfigAttrib(edpy,cfg,EGL_NATIVE_VISUAL_ID,vis);eglGetConfigAttrib(edpy,cfg,EGL_ALPHA_SIZE,alpha);eglGetConfigAttrib(edpy,cfg,EGL_BUFFER_SIZE,bufsz);Visual*vDefaultVisual(dpy,scr);intdepthDefaultDepth(dpy,scr);XVisualInfo templ{.visualidvis},*vinfoNULL;intnvi0;if(match){vinfoXGetVisualInfo(dpy,VisualIDMask,templ,nvi);if(nvi0){vvinfo[0].visual;depthvinfo[0].depth;}}XSetWindowAttributes swa{0};swa.colormapXCreateColormap(dpy,RootWindow(dpy,scr),v,AllocNone);swa.event_maskStructureNotifyMask;Window winXCreateWindow(dpy,RootWindow(dpy,scr),0,0,320,240,0,depth,InputOutput,v,CWColormap|CWBackPixel|CWBorderPixel|CWEventMask,swa);XMapWindow(dpy,win);XSync(dpy,False);printf(vendor%-14s cfg:visual0x%02x alpha%d buffer%d | window visual0x%02x depth%d | ,eglQueryString(edpy,EGL_VENDOR),vis,alpha,bufsz,(unsigned)XVisualIDFromVisual(v),depth);eglBindAPI(EGL_OPENGL_ES_API);EGLSurface seglCreateWindowSurface(edpy,cfg,(EGLNativeWindowType)win,NULL);EGLint erreglGetError();if(sEGL_NO_SURFACE)printf(SURFACE FAILED error0x%04x\n,err);elseprintf(SURFACE OK\n);returnsEGL_NO_SURFACE?2:0;}附錄 B 命令速查# 攝像頭是否正常lsusbls-l/dev/video* getfacl-p/dev/video0 journalctl-k-b|grep-iuvc ffmpeg-fv4l2-list_formatsall-i/dev/video0fuser-v/dev/video*# 誰占著攝像頭# 騰訊會(huì)議的媒體日志L~/.local/share/wemeetapp/Saas/Logs/xcast_$(date%Y%m%d%H).loggrep-ccreate_window_surface succeeded$Lgrep-cEGL_NO_SURFACE$Lgrep-aoErender\.avg\.fps\.[0-9.]$L|sort|uniq-cgrep-aoESnd\[big Cap:[0-9x./]$L|tail-3# 渲染器依賴哪個(gè)平臺(tái)nm-D--undefined-only /opt/wemeet/lib/libxcast.so|grep-iEegl|wl_|X[A-Z]# EGL 屬性數(shù)組在 .rodata 中的位置objdump-d--no-show-raw-insn /opt/wemeet/lib/libxcast.so|grep-B20call.*eglChooseConfig# 窗口真實(shí)的深度與 visualDISPLAY:0 xwininfo-idwid|grep-EDepth|Visual|Map State# 在 XWayland 上抓窗口內(nèi)容DISPLAY:0 xdotool search--name騰訊會(huì)議DISPLAY:0 xwd-idwid-silent-outw.xwdffmpeg-iw.xwd w.png# 修復(fù)后確認(rèn)進(jìn)程環(huán)境tr\0\n/proc/pid/environ|grep-EEGL_VENDOR|LIBGL_ALWAYSgrep-aoElibEGL[^ ]*\.so[^ ]*/proc/pid/maps|sort-u# 刷新用戶級(jí)桌面條目update-desktop-database ~/.local/share/applications參考Khronos EGL 注冊(cè)與規(guī)范https://registry.khronos.org/EGL/Mesa 環(huán)境變量文檔LIBGL_ALWAYS_SOFTWARE等https://docs.mesa3d.org/envvars.htmllibglvnd 項(xiàng)目EGL 實(shí)現(xiàn)分發(fā)與廠商配置https://gitlab.freedesktop.org/glvnd/libglvnd騰訊會(huì)議 Linux 客戶端下載https://meeting.tencent.com/download-center.html