GS:SW: Prepare DrawScanline for being more than 2GB from const data

This commit is contained in:
TellowKrinkle
2026-03-28 17:49:17 -04:00
committed by Ty
parent 5c205d6287
commit 1160758a81
2 changed files with 68 additions and 24 deletions
@@ -16,7 +16,6 @@ using namespace Xbyak;
#pragma clang diagnostic ignored "-Winvalid-offsetof" // We know what we're doing.
#endif
#define _rip_const(cptr) ptr[rip + ((char*)(cptr))]
#define _rip_local(field) ptr[_m_local + offsetof(GSScanlineLocalData, field)]
#define _rip_local_offset(field, offset) ptr[_m_local + offsetof(GSScanlineLocalData, field) + (offset)]
#define _rip_global(field) ptr[_m_local__gd + offsetof(GSScanlineGlobalData, field)]
@@ -68,6 +67,23 @@ using namespace Xbyak;
#define _rip_local_d_p(x) _rip_local_d(x)
#endif
template <typename A, typename B>
static bool IsInRipRelativeRange(A* a, B* b)
{
uptr ai = reinterpret_cast<uptr>(a);
uptr bi = reinterpret_cast<uptr>(b);
sptr diff = static_cast<sptr>(bi - ai);
return diff == static_cast<s32>(diff);
}
template <typename A, typename B>
static s32 CalcOffset(A* from, B* to)
{
uptr ai = reinterpret_cast<uptr>(from);
uptr bi = reinterpret_cast<uptr>(to);
return static_cast<s32>(bi - ai);
}
GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(u64 key, void* code, size_t maxsize)
: GSNewCodeGenerator(code, maxsize)
#ifdef _WIN32
@@ -83,6 +99,7 @@ GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(u64 key, void* code, si
, t2(rcx), t3(rsi)
, _m_local(r8)
#endif
, _m_const(r14)
, _m_local__gd(r12)
, _m_local__gd__vm(t3)
, _m_local__gd__clut(r11)
@@ -90,7 +107,7 @@ GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(u64 key, void* code, si
, _rb(xym5), _ga(xym6), _fm(xym3), _zm(xym4), _fd(xym2), _test(xym15)
, _z(xym8), _f(xym9), _s(xym10), _t(xym11), _q(xym12), _f_rb(xym13), _f_ga(xym14)
{
// Free: r14, r15, rbp, to use, remember to save them.
// Free: r15, rbp, to use, remember to save them.
m_sel.key = key;
use_lod = m_sel.mmin;
if (isYmm)
@@ -315,8 +332,16 @@ void GSDrawScanlineCodeGenerator::Generate()
if (GSDrawScanline::ShouldUseCDrawScanline(m_sel.key))
{
jmp(reinterpret_cast<const void*>(static_cast<void (*)(int, int, int, const GSVertexSW&, GSScanlineLocalData&)>(
&GSDrawScanline::CDrawScanline)));
auto cds = static_cast<void(*)(int, int, int, const GSVertexSW&, GSScanlineLocalData&)>(&GSDrawScanline::CDrawScanline);
if (IsInRipRelativeRange(actual.getCode(), cds))
{
jmp(reinterpret_cast<void*>(cds));
}
else
{
mov(rax, reinterpret_cast<uptr>(cds));
actual.jmp(ptr[rax]);
}
return;
}
@@ -329,6 +354,7 @@ void GSDrawScanlineCodeGenerator::Generate()
push(rdi);
push(r12);
push(r13);
push(r14);
sub(rsp, _64_win_stack_size);
@@ -340,14 +366,22 @@ void GSDrawScanlineCodeGenerator::Generate()
mov(ptr[rsp + _64_rz_rbx], rbx);
mov(ptr[rsp + _64_rz_r12], r12);
mov(ptr[rsp + _64_rz_r13], r13);
mov(ptr[rsp + _64_rz_r14], r14);
#endif
#ifdef _WIN32
// Local (5th arg) is passed on the stack in Windows.
// 32 bytes shadow space less the 5 pushed registers and return address = 80.
mov(_m_local, ptr[rsp + _64_win_stack_size + 80]);
// 32 bytes shadow space less the 6 pushed registers and return address = 88.
mov(_m_local, ptr[rsp + _64_win_stack_size + 88]);
#endif
const void* code = actual.getCode();
const char* codeEnd = reinterpret_cast<const char*>(code) + (1024 * 1024);
if (IsInRipRelativeRange(code, &g_const) && IsInRipRelativeRange(codeEnd, &g_const))
lea(_m_const, ptr[rip + &g_const]);
else
mov(_m_const, reinterpret_cast<uptr>(&g_const));
mov(_m_local__gd, _rip_local(gd));
if (need_clut)
@@ -577,6 +611,7 @@ L("exit");
}
add(rsp, _64_win_stack_size);
pop(r14);
pop(r13);
pop(r12);
pop(rdi);
@@ -586,6 +621,7 @@ L("exit");
mov(rbx, ptr[rsp + _64_rz_rbx]);
mov(r12, ptr[rsp + _64_rz_r12]);
mov(r13, ptr[rsp + _64_rz_r13]);
mov(r14, ptr[rsp + _64_rz_r14]);
#endif
if (isYmm)
vzeroupper();
@@ -623,16 +659,14 @@ void GSDrawScanlineCodeGenerator::Init()
if (isXmm)
{
lea(t1, _rip_const(&g_const.m_test_128b[0]));
shl(a1.cvt32(), 4); // * sizeof(m_test[0])
movdqa(_test, ptr[a1 + t1]);
por(_test, ptr[rax + t1 + (offsetof(GSScanlineConstantData, m_test_128b[7]) - offsetof(GSScanlineConstantData, m_test_128b[0]))]);
movdqa(_test, ptr[a1 + _m_const + offsetof(GSScanlineConstantData, m_test_128b[0])]);
por(_test, ptr[rax + _m_const + offsetof(GSScanlineConstantData, m_test_128b[7])]);
}
else
{
lea(t1, _rip_const(&g_const.m_test_256b[0]));
pmovsxbd(_test, ptr[a1 * 8 + t1]);
pmovsxbd(xym0, ptr[rax * 8 + t1 + (offsetof(GSScanlineConstantData, m_test_256b[15]) - offsetof(GSScanlineConstantData, m_test_256b[0]))]);
pmovsxbd(_test, ptr[a1 * 8 + _m_const + offsetof(GSScanlineConstantData, m_test_256b[0])]);
pmovsxbd(xym0, ptr[rax * 8 + _m_const + offsetof(GSScanlineConstantData, m_test_256b[15])]);
por(_test, xym0);
shl(a1.cvt32(), 5); // * sizeof(m_test[0])
}
@@ -1011,11 +1045,9 @@ void GSDrawScanlineCodeGenerator::Step()
cdqe();
#if USING_XMM
lea(t2, _rip_const(&g_const.m_test_128b[7]));
movdqa(_test, ptr[rax + t2]);
movdqa(_test, ptr[rax + _m_const + offsetof(GSScanlineConstantData, m_test_128b[7])]);
#else
lea(t2, _rip_const(&g_const.m_test_256b[15]));
pmovsxbd(_test, ptr[rax * 8 + t2]);
pmovsxbd(_test, ptr[rax * 8 + _m_const + offsetof(GSScanlineConstantData, m_test_256b[15])]);
#endif
}
}
@@ -1051,7 +1083,7 @@ void GSDrawScanlineCodeGenerator::TestZ(const XYm& temp1, const XYm& temp2)
// zs = GSVector8i(zl, zh);
// zs += VectorI::x80000000();
broadcastsd(temp1, _rip_const(&GSVector4::m_xc1e00000000fffff));
broadcastsd(temp1, ptr[_m_const + CalcOffset(&g_const, &GSVector4::m_xc1e00000000fffff)]);
addpd(xym7, temp1);
addpd(temp1, _z);
@@ -1551,6 +1583,18 @@ void GSDrawScanlineCodeGenerator::Wrap(const XYm& uv0, const XYm& uv1)
}
}
static s32 log2_coeff_offset(int i)
{
// Yay, you can't offsetof with non-constant array indices
uptr base = reinterpret_cast<uptr>(&g_const);
#if USING_XMM
uptr target = reinterpret_cast<uptr>(&g_const.m_log2_coef_128b[i]);
#else
uptr target = reinterpret_cast<uptr>(&g_const.m_log2_coef_256b[i]);
#endif
return static_cast<s32>(target - base);
};
/// Input[x86]: xym4=q, xym2=s, xym3=t
/// Output: _rb, _ga
/// Destroys everything except xym7[x86]
@@ -1604,10 +1648,7 @@ void GSDrawScanlineCodeGenerator::SampleTextureLOD()
auto log2_coeff = [this](int i) -> Address
{
if (isXmm)
return _rip_const(&g_const.m_log2_coef_128b[i]);
else
return _rip_const(&g_const.m_log2_coef_256b[i]);
return ptr[_m_const + log2_coeff_offset(i)];
};
orps(xym4, log2_coeff(3));
@@ -41,9 +41,12 @@ class GSDrawScanlineCodeGenerator : public GSNewCodeGenerator
constexpr static int _64_top = 8 * 0;
// XMM registers will be saved to `rsp + _64_win_xmm_start + id - 6`
// Which will put xmm6 after the temporaries, them xmm7, etc
constexpr static int _64_win_xmm_start = 8 * 2;
constexpr static int _64_win_xmm_start = 8 * 1;
// Align the xmm variables on the stack with this
// Since we don't call any other functions, we don't need to keep rsp itself aligned
constexpr static int _64_win_xmm_align = 8 * 1;
// Windows has no redzone and also has 10 xmm registers to save
constexpr static int _64_win_stack_size = _64_win_xmm_start + 16 * 10;
constexpr static int _64_win_stack_size = _64_win_xmm_align + _64_win_xmm_start + 16 * 10;
#else
// System-V has a redzone so stick everything there
constexpr static int _64_rz_rbx = -8 * 1;
@@ -62,7 +65,7 @@ class GSDrawScanlineCodeGenerator : public GSNewCodeGenerator
/// Note: a2 and t3 are only available on x86-64
/// Outside of Init, usable registers are a0, t0, t1, t2, t3[x64], rax, rbx, rdx, r10+
const AddressReg a0, a1, a2, a3, t0, t1, t2, t3;
const AddressReg _m_local, _m_local__gd, _m_local__gd__vm, _m_local__gd__clut;
const AddressReg _m_local, _m_const, _m_local__gd, _m_local__gd__vm, _m_local__gd__clut;
// If use_lod, m_local.gd->tex, else m_local.gd->tex[0]
const AddressReg _m_local__gd__tex;
/// Available on both x86 and x64, not always valid