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

Create off-screen plain surfaces in system memory

Fixes performance issues with Desperados mentioned in issue #5
(possibly #8 as well).
This commit is contained in:
narzoul 2016-12-22 20:12:11 +01:00
parent 1c82165214
commit 347713cdce
5 changed files with 178 additions and 51 deletions

View File

@ -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<IDirectDraw7> dd, const DDSURFACEDESC2& desc)
: Surface(getSurface(dd, desc))
{
if (surface)
{
surface->SetColorKey(surface, DDCKEY_SRCBLT, nullptr);
surface->SetColorKey(surface, DDCKEY_DESTBLT, nullptr);
}
}
ScopedSurface::~ScopedSurface()

View File

@ -185,7 +185,6 @@ namespace DDraw
result = m_impl.Restore(This);
if (SUCCEEDED(result))
{
fixSurfacePtrs(*This);
Gdi::invalidate(nullptr);
}
}

View File

@ -13,6 +13,29 @@ DEFINE_GUID(IID_CompatSurfacePrivateData,
namespace
{
template <typename TDirectDraw, typename TSurface, typename TSurfaceDesc>
HRESULT createSurface(CompatRef<TDirectDraw> dd, TSurfaceDesc desc, TSurface*& surface)
{
auto dd7(CompatPtr<IDirectDraw7>::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<IDirectDraw7> dd, DWORD& flags, DWORD& caps, DDPIXELFORMAT& pf)
{
if ((flags & DDSD_WIDTH) &&
@ -139,14 +162,9 @@ namespace DDraw
template <typename TDirectDraw, typename TSurface, typename TSurfaceDesc>
HRESULT Surface::create(CompatRef<TDirectDraw> dd, TSurfaceDesc desc, TSurface*& surface)
{
CompatPtr<IDirectDraw7> dd7(Compat::queryInterface<IDirectDraw7>(&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<TSurface>::fixSurfacePtrs(*surface);
CompatPtr<IDirectDrawSurface7> surface7(
Compat::queryInterface<IDirectDrawSurface7>(surface));
std::unique_ptr<Surface> privateData(new Surface());

View File

@ -1,5 +1,7 @@
#include <set>
#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<IDirectDrawSurface7> surface, const DDSURFACEDESC2& desc)
template <typename TSurface>
void copyColorKey(CompatRef<TSurface> dst, CompatRef<TSurface> 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<std::set<IDirectDrawSurface7*>*>(lpContext);
CompatPtr<IDirectDrawSurface7> 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<IDirectDrawSurface7> surface)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
surface->GetSurfaceDesc(&surface, &desc);
fixSurfacePtr(surface, desc);
std::set<IDirectDrawSurface7*> visitedSurfaces{ &surface };
surface->EnumAttachedSurfaces(&surface, &visitedSurfaces, &fixSurfacePtrEnumCallback);
}
}
@ -64,10 +36,97 @@ namespace DDraw
}
template <typename TSurface>
void SurfaceImpl<TSurface>::fixSurfacePtrs(CompatRef<TSurface> surface)
bool SurfaceImpl<TSurface>::bltRetry(TSurface*& dstSurface, RECT*& dstRect,
TSurface*& srcSurface, RECT*& srcRect, bool isTransparentBlt,
const std::function<HRESULT()>& blt)
{
CompatPtr<IDirectDrawSurface7> surface7(Compat::queryInterface<IDirectDrawSurface7>(&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 <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;
DDraw::Repository::ScopedSurface replacementSurface(*m_data->getDirectDraw(), replDesc);
if (replacementSurface.surface)
{
surface = CompatPtr<TSurface>::from(replacementSurface.surface.get());
rect = &replRect;
}
}
template <typename TSurface>
@ -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 <typename TSurface>
HRESULT SurfaceImpl<TSurface>::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<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>

View File

@ -2,9 +2,10 @@
#define CINTERFACE
#include <functional>
#include <ddraw.h>
#include "Common/CompatRef.h"
#include "Common/CompatVtable.h"
#include "DDraw/Types.h"
@ -36,8 +37,6 @@ namespace DDraw
virtual ~SurfaceImpl();
static void fixSurfacePtrs(CompatRef<TSurface> 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<HRESULT()>& 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<TSurface>& s_origVtable;
};
}