// Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. // // NVIDIA CORPORATION and its licensors retain all intellectual property // and proprietary rights in and to this software, related documentation // and any modifications thereto. Any use, reproduction, disclosure or // distribution of this software and related documentation without an express // license agreement from NVIDIA CORPORATION is strictly prohibited. #define NOMINMAX #include #include #include #include #include #include #include #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") #include "D3D12Renderer.h" #include "D3D12RenderVs.h" #include "D3D12RenderImage.h" #include "nvapi.h" using namespace Microsoft::WRL; // Normally, this is a constant set by IHV. We use a variable for testing purposes. //UINT32 g_VENDOR_ID = 0xD23E; UINT32 g_VENDOR_ID = 0x2709; bool g_bPause = false; #define VENDOR_ID g_VENDOR_ID INT32 g_FrameDumpIndex = 0; // Frame Number to dump (base frame is #1) INT32 g_FrameCounter = 0; // To keep track of number of frames rendered std::string g_Filename = "frame_dump.bmp"; // filename to dump image to // To be used for storing Display Properties struct DIRECT_MODE_DISPLAY_PROPERTIES { LUID AdapterLuid; // The Adapter the display is plugged into. BYTE EDID[512]; // Extended display identification data. UINT EdidSize; // Size of the EDID by NvAPI }; // To be used for DSC configuration struct DIRECT_MODE_DSC_CONFIG { UINT DscVersion; // DSC version supported by the HMD UINT DscSliceCount; // DSC slice count needed for DSC hardware UINT DscOutputBppx16; // DSC Output BPP multiplied by 16 to achieve the desired compression ratio }; void printUsage(); void directModeCheckVendorId(); bool directModeEnableDisable(bool bEnable); bool directModeEnumDisplays(); bool directModeEnumModes(unsigned int displaySelection); bool directModePresentVsyncTest(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest); bool directModeDscPresentVsyncTest(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig, unsigned int hexColor); bool runDirectModeVsPattern(); bool runDirectModeImage(const wchar_t* image_filename); bool directModeVsPattern(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig); bool directModeImage(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig, const wchar_t* filename); bool directModeGetDisplayInfo(NV_DIRECT_MODE_DISPLAY_HANDLE* hDisplay, DIRECT_MODE_DISPLAY_PROPERTIES* pProperties); HRESULT directModeRegisterEventCallbacks(NvU32 displayId, NvEventHandle *pEventHandle, void* pHpCallback, void* pHdcpCallback, void* pHdcpStatusCallback); extern UINT32 g_VENDOR_ID; int main(int argc, char *argv[]) { #if defined(DEBUG) || defined(_DEBUG) ComPtr debugController; if (FAILED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { std::printf("Failed to initialize DirectX12 debug interface\r\n"); } debugController->EnableDebugLayer(); #endif if (FAILED(CoInitializeEx(NULL, COINITBASE_MULTITHREADED))) { std::printf("Failed to initialize COM library\r\n"); } if (argc <= 1) { printUsage(); return 0; } const char* command = argv[1]; if (std::strcmp(command, "-h") == 0 || std::strcmp(command, "-help") == 0) { printUsage(); } else if (strcmp(command, "-enable") == 0) { directModeEnableDisable(true); } else if (strcmp(command, "-disable") == 0) { directModeEnableDisable(false); } else if ((std::strcmp(command, "-enum_disp") == 0) || (std::strcmp(command, "-enum_disps") == 0)) { directModeEnumDisplays(); } else if ((std::strcmp(command, "-enum_mode") == 0) || (std::strcmp(command, "-enum_modes") == 0)) { if (argc <= 2) { std::fprintf(stderr, __FUNCTION__ ": Expected display selection as output by -enum_disp\n"); return 1; } int displaySelection = std::strtoul(argv[2], nullptr, 0); directModeEnumModes(displaySelection); } else if (strcmp(command, "-present_test") == 0) { if (argc <= 3) { std::fprintf(stderr, __FUNCTION__ ": Expected display selection as output by -enum_disp, ex. 0\n"); std::fprintf(stderr, __FUNCTION__ ": Expected mode selection as output by -enum_mode, ex. 0\n"); return 1; } int displaySelection = std::strtoul(argv[2], nullptr, 0); int modeSelection = std::strtoul(argv[3], nullptr, 0); int presentType = (argc >= 5) ? std::strtoul(argv[4], nullptr, 0) : 0; int notifyTest = (argc >= 6) ? std::strtoul(argv[5], nullptr, 0) : 0; g_FrameDumpIndex = (argc >= 7) ? std::strtoul(argv[6], nullptr, 0) : 0; if (argc >= 8) { g_Filename = argv[7]; } if (presentType > 0) { directModePresentVsyncTest(displaySelection, modeSelection, presentType == 2, notifyTest == 1); } else { std::fprintf(stderr, __FUNCTION__ ": Unrecognized option: %s\nUse -h to see available options\n", command); } } else if (strcmp(command, "-dsc_present_test") == 0) { switch (argc) { case 2: std::fprintf(stderr, __FUNCTION__ ": Expected display selection as output by -enum_disp, ex. 0\n"); case 3: std::fprintf(stderr, __FUNCTION__ ": Expected mode selection as output by -enum_mode, ex. 0\n"); case 4: std::fprintf(stderr, __FUNCTION__ ": Expected present type selection as 1: Event; OR 2: Queued\n"); case 5: std::fprintf(stderr, __FUNCTION__ ": Expected notify selection as 0: Not Needed; OR 2: Needed\n"); case 6: std::fprintf(stderr, __FUNCTION__ ": Expected DSC version as either 1:0x11 (v1.1); OR 2:0x12 (v1.2)\n"); case 7: std::fprintf(stderr, __FUNCTION__ ": Expected DSC slice count selection as either 1, 2, 4, or 8\n"); case 8: std::fprintf(stderr, __FUNCTION__ ": Expected DSC output BPP multipled by 16, ex. 128\n"); return 1; } UINT displaySelection = (argc >= 3) ? std::strtoul(argv[2], nullptr, 0) : 0; UINT modeSelection = (argc >= 4) ? std::strtoul(argv[3], nullptr, 0) : 0; UINT presentType = (argc >= 5) ? std::strtoul(argv[4], nullptr, 0) : 0; UINT notifyTest = (argc >= 6) ? std::strtoul(argv[5], nullptr, 0) : 0; UINT dscVersion = (argc >= 7) ? std::strtoul(argv[6], nullptr, 0) : 0; UINT dscSliceCount = (argc >= 8) ? std::strtoul(argv[7], nullptr, 0) : 0; UINT dscOutputBppx16 = (argc >= 9) ? std::strtoul(argv[8], nullptr, 0) : 0; UINT hexColor = (argc >= 10) ? std::strtoul(argv[9], nullptr, 0) : 0; if (presentType > 0) { DIRECT_MODE_DSC_CONFIG dscConfig = { dscVersion , dscSliceCount , dscOutputBppx16 }; directModeDscPresentVsyncTest(displaySelection, modeSelection, presentType == 2, notifyTest == 1, dscConfig, hexColor); } else { std::fprintf(stderr, __FUNCTION__ ": Unrecognized option: %s\nUse -h to see available options\n", command); } } else if (strcmp(command, "-vspattern") == 0) { g_FrameDumpIndex = (argc >= 3) ? std::strtoul(argv[2], nullptr, 0) : 0; if (argc >= 3) { std::printf("Dumping frame\n"); } runDirectModeVsPattern(); } else if (strcmp(command, "-image") == 0) { // Check input arguments if (argc < 3) { std::fprintf(stderr, __FUNCTION__ ": Expected image filename, ex: \"sunset.jpg\"\n"); return 1; } // Check that file exists std::string image_path(argv[2]); std::wstring image_pathw(image_path.begin(), image_path.end()); if (!PathFileExistsW(image_pathw.c_str())) { std::fprintf(stderr, __FUNCTION__ ": Could not find image file specified (%s)", image_path.c_str()); return 1; } g_FrameDumpIndex = (argc >= 4) ? std::strtoul(argv[3], nullptr, 0) : 0; if (argc >= 4) { std::printf("Dumping frame\n"); } runDirectModeImage(image_pathw.c_str()); } else { std::fprintf(stderr, __FUNCTION__ ": Unrecognized option: %s\nUse -h to see available options\n", command); return 1; } return 0; } void printUsage() { std::printf( "Usage: directmode.exe \n" " Available options:\n" " -h, -help Show usage\n" " -enable Enable DirectMode\n" " -disable Disable DirectMode\n" " -enum_disp Enumerate all VR-capable displays\n" " -enum_mode Enumerate all modes for given display\n" " -present_test \n" " Test presenting surfaces\n" " where is either 1: event 2: queued\n" " is the frame number to dump (if needed)\n" " is the filename to save at\n" " -dsc_present_test \n" " Test presenting surfaces\n" " where is either 1: event 2: queued\n" " is either 0x11 (v1.1) or 0x12 (v1.2)\n" " is either 1, 2, 4 or 8\n" " is DSC output BPP multiplied by 16\n" "\n" " Environment variables:\n" " DM_VENDOR_ID Used for all API calls; ex. 0xAC10\n" ); } void directModeCheckVendorId() { wchar_t vendorIdString[MAX_PATH] = { 0 }; auto ret = GetEnvironmentVariable(L"DM_VENDOR_ID", vendorIdString, _countof(vendorIdString)); if ((ret > 0) && (ret < _countof(vendorIdString))) { g_VENDOR_ID = std::wcstoul(vendorIdString, nullptr, 0); std::printf(" Using environment vendorId: 0x%x\n", g_VENDOR_ID); } else { std::printf(" Using default vendorId: 0x%x\n", g_VENDOR_ID); } } bool directModeEnableDisable(bool bEnable) { std::printf(">>DirectMode Enable/Disable<<\n"); directModeCheckVendorId(); auto status = NVAPI_ERROR; // Toggle system-wide enablement or disablement if (bEnable) { status = NvAPI_DISP_EnableDirectMode(VENDOR_ID, 0); } else { status = NvAPI_DISP_DisableDirectMode(VENDOR_ID, 0); } if (status != NVAPI_OK) { if (status == NVAPI_REQUIRES_REBOOT) { std::printf("%s the DirectMode requires a reboot to complete the operation\n", bEnable ? "Enabling" : "Disabling"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to %s DirectMode with error=0x%x\n", bEnable ? "enable" : "disable", status); return false; } } std::printf("NVIDIA DirectMode VR %s successfully.\n", bEnable ? "ENABLED" : "DISABLED"); return true; } bool directModeGetDisplayInfo(NV_DIRECT_MODE_DISPLAY_HANDLE* hDisplay, DIRECT_MODE_DISPLAY_PROPERTIES* pProperties) { if (!pProperties) { return false; } memset(pProperties, 0, sizeof(DIRECT_MODE_DISPLAY_PROPERTIES)); NvPhysicalGpuHandle hNvPhysicalGpu; NvAPI_Status status; status = NvAPI_SYS_GetPhysicalGpuFromDisplayId(hDisplay->displayId, &hNvPhysicalGpu); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get GPU for DisplayId: 0x%x\n", hDisplay->displayId); return false; } // Get the raw EDID using the displayId obtained from enumeration NV_EDID nvEdid = { 0 }; nvEdid.version = NV_EDID_VER; status = NvAPI_GPU_GetEDID(hNvPhysicalGpu, hDisplay->displayId, &nvEdid); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get EDID for DisplayId: 0x%x\n", hDisplay->displayId); return false; } // Sanity Check if the size is bigger than the Buffer Size if (nvEdid.sizeofEDID > sizeof(pProperties->EDID)) { return false; } // Copy the first 256 Bytes of EDID memcpy(pProperties->EDID, nvEdid.EDID_Data, NV_EDID_DATA_SIZE); // If the size of the EDID is greater than 256 Bytes, // then call the GetEDID again with offset of 256 to get next 256 Bytes of EDID. if (nvEdid.sizeofEDID > NV_EDID_DATA_SIZE) { memset(&nvEdid, 0, sizeof(NV_EDID)); nvEdid.version = NV_EDID_VER; nvEdid.offset = NV_EDID_DATA_SIZE; status = NvAPI_GPU_GetEDID(hNvPhysicalGpu, hDisplay->displayId, &nvEdid); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get EDID for DisplayId: 0x%x\n", hDisplay->displayId); return false; } memcpy((pProperties->EDID + NV_EDID_DATA_SIZE), nvEdid.EDID_Data, NV_EDID_DATA_SIZE); } // Store the exact size of the EDID pProperties->EdidSize = nvEdid.sizeofEDID; // Get a LUID to identify the adapter IDXGIAdapter* pAdapter = NULL; status = NvAPI_D3D_GetIDXGIAdapter(hNvPhysicalGpu, &pAdapter); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get the Adapter\n"); return false; } DXGI_ADAPTER_DESC adapterDesc = { 0 }; pAdapter->GetDesc(&adapterDesc); pAdapter->Release(); pAdapter = NULL; // Return the requested information pProperties->AdapterLuid = adapterDesc.AdapterLuid; return true; } bool directModeEnumDisplays() { std::printf(">>DirectMode Display Enumeration<<\n"); directModeCheckVendorId(); // Get the display handles and display identifiers // NVIDIA GPUs currently support up to a maximum of four simultaneous outputs NvU32 numDisplays = 4; NV_DIRECT_MODE_DISPLAY_HANDLE displays[4] = { 0 }; auto status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for displayIds with error=0x%x\n", status); return false; } if (numDisplays == 0) { std::fprintf(stderr, __FUNCTION__ ": No DM-enabled displays found. Ensure Direct Mode is enabled.\n"); return false; } DIRECT_MODE_DISPLAY_PROPERTIES displayProprties = { 0 }; // List out all DM-enabled displays with their identifiers and properties std::printf(" DM-enabled displays:\n"); for (NvU32 i = 0; i < numDisplays; ++i) { std::printf("\n display: [%d]\n", i); std::printf(" displayId: 0x%x \n", displays[i].displayId); auto ret = directModeGetDisplayInfo(&displays[i], &displayProprties); if (ret) { std::printf(" adapter LUID: { 0x%x, 0x%x }\n", displayProprties.AdapterLuid.HighPart, displayProprties.AdapterLuid.LowPart); std::printf(" raw EDID:"); for (auto j = 0u; j < displayProprties.EdidSize; ++j) { if ((j % 16) == 0) { std::printf("\n "); } // Distinguish Extension Blocks if ((j % 128) == 0) { std::printf("\n "); } std::printf(" %02x", displayProprties.EDID[j]); } std::printf("\n"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to query for display properties"); } } return true; } bool directModeEnumModes(unsigned int displaySelection) { std::printf(">>DirectMode Display Mode Enumeration<<\n"); directModeCheckVendorId(); // Get the display handle NvU32 numDisplays = 4; NV_DIRECT_MODE_DISPLAY_HANDLE displays[4] = { 0 }; auto status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { return false; } if (numDisplays > displaySelection) { auto& display = displays[displaySelection]; // Get the number of supported modes NvU32 numModes = 0; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, nullptr, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode count with error=0x%x\n", status); return false; } if (numModes == 0) { std::fprintf(stderr, __FUNCTION__ ": No modes found for the specified displayId.\n"); return false; } // Get the modes' descriptions NV_DIRECT_MODE_INFO* modeDescs = new NV_DIRECT_MODE_INFO[numModes]; for (NvU32 i = 0; i < numModes; ++i) { memset(&modeDescs[i], 0, sizeof(NV_DIRECT_MODE_INFO)); modeDescs[i].version = NV_DIRECT_MODE_INFO_VER; } status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, modeDescs, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { NV_DIRECT_MODE_INFO_V1* modeDescs_v1 = new NV_DIRECT_MODE_INFO_V1[numModes]; memset(modeDescs, 0, sizeof(NV_DIRECT_MODE_INFO) * numModes); memset(modeDescs_v1, 0, sizeof(NV_DIRECT_MODE_INFO_V1) * numModes); for (NvU32 i = 0; i < numModes; ++i) { modeDescs_v1[i].version = NV_DIRECT_MODE_INFO_VER1; } status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, (NV_DIRECT_MODE_INFO*)modeDescs_v1, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); delete[] modeDescs; delete[] modeDescs_v1; return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); for (NvU32 i = 0; i < numModes; ++i) { modeDescs[i].width = modeDescs_v1[i].width; modeDescs[i].height = modeDescs_v1[i].height; modeDescs[i].format = modeDescs_v1[i].format; modeDescs[i].refresh.numerator = modeDescs_v1[i].refresh.numerator; modeDescs[i].refresh.denominator = modeDescs_v1[i].refresh.denominator; modeDescs[i].ScanlineOrdering = modeDescs_v1[i].ScanlineOrdering; modeDescs[i].scaling = modeDescs_v1[i].scaling; } delete[] modeDescs_v1; } if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); delete[] modeDescs; return false; } // List out all modes and their mode info std::printf(" Mode descriptions for displayId: 0x%x:\n", display.displayId); for (NvU32 i = 0; i < numModes; ++i) { std::printf("\n mode: [%d]\n", i); std::printf(" width: %d\n", modeDescs[i].width); std::printf(" height: %d\n", modeDescs[i].height); std::printf(" format: %d\n", modeDescs[i].format); std::printf(" refreshrate: %d %d\n", modeDescs[i].refresh.numerator, modeDescs[i].refresh.denominator); std::printf(" scanline: %d\n", modeDescs[i].ScanlineOrdering); std::printf(" scaling: %d\n", modeDescs[i].scaling); } delete[] modeDescs; return true; } std::fprintf(stderr, __FUNCTION__ ": No display found for the specified selection.\n"); return false; } bool translateParams(unsigned int displaySelection, unsigned int modeSelection, NV_DIRECT_MODE_DISPLAY_HANDLE& display, NV_DIRECT_MODE_INFO& modeDesc, DXGI_FORMAT &dxgiFormat, DIRECT_MODE_DSC_CONFIG* pDscConfig) { NvU32 numDisplays = 4; NV_DIRECT_MODE_DISPLAY_HANDLE displays[4] = { 0 }; auto status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { return false; } if (numDisplays > displaySelection) { display = displays[displaySelection]; NvU32 numModes = 0; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, nullptr, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { return false; } NV_DIRECT_MODE_INFO* modeDescs = new NV_DIRECT_MODE_INFO[numModes]; for (NvU32 i = 0; i < numModes; ++i) { memset(&modeDescs[i], 0, sizeof(NV_DIRECT_MODE_INFO)); modeDescs[i].version = NV_DIRECT_MODE_INFO_VER; } status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, modeDescs, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { NV_DIRECT_MODE_INFO_V1* modeDescs_v1 = new NV_DIRECT_MODE_INFO_V1[numModes]; memset(modeDescs, 0, sizeof(NV_DIRECT_MODE_INFO) * numModes); memset(modeDescs_v1, 0, sizeof(NV_DIRECT_MODE_INFO_V1) * numModes); for (NvU32 i = 0; i < numModes; ++i) { modeDescs_v1[i].version = NV_DIRECT_MODE_INFO_VER1; } status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &numModes, (NV_DIRECT_MODE_INFO*)modeDescs_v1, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); delete[] modeDescs; delete[] modeDescs_v1; return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); for (NvU32 i = 0; i < numModes; ++i) { modeDescs[i].width = modeDescs_v1[i].width; modeDescs[i].height = modeDescs_v1[i].height; modeDescs[i].format = modeDescs_v1[i].format; modeDescs[i].refresh.numerator = modeDescs_v1[i].refresh.numerator; modeDescs[i].refresh.denominator = modeDescs_v1[i].refresh.denominator; modeDescs[i].ScanlineOrdering = modeDescs_v1[i].ScanlineOrdering; modeDescs[i].scaling = modeDescs_v1[i].scaling; } delete[] modeDescs_v1; } if (status != NVAPI_OK) { delete[] modeDescs; return false; } if (numModes > modeSelection) { modeDesc = modeDescs[modeSelection]; switch (modeDesc.format) { case NV_FORMAT_A8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; case NV_FORMAT_A8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break; case NV_FORMAT_X8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8X8_UNORM; break; case NV_FORMAT_A2B10G10R10: dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM; break; case NV_FORMAT_A16B16G16R16F: dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; break; case NV_FORMAT_UNKNOWN: default: // Add other desired formats dxgiFormat = DXGI_FORMAT_UNKNOWN; break; } std::printf(" Using the following display and mode:\n"); std::printf("\n display: [%d]\n", displaySelection); std::printf(" displayId: 0x%x \n", display.displayId); std::printf("\n mode: [%d]\n", modeSelection); std::printf(" width: %d\n", modeDesc.width); std::printf(" height: %d\n", modeDesc.height); std::printf(" format: %d\n", modeDesc.format); std::printf(" refreshrate: %d %d\n", modeDesc.refresh.numerator, modeDesc.refresh.denominator); std::printf(" scanline: %d\n", modeDesc.ScanlineOrdering); std::printf(" scaling: %d\n\n", modeDesc.scaling); if (pDscConfig && pDscConfig->DscVersion && pDscConfig->DscSliceCount && pDscConfig->DscOutputBppx16) { modeDesc.dscParams.dscMode = NV_DIRECT_MODE_DSC_MODE_FORCE_ENABLED; switch (pDscConfig->DscVersion) { case 0x11: modeDesc.dscParams.dscVersion = NV_DIRECT_MODE_DSC_VERSION_V11; break; case 0x12: modeDesc.dscParams.dscVersion = NV_DIRECT_MODE_DSC_VERSION_V12; break; default: modeDesc.dscParams.dscVersion = NV_DIRECT_MODE_DSC_VERSION_INVALID; delete[] modeDescs; return false; } switch (pDscConfig->DscSliceCount) { case 1: modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_1; break; case 2: modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_2; break; case 4: modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_4; break; case 8: modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_8; break; default: modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_INVALID; delete[] modeDescs; return false; } modeDesc.dscParams.outputBPPx16 = pDscConfig->DscOutputBppx16; std::printf("\n DSC: Enabled\n"); std::printf(" Version: %d\n", pDscConfig->DscVersion); std::printf(" Slice count: %d\n", pDscConfig->DscSliceCount); std::printf(" Output BPP: %d\n", pDscConfig->DscOutputBppx16); } else { std::printf("\n DSC: Disabled\n"); modeDesc.dscParams.dscMode = NV_DIRECT_MODE_DSC_MODE_DRIVER_CONTROLLED; modeDesc.dscParams.dscVersion = NV_DIRECT_MODE_DSC_VERSION_INVALID; modeDesc.dscParams.sliceCount = NV_DIRECT_MODE_DSC_SLICE_COUNT_INVALID; modeDesc.dscParams.outputBPPx16 = 0; } delete[] modeDescs; return true; } delete[] modeDescs; } std::fprintf(stderr, __FUNCTION__ ": No display and/or mode found for the specified selection(s).\n"); return false; } void hpdHandler(int displayId, int bIsHotplug, void* callbackParam) { auto targetDisplayId = int(size_t(callbackParam)); if (displayId == targetDisplayId) { std::printf("\n~~Detected %s on displayId=0x%x~~\n", bIsHotplug ? "connection" : "disconnection", displayId); if (bIsHotplug) { g_bPause = false; } else { g_bPause = true; } } } void hdcpErrorHandler(int displayId, int errorCode, void* callbackParam) { auto targetDisplayId = int(size_t(callbackParam)); if (displayId == targetDisplayId) { std::printf("\n~~HDCP Link Error %d on displayId=0x%x~~\n", errorCode, displayId); } } void hdcpStatusHandler(int displayId, int hdcpStatus, void* callbackParam) { auto targetDisplayId = int(size_t(callbackParam)); if (displayId == targetDisplayId) { std::printf("\n~~HDCP Status 0x%x on displayId=0x%x~~\n", hdcpStatus, displayId); } } bool directModePresentVsyncTest(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest) { std::printf(">>DirectMode Present Test<<\n\n"); directModeCheckVendorId(); // Get the display handle and mode description NV_DIRECT_MODE_DISPLAY_HANDLE display = { 0 }; NV_DIRECT_MODE_INFO modeDesc = { 0 }; DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; if (!translateParams(displaySelection, modeSelection, display, modeDesc, dxgiFormat, nullptr)) { return false; } // Get the adapter attached to the display, upon where we create the render device ComPtr adapter; NvPhysicalGpuHandle physicalGpu; auto status = NvAPI_SYS_GetPhysicalGpuFromDisplayId(display.displayId, &physicalGpu); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get GPU handle with error=0x%x.\n", status); return false; } status = NvAPI_D3D_GetIDXGIAdapter(physicalGpu, adapter.GetAddressOf()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get adapter with error=0x%x.\n", status); return false; } // Create renderer object that contains the D3D drawing logic D3D12Renderer renderer(adapter.Get()); // Acquire access status = NvAPI_D3D_AcquireDirectModeDisplay(VENDOR_ID, renderer.GetDevice().Get(), &display); if (status != NVAPI_OK) { if (status == NVAPI_HDCP_DISABLED) { std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to acquire display access with error=0x%x.\n", status); return false; } } // Register for event callback notifications NvEventHandle eventHandle = NULL; directModeRegisterEventCallbacks(display.displayId, &eventHandle, hpdHandler, hdcpErrorHandler, hdcpStatusHandler); // Create the present surfaces prior to modeset HRESULT hr = S_OK; static const auto NUM_BACKBUFFERS = 2; NV_DIRECT_MODE_SURFACE_HANDLE surfaces[NUM_BACKBUFFERS]; HANDLE sharedHandles[NUM_BACKBUFFERS]; for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeCreateSurface(&display, &modeDesc, &surfaces[i], &sharedHandles[i]); if (status != NVAPI_OK) { hr = E_OUTOFMEMORY; std::fprintf(stderr, __FUNCTION__ ": Failed to create private surface with hr=0x%x.\n", hr); break; } } if (SUCCEEDED(hr)) { // Perform a modeset to the desired presentation characteristics status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); // To support backward compatibility with older NVIDIA drivers, // change the version in the header of the structure before call NvAPI_D3D_DirectModeSetDisplayMode again. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); } // NVAPI_HDCP_DISBLAED means that it is OK to continue the DirectMode Run, // But Protected Content will be disabled for all the displays. if (status == NVAPI_HDCP_DISABLED) { status = NVAPI_OK; std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } if (status == NVAPI_OK) { // Print out the actual, current mode NvU32 modeCount = 1; memset(&modeDesc, 0, sizeof(modeDesc)); modeDesc.version = NV_DIRECT_MODE_INFO_VER; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, &modeDesc, NV_DIRECTMODE_GETMODES_FLAG_CURRENT); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { memset(&modeDesc, 0, sizeof(NV_DIRECT_MODE_INFO)); NV_DIRECT_MODE_INFO_V1 modeDesc_v1 = { 0 }; modeDesc_v1.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, (NV_DIRECT_MODE_INFO*)&modeDesc_v1, NV_DIRECTMODE_GETMODES_FLAG_CURRENT); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.width = modeDesc_v1.width; modeDesc.height = modeDesc_v1.height; modeDesc.format = modeDesc_v1.format; modeDesc.refresh.numerator = modeDesc_v1.refresh.numerator; modeDesc.refresh.denominator = modeDesc_v1.refresh.denominator; modeDesc.ScanlineOrdering = modeDesc_v1.ScanlineOrdering; modeDesc.scaling = modeDesc_v1.scaling; } if (status == NVAPI_OK) { std::printf(" Actual mode currently used:\n"); std::printf("\n mode: [%d]\n", modeSelection); std::printf(" width: %d\n", modeDesc.width); std::printf(" height: %d\n", modeDesc.height); std::printf(" format: %d\n", modeDesc.format); std::printf(" refreshrate: %d %d\n", modeDesc.refresh.numerator, modeDesc.refresh.denominator); std::printf(" scanline: %d\n", modeDesc.ScanlineOrdering); std::printf(" scaling: %d\n\n", modeDesc.scaling); } bool bQuit = false; auto fnProcessRender = [&]() { // Use the presentation event whenever possible as this ensures // that we only render to surfaces that are finished presenting HANDLE waitEvent = NULL; bool bUsingPresentWait = true; status = NvAPI_DISP_DirectModeGetPresentWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get present event. Attempting using vsync event...\n"); bUsingPresentWait = false; // Fallback to vsync event if this is an older driver without the present event // This may cause tearing/artifacts if rendering gets out-of-sync with display status = NvAPI_DISP_DirectModeGetVSyncWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get vsync event with status=0x%x.\n", status); bQuit = true; } } HANDLE hNotification = NULL; HANDLE hNotificationEvent = NULL; // Time values are in multiples of 100 nanoseconds i.e. 10000 = 1ms // Here we are using 3ms, 3.5ms, 4ms and 4.5ms unsigned int timeArr[4] = { 30000, 35000, 40000, 45000 }, t = 1; if (bNotifyTest) { NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS querySupportParams = { 0 }; querySupportParams.version = NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS_VER1; querySupportParams.displayId = display.displayId; status = NvAPI_D3D_QueryPeriodicFrameNotificationSupport(&querySupportParams); if (status != NVAPI_OK || querySupportParams.bSupported == false) { fprintf(stderr, __FUNCTION__ ": Periodic Frame Notification not supported. Status %d\n", status); bQuit = true; } NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS createNotifier = { 0 }; createNotifier.version = NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; createNotifier.displayId = display.displayId; createNotifier.time = timeArr[0]; createNotifier.notificationID = 0xabcd1234; // Non-zero, unique identifier for each notification created by the application createNotifier.waitType = PERIODIC_FRAME_NOTIFICATION_TYPE_EVENT_WAIT; status = NvAPI_D3D_CreatePeriodicFrameNotification(&createNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Create notification event failed.\n"); bQuit = true; } hNotification = createNotifier.hNotification; hNotificationEvent = createNotifier.hNotificationEvent; } // Initialize renderer with the display surfaces are RTs renderer.Init(_countof(sharedHandles), sharedHandles, dxgiFormat); while (!bQuit) { if (!g_bPause) { if (bNotifyTest && hNotificationEvent) { if (WaitForSingleObject(hNotificationEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for notification event.\n"); } } // Render to the display surface renderer.Render(); // And now, save to disk if frame capture is on if (g_FrameDumpIndex > 0) { ++g_FrameCounter; if (g_FrameCounter == g_FrameDumpIndex) { renderer.WriteRenderTargetToBMP(g_Filename.c_str()); g_FrameDumpIndex = 0; // Save Once } } if (bQueued) { // Reset command list to recording state renderer.ResetCommandList(); } // Present the surface status = NvAPI_D3D_DirectModePresent12( &display, surfaces[renderer.GetRenderedFrameIndex()], bQueued ? NV_DIRECTMODE_PRESENT_FLAG_QUEUED_VSYNC : NV_DIRECTMODE_PRESENT_FLAG_VSYNC, renderer.GetCommandQueue().Get(), renderer.GetCommandList().Get()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to present surface [%d], with error=0x%x.\n", renderer.GetRenderedFrameIndex(), status); } // If required, application can call NvAPI_D3D_UpdatePeriodicFrameNotification to update the notification time. // Otherwise, application will continue to receive notifications at the time value programmed via previous NvAPI_D3D_CreatePeriodicFrameNotification/NvAPI_D3D_UpdatePeriodicFrameNotification if (bNotifyTest && hNotification) { NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS updateNotifier = { 0 }; updateNotifier.version = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; updateNotifier.time = timeArr[t]; updateNotifier.flags = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_FLAGS_DEFAULT; t = (t + 1) % 4; updateNotifier.hNotification = hNotification; status = NvAPI_D3D_UpdatePeriodicFrameNotification(&updateNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Update notification event failed.\n"); } } // Wait for that present to finish // Include a timeout in case something goes wrong, so that we don't deadlock if (WaitForSingleObject(waitEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for Present event.\n"); } // Clear any Periodic Frame Notification event that occurred before the present event if (bNotifyTest && bUsingPresentWait && hNotificationEvent) { ResetEvent(hNotificationEvent); } } } if (bNotifyTest && hNotification) { NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS destroyNotifier = { 0 }; destroyNotifier.version = NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER1; destroyNotifier.hNotification = hNotification; status = NvAPI_D3D_DestroyPeriodicFrameNotification(&destroyNotifier); if (status != NVAPI_OK) { bQuit = true; std::fprintf(stderr, __FUNCTION__ ": Destroy notification event failed.\n"); } } }; // Rendering & presenting occurs on a worker thread std::thread renderWorker(fnProcessRender); // Menu prompt to quit, etc. while (!bQuit) { // Prompt to continue std::printf("Select an option.\n"); std::printf(" 1: turn on the display\n"); std::printf(" 2: turn off the display\n"); std::printf(" 3: blank the display\n"); std::printf(" 4: unblank the display\n"); std::printf(" q: quit\n\n"); auto cmd = getchar(); if (cmd == 'q') { bQuit = true; renderWorker.join(); } else { switch (cmd) { case '1': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_ON); g_bPause = false; break; case '2': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_OFF); break; case '3': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_OFF); break; case '4': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_ON); g_bPause = false; break; } } } } else { std::fprintf(stderr, __FUNCTION__ ": Failed to set mode with error=0x%x.\n", status); } } for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeDestroySurface(&display, surfaces[i]); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Destroy Surface. Error: 0x%x\n", status); return false; } surfaces[i] = NULL; } // Unregister for hotplug notifications NvAPI_Event_UnregisterCallback(eventHandle); // Release access and associated surfaces status = NvAPI_D3D_ReleaseDirectModeDisplay(VENDOR_ID, &display); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to release display access with status=0x%x.\n", status); return false; } std::printf("NVIDIA DirectMode VR - present test finished.\n"); return true; } bool directModeDscPresentVsyncTest(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig, unsigned int hexColor) { std::printf(">>DirectMode Present Test<<\n\n"); directModeCheckVendorId(); // Get the display handle and mode description NV_DIRECT_MODE_DISPLAY_HANDLE display = { 0 }; NV_DIRECT_MODE_INFO modeDesc = { 0 }; DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; if (!translateParams(displaySelection, modeSelection, display, modeDesc, dxgiFormat, &dscConfig)) { return false; } // Get the adapter attached to the display, upon where we create the render device ComPtr adapter; NvPhysicalGpuHandle physicalGpu; auto status = NvAPI_SYS_GetPhysicalGpuFromDisplayId(display.displayId, &physicalGpu); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get GPU handle with error=0x%x.\n", status); return false; } status = NvAPI_D3D_GetIDXGIAdapter(physicalGpu, adapter.GetAddressOf()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get adapter with error=0x%x.\n", status); return false; } // Create renderer object that contains the D3D drawing logic D3D12Renderer renderer(adapter.Get()); // Acquire access status = NvAPI_D3D_AcquireDirectModeDisplay(VENDOR_ID, renderer.GetDevice().Get(), &display); if (status != NVAPI_OK) { if (status == NVAPI_HDCP_DISABLED) { std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to acquire display access with error=0x%x.\n", status); return false; } } // Register for event callback notifications NvEventHandle eventHandle = NULL; directModeRegisterEventCallbacks(display.displayId, &eventHandle, hpdHandler, hdcpErrorHandler, hdcpStatusHandler); // Create the present surfaces prior to modeset HRESULT hr = S_OK; static const auto NUM_BACKBUFFERS = 2; NV_DIRECT_MODE_SURFACE_HANDLE surfaces[NUM_BACKBUFFERS]; HANDLE sharedHandles[NUM_BACKBUFFERS]; for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeCreateSurface(&display, &modeDesc, &surfaces[i], &sharedHandles[i]); if (status != NVAPI_OK) { hr = E_OUTOFMEMORY; std::fprintf(stderr, __FUNCTION__ ": Failed to create private surface with hr=0x%x.\n", hr); break; } } if (SUCCEEDED(hr)) { // Perform a modeset to the desired presentation characteristics status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); // To support backward compatibility with older NVIDIA drivers, // change the version in the header of the structure before call NvAPI_D3D_DirectModeSetDisplayMode again. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); } // NVAPI_HDCP_DISBLAED means that it is OK to continue the DirectMode Run, // But Protected Content will be disabled for all the displays. if (status == NVAPI_HDCP_DISABLED) { status = NVAPI_OK; std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } if (status == NVAPI_OK) { // Print out the actual, current mode NvU32 modeCount = 1; memset(&modeDesc, 0, sizeof(modeDesc)); modeDesc.version = NV_DIRECT_MODE_INFO_VER; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, &modeDesc, NV_DIRECTMODE_GETMODES_FLAG_CURRENT); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { memset(&modeDesc, 0, sizeof(NV_DIRECT_MODE_INFO)); NV_DIRECT_MODE_INFO_V1 modeDesc_v1 = { 0 }; modeDesc_v1.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, (NV_DIRECT_MODE_INFO*)&modeDesc_v1, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.width = modeDesc_v1.width; modeDesc.height = modeDesc_v1.height; modeDesc.format = modeDesc_v1.format; modeDesc.refresh.numerator = modeDesc_v1.refresh.numerator; modeDesc.refresh.denominator = modeDesc_v1.refresh.denominator; modeDesc.ScanlineOrdering = modeDesc_v1.ScanlineOrdering; modeDesc.scaling = modeDesc_v1.scaling; } if (status == NVAPI_OK) { std::printf(" Actual mode currently used:\n"); std::printf("\n mode: [%d]\n", modeSelection); std::printf(" width: %d\n", modeDesc.width); std::printf(" height: %d\n", modeDesc.height); std::printf(" format: %d\n", modeDesc.format); std::printf(" refreshrate: %d %d\n", modeDesc.refresh.numerator, modeDesc.refresh.denominator); std::printf(" scanline: %d\n", modeDesc.ScanlineOrdering); std::printf(" scaling: %d\n\n", modeDesc.scaling); } bool bQuit = false; auto fnProcessRender = [&]() { // Use the presentation event whenever possible as this ensures // that we only render to surfaces that are finished presenting HANDLE waitEvent = NULL; bool bUsingPresentWait = true; status = NvAPI_DISP_DirectModeGetPresentWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get present event. Attempting using vsync event...\n"); bUsingPresentWait = false; // Fallback to vsync event if this is an older driver without the present event // This may cause tearing/artifacts if rendering gets out-of-sync with display status = NvAPI_DISP_DirectModeGetVSyncWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get vsync event with status=0x%x.\n", status); bQuit = true; } } HANDLE hNotification = NULL; HANDLE hNotificationEvent = NULL; // Time values are in multiples of 100 nanoseconds i.e. 10000 = 1ms // Here we are using 3ms, 3.5ms, 4ms and 4.5ms unsigned int timeArr[4] = { 30000, 35000, 40000, 45000 }, t = 1; if (bNotifyTest) { NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS querySupportParams = { 0 }; querySupportParams.version = NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS_VER1; querySupportParams.displayId = display.displayId; status = NvAPI_D3D_QueryPeriodicFrameNotificationSupport(&querySupportParams); if (status != NVAPI_OK || querySupportParams.bSupported == false) { fprintf(stderr, __FUNCTION__ ": Periodic Frame Notification not supported. Status %d\n", status); bQuit = true; } NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS createNotifier = { 0 }; createNotifier.version = NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; createNotifier.displayId = display.displayId; createNotifier.time = timeArr[0]; createNotifier.notificationID = 0xabcd1234; // Non-zero, unique identifier for each notification created by the application createNotifier.waitType = PERIODIC_FRAME_NOTIFICATION_TYPE_EVENT_WAIT; status = NvAPI_D3D_CreatePeriodicFrameNotification(&createNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Create notification event failed.\n"); bQuit = true; } hNotification = createNotifier.hNotification; hNotificationEvent = createNotifier.hNotificationEvent; } // Initialize renderer with the display surfaces are RTs renderer.Init(_countof(sharedHandles), sharedHandles, dxgiFormat); // Set fixed color if requested if (hexColor != 0) { float red = (float)((hexColor & 0xFF0000) >> 16) / 255.0f; float green = (float)((hexColor & 0x00FF00) >> 8) / 255.0f; float blue = (float)((hexColor & 0x0000FF) >> 0) / 255.0f; renderer.SetClearColor(red, green, blue); } while (!bQuit) { if (!g_bPause) { if (bNotifyTest && hNotificationEvent) { if (WaitForSingleObject(hNotificationEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for notification event.\n"); } } // Render to the display surface renderer.Render(); // And now, save to disk if frame capture is on if (g_FrameDumpIndex > 0) { ++g_FrameCounter; if (g_FrameCounter == g_FrameDumpIndex) { renderer.WriteRenderTargetToBMP(g_Filename.c_str()); g_FrameDumpIndex = 0; // Save Once } } if (bQueued) { // Reset command list to recording state renderer.ResetCommandList(); } // Present the surface status = NvAPI_D3D_DirectModePresent12( &display, surfaces[renderer.GetRenderedFrameIndex()], bQueued ? NV_DIRECTMODE_PRESENT_FLAG_QUEUED_VSYNC : NV_DIRECTMODE_PRESENT_FLAG_VSYNC, renderer.GetCommandQueue().Get(), renderer.GetCommandList().Get()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to present surface [%d], with error=0x%x.\n", renderer.GetRenderedFrameIndex(), status); } // If required, application can call NvAPI_D3D_UpdatePeriodicFrameNotification to update the notification time. // Otherwise, application will continue to receive notifications at the time value programmed via previous NvAPI_D3D_CreatePeriodicFrameNotification/NvAPI_D3D_UpdatePeriodicFrameNotification if (bNotifyTest && hNotification) { NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS updateNotifier = { 0 }; updateNotifier.version = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; updateNotifier.time = timeArr[t]; updateNotifier.flags = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_FLAGS_DEFAULT; t = (t + 1) % 4; updateNotifier.hNotification = hNotification; status = NvAPI_D3D_UpdatePeriodicFrameNotification(&updateNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Update notification event failed.\n"); } } // Wait for that present to finish // Include a timeout in case something goes wrong, so that we don't deadlock if (WaitForSingleObject(waitEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for Present event.\n"); } // Clear any Periodic Frame Notification event that occurred before the present event if (bNotifyTest && bUsingPresentWait && hNotificationEvent) { ResetEvent(hNotificationEvent); } } } if (bNotifyTest && hNotification) { NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS destroyNotifier = { 0 }; destroyNotifier.version = NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER1; destroyNotifier.hNotification = hNotification; status = NvAPI_D3D_DestroyPeriodicFrameNotification(&destroyNotifier); if (status != NVAPI_OK) { bQuit = true; std::fprintf(stderr, __FUNCTION__ ": Destroy notification event failed.\n"); } } }; // Rendering & presenting occurs on a worker thread std::thread renderWorker(fnProcessRender); // Menu prompt to quit, etc. while (!bQuit) { // Prompt to continue std::printf("Select an option.\n"); std::printf(" 1: turn on the display\n"); std::printf(" 2: turn off the display\n"); std::printf(" 3: blank the display\n"); std::printf(" 4: unblank the display\n"); std::printf(" R: red background\n"); std::printf(" G: green background\n"); std::printf(" B: blue background\n"); std::printf(" q: quit\n\n"); auto cmd = getchar(); if (cmd == 'q') { bQuit = true; renderWorker.join(); } else { switch (cmd) { case '1': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_ON); g_bPause = false; break; case '2': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_OFF); break; case '3': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_OFF); break; case '4': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_ON); g_bPause = false; break; case 'R': renderer.SetClearColor(0.7f, 0.0f, 0.0f); break; case 'G': renderer.SetClearColor(0.0f, 0.7f, 0.0f); break; case 'B': renderer.SetClearColor(0.0f, 0.0f, 0.7f); break; } } } } else { std::fprintf(stderr, __FUNCTION__ ": Failed to set mode with error=0x%x.\n", status); } } for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeDestroySurface(&display, surfaces[i]); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Destroy Surface. Error: 0x%x\n", status); return false; } surfaces[i] = NULL; } // Unregister for hotplug notifications NvAPI_Event_UnregisterCallback(eventHandle); // Release access and associated surfaces status = NvAPI_D3D_ReleaseDirectModeDisplay(VENDOR_ID, &display); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to release display access with status=0x%x.\n", status); return false; } std::printf("NVIDIA DirectMode VR - present test finished.\n"); return true; } bool directModeVsPattern(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig) { std::printf(">>Vertical Stripe Pattern<<\n\n"); directModeCheckVendorId(); // Get the display handle and mode description NV_DIRECT_MODE_DISPLAY_HANDLE display = { 0 }; NV_DIRECT_MODE_INFO modeDesc = { 0 }; DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; if (!translateParams(displaySelection, modeSelection, display, modeDesc, dxgiFormat, &dscConfig)) { return false; } // Get the adapter attached to the display, upon where we create the render device ComPtr adapter; NvPhysicalGpuHandle physicalGpu; auto status = NvAPI_SYS_GetPhysicalGpuFromDisplayId(display.displayId, &physicalGpu); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get GPU handle with error=0x%x.\n", status); return false; } status = NvAPI_D3D_GetIDXGIAdapter(physicalGpu, adapter.GetAddressOf()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get adapter with error=0x%x.\n", status); return false; } // Create renderer object that contains the D3D drawing logic D3D12RenderVs renderer(adapter.Get()); // Acquire access status = NvAPI_D3D_AcquireDirectModeDisplay(VENDOR_ID, renderer.GetDevice().Get(), &display); if (status != NVAPI_OK) { if (status == NVAPI_HDCP_DISABLED) { std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to acquire display access with error=0x%x.\n", status); return false; } } // Register for event callback notifications NvEventHandle eventHandle = NULL; directModeRegisterEventCallbacks(display.displayId, &eventHandle, hpdHandler, hdcpErrorHandler, hdcpStatusHandler); // Create the present surfaces prior to modeset HRESULT hr = S_OK; static const auto NUM_BACKBUFFERS = 2; NV_DIRECT_MODE_SURFACE_HANDLE surfaces[NUM_BACKBUFFERS]; HANDLE sharedHandles[NUM_BACKBUFFERS]; for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeCreateSurface(&display, &modeDesc, &surfaces[i], &sharedHandles[i]); if (status != NVAPI_OK) { hr = E_OUTOFMEMORY; std::fprintf(stderr, __FUNCTION__ ": Failed to create private surface with hr=0x%x.\n", hr); break; } } if (SUCCEEDED(hr)) { // Perform a modeset to the desired presentation characteristics status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); // To support backward compatibility with older NVIDIA drivers, // change the version in the header of the structure before call NvAPI_D3D_DirectModeSetDisplayMode again. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); } // NVAPI_HDCP_DISBLAED means that it is OK to continue the DirectMode Run, // But Protected Content will be disabled for all the displays. if (status == NVAPI_HDCP_DISABLED) { status = NVAPI_OK; std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } if (status == NVAPI_OK) { // Print out the actual, current mode NvU32 modeCount = 1; memset(&modeDesc, 0, sizeof(modeDesc)); modeDesc.version = NV_DIRECT_MODE_INFO_VER; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, &modeDesc, NV_DIRECTMODE_GETMODES_FLAG_CURRENT); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { memset(&modeDesc, 0, sizeof(NV_DIRECT_MODE_INFO)); NV_DIRECT_MODE_INFO_V1 modeDesc_v1 = { 0 }; modeDesc_v1.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, (NV_DIRECT_MODE_INFO*)&modeDesc_v1, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.width = modeDesc_v1.width; modeDesc.height = modeDesc_v1.height; modeDesc.format = modeDesc_v1.format; modeDesc.refresh.numerator = modeDesc_v1.refresh.numerator; modeDesc.refresh.denominator = modeDesc_v1.refresh.denominator; modeDesc.ScanlineOrdering = modeDesc_v1.ScanlineOrdering; modeDesc.scaling = modeDesc_v1.scaling; } if (status == NVAPI_OK) { std::printf(" Actual mode currently used:\n"); std::printf("\n mode: [%d]\n", modeSelection); std::printf(" width: %d\n", modeDesc.width); std::printf(" height: %d\n", modeDesc.height); std::printf(" format: %d\n", modeDesc.format); std::printf(" refreshrate: %d %d\n", modeDesc.refresh.numerator, modeDesc.refresh.denominator); std::printf(" scanline: %d\n", modeDesc.ScanlineOrdering); std::printf(" scaling: %d\n\n", modeDesc.scaling); } bool bQuit = false; auto fnProcessRender = [&]() { // Use the presentation event whenever possible as this ensures // that we only render to surfaces that are finished presenting HANDLE waitEvent = NULL; bool bUsingPresentWait = true; status = NvAPI_DISP_DirectModeGetPresentWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get present event. Attempting using vsync event...\n"); bUsingPresentWait = false; // Fallback to vsync event if this is an older driver without the present event // This may cause tearing/artifacts if rendering gets out-of-sync with display status = NvAPI_DISP_DirectModeGetVSyncWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get vsync event with status=0x%x.\n", status); bQuit = true; } } HANDLE hNotification = NULL; HANDLE hNotificationEvent = NULL; // Time values are in multiples of 100 nanoseconds i.e. 10000 = 1ms // Here we are using 3ms, 3.5ms, 4ms and 4.5ms unsigned int timeArr[4] = { 30000, 35000, 40000, 45000 }, t = 1; if (bNotifyTest) { NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS querySupportParams = { 0 }; querySupportParams.version = NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS_VER1; querySupportParams.displayId = display.displayId; status = NvAPI_D3D_QueryPeriodicFrameNotificationSupport(&querySupportParams); if (status != NVAPI_OK || querySupportParams.bSupported == false) { fprintf(stderr, __FUNCTION__ ": Periodic Frame Notification not supported. Status %d\n", status); bQuit = true; } NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS createNotifier = { 0 }; createNotifier.version = NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; createNotifier.displayId = display.displayId; createNotifier.time = timeArr[0]; createNotifier.notificationID = 0xabcd1234; // Non-zero, unique identifier for each notification created by the application createNotifier.waitType = PERIODIC_FRAME_NOTIFICATION_TYPE_EVENT_WAIT; status = NvAPI_D3D_CreatePeriodicFrameNotification(&createNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Create notification event failed.\n"); bQuit = true; } hNotification = createNotifier.hNotification; hNotificationEvent = createNotifier.hNotificationEvent; } // Initialize renderer with the display surfaces are RTs renderer.Init(_countof(sharedHandles), sharedHandles, dxgiFormat); while (!bQuit) { if (!g_bPause) { if (bNotifyTest && hNotificationEvent) { if (WaitForSingleObject(hNotificationEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for notification event.\n"); } } // Render to the display surface renderer.Render(); // And now, save to disk if frame capture is on if (g_FrameDumpIndex > 0) { ++g_FrameCounter; if (g_FrameCounter == g_FrameDumpIndex) { renderer.WriteRenderTargetToBMP(g_Filename.c_str()); g_FrameDumpIndex = 0; // Save Once } } if (bQueued) { // Reset command list to recording state renderer.ResetCommandList(); } // Present the surface status = NvAPI_D3D_DirectModePresent12( &display, surfaces[renderer.GetRenderedFrameIndex()], bQueued ? NV_DIRECTMODE_PRESENT_FLAG_QUEUED_VSYNC : NV_DIRECTMODE_PRESENT_FLAG_VSYNC, renderer.GetCommandQueue().Get(), renderer.GetCommandList().Get()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to present surface [%d], with error=0x%x.\n", renderer.GetRenderedFrameIndex(), status); } // If required, application can call NvAPI_D3D_UpdatePeriodicFrameNotification to update the notification time. // Otherwise, application will continue to receive notifications at the time value programmed via previous NvAPI_D3D_CreatePeriodicFrameNotification/NvAPI_D3D_UpdatePeriodicFrameNotification if (bNotifyTest && hNotification) { NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS updateNotifier = { 0 }; updateNotifier.version = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; updateNotifier.time = timeArr[t]; updateNotifier.flags = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_FLAGS_DEFAULT; t = (t + 1) % 4; updateNotifier.hNotification = hNotification; status = NvAPI_D3D_UpdatePeriodicFrameNotification(&updateNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Update notification event failed.\n"); } } // Wait for that present to finish // Include a timeout in case something goes wrong, so that we don't deadlock if (WaitForSingleObject(waitEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for Present event.\n"); } // Clear any Periodic Frame Notification event that occurred before the present event if (bNotifyTest && bUsingPresentWait && hNotificationEvent) { ResetEvent(hNotificationEvent); } } } if (bNotifyTest && hNotification) { NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS destroyNotifier = { 0 }; destroyNotifier.version = NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER1; destroyNotifier.hNotification = hNotification; status = NvAPI_D3D_DestroyPeriodicFrameNotification(&destroyNotifier); if (status != NVAPI_OK) { bQuit = true; std::fprintf(stderr, __FUNCTION__ ": Destroy notification event failed.\n"); } } }; // Rendering & presenting occurs on a worker thread std::thread renderWorker(fnProcessRender); // Menu prompt to quit, etc. while (!bQuit) { // Prompt to continue std::printf("Select an option.\n"); std::printf(" 1: turn on the display\n"); std::printf(" 2: turn off the display\n"); std::printf(" 3: blank the display\n"); std::printf(" 4: unblank the display\n"); std::printf(" q: quit\n\n"); auto cmd = getchar(); if (cmd == 'q') { bQuit = true; renderWorker.join(); } else { switch (cmd) { case '1': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_ON); g_bPause = false; break; case '2': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_OFF); break; case '3': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_OFF); break; case '4': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_ON); g_bPause = false; break; } } } } else { std::fprintf(stderr, __FUNCTION__ ": Failed to set mode with error=0x%x.\n", status); } } for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeDestroySurface(&display, surfaces[i]); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Destroy Surface. Error: 0x%x\n", status); return false; } surfaces[i] = NULL; } // Unregister for hotplug notifications NvAPI_Event_UnregisterCallback(eventHandle); // Release access and associated surfaces status = NvAPI_D3D_ReleaseDirectModeDisplay(VENDOR_ID, &display); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to release display access with status=0x%x.\n", status); return false; } std::printf("NVIDIA DirectMode VR - present test finished.\n"); return true; } bool directModeImage(unsigned int displaySelection, unsigned int modeSelection, bool bQueued, bool bNotifyTest, DIRECT_MODE_DSC_CONFIG dscConfig, const wchar_t* filename) { std::printf(">>Image Display<<\n\n"); directModeCheckVendorId(); // Get the display handle and mode description NV_DIRECT_MODE_DISPLAY_HANDLE display = { 0 }; NV_DIRECT_MODE_INFO modeDesc = { 0 }; DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; if (!translateParams(displaySelection, modeSelection, display, modeDesc, dxgiFormat, &dscConfig)) { return false; } // Get the adapter attached to the display, upon where we create the render device ComPtr adapter; NvPhysicalGpuHandle physicalGpu; auto status = NvAPI_SYS_GetPhysicalGpuFromDisplayId(display.displayId, &physicalGpu); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get GPU handle with error=0x%x.\n", status); return false; } status = NvAPI_D3D_GetIDXGIAdapter(physicalGpu, adapter.GetAddressOf()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get adapter with error=0x%x.\n", status); return false; } // Create renderer object that contains the D3D drawing logic D3D12RenderImage renderer(adapter.Get(), filename); // Acquire access status = NvAPI_D3D_AcquireDirectModeDisplay(VENDOR_ID, renderer.GetDevice().Get(), &display); if (status != NVAPI_OK) { if (status == NVAPI_HDCP_DISABLED) { std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } else { std::fprintf(stderr, __FUNCTION__ ": Failed to acquire display access with error=0x%x.\n", status); return false; } } // Register for event callback notifications NvEventHandle eventHandle = NULL; directModeRegisterEventCallbacks(display.displayId, &eventHandle, hpdHandler, hdcpErrorHandler, hdcpStatusHandler); // Create the present surfaces prior to modeset HRESULT hr = S_OK; static const auto NUM_BACKBUFFERS = 2; NV_DIRECT_MODE_SURFACE_HANDLE surfaces[NUM_BACKBUFFERS]; HANDLE sharedHandles[NUM_BACKBUFFERS]; for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeCreateSurface(&display, &modeDesc, &surfaces[i], &sharedHandles[i]); if (status != NVAPI_OK) { hr = E_OUTOFMEMORY; std::fprintf(stderr, __FUNCTION__ ": Failed to create private surface with hr=0x%x.\n", hr); break; } } if (SUCCEEDED(hr)) { // Perform a modeset to the desired presentation characteristics status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); // To support backward compatibility with older NVIDIA drivers, // change the version in the header of the structure before call NvAPI_D3D_DirectModeSetDisplayMode again. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeSetDisplayMode(&display, &modeDesc); } // NVAPI_HDCP_DISBLAED means that it is OK to continue the DirectMode Run, // But Protected Content will be disabled for all the displays. if (status == NVAPI_HDCP_DISABLED) { status = NVAPI_OK; std::fprintf(stderr, __FUNCTION__": HDCP is disabled for the GPU\n"); } if (status == NVAPI_OK) { // Print out the actual, current mode NvU32 modeCount = 1; memset(&modeDesc, 0, sizeof(modeDesc)); modeDesc.version = NV_DIRECT_MODE_INFO_VER; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, &modeDesc, NV_DIRECTMODE_GETMODES_FLAG_CURRENT); // To support backward compatibility with older NVIDIA drivers, // call NvAPI_D3D_DirectModeGetDisplayModes with older version of the structure. if (status == NVAPI_INCOMPATIBLE_STRUCT_VERSION) { memset(&modeDesc, 0, sizeof(NV_DIRECT_MODE_INFO)); NV_DIRECT_MODE_INFO_V1 modeDesc_v1 = { 0 }; modeDesc_v1.version = NV_DIRECT_MODE_INFO_VER1; status = NvAPI_D3D_DirectModeGetDisplayModes(&display, &modeCount, (NV_DIRECT_MODE_INFO*)&modeDesc_v1, NV_DIRECTMODE_GETMODES_FLAG_SUPPORTED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for mode descriptions with error=0x%x\n", status); return false; } std::printf(__FUNCTION__": Using version 1 for NV_DIRECT_MODE_INFO\n"); modeDesc.width = modeDesc_v1.width; modeDesc.height = modeDesc_v1.height; modeDesc.format = modeDesc_v1.format; modeDesc.refresh.numerator = modeDesc_v1.refresh.numerator; modeDesc.refresh.denominator = modeDesc_v1.refresh.denominator; modeDesc.ScanlineOrdering = modeDesc_v1.ScanlineOrdering; modeDesc.scaling = modeDesc_v1.scaling; } if (status == NVAPI_OK) { std::printf(" Actual mode currently used:\n"); std::printf("\n mode: [%d]\n", modeSelection); std::printf(" width: %d\n", modeDesc.width); std::printf(" height: %d\n", modeDesc.height); std::printf(" format: %d\n", modeDesc.format); std::printf(" refreshrate: %d %d\n", modeDesc.refresh.numerator, modeDesc.refresh.denominator); std::printf(" scanline: %d\n", modeDesc.ScanlineOrdering); std::printf(" scaling: %d\n\n", modeDesc.scaling); } bool bQuit = false; auto fnProcessRender = [&]() { // Use the presentation event whenever possible as this ensures // that we only render to surfaces that are finished presenting HANDLE waitEvent = NULL; bool bUsingPresentWait = true; status = NvAPI_DISP_DirectModeGetPresentWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get present event. Attempting using vsync event...\n"); bUsingPresentWait = false; // Fallback to vsync event if this is an older driver without the present event // This may cause tearing/artifacts if rendering gets out-of-sync with display status = NvAPI_DISP_DirectModeGetVSyncWaitableObject(&display, reinterpret_cast(&waitEvent)); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to get vsync event with status=0x%x.\n", status); bQuit = true; } } HANDLE hNotification = NULL; HANDLE hNotificationEvent = NULL; // Time values are in multiples of 100 nanoseconds i.e. 10000 = 1ms // Here we are using 3ms, 3.5ms, 4ms and 4.5ms unsigned int timeArr[4] = { 30000, 35000, 40000, 45000 }, t = 1; if (bNotifyTest) { NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS querySupportParams = { 0 }; querySupportParams.version = NV_QUERY_PERIODIC_FRAME_NOTIFICATION_SUPPORT_PARAMS_VER1; querySupportParams.displayId = display.displayId; status = NvAPI_D3D_QueryPeriodicFrameNotificationSupport(&querySupportParams); if (status != NVAPI_OK || querySupportParams.bSupported == false) { fprintf(stderr, __FUNCTION__ ": Periodic Frame Notification not supported. Status %d\n", status); bQuit = true; } NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS createNotifier = { 0 }; createNotifier.version = NV_CREATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; createNotifier.displayId = display.displayId; createNotifier.time = timeArr[0]; createNotifier.notificationID = 0xabcd1234; // Non-zero, unique identifier for each notification created by the application createNotifier.waitType = PERIODIC_FRAME_NOTIFICATION_TYPE_EVENT_WAIT; status = NvAPI_D3D_CreatePeriodicFrameNotification(&createNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Create notification event failed.\n"); bQuit = true; } hNotification = createNotifier.hNotification; hNotificationEvent = createNotifier.hNotificationEvent; } // Initialize renderer with the display surfaces are RTs renderer.Init(_countof(sharedHandles), sharedHandles, dxgiFormat); while (!bQuit) { if (!g_bPause) { if (bNotifyTest && hNotificationEvent) { if (WaitForSingleObject(hNotificationEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for notification event.\n"); } } // Render to the display surface renderer.Render(); // And now, save to disk if frame capture is on if (g_FrameDumpIndex > 0) { ++g_FrameCounter; if (g_FrameCounter == g_FrameDumpIndex) { renderer.WriteRenderTargetToBMP(g_Filename.c_str()); g_FrameDumpIndex = 0; // Save Once } } if (bQueued) { // Reset command list to recording state renderer.ResetCommandList(); } // Present the surface status = NvAPI_D3D_DirectModePresent12( &display, surfaces[renderer.GetRenderedFrameIndex()], bQueued ? NV_DIRECTMODE_PRESENT_FLAG_QUEUED_VSYNC : NV_DIRECTMODE_PRESENT_FLAG_VSYNC, renderer.GetCommandQueue().Get(), renderer.GetCommandList().Get()); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to present surface [%d], with error=0x%x.\n", renderer.GetRenderedFrameIndex(), status); } // If required, application can call NvAPI_D3D_UpdatePeriodicFrameNotification to update the notification time. // Otherwise, application will continue to receive notifications at the time value programmed via previous NvAPI_D3D_CreatePeriodicFrameNotification/NvAPI_D3D_UpdatePeriodicFrameNotification if (bNotifyTest && hNotification) { NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS updateNotifier = { 0 }; updateNotifier.version = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER2; updateNotifier.time = timeArr[t]; updateNotifier.flags = NV_UPDATE_PERIODIC_FRAME_NOTIFICATION_FLAGS_DEFAULT; t = (t + 1) % 4; updateNotifier.hNotification = hNotification; status = NvAPI_D3D_UpdatePeriodicFrameNotification(&updateNotifier); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Update notification event failed.\n"); } } // Wait for that present to finish // Include a timeout in case something goes wrong, so that we don't deadlock if (WaitForSingleObject(waitEvent, 1000) == WAIT_TIMEOUT) { std::fprintf(stderr, __FUNCTION__ ": Timeout waiting for Present event.\n"); } // Clear any Periodic Frame Notification event that occurred before the present event if (bNotifyTest && bUsingPresentWait && hNotificationEvent) { ResetEvent(hNotificationEvent); } } } if (bNotifyTest && hNotification) { NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS destroyNotifier = { 0 }; destroyNotifier.version = NV_DESTROY_PERIODIC_FRAME_NOTIFICATION_PARAMS_VER1; destroyNotifier.hNotification = hNotification; status = NvAPI_D3D_DestroyPeriodicFrameNotification(&destroyNotifier); if (status != NVAPI_OK) { bQuit = true; std::fprintf(stderr, __FUNCTION__ ": Destroy notification event failed.\n"); } } }; // Rendering & presenting occurs on a worker thread std::thread renderWorker(fnProcessRender); // Menu prompt to quit, etc. while (!bQuit) { // Prompt to continue std::printf("Select an option.\n"); std::printf(" 1: turn on the display\n"); std::printf(" 2: turn off the display\n"); std::printf(" 3: blank the display\n"); std::printf(" 4: unblank the display\n"); std::printf(" F: show any file\n"); std::printf(" q: quit\n\n"); auto cmd = getchar(); if (cmd == 'q') { bQuit = true; renderWorker.join(); } else { switch (cmd) { case '1': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_ON); g_bPause = false; break; case '2': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_POWER_OFF); break; case '3': g_bPause = true; NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_OFF); break; case '4': NvAPI_DISP_DirectModeDisplayControl(&display, NV_DM_DISPLAY_CONTROL_VISIBILITY_ON); g_bPause = false; break; //case '5': // renderer.ShowNewTexture(L"sunset.jpg"); // break; case 'F': char rest_of_stdin[128]; gets_s(rest_of_stdin, 128); std::printf("you requested file: %s\n", rest_of_stdin); std::string image_path(rest_of_stdin); //std::string image_path("sunset.jpg"); std::wstring image_pathw(image_path.begin(), image_path.end()); //std::wstring image_pathw(L"C:/temp/core_jpeg_encoder/scripts/DOORS.JPG"); renderer.ShowNewTexture(image_pathw.c_str()); break; } } } } else { std::fprintf(stderr, __FUNCTION__ ": Failed to set mode with error=0x%x.\n", status); } } for (auto i = 0u; i < _countof(surfaces); i++) { status = NvAPI_D3D_DirectModeDestroySurface(&display, surfaces[i]); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Destroy Surface. Error: 0x%x\n", status); return false; } surfaces[i] = NULL; } // Unregister for hotplug notifications NvAPI_Event_UnregisterCallback(eventHandle); // Release access and associated surfaces status = NvAPI_D3D_ReleaseDirectModeDisplay(VENDOR_ID, &display); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to release display access with status=0x%x.\n", status); return false; } std::printf("NVIDIA DirectMode VR - present test finished.\n"); return true; } HRESULT directModeRegisterEventCallbacks( NvU32 displayId, // display Id NvEventHandle *pEventHandle, // evnet handle void* pHpdCallback, // Function for handling hotplug events void* pHdcpCallback, // Function for handling HDCP events void* pHdcpStatusCallback // Function for handling HDCP Status event ) { *pEventHandle = NULL; if (pHpdCallback) { NV_EVENT_REGISTER_CALLBACK eventRegister = { 0 }; eventRegister.eventId = NV_EVENT_TYPE_HOTPLUG_UNPLUG; eventRegister.nvCallBackFunc.nvHPDCallback = reinterpret_cast(pHpdCallback); eventRegister.callbackParam = reinterpret_cast(size_t(displayId)); NvAPI_Status status; status = NvAPI_Event_RegisterCallback(&eventRegister, pEventHandle); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Register Callback for NV_EVENT_TYPE_HOTPLUG_UNPLUG. Error: 0x%x\n", status); return E_FAIL; } } if (pHdcpCallback) { NV_EVENT_REGISTER_CALLBACK eventRegister = { 0 }; eventRegister.eventId = NV_EVENT_TYPE_HDCP_LINK_FAILED; eventRegister.nvCallBackFunc.nvHDCPCallback = static_cast(pHdcpCallback); eventRegister.callbackParam = reinterpret_cast(size_t(displayId)); NvAPI_Status status; status = NvAPI_Event_RegisterCallback(&eventRegister, pEventHandle); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Register Callback for NV_EVENT_TYPE_HDCP_LINK_FAILED. Error: 0x%x\n", status); return E_FAIL; } } if (pHdcpStatusCallback) { NV_EVENT_REGISTER_CALLBACK eventRegister = { 0 }; eventRegister.eventId = NV_EVENT_TYPE_HDCP_STATUS; eventRegister.nvCallBackFunc.nvHDCPStatusCallback = static_cast(pHdcpStatusCallback); eventRegister.callbackParam = reinterpret_cast(size_t(displayId)); NvAPI_Status status; status = NvAPI_Event_RegisterCallback(&eventRegister, pEventHandle); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__": Failed to Register Callback for NV_EVENT_TYPE_HDCP_STATUS. Error: 0x%x\n", status); return E_FAIL; } } return S_OK; } bool runDirectModeVsPattern() { bool dm_was_disabled = false; // Check if any direct mode display is present directModeCheckVendorId(); NvU32 numDisplays = 4; NV_DIRECT_MODE_DISPLAY_HANDLE displays[4] = { 0 }; auto status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for displayIds with error=0x%x\n", status); return false; } if (numDisplays == 0) { // This means either: // - no beyond plugged in // - or, direct mode isn't enabled. // // So try to enable direct mode and check displays again if (!directModeEnableDisable(true)) { return false; } numDisplays = 4; status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for displayIds with error=0x%x\n", status); return false; } if (numDisplays == 0) { // Okay, now for real there is no Beyond plugged in std::fprintf(stderr, __FUNCTION__ ": No DM-enabled displays found. Ensure display is plugged in and functional.\n"); return false; } else { dm_was_disabled = true; // we have a display only after enabling direct mode // so we know that direct mode was previously disabled // disable it again after we're all done. } } DIRECT_MODE_DSC_CONFIG dscConfig; dscConfig.DscVersion = 0x11; dscConfig.DscSliceCount = 4; dscConfig.DscOutputBppx16 = 128; directModeVsPattern(0, 0, false, false, dscConfig); if (dm_was_disabled) { directModeEnableDisable(false); } return true; } bool runDirectModeImage(const wchar_t* image_filename) { bool dm_was_disabled = false; // Check if any direct mode display is present directModeCheckVendorId(); NvU32 numDisplays = 4; NV_DIRECT_MODE_DISPLAY_HANDLE displays[4] = { 0 }; auto status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for displayIds with error=0x%x\n", status); return false; } if (numDisplays == 0) { // This means either: // - no beyond plugged in // - or, direct mode isn't enabled. // // So try to enable direct mode and check displays again if (!directModeEnableDisable(true)) { return false; } numDisplays = 4; status = NvAPI_DISP_EnumerateDirectModeDisplays(VENDOR_ID, &numDisplays, displays, NV_ENUM_DIRECTMODE_DISPLAY_ENABLED); if (status != NVAPI_OK) { std::fprintf(stderr, __FUNCTION__ ": Failed to query for displayIds with error=0x%x\n", status); return false; } if (numDisplays == 0) { // Okay, now for real there is no Beyond plugged in std::fprintf(stderr, __FUNCTION__ ": No DM-enabled displays found. Ensure display is plugged in and functional.\n"); return false; } else { dm_was_disabled = true; // we have a display only after enabling direct mode // so we know that direct mode was previously disabled // disable it again after we're all done. } } DIRECT_MODE_DSC_CONFIG dscConfig; dscConfig.DscVersion = 0x11; dscConfig.DscSliceCount = 4; dscConfig.DscOutputBppx16 = 128; directModeImage(0, 0, false, false, dscConfig, image_filename); if (dm_was_disabled) { directModeEnableDisable(false); } return true; }