1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Added separate critical section for GDI emulation

GDI emulation was using the DirectDraw critical section for thread safety,
but it caused a deadlock when Alt-Tabbing in Commandos BCD.
Now it uses its own critical section and some atomic shared variables.
This commit is contained in:
narzoul 2015-12-31 00:01:28 +01:00
parent 0474e7f95f
commit 9b62f21a3d
7 changed files with 65 additions and 27 deletions

View File

@ -1,6 +1,7 @@
#define CINTERFACE #define CINTERFACE
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <atomic>
#include <unordered_map> #include <unordered_map>
#include <oleacc.h> #include <oleacc.h>
@ -13,6 +14,7 @@
#include "CompatPrimarySurface.h" #include "CompatPrimarySurface.h"
#include "DDrawLog.h" #include "DDrawLog.h"
#include "DDrawProcs.h" #include "DDrawProcs.h"
#include "DDrawScopedThreadLock.h"
#include "RealPrimarySurface.h" #include "RealPrimarySurface.h"
namespace namespace
@ -40,7 +42,7 @@ namespace
HWND rootWnd; HWND rootWnd;
}; };
bool g_suppressGdiHooks = false; std::atomic<bool> g_suppressGdiHooks = false;
class HookRecursionGuard class HookRecursionGuard
{ {
@ -72,6 +74,22 @@ namespace
CaretData g_caret = {}; CaretData g_caret = {};
CRITICAL_SECTION g_gdiCriticalSection;
class GdiScopedThreadLock
{
public:
GdiScopedThreadLock()
{
EnterCriticalSection(&g_gdiCriticalSection);
}
~GdiScopedThreadLock()
{
LeaveCriticalSection(&g_gdiCriticalSection);
}
};
POINT getClientOrigin(HWND hwnd); POINT getClientOrigin(HWND hwnd);
HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin); HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin);
HDC releaseCompatDc(HDC hdc); HDC releaseCompatDc(HDC hdc);
@ -88,6 +106,7 @@ namespace
else if (WM_ERASEBKGND == ret->message && ret->lResult) else if (WM_ERASEBKGND == ret->message && ret->lResult)
{ {
HDC origDc = reinterpret_cast<HDC>(ret->wParam); HDC origDc = reinterpret_cast<HDC>(ret->wParam);
GdiScopedThreadLock gdiLock;
if (g_dcToSurface.find(origDc) == g_dcToSurface.end()) if (g_dcToSurface.find(origDc) == g_dcToSurface.end())
{ {
HWND hwnd = WindowFromDC(origDc); HWND hwnd = WindowFromDC(origDc);
@ -129,6 +148,8 @@ namespace
IDirectDrawSurface7* createGdiSurface() IDirectDrawSurface7* createGdiSurface()
{ {
Compat::DDrawScopedThreadLock ddLock;
DDSURFACEDESC2 desc = {}; DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc); desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
@ -184,11 +205,12 @@ namespace
HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin) HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin)
{ {
if (!CompatPrimarySurface::surfacePtr || !origDc || !RealPrimarySurface::isFullScreen() || g_suppressGdiHooks) if (!CompatPrimarySurface::surfacePtr || !origDc || !RealPrimarySurface::isFullScreen() || g_suppressGdiHooks)
{ {
return origDc; return origDc;
} }
GdiScopedThreadLock gdiLock;
HookRecursionGuard recursionGuard; HookRecursionGuard recursionGuard;
IDirectDrawSurface7* surface = createGdiSurface(); IDirectDrawSurface7* surface = createGdiSurface();
@ -296,6 +318,7 @@ namespace
return hdc; return hdc;
} }
GdiScopedThreadLock gdiLock;
HookRecursionGuard recursionGuard; HookRecursionGuard recursionGuard;
auto it = g_dcToSurface.find(hdc); auto it = g_dcToSurface.find(hdc);
@ -360,6 +383,7 @@ namespace
{ {
if (STATE_SYSTEM_INVISIBLE == getCaretState(accessible)) if (STATE_SYSTEM_INVISIBLE == getCaretState(accessible))
{ {
GdiScopedThreadLock gdiLock;
drawCaret(); drawCaret();
g_caret.isDrawn = false; g_caret.isDrawn = false;
} }
@ -369,69 +393,48 @@ namespace
HDC WINAPI getDc(HWND hWnd) HDC WINAPI getDc(HWND hWnd)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("GetDC", hWnd); Compat::LogEnter("GetDC", hWnd);
HDC compatDc = getCompatDc(hWnd, g_origGetDc(hWnd), getClientOrigin(hWnd)); HDC compatDc = getCompatDc(hWnd, g_origGetDc(hWnd), getClientOrigin(hWnd));
Compat::LogLeave("GetDC", hWnd) << compatDc; Compat::LogLeave("GetDC", hWnd) << compatDc;
Compat::origProcs.ReleaseDDThreadLock();
return compatDc; return compatDc;
} }
HDC WINAPI getDcEx(HWND hWnd, HRGN hrgnClip, DWORD flags) HDC WINAPI getDcEx(HWND hWnd, HRGN hrgnClip, DWORD flags)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("GetDCEx", hWnd); Compat::LogEnter("GetDCEx", hWnd);
HDC compatDc = getCompatDc(hWnd, g_origGetDcEx(hWnd, hrgnClip, flags), HDC compatDc = getCompatDc(hWnd, g_origGetDcEx(hWnd, hrgnClip, flags),
flags & (DCX_WINDOW | DCX_PARENTCLIP) ? getWindowOrigin(hWnd) : getClientOrigin(hWnd)); flags & (DCX_WINDOW | DCX_PARENTCLIP) ? getWindowOrigin(hWnd) : getClientOrigin(hWnd));
Compat::LogLeave("GetDCEx", hWnd) << compatDc; Compat::LogLeave("GetDCEx", hWnd) << compatDc;
Compat::origProcs.ReleaseDDThreadLock();
return compatDc; return compatDc;
} }
HDC WINAPI getWindowDc(HWND hWnd) HDC WINAPI getWindowDc(HWND hWnd)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("GetWindowDC", hWnd); Compat::LogEnter("GetWindowDC", hWnd);
HDC compatDc = getCompatDc(hWnd, g_origGetWindowDc(hWnd), getWindowOrigin(hWnd)); HDC compatDc = getCompatDc(hWnd, g_origGetWindowDc(hWnd), getWindowOrigin(hWnd));
Compat::LogLeave("GetWindowDC", hWnd) << compatDc; Compat::LogLeave("GetWindowDC", hWnd) << compatDc;
Compat::origProcs.ReleaseDDThreadLock();
return compatDc; return compatDc;
} }
int WINAPI releaseDc(HWND hWnd, HDC hDC) int WINAPI releaseDc(HWND hWnd, HDC hDC)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("ReleaseDC", hWnd, hDC); Compat::LogEnter("ReleaseDC", hWnd, hDC);
int result = g_origReleaseDc(hWnd, releaseCompatDc(hDC)); int result = g_origReleaseDc(hWnd, releaseCompatDc(hDC));
Compat::LogLeave("ReleaseDC", hWnd, hDC) << result; Compat::LogLeave("ReleaseDC", hWnd, hDC) << result;
Compat::origProcs.ReleaseDDThreadLock();
return result; return result;
} }
HDC WINAPI beginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) HDC WINAPI beginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("BeginPaint", hWnd, lpPaint); Compat::LogEnter("BeginPaint", hWnd, lpPaint);
HDC compatDc = getCompatDc(hWnd, g_origBeginPaint(hWnd, lpPaint), getClientOrigin(hWnd)); HDC compatDc = getCompatDc(hWnd, g_origBeginPaint(hWnd, lpPaint), getClientOrigin(hWnd));
lpPaint->hdc = compatDc; lpPaint->hdc = compatDc;
Compat::LogLeave("BeginPaint", hWnd, lpPaint) << compatDc; Compat::LogLeave("BeginPaint", hWnd, lpPaint) << compatDc;
Compat::origProcs.ReleaseDDThreadLock();
return compatDc; return compatDc;
} }
BOOL WINAPI endPaint(HWND hWnd, const PAINTSTRUCT* lpPaint) BOOL WINAPI endPaint(HWND hWnd, const PAINTSTRUCT* lpPaint)
{ {
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter("EndPaint", hWnd, lpPaint); Compat::LogEnter("EndPaint", hWnd, lpPaint);
BOOL result = FALSE; BOOL result = FALSE;
@ -447,7 +450,6 @@ namespace
} }
Compat::LogLeave("EndPaint", hWnd, lpPaint) << result; Compat::LogLeave("EndPaint", hWnd, lpPaint) << result;
Compat::origProcs.ReleaseDDThreadLock();
return result; return result;
} }
@ -456,6 +458,7 @@ namespace
BOOL result = g_origCreateCaret(hWnd, hBitmap, nWidth, nHeight); BOOL result = g_origCreateCaret(hWnd, hBitmap, nWidth, nHeight);
if (result) if (result)
{ {
GdiScopedThreadLock gdiLock;
if (g_caret.isDrawn) if (g_caret.isDrawn)
{ {
drawCaret(); drawCaret();
@ -470,6 +473,7 @@ namespace
BOOL WINAPI showCaret(HWND hWnd) BOOL WINAPI showCaret(HWND hWnd)
{ {
BOOL result = g_origShowCaret(hWnd); BOOL result = g_origShowCaret(hWnd);
GdiScopedThreadLock gdiLock;
if (result && !g_caret.isDrawn) if (result && !g_caret.isDrawn)
{ {
IAccessible* accessible = nullptr; IAccessible* accessible = nullptr;
@ -496,6 +500,7 @@ namespace
BOOL WINAPI hideCaret(HWND hWnd) BOOL WINAPI hideCaret(HWND hWnd)
{ {
BOOL result = g_origHideCaret(hWnd); BOOL result = g_origHideCaret(hWnd);
GdiScopedThreadLock gdiLock;
if (result && g_caret.isDrawn) if (result && g_caret.isDrawn)
{ {
drawCaret(); drawCaret();
@ -513,6 +518,7 @@ void CompatGdiSurface::hookGdi()
return; return;
} }
InitializeCriticalSection(&g_gdiCriticalSection);
g_directDraw = createDirectDraw(); g_directDraw = createDirectDraw();
if (g_directDraw) if (g_directDraw)
{ {

View File

@ -57,6 +57,6 @@ namespace CompatPrimarySurface
LONG height = 0; LONG height = 0;
DDPIXELFORMAT pixelFormat = {}; DDPIXELFORMAT pixelFormat = {};
LONG pitch = 0; LONG pitch = 0;
void* surfacePtr = nullptr; std::atomic<void*> surfacePtr = nullptr;
IReleaseNotifier releaseNotifier(onRelease); IReleaseNotifier releaseNotifier(onRelease);
} }

View File

@ -2,6 +2,7 @@
#define CINTERFACE #define CINTERFACE
#include <atomic>
#include <ddraw.h> #include <ddraw.h>
class IReleaseNotifier; class IReleaseNotifier;
@ -25,6 +26,6 @@ namespace CompatPrimarySurface
extern LONG height; extern LONG height;
extern DDPIXELFORMAT pixelFormat; extern DDPIXELFORMAT pixelFormat;
extern LONG pitch; extern LONG pitch;
extern void* surfacePtr; extern std::atomic<void*> surfacePtr;
extern IReleaseNotifier releaseNotifier; extern IReleaseNotifier releaseNotifier;
} }

View File

@ -150,6 +150,7 @@
<ClInclude Include="DDrawProcs.h" /> <ClInclude Include="DDrawProcs.h" />
<ClInclude Include="CompatDirectDraw.h" /> <ClInclude Include="CompatDirectDraw.h" />
<ClInclude Include="CompatPrimarySurface.h" /> <ClInclude Include="CompatPrimarySurface.h" />
<ClInclude Include="DDrawScopedThreadLock.h" />
<ClInclude Include="DDrawTypes.h" /> <ClInclude Include="DDrawTypes.h" />
<ClInclude Include="CompatDirectDrawSurface.h" /> <ClInclude Include="CompatDirectDrawSurface.h" />
<ClInclude Include="DDrawVtableVisitor.h" /> <ClInclude Include="DDrawVtableVisitor.h" />

View File

@ -63,6 +63,9 @@
<ClInclude Include="Config.h"> <ClInclude Include="Config.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="DDrawScopedThreadLock.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="DllMain.cpp"> <ClCompile Include="DllMain.cpp">

View File

@ -0,0 +1,20 @@
#pragma once
#include "DDrawProcs.h"
namespace Compat
{
class DDrawScopedThreadLock
{
public:
DDrawScopedThreadLock()
{
origProcs.AcquireDDThreadLock();
}
~DDrawScopedThreadLock()
{
origProcs.ReleaseDDThreadLock();
}
};
}

View File

@ -1,3 +1,5 @@
#include <atomic>
#include "CompatDirectDraw.h" #include "CompatDirectDraw.h"
#include "CompatDirectDrawSurface.h" #include "CompatDirectDrawSurface.h"
#include "CompatGdiSurface.h" #include "CompatGdiSurface.h"
@ -24,6 +26,8 @@ namespace
LARGE_INTEGER g_lastUpdateTime = {}; LARGE_INTEGER g_lastUpdateTime = {};
LARGE_INTEGER g_qpcFrequency = {}; LARGE_INTEGER g_qpcFrequency = {};
std::atomic<bool> g_isFullScreen(false);
bool compatBlt(IDirectDrawSurface7* dest) bool compatBlt(IDirectDrawSurface7* dest)
{ {
Compat::LogEnter("RealPrimarySurface::compatBlt", dest); Compat::LogEnter("RealPrimarySurface::compatBlt", dest);
@ -111,6 +115,7 @@ namespace
g_frontBuffer = nullptr; g_frontBuffer = nullptr;
g_backBuffer = nullptr; g_backBuffer = nullptr;
g_isFullScreen = false;
if (g_paletteConverterSurface) if (g_paletteConverterSurface)
{ {
g_paletteConverterSurface->lpVtbl->Release(g_paletteConverterSurface); g_paletteConverterSurface->lpVtbl->Release(g_paletteConverterSurface);
@ -246,6 +251,8 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd)
g_frontBuffer->lpVtbl->SetPrivateData(g_frontBuffer, g_frontBuffer->lpVtbl->SetPrivateData(g_frontBuffer,
IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER);
g_isFullScreen = isFlippable;
return DD_OK; return DD_OK;
} }
@ -290,7 +297,7 @@ IDirectDrawSurface7* RealPrimarySurface::getSurface()
bool RealPrimarySurface::isFullScreen() bool RealPrimarySurface::isFullScreen()
{ {
return nullptr != g_backBuffer; return g_isFullScreen;
} }
bool RealPrimarySurface::isLost() bool RealPrimarySurface::isLost()