共用方式為


自動顯示開關

重要

某些資訊與發行前版本產品有關,在發行前可能會大幅修改。 Microsoft未就此處提供的資訊提供任何明示或默示擔保。

本文說明自動顯示切換(ADS)功能,這項功能支持筆記型電腦的內部面板無縫地在整合式 GPU(iGPU)和獨立 GPU(dGPU)之間切換。 ADS 是選用的 WDDM 功能,從 Windows 11 版本 24H2 Update 2025.01D (WDDM 3.2) 開始支援。

在本文中:

  • GPU0 是指整合式面板目前連接的 GPU。
  • GPU1 是指該面板將切換到的 GPU。

概述

市面上發售的某些筆記型電腦具有多工器(多工器,mux)裝置,可讓內部面板在整合式 GPU (iGPU) 和獨立 GPU (dGPU) 之間切換。 圖形驅動程式目前在不通知作業系統的情況下於這些筆記型電腦中引發並執行切換,這導致不理想的使用者體驗。

ADS 可讓 OS 控制系統中多任務裝置的使用,以在掃描到內部面板時,在 iGPU 與 dGPU 之間切換。 因此,OS 可以提供更佳的用戶體驗。

ADS 的初始版本僅支援在 iGPU 與 dGPU 之間切換內部面板。 未來,此功能可能會擴充,以支援筆記型電腦中的外部連接器多工處理。

高階設計

一般而言,系統必須確保內部面板的內容在切換進行時,不會顯示任何閃爍或異常。 OS 不會將此功能限制為任何特定的顯示通訊協定。 本文著重於如何使用 eDP 實作 ADS,但有更多業界標準可以使用(例如 MIPI 或 DSI)。 如果平台設計可以達到相同的體驗,而不需要任何其他操作系統變更,則可以自由使用另一個顯示聯機通訊協定。

本節中的小節會識別功能的設計層面,並詳細說明每個層面的高階方法。

多工器裝置的控制

為了減少 iGPU 和 dGPU 圖形驅動程式之間的依賴性,將多工器公開為一個獨立的裝置,讓作業系統可以在不依賴圖形驅動程式的情況下進行控制。 此方法的優點如下:

  1. 這可減少顯示卡驅動程式的複雜性,因為驅動程式不需要知道如何控制 OEM 可能使用的每一種不同多工器。
  2. 它可減少或排除圖形驅動程序之間的相依性,這可減少驅動程式更新,並讓 OEM 更輕鬆地選取 GPU 和多任務處理。
  3. 當圖形驅動程式無法使用時,OS 可以切換多任務處理器。

介紹多工器裝置

由於此解決方案適用於內部 iGPU 與 dGPU 之間的多任務處理,因此透過 ACPI 公開多任務處理是合理的。

多工器驅動程式的功能

多工器驅動程式必須符合下列高階功能需求:

  1. 它必須提供多工器的狀態,目前是由哪個對象控制內部面板,以及任何能力標章。
  2. 它必須提供觸發開關並報告開關狀態的方法。

如需有關多工 ACPI 裝置及其方法的詳細資訊,請參閱 ACPI

為了實現無縫切換,mux 裝置在 GPU 切換期間必須始終滿足以下條件:

  1. 面板電源。 在任何時刻,多工器需要由任一 GPU 提供面板電源。 讓兩個 GPU 同時提供面板電源是可以的。
  2. 切換時,啟用亮度的控制訊號會來自這兩個 GPU。
  3. 切換時,兩個 GPU 的亮度等級(脈衝寬度調變)。

多工器在兩個 GPU 和面板之間切換以下資訊:

  1. 支援亮度的控制訊號
  2. 亮度等級(脈衝寬度調節)
  3. DisplayPort (DP) 輔助線
  4. 熱插拔偵測(HPD)線
  5. DP 資料行

在面板不啟用時,復用器必須具備切換能力。 至少在內部面板切換時,mux 不應該在切換時觸發任何發送至 GPU 的 HPD 訊號。

GPU 驅動程式不應該呼叫 mux ACPI 方法。

自動顯示開關 DDI

已新增數個 DIS 以滿足多任務需求。 OS 在多路切換期間,透過以下函式來呼叫驅動程式的 DDI,總共有五個不同的呼叫點。 各種呼叫取決於交換器的階段,以及驅動程式是否控制目前具有顯示器控制權的 GPU。

DDI 描述
DxgkDdiDisplayMuxPreSwitchAway 呼叫目前連線到顯示器的驅動程式。 此呼叫會通知驅動程序系統正計劃將顯示器切換至另一個 GPU(從 GPU0 切換至 GPU1)。
DxgkDdiDisplayMuxPreSwitchAwayGetPrivateData 指示從目前連接到面板的驅動程式中收集任何專用交換器數據(來自 GPU0)。
DxgkDdi顯示多工器預切換至 呼叫目前未連線到顯示器的驅動程式。 此呼叫會通知驅動程式作業系統正計劃將顯示器切換至此 GPU(至 GPU1)。
DxgkDdiDisplayMuxSwitchCanceled 呼叫驅動程式以指出切換指令序列在完成之前已被取消。
DxgkDdiDisplayMuxPostSwitchAway 多任務交換器已完成,且 GPU0 的驅動程式已不再連線到顯示器。
DxgkDdiDisplayMuxPostSwitchToPhase1 多任務交換器已完成,GPU1 的驅動程式現在已連線到顯示器。 此驅動程式現在應該會執行階段 1 工作。
DxgkDdiDisplayMuxPostSwitchToPhase2 多任務交換器已完成,GPU1 的驅動程式現在已連線到顯示器。 此驅動程式現在應該會執行階段 2 工作。
DxgkDdiDisplayMuxUpdateState 在介面卡啟動時呼叫,並返回至 D0 電源狀態,以便讓驅動程式知道目前的多工器狀態。

司機需要在每個階段完成的具體行動。 本文稍後會說明這些動作。

如需 ADS 相關 DDI 更新的完整清單,請參閱 關於自動顯示切換的 WDDM DDI 變更

在 GPU0 與 GPU1 之間共享數據

在某些情況下,在下列情況下可以建置更好的用戶體驗:

  • GPU0 和 GPU1 來自相同的 IHV。
  • GPU0 可以將作業系統無法辨識的顯示組態相關信息傳遞給 GPU1。

數據 Blob 由 GUID 描述,如果 GPU1 的驅動程式了解這個數據 Blob 的話,就能快速識別。 概括而言,OS 會呼叫 GPU0,在切換之前取得 Blob GUID 和數據,然後將它傳遞至 GPU1,再要求顯示器的 HPD。

GPU1 的驅動程式負責:

  • 檢查它是否瞭解 Blob 的 GUID。
  • 驗證 Blob 中每個數據元素,以避免對 Blob 中格式不正確的數據產生任何有害影響。

驅動程式互操作性

如果WDDM驅動程序支援 ADS,它必須支援 ADS,無論其執行所在的 OEM 系統,或系統上的其他 GPU 為何。

切換序列

雖然在技術上,當 GPU 的驅動程式停止時,可以切換退出 GPU,但目前不支援這種情況。 因此,只有當兩個 GPU 都載入了支援切換 DDI 的驅動程式時,才會進行切換。

