虚拟显卡 display port

1.虚拟显卡 DisplayPort介绍

1. 基本概念:
   DisplayPort 是一种高带宽的数字音视频接口,支持高分辨率和高刷新率的显示输出。

2. 驱动支持:
   - Windows 操作系统通常会自动识别并安装基本的 DisplayPort 驱动。
   - 然而,为了获得最佳性能和全部功能,建议安装显卡制造商提供的最新驱动程序。

3. 主要显卡制造商:
   - NVIDIA
   - AMD
   - Intel(集成显卡)

4. 驱动更新:
   - 可以通过 Windows Update 自动更新驱动。
   - 也可以从显卡制造商的官方网站手动下载并安装最新驱动。

5. DisplayPort 特性支持:
   - 高分辨率输出(如 4K、8K)
   - 高刷新率(如 144Hz、240Hz)
   - HDR(高动态范围)
   - 多显示器设置
   - 音频传输

6. 故障排除:
   - 如果遇到 DisplayPort 连接问题,可以尝试更新驱动、检查线缆、或在 Windows 显示设置中调整。

7. 与其他技术的兼容性:
   - DisplayPort 可以通过适配器与 HDMI、DVI 等其他接口兼容。

8. 版本差异:
   - 不同版本的 DisplayPort(如 1.2、1.4、2.0)支持不同的最大带宽和功能,驱动程序需要相应支持。

要获得最佳的 DisplayPort 性能,建议定期更新显卡驱动,并确保使用与您的硬件兼容的高质量 DisplayPort 线缆。
 

2. 虚拟 display port 举例

虚拟 DisplayPort(Virtual DisplayPort)是一种软件模拟的显示输出技术,它可以在没有物理显示器连接的情况下创建虚拟显示器。以下是一些虚拟 DisplayPort 的应用举例:

1. 远程桌面解决方案:
   例如:Microsoft Remote Desktop, TeamViewer, VNC 等软件可以创建虚拟显示器,使远程用户能够访问额外的桌面空间。

2. 虚拟机环境:
   VMware, VirtualBox 等虚拟化软件可以为虚拟机创建虚拟显示器,无需物理连接额外的显示器。

3. 屏幕录制和流媒体:
   OBS Studio(Open Broadcaster Software)可以利用虚拟显示输出来捕获和录制特定应用程序的画面。

4. 无头服务器(Headless Servers):
   在没有物理显示器的服务器上,可以使用虚拟 DisplayPort 来模拟显示输出,便于远程管理和故障排除。

5. 多显示器仿真:
   开发人员可以使用虚拟 DisplayPort 来测试多显示器设置的应用程序,而无需实际连接多个物理显示器。

6. 云游戏平台:
   如 NVIDIA GeForce Now, Google Stadia 等,使用虚拟显示技术将游戏画面传输到客户端设备。

7. 虚拟现实(VR)开发:
   开发者可以使用虚拟 DisplayPort 来模拟 VR 头显的输出,便于在非 VR 环境中进行开发和测试。

8. 数字标牌系统:
   在数字广告牌或信息显示系统中,可以使用虚拟 DisplayPort 来管理和预览内容,而无需物理访问显示设备。

9. 教育和培训模拟:
   在远程教育或企业培训中,可以使用虚拟显示技术来共享和操作虚拟桌面环境。

10. 辅助功能应用:
    为视障用户创建虚拟显示器,配合屏幕阅读器等辅助技术,提供更好的无障碍体验。

这些例子展示了虚拟 DisplayPort 技术在各种场景下的应用,它为软件开发、远程访问、测试和模拟等领域提供了灵活的解决方案。
 

3 .简单的驱动框架

在内核级别实现虚拟 DisplayPort 是一个复杂的任务,需要深入了解 Windows 驱动程序开发和显示驱动架构。以下是一个简化的内核模式驱动程序示例,展示了创建虚拟显示设备的基本概念。请注意,这个示例仅供学习目的,并不是一个完整的虚拟 DisplayPort 实现。

首先,你需要安装 Windows Driver Kit (WDK) 和 Visual Studio。然后,创建一个新的 KMDF (Kernel-Mode Driver Framework) 项目。

好的,我来为您提供一个更完整的虚拟 DisplayPort 驱动程序的代码示例。这个示例仍然是简化的,但比之前的版本更加完整,包含了更多的 DisplayPort 相关功能。

首先,我们需要定义一些结构体和常量:


// VirtualDisplayPort.h
#pragma once

