mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-26 17:01:08 +01:00
387 lines
11 KiB
C++
387 lines
11 KiB
C++
/* Transposed matrix packet from January target. GPU payload writes only. */
|
|
#include <xmmintrin.h>
|
|
#include "push_internal.h"
|
|
#include "pusher_state.h"
|
|
#include "resource_internal.h"
|
|
#include "pixeljar.h"
|
|
#include "kernel_dispatcher.h"
|
|
#pragma code_seg("D3D")
|
|
namespace D3D
|
|
{
|
|
void WINAPI DumpMatrixTransposed(
|
|
DWORD *push,
|
|
DWORD method,
|
|
const D3DMATRIX *matrix)
|
|
{
|
|
push[0] = method;
|
|
memcpy(push + 1, &matrix->m[0][0], sizeof(float));
|
|
memcpy(push + 2, &matrix->m[1][0], sizeof(float));
|
|
memcpy(push + 3, &matrix->m[2][0], sizeof(float));
|
|
memcpy(push + 4, &matrix->m[3][0], sizeof(float));
|
|
memcpy(push + 5, &matrix->m[0][1], sizeof(float));
|
|
memcpy(push + 6, &matrix->m[1][1], sizeof(float));
|
|
memcpy(push + 7, &matrix->m[2][1], sizeof(float));
|
|
memcpy(push + 8, &matrix->m[3][1], sizeof(float));
|
|
memcpy(push + 9, &matrix->m[0][2], sizeof(float));
|
|
memcpy(push + 10, &matrix->m[1][2], sizeof(float));
|
|
memcpy(push + 11, &matrix->m[2][2], sizeof(float));
|
|
memcpy(push + 12, &matrix->m[3][2], sizeof(float));
|
|
memcpy(push + 13, &matrix->m[0][3], sizeof(float));
|
|
memcpy(push + 14, &matrix->m[1][3], sizeof(float));
|
|
memcpy(push + 15, &matrix->m[2][3], sizeof(float));
|
|
memcpy(push + 16, &matrix->m[3][3], sizeof(float));
|
|
return;
|
|
}
|
|
}
|
|
|
|
namespace D3D
|
|
{
|
|
/* Deliberate, short polling delay. The target performs these counter accesses
|
|
* and the reference BusyLoop documents avoidance of excessive HwGet polling. */
|
|
void WINAPI BusyLoop(
|
|
void)
|
|
{
|
|
volatile DWORD count = 400;
|
|
do { } while (--count != 0);
|
|
return;
|
|
}
|
|
void CDevice::InitializePushBuffer(
|
|
void)
|
|
{
|
|
m_PushSegmentSize = m_PushBufferSize / 16;
|
|
m_Pusher.m_pPut = m_pPushBase;
|
|
m_Pusher.m_pThreshold = m_pPushBase + m_PushSegmentSize / sizeof(DWORD) - 129;
|
|
m_CpuTime = 5;
|
|
*m_pGpuTime = 3;
|
|
m_LastRunPushBufferTime = 3;
|
|
return;
|
|
}
|
|
void CDevice::HwPut(
|
|
DWORD *put)
|
|
{
|
|
m_pControlDma->Put = (DWORD)put & 0x03ffffffUL;
|
|
return;
|
|
}
|
|
DWORD *CDevice::GpuGet(
|
|
void)
|
|
{
|
|
DWORD *get = HwGet();
|
|
if (get < m_pPushBase || get >= m_pPushLimit)
|
|
{
|
|
get = (DWORD *)((REG_RD32(m_NvBase, 0x324c) & ~1UL) | 0x80000000UL);
|
|
}
|
|
return get;
|
|
}
|
|
void WINAPI FlushWCCache(
|
|
void)
|
|
{
|
|
volatile DWORD *base = (volatile DWORD *)g_pDevice->m_Miniport.m_RegisterBase;
|
|
/* One-instruction, target-evidenced cache fence. The intrinsic trial lets
|
|
* VC7 move an MMIO read before SFENCE. No raw instruction bytes are used. */
|
|
__asm sfence;
|
|
base[0x100410 / sizeof(DWORD)] |= 0x10000;
|
|
while ((base[0x100410 / sizeof(DWORD)] >> 16) & 1) { }
|
|
return;
|
|
}
|
|
void CDevice::KickOff(
|
|
void)
|
|
{
|
|
if (!D3D__NullHardware)
|
|
{
|
|
DWORD *put = (m_StateFlags & 4) ? m_pPushBufferRecordSavedPut : m_Pusher.m_pPut;
|
|
FlushWCCache();
|
|
HwPut(put);
|
|
m_pKickOff = put;
|
|
}
|
|
else
|
|
{
|
|
m_Pusher.m_pPut = HwGet();
|
|
m_pKickOff = m_Pusher.m_pPut;
|
|
*m_pGpuTime = m_CpuTime - 2;
|
|
m_Miniport.m_VBlankFlipCount = m_PresentCount;
|
|
}
|
|
return;
|
|
}
|
|
DWORD WINAPI SetFence(
|
|
DWORD flags)
|
|
{
|
|
CDevice *device = g_pDevice;
|
|
DWORD *push = device->StartPush();
|
|
FenceEncoding *encoding = (FenceEncoding *)push;
|
|
DWORD time = device->m_CpuTime;
|
|
encoding->m_SemaphoreCommand = 0x41d70;
|
|
encoding->m_Time = time;
|
|
encoding->m_SetColorClearCommand1 = 0x41d90;
|
|
encoding->m_SetColorClearArgument1 = 0;
|
|
encoding->m_SetColorClearCommand2 = 0x41d90;
|
|
encoding->m_SetColorClearArgument2 = 0;
|
|
device->EndPush(push + sizeof(FenceEncoding) / sizeof(DWORD));
|
|
DWORD index = (time >> 1) & 63;
|
|
DWORD distance = device->m_PusherPutRunSize;
|
|
device->m_PusherFence[index].Time = time;
|
|
device->m_PusherFence[index].pEncoding = encoding;
|
|
device->m_PusherFence[index].Distance = distance;
|
|
if (flags & 1)
|
|
{
|
|
index = (device->m_PusherLastSegment + 1) & 15;
|
|
device->m_PusherLastSegment = index;
|
|
device->m_PusherSegment[index].Time = time;
|
|
device->m_PusherSegment[index].pEncoding = encoding;
|
|
device->m_PusherSegment[index].Distance = distance;
|
|
}
|
|
if (!(flags & 2)) device->KickOff();
|
|
device->m_CpuTime += 2;
|
|
return time;
|
|
}
|
|
Fence *WINAPI FindFence(
|
|
DWORD time)
|
|
{
|
|
CDevice *device = g_pDevice;
|
|
DWORD age = device->Age(time);
|
|
if (age <= 128)
|
|
{
|
|
return &device->m_PusherFence[(time >> 1) & 63];
|
|
}
|
|
DWORD index = device->m_PusherLastSegment;
|
|
if (device->Age(device->m_PusherSegment[index].Time) > age)
|
|
{
|
|
return &device->m_PusherFence[(device->m_CpuTime >> 1) & 63];
|
|
}
|
|
do
|
|
{
|
|
index = (index + 1) & 15;
|
|
} while (device->Age(device->m_PusherSegment[index].Time) > age);
|
|
return &device->m_PusherSegment[index];
|
|
}
|
|
DWORD __fastcall ComputeGap(
|
|
CDevice *device,
|
|
Fence *fence)
|
|
{
|
|
DWORD get = (DWORD)device->GpuGet();
|
|
DWORD put = (DWORD)device->m_Pusher.m_pPut;
|
|
if (get <= put) get += device->m_PusherLastSize;
|
|
DWORD fenceAddress = (DWORD)fence->pEncoding;
|
|
if (fenceAddress <= put) fenceAddress += device->m_PusherLastSize;
|
|
LONG gap = (LONG)(fenceAddress - get);
|
|
if (gap < 0) return 0;
|
|
return gap + device->m_PusherPutRunSize - device->m_Miniport.m_PusherGetRunSize;
|
|
}
|
|
void WINAPI KickOffAndWaitForIdle(
|
|
void)
|
|
{
|
|
BlockOnTime(g_pDevice->m_CpuTime, FALSE);
|
|
return;
|
|
}
|
|
void WINAPI BlockOnResource(
|
|
D3DResource *resource)
|
|
{
|
|
if (g_pDevice)
|
|
{
|
|
D3DResource *parent = PixelJar::GetParent(resource);
|
|
if (parent)
|
|
{
|
|
if (IsResourceSetInDevice(resource))
|
|
{
|
|
KickOffAndWaitForIdle();
|
|
return;
|
|
}
|
|
resource = parent;
|
|
}
|
|
DWORD time = resource->Lock;
|
|
if (IsResourceSetInDevice(resource)) KickOffAndWaitForIdle();
|
|
else BlockOnTime(time, FALSE);
|
|
resource->Lock = 0;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
using namespace D3D;
|
|
BOOL WINAPI D3DDevice_IsFencePending(
|
|
DWORD fence)
|
|
{
|
|
return g_pDevice->IsTimePending(fence);
|
|
}
|
|
void WINAPI D3DDevice_InsertCallback(
|
|
D3DCALLBACKTYPE type,
|
|
D3DCALLBACK callback,
|
|
DWORD context)
|
|
{
|
|
CDevice *device = g_pDevice;
|
|
DWORD *push = device->StartPush();
|
|
Push2(push, 0x1d8c, (DWORD)callback, context);
|
|
if (type == D3DCALLBACK_READ)
|
|
{
|
|
Push1(push + 3, 0x100, 0x314);
|
|
device->EndPush(push + 5);
|
|
}
|
|
else
|
|
{
|
|
Push1(push + 3, 0x110, 0);
|
|
Push1(push + 5, 0x100, 0x318);
|
|
device->EndPush(push + 7);
|
|
}
|
|
return;
|
|
}
|
|
extern "C" DWORD *WINAPI XMETAL_StartPush(
|
|
_XMETAL_PushBuffer *buffer)
|
|
{
|
|
DWORD *put = buffer->m_pPut;
|
|
if (put >= buffer->m_pThreshold)
|
|
{
|
|
put = ((CDevice *)buffer)->MakeSpace();
|
|
}
|
|
return put;
|
|
}
|
|
extern "C" DWORD *WINAPI XMETAL_StartPushCount(
|
|
_XMETAL_PushBuffer *buffer,
|
|
DWORD count)
|
|
{
|
|
DWORD *put = buffer->m_pPut;
|
|
if (put + count >= buffer->m_pThreshold + 128)
|
|
{
|
|
put = ((CDevice *)buffer)->MakeSpace();
|
|
}
|
|
return put;
|
|
}
|
|
|
|
namespace D3D
|
|
{
|
|
/* The January wait protocol patches a live GPU fence only while its gap is
|
|
* at least 32 KiB. If the GPU wins the race, restore harmless commands and
|
|
* spin on the fence time. No target code is executed by the build tools. */
|
|
void WINAPI BlockOnTime(
|
|
DWORD time,
|
|
BOOL makeSpace)
|
|
{
|
|
CDevice *device = g_pDevice;
|
|
if (time == 0 || !device->IsTimePending(time))
|
|
{
|
|
return;
|
|
}
|
|
if (time == device->m_CpuTime)
|
|
{
|
|
SetFence(0);
|
|
}
|
|
DWORD spinTime = time;
|
|
Fence *fence = FindFence(time);
|
|
DWORD gap = ComputeGap(device, fence);
|
|
if (gap >= 32768)
|
|
{
|
|
FenceEncoding *encoding = fence->pEncoding;
|
|
KeClearEvent(&device->m_Miniport.m_BusyBlockEvent);
|
|
if (!makeSpace)
|
|
{
|
|
encoding->m_WaitForIdleCommand = 0x40110;
|
|
encoding->m_WaitForIdleArgument = 0;
|
|
}
|
|
encoding->m_NoOperationCommand = 0x40100;
|
|
encoding->m_FenceCommand = 0x310;
|
|
FlushWCCache();
|
|
DWORD newGap = ComputeGap(device, fence);
|
|
if (newGap < 32768)
|
|
{
|
|
encoding->m_WaitForIdleCommand = 0x40100;
|
|
encoding->m_WaitForIdleArgument = 0;
|
|
encoding->m_NoOperationCommand = 0x40100;
|
|
encoding->m_FenceCommand = 0;
|
|
FlushWCCache();
|
|
spinTime = fence->Time;
|
|
}
|
|
else
|
|
{
|
|
while (KeWaitForSingleObject(&device->m_Miniport.m_BusyBlockEvent,
|
|
UserRequest, UserMode, FALSE, NULL) != 0)
|
|
{
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
while (device->IsTimePending(spinTime))
|
|
{
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
namespace D3D
|
|
{
|
|
/* Addresses used to compare prospective segment limits are integer byte
|
|
* offsets, not pointers formed outside the allocated circular push buffer. */
|
|
DWORD *CDevice::MakeSpace(
|
|
void)
|
|
{
|
|
DWORD *put = m_Pusher.m_pPut;
|
|
if (m_StateFlags & 4)
|
|
{
|
|
DWORD *base = (DWORD *)m_pPushBufferRecordResource->Data;
|
|
m_PushBufferRecordWrapSize += (DWORD)((BYTE *)put - (BYTE *)base);
|
|
m_Pusher.m_pPut = (DWORD *)m_pPushBufferRecordResource->Data;
|
|
return m_Pusher.m_pPut;
|
|
}
|
|
ULONG_PTR limit = (ULONG_PTR)put + m_PushSegmentSize;
|
|
if (limit + m_PushSegmentSize / 2 >= (ULONG_PTR)m_pPushLimit)
|
|
{
|
|
if ((ULONG_PTR)put + m_PushSegmentSize / 2 <= (ULONG_PTR)m_pPushLimit)
|
|
{
|
|
limit = (ULONG_PTR)m_pPushLimit;
|
|
}
|
|
else
|
|
{
|
|
m_PusherLastSize = (DWORD)((BYTE *)put - (BYTE *)m_pPushBase);
|
|
*m_Pusher.m_pPut = ((DWORD)m_pPushBase & 0x0fffffffUL) | 1;
|
|
while (HwGet() == m_pPushBase)
|
|
{
|
|
}
|
|
put = m_pPushBase;
|
|
limit = (ULONG_PTR)put + m_PushSegmentSize;
|
|
m_Pusher.m_pPut = put;
|
|
}
|
|
}
|
|
DWORD gpuTime = GpuTime();
|
|
DWORD *get = GpuGet();
|
|
if (get > put && (ULONG_PTR)get <= limit)
|
|
{
|
|
DWORD index = m_PusherLastSegment;
|
|
ULONG_PTR midpoint = (ULONG_PTR)put + m_PushBufferSize / 2;
|
|
DWORD fenceTime = m_PusherSegment[index].Time;
|
|
ULONG_PTR fence = (ULONG_PTR)m_PusherSegment[index].pEncoding;
|
|
BOOL blocked = FALSE;
|
|
while (Age(fenceTime) < Age(gpuTime))
|
|
{
|
|
if (fence < (ULONG_PTR)put) fence += m_PusherLastSize;
|
|
if (fence < midpoint)
|
|
{
|
|
BlockOnTime(fenceTime, TRUE);
|
|
blocked = TRUE;
|
|
break;
|
|
}
|
|
index = (index - 1) & 15;
|
|
if (index == m_PusherLastSegment) break;
|
|
fence = (ULONG_PTR)m_PusherSegment[index].pEncoding;
|
|
fenceTime = m_PusherSegment[index].Time;
|
|
}
|
|
if (!blocked)
|
|
{
|
|
do
|
|
{
|
|
BusyLoop();
|
|
get = GpuGet();
|
|
} while (get > put && (ULONG_PTR)get <= limit);
|
|
}
|
|
GpuGet();
|
|
}
|
|
m_Pusher.m_pThreshold = (DWORD *)(limit - 129 * sizeof(DWORD));
|
|
if (m_StateFlags & 0x800)
|
|
{
|
|
m_StateFlags |= 0x1000;
|
|
KickOff();
|
|
}
|
|
else
|
|
{
|
|
SetFence(1);
|
|
KickOff();
|
|
}
|
|
return m_Pusher.m_pPut;
|
|
}
|
|
}
|