當面板為使用中時,下列序列是整個交換器序列的高階檢視,其中 GPU0 和 GPU1 分別代表 iGPU 和 dGPU。 GPU0 目前通過多工切換器連接到內部面板,我們希望切換到 GPU1 輸出到面板。

  1. 切換呼叫是在 API 層級進行。
  2. 作業系統會收集目前內部面板的狀態屬性(如 HDR、模式、刷新率等等),並檢查暫時顯示模式。
  3. 作業系統會停用任何顯示佈局的運行,原因是系統中任何 GPU 的熱插拔檢測信號 (HPD)。
  4. 操作系統(OS)會呼叫 GPU1 驅動程式的 DxgkDdiDisplayMuxPreSwitchTo,並傳遞目前的亮度級別。 只有在蓋子開啟時,驅動程式才應該執行下列動作:
    • 開啟面板的電源。
    • 設定亮度啟用信號。
    • 設定OS通過的亮度等級。
  5. 為確保在 mux 切換之後才可處理蓋 HPD 偏移事件,OS 會停用在 GPU0 上呼叫 DxgkDdiQueryConnectionChange
  6. OS 會呼叫 GPU0 的驅動程式 DxgkDdiDisplayMuxPreSwitchAway DDI。 駕駛應該:
    • 如果蓋子處於作用中狀態,請啟用面板上的 PSR1(面板自我刷新 1),並確保不會停用該功能,直到稍後的 OS 要求停用。
    • 將封包新增至其聯機變更清單,並將 DXGK_CONNECTION_CHANGEConnectionStatus 設為 MonitorStatusDisconnected,然後將 MonitorConnect.MonitorConnectFlags.DisplayMuxConnectionChange 設為 1。
    • GPU0 無法將任何針對蓋子目標的連線變更封包新增到其佇列中。 操作系統錯誤會檢查是否滿足該條件。
    • 將任何私人 ADS 數據 Blob 的大小(GUID 和數據)傳回 OS。 如果 GPU0 驅動程式在進行此呼叫時失敗,它必須在返回前移除放入佇列的任何 ADS 連線狀態封包。
  7. 如果 GPU0 的驅動程式傳回非零的私人數據大小,OS 會配置該大小,並將它傳遞給 GPU0 的 DxgkDdiDisplayMuxPreSwitchAwayGetPrivateData 回呼以取得私人交換器數據。
  8. OS 會呼叫 mux 的 ACPI 方法,以從 GPU0 切換至 GPU1。
  9. OS 允許再次呼叫 GPU0 的 DxgkDdiQueryConnectionChange
  10. OS 會呼叫 GPU0 的 DxgkDdiQueryConnectionChanges 來處理 MonitorStatusDisconnected 連線封 包,並將 DisplayMuxConnectionChange 設為 1。
  11. OS 會呼叫 GPU0 的 DxgkddiSettimingsfromvidpn 來停用要切換的顯示器路徑。 GPU0 的驅動程式應該:
    • 關閉面板電源。
    • 停用亮度訊號。
    • 停止將亮度等級傳送至多工器。
  12. 作業系統會處理螢幕顯示切換的過程。 它不會觸發拓撲變更,以避免不必要的拓撲變更。
  13. OS 會呼叫 GPU1 的 DxgkDdiDisplayMuxPostSwitchToPhase1 回呼,並傳遞它從 GPU0 取得的任何 ADS 私有資料段。 駕駛應該:
    • 判斷蓋子是否開啟或關閉。
    • 使用 DXGK_CONNECTION_CHANGE's,將封包新增至其連線變更清單:
      • MonitorConnect.MonitorConnectFlags.DisplayMuxConnectionChange 位元已設定。
      • ConnectionStatus 設定為 MonitorStatusConnected如果蓋子已開啟,或設定為 MonitorStatusDisconnected如果蓋子已關閉。
    • 如果蓋子關閉,請關閉電源和面板上的亮度啟用訊號。
  14. 如果操作系統尚未呼叫 DxgkDdiQueryAdapterInfo 來對 GPU1 的內部目標使用 DXGKQAITYPE_INTEGRATED_DISPLAY_DESCRIPTOR2,則會這麼做。 由於此呼叫,OS 也會呼叫 DxgkDdiQueryDeviceDescriptor
  15. OS 會呼叫 GPU1 的 DxgkDdiQueryConnectionChange 來處理其連線變更清單中的事件。 此呼叫會導致 DxgkDdiQueryDeviceDescriptor 針對要輸入 HPD 的新監視器呼叫。
  16. OS 允許因為 HPD 而產生的顯示拓撲變更。
  17. OS 會以異步方式處理來自 GPU0 和 GPU1 的連線封包,DisplayMuxConnectionChange 設為 1。
  18. 如果 GPU1 已排入佇列 MonitorStatusConnected
    • OS 會呼叫 GPU1 的 DWM 函式來列舉模式。
    • GPU1 上呼叫 DxgkddiSettimingsfromvidpn,以啟動顯示路徑。
    • DWM 會在 GPU1 上轉譯並呈現畫面格到顯示路徑。
    • OS 會等候第一個畫面顯示。
  19. OS 會呼叫 GPU1 的 DxgkDdiDisplayMuxPostSwitchToPhase2 回呼,如果 GPU1 已將 MonitorStatusConnected 排入佇列,驅動程式應該關閉顯示上的 PSR1; 否則,不需要執行任何動作。
  20. OS 會呼叫 GPU0 的 DxgkDdiDisplayMuxPreSwitchAway。 雖然驅動程式沒有預期的動作,但對於任何與切換相關的驅動程式清理或記錄,此呼叫仍然非常有用。
  21. OS 會收集目前內部面板狀態的屬性。 如果面板狀態與先前儲存的內容不同,OS 會觸發遙測。

iGPU->dGPU 和 dGPU->iGPU 的交換器序列相同。 當面板處於閒置狀態時,可能需要切換多工器。 在此情況下,不需要此序列,而且作業系統只需在多工器上呼叫ACPI方法即可切換。

大部分的 OS 都不知道驅動程式處於 PSR 模式。 因此,驅動程式仍然需要產生 Vsync 同步處理、報告翻轉為已完成等等,即使使用者看不到這些情況也一樣。

恢復過程

如果在切換順序的任何階段發生失敗,則會執行下列清除:

  1. 當 GPU0 的 DxgkDdiDisplayMuxPreSwitchAway 呼叫成功但其DxgkDdiDisplayMuxPostSwitchAway 尚未被呼叫時,作業系統會呼叫 GPU0 的 DxgkDdiDisplayMuxSwitchCanceled
  2. 如果 OS 呼叫了 GPU1 的 DxgkDdiDisplayMuxSwitchCanceled,這表示已成功呼叫了 GPU1 的 DxgkDdiDisplayMuxPreSwitchTo 但尚未呼叫其 DxgkDdiDisplayMuxPostSwitchToPhase2
  3. 如果顯示拓撲變更被停用,操作系統會重新啟用它們。
  4. 如果停用,OS 會重新啟用在 GPU0 上呼叫 DxgkDdiQueryConnectionChange
  5. OS 會在蓋子所連接的 GPU 上檢測蓋子的連接狀態。
  6. OS 會觸發集合顯示組態 (SDC) 重設。 具有透過多工器將面板連接起來的驅動程式(由 DxgkDdiDisplayMuxSwitchCanceled傳回的)需要確保 PSR 已停用。

切換期間可能發生的異常事件

  • 使用者外接或拔除顯示器

    作為切換序列的一部分,我們會停用作業系統對 HPD 事件的處理。 如此一來,任何 HPD 都會排入佇列處理,並且與蓋子在一個原子操作中一同抵達。

  • 另一個應用程式在切換時呼叫 SDC

    當切換正在進行時,會暫停對 SDC 的呼叫,並在處理完切換後執行。

  • 切換時驅動程式被停用

    當驅動程式中止時,交換序列中的呼叫失敗,復原序列會啟動。 PnPStop 區段 也會詳細說明如何確保畫面一律可見。

蓋子關閉場景

驅動程式通常可以使用下列任一方法來偵測蓋子開啟/關閉事件:

不過,整體而言,對於 WDDM,驅動程式必須使用 DxgkDdiNotifyAcpiEvent 方法,因為這允許 Dxgkrnl 狀態和驅動程式狀態保持同步。由於 iGPU 和 dGPU 在切換序列中皆可成為 GPU1,因此所有 ADS 驅動程式追蹤蓋子狀態是合理的,即使蓋子不在其控制下。

一旦 OS 處理來自 GPU0 的 DisplayMuxConnectionChange 事件,它會認為 GPU0 不再擁有蓋子開關狀態,因此在切換蓋子之前,GPU0 無法為該目標報告更多連線狀態封包。 如果 GPU0 這樣做,作業系統將會進行錯誤檢查。 一旦操作系統從 GPU1 處理 DisplayMuxConnectionChange,它會將 GPU1 視為蓋子狀態的擁有者。 這兩個事件之間的任何蓋子開啟或關閉事件都可以被忽略,因為預期 GPU1 會了解蓋子的狀態並回報正確的 DisplayMuxConnectionChange 封包。

當作業系統認為驅動程式擁有面板時

下表描述作業系統將 GPU 視為擁有面板的順序階段。 具有該功能的 GPU 可以使用切換序列來報告連線變更。 步驟編號來自先前所述的切換序列。