#include <ntddk.h>
#include <wdf.h>

// DisplayPort specific structures and constants
typedef struct _DISPLAY_MODE {
    ULONG Width;
    ULONG Height;
    ULONG RefreshRate;
} DISPLAY_MODE, *PDISPLAY_MODE;

typedef struct _EDID_DATA {
    UCHAR Data[128];
} EDID_DATA, *PEDID_DATA;

#define IOCTL_SET_DISPLAY_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_GET_DISPLAY_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SET_EDID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_GET_EDID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SIMULATE_HPD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)

// Device context structure
typedef struct _DEVICE_CONTEXT
{
    DISPLAY_MODE CurrentMode;
    EDID_DATA Edid;
    BOOLEAN IsConnected;
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext)

DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD VirtualDisplayPortEvtDeviceAdd;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL VirtualDisplayPortEvtIoDeviceControl;
```

接下来是驱动程序的主要代码:

```c
// VirtualDisplayPort.c
#include "VirtualDisplayPort.h"

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT  DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    WDF_DRIVER_CONFIG config;

    WDF_DRIVER_CONFIG_INIT(&config, VirtualDisplayPortEvtDeviceAdd);

    status = WdfDriverCreate(DriverObject,
                             RegistryPath,
                             WDF_NO_OBJECT_ATTRIBUTES,
                             &config,
                             WDF_NO_HANDLE);

    return status;
}

NTSTATUS VirtualDisplayPortEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
{
    NTSTATUS status;
    WDFDEVICE hDevice;
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    WDF_IO_QUEUE_CONFIG queueConfig;
    PDEVICE_CONTEXT deviceContext;

    UNREFERENCED_PARAMETER(Driver);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &hDevice);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    deviceContext = DeviceGetContext(hDevice);
    RtlZeroMemory(deviceContext, sizeof(DEVICE_CONTEXT));

    // Initialize with default values
    deviceContext->CurrentMode.Width = 1920;
    deviceContext->CurrentMode.Height = 1080;
    deviceContext->CurrentMode.RefreshRate = 60;
    deviceContext->IsConnected = TRUE;

    // Initialize EDID with some dummy data
    RtlFillMemory(deviceContext->Edid.Data, sizeof(deviceContext->Edid.Data), 0xAA);

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential);
    queueConfig.EvtIoDeviceControl = VirtualDisplayPortEvtIoDeviceControl;

    status = WdfIoQueueCreate(hDevice, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);

    return status;
}

