window显示驱动开发—多平面覆盖 VidPN 呈现

使用多平面覆盖时,这些要求适用于用于在视频呈现网络 (VidPNs) 的多个表面上显示的功能:

DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay

  • 如果 DXGK_MULTIPLANE_OVERLAY_PLANE。Enabled 为 false,显示微型端口驱动程序应禁用指定的平面。
  • 如果在上一次调用 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay 时启用了平面,但当前调用中不存在某个平面,则驱动程序应继续显示平面而不翻转平面。
  • 在同一 VSync 期间,驱动程序可能会收到对 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay 的多个调用, (一个调用翻转一个平面,另一个调用) 翻转其他平面。 在这种情况下,驱动程序应处理这两个调用。
  • 传递的数据应在用户模式下由受信任的源进行验证。 但是,显示微型端口驱动程序仍应检查数据,以确保它不会导致问题。 如果数据不正确,驱动程序可能会使调用失败并 出现STATUS_INVALID_PARAMETER 错误代码,但此类故障可能无法正常处理,并且意味着操作系统或用户模式驱动程序中存在 bug。

DxgkDdiSetVidPnSourceVisibility

DXGKARG_SETVIDPNSOURCEVISIBILITY时。在调用此函数时,在给定源上,Visible 设置为 FALSE,必须禁用所有硬件平面,包括用于主表面的层。 当 Visible 设置为 TRUE 时,必须仅启用用于主图面的平面,并且所有其他平面必须保持禁用状态。

DxgkDdiSetVidPnSourceAddress

调用此函数时,驱动程序应禁用所有非主覆盖平面。 在多平面覆盖模式下,主图面使用 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay 翻转。

1. DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay 实现规范

1.1 平面启用/禁用控制

复制代码
NTSTATUS APIENTRY DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay(
    _In_ const DXGKARG_SETVIDPNSOURCEADDRESSWITHMULTIPLANEOVERLAY* pArgs)
{
    // 处理每个平面
    for (UINT i = 0; i < pArgs->PlaneCount; i++) {
        const DXGK_MULTIPLANE_OVERLAY_PLANE* pPlane = &pArgs->PlaneList[i];
        
        if (!pPlane->Enabled) {
            // 禁用指定平面
            DisableHwPlane(pPlane->PlaneId);
        } else {
            // 配置平面属性
            ProgramPlaneAttributes(pPlane);
        }
    }
    
    // 处理未包含但之前启用的平面
    for (UINT id : g_ActivePlanes) {
        if (!IsPlaneInCurrentRequest(id, pArgs)) {
            // 保持显示但不翻转内容
            MaintainPlaneWithoutFlip(id);
        }
    }
    
    return STATUS_SUCCESS;
}

1.2 多VSync调用处理

复制代码
sequenceDiagram
    UMD->>KMD: SetVidPnSourceAddress(PlaneA Flip)
    KMD->>Hardware: 更新PlaneA缓冲区
    UMD->>KMD: SetVidPnSourceAddress(PlaneB Flip)
    KMD->>Hardware: 更新PlaneB缓冲区
    Hardware-->>Display: 同一VSync周期内提交AB平面

关键要求:

  • 必须支持同一VSync周期内的多次调用
  • 各平面更新需原子化提交

2. 可见性控制函数

2.1 DxgkDdiSetVidPnSourceVisibility 实现

复制代码
NTSTATUS APIENTRY DxgkDdiSetVidPnSourceVisibility(
    _In_ const DXGKARG_SETVIDPNSOURCEVISIBILITY* pArgs)
{
    if (!pArgs->Visible) {
        // 禁用所有平面(包括主平面)
        for (UINT i = 0; i < MAX_PLANES; i++) {
            DisableHwPlane(i);
        }
    } else {
        // 仅启用主平面
        EnablePrimaryPlane(pArgs->VidPnSourceId);
        
        // 保持其他平面禁用
        for (UINT i = 1; i < MAX_PLANES; i++) {
            DisableHwPlane(i);
        }
    }
    
    return STATUS_SUCCESS;
}

2.2 状态转换规则

当前状态 新Visible值 操作要求
全部启用 FALSE 立即禁用所有平面
部分启用 FALSE 禁用剩余平面
全部禁用 TRUE 仅启用主平面
主平面启用 TRUE 无操作(维持状态)

3. 传统模式兼容性

3.1 DxgkDdiSetVidPnSourceAddress 行为

复制代码
NTSTATUS APIENTRY DxgkDdiSetVidPnSourceAddress(
    _In_ const DXGKARG_SETVIDPNSOURCEADDRESS* pArgs)
{
    // 在非MPO模式下禁用所有叠加平面
    if (!IsInMpoMode()) {
        for (UINT i = 1; i < MAX_PLANES; i++) {
            DisableHwPlane(i);
        }
    }
    
    // 标准处理流程
    return OriginalSetVidPnSourceAddress(pArgs);
}

