Files
Virtual-Display-Driver/IddSampleDriver/Driver.cpp
T

999 lines
35 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Abstract:
This module contains a sample implementation of an indirect display driver. See the included README.md file and the
various TODO blocks throughout this file and all accompanying files for information on building a production driver.
MSDN documentation on indirect displays can be found at https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx.
Environment:
User Mode, UMDF
--*/
#include "Driver.h"
//#include "Driver.tmh"
#include<fstream>
#include<sstream>
#include<string>
#include<tuple>
#include<vector>
// jocke added for comming dynamic edid loading
#include <iostream>
using namespace std;
using namespace Microsoft::IndirectDisp;
using namespace Microsoft::WRL;
extern "C" DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD IddSampleDeviceAdd;
EVT_WDF_DEVICE_D0_ENTRY IddSampleDeviceD0Entry;
EVT_IDD_CX_ADAPTER_INIT_FINISHED IddSampleAdapterInitFinished;
EVT_IDD_CX_ADAPTER_COMMIT_MODES IddSampleAdapterCommitModes;
EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION IddSampleParseMonitorDescription;
EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES IddSampleMonitorGetDefaultModes;
EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES IddSampleMonitorQueryModes;
EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN IddSampleMonitorAssignSwapChain;
EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN IddSampleMonitorUnassignSwapChain;
EVT_IDD_CX_ADAPTER_QUERY_TARGET_INFO IddSampleEvtIddCxAdapterQueryTargetInfo;
EVT_IDD_CX_MONITOR_SET_DEFAULT_HDR_METADATA IddSampleEvtIddCxMonitorSetDefaultHdrMetadata;
EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION2 IddSampleEvtIddCxParseMonitorDescription2;
EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES2 IddSampleEvtIddCxMonitorQueryTargetModes2;
EVT_IDD_CX_ADAPTER_COMMIT_MODES2 IddSampleEvtIddCxAdapterCommitModes2;
EVT_IDD_CX_MONITOR_SET_GAMMA_RAMP IddSampleEvtIddCxMonitorSetGammaRamp;
vector<tuple<int, int, int>> monitorModes;
vector< DISPLAYCONFIG_VIDEO_SIGNAL_INFO> s_KnownMonitorModes2;
UINT numVirtualDisplays;
std::vector<unsigned char>
Microsoft::IndirectDisp::IndirectDeviceContext::s_knownMonitorEdidData;
struct IndirectDeviceContextWrapper
{
IndirectDeviceContext* pContext;
void Cleanup()
{
delete pContext;
pContext = nullptr;
}
};
// This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object
WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper);
extern "C" BOOL WINAPI DllMain(
_In_ HINSTANCE hInstance,
_In_ UINT dwReason,
_In_opt_ LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(hInstance);
UNREFERENCED_PARAMETER(lpReserved);
UNREFERENCED_PARAMETER(dwReason);
return TRUE;
}
_Use_decl_annotations_
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING pRegistryPath
)
{
WDF_DRIVER_CONFIG Config;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
WDF_DRIVER_CONFIG_INIT(&Config,
IddSampleDeviceAdd
);
Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE);
if (!NT_SUCCESS(Status))
{
return Status;
}
return Status;
}
vector<string> split(string& input, char delimiter)
{
istringstream stream(input);
string field;
vector<string> result;
while (getline(stream, field, delimiter)) {
result.push_back(field);
}
return result;
}
void loadOptions(string filepath) {
ifstream ifs(filepath);
string line;
vector<tuple<int, int, int>> res;
getline(ifs, line);//num of displays
numVirtualDisplays = stoi(line);
while (getline(ifs, line)) {
vector<string> strvec = split(line, ',');
if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") {
res.push_back({ stoi(strvec[0]),stoi(strvec[1]),stoi(strvec[2]) });
}
}
monitorModes = res; return;
}
_Use_decl_annotations_
NTSTATUS IddSampleDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit)
{
NTSTATUS Status = STATUS_SUCCESS;
WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
UNREFERENCED_PARAMETER(Driver);
// Register for power callbacks - in this sample only power-on is needed
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
PnpPowerCallbacks.EvtDeviceD0Entry = IddSampleDeviceD0Entry;
WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks);
IDD_CX_CLIENT_CONFIG IddConfig;
IDD_CX_CLIENT_CONFIG_INIT(&IddConfig);
// If the driver wishes to handle custom IoDeviceControl requests, it's necessary to use this callback since IddCx
// redirects IoDeviceControl requests to an internal queue. This sample does not need this.
// IddConfig.EvtIddCxDeviceIoControl = IddSampleIoDeviceControl;
loadOptions("C:\\IddSampleDriver\\option.txt");
IddConfig.EvtIddCxAdapterInitFinished = IddSampleAdapterInitFinished;
IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = IddSampleMonitorGetDefaultModes;
IddConfig.EvtIddCxMonitorAssignSwapChain = IddSampleMonitorAssignSwapChain;
IddConfig.EvtIddCxMonitorUnassignSwapChain = IddSampleMonitorUnassignSwapChain;
if (IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterQueryTargetInfo))
{
IddConfig.EvtIddCxAdapterQueryTargetInfo = IddSampleEvtIddCxAdapterQueryTargetInfo;
IddConfig.EvtIddCxMonitorSetDefaultHdrMetaData = IddSampleEvtIddCxMonitorSetDefaultHdrMetadata;
IddConfig.EvtIddCxParseMonitorDescription2 = IddSampleEvtIddCxParseMonitorDescription2;
IddConfig.EvtIddCxMonitorQueryTargetModes2 = IddSampleEvtIddCxMonitorQueryTargetModes2;
IddConfig.EvtIddCxAdapterCommitModes2 = IddSampleEvtIddCxAdapterCommitModes2;
IddConfig.EvtIddCxMonitorSetGammaRamp = IddSampleEvtIddCxMonitorSetGammaRamp;
}
else {
IddConfig.EvtIddCxParseMonitorDescription = IddSampleParseMonitorDescription;
IddConfig.EvtIddCxMonitorQueryTargetModes = IddSampleMonitorQueryModes;
IddConfig.EvtIddCxAdapterCommitModes = IddSampleAdapterCommitModes;
}
Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig);
if (!NT_SUCCESS(Status))
{
return Status;
}
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
Attr.EvtCleanupCallback = [](WDFOBJECT Object)
{
// Automatically cleanup the context when the WDF object is about to be deleted
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object);
if (pContext)
{
pContext->Cleanup();
}
};
WDFDEVICE Device = nullptr;
Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = IddCxDeviceInitialize(Device);
// Create a new device context object and attach it to the WDF device object
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device);
pContext->pContext = new IndirectDeviceContext(Device);
return Status;
}
_Use_decl_annotations_
NTSTATUS IddSampleDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState)
{
UNREFERENCED_PARAMETER(PreviousState);
// This function is called by WDF to start the device in the fully-on power state.
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device);
pContext->pContext->InitAdapter();
return STATUS_SUCCESS;
}
#pragma region Direct3DDevice
Direct3DDevice::Direct3DDevice(LUID AdapterLuid) : AdapterLuid(AdapterLuid)
{
}
Direct3DDevice::Direct3DDevice()
{
}
HRESULT Direct3DDevice::Init()
{
// The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be
// created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent.
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory));
if (FAILED(hr))
{
return hr;
}
// Find the specified render adapter
hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter));
if (FAILED(hr))
{
return hr;
}
// Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite.
hr = D3D11CreateDevice(Adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &Device, nullptr, &DeviceContext);
if (FAILED(hr))
{
// If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the
// system is in a transient state.
return hr;
}
return S_OK;
}
#pragma endregion
#pragma region SwapChainProcessor
SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, shared_ptr<Direct3DDevice> Device, HANDLE NewFrameEvent)
: m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent)
{
m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
// Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter
m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr));
}
SwapChainProcessor::~SwapChainProcessor()
{
// Alert the swap-chain processing thread to terminate
SetEvent(m_hTerminateEvent.Get());
if (m_hThread.Get())
{
// Wait for the thread to terminate
WaitForSingleObject(m_hThread.Get(), INFINITE);
}
}
DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument)
{
reinterpret_cast<SwapChainProcessor*>(Argument)->Run();
return 0;
}
void SwapChainProcessor::Run()
{
// For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently
// prioritize this thread for improved throughput in high CPU-load scenarios.
DWORD AvTask = 0;
HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask);
RunCore();
// Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to
// provide a new swap-chain if necessary.
WdfObjectDelete((WDFOBJECT)m_hSwapChain);
m_hSwapChain = nullptr;
AvRevertMmThreadCharacteristics(AvTaskHandle);
}
void SwapChainProcessor::RunCore()
{
// Get the DXGI device interface
ComPtr<IDXGIDevice> DxgiDevice;
HRESULT hr = m_Device->Device.As(&DxgiDevice);
if (FAILED(hr))
{
return;
}
IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {};
SetDevice.pDevice = DxgiDevice.Get();
hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice);
if (FAILED(hr))
{
return;
}
// Acquire and release buffers in a loop
for (;;)
{
ComPtr<IDXGIResource> AcquiredBuffer;
// Ask for the next buffer from the producer
IDARG_IN_RELEASEANDACQUIREBUFFER2 BufferInArgs = {};
BufferInArgs.Size = sizeof(BufferInArgs);
IDXGIResource* pSurface;
if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) {
IDARG_OUT_RELEASEANDACQUIREBUFFER2 Buffer = {};
hr = IddCxSwapChainReleaseAndAcquireBuffer2(m_hSwapChain, &BufferInArgs, &Buffer);
pSurface = Buffer.MetaData.pSurface;
}
else
{
IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {};
hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer);
pSurface = Buffer.MetaData.pSurface;
}
// AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available
if (hr == E_PENDING)
{
// We must wait for a new buffer
HANDLE WaitHandles[] =
{
m_hAvailableBufferEvent,
m_hTerminateEvent.Get()
};
DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), WaitHandles, FALSE, 16);
if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT)
{
// We have a new buffer, so try the AcquireBuffer again
continue;
}
else if (WaitResult == WAIT_OBJECT_0 + 1)
{
// We need to terminate
break;
}
else
{
// The wait was cancelled or something unexpected happened
hr = HRESULT_FROM_WIN32(WaitResult);
break;
}
}
else if (SUCCEEDED(hr))
{
AcquiredBuffer.Attach(pSurface);
// ==============================
// TODO: Process the frame here
//
// This is the most performance-critical section of code in an IddCx driver. It's important that whatever
// is done with the acquired surface be finished as quickly as possible. This operation could be:
// * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory)
// * a GPU encode operation
// * a GPU VPBlt to another surface
// * a GPU custom compute shader encode operation
// ==============================
AcquiredBuffer.Reset();
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
if (FAILED(hr))
{
break;
}
// ==============================
// TODO: Report frame statistics once the asynchronous encode/send work is completed
//
// Drivers should report information about sub-frame timings, like encode time, send time, etc.
// ==============================
// IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...);
}
else
{
// The swap-chain was likely abandoned (e.g. DXGI_ERROR_ACCESS_LOST), so exit the processing loop
break;
}
}
}
#pragma endregion
#pragma region IndirectDeviceContext
const UINT64 MHZ = 1000000;
const UINT64 KHZ = 1000;
constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 r) {
const UINT32 clock_rate = r * (v + 4) * (v + 4) + 1000;
return {
clock_rate, // pixel clock rate [Hz]
{ clock_rate, v + 4 }, // fractional horizontal refresh rate [Hz]
{ clock_rate, (v + 4) * (v + 4) }, // fractional vertical refresh rate [Hz]
{ h, v }, // (horizontal, vertical) active pixel resolution
{ h + 4, v + 4 }, // (horizontal, vertical) total pixel resolution
{ { 255, 0 }}, // video standard and vsync divider
DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE
};
}
// Jocke, createing a 512 byte array filled with 0 values (largest edid bin without breaking standards)
std::vector<BYTE> s_knownMonitorEdidData;
static void edidLoad(const std::string& filename) {
std::wofstream logFile("C:\\IddSampleDriver\\lddlog.txt", std::ios::app);
logFile << L"Entered EDIDLoad" << std::endl;
std::ifstream file(filename, std::ios::binary);
if (file.is_open()) {
logFile << L"Filename found for custom edid" << std::endl;
// Read file into vector
s_knownMonitorEdidData = std::vector<BYTE>(std::istreambuf_iterator<char>(file), {});
logFile << L"Replacing edid data in custom bin" << std::endl;
// Modify bytes at positions 8, 9, 10, and 11
s_knownMonitorEdidData[8] = 0x36;
s_knownMonitorEdidData[9] = 0x94;
s_knownMonitorEdidData[10] = 0x37;
s_knownMonitorEdidData[11] = 0x13;
logFile << L"calculating new checksum" << std::endl;
// Calculate checksum
int checksum = 0;
for (int i = 0; i < 128; ++i) {
checksum += s_knownMonitorEdidData[i];
}
checksum %= 256;
uint8_t bytesum = static_cast<BYTE>(checksum);
s_knownMonitorEdidData[127] = bytesum; // Store checksum as a byte, all byte number might be off by 1 (don't knwo how c++ handles array/vector position 0, if it's resereved or filed with data
logFile << L"Checksum calculated and placed on last byte of edid data" << std::endl;
}
else {
logFile << L"CustomEdid not found, checking default" << std::endl;
std::ifstream filedef("default.bin", std::ios::binary);
if (filedef.is_open()) {
// Read file into vector this is our pre patched default edid
s_knownMonitorEdidData = std::vector<BYTE>(std::istreambuf_iterator<char>(filedef), {});
logFile << L"Default.bin loaded into edid data" << std::endl;
}
else { // if all files are gone from install folder then use our minimal hardcoded edid
logFile << L"Defaulting to hardcoded edid" << std::endl;
string incode = " {\
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x36,0x94,0x37,0x13,0x00,0x00,0x00,0x00,\
0x05,0x16,0x01,0x03,0x6D,0x32,0x1C,0x78,0xEA,0x5E,0xC0,0xA4,0x59,0x4A,0x98,0x25,\
0x20,0x50,0x54,0x00,0x00,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,\
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A,0x80,0x18,0x71,0x38,0x2D,0x40,0x58,0x2C,\
0x45,0x00,0xF4,0x19,0x11,0x00,0x00,0x1E,0x00,0x00,0x00,0xFF,0x00,0x4D,0x54,0x54,\
0x31,0x33,0x33,0x37,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,0x00,0x3B,\
0x3D,0x42,0x44,0x0F,0x00,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,\
0x00,0x4D,0x54,0x54,0x31,0x33,0x33,0x37,0x0A,0x20,0x20,0x20,0x20,0x20,0x00,0x83}";
s_knownMonitorEdidData = std::vector<BYTE>(incode.begin(), incode.end());
}
}
logFile << L"Exiting EDID Load and closing log" << std::endl;
logFile.close();
};
// This is a sample monitor EDID - FOR SAMPLE PURPOSES ONLY
/*
const BYTE IndirectDeviceContext::s_KnownMonitorEdid[] = {
0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x36,0x94,0x37,0x13,0x37,0x13,0x37,0x13,
0x0d,0x22,0x01,0x03,0x80,0x32,0x1f,0x78,0x07,0xee,0x95,0xa3,0x54,0x4c,0x99,0x26,
0x0f,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3a,0x80,0x18,0x71,0x38,0x2d,0x40,0x58,0x2c,
0x45,0x00,0x63,0xc8,0x10,0x00,0x00,0x06,0x00,0x00,0x00,0xfd,0x00,0x17,0xf0,0x0f,
0xff,0x37,0x00,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfc,0x00,0x4d,
0x54,0x54,0x31,0x33,0x33,0x37,0x0a,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x10,
0x00,0x13,0x37,0x13,0x37,0x13,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x33,
0x02,0x03,0x23,0x40,0x23,0x09,0x00,0x00,0xe6,0x06,0x0d,0x01,0xb4,0x78,0x01,0x67,
0x03,0x0c,0x00,0x00,0x00,0x10,0x6e,0xe3,0x05,0x80,0x00,0x67,0xd8,0x5d,0xc4,0x01,
0x6e,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9a,
0x70,0x13,0x79,0x00,0x00,0x00,0x00,0x1e,0x4d,0x54,0x54,0x0d,0x00,0x39,0x05,0x00,
0x00,0x0e,0x18,0x12,0x56,0x44,0x44,0x20,0x62,0x79,0x20,0x4d,0x69,0x6b,0x65,0x54,
0x68,0x65,0x54,0x65,0x63,0x68,0x03,0x01,0x14,0x72,0xd6,0x00,0x84,0xff,0x1d,0x9f,
0x00,0x2f,0x80,0x1f,0x00,0xdf,0x10,0x18,0x02,0x02,0x00,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x90
};
*/
IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) :
m_WdfDevice(WdfDevice)
{
m_Adapter = {};
}
IndirectDeviceContext::~IndirectDeviceContext()
{
m_ProcessingThread.reset();
}
#define NUM_VIRTUAL_DISPLAYS 1
void IndirectDeviceContext::InitAdapter()
{
edidLoad("C:\\IddSampleDriver\\user_edid.bin");
// ==============================
// TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version
// numbers are used for telemetry and may be displayed to the user in some situations.
//
// This is also where static per-adapter capabilities are determined.
// ==============================
IDDCX_ADAPTER_CAPS AdapterCaps = {};
AdapterCaps.Size = sizeof(AdapterCaps);
if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) {
AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
}
// Declare basic feature support for the adapter (required)
AdapterCaps.MaxMonitorsSupported = numVirtualDisplays;
AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics);
AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER;
// Declare your device strings for telemetry (required)
AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"IddSample Device";
AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"Microsoft";
AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"IddSample Model";
// Declare your hardware and firmware versions (required)
IDDCX_ENDPOINT_VERSION Version = {};
Version.Size = sizeof(Version);
Version.MajorVer = 1;
AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version;
AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version;
// Initialize a WDF context that can store a pointer to the device context object
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
IDARG_IN_ADAPTER_INIT AdapterInit = {};
AdapterInit.WdfDevice = m_WdfDevice;
AdapterInit.pCaps = &AdapterCaps;
AdapterInit.ObjectAttributes = &Attr;
// Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later
IDARG_OUT_ADAPTER_INIT AdapterInitOut;
NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut);
if (NT_SUCCESS(Status))
{
// Store a reference to the WDF adapter handle
m_Adapter = AdapterInitOut.AdapterObject;
// Store the device context object into the WDF object context
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject);
pContext->pContext = this;
}
}
void IndirectDeviceContext::FinishInit()
{
for (unsigned int i = 0; i < numVirtualDisplays; i++) {
CreateMonitor(i);
}
}
void IndirectDeviceContext::CreateMonitor(unsigned int index) {
// ==============================
// TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDID
// provided here is purely for demonstration, as it describes only 640x480 @ 60 Hz and 800x600 @ 60 Hz. Monitor
// manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS to optimize
// settings like viewing distance and scale factor. Manufacturers should also use a unique serial number every
// single device to ensure the OS can tell the monitors apart.
// ==============================
std::wofstream logFile("C:\\IddSampleDriver\\lddlog.txt", std::ios::app);
logFile << L"Creating a monitor start at " << std::endl;
WDF_OBJECT_ATTRIBUTES Attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
logFile << L"Initialized WDF object attributes at " << std::endl;
IDDCX_MONITOR_INFO MonitorInfo = {};
MonitorInfo.Size = sizeof(MonitorInfo);
MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
MonitorInfo.ConnectorIndex = index;
MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription);
MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
MonitorInfo.MonitorDescription.DataSize = sizeof(s_knownMonitorEdidData);
MonitorInfo.MonitorDescription.pData = s_knownMonitorEdidData.data();
// ==============================
// TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not
// permanently attached to the display adapter device object. The container ID is typically made unique for each
// monitor and can be used to associate the monitor with other devices, like audio or input devices. In this
// sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a
// unique monitor or to use "this" device's container ID for a permanent/integrated monitor.
// ==============================
logFile << L"Initialized monitor info at " << std::endl;
CoCreateGuid(&MonitorInfo.MonitorContainerId);
logFile << L"Created container ID at " << std::endl;
IDARG_IN_MONITORCREATE MonitorCreate = {};
MonitorCreate.ObjectAttributes = &Attr;
MonitorCreate.pMonitorInfo = &MonitorInfo;
IDARG_OUT_MONITORCREATE MonitorCreateOut;
NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut);
if (!NT_SUCCESS(Status))
{
logFile << L"Failed to create monitor object. Status code: " << Status << std::endl;
}
if (NT_SUCCESS(Status))
{
m_Monitor = MonitorCreateOut.MonitorObject;
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject);
pContext->pContext = this;
logFile << L"Created monitor object and associated it with this device context at " << std::endl;
IDARG_OUT_MONITORARRIVAL ArrivalOut;
Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut);
if (!NT_SUCCESS(Status))
{
logFile << L"Failed to plug in monitor. Status code: " << Status << std::endl;
}
else
{
logFile << L"Monitor has been plugged in at " << std::endl;
}
}
else {
logFile << L"Weird " << std::endl;
}
logFile << L"Creating a monitor end at " << std::endl;
logFile.close();
}
void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) // Jocke, is it here we could assign rendering gpu?
{
m_ProcessingThread.reset();
auto Device = make_shared<Direct3DDevice>(RenderAdapter);
if (FAILED(Device->Init()))
{
// It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new
// swap-chain and try again.
WdfObjectDelete(SwapChain);
}
else
{
// Create a new swap-chain processing thread
m_ProcessingThread.reset(new SwapChainProcessor(SwapChain, Device, NewFrameEvent));
}
}
void IndirectDeviceContext::UnassignSwapChain()
{
// Stop processing the last swap-chain
m_ProcessingThread.reset();
}
#pragma endregion
#pragma region DDI Callbacks
_Use_decl_annotations_
NTSTATUS IddSampleAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs)
{
// This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible
// to report attached monitors.
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject);
if (NT_SUCCESS(pInArgs->AdapterInitStatus))
{
pContext->pContext->FinishInit();
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs)
{
UNREFERENCED_PARAMETER(AdapterObject);
UNREFERENCED_PARAMETER(pInArgs);
// For the sample, do nothing when modes are picked - the swap-chain is taken care of by IddCx
// ==============================
// TODO: In a real driver, this function would be used to reconfigure the device to commit the new modes. Loop
// through pInArgs->pPaths and look for IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor
// should be turned off).
// ==============================
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs)
{
// ==============================
// TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In
// this sample driver, we hard-code the EDID, so this function can generate known modes.
// ==============================
for (int i = 0; i < monitorModes.size(); i++) {
s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i])));
}
pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size();
if (pInArgs->MonitorModeBufferInputCount < monitorModes.size())
{
// Return success if there was no buffer, since the caller was only asking for a count of modes
return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
else
{
// Copy the known modes to the output buffer
for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++)
{
pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE);
pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex];
}
// Set the preferred mode as represented in the EDID
pOutArgs->PreferredMonitorModeIdx = 0;
return STATUS_SUCCESS;
}
}
_Use_decl_annotations_
NTSTATUS IddSampleMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs)
{
UNREFERENCED_PARAMETER(MonitorObject);
UNREFERENCED_PARAMETER(pInArgs);
UNREFERENCED_PARAMETER(pOutArgs);
// Should never be called since we create a single monitor with a known EDID in this sample driver.
// ==============================
// TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID.
// Drivers should report modes that are guaranteed to be supported by the transport protocol and by nearly all
// monitors (such 640x480, 800x600, or 1024x768). If the driver has access to monitor modes from a descriptor other
// than an EDID, those modes would also be reported here.
// ==============================
return STATUS_NOT_IMPLEMENTED;
}
/// <summary>
/// Creates a target mode from the fundamental mode attributes.
/// </summary>
void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSync) // Jocke, is it her we have the issue with 640*480 init when multiple vdds are chosen
{
Mode.totalSize.cx = Mode.activeSize.cx = Width;
Mode.totalSize.cy = Mode.activeSize.cy = Height;
Mode.AdditionalSignalInfo.vSyncFreqDivider = 1;
Mode.AdditionalSignalInfo.videoStandard = 255;
Mode.vSyncFreq.Numerator = VSync;
Mode.vSyncFreq.Denominator = Mode.hSyncFreq.Denominator = 1;
Mode.hSyncFreq.Numerator = VSync * Height;
Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
Mode.pixelRate = VSync * Width * Height;
}
void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSync)
{
Mode.Size = sizeof(Mode);
CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync);
}
void CreateTargetMode2(IDDCX_TARGET_MODE2& Mode, UINT Width, UINT Height, UINT VSync)
{
Mode.Size = sizeof(Mode);
Mode.BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8 | IDDCX_BITS_PER_COMPONENT_10;
CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync);
}
_Use_decl_annotations_
NTSTATUS IddSampleMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs)////////////////////////////////////////////////////////////////////////////////
{
UNREFERENCED_PARAMETER(MonitorObject);
vector<IDDCX_TARGET_MODE> TargetModes(monitorModes.size());
// Create a set of modes supported for frame processing and scan-out. These are typically not based on the
// monitor's descriptor and instead are based on the static processing capability of the device. The OS will
// report the available set of modes for a given output as the intersection of monitor modes with target modes.
for (int i = 0; i < monitorModes.size(); i++) {
CreateTargetMode(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]));
}
pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size();
if (pInArgs->TargetModeBufferInputCount >= TargetModes.size())
{
copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes);
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs)
{
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject);
pContext->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable);
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject)
{
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject);
pContext->pContext->UnassignSwapChain();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxAdapterQueryTargetInfo(
IDDCX_ADAPTER AdapterObject,
IDARG_IN_QUERYTARGET_INFO* pInArgs,
IDARG_OUT_QUERYTARGET_INFO* pOutArgs
)
{
UNREFERENCED_PARAMETER(AdapterObject);
UNREFERENCED_PARAMETER(pInArgs);
pOutArgs->TargetCaps = IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE;
pOutArgs->DitheringSupport.Rgb = IDDCX_BITS_PER_COMPONENT_10;
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxMonitorSetDefaultHdrMetadata(
IDDCX_MONITOR MonitorObject,
const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* pInArgs
)
{
UNREFERENCED_PARAMETER(MonitorObject);
UNREFERENCED_PARAMETER(pInArgs);
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxParseMonitorDescription2(
const IDARG_IN_PARSEMONITORDESCRIPTION2* pInArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs
)
{
// ==============================
// TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In
// this sample driver, we hard-code the EDID, so this function can generate known modes.
// ==============================
for (int i = 0; i < monitorModes.size(); i++) {
s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i])));
}
pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size();
if (pInArgs->MonitorModeBufferInputCount < monitorModes.size())
{
// Return success if there was no buffer, since the caller was only asking for a count of modes
return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
else
{
// Copy the known modes to the output buffer
for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++)
{
pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE2);
pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex];
pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8 | IDDCX_BITS_PER_COMPONENT_10;
}
// Set the preferred mode as represented in the EDID
pOutArgs->PreferredMonitorModeIdx = 0;
return STATUS_SUCCESS;
}
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxMonitorQueryTargetModes2(
IDDCX_MONITOR MonitorObject,
const IDARG_IN_QUERYTARGETMODES2* pInArgs,
IDARG_OUT_QUERYTARGETMODES* pOutArgs
)
{
UNREFERENCED_PARAMETER(MonitorObject);
vector<IDDCX_TARGET_MODE2> TargetModes(monitorModes.size());
// Create a set of modes supported for frame processing and scan-out. These are typically not based on the
// monitor's descriptor and instead are based on the static processing capability of the device. The OS will
// report the available set of modes for a given output as the intersection of monitor modes with target modes.
for (int i = 0; i < monitorModes.size(); i++) {
CreateTargetMode2(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]));
}
pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size();
if (pInArgs->TargetModeBufferInputCount >= TargetModes.size())
{
copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes);
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxAdapterCommitModes2(
IDDCX_ADAPTER AdapterObject,
const IDARG_IN_COMMITMODES2* pInArgs
)
{
UNREFERENCED_PARAMETER(AdapterObject);
UNREFERENCED_PARAMETER(pInArgs);
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS IddSampleEvtIddCxMonitorSetGammaRamp(
IDDCX_MONITOR MonitorObject,
const IDARG_IN_SET_GAMMARAMP* pInArgs
)
{
UNREFERENCED_PARAMETER(MonitorObject);
UNREFERENCED_PARAMETER(pInArgs);
return STATUS_SUCCESS;
}
#pragma endregion