VOID VirtualDisplayPortEvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
)
{
    NTSTATUS status = STATUS_SUCCESS;
    WDFDEVICE device = WdfIoQueueGetDevice(Queue);
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(device);
    PVOID buffer;
    size_t bufferSize;

    UNREFERENCED_PARAMETER(OutputBufferLength);

    switch (IoControlCode)
    {
    case IOCTL_SET_DISPLAY_MODE:
        if (InputBufferLength < sizeof(DISPLAY_MODE))
        {
            status = STATUS_INVALID_PARAMETER;
            break;
        }

        status = WdfRequestRetrieveInputBuffer(Request, sizeof(DISPLAY_MODE), &buffer, &bufferSize);
        if (!NT_SUCCESS(status)) {
            break;
        }

        RtlCopyMemory(&deviceContext->CurrentMode, buffer, sizeof(DISPLAY_MODE));
        DbgPrint("Display mode set to %dx%d@%dHz\n", 
                 deviceContext->CurrentMode.Width,
                 deviceContext->CurrentMode.Height,
                 deviceContext->CurrentMode.RefreshRate);
        break;

    case IOCTL_GET_DISPLAY_MODE:
        status = WdfRequestRetrieveOutputBuffer(Request, sizeof(DISPLAY_MODE), &buffer, &bufferSize);
        if (!NT_SUCCESS(status)) {
            break;
        }

        RtlCopyMemory(buffer, &deviceContext->CurrentMode, sizeof(DISPLAY_MODE));
        WdfRequestSetInformation(Request, sizeof(DISPLAY_MODE));
        break;

    case IOCTL_SET_EDID:
        if (InputBufferLength < sizeof(EDID_DATA))
        {
            status = STATUS_INVALID_PARAMETER;
            break;
        }

        status = WdfRequestRetrieveInputBuffer(Request, sizeof(EDID_DATA), &buffer, &bufferSize);
        if (!NT_SUCCESS(status)) {
            break;
        }

        RtlCopyMemory(&deviceContext->Edid, buffer, sizeof(EDID_DATA));
        DbgPrint("EDID data updated\n");
        break;

    case IOCTL_GET_EDID:
        status = WdfRequestRetrieveOutputBuffer(Request, sizeof(EDID_DATA), &buffer, &bufferSize);
        if (!NT_SUCCESS(status)) {
            break;
        }

        RtlCopyMemory(buffer, &deviceContext->Edid, sizeof(EDID_DATA));
        WdfRequestSetInformation(Request, sizeof(EDID_DATA));
        break;

    case IOCTL_SIMULATE_HPD:
        if (InputBufferLength < sizeof(BOOLEAN))
        {
            status = STATUS_INVALID_PARAMETER;
            break;
        }

        status = WdfRequestRetrieveInputBuffer(Request, sizeof(BOOLEAN), &buffer, &bufferSize);
        if (!NT_SUCCESS(status)) {
            break;
        }

        deviceContext->IsConnected = *(PBOOLEAN)buffer;
        DbgPrint("Display %s\n", deviceContext->IsConnected ? "connected" : "disconnected");
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    WdfRequestComplete(Request, status);
}
```

这个驱动程序实现了以下功能:

1. 设置和获取显示模式 (分辨率和刷新率)
2. 设置和获取 EDID 数据
3. 模拟热插拔检测 (HPD)

要编译这个驱动程序,你需要设置 Windows 驱动程序开发环境,包括 WDK (Windows Driver Kit)。确保在项目设置中正确配置了目标平台和驱动程序模型 (KMDF)。

这个驱动程序仍然是一个简化的实现,实际的 DisplayPort 驱动程序会更加复杂:

1. 它需要实现完整的 WDDM (Windows Display Driver Model) 接口。
2. 需要处理更多的 DisplayPort 特定功能,如链路训练、MST (Multi-Stream Transport) 等。
3. 需要实现实际的帧缓冲区管理和显示更新。
4. 需要处理电源管理、PnP (即插即用) 事件等。

此外,你还需要为这个驱动程序创建一个合适的 INF 文件,以便正确安装驱动程序。最后,记得在正式使用前对驱动程序进行全面测试,因为内核模式驱动程序中的错误可能会导致系统不稳定或崩溃。
 

要编译和运行这个驱动程序:

1. 在 Visual Studio 中创建一个新的 KMDF 驱动程序项目。
2. 将上面的代码复制到项目的主源文件中。
3. 配置项目以进行内核模式驱动程序开发(设置正确的目标平台和驱动程序模型)。
4. 编译驱动程序。
5. 使用 Windows Driver Test (WDT) 框架或其他驱动程序部署工具在测试机上安装和测试驱动程序。

注意事项:

1. 这个示例仅创建了一个基本的设备对象,没有实现完整的显示功能。
2. 实际的虚拟 DisplayPort 驱动程序需要实现 WDDM (Windows Display Driver Model) 接口。
3. 你需要处理更多的 I/O 请求、实现模式设置、管理显示缓冲区等。
4. 内核模式驱动程序开发有潜在的系统风险,请在隔离的测试环境中进行开发和测试。
5. 这个示例没有实现卸载和清理代码,这在实际驱动程序中是必需的。

开发完整的虚拟 DisplayPort 驱动程序需要深入了解 Windows 显示驱动架构、图形子系统和硬件虚拟化技术。这个示例仅提供了一个起点,实际实现还需要大量额外的工作。
 

4.驱动对应的inf 

INF 文件用于描述驱动程序的安装信息。以下是一个基本的 INF 文件,适用于我们的虚拟 DisplayPort 驱动程序:

```inf
[Version]
Signature="$WINDOWS NT$"
Class=Display
ClassGuid={4D36E968-E325-11CE-BFC1-08002BE10318}
Provider=%ManufacturerName%
DriverVer=01/01/2023,1.0.0.1
CatalogFile=VirtualDisplayPort.cat

[DestinationDirs]
DefaultDestDir = 12

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
VirtualDisplayPort.sys  = 1,,

;*****************************************
; Virtual DisplayPort Install Section
;*****************************************

[Manufacturer]
%ManufacturerName%=Standard,NT$ARCH$

[Standard.NT$ARCH$]
%VirtualDisplayPort.DeviceDesc%=VirtualDisplayPort_Device, Root\VirtualDisplayPort

[VirtualDisplayPort_Device.NT]
CopyFiles=Drivers_Dir

[Drivers_Dir]
VirtualDisplayPort.sys

;-------------- Service installation
[VirtualDisplayPort_Device.NT.Services]
AddService = VirtualDisplayPort,%SPSVCINST_ASSOCSERVICE%, VirtualDisplayPort_Service_Inst

; -------------- VirtualDisplayPort driver install sections
[VirtualDisplayPort_Service_Inst]
DisplayName    = %VirtualDisplayPort.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\VirtualDisplayPort.sys

;
;--- VirtualDisplayPort_Device Coinstaller installation ------
;

[DestinationDirs]
VirtualDisplayPort_Device_CoInstaller_CopyFiles = 11

[VirtualDisplayPort_Device.NT.CoInstallers]
AddReg=VirtualDisplayPort_Device_CoInstaller_AddReg
CopyFiles=VirtualDisplayPort_Device_CoInstaller_CopyFiles

[VirtualDisplayPort_Device_CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"

[VirtualDisplayPort_Device_CoInstaller_CopyFiles]
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll

[SourceDisksFiles]
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames

[VirtualDisplayPort_Device.NT.Wdf]
KmdfService =  VirtualDisplayPort, VirtualDisplayPort_wdfsect
[VirtualDisplayPort_wdfsect]
KmdfLibraryVersion = $KMDFVERSION$

[Strings]
SPSVCINST_ASSOCSERVICE= 0x00000002
ManufacturerName="YourCompanyName"
ClassName="Display"
DiskName = "Virtual DisplayPort Installation Disk"
VirtualDisplayPort.DeviceDesc = "Virtual DisplayPort Device"
VirtualDisplayPort.SVCDESC = "Virtual DisplayPort Service"
```

使用此 INF 文件时,需要注意以下几点:

1. 将 `VirtualDisplayPort.sys` 替换为你的驱动程序文件的实际名称。

2. 更新 `DriverVer` 字段以反映你的驱动程序版本和日期。

3. 替换 `ManufacturerName` 为你的公司名称。

4. `$ARCH$`, `$KMDFCOINSTALLERVERSION$`, 和 `$KMDFVERSION$` 是在构建过程中会被自动替换的占位符。确保你的项目设置正确,以便这些值能被正确替换。

5. `Root\VirtualDisplayPort` 是此虚拟设备的硬件 ID。你可能需要根据你的具体实现来调整这个 ID。

6. 确保 `CatalogFile` 字段指向正确的目录签名文件。

7. 如果你的驱动程序需要额外的文件或设置,要在 INF 文件中相应地添加它们。

8. 内核模式驱动程序通常需要数字签名才能在 64 位 Windows 系统上加载。你需要获取一个内核模式代码签名证书并对驱动程序进行签名。

将这个 INF 文件保存在你的项目中,通常命名为 `VirtualDisplayPort.inf` 或类似的名称。在构建驱动程序包时,这个 INF 文件将被用来指导驱动程序的安装过程。

记住,开发和部署内核模式驱动程序需要非常谨慎,因为错误的驱动程序可能会导致系统不稳定或崩溃。始终在隔离的测试环境中进行开发和初始测试。
 

5.应用程序调用

// app.cpp  
#include <windows.h>  
#include <iostream>  

#define IOCTL_SET_DISPLAY_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)  

int main()  
{  
    HANDLE hDevice = CreateFile(L"\\\\.\\VirtualDisplayPort",  
        GENERIC_READ | GENERIC_WRITE,  
        0,  
        NULL,  
        OPEN_EXISTING,  
        FILE_ATTRIBUTE_NORMAL,  
        NULL);  

    if (hDevice == INVALID_HANDLE_VALUE)  
    {  
        std::cout << "Failed to open device. Error: " << GetLastError() << std::endl;  
        return 1;  
    }  

    ULONG mode[2] = { 1920, 1080 };  
    DWORD bytesReturned;  

    BOOL result = DeviceIoControl(  
        hDevice,  
        IOCTL_SET_DISPLAY_MODE,  
        &mode,  
        sizeof(mode),  
        NULL,  
        0,  
        &bytesReturned,  
        NULL  
    );  

    if (!result)  
    {  
        std::cout << "Failed to set display mode. Error: " << GetLastError() << std::endl;  
    }  
    else  
    {  
        std::cout << "Display mode set successfully." << std::endl;  
    }  

    CloseHandle(hDevice);  
    return 0;  
}

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值