Files
Cesarus85--mjolnir-vr/libs/d3d8/d3dbase.cpp
T

1276 lines
38 KiB
C++

/* Additional device methods adapted from RXDK-Libs, 2026 Team Resurgent.
* GPL-3.0-or-later; see libs/d3d8/PROVENANCE.md. */
#include "device_layout.h"
#include "pixeljar.h"
#include "push_internal.h"
#include "resource_internal.h"
#include "display_internal.h"
#include "state_internal.h"
#include "math_internal.h"
#include "memory_internal.h"
#include "kernel_memory.h"
#include "visibility_internal.h"
#include "kernel_dispatcher.h"
#include "hardware_internal.h"
#include "surface_internal.h"
#pragma code_seg("D3D")
using namespace D3D;
void WINAPI D3DDevice_GetDirect3D(
Direct3D **direct3d)
{
*direct3d = (Direct3D *)1;
return;
}
void WINAPI D3DDevice_GetDeviceCaps(
D3DCAPS8 *caps)
{
*caps = g_DeviceCaps;
return;
}
void WINAPI D3DDevice_GetCreationParameters(
D3DDEVICE_CREATION_PARAMETERS *parameters)
{
CDevice *device = g_pDevice;
memset(parameters, 0, sizeof(*parameters));
parameters->DeviceType = D3DDEVTYPE_HAL;
parameters->BehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
if (device->m_StateFlags & 0x10)
{
parameters->BehaviorFlags |= D3DCREATE_PUREDEVICE;
}
return;
}
void WINAPI D3DDevice_SetGammaRamp(
DWORD flags,
const D3DGAMMARAMP *ramp)
{
CDevice *device = g_pDevice;
CMiniport *miniport = &device->m_Miniport;
DWORD index = device->m_PresentCount & 1;
D3DGAMMARAMP *destination = &miniport->m_GammaRamp[index];
*destination = *ramp;
miniport->m_GammaCurrentIndex = index;
if (flags & 2)
{
miniport->DacProgramGammaRamp(destination);
}
else
{
miniport->m_GammaUpdated[index] = TRUE;
}
return;
}
void WINAPI D3DDevice_GetGammaRamp(
D3DGAMMARAMP *ramp)
{
CDevice *device = g_pDevice;
*ramp = device->m_Miniport.m_GammaRamp[device->m_Miniport.m_GammaCurrentIndex];
return;
}
void WINAPI D3DDevice_GetTransform(
D3DTRANSFORMSTATETYPE state,
D3DMATRIX *matrix)
{
CDevice *device = g_pDevice;
*matrix = device->m_Transform[state];
return;
}
void WINAPI D3DDevice_GetViewport(
D3DVIEWPORT8 *viewport)
{
CDevice *device = g_pDevice;
*viewport = device->m_Viewport;
return;
}
void WINAPI D3DDevice_SetMaterial(
const D3DMATERIAL8 *material)
{
CDevice *device = g_pDevice;
device->m_Material = *material;
device->m_DirtyFlags |= 0x20;
return;
}
void WINAPI D3DDevice_GetMaterial(
D3DMATERIAL8 *material)
{
CDevice *device = g_pDevice;
*material = device->m_Material;
return;
}
void WINAPI D3DDevice_SetBackMaterial(
const D3DMATERIAL8 *material)
{
CDevice *device = g_pDevice;
device->m_BackMaterial = *material;
device->m_DirtyFlags |= 0x20;
return;
}
void WINAPI D3DDevice_GetBackMaterial(
D3DMATERIAL8 *material)
{
CDevice *device = g_pDevice;
*material = device->m_BackMaterial;
return;
}
void WINAPI D3DDevice_GetLight(
DWORD index,
D3DLIGHT8 *light)
{
CDevice *device = g_pDevice;
*light = device->m_pLights[index].Light8;
return;
}
void WINAPI D3DDevice_GetLightEnable(
DWORD index,
BOOL *enabled)
{
CDevice *device = g_pDevice;
Light *requested = &device->m_pLights[index];
Light *light = device->m_pActiveLights;
while (light)
{
if (light == requested)
{
*enabled = TRUE;
return;
}
light = light->pNext;
}
*enabled = FALSE;
return;
}
void WINAPI D3DDevice_SetVerticalBlankCallback(
D3DCALLBACK callback)
{
g_pDevice->m_Miniport.m_pVerticalBlankCallback = callback;
return;
}
void WINAPI D3DDevice_GetProjectionViewportMatrix(
D3DMATRIX *matrix)
{
*matrix = g_pDevice->m_ProjectionViewportTransform;
return;
}
HRESULT WINAPI D3DDevice_GetModelView(
D3DMATRIX *matrix)
{
CDevice *device = g_pDevice;
if (matrix)
{
*matrix = device->m_ModelViewTransform[0];
}
return !(device->m_DirtyFlags & 0x80000000);
}
void WINAPI D3DDevice_GetTile(
DWORD index,
D3DTILE *tile)
{
*tile = g_pDevice->m_Tile[index];
return;
}
void WINAPI D3DDevice_GetScissors(
DWORD *count,
BOOL *exclusive,
D3DRECT *rectangles)
{
CDevice *device = g_pDevice;
if (count)
{
*count = device->m_ScissorsCount;
}
if (exclusive)
{
*exclusive = device->m_ScissorsExclusive;
}
if (rectangles)
{
memcpy(rectangles, device->m_ScissorsRects, device->m_ScissorsCount * sizeof(*rectangles));
}
return;
}
HRESULT WINAPI D3DDevice_CreateTexture(
UINT width,
UINT height,
UINT levels,
DWORD usage,
D3DFORMAT format,
D3DPOOL pool,
D3DTexture **texture)
{
return CreateTexture(width, height, 1, levels, usage, format, false, false, (D3DBaseTexture **)texture);
}
HRESULT WINAPI D3DDevice_CreateVolumeTexture(
UINT width,
UINT height,
UINT depth,
UINT levels,
DWORD usage,
D3DFORMAT format,
D3DPOOL pool,
D3DVolumeTexture **texture)
{
return CreateTexture(width, height, depth, levels, usage, format, false, true, (D3DBaseTexture **)texture);
}
HRESULT WINAPI D3DDevice_CreateCubeTexture(
UINT edge,
UINT levels,
DWORD usage,
D3DFORMAT format,
D3DPOOL pool,
D3DCubeTexture **texture)
{
return CreateTexture(edge, edge, 1, levels, usage, format, true, false, (D3DBaseTexture **)texture);
}
HRESULT WINAPI D3DDevice_CreateRenderTarget(
UINT width,
UINT height,
D3DFORMAT format,
D3DMULTISAMPLE_TYPE multisample,
BOOL lockable,
D3DSurface **surface)
{
return CreateStandAloneSurface(width, height, format, true, surface);
}
HRESULT WINAPI D3DDevice_CreateDepthStencilSurface(
UINT width,
UINT height,
D3DFORMAT format,
D3DMULTISAMPLE_TYPE multisample,
D3DSurface **surface)
{
return CreateStandAloneSurface(width, height, format, true, surface);
}
HRESULT WINAPI D3DDevice_CreateImageSurface(
UINT width,
UINT height,
D3DFORMAT format,
D3DSurface **surface)
{
return CreateStandAloneSurface(width, height, format, false, surface);
}
void WINAPI D3DDevice_SetTransform(
D3DTRANSFORMSTATETYPE State,
const D3DMATRIX *pMatrix)
{
DWORD state = (DWORD)State;
CDevice *pDevice = g_pDevice;
pDevice->m_Transform[state] = *pMatrix;
pDevice->m_DirtyFlags |= g_TransformDirtyTable[state];
if (State == D3DTS_PROJECTION)
{
if (pMatrix->_33 == pMatrix->_34 || pMatrix->_33 == 0.0f)
{
pDevice->m_WNear = 0.0f;
pDevice->m_WFar = 1.0f;
}
else
{
pDevice->m_WNear = pMatrix->_44 - pMatrix->_43 / pMatrix->_33 * pMatrix->_34;
pDevice->m_WFar = (pMatrix->_44 - pMatrix->_43) / (pMatrix->_33 - pMatrix->_34) * pMatrix->_34 + pMatrix->_44;
}
pDevice->m_InverseWFar = 1.0f / pDevice->m_WFar;
pDevice->m_StateFlags &= ~2;
if (pMatrix->_14 == 0.0f && pMatrix->_24 == 0.0f && pMatrix->_34 == 0.0f && pMatrix->_44 == 1.0f)
{
pDevice->m_StateFlags |= 2;
}
UpdateProjectionViewportTransform();
CommonSetPassthroughProgram(pDevice);
}
return;
}
void WINAPI D3DDevice_MultiplyTransform(
D3DTRANSFORMSTATETYPE State,
const D3DMATRIX *pMatrix)
{
__declspec(align(16)) D3DMATRIX result;
__declspec(align(16)) D3DMATRIX temp = *pMatrix;
DWORD state = (DWORD)State;
CDevice *pDevice = g_pDevice;
MatrixProduct4x4(&result, &temp, &pDevice->m_Transform[state]);
D3DDevice_SetTransform(State, &result);
return;
}
ULONG WINAPI D3DDevice_AddRef(
void)
{
return ++g_pDevice->m_cRefs;
}
ULONG WINAPI D3DDevice_Release(
void)
{
CDevice *device = g_pDevice;
if (device->m_cRefs == 1)
{
device->UnInit();
g_pPushBuffer = NULL;
g_pDevice = NULL;
memset(device, 0, sizeof(*device));
return 0;
}
return --device->m_cRefs;
}
void WINAPI D3DDevice_BlockUntilIdle(
void)
{
KickOffAndWaitForIdle();
return;
}
void WINAPI D3DDevice_BlockOnFence(
DWORD fence)
{
BlockOnTime(fence, FALSE);
return;
}
void WINAPI D3DDevice_KickPushBuffer(
void)
{
g_pDevice->KickOff();
return;
}
DWORD WINAPI D3DDevice_InsertFence(
void)
{
return SetFence(0);
}
void WINAPI D3DDevice_GetDisplayMode(
D3DDISPLAYMODE *mode)
{
CDevice *device = g_pDevice;
mode->Width = device->m_DisplayWidth;
mode->Height = device->m_DisplayHeight;
mode->RefreshRate = device->m_Miniport.GetRefreshRate();
mode->Flags = CMiniport::GetPresentFlagsFromAvInfo(device->m_Miniport.m_CurrentAvInfo);
mode->Format = PixelJar::GetFormat(&device->m_FrameBufferSurfaces[1]);
return;
}
void WINAPI D3DDevice_GetBackBuffer(
INT BackBuffer,
D3DBACKBUFFER_TYPE Type,
D3DSurface **surface)
{
CDevice *device = g_pDevice;
if (BackBuffer == -1) { BackBuffer = 1; }
else if (BackBuffer) { ++BackBuffer; }
*surface = &device->m_FrameBufferSurfaces[BackBuffer];
(*surface)->AddRef();
return;
}
void WINAPI D3DDevice_GetRenderTarget(
D3DSurface **surface)
{
*surface = g_pDevice->m_pRenderTarget;
if (*surface) { (*surface)->AddRef(); }
return;
}
HRESULT WINAPI D3DDevice_GetDepthStencilSurface(
D3DSurface **surface)
{
*surface = g_pDevice->m_pZBuffer;
if (*surface)
{
(*surface)->AddRef();
return S_OK;
}
return D3DERR_NOTFOUND;
}
void WINAPI D3DDevice_GetTexture(
DWORD Stage,
D3DBaseTexture **texture)
{
CDevice *device = g_pDevice;
D3DBaseTexture **textures = device->m_Textures;
if (textures[Stage]) { textures[Stage]->AddRef(); }
*texture = textures[Stage];
return;
}
void WINAPI D3DDevice_GetPalette(
DWORD Stage,
D3DPalette **palette)
{
CDevice *device = g_pDevice;
D3DPalette **palettes = device->m_Palettes;
if (palettes[Stage]) { palettes[Stage]->AddRef(); }
*palette = palettes[Stage];
return;
}
void WINAPI D3DDevice_SetPalette(
DWORD Stage,
D3DPalette *palette)
{
CDevice *device = g_pDevice;
if (device->m_Palettes[Stage])
{
device->RecordResourceReadPush(device->m_Palettes[Stage]);
InternalRelease(device->m_Palettes[Stage]);
}
device->m_Palettes[Stage] = palette;
if (palette)
{
InternalAddRef(palette);
DWORD size = palette->Common >> 28;
DWORD *push = device->StartPush();
Push1(push, 0x1b20 + Stage * 64, palette->Data | size);
device->EndPush(push + 2);
}
return;
}
void WINAPI D3DDevice_SetIndices(
D3DIndexBuffer *buffer,
UINT base)
{
CDevice *device = g_pDevice;
if (buffer)
{
InternalAddRef(buffer);
D3D__IndexData = (WORD *)buffer->Data;
}
else { D3D__IndexData = NULL; }
if (device->m_pIndexBuffer) { InternalRelease(device->m_pIndexBuffer); }
device->m_pIndexBuffer = buffer;
device->m_IndexBase = base;
return;
}
void WINAPI D3DDevice_GetIndices(
D3DIndexBuffer **buffer,
UINT *base)
{
CDevice *device = g_pDevice;
D3DIndexBuffer *current = device->m_pIndexBuffer;
if (current)
{
current->AddRef();
*buffer = current;
*base = device->m_IndexBase;
}
else
{
*buffer = NULL;
*base = 0;
}
return;
}
void WINAPI D3DDevice_Nop(
void)
{
CDevice *device = g_pDevice;
DWORD *push = device->StartPush();
Push1(push, NV097_NO_OPERATION, 0);
device->EndPush(push + 2);
return;
}
void WINAPI D3DDevice_FlushVertexCache(
void)
{
CDevice *device = g_pDevice;
DWORD *push = device->StartPush();
Push1(push, 0x1710, 0);
device->EndPush(push + 2);
return;
}
void WINAPI D3DDevice_SetViewport(
const D3DVIEWPORT8 *pViewport)
{
D3DSurface *pSurface;
CDevice *pDevice = g_pDevice;
if (pDevice->m_pRenderTarget == &pDevice->m_FrameBufferSurfaces[0])
{
pSurface = &pDevice->m_FrameBufferSurfaces[1];
}
else { pSurface = pDevice->m_pRenderTarget; }
DWORD left = max(pViewport->X, 0);
DWORD top = max(pViewport->Y, 0);
DWORD right = min(pViewport->X + pViewport->Width, (INT)PixelJar::GetWidth(pSurface));
DWORD bottom = min(pViewport->Y + pViewport->Height, (INT)PixelJar::GetHeight(pSurface));
pDevice->m_Viewport.X = left;
pDevice->m_Viewport.Y = top;
pDevice->m_Viewport.Width = right - left;
pDevice->m_Viewport.Height = bottom - top;
pDevice->m_Viewport.MinZ = pViewport->MinZ;
pDevice->m_Viewport.MaxZ = pViewport->MaxZ;
UpdateProjectionViewportTransform();
D3DDevice_SetScissors(0, 0, NULL);
DWORD *push = pDevice->StartPush();
push = CommonSetViewport(pDevice, push);
pDevice->EndPush(push);
pDevice->m_DirtyFlags |= 1;
return;
}
void WINAPI D3DDevice_SetTexture(
DWORD stage,
D3DBaseTexture *texture)
{
DWORD *push;
CDevice *device = g_pDevice;
D3DBaseTexture *previous = device->m_Textures[stage];
if (previous)
{
device->RecordResourceReadPush(previous);
InternalRelease(previous);
}
device->m_Textures[stage] = texture;
if (!texture)
{
push = device->StartPush();
Push1(push, 0x1b0c + stage * 64, 0);
device->EndPush(push + 2);
device->m_TextureCubemapAndDimension[stage] = ~0UL;
device->m_DirtyFlags |= 0x408;
}
else
{
InternalAddRef(texture);
push = device->StartPush();
Push2(push, 0x1b00 + stage * 64, texture->Data, texture->Format);
push += 3;
if (texture->Size != 0)
{
DWORD width = (texture->Size & D3DSIZE_WIDTH_MASK) + 1;
DWORD height = ((texture->Size & D3DSIZE_HEIGHT_MASK) >> D3DSIZE_HEIGHT_SHIFT) + 1;
DWORD pitch = PixelJar::GetPitch(texture);
Push1(push, 0x1b10 + stage * 64, pitch << 16);
Push1(push + 2, 0x1b1c + stage * 64, (width << 16) | height);
push += 4;
}
DWORD dimensions = texture->Format & (D3DFORMAT_CUBEMAP | D3DFORMAT_DIMENSION_MASK);
if (device->m_TextureCubemapAndDimension[stage] != dimensions)
{
device->m_TextureCubemapAndDimension[stage] = dimensions;
/* January always reprograms CONTROL0 when these format bits change. */
Push1(push, 0x1b0c + stage * 64, device->m_TextureControl0Enabled[stage]);
push += 2;
device->m_DirtyFlags |= 0x400;
if (!previous) device->m_DirtyFlags |= 8;
}
device->EndPush(push);
}
return;
}
/* BUG (original): batches advance caller-supplied rectangle/point pointers by
* 16 even when the final batch is shorter. Strict C pointer bounds therefore
* require caller backing arrays rounded up to a 16-entry batch. The binary
* proves the fixed increment; a corrected build would advance by batch.
* Swizzled copies additionally require writable rectangle storage, as noted
* at the actual original store below. Neither behavior is introduced to steer
* the compiler. Normal matching tests do not establish arbitrary-input safety.
*/
void WINAPI D3DDevice_CopyRects(
D3DSurface *sourceSurface,
const RECT *sourceRects,
UINT count,
D3DSurface *destinationSurface,
const POINT *destinationPoints)
{
const DWORD maxRectangles = 16;
RECT rectangles[maxRectangles];
POINT points[maxRectangles];
DWORD swizzledColumns, swizzledRows;
DWORD bytesPerPixel = PixelJar::GetBitsPerPixel(sourceSurface) / 8;
DWORD sourcePitch = PixelJar::GetPitch(sourceSurface);
DWORD destinationPitch = PixelJar::GetPitch(destinationSurface);
BOOL swizzled = PixelJar::IsSwizzled(sourceSurface) || PixelJar::IsCompressed(sourceSurface);
CDevice *device = g_pDevice;
if (count == 0) count = 1;
if (swizzled)
{
DWORD slice = PixelJar::GetSlice(sourceSurface);
bytesPerPixel = 1;
if (slice <= 8128)
{
sourcePitch = (slice + 63) & ~63;
destinationPitch = sourcePitch;
swizzledColumns = slice;
swizzledRows = 1;
}
else
{
sourcePitch = 4096;
destinationPitch = 4096;
swizzledColumns = 4096;
swizzledRows = slice / 4096;
}
}
do
{
RECT *rects;
POINT *destinations;
DWORD batch = min(maxRectangles, count);
count -= batch;
if (sourceRects)
{
rects = (RECT *)sourceRects;
sourceRects += maxRectangles;
}
else
{
rects = rectangles;
for (DWORD i = 0; i < batch; i++)
{
rectangles[i].top = 0;
rectangles[i].bottom = PixelJar::GetHeight(sourceSurface);
rectangles[i].left = 0;
rectangles[i].right = PixelJar::GetWidth(sourceSurface);
}
}
if (destinationPoints)
{
destinations = (POINT *)destinationPoints;
destinationPoints += maxRectangles;
}
else
{
destinations = points;
for (DWORD i = 0; i < batch; i++)
{
points[i].x = rects[i].left;
points[i].y = rects[i].top;
}
}
if (swizzled)
{
/* BUG (original): the target writes through the const-qualified
* input rectangle pointer. This matching entry requires mutable
* backing storage when explicit swizzled rectangles are supplied.
* A corrected build would use a local rectangle copy. */
for (DWORD i = 0; i < batch; i++)
{
rects[i].left = 0;
rects[i].top = 0;
rects[i].right = swizzledColumns;
rects[i].bottom = swizzledRows;
}
}
GPUCopyVideoRectangle(bytesPerPixel, sourceSurface->Data, sourcePitch,
destinationSurface->Data, destinationPitch, batch, rects, destinations);
} while (count);
device->RecordSurfaceWritePush(sourceSurface);
device->RecordSurfaceWritePush(destinationSurface);
return;
}
void WINAPI D3DDevice_SetScissors(
DWORD count,
BOOL exclusive,
const D3DRECT *rects)
{
const D3DRECT *current;
D3DRECT viewport;
DWORD i;
CDevice *device = g_pDevice;
if (count == 0)
{
viewport.x1 = device->m_Viewport.X;
viewport.y1 = device->m_Viewport.Y;
viewport.x2 = viewport.x1 + device->m_Viewport.Width;
viewport.y2 = viewport.y1 + device->m_Viewport.Height;
exclusive = FALSE;
rects = &viewport;
count = 1;
}
DWORD *push = device->StartPush();
Push1(push, 0x2b4, exclusive);
push += 2;
float xScale = device->m_SuperSampleScaleX;
float yScale = device->m_SuperSampleScaleY;
for (current = rects, i = 0; i < count; i++, current++)
{
Push1(push, 0x2c0 + i * 4,
Round(xScale * current->x1) | ((Round(xScale * current->x2) - 1) << 16));
Push1(push + 2, 0x2e0 + i * 4,
Round(yScale * current->y1) | ((Round(yScale * current->y2) - 1) << 16));
push += 4;
}
device->EndPush(push);
memcpy(&device->m_ScissorsRects[0], rects, count * sizeof(D3DRECT));
device->m_ScissorsCount = count;
device->m_ScissorsExclusive = exclusive;
return;
}
BYTE *WINAPI D3D::GetVisibilityAddress(
DWORD index)
{
CDevice *device = g_pDevice;
DWORD page = index >> 8;
if (device->m_ReportAllocations[page] == NULL)
{
device->m_ReportAllocations[page] = MmAllocateContiguousMemoryEx(
4096, 0, 0x00ffffff, 0, PAGE_READWRITE);
if (device->m_ReportAllocations[page] == NULL)
return NULL;
}
VisibilityReport *report = (VisibilityReport *)
((BYTE *)device->m_ReportAllocations[page] + (index & 255) * 16);
if (!(device->m_StateFlags & 4))
report->Status = 0xffffffff;
return (BYTE *)report;
}
void WINAPI D3DDevice_BeginVisibilityTest(
void)
{
CDevice *device = g_pDevice;
DWORD *push = device->StartPush();
Push2(push, 0x17c8, 1, 1);
device->EndPush(push + 3);
return;
}
HRESULT WINAPI D3DDevice_EndVisibilityTest(
DWORD index)
{
BYTE *address = GetVisibilityAddress(index);
if (address == NULL)
return E_OUTOFMEMORY;
CDevice *device = g_pDevice;
DWORD *push = device->StartPush();
Push2(push, 0x17cc, 0, ((DWORD)address & 0x02ffffff) | 0x01000000);
device->EndPush(push + 3);
device->KickOff();
return S_OK;
}
HRESULT WINAPI D3DDevice_GetVisibilityTestResult(
DWORD index,
UINT *result,
ULONGLONG *timeStamp)
{
CDevice *device = g_pDevice;
VisibilityReport *report = (VisibilityReport *)
((BYTE *)device->m_ReportAllocations[index >> 8] + (index & 255) * 16);
if (report->Status == 0xffffffff)
return D3DERR_TESTINCOMPLETE;
*result = report->Samples;
if (timeStamp)
*timeStamp = report->TimeStamp;
return S_OK;
}
HRESULT WINAPI D3DDevice_Reset(
D3DPRESENT_PARAMETERS *parameters)
{
CDevice *device = g_pDevice;
KickOffAndWaitForIdle();
while (device->m_Miniport.IsFlipPending())
{
/* The original API waits for the final queued display flip. */
}
device->FreeFrameBuffers();
HRESULT result = device->InitializeFrameBuffers(parameters);
if (FAILED(result))
{
device->FreeFrameBuffers();
return result;
}
device->m_Miniport.SetVideoMode(parameters->BackBufferWidth,
parameters->BackBufferHeight, parameters->FullScreen_RefreshRateInHz,
parameters->Flags, parameters->BackBufferFormat,
parameters->FullScreen_PresentationInterval,
PixelJar::GetPitch(&device->m_FrameBufferSurfaces[1]));
D3DDevice_SetRenderTarget(&device->m_FrameBufferSurfaces[0],
device->m_pAutoDepthBuffer ? &device->m_AutoDepthSurface : NULL);
D3DDevice_Clear(0, NULL, D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
return S_OK;
}
void WINAPI D3DDevice_SetRenderTarget(
D3DSurface *renderTarget,
D3DSurface *zBuffer)
{
CDevice *device = g_pDevice;
if (renderTarget == NULL)
renderTarget = device->m_pRenderTarget;
DWORD width = PixelJar::GetWidth(renderTarget);
DWORD height = PixelJar::GetHeight(renderTarget);
DWORD renderPitch = PixelJar::GetPitch(renderTarget);
if (renderTarget == &device->m_FrameBufferSurfaces[0])
{
DWORD sampling = D3D__RenderState[D3DRS_MULTISAMPLETYPE];
if ((sampling & 0x3000) && (sampling & 0x1000))
{
width >>= 1;
if ((sampling & 15) > 1)
height >>= 1;
}
}
DWORD depthPitch = zBuffer ? PixelJar::GetPitch(zBuffer) : renderPitch;
InternalAddRef(renderTarget);
if (device->m_pRenderTarget)
{
device->RecordSurfaceWritePush(device->m_pRenderTarget);
InternalReleaseSurface(device->m_pRenderTarget);
}
device->m_pRenderTarget = renderTarget;
if (device->m_pZBuffer)
{
device->RecordSurfaceWritePush(device->m_pZBuffer);
InternalReleaseSurface(device->m_pZBuffer);
}
device->m_pZBuffer = zBuffer;
if (zBuffer)
{
InternalAddRef(zBuffer);
PixelJar::GetDepthBufferScale(zBuffer, &device->m_ZScale);
}
DWORD *push = device->StartPush();
DWORD renderOffset = renderTarget->Data;
DWORD depthOffset = zBuffer ? zBuffer->Data : 0;
DWORD pitch = (renderPitch & 0xffff) | (depthPitch << 16);
/* Target-evidenced NV2A surface switch synchronization: each register
* update is bracketed by NOP and idle methods, and the sequence is repeated
* twice. These are hardware command words, not native instruction data. */
for (DWORD i = 0; i < 2; i++)
{
Push1(push, NV097_NO_OPERATION, 0);
Push1(push + 2, 0x20c, pitch);
Push1(push + 4, NV097_NO_OPERATION, 0);
Push1(push + 6, NV097_WAIT_FOR_IDLE, 0);
push += 8;
Push1(push, NV097_NO_OPERATION, 0);
Push1(push + 2, 0x210, renderOffset);
Push1(push + 4, NV097_NO_OPERATION, 0);
Push1(push + 6, NV097_WAIT_FOR_IDLE, 0);
push += 8;
Push1(push, NV097_NO_OPERATION, 0);
Push1(push + 2, 0x214, depthOffset);
Push1(push + 4, NV097_NO_OPERATION, 0);
Push1(push + 6, NV097_WAIT_FOR_IDLE, 0);
push += 8;
}
Push2(push, 0x200, width << 16, height << 16);
push = CommonSetControl0(device, push + 3);
Push1(push, 0x30c, D3D__RenderState[D3DRS_ZENABLE] && device->m_pZBuffer);
Push1(push + 2, 0x32c, D3D__RenderState[D3DRS_STENCILENABLE] && device->m_pZBuffer);
push += 4;
if ((device->m_StateFlags & 0x100) && renderTarget == &device->m_FrameBufferSurfaces[0])
{
device->m_StateFlags &= ~0x100;
Push1(push, 0x130, 0);
push += 2;
}
device->EndPush(push);
CommonSetSurfaceFormat(device);
CommonSetPassthroughProgram(device);
D3DVIEWPORT8 viewport;
viewport.X = 0;
viewport.Y = 0;
viewport.Width = width;
viewport.Height = height;
viewport.MinZ = 0.0f;
viewport.MaxZ = 1.0f;
D3DDevice_SetViewport(&viewport);
D3DDevice_SetRenderStateNotInline(D3DRS_DXT1NOISEENABLE,
D3D__RenderState[D3DRS_DXT1NOISEENABLE]);
return;
}
HRESULT WINAPI D3DDevice_GetVertexBlendModelView(
UINT count,
D3DMATRIX *modelViews,
D3DMATRIX *projectionViewport)
{
CDevice *device = g_pDevice;
if (modelViews)
memcpy(modelViews, device->m_ModelViewTransform, count * sizeof(D3DMATRIX));
if (projectionViewport)
*projectionViewport = device->m_ProjectionViewport;
return (device->m_DirtyFlags & 0x80000000) ? S_OK : S_FALSE;
}
void WINAPI D3DDevice_SetModelView(
const D3DMATRIX *modelView,
const D3DMATRIX *inverseModelView,
const D3DMATRIX *composite)
{
CDevice *device = g_pDevice;
if (composite == NULL)
{
device->m_DirtyFlags &= ~0x80000000;
device->m_DirtyFlags |= 2;
return;
}
device->m_DirtyFlags &= ~2;
device->m_DirtyFlags |= 0x80000000;
DWORD *push = device->StartPush();
DumpMatrixTransposed(push, 0x400480, modelView);
DumpMatrixTransposed(push + 17, 0x400680, composite);
push += 34;
if (inverseModelView)
{
PushCount(push, 0x580, 12);
memcpy(push + 1, inverseModelView, 12 * sizeof(float));
push += 13;
}
device->EndPush(push);
if (!(device->m_StateFlags & 0x10))
device->m_ModelViewTransform[0] = *modelView;
return;
}
/* Display controls assume an initialized device; field sampling is actual GPIO
* I/O and register reads below are genuine volatile MMIO, not byte steering. */
void WINAPI D3DDevice_BlockUntilVerticalBlank(
void)
{
CDevice *device = g_pDevice;
KeClearEvent(&device->m_Miniport.m_VerticalBlankEvent);
KeWaitForSingleObject(&device->m_Miniport.m_VerticalBlankEvent,
UserRequest, UserMode, FALSE, NULL);
return;
}
void WINAPI D3DDevice_SetFlickerFilter(
DWORD filter)
{
AvSendTVEncoderOption(g_pDevice->m_Miniport.m_RegisterBase,
AV_OPTION_FLICKER_FILTER, filter, NULL);
return;
}
void WINAPI D3DDevice_SetSoftDisplayFilter(
BOOL enable)
{
AvSendTVEncoderOption(g_pDevice->m_Miniport.m_RegisterBase,
AV_OPTION_SOFT_DISPLAY_FILTER, enable, NULL);
return;
}
void WINAPI D3DDevice_GetRasterStatus(
D3DRASTER_STATUS *status)
{
CDevice *device = g_pDevice;
DWORD line = REG_RD32(device->m_NvBase, NV2A_RASTER_POSITION)
& NV2A_RASTER_POSITION_MASK;
if (line != 0 && line < device->m_DisplayHeight)
{
status->InVBlank = FALSE;
status->ScanLine = line;
}
else
{
status->InVBlank = TRUE;
status->ScanLine = 0;
}
return;
}
BOOL WINAPI D3DDevice_IsBusy(
void)
{
CDevice *device = g_pDevice;
_HWREG *registerBase = device->m_NvBase;
return (((DWORD)device->HwGet() & 0x0fffffff) !=
((DWORD)device->m_pKickOff & 0x0fffffff)) ||
REG_RD32(registerBase, NV2A_GRAPHICS_STATUS) != 0;
}
void WINAPI D3DDevice_GetDisplayFieldStatus(
D3DFIELD_STATUS *status)
{
CDevice *device = g_pDevice;
status->VBlankCount = device->m_Miniport.m_VBlankCount;
if (device->m_Miniport.m_CurrentAvInfo &
(AV_FLAGS_INTERLACED | AV_FLAGS_FIELD))
{
status->Field = (BYTE)device->m_Miniport.IsOddField() ? D3DFIELD_ODD : D3DFIELD_EVEN;
}
else
{
status->Field = D3DFIELD_PROGRESSIVE;
}
return;
}
void WINAPI D3DDevice_SetTile(
DWORD index,
D3DTILE *tileInfo)
{
CDevice *device = g_pDevice;
D3DTILE tile;
if (tileInfo != NULL && tileInfo->pMemory != NULL)
{
tile = *tileInfo;
if (!(tile.Flags & D3DTILE_FLAGS_ZCOMPRESS))
{
tile.ZOffset = 0;
tile.ZStartTag = 0;
}
device->m_Tile[index] = tile;
device->m_Miniport.CreateTile(index, (DWORD)tile.pMemory & 0x03ffffff,
tile.Size, tile.Pitch, tile.Flags, tile.ZStartTag, tile.ZOffset);
}
else
{
device->m_Miniport.DestroyTile(index, device->m_Tile[index].ZOffset);
memset(&device->m_Tile[index], 0, sizeof(D3DTILE));
}
return;
}
/* Preserves the original partial-bank iteration: i is compared with the batch
* count, not start+count. These APIs assume a valid tag partition/range and an
* idle GPU. Nonzero starting indices can transfer fewer words than requested;
* changing that historical quirk intentionally changes the target bytes. */
void WINAPI D3DDevice_SetTileCompressionTagBits(
DWORD partition,
DWORD address,
DWORD *data,
DWORD count)
{
_HWREG *registerBase = g_pDevice->m_NvBase;
DWORD dataAddress = address % 16;
address /= 16;
while (count)
{
REG_WR32(registerBase, NV2A_TAG_INDEX,
(((NV2A_TAG_PARTITION_SELECT + partition) & NV2A_TAG_SELECT_MASK) << 16)
| ((address++ & NV2A_TAG_ADDRESS_MASK) << 6));
DWORD dwordCount = min(count, 16 - dataAddress);
count -= dwordCount;
for (DWORD i = dataAddress; i < dwordCount; i++)
{
REG_WR32(registerBase, NV2A_TAG_DATA + i * sizeof(DWORD), *data++);
}
dataAddress = 0;
}
return;
}
void WINAPI D3DDevice_GetTileCompressionTagBits(
DWORD partition,
DWORD address,
DWORD *data,
DWORD count)
{
_HWREG *registerBase = g_pDevice->m_NvBase;
DWORD dataAddress = address % 16;
address /= 16;
while (count)
{
REG_WR32(registerBase, NV2A_TAG_INDEX,
(((NV2A_TAG_PARTITION_SELECT + partition) & NV2A_TAG_SELECT_MASK) << 16)
| ((address++ & NV2A_TAG_ADDRESS_MASK) << 6));
DWORD dwordCount = min(count, 16 - dataAddress);
count -= dwordCount;
for (DWORD i = dataAddress; i < dwordCount; i++)
{
*data++ = REG_RD32(registerBase, NV2A_TAG_DATA + i * sizeof(DWORD));
}
dataAddress = 0;
}
return;
}
/* As with GetTileCompressionTagBits, the original first partial-bank bound is
* retained. ZStartTag/ZEndTag must describe a valid aligned hardware-tag range. */
DWORD WINAPI D3DDevice_GetTileCompressionTags(
DWORD startTag,
DWORD endTag)
{
KickOffAndWaitForIdle();
_HWREG *registerBase = g_pDevice->m_NvBase;
DWORD compressedTags = 0;
DWORD dwordsPerPartition = (endTag - startTag) / (4 * 32);
DWORD partitionAddressStart = startTag / (4 * 32);
for (DWORD partition = 0; partition < 4; partition++)
{
DWORD dwordsToRead = dwordsPerPartition;
DWORD dataStart = partitionAddressStart % 16;
for (DWORD addressOffset = 0; dwordsToRead; addressOffset++)
{
REG_WR32(registerBase, NV2A_TAG_INDEX,
(((NV2A_TAG_PARTITION_SELECT + partition) & NV2A_TAG_SELECT_MASK) << 16)
| (((partitionAddressStart / 16 + addressOffset) & NV2A_TAG_ADDRESS_MASK) << 6));
DWORD dwordCount = min(dwordsToRead, 16 - dataStart);
dwordsToRead -= dwordCount;
for (DWORD i = dataStart; i < dwordCount; i++)
{
DWORD data = REG_RD32(registerBase,
NV2A_TAG_DATA + i * sizeof(DWORD));
while (data)
{
compressedTags++;
data &= data - 1;
}
}
dataStart = 0;
}
}
return compressedTags;
}
/* The source must contain the destination mip chain. Compressed logarithmic
* dimensions stop at their minimum block size; faces occupy 128-byte strides. */
void WINAPI D3DDevice_UpdateTexture(
D3DBaseTexture *source,
D3DBaseTexture *destination)
{
DWORD sourceMips = PixelJar::GetMipmapLevelCount(source);
DWORD size;
DWORD sourceSize;
DWORD sourceOffset = 0;
if (sourceMips == 1)
{
DWORD depth = PixelJar::GetDepth(source);
size = PixelJar::GetSlice(destination) * depth;
sourceSize = size;
}
else
{
DWORD texelSize = PixelJar::GetBitsPerPixel(source);
DWORD logMin = MinimumTextureSizeOfD3DFORMAT(PixelJar::GetFormat(source));
size = 0;
DWORD destinationWidth = PixelJar::GetLogWidth(destination);
DWORD destinationHeight = PixelJar::GetLogHeight(destination);
DWORD destinationDepth = PixelJar::GetLogDepth(destination);
DWORD sourceWidth = PixelJar::GetLogWidth(source);
DWORD sourceHeight = PixelJar::GetLogHeight(source);
DWORD sourceDepth = PixelJar::GetLogDepth(source);
DWORD currentWidth = destinationWidth;
DWORD currentHeight = destinationHeight;
DWORD currentDepth = destinationDepth;
for (DWORD i = PixelJar::GetMipmapLevelCount(destination); i != 0; i--)
{
size += (1UL << (currentWidth + currentHeight + currentDepth)) * texelSize / 8;
if (currentWidth > logMin) currentWidth--;
if (currentHeight > logMin) currentHeight--;
if (currentDepth > logMin) currentDepth--;
}
sourceSize = 0;
currentWidth = sourceWidth;
currentHeight = sourceHeight;
currentDepth = sourceDepth;
for (DWORD i = sourceMips; i != 0; i--)
{
if (currentWidth == destinationWidth && currentHeight == destinationHeight &&
currentDepth == destinationDepth)
{
sourceOffset = sourceSize;
}
sourceSize += (1UL << (currentWidth + currentHeight + currentDepth)) * texelSize / 8;
if (currentWidth > logMin) currentWidth--;
if (currentHeight > logMin) currentHeight--;
if (currentDepth > logMin) currentDepth--;
}
}
if (source->Format & D3DFORMAT_CUBEMAP)
{
DWORD sourceStride = (sourceSize + 127) & ~127UL;
DWORD destinationStride = (size + 127) & ~127UL;
for (DWORD i = 0; i < 6; i++)
{
DWORD sourceData = source->Data + i * sourceStride;
DWORD destinationData = destination->Data + i * destinationStride;
GPUCopyMemory(sourceData + sourceOffset, destinationData, size);
}
}
else
{
DWORD sourceData = source->Data;
DWORD destinationData = destination->Data;
GPUCopyMemory(sourceData + sourceOffset, destinationData, size);
}
return;
}
HRESULT WINAPI D3DDevice_LightEnable(
DWORD index,
BOOL enable)
{
CDevice *device = g_pDevice;
if (index >= device->m_LightCount || !(device->m_pLights[index].Flags & 1))
{
D3DLIGHT8 light;
memset(&light, 0, sizeof(light));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Direction.z = 1.0f;
light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
HRESULT result = D3DDevice_SetLight(index, &light);
if (result != S_OK) return result;
}
Light *light = &device->m_pLights[index];
light->Flags |= 4;
/* A real link pointer replaces the historical fabricated previous Light
* object before m_pActiveLights. No before-object typed member access. */
Light **link = &device->m_pActiveLights;
while (*link != NULL)
{
if (*link == light)
{
*link = light->pNext;
break;
}
link = &(*link)->pNext;
}
if (enable)
{
light->pNext = device->m_pActiveLights;
device->m_pActiveLights = light;
}
device->m_DirtyFlags |= 0x20;
return S_OK;
}
void WINAPI D3DDevice_SetVertexBlendModelView(
UINT count,
const D3DMATRIX *modelViews,
const D3DMATRIX *inverseModelViews,
const D3DMATRIX *projectionViewport)
{
CDevice *device = g_pDevice;
if (modelViews == NULL)
{
device->m_DirtyFlags &= ~0x80000000UL;
device->m_DirtyFlags |= 2;
return;
}
device->m_DirtyFlags &= ~2UL;
device->m_DirtyFlags |= 0x80000000UL;
DWORD *push = device->StartPush(count * 34 + 17);
for (UINT i = 0; i < count; i++)
{
DumpMatrixTransposed(push, 0x400480 + i * 0x40, &modelViews[i]);
push += 17;
}
if (inverseModelViews != NULL)
{
for (UINT i = 0; i < count; i++)
{
push[0] = 0x300580 + i * 0x40;
memcpy(push + 1, &inverseModelViews[i], 12 * sizeof(DWORD));
push += 13;
}
}
DumpMatrixTransposed(push, 0x400680, projectionViewport);
device->EndPush(push + 17);
if (!(device->m_StateFlags & 0x10))
{
memcpy(device->m_ModelViewTransform, modelViews, count * sizeof(D3DMATRIX));
device->m_ProjectionViewport = *projectionViewport;
}
return;
}
HRESULT WINAPI D3DDevice_PersistDisplay(
void)
{
CDevice *device = g_pDevice;
D3DSurface *copy = (D3DSurface *)AvGetSavedDataAddress();
if (copy != NULL)
{
MmFreeContiguousMemory(copy);
AvSetSavedDataAddress(NULL);
}
if (device->m_cRefs == 0) return E_FAIL;
while (device->m_Miniport.IsFlipPending())
{
}
D3DSurface *surface = &device->m_FrameBufferSurfaces[1];
D3DFORMAT format = PixelJar::GetFormat(surface);
DWORD width = PixelJar::GetWidth(surface);
DWORD height = PixelJar::GetHeight(surface);
HRESULT result = CreateSurfaceWithContiguousHeader(width, height, format, &copy);
if (FAILED(result)) return result;
D3DDevice_CopyRects(surface, NULL, 0, copy, NULL);
KickOffAndWaitForIdle();
void *registerBase = device->m_Miniport.m_RegisterBase;
DWORD displayMode = device->m_Miniport.m_DisplayMode;
DWORD step = 0;
do
{
step = AvSetDisplayMode(registerBase, step, displayMode,
MapToLinearD3DFORMAT(format), PixelJar::GetPitch(copy), copy->Data);
} while (step);
DWORD allocationSize = MmQueryAllocationSize(copy);
MmPersistContiguousMemory(copy, allocationSize, TRUE);
AvSetSavedDataAddress(copy);
return S_OK;
}