階段來自 階段至 哪個 GPU 控制顯示面板
切換前 步驟 5 GPU0
步驟 6 步驟 12 無 GPU
步驟 13 切換之後 GPU1

當 GPU 未控制面板時,OS 錯誤會檢查驅動程式佇列中是否有多任務目標連線變更封包。

控制面板自我重新整理 (PSR)

ADS 功能會使用 PSR 來避免轉換期間發生問題。 具體而言,會使用 PSR1 (全螢幕更新模式),讓 GPU0 和 GPU1 不需要交涉要使用的 PSR 模式。

即使在 PSR1 內,也有面板需要支援的選擇性功能:

匯入能力 詳細數據 透過 被公開
DPCD & eDP 版本 公開 eDP v1.3 或更高版本。 DPCD
PSR 功能和版本 接收端應支援第 1 版本。 DPCD 00070h 位 7:0
支援 VSC SDP 以傳達 PSR 狀態 僅適用於 PSR;接收端至少應支援修訂版 2,並可傳送最多 8 個有效位元組以表示 PSR 狀態和 CRC 值。 DPCD 170
接收端應正確報告 PSR 相關狀態 接收端應曝光狀態;例如,連結 CRC 錯誤、RFB 存儲錯誤、接收裝置自我刷新狀態、最大重新同步畫面計數、接收端的最後實際同步延遲,以及上次接收到的 PSR SDP。 DPCD 2008h,2009h,200Ah 應反映接收端的正確狀態。

當 GPU1 作為 OS 發出的 DxgkddiSettimingsfromvidpn 呼叫的一部分執行鏈路訓練時,驅動程式不知道 GPU0 所使用的 DP 通道和頻寬設定,因此必須執行完整的鏈路訓練序列,而不是快速的鏈路訓練。 OS 不會交涉 GPU 之間的任何 PSR 原則,因此面板必須支援 GPU 將使用的所有 PSR 版本和功能。 例如,面板必須支援這樣的情境:首先,GPU0 可能使用 PSR2 搭配一組特定的功能,然後 PSR1 將用於模式切換,最後 GPU1 可能會使用 PSR2 但搭配另一組不同的功能。

確保面板在切換期間保持在 PSR 中

當 GPU1 在面板上設置模式時,無法保證當面板處於 PSR 狀態時,GPU1 所設置的連結屬性會與 PSR 進入模式相符。 例如,重新整理速率或實際大小可能會變更。 今天,DP 或其他業界標準都沒有辦法讓面板在連結屬性變更時報告它可以將自身保持在 PSR 中。 長期而言,我們想要讓這項功能新增至 DP 規格。 在發生這種情況之前,若是啟用 ADS 的系統,OEM 必須挑選 TCon/panel/Mux 組合,該組合可以保留在 PSR 中,而鏈接屬性(例如,重新整理速率、作用中大小)會在 EDID 中公開的任何兩個組合之間變更。 此方法可確保 PSR 可在切換期間保持作用中。

為了讓 ADS HLK 測試在切換過程中確認 PSR 被維持,我們需要一種方法讓 OS 知道當 GPU1 測試模式後,若 PSR 未作用時。 挑戰在於,尚未定義當顯示面板在鏈接訓練中無法支援 PSR 時,它會如何反應。

DxgkDdiDisplayMuxPostSwitchToPhase2中,驅動程式會傳回 pWasPanelInPSR 中的布爾值,以通知 OS 是否偵測到面板不在 PSR 中。

內部面板的EDID

若要讓 OS 在選取連接不同監視器的顯示模式和顯示佈局時提供預期的行為,這兩個 GPU 都必須報告內部顯示的 EDID/DisplayId。 這項需求可確保儲存顯示模式和拓撲的 CCD 資料庫會挑選這些相同的設定,而不論 GPU 控制內部顯示器。

驅動程式向OS報告的EDID應該是使用 aux 命令從面板查詢的EDID,而不需要任何修改。

目前操作系統會在啟動報告內部面板的驅動程式時呼叫 DxgkDdiQueryAdapterInfo(DXGKQAITYPE_INTEGRATED_DISPLAY_DESCRIPTOR2)。 如果多工器從該整合式目標切換走,驅動程式將無法與面板溝通以收集所需資訊。 解決方案是,當驅動程序啟動且多工器從其內部目標切換走時,OS 會延遲呼叫 DxgkDdiQueryAdapterInfo(DXGKQAITYPE_INTEGRATED_DISPLAY_DESCRIPTOR2),直到多工器第一次切換回內部目標為止。

操作系統如何決定是否在系統上啟用 ADS 功能,並允許切換

