diff --git a/DDrawCompat/DDraw/Repository.cpp b/DDrawCompat/DDraw/Repository.cpp index 74e177a..3e14e5a 100644 --- a/DDrawCompat/DDraw/Repository.cpp +++ b/DDrawCompat/DDraw/Repository.cpp @@ -65,7 +65,7 @@ namespace surface.desc.ddpfPixelFormat = pf; surface.desc.ddsCaps.dwCaps = caps; - dd.get().lpVtbl->CreateSurface(&dd, &surface.desc, &surface.surface.getRef(), nullptr); + dd->CreateSurface(&dd, &surface.desc, &surface.surface.getRef(), nullptr); return surface; } @@ -199,6 +199,11 @@ namespace DDraw ScopedSurface::ScopedSurface(CompatRef dd, const DDSURFACEDESC2& desc) : Surface(getSurface(dd, desc)) { + if (surface) + { + surface->SetColorKey(surface, DDCKEY_SRCBLT, nullptr); + surface->SetColorKey(surface, DDCKEY_DESTBLT, nullptr); + } } ScopedSurface::~ScopedSurface() diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index f03857c..066e17f 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -185,7 +185,6 @@ namespace DDraw result = m_impl.Restore(This); if (SUCCEEDED(result)) { - fixSurfacePtrs(*This); Gdi::invalidate(nullptr); } } diff --git a/DDrawCompat/DDraw/Surfaces/Surface.cpp b/DDrawCompat/DDraw/Surfaces/Surface.cpp index fe9f38e..5048e2a 100644 --- a/DDrawCompat/DDraw/Surfaces/Surface.cpp +++ b/DDrawCompat/DDraw/Surfaces/Surface.cpp @@ -13,6 +13,29 @@ DEFINE_GUID(IID_CompatSurfacePrivateData, namespace { + template + HRESULT createSurface(CompatRef dd, TSurfaceDesc desc, TSurface*& surface) + { + auto dd7(CompatPtr::from(&dd)); + fixSurfaceDesc(*dd7, desc.dwFlags, desc.ddsCaps.dwCaps, desc.ddpfPixelFormat); + + if ((desc.ddsCaps.dwCaps & DDSCAPS_OFFSCREENPLAIN) && + !(desc.ddsCaps.dwCaps & (DDSCAPS_SYSTEMMEMORY | DDSCAPS_3DDEVICE))) + { + TSurfaceDesc sysMemDesc = desc; + sysMemDesc.ddsCaps.dwCaps &= + ~(DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM); + sysMemDesc.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + + if (SUCCEEDED(dd->CreateSurface(&dd, &sysMemDesc, &surface, nullptr))) + { + return DD_OK; + } + } + + return dd->CreateSurface(&dd, &desc, &surface, nullptr); + } + void fixSurfaceDesc(CompatRef dd, DWORD& flags, DWORD& caps, DDPIXELFORMAT& pf) { if ((flags & DDSD_WIDTH) && @@ -139,14 +162,9 @@ namespace DDraw template HRESULT Surface::create(CompatRef dd, TSurfaceDesc desc, TSurface*& surface) { - CompatPtr dd7(Compat::queryInterface(&dd)); - fixSurfaceDesc(*dd7, desc.dwFlags, desc.ddsCaps.dwCaps, desc.ddpfPixelFormat); - - HRESULT result = dd->CreateSurface(&dd, &desc, &surface, nullptr); + HRESULT result = createSurface(dd, desc, surface); if (SUCCEEDED(result)) { - SurfaceImpl::fixSurfacePtrs(*surface); - CompatPtr surface7( Compat::queryInterface(surface)); std::unique_ptr privateData(new Surface()); diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index 3ee33fe..ee226f6 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -1,5 +1,7 @@ #include +#include "Common/CompatRef.h" +#include "DDraw/Repository.h" #include "DDraw/Surfaces/Surface.h" #include "DDraw/Surfaces/SurfaceImpl.h" @@ -15,44 +17,14 @@ namespace DWORD unknown2; }; - void fixSurfacePtr(CompatRef surface, const DDSURFACEDESC2& desc) + template + void copyColorKey(CompatRef dst, CompatRef src, DWORD ckFlag) { - if ((desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) || !(desc.ddpfPixelFormat.dwFlags & DDPF_RGB)) + DDCOLORKEY ck = {}; + if (SUCCEEDED(src->GetColorKey(&src, ckFlag, &ck))) { - return; + dst->SetColorKey(&dst, ckFlag, &ck); } - - RECT r = { 0, 0, 1, 1 }; - surface->Blt(&surface, &r, &surface, &r, DDBLT_WAIT, nullptr); - } - - HRESULT WINAPI fixSurfacePtrEnumCallback( - LPDIRECTDRAWSURFACE7 lpDDSurface, - LPDDSURFACEDESC2 lpDDSurfaceDesc, - LPVOID lpContext) - { - auto& visitedSurfaces = *static_cast*>(lpContext); - - CompatPtr surface(lpDDSurface); - if (visitedSurfaces.find(surface) == visitedSurfaces.end()) - { - visitedSurfaces.insert(surface); - fixSurfacePtr(*surface, *lpDDSurfaceDesc); - surface->EnumAttachedSurfaces(surface, lpContext, &fixSurfacePtrEnumCallback); - } - - return DDENUMRET_OK; - } - - void fixSurfacePtrs(CompatRef surface) - { - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - surface->GetSurfaceDesc(&surface, &desc); - - fixSurfacePtr(surface, desc); - std::set visitedSurfaces{ &surface }; - surface->EnumAttachedSurfaces(&surface, &visitedSurfaces, &fixSurfacePtrEnumCallback); } } @@ -64,10 +36,97 @@ namespace DDraw } template - void SurfaceImpl::fixSurfacePtrs(CompatRef surface) + bool SurfaceImpl::bltRetry(TSurface*& dstSurface, RECT*& dstRect, + TSurface*& srcSurface, RECT*& srcRect, bool isTransparentBlt, + const std::function& blt) { - CompatPtr surface7(Compat::queryInterface(&surface)); - ::fixSurfacePtrs(*surface7); + 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 + bool SurfaceImpl::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(*replSurface, *surface, DDCKEY_SRCBLT); + copyColorKey(*replSurface, *surface, DDCKEY_DESTBLT); + } + surface = replSurface; + rect = replRect; + return true; + } + + template + void SurfaceImpl::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(desc.dwWidth), static_cast(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; + + DDraw::Repository::ScopedSurface replacementSurface(*m_data->getDirectDraw(), replDesc); + if (replacementSurface.surface) + { + surface = CompatPtr::from(replacementSurface.surface.get()); + rect = &replRect; + } } template @@ -95,14 +154,54 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { - return s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + 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 HRESULT SurfaceImpl::BltFast( TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { - return s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result) + { + RECT dstRect = { static_cast(dwX), static_cast(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 diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h index 19aea56..4a7b4f4 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h @@ -2,9 +2,10 @@ #define CINTERFACE +#include + #include -#include "Common/CompatRef.h" #include "Common/CompatVtable.h" #include "DDraw/Types.h" @@ -36,8 +37,6 @@ namespace DDraw virtual ~SurfaceImpl(); - static void fixSurfacePtrs(CompatRef surface); - virtual HRESULT Blt(TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx); virtual HRESULT BltFast(TSurface* This, DWORD dwX, DWORD dwY, @@ -59,6 +58,13 @@ namespace DDraw void undoFlip(TSurface* This, TSurface* targetOverride); private: + bool bltRetry(TSurface*& dstSurface, RECT*& dstRect, + TSurface*& srcSurface, RECT*& srcRect, bool isTransparentBlt, + const std::function& blt); + bool prepareBltRetrySurface(TSurface*& surface, RECT*& rect, + const TSurfaceDesc& desc, bool isTransparentBlt, bool isCopyNeeded); + void replaceWithVidMemSurface(TSurface*& surface, RECT*& rect, const TSurfaceDesc& desc); + static const Vtable& s_origVtable; }; }