mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
429 lines
9.6 KiB
C++
429 lines
9.6 KiB
C++
#include <sstream>
|
|
|
|
#include <d3d.h>
|
|
#include <winternl.h>
|
|
#include <d3dkmthk.h>
|
|
|
|
#include <Common/CompatVtable.h>
|
|
#include <Common/HResultException.h>
|
|
#include <Common/Log.h>
|
|
#include <D3dDdi/Adapter.h>
|
|
#include <D3dDdi/Device.h>
|
|
#include <D3dDdi/DeviceFuncs.h>
|
|
#include <D3dDdi/Resource.h>
|
|
#include <D3dDdi/ScopedCriticalSection.h>
|
|
#include <DDraw/ScopedThreadLock.h>
|
|
|
|
namespace
|
|
{
|
|
HANDLE g_gdiResourceHandle = nullptr;
|
|
D3dDdi::Resource* g_gdiResource = nullptr;
|
|
bool g_isConfigUpdatePending = false;
|
|
}
|
|
|
|
namespace D3dDdi
|
|
{
|
|
Device::Device(Adapter& adapter, HANDLE device)
|
|
: m_origVtable(CompatVtable<D3DDDI_DEVICEFUNCS>::s_origVtable)
|
|
, m_adapter(adapter)
|
|
, m_device(device)
|
|
, m_eventQuery(nullptr)
|
|
, m_renderTarget(nullptr)
|
|
, m_renderTargetSubResourceIndex(0)
|
|
, m_sharedPrimary(nullptr)
|
|
, m_drawPrimitive(*this)
|
|
, m_state(*this)
|
|
, m_shaderBlitter(*this)
|
|
{
|
|
D3DDDIARG_CREATEQUERY createQuery = {};
|
|
createQuery.QueryType = D3DDDIQUERYTYPE_EVENT;
|
|
m_origVtable.pfnCreateQuery(m_device, &createQuery);
|
|
m_eventQuery = createQuery.hQuery;
|
|
}
|
|
|
|
void Device::add(Adapter& adapter, HANDLE device)
|
|
{
|
|
s_devices.try_emplace(device, adapter, device);
|
|
}
|
|
|
|
HRESULT Device::createPrivateResource(D3DDDIARG_CREATERESOURCE2& data)
|
|
{
|
|
const bool isPalettized = D3DDDIFMT_P8 == data.Format;
|
|
const bool isTexture = data.Flags.Texture;
|
|
if (isPalettized)
|
|
{
|
|
data.Format = D3DDDIFMT_L8;
|
|
data.Flags.Texture = 1;
|
|
}
|
|
|
|
HRESULT result = m_origVtable.pfnCreateResource2
|
|
? m_origVtable.pfnCreateResource2(m_device, &data)
|
|
: m_origVtable.pfnCreateResource(m_device, reinterpret_cast<D3DDDIARG_CREATERESOURCE*>(&data));
|
|
|
|
if (isPalettized)
|
|
{
|
|
data.Format = D3DDDIFMT_P8;
|
|
data.Flags.Texture = isTexture;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Device* Device::findDeviceByResource(HANDLE resource)
|
|
{
|
|
for (auto& device : s_devices)
|
|
{
|
|
if (device.second.m_resources.find(resource) != device.second.m_resources.end())
|
|
{
|
|
return &device.second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Resource* Device::findResource(HANDLE resource)
|
|
{
|
|
for (auto& device : s_devices)
|
|
{
|
|
auto res = device.second.getResource(resource);
|
|
if (res)
|
|
{
|
|
return res;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Resource* Device::getGdiResource()
|
|
{
|
|
return g_gdiResource;
|
|
}
|
|
|
|
Resource* Device::getResource(HANDLE resource)
|
|
{
|
|
auto it = m_resources.find(resource);
|
|
return it != m_resources.end() ? it->second.get() : nullptr;
|
|
}
|
|
|
|
void Device::prepareForGpuWrite()
|
|
{
|
|
if (m_renderTarget)
|
|
{
|
|
m_renderTarget->prepareForGpuWrite(m_renderTargetSubResourceIndex);
|
|
}
|
|
}
|
|
|
|
void Device::setGdiResourceHandle(HANDLE resource)
|
|
{
|
|
LOG_FUNC("Device::setGdiResourceHandle", resource);
|
|
ScopedCriticalSection lock;
|
|
if ((!resource && !g_gdiResource) ||
|
|
(g_gdiResource && resource == *g_gdiResource))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g_gdiResource)
|
|
{
|
|
g_gdiResource->setAsGdiResource(false);
|
|
}
|
|
|
|
g_gdiResourceHandle = resource;
|
|
g_gdiResource = findResource(resource);
|
|
|
|
if (g_gdiResource)
|
|
{
|
|
g_gdiResource->setAsGdiResource(true);
|
|
}
|
|
}
|
|
|
|
void Device::setRenderTarget(const D3DDDIARG_SETRENDERTARGET& data)
|
|
{
|
|
if (0 == data.RenderTargetIndex)
|
|
{
|
|
m_renderTarget = getResource(data.hRenderTarget);
|
|
m_renderTargetSubResourceIndex = data.SubResourceIndex;
|
|
}
|
|
}
|
|
|
|
HRESULT Device::pfnBlt(const D3DDDIARG_BLT* data)
|
|
{
|
|
flushPrimitives();
|
|
auto it = m_resources.find(data->hDstResource);
|
|
if (it != m_resources.end())
|
|
{
|
|
return it->second->blt(*data);
|
|
}
|
|
|
|
it = m_resources.find(data->hSrcResource);
|
|
if (it != m_resources.end())
|
|
{
|
|
it->second->prepareForBltSrc(*data);
|
|
}
|
|
return m_origVtable.pfnBlt(m_device, data);
|
|
}
|
|
|
|
HRESULT Device::pfnClear(const D3DDDIARG_CLEAR* data, UINT numRect, const RECT* rect)
|
|
{
|
|
flushPrimitives();
|
|
if (data->Flags & D3DCLEAR_TARGET)
|
|
{
|
|
setRenderTarget(m_state.getAppState().renderTarget);
|
|
prepareForGpuWrite();
|
|
}
|
|
m_state.flush();
|
|
|
|
if (m_renderTarget && rect)
|
|
{
|
|
std::vector<RECT> scaledRect(rect, rect + numRect);
|
|
for (UINT i = 0; i < numRect; ++i)
|
|
{
|
|
m_renderTarget->scaleRect(scaledRect[i]);
|
|
}
|
|
return m_origVtable.pfnClear(m_device, data, numRect, scaledRect.data());
|
|
}
|
|
|
|
return m_origVtable.pfnClear(m_device, data, numRect, rect);
|
|
}
|
|
|
|
HRESULT Device::pfnColorFill(const D3DDDIARG_COLORFILL* data)
|
|
{
|
|
auto it = m_resources.find(data->hResource);
|
|
if (it != m_resources.end())
|
|
{
|
|
return it->second->colorFill(*data);
|
|
}
|
|
return m_origVtable.pfnColorFill(m_device, data);
|
|
}
|
|
|
|
HRESULT Device::pfnCreateResource(D3DDDIARG_CREATERESOURCE* data)
|
|
{
|
|
D3DDDIARG_CREATERESOURCE2 data2 = {};
|
|
memcpy(&data2, data, sizeof(*data));
|
|
HRESULT result = pfnCreateResource2(&data2);
|
|
data->hResource = data2.hResource;
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::pfnCreateResource2(D3DDDIARG_CREATERESOURCE2* data)
|
|
{
|
|
try
|
|
{
|
|
auto resource(std::make_unique<Resource>(*this, *data));
|
|
m_resources.emplace(*resource, std::move(resource));
|
|
if (data->Flags.VertexBuffer &&
|
|
D3DDDIPOOL_SYSTEMMEM == data->Pool &&
|
|
data->pSurfList[0].pSysMem)
|
|
{
|
|
m_drawPrimitive.addSysMemVertexBuffer(data->hResource,
|
|
static_cast<BYTE*>(const_cast<void*>(data->pSurfList[0].pSysMem)));
|
|
}
|
|
return S_OK;
|
|
}
|
|
catch (const HResultException& e)
|
|
{
|
|
return e.getResult();
|
|
}
|
|
}
|
|
|
|
HRESULT Device::pfnDestroyDevice()
|
|
{
|
|
auto device = m_device;
|
|
auto pfnDestroyDevice = m_origVtable.pfnDestroyDevice;
|
|
s_devices.erase(device);
|
|
return pfnDestroyDevice(device);
|
|
}
|
|
|
|
HRESULT Device::pfnDestroyResource(HANDLE resource)
|
|
{
|
|
flushPrimitives();
|
|
if (g_gdiResource)
|
|
{
|
|
g_gdiResource->onDestroyResource(resource);
|
|
}
|
|
|
|
if (resource == m_sharedPrimary)
|
|
{
|
|
D3DKMTReleaseProcessVidPnSourceOwners(GetCurrentProcess());
|
|
}
|
|
|
|
HRESULT result = m_origVtable.pfnDestroyResource(m_device, resource);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
auto it = m_resources.find(resource);
|
|
Resource* res = nullptr;
|
|
if (it != m_resources.end())
|
|
{
|
|
res = it->second.get();
|
|
m_resources.erase(it);
|
|
}
|
|
if (resource == m_sharedPrimary)
|
|
{
|
|
m_sharedPrimary = nullptr;
|
|
}
|
|
if (resource == g_gdiResourceHandle)
|
|
{
|
|
g_gdiResourceHandle = nullptr;
|
|
g_gdiResource = nullptr;
|
|
}
|
|
m_drawPrimitive.removeSysMemVertexBuffer(resource);
|
|
m_state.onDestroyResource(res, resource);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::pfnDrawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2* data,
|
|
UINT /*indicesSize*/, const void* indexBuffer, const UINT* flagBuffer)
|
|
{
|
|
return m_drawPrimitive.drawIndexed(*data, static_cast<const UINT16*>(indexBuffer), flagBuffer);
|
|
}
|
|
|
|
HRESULT Device::pfnDrawPrimitive(const D3DDDIARG_DRAWPRIMITIVE* data, const UINT* flagBuffer)
|
|
{
|
|
return m_drawPrimitive.draw(*data, flagBuffer);
|
|
}
|
|
|
|
HRESULT Device::pfnFlush()
|
|
{
|
|
if (!s_isFlushEnabled)
|
|
{
|
|
return S_OK;
|
|
}
|
|
flushPrimitives();
|
|
return m_origVtable.pfnFlush(m_device);
|
|
}
|
|
|
|
HRESULT Device::pfnFlush1(UINT FlushFlags)
|
|
{
|
|
if (!s_isFlushEnabled && 0 == FlushFlags)
|
|
{
|
|
return S_OK;
|
|
}
|
|
flushPrimitives();
|
|
return m_origVtable.pfnFlush1(m_device, FlushFlags);
|
|
}
|
|
|
|
HRESULT Device::pfnLock(D3DDDIARG_LOCK* data)
|
|
{
|
|
flushPrimitives();
|
|
auto it = m_resources.find(data->hResource);
|
|
if (it != m_resources.end())
|
|
{
|
|
return it->second->lock(*data);
|
|
}
|
|
return m_origVtable.pfnLock(m_device, data);
|
|
}
|
|
|
|
HRESULT Device::pfnOpenResource(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::pfnPresent(const D3DDDIARG_PRESENT* data)
|
|
{
|
|
flushPrimitives();
|
|
auto d = *data;
|
|
auto resource = getResource(data->hSrcResource);
|
|
if (resource)
|
|
{
|
|
d.hSrcResource = resource->prepareForGpuRead(data->SrcSubResourceIndex);
|
|
}
|
|
|
|
HRESULT result = m_origVtable.pfnPresent(m_device, &d);
|
|
updateAllConfigNow();
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::pfnPresent1(D3DDDIARG_PRESENT1* data)
|
|
{
|
|
flushPrimitives();
|
|
std::vector<D3DDDIARG_PRESENTSURFACE> srcResources(data->phSrcResources, data->phSrcResources + data->SrcResources);
|
|
data->phSrcResources = srcResources.data();
|
|
for (UINT i = 0; i < data->SrcResources; ++i)
|
|
{
|
|
auto resource = getResource(srcResources[i].hResource);
|
|
if (resource)
|
|
{
|
|
srcResources[i].hResource = resource->prepareForGpuRead(srcResources[i].SubResourceIndex);
|
|
}
|
|
}
|
|
|
|
HRESULT result = m_origVtable.pfnPresent1(m_device, data);
|
|
updateAllConfigNow();
|
|
return result;
|
|
}
|
|
|
|
HRESULT Device::pfnUnlock(const D3DDDIARG_UNLOCK* data)
|
|
{
|
|
flushPrimitives();
|
|
auto it = m_resources.find(data->hResource);
|
|
if (it != m_resources.end())
|
|
{
|
|
return it->second->unlock(*data);
|
|
}
|
|
return m_origVtable.pfnUnlock(m_device, data);
|
|
}
|
|
|
|
void Device::updateAllConfig()
|
|
{
|
|
g_isConfigUpdatePending = true;
|
|
}
|
|
|
|
void Device::updateAllConfigNow()
|
|
{
|
|
if (g_isConfigUpdatePending)
|
|
{
|
|
g_isConfigUpdatePending = false;
|
|
for (auto& device : s_devices)
|
|
{
|
|
device.second.updateConfig();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Device::updateConfig()
|
|
{
|
|
for (auto& resource : m_resources)
|
|
{
|
|
resource.second->updateConfig();
|
|
}
|
|
m_state.updateConfig();
|
|
}
|
|
|
|
void Device::waitForIdle()
|
|
{
|
|
D3dDdi::ScopedCriticalSection lock;
|
|
flushPrimitives();
|
|
D3DDDIARG_ISSUEQUERY issueQuery = {};
|
|
issueQuery.hQuery = m_eventQuery;
|
|
issueQuery.Flags.End = 1;
|
|
m_origVtable.pfnIssueQuery(m_device, &issueQuery);
|
|
|
|
if (m_origVtable.pfnFlush1)
|
|
{
|
|
m_origVtable.pfnFlush1(m_device, 0);
|
|
}
|
|
else
|
|
{
|
|
m_origVtable.pfnFlush(m_device);
|
|
}
|
|
|
|
BOOL result = FALSE;
|
|
D3DDDIARG_GETQUERYDATA getQueryData = {};
|
|
getQueryData.hQuery = m_eventQuery;
|
|
getQueryData.pData = &result;
|
|
while (S_FALSE == m_origVtable.pfnGetQueryData(m_device, &getQueryData))
|
|
{
|
|
}
|
|
}
|
|
|
|
std::map<HANDLE, Device> Device::s_devices;
|
|
bool Device::s_isFlushEnabled = true;
|
|
}
|