OS 會執行下列檢查清單,以判斷系統上是否有 ADS 可用。 所有檢查都必須為「真」,才能支援 ADS。

  1. 有一個 GPU 被標示為整合式混合式(DXGK_DRIVERCAPS.HybridIntegrated
  2. 有一個 GPU 標示為離散混合式 (DXGK_DRIVERCAPS。HybridDiscrete
  3. 「從步驟 1 和步驟 2 的 ACPI DMID 方法中傳回的 mux ACPI 名稱是匹配的。」
  4. ACPI 多工器裝置具有 ACPI DMQU、DMCF 和 DMSL 方法。
  5. 多工 ACPI DMQU 方法從其中一個 GPU 傳回了內部面板目標的 ACPI 名稱。
  6. ADS 目前僅支援具有單一內部面板的系統。
  7. 要麼:
    1. GPU0、GPU1 和 Mux ACPI 全都報告完整的 ADS 支援。
    2. GPU0、GPU1 和 Mux ACPI 都會報告實驗性或完整 ADS 支援,並設定 EnableMDMExperimentalFeature 登錄機碼。

條件 1 和 2 意味著必須啟動這兩個適配器,才能切換多工器。

管理 ADS 功能發佈的品質

若要讓 ADS 提供良好的用戶體驗,下列所有元件都必須完美搭配運作:

  1. OS 會顯示多任務處理功能。
  2. 用於多工切換的平臺 ACPI 方法。
  3. iGPU 和 dGPU 驅動程式中的顯示多工切換功能。

為了協助 IHV/OEM 在版本中包含未達出貨品質的程式代碼,他們可以提供下列任一層級的 ADS 支援:

  • 不支援:驅動程式不支援任何 ADS 功能。
  • 開發支援:驅動程式支援 ADS,但驅動程式的實作仍在開發中,不應在此用途之外使用。
  • 實驗性支持:驅動程序支援 ADS,但尚未達到出貨品質。 OS 預設不會啟用 ADS,但可以設定為啟用它。
  • 完整支援:驅動程式在出廠品質上支援 ADS。 OS 會考慮驅動程序支援 ADS。

顯示切換後應保持不變的屬性

顯示切換器不應變更下列任何顯示屬性:

  1. 桌面解析度
  2. VidPn 路徑(包括 VidPn 來源模式、目標模式、縮放等等)
  3. DPI
  4. 夜燈設定
  5. 伽馬
  6. 顯示拓撲
  7. HDR 開啟/關閉
  8. SDR 白色亮度
  9. 色彩配置檔
  10. 監視器的 OPM 目標類型
  11. 顯示亮度

為實現無縫切換體驗,調整 GPU 限制

若要為使用者提供順暢的切換體驗,應該在切換後將顯示器設定為和切換前相同。 有一些 GPU 功能,兩個 GPU 都需要相同的支援才能達到此行為。 例如,如果一個 GPU 支援 HDR,而另一個 GPU 則不支援,則在一個 GPU 上啟用 HDR 時,切換不會順暢。

下表列出 GPU 顯示功能和功能,並描述兩個 GPU 之間的對齊需求。

功能 GPU 需要有無縫支援功能
HDR 如果面板支援 HDR,則這兩個 GPU 都必須支援 fp16 HDR 或不支援 HDR。
Hw 游標 不。 OS 適應不同的游標功能,而不會對使用者造成明顯干擾。
MPO 不。 OS 會適應不同的 MPO 功能,而不會對使用者造成明顯的中斷。
PSR 這兩個 GPU 都需要支援這項功能。
EDID/DisplayID 這兩個 GPU 都必須公開相同的 EDID/DisplayId。
亮度上限 這兩個 GPU 都必須支援相同的亮度介面和亮度上限。
亮度等級 這兩個 GPU 都需要公開相同的亮度等級和間隔。
解析度 這兩個 GPU 都需要支援相同的來源模式和目標解析度。
更新率 如需詳細資訊,請參閱 如果 GPU1 不支援 GPU0 在 執行面板的重新整理速率,請參閱 問題。
動態刷新速率 不。 作業系統會適應不同的虛擬更新率支援。
變數重新整理速率 如需詳細資訊,請參閱 如果 GPU1 不支援 GPU0 在 執行面板的重新整理速率,請參閱 問題。

如果 GPU1 不支援 GPU0 執行面板的重新整理速率,則發生問題

如果 GPU1 不支援與 GPU0 相同的模式,則縮減模式可能會儲存在顯示拓撲資料庫中。 然後,當系統切換回 GPU0 時,將會設定縮減模式。 例如,如果 GPU0 支援 120Hz,但 GPU1 只支援 60Hz,則可能會發生下列序列:

  1. 系統已設定,因此 GPU0 會控制顯示器,且模式為 120Hz。
  2. 用戶手動切換至 GPU1。
  3. 顯示拓撲資料庫已針對顯示器儲存 120Hz,但 GPU1 不支援它,因此 OS 會挑選 60Hz。
  4. 60Hz 會設定並儲存在顯示拓撲資料庫中。
  5. 用戶手動切換回 GPU0。
  6. 顯示拓撲資料庫會從資料庫讀取 60Hz。

為了提供最佳體驗,OEM 應該選取支援內部面板最大重新整理速率的 iGPU 和 dGPU。 如果不可能,且一個 GPU 無法支援面板的最大重新整理速率,則支援面板重新整理速率的 GPU 必須支援包含下列範圍的 Windows 動態重新整理速率 (DRR) 功能:

  • 其他 GPU 的最高更新率。
  • 內部面板的最高重新整理速率。

例如,如果面板可以支援 300Hz,而 iGPU 只能支援 60Hz,則 dGPU 必須支援至少 60Hz 到 300Hz 的 VRR。

總結來說,重新整理速率的 ADS 需求可能是以下其中之一:

  1. iGPU 和 dGPU 支援內部面板的最大重新整理速率。
  2. 支援內部面板最大重新整理速率的 GPU 必須支援動態更新率(DRR),其範圍應從其他 GPU 能支援的最高重新整理速率,到內部面板的最大重新整理速率。

HDR 和 Dolby Vision

OS 會在切換後在 GPU1 的內部面板上設定與切換前 GPU0 的內部面板上相同的 HDR/Dolby 視覺狀態。 用戶不應該注意到任何變更。

夜燈

Nightlight 是透過 WDDM gamma 或色彩矩陣 DIS 實作。 在這兩種情況下,OS 會自動在切換至 GPU1 之後設定相同的夜燈層級,如同切換前 GPU0。

色彩配置檔

這個作業系統會在切換之後,對面板套用與切換之前相同的色彩配置檔。

顯示錯誤檢查畫面

OS 目前支援在非 POST 裝置上顯示錯誤檢查畫面。 當錯誤檢查發生時,作業系統:

  • 不進行多工器切換。
  • 使用目前的OS支援來顯示錯誤檢查畫面。

評估潛在目標以顯示錯誤檢查時,OS 會略過連線到切換至不同目標之多任務的任何目標。

在 HPD 離開 GPU0 已經處理完成,但來自 GPU1 的 HPD 尚未完全處理的這小段時間內。 如果在這個期間發生錯誤檢查,使用者就不會看到錯誤檢查。 如果在 PSR 仍啟用的短時間內發生錯誤檢查,則控制顯示器的驅動程式應確保在 OS 執行 DxgkDdiSystemDisplayEnable時,面板不處於 PSR 模式。

內容調適型亮度演算法

在理想的世界中,這兩個 GPU 所使用的內容調適型演算法應該會產生相同的效果。 不過,當內部面板切換時,相同的效果可能不會出現,而且使用者可能會注意到差異。

亮度數據

為了確保使用者不會注意到因切換而導致的亮度變化,GPU0 和 GPU1 所暴露的所有亮度屬性都必須相同。 此需求可確保在切換前位於 GPU0 的任何亮度等級,在切換後都能於 GPU1 上支援。

若要這樣做,GPU0 和 GPU1 的驅動程式必須:

  1. 使用相同的亮度介面,DXGK_BRIGHTNESS_INTERFACE_2DXGK_BRIGHTNESS_INTERFACE_3,強烈建議使用第 3 版。
  2. 針對 brightness v3 介面,這兩個驅動程式都必須公開以 nits 為基礎的或未調整的亮度。
  3. 針對 brightness v2 介面,這兩個驅動程式必須從 `GetPossibleBrightness` 傳回完全相同的亮度等級
  4. 針對 brightness V3 介面,這兩個驅動程式都必須傳回完全相同的範圍,也就是說,每個驅動程式都應該從 GetNitRanges傳回相同的 DXGK_BRIGHTNESS_GET_NIT_RANGES_OUT 結構。
  5. 驅動程式用來將作業系統提供的亮度等級轉換為面板特定設定的內部數據表必須相同。

在大部分的筆記型電腦中,GPU 驅動程式會以非標準的方式從平台取得部分或全部的亮度等級數據。 我們預期此平臺對 GPU 數據交換可能需要擴充,才能達到這些需求。

雖然在啟動適配器時查詢了亮度介面,但在內部面板完成 HPD(檢測即插即用)之前,OS 不會調用任何亮度介面的 DDI。 HPD 會在多工切換器切換至 GPU 之後發生,因此驅動程式可以在當時存取內部面板的 EDID。

我們了解驅動程式有 IHV 特定方式,可針對不支援 PWM 的面板設定面板亮度。 不過,此方法會使 TCon 的複雜度提升,因為它可能必須根據所連接的 GPU 與多工器的不同,以 IHV 特定的方式支援取得亮度。

多工器的開機設定

系統韌體在系統啟動時會控制連接到內部面板的 GPU。 操作系統會記錄上次控制面板的 GPU。 然後,在開機順序期間,OS 會在必要時切換多任務處理器,讓正確的 GPU 控制面板。

若要在需要使用多工器切換時保留任何開機映像,則只有在下列情況下才會執行切換:

  • 這兩個 GPU 都已啟動。
  • OS 已經從開機圖形轉變為由 DWM/殼層控制輸出。

因此,在 GPU 上完成 DxgkddiSettimingsfromvidpn 呼叫後,會進行切換,這時控制內部面板的 GPU 將導致使用者在面板處於 PSR 的切換過程中,遇到畫面凍結的情況。

將多工器資訊提供至驅動程式

這項功能是刻意設計為讓操作系統呼叫驅動程式以提供資訊,而不是提供驅動程式隨時可以呼叫的回呼。 此方法可避免驅動程式在切換順序期間查詢 OS 狀態時感到困惑。

OS 會呼叫驅動程式的 DxgkDdiDisplayMuxUpdateState DDI,以在下列情況下提供驅動程式目前的多任務狀態:

  1. 在驅動程式啟動時,將使驅動程式避免在面板未連線時進行頻繁的輪詢序列。
  2. 從 Dx返回到 D0 時。 從某些電源狀態(例如休眠)返回時,韌體可能必須重設多工器,因此驅動程式不知道狀態。

這些案例與切換序列中涉及的一般 DDIs 一起,可確保驅動程式可以在 GPU 處於作用中狀態時判斷多工器的切換方式。

在此功能的第一個版本中,當內部面板未啟用時,沒有計劃切換多工器,因此所有開關都會經歷相同的序列。

配接器開始時間

當驅動程式啟動時,它必須回應來自作業系統的輪詢要求。 驅動程式可以嘗試嘗試透過嘗試通訊來探索多任務是否切換到它們,但可能很耗時或不可靠。 作為 GPU 啟動順序的一部分,作業系統會針對連接到多工器的每個目標呼叫 DxgkDdiDisplayMuxUpdateState DDI,並指出是否已經切換至該目標。

當驅動程式啟動時,它必須回應來自作業系統的輪詢要求。 驅動程式的操作員可以嘗試透過與作業系統通訊,探索多工交換器是否已切換至其 GPU,但這可能會非常耗時或不可靠。

作為 GPU 啟動序列的一部分,OS 會為每個連接到多工器的目標呼叫 DxgkDdiDisplayMuxUpdateState,並檢查多工器是否切換到該目標。 OS 會在呼叫任何輪詢執行介面之前,向驅動程式報告多工器是否切換至驅動程式的 GPU。

ADS 驅動程式會以相同的方式向 OS 回報內部面板,OS 呼叫 DxgkDdiQueryAdapterInfoDXGKQAITYPE_INTEGRATED_DISPLAY_DESCRIPTOR2) 來查詢內部面板詳細數據。 驅動程式必須確保 DXGK_CHILD_CAPABILITIES HpdAwareness 設定為 HpdAwarenessInterruptible,針對任何連線到多路復用器的目標。

D0 轉換

每當具有多工 (mux) 的 GPU 從低功耗狀態返回通電狀態時,OS 會呼叫 DxgkDdiDisplayMuxUpdateState,以通知驅動程式該多工是否連接到其目標或切換到其他 GPU。

開機順序

以下的開機順序著重於 ADS 的特定方面。 在此順序中,系統會使用下列項目開機:

  • 連接到多任務組的 iGPU。
  • 重新啟動前用戶的最後一個設定是多工器已連接到 dGPU。

開機順序本質上是異步的,因此此順序僅供範例之用。

  1. 系統開啟,並且 iGPU 會透過多工器連接到面板。
  2. iGPU 會在面板上顯示開機畫面。
  3. Windows 會在內部蓋上載入並顯示開機動畫。
  4. 由於 iGPU 和 dGPU 上的_DEP,OS 的多任務驅動程式會在任一 GPU 驅動程式之前啟動。 多工器驅動程式會使用 ACPI 呼叫來確保多工器已正確設定。 多任務驅動程式會驗證 ACPI 多任務實作是否符合 ADS 需求。
  5. Dxgkrnl 呼叫 DxgkDdiAddDevice 為 iGPU。
  6. Dxgkrnl 會呼叫 DxgkDdiQueryInterfaceDXGK_DISPLAYMUX_INTERFACE)以供 iGPU 使用。 即使目前的系統不支援 ADS,驅動程式也會在支援 ADS 時傳回其介面。
  7. Dxgkrnl 呼叫 DxgkDdiDisplayMuxGetDriverSupportLevel,以取得驅動程式的 ADS 支援層級。
  8. Dxgkrnl 調用 DxgkDdiDisplayMuxReportPresence(TRUE) 讓 iGPU 知道系統中有正常運作的 ADS 多工器。
  9. Dxgkrnl 會呼叫 DxgkDdiStartDevice。 iGPU 驅動程式會傳回子係的數目,包括內部面板的 VidPn 目標。
  10. Dxgkrnl 會呼叫 DxgkDdiDisplayMuxGetRuntimeStatus 來檢查 iGPU 是否支援 ADS,以及驅動程式是否從系統取得所有必要的資訊。
  11. Dxgkrnl 會為 iGPU 所公開的每一個子系,呼叫 DxgkDdiQueryChildStatus
  12. 一旦 Dxgkrnl 找到由 iGPU 回報、且連接到多工器的子系,便會呼叫 DxgkDdiDisplayMuxUpdateState,以通知 iGPU 該多工器已連接到該目標。
  13. 由於 iGPU 顯示了一個已連接的內部監視器,因此 Dxgkrnl 通過 DxgkddiSettimingsfromvidpn在 iGPU 上設置了一個模式。
  14. Dxgkrnl 啟動 dGPU 驅動程式,然後針對 dGPU 重複步驟 5-12。
  15. Dxgkrnl 會偵測 iGPU、dGPU 和 mux 都已正確設定,因此它會建立多工切換對以及多工切換對的 PnP 裝置介面屬性。
  16. Dxgkrnl 讀取註冊表中的最後一個復用器配置。 因為最後一個組態是 dGPU,Dxgkrnl 現在會啟動先前描述的多工器切換序列,以將多工器切換至 dGPU。

