mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
Fixes cursor flickering issues in Siege of Avalon (issue #34) and multimon flip issues on Windows 8.1 (issue #28).
371 lines
11 KiB
C++
371 lines
11 KiB
C++
#include <set>
|
|
|
|
#include "Common/CompatRef.h"
|
|
#include "DDraw/RealPrimarySurface.h"
|
|
#include "DDraw/Repository.h"
|
|
#include "DDraw/Surfaces/PrimarySurface.h"
|
|
#include "DDraw/Surfaces/Surface.h"
|
|
#include "DDraw/Surfaces/SurfaceImpl.h"
|
|
#include "Gdi/AccessGuard.h"
|
|
|
|
namespace
|
|
{
|
|
struct DirectDrawInterface
|
|
{
|
|
const void* vtable;
|
|
void* ddObject;
|
|
DirectDrawInterface* next;
|
|
DWORD refCount;
|
|
DWORD unknown1;
|
|
DWORD unknown2;
|
|
};
|
|
|
|
template <typename TSurface>
|
|
void copyColorKey(CompatRef<TSurface> dst, CompatRef<TSurface> src, DWORD ckFlag)
|
|
{
|
|
DDCOLORKEY ck = {};
|
|
if (SUCCEEDED(src->GetColorKey(&src, ckFlag, &ck)))
|
|
{
|
|
dst->SetColorKey(&dst, ckFlag, &ck);
|
|
}
|
|
}
|
|
|
|
template <typename TSurface>
|
|
bool waitForFlip(TSurface* This, DWORD flags, DWORD waitFlag, DWORD doNotWaitFlag)
|
|
{
|
|
if (!This)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const bool wait = (flags & waitFlag) || !(flags & doNotWaitFlag) &&
|
|
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtablePtr == static_cast<void*>(This->lpVtbl);
|
|
|
|
return DDraw::RealPrimarySurface::waitForFlip(DDraw::Surface::getSurface(*This), wait);
|
|
}
|
|
}
|
|
|
|
namespace DDraw
|
|
{
|
|
template <typename TSurface>
|
|
SurfaceImpl<TSurface>::~SurfaceImpl()
|
|
{
|
|
}
|
|
|
|
template <typename TSurface>
|
|
bool SurfaceImpl<TSurface>::bltRetry(TSurface*& dstSurface, RECT*& dstRect,
|
|
TSurface*& srcSurface, RECT*& srcRect, bool isTransparentBlt,
|
|
const std::function<HRESULT()>& blt)
|
|
{
|
|
if (!dstSurface || !srcSurface)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TSurfaceDesc dstDesc = {};
|
|
dstDesc.dwSize = sizeof(dstDesc);
|
|
s_origVtable.GetSurfaceDesc(dstSurface, &dstDesc);
|
|
|
|
TSurfaceDesc srcDesc = {};
|
|
srcDesc.dwSize = sizeof(srcDesc);
|
|
s_origVtable.GetSurfaceDesc(srcSurface, &srcDesc);
|
|
|
|
if ((dstDesc.ddpfPixelFormat.dwFlags & DDPF_FOURCC) &&
|
|
(dstDesc.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) &&
|
|
(srcDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY))
|
|
{
|
|
const bool isCopyNeeded = true;
|
|
return prepareBltRetrySurface(srcSurface, srcRect, srcDesc, isTransparentBlt, isCopyNeeded) &&
|
|
SUCCEEDED(blt());
|
|
}
|
|
else if ((srcDesc.ddpfPixelFormat.dwFlags & DDPF_FOURCC) &&
|
|
(srcDesc.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) &&
|
|
(dstDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY))
|
|
{
|
|
TSurface* origDstSurface = dstSurface;
|
|
RECT* origDstRect = dstRect;
|
|
const bool isCopyNeeded = isTransparentBlt;
|
|
return prepareBltRetrySurface(dstSurface, dstRect, dstDesc, isTransparentBlt, isCopyNeeded) &&
|
|
SUCCEEDED(blt()) &&
|
|
SUCCEEDED(s_origVtable.Blt(
|
|
origDstSurface, origDstRect, dstSurface, dstRect, DDBLT_WAIT, nullptr));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
bool SurfaceImpl<TSurface>::prepareBltRetrySurface(TSurface*& surface, RECT*& rect,
|
|
const TSurfaceDesc& desc, bool isTransparentBlt, bool isCopyNeeded)
|
|
{
|
|
TSurface* replSurface = surface;
|
|
RECT* replRect = rect;
|
|
replaceWithVidMemSurface(replSurface, replRect, desc);
|
|
if (replSurface == surface)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (isCopyNeeded && FAILED(s_origVtable.Blt(
|
|
replSurface, replRect, surface, rect, DDBLT_WAIT, nullptr)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (isTransparentBlt)
|
|
{
|
|
copyColorKey<TSurface>(*replSurface, *surface, DDCKEY_SRCBLT);
|
|
copyColorKey<TSurface>(*replSurface, *surface, DDCKEY_DESTBLT);
|
|
}
|
|
surface = replSurface;
|
|
rect = replRect;
|
|
return true;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
void SurfaceImpl<TSurface>::replaceWithVidMemSurface(TSurface*& surface, RECT*& rect,
|
|
const TSurfaceDesc& desc)
|
|
{
|
|
static RECT replRect = {};
|
|
replRect = rect ? RECT{ 0, 0, rect->right - rect->left, rect->bottom - rect->top } :
|
|
RECT{ 0, 0, static_cast<LONG>(desc.dwWidth), static_cast<LONG>(desc.dwHeight) };
|
|
|
|
DDSURFACEDESC2 replDesc = {};
|
|
replDesc.dwSize = sizeof(replDesc);
|
|
replDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
|
|
replDesc.dwWidth = replRect.right;
|
|
replDesc.dwHeight = replRect.bottom;
|
|
replDesc.ddpfPixelFormat = desc.ddpfPixelFormat;
|
|
replDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
|
|
|
|
CompatPtr<IUnknown> ddUnk;
|
|
GetDDInterface(surface, reinterpret_cast<void**>(&ddUnk.getRef()));
|
|
CompatPtr<IDirectDraw7> dd;
|
|
ddUnk->QueryInterface(ddUnk, IID_IDirectDraw7, reinterpret_cast<void**>(&dd.getRef()));
|
|
|
|
DDraw::Repository::ScopedSurface replacementSurface(*dd, replDesc);
|
|
if (replacementSurface.surface)
|
|
{
|
|
surface = CompatPtr<TSurface>::from(replacementSurface.surface.get());
|
|
rect = &replRect;
|
|
}
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::Blt(
|
|
TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
|
|
DWORD dwFlags, LPDDBLTFX lpDDBltFx)
|
|
{
|
|
if (!waitForFlip(This, dwFlags, DDBLT_WAIT, DDBLT_DONOTWAIT))
|
|
{
|
|
return DDERR_WASSTILLDRAWING;
|
|
}
|
|
|
|
Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This));
|
|
Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
|
|
HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
|
|
if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result)
|
|
{
|
|
const bool isTransparentBlt = 0 !=
|
|
(dwFlags & (DDBLT_KEYDEST | DDBLT_KEYSRC | DDBLT_KEYDESTOVERRIDE | DDBLT_KEYSRCOVERRIDE));
|
|
if (bltRetry(This, lpDestRect, lpDDSrcSurface, lpSrcRect, isTransparentBlt,
|
|
[&]() { return s_origVtable.Blt(
|
|
This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); }))
|
|
{
|
|
return DD_OK;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::BltFast(
|
|
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans)
|
|
{
|
|
if (!waitForFlip(This, dwTrans, DDBLTFAST_WAIT, DDBLTFAST_DONOTWAIT))
|
|
{
|
|
return DDERR_WASSTILLDRAWING;
|
|
}
|
|
|
|
Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This));
|
|
Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface));
|
|
HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans);
|
|
if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result)
|
|
{
|
|
RECT dstRect = { static_cast<LONG>(dwX), static_cast<LONG>(dwY) };
|
|
if (lpSrcRect)
|
|
{
|
|
dstRect.right = dwX + lpSrcRect->right - lpSrcRect->left;
|
|
dstRect.bottom = dwY + lpSrcRect->bottom - lpSrcRect->top;
|
|
}
|
|
else
|
|
{
|
|
TSurfaceDesc desc = {};
|
|
desc.dwSize = sizeof(desc);
|
|
s_origVtable.GetSurfaceDesc(lpDDSrcSurface, &desc);
|
|
|
|
dstRect.right = dwX + desc.dwWidth;
|
|
dstRect.bottom = dwY + desc.dwHeight;
|
|
}
|
|
|
|
RECT* dstRectPtr = &dstRect;
|
|
const bool isTransparentBlt = 0 != (dwTrans & (DDBLTFAST_DESTCOLORKEY | DDBLTFAST_SRCCOLORKEY));
|
|
if (bltRetry(This, dstRectPtr, lpDDSrcSurface, lpSrcRect, isTransparentBlt,
|
|
[&]() { return s_origVtable.BltFast(
|
|
This, dstRectPtr->left, dstRectPtr->top, lpDDSrcSurface, lpSrcRect, dwTrans); }))
|
|
{
|
|
return DD_OK;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags)
|
|
{
|
|
return s_origVtable.Flip(This, lpDDSurfaceTargetOverride, dwFlags);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::GetBltStatus(TSurface* This, DWORD dwFlags)
|
|
{
|
|
HRESULT result = s_origVtable.GetBltStatus(This, dwFlags);
|
|
if (SUCCEEDED(result) && (dwFlags & DDGBS_CANBLT))
|
|
{
|
|
const bool wait = false;
|
|
if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
|
|
{
|
|
return DDERR_WASSTILLDRAWING;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps)
|
|
{
|
|
return s_origVtable.GetCaps(This, lpDDSCaps);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC)
|
|
{
|
|
HRESULT result = DD_OK;
|
|
|
|
{
|
|
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE);
|
|
result = s_origVtable.GetDC(This, lphDC);
|
|
}
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
RealPrimarySurface::waitForFlip(Surface::getSurface(*This));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl2<TSurface>::GetDDInterface(TSurface* /*This*/, LPVOID* lplpDD)
|
|
{
|
|
DirectDrawInterface dd = {};
|
|
dd.vtable = IID_IDirectDraw7 == m_data->m_ddId
|
|
? static_cast<const void*>(CompatVtable<IDirectDrawVtbl>::s_origVtablePtr)
|
|
: static_cast<const void*>(CompatVtable<IDirectDraw7Vtbl>::s_origVtablePtr);
|
|
dd.ddObject = m_data->m_ddObject;
|
|
return CompatVtable<IDirectDrawVtbl>::s_origVtable.QueryInterface(
|
|
reinterpret_cast<IDirectDraw*>(&dd), m_data->m_ddId, lplpDD);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::GetFlipStatus(TSurface* This, DWORD dwFlags)
|
|
{
|
|
HRESULT result = s_origVtable.GetFlipStatus(This, dwFlags);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
const bool wait = false;
|
|
if (!RealPrimarySurface::waitForFlip(Surface::getSurface(*This), wait))
|
|
{
|
|
return DDERR_WASSTILLDRAWING;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc)
|
|
{
|
|
return s_origVtable.GetSurfaceDesc(This, lpDDSurfaceDesc);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::IsLost(TSurface* This)
|
|
{
|
|
return s_origVtable.IsLost(This);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::Lock(
|
|
TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
|
|
DWORD dwFlags, HANDLE hEvent)
|
|
{
|
|
if (!waitForFlip(This, dwFlags, DDLOCK_WAIT, DDLOCK_DONOTWAIT))
|
|
{
|
|
return DDERR_WASSTILLDRAWING;
|
|
}
|
|
|
|
Gdi::DDrawAccessGuard accessGuard((dwFlags & DDLOCK_READONLY) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE,
|
|
PrimarySurface::isGdiSurface(This));
|
|
HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent);
|
|
if (DDERR_SURFACELOST == result)
|
|
{
|
|
TSurfaceDesc desc = {};
|
|
desc.dwSize = sizeof(desc);
|
|
if (SUCCEEDED(s_origVtable.GetSurfaceDesc(This, &desc)) && !(desc.dwFlags & DDSD_HEIGHT))
|
|
{
|
|
// Fixes missing handling for lost vertex buffers in Messiah
|
|
s_origVtable.Restore(This);
|
|
// Still, pass back DDERR_SURFACELOST to the application in case it handles it
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::ReleaseDC(TSurface* This, HDC hDC)
|
|
{
|
|
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This));
|
|
return s_origVtable.ReleaseDC(This, hDC);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::Restore(TSurface* This)
|
|
{
|
|
return s_origVtable.Restore(This);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::SetPalette(TSurface* This, LPDIRECTDRAWPALETTE lpDDPalette)
|
|
{
|
|
return s_origVtable.SetPalette(This, lpDDPalette);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
HRESULT SurfaceImpl<TSurface>::Unlock(TSurface* This, TUnlockParam lpRect)
|
|
{
|
|
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This));
|
|
return s_origVtable.Unlock(This, lpRect);
|
|
}
|
|
|
|
template <typename TSurface>
|
|
const Vtable<TSurface>& SurfaceImpl<TSurface>::s_origVtable =
|
|
CompatVtable<Vtable<TSurface>>::s_origVtable;
|
|
|
|
template SurfaceImpl<IDirectDrawSurface>;
|
|
template SurfaceImpl<IDirectDrawSurface2>;
|
|
template SurfaceImpl<IDirectDrawSurface3>;
|
|
template SurfaceImpl<IDirectDrawSurface4>;
|
|
template SurfaceImpl<IDirectDrawSurface7>;
|
|
}
|