mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
403 lines
11 KiB
C++
403 lines
11 KiB
C++
#include <d3d.h>
|
|
#include <../km/d3dkmthk.h>
|
|
|
|
#include "D3dDdi/AdapterFuncs.h"
|
|
#include "D3dDdi/Device.h"
|
|
#include "D3dDdi/DeviceFuncs.h"
|
|
#include "D3dDdi/KernelModeThunks.h"
|
|
#include "Gdi/AccessGuard.h"
|
|
|
|
namespace
|
|
{
|
|
D3DDDI_RESOURCEFLAGS getResourceTypeFlags();
|
|
|
|
const UINT g_resourceTypeFlags = getResourceTypeFlags().Value;
|
|
HANDLE g_gdiResourceHandle = nullptr;
|
|
bool g_isReadOnlyGdiLockEnabled = false;
|
|
|
|
class RenderGuard : public Gdi::DDrawAccessGuard
|
|
{
|
|
public:
|
|
RenderGuard(D3dDdi::Device& device, Gdi::Access access)
|
|
: Gdi::DDrawAccessGuard(access)
|
|
, m_device(device)
|
|
{
|
|
device.prepareForRendering();
|
|
}
|
|
|
|
RenderGuard(D3dDdi::Device& device, Gdi::Access access, HANDLE resource, UINT subResourceIndex = UINT_MAX)
|
|
: Gdi::DDrawAccessGuard(access, g_gdiResourceHandle == resource)
|
|
, m_device(device)
|
|
{
|
|
device.prepareForRendering(resource, subResourceIndex);
|
|
}
|
|
|
|
private:
|
|
D3dDdi::Device & m_device;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
bool isVidMemPool(D3DDDI_POOL pool)
|
|
{
|
|
return D3DDDIPOOL_VIDEOMEMORY == pool ||
|
|
D3DDDIPOOL_LOCALVIDMEM == pool ||
|
|
D3DDDIPOOL_NONLOCALVIDMEM == pool;
|
|
}
|
|
}
|
|
|
|
namespace D3dDdi
|
|
{
|
|
UINT getBytesPerPixel(D3DDDIFORMAT format)
|
|
{
|
|
switch (format)
|
|
{
|
|
case D3DDDIFMT_A8:
|
|
case D3DDDIFMT_P8:
|
|
return 1;
|
|
|
|
case D3DDDIFMT_R5G6B5:
|
|
case D3DDDIFMT_X1R5G5B5:
|
|
case D3DDDIFMT_A1R5G5B5:
|
|
case D3DDDIFMT_A8P8:
|
|
return 2;
|
|
|
|
case D3DDDIFMT_R8G8B8:
|
|
return 3;
|
|
|
|
case D3DDDIFMT_A8R8G8B8:
|
|
case D3DDDIFMT_X8R8G8B8:
|
|
case D3DDDIFMT_A8B8G8R8:
|
|
case D3DDDIFMT_X8B8G8R8:
|
|
return 4;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Device::Device(HANDLE adapter, HANDLE device)
|
|
: m_origVtable(&DeviceFuncs::s_origVtables.at(device))
|
|
, m_adapter(adapter)
|
|
, m_device(device)
|
|
, m_sharedPrimary(nullptr)
|
|
{
|
|
}
|
|
|
|
HRESULT Device::blt(const D3DDDIARG_BLT& data)
|
|
{
|
|
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex);
|
|
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource, data.DstSubResourceIndex);
|
|
|
|
auto it = m_oversizedResources.find(data.hSrcResource);
|
|
if (it != m_oversizedResources.end())
|
|
{
|
|
return it->second.bltFrom(data);
|
|
}
|
|
|
|
it = m_oversizedResources.find(data.hDstResource);
|
|
if (it != m_oversizedResources.end())
|
|
{
|
|
return it->second.bltTo(data);
|
|
}
|
|
|
|
return m_origVtable->pfnBlt(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::clear(const D3DDDIARG_CLEAR& data, UINT numRect, const RECT* rect)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnClear(m_device, &data, numRect, rect);
|
|
}
|
|
|
|
HRESULT Device::colorFill(const D3DDDIARG_COLORFILL& data)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE, data.hResource, data.SubResourceIndex);
|
|
return m_origVtable->pfnColorFill(m_device, &data);
|
|
}
|
|
|
|
template <typename CreateResourceArg, typename CreateResourceFunc>
|
|
HRESULT Device::createOversizedResource(
|
|
CreateResourceArg& data,
|
|
CreateResourceFunc origCreateResource,
|
|
const D3DNTHAL_D3DEXTENDEDCAPS& caps)
|
|
{
|
|
D3DDDI_SURFACEINFO compatSurfaceInfo = data.pSurfList[0];
|
|
if (0 != caps.dwMaxTextureWidth && compatSurfaceInfo.Width > caps.dwMaxTextureWidth)
|
|
{
|
|
compatSurfaceInfo.Width = caps.dwMaxTextureWidth;
|
|
}
|
|
if (0 != caps.dwMaxTextureHeight && compatSurfaceInfo.Height > caps.dwMaxTextureHeight)
|
|
{
|
|
compatSurfaceInfo.Height = caps.dwMaxTextureHeight;
|
|
}
|
|
|
|
const D3DDDI_SURFACEINFO* origSurfList = data.pSurfList;
|
|
data.pSurfList = &compatSurfaceInfo;
|
|
HRESULT result = origCreateResource(m_device, &data);
|
|
data.pSurfList = origSurfList;
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
m_oversizedResources.emplace(data.hResource,
|
|
OversizedResource(*m_origVtable, m_adapter, m_device, data.Format, origSurfList[0]));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename CreateResourceArg, typename CreateResourceFunc>
|
|
HRESULT Device::createResourceImpl(CreateResourceArg& data, CreateResourceFunc origCreateResource)
|
|
{
|
|
const bool isOffScreenPlain = 0 == (data.Flags.Value & g_resourceTypeFlags);
|
|
if (D3DDDIPOOL_SYSTEMMEM == data.Pool &&
|
|
(isOffScreenPlain || data.Flags.Texture) &&
|
|
OversizedResource::isSupportedFormat(data.Format) &&
|
|
1 == data.SurfCount &&
|
|
m_adapter)
|
|
{
|
|
const auto& caps = AdapterFuncs::getD3dExtendedCaps(m_adapter);
|
|
const auto& surfaceInfo = data.pSurfList[0];
|
|
if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth ||
|
|
0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight)
|
|
{
|
|
return createOversizedResource(data, origCreateResource, caps);
|
|
}
|
|
}
|
|
|
|
HRESULT result = origCreateResource(m_device, &data);
|
|
if (SUCCEEDED(result) && data.Flags.RenderTarget && !data.Flags.Primary && isVidMemPool(data.Pool))
|
|
{
|
|
m_renderTargetResources.emplace(data.hResource,
|
|
RenderTargetResource(*m_origVtable, m_device, data.hResource, data.Format, data.SurfCount));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::createResource(D3DDDIARG_CREATERESOURCE& data)
|
|
{
|
|
return createResourceImpl(data, m_origVtable->pfnCreateResource);
|
|
}
|
|
|
|
HRESULT Device::createResource2(D3DDDIARG_CREATERESOURCE2& data)
|
|
{
|
|
return createResourceImpl(data, m_origVtable->pfnCreateResource2);
|
|
}
|
|
|
|
HRESULT Device::destroyResource(HANDLE resource)
|
|
{
|
|
if (resource == m_sharedPrimary)
|
|
{
|
|
D3DKMTReleaseProcessVidPnSourceOwners(GetCurrentProcess());
|
|
}
|
|
|
|
HRESULT result = m_origVtable->pfnDestroyResource(m_device, resource);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
m_oversizedResources.erase(resource);
|
|
m_renderTargetResources.erase(resource);
|
|
m_lockedRenderTargetResources.erase(resource);
|
|
|
|
if (resource == m_sharedPrimary)
|
|
{
|
|
m_sharedPrimary = nullptr;
|
|
}
|
|
if (resource == g_gdiResourceHandle)
|
|
{
|
|
g_gdiResourceHandle = nullptr;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::drawIndexedPrimitive(const D3DDDIARG_DRAWINDEXEDPRIMITIVE& data)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawIndexedPrimitive(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2& data,
|
|
UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawIndexedPrimitive2(m_device, &data, indicesSize, indexBuffer, flagBuffer);
|
|
}
|
|
|
|
HRESULT Device::drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawPrimitive(m_device, &data, flagBuffer);
|
|
}
|
|
|
|
HRESULT Device::drawPrimitive2(const D3DDDIARG_DRAWPRIMITIVE2& data)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawPrimitive2(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::drawRectPatch(const D3DDDIARG_DRAWRECTPATCH& data, const D3DDDIRECTPATCH_INFO* info,
|
|
const FLOAT* patch)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawRectPatch(m_device, &data, info, patch);
|
|
}
|
|
|
|
HRESULT Device::drawTriPatch(const D3DDDIARG_DRAWTRIPATCH& data, const D3DDDITRIPATCH_INFO* info,
|
|
const FLOAT* patch)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE);
|
|
return m_origVtable->pfnDrawTriPatch(m_device, &data, info, patch);
|
|
}
|
|
|
|
HRESULT Device::lock(D3DDDIARG_LOCK& data)
|
|
{
|
|
Gdi::DDrawAccessGuard accessGuard(
|
|
(data.Flags.ReadOnly || g_isReadOnlyGdiLockEnabled) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE,
|
|
data.hResource == g_gdiResourceHandle);
|
|
|
|
auto it = m_renderTargetResources.find(data.hResource);
|
|
if (it != m_renderTargetResources.end())
|
|
{
|
|
HRESULT result = it->second.lock(data);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
m_lockedRenderTargetResources.emplace(it->first, it->second);
|
|
}
|
|
return result;
|
|
}
|
|
return m_origVtable->pfnLock(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::openResource(D3DDDIARG_OPENRESOURCE& data)
|
|
{
|
|
HRESULT result = m_origVtable->pfnOpenResource(m_device, &data);
|
|
if (SUCCEEDED(result) && data.Flags.Fullscreen)
|
|
{
|
|
m_sharedPrimary = data.hResource;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::present(const D3DDDIARG_PRESENT& data)
|
|
{
|
|
RenderGuard renderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex);
|
|
return m_origVtable->pfnPresent(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::present1(D3DDDIARG_PRESENT1& data)
|
|
{
|
|
bool isGdiResourceInvolved = false;
|
|
for (UINT i = 0; i < data.SrcResources && !isGdiResourceInvolved; ++i)
|
|
{
|
|
isGdiResourceInvolved = data.phSrcResources[i].hResource == g_gdiResourceHandle;
|
|
}
|
|
|
|
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, isGdiResourceInvolved);
|
|
|
|
for (UINT i = 0; i < data.SrcResources; ++i)
|
|
{
|
|
prepareForRendering(data.phSrcResources[i].hResource, data.phSrcResources[i].SubResourceIndex);
|
|
}
|
|
|
|
return m_origVtable->pfnPresent1(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::texBlt(const D3DDDIARG_TEXBLT& data)
|
|
{
|
|
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource);
|
|
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource);
|
|
return m_origVtable->pfnTexBlt(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::texBlt1(const D3DDDIARG_TEXBLT1& data)
|
|
{
|
|
RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource);
|
|
RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource);
|
|
return m_origVtable->pfnTexBlt1(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::unlock(const D3DDDIARG_UNLOCK& data)
|
|
{
|
|
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, data.hResource == g_gdiResourceHandle);
|
|
auto it = m_renderTargetResources.find(data.hResource);
|
|
if (it != m_renderTargetResources.end())
|
|
{
|
|
return it->second.unlock(data);
|
|
}
|
|
return m_origVtable->pfnUnlock(m_device, &data);
|
|
}
|
|
|
|
HRESULT Device::updateWInfo(const D3DDDIARG_WINFO& data)
|
|
{
|
|
if (1.0f == data.WNear && 1.0f == data.WFar)
|
|
{
|
|
D3DDDIARG_WINFO wInfo = {};
|
|
wInfo.WNear = 0.0f;
|
|
wInfo.WFar = 1.0f;
|
|
return m_origVtable->pfnUpdateWInfo(m_device, &wInfo);
|
|
}
|
|
return m_origVtable->pfnUpdateWInfo(m_device, &data);
|
|
}
|
|
|
|
void Device::prepareForRendering(RenderTargetResource& resource, UINT subResourceIndex)
|
|
{
|
|
resource.prepareForRendering(subResourceIndex);
|
|
if (!resource.hasLockedSubResources())
|
|
{
|
|
m_lockedRenderTargetResources.erase(resource.getHandle());
|
|
}
|
|
}
|
|
|
|
void Device::prepareForRendering(HANDLE resource, UINT subResourceIndex)
|
|
{
|
|
auto it = m_lockedRenderTargetResources.find(resource);
|
|
if (it != m_lockedRenderTargetResources.end())
|
|
{
|
|
prepareForRendering(it->second, subResourceIndex);
|
|
}
|
|
}
|
|
|
|
void Device::prepareForRendering()
|
|
{
|
|
auto it = m_lockedRenderTargetResources.begin();
|
|
while (it != m_lockedRenderTargetResources.end())
|
|
{
|
|
prepareForRendering((it++)->second);
|
|
}
|
|
}
|
|
|
|
void Device::setGdiResourceHandle(HANDLE resource)
|
|
{
|
|
g_gdiResourceHandle = resource;
|
|
}
|
|
|
|
void Device::setReadOnlyGdiLock(bool enable)
|
|
{
|
|
g_isReadOnlyGdiLockEnabled = enable;
|
|
}
|
|
}
|