面板驅動程式

監視面板驅動程式會根據從EDID產生的PnP硬體識別碼來載入。 假設EDID維持不變,當任一個 GPU 控制內部面板時,面板驅動程式就會載入。 這兩個驅動程式都會公開相同的亮度功能。 因此,加載不會造成任何問題,面板驅動程式也不需要知道哪個 GPU 控制多工器。

識別多工器控制的目標

當操作系統啟動驅動程式時,它會呼叫驅動程式的 DxgkDdiQueryChildRelations 來查詢所報告子系的相關信息。 驅動程式會為每個子裝置填入 DXGK_CHILD_DESCRIPTOR 結構。 AcpiUid 成員會定義為 ACPI 命名空間中該子系下_ADR方法所傳回的值,這可讓 OS 尋找該子系的 ACPI 名稱。

在 ADS 中,我們定義了一種 ACPI DMID 方法,該方法必須位於目標子 ACPI 命名空間之下。 這個 DMID 方法會傳回多任務裝置的 ACPI 名稱。 它可讓作業系統找到目標的多工器(mux)ACPI 名稱。

PnP 停止向目標進行掃描的轉接器

當輸出至內部面板的 GPU 停止運作時,操作系統不會切換多工器。 下列情境涵蓋 GPU 停止時的不同情況。

  1. GPU0 是文章。 它已連接到內部面板,並停止運作。

    在此情況下,基本顯示驅動程式 (BDD) 會接管 GPU0 上目前作用中的模式,並繼續更新畫面。

  2. GPU0 是文章,但 GPU1 已連線到內部面板。 GPU0 已停止。

    由於目前的OS設計,BDD 會在 GPU0 上啟動,這會導致報告鬼影監視器並顯示在顯示控制面板中。

  3. GPU1 不是文章,且已連線到內部面板。 GPU1 已停止。

    由於目前的OS設計,BDD 不會在 GPU1 上啟動,因此使用者將無法看到面板。

  4. GPU1 不是文章。 GPU0 已連線到內部面板,且 GPU1 已停止。

    沒有進行切換,而且沒有發生任何事情。 GPU0 會繼續顯示在面板上。

案例 2 和 3 會為使用者建立不良體驗。 ADS 功能會變更行為,以修正這兩個案例。

不支援外掛程式/外部 GPU

我們認為此功能與外掛 GPU 沒有任何使用案例。

ADS 僅適用於單一內部面板

第一個版本的 ADS 僅支援單一內部面板。 不過,此功能被設計為可以在未來,當作業系統支援時,以最少的驅動程式變更來支援外部顯示和多個內部顯示的複用。

目前的 POST 配接器原則變更

OS 先前有一些關於POST配接器的原則。 例如,POST 配接器是唯一可以揭露內部目標的配接器。 這些類型的限制隨著ADS的引入而從操作系統中移除。

停用監視抵達視覺效果

在 Windows 11 中連接監視器時,Shell 程式/DWM 會有動畫效果。 此動畫會在顯示切換案例中停用。

停用 PnP bonk

新增或移除監視器時,PnP 系統會播放「邦克」音效以通知使用者。 此「bonk」功能在顯示切換情況下已停用。

應用程式通知

當顯示切換發生時,系統會通過一般 HPD 移除和 HPD 抵達碼路徑。 因此,所有一般應用程式通知都會正常觸發,例如,HPD out 和 HPD in 的 PnP 通知,以及 WM_DISPLAYCHANGE 視窗訊息。

用於觸發開關的 API

此計畫是要提供一個公共 API,使作業系統和 IHV 控制面板能夠觸發切換。

由於內部面板僅連接至單一 GPU,因此顯示 API 會如預期般運作,並同時支援 Win+P 功能。

HLK 測試

如果 GPU 驅動程式或 ACPI 韌體報告完整的 ADS 支援,它必須在已啟用 ADS 的系統上通過 ADS HLK 測試。

當多工器切換離開該 GPU 時,GPU 正在內部面板進行 HPDing

當一個驅動程式報告內部面板已連接,而多工器目前已切換離開該驅動程式時,OS 會觸發錯誤檢查。

AC/DC 轉換

對於 ADS 功能的第一個版本,OS 不會儲存 AC 與 DC 多路復用器設置,也不會在 AC <至> DC 轉換時觸發多路復用器切換。

系統電源轉換

電源轉換的主要考慮是當韌體重設多工器狀態時(例如進入休眠),從電源恢復時,多工器未切換回電源轉換前的面板。

初始方法是在 iGPU 和 dGPU 都開機後,將多工器切換回 dGPU。 此方法的問題在於,視不同的異步事件而定,結果可能是多個模式變更。

