From e034377b51a11e6894ba3b558b1990342cac681c Mon Sep 17 00:00:00 2001 From: Oleg Dubinskiy Date: Wed, 21 Jun 2023 18:00:24 +0200 Subject: [PATCH] [WIN32SS][ENG][NTDDRAW] Manage DirectDraw instances when switching display mode (#4519) CORE-17932 [ENG] Implement DirectDraw management in switch display mode functions (e.g. resolution change, color depth, display frequency etc.): - Switch DirectDraw instances between the two PDEVs (the current one and the new one allocated by ourselves) by calling dxg!DxDdDynamicModeChange function. - Suspend them before and resume after the display mode switch, by calling dxg!DxDdsuspendDirectDraw and dxg!DxDdResumeDirectDraw appropriately. We currently don't have these functions implemented, but MS DXG has, so it allows to properly manage DirectDraw PDEVs using this driver, similarly to Windows. My analysis confirms that these functions are always called in XP/2k3 on display mode switch, even when there is no any DirectX app running at the moment. Analyzing their prototypes show that my guesses are correct. - Initialize hDev and dhpdev members for EDD_DIRECTDRAW_GLOBAL for newly created surfaces, switch them during mode change and re-initialize after it also. They are commonly used by DirectDraw stack. In addition, enable DirectDraw for old and new PDEVs, by calling dxg!DxDdEnableDirectDraw function. [NTDDRAW] Additionally, fix usage of DirectDraw lock count in the PDEVOBJ structure. - Enable cDirectDrawDisableLocks member for storing its value, instead of DxDd_nCount, which is marked as ROS-specific. - Use it in win32k!DxEngGet/SetHdevData for getting/setting DirectDraw count appropriately. My analysis also shows that in Windows, the PDEVOBJ::cDirectDrawDisableLocks method calls DxEngGetHdevData with type 8, which corresponds to our DxDd_nCount. So there are no doubts that this member is used there. - Rename DxEngGetHdevData_dd_count alias of type 8 to DxEngGetHdevData_dd_locks, to match more accurately an actual member name. Update the enumeration and fix all code parts appropriately. All these changes allow to properly change display mode during executing DirectDraw applications, when they try to switch in full-screen mode. At least a bugcheck that happened before my changes, does no longer appear. There are still some games that don't run correctly, as if there is no 3D acceleration (which actually exists). This requires further investigations. --- sdk/include/reactos/drivers/directx/dxeng.h | 2 +- sdk/include/reactos/drivers/directx/dxg.h | 2 +- win32ss/gdi/eng/pdevobj.c | 85 +++++++++++++++++++-- win32ss/gdi/eng/pdevobj.h | 3 +- win32ss/reactx/dxg/ddraw.c | 2 +- win32ss/reactx/ntddraw/dxeng.c | 11 +-- win32ss/reactx/ntddraw/intddraw.h | 5 ++ 7 files changed, 94 insertions(+), 16 deletions(-) diff --git a/sdk/include/reactos/drivers/directx/dxeng.h b/sdk/include/reactos/drivers/directx/dxeng.h index 8a349ce6507..fa2f248e7cf 100644 --- a/sdk/include/reactos/drivers/directx/dxeng.h +++ b/sdk/include/reactos/drivers/directx/dxeng.h @@ -57,7 +57,7 @@ typedef enum _DXEGSHDEVDATA DxEGShDevData_DrvFuncs, DxEGShDevData_dhpdev, DxEGShDevData_eddg, - DxEGShDevData_dd_nCount, + DxEGShDevData_dd_locks, DxEGShDevData_dd_flags, DxEGShDevData_disable, DxEGShDevData_metadev, diff --git a/sdk/include/reactos/drivers/directx/dxg.h b/sdk/include/reactos/drivers/directx/dxg.h index 33a815a2a61..470597ad1de 100644 --- a/sdk/include/reactos/drivers/directx/dxg.h +++ b/sdk/include/reactos/drivers/directx/dxg.h @@ -197,7 +197,7 @@ typedef DWORD (NTAPI *PGD_DXGENERICTRUNK)(ULONG_PTR, ULONG_PTR, SIZE_T*, PVOID, //typedef x (NTAPI *PGD_DxDdHeapVidMemAllocAligned)( //typedef x (NTAPI *PGD_DxDdHeapVidMemFree)( typedef BOOLEAN (NTAPI *PGD_DXDDENABLEDIRECTDRAW)(PVOID, BOOLEAN); -//typedef x (NTAPI *PGD_DxDdDisableDirectDraw)( +typedef VOID (NTAPI *PGD_DXDDDISABLEDIRECTDRAW)(PVOID, BOOLEAN); //typedef x (NTAPI *PGD_DxDdSuspendDirectDraw)( //typedef x (NTAPI *PGD_DxDdResumeDirectDraw)( //typedef x (NTAPI *PGD_DxDdDynamicModeChange)( diff --git a/win32ss/gdi/eng/pdevobj.c b/win32ss/gdi/eng/pdevobj.c index c8af5c7e9c3..ed6fc630e76 100644 --- a/win32ss/gdi/eng/pdevobj.c +++ b/win32ss/gdi/eng/pdevobj.c @@ -3,7 +3,8 @@ * PROJECT: ReactOS kernel * PURPOSE: Support for physical devices * FILE: win32ss/gdi/eng/pdevobj.c - * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org) + * PROGRAMERS: Timo Kreuzer (timo.kreuzer@reactos.org) + * Oleg Dubinskiy (oleg.dubinskij30@gmail.com) */ #include @@ -336,6 +337,55 @@ PDEVOBJ_pSurface( return ppdev->pSurface; } +BOOL +PDEVOBJ_bEnableDirectDraw( + _Inout_ PPDEVOBJ ppdev) +{ + PGD_DXDDENABLEDIRECTDRAW pfnDdEnableDirectDraw = (PGD_DXDDENABLEDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdEnableDirectDraw].pfn; + BOOL Success; + + /* Enable DirectDraw */ + TRACE("DxDdEnableDirectDraw(ppdev %p)\n", ppdev); + Success = pfnDdEnableDirectDraw((HDEV)ppdev, TRUE); + TRACE("DxDdEnableDirectDraw(ppdev %p) => %d\n", ppdev, Success); + + return Success; +} + +VOID +PDEVOBJ_vResumeDirectDraw( + _Inout_ PPDEVOBJ ppdev) +{ + PGD_DXDDRESUMEDIRECTDRAW pfnDdResumeDirectDraw = (PGD_DXDDRESUMEDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdResumeDirectDraw].pfn; + + /* Resume DirectDraw after mode change */ + TRACE("DxDdResumeDirectDraw(ppdev %p)\n", ppdev); + pfnDdResumeDirectDraw((HDEV)ppdev, 0); +} + +VOID +PDEVOBJ_vSuspendDirectDraw( + _Inout_ PPDEVOBJ ppdev) +{ + PGD_DXDDSUSPENDDIRECTDRAW pfnDdSuspendDirectDraw = (PGD_DXDDSUSPENDDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdSuspendDirectDraw].pfn; + + /* Suspend DirectDraw for mode change */ + TRACE("DxDdSuspendDirectDraw(ppdev %p)\n", ppdev); + pfnDdSuspendDirectDraw((HDEV)ppdev, 0); +} + +VOID +PDEVOBJ_vSwitchDirectDraw( + _Inout_ PPDEVOBJ ppdev, + _Inout_ PPDEVOBJ ppdev2) +{ + PGD_DXDDDYNAMICMODECHANGE pfnDdDynamicModeChange = (PGD_DXDDDYNAMICMODECHANGE)gpDxFuncs[DXG_INDEX_DxDdDynamicModeChange].pfn; + + /* Switch DirectDraw instances between the PDEVs */ + TRACE("DxDdDynamicModeChange(ppdev %p, ppdev2 %p)\n", ppdev, ppdev2); + pfnDdDynamicModeChange((HDEV)ppdev, (HDEV)ppdev2, 0); +} + VOID PDEVOBJ_vEnableDisplay( _Inout_ PPDEVOBJ ppdev) @@ -365,6 +415,8 @@ PDEVOBJ_bDisableDisplay( if (ppdev->flFlags & PDEV_DISABLED) return TRUE; + PDEVOBJ_vSuspendDirectDraw(ppdev); + TRACE("DrvAssertMode(dhpdev %p, FALSE)\n", ppdev->dhpdev); assertVal = ppdev->pfn.AssertMode(ppdev->dhpdev, FALSE); TRACE("DrvAssertMode(dhpdev %p, FALSE) => %d\n", ppdev->dhpdev, assertVal); @@ -548,6 +600,15 @@ PDEVOBJ_Create( return NULL; } + /* Enable DirectDraw */ + if (!PDEVOBJ_bEnableDirectDraw(ppdev)) + { + ERR("Failed to enable DirectDraw\n"); + PDEVOBJ_vRelease(ppdev); + EngUnloadImage(pldev); + return NULL; + } + /* Remove some acceleration capabilities from driver */ PDEVOBJ_vFilterDriverHooks(ppdev); @@ -631,6 +692,9 @@ PDEVOBJ_bDynamicModeChange( ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev); ppdev2->pfn.CompletePDEV(ppdev2->dhpdev, (HDEV)ppdev2); + /* Switch DirectDraw mode */ + PDEVOBJ_vSwitchDirectDraw(ppdev, ppdev2); + return TRUE; } @@ -660,6 +724,8 @@ PDEVOBJ_bSwitchMode( if (!PDEVOBJ_bDisableDisplay(ppdev)) { DPRINT1("PDEVOBJ_bDisableDisplay() failed\n"); + /* Resume DirectDraw in case of failure */ + PDEVOBJ_vResumeDirectDraw(ppdev); goto leave; } @@ -680,11 +746,11 @@ PDEVOBJ_bSwitchMode( goto leave2; } - /* 4. Get DirectDraw information */ - /* 5. Enable DirectDraw Not traced */ - /* 6. Copy old PDEV state to new PDEV instance */ + /* 4. Temporarily suspend DirectDraw for mode change */ + PDEVOBJ_vSuspendDirectDraw(ppdev); + PDEVOBJ_vSuspendDirectDraw(ppdevTmp); - /* 7. Switch the PDEVs */ + /* 5. Switch the PDEVs */ if (!PDEVOBJ_bDynamicModeChange(ppdev, ppdevTmp)) { DPRINT1("PDEVOBJ_bDynamicModeChange() failed\n"); @@ -692,10 +758,17 @@ PDEVOBJ_bSwitchMode( goto leave2; } - /* 8. Disable DirectDraw */ + /* 6. Resume DirectDraw */ + PDEVOBJ_vResumeDirectDraw(ppdev); + PDEVOBJ_vResumeDirectDraw(ppdevTmp); + /* Release temp PDEV */ PDEVOBJ_vRelease(ppdevTmp); + /* Re-initialize DirectDraw data */ + ppdev->pEDDgpl->hDev = (HDEV)ppdev; + ppdev->pEDDgpl->dhpdev = ppdev->dhpdev; + /* Update primary display capabilities */ if (ppdev == gpmdev->ppdevGlobal) { diff --git a/win32ss/gdi/eng/pdevobj.h b/win32ss/gdi/eng/pdevobj.h index b9d439d3dda..a58eb1aa2ab 100644 --- a/win32ss/gdi/eng/pdevobj.h +++ b/win32ss/gdi/eng/pdevobj.h @@ -101,7 +101,7 @@ typedef struct _PDEVOBJ // RFONT * prfntInactive; // ULONG cInactive; // BYTE ajbo[0x5C]; -// ULONG cDirectDrawDisableLocks; + ULONG cDirectDrawDisableLocks; // PVOID TypeOneInfo; PVOID pvGammaRamp; /* Gamma ramp pointer. */ PVOID RemoteTypeOne; @@ -140,7 +140,6 @@ typedef struct _PDEVOBJ }; /* ros specific */ - ULONG DxDd_nCount; GDIPOINTER Pointer; /* Stuff to keep track of software cursors; win32k gdi part */ UINT SafetyRemoveLevel; /* at what level was the cursor removed? diff --git a/win32ss/reactx/dxg/ddraw.c b/win32ss/reactx/dxg/ddraw.c index 1a639a39ef1..12ed339367d 100644 --- a/win32ss/reactx/dxg/ddraw.c +++ b/win32ss/reactx/dxg/ddraw.c @@ -563,7 +563,7 @@ DxDdReenableDirectDrawObject( gpEngFuncs.DxEngGetDCState(hDC, 2) != 1 && !(gpEngFuncs.DxEngGetHdevData(peDdGl->hDev, DxEGShDevData_OpenRefs)) && !(gpEngFuncs.DxEngGetHdevData(peDdGl->hDev, DxEGShDevData_disable)) && - !(gpEngFuncs.DxEngGetHdevData(peDdGl->hDev, DxEGShDevData_dd_nCount)) && + !(gpEngFuncs.DxEngGetHdevData(peDdGl->hDev, DxEGShDevData_dd_locks)) && gpEngFuncs.DxEngGetHdevData(peDdGl->hDev, DxEGShDevData_DitherFmt) >= BMF_8BPP) { // reset acceleration and suspend flags diff --git a/win32ss/reactx/ntddraw/dxeng.c b/win32ss/reactx/ntddraw/dxeng.c index a096bf5f350..bcd0db67e26 100644 --- a/win32ss/reactx/ntddraw/dxeng.c +++ b/win32ss/reactx/ntddraw/dxeng.c @@ -324,9 +324,9 @@ DxEngGetHdevData(HDEV hDev, DPRINT1("requested DXEGSHDEVDATA DxEGShDevData_eddg\n"); retVal = (DWORD_PTR) PDev->pEDDgpl; break; - case DxEGShDevData_dd_nCount: - DPRINT1("requested DXEGSHDEVDATA DxEGShDevData_dd_nCount\n"); - retVal = (DWORD_PTR) PDev->DxDd_nCount; + case DxEGShDevData_dd_locks: + DPRINT1("requested DXEGSHDEVDATA DxEGShDevData_dd_locks\n"); + retVal = (DWORD_PTR) PDev->cDirectDrawDisableLocks; break; case DxEGShDevData_dd_flags: DPRINT1("requested DXEGSHDEVDATA DxEGShDevData_dd_flags\n"); @@ -413,9 +413,10 @@ DxEngSetHdevData(HDEV hDev, DPRINT1("ReactX Calling : DxEngSetHdevData DXEGSHDEVDATA : %ld\n", Type); - if ( Type == DxEGShDevData_dd_nCount ) + if (Type == DxEGShDevData_dd_locks) { - ((PPDEVOBJ)hDev)->DxDd_nCount = Data; + DPRINT1("Assigning value %d\n", Data); + ((PPDEVOBJ)hDev)->cDirectDrawDisableLocks = Data; retVal = TRUE; // Set } return retVal; diff --git a/win32ss/reactx/ntddraw/intddraw.h b/win32ss/reactx/ntddraw/intddraw.h index c1ab8c945b0..5dcf1d28774 100644 --- a/win32ss/reactx/ntddraw/intddraw.h +++ b/win32ss/reactx/ntddraw/intddraw.h @@ -108,6 +108,11 @@ typedef VOID (APIENTRY *PGD_ENGFREEPRIVATEUSERMEM)(PDD_SURFACE_LOCAL, PVOID); typedef PDD_SURFACE_LOCAL (APIENTRY *PGD_ENGLOCKDIRECTDRAWSURFACE)(HANDLE); typedef BOOL (APIENTRY *PGD_ENGUNLOCKDIRECTDRAWSURFACE)(PDD_SURFACE_LOCAL); +/* Other internal functions */ +typedef VOID (APIENTRY *PGD_DXDDDYNAMICMODECHANGE)(HDEV, HDEV, ULONG_PTR); +typedef VOID (APIENTRY *PGD_DXDDRESUMEDIRECTDRAW)(HDEV, ULONG_PTR); +typedef VOID (APIENTRY *PGD_DXDDSUSPENDDIRECTDRAW)(HDEV, ULONG_PTR); + /* Gammaramp internal prototype */ BOOL FASTCALL IntGetDeviceGammaRamp(HDEV hPDev, PGAMMARAMP Ramp); BOOL FASTCALL IntSetDeviceGammaRamp(HDEV hPDev, PGAMMARAMP Ramp, BOOL);