Files
SpeedBreakerProject--speedb…/runtime/gpu/imports_video.cpp
T
2026-10-22 14:02:14 +00:00

192 lines
8.2 KiB
C++

// SpeedBreaker runtime. GPL-3.0-or-later (see COPYING).
//
// xboxkrnl video exports (Vd*). Semantics follow Xenia's xboxkrnl_video.cc
// (BSD): mode queries report 1280x720 widescreen HD at 60 Hz; VdSwap writes
// the front buffer's texture fetch constant and an XE_SWAP packet into the
// 64-dword slot the game reserved in its ring buffer.
#include <stdafx.h>
#include <kernel/function.h>
#include <kernel/vmem.h>
#include <game/hfr.h>
#include <report/report.h>
#include "command_processor.h"
void VdQueryVideoMode(XVIDEO_MODE* mode)
{
memset(mode, 0, sizeof(XVIDEO_MODE));
mode->DisplayWidth = 1280;
mode->DisplayHeight = 720;
mode->IsInterlaced = 0;
mode->IsWidescreen = 1;
mode->IsHighDefinition = 1;
mode->RefreshRate = 0x42700000; // 60.0f
mode->VideoStandard = 1; // NTSC
mode->Unknown4A = 0x4A;
mode->Unknown01 = 0x01;
}
uint32_t VdQueryVideoFlags()
{
return 1 | 2; // widescreen, width >= 1024
}
void VdGetCurrentDisplayGamma(be<uint32_t>* type, be<float>* power)
{
*type = 2; // Xenia's default kernel_display_gamma_type
*power = 2.22222233f;
}
void VdGetCurrentDisplayInformation(uint8_t* info)
{
// X_DISPLAY_INFO (0x58 bytes), filled as Xenia does for 1280x720.
memset(info, 0, 0x58);
auto u16 = [&](uint32_t off, uint16_t v) { *reinterpret_cast<be<uint16_t>*>(info + off) = v; };
auto u32 = [&](uint32_t off, uint32_t v) { *reinterpret_cast<be<uint32_t>*>(info + off) = v; };
u16(0x00, 1280); u16(0x02, 720); // front buffer
u32(0x08 + 0x08, 1280); u32(0x08 + 0x0C, 720); // scaler source rect x2, y2
u32(0x08 + 0x10, 1280); u32(0x08 + 0x14, 720); // scaled output
u32(0x08 + 0x18, 1); // vertical filter type
u32(0x08 + 0x28, 1); // horizontal filter type
u16(0x40, 320); u16(0x42, 180); u16(0x44, 320); u16(0x46, 180); // overscan
u16(0x48, 1280); u16(0x4A, 720);
u32(0x4C, 0x42700000); // 60.0f
u16(0x56, 1280);
}
uint32_t VdSetDisplayMode(uint32_t flags) { return 0; }
uint32_t VdInitializeEngines(uint32_t unk0, uint32_t callback, uint32_t arg, uint32_t pfp, uint32_t me) { return 1; }
void VdShutdownEngines() {}
uint32_t VdGetGraphicsAsicID() { return 0x11; }
uint32_t VdEnableDisableClockGating(uint32_t enabled) { return 0; }
uint32_t VdIsHSIOTrainingSucceeded() { return 1; }
uint32_t VdRetrainEDRAMWorker(uint32_t unk0) { return 0; }
uint32_t VdRetrainEDRAM(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) { return 0; }
uint32_t VdInitializeEDRAM() { return 0; }
void VdSetSystemCommandBufferGpuIdentifierAddress(uint32_t address) {}
uint32_t VdCallGraphicsNotificationRoutines(uint32_t unk0, void* args) { return 0; }
void VdSetGraphicsInterruptCallback(uint32_t callback, uint32_t userData)
{
gpu::SetInterruptCallback(callback, userData);
}
void VdInitializeRingBuffer(uint32_t ptr, int32_t sizeLog2)
{
gpu::InitializeRingBuffer(ptr, uint32_t(sizeLog2));
}
void VdEnableRingBufferRPtrWriteBack(uint32_t ptr, int32_t blockSizeLog2)
{
gpu::EnableReadPointerWriteBack(ptr, uint32_t(blockSizeLog2));
}
void VdGetSystemCommandBuffer(be<uint32_t>* p0, be<uint32_t>* p1)
{
memset(p0, 0, 0x94);
*p0 = 0xBEEF0000;
*p1 = 0xBEEF0001;
}
uint32_t VdQuerySystemCommandBuffer(be<uint32_t>* p0, be<uint32_t>* p1)
{
VdGetSystemCommandBuffer(p0, p1);
return 0;
}
uint32_t VdSetSystemCommandBuffer(uint32_t unk) { return 0; }
uint32_t VdInitializeScalerCommandBuffer(uint32_t sourceXY, uint32_t sourceWH, uint32_t outputXY, uint32_t outputWH,
uint32_t frontBufferWH, uint32_t verticalFilter, void* verticalParams, uint32_t horizontalFilter,
void* horizontalParams, void* unk9, be<uint32_t>* dest, uint32_t destCount)
{
for (uint32_t i = 0; i < destCount; i++)
dest[i] = 0x80000000; // type-2 filler packets
return destCount;
}
uint32_t VdPersistDisplay(uint32_t unk0, be<uint32_t>* out)
{
if (out)
*out = vmem::Physical().Alloc(64, 32, vmem::X_MEM_RESERVE | vmem::X_MEM_COMMIT, 0x01, false);
return 1;
}
void VdSwap(be<uint32_t>* buffer, be<uint32_t>* fetch, void* unk2, void* unk3, void* unk4,
be<uint32_t>* frontBuffer, be<uint32_t>* textureFormat, be<uint32_t>* colorSpace,
be<uint32_t>* width, be<uint32_t>* height)
{
// NFSMW_TEST_CRASH: a crash with the game's render loop on the stack.
if (report::g_testCrash.load(std::memory_order_relaxed)) [[unlikely]]
report::TestCrash();
// The front buffer's texture fetch constant (6 dwords), with its base
// address rewritten to a physical page number.
uint32_t words[6];
for (int i = 0; i < 6; i++)
words[i] = fetch[i];
uint32_t frontVirtual = (words[1] >> 12) << 12; // dword_1: base_address in bits 12-31
uint32_t frontPhysical = frontVirtual & 0x1FFFFFFF;
words[1] = (words[1] & 0xFFF) | frontPhysical;
memset(buffer, 0, 64 * 4);
uint32_t o = 0;
buffer[o++] = (0u << 30) | ((6 - 1) << 16) | 0x4800; // type 0: SHADER_CONSTANT_FETCH_00_0, 6 regs
for (int i = 0; i < 6; i++)
buffer[o++] = words[i];
// An in-between picture (game/hfr.h) carries 'HFRB' and its fraction of
// the step: the presenter shows it at its share of the vblank before the
// real picture.
// A step drawn only between (hfr::OwnFraction) carries 'HFRR' and its own
// picture's fraction: shown that much of a vblank before a real one.
const bool between = hfr::InBetween();
const float own = between ? 0.0f : hfr::OwnFraction();
// On a grid that isn't a multiple of 60 Hz, also the refresh it is for.
const int64_t target = hfr::PictureTarget();
const uint32_t count = target ? 8 : between || own > 0.0f ? 6 : 4;
buffer[o++] = (3u << 30) | ((count - 1) << 16) | (0x64 << 8); // type 3: XE_SWAP
buffer[o++] = 0x53574150; // 'SWAP'
buffer[o++] = frontPhysical;
buffer[o++] = *width;
buffer[o++] = *height;
if (between || own > 0.0f || target)
{
const float t = between ? hfr::InBetweenFraction() : own > 0.0f ? own : 1.0f;
uint32_t bits;
memcpy(&bits, &t, 4);
buffer[o++] = between ? 0x48465242 : 0x48465252; // 'HFRB', 'HFRR'
buffer[o++] = bits;
if (target)
{
buffer[o++] = uint32_t(uint64_t(target) >> 32);
buffer[o++] = uint32_t(uint64_t(target));
}
}
while (o < 64)
buffer[o++] = 2u << 30; // type-2 filler
}
GUEST_FUNCTION_HOOK(__imp__VdQueryVideoMode, VdQueryVideoMode);
GUEST_FUNCTION_HOOK(__imp__XGetVideoMode, VdQueryVideoMode);
GUEST_FUNCTION_HOOK(__imp__VdQueryVideoFlags, VdQueryVideoFlags);
GUEST_FUNCTION_HOOK(__imp__VdGetCurrentDisplayGamma, VdGetCurrentDisplayGamma);
GUEST_FUNCTION_HOOK(__imp__VdGetCurrentDisplayInformation, VdGetCurrentDisplayInformation);
GUEST_FUNCTION_HOOK(__imp__VdSetDisplayMode, VdSetDisplayMode);
GUEST_FUNCTION_HOOK(__imp__VdInitializeEngines, VdInitializeEngines);
GUEST_FUNCTION_HOOK(__imp__VdShutdownEngines, VdShutdownEngines);
GUEST_FUNCTION_HOOK(__imp__VdGetGraphicsAsicID, VdGetGraphicsAsicID);
GUEST_FUNCTION_HOOK(__imp__VdEnableDisableClockGating, VdEnableDisableClockGating);
GUEST_FUNCTION_HOOK(__imp__VdIsHSIOTrainingSucceeded, VdIsHSIOTrainingSucceeded);
GUEST_FUNCTION_HOOK(__imp__VdRetrainEDRAMWorker, VdRetrainEDRAMWorker);
GUEST_FUNCTION_HOOK(__imp__VdRetrainEDRAM, VdRetrainEDRAM);
GUEST_FUNCTION_HOOK(__imp__VdInitializeEDRAM, VdInitializeEDRAM);
GUEST_FUNCTION_HOOK(__imp__VdSetSystemCommandBufferGpuIdentifierAddress, VdSetSystemCommandBufferGpuIdentifierAddress);
GUEST_FUNCTION_HOOK(__imp__VdCallGraphicsNotificationRoutines, VdCallGraphicsNotificationRoutines);
GUEST_FUNCTION_HOOK(__imp__VdSetGraphicsInterruptCallback, VdSetGraphicsInterruptCallback);
GUEST_FUNCTION_HOOK(__imp__VdInitializeRingBuffer, VdInitializeRingBuffer);
GUEST_FUNCTION_HOOK(__imp__VdEnableRingBufferRPtrWriteBack, VdEnableRingBufferRPtrWriteBack);
GUEST_FUNCTION_HOOK(__imp__VdGetSystemCommandBuffer, VdGetSystemCommandBuffer);
GUEST_FUNCTION_HOOK(__imp__VdQuerySystemCommandBuffer, VdQuerySystemCommandBuffer);
GUEST_FUNCTION_HOOK(__imp__VdSetSystemCommandBuffer, VdSetSystemCommandBuffer);
GUEST_FUNCTION_HOOK(__imp__VdInitializeScalerCommandBuffer, VdInitializeScalerCommandBuffer);
GUEST_FUNCTION_HOOK(__imp__VdPersistDisplay, VdPersistDisplay);
GUEST_FUNCTION_HOOK(__imp__VdSwap, VdSwap);