協助簡化用戶體驗的更新方法是讓系統在iGPU和dGPU都處於睡眠狀態時,將多工器切換回預期的目標,從而避免多次模式變更。

電源轉換順序

下列範例描述 ADS 系統上的休眠電源轉換。

  1. 系統配置為帶多工器連接到 dGPU。
  2. 系統進入休眠狀態。
  3. iGPU 和 dGPU 都會切換至 D3 電源狀態。
  4. 系統關閉。
  5. 用戶啟動系統。
  6. 韌體會將多任務組設定為iGPU,而iGPU會在內部面板上顯示開機順序。
  7. Dxgkrnl 會讀取最後一個多工器組態(在此案例中為 dGPU),並使用 ACPI 將其與目前的多工器位置進行比較(在此案例中為 iGPU)。 Dxgkrnl 然後呼叫 ACPI,將多路轉換器切換至 dGPU。
  8. Dxgkrnl 將 iGPU 轉換為 D0,然後呼叫 iGPU 的DxgkDdiDisplayMuxUpdateState,通知驅動程式這個 Mux 沒有與其連接。
  9. Dxgkrnl 將 dGPU 轉換為 D0,然後呼叫 dGPU 的 DxgkDdiDisplayMuxUpdateState,以通知驅動程式該多工器已與其連線。
  10. Dxgkrnl 在 dGPU 上設定模式。

一體成型系統(AIO)

任何想要支援 ADS 的 AIO 系統,必須將內部面板設定為在這兩個 GPU 上暴露的內部目標類型。

Mux ACPI 裝置

OEM 負責在 ACPI 命名空間中新增多任務裝置,並提供操作多任務處理所需的方法。

GPU 驅動程式不應該呼叫多任務組的 ACPI 方法,因為多任務裝置可能位於 ACPI 樹狀結構中的任何位置。 建議將多工器(mux)放置在兩個 GPU 的最近共享祖先之下。

目前的多工器設備只支援兩個輸入,我們不預期未來的多工器會支援多於兩個輸入,因此設計可以假設每個多工器有兩個輸入和一個輸出。

系統運行時,永遠無法停止多工器。 這是隱藏的系統裝置。

Mux 裝置 ACPI 方法

只有 ACPI 裝置的驅動程式堆疊可以呼叫以評估裝置上的 ACPI 方法。 因此,若要呼叫多工裝置方法來切換多工,OS 必須為多工裝置載入驅動程式。 基於這個理由,OS 現在提供顯示多工器驅動程式作為所有顯示切換多工器的驅動程式。

需要多任務裝置才能有下列方法:

  • _HID透過硬體標識碼識別多工設備。 我們已為 ACPI 顯示多工器預留了「MSFT0005」。
  • DMQU(顯示多工器查詢)會傳回多工器的目前狀態。
  • DMCF(顯示多工配置)會設定多工器。

方法 _HID(硬體識別碼)

自變數:

沒有

傳回:

ASCII 字串,其中包含硬體識別碼,也就是 『MSFT0005』。

方法 DMQU (顯示 Mux 查詢)

在未來的版本中,我們預期會在查詢中新增更多資訊。 若要在未來啟用其他查詢,Arg0 用來指出查詢類型。 如果 DMQU 方法無法識別查詢類型,它應被視為不支援而失敗。

自變數:

Arg0:指定查詢類型的整數。 下表列出查詢類型值及其意義。

查詢類型值 意義
1 查詢目前的切換狀態
2 查詢多工器 ADS 支援層級
3 查詢多工器連接的第一個 GPU 子系統
4 查詢與多工器連接的第二個子GPU

傳回:

如果方法瞭解指定的查詢類型,它應該會傳回下表中所述的適當數據。 如果方法不瞭解指定的查詢類型,它應該會傳回空字串。

查詢類型值 傳回數據
1 ASCII 字串中包含多工器目前切換到的 GPU 子裝置的 ACPI 名稱。
2 代表 ADS 支援層級的整數。 如需詳細資訊,請參閱下一個表格。
3 ASCII 字串,其中包含多工器連接的第一個 GPU 子裝置的 ACPI 名稱。
4 ASCII 字串,其中包含多工器所連接之第二個 GPU 子裝置的 ACPI 名稱。

下表列出 ADS 支援層級值,以及當查詢類型為 2 時的意義。

傳回的數據 意義
0 不支援
1 開發支援。 系統可以隨附此設定,而不需要通過任何 HLK 測試,因為客戶系統上預設會停用 ADS。
2 實驗性支援。 系統可以隨附此設定,而不需要通過任何 HLK 測試,因為客戶系統上預設會停用 ADS。
3 完整支援。 當 ADS 與完全支援的圖形驅動程式配對時,系統將預設啟用 ADS。 系統必須通過 ADS HLK 測試才能寄送。

DMCF 方法 (顯示 Mux Configure)

自變數:

Arg0:多工器應該切換到的 ACPI GPU 子裝置 ASCII 名稱。

傳回:

整數 0 表示成功;非零表示失敗。 OEM 可以定義非零值,以取得更好的診斷。

GPU 裝置 ACPI 方法

啟動 GPU 的圖形驅動程式之前,系統必須知道多任務 ACPI 裝置是否正常運作,以及其目前狀態為何。 若要這樣做,必須已啟動 ACPI 多工器裝置的驅動程式。 系統會在每個 GPU 的 ACPI 命名空間下使用 ACPI _DEP 方法來保證裝置關聯性。

如果 GPU 已經有 _DEP 方法,它應該會將切換器設備的 ACPI 名稱新增至傳回的相依性清單。 如果 GPU 還沒有 _DEP 方法,它應該新增一個。

為了讓 ACPI 韌體只有在 OS 支援 ADS 時,才宣告 GPU 對 mux 的相依性,則會新增 ACPI _OSI 查詢。 ACPI 韌體可以使用此查詢來檢查 ADS 支援。 支援 ADS 的 OS 版本會藉由將 true 傳回給 _OSI(“DisplayMux”) ACPI 命令來回報支援。

GPU 子裝置 ACPI 方法

針對每個連接到多工器的目標,該子節點的ACPI裝置會公開一個ACPI方法,此方法會回傳其所連接的多工器裝置的ACPI名稱。 如需詳細資訊,請參閱 識別多工器所控制的目標

方法 DMID (顯示多工識別碼)

自變數:

沒有

傳回:

ASCII 字串,其中包含此輸出所連接 ACPI 多路复用器裝置的 ACPI 名稱

下列範例示範如何在 ACPI 架構中設定和管理具有兩個 GPU 的系統(GPU0 和 GPU1)及一個多工器。

  • 多工裝置的 ACPI 名稱是 'SB.MUX1'。

  • 針對 GPU0:

    • GPU0 的 ACPI 名稱是 『SB。PCI0。GFX0'。
    • 它公開 VidPn 目標 0x40f04,報告 DXGK_CHILD_DESCRIPTOR.AcpiUid 值為 0x400。
    • 對應至連接到多工器的目標之 ACPI 子裝置名稱為「SB.PCI0.GFX0.DD1F」。
    • ACPI 方法 _ADR 在 'SB.PCI0.GFX0.DD1F' 下傳回 0x400。 此傳回值是OS知道此 ACPI 裝置對應至 VidPn 目標0x40f04的方式。
    • 在 'SB.PCI0.GFX0.DD1F' 底下的 ACPI 方法 DMID 會傳回 'SB.MUX1'。
  • 針對 GPU1:

    • GPU1 的 ACPI 名稱是 'SB。PCI0。PEG0。PEGP'。
    • 它公開 VidPn 目標 0x1103,這個目標報告的 DXGK_CHILD_DESCRIPTOR 的 AcpiUid 值為 0x100。
    • 對應至多工器所連接目標的 ACPI 子裝置名稱為「SB.PCI0.PEG0.PEGP.EDP1」。
    • ACPI 方法 _ADR 在 'SB.PCI0.PEG0.PEGP.EDP1' 下傳回 0x100。 此傳回值是OS知道此 ACPI 裝置對應至 VidPn 目標0x1103的方式。
    • 在 'SB.PCI0.PEG0.PEGP.EDP1' 底下的 ACPI 方法 DMID 會傳回 'SB.MUX1'。
  • OS 知道 GPU0 目標 0x40f04 和 GPU1 目標 0x1103 連接到同一個名稱為 'SB.MUX1' 的 ACPI 多工器。

  • 如果 GPU1 目前連接到面板,OS 可以透過呼叫 'SB.MUX1' 上的 DMCF 方法,將傳遞 'SB.PCI0.GFX0.DD1F' 切換到 GPU0。

