1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
2022-09-27 21:44:59 +02:00

1105 lines
32 KiB
C++

#include <type_traits>
#include <Common/HResultException.h>
#include <Common/Log.h>
#include <Common/Time.h>
#include <Config/Config.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/Log/DeviceFuncsLog.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/Blitter.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/Cursor.h>
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
namespace
{
D3DDDI_RESOURCEFLAGS getResourceTypeFlags();
const UINT g_resourceTypeFlags = getResourceTypeFlags().Value;
RECT g_presentationRect = {};
RECT g_primaryRect = {};
D3DDDIFORMAT g_formatOverride = D3DDDIFMT_UNKNOWN;
std::pair<D3DDDIMULTISAMPLE_TYPE, UINT> g_msaaOverride = {};
RECT calculatePresentationRect()
{
const RECT srcRect = DDraw::PrimarySurface::getMonitorRect();
const RECT dstRect = DDraw::RealPrimarySurface::getMonitorRect();
const int srcWidth = srcRect.right - srcRect.left;
const int srcHeight = srcRect.bottom - srcRect.top;
const int dstWidth = dstRect.right - dstRect.left;
const int dstHeight = dstRect.bottom - dstRect.top;
RECT rect = { 0, 0, dstWidth, dstHeight };
if (dstWidth * srcHeight > dstHeight * srcWidth)
{
rect.right = dstHeight * srcWidth / srcHeight;
}
else
{
rect.bottom = dstWidth * srcHeight / srcWidth;
}
OffsetRect(&rect, (dstWidth - rect.right) / 2, (dstHeight - rect.bottom) / 2);
return rect;
}
LONG divCeil(LONG n, LONG d)
{
return (n + d - 1) / d;
}
D3DDDI_RESOURCEFLAGS getResourceTypeFlags()
{
D3DDDI_RESOURCEFLAGS flags = {};
flags.RenderTarget = 1;
flags.ZBuffer = 1;
flags.DMap = 1;
flags.Points = 1;
flags.RtPatches = 1;
flags.NPatches = 1;
flags.Video = 1;
flags.CaptureBuffer = 1;
flags.Primary = 1;
flags.Texture = 1;
flags.CubeMap = 1;
flags.VertexBuffer = 1;
flags.IndexBuffer = 1;
flags.DecodeRenderTarget = 1;
flags.DecodeCompressedBuffer = 1;
flags.VideoProcessRenderTarget = 1;
flags.Overlay = 1;
flags.TextApi = 1;
return flags;
}
void heapFree(void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}
}
namespace D3dDdi
{
Resource::Data::Data(const D3DDDIARG_CREATERESOURCE2& data)
: D3DDDIARG_CREATERESOURCE2(data)
{
surfaceData.reserve(data.SurfCount);
for (UINT i = 0; i < data.SurfCount; ++i)
{
surfaceData.push_back(data.pSurfList[i]);
}
pSurfList = surfaceData.data();
}
Resource::Resource(Device& device, D3DDDIARG_CREATERESOURCE2& data)
: m_device(device)
, m_handle(nullptr)
, m_origData(data)
, m_fixedData(data)
, m_lockBuffer(nullptr, &heapFree)
, m_lockResource(nullptr, ResourceDeleter(device, device.getOrigVtable().pfnDestroyResource))
, m_msaaSurface{}
, m_msaaResolvedSurface{}
, m_formatConfig(D3DDDIFMT_UNKNOWN)
, m_multiSampleConfig{ D3DDDIMULTISAMPLE_NONE, 0 }
, m_isSurfaceRepoResource(SurfaceRepository::inCreateSurface())
{
if (m_origData.Flags.VertexBuffer &&
m_origData.Flags.MightDrawFromLocked &&
D3DDDIPOOL_SYSTEMMEM != m_origData.Pool)
{
throw HResultException(E_FAIL);
}
if (m_origData.Flags.Primary)
{
g_presentationRect = calculatePresentationRect();
auto& si = m_origData.pSurfList[0];
g_primaryRect = { 0, 0, static_cast<LONG>(si.Width), static_cast<LONG>(si.Height) };
Gdi::Cursor::setMonitorClipRect(DDraw::PrimarySurface::getMonitorRect());
if (!EqualRect(&g_presentationRect, &g_primaryRect))
{
Gdi::Cursor::setEmulated(true);
}
Gdi::VirtualScreen::setFullscreenMode(true);
}
fixResourceData();
m_formatInfo = getFormatInfo(m_fixedData.Format);
m_formatConfig = m_fixedData.Format;
HRESULT result = m_device.createPrivateResource(m_fixedData);
if (FAILED(result))
{
throw HResultException(result);
}
m_handle = m_fixedData.hResource;
updateConfig();
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool &&
0 != m_formatInfo.bytesPerPixel)
{
m_lockData.resize(m_fixedData.SurfCount);
for (UINT i = 0; i < m_fixedData.SurfCount; ++i)
{
m_lockData[i].data = const_cast<void*>(m_fixedData.pSurfList[i].pSysMem);
m_lockData[i].pitch = m_fixedData.pSurfList[i].SysMemPitch;
m_lockData[i].isSysMemUpToDate = true;
}
}
createLockResource();
data.hResource = m_fixedData.hResource;
}
Resource::~Resource()
{
if (m_origData.Flags.Primary)
{
Gdi::VirtualScreen::setFullscreenMode(false);
Gdi::Cursor::setEmulated(false);
Gdi::Cursor::setMonitorClipRect({});
}
}
HRESULT Resource::blt(D3DDDIARG_BLT data)
{
if (!isValidRect(data.DstSubResourceIndex, data.DstRect))
{
return S_OK;
}
auto srcResource = m_device.getResource(data.hSrcResource);
if (srcResource)
{
if (!srcResource->isValidRect(data.SrcSubResourceIndex, data.SrcRect))
{
return S_OK;
}
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool &&
D3DDDIPOOL_SYSTEMMEM == srcResource->m_fixedData.Pool)
{
return m_device.getOrigVtable().pfnBlt(m_device, &data);
}
}
if (isOversized())
{
if (srcResource)
{
srcResource->prepareForBltSrc(data);
}
return splitBlt(data, data.DstSubResourceIndex, data.DstRect, data.SrcRect);
}
if (srcResource)
{
if (srcResource->isOversized())
{
if (m_lockResource)
{
loadVidMemResource(data.DstSubResourceIndex);
clearUpToDateFlags(data.DstSubResourceIndex);
m_lockData[data.DstSubResourceIndex].isVidMemUpToDate = true;
}
return srcResource->splitBlt(data, data.SrcSubResourceIndex, data.SrcRect, data.DstRect);
}
if (m_fixedData.Flags.Primary)
{
return presentationBlt(data, srcResource);
}
return sysMemPreferredBlt(data, *srcResource);
}
prepareForBltDst(data);
return m_device.getOrigVtable().pfnBlt(m_device, &data);
}
HRESULT Resource::bltLock(D3DDDIARG_LOCK& data)
{
LOG_FUNC("Resource::bltLock", data);
loadSysMemResource(data.SubResourceIndex);
auto& lockData = m_lockData[data.SubResourceIndex];
lockData.qpcLastForcedLock = Time::queryPerformanceCounter();
if (!data.Flags.ReadOnly)
{
clearUpToDateFlags(data.SubResourceIndex);
lockData.isSysMemUpToDate = true;
}
unsigned char* ptr = static_cast<unsigned char*>(lockData.data);
if (data.Flags.AreaValid)
{
ptr += data.Area.top * lockData.pitch + data.Area.left * m_formatInfo.bytesPerPixel;
}
data.pSurfData = ptr;
data.Pitch = lockData.pitch;
return LOG_RESULT(S_OK);
}
void Resource::clearUpToDateFlags(UINT subResourceIndex)
{
m_lockData[subResourceIndex].isMsaaUpToDate = false;
m_lockData[subResourceIndex].isMsaaResolvedUpToDate = false;
m_lockData[subResourceIndex].isVidMemUpToDate = false;
m_lockData[subResourceIndex].isSysMemUpToDate = false;
}
void Resource::clipRect(UINT subResourceIndex, RECT& rect)
{
rect.left = std::max<LONG>(rect.left, 0);
rect.top = std::max<LONG>(rect.top, 0);
rect.right = std::min<LONG>(rect.right, m_fixedData.pSurfList[subResourceIndex].Width);
rect.bottom = std::min<LONG>(rect.bottom, m_fixedData.pSurfList[subResourceIndex].Height);
}
HRESULT Resource::colorFill(D3DDDIARG_COLORFILL data)
{
LOG_FUNC("Resource::colorFill", data);
clipRect(data.SubResourceIndex, data.DstRect);
if (data.DstRect.left >= data.DstRect.right || data.DstRect.top >= data.DstRect.bottom)
{
return S_OK;
}
if (m_lockResource)
{
auto& lockData = m_lockData[data.SubResourceIndex];
if (lockData.isVidMemUpToDate)
{
m_device.getOrigVtable().pfnColorFill(m_device, &data);
}
if (lockData.isMsaaUpToDate)
{
data.hResource = *m_msaaSurface.resource;
m_device.getOrigVtable().pfnColorFill(m_device, &data);
}
if (lockData.isMsaaResolvedUpToDate)
{
data.hResource = *m_msaaResolvedSurface.resource;
m_device.getOrigVtable().pfnColorFill(m_device, &data);
}
if (lockData.isSysMemUpToDate)
{
auto dstBuf = static_cast<BYTE*>(lockData.data) +
data.DstRect.top * lockData.pitch + data.DstRect.left * m_formatInfo.bytesPerPixel;
DDraw::Blitter::colorFill(dstBuf, lockData.pitch,
data.DstRect.right - data.DstRect.left, data.DstRect.bottom - data.DstRect.top,
m_formatInfo.bytesPerPixel, colorConvert(m_formatInfo, data.Color));
}
return LOG_RESULT(S_OK);
}
return LOG_RESULT(m_device.getOrigVtable().pfnColorFill(m_device, &data));
}
HRESULT Resource::copySubResource(HANDLE dstResource, HANDLE srcResource, UINT subResourceIndex)
{
LOG_FUNC("Resource::copySubResource", dstResource, srcResource, subResourceIndex);
RECT rect = {};
rect.right = m_fixedData.pSurfList[subResourceIndex].Width;
rect.bottom = m_fixedData.pSurfList[subResourceIndex].Height;
D3DDDIARG_BLT data = {};
data.hSrcResource = srcResource;
data.SrcSubResourceIndex = subResourceIndex;
data.SrcRect = rect;
data.hDstResource = dstResource;
data.DstSubResourceIndex = subResourceIndex;
data.DstRect = rect;
HRESULT result = m_device.getOrigVtable().pfnBlt(m_device, &data);
if (FAILED(result))
{
LOG_ONCE("ERROR: Resource::copySubResource failed: " << Compat::hex(result));
}
return LOG_RESULT(result);
}
void Resource::createGdiLockResource()
{
auto gdiSurfaceDesc(Gdi::VirtualScreen::getSurfaceDesc(DDraw::PrimarySurface::getMonitorRect()));
if (!gdiSurfaceDesc.lpSurface)
{
return;
}
D3DDDI_SURFACEINFO surfaceInfo = {};
surfaceInfo.Width = gdiSurfaceDesc.dwWidth;
surfaceInfo.Height = gdiSurfaceDesc.dwHeight;
surfaceInfo.pSysMem = gdiSurfaceDesc.lpSurface;
surfaceInfo.SysMemPitch = gdiSurfaceDesc.lPitch;
m_lockData.resize(m_fixedData.SurfCount);
createSysMemResource({ surfaceInfo });
if (m_lockResource)
{
clearUpToDateFlags(0);
m_lockData[0].isSysMemUpToDate = true;
}
else
{
m_lockData.clear();
}
}
void Resource::createLockResource()
{
D3DDDI_RESOURCEFLAGS flags = {};
flags.Value = g_resourceTypeFlags;
flags.RenderTarget = 0;
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool ||
0 == m_formatInfo.bytesPerPixel ||
0 != (m_fixedData.Flags.Value & flags.Value))
{
return;
}
std::vector<D3DDDI_SURFACEINFO> surfaceInfo(m_fixedData.SurfCount);
for (UINT i = 0; i < m_fixedData.SurfCount; ++i)
{
surfaceInfo[i].Width = m_fixedData.pSurfList[i].Width;
surfaceInfo[i].Height = m_fixedData.pSurfList[i].Height;
surfaceInfo[i].SysMemPitch = (surfaceInfo[i].Width * m_formatInfo.bytesPerPixel + 3) & ~3;
if (i != 0)
{
std::uintptr_t offset = reinterpret_cast<std::uintptr_t>(surfaceInfo[i - 1].pSysMem) +
((surfaceInfo[i - 1].SysMemPitch * surfaceInfo[i - 1].Height + 15) & ~15);
surfaceInfo[i].pSysMem = reinterpret_cast<void*>(offset);
}
}
std::uintptr_t bufferSize = reinterpret_cast<std::uintptr_t>(surfaceInfo.back().pSysMem) +
surfaceInfo.back().SysMemPitch * surfaceInfo.back().Height + 8;
m_lockBuffer.reset(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufferSize));
BYTE* bufferStart = static_cast<BYTE*>(m_lockBuffer.get());
if (0 == reinterpret_cast<std::uintptr_t>(bufferStart) % 16)
{
bufferStart += 8;
}
for (UINT i = 0; i < m_fixedData.SurfCount; ++i)
{
surfaceInfo[i].pSysMem = bufferStart + reinterpret_cast<uintptr_t>(surfaceInfo[i].pSysMem);
}
createSysMemResource(surfaceInfo);
if (!m_lockResource)
{
m_lockBuffer.reset();
m_lockData.clear();
}
}
void Resource::createSysMemResource(const std::vector<D3DDDI_SURFACEINFO>& surfaceInfo)
{
LOG_FUNC("Resource::createSysMemResource", Compat::array(surfaceInfo.data(), surfaceInfo.size()));
D3DDDIARG_CREATERESOURCE2 data = {};
data.Format = m_fixedData.Format;
data.Pool = D3DDDIPOOL_SYSTEMMEM;
data.pSurfList = surfaceInfo.data();
data.SurfCount = surfaceInfo.size();
data.Rotation = D3DDDI_ROTATION_IDENTITY;
HRESULT result = m_device.createPrivateResource(data);
if (SUCCEEDED(result))
{
m_lockResource.reset(data.hResource);
m_lockData.resize(surfaceInfo.size());
auto qpcLastForcedLock = Time::queryPerformanceCounter() - Time::msToQpc(Config::evictionTimeout);
for (std::size_t i = 0; i < surfaceInfo.size(); ++i)
{
m_lockData[i].data = const_cast<void*>(surfaceInfo[i].pSysMem);
m_lockData[i].pitch = surfaceInfo[i].SysMemPitch;
m_lockData[i].qpcLastForcedLock = qpcLastForcedLock;
m_lockData[i].isSysMemUpToDate = true;
m_lockData[i].isVidMemUpToDate = true;
m_lockData[i].isMsaaUpToDate = m_msaaSurface.resource;
m_lockData[i].isMsaaResolvedUpToDate = m_msaaResolvedSurface.resource;
}
}
#ifdef DEBUGLOGS
LOG_RESULT(m_lockResource.get());
#endif
}
void Resource::fixResourceData()
{
if (m_fixedData.Flags.Primary)
{
RECT r = DDraw::RealPrimarySurface::getMonitorRect();
if (!IsRectEmpty(&r))
{
for (auto& surface : m_fixedData.surfaceData)
{
surface.Width = r.right - r.left;
surface.Height = r.bottom - r.top;
}
}
m_fixedData.Format = D3DDDIFMT_X8R8G8B8;
}
if (D3DDDIFMT_UNKNOWN != g_formatOverride)
{
m_fixedData.Format = g_formatOverride;
}
if (D3DDDIMULTISAMPLE_NONE != g_msaaOverride.first)
{
m_fixedData.MultisampleType = g_msaaOverride.first;
m_fixedData.MultisampleQuality = g_msaaOverride.second;
}
const bool isOffScreenPlain = 0 == (m_fixedData.Flags.Value & g_resourceTypeFlags);
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool &&
(isOffScreenPlain || m_fixedData.Flags.Texture) &&
1 == m_fixedData.SurfCount &&
0 == m_fixedData.pSurfList[0].Depth &&
0 != D3dDdi::getFormatInfo(m_fixedData.Format).bytesPerPixel)
{
const auto& caps = m_device.getAdapter().getInfo().d3dExtendedCaps;
const auto& surfaceInfo = m_fixedData.pSurfList[0];
if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth ||
0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight)
{
splitToTiles(caps.dwMaxTextureWidth, caps.dwMaxTextureHeight);
}
}
}
void* Resource::getLockPtr(UINT subResourceIndex)
{
return m_lockData.empty() ? nullptr : m_lockData[subResourceIndex].data;
}
D3DDDIFORMAT Resource::getFormatConfig()
{
if (m_fixedData.Flags.RenderTarget && !m_fixedData.Flags.Primary && D3DDDIPOOL_SYSTEMMEM != m_fixedData.Pool &&
(D3DDDIFMT_X8R8G8B8 == m_fixedData.Format || D3DDDIFMT_R5G6B5 == m_fixedData.Format))
{
switch (Config::renderColorDepth.get())
{
case 16: return D3DDDIFMT_R5G6B5;
case 32: return D3DDDIFMT_X8R8G8B8;
}
}
return m_fixedData.Format;
}
std::pair<D3DDDIMULTISAMPLE_TYPE, UINT> Resource::getMultisampleConfig()
{
if ((m_fixedData.Flags.RenderTarget && !m_fixedData.Flags.Texture && !m_fixedData.Flags.Primary ||
m_fixedData.Flags.ZBuffer) &&
D3DDDIPOOL_SYSTEMMEM != m_fixedData.Pool)
{
return m_device.getAdapter().getMultisampleConfig(m_fixedData.Format);
}
return { D3DDDIMULTISAMPLE_NONE, 0 };
}
bool Resource::isOversized() const
{
return m_fixedData.SurfCount != m_origData.SurfCount;
}
bool Resource::isValidRect(UINT subResourceIndex, const RECT& rect)
{
return rect.left >= 0 && rect.top >= 0 && rect.left < rect.right && rect.top < rect.bottom &&
rect.right <= static_cast<LONG>(m_fixedData.pSurfList[subResourceIndex].Width) &&
rect.bottom <= static_cast<LONG>(m_fixedData.pSurfList[subResourceIndex].Height);
}
void Resource::loadMsaaResource(UINT subResourceIndex)
{
if (!m_lockData[subResourceIndex].isMsaaUpToDate)
{
if (m_msaaResolvedSurface.resource)
{
loadMsaaResolvedResource(subResourceIndex);
copySubResource(*m_msaaSurface.resource, *m_msaaResolvedSurface.resource, subResourceIndex);
}
else
{
loadVidMemResource(subResourceIndex);
copySubResource(*m_msaaSurface.resource, m_handle, subResourceIndex);
}
m_lockData[subResourceIndex].isMsaaUpToDate = true;
}
}
void Resource::loadMsaaResolvedResource(UINT subResourceIndex)
{
if (!m_lockData[subResourceIndex].isMsaaResolvedUpToDate)
{
if (m_lockData[subResourceIndex].isMsaaUpToDate)
{
copySubResource(*m_msaaResolvedSurface.resource, *m_msaaSurface.resource, subResourceIndex);
}
else
{
loadVidMemResource(subResourceIndex);
copySubResource(*m_msaaResolvedSurface.resource, m_handle, subResourceIndex);
}
m_lockData[subResourceIndex].isMsaaResolvedUpToDate = true;
}
}
void Resource::loadSysMemResource(UINT subResourceIndex)
{
if (!m_lockData[subResourceIndex].isSysMemUpToDate)
{
loadVidMemResource(subResourceIndex);
copySubResource(m_lockResource.get(), m_handle, subResourceIndex);
notifyLock(subResourceIndex);
m_lockData[subResourceIndex].isSysMemUpToDate = true;
}
}
void Resource::loadVidMemResource(UINT subResourceIndex)
{
if (!m_lockData[subResourceIndex].isVidMemUpToDate)
{
if (m_lockData[subResourceIndex].isMsaaUpToDate || m_lockData[subResourceIndex].isMsaaResolvedUpToDate)
{
if (m_msaaResolvedSurface.resource)
{
loadMsaaResolvedResource(subResourceIndex);
copySubResource(m_handle, *m_msaaResolvedSurface.resource, subResourceIndex);
}
else
{
copySubResource(m_handle, *m_msaaSurface.resource, subResourceIndex);
}
}
else
{
copySubResource(m_handle, m_lockResource.get(), subResourceIndex);
notifyLock(subResourceIndex);
}
m_lockData[subResourceIndex].isVidMemUpToDate = true;
}
}
HRESULT Resource::lock(D3DDDIARG_LOCK& data)
{
if (isOversized())
{
if (0 != data.SubResourceIndex ||
data.Flags.RangeValid || data.Flags.AreaValid || data.Flags.BoxValid)
{
LOG_ONCE("WARNING: Unsupported lock of oversized resource: " << data);
return m_device.getOrigVtable().pfnLock(m_device, &data);
}
return splitLock(data, m_device.getOrigVtable().pfnLock);
}
if (m_lockResource)
{
return bltLock(data);
}
return m_device.getOrigVtable().pfnLock(m_device, &data);
}
void Resource::notifyLock(UINT subResourceIndex)
{
D3DDDIARG_LOCK lock = {};
lock.hResource = D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool ? m_handle : m_lockResource.get();
lock.SubResourceIndex = subResourceIndex;
lock.Flags.NotifyOnly = 1;
m_device.getOrigVtable().pfnLock(m_device, &lock);
D3DDDIARG_UNLOCK unlock = {};
unlock.hResource = lock.hResource;
unlock.SubResourceIndex = lock.SubResourceIndex;
unlock.Flags.NotifyOnly = 1;
m_device.getOrigVtable().pfnUnlock(m_device, &unlock);
}
void Resource::prepareForBltSrc(const D3DDDIARG_BLT& data)
{
if (m_lockResource)
{
loadVidMemResource(data.SrcSubResourceIndex);
}
}
void Resource::prepareForBltDst(D3DDDIARG_BLT& data)
{
if (m_lockResource)
{
if (m_lockData[data.DstSubResourceIndex].isMsaaUpToDate)
{
data.hDstResource = *m_msaaSurface.resource;
clearUpToDateFlags(data.DstSubResourceIndex);
m_lockData[data.DstSubResourceIndex].isMsaaUpToDate = true;
}
else if (m_lockData[data.DstSubResourceIndex].isMsaaResolvedUpToDate)
{
data.hDstResource = *m_msaaResolvedSurface.resource;
clearUpToDateFlags(data.DstSubResourceIndex);
m_lockData[data.DstSubResourceIndex].isMsaaResolvedUpToDate = true;
}
else
{
loadVidMemResource(data.DstSubResourceIndex);
clearUpToDateFlags(data.DstSubResourceIndex);
m_lockData[data.DstSubResourceIndex].isVidMemUpToDate = true;
}
}
}
void Resource::prepareForCpuRead(UINT subResourceIndex)
{
if (m_lockResource)
{
loadSysMemResource(subResourceIndex);
}
}
void Resource::prepareForCpuWrite(UINT subResourceIndex)
{
if (m_lockResource)
{
loadSysMemResource(subResourceIndex);
clearUpToDateFlags(subResourceIndex);
m_lockData[subResourceIndex].isSysMemUpToDate = true;
}
}
Resource& Resource::prepareForGpuRead(UINT subResourceIndex)
{
if (m_lockResource)
{
if (m_msaaResolvedSurface.resource)
{
loadMsaaResolvedResource(subResourceIndex);
return *m_msaaResolvedSurface.resource;
}
else
{
loadVidMemResource(subResourceIndex);
}
}
return *this;
}
void Resource::prepareForGpuWrite(UINT subResourceIndex)
{
if (m_lockResource)
{
if (m_msaaSurface.resource)
{
loadMsaaResource(subResourceIndex);
clearUpToDateFlags(subResourceIndex);
m_lockData[subResourceIndex].isMsaaUpToDate = true;
}
else if (m_msaaResolvedSurface.resource)
{
loadMsaaResolvedResource(subResourceIndex);
clearUpToDateFlags(subResourceIndex);
m_lockData[subResourceIndex].isMsaaResolvedUpToDate = true;
}
else
{
loadVidMemResource(subResourceIndex);
clearUpToDateFlags(subResourceIndex);
m_lockData[subResourceIndex].isVidMemUpToDate = true;
}
}
}
HRESULT Resource::presentationBlt(D3DDDIARG_BLT data, Resource* srcResource)
{
if (srcResource->m_lockResource)
{
if (srcResource->m_lockData[0].isSysMemUpToDate &&
!srcResource->m_fixedData.Flags.RenderTarget)
{
srcResource->m_lockData[0].isVidMemUpToDate = false;
}
data.hSrcResource = srcResource->prepareForGpuRead(0);
}
const bool isPalettized = D3DDDIFMT_P8 == srcResource->m_origData.Format;
const auto cursorInfo = Gdi::Cursor::getEmulatedCursorInfo();
const bool isCursorEmulated = cursorInfo.flags == CURSOR_SHOWING && cursorInfo.hCursor;
const RECT monitorRect = DDraw::PrimarySurface::getMonitorRect();
const bool isLayeredPresentNeeded = Gdi::Window::presentLayered(nullptr, monitorRect);
UINT presentationFilter = Config::displayFilter.get();
UINT presentationFilterParam = Config::displayFilter.getParam();
if (Config::Settings::DisplayFilter::BILINEAR == presentationFilter &&
(g_presentationRect.right - g_presentationRect.left == g_primaryRect.right &&
g_presentationRect.bottom - g_presentationRect.top == g_primaryRect.bottom) ||
(0 == presentationFilterParam &&
0 == (g_presentationRect.right - g_presentationRect.left) % g_primaryRect.right &&
0 == (g_presentationRect.bottom - g_presentationRect.top) % g_primaryRect.bottom))
{
presentationFilter = Config::Settings::DisplayFilter::POINT;
}
if (isPalettized || isCursorEmulated || isLayeredPresentNeeded ||
Config::Settings::DisplayFilter::POINT != presentationFilter)
{
const auto& si = srcResource->m_fixedData.pSurfList[0];
const auto& dst(SurfaceRepository::get(m_device.getAdapter()).getRenderTarget(si.Width, si.Height));
if (!dst.resource)
{
return E_OUTOFMEMORY;
}
if (isPalettized)
{
auto entries(Gdi::Palette::getHardwarePalette());
RGBQUAD pal[256] = {};
for (UINT i = 0; i < 256; ++i)
{
pal[i].rgbRed = entries[i].peRed;
pal[i].rgbGreen = entries[i].peGreen;
pal[i].rgbBlue = entries[i].peBlue;
}
m_device.getShaderBlitter().palettizedBlt(*dst.resource, 0, *srcResource, pal);
}
else
{
D3DDDIARG_BLT blt = {};
blt.hSrcResource = data.hSrcResource;
blt.SrcRect = data.SrcRect;
blt.hDstResource = *dst.resource;
blt.DstRect = data.SrcRect;
blt.Flags.Point = 1;
m_device.getOrigVtable().pfnBlt(m_device, &blt);
}
if (isLayeredPresentNeeded)
{
Gdi::Window::presentLayered(dst.surface, monitorRect);
}
if (isCursorEmulated)
{
POINT pos = { cursorInfo.ptScreenPos.x - monitorRect.left, cursorInfo.ptScreenPos.y - monitorRect.top };
m_device.getShaderBlitter().cursorBlt(*dst.resource, 0, cursorInfo.hCursor, pos);
}
if (Config::Settings::DisplayFilter::BILINEAR == presentationFilter)
{
m_device.getShaderBlitter().genBilinearBlt(*this, data.DstSubResourceIndex, g_presentationRect,
*dst.resource, data.SrcRect, presentationFilterParam);
return S_OK;
}
data.hSrcResource = *dst.resource;
}
data.DstRect = g_presentationRect;
data.Flags.Linear = 0;
data.Flags.Point = 1;
return m_device.getOrigVtable().pfnBlt(m_device, &data);
}
void Resource::setAsGdiResource(bool isGdiResource)
{
m_lockResource.reset();
m_lockData.clear();
m_lockBuffer.reset();
if (isGdiResource)
{
createGdiLockResource();
}
else
{
createLockResource();
}
}
HRESULT Resource::splitBlt(D3DDDIARG_BLT& data, UINT& subResourceIndex, RECT& rect, RECT& otherRect)
{
LOG_FUNC("Resource::splitBlt", data, subResourceIndex, rect, otherRect);
if (0 != subResourceIndex ||
data.SrcRect.right - data.SrcRect.left != data.DstRect.right - data.DstRect.left ||
data.SrcRect.bottom - data.SrcRect.top != data.DstRect.bottom - data.DstRect.top ||
data.Flags.MirrorLeftRight ||
data.Flags.MirrorUpDown ||
data.Flags.Rotate)
{
LOG_ONCE("WARNING: Unsupported blt of oversized resource: " << data);
return LOG_RESULT(m_device.getOrigVtable().pfnBlt(m_device, &data));
}
const auto& caps = m_device.getAdapter().getInfo().d3dExtendedCaps;
const auto tilesPerRow = divCeil(m_origData.pSurfList[0].Width, caps.dwMaxTextureWidth);
const RECT origRect = rect;
const POINT otherRectOffset = { otherRect.left - rect.left, otherRect.top - rect.top };
POINT tilePos = {};
tilePos.y = rect.top / static_cast<LONG>(caps.dwMaxTextureHeight);
RECT tileRect = {};
tileRect.top = tilePos.y * caps.dwMaxTextureHeight;
tileRect.bottom = tileRect.top + caps.dwMaxTextureHeight;
while (tileRect.top < origRect.bottom)
{
tilePos.x = origRect.left / static_cast<LONG>(caps.dwMaxTextureWidth);
tileRect.left = tilePos.x * caps.dwMaxTextureWidth;
tileRect.right = tileRect.left + caps.dwMaxTextureWidth;
while (tileRect.left < origRect.right)
{
IntersectRect(&rect, &tileRect, &origRect);
otherRect = rect;
OffsetRect(&otherRect, otherRectOffset.x, otherRectOffset.y);
OffsetRect(&rect, -tileRect.left, -tileRect.top);
subResourceIndex = tilePos.y * tilesPerRow + tilePos.x;
HRESULT result = m_device.getOrigVtable().pfnBlt(m_device, &data);
if (FAILED(result))
{
return LOG_RESULT(result);
}
++tilePos.x;
tileRect.left += caps.dwMaxTextureWidth;
tileRect.right += caps.dwMaxTextureWidth;
notifyLock(subResourceIndex);
}
++tilePos.y;
tileRect.top += caps.dwMaxTextureHeight;
tileRect.bottom += caps.dwMaxTextureHeight;
}
return LOG_RESULT(S_OK);
}
void Resource::splitToTiles(UINT tileWidth, UINT tileHeight)
{
std::vector<D3DDDI_SURFACEINFO> tiles;
const UINT bytesPerPixel = getFormatInfo(m_fixedData.Format).bytesPerPixel;
for (UINT y = 0; y < m_fixedData.pSurfList[0].Height; y += tileHeight)
{
for (UINT x = 0; x < m_fixedData.pSurfList[0].Width; x += tileWidth)
{
D3DDDI_SURFACEINFO tile = {};
tile.Width = min(m_fixedData.pSurfList[0].Width - x, tileWidth);
tile.Height = min(m_fixedData.pSurfList[0].Height - y, tileHeight);
tile.pSysMem = static_cast<const unsigned char*>(m_fixedData.pSurfList[0].pSysMem) +
y * m_fixedData.pSurfList[0].SysMemPitch + x * bytesPerPixel;
tile.SysMemPitch = m_fixedData.pSurfList[0].SysMemPitch;
tiles.push_back(tile);
}
}
m_fixedData.surfaceData = tiles;
m_fixedData.SurfCount = m_fixedData.surfaceData.size();
m_fixedData.pSurfList = m_fixedData.surfaceData.data();
m_fixedData.Flags.Texture = 0;
}
HRESULT Resource::sysMemPreferredBlt(D3DDDIARG_BLT& data, Resource& srcResource)
{
if (m_fixedData.Format == srcResource.m_fixedData.Format &&
!m_lockData.empty() &&
!srcResource.m_lockData.empty())
{
auto& dstLockData = m_lockData[data.DstSubResourceIndex];
auto& srcLockData = srcResource.m_lockData[data.SrcSubResourceIndex];
bool isSysMemBltPreferred = true;
auto now = Time::queryPerformanceCounter();
if (D3DDDIFMT_P8 != m_fixedData.Format)
{
if (data.Flags.MirrorLeftRight || data.Flags.MirrorUpDown ||
(data.Flags.SrcColorKey && !m_device.getAdapter().isSrcColorKeySupported()))
{
dstLockData.qpcLastForcedLock = now;
srcLockData.qpcLastForcedLock = now;
}
else
{
isSysMemBltPreferred = dstLockData.isSysMemUpToDate &&
Time::qpcToMs(now - dstLockData.qpcLastForcedLock) <= Config::evictionTimeout;
}
}
if (isSysMemBltPreferred)
{
prepareForCpuWrite(data.DstSubResourceIndex);
srcResource.prepareForCpuRead(data.SrcSubResourceIndex);
auto dstBuf = static_cast<BYTE*>(dstLockData.data) +
data.DstRect.top * dstLockData.pitch + data.DstRect.left * m_formatInfo.bytesPerPixel;
auto srcBuf = static_cast<const BYTE*>(srcLockData.data) +
data.SrcRect.top * srcLockData.pitch + data.SrcRect.left * m_formatInfo.bytesPerPixel;
DDraw::Blitter::blt(
dstBuf,
dstLockData.pitch,
data.DstRect.right - data.DstRect.left,
data.DstRect.bottom - data.DstRect.top,
srcBuf,
srcLockData.pitch,
(1 - 2 * data.Flags.MirrorLeftRight) * (data.SrcRect.right - data.SrcRect.left),
(1 - 2 * data.Flags.MirrorUpDown) * (data.SrcRect.bottom - data.SrcRect.top),
m_formatInfo.bytesPerPixel,
data.Flags.DstColorKey ? reinterpret_cast<const DWORD*>(&data.ColorKey) : nullptr,
data.Flags.SrcColorKey ? reinterpret_cast<const DWORD*>(&data.ColorKey) : nullptr);
notifyLock(data.DstSubResourceIndex);
return S_OK;
}
}
srcResource.prepareForBltSrc(data);
prepareForBltDst(data);
HRESULT result = m_device.getOrigVtable().pfnBlt(m_device, &data);
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool)
{
notifyLock(data.DstSubResourceIndex);
}
else if (D3DDDIPOOL_SYSTEMMEM == srcResource.m_fixedData.Pool)
{
srcResource.notifyLock(data.SrcSubResourceIndex);
}
return result;
}
template <typename Arg>
HRESULT Resource::splitLock(Arg& data, HRESULT(APIENTRY *lockFunc)(HANDLE, Arg*))
{
LOG_FUNC("Resource::splitLock", data, lockFunc);
std::remove_const<Arg>::type tmpData = data;
HRESULT result = lockFunc(m_device, &data);
if (SUCCEEDED(result))
{
for (UINT i = 1; i < m_fixedData.SurfCount; ++i)
{
tmpData.SubResourceIndex = i;
lockFunc(m_device, &tmpData);
}
}
return LOG_RESULT(result);
}
HRESULT Resource::unlock(const D3DDDIARG_UNLOCK& data)
{
if (isOversized())
{
if (0 != data.SubResourceIndex)
{
LOG_ONCE("WARNING: Unsupported unlock of oversized resource: " << data);
return m_device.getOrigVtable().pfnUnlock(m_device, &data);
}
return splitLock(data, m_device.getOrigVtable().pfnUnlock);
}
return m_device.getOrigVtable().pfnUnlock(m_device, &data);
}
void Resource::updateConfig()
{
if (m_isSurfaceRepoResource)
{
return;
}
const auto msaa = getMultisampleConfig();
const auto formatConfig = getFormatConfig();
if (m_multiSampleConfig == msaa && m_formatConfig == formatConfig)
{
return;
}
m_multiSampleConfig = msaa;
m_formatConfig = formatConfig;
if (m_fixedData.Flags.RenderTarget &&
(m_msaaSurface.resource || m_msaaResolvedSurface.resource))
{
for (UINT i = 0; i < m_lockData.size(); ++i)
{
if (m_lockData[i].isMsaaUpToDate || m_lockData[i].isMsaaResolvedUpToDate)
{
loadVidMemResource(i);
}
m_lockData[i].isMsaaUpToDate = false;
m_lockData[i].isMsaaResolvedUpToDate = false;
}
}
auto& surfaceRepo(SurfaceRepository::get(m_device.getAdapter()));
surfaceRepo.release(m_msaaSurface);
surfaceRepo.release(m_msaaResolvedSurface);
if (D3DDDIMULTISAMPLE_NONE != msaa.first || m_fixedData.Format != formatConfig)
{
g_formatOverride = formatConfig;
if (m_fixedData.Flags.ZBuffer)
{
DDPIXELFORMAT pf = {};
pf.dwSize = sizeof(pf);
pf.dwFlags = DDPF_ZBUFFER;
pf.dwZBufferBitDepth = 16;
pf.dwZBitMask = 0xFFFF;
g_msaaOverride = msaa;
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_msaaSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height, pf,
DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY, m_fixedData.SurfCount);
g_msaaOverride = {};
}
else
{
if (D3DDDIMULTISAMPLE_NONE != msaa.first)
{
g_msaaOverride = msaa;
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_msaaSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height,
DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY);
g_msaaOverride = {};
}
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_msaaResolvedSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height,
DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY);
}
g_formatOverride = D3DDDIFMT_UNKNOWN;
}
}
}