向后兼容要求:

  • 必须保持与非MPO显示模式的兼容性
  • 在传统模式下自动禁用叠加平面

4. 安全验证与错误处理

4.1 参数验证清单

复制代码
BOOL ValidateMpoParams(
    const DXGKARG_SETVIDPNSOURCEADDRESSWITHMULTIPLANEOVERLAY* pArgs)
{
    // 基础检查
    if (pArgs->VidPnSourceId >= MAX_SOURCES || 
        pArgs->PlaneCount > MAX_PLANES) {
        return FALSE;
    }
    
    // 平面属性验证
    for (UINT i = 0; i < pArgs->PlaneCount; i++) {
        const DXGK_MULTIPLANE_OVERLAY_PLANE* pPlane = &pArgs->PlaneList[i];
        
        if (pPlane->PlaneId >= MAX_PLANES ||
            !ValidateRect(&pPlane->SrcRect) ||
            !ValidateRect(&pPlane->DstRect)) {
            return FALSE;
        }
    }
    
    return TRUE;
}

4.2 错误处理规范

错误类型 处理方式
无效VidPnSourceId 返回STATUS_GRAPHICS_INVALID_VIDPN_SOURCEID
非法矩形坐标 返回STATUS_INVALID_PARAMETER
硬件资源冲突 返回STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE

5. 性能优化建议

5.1 平面状态缓存

复制代码
typedef struct _PLANE_STATE_CACHE {
    BOOL Enabled;
    RECT SrcRect;
    RECT DstRect;
    FLOAT Alpha;
} PLANE_STATE_CACHE;

// 全局缓存数组
PLANE_STATE_CACHE g_PlaneCache[MAX_PLANES];

5.2 差异更新机制

复制代码
void SmartPlaneUpdate(UINT PlaneId, const DXGK_MULTIPLANE_OVERLAY_PLANE* pNewState)
{
    if (memcmp(&g_PlaneCache[PlaneId], pNewState, sizeof(PLANE_STATE_CACHE)) != 0) {
        // 仅当状态变化时更新硬件
        ProgramHwPlane(PlaneId, pNewState);
        g_PlaneCache[PlaneId] = *pNewState;
    }
}

6. WHQL 认证要求

6.1 必须通过的测试

Device.Graphics.WDDM.MPO.Visibility

  • 验证Visible=FALSE时的平面禁用
  • 测试主平面独占模式

Device.Graphics.WDDM.MPO.LegacyCompatibility

  • 传统SetVidPnSourceAddress调用时的行为

6.2 认证检查清单

  • 正确处理Enabled=false的平面
  • 实现VSync周期内的多调用支持
  • 完备的参数验证逻辑
  • 与传统模式的正确交互

7. 调试与诊断

7.1 注册表调试键

复制代码
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WSD]
"LogMpoVisibilityChanges"=dword:00000001
"MpoParamValidationLevel"=dword:00000002

7.2 事件日志分析

复制代码
# 查看MPO可见性变更记录
Get-WinEvent -ProviderName Microsoft-Windows-Win32k |
Where-Object {$_.Id -eq 300} |
Format-Table -Property TimeCreated, Message
相关推荐
charlie1145141919 小时前
嵌入式Linux驱动开发pinctrl篇(1)——从寄存器到子系统:驱动演进之路
linux·运维·驱动开发
猫猫的小茶馆14 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32
高翔·权衡之境20 小时前
主题9:DMA与零拷贝——让CPU从数据搬运中解放
驱动开发·安全·缓存·系统安全·信息与通信
小此方1 天前
Re:Linux系统篇(十七)进程篇·二:深入浅出 [进程概念与进程父子关系]:从底层原理到实战应用
linux·运维·驱动开发
小此方1 天前
Re: Linux系统篇(十八)进程篇·三:深度硬核!全面起底 Linux 进程状态变化与内核链表动态解绑
linux·驱动开发·链表
楼兰公子2 天前
# RK3588 Linux 驱动开发完整学习指南RK3588_Linux_Driver_Development.md
linux·驱动开发
念何架构之路3 天前
GoFrameMap转换详解
驱动开发
charlie1145141913 天前
嵌入式Linux嵌入式Linux驱动开发:设备树驱动改造——从硬编码到设备树的实战之旅
linux·运维·驱动开发
国产芯片设计3 天前
小家电单段码屏项目实战|YL1621 LCD驱动开发与调试心得
驱动开发·stm32·单片机·mcu·51单片机
小此方3 天前
Re:Linux系统篇(十六) 进程篇 · 一:深入理解操作系统:从软硬件架构到“先描述,再组织”的管理哲学
linux·驱动开发·硬件架构