下列 ACPI 機器語言程式代碼適用於範例的相關部分。 平台邏輯的偽代碼會被括在 <>中。


DefinitionBlock
{
    Device (MUX1) // This is _SB_.MUX1
    {
        Name (_HID, "MSFT0007")  // _HID: Hardware ID

        Method (DMQU, 1, Serialized)  // DMQU: Display Mux Query
        {
            Switch (ToInteger(Arg0))
            {
                Case (1)
                {
                    If (<Mux is in error>)
                    {
                        Return ("")
                    }
                    If (<Mux switched to GPU0>)
                    {
                        Return ("_SB_.PCI0.GFX0.DD1F")
                    }
                    Else
                    {
                        Return ("_SB_.PCI0.PEG0.PEGP.EDP1")
                    }
                }
                Case (2) 
                {
                    Return (1)  // Mux only has developmental support
                }
                Case (3)
                {
                    If (<Mux is in error>)
                    {
                        Return ("")
                    }
                    Return ("_SB_.PCI0.GFX0.DD1F")
                }
                Case (4)
                {
                    If (<Mux is in error>)
                    {
                        Return ("")
                    }
                    Return ("_SB_.PCI0.PEG0.PEGP.EDP1")
                }

            }
            // Unknown type
            Return ("")
        }

        Method (DMCF, 1, Serialized)  // DMCF: Display Mux Configure
        {
            If (<Arg0 does not match either of the GPU children this mux is connected to>)
            {
                Return (1) // Failure, use 1 to indicate this particular failure
            }

            // Switch the mux

            If (<Mux switch was successful>)
            {
                Return (0) // Success
            }
            Else
            {
                Return (2) // Failure, use 2 to indicate this particular failure
            }
        }
    }

    Scope (_SB_.PCI0.GFX0) // ACPI Device for GPU0
    {
        Method (_DEP, 0, NotSerialized)  // _DEP: Dependency on Mux device
        {
            If (_OSI(“DisplayMux”))
            {
                Return (Package {"_SB_.MUX1"})
            }
            Else
            {
                Return (Package (0x00){})
            }
        }

        Device (DD1F) // SB.PCI0.GFX0.DD1F which is child of GPU that is connected to the Mux
        {
            Name (_ADR, 0x400)  // _ADR: Matches the AcpiUid driver reports for the target connected to mux
            Method (DMID, 0, NotSerialized)  // DMID: ACPI name of the mux this target is connected to
            {
                Return ("_SB_.MUX1")
            }
        }
    }

    Scope (_SB_.PCI0.PEG0.PEGP) // ACPI Device for GPU1
    {
        Method (_DEP, 0, NotSerialized)  // _DEP: Dependency on Mux device
        {
            If (_OSI(“DisplayMux”))
            {
                Return (Package {"_SB_.MUX1"})
            }
            Else
            {
                Return (Package (0x00){})
            }
        }

        Device (EDP1) // SB.PCI0.PEG0.PEGP.EDP1 which is child of GPU that is connected to the Mux
        {
            Name (_ADR, 0x100)  // _ADR: Matches the AcpiUid driver reports for the target connected to mux
            Method (DMID, 0, NotSerialized)  // DMID: ACPI name of the mux this target is connected to
            {
                Return ("_SB_.MUX1")
            }
        }
    }
}

API 變更

ADS 功能會新增下列公用 API 功能:

  1. 列舉系統中的多工器裝置。
  2. 查詢有關多工器的信息,例如,它所連接的目標,以及當前切換到的目標。
  3. 觸發多任務交換器。
  4. 如何偵測多工器什麼時候被切換。

列舉系統中的多工器裝置

應用程式可以使用通用即插即用 API 來尋找代表運作中的顯示切換器的裝置介面。 使用者模式組件可以使用 Windows.Devices.Enumeration.DeviceInformation。 C# 或 C++ 都可以搭配這些 API 來列舉多工裝置。

// Display Mux device interface
// {93c33929-3180-46d3-8aab-008c84ad1e6e}
DEFINE_GUID(GUID_DEVINTERFACE_DISPLAYMUX, 0x93c33929, 0x3180, 0x46d3, 0x8a, 0xab, 0x00, 0x8c, 0x84, 0xad, 0x1e, 0x6e);

IDisplayMuxDevice 介面

IDisplayMuxDevice 介面已新增來表示多工設備。

下列程式代碼示範如何列舉顯示多工裝置、查詢其狀態、切換使用中的顯示目標,以及使用 Windows 執行階段 API 回應狀態變更。

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Devices.Display.Core.h>

#include <string>
#include <sstream>
#include <iomanip>
#include <windows.h>

namespace winrt
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Devices::Enumeration;
using namespace winrt::Windows::Devices::Display;
using namespace winrt::Windows::Devices::Display::Core;
} // namespace winrt

void SwitchDisplayMuxTarget()
{
    // PnP device interface search string for Mux device interface
    std::wstring muxDeviceSelector = L"System.Devices.InterfaceClassGuid:=\"{93c33929-3180-46d3-8aab-008c84ad1e6e}\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";

    // Execute the device interface query
    winrt::DeviceInformationCollection deviceInformations = winrt::DeviceInformation::FindAllAsync(muxDeviceSelector, nullptr).get();
    if (deviceInformations.Size() == 0)
    {
        printf("No DisplayMux devices\n");
        return;
    }
    printf("%ld display mux devices found\n\n", deviceInformations.Size());

    // Only one mux in first release but here is generic code for multiple
    for (unsigned int i = 0; i < deviceInformations.Size(); i++)
    {
        printf("Display Mux device %ld :\n", i);

        // Get the device interface so we can query the info
        winrt::DeviceInformation deviceInfo = deviceInformations.GetAt(i);

        // Get the device id
        std::wstring deviceId = deviceInfo.Id().c_str();
        printf("    Device ID string : %S \n", deviceId.c_str());

        // Create the DisplayMuxDevice object
        auto displayMuxDevice = winrt::DisplayMuxDevice::FromIdAsync(deviceId).get();
        if (!displayMuxDevice)
        {
            printf("Failed to create DisplayMuxDevice object");
            continue;
        }

        // Check if DisplayMux is active
        auto displayMuxActive = displayMuxDevice.IsActive();
        printf("    DisplayMux state : %s \n", displayMuxActive ? "Active" : "Inactive");
        if (!displayMuxActive)
        {
            continue;
        }

        // Register for call back when the state of the DisplayMux changes
        UINT changeCount = 0;
        auto token = displayMuxDevice.Changed([&changeCount](auto, auto Args) -> HRESULT {
            changeCount++;
            return S_OK;
        });

        // Find targets connected to the DisplayMux and the current target
        auto targetsList = displayMuxDevice.GetAvailableMuxTargets();
        winrt::DisplayTarget currentTarget = displayMuxDevice.CurrentTarget();

        // Switch the display mux to the other target
        // NOTE SetPreferredTarget() is a sync method so use .get() to wait for the operation to complete
        printf("\n");
        if (currentTarget == targetsList.GetAt(0))
        {
            printf("DisplayMux currently connected to first target\n");
            displayMuxDevice.SetPreferredTarget(targetsList.GetAt(1)).get();
            printf("Calling SetPreferredTarget to switch DisplayMux to second target\n");
        }
        else if (currentTarget == targetsList.GetAt(1))
        {
            printf("DisplayMux currently connected to second target\n");
            displayMuxDevice.SetPreferredTarget(targetsList.GetAt(0)).get();
            printf("Calling SetPreferredTarget to switch DisplayMux to first target\n");
        }
        else
        {
            printf("Could not find current target in target list\n");
        }

        // Now read the current position
        currentTarget = displayMuxDevice.CurrentTarget();
        targetsList = displayMuxDevice.GetAvailableMuxTargets();
        if (currentTarget == targetsList.GetAt(0))
        {
            printf("DisplayMux is now currently connected to first target\n");
        }
        else if (currentTarget == targetsList.GetAt(1))
        {
            printf("DisplayMux is now currently connected to second target\n");
        }
        else
        {
            printf("Could not find current target in target list\n");
        }

        // Now unregister for change callback and display the
        displayMuxDevice.Changed(token);
        printf("DisplayMux state change callback was called %ld times\n\n", changeCount);
    }
}

