mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
228 lines
4.9 KiB
C++
228 lines
4.9 KiB
C++
#include <cstring>
|
|
#include <vector>
|
|
|
|
#include "Common/CompatPtr.h"
|
|
#include "Common/Log.h"
|
|
#include "Config/Config.h"
|
|
#include "DDraw/Repository.h"
|
|
#include "DDraw/Surfaces/PrimarySurface.h"
|
|
#include "Dll/Procs.h"
|
|
#include "Gdi/DcCache.h"
|
|
|
|
namespace
|
|
{
|
|
using Gdi::DcCache::CachedDc;
|
|
|
|
std::vector<CachedDc> g_cache;
|
|
DWORD g_cacheSize = 0;
|
|
DWORD g_cacheId = 0;
|
|
DWORD g_maxUsedCacheSize = 0;
|
|
DWORD g_ddLockThreadId = 0;
|
|
|
|
CompatWeakPtr<IDirectDrawPalette> g_palette;
|
|
PALETTEENTRY g_paletteEntries[256] = {};
|
|
void* g_surfaceMemory = nullptr;
|
|
LONG g_pitch = 0;
|
|
|
|
CompatPtr<IDirectDrawSurface7> createGdiSurface();
|
|
|
|
CachedDc createCachedDc()
|
|
{
|
|
CachedDc cachedDc = {};
|
|
|
|
CompatPtr<IDirectDrawSurface7> surface(createGdiSurface());
|
|
if (!surface)
|
|
{
|
|
return cachedDc;
|
|
}
|
|
|
|
HDC dc = nullptr;
|
|
HRESULT result = surface->GetDC(surface, &dc);
|
|
if (FAILED(result))
|
|
{
|
|
LOG_ONCE("Failed to create a GDI DC: " << result);
|
|
return cachedDc;
|
|
}
|
|
|
|
// Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks
|
|
Dll::g_origProcs.ReleaseDDThreadLock();
|
|
|
|
cachedDc.surface = surface.detach();
|
|
cachedDc.dc = dc;
|
|
cachedDc.cacheId = g_cacheId;
|
|
return cachedDc;
|
|
}
|
|
|
|
CompatPtr<IDirectDrawSurface7> createGdiSurface()
|
|
{
|
|
DDSURFACEDESC2 desc = DDraw::PrimarySurface::getDesc();
|
|
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
|
|
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
|
|
desc.lPitch = g_pitch;
|
|
desc.lpSurface = g_surfaceMemory;
|
|
|
|
auto dd(DDraw::Repository::getDirectDraw());
|
|
CompatPtr<IDirectDrawSurface7> surface;
|
|
HRESULT result = dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
|
|
if (FAILED(result))
|
|
{
|
|
LOG_ONCE("Failed to create a GDI surface: " << result);
|
|
return nullptr;
|
|
}
|
|
|
|
if (desc.ddpfPixelFormat.dwRGBBitCount <= 8)
|
|
{
|
|
surface->SetPalette(surface, g_palette);
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
void extendCache()
|
|
{
|
|
if (g_cacheSize >= Config::preallocatedGdiDcCount)
|
|
{
|
|
LOG_ONCE("Warning: Preallocated GDI DC count is insufficient. This may lead to graphical issues.");
|
|
}
|
|
|
|
if (GetCurrentThreadId() != g_ddLockThreadId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (DWORD i = 0; i < Config::preallocatedGdiDcCount; ++i)
|
|
{
|
|
CachedDc cachedDc = createCachedDc();
|
|
if (!cachedDc.dc)
|
|
{
|
|
return;
|
|
}
|
|
g_cache.push_back(cachedDc);
|
|
++g_cacheSize;
|
|
}
|
|
}
|
|
|
|
void releaseCachedDc(CachedDc cachedDc)
|
|
{
|
|
// Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC
|
|
Dll::g_origProcs.AcquireDDThreadLock();
|
|
|
|
HRESULT result = cachedDc.surface->ReleaseDC(cachedDc.surface, cachedDc.dc);
|
|
if (FAILED(result))
|
|
{
|
|
LOG_ONCE("Failed to release a cached DC: " << result);
|
|
}
|
|
|
|
cachedDc.surface.release();
|
|
}
|
|
}
|
|
|
|
namespace Gdi
|
|
{
|
|
namespace DcCache
|
|
{
|
|
void clear()
|
|
{
|
|
for (auto& cachedDc : g_cache)
|
|
{
|
|
releaseCachedDc(cachedDc);
|
|
}
|
|
g_cache.clear();
|
|
g_cacheSize = 0;
|
|
++g_cacheId;
|
|
}
|
|
|
|
CachedDc getDc()
|
|
{
|
|
CachedDc cachedDc = {};
|
|
if (!g_surfaceMemory)
|
|
{
|
|
return cachedDc;
|
|
}
|
|
|
|
if (g_cache.empty())
|
|
{
|
|
extendCache();
|
|
}
|
|
|
|
if (!g_cache.empty())
|
|
{
|
|
cachedDc = g_cache.back();
|
|
g_cache.pop_back();
|
|
|
|
const DWORD usedCacheSize = g_cacheSize - g_cache.size();
|
|
if (usedCacheSize > g_maxUsedCacheSize)
|
|
{
|
|
g_maxUsedCacheSize = usedCacheSize;
|
|
Compat::Log() << "GDI used DC cache size: " << g_maxUsedCacheSize;
|
|
}
|
|
}
|
|
|
|
return cachedDc;
|
|
}
|
|
|
|
bool init()
|
|
{
|
|
auto dd(DDraw::Repository::getDirectDraw());
|
|
dd->CreatePalette(dd,
|
|
DDPCAPS_8BIT | DDPCAPS_ALLOW256, g_paletteEntries, &g_palette.getRef(), nullptr);
|
|
return nullptr != g_palette;
|
|
}
|
|
|
|
void releaseDc(const CachedDc& cachedDc)
|
|
{
|
|
if (cachedDc.cacheId == g_cacheId)
|
|
{
|
|
g_cache.push_back(cachedDc);
|
|
}
|
|
else
|
|
{
|
|
releaseCachedDc(cachedDc);
|
|
}
|
|
}
|
|
|
|
void setDdLockThreadId(DWORD ddLockThreadId)
|
|
{
|
|
g_ddLockThreadId = ddLockThreadId;
|
|
}
|
|
|
|
void setSurfaceMemory(void* surfaceMemory, LONG pitch)
|
|
{
|
|
if (g_surfaceMemory == surfaceMemory && g_pitch == pitch)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_surfaceMemory = surfaceMemory;
|
|
g_pitch = pitch;
|
|
clear();
|
|
}
|
|
|
|
void updatePalette(DWORD startingEntry, DWORD count)
|
|
{
|
|
PALETTEENTRY entries[256] = {};
|
|
std::memcpy(&entries[startingEntry],
|
|
&DDraw::PrimarySurface::s_paletteEntries[startingEntry],
|
|
count * sizeof(PALETTEENTRY));
|
|
|
|
for (DWORD i = startingEntry; i < startingEntry + count; ++i)
|
|
{
|
|
if (entries[i].peFlags & PC_RESERVED)
|
|
{
|
|
entries[i] = DDraw::PrimarySurface::s_paletteEntries[0];
|
|
entries[i].peFlags = DDraw::PrimarySurface::s_paletteEntries[i].peFlags;
|
|
}
|
|
}
|
|
|
|
if (0 != std::memcmp(&g_paletteEntries[startingEntry], &entries[startingEntry],
|
|
count * sizeof(PALETTEENTRY)))
|
|
{
|
|
std::memcpy(&g_paletteEntries[startingEntry], &entries[startingEntry],
|
|
count * sizeof(PALETTEENTRY));
|
|
g_palette->SetEntries(g_palette, 0, startingEntry, count, g_paletteEntries);
|
|
clear();
|
|
}
|
|
}
|
|
}
|
|
}
|