diff --git a/qxldod/QxlDod.cpp b/qxldod/QxlDod.cpp index cd22446..9175300 100755 --- a/qxldod/QxlDod.cpp +++ b/qxldod/QxlDod.cpp @@ -82,6 +82,12 @@ QxlDod::QxlDod(_In_ DEVICE_OBJECT* pPhysicalDeviceObject) : m_pPhysicalDevice(pP RtlZeroMemory(m_CurrentModes, sizeof(m_CurrentModes)); RtlZeroMemory(&m_PointerShape, sizeof(m_PointerShape)); m_pHWDevice = NULL; + + KeInitializeDpc(&m_VsyncTimerDpc, VsyncTimerProcGate, this); + KeInitializeTimer(&m_VsyncTimer); + m_VsyncFiredCounter = 0; + m_bVsyncEnabled = FALSE; + DbgPrint(TRACE_LEVEL_INFORMATION, ("<--- %s\n", __FUNCTION__)); } @@ -198,6 +204,7 @@ NTSTATUS QxlDod::StopDevice(VOID) { PAGED_CODE(); m_Flags.DriverStarted = FALSE; + EnableVsync(FALSE); return STATUS_SUCCESS; } @@ -518,6 +525,7 @@ NTSTATUS QxlDod::QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAda pDriverCaps->PointerCaps.Color = 1; pDriverCaps->SupportNonVGA = m_pHWDevice->IsBIOSCompatible(); + pDriverCaps->SchedulingCaps.VSyncPowerSaveAware = g_bSupportVSync; DbgPrint(TRACE_LEVEL_VERBOSE, ("<--- %s 1\n", __FUNCTION__)); return STATUS_SUCCESS; @@ -4812,6 +4820,14 @@ BOOLEAN QxlDevice::InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ return TRUE; } +QXL_NON_PAGED +VOID QxlDevice::VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE pDxgkInterface) +{ + if (!pDxgkInterface->DxgkCbQueueDpc(pDxgkInterface->DeviceHandle)) { + DbgPrint(TRACE_LEVEL_WARNING, ("---> %s can't enqueue DPC, pending interrupts %X\n", __FUNCTION__, m_Pending)); + } +} + QXL_NON_PAGED VOID QxlDevice::DpcRoutine(PVOID) { @@ -4914,3 +4930,69 @@ NTSTATUS HwDeviceInterface::AcquireDisplayInfo(DXGK_DISPLAY_INFORMATION& DispInf } return Status; } + +// Vga device does not generate interrupts +QXL_NON_PAGED VOID VgaDevice::VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE pxface) +{ + pxface->DxgkCbQueueDpc(pxface->DeviceHandle); +} + +QXL_NON_PAGED VOID QxlDod::IndicateVSyncInterrupt() +{ + DXGKARGCB_NOTIFY_INTERRUPT_DATA data = {}; + data.InterruptType = DXGK_INTERRUPT_DISPLAYONLY_VSYNC; + m_DxgkInterface.DxgkCbNotifyInterrupt(m_DxgkInterface.DeviceHandle, &data); + m_pHWDevice->VSyncInterruptPostProcess(&m_DxgkInterface); +} + +QXL_NON_PAGED BOOLEAN QxlDod::VsyncTimerSynchRoutine(PVOID context) +{ + QxlDod* pQxl = reinterpret_cast(context); + pQxl->IndicateVSyncInterrupt(); + return FALSE; +} + +QXL_NON_PAGED VOID QxlDod::VsyncTimerProc() +{ + BOOLEAN bDummy; + DbgPrint(TRACE_LEVEL_VERBOSE, ("<--- %s\n", __FUNCTION__)); + if (m_bVsyncEnabled && m_AdapterPowerState == PowerDeviceD0) + { + m_DxgkInterface.DxgkCbSynchronizeExecution( + m_DxgkInterface.DeviceHandle, + VsyncTimerSynchRoutine, + this, + 0, + &bDummy + ); + INCREMENT_VSYNC_COUNTER(&m_VsyncFiredCounter); + } +} + +VOID QxlDod::EnableVsync(BOOLEAN bEnable) +{ + PAGED_CODE(); + if (g_bSupportVSync) + { + m_bVsyncEnabled = bEnable; + if (!m_bVsyncEnabled) + { + DbgPrint(TRACE_LEVEL_WARNING, ("Disabled VSync(fired %d)\n", InterlockedExchange(&m_VsyncFiredCounter, 0))); + KeCancelTimer(&m_VsyncTimer); + } + else + { + LARGE_INTEGER li; + LONG period = 1000 / VSYNC_RATE; + DbgPrint(TRACE_LEVEL_WARNING, ("Enabled VSync(fired %d)\n", m_VsyncFiredCounter)); + li.QuadPart = -10000000 / VSYNC_RATE; + KeSetTimerEx(&m_VsyncTimer, li, period, &m_VsyncTimerDpc); + } + } +} + +QXL_NON_PAGED VOID QxlDod::VsyncTimerProcGate(_In_ _KDPC *dpc, _In_ PVOID context, _In_ PVOID arg1, _In_ PVOID arg2) +{ + QxlDod* pQxl = reinterpret_cast(context); + pQxl->VsyncTimerProc(); +} diff --git a/qxldod/QxlDod.h b/qxldod/QxlDod.h index 53724e8..9cfb6de 100755 --- a/qxldod/QxlDod.h +++ b/qxldod/QxlDod.h @@ -239,6 +239,7 @@ public: QXL_NON_PAGED virtual BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber) = 0; QXL_NON_PAGED virtual VOID DpcRoutine(PVOID) = 0; QXL_NON_PAGED virtual VOID ResetDevice(void) = 0; + QXL_NON_PAGED virtual VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE) = 0; virtual NTSTATUS AcquireFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode) { return STATUS_SUCCESS; } virtual NTSTATUS ReleaseFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode) { return STATUS_SUCCESS; } @@ -306,6 +307,7 @@ public: QXL_NON_PAGED BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber); QXL_NON_PAGED VOID DpcRoutine(PVOID); QXL_NON_PAGED VOID ResetDevice(VOID); + QXL_NON_PAGED VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE); NTSTATUS AcquireFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode); NTSTATUS ReleaseFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode); NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape); @@ -358,8 +360,10 @@ enum { #ifdef DBG #define RESOURCE_TYPE(res, val) do { res->type = val; } while (0) +#define INCREMENT_VSYNC_COUNTER(counter) InterlockedIncrement(counter) #else #define RESOURCE_TYPE(res, val) +#define INCREMENT_VSYNC_COUNTER(counter) #endif typedef struct Resource Resource; @@ -480,6 +484,7 @@ public: QXL_NON_PAGED BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber); QXL_NON_PAGED VOID DpcRoutine(PVOID); QXL_NON_PAGED VOID ResetDevice(VOID); + QXL_NON_PAGED VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE); NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape); NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition); NTSTATUS Escape(_In_ CONST DXGKARG_ESCAPE* pEscap); @@ -622,6 +627,10 @@ private: DXGKARG_SETPOINTERSHAPE m_PointerShape; HwDeviceInterface* m_pHWDevice; + KTIMER m_VsyncTimer; + KDPC m_VsyncTimerDpc; + BOOLEAN m_bVsyncEnabled; + LONG m_VsyncFiredCounter; public: QxlDod(_In_ DEVICE_OBJECT* pPhysicalDeviceObject); ~QxlDod(void); @@ -719,6 +728,7 @@ public: { return m_DxgkInterface.DxgkCbAcquirePostDisplayOwnership(m_DxgkInterface.DeviceHandle, &DispInfo); } + VOID EnableVsync(BOOLEAN bEnable); private: VOID CleanUp(VOID); NTSTATUS CheckHardware(); @@ -744,6 +754,10 @@ private: NTSTATUS IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const; NTSTATUS IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const; NTSTATUS RegisterHWInfo(_In_ ULONG Id); + QXL_NON_PAGED VOID VsyncTimerProc(); + static QXL_NON_PAGED VOID VsyncTimerProcGate(_In_ _KDPC *dpc, _In_ PVOID context, _In_ PVOID arg1, _In_ PVOID arg2); + QXL_NON_PAGED VOID IndicateVSyncInterrupt(); + static QXL_NON_PAGED BOOLEAN VsyncTimerSynchRoutine(PVOID context); }; NTSTATUS