WDDM DDI 變更,自動顯示切換

本節描述了為了支援 ADS,對 WDDM DDI 所做的新增和變更。 這些變更從 Windows 11 版本 24H2 Update 2025.01D 開始提供(WDDM 3.2)。

查詢 KMD 的 ADS 支援介面

已新增 DXGK_DISPLAYMUX_INTERFACE_2 介面結構。 其中包含支援 ADS 第 2 版所需的作業系統與驅動程式之間的呼叫。 操作系統 會在驅動程序啟動時查詢驅動程序支援的 ADS 介面,並將 InterfaceType 設定為 GUID_WDDM_INTERFACE_DISPLAYMUX_2。

DXGK_DISPLAYMUX_INTERFACE 包含支援 ADS 功能第 1 版所需的 OS 對驅動程式呼叫。此版本是在 ADS 發行前版本期間使用。

支援 ADS 的 KMD 函式

KMD 會實作下列函式以支援 ADS。 Dxgkrnl 透過呼叫 KMD 的 DxgkddiQueryInterface,來取得 KMD 的 ADS 功能介面。

駕駛員報告自動駕駛系統功能

當操作系統 (OS) 呼叫其 DxgkDdiDisplayMuxGetDriverSupportLevel DDI 時,驅動程式會報告其 ADS 支援層級。 如果驅動程式未實作 DXGK_DISPLAYMUX_INTERFACE 介面,OS 會將支援層級視為DXGK_DISPLAYMUX_SUUPORT_LEVEL_NONE。

不論其執行所在的系統為何,驅動程式都應該回報其 ADS 支援層級。 驅動程式所報告的支援層級應該只以驅動程式為基礎。 當報告其 ADS 支援層級時,驅動程式不應該考慮下列任何準則:

  1. 系統 OEM。
  2. 系統中的任何其他 GPU。
  3. ACPI 多工器裝置是否存在。
  4. GPU 的 ACPI 節點下是否存在 ACPI 項目。

在配接器啟動時更新報告目標

配接器啟動時,它會透過 DxgkDdiQueryChildRelations DDI 報告其所有子裝置。 報告包含任何連線到多任務器的內部目標。 內部目標包含 DXGK_CHILD_CAPABILITIES.Type.IntegratedDisplayChild.DescriptorLength 欄位。

如果多工器在適配器啟動時切換到其他 GPU,就會發生問題。 在此情況下,驅動程式無法與內部面板通訊,以查詢EDID/DisplayId大小。 因此,公開 GUID_WDDM_INTERFACE_DISPLAYMUX_2 介面的驅動程式必須在適配器啟動時將 DXGK_CHILD_CAPABILITIES.Type.IntegratedDisplayChild.DescriptorLength 設為零,如果多工器目前未切換至驅動程式的 GPU。 否則,作業系統將導致介面卡啟動失敗。

在首次多工交換器操作中,作業系統會更新其內部描述符大小的資訊。

連線變更更新

如先前所述,在自動顯示切換序列中,有一種專門針對 ADS 的方法,可以報告內部面板狀態。 若要指出連線變更封包是 ADS 切換序列的一部分,會將 DisplayMuxConnectionChange 標誌新增至 DXGK_CONNECTION_MONITOR_CONNECT_FLAGSDisplayMuxConnectionChange 設定時,表示 MonitorStatusConnectedMonitorStatusDisconnected連線狀態 與自動顯示切換有關。

DisplayMuxConnectionChange 只能在進行 ADS 切換時使用,不應用於任何其他用途。 它應該用於下列 ADS 場合:

  • 當驅動程序處理 DxgkDdiDisplayMuxPreSwitchAway時。

    如果內部面板已連線,驅動程式應該將 DXGK_CONNECTION_CHANGE 封包新增至其連線變更清單,並將其 DXGK_CONNECTION_CHANGE.ConnectionStatus 設為 MonitorStatusDisconnected,且將其 DXGK_CONNECTION_CHANGE.MonitorConnect.MonitorConnectFlags.DisplayMuxConnectionChange 設為 1。 這些設定會向操作系統指出驅動程式已釋放內部面板的控制。

  • 當驅動程式正在處理 DxgkDdiDisplayMuxPostSwitchToPhase1時。

    • 驅動程式應該先判斷內部面板是否已連接。
    • 如果面板已連線,驅動程式應將編號為 的 DXGK_CONNECTION_CHANGE 封包新增至其連線變更清單,並將其 DXGK_CONNECTION_CHANGE.ConnectionStatus 設定為 MonitorStatusConnected,且將 DXGK_CONNECTION_CHANGE.MonitorConnect.MonitorConnectFlags.DisplayMuxConnectionChange 設為 1。
    • 如果面板未連線,驅動程式應該將 DXGK_CONNECTION_CHANGE 封包新增至其連線變更清單,並將 DXGK_CONNECTION_CHANGE.ConnectionStatus 設定為 MonitorStatusDisconnected,將 DXGK_CONNECTION_CHANGE.MonitorConnect.MonitorConnectFlags.DisplayMuxConnectionChange 設為 1。
  • 當驅動程式處理 DxgkDdiDisplayMuxSwitchCanceled時。

  • 若在切換期間收到目標輪詢要求,則僅針對從 DxgkDdiDisplayMuxPreSwitchAwayDxgkDdiDisplayMuxPostSwitchToPhase1DxgkDdiDisplayMuxSwitchCanceled新增的連接變更封包設置 DisplayMuxConnectionChange

已更新 DxgkDdiSystemDisplayEnable 的指導方針

當呼叫 ADS 驅動程式的 DxgkDdiSystemDisplayEnable(/windows-hardware/drivers/ddi/dispmprt/nc-dispmprt-dxgkddi_system_display_enable) DDI 時,驅動程式必須確保 PSR 在 DxgkDdiSystemDisplayEnable DDI 呼叫結束時停用。

OEM 指引

ADS 功能的幾個層面低於平臺中OS控件的層級。 OEM 確保其運作正常至關重要。 下列清單摘要說明 OEM 需要考慮的一些重點:

  • 混合式整合式和混合式離散驅動程式都必須支援 ADS。
  • 針對平台選取的多工器可以透過 ACPI 進行控制。
  • _HID、DMQU 和 DMCF 方法會在多任務裝置和內部目標的 GPU 子 ACPI 裝置下實作,並具有 DMID ACPI 方法。
  • 兩個 GPU 的 ACPI 裝置都必須具備 _DEP 以標示出其對多工器 ACPI 裝置的依賴性。
  • 兩個 GPU 公開的亮度介面/上限/範圍完全相符。
  • 亮度數據 一節所述,我們強烈建議您使用亮度 v3 介面,而不是亮度 V2 介面。
  • 如果使用監視面板驅動程式,程式代碼應該與 GPU 無關;也就是說,當任一個 GPU 控制面板時,可以使用相同的邏輯。
  • 至少對於內部多工器,切換多工器的行為不應該產生 HPD 事件。
  • 如果 OEM 想要停用系統中的多工器,DMQU ACPI 方法應該會在使用 Arg0 設定為 2 呼叫時傳回 0。
  • 多工器必須能夠在 GPU 之間切換,即使驅動程式處於低功耗。 在此情況下,將不會使用 PSR。
  • 當多工器從一個 GPU 切換到另一個 GPU 時,應該維持面板的亮度,不會出現任何亮度閃爍。 有多種方式可以這麼做,包括下列方式。 OEM 負責確保系統在交換器之間維持亮度。
    • 使用基於 DisplayPort Aux Nits 的亮度控制。
    • 使用 Tcon 搭配 PWM 重建,以避免任何亮度故障。
  • 當切換前和切換後的鏈接組態由EDID公開,且iGPU和dGPU都支援時,所使用的面板和Tcon可以保持自動刷新(適用於eDP的PSR1)。 這包括,但不限於:
    • 刷新率
    • 活動尺寸
    • 使用的 eDP 通道數目和通道頻寬
    • eDP DSC 設定
    • 使用的 eDP VSC SDP 版本
    • 非交換器案例所使用的 PSR 版本和功能