1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
DDrawCompat/DDrawCompat/D3dDdi/SurfaceRepository.cpp
narzoul c8c6bd0ce9 Updated waitForIdle implementation
Fixes performance issues on old versions of igd9trinity32.dll.
2024-08-24 11:09:48 +02:00

454 lines
12 KiB
C++

#undef WIN32_LEAN_AND_MEAN
#include <map>
#include <Windows.h>
#include <d3dkmthk.h>
#include <Common/Comparison.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/LogUsedResourceFormat.h>
#include <Gdi/VirtualScreen.h>
namespace
{
std::map<LUID, D3dDdi::SurfaceRepository> g_repositories;
D3dDdi::SurfaceRepository* g_primaryRepository = nullptr;
bool g_enableSurfaceCheck = true;
void initDitherTexture(BYTE* tex, DWORD pitch, DWORD x, DWORD y, DWORD size, DWORD mul, DWORD value)
{
if (1 == size)
{
tex[y * pitch + x] = static_cast<BYTE>(value);
}
else
{
size /= 2;
initDitherTexture(tex, pitch, x, y, size, 4 * mul, value + 0 * mul);
initDitherTexture(tex, pitch, x + size, y, size, 4 * mul, value + 2 * mul);
initDitherTexture(tex, pitch, x, y + size, size, 4 * mul, value + 3 * mul);
initDitherTexture(tex, pitch, x + size, y + size, size, 4 * mul, value + 1 * mul);
}
}
void initDitherTexture(BYTE* tex, DWORD pitch, DWORD size)
{
initDitherTexture(tex, pitch, 0, 0, size, 1, 0);
}
}
namespace D3dDdi
{
SurfaceRepository::SurfaceRepository()
: m_cursor(nullptr)
, m_cursorSize{}
, m_cursorHotspot{}
{
}
CompatPtr<IDirectDrawSurface7> SurfaceRepository::createSurface(
DWORD width, DWORD height, D3DDDIFORMAT format, DWORD caps, DWORD caps2, UINT surfaceCount)
{
if (!m_dd)
{
LOG_ONCE("ERROR: no DirectDraw repository available");
return nullptr;
}
m_releasedSurfaces.clear();
CompatPtr<IDirectDrawSurface7> surface;
if (D3DDDIFMT_P8 == format)
{
format = D3DDDIFMT_L8;
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
desc.dwWidth = width;
desc.dwHeight = height;
desc.ddpfPixelFormat = getPixelFormat(format);
desc.ddsCaps.dwCaps = caps;
desc.ddsCaps.dwCaps2 = caps2;
if (caps2 & DDSCAPS2_CUBEMAP)
{
desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX;
surfaceCount /= 6;
}
if (caps & DDSCAPS_MIPMAP)
{
desc.dwFlags |= DDSD_MIPMAPCOUNT;
desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX;
desc.dwMipMapCount = surfaceCount;
}
else if (surfaceCount > 1)
{
desc.dwFlags |= DDSD_BACKBUFFERCOUNT;
desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
desc.dwBackBufferCount = surfaceCount - 1;
}
if (0 == desc.ddpfPixelFormat.dwFlags || D3dDdi::FOURCC_INTZ == format || D3dDdi::FOURCC_DF16 == format)
{
desc.ddpfPixelFormat = getPixelFormat((caps & DDSCAPS_ZBUFFER) ? D3DDDIFMT_D16 : D3DDDIFMT_X8R8G8B8);
D3dDdi::Resource::setFormatOverride(format);
}
DDraw::SuppressResourceFormatLogs suppressResourceFormatLogs;
s_inCreateSurface = true;
HRESULT result = m_dd.get()->lpVtbl->CreateSurface(m_dd, &desc, &surface.getRef(), nullptr);
s_inCreateSurface = false;
D3dDdi::Resource::setFormatOverride(D3DDDIFMT_UNKNOWN);
if (FAILED(result))
{
LOG_ONCE("ERROR: Failed to create repository surface: " << Compat::hex(result) << " " << desc);
return nullptr;
}
return surface;
}
void SurfaceRepository::enableSurfaceCheck(bool enable)
{
g_enableSurfaceCheck = enable;
}
SurfaceRepository& SurfaceRepository::get(const Adapter& adapter)
{
return g_repositories[adapter.getLuid()];
}
SurfaceRepository::Cursor SurfaceRepository::getCursor(HCURSOR cursor)
{
if (m_cursorMaskTexture.resource && isLost(m_cursorMaskTexture) ||
m_cursorColorTexture.resource && isLost(m_cursorColorTexture))
{
m_cursor = nullptr;
m_cursorMaskTexture = {};
m_cursorColorTexture = {};
}
Cursor result = {};
if (cursor != m_cursor)
{
m_cursor = cursor;
std::unique_ptr<HICON__, decltype(&DestroyCursor)> resizedCursor(reinterpret_cast<HCURSOR>(
CopyImage(cursor, IMAGE_CURSOR, 32, 32, LR_COPYRETURNORG | LR_COPYFROMRESOURCE)), DestroyCursor);
if (resizedCursor)
{
if (resizedCursor.get() == cursor)
{
resizedCursor.release();
}
else
{
cursor = resizedCursor.get();
}
}
ICONINFO iconInfo = {};
if (!GetIconInfo(cursor, &iconInfo))
{
return {};
}
std::unique_ptr<void, decltype(&DeleteObject)> bmColor(iconInfo.hbmColor, DeleteObject);
std::unique_ptr<void, decltype(&DeleteObject)> bmMask(iconInfo.hbmMask, DeleteObject);
m_cursorHotspot.x = iconInfo.xHotspot;
m_cursorHotspot.y = iconInfo.yHotspot;
BITMAP bm = {};
GetObject(iconInfo.hbmMask, sizeof(bm), &bm);
m_cursorSize.cx = bm.bmWidth;
m_cursorSize.cy = bm.bmHeight;
if (!iconInfo.hbmColor)
{
m_cursorSize.cy /= 2;
}
if (!getCursorImage(m_cursorColorTexture, cursor, m_cursorSize.cx, m_cursorSize.cy, DI_IMAGE))
{
return {};
}
if (hasAlpha(*m_cursorColorTexture.surface))
{
m_cursorMaskTexture = {};
}
else if (!getCursorImage(m_cursorMaskTexture, cursor, m_cursorSize.cx, m_cursorSize.cy, DI_MASK))
{
return {};
}
}
result.cursor = m_cursor;
result.size = m_cursorSize;
result.hotspot = m_cursorHotspot;
result.colorTexture = m_cursorColorTexture.resource;
if (m_cursorMaskTexture.resource)
{
result.maskTexture = m_cursorMaskTexture.resource;
result.tempTexture = getSurface(m_cursorTempTexture, m_cursorSize.cx, m_cursorSize.cy,
D3DDDIFMT_X8R8G8B8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY).resource;
if (!result.tempTexture)
{
return {};
}
}
return result;
}
bool SurfaceRepository::getCursorImage(Surface& surface, HCURSOR cursor, DWORD width, DWORD height, UINT flags)
{
if (!getSurface(surface, width, height, D3DDDIFMT_A8R8G8B8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY).resource)
{
return false;
}
HDC dc = nullptr;
if (FAILED(surface.surface->GetDC(surface.surface, &dc)))
{
return false;
}
CALL_ORIG_FUNC(DrawIconEx)(dc, 0, 0, cursor, width, height, 0, nullptr, flags);
surface.surface->ReleaseDC(surface.surface, dc);
return true;
}
Resource* SurfaceRepository::getDitherTexture(DWORD size)
{
return getInitializedResource(m_ditherTexture, size, size, D3DDDIFMT_L8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY,
[](const DDSURFACEDESC2& desc) {
initDitherTexture(static_cast<BYTE*>(desc.lpSurface), desc.lPitch, 16);
});
}
Resource* SurfaceRepository::getGammaRampTexture()
{
return getSurface(m_gammaRampTexture, 256, 3, D3DDDIFMT_L8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY).resource;
}
Resource* SurfaceRepository::getLogicalXorTexture()
{
return getInitializedResource(m_logicalXorTexture, 256, 256, D3DDDIFMT_L8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY,
[](const DDSURFACEDESC2& desc) {
BYTE* p = static_cast<BYTE*>(desc.lpSurface);
for (UINT y = 0; y < 256; ++y)
{
for (UINT x = 0; x < 256; ++x)
{
p[x] = static_cast<BYTE>(x ^ y);
}
p += desc.lPitch;
}
});
}
Resource* SurfaceRepository::getInitializedResource(Surface& surface, DWORD width, DWORD height,
D3DDDIFORMAT format, DWORD caps, std::function<void(const DDSURFACEDESC2&)> initFunc)
{
if (!isLost(surface) || !getSurface(surface, width, height, format, caps).resource)
{
return surface.resource;
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
surface.surface->Lock(surface.surface, nullptr, &desc, DDLOCK_DISCARDCONTENTS | DDLOCK_WAIT, nullptr);
if (!desc.lpSurface)
{
return nullptr;
}
initFunc(desc);
surface.surface->Unlock(surface.surface, nullptr);
surface.resource->prepareForGpuRead(0);
return surface.resource;
}
const SurfaceRepository::Surface& SurfaceRepository::getNextRenderTarget(
DWORD width, DWORD height, D3DDDIFORMAT format, const Resource* currentSrcRt, const Resource* currentDstRt)
{
const bool hq = getFormatInfo(format).red.bitCount > 8;
auto& renderTargets = hq ? m_hqRenderTargets : m_renderTargets;
std::size_t index = 0;
while (index < renderTargets.size())
{
auto rt = renderTargets[index].resource;
if (!rt || rt != currentSrcRt && rt != currentDstRt)
{
break;
}
++index;
}
return getTempSurface(renderTargets[index], width, height, hq ? format : D3DDDIFMT_X8R8G8B8,
DDSCAPS_3DDEVICE | DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
}
Resource* SurfaceRepository::getPaletteTexture()
{
return getSurface(m_paletteTexture, 256, 1, D3DDDIFMT_A8R8G8B8, DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY).resource;
}
SurfaceRepository& SurfaceRepository::getPrimaryRepo()
{
return *g_primaryRepository;
}
SurfaceRepository::Surface& SurfaceRepository::getSurface(Surface& surface, DWORD width, DWORD height,
D3DDDIFORMAT format, DWORD caps, UINT surfaceCount, DWORD caps2)
{
if (!g_enableSurfaceCheck)
{
return surface;
}
if (surface.width != width || surface.height != height || surface.format != format || isLost(surface))
{
surface = {};
}
if (!surface.surface)
{
surface.surface = createSurface(width, height, format, caps, caps2, surfaceCount);
if (surface.surface)
{
surface.resource = D3dDdi::Device::findResource(
DDraw::DirectDrawSurface::getDriverResourceHandle(*surface.surface));
surface.width = width;
surface.height = height;
surface.format = format;
}
}
return surface;
}
SurfaceRepository::Surface& SurfaceRepository::getSyncSurface(D3DDDIFORMAT format)
{
return getSurface(m_syncSurface[format], 16, 16, format, DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY);
}
SurfaceRepository::Surface& SurfaceRepository::getTempSurface(Surface& surface, DWORD width, DWORD height,
D3DDDIFORMAT format, DWORD caps, UINT surfaceCount)
{
return getSurface(surface, std::max(width, surface.width), std::max(height, surface.height),
format, caps, surfaceCount);
}
SurfaceRepository::Surface& SurfaceRepository::getTempSysMemSurface(DWORD width, DWORD height)
{
return getTempSurface(m_sysMemSurface, width, height, D3DDDIFMT_A8R8G8B8,
DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY);
}
const SurfaceRepository::Surface& SurfaceRepository::getTempTexture(DWORD width, DWORD height, D3DDDIFORMAT format)
{
return getTempSurface(m_textures[format], width, height, format,
(D3DDDIFMT_P8 == format ? 0 : DDSCAPS_TEXTURE) | DDSCAPS_VIDEOMEMORY);
}
CompatPtr<IDirectDrawSurface7> SurfaceRepository::getWindowedBackBuffer(DWORD width, DWORD height)
{
s_isLockResourceEnabled = true;
auto surface = getSurface(m_windowedBackBuffer, width, height, D3DDDIFMT_X8R8G8B8,
DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY).surface;
s_isLockResourceEnabled = false;
return surface;
}
CompatWeakPtr<IDirectDrawSurface7> SurfaceRepository::getWindowedPrimary()
{
if (m_windowedPrimary)
{
if (SUCCEEDED(m_windowedPrimary->IsLost(m_windowedPrimary)))
{
return m_windowedPrimary;
}
m_windowedPrimary.release();
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_CAPS;
desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
HRESULT result = m_dd->CreateSurface(m_dd, &desc, &m_windowedPrimary.getRef(), nullptr);
if (FAILED(result))
{
LOG_ONCE("ERROR: Failed to create primary surface in repository: " << Compat::hex(result) << " " << desc);
}
return m_windowedPrimary;
}
CompatPtr<IDirectDrawSurface7> SurfaceRepository::getWindowedSrc(RECT rect)
{
CompatPtr<IDirectDrawSurface7> src;
auto desc = Gdi::VirtualScreen::getSurfaceDesc(rect);
m_dd->CreateSurface(m_dd, &desc, &src.getRef(), nullptr);
return src;
}
bool SurfaceRepository::hasAlpha(CompatRef<IDirectDrawSurface7> surface)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
if (FAILED(surface->Lock(&surface, nullptr, &desc, DDLOCK_WAIT, nullptr)))
{
return false;
}
for (UINT y = 0; y < desc.dwHeight; ++y)
{
const DWORD* p = reinterpret_cast<DWORD*>(static_cast<BYTE*>(desc.lpSurface) + y * desc.lPitch);
for (UINT x = 0; x < desc.dwWidth; ++x)
{
if (*p & 0xFF000000)
{
surface->Unlock(&surface, nullptr);
return true;
}
++p;
}
}
surface->Unlock(&surface, nullptr);
return false;
}
bool SurfaceRepository::isLost(Surface& surface)
{
return !surface.surface || FAILED(surface.surface->IsLost(surface.surface));
}
void SurfaceRepository::release(Surface& surface)
{
if (surface.surface)
{
m_releasedSurfaces.push_back(surface);
surface = {};
}
}
void SurfaceRepository::setRepository(CompatWeakPtr<IDirectDraw7> dd)
{
m_dd = dd;
if (!g_primaryRepository)
{
g_primaryRepository = this;
}
}
bool SurfaceRepository::s_inCreateSurface = false;
bool SurfaceRepository::s_isLockResourceEnabled = false;
}