diff --git a/common/Pcsx2Defs.h b/common/Pcsx2Defs.h index 2e68b39a6c2f5..111db240871a0 100644 --- a/common/Pcsx2Defs.h +++ b/common/Pcsx2Defs.h @@ -31,6 +31,10 @@ #include "common/emitter/x86_intrin.h" +// The C++ standard doesn't allow `offsetof` to be used on non-constant values (e.g. `offsetof(class, field[i])`) +// Use this in those situations +#define OFFSETOF(a, b) (reinterpret_cast(&(static_cast(0)->b))) + // Renamed ARRAYSIZE to ArraySize -- looks nice and gets rid of Windows.h conflicts (air) // Notes: I'd have used ARRAY_SIZE instead but ran into cross-platform lib conflicts with // that as well. >_< diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 9b5328bc94b67..351d56c136814 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -639,21 +639,12 @@ set(pcsx2GSSources GS/Renderers/HW/GSTextureCache.cpp GS/Renderers/SW/GSDrawScanline.cpp GS/Renderers/SW/GSDrawScanlineCodeGenerator.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx2.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx.cpp - GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx2.cpp + GS/Renderers/SW/GSDrawScanlineCodeGenerator.all.cpp + GS/Renderers/SW/GSNewCodeGenerator.cpp GS/Renderers/SW/GSRasterizer.cpp GS/Renderers/SW/GSRendererSW.cpp GS/Renderers/SW/GSSetupPrimCodeGenerator.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx2.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx.cpp - GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx2.cpp + GS/Renderers/SW/GSSetupPrimCodeGenerator.all.cpp GS/Renderers/SW/GSTextureCacheSW.cpp GS/Renderers/SW/GSTextureSW.cpp GS/Renderers/OpenGL/GLLoader.cpp @@ -679,7 +670,6 @@ set(pcsx2GSHeaders GS/GSDrawingEnvironment.h GS/GSDump.h GS/GS_types.h - GS/GS_codegen.h GS/GS.h GS/GSLocalMemory.h GS/GSLzma.h @@ -712,11 +702,14 @@ set(pcsx2GSHeaders GS/Renderers/HW/GSTextureCache.h GS/Renderers/HW/GSVertexHW.h GS/Renderers/SW/GSDrawScanlineCodeGenerator.h + GS/Renderers/SW/GSDrawScanlineCodeGenerator.all.h GS/Renderers/SW/GSDrawScanline.h + GS/Renderers/SW/GSNewCodeGenerator.h GS/Renderers/SW/GSRasterizer.h GS/Renderers/SW/GSRendererSW.h GS/Renderers/SW/GSScanlineEnvironment.h GS/Renderers/SW/GSSetupPrimCodeGenerator.h + GS/Renderers/SW/GSSetupPrimCodeGenerator.all.h GS/Renderers/SW/GSTextureCacheSW.h GS/Renderers/SW/GSTextureSW.h GS/Renderers/SW/GSVertexSW.h diff --git a/pcsx2/GS/GSAlignedClass.h b/pcsx2/GS/GSAlignedClass.h index fec0b82e0f460..cafed3348237d 100644 --- a/pcsx2/GS/GSAlignedClass.h +++ b/pcsx2/GS/GSAlignedClass.h @@ -18,10 +18,11 @@ template class GSAlignedClass { -public: - GSAlignedClass() {} - virtual ~GSAlignedClass() {} +protected: + GSAlignedClass() = default; + ~GSAlignedClass() = default; +public: void* operator new(size_t size) { return _aligned_malloc(size, i); diff --git a/pcsx2/GS/GS_types.h b/pcsx2/GS/GS_types.h index 9cbfad9151e07..3c0be53d023a6 100644 --- a/pcsx2/GS/GS_types.h +++ b/pcsx2/GS/GS_types.h @@ -110,11 +110,7 @@ extern void vmfree(void* ptr, size_t size); // Convert gcc see define into GS (windows) define #if defined(__AVX2__) - #if defined(__x86_64__) - #define _M_SSE 0x500 // TODO - #else - #define _M_SSE 0x501 - #endif + #define _M_SSE 0x501 #elif defined(__AVX__) #define _M_SSE 0x500 #elif defined(__SSE4_1__) diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.all.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.all.cpp new file mode 100644 index 0000000000000..2b647ba92a30f --- /dev/null +++ b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.all.cpp @@ -0,0 +1,3508 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "GSDrawScanlineCodeGenerator.all.h" +#include "GS/Renderers/Common/GSFunctionMap.h" +#include "GSVertexSW.h" + +using namespace Xbyak; + +// Ease the reading of the code +// Note, there are versions without the _64 prefix that can be used as source (but not destination) operands on both 32 and 64 bit +#define _64_g_const r10 +#define _64_m_local r12 +#define _64_m_local__gd r13 +#define _64_m_local__gd__vm t3 +#define _64_m_local__gd__clut r11 +// If use_lod, m_local.gd->tex, else m_local.gd->tex[0] +#define _64_m_local__gd__tex r14 + +#define _rip_local(field) ((is32 || m_rip) ? ptr[rip + (char*)&m_local.field] : ptr[_m_local + OFFSETOF(GSScanlineLocalData, field)]) +#define _rip_global(field) ((is32 || m_rip) ? ptr[rip + (char*)&m_local.gd->field] : ptr[_m_local__gd + OFFSETOF(GSScanlineGlobalData, field)]) + +/// Executes the given code only if targeting 32-bit +#define ONLY32(code) if (is32) (code) + +/// Executes the given code only if targeting 64-bit +#define ONLY64(code) if (is64) (code) + +/// Combines temporary with either dst64 on 64-bit or src32 on 32-bit +/// Follow up with an ONLY32 save back to src32 +#define REG_64_MEM_32(operation, dst64, temporary, src32) \ + if (is32) \ + operation(temporary, src32); \ + else \ + operation(dst64, temporary) + +/// On AVX, does a v-prefixed separate destination operation +/// On SSE, moves src1 into dst using movdqa, then does the operation +#define THREEARG(operation, dst, src1, ...) \ + do \ + { \ + if (hasAVX) \ + { \ + v##operation(dst, src1, __VA_ARGS__); \ + } \ + else \ + { \ + movdqa(dst, src1); \ + operation(dst, __VA_ARGS__); \ + } \ + } while (0) + +/// On x64, does a 3-operand move, on x86 uses a two-operand SSE-style +#define MOVE_IF_64(operation, dst, src64, ...) \ + do \ + { \ + if (is64) \ + { \ + THREEARG(operation, dst, src64, __VA_ARGS__); \ + } \ + else \ + { \ + operation(dst, __VA_ARGS__); \ + } \ + } while (0) + +#define USING_XMM DRAW_SCANLINE_USING_XMM +#define USING_YMM DRAW_SCANLINE_USING_YMM + +#if _M_SSE >= 0x501 + /// On AVX2, uses the given broadcast to load into the temp register, then applies the given op + /// Otherwise, applies the given op directly + #define BROADCAST_AND_OP(broadcast, op, dst, tmpReg, src) \ + do \ + { \ + broadcast(tmpReg, src); \ + op(dst, tmpReg); \ + } while (0) + #define _rip_local_d(x) _rip_local(d8.x) + #define _rip_local_d_p(x) _rip_local_d(p.x) +#else + /// On AVX2, uses the given broadcast to load into the temp register, then applies the given op + /// Otherwise, applies the given op directly + #define BROADCAST_AND_OP(broadcast, op, dst, tmpReg, src) \ + op(dst, src) + #define _rip_local_d(x) _rip_local(d4.x) + #define _rip_local_d_p(x) _rip_local_d(x) +#endif + +GSDrawScanlineCodeGenerator2::GSDrawScanlineCodeGenerator2(Xbyak::CodeGenerator* base, CPUInfo cpu, void* param, uint64 key) + : _parent(base, cpu) + , m_local(*(GSScanlineLocalData*)param) + , m_rip(false) +#ifdef _WIN32 + , a0(rcx) , a1(rdx) + , a2(r8) , a3(is64 ? r9 : rbx) + , t0(rdi) , t1(rsi) + , t2(is64 ? r8 : rbp), t3(r9) +#else + , a0(is64 ? rdi : rcx), a1(is64 ? rsi : rdx) + , a2(is64 ? rdx : r8), a3(is64 ? rcx : rbx) + , t0(is64 ? r8 : rdi), t1(is64 ? r9 : rsi) + , t2(is64 ? rcx : rbp), t3(is64 ? rsi : r8) +#endif + , _g_const(chooseLocal(&*g_const, _64_g_const)) + , _m_local(chooseLocal(&m_local, _64_m_local)) + , _m_local__gd(chooseLocal(m_local.gd, _64_m_local__gd)) + , _m_local__gd__vm(chooseLocal(m_local.gd->vm, _64_m_local__gd__vm)) + , _rb(xym5), _ga(xym6), _fm(xym3), _zm(xym4), _fd(xym2), _test(is64 ? xym15 : xym7) + , _z(xym8), _f(xym9), _s(xym10), _t(xym11), _q(xym12), _f_rb(xym13), _f_ga(xym14) +{ + m_sel.key = key; + use_lod = m_sel.mmin; + if (isYmm) + ASSERT(hasAVX2); +} + +// MARK: - Helpers + +GSDrawScanlineCodeGenerator2::LocalAddr GSDrawScanlineCodeGenerator2::loadAddress(AddressReg reg, const void* addr) +{ + if (is64) + mov(reg, (size_t)addr); + return choose3264((size_t)addr, reg); +} + +void GSDrawScanlineCodeGenerator2::broadcastf128(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vbroadcastf128(reg, mem); +#else + movaps(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::broadcasti128(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vbroadcasti128(reg, mem); +#else + movdqa(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::broadcastssLocal(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vbroadcastss(reg, mem); +#else + movaps(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::pbroadcastqLocal(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vpbroadcastq(reg, mem); +#else + movdqa(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::pbroadcastdLocal(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vpbroadcastd(reg, mem); +#else + movdqa(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::pbroadcastwLocal(const XYm& reg, const Address& mem) +{ +#if USING_YMM + vpbroadcastw(reg, mem); +#else + movdqa(reg, mem); +#endif +} + +void GSDrawScanlineCodeGenerator2::broadcastGPRToVec(const XYm& vec, const Xbyak::Reg32& gpr) +{ + movd(Xmm(vec.getIdx()), gpr); +#if USING_YMM + vpbroadcastd(vec, Xmm(vec.getIdx())); +#else + pshufd(vec, vec, _MM_SHUFFLE(0, 0, 0, 0)); +#endif +} + +void GSDrawScanlineCodeGenerator2::modulate16(const XYm& a, const Operand& f, uint8 shift) +{ + if (shift == 0) + { + pmulhrsw(a, f); + } + else + { + psllw(a, shift + 1); + pmulhw(a, f); + } +} + +void GSDrawScanlineCodeGenerator2::lerp16(const XYm& a, const XYm& b, const XYm& f, uint8 shift) +{ + psubw(a, b); + modulate16(a, f, shift); + paddw(a, b); +} + +void GSDrawScanlineCodeGenerator2::lerp16_4(const XYm& a, const XYm& b, const XYm& f) +{ + psubw(a, b); + pmullw(a, f); + psraw(a, 4); + paddw(a, b); +} + +void GSDrawScanlineCodeGenerator2::mix16(const XYm& a, const XYm& b, const XYm& temp) +{ + pblendw(a, b, 0xaa); +} + +void GSDrawScanlineCodeGenerator2::clamp16(const XYm& a, const XYm& temp) +{ + if (isXmm) + { + packuswb(a, a); + pmovzxbw(a, a); + } + else + { + packuswb(a, a); + pxor(temp, temp); + punpcklbw(a, temp); + } +} + +void GSDrawScanlineCodeGenerator2::alltrue(const XYm& test) +{ + uint32 mask = test.isYMM() ? 0xffffffff : 0xffff; + pmovmskb(eax, test); + cmp(eax, mask); + je("step", GSCodeGenerator::T_NEAR); +} + +void GSDrawScanlineCodeGenerator2::blend(const XYm& a, const XYm& b, const XYm& mask) +{ + pand(b, mask); + pandn(mask, a); + if (hasAVX) + { + vpor(a, b, mask); + } + else + { + por(b, mask); + movdqa(a, b); + } +} + +void GSDrawScanlineCodeGenerator2::blendr(const XYm& b, const XYm& a, const XYm& mask) +{ + pand(b, mask); + pandn(mask, a); + por(b, mask); +} + +void GSDrawScanlineCodeGenerator2::blend8(const XYm& a, const XYm& b) +{ + pblendvb(a, b /*, xym0 */); +} + +void GSDrawScanlineCodeGenerator2::blend8r(const XYm& b, const XYm& a) +{ + if (hasAVX) + { + vpblendvb(b, a, b, xym0); + } + else + { + pblendvb(a, b); + movdqa(b, a); + } +} + +void GSDrawScanlineCodeGenerator2::split16_2x8(const XYm& l, const XYm& h, const XYm& src) +{ + // l = src & 0xFF; (1 left shift + 1 right shift) + // h = (src >> 8) & 0xFF; (1 right shift) + + if (hasAVX) + { + if (src == h) + { + vpsllw(l, src, 8); + psrlw(h, 8); + } + else if (src == l) + { + vpsrlw(h, src, 8); + psllw(l, 8); + } + else + { + vpsllw(l, src, 8); + vpsrlw(h, src, 8); + } + psrlw(l, 8); + } + else + { + if (src == h) + { + movdqa(l, src); + } + else if (src == l) + { + movdqa(h, src); + } + else + { + movdqa(l, src); + movdqa(h, src); + } + psllw(l, 8); + psrlw(l, 8); + psrlw(h, 8); + } +} + +// MARK: - Main Implementation + +void GSDrawScanlineCodeGenerator2::Generate() +{ + bool need_tex = m_sel.fb && m_sel.tfx != TFX_NONE; + bool need_clut = need_tex && m_sel.tlu; + m_rip = (size_t)getCurr() < 0x80000000; + m_rip &= (size_t)&m_local < 0x80000000; + m_rip &= (size_t)&m_local.gd < 0x80000000; + + if (is32) + { + push(rbx); + push(rsi); + push(rdi); + push(rbp); + } + else + { + push(rbp); + mov(rbp, rsp); // Stack traces look much nicer this way +#ifdef _WIN32 + push(rbx); + push(rsi); + push(rdi); + push(r12); + push(r13); + push(r14); + + sub(rsp, _64_win_stack_size); + + for (int i = 0; i < 10; i++) + { + movdqa(ptr[rsp + _64_win_xmm_start + 16 * i], Xmm(i + 6)); + } +#else + mov(ptr[rsp + _64_rz_rbx], rbx); + if (!m_rip) + { + mov(ptr[rsp + _64_rz_r12], r12); + mov(ptr[rsp + _64_rz_r13], r13); + } + mov(ptr[rsp + _64_rz_r14], r14); + mov(ptr[rsp + _64_rz_r15], r15); +#endif + mov(_64_g_const, (size_t)&*g_const); + if (!m_rip) + { + mov(_64_m_local, (size_t)&m_local); + mov(_64_m_local__gd, _rip_local(gd)); + } + + if (need_clut) + mov(_64_m_local__gd__clut, _rip_global(clut)); + } + + Init(); + + if (!m_sel.edge) + { + align(16); + } + +L("loop"); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // xym0 = z/zi | + // xym2 = s/u (tme) | free + // xym3 = t/v (tme) | free + // xym4 = q (tme) | free + // xym5 = rb (!tme) + // xym6 = ga (!tme) + // xym7 = test | free + // xym15 = | test + + bool tme = m_sel.tfx != TFX_NONE; + + TestZ(tme ? xym5 : xym2, tme ? xym6 : xym3); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = s/u (tme) | free + // xym3 = t/v (tme) | free + // xym4 = q (tme) | free + // xym5 = rb (!tme) + // xym6 = ga (!tme) + // xym7 = test | free + // xym15 = | test + + if (use_lod) + { + SampleTextureLOD(); + } + else + { + SampleTexture(); + } + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = free + // xym3 = free + // xym4 = free + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + AlphaTFX(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) | free + // xym3 = free | free + // xym4 = free | free + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + ReadMask(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) | free + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + TestAlpha(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) | free + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + ColorTFX(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = free + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + Fog(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // xym2 = free + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + ReadFrame(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // ebx = fa + // xym2 = fd + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + TestDestAlpha(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // ebx = fa + // xym2 = fd + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + // xym7 = test | free + // xym15 = | test + + WriteMask(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // t2 = za + // edx = fzm + // ebx = fa + // xym2 = fd + // xym3 = fm + // xym4 = zm + // xym5 = rb + // xym6 = ga + + WriteZBuf(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // edx = fzm + // ebx = fa + // xym2 = fd + // xym3 = fm + // xym4 = free + // xym5 = rb + // xym6 = ga + + AlphaBlend(); + + // a0 = steps + // t1 = fza_base + // t0 = fza_offset + // edx = fzm + // ebx = fa + // xym2 = fd + // xym3 = fm + // xym4 = free + // xym5 = rb + // xym6 = ga + + WriteFrame(); + +L("step"); + + // if(steps <= 0) break; + + if (!m_sel.edge) + { + test(a0.cvt32(), a0.cvt32()); + + jle("exit", CodeGenerator::T_NEAR); + + Step(); + + jmp("loop", CodeGenerator::T_NEAR); + } + +L("exit"); + + + + if (is32) + { + pop(ebp); + pop(edi); + pop(esi); + pop(ebx); + + ret(8); + } + else + { +#ifdef _WIN32 + for (int i = 0; i < 10; i++) + { + movdqa(Xmm(i + 6), ptr[rsp + _64_win_xmm_start + 16 * i]); + } + add(rsp, _64_win_stack_size); + + pop(r14); + pop(r13); + pop(r12); + pop(rdi); + pop(rsi); + pop(rbx); +#else + mov(rbx, ptr[rsp + _64_rz_rbx]); + if (!m_rip) + { + mov(r12, ptr[rsp + _64_rz_r12]); + mov(r13, ptr[rsp + _64_rz_r13]); + } + mov(r14, ptr[rsp + _64_rz_r14]); + mov(r15, ptr[rsp + _64_rz_r15]); +#endif + pop(rbp); + if (isYmm) + vzeroupper(); + ret(); + } +} + +/// Inputs: a0=pixels, a1=left, a2[x64]=top, a3[x64]=v +void GSDrawScanlineCodeGenerator2::Init() +{ + if (!m_sel.notest) + { + // int skip = left & 3; + + mov(ebx, a1.cvt32()); + and(a1.cvt32(), vecints - 1); + + // left -= skip; + + sub(ebx, a1.cvt32()); + + // int steps = pixels + skip - 4; + + lea(a0.cvt32(), ptr[a0 + a1 - vecints]); + + // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; + + mov(eax, a0.cvt32()); + sar(eax, 31); // GH: 31 to extract the sign of the register + and(eax, a0.cvt32()); + if (isXmm) + shl(eax, 4); // * sizeof(m_test[0]) + ONLY64(cdqe()); + + if (isXmm) + { + shl(a1.cvt32(), 4); // * sizeof(m_test[0]) + movdqa(_test, ptr[a1 + _g_const + offsetof(GSScanlineConstantData, m_test_128b[0])]); + por(_test, ptr[rax + _g_const + offsetof(GSScanlineConstantData, m_test_128b[7])]); + } + else + { + pmovsxbd(_test, ptr[a1 * 8 + _g_const + offsetof(GSScanlineConstantData, m_test_256b[0])]); + pmovsxbd(xym0, ptr[rax * 8 + _g_const + offsetof(GSScanlineConstantData, m_test_256b[15])]); + por(_test, xym0); + shl(a1.cvt32(), 5); // * sizeof(m_test[0]) + } + } + else + { + mov(ebx, a1.cvt32()); // left + xor(a1.cvt32(), a1.cvt32()); // skip + lea(a0.cvt32(), ptr[a0 - vecints]); // steps + } + + // a0 = steps + // a1 = skip + // a2[x64] = top + // a3[x64] = v + // rbx = left + // Free: rax, t0, t1 + + if (is64) + { + // GSVector2i* fza_base = &m_local.gd->fzbr[top]; + mov(rax, _rip_global(fzbr)); + lea(t1, ptr[rax + a2 * 8]); + + // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; + mov(rax, _rip_global(fzbc)); + lea(t0, ptr[rax + rbx * 2]); + } + else + { + // GSVector2i* fza_base = &m_local.gd->fzbr[top]; + mov(t1, ptr[rsp + _top]); + lea(t1, ptr[t1 * 8]); + add(t1, ptr[&m_local.gd->fzbr]); + + // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; + lea(t0, ptr[rbx * 2]); + add(t0, ptr[(size_t)&m_local.gd->fzbc]); + } + + if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) + { + // a1 = &m_local.d[skip] // note a1 was (skip << 4) + + if (is64) + { + lea(rax, _rip_local(d)); + lea(a1, ptr[rax + a1 * 8]); + } + else + { + lea(a1, ptr[(size_t)m_local.d + a1 * 8]); + // a3 starts on the stack in x86, we want it in a register + mov(a3, ptr[rsp + _v]); + } + } + + // a0 = steps (rcx | rdi) + // a1 = skip (rdx | rsi) + // a2[x64] = top (r8 | rdx) + // a3 = v (rbx | rcx) + // t0 = fza_offset (rdi | r8 ) + // t1 = fza_base (rsi | r9 ) + // Free: rax + + const XYm& f = is64 ? _f : xym1; + const XYm& z = is64 ? _z : xym0; + + if (m_sel.prim != GS_SPRITE_CLASS) + { + if (m_sel.fwrite && m_sel.fge || m_sel.zb) + { + broadcastf128(z, ptr[a3 + offsetof(GSVertexSW, p)]); // v.p + + if (m_sel.fwrite && m_sel.fge) + { + // f = GSVector4i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); + + cvttps2dq(f, z); + pshufhw(f, f, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(f, f, _MM_SHUFFLE(2, 2, 2, 2)); + paddw(f, ptr[a1 + offsetof(GSScanlineLocalData::skip, f)]); + + if (is32) // _f is shared on x86 + movdqa(ptr[&m_local.temp.f], f); + } + + if (m_sel.zb) + { + // z = vp.zzzz() + m_local.d[skip].z; + shufps(z, z, _MM_SHUFFLE(2, 2, 2, 2)); + if (is64) + { + addps(z, ptr[a1 + offsetof(GSScanlineLocalData::skip, z)]); + } + else + { + movaps(ptr[&m_local.temp.z], z); + movaps(xym2, ptr[a1 + offsetof(GSScanlineLocalData::skip, z)]); + movaps(ptr[&m_local.temp.zo], xym2); + addps(z, xym2); + } + } + } + } + else + { + if (m_sel.ztest) + { + pbroadcastdLocal(z, _rip_local(p.z)); + } + + if (m_sel.fwrite && m_sel.fge && is64) + pbroadcastdLocal(_f, _rip_local(p.f)); + } + + const XYm& vt = xym4; + + if (m_sel.fb) + { + if (m_sel.edge || m_sel.tfx != TFX_NONE) + { + broadcastf128(vt, ptr[a3 + offsetof(GSVertexSW, t)]); // v.t + } + + if (m_sel.edge) + { + // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); + + pshufhw(xym3, vt, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(xym3, xym3, _MM_SHUFFLE(3, 3, 3, 3)); + psrlw(xym3, 9); + + movdqa(_rip_local(temp.cov), xym3); + } + + if (m_sel.tfx != TFX_NONE) + { + // a1 = &m_local.d[skip] + + const XYm& s = is64 ? _s : xym2; + const XYm& t = is64 ? _t : xym3; + + if (m_sel.fst) + { + // GSVector4i vti(vt); + + cvttps2dq(xym6, vt); + + // s = vti.xxxx() + m_local.d[skip].s; + // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; + + pshufd(s, xym6, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(t, xym6, _MM_SHUFFLE(1, 1, 1, 1)); + + paddd(s, ptr[a1 + offsetof(GSScanlineLocalData::skip, s)]); + + if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) + { + paddd(t, ptr[a1 + offsetof(GSScanlineLocalData::skip, t)]); + } + else if (m_sel.ltf) + { + XYm vf = is64 ? xym7 : xym6; + pshuflw(vf, t, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(vf, vf, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(vf, 12); + movdqa(_rip_local(temp.vf), vf); + } + + ONLY32(movdqa(_rip_local(temp.s), s)); + ONLY32(movdqa(_rip_local(temp.t), t)); + } + else + { + const XYm& q = is64 ? _q : vt; + + // s = vt.xxxx() + m_local.d[skip].s; + // t = vt.yyyy() + m_local.d[skip].t; + // q = vt.zzzz() + m_local.d[skip].q; + + if (hasAVX) + { + vshufps(s, vt, vt, _MM_SHUFFLE(0, 0, 0, 0)); + vshufps(t, vt, vt, _MM_SHUFFLE(1, 1, 1, 1)); + vshufps(q, vt, vt, _MM_SHUFFLE(2, 2, 2, 2)); + } + else + { + movaps(s, vt); + movaps(t, vt); + ONLY64(movaps(q, vt)); + + shufps(s, s, _MM_SHUFFLE(0, 0, 0, 0)); + shufps(t, t, _MM_SHUFFLE(1, 1, 1, 1)); + shufps(q, q, _MM_SHUFFLE(2, 2, 2, 2)); + } + + addps(s, ptr[a1 + offsetof(GSScanlineLocalData::skip, s)]); + addps(t, ptr[a1 + offsetof(GSScanlineLocalData::skip, t)]); + addps(q, ptr[a1 + offsetof(GSScanlineLocalData::skip, q)]); + + if (is32) + { + movaps(ptr[&m_local.temp.s], s); + movaps(ptr[&m_local.temp.t], t); + movaps(ptr[&m_local.temp.q], q); + } + } + } + + if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) + { + const XYm& f_rb = is64 ? _f_rb : xym5; + const XYm& f_ga = is64 ? _f_ga : xym6; + if (m_sel.iip) + { + // GSVector4i vc = GSVector4i(v.c); + + if (isXmm) + { + cvttps2dq(xym6, ptr[a3 + offsetof(GSVertexSW, c)]); // v.c + } + else + { + vbroadcastf128(ymm6, ptr[a3 + offsetof(GSVertexSW, c)]); + cvttps2dq(ymm6, ymm6); + } + + // vc = vc.upl16(vc.zwxy()); + + pshufd(xym5, xym6, _MM_SHUFFLE(1, 0, 3, 2)); + punpcklwd(xym6, xym5); + + // rb = vc.xxxx().add16(m_local.d[skip].rb); + // ga = vc.zzzz().add16(m_local.d[skip].ga); + + pshufd(f_rb, xym6, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(f_ga, xym6, _MM_SHUFFLE(2, 2, 2, 2)); + + paddw(f_rb, ptr[a1 + offsetof(GSScanlineLocalData::skip, rb)]); + paddw(f_ga, ptr[a1 + offsetof(GSScanlineLocalData::skip, ga)]); + + ONLY32(movdqa(ptr[&m_local.temp.rb], f_rb)); + ONLY32(movdqa(ptr[&m_local.temp.ga], f_ga)); + } + else if (is64 || m_sel.tfx == TFX_NONE) + { + movdqa(f_rb, _rip_local(c.rb)); + movdqa(f_ga, _rip_local(c.ga)); + } + + ONLY64(movdqa(_rb, _f_rb)); + ONLY64(movdqa(_ga, _f_ga)); + } + } + + if (is64) + { + if (m_sel.fwrite && m_sel.fpsm == 2 && m_sel.dthe) + { + // On linux, a2 is edx which will be used for fzm + // In all case, it will require a mov in dthe code, so let's keep the value on the stack + mov(ptr[rsp + _top], a2); + } + + mov(_64_m_local__gd__vm, _rip_global(vm)); + if (m_sel.fb && m_sel.tfx != TFX_NONE) + { + if (use_lod) + lea(_64_m_local__gd__tex, _rip_global(tex)); + else + mov(_64_m_local__gd__tex, _rip_global(tex)); + } + } +} + +/// Inputs: a0=steps, t0=fza_offset +/// Outputs[x86]: xym0=z xym2=s, xym3=t, xym4=q, xym5=rb, xym6=ga, xym7=test +/// Destroys[x86]: all +/// Destroys[x64]: xym0, xym1, xym2, xym3 +void GSDrawScanlineCodeGenerator2::Step() +{ + // steps -= 4; + + sub(a0.cvt32(), vecints); + + // fza_offset++; + + add(t0, vecsize / 2); + + const XYm& z = is64 ? _z : xym0; + const XYm& f = is64 ? _f : xym1; + + if (m_sel.prim != GS_SPRITE_CLASS) + { + // z += m_local.d4.z; + + if (m_sel.zb) + { + if (is32) + { + broadcastssLocal(z, _rip_local_d_p(z)); + addps(z, _rip_local(temp.zo)); + movaps(_rip_local(temp.zo), z); + addps(z, _rip_local(temp.z)); + } + else + { + BROADCAST_AND_OP(vbroadcastss, addps, z, xym0, _rip_local_d_p(z)); + } + } + + // f = f.add16(m_local.d4.f); + + if (m_sel.fwrite && m_sel.fge) + { + if (is32) + { + pbroadcastwLocal(f, _rip_local_d_p(f)); + paddw(f, _rip_local(temp.f)); + movdqa(_rip_local(temp.f), f); + } + else + { + BROADCAST_AND_OP(vpbroadcastw, paddw, f, xym0, _rip_local_d_p(f)); + } + } + } + else + { + if (is32 && m_sel.ztest) + { + pbroadcastdLocal(z, _rip_local(p.z)); + } + } + + if (m_sel.fb) + { + if (m_sel.tfx != TFX_NONE) + { + if (m_sel.fst) + { + const XYm& stq = is64 ? xym0 : xym4; + // GSVector4i stq = m_local.d4.stq; + + // s += stq.xxxx(); + // if(!sprite) t += st.yyyy(); + + broadcasti128(stq, _rip_local_d(stq)); + + XYm s = is64 ? xym1 : xym2; + pshufd(s, stq, _MM_SHUFFLE(0, 0, 0, 0)); + REG_64_MEM_32(paddd, _s, s, _rip_local(temp.s)); + ONLY32(movdqa(_rip_local(temp.s), s)); + + XYm t = is64 ? xym1 : xym3; + if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) + { + pshufd(t, stq, _MM_SHUFFLE(1, 1, 1, 1)); + REG_64_MEM_32(paddd, _t, t, _rip_local(temp.t)); + ONLY32(movdqa(_rip_local(temp.t), t)); + } + else + { + ONLY32(movdqa(t, _rip_local(temp.t))); + } + } + else + { + const XYm& s = xym2; + const XYm& t = xym3; + const XYm& q = is64 ? xym1 : xym4; + // GSVector4 stq = m_local.d4.stq; + + // s += stq.xxxx(); + // t += stq.yyyy(); + // q += stq.zzzz(); + + if (hasAVX) + { + broadcastf128(q, _rip_local_d(stq)); + + vshufps(s, q, q, _MM_SHUFFLE(0, 0, 0, 0)); + vshufps(t, q, q, _MM_SHUFFLE(1, 1, 1, 1)); + vshufps(q, q, q, _MM_SHUFFLE(2, 2, 2, 2)); + } + else + { + movaps(q, _rip_local_d(stq)); + movaps(s, q); + movaps(t, q); + + shufps(s, s, _MM_SHUFFLE(0, 0, 0, 0)); + shufps(t, t, _MM_SHUFFLE(1, 1, 1, 1)); + shufps(q, q, _MM_SHUFFLE(2, 2, 2, 2)); + } + + REG_64_MEM_32(addps, _s, s, _rip_local(temp.s)); + REG_64_MEM_32(addps, _t, t, _rip_local(temp.t)); + REG_64_MEM_32(addps, _q, q, _rip_local(temp.q)); + + ONLY32(movaps(_rip_local(temp.s), s)); + ONLY32(movaps(_rip_local(temp.t), t)); + ONLY32(movaps(_rip_local(temp.q), q)); + } + } + + if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) + { + if (m_sel.iip) + { + XYm c = is64 ? xym0 : xym7; + // GSVector4i c = m_local.d4.c; + + // rb = rb.add16(c.xxxx()); + // ga = ga.add16(c.yyyy()); + + pbroadcastqLocal(c, _rip_local_d(c)); + + pshufd(_rb, c, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(_ga, c, _MM_SHUFFLE(1, 1, 1, 1)); + + REG_64_MEM_32(paddw, _f_rb, _rb, _rip_local(temp.rb)); + REG_64_MEM_32(paddw, _f_ga, _ga, _rip_local(temp.ga)); + + // FIXME: color may underflow and roll over at the end of the line, if decreasing + + pxor(c, c); + pmaxsw(is64 ? _f_rb : _rb, c); + pmaxsw(is64 ? _f_ga : _ga, c); + + ONLY32(movdqa(_rip_local(temp.rb), _rb)); + ONLY32(movdqa(_rip_local(temp.ga), _ga)); + } + else + { + if (m_sel.tfx == TFX_NONE) + { + ONLY32(movdqa(_rb, ptr[&m_local.c.rb])); + ONLY32(movdqa(_ga, ptr[&m_local.c.ga])); + } + } + + ONLY64(movdqa(_rb, _f_rb)); + ONLY64(movdqa(_ga, _f_ga)); + } + } + + if (!m_sel.notest) + { + // test = m_test[7 + (steps & (steps >> 31))]; + + mov(eax, a0.cvt32()); + sar(eax, 31); // GH: 31 to extract the sign of the register + and(eax, a0.cvt32()); + if (isXmm) + shl(eax, 4); + ONLY64(cdqe()); + +#if USING_XMM + movdqa(_test, ptr[rax + _g_const + offsetof(GSScanlineConstantData, m_test_128b[7])]); +#else + pmovsxbd(_test, ptr[rax * 8 + _g_const + offsetof(GSScanlineConstantData, m_test_256b[15])]); +#endif + } +} + +/// Inputs: xym0[x86]=z, t1=fza_base, t0=fza_offset, _test +/// Outputs: t2=za +/// Destroys: rax, xym0, temp1, temp2 +void GSDrawScanlineCodeGenerator2::TestZ(const XYm& temp1, const XYm& temp2) +{ + if (!m_sel.zb) + { + return; + } + + const XYm& z = is64 ? _z : xym0; + + // int za = fza_base.y + fza_offset->y; + + mov(t2.cvt32(), dword[t1 + 4]); + add(t2.cvt32(), dword[t0 + 4]); + and(t2.cvt32(), HALF_VM_SIZE - 1); + + // GSVector4i zs = zi; + + if (m_sel.prim != GS_SPRITE_CLASS) + { + if (m_sel.zoverflow) + { + // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); + + auto m_half = loadAddress(rax, &GSVector4::m_half); + + if (hasAVX) + vbroadcastss(temp1, ptr[m_half]); + else + movaps(temp1, ptr[m_half]); + mulps(temp1, z); + cvttps2dq(temp1, temp1); + pslld(temp1, 1); + + cvttps2dq(xym0, z); + pcmpeqd(temp2, temp2); + psrld(temp2, 31); + pand(xym0, temp2); + + por(xym0, temp1); + } + else + { + // zs = GSVector4i(z); + + cvttps2dq(xym0, z); + } + + if (m_sel.zclamp) + { + const uint8 amt = (uint8)((m_sel.zpsm & 0x3) * 8); + pcmpeqd(temp1, temp1); + psrld(temp1, amt); + pminsd(xym0, temp1); + } + + if (m_sel.zwrite) + { + movdqa(_rip_local(temp.zs), xym0); + } + } + else + { + ONLY64(movdqa(xym0, _z)); + } + + if (m_sel.ztest) + { + ReadPixel(temp2, temp1, t2); + + if (m_sel.zwrite && m_sel.zpsm < 2) + { + movdqa(_rip_local(temp.zd), temp2); + } + + // zd &= 0xffffffff >> m_sel.zpsm * 8; + + if (m_sel.zpsm) + { + pslld(temp2, static_cast(m_sel.zpsm * 8)); + psrld(temp2, static_cast(m_sel.zpsm * 8)); + } + + if (m_sel.zoverflow || m_sel.zpsm == 0) + { + // GSVector4i o = GSVector4i::x80000000(); + + pcmpeqd(temp1, temp1); + pslld(temp1, 31); + + // GSVector4i zso = zs - o; + // GSVector4i zdo = zd - o; + + psubd(xym0, temp1); + psubd(temp2, temp1); + } + + switch (m_sel.ztst) + { + case ZTST_GEQUAL: + // test |= zso < zdo; // ~(zso >= zdo) + pcmpgtd(temp2, xym0); + por(_test, temp2); + break; + + case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL + // test |= zso <= zdo; // ~(zso > zdo) + pcmpgtd(xym0, temp2); + pcmpeqd(temp1, temp1); + pxor(xym0, temp1); + por(_test, xym0); + break; + } + + alltrue(_test); + } +} + +/// Input[x86]: xym4=q, xym2=s, xym3=t +/// Output: _rb, _ga +/// Destroys everything except xym7[x86] +void GSDrawScanlineCodeGenerator2::SampleTexture() +{ + if (!m_sel.fb || m_sel.tfx == TFX_NONE) + { + return; + } + + + if (is32) + { + mov(ebx, ptr[&m_local.gd->tex[0]]); + + if (m_sel.tlu) + { + mov(edx, ptr[&m_local.gd->clut]); + } + } + + const bool needsMoreRegs = isYmm; + + if (!m_sel.fst) + { + rcpps(xym0, is64 ? _q : xym4); + + MOVE_IF_64(mulps, xym2, _s, xym0); + MOVE_IF_64(mulps, xym3, _t, xym0); + + cvttps2dq(xym2, xym2); + cvttps2dq(xym3, xym3); + + if (m_sel.ltf) + { + // u -= 0x8000; + // v -= 0x8000; + + mov(eax, 0x8000); + broadcastGPRToVec(xym1, eax); + + psubd(xym2, xym1); + psubd(xym3, xym1); + } + } + else + { + ONLY64(movdqa(xym2, _s)); + ONLY64(movdqa(xym3, _t)); + } + + if (m_sel.ltf) + { + const XYm& vf = is64 ? xym7 : xym0; + + // GSVector4i uf = u.xxzzlh().srl16(12); + + pshuflw(xym4, xym2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xym4, xym4, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xym4, 12); + if (is32 && needsMoreRegs) + movdqa(_rip_local(temp.uf), xym4); + + if (m_sel.prim != GS_SPRITE_CLASS) + { + // GSVector4i vf = v.xxzzlh().srl16(12); + + pshuflw(vf, xym3, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(vf, vf, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(vf, 12); + if (is32 || needsMoreRegs) + movdqa(_rip_local(temp.vf), vf); + } + else if (is64 && !needsMoreRegs) + { + movdqa(vf, _rip_local(temp.vf)); + } + } + + // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); + + psrad(xym2, 16); + psrad(xym3, 16); + packssdw(xym2, xym3); + + if (m_sel.ltf) + { + // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); + + pcmpeqd(xym0, xym0); + psrlw(xym0, 15); + THREEARG(paddw, xym3, xym2, xym0); + + // uv0 = Wrap(uv0); + // uv1 = Wrap(uv1); + + Wrap(xym2, xym3); + } + else + { + // uv0 = Wrap(uv0); + + Wrap(xym2); + } + + // xym2 = uv0 + // xym3 = uv1 + // xym4 = uf[x64||!needsMoreRegs] + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym0, xym1, xym5, xym6 + + SampleTexture_TexelReadHelper(0); + + // xym5 = rb (xym5[x86], xym2[x64]) + // xym6 = ga (xym6[x86], xym3[x64]) +} + +/// Input[x86]: xym2=uv0, xym3=uv1 (ltf), xym4=uf (!needsMoreRegs) +/// Input[x64]: xym2=uv0, xym3=uv1 (ltf), xym4=uf, xym7=vf (!needsMoreRegs) +/// Output: _rb, _ga +/// Destroys all registers except outputs, xmm4 and xmm7 +void GSDrawScanlineCodeGenerator2::SampleTexture_TexelReadHelper(int mip_offset) +{ + const bool needsMoreRegs = isYmm; + + // GSVector4i x0 = uv0.upl16(); + // GSVector4i y0 = uv0.uph16() << tw; + + pxor(xym0, xym0); + + THREEARG(punpcklwd, xym5, xym2, xym0); + punpckhwd(xym2, xym0); + pslld(xym2, static_cast(m_sel.tw + 3)); + + // xym0 = 0 + // xym2 = y0 + // xym3 = uv1 (ltf) + // xym4 = uf[x64||!needsMoreRegs] + // xym5 = x0 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym1, xym6 + + if (m_sel.ltf) + { + // GSVector4i x1 = uv1.upl16(); + // GSVector4i y1 = uv1.uph16() << tw; + + THREEARG(punpcklwd, xym1, xym3, xym0); + punpckhwd(xym3, xym0); + pslld(xym3, static_cast(m_sel.tw + 3)); + + // xym1 = x1 + // xym2 = y0 + // xym3 = y1 + // xym4 = uf[x64||!needsMoreRegs] + // xym5 = x0 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym0, xym6 + + // GSVector4i addr00 = y0 + x0; + // GSVector4i addr01 = y0 + x1; + // GSVector4i addr10 = y1 + x0; + // GSVector4i addr11 = y1 + x1; + + THREEARG(paddd, xym0, xym3, xym1); // addr11 + paddd(xym1, xym2); // addr01 + paddd(xym2, xym5); // addr00 + paddd(xym3, xym5); // addr10 + + // xym0 = addr11 + // xym1 = addr01 + // xym2 = addr00 + // xym3 = addr10 + // xym4 = uf[x64||!needsMoreRegs] + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym4, xym5 + + // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); + // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); + // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); + // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); + + const XYm& tmp1 = is64 ? xym7 : xym4; // OK to destroy if needsMoreRegs + const XYm& tmp2 = is64 ? xym4 : xym7; + // d0 d1 d2s0 d3s1 s2 s3 + ReadTexel4(xym5, xym6, xym0, xym2, xym1, xym3, tmp1, tmp2, mip_offset); + + // xym0 = c01 + // xym2 = c10 + // xym4 = uf[x64||!needsMoreRegs] + // xym5 = c11 + // xym6 = c00 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + + if (is32 && needsMoreRegs) + movdqa(xym4, _rip_local(temp.uf)); + + // GSVector4i rb00 = c00 & mask; + // GSVector4i ga00 = (c00 >> 8) & mask; + + split16_2x8(xym3, xym6, xym6); + + // GSVector4i rb01 = c01 & mask; + // GSVector4i ga01 = (c01 >> 8) & mask; + + split16_2x8(xym0, xym1, xym0); + + // xym0 = rb01 + // xym1 = ga01 + // xym2 = c10 + // xym3 = rb00 + // xym4 = uf + // xym5 = c11 + // xym6 = ga00 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + + // rb00 = rb00.lerp16_4(rb01, uf); + // ga00 = ga00.lerp16_4(ga01, uf); + + lerp16_4(xym0, xym3, xym4); + lerp16_4(xym1, xym6, xym4); + + // xym0 = rb00 + // xym1 = ga00 + // xym2 = c10 + // xym4 = uf + // xym5 = c11 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + + // GSVector4i rb10 = c10 & mask; + // GSVector4i ga10 = (c10 >> 8) & mask; + + split16_2x8(xym2, xym3, xym2); + + // GSVector4i rb11 = c11 & mask; + // GSVector4i ga11 = (c11 >> 8) & mask; + + split16_2x8(xym5, xym6, xym5); + + // xym0 = rb00 + // xym1 = ga00 + // xym2 = rb10 + // xym3 = ga10 + // xym4 = uf + // xym5 = rb11 + // xym6 = ga11 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + + // rb10 = rb10.lerp16_4(rb11, uf); + // ga10 = ga10.lerp16_4(ga11, uf); + + lerp16_4(xym5, xym2, xym4); + lerp16_4(xym6, xym3, xym4); + + // xym0 = rb00 + // xym1 = ga00 + // xym5 = rb10 + // xym6 = ga10 + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + + // rb00 = rb00.lerp16_4(rb10, vf); + // ga00 = ga00.lerp16_4(ga10, vf); + + XYm vf = is64 ? xym7 : xym2; + if (needsMoreRegs || is32) + movdqa(vf, _rip_local(temp.vf)); + + lerp16_4(xym5, xym0, vf); + lerp16_4(xym6, xym1, vf); + } + else + { + // GSVector4i addr00 = y0 + x0; + + paddd(xym2, xym5); + + // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); + + ReadTexel1(xym5, xym2, xym0, xym1, mip_offset); + + // GSVector4i mask = GSVector4i::x00ff(); + + // c[0] = c00 & mask; + // c[1] = (c00 >> 8) & mask; + + split16_2x8(xym5, xym6, xym5); + } +} + +void GSDrawScanlineCodeGenerator2::Wrap(const XYm& uv) +{ + // Registers free from SampleTexture + const XYm& mask = xym0; + const XYm& min = xym1; + const XYm& max = xym5; + const XYm& tmp = xym6; + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if (wms_clamp == wmt_clamp) + { + if (wms_clamp) + { + if (region) + { + BROADCAST_AND_OP(vbroadcasti128, pmaxsw, uv, min, _rip_global(t.min)); + } + else + { + pxor(tmp, tmp); + pmaxsw(uv, tmp); + } + + BROADCAST_AND_OP(vbroadcasti128, pminsw, uv, max, _rip_global(t.max)); + } + else + { + BROADCAST_AND_OP(vbroadcasti128, pand, uv, min, _rip_global(t.min)); + + if (region) + { + BROADCAST_AND_OP(vbroadcasti128, por, uv, max, _rip_global(t.max)); + } + } + } + else + { + broadcasti128(min, _rip_global(t.min)); + broadcasti128(max, _rip_global(t.max)); + broadcasti128(mask, _rip_global(t.mask)); + + // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; + THREEARG(pand, tmp, uv, min); + if (region) + por(tmp, max); + // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); + pmaxsw(uv, min); + pminsw(uv, max); + // clamp.blend8(repeat, m_local.gd->t.mask); + blend8(uv, tmp /*, xym0==mask */); + } +} + +/// Destroys[x86]: xym0, xym1, xym2, xym3, xym4[!sse41] +/// Destroys[x64]: xym0, xym1, xym5, xym6, xym7[!sse41] +void GSDrawScanlineCodeGenerator2::Wrap(const XYm& uv0, const XYm& uv1) +{ + // Registers free from SampleTexture + const XYm& mask = xym0; + const XYm& min = xym1; + const XYm& max = xym5; + const XYm& tmp = xym6; + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if (wms_clamp == wmt_clamp) + { + if (wms_clamp) + { + if (region) + { + broadcasti128(min, _rip_global(t.min)); + pmaxsw(uv0, min); + pmaxsw(uv1, min); + } + else + { + pxor(tmp, tmp); + pmaxsw(uv0, tmp); + pmaxsw(uv1, tmp); + } + + broadcasti128(max, _rip_global(t.max)); + pminsw(uv0, max); + pminsw(uv1, max); + } + else + { + broadcasti128(min, _rip_global(t.min)); + pand(uv0, min); + pand(uv1, min); + + if (region) + { + broadcasti128(max, _rip_global(t.max)); + por(uv0, max); + por(uv1, max); + } + } + } + else + { + broadcasti128(min, _rip_global(t.min)); + broadcasti128(max, _rip_global(t.max)); + broadcasti128(mask, _rip_global(t.mask)); + + for (const XYm& uv : {uv0, uv1}) + { + // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; + THREEARG(pand, tmp, uv, min); + if (region) + por(tmp, max); + // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); + pmaxsw(uv, min); + pminsw(uv, max); + // clamp.blend8(repeat, m_local.gd->t.mask); + pblendvb(uv, tmp /*, xym0==mask */); + } + } +} + +/// Input[x86]: xym4=q, xym2=s, xym3=t +/// Output: _rb, _ga +/// Destroys everything except xym7[x86] +void GSDrawScanlineCodeGenerator2::SampleTextureLOD() +{ + if (!m_sel.fb || m_sel.tfx == TFX_NONE) + { + return; + } + + if (is32) + { + push(t2); + + mov(t2, (size_t)m_local.gd->tex); + + if (m_sel.tlu) + { + mov(edx, ptr[&m_local.gd->clut]); + } + } + + const bool needsMoreRegs = isYmm; + + if (is64) + movdqa(xym4, _q); + + if (!m_sel.fst) + { + rcpps(xym0, xym4); + + MOVE_IF_64(mulps, xym2, _s, xym0); + MOVE_IF_64(mulps, xym3, _t, xym0); + + cvttps2dq(xym2, xym2); + cvttps2dq(xym3, xym3); + } + + // xym2 = u + // xym3 = v + // xym4 = q + // xym0 = xym1 = xym5 = xym6 = free + + // TODO: if the fractional part is not needed in round-off mode then there is a faster integer log2 (just take the exp) (but can we round it?) + + if (!m_sel.lcm) + { + // lod = -log2(Q) * (1 << L) + K + + pcmpeqd(xym1, xym1); + psrld(xym1, 25); + THREEARG(pslld, xym0, xym4, 1); + psrld(xym0, 24); + psubd(xym0, xym1); + cvtdq2ps(xym0, xym0); + + // xym0 = (float)(exp(q) - 127) + + pslld(xym4, 9); + psrld(xym4, 9); + + auto log2_coeff = [this](int i) -> Address + { + if (isXmm) + return ptr[_g_const + OFFSETOF(GSScanlineConstantData, m_log2_coef_128b[i])]; + else + return ptr[_g_const + OFFSETOF(GSScanlineConstantData, m_log2_coef_256b[i])]; + }; + + orps(xym4, log2_coeff(3)); + + // xym4 = mant(q) | 1.0f + + if (hasFMA) + { + movaps(xym5, log2_coeff(0)); // c0 + vfmadd213ps(xym5, xym4, log2_coeff(1)); // c0 * xym4 + c1 + vfmadd213ps(xym5, xym4, log2_coeff(2)); // (c0 * xym4 + c1) * xym4 + c2 + subps(xym4, log2_coeff(3)); // xym4 - 1.0f + vfmadd213ps(xym4, xym5, xym0); // ((c0 * xym4 + c1) * xym4 + c2) * (xym4 - 1.0f) + xym0 + } + else + { + THREEARG(mulps, xym5, xym4, log2_coeff(0)); + addps(xym5, log2_coeff(1)); + mulps(xym5, xym4); + subps(xym4, log2_coeff(3)); + addps(xym5, log2_coeff(2)); + mulps(xym4, xym5); + addps(xym4, xym0); + } + + // xym4 = log2(Q) = ((((c0 * xym4) + c1) * xym4) + c2) * (xym4 - 1.0f) + xym0 + + if (hasFMA) + { + movaps(xym5, _rip_global(l)); + vfmadd213ps(xym4, xym5, _rip_global(k)); + } + else + { + mulps(xym4, _rip_global(l)); + addps(xym4, _rip_global(k)); + } + + // xym4 = (-log2(Q) * (1 << L) + K) * 0x10000 + + xorps(xym0, xym0); + minps(xym4, _rip_global(mxl)); + maxps(xym4, xym0); + cvtps2dq(xym4, xym4); + + if (m_sel.mmin == 1) // round-off mode + { + mov(eax, 0x8000); + broadcastGPRToVec(xym0, eax); + paddd(xym4, xym0); + } + + THREEARG(psrld, xym0, xym4, 16); + + movdqa(_rip_local(temp.lod.i), xym0); + /* + vpslld(xym5, xym0, 6); + vpslld(xym6, xym4, 16); + vpsrld(xym6, xym6, 24); + return; + */ + if (m_sel.mmin == 2) // trilinear mode + { + pshuflw(xym1, xym4, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xym1, xym1, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(_rip_local(temp.lod.f), xym1); + } + + // shift u/v/minmax by (int)lod + + if (hasAVX2) + { + vpsravd(xym2, xym2, xym0); + vpsravd(xym3, xym3, xym0); + + movdqa(_rip_local(temp.uv[0]), xym2); + movdqa(_rip_local(temp.uv[1]), xym3); + + // m_local.gd->t.minmax => m_local.temp.uv_minmax[0/1] + + pxor(xym1, xym1); + + broadcasti128(xym4, _rip_global(t.min)); + vpunpcklwd(xym5, xym4, xym1); // minu + vpunpckhwd(xym6, xym4, xym1); // minv + vpsrlvd(xym5, xym5, xym0); + vpsrlvd(xym6, xym6, xym0); + packusdw(xym5, xym6); + + broadcasti128(xym4, _rip_global(t.max)); + vpunpcklwd(xym6, xym4, xym1); // maxu + vpunpckhwd(xym4, xym4, xym1); // maxv + vpsrlvd(xym6, xym6, xym0); + vpsrlvd(xym4, xym4, xym0); + packusdw(xym6, xym4); + + movdqa(_rip_local(temp.uv_minmax[0]), xym5); + movdqa(_rip_local(temp.uv_minmax[1]), xym6); + } + else + { + movq(xym4, _rip_global(t.minmax)); + + THREEARG(punpckhdq, xym6, xym2, xym3); + punpckldq(xym2, xym3); + movdqa(xym5, xym2); + movdqa(xym3, xym6); + + movd(xym0, _rip_local(temp.lod.i.u32[0])); + psrad(xym2, xym0); + THREEARG(psrlw, xym1, xym4, xym0); + movq(_rip_local(temp.uv_minmax[0].u32[0]), xym1); + + movd(xym0, _rip_local(temp.lod.i.u32[1])); + psrad(xym5, xym0); + THREEARG(psrlw, xym1, xym4, xym0); + movq(_rip_local(temp.uv_minmax[1].u32[0]), xym1); + + movd(xym0, _rip_local(temp.lod.i.u32[2])); + psrad(xym3, xym0); + THREEARG(psrlw, xym1, xym4, xym0); + movq(_rip_local(temp.uv_minmax[0].u32[2]), xym1); + + movd(xym0, _rip_local(temp.lod.i.u32[3])); + psrad(xym6, xym0); + THREEARG(psrlw, xym1, xym4, xym0); + movq(_rip_local(temp.uv_minmax[1].u32[2]), xym1); + + punpckldq(xym2, xym3); + punpckhdq(xym5, xym6); + THREEARG(punpckhdq, xym3, xym2, xym5); + punpckldq(xym2, xym5); + + movdqa(_rip_local(temp.uv[0]), xym2); + movdqa(_rip_local(temp.uv[1]), xym3); + + movdqa(xym5, _rip_local(temp.uv_minmax[0])); + movdqa(xym6, _rip_local(temp.uv_minmax[1])); + + if (hasAVX) + { + vpunpcklwd(xym0, xym5, xym6); + vpunpckhwd(xym1, xym5, xym6); + vpunpckldq(xym5, xym0, xym1); + vpunpckhdq(xym6, xym0, xym1); + } + else + { + movdqa(xym0, xym5); + punpcklwd(xym5, xym6); + punpckhwd(xym0, xym6); + movdqa(xym6, xym5); + punpckldq(xym5, xym0); + punpckhdq(xym6, xym0); + } + + movdqa(_rip_local(temp.uv_minmax[0]), xym5); + movdqa(_rip_local(temp.uv_minmax[1]), xym6); + } + } + else + { + // lod = K + + movd(Xmm(xym0.getIdx()), _rip_global(lod.i.u32[0])); + + psrad(xym2, Xmm(xym0.getIdx())); + psrad(xym3, Xmm(xym0.getIdx())); + + movdqa(_rip_local(temp.uv[0]), xym2); + movdqa(_rip_local(temp.uv[1]), xym3); + + movdqa(xym5, _rip_local(temp.uv_minmax[0])); + movdqa(xym6, _rip_local(temp.uv_minmax[1])); + } + + // xym2 = m_local.temp.uv[0] = u (level m) + // xym3 = m_local.temp.uv[1] = v (level m) + // xym5 = minuv + // xym6 = maxuv + + if (m_sel.ltf) + { + const XYm& vf = is64 ? xym7 : xym0; + // u -= 0x8000; + // v -= 0x8000; + + mov(eax, 0x8000); + broadcastGPRToVec(xym4, eax); + + psubd(xym2, xym4); + psubd(xym3, xym4); + + // GSVector4i uf = u.xxzzlh().srl16(1); + + pshuflw(xym4, xym2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xym4, xym4, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xym4, 12); + if (is32 && needsMoreRegs) + movdqa(_rip_local(temp.uf), xym4); + + // GSVector4i vf = v.xxzzlh().srl16(1); + + pshuflw(vf, xym3, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(vf, vf, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(vf, 12); + if (is32 || needsMoreRegs) + movdqa(_rip_local(temp.vf), vf); + } + + // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); + + psrad(xym2, 16); + psrad(xym3, 16); + packssdw(xym2, xym3); + + if (m_sel.ltf) + { + // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); + + pcmpeqd(xym1, xym1); + psrlw(xym1, 15); + THREEARG(paddw, xym3, xym2, xym1); + + // uv0 = Wrap(uv0); + // uv1 = Wrap(uv1); + + WrapLOD(xym2, xym3); + } + else + { + // uv0 = Wrap(uv0); + + WrapLOD(xym2); + } + + // xym2 = uv0 + // xym3 = uv1 (ltf) + // xym4 = uf[x64||!needsMoreRegs] + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym0, xym1, xym5, xym6 + + SampleTexture_TexelReadHelper(0); + + // xym5: rb + // xym6: ga + + + if (m_sel.mmin != 1) // !round-off mode + { + movdqa(_rip_local(temp.trb), xym5); + movdqa(_rip_local(temp.tga), xym6); + + movdqa(xym2, _rip_local(temp.uv[0])); + movdqa(xym3, _rip_local(temp.uv[1])); + + psrad(xym2, 1); + psrad(xym3, 1); + + movdqa(xym5, _rip_local(temp.uv_minmax[0])); + movdqa(xym6, _rip_local(temp.uv_minmax[1])); + + psrlw(xym5, 1); + psrlw(xym6, 1); + + if (m_sel.ltf) + { + const XYm& vf = is64 ? xym7 : xym0; + // u -= 0x8000; + // v -= 0x8000; + + mov(eax, 0x8000); + broadcastGPRToVec(xym4, eax); + + psubd(xym2, xym4); + psubd(xym3, xym4); + + // GSVector4i uf = u.xxzzlh().srl16(1); + + pshuflw(xym4, xym2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xym4, xym4, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xym4, 12); + if (is32 && needsMoreRegs) + movdqa(_rip_local(temp.uf), xym4); + + // GSVector4i vf = v.xxzzlh().srl16(1); + + pshuflw(vf, xym3, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(vf, vf, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(vf, 12); + if (is32 || needsMoreRegs) + movdqa(_rip_local(temp.vf), vf); + } + + // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); + + psrad(xym2, 16); + psrad(xym3, 16); + packssdw(xym2, xym3); + + if (m_sel.ltf) + { + // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); + + pcmpeqd(xym1, xym1); + psrlw(xym1, 15); + THREEARG(paddw, xym3, xym2, xym1); + + // uv0 = Wrap(uv0); + // uv1 = Wrap(uv1); + + WrapLOD(xym2, xym3); + } + else + { + // uv0 = Wrap(uv0); + + WrapLOD(xym2); + } + + // xym2 = uv0 + // xym3 = uv1 (ltf) + // xym4 = uf[x64||!needsMoreRegs] + // xym7 = used[x86] vf[x64&&!needsMoreRegs] + // Free: xym0, xym1, xym5, xym6 + + SampleTexture_TexelReadHelper(1); + + // xym5: rb + // xym6: ga + + movdqa(xym0, m_sel.lcm ? _rip_global(lod.f) : _rip_local(temp.lod.f)); + psrlw(xym0, 1); + + movdqa(xym2, _rip_local(temp.trb)); + movdqa(xym3, _rip_local(temp.tga)); + + lerp16(xym5, xym2, xym0, 0); + lerp16(xym6, xym3, xym0, 0); + } + + if (is32) + pop(t2); +} + +void GSDrawScanlineCodeGenerator2::WrapLOD(const XYm& uv) +{ + // Registers free from SampleTexture + const XYm& mask = xym0; + const XYm& tmp = xym1; + const XYm& min = xym5; + const XYm& max = xym6; + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if (wms_clamp == wmt_clamp) + { + if (wms_clamp) + { + if (region) + { + pmaxsw(uv, min); + } + else + { + pxor(tmp, tmp); + pmaxsw(uv, tmp); + } + + pminsw(uv, max); + } + else + { + pand(uv, min); + + if (region) + { + por(uv, max); + } + } + } + else + { + broadcasti128(mask, _rip_global(t.mask)); + + // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; + THREEARG(pand, tmp, uv, min); + if (region) + por(tmp, max); + // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); + pmaxsw(uv, min); + pminsw(uv, max); + // clamp.blend8(repeat, m_local.gd->t.mask); + blend8(uv, tmp /*, xym0==mask */); + } +} + +void GSDrawScanlineCodeGenerator2::WrapLOD(const XYm& uv0, const XYm& uv1) +{ + // Registers free from SampleTexture + const XYm& mask = xym0; + const XYm& tmp = xym1; + const XYm& min = xym5; + const XYm& max = xym6; + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if (wms_clamp == wmt_clamp) + { + if (wms_clamp) + { + if (region) + { + pmaxsw(uv0, min); + pmaxsw(uv1, min); + } + else + { + pxor(tmp, tmp); + pmaxsw(uv0, tmp); + pmaxsw(uv1, tmp); + } + + pminsw(uv0, max); + pminsw(uv1, max); + } + else + { + pand(uv0, min); + pand(uv1, min); + + if (region) + { + por(uv0, max); + por(uv1, max); + } + } + } + else + { + broadcasti128(mask, _rip_global(t.mask)); + + for (const XYm& uv : {uv0, uv1}) + { + // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; + THREEARG(pand, tmp, uv, min); + if (region) + por(tmp, max); + // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); + pmaxsw(uv, min); + pminsw(uv, max); + // clamp.blend8(repeat, m_local.gd->t.mask);* + pblendvb(uv, tmp /*, xym0==mask */); + } + } +} + +/// Input: _ga +/// Output: xym2[x86]=gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) +/// Destroys: xym0, xym1, xym3[x86], xym4[x86] +void GSDrawScanlineCodeGenerator2::AlphaTFX() +{ + if (!m_sel.fb) + { + return; + } + + const XYm& f_ga = is64 ? _f_ga : xym4; + const XYm& tmpga = is64 ? xym1 : f_ga; + const XYm& tmp = is64 ? xym0 : xym3; + Address _32_gaptr = m_sel.iip ? _rip_local(temp.ga) : _rip_local(c.ga); + + switch (m_sel.tfx) + { + case TFX_MODULATE: + + // GSVector4i ga = iip ? gaf : m_local.c.ga; + + ONLY32(movdqa(f_ga, _32_gaptr)); + + // gat = gat.modulate16<1>(ga).clamp8(); + + modulate16(_ga, f_ga, 1); + + clamp16(_ga, tmp); + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + + if (!m_sel.tcc) + { + MOVE_IF_64(psrlw, tmpga, f_ga, 7); + + mix16(_ga, tmpga, tmp); + } + + break; + + case TFX_DECAL: + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + if (!m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_local.c.ga; + + ONLY32(movdqa(f_ga, _32_gaptr)); + + MOVE_IF_64(psrlw, tmpga, f_ga, 7); + + mix16(_ga, tmpga, tmp); + } + + break; + + case TFX_HIGHLIGHT: + + // GSVector4i ga = iip ? gaf : m_local.c.ga; + + ONLY32(movdqa(f_ga, _32_gaptr)); + ONLY32(movdqa(xym2, f_ga)); // WHY + + // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); + + MOVE_IF_64(psrlw, tmpga, f_ga, 7); + + if (m_sel.tcc) + { + paddusb(tmpga, _ga); + } + + mix16(_ga, tmpga, tmp); + + break; + + case TFX_HIGHLIGHT2: + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + + if (!m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_local.c.ga; + + ONLY32(movdqa(f_ga, _32_gaptr)); + ONLY32(movdqa(xym2, f_ga)); + + MOVE_IF_64(psrlw, tmpga, f_ga, 7); + + mix16(_ga, tmpga, tmp); + } + + break; + + case TFX_NONE: + + // gat = iip ? ga.srl16(7) : ga; + + if (m_sel.iip) + { + MOVE_IF_64(psrlw, _ga, f_ga, 7); + } + + break; + } + + if (m_sel.aa1) + { + // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha + + // FIXME: bios config screen cubes + + if (!m_sel.abe) + { + // a = cov + + if (m_sel.edge) + { + movdqa(xym0, _rip_local(temp.cov)); + } + else + { + pcmpeqd(xym0, xym0); + psllw(xym0, 15); + psrlw(xym0, 8); + } + + mix16(_ga, xym0, xym1); + } + else + { + // a = a == 0x80 ? cov : a + + pcmpeqd(xym0, xym0); + psllw(xym0, 15); + psrlw(xym0, 8); + + if (m_sel.edge) + { + movdqa(xym1, _rip_local(temp.cov)); + } + else + { + movdqa(xym1, xym0); + } + + pcmpeqw(xym0, _ga); + psrld(xym0, 16); + pslld(xym0, 16); + + blend8(_ga, xym1 /*, xym0 */); + } + } +} + +/// Output: _fm, _zm +void GSDrawScanlineCodeGenerator2::ReadMask() +{ + if (m_sel.fwrite) + { + pbroadcastdLocal(_fm, _rip_global(fm)); + } + + if (m_sel.zwrite) + { + pbroadcastdLocal(_zm, _rip_global(zm)); + } +} + +/// Input: _ga, _fm, _zm +/// Destroys: xym0, xym1 +void GSDrawScanlineCodeGenerator2::TestAlpha() +{ + switch (m_sel.atst) + { + case ATST_NEVER: + // t = GSVector4i::xffffffff(); + pcmpeqd(xym1, xym1); + break; + + case ATST_ALWAYS: + return; + + case ATST_LESS: + case ATST_LEQUAL: + // t = (ga >> 16) > m_local.gd->aref; + THREEARG(psrld, xym1, _ga, 16); + BROADCAST_AND_OP(vbroadcasti128, pcmpgtd, xym1, xym0, _rip_global(aref)); + break; + + case ATST_EQUAL: + // t = (ga >> 16) != m_local.gd->aref; + THREEARG(psrld, xym1, _ga, 16); + BROADCAST_AND_OP(vbroadcasti128, pcmpeqd, xym1, xym0, _rip_global(aref)); + pcmpeqd(xym0, xym0); + pxor(xym1, xym0); + break; + + case ATST_GEQUAL: + case ATST_GREATER: + // t = (ga >> 16) < m_local.gd->aref; + THREEARG(psrld, xym0, _ga, 16); + broadcasti128(xym1, _rip_global(aref)); + pcmpgtd(xym1, xym0); + break; + + case ATST_NOTEQUAL: + // t = (ga >> 16) == m_local.gd->aref; + THREEARG(psrld, xym1, _ga, 16); + BROADCAST_AND_OP(vbroadcasti128, pcmpeqd, xym1, xym0, _rip_global(aref)); + break; + } + + switch (m_sel.afail) + { + case AFAIL_KEEP: + // test |= t; + por(_test, xym1); + alltrue(_test); + break; + + case AFAIL_FB_ONLY: + // zm |= t; + por(_zm, xym1); + break; + + case AFAIL_ZB_ONLY: + // fm |= t; + por(_fm, xym1); + break; + + case AFAIL_RGB_ONLY: + // zm |= t; + por(_zm, xym1); + // fm |= t & GSVector4i::xff000000(); + psrld(xym1, 24); + pslld(xym1, 24); + por(_fm, xym1); + break; + } +} + +/// Input: xym2[x86]=gaf, _rb, _ga +/// Destroys: xym0, xym1, xym2 +void GSDrawScanlineCodeGenerator2::ColorTFX() +{ + if (!m_sel.fwrite) + { + return; + } + + const XYm& f_ga = is64 ? _f_ga : xym2; + const XYm& tmpga = is64 ? xym2 : f_ga; + + auto modulate16_1_rb = [&] + { + // GSVector4i rb = iip ? rbf : m_local.c.rb; + if (is64) + modulate16(_rb, _f_rb, 1); + else + modulate16(_rb, m_sel.iip ? _rip_local(temp.rb) : _rip_local(c.rb), 1); + }; + + switch (m_sel.tfx) + { + case TFX_MODULATE: + + // GSVector4i rb = iip ? rbf : m_local.c.rb; + + // rbt = rbt.modulate16<1>(rb).clamp8(); + + modulate16_1_rb(); + + clamp16(_rb, xym0); + + break; + + case TFX_DECAL: + + break; + + case TFX_HIGHLIGHT: + case TFX_HIGHLIGHT2: + + if (m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_local.c.ga; + + ONLY32(movdqa(f_ga, m_sel.iip ? _rip_local(temp.ga) : _rip_local(c.ga))); + } + + // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); + + movdqa(xym1, _ga); + + modulate16(_ga, f_ga, 1); + + pshuflw(tmpga, f_ga, _MM_SHUFFLE(3, 3, 1, 1)); + pshufhw(tmpga, tmpga, _MM_SHUFFLE(3, 3, 1, 1)); + psrlw(tmpga, 7); + + paddw(_ga, tmpga); + + clamp16(_ga, xym0); + + mix16(_ga, xym1, xym0); + + // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); + + modulate16_1_rb(); + + paddw(_rb, tmpga); + + clamp16(_rb, xym0); + + break; + + case TFX_NONE: + + // rbt = iip ? rb.srl16(7) : rb; + + if (m_sel.iip) + { + MOVE_IF_64(psrlw, _rb, _f_rb, 7); + } + + break; + } +} + +/// Input: _rb, _ga +/// Destroys: xym0, xym1, xym2[x86] +void GSDrawScanlineCodeGenerator2::Fog() +{ + if (!m_sel.fwrite || !m_sel.fge) + { + return; + } + + const XYm& f = is64 ? _f : xym0; + const XYm& tmp = is64 ? xym0 : xym2; + + // rb = m_local.gd->frb.lerp16<0>(rb, f); + // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); + + if (m_sel.prim != GS_SPRITE_CLASS) + { + ONLY32(movdqa(f, _rip_local(temp.f))); + } + else + { + ONLY32(pbroadcastwLocal(f, _rip_local(p.f))); + } + + movdqa(xym1, _ga); + + pbroadcastdLocal(tmp, _rip_global(frb)); + lerp16(_rb, tmp, f, 0); + + pbroadcastdLocal(tmp, _rip_global(fga)); + lerp16(_ga, tmp, f, 0); + + mix16(_ga, xym1, xym0); +} + +/// Outputs: _fd, rbx=fa +void GSDrawScanlineCodeGenerator2::ReadFrame() +{ + if (!m_sel.fb) + { + return; + } + + mov(ebx, dword[t1]); + add(ebx, dword[t0]); + and(ebx, HALF_VM_SIZE - 1); + + if (!m_sel.rfb) + { + return; + } + + ReadPixel(_fd, xym0, rbx); +} + +/// Input: _fd, _test +/// Destroys: xym0, xym1 +void GSDrawScanlineCodeGenerator2::TestDestAlpha() +{ + if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) + { + return; + } + + // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); + + if (m_sel.datm) + { + if (m_sel.fpsm == 2) + { + pxor(xym0, xym0); + //vpsrld(xym1, _fd, 15); + THREEARG(pslld, xym1, _fd, 16); + psrad(xym1, 31); + pcmpeqd(xym1, xym0); + } + else + { + pcmpeqd(xym0, xym0); + THREEARG(pxor, xym1, _fd, xym0); + psrad(xym1, 31); + } + } + else + { + if (m_sel.fpsm == 2) + { + THREEARG(pslld, xym1, _fd, 16); + psrad(xym1, 31); + } + else + { + THREEARG(psrad, xym1, _fd, 31); + } + } + + por(_test, xym1); + + alltrue(_test); +} + +/// Input: _fm, _zm, _test +/// Output: edx=fzm +/// Destroys: xym0, xym1 +void GSDrawScanlineCodeGenerator2::WriteMask() +{ + if (m_sel.notest) + { + return; + } + + // fm |= test; + // zm |= test; + + if (m_sel.fwrite) + { + por(_fm, _test); + } + + if (m_sel.zwrite) + { + por(_zm, _test); + } + + // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); + + pcmpeqd(xym1, xym1); + + if (m_sel.fwrite && m_sel.zwrite) + { + THREEARG(pcmpeqd, xym0, xym1, _zm); + pcmpeqd(xym1, _fm); + packssdw(xym1, xym0); + } + else if (m_sel.fwrite) + { + pcmpeqd(xym1, _fm); + packssdw(xym1, xym1); + } + else if (m_sel.zwrite) + { + pcmpeqd(xym1, _zm); + packssdw(xym1, xym1); + } + + pmovmskb(edx, xym1); + + not(edx); +} + +/// Inputs: t2=za, edx=fzm, _zm +/// Destroys: xym0, xym1, xym7 +void GSDrawScanlineCodeGenerator2::WriteZBuf() +{ + if (!m_sel.zwrite) + { + return; + } + + if (m_sel.prim != GS_SPRITE_CLASS) + movdqa(xym1, _rip_local(temp.zs)); + else + pbroadcastdLocal(xym1, _rip_local(p.z)); + + if (m_sel.ztest && m_sel.zpsm < 2) + { + // zs = zs.blend8(zd, zm); + + if (hasAVX) + { + vpblendvb(xym1, xym1, _rip_local(temp.zd), _zm); + } + else + { + movdqa(xym0, _zm); + movdqa(xym7, _rip_local(temp.zd)); + blend8(xym1, xym7 /*, xym0 */); + } + } + + // Clamp Z to ZPSM_FMT_MAX + if (m_sel.zclamp) + { + const uint8 amt = (uint8)((m_sel.zpsm & 0x3) * 8); + pcmpeqd(xym7, xym7); + psrld(xym7, amt); + pminsd(xym1, xym7); + } + + bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; + +#if USING_XMM + WritePixel(xym1, t2, dh, fast, m_sel.zpsm, 1); +#else + WritePixel(xym1, t2, edx, fast, m_sel.zpsm, 1); +#endif +} + +/// Input: _fd, _rb, _ga +/// Destroys: xym0, xym1, xym4, xym7[x86], xym15[x64] +void GSDrawScanlineCodeGenerator2::AlphaBlend() +{ + if (!m_sel.fwrite) + { + return; + } + + if (m_sel.abe == 0 && m_sel.aa1 == 0) + { + return; + } + + const XYm& _dst_rb = xym0; + const XYm& _dst_ga = xym1; + const XYm& tmp1 = _test; + const XYm& tmp2 = xym4; + + if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) + { + switch (m_sel.fpsm) + { + case 0: + case 1: + + // c[2] = fd & mask; + // c[3] = (fd >> 8) & mask; + + split16_2x8(_dst_rb, _dst_ga, _fd); + + break; + + case 2: + + // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); + // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); + + pcmpeqd(tmp1, tmp1); + + psrld(tmp1, 27); // 0x0000001f + THREEARG(pand, _dst_rb, _fd, tmp1); + pslld(_dst_rb, 3); + + pslld(tmp1, 10); // 0x00007c00 + THREEARG(pand, tmp2, _fd, tmp1); + pslld(tmp2, 9); + + por(_dst_rb, tmp2); + + psrld(tmp1, 5); // 0x000003e0 + THREEARG(pand, _dst_ga, _fd, tmp1); + psrld(_dst_ga, 2); + + psllw(tmp1, 10); // 0x00008000 + THREEARG(pand, tmp2, _fd, tmp1); + pslld(tmp2, 8); + + por(_dst_ga, tmp2); + + break; + } + } + + // rb, ga = src rb, ga + // xym0, xym1 = dst rb, ga + // tmp1, tmp2 = free + + if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) + { + movdqa(tmp2, _rb); + } + + if (m_sel.aba != m_sel.abb) + { + // rb = c[aba * 2 + 0]; + + switch (m_sel.aba) + { + case 0: + break; + case 1: + movdqa(_rb, _dst_rb); + break; + case 2: + pxor(_rb, _rb); + break; + } + + // rb = rb.sub16(c[abb * 2 + 0]); + + switch (m_sel.abb) + { + case 0: + psubw(_rb, tmp2); + break; + case 1: + psubw(_rb, _dst_rb); + break; + case 2: + break; + } + + if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) + { + // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; + + switch (m_sel.abc) + { + case 0: + case 1: + pshuflw(tmp1, m_sel.abc ? _dst_ga : _ga, _MM_SHUFFLE(3, 3, 1, 1)); + pshufhw(tmp1, tmp1, _MM_SHUFFLE(3, 3, 1, 1)); + psllw(tmp1, 7); + break; + case 2: + pbroadcastwLocal(tmp1, _rip_global(afix)); + break; + } + + // rb = rb.modulate16<1>(a); + + modulate16(_rb, tmp1, 1); + } + + // rb = rb.add16(c[abd * 2 + 0]); + + switch (m_sel.abd) + { + case 0: + paddw(_rb, tmp2); + break; + case 1: + paddw(_rb, _dst_rb); + break; + case 2: + break; + } + } + else + { + // rb = c[abd * 2 + 0]; + + switch (m_sel.abd) + { + case 0: + break; + case 1: + movdqa(_rb, _dst_rb); + break; + case 2: + pxor(_rb, _rb); + break; + } + } + + if (m_sel.pabe) + { + // mask = (c[1] << 8).sra32(31); + + THREEARG(pslld, xym0, _ga, 8); + psrad(xym0, 31); + + // rb = c[0].blend8(rb, mask); + + blend8r(_rb, tmp2 /*, xym0 */); + } + + // xym0 = pabe mask (>=sse41) + // ga = src ga + // xym1 = dst ga + // rb = rb + // tmp1 = a + // tmp2 = free + + movdqa(tmp2, _ga); + + if (m_sel.aba != m_sel.abb) + { + // ga = c[aba * 2 + 1]; + + switch (m_sel.aba) + { + case 0: + break; + case 1: + movdqa(_ga, _dst_ga); + break; + case 2: + pxor(_ga, _ga); + break; + } + + // ga = ga.sub16(c[abeb * 2 + 1]); + + switch (m_sel.abb) + { + case 0: + psubw(_ga, tmp2); + break; + case 1: + psubw(_ga, _dst_ga); + break; + case 2: + break; + } + + if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) + { + // ga = ga.modulate16<1>(a); + + modulate16(_ga, tmp1, 1); + } + + // ga = ga.add16(c[abd * 2 + 1]); + + switch (m_sel.abd) + { + case 0: + paddw(_ga, tmp2); + break; + case 1: + paddw(_ga, _dst_ga); + break; + case 2: + break; + } + } + else + { + // ga = c[abd * 2 + 1]; + + switch (m_sel.abd) + { + case 0: + break; + case 1: + movdqa(_ga, _dst_ga); + break; + case 2: + pxor(_ga, _ga); + break; + } + } + + // xym0 = pabe mask (>=sse41) + // tmp2 = src ga + // rb = rb + // ga = ga + // xym1, tmp1 = free + + if (m_sel.pabe) + { + psrld(xym0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) + + // ga = c[1].blend8(ga, mask).mix16(c[1]); + + blend8r(_ga, tmp2 /*, xym0 */); + } + else + { + if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx + { + mix16(_ga, tmp2, tmp1); + } + } +} + +/// Input: rbx=fa, rdx=fzm, _fd, _fm +/// Destroys: rax, xym0, xym1, xym5, xym6, xym7[x86], xmm15[x64] +void GSDrawScanlineCodeGenerator2::WriteFrame() +{ + if (!m_sel.fwrite) + { + return; + } + + + const XYm& tmp = is64 ? xym15 : xym7; + + if (m_sel.fpsm == 2 && m_sel.dthe) + { + // y = (top & 3) << 5 + + mov(eax, ptr[rsp + _top]); + and(eax, 3); + shl(eax, 5); + + // rb = rb.add16(m_global.dimx[0 + y]); + // ga = ga.add16(m_global.dimx[1 + y]); + + add(rax, _rip_global(dimx)); + + BROADCAST_AND_OP(vbroadcasti128, paddw, xym5, tmp, ptr[rax + sizeof(GSVector4i) * 0]); + BROADCAST_AND_OP(vbroadcasti128, paddw, xym6, tmp, ptr[rax + sizeof(GSVector4i) * 1]); + } + + if (m_sel.colclamp == 0) + { + // c[0] &= 0x00ff00ff; + // c[1] &= 0x00ff00ff; + + pcmpeqd(tmp, tmp); + psrlw(tmp, 8); + pand(xym5, tmp); + pand(xym6, tmp); + } + + // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); + + THREEARG(punpckhwd, tmp, xym5, xym6); + punpcklwd(xym5, xym6); + packuswb(xym5, tmp); + + if (m_sel.fba && m_sel.fpsm != 1) + { + // fs |= 0x80000000; + + pcmpeqd(tmp, tmp); + pslld(tmp, 31); + por(xym5, tmp); + } + + // tmp1 = fs + // xym4 = fm + // xym6 = fd + + if (m_sel.fpsm == 2) + { + // GSVector4i rb = fs & 0x00f800f8; + // GSVector4i ga = fs & 0x8000f800; + + mov(eax, 0x00f800f8); + broadcastGPRToVec(xym0, eax); + + mov(eax, 0x8000f800); + broadcastGPRToVec(xym1, eax); + + pand(xym0, xym5); + pand(xym1, xym5); + + // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); + + THREEARG(psrld, xym5, xym0, 9); + psrld(xym0, 3); + THREEARG(psrld, xym6, xym1, 16); + psrld(xym1, 6); + + por(xym0, xym1); + por(xym5, xym6); + por(xym5, xym0); + } + + if (m_sel.rfb) + { + // fs = fs.blend(fd, fm); + + blend(xym5, _fd, _fm); // TODO: could be skipped in certain cases, depending on fpsm and fm + } + + bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; + +#if USING_XMM + WritePixel(xym5, rbx, dl, fast, m_sel.fpsm, 0); +#else + WritePixel(xym5, rbx, edx, fast, m_sel.fpsm, 0); +#endif +} + +/// Destroys: tmp[isYmm] +void GSDrawScanlineCodeGenerator2::ReadPixel(const XYm& dst, const XYm& tmp, const AddressReg& addr) +{ + RegExp base = _m_local__gd__vm + addr * 2; +#if USING_XMM + movq(dst, qword[base]); + movhps(dst, qword[base + 8 * 2]); +#else + Xmm dstXmm = Xmm(dst.getIdx()); + Xmm tmpXmm = Xmm(tmp.getIdx()); + movq(dstXmm, qword[base]); + movhps(dstXmm, qword[base + 8 * 2]); + movq(tmpXmm, qword[base + 16 * 2]); + movhps(tmpXmm, qword[base + 24 * 2]); + vinserti128(dst, dst, tmpXmm, 1); +#endif +} + +#if USING_XMM +void GSDrawScanlineCodeGenerator2::WritePixel(const XYm& src_, const AddressReg& addr, const Reg8& mask, bool fast, int psm, int fz) +#else +void GSDrawScanlineCodeGenerator2::WritePixel(const XYm& src_, const AddressReg& addr, const Reg32& mask, bool fast, int psm, int fz) +#endif +{ +#if USING_XMM + const Xmm& src = src_; + int shift = 0; +#else + Xmm src = Xmm(src_.getIdx()); + int shift = fz * 8; +#endif + RegExp base = _m_local__gd__vm + addr * 2; + + if (m_sel.notest) + { + if (fast) + { + movq(qword[base], src); + movhps(qword[base + 8 * 2], src); +#if USING_YMM + vextracti128(src, src_, 1); + movq(qword[base + 16 * 2], src); + movhps(qword[base + 24 * 2], src); +#endif + } + else + { + WritePixel(src, addr, 0, 0, psm); + WritePixel(src, addr, 1, 1, psm); + WritePixel(src, addr, 2, 2, psm); + WritePixel(src, addr, 3, 3, psm); +#if USING_YMM + vextracti128(src, src_, 1); + WritePixel(src, addr, 4, 0, psm); + WritePixel(src, addr, 5, 1, psm); + WritePixel(src, addr, 6, 2, psm); + WritePixel(src, addr, 7, 3, psm); +#endif + } + } + else + { + if (fast) + { + // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); + // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); + + test(mask, 0x0000000f << shift); + je("@f"); + movq(qword[base], src); + L("@@"); + + test(mask, 0x000000f0 << shift); + je("@f"); + movhps(qword[base + 8 * 2], src); + L("@@"); + +#if USING_YMM + vextracti128(src, src_, 1); + + test(mask, 0x000f0000 << shift); + je("@f"); + movq(qword[base + 16 * 2], src); + L("@@"); + + test(mask, 0x00f00000 << shift); + je("@f"); + movhps(qword[base + 24 * 2], src); + L("@@"); +#endif + // vmaskmovps? + } + else + { + // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); + // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); + // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); + // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); + + test(mask, 0x00000003 << shift); + je("@f"); + WritePixel(src, addr, 0, 0, psm); + L("@@"); + + test(mask, 0x0000000c << shift); + je("@f"); + WritePixel(src, addr, 1, 1, psm); + L("@@"); + + test(mask, 0x00000030 << shift); + je("@f"); + WritePixel(src, addr, 2, 2, psm); + L("@@"); + + test(mask, 0x000000c0 << shift); + je("@f"); + WritePixel(src, addr, 3, 3, psm); + L("@@"); + +#if USING_YMM + vextracti128(src, src_, 1); + + test(mask, 0x00030000 << shift); + je("@f"); + WritePixel(src, addr, 4, 0, psm); + L("@@"); + + test(mask, 0x000c0000 << shift); + je("@f"); + WritePixel(src, addr, 5, 1, psm); + L("@@"); + + test(mask, 0x00300000 << shift); + je("@f"); + WritePixel(src, addr, 6, 2, psm); + L("@@"); + + test(mask, 0x00c00000 << shift); + je("@f"); + WritePixel(src, addr, 7, 3, psm); + L("@@"); +#endif + } + } +} + +void GSDrawScanlineCodeGenerator2::WritePixel(const Xmm& src, const AddressReg& addr, uint8 i, uint8 j, int psm) +{ + constexpr int s_offsets[8] = {0, 2, 8, 10, 16, 18, 24, 26}; + + Address dst = ptr[_m_local__gd__vm + addr * 2 + s_offsets[i] * 2]; + + switch (psm) + { + case 0: + if (j == 0) + movd(dst, src); + else + pextrd(dst, src, j); + break; + case 1: + if (j == 0) + movd(eax, src); + else + pextrd(eax, src, j); + xor(eax, dst); + and(eax, 0xffffff); + xor(dst, eax); + break; + case 2: + if (j == 0) + movd(eax, src); + else + pextrw(eax, src, j * 2); + mov(dst, ax); + break; + } +} + +/// Input: +/// rbx = m_local.tex[0] (x86 && !use_lod) +/// t2 = m_local.tex (x86 && use_lod) +/// rdx = m_local.clut (x86 && m_sel.tlu) +/// Destroys: rax, src, tmp1, tmp2 +/// Destroys rbx (!use_lod) +void GSDrawScanlineCodeGenerator2::ReadTexel1(const XYm& dst, const XYm& src, const XYm& tmp1, const XYm& tmp2, int mip_offset) +{ + const XYm no(-1); // Hopefully this will assert if we accidentally use it + ReadTexelImpl(dst, tmp1, src, no, no, no, tmp2, no, 1, mip_offset); +} + +/// Will process addr## to c## from s registers to d registers +/// Destroys contents of s registers +/// Destroys tmp1 if . + */ + +#pragma once + +#include "GSScanlineEnvironment.h" +#include "GSNewCodeGenerator.h" + +#undef _t // Conflict with wx, hopefully no one needs this + +#if _M_SSE >= 0x501 + #define DRAW_SCANLINE_VECTOR_REGISTER Xbyak::Ymm + #define DRAW_SCANLINE_USING_XMM 0 + #define DRAW_SCANLINE_USING_YMM 1 +#else + #define DRAW_SCANLINE_VECTOR_REGISTER Xbyak::Xmm + #define DRAW_SCANLINE_USING_XMM 1 + #define DRAW_SCANLINE_USING_YMM 0 +#endif + +class GSDrawScanlineCodeGenerator2 : public GSNewCodeGenerator +{ + using _parent = GSNewCodeGenerator; + using XYm = DRAW_SCANLINE_VECTOR_REGISTER; + + /// On x86-64 we reserve a bunch of GPRs for holding addresses of locals that would otherwise be hard to reach + /// On x86-32 the same values are just raw 32-bit addresses + using LocalAddr = Choose3264::type; + + constexpr static bool isXmm = std::is_same::value; + constexpr static bool isYmm = std::is_same::value; + constexpr static int wordsize = is64 ? 8 : 4; + constexpr static int vecsize = isXmm ? 16 : 32; + constexpr static int vecsizelog = isXmm ? 4 : 5; + constexpr static int vecints = vecsize / 4; + + +// MARK: - Constants + + constexpr static int _32_args = 16; + constexpr static int _invalid = 0xaaaaaaaa; +#ifdef _WIN32 + 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; + // 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; +#else + // System-V has a redzone so stick everything there + constexpr static int _64_rz_rbx = -8 * 1; + constexpr static int _64_rz_r12 = -8 * 2; + constexpr static int _64_rz_r13 = -8 * 3; + constexpr static int _64_rz_r14 = -8 * 4; + constexpr static int _64_rz_r15 = -8 * 5; + constexpr static int _64_top = -8 * 6; +#endif + constexpr static int _top = is64 ? _64_top : _32_args + 4; + constexpr static int _v = is64 ? _invalid : _32_args + 8; + + GSScanlineSelector m_sel; + GSScanlineLocalData& m_local; + bool m_rip; + bool use_lod; + + const XYm xym0{0}, xym1{1}, xym2{2}, xym3{3}, xym4{4}, xym5{5}, xym6{6}, xym7{7}, xym8{8}, xym9{9}, xym10{10}, xym11{11}, xym12{12}, xym13{13}, xym14{14}, xym15{15}; + /// 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 LocalAddr _g_const, _m_local, _m_local__gd, _m_local__gd__vm; + /// Available on both x86 and x64, not always valid + const XYm _rb, _ga, _fm, _zm, _fd, _test; + /// Always valid if needed, x64 only + const XYm _z, _f, _s, _t, _q, _f_rb, _f_ga; + + /// Returns the first arg on 32-bit, second on 64-bit + static LocalAddr chooseLocal(const void* addr32, AddressReg reg64) + { + return choose3264((size_t)addr32, reg64); + } + +public: + GSDrawScanlineCodeGenerator2(Xbyak::CodeGenerator* base, CPUInfo cpu, void* param, uint64 key); + void Generate(); + +private: + /// Loads the given address into the given register if needed, and returns something that can be used in a `ptr[]` + LocalAddr loadAddress(AddressReg reg, const void* addr); + /// Broadcast 128 bits of floats from memory to the whole register, whatever size that register might be + void broadcastf128(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast 128 bits of integers from memory to the whole register, whatever size that register might be + void broadcasti128(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast a floating-point variable stored in GSScanlineLocalData to the whole register + /// On YMM registers this will be a broadcast from a 32-bit value + /// On XMM registers this will be a load of a full 128-bit value, with the broadcast happening before storing to the local data + void broadcastssLocal(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast a qword variable stored in GSScanlineLocalData to the whole register + /// On YMM registers this will be a broadcast from a 64-bit value + /// On XMM registers this will be a load of a full 128-bit value, with the broadcast happening before storing to the local data + void pbroadcastqLocal(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast a dword variable stored in GSScanlineLocalData to the whole register + /// On YMM registers this will be a broadcast from a 32-bit value + /// On XMM registers this will be a load of a full 128-bit value, with the broadcast happening before storing to the local data + void pbroadcastdLocal(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast a word variable stored in GSScanlineLocalData to the whole register + /// On YMM registers this will be a broadcast from a 16-bit value + /// On XMM registers this will be a load of a full 128-bit value, with the broadcast happening before storing to the local data + void pbroadcastwLocal(const XYm& reg, const Xbyak::Address& mem); + /// Broadcast a 32-bit GPR to a vector register + void broadcastGPRToVec(const XYm& vec, const Xbyak::Reg32& gpr); + void modulate16(const XYm& a, const Xbyak::Operand& f, uint8 shift); + void lerp16(const XYm& a, const XYm& b, const XYm& f, uint8 shift); + void lerp16_4(const XYm& a, const XYm& b, const XYm& f); + void mix16(const XYm& a, const XYm& b, const XYm& temp); + void clamp16(const XYm& a, const XYm& temp); + void alltrue(const XYm& test); + void blend(const XYm& a, const XYm& b, const XYm& mask); + void blendr(const XYm& b, const XYm& a, const XYm& mask); + void blend8(const XYm& a, const XYm& b); + void blend8r(const XYm& b, const XYm& a); + void split16_2x8(const XYm& l, const XYm& h, const XYm& src); + + void Init(); + void Step(); + void TestZ(const XYm& temp1, const XYm& temp2); + void SampleTexture(); + void SampleTexture_TexelReadHelper(int mip_offset); + void Wrap(const XYm& uv); + void Wrap(const XYm& uv0, const XYm& uv1); + void SampleTextureLOD(); + void WrapLOD(const XYm& uv); + void WrapLOD(const XYm& uv0, const XYm& uv1); + void AlphaTFX(); + void ReadMask(); + void TestAlpha(); + void ColorTFX(); + void Fog(); + void ReadFrame(); + void TestDestAlpha(); + void WriteMask(); + void WriteZBuf(); + void AlphaBlend(); + void WriteFrame(); + void ReadPixel(const XYm& dst, const XYm& tmp, const AddressReg& addr); +#if DRAW_SCANLINE_USING_XMM + void WritePixel(const XYm& src_, const AddressReg& addr, const Xbyak::Reg8& mask, bool fast, int psm, int fz); +#else + void WritePixel(const XYm& src_, const AddressReg& addr, const Xbyak::Reg32& mask, bool fast, int psm, int fz); +#endif + void WritePixel(const Xmm& src, const AddressReg& addr, uint8 i, uint8 j, int psm); + void ReadTexel1(const XYm& dst, const XYm& src, const XYm& tmp1, const XYm& tmp2, int mip_offset); + void ReadTexel4( + const XYm& d0, const XYm& d1, + const XYm& d2s0, const XYm& d3s1, + const XYm& s2, const XYm& s3, + const XYm& tmp1, const XYm& tmp2, + int mip_offset); + void ReadTexelImpl( + const XYm& d0, const XYm& d1, + const XYm& d2s0, const XYm& d3s1, + const XYm& s2, const XYm& s3, + const XYm& tmp1, const XYm& tmp2, + int pixels, int mip_offset); + void ReadTexelImplLoadTexLOD(int lod, int mip_offset); + void ReadTexelImplYmm( + const Ymm& d0, const Ymm& d1, + const Ymm& d2s0, const Ymm& d3s1, + const Ymm& s2, const Ymm& s3, + const Ymm& tmp, + int pixels, int mip_offset); + void ReadTexelImplSSE4( + const Xmm& d0, const Xmm& d1, + const Xmm& d2s0, const Xmm& d3s1, + const Xmm& s2, const Xmm& s3, + int pixels, int mip_offset); + void ReadTexelImpl(const Xmm& dst, const Xmm& addr, uint8 i, bool texInA3, bool preserveDst); +}; diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.cpp index ba1ad03230795..c9f23ff64c559 100644 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.cpp +++ b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.cpp @@ -15,17 +15,8 @@ #include "PrecompiledHeader.h" #include "GSDrawScanlineCodeGenerator.h" +#include "GSDrawScanlineCodeGenerator.all.h" -#if _M_SSE >= 0x501 -#else -void GSDrawScanlineCodeGenerator::Generate() -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - Generate_AVX(); - else - Generate_SSE(); -} -#endif GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(void* param, uint64 key, void* code, size_t maxsize) : GSCodeGenerator(code, maxsize) @@ -37,227 +28,5 @@ GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(void* param, uint64 key if (m_sel.breakpoint) db(0xCC); - try - { - Generate(); - } - catch (std::exception& e) - { - fprintf(stderr, "ERR:GSDrawScanlineCodeGenerator %s\n", e.what()); - } -} - -void GSDrawScanlineCodeGenerator::modulate16(const Xmm& a, const Operand& f, uint8 shift) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - if (shift == 0) - { - vpmulhrsw(a, f); - } - else - { - vpsllw(a, shift + 1); - vpmulhw(a, f); - } - } - else - { - if (shift == 0 && m_cpu.has(Xbyak::util::Cpu::tSSSE3)) - { - pmulhrsw(a, f); - } - else - { - psllw(a, shift + 1); - pmulhw(a, f); - } - } -} - -void GSDrawScanlineCodeGenerator::lerp16(const Xmm& a, const Xmm& b, const Xmm& f, uint8 shift) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpsubw(a, b); - modulate16(a, f, shift); - vpaddw(a, b); - } - else - { - psubw(a, b); - modulate16(a, f, shift); - paddw(a, b); - } -} - -void GSDrawScanlineCodeGenerator::lerp16_4(const Xmm& a, const Xmm& b, const Xmm& f) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpsubw(a, b); - vpmullw(a, f); - vpsraw(a, 4); - vpaddw(a, b); - } - else - { - psubw(a, b); - pmullw(a, f); - psraw(a, 4); - paddw(a, b); - } -} - -void GSDrawScanlineCodeGenerator::mix16(const Xmm& a, const Xmm& b, const Xmm& temp) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpblendw(a, b, 0xaa); - } - else - { - pblendw(a, b, 0xaa); - } -} - -void GSDrawScanlineCodeGenerator::clamp16(const Xmm& a, const Xmm& temp) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpackuswb(a, a); - -#if _M_SSE >= 0x501 - // Greg: why ? - if (m_cpu.has(Xbyak::util::Cpu::tAVX2)) - { - ASSERT(a.isYMM()); - vpermq(Ymm(a.getIdx()), Ymm(a.getIdx()), _MM_SHUFFLE(3, 1, 2, 0)); // this sucks - } -#endif - - vpmovzxbw(a, a); - } - else - { - packuswb(a, a); - pmovzxbw(a, a); - } -} - -void GSDrawScanlineCodeGenerator::alltrue(const Xmm& test) -{ - uint32 mask = test.isYMM() ? 0xffffffff : 0xffff; - - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpmovmskb(eax, test); - cmp(eax, mask); - je("step", T_NEAR); - } - else - { - pmovmskb(eax, test); - cmp(eax, mask); - je("step", T_NEAR); - } -} - -void GSDrawScanlineCodeGenerator::blend(const Xmm& a, const Xmm& b, const Xmm& mask) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpand(b, mask); - vpandn(mask, a); - vpor(a, b, mask); - } - else - { - pand(b, mask); - pandn(mask, a); - por(b, mask); - movdqa(a, b); - } -} - -void GSDrawScanlineCodeGenerator::blendr(const Xmm& b, const Xmm& a, const Xmm& mask) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpand(b, mask); - vpandn(mask, a); - vpor(b, mask); - } - else - { - pand(b, mask); - pandn(mask, a); - por(b, mask); - } -} - -void GSDrawScanlineCodeGenerator::blend8(const Xmm& a, const Xmm& b) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - vpblendvb(a, a, b, xmm0); - else - pblendvb(a, b); -} - -void GSDrawScanlineCodeGenerator::blend8r(const Xmm& b, const Xmm& a) -{ - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - vpblendvb(b, a, b, xmm0); - } - else - { - pblendvb(a, b); - movdqa(b, a); - } -} - -void GSDrawScanlineCodeGenerator::split16_2x8(const Xmm& l, const Xmm& h, const Xmm& src) -{ - // l = src & 0xFF; (1 left shift + 1 right shift) - // h = (src >> 8) & 0xFF; (1 right shift) - - if (m_cpu.has(Xbyak::util::Cpu::tAVX)) - { - if (src == h) - { - vpsllw(l, src, 8); - vpsrlw(h, 8); - } - else if (src == l) - { - vpsrlw(h, src, 8); - vpsllw(l, 8); - } - else - { - vpsllw(l, src, 8); - vpsrlw(h, src, 8); - } - vpsrlw(l, 8); - } - else - { - if (src == h) - { - movdqa(l, src); - } - else if (src == l) - { - movdqa(h, src); - } - else - { - movdqa(l, src); - movdqa(h, src); - } - psllw(l, 8); - psrlw(l, 8); - psrlw(h, 8); - } + GSDrawScanlineCodeGenerator2(this, CPUInfo(m_cpu), (void*)&m_local, m_sel.key).Generate(); } diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.h b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.h index 253bcb678ae6b..70eda1b31ad27 100644 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.h +++ b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.h @@ -27,117 +27,12 @@ class GSDrawScanlineCodeGenerator : public GSCodeGenerator { - typedef Xbyak::Ymm Ymm; - typedef Xbyak::Xmm Xmm; - typedef Xbyak::Reg8 Reg8; - typedef Xbyak::Operand Operand; - void operator=(const GSDrawScanlineCodeGenerator&); GSScanlineSelector m_sel; GSScanlineLocalData& m_local; bool m_rip; - void Generate(); - -#if _M_SSE >= 0x501 - - void Init(); - void Step(); - void TestZ(const Ymm& temp1, const Ymm& temp2); - void SampleTexture(); - void Wrap(const Ymm& uv0); - void Wrap(const Ymm& uv0, const Ymm& uv1); - void SampleTextureLOD(); - void WrapLOD(const Ymm& uv0); - void WrapLOD(const Ymm& uv0, const Ymm& uv1); - void AlphaTFX(); - void ReadMask(); - void TestAlpha(); - void ColorTFX(); - void Fog(); - void ReadFrame(); - void TestDestAlpha(); - void WriteMask(); - void WriteZBuf(); - void AlphaBlend(); - void WriteFrame(); - void ReadPixel(const Ymm& dst, const Ymm& temp, const RegLong& addr); - void WritePixel(const Ymm& src, const Ymm& temp, const RegLong& addr, const Xbyak::Reg32& mask, bool fast, int psm, int fz); - void WritePixel(const Xmm& src, const RegLong& addr, uint8 i, uint8 j, int psm); - void ReadTexel(int pixels, int mip_offset = 0); - void ReadTexel(const Ymm& dst, const Ymm& addr, uint8 i); - -#else - - void Generate_SSE(); - void Init_SSE(); - void Step_SSE(); - void TestZ_SSE(const Xmm& temp1, const Xmm& temp2); - void SampleTexture_SSE(); - void Wrap_SSE(const Xmm& uv0); - void Wrap_SSE(const Xmm& uv0, const Xmm& uv1); - void SampleTextureLOD_SSE(); - void WrapLOD_SSE(const Xmm& uv0); - void WrapLOD_SSE(const Xmm& uv0, const Xmm& uv1); - void AlphaTFX_SSE(); - void ReadMask_SSE(); - void TestAlpha_SSE(); - void ColorTFX_SSE(); - void Fog_SSE(); - void ReadFrame_SSE(); - void TestDestAlpha_SSE(); - void WriteMask_SSE(); - void WriteZBuf_SSE(); - void AlphaBlend_SSE(); - void WriteFrame_SSE(); - void ReadPixel_SSE(const Xmm& dst, const RegLong& addr); - void WritePixel_SSE(const Xmm& src, const RegLong& addr, const Reg8& mask, bool fast, int psm, int fz); - void WritePixel_SSE(const Xmm& src, const RegLong& addr, uint8 i, int psm); - void ReadTexel_SSE(int pixels, int mip_offset = 0); - void ReadTexel_SSE(const Xmm& dst, const Xmm& addr, uint8 i); - - void Generate_AVX(); - void Init_AVX(); - void Step_AVX(); - void TestZ_AVX(const Xmm& temp1, const Xmm& temp2); - void SampleTexture_AVX(); - void Wrap_AVX(const Xmm& uv0); - void Wrap_AVX(const Xmm& uv0, const Xmm& uv1); - void SampleTextureLOD_AVX(); - void WrapLOD_AVX(const Xmm& uv0); - void WrapLOD_AVX(const Xmm& uv0, const Xmm& uv1); - void AlphaTFX_AVX(); - void ReadMask_AVX(); - void TestAlpha_AVX(); - void ColorTFX_AVX(); - void Fog_AVX(); - void ReadFrame_AVX(); - void TestDestAlpha_AVX(); - void WriteMask_AVX(); - void WriteZBuf_AVX(); - void AlphaBlend_AVX(); - void WriteFrame_AVX(); - void ReadPixel_AVX(const Xmm& dst, const RegLong& addr); - void WritePixel_AVX(const Xmm& src, const RegLong& addr, const Reg8& mask, bool fast, int psm, int fz); - void WritePixel_AVX(const Xmm& src, const RegLong& addr, uint8 i, int psm); - void ReadTexel_AVX(int pixels, int mip_offset = 0); - void ReadTexel_AVX(const Xmm& dst, const Xmm& addr, uint8 i); - -#endif - - void modulate16(const Xmm& a, const Operand& f, uint8 shift); - void lerp16(const Xmm& a, const Xmm& b, const Xmm& f, uint8 shift); - void lerp16_4(const Xmm& a, const Xmm& b, const Xmm& f); - void mix16(const Xmm& a, const Xmm& b, const Xmm& temp); - void clamp16(const Xmm& a, const Xmm& temp); - void alltrue(const Xmm& test); - void blend(const Xmm& a, const Xmm& b, const Xmm& mask); - void blendr(const Xmm& b, const Xmm& a, const Xmm& mask); - void blend8(const Xmm& a, const Xmm& b); - void blend8r(const Xmm& b, const Xmm& a); - void split16_2x8(const Xmm& l, const Xmm& h, const Xmm& src); - public: GSDrawScanlineCodeGenerator(void* param, uint64 key, void* code, size_t maxsize); }; diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx.cpp deleted file mode 100644 index eb3be1a385664..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx.cpp +++ /dev/null @@ -1,2129 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#undef _t - -#if _M_SSE < 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -// Ease the reading of the code -#define _m_local r12 -#define _m_local__gd r13 -#define _m_local__gd__vm a1 -#define _m_local__gd__clut r11 -#define _m_local__gd__tex a3 -// More pretty name -#define _z xmm8 -#define _f xmm9 -#define _s xmm10 -#define _t xmm11 -#define _q xmm12 -#define _f_rb xmm13 -#define _f_ga xmm14 -#define _test xmm15 -// Extra bonus -#define _rb xmm2 -#define _ga xmm3 -#define _fm xmm4 -#define _zm xmm5 -#define _fd xmm6 - -#define _rip_local(field) (m_rip ? ptr[rip + &m_local.field] : ptr[_m_local + offsetof(GSScanlineLocalData, field)]) -#define _rip_global(field) (m_rip ? ptr[rip + &m_local.gd->field] : ptr[_m_local__gd + offsetof(GSScanlineGlobalData, field)]) - -#ifdef _WIN64 -#else -static const int _rz_rbx = -8 * 1; -static const int _rz_r12 = -8 * 2; -static const int _rz_r13 = -8 * 3; -//static const int _rz_r14 = -8 * 4; -//static const int _rz_r15 = -8 * 5; -static const int _rz_top = -8 * 6; -static const int _rz_zs = -8 * 8; -static const int _rz_zd = -8 * 10; -static const int _rz_cov = -8 * 12; -#endif - -void GSDrawScanlineCodeGenerator::Generate_AVX() -{ - bool need_tex = m_sel.fb && m_sel.tfx != TFX_NONE; - bool need_clut = need_tex && m_sel.tlu; - m_rip = (size_t)getCurr() < 0x80000000; - m_rip &= (size_t)&m_local < 0x80000000; - m_rip &= (size_t)&m_local.gd < 0x80000000; - -#ifdef _WIN64 - push(rbx); - push(rsi); - push(rdi); - push(rbp); - push(r12); - push(r13); - - sub(rsp, 8 + 10 * 16); - - for (int i = 6; i < 16; i++) - { - vmovdqa(ptr[rsp + (i - 6) * 16], Xmm(i)); - } -#else - // No reservation on the stack as a red zone is available - push(rbp); - mov(ptr[rsp + _rz_rbx], rbx); - if (!m_rip) - { - mov(ptr[rsp + _rz_r12], r12); - mov(ptr[rsp + _rz_r13], r13); - } -#endif - - mov(r10, (size_t)g_const->m_test_128b[0]); - if (!m_rip) - { - mov(_m_local, (size_t)&m_local); - mov(_m_local__gd, _rip_local(gd)); - } - - if (need_clut) - mov(_m_local__gd__clut, _rip_global(clut)); - - Init_AVX(); - - // a0 = steps - // t1 = fza_base - // t0 = fza_offset - // r10 = &m_test[0] - // _m_local = &m_local - // _m_local__gd = m_local->gd - // _m_local__gd__vm = m_local->gd.vm - // xmm7 = vf (sprite && ltf) - // xmm8 = z - // xmm9 = f - // xmm10 = s - // xmm11 = t - // xmm12 = q - // xmm13 = rb - // xmm14 = ga - // xmm15 = test - - if (!m_sel.edge) - { - align(16); - } - -L("loop"); - - TestZ_AVX(xmm5, xmm6); - - // ebp = za - - // FIXME not yet done - if (m_sel.mmin && 0) - { - SampleTextureLOD_AVX(); - } - else - { - SampleTexture_AVX(); - } - - // ebp = za - // xmm2 = rb - // xmm3 = ga - - AlphaTFX_AVX(); - - // ebp = za - // xmm2 = rb - // xmm3 = ga - - ReadMask_AVX(); - - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - - TestAlpha_AVX(); - - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - - ColorTFX_AVX(); - - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - - Fog_AVX(); - - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - - ReadFrame_AVX(); - - // ebx = fa - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - // xmm6 = fd - - TestDestAlpha_AVX(); - - // ebx = fa - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - // xmm6 = fd - - WriteMask_AVX(); - - // ebx = fa - // edx = fzm - // ebp = za - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm5 = zm - // xmm6 = fd - - WriteZBuf_AVX(); - - // ebx = fa - // edx = fzm - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm6 = fd - - AlphaBlend_AVX(); - - // ebx = fa - // edx = fzm - // xmm2 = rb - // xmm3 = ga - // xmm4 = fm - // xmm6 = fd - - WriteFrame_AVX(); - -L("step"); - - // if(steps <= 0) break; - - if (!m_sel.edge) - { - test(a0.cvt32(), a0.cvt32()); - - jle("exit", T_NEAR); - - Step_AVX(); - - jmp("loop", T_NEAR); - } - -L("exit"); - -#ifdef _WIN64 - for (int i = 6; i < 16; i++) - { - vmovdqa(Xmm(i), ptr[rsp + (i - 6) * 16]); - } - - add(rsp, 8 + 10 * 16); - - pop(r13); - pop(r12); - pop(rbp); - pop(rdi); - pop(rsi); - pop(rbx); -#else - mov(rbx, ptr[rsp + _rz_rbx]); - if (!m_rip) - { - mov(r12, ptr[rsp + _rz_r12]); - mov(r13, ptr[rsp + _rz_r13]); - } - pop(rbp); -#endif - - ret(); -} - -void GSDrawScanlineCodeGenerator::Init_AVX() -{ - if (!m_sel.notest) - { - // int skip = left & 3; - - mov(ebx, a1.cvt32()); - and(a1.cvt32(), 3); - - // left -= skip; - - sub(ebx, a1.cvt32()); - - // int steps = pixels + skip - 4; - - lea(a0.cvt32(), ptr[a0 + a1 - 4]); - - // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; - - shl(a1.cvt32(), 4); // * sizeof(m_test[0]) - - vmovdqa(_test, ptr[a1 + r10]); - - mov(eax, a0.cvt32()); - sar(eax, 31); // GH: 31 to extract the sign of the register - and(eax, a0.cvt32()); - shl(eax, 4); // * sizeof(m_test[0]) - cdqe(); - - vpor(_test, ptr[rax + r10 + 7 * 16]); - } - else - { - mov(ebx, a1.cvt32()); // left - xor(a1.cvt32(), a1.cvt32()); // skip - lea(a0.cvt32(), ptr[a0 - 4]); // steps - } - - // a0 = steps - // a1 = skip - // rbx = left - - - // GSVector2i* fza_base = &m_local.gd->fzbr[top]; - - mov(rax, _rip_global(fzbr)); - lea(t1, ptr[rax + a2 * 8]); - - // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; - - mov(rax, _rip_global(fzbc)); - lea(t0, ptr[rax + rbx * 2]); - - if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // a1 = &m_local.d[skip] // note a1 was (skip << 4) - - // FIXME - //lea(a1, ptr[a1 * 8 + _m_local + offsetof(GSScanlineLocalData, d)]); - lea(rax, _rip_local(d)); - lea(a1, ptr[rax + a1 * 8]); - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.fwrite && m_sel.fge || m_sel.zb) - { - vmovaps(xmm0, ptr[a3 + offsetof(GSVertexSW, p)]); // v.p - - if (m_sel.fwrite && m_sel.fge) - { - // f = GSVector4i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); - - vcvttps2dq(_f, xmm0); - vpshufhw(_f, _f, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(_f, _f, _MM_SHUFFLE(2, 2, 2, 2)); - vpaddw(_f, ptr[a1 + 16 * 6]); - } - - if (m_sel.zb) - { - // z = vp.zzzz() + m_local.d[skip].z; - - vshufps(_z, xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - vaddps(_z, ptr[a1]); - } - } - } - else - { - if (m_sel.ztest) - { - vmovdqa(_z, _rip_local(p.z)); - } - - if (m_sel.fwrite && m_sel.fge) - vmovdqa(_f, _rip_local(p.f)); - } - - if (m_sel.fb) - { - if (m_sel.edge || m_sel.tfx != TFX_NONE) - { - vmovaps(xmm0, ptr[a3 + offsetof(GSVertexSW, t)]); // v.t - } - - if (m_sel.edge) - { - // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); - - vpshufhw(xmm1, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - vpsrlw(xmm1, 9); - -#ifdef _WIN64 - vmovdqa(_rip_local(temp.cov), xmm1); -#else - vmovdqa(ptr[rsp + _rz_cov], xmm1); -#endif - } - - if (m_sel.tfx != TFX_NONE) - { - // a1 = &m_local.d[skip] - - if (m_sel.fst) - { - // GSVector4i vti(vt); - - vcvttps2dq(xmm0, xmm0); - - // s = vti.xxxx() + m_local.d[skip].s; - // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; - - vpshufd(_s, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(_t, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddd(_s, ptr[a1 + offsetof(GSScanlineLocalData::skip, s)]); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpaddd(_t, ptr[a1 + offsetof(GSScanlineLocalData::skip, t)]); - } - else if (m_sel.ltf) - { - vpshuflw(xmm7, _t, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm7, xmm7, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm7, 12); - } - } - else - { - // s = vt.xxxx() + m_local.d[skip].s; - // t = vt.yyyy() + m_local.d[skip].t; - // q = vt.zzzz() + m_local.d[skip].q; - - vshufps(_s, xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(_t, xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(_q, xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(_s, ptr[a1 + offsetof(GSScanlineLocalData::skip, s)]); - vaddps(_t, ptr[a1 + offsetof(GSScanlineLocalData::skip, t)]); - vaddps(_q, ptr[a1 + offsetof(GSScanlineLocalData::skip, q)]); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - vcvttps2dq(xmm0, ptr[a3 + offsetof(GSVertexSW, c)]); // v.c - - // vc = vc.upl16(vc.zwxy()); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(xmm0, xmm1); - - // rb = vc.xxxx().add16(m_local.d[skip].rb); - // ga = vc.zzzz().add16(m_local.d[skip].ga); - - vpshufd(_f_rb, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(_f_ga, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vpaddw(_f_rb, ptr[a1 + offsetof(GSScanlineLocalData::skip, rb)]); - vpaddw(_f_ga, ptr[a1 + offsetof(GSScanlineLocalData::skip, ga)]); - } - else - { - vmovdqa(_f_rb, _rip_local(c.rb)); - vmovdqa(_f_ga, _rip_local(c.ga)); - } - - vmovdqa(_rb, _f_rb); - vmovdqa(_ga, _f_ga); - } - } - - if (m_sel.fwrite && m_sel.fpsm == 2 && m_sel.dthe) - { - // On linux, a2 is edx which will be used for fzm - // In all case, it will require a mov in dthe code, so let's keep the value on the stack -#ifdef _WIN64 - ASSERT(0); -#else - mov(ptr[rsp + _rz_top], a2); -#endif - } - - mov(_m_local__gd__vm, _rip_global(vm)); - if (m_sel.fb && m_sel.tfx != TFX_NONE) - mov(_m_local__gd__tex, _rip_global(tex)); -} - -void GSDrawScanlineCodeGenerator::Step_AVX() -{ - // steps -= 4; - - sub(a0.cvt32(), 4); - - // fza_offset++; - - add(t0, 8); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // z += m_local.d4.z; - - if (m_sel.zb) - { - vaddps(_z, _rip_local(d4.z)); - } - - // f = f.add16(m_local.d4.f); - - if (m_sel.fwrite && m_sel.fge) - { - vpaddw(_f, _rip_local(d4.f)); - } - } - else - { - if (m_sel.ztest) - { - } - } - - if (m_sel.fb) - { - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i st = m_local.d4.st; - - // si += st.xxxx(); - // if(!sprite) ti += st.yyyy(); - - vmovdqa(xmm0, _rip_local(d4.stq)); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpaddd(_s, xmm1); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpshufd(xmm1, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - vpaddd(_t, xmm1); - } - } - else - { - // GSVector4 stq = m_local.d4.stq; - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - vmovaps(xmm0, _rip_local(d4.stq)); - - vshufps(xmm1, xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(xmm2, xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(xmm3, xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(_s, xmm1); - vaddps(_t, xmm2); - vaddps(_q, xmm3); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i c = m_local.d4.c; - - // rb = rb.add16(c.xxxx()); - // ga = ga.add16(c.yyyy()); - - vmovdqa(xmm0, _rip_local(d4.c)); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm2, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddw(_f_rb, xmm1); - vpaddw(_f_ga, xmm2); - - // FIXME: color may underflow and roll over at the end of the line, if decreasing - - vpxor(xmm0, xmm0); - vpmaxsw(_f_rb, xmm0); - vpmaxsw(_f_ga, xmm0); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - } - } - - vmovdqa(_rb, _f_rb); - vmovdqa(_ga, _f_ga); - } - } - - if (!m_sel.notest) - { - // test = m_test[7 + (steps & (steps >> 31))]; - - mov(eax, a0.cvt32()); - sar(eax, 31); // GH: 31 to extract the sign of the register - and(eax, a0.cvt32()); - shl(eax, 4); - cdqe(); - - vmovdqa(_test, ptr[rax + r10 + 7 * 16]); - } -} - -void GSDrawScanlineCodeGenerator::TestZ_AVX(const Xmm& temp1, const Xmm& temp2) -{ - if (!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, dword[t1 + 4]); - add(ebp, dword[t0 + 4]); - and(ebp, HALF_VM_SIZE - 1); - - // GSVector4i zs = zi; - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.zoverflow) - { - // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); - - mov(rax, (size_t)&GSVector4::m_half); - - vbroadcastss(xmm0, ptr[rax]); - vmulps(xmm0, _z); - vcvttps2dq(xmm0, xmm0); - vpslld(xmm0, 1); - - vcvttps2dq(xmm1, _z); - vpcmpeqd(xmm2, xmm2); - vpsrld(xmm2, 31); - vpand(xmm1, xmm2); - - vpor(xmm0, xmm1); - } - else - { - // zs = GSVector4i(z); - - vcvttps2dq(xmm0, _z); - } - - if (m_sel.zwrite) - { -#ifdef _WIN64 - vmovdqa(_rip_local(temp.zs), xmm0); -#else - vmovdqa(ptr[rsp + _rz_zs], xmm0); -#endif - } - } - else - { - movdqa(xmm0, _z); - } - - if (m_sel.ztest) - { - ReadPixel_AVX(xmm1, rbp); - - if (m_sel.zwrite && m_sel.zpsm < 2) - { -#ifdef _WIN64 - vmovdqa(_rip_local(temp.zd), xmm1); -#else - vmovdqa(ptr[rsp + _rz_zd], xmm1); -#endif - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if (m_sel.zpsm) - { - vpslld(xmm1, static_cast(m_sel.zpsm * 8)); - vpsrld(xmm1, static_cast(m_sel.zpsm * 8)); - } - - if (m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector4i o = GSVector4i::x80000000(); - - vpcmpeqd(xmm2, xmm2); - vpslld(xmm2, 31); - - // GSVector4i zso = zs - o; - // GSVector4i zdo = zd - o; - - vpsubd(xmm0, xmm2); - vpsubd(xmm1, xmm2); - } - - switch (m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - vpcmpgtd(xmm1, xmm0); - vpor(_test, xmm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - vpcmpgtd(xmm0, xmm1); - vpcmpeqd(xmm2, xmm2); - vpxor(xmm0, xmm2); - vpor(_test, xmm0); - break; - } - - alltrue(_test); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture_AVX() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - if (!m_sel.fst) - { - vrcpps(xmm0, _q); - - vmulps(xmm4, _s, xmm0); - vmulps(xmm5, _t, xmm0); - - vcvttps2dq(xmm4, xmm4); - vcvttps2dq(xmm5, xmm5); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm0, eax); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - - vpsubd(xmm4, xmm0); - vpsubd(xmm5, xmm0); - } - } - else - { - vmovdqa(xmm4, _s); - vmovdqa(xmm5, _t); - } - - if (m_sel.ltf) - { - // GSVector4i uf = u.xxzzlh().srl16(12); - - vpshuflw(xmm6, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm6, xmm6, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm6, 12); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4i vf = v.xxzzlh().srl16(12); - - vpshuflw(xmm7, xmm5, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm7, xmm7, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm7, 12); - } - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(xmm4, 16); - vpsrad(xmm5, 16); - vpackssdw(xmm4, xmm5); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(xmm0, xmm0); - vpsrlw(xmm0, 15); - vpaddw(xmm5, xmm4, xmm0); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap_AVX(xmm4, xmm5); - } - else - { - // uv0 = Wrap(uv0); - - Wrap_AVX(xmm4); - } - - // xmm4 = uv0 - // xmm5 = uv1 (ltf) - // xmm6 = uf - // xmm7 = vf - - // GSVector4i x0 = uv0.upl16(); - // GSVector4i y0 = uv0.uph16() << tw; - - vpxor(xmm0, xmm0); - - vpunpcklwd(xmm2, xmm4, xmm0); - vpunpckhwd(xmm3, xmm4, xmm0); - vpslld(xmm3, static_cast(m_sel.tw + 3)); - - // xmm0 = 0 - // xmm2 = x0 - // xmm3 = y0 - // xmm5 = uv1 (ltf) - // xmm6 = uf - // xmm7 = vf - - if (m_sel.ltf) - { - // GSVector4i x1 = uv1.upl16(); - // GSVector4i y1 = uv1.uph16() << tw; - - vpunpcklwd(xmm4, xmm5, xmm0); - vpunpckhwd(xmm5, xmm5, xmm0); - vpslld(xmm5, static_cast(m_sel.tw + 3)); - - // xmm2 = x0 - // xmm3 = y0 - // xmm4 = x1 - // xmm5 = y1 - // xmm6 = uf - // xmm7 = vf - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - vpaddd(xmm0, xmm3, xmm2); - vpaddd(xmm1, xmm3, xmm4); - vpaddd(xmm2, xmm5, xmm2); - vpaddd(xmm3, xmm5, xmm4); - - // xmm0 = addr00 - // xmm1 = addr01 - // xmm2 = addr10 - // xmm3 = addr11 - // xmm6 = uf - // xmm7 = vf - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(4, 0); - - // xmm0 = c10 - // xmm1 = c11 - // xmm4 = c00 - // xmm5 = c01 - // xmm6 = uf - // xmm7 = vf - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm3, xmm4); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm4, xmm5, xmm5); - - // xmm0 = c10 - // xmm1 = c11 - // xmm2 = rb00 - // xmm3 = ga00 - // xmm4 = rb01 - // xmm5 = ga01 - // xmm6 = uf - // xmm7 = vf - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(xmm4, xmm2, xmm6); - lerp16_4(xmm5, xmm3, xmm6); - - // xmm0 = c10 - // xmm1 = c11 - // xmm4 = rb00 - // xmm5 = ga00 - // xmm6 = uf - // xmm7 = vf - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm2, xmm3, xmm0); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm0, xmm1, xmm1); - - // xmm0 = rb11 - // xmm1 = ga11 - // xmm2 = rb10 - // xmm3 = ga10 - // xmm4 = rb00 - // xmm5 = ga00 - // xmm6 = uf - // xmm7 = vf - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(xmm0, xmm2, xmm6); - lerp16_4(xmm1, xmm3, xmm6); - - // xmm0 = rb10 - // xmm1 = ga10 - // xmm4 = rb00 - // xmm5 = ga00 - // xmm7 = vf - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - lerp16_4(xmm0, xmm4, xmm7); - lerp16_4(xmm1, xmm5, xmm7); - - // FIXME not ideal (but allow different source in ReadTexel and less register dependency) - vmovdqa(xmm2, xmm0); - vmovdqa(xmm3, xmm1); - } - else - { - // GSVector4i addr00 = y0 + x0; - - vpaddd(xmm0, xmm3, xmm2); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(1, 0); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(_rb, _ga, xmm4); - } - - // xmm2 = rb - // xmm3 = ga -} - -void GSDrawScanlineCodeGenerator::Wrap_AVX(const Xmm& uv) -{ - // xmm0, xmm1, xmm2, xmm3 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv, _rip_global(t.min)); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv, xmm0); - } - - vpminsw(uv, _rip_global(t.max)); - } - else - { - vpand(uv, _rip_global(t.min)); - - if (region) - { - vpor(uv, _rip_global(t.max)); - } - } - } - else - { - vmovdqa(xmm2, _rip_global(t.min)); - vmovdqa(xmm3, _rip_global(t.max)); - vmovdqa(xmm0, _rip_global(t.mask)); - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv, xmm2); - - if (region) - { - vpor(xmm1, xmm3); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, xmm2); - vpminsw(uv, xmm3); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::Wrap_AVX(const Xmm& uv0, const Xmm& uv1) -{ - // xmm0, xmm1, xmm2, xmm3 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vmovdqa(xmm0, _rip_global(t.min)); - vpmaxsw(uv0, xmm0); - vpmaxsw(uv1, xmm0); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv0, xmm0); - vpmaxsw(uv1, xmm0); - } - - vmovdqa(xmm0, _rip_global(t.max)); - vpminsw(uv0, xmm0); - vpminsw(uv1, xmm0); - } - else - { - vmovdqa(xmm0, _rip_global(t.min)); - vpand(uv0, xmm0); - vpand(uv1, xmm0); - - if (region) - { - vmovdqa(xmm0, _rip_global(t.max)); - vpor(uv0, xmm0); - vpor(uv1, xmm0); - } - } - } - else - { - vmovdqa(xmm2, _rip_global(t.min)); - vmovdqa(xmm3, _rip_global(t.max)); - vmovdqa(xmm0, _rip_global(t.mask)); - - // uv0 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv0, xmm2); - - if (region) - { - vpor(xmm1, xmm3); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, xmm2); - vpminsw(uv0, xmm3); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, xmm1, xmm0); - - // uv1 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv1, xmm2); - - if (region) - { - vpor(xmm1, xmm3); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, xmm2); - vpminsw(uv1, xmm3); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::SampleTextureLOD_AVX() -{ -} - -void GSDrawScanlineCodeGenerator::WrapLOD_AVX(const Xmm& uv) -{ -} - -void GSDrawScanlineCodeGenerator::WrapLOD_AVX(const Xmm& uv0, const Xmm& uv1) -{ -} - -void GSDrawScanlineCodeGenerator::AlphaTFX_AVX() -{ - if (!m_sel.fb) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16(_ga, _f_ga, 1); - - clamp16(_ga, xmm0); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(xmm1, _f_ga, 7); - - mix16(_ga, xmm1, xmm0); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(xmm1, _f_ga, 7); - - mix16(_ga, xmm1, xmm0); - } - - break; - - case TFX_HIGHLIGHT: - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - vpsrlw(xmm1, _f_ga, 7); - - if (m_sel.tcc) - { - vpaddusb(xmm1, _ga); - } - - mix16(_ga, xmm1, xmm0); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(xmm1, _f_ga, 7); - - mix16(_ga, xmm1, xmm0); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if (m_sel.iip) - { - vpsrlw(_ga, _f_ga, 7); - } - - break; - } - - if (m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if (!m_sel.abe) - { - // a = cov - - if (m_sel.edge) - { -#ifdef _WIN64 - vmovdqa(xmm0, _rip_local(temp.cov)); -#else - vmovdqa(xmm0, ptr[rsp + _rz_cov]); -#endif - } - else - { - vpcmpeqd(xmm0, xmm0); - vpsllw(xmm0, 15); - vpsrlw(xmm0, 8); - } - - mix16(_ga, xmm0, xmm1); - } - else - { - // a = a == 0x80 ? cov : a - - vpcmpeqd(xmm0, xmm0); - vpsllw(xmm0, 15); - vpsrlw(xmm0, 8); - - if (m_sel.edge) - { -#ifdef _WIN64 - vmovdqa(xmm1, _rip_local(temp.cov)); -#else - vmovdqa(xmm1, ptr[rsp + _rz_cov]); -#endif - } - else - { - vmovdqa(xmm1, xmm0); - } - - vpcmpeqw(xmm0, _ga); - vpsrld(xmm0, 16); - vpslld(xmm0, 16); - - vpblendvb(_ga, xmm1, xmm0); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadMask_AVX() -{ - if (m_sel.fwrite) - { - vmovdqa(_fm, _rip_global(fm)); - } - - if (m_sel.zwrite) - { - vmovdqa(_zm, _rip_global(zm)); - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha_AVX() -{ - switch (m_sel.atst) - { - case ATST_NEVER: - // t = GSVector4i::xffffffff(); - vpcmpeqd(xmm1, xmm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_local.gd->aref; - vpsrld(xmm1, _ga, 16); - vpcmpgtd(xmm1, _rip_global(aref)); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_local.gd->aref; - vpsrld(xmm1, _ga, 16); - vpcmpeqd(xmm1, _rip_global(aref)); - vpcmpeqd(xmm0, xmm0); - vpxor(xmm1, xmm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_local.gd->aref; - vpsrld(xmm0, _ga, 16); - vmovdqa(xmm1, _rip_global(aref)); - vpcmpgtd(xmm1, xmm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_local.gd->aref; - vpsrld(xmm1, _ga, 16); - vpcmpeqd(xmm1, _rip_global(aref)); - break; - } - - switch (m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - vpor(_test, xmm1); - alltrue(_test); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - vpor(_zm, xmm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - vpor(_fm, xmm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - vpor(_zm, xmm1); - // fm |= t & GSVector4i::xff000000(); - vpsrld(xmm1, 24); - vpslld(xmm1, 24); - vpor(_fm, xmm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16(_rb, _f_rb, 1); - - clamp16(_rb, xmm0); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - vmovdqa(xmm1, _ga); - - modulate16(_ga, _f_ga, 1); - - vpshuflw(xmm6, _f_ga, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(xmm6, xmm6, _MM_SHUFFLE(3, 3, 1, 1)); - vpsrlw(xmm6, 7); - - vpaddw(_ga, xmm6); - - clamp16(_ga, xmm0); - - mix16(_ga, xmm1, xmm0); - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16(_rb, _f_rb, 1); - - vpaddw(_rb, xmm6); - - clamp16(_rb, xmm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if (m_sel.iip) - { - vpsrlw(_rb, _f_rb, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog_AVX() -{ - if (!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_local.gd->frb.lerp16<0>(rb, f); - // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); - - vmovdqa(xmm6, _ga); - - vmovdqa(xmm0, _rip_global(frb)); - vmovdqa(xmm1, _rip_global(fga)); - - lerp16(_rb, xmm0, _f, 0); - lerp16(_ga, xmm1, _f, 0); - - mix16(_ga, xmm6, _f); -} - -void GSDrawScanlineCodeGenerator::ReadFrame_AVX() -{ - if (!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, dword[t1]); - add(ebx, dword[t0]); - and(ebx, HALF_VM_SIZE - 1); - - if (!m_sel.rfb) - { - return; - } - - ReadPixel_AVX(_fd, rbx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha_AVX() -{ - if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); - - if (m_sel.datm) - { - if (m_sel.fpsm == 2) - { - vpxor(xmm0, xmm0); - //vpsrld(xmm1, _fd, 15); - vpslld(xmm1, _fd, 16); - vpsrad(xmm1, 31); - vpcmpeqd(xmm1, xmm0); - } - else - { - vpcmpeqd(xmm0, xmm0); - vpxor(xmm1, _fd, xmm0); - vpsrad(xmm1, 31); - } - } - else - { - if (m_sel.fpsm == 2) - { - vpslld(xmm1, _fd, 16); - vpsrad(xmm1, 31); - } - else - { - vpsrad(xmm1, _fd, 31); - } - } - - vpor(_test, xmm1); - - alltrue(_test); -} - -void GSDrawScanlineCodeGenerator::WriteMask_AVX() -{ - if (m_sel.notest) - { - return; - } - - // fm |= test; - // zm |= test; - - if (m_sel.fwrite) - { - vpor(_fm, _test); - } - - if (m_sel.zwrite) - { - vpor(_zm, _test); - } - - // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); - - vpcmpeqd(xmm1, xmm1); - - if (m_sel.fwrite && m_sel.zwrite) - { - vpcmpeqd(xmm0, xmm1, _zm); - vpcmpeqd(xmm1, _fm); - vpackssdw(xmm1, xmm0); - } - else if (m_sel.fwrite) - { - vpcmpeqd(xmm1, _fm); - vpackssdw(xmm1, xmm1); - } - else if (m_sel.zwrite) - { - vpcmpeqd(xmm1, _zm); - vpackssdw(xmm1, xmm1); - } - - vpmovmskb(edx, xmm1); - - not(edx); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf_AVX() -{ - if (!m_sel.zwrite) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) -#ifdef _WIN64 - vmovdqa(xmm1, _rip_local(temp.zs)); -#else - vmovdqa(xmm1, ptr[rsp + _rz_zs]); -#endif - else - vmovdqa(xmm1, _rip_local(p.z)); - - if (m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - -#ifdef _WIN64 - vpblendvb(xmm1, _rip_local(temp.zd), _zm); -#else - vpblendvb(xmm1, ptr[rsp + _rz_zd], _zm); -#endif - } - - bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; - - WritePixel_AVX(xmm1, rbp, dh, fast, m_sel.zpsm, 1); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - const Xmm& _dst_rb = xmm0; - const Xmm& _dst_ga = xmm1; - - if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch (m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - split16_2x8(_dst_rb, _dst_ga, _fd); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - vpcmpeqd(xmm15, xmm15); - - vpsrld(xmm15, 27); // 0x0000001f - vpand(_dst_rb, _fd, xmm15); - vpslld(_dst_rb, 3); - - vpslld(xmm15, 10); // 0x00007c00 - vpand(xmm5, _fd, xmm15); - vpslld(xmm5, 9); - - vpor(_dst_rb, xmm5); - - vpsrld(xmm15, 5); // 0x000003e0 - vpand(_dst_ga, _fd, xmm15); - vpsrld(_dst_ga, 2); - - vpsllw(xmm15, 10); // 0x00008000 - vpand(xmm5, _fd, xmm15); - vpslld(xmm5, 8); - - vpor(_dst_ga, xmm5); - - break; - } - } - - // xmm2, xmm3 = src rb, ga - // xmm0, xmm1 = dst rb, ga - // xmm5, xmm15 = free - - if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - vmovdqa(xmm5, _rb); - } - - if (m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(_rb, _dst_rb); - break; - case 2: - vpxor(_rb, _rb); - break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch (m_sel.abb) - { - case 0: - vpsubw(_rb, xmm5); - break; - case 1: - vpsubw(_rb, _dst_rb); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; - - switch (m_sel.abc) - { - case 0: - case 1: - vpshuflw(xmm15, m_sel.abc ? _dst_ga : _ga, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(xmm15, xmm15, _MM_SHUFFLE(3, 3, 1, 1)); - vpsllw(xmm15, 7); - break; - case 2: - vmovdqa(xmm15, _rip_global(afix)); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16(_rb, xmm15, 1); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch (m_sel.abd) - { - case 0: - vpaddw(_rb, xmm5); - break; - case 1: - vpaddw(_rb, _dst_rb); - break; - case 2: - break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(_rb, _dst_rb); - break; - case 2: - vpxor(_rb, _rb); - break; - } - } - - if (m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - vpslld(xmm0, _ga, 8); - vpsrad(xmm0, 31); - - // rb = c[0].blend8(rb, mask); - - vpblendvb(_rb, xmm5, _rb, xmm0); - } - - // xmm0 = pabe mask - // xmm3 = src ga - // xmm1 = dst ga - // xmm2 = rb - // xmm15 = a - // xmm5 = free - - vmovdqa(xmm5, _ga); - - if (m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(_ga, _dst_ga); - break; - case 2: - vpxor(_ga, _ga); - break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch (m_sel.abb) - { - case 0: - vpsubw(_ga, xmm5); - break; - case 1: - vpsubw(_ga, _dst_ga); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16(_ga, xmm15, 1); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch (m_sel.abd) - { - case 0: - vpaddw(_ga, xmm5); - break; - case 1: - vpaddw(_ga, _dst_ga); - break; - case 2: - break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(_ga, _dst_ga); - break; - case 2: - vpxor(_ga, _ga); - break; - } - } - - // xmm0 = pabe mask - // xmm5 = src ga - // xmm2 = rb - // xmm3 = ga - // xmm1, xmm15 = free - - if (m_sel.pabe) - { - vpsrld(xmm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - vpblendvb(_ga, xmm5, _ga, xmm0); - } - else - { - if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(_ga, xmm5, xmm15); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.fpsm == 2 && m_sel.dthe) - { - // y = (top & 3) << 5 - -#ifdef _WIN64 - ASSERT(0); -#else - mov(eax, ptr[rsp + _rz_top]); -#endif - and(eax, 3); - shl(eax, 5); - - // rb = rb.add16(m_global.dimx[0 + y]); - // ga = ga.add16(m_global.dimx[1 + y]); - - add(rax, _rip_global(dimx)); - - vpaddw(xmm2, ptr[rax + sizeof(GSVector4i) * 0]); - vpaddw(xmm3, ptr[rax + sizeof(GSVector4i) * 1]); - } - - if (m_sel.colclamp == 0) - { - // c[0] &= 0x00ff00ff; - // c[1] &= 0x00ff00ff; - - vpcmpeqd(xmm15, xmm15); - vpsrlw(xmm15, 8); - vpand(xmm2, xmm15); - vpand(xmm3, xmm15); - } - - // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - vpunpckhwd(xmm15, xmm2, xmm3); - vpunpcklwd(xmm2, xmm3); - vpackuswb(xmm2, xmm15); - - if (m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - vpcmpeqd(xmm15, xmm15); - vpslld(xmm15, 31); - vpor(xmm2, xmm15); - } - - // xmm2 = fs - // xmm4 = fm - // xmm6 = fd - - if (m_sel.fpsm == 2) - { - // GSVector4i rb = fs & 0x00f800f8; - // GSVector4i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - vmovd(xmm0, eax); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - - mov(eax, 0x8000f800); - vmovd(xmm1, eax); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); - - vpand(xmm0, xmm2); - vpand(xmm1, xmm2); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - vpsrld(xmm2, xmm0, 9); - vpsrld(xmm0, 3); - vpsrld(xmm3, xmm1, 16); - vpsrld(xmm1, 6); - - vpor(xmm0, xmm1); - vpor(xmm2, xmm3); - vpor(xmm2, xmm0); - } - - if (m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(xmm2, _fd, _fm); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; - - WritePixel_AVX(xmm2, rbx, dl, fast, m_sel.fpsm, 0); -} - -void GSDrawScanlineCodeGenerator::ReadPixel_AVX(const Xmm& dst, const Reg64& addr) -{ - vmovq(dst, qword[_m_local__gd__vm + addr * 2]); - vmovhps(dst, qword[_m_local__gd__vm + addr * 2 + 8 * 2]); -} - -void GSDrawScanlineCodeGenerator::WritePixel_AVX(const Xmm& src, const Reg64& addr, const Reg8& mask, bool fast, int psm, int fz) -{ - if (m_sel.notest) - { - if (fast) - { - vmovq(qword[_m_local__gd__vm + addr * 2], src); - vmovhps(qword[_m_local__gd__vm + addr * 2 + 8 * 2], src); - } - else - { - WritePixel_AVX(src, addr, 0, psm); - WritePixel_AVX(src, addr, 1, psm); - WritePixel_AVX(src, addr, 2, psm); - WritePixel_AVX(src, addr, 3, psm); - } - } - else - { - if (fast) - { - // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); - // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); - - test(mask, 0x0f); - je("@f"); - vmovq(qword[_m_local__gd__vm + addr * 2], src); - L("@@"); - - test(mask, 0xf0); - je("@f"); - vmovhps(qword[_m_local__gd__vm + addr * 2 + 8 * 2], src); - L("@@"); - - // vmaskmovps? - } - else - { - // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); - // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); - // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); - // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); - - test(mask, 0x03); - je("@f"); - WritePixel_AVX(src, addr, 0, psm); - L("@@"); - - test(mask, 0x0c); - je("@f"); - WritePixel_AVX(src, addr, 1, psm); - L("@@"); - - test(mask, 0x30); - je("@f"); - WritePixel_AVX(src, addr, 2, psm); - L("@@"); - - test(mask, 0xc0); - je("@f"); - WritePixel_AVX(src, addr, 3, psm); - L("@@"); - } - } -} - -static const int s_offsets[4] = {0, 2, 8, 10}; - -void GSDrawScanlineCodeGenerator::WritePixel_AVX(const Xmm& src, const Reg64& addr, uint8 i, int psm) -{ - Address dst = ptr[_m_local__gd__vm + addr * 2 + s_offsets[i] * 2]; - - switch (psm) - { - case 0: - if (i == 0) - vmovd(dst, src); - else - vpextrd(dst, src, i); - break; - case 1: - if (i == 0) - vmovd(eax, src); - else - vpextrd(eax, src, i); - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - vpextrw(eax, src, i * 2); - mov(dst, ax); - break; - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_AVX(int pixels, int mip_offset) -{ - const int in[] = {0, 1, 2, 3}; - const int out[] = {4, 5, 0, 1}; - - for (int i = 0; i < pixels; i++) - { - for (uint8 j = 0; j < 4; j++) - { - ReadTexel_AVX(Xmm(out[i]), Xmm(in[i]), j); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_AVX(const Xmm& dst, const Xmm& addr, uint8 i) -{ - const Address& src = m_sel.tlu ? ptr[_m_local__gd__clut + rax * 4] : ptr[_m_local__gd__tex + rax * 4]; - - // Extract address offset - if (i == 0) - vmovd(eax, addr); - else - vpextrd(eax, addr, i); - - // If clut, load the value as a byte index - if (m_sel.tlu) - movzx(eax, byte[_m_local__gd__tex + rax]); - - if (i == 0) - vmovd(dst, src); - else - vpinsrd(dst, src, i); -} - -// Gather example (AVX2). Not faster on Haswell but potentially better on recent CPU -// Worst case reduce Icache. -// -// Current limitation requires 1 extra free register for the mask. -// And palette need zero masking. -// It is not possible to use same source/destination so linear interpolation must be updated -#if 0 -void GSDrawScanlineCodeGenerator::ReadTexel_AVX(int pixels, int mip_offset) -{ - const int in[] = {0, 1, 2, 3}; - const int out[] = {4, 5, 0, 1}; - const int mask[] = {5, 0, 1, 2}; - - if (m_sel.tlu) { - for(int i = 0; i < pixels; i++) { - // FIXME can't use same dst and add register - Gather4Texel(Xmm(in[i]), _m_local__gd__tex, Xmm(in[i]), Xmm(mask[i])); - // FIXME need a memory and could be faster - vpslld(Xmm(in[i]), 24); - vpsrld(Xmm(in[i]), 24); - Gather4Texel(Xmm(out[i]), _m_local__gd__clut, Xmm(in[i]), Xmm(mask[i])); - } - } else { - for(int i = 0; i < pixels; i++) { - Gather4Texel(Xmm(out[i]), _m_local__gd__tex, Xmm(in[i]), Xmm(mask[i])); - } - } -} - -static void Gather4Texel(const Xmm& dst, const Reg64& base, const Xmm& addr, const Xmm& Mask) -{ - //void vpgatherdd(const Xmm& x1, const Address& addr, const Xmm& x2) - vpcmpeqd(Mask, Mask); - vpgatherdd(dst, ptr[base + addr * 4], Mask); -} - -#endif - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx2.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx2.cpp deleted file mode 100644 index 27ab2d8ce0259..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.avx2.cpp +++ /dev/null @@ -1,3103 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#undef _t - -#if _M_SSE >= 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 16; -static const int _top = _args + 4; -static const int _v = _args + 8; - -// Ease the reading of the code -#define _m_local r12 -#define _m_local__gd r13 -#define _m_local__gd__vm a1 -#define _m_local__gd__clut r11 -#define _m_local__gd__tex a3 -// More pretty name -#define _z ymm8 -#define _f ymm9 -#define _s ymm10 -#define _t ymm11 -#define _q ymm12 -#define _f_rb ymm13 -#define _f_ga ymm14 -#define _test ymm15 -// Extra bonus -#define _rb ymm2 -#define _ga ymm3 -#define _fm ymm4 -#define _zm ymm5 -#define _fd ymm6 - -#define _rip_local(field) (m_rip ? ptr[rip + &m_local.field] : ptr[_m_local + offsetof(GSScanlineLocalData, field)]) -#define _rip_global(field) (m_rip ? ptr[rip + &m_local.gd->field] : ptr[_m_local__gd + offsetof(GSScanlineGlobalData, field)]) - -#ifdef _WIN64 -#else -static const int _rz_rbx = -8 * 1; -static const int _rz_r12 = -8 * 2; -static const int _rz_r13 = -8 * 3; -//static const int _rz_r14 = -8 * 4; -//static const int _rz_r15 = -8 * 5; -static const int _rz_top = -8 * 4; -static const int _rz_zs = -8 * 8; -static const int _rz_zd = -8 * 12; -static const int _rz_cov = -8 * 16; -#endif - -void GSDrawScanlineCodeGenerator::Generate() -{ - ret(); - return; - - bool need_tex = m_sel.fb && m_sel.tfx != TFX_NONE; - bool need_clut = need_tex && m_sel.tlu; - m_rip = (size_t)getCurr() < 0x80000000; - m_rip &= (size_t)&m_local < 0x80000000; - m_rip &= (size_t)&m_local.gd < 0x80000000; - -#ifdef _WIN64 - push(rbx); - push(rsi); - push(rdi); - push(rbp); - push(r12); - push(r13); - - sub(rsp, 8 + 10 * 16); - - for (int i = 6; i < 16; i++) - { - vmovdqa(ptr[rsp + (i - 6) * 16], Xmm(i)); - } -#else - // No reservation on the stack as a red zone is available - push(rbp); - mov(ptr[rsp + _rz_rbx], rbx); - if (!m_rip) - { - mov(ptr[rsp + _rz_r12], r12); - mov(ptr[rsp + _rz_r13], r13); - } -#endif - - mov(r10, (size_t)g_const->m_test_256b[0]); - if (!m_rip) - { - mov(_m_local, (size_t)&m_local); - mov(_m_local__gd, _rip_local(gd)); - } - - if (need_clut) - mov(_m_local__gd__clut, _rip_global(clut)); - - //db(0xcc); - - Init(); - - if (!m_sel.edge) - { - align(16); - } - -L("loop"); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ymm0 = z/zi - // ymm2 = s/u (tme) - // ymm3 = t/v (tme) - // ymm4 = q (tme) - // ymm5 = rb (!tme) - // ymm6 = ga (!tme) - // ymm7 = test - - TestZ(ymm5, ymm6); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - ymm0 - // ymm2 = s/u (tme) - // ymm3 = t/v (tme) - // ymm4 = q (tme) - // ymm5 = rb (!tme) - // ymm6 = ga (!tme) - // ymm7 = test - - if (m_sel.mmin) - { - SampleTextureLOD(); - } - else - { - SampleTexture(); - } - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - ymm2 - // - ymm3 - // - ymm4 - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - AlphaTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ReadMask(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - TestAlpha(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ColorTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - Fog(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ReadFrame(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - TestDestAlpha(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - WriteMask(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - - WriteZBuf(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // - ebp - // ymm2 = fd - // ymm3 = fm - // - ymm4 - // ymm5 = rb - // ymm6 = ga - - AlphaBlend(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ymm2 = fd - // ymm3 = fm - // ymm5 = rb - // ymm6 = ga - - WriteFrame(); - -L("step"); - - // if(steps <= 0) break; - - if (!m_sel.edge) - { - test(ecx, ecx); - - jle("exit", T_NEAR); - - Step(); - - jmp("loop", T_NEAR); - } - -L("exit"); - -#ifdef _WIN64 - for (int i = 6; i < 16; i++) - { - vmovdqa(Xmm(i), ptr[rsp + (i - 6) * 16]); - } - - add(rsp, 8 + 10 * 16); - - pop(r13); - pop(r12); - pop(rbp); - pop(rdi); - pop(rsi); - pop(rbx); -#else - mov(rbx, ptr[rsp + _rz_rbx]); - if (!m_rip) - { - mov(r12, ptr[rsp + _rz_r12]); - mov(r13, ptr[rsp + _rz_r13]); - } - pop(rbp); -#endif -} - -void GSDrawScanlineCodeGenerator::Init() -{ - if (!m_sel.notest) - { - // int skip = left & 7; - - mov(ebx, a1.cvt32()); - and(a1.cvt32(), 7); - - // int steps = pixels + skip - 8; - - lea(a0, ptr[a0 + a1 - 8]); - - // left -= skip; - - sub(ebx, a1.cvt32()); - - // GSVector4i test = m_test[skip] | m_test[15 + (steps & (steps >> 31))]; - - mov(eax, ecx); - sar(eax, 31); - and(eax, ecx); - - vpmovsxbd(ymm7, ptr[edx * 8 + (size_t)g_const->m_test_256b[0]]); - vpmovsxbd(ymm0, ptr[eax * 8 + (size_t)g_const->m_test_256b[15]]); - vpor(ymm7, ymm0); - - shl(edx, 5); - } - else - { - mov(ebx, edx); // left - xor(edx, edx); // skip - lea(ecx, ptr[ecx - 8]); // steps - } - - // GSVector2i* fza_base = &m_local.gd->fzbr[top]; - - mov(esi, ptr[esp + _top]); - lea(esi, ptr[esi * 8]); - add(esi, ptr[&m_local.gd->fzbr]); - - // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; - - lea(edi, ptr[ebx * 2]); - add(edi, ptr[&m_local.gd->fzbc]); - - if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // edx = &m_local.d[skip] - - lea(edx, ptr[edx * 8 + (size_t)m_local.d]); - - // ebx = &v - - mov(ebx, ptr[esp + _v]); - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.fwrite && m_sel.fge || m_sel.zb) - { - vbroadcastf128(ymm0, ptr[ebx + offsetof(GSVertexSW, p)]); // v.p - - if (m_sel.fwrite && m_sel.fge) - { - // f = GSVector8i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); - - vcvttps2dq(ymm1, ymm0); - vpshufhw(ymm1, ymm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(ymm1, ymm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpaddw(ymm1, ptr[edx + offsetof(GSScanlineLocalData::skip, f)]); - - vmovdqa(ptr[&m_local.temp.f], ymm1); - } - - if (m_sel.zb) - { - // z = vp.zzzz() + m_local.d[skip].z; - - vshufps(ymm0, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - vmovaps(ptr[&m_local.temp.z], ymm0); - vmovaps(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, z)]); - vmovaps(ptr[&m_local.temp.zo], ymm2); - vaddps(ymm0, ymm2); - } - } - } - else - { - if (m_sel.ztest) - { - vpbroadcastd(ymm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.edge || m_sel.tfx != TFX_NONE) - { - vbroadcastf128(ymm4, ptr[ebx + offsetof(GSVertexSW, t)]); // v.t - } - - if (m_sel.edge) - { - // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); - - vpshufhw(ymm3, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(ymm3, ymm3, _MM_SHUFFLE(3, 3, 3, 3)); - vpsrlw(ymm3, 9); - - vmovdqa(ptr[&m_local.temp.cov], ymm3); - } - - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i vti(vt); - - vcvttps2dq(ymm6, ymm4); - - // s = vti.xxxx() + m_local.d[skip].s; - // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; - - vpshufd(ymm2, ymm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm3, ymm6, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddd(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpaddd(ymm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - } - else - { - if (m_sel.ltf) - { - vpshuflw(ymm6, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm6, ymm6, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm6, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm6); - } - } - - vmovdqa(ptr[&m_local.temp.s], ymm2); - vmovdqa(ptr[&m_local.temp.t], ymm3); - } - else - { - // s = vt.xxxx() + m_local.d[skip].s; - // t = vt.yyyy() + m_local.d[skip].t; - // q = vt.zzzz() + m_local.d[skip].q; - - vshufps(ymm2, ymm4, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, ymm4, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm4, ymm4, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - vaddps(ymm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - vaddps(ymm4, ptr[edx + offsetof(GSScanlineLocalData::skip, q)]); - - vmovaps(ptr[&m_local.temp.s], ymm2); - vmovaps(ptr[&m_local.temp.t], ymm3); - vmovaps(ptr[&m_local.temp.q], ymm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - vbroadcastf128(ymm6, ptr[ebx + offsetof(GSVertexSW, c)]); // v.c - vcvttps2dq(ymm6, ymm6); - - // vc = vc.upl16(vc.zwxy()); - - vpshufd(ymm5, ymm6, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(ymm6, ymm5); - - // rb = vc.xxxx().add16(m_local.d[skip].rb); - // ga = vc.zzzz().add16(m_local.d[skip].ga); - - vpshufd(ymm5, ymm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm6, ymm6, _MM_SHUFFLE(2, 2, 2, 2)); - - vpaddw(ymm5, ptr[edx + offsetof(GSScanlineLocalData::skip, rb)]); - vpaddw(ymm6, ptr[edx + offsetof(GSScanlineLocalData::skip, ga)]); - - vmovdqa(ptr[&m_local.temp.rb], ymm5); - vmovdqa(ptr[&m_local.temp.ga], ymm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(ymm5, ptr[&m_local.c.rb]); - vmovdqa(ymm6, ptr[&m_local.c.ga]); - } - } - } - } -} - -void GSDrawScanlineCodeGenerator::Step() -{ - // steps -= 8; - - sub(a0, 8); - - // fza_offset += 2; - - add(t0, 16); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // zo += GSVector8::broadcast32(&m_local.d8.p.z); - - if (m_sel.zb) - { - vbroadcastss(ymm0, ptr[&m_local.d8.p.z]); - vaddps(ymm0, ptr[&m_local.temp.zo]); - vmovaps(ptr[&m_local.temp.zo], ymm0); - vaddps(ymm0, ptr[&m_local.temp.z]); - } - - // f = f.add16(GSVector8i::broadcast16(&m_local.d8.p.f)); - - if (m_sel.fwrite && m_sel.fge) - { - vpbroadcastw(ymm1, ptr[&m_local.d8.p.f]); - vpaddw(ymm1, ptr[&m_local.temp.f]); - vmovdqa(ptr[&m_local.temp.f], ymm1); - } - } - else - { - if (m_sel.ztest) - { - vpbroadcastd(ymm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector8i stq = GSVector8i::cast(GSVector8(m_local.d8.stq)); - - vbroadcasti128(ymm4, ptr[&m_local.d8.stq]); - - // s = GSVector8::cast(GSVector8i::cast(s) + stq.xxxx()); - - vpshufd(ymm2, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vpaddd(ymm2, ptr[&m_local.temp.s]); - vmovdqa(ptr[&m_local.temp.s], ymm2); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - // t = GSVector8::cast(GSVector8i::cast(t) + stq.yyyy()); - - vpshufd(ymm3, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vpaddd(ymm3, ptr[&m_local.temp.t]); - vmovdqa(ptr[&m_local.temp.t], ymm3); - } - else - { - vmovdqa(ymm3, ptr[&m_local.temp.t]); - } - } - else - { - // GSVector8 stq(m_local.d8.stq); - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - vbroadcastf128(ymm4, ptr[&m_local.d8.stq]); - - vshufps(ymm2, ymm4, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, ymm4, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm4, ymm4, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(ymm2, ptr[&m_local.temp.s]); - vaddps(ymm3, ptr[&m_local.temp.t]); - vaddps(ymm4, ptr[&m_local.temp.q]); - - vmovaps(ptr[&m_local.temp.s], ymm2); - vmovaps(ptr[&m_local.temp.t], ymm3); - vmovaps(ptr[&m_local.temp.q], ymm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector8i c = GSVector8i::broadcast64(&m_local.d8.c); - - vpbroadcastq(ymm7, ptr[&m_local.d8.c]); - - // rb = rb.add16(c.xxxx()).max_i16(GSVector8i::zero()); - // ga = ga.add16(c.yyyy()).max_i16(GSVector8i::zero()); - - vpshufd(ymm5, ymm7, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm6, ymm7, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddw(ymm5, ptr[&m_local.temp.rb]); - vpaddw(ymm6, ptr[&m_local.temp.ga]); - - // FIXME: color may underflow and roll over at the end of the line, if decreasing - - vpxor(ymm7, ymm7); - vpmaxsw(ymm5, ymm7); - vpmaxsw(ymm6, ymm7); - - vmovdqa(ptr[&m_local.temp.rb], ymm5); - vmovdqa(ptr[&m_local.temp.ga], ymm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(ymm5, ptr[&m_local.c.rb]); - vmovdqa(ymm6, ptr[&m_local.c.ga]); - } - } - } - } - - if (!m_sel.notest) - { - // test = m_test[15 + (steps & (steps >> 31))]; - - mov(edx, ecx); - sar(edx, 31); - and(edx, ecx); - - vpmovsxbd(ymm7, ptr[edx * 8 + (size_t)g_const->m_test_256b[15]]); - } -} - -void GSDrawScanlineCodeGenerator::TestZ(const Ymm& temp1, const Ymm& temp2) -{ - if (!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, ptr[esi + 4]); - add(ebp, ptr[edi + 4]); - and(ebp, HALF_VM_SIZE - 1); - - // GSVector8i zs = zi; - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.zoverflow) - { - // zs = (GSVector8i(z * 0.5f) << 1) | (GSVector8i(z) & GSVector8i::x00000001()); - - vbroadcastss(ymm0, ptr[&GSVector8::m_half]); - vmulps(ymm0, _z); - vcvttps2dq(ymm0, ymm0); - vpslld(ymm0, 1); - - vcvttps2dq(ymm1, _z); - vpcmpeqd(ymm2, ymm2); - vpsrld(ymm2, 31); - vpand(ymm1, ymm2); - - vpor(ymm0, ymm1); - } - else - { - // zs = GSVector8i(z); - - vcvttps2dq(ymm0, ymm0); - } - - if (m_sel.zwrite) - { -#ifdef _WIN64 - vmovdqa(ptr[&m_local.temp.zs], ymm0); -#else - vmovdqa(ptr[rsp + _rz_zs], ymm0); -#endif - } - } - - if (m_sel.ztest) - { - ReadPixel(ymm1, temp1, rbp); - - if (m_sel.zwrite && m_sel.zpsm < 2) - { -#ifdef _WIN64 - vmovdqa(_rip_local(temp.zd), ymm1); -#else - vmovdqa(ptr[rsp + _rz_zd], ymm1); -#endif - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if (m_sel.zpsm) - { - vpslld(ymm1, (uint8)(m_sel.zpsm * 8)); - vpsrld(ymm1, (uint8)(m_sel.zpsm * 8)); - } - - if (m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector8i o = GSVector8i::x80000000(); - - vpcmpeqd(temp1, temp1); - vpslld(temp1, 31); - - // GSVector8i zso = zs - o; - // GSVector8i zdo = zd - o; - - vpsubd(ymm0, temp1); - vpsubd(ymm1, temp1); - } - - switch (m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - vpcmpgtd(ymm1, ymm0); - vpor(ymm7, ymm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - vpcmpgtd(ymm0, ymm1); - vpcmpeqd(temp1, temp1); - vpxor(ymm0, temp1); - vpor(ymm7, ymm0); - break; - } - - alltrue(ymm7); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - mov(ebx, ptr[&m_local.gd->tex[0]]); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - // ebx = tex - // edx = clut - - if (!m_sel.fst) - { - vrcpps(ymm0, ymm4); - - vmulps(ymm2, ymm0); - vmulps(ymm3, ymm0); - - vcvttps2dq(ymm2, ymm2); - vcvttps2dq(ymm3, ymm3); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - } - } - - // ymm2 = u - // ymm3 = v - - if (m_sel.ltf) - { - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector8i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - Wrap(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i y0 = uv0.uph16() << tw; - // GSVector8i x0 = uv0.upl16(); - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i y1 = uv1.uph16() << tw; - // GSVector8i x1 = uv1.upl16(); - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 0); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 0); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Ymm& uv) -{ - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.min]); - vpmaxsw(uv, ymm0); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv, ymm0); - } - - vbroadcasti128(ymm0, ptr[&m_local.gd->t.max]); - vpminsw(uv, ymm0); - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.min]); - vpand(uv, ymm0); - - if (region) - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.max]); - vpor(uv, ymm0); - } - } - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, ymm4); - vpminsw(uv, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Ymm& uv0, const Ymm& uv1) -{ - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpmaxsw(uv0, ymm4); - vpmaxsw(uv1, ymm4); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv0, ymm0); - vpmaxsw(uv1, ymm0); - } - - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vpminsw(uv0, ymm5); - vpminsw(uv1, ymm5); - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpand(uv0, ymm4); - vpand(uv1, ymm4); - - if (region) - { - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vpor(uv0, ymm5); - vpor(uv1, ymm5); - } - } - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv0, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, ymm4); - vpminsw(uv0, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, ymm1, ymm0); - - // uv1 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv1, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, ymm4); - vpminsw(uv1, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::SampleTextureLOD() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - push(ebp); - - mov(ebp, (size_t)m_local.gd->tex); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - if (!m_sel.fst) - { - vrcpps(ymm0, ymm4); - - vmulps(ymm2, ymm0); - vmulps(ymm3, ymm0); - - vcvttps2dq(ymm2, ymm2); - vcvttps2dq(ymm3, ymm3); - } - - // ymm2 = u - // ymm3 = v - // ymm4 = q - // ymm0 = ymm1 = ymm5 = ymm6 = free - - // TODO: if the fractional part is not needed in round-off mode then there is a faster integer log2 (just take the exp) (but can we round it?) - - if (!m_sel.lcm) - { - // lod = -log2(Q) * (1 << L) + K - - vpcmpeqd(ymm1, ymm1); - vpsrld(ymm1, ymm1, 25); - vpslld(ymm0, ymm4, 1); - vpsrld(ymm0, ymm0, 24); - vpsubd(ymm0, ymm1); - vcvtdq2ps(ymm0, ymm0); - - // ymm0 = (float)(exp(q) - 127) - - vpslld(ymm4, ymm4, 9); - vpsrld(ymm4, ymm4, 9); - vorps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); - - // ymm4 = mant(q) | 1.0f - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(ymm5, ptr[g_const->m_log2_coef_256b[0]]); // c0 - vfmadd213ps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[1]]); // c0 * ymm4 + c1 - vfmadd213ps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[2]]); // (c0 * ymm4 + c1) * ymm4 + c2 - vsubps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); // ymm4 - 1.0f - vfmadd213ps(ymm4, ymm5, ymm0); // ((c0 * ymm4 + c1) * ymm4 + c2) * (ymm4 - 1.0f) + ymm0 - } - else - { - vmulps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[0]]); - vaddps(ymm5, ptr[g_const->m_log2_coef_256b[1]]); - vmulps(ymm5, ymm4); - vsubps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); - vaddps(ymm5, ptr[g_const->m_log2_coef_256b[2]]); - vmulps(ymm4, ymm5); - vaddps(ymm4, ymm0); - } - - // ymm4 = log2(Q) = ((((c0 * ymm4) + c1) * ymm4) + c2) * (ymm4 - 1.0f) + ymm0 - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(ymm5, ptr[&m_local.gd->l]); - vfmadd213ps(ymm4, ymm5, ptr[&m_local.gd->k]); - } - else - { - vmulps(ymm4, ptr[&m_local.gd->l]); - vaddps(ymm4, ptr[&m_local.gd->k]); - } - - // ymm4 = (-log2(Q) * (1 << L) + K) * 0x10000 - - vxorps(ymm0, ymm0); - vminps(ymm4, ptr[&m_local.gd->mxl]); - vmaxps(ymm4, ymm0); - vcvtps2dq(ymm4, ymm4); - - if (m_sel.mmin == 1) // round-off mode - { - mov(eax, 0x8000); - vmovd(xmm0, eax); - vpbroadcastd(ymm0, xmm0); - vpaddd(ymm4, ymm0); - } - - vpsrld(ymm0, ymm4, 16); - - vmovdqa(ptr[&m_local.temp.lod.i], ymm0); -/* -vpslld(ymm5, ymm0, 6); -vpslld(ymm6, ymm4, 16); -vpsrld(ymm6, ymm6, 24); -return; -*/ - if (m_sel.mmin == 2) // trilinear mode - { - vpshuflw(ymm1, ymm4, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm1, ymm1, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.temp.lod.f], ymm1); - } - - // shift u/v/minmax by (int)lod - - vpsravd(ymm2, ymm2, ymm0); - vpsravd(ymm3, ymm3, ymm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], ymm2); - vmovdqa(ptr[&m_local.temp.uv[1]], ymm3); - - // m_local.gd->t.minmax => m_local.temp.uv_minmax[0/1] - - vpxor(ymm1, ymm1); - - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpunpcklwd(ymm5, ymm4, ymm1); // minu - vpunpckhwd(ymm6, ymm4, ymm1); // minv - vpsrlvd(ymm5, ymm5, ymm0); - vpsrlvd(ymm6, ymm6, ymm0); - vpackusdw(ymm5, ymm6); - - vbroadcasti128(ymm4, ptr[&m_local.gd->t.max]); - vpunpcklwd(ymm6, ymm4, ymm1); // maxu - vpunpckhwd(ymm4, ymm4, ymm1); // maxv - vpsrlvd(ymm6, ymm6, ymm0); - vpsrlvd(ymm4, ymm4, ymm0); - vpackusdw(ymm6, ymm4); - - vmovdqa(ptr[&m_local.temp.uv_minmax[0]], ymm5); - vmovdqa(ptr[&m_local.temp.uv_minmax[1]], ymm6); - } - else - { - // lod = K - - vmovd(xmm0, ptr[&m_local.gd->lod.i.u32[0]]); - - vpsrad(ymm2, xmm0); - vpsrad(ymm3, xmm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], ymm2); - vmovdqa(ptr[&m_local.temp.uv[1]], ymm3); - - vmovdqa(ymm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(ymm6, ptr[&m_local.temp.uv_minmax[1]]); - } - - // ymm2 = m_local.temp.uv[0] = u (level m) - // ymm3 = m_local.temp.uv[1] = v (level m) - // ymm5 = minuv - // ymm6 = maxuv - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector8i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i x0 = uv0.upl16(); - // GSVector8i y0 = uv0.uph16() << tw; - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i x1 = uv1.upl16(); - // GSVector8i y1 = uv1.uph16() << tw; - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 0); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 0); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } - - if (m_sel.mmin != 1) // !round-off mode - { - vmovdqa(ptr[&m_local.temp.trb], ymm5); - vmovdqa(ptr[&m_local.temp.tga], ymm6); - - vmovdqa(ymm2, ptr[&m_local.temp.uv[0]]); - vmovdqa(ymm3, ptr[&m_local.temp.uv[1]]); - - vpsrad(ymm2, 1); - vpsrad(ymm3, 1); - - vmovdqa(ymm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(ymm6, ptr[&m_local.temp.uv_minmax[1]]); - - vpsrlw(ymm5, 1); - vpsrlw(ymm6, 1); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i x0 = uv0.upl16(); - // GSVector8i y0 = uv0.uph16() << tw; - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i x1 = uv1.upl16(); - // GSVector8i y1 = uv1.uph16() << tw; - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 1); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 1); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } - - vmovdqa(ymm0, ptr[m_sel.lcm ? &m_local.gd->lod.f : &m_local.temp.lod.f]); - vpsrlw(ymm0, ymm0, 1); - - vmovdqa(ymm2, ptr[&m_local.temp.trb]); - vmovdqa(ymm3, ptr[&m_local.temp.tga]); - - lerp16(ymm5, ymm2, ymm0, 0); - lerp16(ymm6, ymm3, ymm0, 0); - } - - pop(ebp); -} - -void GSDrawScanlineCodeGenerator::WrapLOD(const Ymm& uv) -{ - // ymm5 = minuv - // ymm6 = maxuv - // ymm0, ymm1, ymm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv, ymm5); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv, ymm0); - } - - vpminsw(uv, ymm6); - } - else - { - vpand(uv, ymm5); - - if (region) - { - vpor(uv, ymm6); - } - } - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, ymm5); - vpminsw(uv, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::WrapLOD(const Ymm& uv0, const Ymm& uv1) -{ - // ymm5 = minuv - // ymm6 = maxuv - // ymm0, ymm1, ymm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv0, ymm5); - vpmaxsw(uv1, ymm5); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv0, ymm0); - vpmaxsw(uv1, ymm0); - } - - vpminsw(uv0, ymm6); - vpminsw(uv1, ymm6); - } - else - { - vpand(uv0, ymm5); - vpand(uv1, ymm5); - - if (region) - { - vpor(uv0, ymm6); - vpor(uv1, ymm6); - } - } - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv0, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, ymm5); - vpminsw(uv0, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, ymm1, ymm0); - - // uv1 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv1, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, ymm5); - vpminsw(uv1, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::AlphaTFX() -{ - if (!m_sel.fb) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector8i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16(ymm6, ymm4, 1); - - clamp16(ymm6, ymm3); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_HIGHLIGHT: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(ymm2, ymm4); - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - vpsrlw(ymm4, 7); - - if (m_sel.tcc) - { - vpaddusb(ymm4, ymm6); - } - - mix16(ymm6, ymm4, ymm3); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(ymm2, ymm4); - - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if (m_sel.iip) - { - vpsrlw(ymm6, 7); - } - - break; - } - - if (m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if (!m_sel.abe) - { - // a = cov - - if (m_sel.edge) - { - vmovdqa(ymm0, ptr[&m_local.temp.cov]); - } - else - { - vpcmpeqd(ymm0, ymm0); - vpsllw(ymm0, 15); - vpsrlw(ymm0, 8); - } - - mix16(ymm6, ymm0, ymm1); - } - else - { - // a = a == 0x80 ? cov : a - - vpcmpeqd(ymm0, ymm0); - vpsllw(ymm0, 15); - vpsrlw(ymm0, 8); - - if (m_sel.edge) - { - vmovdqa(ymm1, ptr[&m_local.temp.cov]); - } - else - { - vmovdqa(ymm1, ymm0); - } - - vpcmpeqw(ymm0, ymm6); - vpsrld(ymm0, 16); - vpslld(ymm0, 16); - - vpblendvb(ymm6, ymm1, ymm0); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadMask() -{ - if (m_sel.fwrite) - { - vpbroadcastd(ymm3, ptr[&m_local.gd->fm]); - } - - if (m_sel.zwrite) - { - vpbroadcastd(ymm4, ptr[&m_local.gd->zm]); - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha() -{ - switch (m_sel.atst) - { - case ATST_NEVER: - // t = GSVector8i::xffffffff(); - vpcmpeqd(ymm1, ymm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpgtd(ymm1, ymm0); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpeqd(ymm1, ymm0); - vpcmpeqd(ymm0, ymm0); - vpxor(ymm1, ymm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_local.gd->aref; - vpsrld(ymm0, ymm6, 16); - vbroadcasti128(ymm1, ptr[&m_local.gd->aref]); - vpcmpgtd(ymm1, ymm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpeqd(ymm1, ymm0); - break; - } - - switch (m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - vpor(ymm7, ymm1); - alltrue(ymm7); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - vpor(ymm4, ymm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - vpor(ymm3, ymm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - vpor(ymm4, ymm1); - // fm |= t & GSVector8i::xff000000(); - vpsrld(ymm1, 24); - vpslld(ymm1, 24); - vpor(ymm3, ymm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX() -{ - if (!m_sel.fwrite) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector8i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16(ymm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - clamp16(ymm5, ymm1); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - if (m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) - { - // GSVector8i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm2, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - } - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - vmovdqa(ymm1, ymm6); - - modulate16(ymm6, ymm2, 1); - - vpshuflw(ymm2, ymm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(ymm2, ymm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpsrlw(ymm2, 7); - - vpaddw(ymm6, ymm2); - - clamp16(ymm6, ymm0); - - mix16(ymm6, ymm1, ymm0); - - // GSVector8i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16(ymm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - vpaddw(ymm5, ymm2); - - clamp16(ymm5, ymm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if (m_sel.iip) - { - vpsrlw(ymm5, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog() -{ - if (!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_local.gd->frb.lerp16<0>(rb, f); - // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - vmovdqa(ymm0, ptr[&m_local.temp.f]); - } - else - { - vpbroadcastw(ymm0, ptr[&m_local.p.f]); - } - - vmovdqa(ymm1, ymm6); - - vpbroadcastd(ymm2, ptr[&m_local.gd->frb]); - lerp16(ymm5, ymm2, ymm0, 0); - - vpbroadcastd(ymm2, ptr[&m_local.gd->fga]); - lerp16(ymm6, ymm2, ymm0, 0); - mix16(ymm6, ymm1, ymm0); -} - -void GSDrawScanlineCodeGenerator::ReadFrame() -{ - if (!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, ptr[esi]); - add(ebx, ptr[edi]); - and(ebx, HALF_VM_SIZE - 1); - - if (!m_sel.rfb) - { - return; - } - - ReadPixel(ymm2, ymm0, rbx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha() -{ - if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); - - if (m_sel.datm) - { - if (m_sel.fpsm == 2) - { - vpxor(ymm0, ymm0); - //vpsrld(ymm1, ymm2, 15); - vpslld(ymm1, ymm2, 16); - vpsrad(ymm1, 31); - vpcmpeqd(ymm1, ymm0); - } - else - { - vpcmpeqd(ymm0, ymm0); - vpxor(ymm1, ymm2, ymm0); - vpsrad(ymm1, 31); - } - } - else - { - if (m_sel.fpsm == 2) - { - vpslld(ymm1, ymm2, 16); - vpsrad(ymm1, 31); - } - else - { - vpsrad(ymm1, ymm2, 31); - } - } - - vpor(ymm7, ymm1); - - alltrue(ymm7); -} - -void GSDrawScanlineCodeGenerator::WriteMask() -{ - if (m_sel.notest) - { - return; - } - - // fm |= test; - // zm |= test; - - if (m_sel.fwrite) - { - vpor(ymm3, ymm7); - } - - if (m_sel.zwrite) - { - vpor(ymm4, ymm7); - } - - // int fzm = ~(fm == GSVector8i::xffffffff()).ps32(zm == GSVector8i::xffffffff()).mask(); - - vpcmpeqd(ymm1, ymm1); - - if (m_sel.fwrite && m_sel.zwrite) - { - vpcmpeqd(ymm0, ymm1, ymm4); - vpcmpeqd(ymm1, ymm3); - vpackssdw(ymm1, ymm0); - } - else if (m_sel.fwrite) - { - vpcmpeqd(ymm1, ymm3); - vpackssdw(ymm1, ymm1); - } - else if (m_sel.zwrite) - { - vpcmpeqd(ymm1, ymm4); - vpackssdw(ymm1, ymm1); - } - - vpmovmskb(edx, ymm1); - - not(edx); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf() -{ - if (!m_sel.zwrite) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - vmovdqa(ymm1, ptr[&m_local.temp.zs]); - } - else - { - vpbroadcastd(ymm1, ptr[&m_local.p.z]); - } - - if (m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - - vpblendvb(ymm1, ptr[&m_local.temp.zd], ymm4); - } - - bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; - - WritePixel(ymm1, ymm0, rbp, edx, fast, m_sel.zpsm, 1); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch (m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - vpsllw(ymm0, ymm2, 8); - vpsrlw(ymm0, 8); - vpsrlw(ymm1, ymm2, 8); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - vpcmpeqd(ymm7, ymm7); - - vpsrld(ymm7, 27); // 0x0000001f - vpand(ymm0, ymm2, ymm7); - vpslld(ymm0, 3); - - vpslld(ymm7, 10); // 0x00007c00 - vpand(ymm4, ymm2, ymm7); - vpslld(ymm4, 9); - - vpor(ymm0, ymm4); - - vpsrld(ymm7, 5); // 0x000003e0 - vpand(ymm1, ymm2, ymm7); - vpsrld(ymm1, 2); - - vpsllw(ymm7, 10); // 0x00008000 - vpand(ymm4, ymm2, ymm7); - vpslld(ymm4, 8); - - vpor(ymm1, ymm4); - - break; - } - } - - // ymm5, ymm6 = src rb, ga - // ymm0, ymm1 = dst rb, ga - // ymm2, ymm3 = used - // ymm4, ymm7 = free - - if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - vmovdqa(ymm4, ymm5); - } - - if (m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(ymm5, ymm0); - break; - case 2: - vpxor(ymm5, ymm5); - break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch (m_sel.abb) - { - case 0: - vpsubw(ymm5, ymm4); - break; - case 1: - vpsubw(ymm5, ymm0); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; - - switch (m_sel.abc) - { - case 0: - case 1: - vpshuflw(ymm7, m_sel.abc ? ymm1 : ymm6, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(ymm7, ymm7, _MM_SHUFFLE(3, 3, 1, 1)); - vpsllw(ymm7, 7); - break; - case 2: - vpbroadcastw(ymm7, ptr[&m_local.gd->afix]); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16(ymm5, ymm7, 1); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch (m_sel.abd) - { - case 0: - vpaddw(ymm5, ymm4); - break; - case 1: - vpaddw(ymm5, ymm0); - break; - case 2: - break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(ymm5, ymm0); - break; - case 2: - vpxor(ymm5, ymm5); - break; - } - } - - if (m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - vpslld(ymm0, ymm6, 8); - vpsrad(ymm0, 31); - - // rb = c[0].blend8(rb, mask); - - vpblendvb(ymm5, ymm4, ymm5, ymm0); - } - - // ymm6 = src ga - // ymm1 = dst ga - // ymm5 = rb - // ymm7 = a - // ymm2, ymm3 = used - // ymm0, ymm4 = free - - vmovdqa(ymm4, ymm6); - - if (m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(ymm6, ymm1); - break; - case 2: - vpxor(ymm6, ymm6); - break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch (m_sel.abb) - { - case 0: - vpsubw(ymm6, ymm4); - break; - case 1: - vpsubw(ymm6, ymm1); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16(ymm6, ymm7, 1); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch (m_sel.abd) - { - case 0: - vpaddw(ymm6, ymm4); - break; - case 1: - vpaddw(ymm6, ymm1); - break; - case 2: - break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(ymm6, ymm1); - break; - case 2: - vpxor(ymm6, ymm6); - break; - } - } - - // ymm4 = src ga - // ymm5 = rb - // ymm6 = ga - // ymm2, ymm3 = used - // ymm0, ymm1, ymm7 = free - - if (m_sel.pabe) - { - vpsrld(ymm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - vpblendvb(ymm6, ymm4, ymm6, ymm0); - } - else - { - if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(ymm6, ymm4, ymm7); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.fpsm == 2 && m_sel.dthe) - { - mov(eax, ptr[esp + _top]); - and(eax, 3); - shl(eax, 5); - mov(ebp, ptr[&m_local.gd->dimx]); - vbroadcasti128(ymm7, ptr[ebp + eax + sizeof(GSVector4i) * 0]); - vpaddw(ymm5, ymm7); - vbroadcasti128(ymm7, ptr[ebp + eax + sizeof(GSVector4i) * 1]); - vpaddw(ymm6, ymm7); - } - - if (m_sel.colclamp == 0) - { - // c[0] &= 0x00ff00ff; - // c[1] &= 0x00ff00ff; - - vpcmpeqd(ymm7, ymm7); - vpsrlw(ymm7, 8); - vpand(ymm5, ymm7); - vpand(ymm6, ymm7); - } - - // GSVector8i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - vpunpckhwd(ymm7, ymm5, ymm6); - vpunpcklwd(ymm5, ymm6); - vpackuswb(ymm5, ymm7); - - if (m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - vpcmpeqd(ymm7, ymm7); - vpslld(ymm7, 31); - vpor(ymm5, ymm7); - } - - if (m_sel.fpsm == 2) - { - // GSVector8i rb = fs & 0x00f800f8; - // GSVector8i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - vmovd(xmm6, eax); - vpbroadcastd(ymm6, xmm6); - - mov(eax, 0x8000f800); - vmovd(xmm7, eax); - vpbroadcastd(ymm7, xmm7); - - vpand(ymm4, ymm5, ymm6); - vpand(ymm5, ymm7); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - vpsrld(ymm6, ymm4, 9); - vpsrld(ymm4, 3); - vpsrld(ymm7, ymm5, 16); - vpsrld(ymm5, 6); - - vpor(ymm5, ymm4); - vpor(ymm7, ymm6); - vpor(ymm5, ymm7); - } - - if (m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(ymm5, ymm2, ymm3); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; - - WritePixel(ymm5, ymm0, rbx, edx, fast, m_sel.fpsm, 0); -} - -void GSDrawScanlineCodeGenerator::ReadPixel(const Ymm& dst, const Ymm& temp, const RegLong& addr) -{ - vmovq(Xmm(dst.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm]); - vmovhps(Xmm(dst.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2]); - vmovq(Xmm(temp.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2]); - vmovhps(Xmm(temp.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2]); - vinserti128(dst, dst, Xmm(temp.getIdx()), 1); - /* - vmovdqu(dst, ptr[addr * 2 + (size_t)m_local.gd->vm]); - vmovdqu(temp, ptr[addr * 2 + (size_t)m_local.gd->vm + 16 * 2]); - vpunpcklqdq(dst, dst, temp); - vpermq(dst, dst, _MM_SHUFFLE(3, 1, 2, 0)); -*/ -} - -void GSDrawScanlineCodeGenerator::WritePixel(const Ymm& src, const Ymm& temp, const RegLong& addr, const Reg32& mask, bool fast, int psm, int fz) -{ - Xmm src1 = Xmm(src.getIdx()); - Xmm src2 = Xmm(temp.getIdx()); - - vextracti128(src2, src, 1); - - if (m_sel.notest) - { - if (fast) - { - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src1); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src1); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2], src2); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2], src2); - } - else - { - WritePixel(src1, addr, 0, 0, psm); - WritePixel(src1, addr, 1, 1, psm); - WritePixel(src1, addr, 2, 2, psm); - WritePixel(src1, addr, 3, 3, psm); - WritePixel(src2, addr, 4, 0, psm); - WritePixel(src2, addr, 5, 1, psm); - WritePixel(src2, addr, 6, 2, psm); - WritePixel(src2, addr, 7, 3, psm); - } - } - else - { - // cascade tests? - - if (fast) - { - test(mask, 0x0000000f << (fz * 8)); - je("@f"); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src1); - L("@@"); - - test(mask, 0x000000f0 << (fz * 8)); - je("@f"); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src1); - L("@@"); - - test(mask, 0x000f0000 << (fz * 8)); - je("@f"); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2], src2); - L("@@"); - - test(mask, 0x00f00000 << (fz * 8)); - je("@f"); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2], src2); - L("@@"); - - // vmaskmovps? - } - else - { - test(mask, 0x00000003 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 0, 0, psm); - L("@@"); - - test(mask, 0x0000000c << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 1, 1, psm); - L("@@"); - - test(mask, 0x00000030 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 2, 2, psm); - L("@@"); - - test(mask, 0x000000c0 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 3, 3, psm); - L("@@"); - - test(mask, 0x00030000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 4, 0, psm); - L("@@"); - - test(mask, 0x000c0000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 5, 1, psm); - L("@@"); - - test(mask, 0x00300000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 6, 2, psm); - L("@@"); - - test(mask, 0x00c00000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 7, 3, psm); - L("@@"); - } - } -} - -static const int s_offsets[] = {0, 2, 8, 10, 16, 18, 24, 26}; - -void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const RegLong& addr, uint8 i, uint8 j, int psm) -{ - Address dst = ptr[addr * 2 + (size_t)m_local.gd->vm + s_offsets[i] * 2]; - - switch (psm) - { - case 0: - if (j == 0) - vmovd(dst, src); - else - vpextrd(dst, src, j); - break; - case 1: - if (j == 0) - vmovd(eax, src); - else - vpextrd(eax, src, j); - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - if (j == 0) - vmovd(eax, src); - else - vpextrw(eax, src, j * 2); - mov(dst, ax); - break; - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(int pixels, int mip_offset) -{ - // in - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ebx = m_local.tex[0] (!m_sel.mmin) - // ebp = m_local.tex (m_sel.mmin) - // edx = m_local.clut (m_sel.tlu) - - // out - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - - ASSERT(pixels == 1 || pixels == 4); - - mip_offset *= sizeof(void*); - - const GSVector8i* lod_i = m_sel.lcm ? &m_local.gd->lod.i : &m_local.temp.lod.i; - - if (m_sel.mmin && !m_sel.lcm) - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - const int t[] = {1, 4, 5, 1, 2, 5, 0, 2}; - - for (int i = 0; i < pixels; i++) - { - Ymm src = Ymm(r[i * 2 + 0]); - Ymm dst = Ymm(r[i * 2 + 1]); - Ymm t1 = Ymm(t[i * 2 + 0]); - Ymm t2 = Ymm(t[i * 2 + 1]); - - vextracti128(Xmm(t1.getIdx()), src, 1); - - for (uint8 j = 0; j < 4; j++) - { - mov(ebx, ptr[&lod_i->u32[j + 0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - ReadTexel(dst, src, j); - - mov(ebx, ptr[&lod_i->u32[j + 4]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - ReadTexel(t2, t1, j); - } - - vinserti128(dst, dst, Xmm(t2.getIdx()), 1); - } - } - else - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - const int t[] = {1, 4, 5, 1, 2, 5, 0, 2}; - - if (m_sel.mmin && m_sel.lcm) - { - mov(ebx, ptr[&lod_i->u32[0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - } - - for (int i = 0; i < pixels; i++) - { - Ymm src = Ymm(r[i * 2 + 0]); - Ymm dst = Ymm(r[i * 2 + 1]); - Ymm t1 = Ymm(t[i * 2 + 0]); - Ymm t2 = Ymm(t[i * 2 + 1]); - - if (!m_sel.tlu) - { - vpcmpeqd(t1, t1); - vpgatherdd(dst, ptr[ebx + src * 4], t1); - } - else - { - vextracti128(Xmm(t1.getIdx()), src, 1); - - for (uint8 j = 0; j < 4; j++) - { - ReadTexel(dst, src, j); - ReadTexel(t2, t1, j); - } - - vinserti128(dst, dst, Xmm(t2.getIdx()), 1); - /* - vpcmpeqd(t1, t1); - vpgatherdd(t2, ptr[ebx + src * 1], t1); // either this 1x scale, or the latency of two dependendent gathers are too slow - vpslld(t2, 24); - vpsrld(t2, 24); - vpcmpeqd(t1, t1); - vpgatherdd(dst, ptr[edx + t2 * 4], t1); - */ - } - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(const Ymm& dst, const Ymm& addr, uint8 i) -{ - ASSERT(i < 4); - - const Address& src = m_sel.tlu ? ptr[edx + eax * 4] : ptr[ebx + eax * 4]; - - if (i == 0) - vmovd(eax, Xmm(addr.getIdx())); - else - vpextrd(eax, Xmm(addr.getIdx()), i); - - if (m_sel.tlu) - movzx(eax, byte[ebx + eax]); - - if (i == 0) - vmovd(Xmm(dst.getIdx()), src); - else - vpinsrd(Xmm(dst.getIdx()), src, i); -} - - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.cpp deleted file mode 100644 index c50205d324f87..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x64.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" - -#if _M_SSE < 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -// It is useless to port the code to SSEx, better use the faster 32 bits version instead -void GSDrawScanlineCodeGenerator::Generate_SSE() -{ - // Avoid a crash if someone want to use it - ret(); -} - -void GSDrawScanlineCodeGenerator::Init_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::Step_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::TestZ_SSE(const Xmm& temp1, const Xmm& temp2) -{ -} - -void GSDrawScanlineCodeGenerator::SampleTexture_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::Wrap_SSE(const Xmm& uv) -{ -} - -void GSDrawScanlineCodeGenerator::Wrap_SSE(const Xmm& uv0, const Xmm& uv1) -{ -} - -void GSDrawScanlineCodeGenerator::AlphaTFX_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::ReadMask_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::TestAlpha_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::ColorTFX_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::Fog_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::ReadFrame_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::WriteMask_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::WriteZBuf_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::AlphaBlend_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::WriteFrame_SSE() -{ -} - -void GSDrawScanlineCodeGenerator::ReadPixel_SSE(const Xmm& dst, const RegLong& addr) -{ -} - -void GSDrawScanlineCodeGenerator::WritePixel_SSE(const Xmm& src, const RegLong& addr, const Reg8& mask, bool fast, int psm, int fz) -{ -} - -//static const int s_offsets[4] = {0, 2, 8, 10}; - -void GSDrawScanlineCodeGenerator::WritePixel_SSE(const Xmm& src, const RegLong& addr, uint8 i, int psm) -{ -} - -void GSDrawScanlineCodeGenerator::ReadTexel_SSE(int pixels, int mip_offset) -{ -} - -void GSDrawScanlineCodeGenerator::ReadTexel_SSE(const Xmm& dst, const Xmm& addr, uint8 i) -{ -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx.cpp deleted file mode 100644 index 33c468f9128db..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx.cpp +++ /dev/null @@ -1,2936 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 16; -static const int _top = _args + 4; -static const int _v = _args + 8; - -void GSDrawScanlineCodeGenerator::Generate_AVX() -{ - push(ebx); - push(esi); - push(edi); - push(ebp); - - Init_AVX(); - - if (!m_sel.edge) - { - align(16); - } - -L("loop"); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // xmm0 = z/zi - // xmm2 = s/u (tme) - // xmm3 = t/v (tme) - // xmm4 = q (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - bool tme = m_sel.tfx != TFX_NONE; - - TestZ_AVX(tme ? xmm5 : xmm2, tme ? xmm6 : xmm3); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - xmm0 - // xmm2 = s/u (tme) - // xmm3 = t/v (tme) - // xmm4 = q (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - if (m_sel.mmin) - { - SampleTextureLOD_AVX(); - } - else - { - SampleTexture_AVX(); - } - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - xmm2 - // - xmm3 - // - xmm4 - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - AlphaTFX_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ReadMask_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestAlpha_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ColorTFX_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - Fog_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ReadFrame_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestDestAlpha_AVX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - WriteMask_AVX(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - - WriteZBuf_AVX(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // - ebp - // xmm2 = fd - // xmm3 = fm - // - xmm4 - // xmm5 = rb - // xmm6 = ga - - AlphaBlend_AVX(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // xmm2 = fd - // xmm3 = fm - // xmm5 = rb - // xmm6 = ga - - WriteFrame_AVX(); - -L("step"); - - // if(steps <= 0) break; - - if (!m_sel.edge) - { - test(ecx, ecx); - - jle("exit", T_NEAR); - - Step_AVX(); - - jmp("loop", T_NEAR); - } - -L("exit"); - - // vzeroupper(); - - pop(ebp); - pop(edi); - pop(esi); - pop(ebx); - - ret(8); -} - -void GSDrawScanlineCodeGenerator::Init_AVX() -{ - if (!m_sel.notest) - { - // int skip = left & 3; - - mov(ebx, edx); - and(edx, 3); - - // int steps = pixels + skip - 4; - - lea(ecx, ptr[ecx + edx - 4]); - - // left -= skip; - - sub(ebx, edx); - - // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; - - shl(edx, 4); - - vmovdqa(xmm7, ptr[edx + (size_t)g_const->m_test_128b[0]]); - - mov(eax, ecx); - sar(eax, 31); - and(eax, ecx); - shl(eax, 4); - - vpor(xmm7, ptr[eax + (size_t)g_const->m_test_128b[7]]); - } - else - { - mov(ebx, edx); // left - xor(edx, edx); // skip - lea(ecx, ptr[ecx - 4]); // steps - } - - // GSVector2i* fza_base = &m_local.gd->fzbr[top]; - - mov(esi, ptr[esp + _top]); - lea(esi, ptr[esi * 8]); - add(esi, ptr[&m_local.gd->fzbr]); - - // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; - - lea(edi, ptr[ebx * 2]); - add(edi, ptr[&m_local.gd->fzbc]); - - if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // edx = &m_local.d[skip] - - lea(edx, ptr[edx * 8 + (size_t)m_local.d]); - - // ebx = &v - - mov(ebx, ptr[esp + _v]); - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.fwrite && m_sel.fge || m_sel.zb) - { - vmovaps(xmm0, ptr[ebx + offsetof(GSVertexSW, p)]); // v.p - - if (m_sel.fwrite && m_sel.fge) - { - // f = GSVector4i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); - - vcvttps2dq(xmm1, xmm0); - vpshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpaddw(xmm1, ptr[edx + offsetof(GSScanlineLocalData::skip, f)]); - - vmovdqa(ptr[&m_local.temp.f], xmm1); - } - - if (m_sel.zb) - { - // z = vp.zzzz() + m_local.d[skip].z; - - vshufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - vmovaps(ptr[&m_local.temp.z], xmm0); - vmovaps(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, z)]); - vmovaps(ptr[&m_local.temp.zo], xmm2); - vaddps(xmm0, xmm2); - } - } - } - else - { - if (m_sel.ztest) - { - vmovdqa(xmm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.edge || m_sel.tfx != TFX_NONE) - { - vmovaps(xmm4, ptr[ebx + offsetof(GSVertexSW, t)]); // v.t - } - - if (m_sel.edge) - { - // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); - - vpshufhw(xmm3, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(xmm3, xmm3, _MM_SHUFFLE(3, 3, 3, 3)); - vpsrlw(xmm3, 9); - - vmovdqa(ptr[&m_local.temp.cov], xmm3); - } - - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i vti(vt); - - vcvttps2dq(xmm6, xmm4); - - // s = vti.xxxx() + m_local.d[skip].s; - // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; - - vpshufd(xmm2, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm3, xmm6, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddd(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpaddd(xmm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - } - else - { - if (m_sel.ltf) - { - vpshuflw(xmm6, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm6, xmm6, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm6, 12); - vmovdqa(ptr[&m_local.temp.vf], xmm6); - } - } - - vmovdqa(ptr[&m_local.temp.s], xmm2); - vmovdqa(ptr[&m_local.temp.t], xmm3); - } - else - { - // s = vt.xxxx() + m_local.d[skip].s; - // t = vt.yyyy() + m_local.d[skip].t; - // q = vt.zzzz() + m_local.d[skip].q; - - vshufps(xmm2, xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(xmm3, xmm4, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(xmm4, xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - vaddps(xmm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - vaddps(xmm4, ptr[edx + offsetof(GSScanlineLocalData::skip, q)]); - - vmovaps(ptr[&m_local.temp.s], xmm2); - vmovaps(ptr[&m_local.temp.t], xmm3); - vmovaps(ptr[&m_local.temp.q], xmm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - vcvttps2dq(xmm6, ptr[ebx + offsetof(GSVertexSW, c)]); // v.c - - // vc = vc.upl16(vc.zwxy()); - - vpshufd(xmm5, xmm6, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(xmm6, xmm5); - - // rb = vc.xxxx().add16(m_local.d[skip].rb); - // ga = vc.zzzz().add16(m_local.d[skip].ga); - - vpshufd(xmm5, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm6, xmm6, _MM_SHUFFLE(2, 2, 2, 2)); - - vpaddw(xmm5, ptr[edx + offsetof(GSScanlineLocalData::skip, rb)]); - vpaddw(xmm6, ptr[edx + offsetof(GSScanlineLocalData::skip, ga)]); - - vmovdqa(ptr[&m_local.temp.rb], xmm5); - vmovdqa(ptr[&m_local.temp.ga], xmm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(xmm5, ptr[&m_local.c.rb]); - vmovdqa(xmm6, ptr[&m_local.c.ga]); - } - } - } - } -} - -void GSDrawScanlineCodeGenerator::Step_AVX() -{ - // steps -= 4; - - sub(ecx, 4); - - // fza_offset++; - - add(edi, 8); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // z += m_local.d4.z; - - if (m_sel.zb) - { - vmovaps(xmm0, ptr[&m_local.temp.zo]); - vaddps(xmm0, ptr[&m_local.d4.z]); - vmovaps(ptr[&m_local.temp.zo], xmm0); - vaddps(xmm0, ptr[&m_local.temp.z]); - } - - // f = f.add16(m_local.d4.f); - - if (m_sel.fwrite && m_sel.fge) - { - vmovdqa(xmm1, ptr[&m_local.temp.f]); - vpaddw(xmm1, ptr[&m_local.d4.f]); - vmovdqa(ptr[&m_local.temp.f], xmm1); - } - } - else - { - if (m_sel.ztest) - { - vmovdqa(xmm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i stq = m_local.d4.stq; - - // s += stq.xxxx(); - // if(!sprite) t += stq.yyyy(); - - vmovdqa(xmm4, ptr[&m_local.d4.stq]); - - vpshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - vpaddd(xmm2, ptr[&m_local.temp.s]); - vmovdqa(ptr[&m_local.temp.s], xmm2); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - vpaddd(xmm3, ptr[&m_local.temp.t]); - vmovdqa(ptr[&m_local.temp.t], xmm3); - } - else - { - vmovdqa(xmm3, ptr[&m_local.temp.t]); - } - } - else - { - // GSVector4 stq = m_local.d4.stq; - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - vmovaps(xmm4, ptr[&m_local.d4.stq]); - - vshufps(xmm2, xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(xmm3, xmm4, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(xmm4, xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(xmm2, ptr[&m_local.temp.s]); - vaddps(xmm3, ptr[&m_local.temp.t]); - vaddps(xmm4, ptr[&m_local.temp.q]); - - vmovaps(ptr[&m_local.temp.s], xmm2); - vmovaps(ptr[&m_local.temp.t], xmm3); - vmovaps(ptr[&m_local.temp.q], xmm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i c = m_local.d4.c; - - // rb = rb.add16(c.xxxx()); - // ga = ga.add16(c.yyyy()); - - vmovdqa(xmm7, ptr[&m_local.d4.c]); - - vpshufd(xmm5, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm6, xmm7, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddw(xmm5, ptr[&m_local.temp.rb]); - vpaddw(xmm6, ptr[&m_local.temp.ga]); - - // FIXME: color may underflow and roll over at the end of the line, if decreasing - - vpxor(xmm7, xmm7); - vpmaxsw(xmm5, xmm7); - vpmaxsw(xmm6, xmm7); - - vmovdqa(ptr[&m_local.temp.rb], xmm5); - vmovdqa(ptr[&m_local.temp.ga], xmm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(xmm5, ptr[&m_local.c.rb]); - vmovdqa(xmm6, ptr[&m_local.c.ga]); - } - } - } - } - - if (!m_sel.notest) - { - // test = m_test[7 + (steps & (steps >> 31))]; - - mov(edx, ecx); - sar(edx, 31); - and(edx, ecx); - shl(edx, 4); - - vmovdqa(xmm7, ptr[edx + (size_t)g_const->m_test_128b[7]]); - } -} - -void GSDrawScanlineCodeGenerator::TestZ_AVX(const Xmm& temp1, const Xmm& temp2) -{ - if (!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, ptr[esi + 4]); - add(ebp, ptr[edi + 4]); - and(ebp, HALF_VM_SIZE - 1); - - // GSVector4i zs = zi; - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.zoverflow) - { - // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); - - vbroadcastss(temp1, ptr[&GSVector4::m_half]); - vmulps(temp1, xmm0); - vcvttps2dq(temp1, temp1); - vpslld(temp1, 1); - - vcvttps2dq(xmm0, xmm0); - vpcmpeqd(temp2, temp2); - vpsrld(temp2, 31); - vpand(xmm0, temp2); - - vpor(xmm0, temp1); - } - else - { - // zs = GSVector4i(z); - - vcvttps2dq(xmm0, xmm0); - } - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - vpcmpeqd(temp1, temp1); - vpsrld(temp1, (uint8)((m_sel.zpsm & 0x3) * 8)); - vpminsd(xmm0, temp1); - } - - if (m_sel.zwrite) - { - vmovdqa(ptr[&m_local.temp.zs], xmm0); - } - } - - if (m_sel.ztest) - { - ReadPixel_AVX(xmm1, ebp); - - if (m_sel.zwrite && m_sel.zpsm < 2) - { - vmovdqa(ptr[&m_local.temp.zd], xmm1); - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if (m_sel.zpsm) - { - vpslld(xmm1, static_cast(m_sel.zpsm * 8)); - vpsrld(xmm1, static_cast(m_sel.zpsm * 8)); - } - - if (m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector4i o = GSVector4i::x80000000(); - - vpcmpeqd(temp1, temp1); - vpslld(temp1, 31); - - // GSVector4i zso = zs - o; - // GSVector4i zdo = zd - o; - - vpsubd(xmm0, temp1); - vpsubd(xmm1, temp1); - } - - switch (m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - vpcmpgtd(xmm1, xmm0); - vpor(xmm7, xmm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - vpcmpgtd(xmm0, xmm1); - vpcmpeqd(temp1, temp1); - vpxor(xmm0, temp1); - vpor(xmm7, xmm0); - break; - } - - alltrue(xmm7); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture_AVX() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - mov(ebx, ptr[&m_local.gd->tex[0]]); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - // ebx = tex - // edx = clut - - if (!m_sel.fst) - { - vrcpps(xmm0, xmm4); - - vmulps(xmm2, xmm0); - vmulps(xmm3, xmm0); - - vcvttps2dq(xmm2, xmm2); - vcvttps2dq(xmm3, xmm3); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - vpsubd(xmm2, xmm4); - vpsubd(xmm3, xmm4); - } - } - - // xmm2 = u - // xmm3 = v - - if (m_sel.ltf) - { - // GSVector4i uf = u.xxzzlh().srl16(12); - - vpshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.uf], xmm0); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4i vf = v.xxzzlh().srl16(12); - - vpshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.vf], xmm0); - } - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(xmm2, 16); - vpsrad(xmm3, 16); - vpackssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(xmm1, xmm1); - vpsrlw(xmm1, 15); - vpaddw(xmm3, xmm2, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap_AVX(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - Wrap_AVX(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i y0 = uv0.uph16() << tw; - // GSVector4i x0 = uv0.upl16(); - - vpxor(xmm0, xmm0); - - vpunpcklwd(xmm4, xmm2, xmm0); - vpunpckhwd(xmm2, xmm2, xmm0); - vpslld(xmm2, static_cast(m_sel.tw + 3)); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i y1 = uv1.uph16() << tw; - // GSVector4i x1 = uv1.upl16(); - - vpunpcklwd(xmm6, xmm3, xmm0); - vpunpckhwd(xmm3, xmm3, xmm0); - vpslld(xmm3, static_cast(m_sel.tw + 3)); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - vpaddd(xmm5, xmm2, xmm4); - vpaddd(xmm2, xmm2, xmm6); - vpaddd(xmm0, xmm3, xmm4); - vpaddd(xmm3, xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(4, 0); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - vmovdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - vpaddd(xmm5, xmm2, xmm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(1, 0); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } -} - -void GSDrawScanlineCodeGenerator::Wrap_AVX(const Xmm& uv) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv, ptr[&m_local.gd->t.min]); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv, xmm0); - } - - vpminsw(uv, ptr[&m_local.gd->t.max]); - } - else - { - vpand(uv, ptr[&m_local.gd->t.min]); - - if (region) - { - vpor(uv, ptr[&m_local.gd->t.max]); - } - } - } - else - { - vmovdqa(xmm4, ptr[&m_local.gd->t.min]); - vmovdqa(xmm5, ptr[&m_local.gd->t.max]); - vmovdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv, xmm4); - - if (region) - { - vpor(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, xmm4); - vpminsw(uv, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::Wrap_AVX(const Xmm& uv0, const Xmm& uv1) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vmovdqa(xmm4, ptr[&m_local.gd->t.min]); - vpmaxsw(uv0, xmm4); - vpmaxsw(uv1, xmm4); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv0, xmm0); - vpmaxsw(uv1, xmm0); - } - - vmovdqa(xmm5, ptr[&m_local.gd->t.max]); - vpminsw(uv0, xmm5); - vpminsw(uv1, xmm5); - } - else - { - vmovdqa(xmm4, ptr[&m_local.gd->t.min]); - vpand(uv0, xmm4); - vpand(uv1, xmm4); - - if (region) - { - vmovdqa(xmm5, ptr[&m_local.gd->t.max]); - vpor(uv0, xmm5); - vpor(uv1, xmm5); - } - } - } - else - { - vmovdqa(xmm4, ptr[&m_local.gd->t.min]); - vmovdqa(xmm5, ptr[&m_local.gd->t.max]); - vmovdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv0, xmm4); - - if (region) - { - vpor(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, xmm4); - vpminsw(uv0, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, xmm1, xmm0); - - // uv1 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv1, xmm4); - - if (region) - { - vpor(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, xmm4); - vpminsw(uv1, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::SampleTextureLOD_AVX() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - push(ebp); - - mov(ebp, (size_t)m_local.gd->tex); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - if (!m_sel.fst) - { - vrcpps(xmm0, xmm4); - - vmulps(xmm2, xmm0); - vmulps(xmm3, xmm0); - - vcvttps2dq(xmm2, xmm2); - vcvttps2dq(xmm3, xmm3); - } - - // xmm2 = u - // xmm3 = v - // xmm4 = q - // xmm0 = xmm1 = xmm5 = xmm6 = free - - // TODO: if the fractional part is not needed in round-off mode then there is a faster integer log2 (just take the exp) (but can we round it?) - - if (!m_sel.lcm) - { - // lod = -log2(Q) * (1 << L) + K - - vpcmpeqd(xmm1, xmm1); - vpsrld(xmm1, xmm1, 25); - vpslld(xmm0, xmm4, 1); - vpsrld(xmm0, xmm0, 24); - vpsubd(xmm0, xmm1); - vcvtdq2ps(xmm0, xmm0); - - // xmm0 = (float)(exp(q) - 127) - - vpslld(xmm4, xmm4, 9); - vpsrld(xmm4, xmm4, 9); - vorps(xmm4, ptr[g_const->m_log2_coef_128b[3]]); - - // xmm4 = mant(q) | 1.0f - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(xmm5, ptr[g_const->m_log2_coef_128b[0]]); // c0 - vfmadd213ps(xmm5, xmm4, ptr[g_const->m_log2_coef_128b[1]]); // c0 * xmm4 + c1 - vfmadd213ps(xmm5, xmm4, ptr[g_const->m_log2_coef_128b[2]]); // (c0 * xmm4 + c1) * xmm4 + c2 - vsubps(xmm4, ptr[g_const->m_log2_coef_128b[3]]); // xmm4 - 1.0f - vfmadd213ps(xmm4, xmm5, xmm0); // ((c0 * xmm4 + c1) * xmm4 + c2) * (xmm4 - 1.0f) + xmm0 - } - else - { - vmulps(xmm5, xmm4, ptr[g_const->m_log2_coef_128b[0]]); - vaddps(xmm5, ptr[g_const->m_log2_coef_128b[1]]); - vmulps(xmm5, xmm4); - vsubps(xmm4, ptr[g_const->m_log2_coef_128b[3]]); - vaddps(xmm5, ptr[g_const->m_log2_coef_128b[2]]); - vmulps(xmm4, xmm5); - vaddps(xmm4, xmm0); - } - - // xmm4 = log2(Q) = ((((c0 * xmm4) + c1) * xmm4) + c2) * (xmm4 - 1.0f) + xmm0 - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(xmm5, ptr[&m_local.gd->l]); - vfmadd213ps(xmm4, xmm5, ptr[&m_local.gd->k]); - } - else - { - vmulps(xmm4, ptr[&m_local.gd->l]); - vaddps(xmm4, ptr[&m_local.gd->k]); - } - - // xmm4 = (-log2(Q) * (1 << L) + K) * 0x10000 - - vxorps(xmm0, xmm0); - vminps(xmm4, ptr[&m_local.gd->mxl]); - vmaxps(xmm4, xmm0); - vcvtps2dq(xmm4, xmm4); - - if (m_sel.mmin == 1) // round-off mode - { - mov(eax, 0x8000); - vmovd(xmm0, eax); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpaddd(xmm4, xmm0); - } - - vpsrld(xmm0, xmm4, 16); - - vmovdqa(ptr[&m_local.temp.lod.i], xmm0); -/* -vpslld(xmm5, xmm0, 6); -vpslld(xmm6, xmm4, 16); -vpsrld(xmm6, xmm6, 24); -return; -*/ - if (m_sel.mmin == 2) // trilinear mode - { - vpshuflw(xmm1, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.temp.lod.f], xmm1); - } - - // shift u/v/minmax by (int)lod - - if (m_cpu.has(util::Cpu::tAVX2)) - { - vpsravd(xmm2, xmm2, xmm0); - vpsravd(xmm3, xmm3, xmm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], xmm2); - vmovdqa(ptr[&m_local.temp.uv[1]], xmm3); - - // m_local.gd->t.minmax => m_local.temp.uv_minmax[0/1] - - vpxor(xmm1, xmm1); - - vmovdqa(xmm4, ptr[&m_local.gd->t.min]); - vpunpcklwd(xmm5, xmm4, xmm1); // minu - vpunpckhwd(xmm6, xmm4, xmm1); // minv - vpsrlvd(xmm5, xmm5, xmm0); - vpsrlvd(xmm6, xmm6, xmm0); - vpackusdw(xmm5, xmm6); - - vmovdqa(xmm4, ptr[&m_local.gd->t.max]); - vpunpcklwd(xmm6, xmm4, xmm1); // maxu - vpunpckhwd(xmm4, xmm4, xmm1); // maxv - vpsrlvd(xmm6, xmm6, xmm0); - vpsrlvd(xmm4, xmm4, xmm0); - vpackusdw(xmm6, xmm4); - - vmovdqa(ptr[&m_local.temp.uv_minmax[0]], xmm5); - vmovdqa(ptr[&m_local.temp.uv_minmax[1]], xmm6); - } - else - { - vmovq(xmm4, ptr[&m_local.gd->t.minmax]); - - vpunpckldq(xmm5, xmm2, xmm3); - vpunpckhdq(xmm6, xmm2, xmm3); - vmovdqa(xmm2, xmm5); - vmovdqa(xmm3, xmm6); - - vmovd(xmm0, ptr[&m_local.temp.lod.i.u32[0]]); - vpsrad(xmm2, xmm0); - vpsrlw(xmm1, xmm4, xmm0); - vmovq(ptr[&m_local.temp.uv_minmax[0].u32[0]], xmm1); - - vmovd(xmm0, ptr[&m_local.temp.lod.i.u32[1]]); - vpsrad(xmm5, xmm0); - vpsrlw(xmm1, xmm4, xmm0); - vmovq(ptr[&m_local.temp.uv_minmax[1].u32[0]], xmm1); - - vmovd(xmm0, ptr[&m_local.temp.lod.i.u32[2]]); - vpsrad(xmm3, xmm0); - vpsrlw(xmm1, xmm4, xmm0); - vmovq(ptr[&m_local.temp.uv_minmax[0].u32[2]], xmm1); - - vmovd(xmm0, ptr[&m_local.temp.lod.i.u32[3]]); - vpsrad(xmm6, xmm0); - vpsrlw(xmm1, xmm4, xmm0); - vmovq(ptr[&m_local.temp.uv_minmax[1].u32[2]], xmm1); - - vpunpckldq(xmm2, xmm3); - vpunpckhdq(xmm5, xmm6); - vpunpckhdq(xmm3, xmm2, xmm5); - vpunpckldq(xmm2, xmm5); - - vmovdqa(ptr[&m_local.temp.uv[0]], xmm2); - vmovdqa(ptr[&m_local.temp.uv[1]], xmm3); - - vmovdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - - vpunpcklwd(xmm0, xmm5, xmm6); - vpunpckhwd(xmm1, xmm5, xmm6); - vpunpckldq(xmm5, xmm0, xmm1); - vpunpckhdq(xmm6, xmm0, xmm1); - - vmovdqa(ptr[&m_local.temp.uv_minmax[0]], xmm5); - vmovdqa(ptr[&m_local.temp.uv_minmax[1]], xmm6); - } - } - else - { - // lod = K - - vmovd(xmm0, ptr[&m_local.gd->lod.i.u32[0]]); - - vpsrad(xmm2, xmm0); - vpsrad(xmm3, xmm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], xmm2); - vmovdqa(ptr[&m_local.temp.uv[1]], xmm3); - - vmovdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - } - - // xmm2 = m_local.temp.uv[0] = u (level m) - // xmm3 = m_local.temp.uv[1] = v (level m) - // xmm5 = minuv - // xmm6 = maxuv - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - vpsubd(xmm2, xmm4); - vpsubd(xmm3, xmm4); - - // GSVector4i uf = u.xxzzlh().srl16(1); - - vpshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.uf], xmm0); - - // GSVector4i vf = v.xxzzlh().srl16(1); - - vpshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.vf], xmm0); - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(xmm2, 16); - vpsrad(xmm3, 16); - vpackssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(xmm1, xmm1); - vpsrlw(xmm1, 15); - vpaddw(xmm3, xmm2, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD_AVX(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD_AVX(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i x0 = uv0.upl16(); - // GSVector4i y0 = uv0.uph16() << tw; - - vpxor(xmm0, xmm0); - - vpunpcklwd(xmm4, xmm2, xmm0); - vpunpckhwd(xmm2, xmm2, xmm0); - vpslld(xmm2, static_cast(m_sel.tw + 3)); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i x1 = uv1.upl16(); - // GSVector4i y1 = uv1.uph16() << tw; - - vpunpcklwd(xmm6, xmm3, xmm0); - vpunpckhwd(xmm3, xmm3, xmm0); - vpslld(xmm3, static_cast(m_sel.tw + 3)); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - vpaddd(xmm5, xmm2, xmm4); - vpaddd(xmm2, xmm2, xmm6); - vpaddd(xmm0, xmm3, xmm4); - vpaddd(xmm3, xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(4, 0); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - vmovdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - vpaddd(xmm5, xmm2, xmm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(1, 0); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } - - if (m_sel.mmin != 1) // !round-off mode - { - vmovdqa(ptr[&m_local.temp.trb], xmm5); - vmovdqa(ptr[&m_local.temp.tga], xmm6); - - vmovdqa(xmm2, ptr[&m_local.temp.uv[0]]); - vmovdqa(xmm3, ptr[&m_local.temp.uv[1]]); - - vpsrad(xmm2, 1); - vpsrad(xmm3, 1); - - vmovdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - - vpsrlw(xmm5, 1); - vpsrlw(xmm6, 1); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - vpsubd(xmm2, xmm4); - vpsubd(xmm3, xmm4); - - // GSVector4i uf = u.xxzzlh().srl16(1); - - vpshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.uf], xmm0); - - // GSVector4i vf = v.xxzzlh().srl16(1); - - vpshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(xmm0, 12); - vmovdqa(ptr[&m_local.temp.vf], xmm0); - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(xmm2, 16); - vpsrad(xmm3, 16); - vpackssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(xmm1, xmm1); - vpsrlw(xmm1, 15); - vpaddw(xmm3, xmm2, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD_AVX(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD_AVX(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i x0 = uv0.upl16(); - // GSVector4i y0 = uv0.uph16() << tw; - - vpxor(xmm0, xmm0); - - vpunpcklwd(xmm4, xmm2, xmm0); - vpunpckhwd(xmm2, xmm2, xmm0); - vpslld(xmm2, static_cast(m_sel.tw + 3)); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i x1 = uv1.upl16(); - // GSVector4i y1 = uv1.uph16() << tw; - - vpunpcklwd(xmm6, xmm3, xmm0); - vpunpckhwd(xmm3, xmm3, xmm0); - vpslld(xmm3, static_cast(m_sel.tw + 3)); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - vpaddd(xmm5, xmm2, xmm4); - vpaddd(xmm2, xmm2, xmm6); - vpaddd(xmm0, xmm3, xmm4); - vpaddd(xmm3, xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(4, 1); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - vmovdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - vpaddd(xmm5, xmm2, xmm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_AVX(1, 1); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } - - vmovdqa(xmm0, ptr[m_sel.lcm ? &m_local.gd->lod.f : &m_local.temp.lod.f]); - vpsrlw(xmm0, xmm0, 1); - - vmovdqa(xmm2, ptr[&m_local.temp.trb]); - vmovdqa(xmm3, ptr[&m_local.temp.tga]); - - lerp16(xmm5, xmm2, xmm0, 0); - lerp16(xmm6, xmm3, xmm0, 0); - } - - pop(ebp); -} - -void GSDrawScanlineCodeGenerator::WrapLOD_AVX(const Xmm& uv) -{ - // xmm5 = minuv - // xmm6 = maxuv - // xmm0, xmm1, xmm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv, xmm5); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv, xmm0); - } - - vpminsw(uv, xmm6); - } - else - { - vpand(uv, xmm5); - - if (region) - { - vpor(uv, xmm6); - } - } - } - else - { - vmovdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv, xmm5); - - if (region) - { - vpor(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, xmm5); - vpminsw(uv, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::WrapLOD_AVX(const Xmm& uv0, const Xmm& uv1) -{ - // xmm5 = minuv - // xmm6 = maxuv - // xmm0, xmm1, xmm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv0, xmm5); - vpmaxsw(uv1, xmm5); - } - else - { - vpxor(xmm0, xmm0); - vpmaxsw(uv0, xmm0); - vpmaxsw(uv1, xmm0); - } - - vpminsw(uv0, xmm6); - vpminsw(uv1, xmm6); - } - else - { - vpand(uv0, xmm5); - vpand(uv1, xmm5); - - if (region) - { - vpor(uv0, xmm6); - vpor(uv1, xmm6); - } - } - } - else - { - vmovdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv0, xmm5); - - if (region) - { - vpor(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, xmm5); - vpminsw(uv0, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, xmm1, xmm0); - - // uv1 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(xmm1, uv1, xmm5); - - if (region) - { - vpor(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, xmm5); - vpminsw(uv1, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, xmm1, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::AlphaTFX_AVX() -{ - if (!m_sel.fb) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16(xmm6, xmm4, 1); - - clamp16(xmm6, xmm3); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - vpsrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_HIGHLIGHT: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(xmm2, xmm4); - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - vpsrlw(xmm4, 7); - - if (m_sel.tcc) - { - vpaddusb(xmm4, xmm6); - } - - mix16(xmm6, xmm4, xmm3); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(xmm2, xmm4); - - vpsrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if (m_sel.iip) - { - vpsrlw(xmm6, 7); - } - - break; - } - - if (m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if (!m_sel.abe) - { - // a = cov - - if (m_sel.edge) - { - vmovdqa(xmm0, ptr[&m_local.temp.cov]); - } - else - { - vpcmpeqd(xmm0, xmm0); - vpsllw(xmm0, 15); - vpsrlw(xmm0, 8); - } - - mix16(xmm6, xmm0, xmm1); - } - else - { - // a = a == 0x80 ? cov : a - - vpcmpeqd(xmm0, xmm0); - vpsllw(xmm0, 15); - vpsrlw(xmm0, 8); - - if (m_sel.edge) - { - vmovdqa(xmm1, ptr[&m_local.temp.cov]); - } - else - { - vmovdqa(xmm1, xmm0); - } - - vpcmpeqw(xmm0, xmm6); - vpsrld(xmm0, 16); - vpslld(xmm0, 16); - - vpblendvb(xmm6, xmm1, xmm0); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadMask_AVX() -{ - if (m_sel.fwrite) - { - vmovdqa(xmm3, ptr[&m_local.gd->fm]); - } - - if (m_sel.zwrite) - { - vmovdqa(xmm4, ptr[&m_local.gd->zm]); - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha_AVX() -{ - switch (m_sel.atst) - { - case ATST_NEVER: - // t = GSVector4i::xffffffff(); - vpcmpeqd(xmm1, xmm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_local.gd->aref; - vpsrld(xmm1, xmm6, 16); - vpcmpgtd(xmm1, ptr[&m_local.gd->aref]); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_local.gd->aref; - vpsrld(xmm1, xmm6, 16); - vpcmpeqd(xmm1, ptr[&m_local.gd->aref]); - vpcmpeqd(xmm0, xmm0); - vpxor(xmm1, xmm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_local.gd->aref; - vpsrld(xmm0, xmm6, 16); - vmovdqa(xmm1, ptr[&m_local.gd->aref]); - vpcmpgtd(xmm1, xmm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_local.gd->aref; - vpsrld(xmm1, xmm6, 16); - vpcmpeqd(xmm1, ptr[&m_local.gd->aref]); - break; - } - - switch (m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - vpor(xmm7, xmm1); - alltrue(xmm7); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - vpor(xmm4, xmm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - vpor(xmm3, xmm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - vpor(xmm4, xmm1); - // fm |= t & GSVector4i::xff000000(); - vpsrld(xmm1, 24); - vpslld(xmm1, 24); - vpor(xmm3, xmm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16(xmm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - clamp16(xmm5, xmm1); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - if (m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(xmm2, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - } - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - vmovdqa(xmm1, xmm6); - - modulate16(xmm6, xmm2, 1); - - vpshuflw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpsrlw(xmm2, 7); - - vpaddw(xmm6, xmm2); - - clamp16(xmm6, xmm0); - - mix16(xmm6, xmm1, xmm0); - - // GSVector4i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16(xmm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - vpaddw(xmm5, xmm2); - - clamp16(xmm5, xmm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if (m_sel.iip) - { - vpsrlw(xmm5, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog_AVX() -{ - if (!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_local.gd->frb.lerp16<0>(rb, f); - // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); - - vmovdqa(xmm0, ptr[m_sel.prim != GS_SPRITE_CLASS ? &m_local.temp.f : &m_local.p.f]); - vmovdqa(xmm1, xmm6); - - vmovdqa(xmm2, ptr[&m_local.gd->frb]); - lerp16(xmm5, xmm2, xmm0, 0); - - vmovdqa(xmm2, ptr[&m_local.gd->fga]); - lerp16(xmm6, xmm2, xmm0, 0); - mix16(xmm6, xmm1, xmm0); -} - -void GSDrawScanlineCodeGenerator::ReadFrame_AVX() -{ - if (!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, ptr[esi]); - add(ebx, ptr[edi]); - and(ebx, HALF_VM_SIZE - 1); - - if (!m_sel.rfb) - { - return; - } - - ReadPixel_AVX(xmm2, ebx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha_AVX() -{ - if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); - - if (m_sel.datm) - { - if (m_sel.fpsm == 2) - { - vpxor(xmm0, xmm0); - //vpsrld(xmm1, xmm2, 15); - vpslld(xmm1, xmm2, 16); - vpsrad(xmm1, 31); - vpcmpeqd(xmm1, xmm0); - } - else - { - vpcmpeqd(xmm0, xmm0); - vpxor(xmm1, xmm2, xmm0); - vpsrad(xmm1, 31); - } - } - else - { - if (m_sel.fpsm == 2) - { - vpslld(xmm1, xmm2, 16); - vpsrad(xmm1, 31); - } - else - { - vpsrad(xmm1, xmm2, 31); - } - } - - vpor(xmm7, xmm1); - - alltrue(xmm7); -} - -void GSDrawScanlineCodeGenerator::WriteMask_AVX() -{ - if (m_sel.notest) - { - return; - } - - // fm |= test; - // zm |= test; - - if (m_sel.fwrite) - { - vpor(xmm3, xmm7); - } - - if (m_sel.zwrite) - { - vpor(xmm4, xmm7); - } - - // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); - - vpcmpeqd(xmm1, xmm1); - - if (m_sel.fwrite && m_sel.zwrite) - { - vpcmpeqd(xmm0, xmm1, xmm4); - vpcmpeqd(xmm1, xmm3); - vpackssdw(xmm1, xmm0); - } - else if (m_sel.fwrite) - { - vpcmpeqd(xmm1, xmm3); - vpackssdw(xmm1, xmm1); - } - else if (m_sel.zwrite) - { - vpcmpeqd(xmm1, xmm4); - vpackssdw(xmm1, xmm1); - } - - vpmovmskb(edx, xmm1); - - not(edx); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf_AVX() -{ - if (!m_sel.zwrite) - { - return; - } - - vmovdqa(xmm1, ptr[m_sel.prim != GS_SPRITE_CLASS ? &m_local.temp.zs : &m_local.p.z]); - - if (m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - - vpblendvb(xmm1, ptr[&m_local.temp.zd], xmm4); - } - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - vpcmpeqd(xmm7, xmm7); - vpsrld(xmm7, (uint8)((m_sel.zpsm & 0x3) * 8)); - vpminsd(xmm1, xmm7); - } - - bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; - - WritePixel_AVX(xmm1, ebp, dh, fast, m_sel.zpsm, 1); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch (m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - split16_2x8(xmm0, xmm1, xmm2); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - vpcmpeqd(xmm7, xmm7); - - vpsrld(xmm7, 27); // 0x0000001f - vpand(xmm0, xmm2, xmm7); - vpslld(xmm0, 3); - - vpslld(xmm7, 10); // 0x00007c00 - vpand(xmm4, xmm2, xmm7); - vpslld(xmm4, 9); - - vpor(xmm0, xmm4); - - vpsrld(xmm7, 5); // 0x000003e0 - vpand(xmm1, xmm2, xmm7); - vpsrld(xmm1, 2); - - vpsllw(xmm7, 10); // 0x00008000 - vpand(xmm4, xmm2, xmm7); - vpslld(xmm4, 8); - - vpor(xmm1, xmm4); - - break; - } - } - - // xmm5, xmm6 = src rb, ga - // xmm0, xmm1 = dst rb, ga - // xmm2, xmm3 = used - // xmm4, xmm7 = free - - if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - vmovdqa(xmm4, xmm5); - } - - if (m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(xmm5, xmm0); - break; - case 2: - vpxor(xmm5, xmm5); - break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch (m_sel.abb) - { - case 0: - vpsubw(xmm5, xmm4); - break; - case 1: - vpsubw(xmm5, xmm0); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; - - switch (m_sel.abc) - { - case 0: - case 1: - vpshuflw(xmm7, m_sel.abc ? xmm1 : xmm6, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); - vpsllw(xmm7, 7); - break; - case 2: - vmovdqa(xmm7, ptr[&m_local.gd->afix]); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16(xmm5, xmm7, 1); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch (m_sel.abd) - { - case 0: - vpaddw(xmm5, xmm4); - break; - case 1: - vpaddw(xmm5, xmm0); - break; - case 2: - break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(xmm5, xmm0); - break; - case 2: - vpxor(xmm5, xmm5); - break; - } - } - - if (m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - vpslld(xmm0, xmm6, 8); - vpsrad(xmm0, 31); - - // rb = c[0].blend8(rb, mask); - - vpblendvb(xmm5, xmm4, xmm5, xmm0); - } - - // xmm6 = src ga - // xmm1 = dst ga - // xmm5 = rb - // xmm7 = a - // xmm2, xmm3 = used - // xmm0, xmm4 = free - - vmovdqa(xmm4, xmm6); - - if (m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(xmm6, xmm1); - break; - case 2: - vpxor(xmm6, xmm6); - break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch (m_sel.abb) - { - case 0: - vpsubw(xmm6, xmm4); - break; - case 1: - vpsubw(xmm6, xmm1); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16(xmm6, xmm7, 1); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch (m_sel.abd) - { - case 0: - vpaddw(xmm6, xmm4); - break; - case 1: - vpaddw(xmm6, xmm1); - break; - case 2: - break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(xmm6, xmm1); - break; - case 2: - vpxor(xmm6, xmm6); - break; - } - } - - // xmm4 = src ga - // xmm5 = rb - // xmm6 = ga - // xmm2, xmm3 = used - // xmm0, xmm1, xmm7 = free - - if (m_sel.pabe) - { - vpsrld(xmm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - vpblendvb(xmm6, xmm4, xmm6, xmm0); - } - else - { - if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(xmm6, xmm4, xmm7); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame_AVX() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.fpsm == 2 && m_sel.dthe) - { - mov(eax, ptr[esp + _top]); - and(eax, 3); - shl(eax, 5); - mov(ebp, ptr[&m_local.gd->dimx]); - vpaddw(xmm5, ptr[ebp + eax + sizeof(GSVector4i) * 0]); - vpaddw(xmm6, ptr[ebp + eax + sizeof(GSVector4i) * 1]); - } - - if (m_sel.colclamp == 0) - { - // c[0] &= 0x00ff00ff; - // c[1] &= 0x00ff00ff; - - vpcmpeqd(xmm7, xmm7); - vpsrlw(xmm7, 8); - vpand(xmm5, xmm7); - vpand(xmm6, xmm7); - } - - // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - vpunpckhwd(xmm7, xmm5, xmm6); - vpunpcklwd(xmm5, xmm6); - vpackuswb(xmm5, xmm7); - - if (m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - vpcmpeqd(xmm7, xmm7); - vpslld(xmm7, 31); - vpor(xmm5, xmm7); - } - - if (m_sel.fpsm == 2) - { - // GSVector4i rb = fs & 0x00f800f8; - // GSVector4i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - vmovd(xmm6, eax); - vpshufd(xmm6, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - - mov(eax, 0x8000f800); - vmovd(xmm7, eax); - vpshufd(xmm7, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - - vpand(xmm4, xmm5, xmm6); - vpand(xmm5, xmm7); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - vpsrld(xmm6, xmm4, 9); - vpsrld(xmm4, 3); - vpsrld(xmm7, xmm5, 16); - vpsrld(xmm5, 6); - - vpor(xmm5, xmm4); - vpor(xmm7, xmm6); - vpor(xmm5, xmm7); - } - - if (m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(xmm5, xmm2, xmm3); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; - - WritePixel_AVX(xmm5, ebx, dl, fast, m_sel.fpsm, 0); -} - -void GSDrawScanlineCodeGenerator::ReadPixel_AVX(const Xmm& dst, const Reg32& addr) -{ - vmovq(dst, qword[addr * 2 + (size_t)m_local.gd->vm]); - vmovhps(dst, qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2]); -} - -void GSDrawScanlineCodeGenerator::WritePixel_AVX(const Xmm& src, const Reg32& addr, const Reg8& mask, bool fast, int psm, int fz) -{ - if (m_sel.notest) - { - if (fast) - { - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src); - } - else - { - WritePixel_AVX(src, addr, 0, psm); - WritePixel_AVX(src, addr, 1, psm); - WritePixel_AVX(src, addr, 2, psm); - WritePixel_AVX(src, addr, 3, psm); - } - } - else - { - if (fast) - { - // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); - // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); - - test(mask, 0x0f); - je("@f"); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src); - L("@@"); - - test(mask, 0xf0); - je("@f"); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src); - L("@@"); - - // vmaskmovps? - } - else - { - // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); - // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); - // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); - // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); - - test(mask, 0x03); - je("@f"); - WritePixel_AVX(src, addr, 0, psm); - L("@@"); - - test(mask, 0x0c); - je("@f"); - WritePixel_AVX(src, addr, 1, psm); - L("@@"); - - test(mask, 0x30); - je("@f"); - WritePixel_AVX(src, addr, 2, psm); - L("@@"); - - test(mask, 0xc0); - je("@f"); - WritePixel_AVX(src, addr, 3, psm); - L("@@"); - } - } -} - -static const int s_offsets[] = {0, 2, 8, 10}; - -void GSDrawScanlineCodeGenerator::WritePixel_AVX(const Xmm& src, const Reg32& addr, uint8 i, int psm) -{ - Address dst = ptr[addr * 2 + (size_t)m_local.gd->vm + s_offsets[i] * 2]; - - switch (psm) - { - case 0: - if (i == 0) - vmovd(dst, src); - else - vpextrd(dst, src, i); - break; - case 1: - if (i == 0) - vmovd(eax, src); - else - vpextrd(eax, src, i); - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - if (i == 0) - vmovd(eax, src); - else - vpextrw(eax, src, i * 2); - mov(dst, ax); - break; - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_AVX(int pixels, int mip_offset) -{ - // in - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // ebx = m_local.tex[0] (!m_sel.mmin) - // ebp = m_local.tex (m_sel.mmin) - // edx = m_local.clut (m_sel.tlu) - - // out - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - - ASSERT(pixels == 1 || pixels == 4); - - mip_offset *= sizeof(void*); - - const GSVector4i* lod_i = m_sel.lcm ? &m_local.gd->lod.i : &m_local.temp.lod.i; - - if (m_sel.mmin && !m_sel.lcm) - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 7}; - - if (pixels == 4) - { - vmovdqa(ptr[&m_local.temp.test], xmm7); - } - - for (uint8 j = 0; j < 4; j++) - { - mov(ebx, ptr[&lod_i->u32[j]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - for (int i = 0; i < pixels; i++) - { - ReadTexel_AVX(Xmm(r[i * 2 + 1]), Xmm(r[i * 2 + 0]), j); - } - } - - if (pixels == 4) - { - vmovdqa(xmm5, xmm7); - vmovdqa(xmm7, ptr[&m_local.temp.test]); - } - } - else - { - if (m_sel.mmin && m_sel.lcm) - { - mov(ebx, ptr[&lod_i->u32[0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - } - - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - - for (int i = 0; i < pixels; i++) - { - for (uint8 j = 0; j < 4; j++) - { - ReadTexel_AVX(Xmm(r[i * 2 + 1]), Xmm(r[i * 2 + 0]), j); - } - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_AVX(const Xmm& dst, const Xmm& addr, uint8 i) -{ - ASSERT(i < 4); - - const Address& src = m_sel.tlu ? ptr[edx + eax * 4] : ptr[ebx + eax * 4]; - - if (i == 0) - vmovd(eax, addr); - else - vpextrd(eax, addr, i); - - if (m_sel.tlu) - movzx(eax, byte[ebx + eax]); - - if (i == 0) - vmovd(dst, src); - else - vpinsrd(dst, src, i); -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx2.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx2.cpp deleted file mode 100644 index 7c14c55d8f599..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.avx2.cpp +++ /dev/null @@ -1,3020 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" -#include "GS/GSVector.h" - -#if _M_SSE >= 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 16; -static const int _top = _args + 4; -static const int _v = _args + 8; - -void GSDrawScanlineCodeGenerator::Generate() -{ - //ret(8); - - push(ebx); - push(esi); - push(edi); - push(ebp); - - //db(0xcc); - - Init(); - - if (!m_sel.edge) - { - align(16); - } - -L("loop"); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ymm0 = z/zi - // ymm2 = s/u (tme) - // ymm3 = t/v (tme) - // ymm4 = q (tme) - // ymm5 = rb (!tme) - // ymm6 = ga (!tme) - // ymm7 = test - - bool tme = m_sel.tfx != TFX_NONE; - - TestZ(tme ? ymm5 : ymm2, tme ? ymm6 : ymm3); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - ymm0 - // ymm2 = s/u (tme) - // ymm3 = t/v (tme) - // ymm4 = q (tme) - // ymm5 = rb (!tme) - // ymm6 = ga (!tme) - // ymm7 = test - - if (m_sel.mmin) - { - SampleTextureLOD(); - } - else - { - SampleTexture(); - } - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - ymm2 - // - ymm3 - // - ymm4 - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - AlphaTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ReadMask(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - TestAlpha(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ColorTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - Fog(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - ReadFrame(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - TestDestAlpha(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - // ymm7 = test - - WriteMask(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ebp = za - // ymm2 = fd - // ymm3 = fm - // ymm4 = zm - // ymm5 = rb - // ymm6 = ga - - WriteZBuf(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // - ebp - // ymm2 = fd - // ymm3 = fm - // - ymm4 - // ymm5 = rb - // ymm6 = ga - - AlphaBlend(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ymm2 = fd - // ymm3 = fm - // ymm5 = rb - // ymm6 = ga - - WriteFrame(); - -L("step"); - - // if(steps <= 0) break; - - if (!m_sel.edge) - { - test(ecx, ecx); - - jle("exit", T_NEAR); - - Step(); - - jmp("loop", T_NEAR); - } - -L("exit"); - - pop(ebp); - pop(edi); - pop(esi); - pop(ebx); - - ret(8); -} - -void GSDrawScanlineCodeGenerator::Init() -{ - if (!m_sel.notest) - { - // int skip = left & 7; - - mov(ebx, edx); - and(edx, 7); - - // int steps = pixels + skip - 8; - - lea(ecx, ptr[ecx + edx - 8]); - - // left -= skip; - - sub(ebx, edx); - - // GSVector4i test = m_test[skip] | m_test[15 + (steps & (steps >> 31))]; - - mov(eax, ecx); - sar(eax, 31); - and(eax, ecx); - - vpmovsxbd(ymm7, ptr[edx * 8 + (size_t)g_const->m_test_256b[0]]); - vpmovsxbd(ymm0, ptr[eax * 8 + (size_t)g_const->m_test_256b[15]]); - vpor(ymm7, ymm0); - - shl(edx, 5); - } - else - { - mov(ebx, edx); // left - xor(edx, edx); // skip - lea(ecx, ptr[ecx - 8]); // steps - } - - // GSVector2i* fza_base = &m_local.gd->fzbr[top]; - - mov(esi, ptr[esp + _top]); - lea(esi, ptr[esi * 8]); - add(esi, ptr[&m_local.gd->fzbr]); - - // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; - - lea(edi, ptr[ebx * 2]); - add(edi, ptr[&m_local.gd->fzbc]); - - if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // edx = &m_local.d[skip] - - lea(edx, ptr[edx * 8 + (size_t)m_local.d]); - - // ebx = &v - - mov(ebx, ptr[esp + _v]); - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.fwrite && m_sel.fge || m_sel.zb) - { - vbroadcastf128(ymm0, ptr[ebx + offsetof(GSVertexSW, p)]); // v.p - - if (m_sel.fwrite && m_sel.fge) - { - // f = GSVector8i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); - - vcvttps2dq(ymm1, ymm0); - vpshufhw(ymm1, ymm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(ymm1, ymm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpaddw(ymm1, ptr[edx + offsetof(GSScanlineLocalData::skip, f)]); - - vmovdqa(ptr[&m_local.temp.f], ymm1); - } - - if (m_sel.zb) - { - // z = vp.zzzz() + m_local.d[skip].z; - - vshufps(ymm0, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - vmovaps(ptr[&m_local.temp.z], ymm0); - vmovaps(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, z)]); - vmovaps(ptr[&m_local.temp.zo], ymm2); - vaddps(ymm0, ymm2); - } - } - } - else - { - if (m_sel.ztest) - { - vpbroadcastd(ymm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.edge || m_sel.tfx != TFX_NONE) - { - vbroadcastf128(ymm4, ptr[ebx + offsetof(GSVertexSW, t)]); // v.t - } - - if (m_sel.edge) - { - // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); - - vpshufhw(ymm3, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(ymm3, ymm3, _MM_SHUFFLE(3, 3, 3, 3)); - vpsrlw(ymm3, 9); - - vmovdqa(ptr[&m_local.temp.cov], ymm3); - } - - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i vti(vt); - - vcvttps2dq(ymm6, ymm4); - - // s = vti.xxxx() + m_local.d[skip].s; - // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; - - vpshufd(ymm2, ymm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm3, ymm6, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddd(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - vpaddd(ymm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - } - else - { - if (m_sel.ltf) - { - vpshuflw(ymm6, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm6, ymm6, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm6, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm6); - } - } - - vmovdqa(ptr[&m_local.temp.s], ymm2); - vmovdqa(ptr[&m_local.temp.t], ymm3); - } - else - { - // s = vt.xxxx() + m_local.d[skip].s; - // t = vt.yyyy() + m_local.d[skip].t; - // q = vt.zzzz() + m_local.d[skip].q; - - vshufps(ymm2, ymm4, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, ymm4, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm4, ymm4, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(ymm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - vaddps(ymm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - vaddps(ymm4, ptr[edx + offsetof(GSScanlineLocalData::skip, q)]); - - vmovaps(ptr[&m_local.temp.s], ymm2); - vmovaps(ptr[&m_local.temp.t], ymm3); - vmovaps(ptr[&m_local.temp.q], ymm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - vbroadcastf128(ymm6, ptr[ebx + offsetof(GSVertexSW, c)]); // v.c - vcvttps2dq(ymm6, ymm6); - - // vc = vc.upl16(vc.zwxy()); - - vpshufd(ymm5, ymm6, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(ymm6, ymm5); - - // rb = vc.xxxx().add16(m_local.d[skip].rb); - // ga = vc.zzzz().add16(m_local.d[skip].ga); - - vpshufd(ymm5, ymm6, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm6, ymm6, _MM_SHUFFLE(2, 2, 2, 2)); - - vpaddw(ymm5, ptr[edx + offsetof(GSScanlineLocalData::skip, rb)]); - vpaddw(ymm6, ptr[edx + offsetof(GSScanlineLocalData::skip, ga)]); - - vmovdqa(ptr[&m_local.temp.rb], ymm5); - vmovdqa(ptr[&m_local.temp.ga], ymm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(ymm5, ptr[&m_local.c.rb]); - vmovdqa(ymm6, ptr[&m_local.c.ga]); - } - } - } - } -} - -void GSDrawScanlineCodeGenerator::Step() -{ - // steps -= 8; - - sub(ecx, 8); - - // fza_offset += 2; - - add(edi, 16); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // zo += GSVector8::broadcast32(&m_local.d8.p.z); - - if (m_sel.zb) - { - vbroadcastss(ymm0, ptr[&m_local.d8.p.z]); - vaddps(ymm0, ptr[&m_local.temp.zo]); - vmovaps(ptr[&m_local.temp.zo], ymm0); - vaddps(ymm0, ptr[&m_local.temp.z]); - } - - // f = f.add16(GSVector8i::broadcast16(&m_local.d8.p.f)); - - if (m_sel.fwrite && m_sel.fge) - { - vpbroadcastw(ymm1, ptr[&m_local.d8.p.f]); - vpaddw(ymm1, ptr[&m_local.temp.f]); - vmovdqa(ptr[&m_local.temp.f], ymm1); - } - } - else - { - if (m_sel.ztest) - { - vpbroadcastd(ymm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector8i stq = GSVector8i::cast(GSVector8(m_local.d8.stq)); - - vbroadcasti128(ymm4, ptr[&m_local.d8.stq]); - - // s = GSVector8::cast(GSVector8i::cast(s) + stq.xxxx()); - - vpshufd(ymm2, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vpaddd(ymm2, ptr[&m_local.temp.s]); - vmovdqa(ptr[&m_local.temp.s], ymm2); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - // t = GSVector8::cast(GSVector8i::cast(t) + stq.yyyy()); - - vpshufd(ymm3, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vpaddd(ymm3, ptr[&m_local.temp.t]); - vmovdqa(ptr[&m_local.temp.t], ymm3); - } - else - { - vmovdqa(ymm3, ptr[&m_local.temp.t]); - } - } - else - { - // GSVector8 stq(m_local.d8.stq); - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - vbroadcastf128(ymm4, ptr[&m_local.d8.stq]); - - vshufps(ymm2, ymm4, ymm4, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, ymm4, ymm4, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm4, ymm4, ymm4, _MM_SHUFFLE(2, 2, 2, 2)); - - vaddps(ymm2, ptr[&m_local.temp.s]); - vaddps(ymm3, ptr[&m_local.temp.t]); - vaddps(ymm4, ptr[&m_local.temp.q]); - - vmovaps(ptr[&m_local.temp.s], ymm2); - vmovaps(ptr[&m_local.temp.t], ymm3); - vmovaps(ptr[&m_local.temp.q], ymm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector8i c = GSVector8i::broadcast64(&m_local.d8.c); - - vpbroadcastq(ymm7, ptr[&m_local.d8.c]); - - // rb = rb.add16(c.xxxx()).max_i16(GSVector8i::zero()); - // ga = ga.add16(c.yyyy()).max_i16(GSVector8i::zero()); - - vpshufd(ymm5, ymm7, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm6, ymm7, _MM_SHUFFLE(1, 1, 1, 1)); - - vpaddw(ymm5, ptr[&m_local.temp.rb]); - vpaddw(ymm6, ptr[&m_local.temp.ga]); - - // FIXME: color may underflow and roll over at the end of the line, if decreasing - - vpxor(ymm7, ymm7); - vpmaxsw(ymm5, ymm7); - vpmaxsw(ymm6, ymm7); - - vmovdqa(ptr[&m_local.temp.rb], ymm5); - vmovdqa(ptr[&m_local.temp.ga], ymm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - vmovdqa(ymm5, ptr[&m_local.c.rb]); - vmovdqa(ymm6, ptr[&m_local.c.ga]); - } - } - } - } - - if (!m_sel.notest) - { - // test = m_test[15 + (steps & (steps >> 31))]; - - mov(edx, ecx); - sar(edx, 31); - and(edx, ecx); - - vpmovsxbd(ymm7, ptr[edx * 8 + (size_t)g_const->m_test_256b[15]]); - } -} - -void GSDrawScanlineCodeGenerator::TestZ(const Ymm& temp1, const Ymm& temp2) -{ - if (!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, ptr[esi + 4]); - add(ebp, ptr[edi + 4]); - and(ebp, HALF_VM_SIZE - 1); - - // GSVector8i zs = zi; - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.zoverflow) - { - // zs = (GSVector8i(z * 0.5f) << 1) | (GSVector8i(z) & GSVector8i::x00000001()); - - vbroadcastss(temp1, ptr[&GSVector8::m_half]); - vmulps(temp1, ymm0); - vcvttps2dq(temp1, temp1); - vpslld(temp1, 1); - - vcvttps2dq(ymm0, ymm0); - vpcmpeqd(temp2, temp2); - vpsrld(temp2, 31); - vpand(ymm0, temp2); - - vpor(ymm0, temp1); - } - else - { - // zs = GSVector8i(z); - - vcvttps2dq(ymm0, ymm0); - } - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - vpcmpeqd(temp1, temp1); - vpsrld(temp1, (uint8)((m_sel.zpsm & 0x3) * 8)); - vpminsd(ymm0, temp1); - } - - if (m_sel.zwrite) - { - vmovdqa(ptr[&m_local.temp.zs], ymm0); - } - } - - if (m_sel.ztest) - { - ReadPixel(ymm1, temp1, ebp); - - if (m_sel.zwrite && m_sel.zpsm < 2) - { - vmovdqa(ptr[&m_local.temp.zd], ymm1); - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if (m_sel.zpsm) - { - vpslld(ymm1, (uint8)(m_sel.zpsm * 8)); - vpsrld(ymm1, (uint8)(m_sel.zpsm * 8)); - } - - if (m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector8i o = GSVector8i::x80000000(); - - vpcmpeqd(temp1, temp1); - vpslld(temp1, 31); - - // GSVector8i zso = zs - o; - // GSVector8i zdo = zd - o; - - vpsubd(ymm0, temp1); - vpsubd(ymm1, temp1); - } - - switch (m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - vpcmpgtd(ymm1, ymm0); - vpor(ymm7, ymm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - vpcmpgtd(ymm0, ymm1); - vpcmpeqd(temp1, temp1); - vpxor(ymm0, temp1); - vpor(ymm7, ymm0); - break; - } - - alltrue(ymm7); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - mov(ebx, ptr[&m_local.gd->tex[0]]); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - // ebx = tex - // edx = clut - - if (!m_sel.fst) - { - vrcpps(ymm0, ymm4); - - vmulps(ymm2, ymm0); - vmulps(ymm3, ymm0); - - vcvttps2dq(ymm2, ymm2); - vcvttps2dq(ymm3, ymm3); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - } - } - - // ymm2 = u - // ymm3 = v - - if (m_sel.ltf) - { - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector8i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - Wrap(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i y0 = uv0.uph16() << tw; - // GSVector8i x0 = uv0.upl16(); - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i y1 = uv1.uph16() << tw; - // GSVector8i x1 = uv1.upl16(); - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 0); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 0); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Ymm& uv) -{ - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.min]); - vpmaxsw(uv, ymm0); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv, ymm0); - } - - vbroadcasti128(ymm0, ptr[&m_local.gd->t.max]); - vpminsw(uv, ymm0); - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.min]); - vpand(uv, ymm0); - - if (region) - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.max]); - vpor(uv, ymm0); - } - } - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, ymm4); - vpminsw(uv, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Ymm& uv0, const Ymm& uv1) -{ - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpmaxsw(uv0, ymm4); - vpmaxsw(uv1, ymm4); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv0, ymm0); - vpmaxsw(uv1, ymm0); - } - - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vpminsw(uv0, ymm5); - vpminsw(uv1, ymm5); - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpand(uv0, ymm4); - vpand(uv1, ymm4); - - if (region) - { - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vpor(uv0, ymm5); - vpor(uv1, ymm5); - } - } - } - else - { - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vbroadcasti128(ymm5, ptr[&m_local.gd->t.max]); - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv0, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, ymm4); - vpminsw(uv0, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, ymm1, ymm0); - - // uv1 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv1, ymm4); - - if (region) - { - vpor(ymm1, ymm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, ymm4); - vpminsw(uv1, ymm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::SampleTextureLOD() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - push(ebp); - - mov(ebp, (size_t)m_local.gd->tex); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - if (!m_sel.fst) - { - vrcpps(ymm0, ymm4); - - vmulps(ymm2, ymm0); - vmulps(ymm3, ymm0); - - vcvttps2dq(ymm2, ymm2); - vcvttps2dq(ymm3, ymm3); - } - - // ymm2 = u - // ymm3 = v - // ymm4 = q - // ymm0 = ymm1 = ymm5 = ymm6 = free - - // TODO: if the fractional part is not needed in round-off mode then there is a faster integer log2 (just take the exp) (but can we round it?) - - if (!m_sel.lcm) - { - // lod = -log2(Q) * (1 << L) + K - - vpcmpeqd(ymm1, ymm1); - vpsrld(ymm1, ymm1, 25); - vpslld(ymm0, ymm4, 1); - vpsrld(ymm0, ymm0, 24); - vpsubd(ymm0, ymm1); - vcvtdq2ps(ymm0, ymm0); - - // ymm0 = (float)(exp(q) - 127) - - vpslld(ymm4, ymm4, 9); - vpsrld(ymm4, ymm4, 9); - vorps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); - - // ymm4 = mant(q) | 1.0f - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(ymm5, ptr[g_const->m_log2_coef_256b[0]]); // c0 - vfmadd213ps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[1]]); // c0 * ymm4 + c1 - vfmadd213ps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[2]]); // (c0 * ymm4 + c1) * ymm4 + c2 - vsubps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); // ymm4 - 1.0f - vfmadd213ps(ymm4, ymm5, ymm0); // ((c0 * ymm4 + c1) * ymm4 + c2) * (ymm4 - 1.0f) + ymm0 - } - else - { - vmulps(ymm5, ymm4, ptr[g_const->m_log2_coef_256b[0]]); - vaddps(ymm5, ptr[g_const->m_log2_coef_256b[1]]); - vmulps(ymm5, ymm4); - vsubps(ymm4, ptr[g_const->m_log2_coef_256b[3]]); - vaddps(ymm5, ptr[g_const->m_log2_coef_256b[2]]); - vmulps(ymm4, ymm5); - vaddps(ymm4, ymm0); - } - - // ymm4 = log2(Q) = ((((c0 * ymm4) + c1) * ymm4) + c2) * (ymm4 - 1.0f) + ymm0 - - if (m_cpu.has(util::Cpu::tFMA)) - { - vmovaps(ymm5, ptr[&m_local.gd->l]); - vfmadd213ps(ymm4, ymm5, ptr[&m_local.gd->k]); - } - else - { - vmulps(ymm4, ptr[&m_local.gd->l]); - vaddps(ymm4, ptr[&m_local.gd->k]); - } - - // ymm4 = (-log2(Q) * (1 << L) + K) * 0x10000 - - vxorps(ymm0, ymm0); - vminps(ymm4, ptr[&m_local.gd->mxl]); - vmaxps(ymm4, ymm0); - vcvtps2dq(ymm4, ymm4); - - if (m_sel.mmin == 1) // round-off mode - { - mov(eax, 0x8000); - vmovd(xmm0, eax); - vpbroadcastd(ymm0, xmm0); - vpaddd(ymm4, ymm0); - } - - vpsrld(ymm0, ymm4, 16); - - vmovdqa(ptr[&m_local.temp.lod.i], ymm0); - /* -vpslld(ymm5, ymm0, 6); -vpslld(ymm6, ymm4, 16); -vpsrld(ymm6, ymm6, 24); -return; -*/ - if (m_sel.mmin == 2) // trilinear mode - { - vpshuflw(ymm1, ymm4, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm1, ymm1, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.temp.lod.f], ymm1); - } - - // shift u/v/minmax by (int)lod - - vpsravd(ymm2, ymm2, ymm0); - vpsravd(ymm3, ymm3, ymm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], ymm2); - vmovdqa(ptr[&m_local.temp.uv[1]], ymm3); - - // m_local.gd->t.minmax => m_local.temp.uv_minmax[0/1] - - vpxor(ymm1, ymm1); - - vbroadcasti128(ymm4, ptr[&m_local.gd->t.min]); - vpunpcklwd(ymm5, ymm4, ymm1); // minu - vpunpckhwd(ymm6, ymm4, ymm1); // minv - vpsrlvd(ymm5, ymm5, ymm0); - vpsrlvd(ymm6, ymm6, ymm0); - vpackusdw(ymm5, ymm6); - - vbroadcasti128(ymm4, ptr[&m_local.gd->t.max]); - vpunpcklwd(ymm6, ymm4, ymm1); // maxu - vpunpckhwd(ymm4, ymm4, ymm1); // maxv - vpsrlvd(ymm6, ymm6, ymm0); - vpsrlvd(ymm4, ymm4, ymm0); - vpackusdw(ymm6, ymm4); - - vmovdqa(ptr[&m_local.temp.uv_minmax[0]], ymm5); - vmovdqa(ptr[&m_local.temp.uv_minmax[1]], ymm6); - } - else - { - // lod = K - - vmovd(xmm0, ptr[&m_local.gd->lod.i.u32[0]]); - - vpsrad(ymm2, xmm0); - vpsrad(ymm3, xmm0); - - vmovdqa(ptr[&m_local.temp.uv[0]], ymm2); - vmovdqa(ptr[&m_local.temp.uv[1]], ymm3); - - vmovdqa(ymm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(ymm6, ptr[&m_local.temp.uv_minmax[1]]); - } - - // ymm2 = m_local.temp.uv[0] = u (level m) - // ymm3 = m_local.temp.uv[1] = v (level m) - // ymm5 = minuv - // ymm6 = maxuv - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector8i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i x0 = uv0.upl16(); - // GSVector8i y0 = uv0.uph16() << tw; - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i x1 = uv1.upl16(); - // GSVector8i y1 = uv1.uph16() << tw; - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 0); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 0); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } - - if (m_sel.mmin != 1) // !round-off mode - { - vmovdqa(ptr[&m_local.temp.trb], ymm5); - vmovdqa(ptr[&m_local.temp.tga], ymm6); - - vmovdqa(ymm2, ptr[&m_local.temp.uv[0]]); - vmovdqa(ymm3, ptr[&m_local.temp.uv[1]]); - - vpsrad(ymm2, 1); - vpsrad(ymm3, 1); - - vmovdqa(ymm5, ptr[&m_local.temp.uv_minmax[0]]); - vmovdqa(ymm6, ptr[&m_local.temp.uv_minmax[1]]); - - vpsrlw(ymm5, 1); - vpsrlw(ymm6, 1); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - vmovd(xmm4, eax); - vpbroadcastd(ymm4, xmm4); - - vpsubd(ymm2, ymm4); - vpsubd(ymm3, ymm4); - - // GSVector8i uf = u.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.uf], ymm0); - - // GSVector8i vf = v.xxzzlh().srl16(1); - - vpshuflw(ymm0, ymm3, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpsrlw(ymm0, 12); - vmovdqa(ptr[&m_local.temp.vf], ymm0); - } - - // GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16)); - - vpsrad(ymm2, 16); - vpsrad(ymm3, 16); - vpackssdw(ymm2, ymm3); - - if (m_sel.ltf) - { - // GSVector8i uv1 = uv0.add16(GSVector4i::x0001()); - - vpcmpeqd(ymm1, ymm1); - vpsrlw(ymm1, 15); - vpaddw(ymm3, ymm2, ymm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD(ymm2, ymm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD(ymm2); - } - - // ymm2 = uv0 - // ymm3 = uv1 (ltf) - // ymm0, ymm1, ymm4, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i x0 = uv0.upl16(); - // GSVector8i y0 = uv0.uph16() << tw; - - vpxor(ymm0, ymm0); - - vpunpcklwd(ymm4, ymm2, ymm0); - vpunpckhwd(ymm2, ymm2, ymm0); - vpslld(ymm2, (uint8)(m_sel.tw + 3)); - - // ymm0 = 0 - // ymm2 = y0 - // ymm3 = uv1 (ltf) - // ymm4 = x0 - // ymm1, ymm5, ymm6 = free - // ymm7 = used - - if (m_sel.ltf) - { - // GSVector8i x1 = uv1.upl16(); - // GSVector8i y1 = uv1.uph16() << tw; - - vpunpcklwd(ymm6, ymm3, ymm0); - vpunpckhwd(ymm3, ymm3, ymm0); - vpslld(ymm3, (uint8)(m_sel.tw + 3)); - - // ymm2 = y0 - // ymm3 = y1 - // ymm4 = x0 - // ymm6 = x1 - // ymm0, ymm5, ymm6 = free - // ymm7 = used - - // GSVector8i addr00 = y0 + x0; - // GSVector8i addr01 = y0 + x1; - // GSVector8i addr10 = y1 + x0; - // GSVector8i addr11 = y1 + x1; - - vpaddd(ymm5, ymm2, ymm4); - vpaddd(ymm2, ymm2, ymm6); - vpaddd(ymm0, ymm3, ymm4); - vpaddd(ymm3, ymm3, ymm6); - - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ymm1, ymm4, ymm6 = free - // ymm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(4, 1); - - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - // ymm0, ymm2, ymm3 = free - // ymm7 = used - - vmovdqa(ymm0, ptr[&m_local.temp.uf]); - - // GSVector8i rb00 = c00 & mask; - // GSVector8i ga00 = (c00 >> 8) & mask; - - vpsllw(ymm2, ymm6, 8); - vpsrlw(ymm2, 8); - vpsrlw(ymm6, 8); - - // GSVector8i rb01 = c01 & mask; - // GSVector8i ga01 = (c01 >> 8) & mask; - - vpsllw(ymm3, ymm4, 8); - vpsrlw(ymm3, 8); - vpsrlw(ymm4, 8); - - // ymm0 = uf - // ymm2 = rb00 - // ymm3 = rb01 - // ymm6 = ga00 - // ymm4 = ga01 - // ymm1 = c10 - // ymm5 = c11 - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb01, uf); - // ga00 = ga00.lerp16_4(ga01, uf); - - lerp16_4(ymm3, ymm2, ymm0); - lerp16_4(ymm4, ymm6, ymm0); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = c10 - // ymm5 = c11 - // ymm2, ymm6 = free - // ymm7 = used - - // GSVector8i rb10 = c10 & mask; - // GSVector8i ga10 = (c10 >> 8) & mask; - - vpsrlw(ymm2, ymm1, 8); - vpsllw(ymm1, 8); - vpsrlw(ymm1, 8); - - // GSVector8i rb11 = c11 & mask; - // GSVector8i ga11 = (c11 >> 8) & mask; - - vpsrlw(ymm6, ymm5, 8); - vpsllw(ymm5, 8); - vpsrlw(ymm5, 8); - - // ymm0 = uf - // ymm3 = rb00 - // ymm4 = ga00 - // ymm1 = rb10 - // ymm5 = rb11 - // ymm2 = ga10 - // ymm6 = ga11 - // ymm7 = used - - // rb10 = rb10.lerp16_4(rb11, uf); - // ga10 = ga10.lerp16_4(ga11, uf); - - lerp16_4(ymm5, ymm1, ymm0); - lerp16_4(ymm6, ymm2, ymm0); - - // ymm3 = rb00 - // ymm4 = ga00 - // ymm5 = rb10 - // ymm6 = ga10 - // ymm0, ymm1, ymm2 = free - // ymm7 = used - - // rb00 = rb00.lerp16_4(rb10, vf); - // ga00 = ga00.lerp16_4(ga10, vf); - - vmovdqa(ymm0, ptr[&m_local.temp.vf]); - - lerp16_4(ymm5, ymm3, ymm0); - lerp16_4(ymm6, ymm4, ymm0); - } - else - { - // GSVector8i addr00 = y0 + x0; - - vpaddd(ymm5, ymm2, ymm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(1, 1); - - // GSVector8i mask = GSVector8i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - vpsllw(ymm5, ymm6, 8); - vpsrlw(ymm5, 8); - vpsrlw(ymm6, 8); - } - - vmovdqa(ymm0, ptr[m_sel.lcm ? &m_local.gd->lod.f : &m_local.temp.lod.f]); - vpsrlw(ymm0, ymm0, 1); - - vmovdqa(ymm2, ptr[&m_local.temp.trb]); - vmovdqa(ymm3, ptr[&m_local.temp.tga]); - - lerp16(ymm5, ymm2, ymm0, 0); - lerp16(ymm6, ymm3, ymm0, 0); - } - - pop(ebp); -} - -void GSDrawScanlineCodeGenerator::WrapLOD(const Ymm& uv) -{ - // ymm5 = minuv - // ymm6 = maxuv - // ymm0, ymm1, ymm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv, ymm5); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv, ymm0); - } - - vpminsw(uv, ymm6); - } - else - { - vpand(uv, ymm5); - - if (region) - { - vpor(uv, ymm6); - } - } - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv, ymm5); - vpminsw(uv, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::WrapLOD(const Ymm& uv0, const Ymm& uv1) -{ - // ymm5 = minuv - // ymm6 = maxuv - // ymm0, ymm1, ymm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - vpmaxsw(uv0, ymm5); - vpmaxsw(uv1, ymm5); - } - else - { - vpxor(ymm0, ymm0); - vpmaxsw(uv0, ymm0); - vpmaxsw(uv1, ymm0); - } - - vpminsw(uv0, ymm6); - vpminsw(uv1, ymm6); - } - else - { - vpand(uv0, ymm5); - vpand(uv1, ymm5); - - if (region) - { - vpor(uv0, ymm6); - vpor(uv1, ymm6); - } - } - } - else - { - vbroadcasti128(ymm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv0, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv0, ymm5); - vpminsw(uv0, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv0, ymm1, ymm0); - - // uv1 - - // GSVector8i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - vpand(ymm1, uv1, ymm5); - - if (region) - { - vpor(ymm1, ymm6); - } - - // GSVector8i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - vpmaxsw(uv1, ymm5); - vpminsw(uv1, ymm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - vpblendvb(uv1, ymm1, ymm0); - } -} - -void GSDrawScanlineCodeGenerator::AlphaTFX() -{ - if (!m_sel.fb) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector8i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16(ymm6, ymm4, 1); - - clamp16(ymm6, ymm3); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_HIGHLIGHT: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(ymm2, ymm4); - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - vpsrlw(ymm4, 7); - - if (m_sel.tcc) - { - vpaddusb(ymm4, ymm6); - } - - mix16(ymm6, ymm4, ymm3); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - vmovdqa(ymm2, ymm4); - - vpsrlw(ymm4, 7); - - mix16(ymm6, ymm4, ymm3); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if (m_sel.iip) - { - vpsrlw(ymm6, 7); - } - - break; - } - - if (m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if (!m_sel.abe) - { - // a = cov - - if (m_sel.edge) - { - vmovdqa(ymm0, ptr[&m_local.temp.cov]); - } - else - { - vpcmpeqd(ymm0, ymm0); - vpsllw(ymm0, 15); - vpsrlw(ymm0, 8); - } - - mix16(ymm6, ymm0, ymm1); - } - else - { - // a = a == 0x80 ? cov : a - - vpcmpeqd(ymm0, ymm0); - vpsllw(ymm0, 15); - vpsrlw(ymm0, 8); - - if (m_sel.edge) - { - vmovdqa(ymm1, ptr[&m_local.temp.cov]); - } - else - { - vmovdqa(ymm1, ymm0); - } - - vpcmpeqw(ymm0, ymm6); - vpsrld(ymm0, 16); - vpslld(ymm0, 16); - - vpblendvb(ymm6, ymm1, ymm0); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadMask() -{ - if (m_sel.fwrite) - { - vpbroadcastd(ymm3, ptr[&m_local.gd->fm]); - } - - if (m_sel.zwrite) - { - vpbroadcastd(ymm4, ptr[&m_local.gd->zm]); - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha() -{ - switch (m_sel.atst) - { - case ATST_NEVER: - // t = GSVector8i::xffffffff(); - vpcmpeqd(ymm1, ymm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpgtd(ymm1, ymm0); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpeqd(ymm1, ymm0); - vpcmpeqd(ymm0, ymm0); - vpxor(ymm1, ymm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_local.gd->aref; - vpsrld(ymm0, ymm6, 16); - vbroadcasti128(ymm1, ptr[&m_local.gd->aref]); - vpcmpgtd(ymm1, ymm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_local.gd->aref; - vpsrld(ymm1, ymm6, 16); - vbroadcasti128(ymm0, ptr[&m_local.gd->aref]); - vpcmpeqd(ymm1, ymm0); - break; - } - - switch (m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - vpor(ymm7, ymm1); - alltrue(ymm7); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - vpor(ymm4, ymm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - vpor(ymm3, ymm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - vpor(ymm4, ymm1); - // fm |= t & GSVector8i::xff000000(); - vpsrld(ymm1, 24); - vpslld(ymm1, 24); - vpor(ymm3, ymm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX() -{ - if (!m_sel.fwrite) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector8i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16(ymm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - clamp16(ymm5, ymm1); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - if (m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) - { - // GSVector8i ga = iip ? gaf : m_local.c.ga; - - vmovdqa(ymm2, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - } - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - vmovdqa(ymm1, ymm6); - - modulate16(ymm6, ymm2, 1); - - vpshuflw(ymm2, ymm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(ymm2, ymm2, _MM_SHUFFLE(3, 3, 1, 1)); - vpsrlw(ymm2, 7); - - vpaddw(ymm6, ymm2); - - clamp16(ymm6, ymm0); - - mix16(ymm6, ymm1, ymm0); - - // GSVector8i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16(ymm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - vpaddw(ymm5, ymm2); - - clamp16(ymm5, ymm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if (m_sel.iip) - { - vpsrlw(ymm5, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog() -{ - if (!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_local.gd->frb.lerp16<0>(rb, f); - // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - vmovdqa(ymm0, ptr[&m_local.temp.f]); - } - else - { - vpbroadcastw(ymm0, ptr[&m_local.p.f]); - } - - vmovdqa(ymm1, ymm6); - - vpbroadcastd(ymm2, ptr[&m_local.gd->frb]); - lerp16(ymm5, ymm2, ymm0, 0); - - vpbroadcastd(ymm2, ptr[&m_local.gd->fga]); - lerp16(ymm6, ymm2, ymm0, 0); - mix16(ymm6, ymm1, ymm0); -} - -void GSDrawScanlineCodeGenerator::ReadFrame() -{ - if (!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, ptr[esi]); - add(ebx, ptr[edi]); - and(ebx, HALF_VM_SIZE - 1); - - if (!m_sel.rfb) - { - return; - } - - ReadPixel(ymm2, ymm0, ebx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha() -{ - if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); - - if (m_sel.datm) - { - if (m_sel.fpsm == 2) - { - vpxor(ymm0, ymm0); - //vpsrld(ymm1, ymm2, 15); - vpslld(ymm1, ymm2, 16); - vpsrad(ymm1, 31); - vpcmpeqd(ymm1, ymm0); - } - else - { - vpcmpeqd(ymm0, ymm0); - vpxor(ymm1, ymm2, ymm0); - vpsrad(ymm1, 31); - } - } - else - { - if (m_sel.fpsm == 2) - { - vpslld(ymm1, ymm2, 16); - vpsrad(ymm1, 31); - } - else - { - vpsrad(ymm1, ymm2, 31); - } - } - - vpor(ymm7, ymm1); - - alltrue(ymm7); -} - -void GSDrawScanlineCodeGenerator::WriteMask() -{ - if (m_sel.notest) - { - return; - } - - // fm |= test; - // zm |= test; - - if (m_sel.fwrite) - { - vpor(ymm3, ymm7); - } - - if (m_sel.zwrite) - { - vpor(ymm4, ymm7); - } - - // int fzm = ~(fm == GSVector8i::xffffffff()).ps32(zm == GSVector8i::xffffffff()).mask(); - - vpcmpeqd(ymm1, ymm1); - - if (m_sel.fwrite && m_sel.zwrite) - { - vpcmpeqd(ymm0, ymm1, ymm4); - vpcmpeqd(ymm1, ymm3); - vpackssdw(ymm1, ymm0); - } - else if (m_sel.fwrite) - { - vpcmpeqd(ymm1, ymm3); - vpackssdw(ymm1, ymm1); - } - else if (m_sel.zwrite) - { - vpcmpeqd(ymm1, ymm4); - vpackssdw(ymm1, ymm1); - } - - vpmovmskb(edx, ymm1); - - not(edx); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf() -{ - if (!m_sel.zwrite) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - vmovdqa(ymm1, ptr[&m_local.temp.zs]); - } - else - { - vpbroadcastd(ymm1, ptr[&m_local.p.z]); - } - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - vpcmpeqd(ymm7, ymm7); - vpsrld(ymm7, (uint8)((m_sel.zpsm & 0x3) * 8)); - vpminsd(ymm1, ymm7); - } - - if (m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - - vpblendvb(ymm1, ptr[&m_local.temp.zd], ymm4); - } - - bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; - - WritePixel(ymm1, ymm0, ebp, edx, fast, m_sel.zpsm, 1); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch (m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - vpsllw(ymm0, ymm2, 8); - vpsrlw(ymm0, 8); - vpsrlw(ymm1, ymm2, 8); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - vpcmpeqd(ymm7, ymm7); - - vpsrld(ymm7, 27); // 0x0000001f - vpand(ymm0, ymm2, ymm7); - vpslld(ymm0, 3); - - vpslld(ymm7, 10); // 0x00007c00 - vpand(ymm4, ymm2, ymm7); - vpslld(ymm4, 9); - - vpor(ymm0, ymm4); - - vpsrld(ymm7, 5); // 0x000003e0 - vpand(ymm1, ymm2, ymm7); - vpsrld(ymm1, 2); - - vpsllw(ymm7, 10); // 0x00008000 - vpand(ymm4, ymm2, ymm7); - vpslld(ymm4, 8); - - vpor(ymm1, ymm4); - - break; - } - } - - // ymm5, ymm6 = src rb, ga - // ymm0, ymm1 = dst rb, ga - // ymm2, ymm3 = used - // ymm4, ymm7 = free - - if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - vmovdqa(ymm4, ymm5); - } - - if (m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(ymm5, ymm0); - break; - case 2: - vpxor(ymm5, ymm5); - break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch (m_sel.abb) - { - case 0: - vpsubw(ymm5, ymm4); - break; - case 1: - vpsubw(ymm5, ymm0); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; - - switch (m_sel.abc) - { - case 0: - case 1: - vpshuflw(ymm7, m_sel.abc ? ymm1 : ymm6, _MM_SHUFFLE(3, 3, 1, 1)); - vpshufhw(ymm7, ymm7, _MM_SHUFFLE(3, 3, 1, 1)); - vpsllw(ymm7, 7); - break; - case 2: - vpbroadcastw(ymm7, ptr[&m_local.gd->afix]); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16(ymm5, ymm7, 1); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch (m_sel.abd) - { - case 0: - vpaddw(ymm5, ymm4); - break; - case 1: - vpaddw(ymm5, ymm0); - break; - case 2: - break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(ymm5, ymm0); - break; - case 2: - vpxor(ymm5, ymm5); - break; - } - } - - if (m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - vpslld(ymm0, ymm6, 8); - vpsrad(ymm0, 31); - - // rb = c[0].blend8(rb, mask); - - vpblendvb(ymm5, ymm4, ymm5, ymm0); - } - - // ymm6 = src ga - // ymm1 = dst ga - // ymm5 = rb - // ymm7 = a - // ymm2, ymm3 = used - // ymm0, ymm4 = free - - vmovdqa(ymm4, ymm6); - - if (m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - vmovdqa(ymm6, ymm1); - break; - case 2: - vpxor(ymm6, ymm6); - break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch (m_sel.abb) - { - case 0: - vpsubw(ymm6, ymm4); - break; - case 1: - vpsubw(ymm6, ymm1); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16(ymm6, ymm7, 1); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch (m_sel.abd) - { - case 0: - vpaddw(ymm6, ymm4); - break; - case 1: - vpaddw(ymm6, ymm1); - break; - case 2: - break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - vmovdqa(ymm6, ymm1); - break; - case 2: - vpxor(ymm6, ymm6); - break; - } - } - - // ymm4 = src ga - // ymm5 = rb - // ymm6 = ga - // ymm2, ymm3 = used - // ymm0, ymm1, ymm7 = free - - if (m_sel.pabe) - { - vpsrld(ymm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - vpblendvb(ymm6, ymm4, ymm6, ymm0); - } - else - { - if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(ymm6, ymm4, ymm7); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.fpsm == 2 && m_sel.dthe) - { - mov(eax, ptr[esp + _top]); - and(eax, 3); - shl(eax, 5); - mov(ebp, ptr[&m_local.gd->dimx]); - vbroadcasti128(ymm7, ptr[ebp + eax + sizeof(GSVector4i) * 0]); - vpaddw(ymm5, ymm7); - vbroadcasti128(ymm7, ptr[ebp + eax + sizeof(GSVector4i) * 1]); - vpaddw(ymm6, ymm7); - } - - if (m_sel.colclamp == 0) - { - // c[0] &= 0x00ff00ff; - // c[1] &= 0x00ff00ff; - - vpcmpeqd(ymm7, ymm7); - vpsrlw(ymm7, 8); - vpand(ymm5, ymm7); - vpand(ymm6, ymm7); - } - - // GSVector8i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - vpunpckhwd(ymm7, ymm5, ymm6); - vpunpcklwd(ymm5, ymm6); - vpackuswb(ymm5, ymm7); - - if (m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - vpcmpeqd(ymm7, ymm7); - vpslld(ymm7, 31); - vpor(ymm5, ymm7); - } - - if (m_sel.fpsm == 2) - { - // GSVector8i rb = fs & 0x00f800f8; - // GSVector8i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - vmovd(xmm6, eax); - vpbroadcastd(ymm6, xmm6); - - mov(eax, 0x8000f800); - vmovd(xmm7, eax); - vpbroadcastd(ymm7, xmm7); - - vpand(ymm4, ymm5, ymm6); - vpand(ymm5, ymm7); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - vpsrld(ymm6, ymm4, 9); - vpsrld(ymm4, 3); - vpsrld(ymm7, ymm5, 16); - vpsrld(ymm5, 6); - - vpor(ymm5, ymm4); - vpor(ymm7, ymm6); - vpor(ymm5, ymm7); - } - - if (m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(ymm5, ymm2, ymm3); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; - - WritePixel(ymm5, ymm0, ebx, edx, fast, m_sel.fpsm, 0); -} - -void GSDrawScanlineCodeGenerator::ReadPixel(const Ymm& dst, const Ymm& temp, const Reg32& addr) -{ - vmovq(Xmm(dst.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm]); - vmovhps(Xmm(dst.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2]); - vmovq(Xmm(temp.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2]); - vmovhps(Xmm(temp.getIdx()), qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2]); - vinserti128(dst, dst, Xmm(temp.getIdx()), 1); - /* - vmovdqu(dst, ptr[addr * 2 + (size_t)m_local.gd->vm]); - vmovdqu(temp, ptr[addr * 2 + (size_t)m_local.gd->vm + 16 * 2]); - vpunpcklqdq(dst, dst, temp); - vpermq(dst, dst, _MM_SHUFFLE(3, 1, 2, 0)); -*/ -} - -void GSDrawScanlineCodeGenerator::WritePixel(const Ymm& src, const Ymm& temp, const Reg32& addr, const Reg32& mask, bool fast, int psm, int fz) -{ - Xmm src1 = Xmm(src.getIdx()); - Xmm src2 = Xmm(temp.getIdx()); - - vextracti128(src2, src, 1); - - if (m_sel.notest) - { - if (fast) - { - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src1); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src1); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2], src2); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2], src2); - } - else - { - WritePixel(src1, addr, 0, 0, psm); - WritePixel(src1, addr, 1, 1, psm); - WritePixel(src1, addr, 2, 2, psm); - WritePixel(src1, addr, 3, 3, psm); - WritePixel(src2, addr, 4, 0, psm); - WritePixel(src2, addr, 5, 1, psm); - WritePixel(src2, addr, 6, 2, psm); - WritePixel(src2, addr, 7, 3, psm); - } - } - else - { - // cascade tests? - - if (fast) - { - test(mask, 0x0000000f << (fz * 8)); - je("@f"); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm], src1); - L("@@"); - - test(mask, 0x000000f0 << (fz * 8)); - je("@f"); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src1); - L("@@"); - - test(mask, 0x000f0000 << (fz * 8)); - je("@f"); - vmovq(qword[addr * 2 + (size_t)m_local.gd->vm + 16 * 2], src2); - L("@@"); - - test(mask, 0x00f00000 << (fz * 8)); - je("@f"); - vmovhps(qword[addr * 2 + (size_t)m_local.gd->vm + 24 * 2], src2); - L("@@"); - - // vmaskmovps? - } - else - { - test(mask, 0x00000003 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 0, 0, psm); - L("@@"); - - test(mask, 0x0000000c << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 1, 1, psm); - L("@@"); - - test(mask, 0x00000030 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 2, 2, psm); - L("@@"); - - test(mask, 0x000000c0 << (fz * 8)); - je("@f"); - WritePixel(src1, addr, 3, 3, psm); - L("@@"); - - test(mask, 0x00030000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 4, 0, psm); - L("@@"); - - test(mask, 0x000c0000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 5, 1, psm); - L("@@"); - - test(mask, 0x00300000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 6, 2, psm); - L("@@"); - - test(mask, 0x00c00000 << (fz * 8)); - je("@f"); - WritePixel(src2, addr, 7, 3, psm); - L("@@"); - } - } -} - -static const int s_offsets[] = {0, 2, 8, 10, 16, 18, 24, 26}; - -void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const Reg32& addr, uint8 i, uint8 j, int psm) -{ - Address dst = ptr[addr * 2 + (size_t)m_local.gd->vm + s_offsets[i] * 2]; - - switch (psm) - { - case 0: - if (j == 0) - vmovd(dst, src); - else - vpextrd(dst, src, j); - break; - case 1: - if (j == 0) - vmovd(eax, src); - else - vpextrd(eax, src, j); - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - if (j == 0) - vmovd(eax, src); - else - vpextrw(eax, src, j * 2); - mov(dst, ax); - break; - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(int pixels, int mip_offset) -{ - // in - // ymm5 = addr00 - // ymm2 = addr01 - // ymm0 = addr10 - // ymm3 = addr11 - // ebx = m_local.tex[0] (!m_sel.mmin) - // ebp = m_local.tex (m_sel.mmin) - // edx = m_local.clut (m_sel.tlu) - - // out - // ymm6 = c00 - // ymm4 = c01 - // ymm1 = c10 - // ymm5 = c11 - - ASSERT(pixels == 1 || pixels == 4); - - mip_offset *= sizeof(void*); - - const GSVector8i* lod_i = m_sel.lcm ? &m_local.gd->lod.i : &m_local.temp.lod.i; - - if (m_sel.mmin && !m_sel.lcm) - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - const int t[] = {1, 4, 5, 1, 2, 5, 0, 2}; - - for (int i = 0; i < pixels; i++) - { - Ymm src = Ymm(r[i * 2 + 0]); - Ymm dst = Ymm(r[i * 2 + 1]); - Ymm t1 = Ymm(t[i * 2 + 0]); - Ymm t2 = Ymm(t[i * 2 + 1]); - - vextracti128(Xmm(t1.getIdx()), src, 1); - - for (uint8 j = 0; j < 4; j++) - { - mov(ebx, ptr[&lod_i->u32[j + 0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - ReadTexel(dst, src, j); - - mov(ebx, ptr[&lod_i->u32[j + 4]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - ReadTexel(t2, t1, j); - } - - vinserti128(dst, dst, Xmm(t2.getIdx()), 1); - } - } - else - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - const int t[] = {1, 4, 5, 1, 2, 5, 0, 2}; - - if (m_sel.mmin && m_sel.lcm) - { - mov(ebx, ptr[&lod_i->u32[0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - } - - for (int i = 0; i < pixels; i++) - { - Ymm src = Ymm(r[i * 2 + 0]); - Ymm dst = Ymm(r[i * 2 + 1]); - Ymm t1 = Ymm(t[i * 2 + 0]); - Ymm t2 = Ymm(t[i * 2 + 1]); - - if (!m_sel.tlu) - { - vpcmpeqd(t1, t1); - vpgatherdd(dst, ptr[ebx + src * 4], t1); - } - else - { - vextracti128(Xmm(t1.getIdx()), src, 1); - - for (uint8 j = 0; j < 4; j++) - { - ReadTexel(dst, src, j); - ReadTexel(t2, t1, j); - } - - vinserti128(dst, dst, Xmm(t2.getIdx()), 1); - /* - vpcmpeqd(t1, t1); - vpgatherdd(t2, ptr[ebx + src * 1], t1); // either this 1x scale, or the latency of two dependendent gathers are too slow - vpslld(t2, 24); - vpsrld(t2, 24); - vpcmpeqd(t1, t1); - vpgatherdd(dst, ptr[edx + t2 * 4], t1); - */ - } - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(const Ymm& dst, const Ymm& addr, uint8 i) -{ - ASSERT(i < 4); - - const Address& src = m_sel.tlu ? ptr[edx + eax * 4] : ptr[ebx + eax * 4]; - - if (i == 0) - vmovd(eax, Xmm(addr.getIdx())); - else - vpextrd(eax, Xmm(addr.getIdx()), i); - - if (m_sel.tlu) - movzx(eax, byte[ebx + eax]); - - if (i == 0) - vmovd(Xmm(dst.getIdx()), src); - else - vpinsrd(Xmm(dst.getIdx()), src, i); -} - - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.cpp b/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.cpp deleted file mode 100644 index fa0cccd76acd4..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSDrawScanlineCodeGenerator.x86.cpp +++ /dev/null @@ -1,2953 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSDrawScanlineCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 16; -static const int _top = _args + 4; -static const int _v = _args + 8; - -void GSDrawScanlineCodeGenerator::Generate_SSE() -{ - push(ebx); - push(esi); - push(edi); - push(ebp); - - Init_SSE(); - - if (!m_sel.edge) - { - align(16); - } - -L("loop"); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // xmm0 = z/zi - // xmm2 = s/u (tme) - // xmm3 = t/v (tme) - // xmm4 = q (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - bool tme = m_sel.tfx != TFX_NONE; - - TestZ_SSE(tme ? xmm5 : xmm2, tme ? xmm6 : xmm3); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - xmm0 - // xmm2 = s/u (tme) - // xmm3 = t/v (tme) - // xmm4 = q (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - if (m_sel.mmin) - { - SampleTextureLOD_SSE(); - } - else - { - SampleTexture_SSE(); - } - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - xmm2 - // - xmm3 - // - xmm4 - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - AlphaTFX_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ReadMask_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestAlpha_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ColorTFX_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - Fog_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ReadFrame_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestDestAlpha_SSE(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - WriteMask_SSE(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - - WriteZBuf_SSE(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // - ebp - // xmm2 = fd - // xmm3 = fm - // - xmm4 - // xmm5 = rb - // xmm6 = ga - - AlphaBlend_SSE(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // xmm2 = fd - // xmm3 = fm - // xmm5 = rb - // xmm6 = ga - - WriteFrame_SSE(); - -L("step"); - - // if(steps <= 0) break; - - if (!m_sel.edge) - { - test(ecx, ecx); - - jle("exit", T_NEAR); - - Step_SSE(); - - jmp("loop", T_NEAR); - } - -L("exit"); - - // vzeroupper(); - - pop(ebp); - pop(edi); - pop(esi); - pop(ebx); - - ret(8); -} - -void GSDrawScanlineCodeGenerator::Init_SSE() -{ - if (!m_sel.notest) - { - // int skip = left & 3; - - mov(ebx, edx); - and(edx, 3); - - // int steps = pixels + skip - 4; - - lea(ecx, ptr[ecx + edx - 4]); - - // left -= skip; - - sub(ebx, edx); - - // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; - - shl(edx, 4); - - movdqa(xmm7, ptr[edx + (size_t)g_const->m_test_128b[0]]); - - mov(eax, ecx); - sar(eax, 31); - and(eax, ecx); - shl(eax, 4); - - por(xmm7, ptr[eax + (size_t)g_const->m_test_128b[7]]); - } - else - { - mov(ebx, edx); // left - xor(edx, edx); // skip - lea(ecx, ptr[ecx - 4]); // steps - } - - // GSVector2i* fza_base = &m_local.gd->fzbr[top]; - - mov(esi, ptr[esp + _top]); - lea(esi, ptr[esi * 8]); - add(esi, ptr[&m_local.gd->fzbr]); - - // GSVector2i* fza_offset = &m_local.gd->fzbc[left >> 2]; - - lea(edi, ptr[ebx * 2]); - add(edi, ptr[&m_local.gd->fzbc]); - - if (m_sel.prim != GS_SPRITE_CLASS && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // edx = &m_local.d[skip] - - lea(edx, ptr[edx * 8 + (size_t)m_local.d]); - - // ebx = &v - - mov(ebx, ptr[esp + _v]); - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.fwrite && m_sel.fge || m_sel.zb) - { - movaps(xmm0, ptr[ebx + offsetof(GSVertexSW, p)]); // v.p - - if (m_sel.fwrite && m_sel.fge) - { - // f = GSVector4i(vp).zzzzh().zzzz().add16(m_local.d[skip].f); - - cvttps2dq(xmm1, xmm0); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - paddw(xmm1, ptr[edx + offsetof(GSScanlineLocalData::skip, f)]); - - movdqa(ptr[&m_local.temp.f], xmm1); - } - - if (m_sel.zb) - { - // z = vp.zzzz() + m_local.d[skip].z; - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - movaps(ptr[&m_local.temp.z], xmm0); - movaps(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, z)]); - movaps(ptr[&m_local.temp.zo], xmm2); - addps(xmm0, xmm2); - } - } - } - else - { - if (m_sel.ztest) - { - movdqa(xmm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.edge || m_sel.tfx != TFX_NONE) - { - movaps(xmm4, ptr[ebx + offsetof(GSVertexSW, t)]); // v.t - } - - if (m_sel.edge) - { - // m_local.temp.cov = GSVector4i::cast(v.t).zzzzh().wwww().srl16(9); - - pshufhw(xmm3, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm3, xmm3, _MM_SHUFFLE(3, 3, 3, 3)); - psrlw(xmm3, 9); - - movdqa(ptr[&m_local.temp.cov], xmm3); - } - - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i vti(vt); - - cvttps2dq(xmm6, xmm4); - - // s = vti.xxxx() + m_local.d[skip].s; - // t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t; - - pshufd(xmm2, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm3, xmm6, _MM_SHUFFLE(1, 1, 1, 1)); - - paddd(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - paddd(xmm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - } - else - { - if (m_sel.ltf) - { - pshuflw(xmm6, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm6, xmm6, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm6, 12); - movdqa(ptr[&m_local.temp.vf], xmm6); - } - } - - movdqa(ptr[&m_local.temp.s], xmm2); - movdqa(ptr[&m_local.temp.t], xmm3); - } - else - { - // s = vt.xxxx() + m_local.d[skip].s; - // t = vt.yyyy() + m_local.d[skip].t; - // q = vt.zzzz() + m_local.d[skip].q; - - movaps(xmm2, xmm4); - movaps(xmm3, xmm4); - - shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - addps(xmm2, ptr[edx + offsetof(GSScanlineLocalData::skip, s)]); - addps(xmm3, ptr[edx + offsetof(GSScanlineLocalData::skip, t)]); - addps(xmm4, ptr[edx + offsetof(GSScanlineLocalData::skip, q)]); - - movaps(ptr[&m_local.temp.s], xmm2); - movaps(ptr[&m_local.temp.t], xmm3); - movaps(ptr[&m_local.temp.q], xmm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - cvttps2dq(xmm6, ptr[ebx + offsetof(GSVertexSW, c)]); // v.c - - // vc = vc.upl16(vc.zwxy()); - - pshufd(xmm5, xmm6, _MM_SHUFFLE(1, 0, 3, 2)); - punpcklwd(xmm6, xmm5); - - // rb = vc.xxxx().add16(m_local.d[skip].rb); - // ga = vc.zzzz().add16(m_local.d[skip].ga); - - pshufd(xmm5, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm6, xmm6, _MM_SHUFFLE(2, 2, 2, 2)); - - paddw(xmm5, ptr[edx + offsetof(GSScanlineLocalData::skip, rb)]); - paddw(xmm6, ptr[edx + offsetof(GSScanlineLocalData::skip, ga)]); - - movdqa(ptr[&m_local.temp.rb], xmm5); - movdqa(ptr[&m_local.temp.ga], xmm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - movdqa(xmm5, ptr[&m_local.c.rb]); - movdqa(xmm6, ptr[&m_local.c.ga]); - } - } - } - } -} - -void GSDrawScanlineCodeGenerator::Step_SSE() -{ - // steps -= 4; - - sub(ecx, 4); - - // fza_offset++; - - add(edi, 8); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // z += m_local.d4.z; - - if (m_sel.zb) - { - movaps(xmm0, ptr[&m_local.temp.zo]); - addps(xmm0, ptr[&m_local.d4.z]); - movaps(ptr[&m_local.temp.zo], xmm0); - addps(xmm0, ptr[&m_local.temp.z]); - } - - // f = f.add16(m_local.d4.f); - - if (m_sel.fwrite && m_sel.fge) - { - movdqa(xmm1, ptr[&m_local.temp.f]); - paddw(xmm1, ptr[&m_local.d4.f]); - movdqa(ptr[&m_local.temp.f], xmm1); - } - } - else - { - if (m_sel.ztest) - { - movdqa(xmm0, ptr[&m_local.p.z]); - } - } - - if (m_sel.fb) - { - if (m_sel.tfx != TFX_NONE) - { - if (m_sel.fst) - { - // GSVector4i stq = m_local.d4.stq; - - // s += stq.xxxx(); - // if(!sprite) t += stq.yyyy(); - - movdqa(xmm4, ptr[&m_local.d4.stq]); - - pshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - paddd(xmm2, ptr[&m_local.temp.s]); - movdqa(ptr[&m_local.temp.s], xmm2); - - if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin) - { - pshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - paddd(xmm3, ptr[&m_local.temp.t]); - movdqa(ptr[&m_local.temp.t], xmm3); - } - else - { - movdqa(xmm3, ptr[&m_local.temp.t]); - } - } - else - { - // GSVector4 stq = m_local.d4.stq; - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - movaps(xmm4, ptr[&m_local.d4.stq]); - movaps(xmm2, xmm4); - movaps(xmm3, xmm4); - - shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - addps(xmm2, ptr[&m_local.temp.s]); - addps(xmm3, ptr[&m_local.temp.t]); - addps(xmm4, ptr[&m_local.temp.q]); - - movaps(ptr[&m_local.temp.s], xmm2); - movaps(ptr[&m_local.temp.t], xmm3); - movaps(ptr[&m_local.temp.q], xmm4); - } - } - - if (!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if (m_sel.iip) - { - // GSVector4i c = m_local.d4.c; - - // rb = rb.add16(c.xxxx()); - // ga = ga.add16(c.yyyy()); - - movdqa(xmm7, ptr[&m_local.d4.c]); - - pshufd(xmm5, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm6, xmm7, _MM_SHUFFLE(1, 1, 1, 1)); - - paddw(xmm5, ptr[&m_local.temp.rb]); - paddw(xmm6, ptr[&m_local.temp.ga]); - - // FIXME: color may underflow and roll over at the end of the line, if decreasing - - pxor(xmm7, xmm7); - pmaxsw(xmm5, xmm7); - pmaxsw(xmm6, xmm7); - - movdqa(ptr[&m_local.temp.rb], xmm5); - movdqa(ptr[&m_local.temp.ga], xmm6); - } - else - { - if (m_sel.tfx == TFX_NONE) - { - movdqa(xmm5, ptr[&m_local.c.rb]); - movdqa(xmm6, ptr[&m_local.c.ga]); - } - } - } - } - - if (!m_sel.notest) - { - // test = m_test[7 + (steps & (steps >> 31))]; - - mov(edx, ecx); - sar(edx, 31); - and(edx, ecx); - shl(edx, 4); - - movdqa(xmm7, ptr[edx + (size_t)g_const->m_test_128b[7]]); - } -} - -void GSDrawScanlineCodeGenerator::TestZ_SSE(const Xmm& temp1, const Xmm& temp2) -{ - if (!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, ptr[esi + 4]); - add(ebp, ptr[edi + 4]); - and(ebp, HALF_VM_SIZE - 1); - - // GSVector4i zs = zi; - - if (m_sel.prim != GS_SPRITE_CLASS) - { - if (m_sel.zoverflow) - { - // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); - - movaps(temp1, ptr[&GSVector4::m_half]); - mulps(temp1, xmm0); - cvttps2dq(temp1, temp1); - pslld(temp1, 1); - - cvttps2dq(xmm0, xmm0); - pcmpeqd(temp2, temp2); - psrld(temp2, 31); - pand(xmm0, temp2); - - por(xmm0, temp1); - } - else - { - // zs = GSVector4i(z); - - cvttps2dq(xmm0, xmm0); - } - - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - pcmpeqd(temp1, temp1); - psrld(temp1, (uint8)((m_sel.zpsm & 0x3) * 8)); - pminsd(xmm0, temp1); - } - - if (m_sel.zwrite) - { - movdqa(ptr[&m_local.temp.zs], xmm0); - } - } - - if (m_sel.ztest) - { - ReadPixel_SSE(xmm1, ebp); - - if (m_sel.zwrite && m_sel.zpsm < 2) - { - movdqa(ptr[&m_local.temp.zd], xmm1); - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if (m_sel.zpsm) - { - pslld(xmm1, m_sel.zpsm * 8); - psrld(xmm1, m_sel.zpsm * 8); - } - - if (m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector4i o = GSVector4i::x80000000(); - - pcmpeqd(temp1, temp1); - pslld(temp1, 31); - - // GSVector4i zso = zs - o; - // GSVector4i zdo = zd - o; - - psubd(xmm0, temp1); - psubd(xmm1, temp1); - } - - switch (m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - pcmpgtd(xmm1, xmm0); - por(xmm7, xmm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - pcmpgtd(xmm0, xmm1); - pcmpeqd(temp1, temp1); - pxor(xmm0, temp1); - por(xmm7, xmm0); - break; - } - - alltrue(xmm7); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture_SSE() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - mov(ebx, ptr[&m_local.gd->tex[0]]); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - // ebx = tex - // edx = clut - - if (!m_sel.fst) - { - rcpps(xmm4, xmm4); - - mulps(xmm2, xmm4); - mulps(xmm3, xmm4); - - cvttps2dq(xmm2, xmm2); - cvttps2dq(xmm3, xmm3); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - movd(xmm4, eax); - pshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - psubd(xmm2, xmm4); - psubd(xmm3, xmm4); - } - } - - // xmm2 = u - // xmm3 = v - - if (m_sel.ltf) - { - // GSVector4i uf = u.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.uf], xmm0); - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4i vf = v.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.vf], xmm0); - } - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - psrad(xmm2, 16); - psrad(xmm3, 16); - packssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - movdqa(xmm3, xmm2); - pcmpeqd(xmm1, xmm1); - psrlw(xmm1, 15); - paddw(xmm3, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap_SSE(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - Wrap_SSE(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i y0 = uv0.uph16() << tw; - // GSVector4i x0 = uv0.upl16(); - - pxor(xmm0, xmm0); - - movdqa(xmm4, xmm2); - punpckhwd(xmm2, xmm0); - punpcklwd(xmm4, xmm0); - pslld(xmm2, m_sel.tw + 3); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i y1 = uv1.uph16() << tw; - // GSVector4i x1 = uv1.upl16(); - - movdqa(xmm6, xmm3); - punpckhwd(xmm3, xmm0); - punpcklwd(xmm6, xmm0); - pslld(xmm3, m_sel.tw + 3); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - movdqa(xmm5, xmm2); - paddd(xmm5, xmm4); - paddd(xmm2, xmm6); - - movdqa(xmm0, xmm3); - paddd(xmm0, xmm4); - paddd(xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(4, 0); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - movdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp_4(rb01, uf); - // ga00 = ga00.lerp_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp_4(rb11, uf); - // ga10 = ga10.lerp_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp_4(rb10, vf); - // ga00 = ga00.lerp_4(ga10, vf); - - movdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - paddd(xmm2, xmm4); - movdqa(xmm5, xmm2); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(1, 0); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } -} - -void GSDrawScanlineCodeGenerator::Wrap_SSE(const Xmm& uv) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - pmaxsw(uv, ptr[&m_local.gd->t.min]); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv, xmm0); - } - - pminsw(uv, ptr[&m_local.gd->t.max]); - } - else - { - pand(uv, ptr[&m_local.gd->t.min]); - - if (region) - { - por(uv, ptr[&m_local.gd->t.max]); - } - } - } - else - { - movdqa(xmm4, ptr[&m_local.gd->t.min]); - movdqa(xmm5, ptr[&m_local.gd->t.max]); - movdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv); - - pand(xmm1, xmm4); - - if (region) - { - por(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv, xmm4); - pminsw(uv, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - blend8(uv, xmm1); - } -} - -void GSDrawScanlineCodeGenerator::Wrap_SSE(const Xmm& uv0, const Xmm& uv1) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - movdqa(xmm4, ptr[&m_local.gd->t.min]); - pmaxsw(uv0, xmm4); - pmaxsw(uv1, xmm4); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv0, xmm0); - pmaxsw(uv1, xmm0); - } - - movdqa(xmm5, ptr[&m_local.gd->t.max]); - pminsw(uv0, xmm5); - pminsw(uv1, xmm5); - } - else - { - movdqa(xmm4, ptr[&m_local.gd->t.min]); - pand(uv0, xmm4); - pand(uv1, xmm4); - - if (region) - { - movdqa(xmm5, ptr[&m_local.gd->t.max]); - por(uv0, xmm5); - por(uv1, xmm5); - } - } - } - else - { - movdqa(xmm4, ptr[&m_local.gd->t.min]); - movdqa(xmm5, ptr[&m_local.gd->t.max]); - - movdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv0); - - pand(xmm1, xmm4); - - if (region) - { - por(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv0, xmm4); - pminsw(uv0, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - pblendvb(uv0, xmm1); - - // uv1 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv1); - - pand(xmm1, xmm4); - - if (region) - { - por(xmm1, xmm5); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv1, xmm4); - pminsw(uv1, xmm5); - - // clamp.blend8(repeat, m_local.gd->t.mask); - pblendvb(uv1, xmm1); - } -} - -void GSDrawScanlineCodeGenerator::SampleTextureLOD_SSE() -{ - if (!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - push(ebp); - - mov(ebp, (size_t)m_local.gd->tex); - - if (m_sel.tlu) - { - mov(edx, ptr[&m_local.gd->clut]); - } - - if (!m_sel.fst) - { - rcpps(xmm0, xmm4); - - mulps(xmm2, xmm0); - mulps(xmm3, xmm0); - - cvttps2dq(xmm2, xmm2); - cvttps2dq(xmm3, xmm3); - } - - // xmm2 = u - // xmm3 = v - // xmm4 = q - // xmm0 = xmm1 = xmm5 = xmm6 = free - - // TODO: if the fractional part is not needed in round-off mode then there is a faster integer log2 (just take the exp) (but can we round it?) - - if (!m_sel.lcm) - { - // store u/v - - movdqa(xmm0, xmm2); - punpckldq(xmm2, xmm3); - movdqa(ptr[&m_local.temp.uv[0]], xmm2); - punpckhdq(xmm0, xmm3); - movdqa(ptr[&m_local.temp.uv[1]], xmm0); - - // lod = -log2(Q) * (1 << L) + K - - movdqa(xmm0, xmm4); - pcmpeqd(xmm1, xmm1); - psrld(xmm1, 25); - pslld(xmm0, 1); - psrld(xmm0, 24); - psubd(xmm0, xmm1); - cvtdq2ps(xmm0, xmm0); - - // xmm0 = (float)(exp(q) - 127) - - pslld(xmm4, 9); - psrld(xmm4, 9); - orps(xmm4, ptr[g_const->m_log2_coef_128b[3]]); - - // xmm4 = mant(q) | 1.0f - - movdqa(xmm5, xmm4); - mulps(xmm5, ptr[g_const->m_log2_coef_128b[0]]); - addps(xmm5, ptr[g_const->m_log2_coef_128b[1]]); - mulps(xmm5, xmm4); - subps(xmm4, ptr[g_const->m_log2_coef_128b[3]]); - addps(xmm5, ptr[g_const->m_log2_coef_128b[2]]); - mulps(xmm4, xmm5); - addps(xmm4, xmm0); - - // xmm4 = log2(Q) = ((((c0 * xmm4) + c1) * xmm4) + c2) * (xmm4 - 1.0f) + xmm0 - - mulps(xmm4, ptr[&m_local.gd->l]); - addps(xmm4, ptr[&m_local.gd->k]); - - // xmm4 = (-log2(Q) * (1 << L) + K) * 0x10000 - - xorps(xmm0, xmm0); - minps(xmm4, ptr[&m_local.gd->mxl]); - maxps(xmm4, xmm0); - cvtps2dq(xmm4, xmm4); - - if (m_sel.mmin == 1) // round-off mode - { - mov(eax, 0x8000); - movd(xmm0, eax); - pshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - paddd(xmm4, xmm0); - } - - movdqa(xmm0, xmm4); - psrld(xmm4, 16); - movdqa(ptr[&m_local.temp.lod.i], xmm4); - - if (m_sel.mmin == 2) // trilinear mode - { - pshuflw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(ptr[&m_local.temp.lod.f], xmm0); - } - - // shift u/v by (int)lod - - movq(xmm4, ptr[&m_local.gd->t.minmax]); - - movdqa(xmm2, ptr[&m_local.temp.uv[0]]); - movdqa(xmm5, xmm2); - movdqa(xmm3, ptr[&m_local.temp.uv[1]]); - movdqa(xmm6, xmm3); - - movd(xmm0, ptr[&m_local.temp.lod.i.u32[0]]); - psrad(xmm2, xmm0); - movdqa(xmm1, xmm4); - psrlw(xmm1, xmm0); - movq(ptr[&m_local.temp.uv_minmax[0].u32[0]], xmm1); - - movd(xmm0, ptr[&m_local.temp.lod.i.u32[1]]); - psrad(xmm5, xmm0); - movdqa(xmm1, xmm4); - psrlw(xmm1, xmm0); - movq(ptr[&m_local.temp.uv_minmax[1].u32[0]], xmm1); - - movd(xmm0, ptr[&m_local.temp.lod.i.u32[2]]); - psrad(xmm3, xmm0); - movdqa(xmm1, xmm4); - psrlw(xmm1, xmm0); - movq(ptr[&m_local.temp.uv_minmax[0].u32[2]], xmm1); - - movd(xmm0, ptr[&m_local.temp.lod.i.u32[3]]); - psrad(xmm6, xmm0); - movdqa(xmm1, xmm4); - psrlw(xmm1, xmm0); - movq(ptr[&m_local.temp.uv_minmax[1].u32[2]], xmm1); - - punpckldq(xmm2, xmm3); - punpckhdq(xmm5, xmm6); - movdqa(xmm3, xmm2); - punpckldq(xmm2, xmm5); - punpckhdq(xmm3, xmm5); - - movdqa(ptr[&m_local.temp.uv[0]], xmm2); - movdqa(ptr[&m_local.temp.uv[1]], xmm3); - - movdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - movdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - - movdqa(xmm0, xmm5); - punpcklwd(xmm5, xmm6); - punpckhwd(xmm0, xmm6); - movdqa(xmm6, xmm5); - punpckldq(xmm5, xmm0); - punpckhdq(xmm6, xmm0); - - movdqa(ptr[&m_local.temp.uv_minmax[0]], xmm5); - movdqa(ptr[&m_local.temp.uv_minmax[1]], xmm6); - } - else - { - // lod = K - - movd(xmm0, ptr[&m_local.gd->lod.i.u32[0]]); - - psrad(xmm2, xmm0); - psrad(xmm3, xmm0); - - movdqa(ptr[&m_local.temp.uv[0]], xmm2); - movdqa(ptr[&m_local.temp.uv[1]], xmm3); - - movdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - movdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - } - - // xmm2 = m_local.temp.uv[0] = u (level m) - // xmm3 = m_local.temp.uv[1] = v (level m) - // xmm5 = minuv - // xmm6 = maxuv - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - movd(xmm4, eax); - pshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - psubd(xmm2, xmm4); - psubd(xmm3, xmm4); - - // GSVector4i uf = u.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.uf], xmm0); - - // GSVector4i vf = v.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.vf], xmm0); - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - psrad(xmm2, 16); - psrad(xmm3, 16); - packssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - movdqa(xmm3, xmm2); - pcmpeqd(xmm1, xmm1); - psrlw(xmm1, 15); - paddw(xmm3, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD_SSE(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD_SSE(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i x0 = uv0.upl16(); - // GSVector4i y0 = uv0.uph16() << tw; - - pxor(xmm0, xmm0); - - movdqa(xmm4, xmm2); - punpckhwd(xmm2, xmm0); - punpcklwd(xmm4, xmm0); - pslld(xmm2, m_sel.tw + 3); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i x1 = uv1.upl16(); - // GSVector4i y1 = uv1.uph16() << tw; - - movdqa(xmm6, xmm3); - punpcklwd(xmm6, xmm0); - punpckhwd(xmm3, xmm0); - pslld(xmm3, m_sel.tw + 3); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - movdqa(xmm5, xmm2); - paddd(xmm5, xmm4); - paddd(xmm2, xmm6); - - movdqa(xmm0, xmm3); - paddd(xmm0, xmm4); - paddd(xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(4, 0); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - movdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp_4(rb01, uf); - // ga00 = ga00.lerp_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp_4(rb11, uf); - // ga10 = ga10.lerp_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp_4(rb10, vf); - // ga00 = ga00.lerp_4(ga10, vf); - - movdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - paddd(xmm2, xmm4); - movdqa(xmm5, xmm2); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(1, 0); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } - - if (m_sel.mmin != 1) // !round-off mode - { - movdqa(ptr[&m_local.temp.trb], xmm5); - movdqa(ptr[&m_local.temp.tga], xmm6); - - movdqa(xmm2, ptr[&m_local.temp.uv[0]]); - movdqa(xmm3, ptr[&m_local.temp.uv[1]]); - - psrad(xmm2, 1); - psrad(xmm3, 1); - - movdqa(xmm5, ptr[&m_local.temp.uv_minmax[0]]); - movdqa(xmm6, ptr[&m_local.temp.uv_minmax[1]]); - - psrlw(xmm5, 1); - psrlw(xmm6, 1); - - if (m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - movd(xmm4, eax); - pshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - - psubd(xmm2, xmm4); - psubd(xmm3, xmm4); - - // GSVector4i uf = u.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.uf], xmm0); - - // GSVector4i vf = v.xxzzlh().srl16(1); - - pshuflw(xmm0, xmm3, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 12); - movdqa(ptr[&m_local.temp.vf], xmm0); - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - psrad(xmm2, 16); - psrad(xmm3, 16); - packssdw(xmm2, xmm3); - - if (m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - movdqa(xmm3, xmm2); - pcmpeqd(xmm1, xmm1); - psrlw(xmm1, 15); - paddw(xmm3, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - WrapLOD_SSE(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - WrapLOD_SSE(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i x0 = uv0.upl16(); - // GSVector4i y0 = uv0.uph16() << tw; - - pxor(xmm0, xmm0); - - movdqa(xmm4, xmm2); - punpckhwd(xmm2, xmm0); - punpcklwd(xmm4, xmm0); - pslld(xmm2, m_sel.tw + 3); - - // xmm0 = 0 - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm1, xmm5, xmm6 = free - // xmm7 = used - - if (m_sel.ltf) - { - // GSVector4i x1 = uv1.upl16(); - // GSVector4i y1 = uv1.uph16() << tw; - - movdqa(xmm6, xmm3); - punpckhwd(xmm3, xmm0); - punpcklwd(xmm6, xmm0); - pslld(xmm3, m_sel.tw + 3); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - movdqa(xmm5, xmm2); - paddd(xmm5, xmm4); - paddd(xmm2, xmm6); - - movdqa(xmm0, xmm3); - paddd(xmm0, xmm4); - paddd(xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(4, 1); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - movdqa(xmm0, ptr[&m_local.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - split16_2x8(xmm2, xmm6, xmm6); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - split16_2x8(xmm3, xmm4, xmm4); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp_4(rb01, uf); - // ga00 = ga00.lerp_4(ga01, uf); - - lerp16_4(xmm3, xmm2, xmm0); - lerp16_4(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - split16_2x8(xmm1, xmm2, xmm1); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm5); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp_4(rb11, uf); - // ga10 = ga10.lerp_4(ga11, uf); - - lerp16_4(xmm5, xmm1, xmm0); - lerp16_4(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp_4(rb10, vf); - // ga00 = ga00.lerp_4(ga10, vf); - - movdqa(xmm0, ptr[&m_local.temp.vf]); - - lerp16_4(xmm5, xmm3, xmm0); - lerp16_4(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - paddd(xmm2, xmm4); - movdqa(xmm5, xmm2); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel_SSE(1, 1); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - split16_2x8(xmm5, xmm6, xmm6); - } - - movdqa(xmm0, ptr[m_sel.lcm ? &m_local.gd->lod.f : &m_local.temp.lod.f]); - psrlw(xmm0, 1); - - movdqa(xmm2, ptr[&m_local.temp.trb]); - movdqa(xmm3, ptr[&m_local.temp.tga]); - - lerp16(xmm5, xmm2, xmm0, 0); - lerp16(xmm6, xmm3, xmm0, 0); - } - - pop(ebp); -} - -void GSDrawScanlineCodeGenerator::WrapLOD_SSE(const Xmm& uv) -{ - // xmm5 = minuv - // xmm6 = maxuv - // xmm0, xmm1, xmm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - pmaxsw(uv, xmm5); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv, xmm0); - } - - pminsw(uv, xmm6); - } - else - { - pand(uv, xmm5); - - if (region) - { - por(uv, xmm6); - } - } - } - else - { - movdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv); - - pand(xmm1, xmm5); - - if (region) - { - por(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv, xmm5); - pminsw(uv, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - blend8(uv, xmm1); - } -} - -void GSDrawScanlineCodeGenerator::WrapLOD_SSE(const Xmm& uv0, const Xmm& uv1) -{ - // xmm5 = minuv - // xmm6 = maxuv - // xmm0, xmm1, xmm4 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if (wms_clamp == wmt_clamp) - { - if (wms_clamp) - { - if (region) - { - pmaxsw(uv0, xmm5); - pmaxsw(uv1, xmm5); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv0, xmm0); - pmaxsw(uv1, xmm0); - } - - pminsw(uv0, xmm6); - pminsw(uv1, xmm6); - } - else - { - pand(uv0, xmm5); - pand(uv1, xmm5); - - if (region) - { - por(uv0, xmm6); - por(uv1, xmm6); - } - } - } - else - { - movdqa(xmm0, ptr[&m_local.gd->t.mask]); - - // uv0 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv0); - - pand(xmm1, xmm5); - - if (region) - { - por(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv0, xmm5); - pminsw(uv0, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - pblendvb(uv0, xmm1); - - // uv1 - - // GSVector4i repeat = (t & m_local.gd->t.min) | m_local.gd->t.max; - - movdqa(xmm1, uv1); - - pand(xmm1, xmm5); - - if (region) - { - por(xmm1, xmm6); - } - - // GSVector4i clamp = t.sat_i16(m_local.gd->t.min, m_local.gd->t.max); - - pmaxsw(uv1, xmm5); - pminsw(uv1, xmm6); - - // clamp.blend8(repeat, m_local.gd->t.mask); - - pblendvb(uv1, xmm1); - } -} - -void GSDrawScanlineCodeGenerator::AlphaTFX_SSE() -{ - if (!m_sel.fb) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - movdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16(xmm6, xmm4, 1); - - clamp16(xmm6, xmm3); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - movdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_HIGHLIGHT: - - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - movdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - movdqa(xmm2, xmm4); - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - psrlw(xmm4, 7); - - if (m_sel.tcc) - { - paddusb(xmm4, xmm6); - } - - mix16(xmm6, xmm4, xmm3); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if (!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - movdqa(xmm4, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - movdqa(xmm2, xmm4); - - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if (m_sel.iip) - { - psrlw(xmm6, 7); - } - - break; - } - - if (m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if (!m_sel.abe) - { - // a = cov - - if (m_sel.edge) - { - movdqa(xmm0, ptr[&m_local.temp.cov]); - } - else - { - pcmpeqd(xmm0, xmm0); - psllw(xmm0, 15); - psrlw(xmm0, 8); - } - - mix16(xmm6, xmm0, xmm1); - } - else - { - // a = a == 0x80 ? cov : a - - pcmpeqd(xmm0, xmm0); - psllw(xmm0, 15); - psrlw(xmm0, 8); - - if (m_sel.edge) - { - movdqa(xmm1, ptr[&m_local.temp.cov]); - } - else - { - movdqa(xmm1, xmm0); - } - - pcmpeqw(xmm0, xmm6); - psrld(xmm0, 16); - pslld(xmm0, 16); - - blend8(xmm6, xmm1); - } - } -} - -void GSDrawScanlineCodeGenerator::ReadMask_SSE() -{ - if (m_sel.fwrite) - { - movdqa(xmm3, ptr[&m_local.gd->fm]); - } - - if (m_sel.zwrite) - { - movdqa(xmm4, ptr[&m_local.gd->zm]); - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha_SSE() -{ - switch (m_sel.atst) - { - case ATST_NEVER: - // t = GSVector4i::xffffffff(); - pcmpeqd(xmm1, xmm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_local.gd->aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpgtd(xmm1, ptr[&m_local.gd->aref]); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_local.gd->aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpeqd(xmm1, ptr[&m_local.gd->aref]); - pcmpeqd(xmm0, xmm0); - pxor(xmm1, xmm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_local.gd->aref; - movdqa(xmm0, xmm6); - psrld(xmm0, 16); - movdqa(xmm1, ptr[&m_local.gd->aref]); - pcmpgtd(xmm1, xmm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_local.gd->aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpeqd(xmm1, ptr[&m_local.gd->aref]); - break; - } - - switch (m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - por(xmm7, xmm1); - alltrue(xmm7); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - por(xmm4, xmm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - por(xmm3, xmm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - por(xmm4, xmm1); - // fm |= t & GSVector4i::xff000000(); - psrld(xmm1, 24); - pslld(xmm1, 24); - por(xmm3, xmm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX_SSE() -{ - if (!m_sel.fwrite) - { - return; - } - - switch (m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16(xmm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - clamp16(xmm5, xmm1); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - if (m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_local.c.ga; - - movdqa(xmm2, ptr[m_sel.iip ? &m_local.temp.ga : &m_local.c.ga]); - } - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - movdqa(xmm1, xmm6); - - modulate16(xmm6, xmm2, 1); - - pshuflw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - psrlw(xmm2, 7); - - paddw(xmm6, xmm2); - - clamp16(xmm6, xmm0); - - mix16(xmm6, xmm1, xmm0); - - // GSVector4i rb = iip ? rbf : m_local.c.rb; - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16(xmm5, ptr[m_sel.iip ? &m_local.temp.rb : &m_local.c.rb], 1); - - paddw(xmm5, xmm2); - - clamp16(xmm5, xmm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if (m_sel.iip) - { - psrlw(xmm5, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog_SSE() -{ - if (!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_local.gd->frb.lerp16<0>(rb, f); - // ga = m_local.gd->fga.lerp16<0>(ga, f).mix16(ga); - - movdqa(xmm0, ptr[m_sel.prim != GS_SPRITE_CLASS ? &m_local.temp.f : &m_local.p.f]); - movdqa(xmm1, xmm6); - - movdqa(xmm2, ptr[&m_local.gd->frb]); - lerp16(xmm5, xmm2, xmm0, 0); - - movdqa(xmm2, ptr[&m_local.gd->fga]); - lerp16(xmm6, xmm2, xmm0, 0); - mix16(xmm6, xmm1, xmm0); -} - -void GSDrawScanlineCodeGenerator::ReadFrame_SSE() -{ - if (!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, ptr[esi]); - add(ebx, ptr[edi]); - and(ebx, HALF_VM_SIZE - 1); - - if (!m_sel.rfb) - { - return; - } - - ReadPixel_SSE(xmm2, ebx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha_SSE() -{ - if (!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_local.gd->datm).sra32(31); - - movdqa(xmm1, xmm2); - - if (m_sel.datm) - { - if (m_sel.fpsm == 2) - { - pxor(xmm0, xmm0); - // psrld(xmm1, 15); - pslld(xmm1, 16); - psrld(xmm1, 31); - pcmpeqd(xmm1, xmm0); - } - else - { - pcmpeqd(xmm0, xmm0); - pxor(xmm1, xmm0); - psrad(xmm1, 31); - } - } - else - { - if (m_sel.fpsm == 2) - { - pslld(xmm1, 16); - } - - psrad(xmm1, 31); - } - - por(xmm7, xmm1); - - alltrue(xmm7); -} - -void GSDrawScanlineCodeGenerator::WriteMask_SSE() -{ - if (m_sel.notest) - { - return; - } - - // fm |= test; - // zm |= test; - - if (m_sel.fwrite) - { - por(xmm3, xmm7); - } - - if (m_sel.zwrite) - { - por(xmm4, xmm7); - } - - // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); - - pcmpeqd(xmm1, xmm1); - - if (m_sel.fwrite && m_sel.zwrite) - { - movdqa(xmm0, xmm1); - pcmpeqd(xmm1, xmm3); - pcmpeqd(xmm0, xmm4); - packssdw(xmm1, xmm0); - } - else if (m_sel.fwrite) - { - pcmpeqd(xmm1, xmm3); - packssdw(xmm1, xmm1); - } - else if (m_sel.zwrite) - { - pcmpeqd(xmm1, xmm4); - packssdw(xmm1, xmm1); - } - - pmovmskb(edx, xmm1); - - not(edx); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf_SSE() -{ - if (!m_sel.zwrite) - { - return; - } - - movdqa(xmm1, ptr[m_sel.prim != GS_SPRITE_CLASS ? &m_local.temp.zs : &m_local.p.z]); - - if (m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - - movdqa(xmm0, xmm4); - movdqa(xmm7, ptr[&m_local.temp.zd]); - blend8(xmm1, xmm7); - } - - // Clamp Z to ZPSM_FMT_MAX - if (m_sel.zclamp) - { - pcmpeqd(xmm7, xmm7); - psrld(xmm7, (uint8)((m_sel.zpsm & 0x3) * 8)); - pminsd(xmm1, xmm7); - } - - bool fast = m_sel.ztest ? m_sel.zpsm < 2 : m_sel.zpsm == 0 && m_sel.notest; - - WritePixel_SSE(xmm1, ebp, dh, fast, m_sel.zpsm, 1); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend_SSE() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - if ((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch (m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - split16_2x8(xmm0, xmm1, xmm2); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - movdqa(xmm0, xmm2); - movdqa(xmm1, xmm2); - movdqa(xmm4, xmm2); - - pcmpeqd(xmm7, xmm7); - psrld(xmm7, 27); // 0x0000001f - pand(xmm0, xmm7); - pslld(xmm0, 3); - - pslld(xmm7, 10); // 0x00007c00 - pand(xmm4, xmm7); - pslld(xmm4, 9); - - por(xmm0, xmm4); - - movdqa(xmm4, xmm1); - - psrld(xmm7, 5); // 0x000003e0 - pand(xmm1, xmm7); - psrld(xmm1, 2); - - psllw(xmm7, 10); // 0x00008000 - pand(xmm4, xmm7); - pslld(xmm4, 8); - - por(xmm1, xmm4); - - break; - } - } - - // xmm5, xmm6 = src rb, ga - // xmm0, xmm1 = dst rb, ga - // xmm2, xmm3 = used - // xmm4, xmm7 = free - - if (m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - movdqa(xmm4, xmm5); - } - - if (m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - movdqa(xmm5, xmm0); - break; - case 2: - pxor(xmm5, xmm5); - break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch (m_sel.abb) - { - case 0: - psubw(xmm5, xmm4); - break; - case 1: - psubw(xmm5, xmm0); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_local.gd->afix; - - switch (m_sel.abc) - { - case 0: - case 1: - pshuflw(xmm7, m_sel.abc ? xmm1 : xmm6, _MM_SHUFFLE(3, 3, 1, 1)); - pshufhw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); - psllw(xmm7, 7); - break; - case 2: - movdqa(xmm7, ptr[&m_local.gd->afix]); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16(xmm5, xmm7, 1); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch (m_sel.abd) - { - case 0: - paddw(xmm5, xmm4); - break; - case 1: - paddw(xmm5, xmm0); - break; - case 2: - break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - movdqa(xmm5, xmm0); - break; - case 2: - pxor(xmm5, xmm5); - break; - } - } - - if (m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - movdqa(xmm0, xmm6); - pslld(xmm0, 8); - psrad(xmm0, 31); - - // rb = c[0].blend8(rb, mask); - - blend8r(xmm5, xmm4); - } - - // xmm6 = src ga - // xmm1 = dst ga - // xmm5 = rb - // xmm7 = a - // xmm2, xmm3 = used - // xmm0, xmm4 = free - - movdqa(xmm4, xmm6); - - if (m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch (m_sel.aba) - { - case 0: - break; - case 1: - movdqa(xmm6, xmm1); - break; - case 2: - pxor(xmm6, xmm6); - break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch (m_sel.abb) - { - case 0: - psubw(xmm6, xmm4); - break; - case 1: - psubw(xmm6, xmm1); - break; - case 2: - break; - } - - if (!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16(xmm6, xmm7, 1); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch (m_sel.abd) - { - case 0: - paddw(xmm6, xmm4); - break; - case 1: - paddw(xmm6, xmm1); - break; - case 2: - break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch (m_sel.abd) - { - case 0: - break; - case 1: - movdqa(xmm6, xmm1); - break; - case 2: - pxor(xmm6, xmm6); - break; - } - } - - // xmm4 = src ga - // xmm5 = rb - // xmm6 = ga - // xmm2, xmm3 = used - // xmm0, xmm1, xmm7 = free - - if (m_sel.pabe) - { - psrld(xmm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - blend8r(xmm6, xmm4); - } - else - { - if (m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(xmm6, xmm4, xmm7); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame_SSE() -{ - if (!m_sel.fwrite) - { - return; - } - - if (m_sel.fpsm == 2 && m_sel.dthe) - { - mov(eax, ptr[esp + _top]); - and(eax, 3); - shl(eax, 5); - mov(ebp, ptr[&m_local.gd->dimx]); - paddw(xmm5, ptr[ebp + eax + sizeof(GSVector4i) * 0]); - paddw(xmm6, ptr[ebp + eax + sizeof(GSVector4i) * 1]); - } - - if (m_sel.colclamp == 0) - { - // c[0] &= 0x000000ff; - // c[1] &= 0x000000ff; - - pcmpeqd(xmm7, xmm7); - psrlw(xmm7, 8); - pand(xmm5, xmm7); - pand(xmm6, xmm7); - } - - // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - movdqa(xmm7, xmm5); - punpcklwd(xmm5, xmm6); - punpckhwd(xmm7, xmm6); - packuswb(xmm5, xmm7); - - if (m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - pcmpeqd(xmm7, xmm7); - pslld(xmm7, 31); - por(xmm5, xmm7); - } - - if (m_sel.fpsm == 2) - { - // GSVector4i rb = fs & 0x00f800f8; - // GSVector4i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - movd(xmm6, eax); - pshufd(xmm6, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - - mov(eax, 0x8000f800); - movd(xmm7, eax); - pshufd(xmm7, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - - movdqa(xmm4, xmm5); - pand(xmm4, xmm6); - pand(xmm5, xmm7); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - movdqa(xmm6, xmm4); - movdqa(xmm7, xmm5); - - psrld(xmm4, 3); - psrld(xmm6, 9); - psrld(xmm5, 6); - psrld(xmm7, 16); - - por(xmm5, xmm4); - por(xmm7, xmm6); - por(xmm5, xmm7); - } - - if (m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(xmm5, xmm2, xmm3); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb ? m_sel.fpsm < 2 : m_sel.fpsm == 0 && m_sel.notest; - - WritePixel_SSE(xmm5, ebx, dl, fast, m_sel.fpsm, 0); -} - -void GSDrawScanlineCodeGenerator::ReadPixel_SSE(const Xmm& dst, const Reg32& addr) -{ - movq(dst, qword[addr * 2 + (size_t)m_local.gd->vm]); - movhps(dst, qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2]); -} - -void GSDrawScanlineCodeGenerator::WritePixel_SSE(const Xmm& src, const Reg32& addr, const Reg8& mask, bool fast, int psm, int fz) -{ - if (m_sel.notest) - { - if (fast) - { - movq(qword[addr * 2 + (size_t)m_local.gd->vm], src); - movhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src); - } - else - { - WritePixel_SSE(src, addr, 0, psm); - WritePixel_SSE(src, addr, 1, psm); - WritePixel_SSE(src, addr, 2, psm); - WritePixel_SSE(src, addr, 3, psm); - } - } - else - { - if (fast) - { - // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); - // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); - - test(mask, 0x0f); - je("@f"); - movq(qword[addr * 2 + (size_t)m_local.gd->vm], src); - L("@@"); - - test(mask, 0xf0); - je("@f"); - movhps(qword[addr * 2 + (size_t)m_local.gd->vm + 8 * 2], src); - L("@@"); - } - else - { - // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); - // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); - // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); - // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); - - test(mask, 0x03); - je("@f"); - WritePixel_SSE(src, addr, 0, psm); - L("@@"); - - test(mask, 0x0c); - je("@f"); - WritePixel_SSE(src, addr, 1, psm); - L("@@"); - - test(mask, 0x30); - je("@f"); - WritePixel_SSE(src, addr, 2, psm); - L("@@"); - - test(mask, 0xc0); - je("@f"); - WritePixel_SSE(src, addr, 3, psm); - L("@@"); - } - } -} - -static const int s_offsets[4] = {0, 2, 8, 10}; - -void GSDrawScanlineCodeGenerator::WritePixel_SSE(const Xmm& src, const Reg32& addr, uint8 i, int psm) -{ - Address dst = ptr[addr * 2 + (size_t)m_local.gd->vm + s_offsets[i] * 2]; - - switch (psm) - { - case 0: - if (i == 0) - movd(dst, src); - else - { - pextrd(dst, src, i); - } - break; - case 1: - if (i == 0) - movd(eax, src); - else - { - pextrd(eax, src, i); - } - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - if (i == 0) - movd(eax, src); - else - pextrw(eax, src, i * 2); - mov(dst, ax); - break; - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_SSE(int pixels, int mip_offset) -{ - // in - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // ebx = m_local.tex[0] (!m_sel.mmin) - // ebp = m_local.tex (m_sel.mmin) - // edx = m_local.clut (m_sel.tlu) - - // out - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - - ASSERT(pixels == 1 || pixels == 4); - - mip_offset *= sizeof(void*); - - const GSVector4i* lod_i = m_sel.lcm ? &m_local.gd->lod.i : &m_local.temp.lod.i; - - if (m_sel.mmin && !m_sel.lcm) - { - const int r[] = {5, 6, 2, 4, 0, 1, 3, 7}; - - if (pixels == 4) - { - movdqa(ptr[&m_local.temp.test], xmm7); - } - - for (uint8 j = 0; j < 4; j++) - { - mov(ebx, ptr[&lod_i->u32[j]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - - for (int i = 0; i < pixels; i++) - { - ReadTexel_SSE(Xmm(r[i * 2 + 1]), Xmm(r[i * 2 + 0]), j); - } - } - - if (pixels == 4) - { - movdqa(xmm5, xmm7); - movdqa(xmm7, ptr[&m_local.temp.test]); - } - } - else - { - if (m_sel.mmin && m_sel.lcm) - { - mov(ebx, ptr[&lod_i->u32[0]]); - mov(ebx, ptr[ebp + ebx * sizeof(void*) + mip_offset]); - } - - const int r[] = {5, 6, 2, 4, 0, 1, 3, 5}; - - for (int i = 0; i < pixels; i++) - { - for (uint8 j = 0; j < 4; j++) - { - ReadTexel_SSE(Xmm(r[i * 2 + 1]), Xmm(r[i * 2 + 0]), j); - } - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel_SSE(const Xmm& dst, const Xmm& addr, uint8 i) -{ - const Address& src = m_sel.tlu ? ptr[edx + eax * 4] : ptr[ebx + eax * 4]; - - ASSERT(i == 0 || m_cpu.has(util::Cpu::tSSE41)); - - if (i == 0) - movd(eax, addr); - else - pextrd(eax, addr, i); - - if (m_sel.tlu) - movzx(eax, byte[ebx + eax]); - - if (i == 0) - movd(dst, src); - else - pinsrd(dst, src, i); -} - -#endif diff --git a/pcsx2/GS/GS_codegen.h b/pcsx2/GS/Renderers/SW/GSNewCodeGenerator.cpp similarity index 69% rename from pcsx2/GS/GS_codegen.h rename to pcsx2/GS/Renderers/SW/GSNewCodeGenerator.cpp index 7ca1b4b847d00..5e0efb5a015db 100644 --- a/pcsx2/GS/GS_codegen.h +++ b/pcsx2/GS/Renderers/SW/GSNewCodeGenerator.cpp @@ -13,26 +13,5 @@ * If not, see . */ -#pragma once - -using namespace Xbyak; - -#ifdef _M_AMD64 -// Yeah let use mips naming ;) - #ifdef _WIN64 - #define a0 rcx - #define a1 rdx - #define a2 r8 - #define a3 r9 - #define t0 rdi - #define t1 rsi - #else - #define a0 rdi - #define a1 rsi - #define a2 rdx - #define a3 rcx - #define t0 r8 - #define t1 r9 - #endif -#endif - +#include "PrecompiledHeader.h" +#include "GSNewCodeGenerator.h" diff --git a/pcsx2/GS/Renderers/SW/GSNewCodeGenerator.h b/pcsx2/GS/Renderers/SW/GSNewCodeGenerator.h new file mode 100644 index 0000000000000..7ee0e042670b1 --- /dev/null +++ b/pcsx2/GS/Renderers/SW/GSNewCodeGenerator.h @@ -0,0 +1,489 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "GS/GS_types.h" +#include "xbyak/xbyak.h" +#include "xbyak/xbyak_util.h" + +namespace SSEVersion +{ + enum SSEVersion + { + AVX2 = 0x501, + AVX = 0x500, + SSE41 = 0x401, + }; +} + +/// Similar to Xbyak::util::cpu but more open to us putting in extra flags (e.g. "vpgatherdd is fast"), as well as making it easier to test other configurations by artifically limiting features +struct CPUInfo +{ + bool hasFMA = false; + SSEVersion::SSEVersion sseVersion = SSEVersion::SSE41; + + CPUInfo() = default; + CPUInfo(const Xbyak::util::Cpu& cpu) + { + auto version = SSEVersion::SSE41; + if (cpu.has(cpu.tAVX)) + version = SSEVersion::AVX; + if (cpu.has(cpu.tAVX2)) + version = SSEVersion::AVX2; + + hasFMA = cpu.has(cpu.tFMA); + sseVersion = version; + } +}; + +/// Code generator that automatically selects between SSE and AVX, x86 and x64 so you don't have to +/// Should make combined SSE and AVX codegen much easier +class GSNewCodeGenerator +{ +public: + using Address = Xbyak::Address; + using Label = Xbyak::Label; + using Operand = Xbyak::Operand; + using Reg32e = Xbyak::Reg32e; + using Reg32 = Xbyak::Reg32; + using Reg16 = Xbyak::Reg16; + using Reg8 = Xbyak::Reg8; + using Reg = Xbyak::Reg; + using Xmm = Xbyak::Xmm; + using Ymm = Xbyak::Ymm; + using Zmm = Xbyak::Zmm; + + class Error : public std::exception + { + public: + enum Value + { + ERR_64_BIT_REG_IN_32, + ERR_64_INSTR_IN_32, + ERR_SSE_INSTR_IN_AVX, + ERR_AVX_INSTR_IN_SSE, + }; + + Value value; + + Error(Value value) : value(value) {} + + const char* what() const noexcept + { + static const char* tbl[] = { + "used 64-bit register in 32-bit code", + "used 64-bit only instruction in 32-bit code", + "used SSE instruction in AVX code", + "used AVX instruction in SSE code", + }; + if (static_cast(value) < (sizeof(tbl) / sizeof(*tbl))) + { + return tbl[value]; + } + else + { + return "GSNewCodeGenerator Unknown Error"; + } + } + }; + +private: + /// Make sure the register is okay to use + void validateRegister(const Operand& op) + { + if (is64) + return; + if (op.isREG() && (op.isExtIdx() || op.isExt8bit())) + throw Error(Error::ERR_64_BIT_REG_IN_32); + if (op.isMEM()) + { + auto e = static_cast(op).getRegExp(); + validateRegister(e.getIndex()); + validateRegister(e.getBase()); + } + } + /// For easier macro-ing + void validateRegister(int imm) + { + } + + void require64() + { + if (!is64) + throw Error(Error::ERR_64_INSTR_IN_32); + } + void requireAVX() + { + if (!hasAVX) + throw Error(Error::ERR_AVX_INSTR_IN_SSE); + } + +public: + Xbyak::CodeGenerator& actual; + +#if defined(_M_X86_64) + constexpr static bool is32 = false; + constexpr static bool is64 = true; + using AddressReg = Xbyak::Reg64; + using RipType = Xbyak::RegRip; + + template + struct Choose3264 { using type = T64; }; + + template + static T64 choose3264(T32 t32, T64 t64) { return t64; } +#else + constexpr static bool is32 = true; + constexpr static bool is64 = false; + using AddressReg = Xbyak::Reg32; + using RipType = int; + + template + struct Choose3264 { using type = T32; }; + + template + static T32 choose3264(T32 t32, T64 t64) { return t32; } +#endif + + const bool hasAVX, hasAVX2, hasFMA; + + const Xmm xmm0{0}, xmm1{1}, xmm2{2}, xmm3{3}, xmm4{4}, xmm5{5}, xmm6{6}, xmm7{7}, xmm8{8}, xmm9{9}, xmm10{10}, xmm11{11}, xmm12{12}, xmm13{13}, xmm14{14}, xmm15{15}; + const Ymm ymm0{0}, ymm1{1}, ymm2{2}, ymm3{3}, ymm4{4}, ymm5{5}, ymm6{6}, ymm7{7}, ymm8{8}, ymm9{9}, ymm10{10}, ymm11{11}, ymm12{12}, ymm13{13}, ymm14{14}, ymm15{15}; + const AddressReg rax{0}, rcx{1}, rdx{2}, rbx{3}, rsp{4}, rbp{5}, rsi{6}, rdi{7}, r8{8}, r9{9}, r10{10}, r11{11}, r12{12}, r13{13}, r14{14}, r15{15}; + const Reg32 eax{0}, ecx{1}, edx{2}, ebx{3}, esp{4}, ebp{5}, esi{6}, edi{7}, r8d{8}, r9d{9}, r10d{10}, r11d{11}, r12d{12}, r13d{13}, r14d{14}, r15d{15}; + const Reg16 ax{0}, cx{1}, dx{2}, bx{3}, sp{4}, bp{5}, si{6}, di{7}; + const Reg8 al{0}, cl{1}, dl{2}, bl{3}, ah{4}, ch{5}, dh{6}, bh{7}; + + const RipType rip{}; + const Xbyak::AddressFrame ptr{0}, byte{8}, word{16}, dword{32}, qword{64}, xword{128}, yword{256}, zword{512}; + + GSNewCodeGenerator(Xbyak::CodeGenerator* actual, CPUInfo cpu) + : actual(*actual) + , hasAVX(cpu.sseVersion >= SSEVersion::AVX) + , hasAVX2(cpu.sseVersion >= SSEVersion::AVX2) + , hasFMA(cpu.hasFMA) + { + } + + +// ------------ Forwarding instructions ------------ +// Note: Only instructions used by codegen were added here, so if you're modifying codegen, you may need to add instructions here + +// For instructions available in SSE and AVX, functions with the SSE name and arguments that forward to SSE or AVX depending on the target, as well as functions with the AVX name and arguments that forward to the AVX version or assert on SSE + +// ARGS_* macros are provided for shorter argument lists. The following single-letter abbreviations are used: X=Xmm, Y=Ymm, O=Operand, A=Address, I=Immediate +// FORWARD(argcount, category, instrname, argtypes...) forwards an instruction. The following categories are available: +// BASE: non-SSE +// SSE: available on SSE and v-prefixed on AVX +// SSEONLY: available only on SSE (exception on AVX) +// AVX: available only on AVX (exception on SSE) +// AVX2: available only on AVX2 (exception on AVX/SSE) +// FMA: available only with FMA +// SFORWARD forwards an SSE-AVX pair where the AVX variant takes the same number of registers (e.g. pshufd dst, src + vpshufd dst, src) +// AFORWARD forwards an SSE-AVX pair where the AVX variant takes an extra destination register (e.g. shufps dst, src + vshufps dst, src, src) + +// Implementation details: +// ACTUAL_FORWARD_*: Actually forward the function of the given type +// FORWARD#: First validates the arguments (e.g. make sure you're not passing registers over 7 on x86), then forwards to an ACTUAL_FORWARD_* + +// Big thanks to https://stackoverflow.com/a/24028231 for helping me figure out how to work around MSVC's terrible macro expander +// Of course GCC/Clang don't like the workaround so enjoy the ifdefs +#define EXPAND_ARGS(macro, args) macro args + +#define ACTUAL_FORWARD_BASE(name, ...) \ + actual.name(__VA_ARGS__); + +#define ACTUAL_FORWARD_SSE(name, ...) \ + if (hasAVX) \ + actual.v##name(__VA_ARGS__); \ + else \ + actual.name(__VA_ARGS__); + +#define ACTUAL_FORWARD_SSEONLY(name, ...) \ + if (hasAVX) \ + throw Error(Error::ERR_SSE_INSTR_IN_AVX); \ + else \ + actual.name(__VA_ARGS__); + +#define ACTUAL_FORWARD_AVX(name, ...) \ + if (hasAVX) \ + actual.name(__VA_ARGS__); \ + else \ + throw Error(Error::ERR_AVX_INSTR_IN_SSE); + +#define ACTUAL_FORWARD_AVX2(name, ...) \ + if (hasAVX2) \ + actual.name(__VA_ARGS__); \ + else \ + throw Error(Error::ERR_AVX_INSTR_IN_SSE); + +#define ACTUAL_FORWARD_FMA(name, ...) \ + if (hasFMA) \ + actual.name(__VA_ARGS__); \ + else \ + throw Error(Error::ERR_AVX_INSTR_IN_SSE); + +#define FORWARD1(category, name, type) \ + void name(type a) \ + { \ + validateRegister(a); \ + ACTUAL_FORWARD_##category(name, a) \ + } + +#define FORWARD2(category, name, type1, type2) \ + void name(type1 a, type2 b) \ + { \ + validateRegister(a); \ + validateRegister(b); \ + ACTUAL_FORWARD_##category(name, a, b) \ + } + +#define FORWARD3(category, name, type1, type2, type3) \ + void name(type1 a, type2 b, type3 c) \ + { \ + validateRegister(a); \ + validateRegister(b); \ + validateRegister(c); \ + ACTUAL_FORWARD_##category(name, a, b, c) \ + } + +#define FORWARD4(category, name, type1, type2, type3, type4) \ + void name(type1 a, type2 b, type3 c, type4 d) \ + { \ + validateRegister(a); \ + validateRegister(b); \ + validateRegister(c); \ + validateRegister(d); \ + ACTUAL_FORWARD_##category(name, a, b, c, d) \ + } + +#ifdef __GNUC__ + #define FORWARD_(argcount, ...) FORWARD##argcount(__VA_ARGS__) + // Gets the macro evaluator to evaluate in the right order + #define FORWARD(...) FORWARD_(__VA_ARGS__) +#else + #define FORWARD_(argcount, ...) EXPAND_ARGS(FORWARD##argcount, (__VA_ARGS__)) + // Gets the macro evaluator to evaluate in the right order + #define FORWARD(...) EXPAND_ARGS(FORWARD_, (__VA_ARGS__)) +#endif + +#define FORWARD_SSE_XMM0(name) \ + void name(const Xmm& a, const Operand& b) \ + { \ + validateRegister(a); \ + validateRegister(b); \ + if (hasAVX) \ + actual.v##name(a, b, Xmm(0)); \ + else \ + actual.name(a, b); \ + } \ + FORWARD(4, AVX, v##name, const Xmm&, const Xmm&, const Operand&, const Xmm&) + +#define FORWARD_JUMP(name) \ + void name(const void *addr) { actual.name(addr); } \ + void name(const Label& label, Xbyak::CodeGenerator::LabelType type = Xbyak::CodeGenerator::T_AUTO) { actual.name(label, type); } \ + void name(const char *label, Xbyak::CodeGenerator::LabelType type = Xbyak::CodeGenerator::T_AUTO) { actual.name(label, type); } + +#define ADD_ONE_2 3 +#define ADD_ONE_3 4 + +#ifdef __GNUC__ + #define SFORWARD(argcount, name, ...) FORWARD(argcount, SSE, name, __VA_ARGS__) + #define AFORWARD_(argcount, name, arg1, ...) \ + SFORWARD(argcount, name, arg1, __VA_ARGS__) \ + FORWARD(ADD_ONE_##argcount, AVX, v##name, arg1, arg1, __VA_ARGS__) + // Gets the macro evaluator to evaluate in the right order + #define AFORWARD(...) EXPAND_ARGS(AFORWARD_, (__VA_ARGS__)) +#else + #define SFORWARD(argcount, name, ...) EXPAND_ARGS(FORWARD, (argcount, SSE, name, __VA_ARGS__)) + #define AFORWARD_(argcount, name, arg1, ...) \ + EXPAND_ARGS(SFORWARD, (argcount, name, arg1, __VA_ARGS__)) \ + EXPAND_ARGS(FORWARD, (ADD_ONE_##argcount, AVX, v##name, arg1, arg1, __VA_ARGS__)) + // Gets the macro evaluator to evaluate in the right order + #define AFORWARD(...) EXPAND_ARGS(AFORWARD_, (__VA_ARGS__)) +#endif + +#define FORWARD_OO_OI(name) \ + FORWARD(2, BASE, name, ARGS_OO) \ + FORWARD(2, BASE, name, ARGS_OI) + +#define ARGS_OI const Operand&, uint32 +#define ARGS_OO const Operand&, const Operand& +#define ARGS_XI const Xmm&, int +#define ARGS_XO const Xmm&, const Operand& +#define ARGS_XOI const Xmm&, const Operand&, uint8 +#define ARGS_XXO const Xmm&, const Xmm&, const Operand& + +// For instructions that are ifdef'd out without XBYAK64 +#ifdef XBYAK64 + #define REQUIRE64(action) require64(); action +#else + #define REQUIRE64(action) require64() +#endif + + const uint8 *getCurr() { return actual.getCurr(); } + void align(int x = 16) { return actual.align(x); } + void db(int code) { actual.db(code); } + void L(const std::string& label) { actual.L(label); } + + void cdqe() { REQUIRE64(actual.cdqe()); } + void ret(int imm = 0) { actual.ret(imm); } + void vzeroupper() { requireAVX(); actual.vzeroupper(); } + void vzeroall() { requireAVX(); actual.vzeroall(); } + + FORWARD_OO_OI(add) + FORWARD_OO_OI(and) + FORWARD_OO_OI(cmp) + FORWARD_OO_OI(or) + FORWARD_OO_OI(sub) + FORWARD_OO_OI(xor) + FORWARD(2, BASE, lea, const Reg&, const Address&) + FORWARD(2, BASE, mov, const Operand&, size_t) + FORWARD(2, BASE, mov, ARGS_OO) + FORWARD(2, BASE, movzx, const Reg&, const Operand&) + FORWARD(1, BASE, not, const Operand&) + FORWARD(1, BASE, pop, const Operand&) + FORWARD(1, BASE, push, const Operand&) + FORWARD(2, BASE, sar, const Operand&, const Reg8&) + FORWARD(2, BASE, sar, ARGS_OI) + FORWARD(2, BASE, shl, const Operand&, const Reg8&) + FORWARD(2, BASE, shl, ARGS_OI) + FORWARD(2, BASE, shr, const Operand&, const Reg8&) + FORWARD(2, BASE, shr, ARGS_OI) + FORWARD(2, BASE, test, const Operand&, const Reg&); + FORWARD(2, BASE, test, ARGS_OI); + + FORWARD_JUMP(je) + FORWARD_JUMP(jle) + FORWARD_JUMP(jmp) + + AFORWARD(2, addps, ARGS_XO) + SFORWARD(2, cvtdq2ps, ARGS_XO) + SFORWARD(2, cvtps2dq, ARGS_XO) + SFORWARD(2, cvttps2dq, ARGS_XO) + SFORWARD(3, extractps, const Operand&, const Xmm&, uint8) + AFORWARD(2, maxps, ARGS_XO) + AFORWARD(2, minps, ARGS_XO) + SFORWARD(2, movaps, ARGS_XO) + SFORWARD(2, movaps, const Address&, const Xmm&) + SFORWARD(2, movd, const Address&, const Xmm&) + SFORWARD(2, movd, const Reg32&, const Xmm&) + SFORWARD(2, movd, const Xmm&, const Address&) + SFORWARD(2, movd, const Xmm&, const Reg32&) + SFORWARD(2, movdqa, ARGS_XO) + SFORWARD(2, movdqa, const Address&, const Xmm&) + SFORWARD(2, movhps, ARGS_XO) + SFORWARD(2, movhps, const Address&, const Xmm&) + SFORWARD(2, movq, const Address&, const Xmm&) + SFORWARD(2, movq, const Xmm&, const Address&) + AFORWARD(2, mulps, ARGS_XO) + AFORWARD(2, orps, ARGS_XO) + AFORWARD(2, packssdw, ARGS_XO) + AFORWARD(2, packusdw, ARGS_XO) + AFORWARD(2, packuswb, ARGS_XO) + AFORWARD(2, paddd, ARGS_XO) + AFORWARD(2, paddusb, ARGS_XO) + AFORWARD(2, paddw, ARGS_XO) + AFORWARD(2, pand, ARGS_XO) + AFORWARD(2, pandn, ARGS_XO) + AFORWARD(3, pblendw, ARGS_XOI) + AFORWARD(2, pcmpeqd, ARGS_XO) + AFORWARD(2, pcmpeqw, ARGS_XO) + AFORWARD(2, pcmpgtd, ARGS_XO) + SFORWARD(3, pextrd, const Operand&, const Xmm&, uint8) + SFORWARD(3, pextrw, const Operand&, const Xmm&, uint8) + AFORWARD(3, pinsrd, ARGS_XOI) + AFORWARD(2, pmaxsw, ARGS_XO) + AFORWARD(2, pminsd, ARGS_XO) + AFORWARD(2, pminsw, ARGS_XO) + SFORWARD(2, pmovsxbd, ARGS_XO) + SFORWARD(2, pmovmskb, const Reg32e&, const Xmm&) + SFORWARD(2, pmovzxbw, ARGS_XO) + AFORWARD(2, pmulhrsw, ARGS_XO) + AFORWARD(2, pmulhw, ARGS_XO) + AFORWARD(2, pmullw, ARGS_XO) + AFORWARD(2, por, ARGS_XO) + SFORWARD(3, pshufd, ARGS_XOI) + SFORWARD(3, pshufhw, ARGS_XOI) + SFORWARD(3, pshuflw, ARGS_XOI) + AFORWARD(2, pslld, ARGS_XI) + AFORWARD(2, psllw, ARGS_XI) + AFORWARD(2, psrad, ARGS_XI) + AFORWARD(2, psrad, ARGS_XO) + AFORWARD(2, psraw, ARGS_XI) + AFORWARD(2, psrld, ARGS_XI) + AFORWARD(2, psrldq, ARGS_XI) + AFORWARD(2, psrlw, ARGS_XI) + AFORWARD(2, psrlw, ARGS_XO) + AFORWARD(2, psubd, ARGS_XO) + AFORWARD(2, psubw, ARGS_XO) + AFORWARD(2, punpckhdq, ARGS_XO) + AFORWARD(2, punpckhwd, ARGS_XO) + AFORWARD(2, punpcklbw, ARGS_XO) + AFORWARD(2, punpckldq, ARGS_XO) + AFORWARD(2, punpcklqdq,ARGS_XO) + AFORWARD(2, punpcklwd, ARGS_XO) + AFORWARD(2, pxor, ARGS_XO) + SFORWARD(2, rcpps, ARGS_XO) + AFORWARD(3, shufps, ARGS_XOI) + AFORWARD(2, subps, ARGS_XO) + AFORWARD(2, xorps, ARGS_XO) + + FORWARD_SSE_XMM0(pblendvb) + + FORWARD(2, AVX, vbroadcastss, ARGS_XO) + FORWARD(2, AVX2, vbroadcasti128, const Ymm&, const Address&) + FORWARD(2, AVX, vbroadcastf128, const Ymm&, const Address&) + FORWARD(3, FMA, vfmadd213ps, ARGS_XXO) + FORWARD(3, AVX2, vextracti128, const Operand&, const Ymm&, uint8) + FORWARD(4, AVX2, vinserti128, const Ymm&, const Ymm&, const Operand&, uint8); + FORWARD(2, AVX2, vpbroadcastd, ARGS_XO) + FORWARD(2, AVX2, vpbroadcastq, ARGS_XO) + FORWARD(2, AVX2, vpbroadcastw, ARGS_XO) + FORWARD(3, AVX2, vpermq, const Ymm&, const Operand&, uint8) + FORWARD(3, AVX2, vpgatherdd, const Xmm&, const Address&, const Xmm&); + FORWARD(3, AVX2, vpsravd, ARGS_XXO) + FORWARD(3, AVX2, vpsrlvd, ARGS_XXO) + +#undef REQUIRE64 +#undef ARGS_OI +#undef ARGS_OO +#undef ARGS_XI +#undef ARGS_XO +#undef ARGS_XOI +#undef ARGS_XXO +#undef FORWARD_OO_OI +#undef AFORWARD +#undef AFORWARD_ +#undef SFORWARD +#undef ADD_ONE_2 +#undef ADD_ONE_3 +#undef FORWARD_SSE_XMM0 +#undef FORWARD_JUMP +#undef FORWARD +#undef FORWARD_ +#undef FORWARD4 +#undef FORWARD3 +#undef FORWARD2 +#undef FORWARD1 +#undef ACTUAL_FORWARD_FMA +#undef ACTUAL_FORWARD_AVX2 +#undef ACTUAL_FORWARD_AVX +#undef ACTUAL_FORWARD_SSE +#undef ACTUAL_FORWARD_SSEONLY +#undef ACTUAL_FORWARD_BASE +#undef EXPAND_ARGS +}; diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.cpp new file mode 100644 index 0000000000000..b6468137322e9 --- /dev/null +++ b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.cpp @@ -0,0 +1,566 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "GS/GS_types.h" +#include "GSSetupPrimCodeGenerator.all.h" +#include "GSVertexSW.h" + +using namespace Xbyak; + +#define _rip_local(field) ((is32 || m_rip) ? ptr[rip + (char*)&m_local.field] : ptr[_m_local + OFFSETOF(GSScanlineLocalData, field)]) + +#define _64_m_local _64_t0 + +/// On AVX, does a v-prefixed separate destination operation +/// On SSE, moves src1 into dst using movdqa, then does the operation +#define THREEARG(operation, dst, src1, ...) \ + do \ + { \ + if (hasAVX) \ + { \ + v##operation(dst, src1, __VA_ARGS__); \ + } \ + else \ + { \ + movdqa(dst, src1); \ + operation(dst, __VA_ARGS__); \ + } \ + } while (0) + +#if _M_SSE >= 0x501 + #define _rip_local_d(x) _rip_local(d8.x) + #define _rip_local_d_p(x) _rip_local_d(p.x) +#else + #define _rip_local_d(x) _rip_local(d4.x) + #define _rip_local_d_p(x) _rip_local_d(x) +#endif + +GSSetupPrimCodeGenerator2::GSSetupPrimCodeGenerator2(Xbyak::CodeGenerator* base, CPUInfo cpu, void* param, uint64 key) + : _parent(base, cpu) + , m_local(*(GSScanlineLocalData*)param) + , m_rip(false), many_regs(false) + // On x86 arg registers are very temporary but on x64 they aren't, so on x86 some registers overlap +#ifdef _WIN32 + , _64_vertex(is64 ? rcx : r8) + , _index(is64 ? rdx : rcx) + , _dscan(is64 ? r8 : rdx) + , _64_t0(r9), t1(is64 ? r10 : rcx) +#else + , _64_vertex(is64 ? rdi : r8) + , _index(is64 ? rsi : rcx) + , _dscan(rdx) + , _64_t0(is64 ? rcx : r8), t1(is64 ? r8 : rcx) +#endif + , _m_local(chooseLocal(&m_local, _64_m_local)) +{ + m_sel.key = key; + + m_en.z = m_sel.zb ? 1 : 0; + m_en.f = m_sel.fb && m_sel.fge ? 1 : 0; + m_en.t = m_sel.fb && m_sel.tfx != TFX_NONE ? 1 : 0; + m_en.c = m_sel.fb && !(m_sel.tfx == TFX_DECAL && m_sel.tcc) ? 1 : 0; +} + +void GSSetupPrimCodeGenerator2::broadcastf128(const XYm& reg, const Address& mem) +{ +#if SETUP_PRIM_USING_YMM + vbroadcastf128(reg, mem); +#else + movaps(reg, mem); +#endif +} + +void GSSetupPrimCodeGenerator2::Generate() +{ + // Technically we just need the delta < 2GB + m_rip = (size_t)&m_local < 0x80000000 && (size_t)getCurr() < 0x80000000; + + bool needs_shift = (m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip; + many_regs = is64 && isYmm && !m_sel.notest && needs_shift; + +#ifdef _WIN64 + int needs_saving = many_regs ? 6 : m_sel.notest ? 0 : 2; + if (needs_saving) + { + sub(rsp, 8 + 16 * needs_saving); + for (int i = 0; i < needs_saving; i++) + { + movdqa(ptr[rsp + i * 16], Xmm(i + 6)); + } + } +#endif + + if (is64 && !m_rip) + mov(_64_m_local, (size_t)&m_local); + + if (needs_shift) + { + if (is32) + mov(_dscan, ptr[rsp + _32_dscan]); + + if (isXmm) + mov(rax, (size_t)g_const->m_shift_128b); + else + mov(rax, (size_t)g_const->m_shift_256b); + + for (int i = 0; i < (m_sel.notest ? 2 : many_regs ? 9 : 5); i++) + { + movaps(XYm(3 + i), ptr[rax + i * vecsize]); + } + } + + if (isXmm) + Depth_XMM(); + else + Depth_YMM(); + + Texture(); + + Color(); + +#ifdef _WIN64 + if (needs_saving) + { + for (int i = 0; i < needs_saving; i++) + { + movdqa(Xmm(i + 6), ptr[rsp + i * 16]); + } + add(rsp, 8 + 16 * needs_saving); + } +#endif + if (isYmm) + vzeroupper(); + ret(); +} + +void GSSetupPrimCodeGenerator2::Depth_XMM() +{ + if (!m_en.z && !m_en.f) + { + return; + } + + if (m_sel.prim != GS_SPRITE_CLASS) + { + // GSVector4 p = dscan.p; + + + movaps(xmm0, ptr[_dscan + offsetof(GSVertexSW, p)]); + + if (m_en.f) + { + // GSVector4 df = p.wwww(); + + THREEARG(shufps, xmm1, xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); + + // m_local.d4.f = GSVector4i(df * 4.0f).xxzzlh(); + + THREEARG(mulps, xmm2, xmm1, xmm3); + cvttps2dq(xmm2, xmm2); + pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(_rip_local_d_p(f), xmm2); + + for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) + { + // m_local.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); + + THREEARG(mulps, xmm2, xmm1, XYm(4 + i)); + cvttps2dq(xmm2, xmm2); + pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(_rip_local(d[i].f), xmm2); + } + } + + if (m_en.z) + { + // GSVector4 dz = p.zzzz(); + + shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); + + // m_local.d4.z = dz * 4.0f; + + THREEARG(mulps, xmm1, xmm0, xmm3); + movdqa(_rip_local_d_p(z), xmm1); + + for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) + { + // m_local.d[i].z = dz * m_shift[i]; + + THREEARG(mulps, xmm1, xmm0, XYm(4 + i)); + movdqa(_rip_local(d[i].z), xmm1); + } + } + } + else + { + // GSVector4 p = vertex[index[1]].p; + + if (is32) + mov(_index, ptr[rsp + _32_index]); + mov(eax, ptr[_index + sizeof(uint32) * 1]); + shl(eax, 6); // * sizeof(GSVertexSW) + if (is64) + add(rax, _64_vertex); + else + add(rax, ptr[rsp + _32_vertex]); + + if (m_en.f) + { + // m_local.p.f = GSVector4i(p).zzzzh().zzzz(); + movaps(xmm0, ptr[rax + offsetof(GSVertexSW, p)]); + + cvttps2dq(xmm1, xmm0); + pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + movdqa(_rip_local(p.f), xmm1); + } + + if (m_en.z) + { + // uint32 z is bypassed in t.w + + movdqa(xmm0, ptr[rax + offsetof(GSVertexSW, t)]); + pshufd(xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); + movdqa(_rip_local(p.z), xmm0); + } + } +} + +void GSSetupPrimCodeGenerator2::Depth_YMM() +{ + if (!m_en.z && !m_en.f) + { + return; + } + + if (m_sel.prim != GS_SPRITE_CLASS) + { + // GSVector4 dp8 = dscan.p * GSVector4::broadcast32(&shift[0]); + + broadcastf128(xym0, ptr[_dscan + offsetof(GSVertexSW, p)]); + + vmulps(ymm1, ymm0, ymm3); + + if (m_en.z) + { + // m_local.d8.p.z = dp8.extract32<2>(); + + extractps(_rip_local_d_p(z), xmm1, 2); + + // GSVector8 dz = GSVector8(dscan.p).zzzz(); + + vshufps(ymm2, ymm0, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); + } + + if (m_en.f) + { + // m_local.d8.p.f = GSVector4i(dp8).extract32<3>(); + + cvtps2dq(ymm1, ymm1); + pextrd(_rip_local_d_p(f), xmm1, 3); + + // GSVector8 df = GSVector8(dscan.p).wwww(); + + vshufps(ymm1, ymm0, ymm0, _MM_SHUFFLE(3, 3, 3, 3)); + } + + for (int i = 0; i < (m_sel.notest ? 1 : dsize); i++) + { + if (m_en.z) + { + // m_local.d[i].z = dz * shift[1 + i]; + + // Save a byte in the encoding for ymm8-11 by swapping with ymm2 (multiplication is communative) + if (i < 4 || many_regs) + vmulps(ymm0, Ymm(4 + i), ymm2); + else + vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); + movaps(_rip_local(d[i].z), ymm0); + } + + if (m_en.f) + { + // m_local.d[i].f = GSVector8i(df * m_shift[i]).xxzzlh(); + + if (i < 4 || many_regs) + vmulps(ymm0, Ymm(4 + i), ymm1); + else + vmulps(ymm0, ymm1, ptr[g_const->m_shift_256b[i + 1]]); + cvttps2dq(ymm0, ymm0); + pshuflw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(_rip_local(d[i].f), ymm0); + } + } + } + else + { + // GSVector4 p = vertex[index[1]].p; + + if (is32) + mov(_index, ptr[rsp + _32_index]); + mov(eax, ptr[_index + sizeof(uint32) * 1]); + shl(eax, 6); // * sizeof(GSVertexSW) + if (is64) + add(rax, _64_vertex); + else + add(rax, ptr[rsp + _32_vertex]); + + if (m_en.f) + { + // m_local.p.f = GSVector4i(vertex[index[1]].p).extract32<3>(); + + movaps(xmm0, ptr[rax + offsetof(GSVertexSW, p)]); + cvttps2dq(xmm0, xmm0); + pextrd(_rip_local(p.f), xmm0, 3); + } + + if (m_en.z) + { + // m_local.p.z = vertex[index[1]].t.u32[3]; // uint32 z is bypassed in t.w + + mov(t1.cvt32(), ptr[rax + offsetof(GSVertexSW, t.w)]); + mov(_rip_local(p.z), t1.cvt32()); + } + } +} + +void GSSetupPrimCodeGenerator2::Texture() +{ + if (!m_en.t) + { + return; + } + + // GSVector4 t = dscan.t; + + broadcastf128(xym0, ptr[_dscan + offsetof(GSVertexSW, t)]); + + THREEARG(mulps, xmm1, xmm0, xmm3); + + if (m_sel.fst) + { + // m_local.d4.stq = GSVector4i(t * 4.0f); + + cvttps2dq(xmm1, xmm1); + + movdqa(_rip_local_d(stq), xmm1); + } + else + { + // m_local.d4.stq = t * 4.0f; + + movaps(_rip_local_d(stq), xmm1); + } + + for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) + { + // GSVector4 ds = t.xxxx(); + // GSVector4 dt = t.yyyy(); + // GSVector4 dq = t.zzzz(); + + THREEARG(shufps, xym1, xym0, xym0, _MM_SHUFFLE(j, j, j, j)); + + for (int i = 0; i < (m_sel.notest ? 1 : dsize); i++) + { + // GSVector4 v = ds/dt * m_shift[i]; + + if (i < 4 || many_regs) + THREEARG(mulps, xym2, XYm(4 + i), xym1); + else + vmulps(ymm2, ymm1, ptr[g_const->m_shift_256b[i + 1]]); + + if (m_sel.fst) + { + // m_local.d[i].s/t = GSVector4i(v); + + cvttps2dq(xym2, xym2); + + switch (j) + { + case 0: movdqa(_rip_local(d[i].s), xym2); break; + case 1: movdqa(_rip_local(d[i].t), xym2); break; + } + } + else + { + // m_local.d[i].s/t/q = v; + + switch (j) + { + case 0: movaps(_rip_local(d[i].s), xym2); break; + case 1: movaps(_rip_local(d[i].t), xym2); break; + case 2: movaps(_rip_local(d[i].q), xym2); break; + } + } + } + } +} + +void GSSetupPrimCodeGenerator2::Color() +{ + if (!m_en.c) + { + return; + } + + if (m_sel.iip) + { + // GSVector4 c = dscan.c; + + broadcastf128(xym0, ptr[_dscan + offsetof(GSVertexSW, c)]); + + // m_local.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); + + THREEARG(mulps, xmm1, xmm0, xmm3); + cvttps2dq(xmm1, xmm1); + pshufd(xmm1, xmm1, _MM_SHUFFLE(3, 1, 2, 0)); + packssdw(xmm1, xmm1); + if (isXmm) + movdqa(_rip_local_d(c), xmm1); + else + movq(_rip_local_d(c), xmm1); + + // xym3 is not needed anymore + + // GSVector4 dr = c.xxxx(); + // GSVector4 db = c.zzzz(); + + THREEARG(shufps, xym2, xym0, xym0, _MM_SHUFFLE(0, 0, 0, 0)); + THREEARG(shufps, xym3, xym0, xym0, _MM_SHUFFLE(2, 2, 2, 2)); + + for (int i = 0; i < (m_sel.notest ? 1 : dsize); i++) + { + // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); + + if (i < 4 || many_regs) + THREEARG(mulps, xym0, XYm(4 + i), xym2); + else + vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); + cvttps2dq(xym0, xym0); + packssdw(xym0, xym0); + + // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); + + if (i < 4 || many_regs) + THREEARG(mulps, xym1, XYm(4 + i), xym3); + else + vmulps(ymm1, ymm3, ptr[g_const->m_shift_256b[i + 1]]); + cvttps2dq(xym1, xym1); + packssdw(xym1, xym1); + + // m_local.d[i].rb = r.upl16(b); + + punpcklwd(xym0, xym1); + movdqa(_rip_local(d[i].rb), xym0); + } + + // GSVector4 c = dscan.c; + + broadcastf128(xym0, ptr[_dscan + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it + + // GSVector4 dg = c.yyyy(); + // GSVector4 da = c.wwww(); + + THREEARG(shufps, xym2, xym0, xym0, _MM_SHUFFLE(1, 1, 1, 1)); + THREEARG(shufps, xym3, xym0, xym0, _MM_SHUFFLE(3, 3, 3, 3)); + + for (int i = 0; i < (m_sel.notest ? 1 : dsize); i++) + { + // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); + + if (i < 4 || many_regs) + THREEARG(mulps, xym0, XYm(4 + i), xym2); + else + vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); + cvttps2dq(xym0, xym0); + packssdw(xym0, xym0); + + // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); + + if (i < 4 || many_regs) + THREEARG(mulps, xym1, XYm(4 + i), xym3); + else + vmulps(ymm1, ymm3, ptr[g_const->m_shift_256b[i + 1]]); + cvttps2dq(xym1, xym1); + packssdw(xym1, xym1); + + // m_local.d[i].ga = g.upl16(a); + + punpcklwd(xym0, xym1); + movdqa(_rip_local(d[i].ga), xym0); + } + } + else + { + // GSVector4i c = GSVector4i(vertex[index[last].c); + + int last = 0; + + switch (m_sel.prim) + { + case GS_POINT_CLASS: last = 0; break; + case GS_LINE_CLASS: last = 1; break; + case GS_TRIANGLE_CLASS: last = 2; break; + case GS_SPRITE_CLASS: last = 1; break; + } + + if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() + { + if (is32) + mov(_index, ptr[rsp + _32_index]); + mov(eax, ptr[_index + sizeof(uint32) * last]); + shl(eax, 6); // * sizeof(GSVertexSW) + if (is64) + add(rax, _64_vertex); + else + add(rax, ptr[rsp + _32_vertex]); + } + + if (isXmm) + { + cvttps2dq(xmm0, ptr[rax + offsetof(GSVertexSW, c)]); + } + else + { + vbroadcasti128(ymm0, ptr[rax + offsetof(GSVertexSW, c)]); + cvttps2dq(ymm0, ymm0); + } + + // c = c.upl16(c.zwxy()); + + pshufd(xym1, xym0, _MM_SHUFFLE(1, 0, 3, 2)); + punpcklwd(xym0, xym1); + + // if(!tme) c = c.srl16(7); + + if (m_sel.tfx == TFX_NONE) + { + psrlw(xym0, 7); + } + + // m_local.c.rb = c.xxxx(); + // m_local.c.ga = c.zzzz(); + + pshufd(xym1, xym0, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(xym2, xym0, _MM_SHUFFLE(2, 2, 2, 2)); + + movdqa(_rip_local(c.rb), xym1); + movdqa(_rip_local(c.ga), xym2); + } +} diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.h b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.h new file mode 100644 index 0000000000000..df68e9f47934d --- /dev/null +++ b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.all.h @@ -0,0 +1,83 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "GSScanlineEnvironment.h" +#include "GSNewCodeGenerator.h" + +#if _M_SSE >= 0x501 + #define SETUP_PRIM_VECTOR_REGISTER Xbyak::Ymm + #define SETUP_PRIM_USING_XMM 0 + #define SETUP_PRIM_USING_YMM 1 +#else + #define SETUP_PRIM_VECTOR_REGISTER Xbyak::Xmm + #define SETUP_PRIM_USING_XMM 1 + #define SETUP_PRIM_USING_YMM 0 +#endif + +class GSSetupPrimCodeGenerator2 : public GSNewCodeGenerator +{ + using _parent = GSNewCodeGenerator; + using XYm = SETUP_PRIM_VECTOR_REGISTER; + + using Xmm = Xbyak::Xmm; + using Ymm = Xbyak::Ymm; + + /// On x86-64 we reserve a bunch of GPRs for holding addresses of locals that would otherwise be hard to reach + /// On x86-32 the same values are just raw 32-bit addresses + using LocalAddr = Choose3264::type; + + constexpr static bool isXmm = std::is_same::value; + constexpr static bool isYmm = std::is_same::value; + constexpr static int vecsize = isXmm ? 16 : 32; + + constexpr static int dsize = isXmm ? 4 : 8; + + constexpr static int _32_args = 0; + constexpr static int _invalid = 0xaaaaaaaa; + constexpr static int _32_vertex = is64 ? _invalid : _32_args + 4; + constexpr static int _32_index = is64 ? _invalid : _32_args + 8; + constexpr static int _32_dscan = is64 ? _invalid : _32_args + 12; + + GSScanlineSelector m_sel; + GSScanlineLocalData& m_local; + bool m_rip; + bool many_regs; + + struct {uint32 z:1, f:1, t:1, c:1;} m_en; + + const XYm xym0{0}, xym1{1}, xym2{2}, xym3{3}, xym4{4}, xym5{5}, xym6{6}, xym7{7}, xym8{8}, xym9{9}, xym10{10}, xym11{11}, xym12{12}, xym13{13}, xym14{14}, xym15{15}; + const AddressReg _64_vertex, _index, _dscan, _64_t0, t1; + const LocalAddr _m_local; + /// Returns the first arg on 32-bit, second on 64-bit + static LocalAddr chooseLocal(const void* addr32, AddressReg reg64) + { + return choose3264((size_t)addr32, reg64); + } + +public: + GSSetupPrimCodeGenerator2(Xbyak::CodeGenerator* base, CPUInfo cpu, void* param, uint64 key); + void Generate(); + +private: + /// Broadcast 128 bits of floats from memory to the whole register, whatever size that register might be + void broadcastf128(const XYm& reg, const Xbyak::Address& mem); + + void Depth_XMM(); + void Depth_YMM(); + void Texture(); + void Color(); +}; diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.cpp index f8d1137ee0755..4d90992eade28 100644 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.cpp +++ b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.cpp @@ -15,6 +15,7 @@ #include "PrecompiledHeader.h" #include "GSSetupPrimCodeGenerator.h" +#include "GSSetupPrimCodeGenerator.all.h" using namespace Xbyak; @@ -30,19 +31,5 @@ GSSetupPrimCodeGenerator::GSSetupPrimCodeGenerator(void* param, uint64 key, void m_en.t = m_sel.fb && m_sel.tfx != TFX_NONE ? 1 : 0; m_en.c = m_sel.fb && !(m_sel.tfx == TFX_DECAL && m_sel.tcc) ? 1 : 0; - try - { -#if _M_SSE >= 0x501 - Generate_AVX2(); -#else - if (m_cpu.has(util::Cpu::tAVX)) - Generate_AVX(); - else - Generate_SSE(); -#endif - } - catch (std::exception& e) - { - fprintf(stderr, "ERR:GSSetupPrimCodeGenerator %s\n", e.what()); - } + GSSetupPrimCodeGenerator2(this, CPUInfo(m_cpu), param, key).Generate(); } diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.h b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.h index 2bda1b07776f2..121e6c26c9c1f 100644 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.h +++ b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.h @@ -32,23 +32,6 @@ class GSSetupPrimCodeGenerator : public GSCodeGenerator uint32 z : 1, f : 1, t : 1, c : 1; } m_en; -#if _M_SSE < 0x501 - void Generate_SSE(); - void Depth_SSE(); - void Texture_SSE(); - void Color_SSE(); - - void Generate_AVX(); - void Depth_AVX(); - void Texture_AVX(); - void Color_AVX(); -#else - void Generate_AVX2(); - void Depth_AVX2(); - void Texture_AVX2(); - void Color_AVX2(); -#endif - public: GSSetupPrimCodeGenerator(void* param, uint64 key, void* code, size_t maxsize); }; diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx.cpp deleted file mode 100644 index d965b49bc8cc3..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -#define _rip_local(field) (m_rip ? ptr[rip + &m_local.field] : ptr[t0 + offsetof(GSScanlineLocalData, field)]) -#define _rip_local_v(field, offset) (m_rip ? ptr[rip + &m_local.field] : ptr[t0 + offset]) - -void GSSetupPrimCodeGenerator::Generate_AVX() -{ - // Technically we just need the delta < 2GB - m_rip = (size_t)&m_local < 0x80000000 && (size_t)getCurr() < 0x80000000; - -#ifdef _WIN64 - sub(rsp, 8 + 2 * 16); - - vmovdqa(ptr[rsp + 0], xmm6); - vmovdqa(ptr[rsp + 16], xmm7); -#endif - - if (!m_rip) - mov(t0, (size_t)&m_local); - - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(rax, (size_t)g_const->m_shift_128b); - - for (int i = 0; i < (m_sel.notest ? 2 : 5); i++) - { - vmovaps(Xmm(3 + i), ptr[rax + i * 16]); - } - } - - Depth_AVX(); - - Texture_AVX(); - - Color_AVX(); - -#ifdef _WIN64 - vmovdqa(xmm6, ptr[rsp + 0]); - vmovdqa(xmm7, ptr[rsp + 16]); - - add(rsp, 8 + 2 * 16); -#endif - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_AVX() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4 p = dscan.p; - - vmovaps(xmm0, ptr[a2 + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // GSVector4 df = p.wwww(); - - vshufps(xmm1, xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - - // m_local.d4.f = GSVector4i(df * 4.0f).xxzzlh(); - - vmulps(xmm2, xmm1, xmm3); - vcvttps2dq(xmm2, xmm2); - vpshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(_rip_local(d4.f), xmm2); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); - - vmulps(xmm2, xmm1, Xmm(4 + i)); - vcvttps2dq(xmm2, xmm2); - vpshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].f) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].f, variableOffset), xmm2); - } - } - - if (m_en.z) - { - // GSVector4 dz = p.zzzz(); - - vshufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - // m_local.d4.z = dz * 4.0f; - - vmulps(xmm1, xmm0, xmm3); - vmovdqa(_rip_local(d4.z), xmm1); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].z = dz * m_shift[i]; - - vmulps(xmm1, xmm0, Xmm(4 + i)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].z) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].z, variableOffset), xmm1); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(eax, ptr[a1 + sizeof(uint32) * 1]); - shl(eax, 6); // * sizeof(GSVertexSW) - add(rax, a0); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(p).zzzzh().zzzz(); - vmovaps(xmm0, ptr[rax + offsetof(GSVertexSW, p)]); - - vcvttps2dq(xmm1, xmm0); - vpshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vmovdqa(_rip_local(p.f), xmm1); - } - - if (m_en.z) - { - // uint32 z is bypassed in t.w - - vmovdqa(xmm0, ptr[rax + offsetof(GSVertexSW, t)]); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - vmovdqa(_rip_local(p.z), xmm0); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_AVX() -{ - if (!m_en.t) - { - return; - } - - // GSVector4 t = dscan.t; - - vmovaps(xmm0, ptr[a2 + offsetof(GSVertexSW, t)]); - - vmulps(xmm1, xmm0, xmm3); - - if (m_sel.fst) - { - // m_local.d4.stq = GSVector4i(t * 4.0f); - - vcvttps2dq(xmm1, xmm1); - - vmovdqa(_rip_local(d4.stq), xmm1); - } - else - { - // m_local.d4.stq = t * 4.0f; - - vmovaps(_rip_local(d4.stq), xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector4 ds = t.xxxx(); - // GSVector4 dt = t.yyyy(); - // GSVector4 dq = t.zzzz(); - - vshufps(xmm1, xmm0, xmm0, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4 v = ds/dt * m_shift[i]; - - vmulps(xmm2, xmm1, Xmm(4 + i)); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector4i(v); - - vcvttps2dq(xmm2, xmm2); - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: vmovdqa(_rip_local_v(d[i].s, variableOffsetS), xmm2); break; - case 1: vmovdqa(_rip_local_v(d[i].t, variableOffsetT), xmm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetQ = offsetof(GSScanlineLocalData, d[0].q) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: vmovaps(_rip_local_v(d[i].s, variableOffsetS), xmm2); break; - case 1: vmovaps(_rip_local_v(d[i].t, variableOffsetT), xmm2); break; - case 2: vmovaps(_rip_local_v(d[i].q, variableOffsetQ), xmm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_AVX() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - // GSVector4 c = dscan.c; - - vmovaps(xmm0, ptr[a2 + offsetof(GSVertexSW, c)]); - - // m_local.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - vmulps(xmm1, xmm0, xmm3); - vcvttps2dq(xmm1, xmm1); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(3, 1, 2, 0)); - vpackssdw(xmm1, xmm1); - vmovdqa(_rip_local(d4.c), xmm1); - - // xmm3 is not needed anymore - - // GSVector4 dr = c.xxxx(); - // GSVector4 db = c.zzzz(); - - vshufps(xmm2, xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(xmm3, xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); - - vmulps(xmm0, xmm2, Xmm(4 + i)); - vcvttps2dq(xmm0, xmm0); - vpackssdw(xmm0, xmm0); - - // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); - - vmulps(xmm1, xmm3, Xmm(4 + i)); - vcvttps2dq(xmm1, xmm1); - vpackssdw(xmm1, xmm1); - - // m_local.d[i].rb = r.upl16(b); - - vpunpcklwd(xmm0, xmm1); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].rb) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].rb, variableOffset), xmm0); - } - - // GSVector4 c = dscan.c; - - vmovaps(xmm0, ptr[a2 + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it - - // GSVector4 dg = c.yyyy(); - // GSVector4 da = c.wwww(); - - vshufps(xmm2, xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(xmm3, xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); - - vmulps(xmm0, xmm2, Xmm(4 + i)); - vcvttps2dq(xmm0, xmm0); - vpackssdw(xmm0, xmm0); - - // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); - - vmulps(xmm1, xmm3, Xmm(4 + i)); - vcvttps2dq(xmm1, xmm1); - vpackssdw(xmm1, xmm1); - - // m_local.d[i].ga = g.upl16(a); - - vpunpcklwd(xmm0, xmm1); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].ga) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].ga, variableOffset), xmm0); - } - } - else - { - // GSVector4i c = GSVector4i(vertex[index[last].c); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(eax, ptr[a1 + sizeof(uint32) * last]); - shl(eax, 6); // * sizeof(GSVertexSW) - add(rax, a0); - } - - vcvttps2dq(xmm0, ptr[rax + offsetof(GSVertexSW, c)]); - - // c = c.upl16(c.zwxy()); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(xmm0, xmm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - vpsrlw(xmm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm2, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vmovdqa(_rip_local(c.rb), xmm1); - vmovdqa(_rip_local(c.ga), xmm2); - } -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx2.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx2.cpp deleted file mode 100644 index 916f4e682b795..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.avx2.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE >= 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -#define _rip_local(field) (m_rip ? ptr[rip + &m_local.field] : ptr[t0 + offsetof(GSScanlineLocalData, field)]) -#define _rip_local_v(field, offset) (m_rip ? ptr[rip + &m_local.field] : ptr[t0 + offset]) - -#define _m_shift(i) (Ymm(7 + i)) - -// FIXME windows ? -#define _vertex rcx - -void GSSetupPrimCodeGenerator::Generate_AVX2() -{ - // Technically we just need the delta < 2GB - m_rip = (size_t)&m_local < 0x80000000 && (size_t)getCurr() < 0x80000000; - -#ifdef _WIN64 - sub(rsp, 8 + 2 * 16); - - vmovdqa(ptr[rsp + 0], ymm6); - vmovdqa(ptr[rsp + 16], ymm7); -#endif - - if (!m_rip) - mov(t0, (size_t)&m_local); - - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(rax, (size_t)g_const->m_shift_256b); - - for (int i = 0; i < (m_sel.notest ? 2 : 9); i++) - { - vmovaps(_m_shift(i), ptr[rax + i * 32]); - } - } - // ymm7 to ymm 15 = m_shift[i] - - Depth_AVX2(); - - Texture_AVX2(); - - Color_AVX2(); - -#ifdef _WIN64 - vmovdqa(ymm6, ptr[rsp + 0]); - vmovdqa(ymm7, ptr[rsp + 16]); - - add(rsp, 8 + 2 * 16); -#endif - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_AVX2() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - const Ymm& dscan_p = ymm6; - - // GSVector4 dp8 = dscan.p * GSVector4::broadcast32(&shift[0]); - - vbroadcastf128(dscan_p, ptr[a2 + offsetof(GSVertexSW, p)]); - - vmulps(ymm1, dscan_p, _m_shift(0)); - - if (m_en.z) - { - // m_local.d8.p.z = dp8.extract32<2>(); - - vextractps(_rip_local(d8.p.z), xmm1, 2); - - // GSVector8 dz = GSVector8(dscan.p).zzzz(); - - vshufps(ymm2, dscan_p, dscan_p, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // m_local.d[i].z = dz * shift[1 + i]; - - vmulps(ymm0, ymm2, _m_shift(1 + i)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].z) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovaps(_rip_local_v(d[i].z, variableOffset), ymm0); - } - } - - if (m_en.f) - { - // m_local.d8.p.f = GSVector4i(dp8).extract32<3>(); - - // FIXME no truncate ? why ? vcvttps2dq ? - //vcvtps2dq(ymm2, ymm1); // let's guess a typo - vcvttps2dq(ymm2, ymm1); - vpextrd(_rip_local(d8.p.f), xmm2, 3); - - // GSVector8 df = GSVector8(dscan.p).wwww(); - - vshufps(ymm3, dscan_p, dscan_p, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // m_local.d[i].f = GSVector8i(df * m_shift[i]).xxzzlh(); - - vmulps(ymm0, ymm3, _m_shift(1 + i)); - vcvttps2dq(ymm0, ymm0); - - vpshuflw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].f) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].f, variableOffset), ymm0); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(_vertex.cvt32(), ptr[a1 + sizeof(uint32) * 1]); - shl(_vertex.cvt32(), 6); // * sizeof(GSVertexSW) - add(_vertex, a0); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(vertex[index[1]].p).extract32<3>(); - - vmovaps(xmm0, ptr[_vertex + offsetof(GSVertexSW, p)]); - vcvttps2dq(xmm0, xmm0); - vpextrd(_rip_local(p.f), xmm0, 3); - } - - if (m_en.z) - { - // m_local.p.z = vertex[index[1]].t.u32[3]; // uint32 z is bypassed in t.w - - mov(eax, ptr[ecx + offsetof(GSVertexSW, t.w)]); - mov(_rip_local(p.z), eax); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_AVX2() -{ - if (!m_en.t) - { - return; - } - - // GSVector8 dt(dscan.t); - - vbroadcastf128(ymm0, ptr[a2 + offsetof(GSVertexSW, t)]); - - // GSVector8 dt8 = dt * shift[0]; - - vmulps(ymm1, ymm0, _m_shift(0)); - - if (m_sel.fst) - { - // m_local.84.stq = GSVector4i(t * 4.0f); - - vcvttps2dq(ymm1, ymm1); - - vmovdqa(_rip_local(d8.stq), xmm1); - } - else - { - // m_local.d8.stq = t * 4.0f; - - vmovaps(_rip_local(d8.stq), xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector8 dstq = dt.xxxx/yyyy/zzzz(); - - vshufps(ymm1, ymm0, ymm0, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8 v = dstq * shift[1 + i]; - - vmulps(ymm2, ymm1, _m_shift(1 + i)); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector8::cast(GSVector8i(v)); - - vcvttps2dq(ymm2, ymm2); - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: vmovdqa(_rip_local_v(d[i].s, variableOffsetS), ymm2); break; - case 1: vmovdqa(_rip_local_v(d[i].t, variableOffsetT), ymm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetQ = offsetof(GSScanlineLocalData, d[0].q) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: vmovaps(_rip_local_v(d[i].s, variableOffsetS), ymm2); break; - case 1: vmovaps(_rip_local_v(d[i].t, variableOffsetT), ymm2); break; - case 2: vmovaps(_rip_local_v(d[i].q, variableOffsetQ), ymm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_AVX2() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - const Ymm& dscan_c = ymm6; - - // GSVector8 dc(dscan.c); - - vbroadcastf128(dscan_c, ptr[a2 + offsetof(GSVertexSW, c)]); - - // m_local.d8.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - vmulps(ymm1, dscan_c, ymm3); - vcvttps2dq(ymm1, ymm1); - vpshufd(ymm1, ymm1, _MM_SHUFFLE(3, 1, 2, 0)); - vpackssdw(ymm1, ymm1); - vmovq(_rip_local(d8.c), xmm1); - - // GSVector8 dr = dc.xxxx(); - // GSVector8 db = dc.zzzz(); - - vshufps(ymm2, dscan_c, dscan_c, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, dscan_c, dscan_c, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8i r = GSVector8i(dr * shift[1 + i]).ps32(); - - vmulps(ymm0, ymm2, _m_shift(1 + i)); - vcvttps2dq(ymm0, ymm0); - vpackssdw(ymm0, ymm0); - - // GSVector4i b = GSVector8i(db * shift[1 + i]).ps32(); - - vmulps(ymm1, ymm3, _m_shift(1 + i)); - vcvttps2dq(ymm1, ymm1); - vpackssdw(ymm1, ymm1); - - // m_local.d[i].rb = r.upl16(b); - - vpunpcklwd(ymm0, ymm1); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].rb) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].rb, variableOffset), ymm0); - } - - // GSVector8 dg = dc.yyyy(); - // GSVector8 da = dc.wwww(); - - vshufps(ymm2, dscan_c, dscan_c, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm3, dscan_c, dscan_c, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8i g = GSVector8i(dg * shift[1 + i]).ps32(); - - vmulps(ymm0, ymm2, _m_shift(1 + i)); - vcvttps2dq(ymm0, ymm0); - vpackssdw(ymm0, ymm0); - - // GSVector8i a = GSVector8i(da * shift[1 + i]).ps32(); - - vmulps(ymm1, ymm3, _m_shift(1 + i)); - vcvttps2dq(ymm1, ymm1); - vpackssdw(ymm1, ymm1); - - // m_local.d[i].ga = g.upl16(a); - - vpunpcklwd(ymm0, ymm1); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].ga) + (i * sizeof(GSScanlineLocalData::d[0])); - vmovdqa(_rip_local_v(d[i].ga, variableOffset), ymm0); - } - } - else - { - // GSVector4i c = GSVector4i(vertex[index[last].c); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(_vertex.cvt32(), ptr[a1 + sizeof(uint32) * last]); - shl(_vertex.cvt32(), 6); // * sizeof(GSVertexSW) - add(_vertex, a0); - } - - vbroadcasti128(ymm0, ptr[_vertex + offsetof(GSVertexSW, c)]); - vcvttps2dq(ymm0, ymm0); - - // c = c.upl16(c.zwxy()); - - vpshufd(ymm1, ymm0, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(ymm0, ymm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - vpsrlw(ymm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - vpshufd(ymm1, ymm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm2, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vmovdqa(_rip_local(c.rb), ymm1); - vmovdqa(_rip_local(c.ga), ymm2); - } -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.cpp deleted file mode 100644 index 3a12b461065b7..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x64.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && (defined(_M_AMD64) || defined(_WIN64)) - -void GSSetupPrimCodeGenerator::Generate_SSE() -{ -#ifdef _WIN64 - sub(rsp, 8 + 2 * 16); - - vmovdqa(ptr[rsp + 0], xmm6); - vmovdqa(ptr[rsp + 16], xmm7); -#endif - - mov(t0, (size_t)&m_local); - - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(rax, (size_t)g_const->m_shift_128b[0]); - - for (int i = 0; i < (m_sel.notest ? 2 : 5); i++) - { - movaps(Xmm(3 + i), ptr[rax + i * 16]); - } - } - - Depth_SSE(); - - Texture_SSE(); - - Color_SSE(); - -#ifdef _WIN64 - vmovdqa(xmm6, ptr[rsp + 0]); - vmovdqa(xmm7, ptr[rsp + 16]); - - add(rsp, 8 + 2 * 16); -#endif - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_SSE() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4 p = dscan.p; - - movaps(xmm0, ptr[a2 + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // GSVector4 df = p.wwww(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - // m_local.d4.f = GSVector4i(df * 4.0f).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, d4.f)], xmm2); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].f) + (i * sizeof(GSScanlineLocalData::d[0])); - movdqa(ptr[t0 + variableOffset], xmm2); - } - } - - if (m_en.z) - { - // GSVector4 dz = p.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - // m_local.d4.z = dz * 4.0f; - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, d4.z)], xmm1); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].z = dz * m_shift[i]; - - movaps(xmm1, xmm0); - mulps(xmm1, Xmm(4 + i)); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].z) + (i * sizeof(GSScanlineLocalData::d[0])); - movdqa(ptr[t0 + variableOffset], xmm1); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(eax, ptr[a1 + sizeof(uint32) * 1]); - shl(eax, 6); // * sizeof(GSVertexSW) - add(rax, a0); - - movaps(xmm0, ptr[rax + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(p).zzzzh().zzzz(); - - cvttps2dq(xmm1, xmm0); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, p.f)], xmm1); - } - - if (m_en.z) - { - // uint32 z is bypassed in t.w - - vmovdqa(xmm0, ptr[rax + offsetof(GSVertexSW, t)]); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - vmovdqa(ptr[t0 + offsetof(GSScanlineLocalData, p.z)], xmm0); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_SSE() -{ - if (!m_en.t) - { - return; - } - - // GSVector4 t = dscan.t; - - movaps(xmm0, ptr[a2 + offsetof(GSVertexSW, t)]); - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - - if (m_sel.fst) - { - // m_local.d4.stq = GSVector4i(t * 4.0f); - - cvttps2dq(xmm1, xmm1); - - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, d4.stq)], xmm1); - } - else - { - // m_local.d4.stq = t * 4.0f; - - movaps(ptr[t0 + offsetof(GSScanlineLocalData, d4.stq)], xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector4 ds = t.xxxx(); - // GSVector4 dt = t.yyyy(); - // GSVector4 dq = t.zzzz(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4 v = ds/dt * m_shift[i]; - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector4i(v); - - cvttps2dq(xmm2, xmm2); - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: movdqa(ptr[t0 + variableOffsetS], xmm2); break; - case 1: movdqa(ptr[t0 + variableOffsetT], xmm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - const size_t variableOffsetS = offsetof(GSScanlineLocalData, d[0].s) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetT = offsetof(GSScanlineLocalData, d[0].t) + (i * sizeof(GSScanlineLocalData::d[0])); - const size_t variableOffsetQ = offsetof(GSScanlineLocalData, d[0].q) + (i * sizeof(GSScanlineLocalData::d[0])); - - switch (j) - { - case 0: movaps(ptr[t0 + variableOffsetS], xmm2); break; - case 1: movaps(ptr[t0 + variableOffsetT], xmm2); break; - case 2: movaps(ptr[t0 + variableOffsetQ], xmm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_SSE() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - // GSVector4 c = dscan.c; - - movaps(xmm0, ptr[a2 + offsetof(GSVertexSW, c)]); - movaps(xmm1, xmm0); - - // m_local.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshufd(xmm2, xmm2, _MM_SHUFFLE(3, 1, 2, 0)); - packssdw(xmm2, xmm2); - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, d4.c)], xmm2); - - // xmm3 is not needed anymore - - // GSVector4 dr = c.xxxx(); - // GSVector4 db = c.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_local.d[i].rb = r.upl16(b); - - punpcklwd(xmm2, xmm3); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].rb) + (i * sizeof(GSScanlineLocalData::d[0])); - movdqa(ptr[t0 + variableOffset], xmm2); - } - - // GSVector4 c = dscan.c; - - movaps(xmm0, ptr[a2 + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it - movaps(xmm1, xmm0); - - // GSVector4 dg = c.yyyy(); - // GSVector4 da = c.wwww(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_local.d[i].ga = g.upl16(a); - - punpcklwd(xmm2, xmm3); - - const size_t variableOffset = offsetof(GSScanlineLocalData, d[0].ga) + (i * sizeof(GSScanlineLocalData::d[0])); - movdqa(ptr[t0 + variableOffset], xmm2); - } - } - else - { - // GSVector4i c = GSVector4i(vertex[index[last].c); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(eax, ptr[a1 + sizeof(uint32) * last]); - shl(eax, 6); // * sizeof(GSVertexSW) - add(rax, a0); - } - - cvttps2dq(xmm0, ptr[rax + offsetof(GSVertexSW, c)]); - - // c = c.upl16(c.zwxy()); - - pshufd(xmm1, xmm0, _MM_SHUFFLE(1, 0, 3, 2)); - punpcklwd(xmm0, xmm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - psrlw(xmm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - pshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm2, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, c.rb)], xmm1); - movdqa(ptr[t0 + offsetof(GSScanlineLocalData, c.ga)], xmm2); - } -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx.cpp deleted file mode 100644 index 555abda7a7d3a..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 0; -static const int _vertex = _args + 4; -static const int _index = _args + 8; -static const int _dscan = _args + 12; - -void GSSetupPrimCodeGenerator::Generate_AVX() -{ - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(edx, dword[esp + _dscan]); - - for (int i = 0; i < (m_sel.notest ? 2 : 5); i++) - { - vmovaps(Xmm(3 + i), ptr[g_const->m_shift_128b[i]]); - } - } - - Depth_AVX(); - - Texture_AVX(); - - Color_AVX(); - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_AVX() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4 p = dscan.p; - - vmovaps(xmm0, ptr[edx + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // GSVector4 df = p.wwww(); - - vshufps(xmm1, xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - - // m_local.d4.f = GSVector4i(df * 4.0f).xxzzlh(); - - vmulps(xmm2, xmm1, xmm3); - vcvttps2dq(xmm2, xmm2); - vpshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.d4.f], xmm2); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); - - vmulps(xmm2, xmm1, Xmm(4 + i)); - vcvttps2dq(xmm2, xmm2); - vpshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.d[i].f], xmm2); - } - } - - if (m_en.z) - { - // GSVector4 dz = p.zzzz(); - - vshufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - // m_local.d4.z = dz * 4.0f; - - vmulps(xmm1, xmm0, xmm3); - vmovdqa(ptr[&m_local.d4.z], xmm1); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].z = dz * m_shift[i]; - - vmulps(xmm1, xmm0, Xmm(4 + i)); - vmovdqa(ptr[&m_local.d[i].z], xmm1); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * 1]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - - vmovaps(xmm0, ptr[ecx + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(p).zzzzh().zzzz(); - - vcvttps2dq(xmm1, xmm0); - vpshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - vmovdqa(ptr[&m_local.p.f], xmm1); - } - - if (m_en.z) - { - // uint32 z is bypassed in t.w - - vmovdqa(xmm0, ptr[ecx + offsetof(GSVertexSW, t)]); - vpshufd(xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - vmovdqa(ptr[&m_local.p.z], xmm0); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_AVX() -{ - if (!m_en.t) - { - return; - } - - // GSVector4 t = dscan.t; - - vmovaps(xmm0, ptr[edx + offsetof(GSVertexSW, t)]); - - vmulps(xmm1, xmm0, xmm3); - - if (m_sel.fst) - { - // m_local.d4.stq = GSVector4i(t * 4.0f); - - vcvttps2dq(xmm1, xmm1); - - vmovdqa(ptr[&m_local.d4.stq], xmm1); - } - else - { - // m_local.d4.stq = t * 4.0f; - - vmovaps(ptr[&m_local.d4.stq], xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector4 ds = t.xxxx(); - // GSVector4 dt = t.yyyy(); - // GSVector4 dq = t.zzzz(); - - vshufps(xmm1, xmm0, xmm0, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4 v = ds/dt * m_shift[i]; - - vmulps(xmm2, xmm1, Xmm(4 + i)); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector4i(v); - - vcvttps2dq(xmm2, xmm2); - - switch (j) - { - case 0: vmovdqa(ptr[&m_local.d[i].s], xmm2); break; - case 1: vmovdqa(ptr[&m_local.d[i].t], xmm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - switch (j) - { - case 0: vmovaps(ptr[&m_local.d[i].s], xmm2); break; - case 1: vmovaps(ptr[&m_local.d[i].t], xmm2); break; - case 2: vmovaps(ptr[&m_local.d[i].q], xmm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_AVX() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - // GSVector4 c = dscan.c; - - vmovaps(xmm0, ptr[edx + offsetof(GSVertexSW, c)]); - - // m_local.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - vmulps(xmm1, xmm0, xmm3); - vcvttps2dq(xmm1, xmm1); - vpshufd(xmm1, xmm1, _MM_SHUFFLE(3, 1, 2, 0)); - vpackssdw(xmm1, xmm1); - vmovdqa(ptr[&m_local.d4.c], xmm1); - - // xmm3 is not needed anymore - - // GSVector4 dr = c.xxxx(); - // GSVector4 db = c.zzzz(); - - vshufps(xmm2, xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(xmm3, xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); - - vmulps(xmm0, xmm2, Xmm(4 + i)); - vcvttps2dq(xmm0, xmm0); - vpackssdw(xmm0, xmm0); - - // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); - - vmulps(xmm1, xmm3, Xmm(4 + i)); - vcvttps2dq(xmm1, xmm1); - vpackssdw(xmm1, xmm1); - - // m_local.d[i].rb = r.upl16(b); - - vpunpcklwd(xmm0, xmm1); - vmovdqa(ptr[&m_local.d[i].rb], xmm0); - } - - // GSVector4 c = dscan.c; - - vmovaps(xmm0, ptr[edx + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it - - // GSVector4 dg = c.yyyy(); - // GSVector4 da = c.wwww(); - - vshufps(xmm2, xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(xmm3, xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); - - vmulps(xmm0, xmm2, Xmm(4 + i)); - vcvttps2dq(xmm0, xmm0); - vpackssdw(xmm0, xmm0); - - // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); - - vmulps(xmm1, xmm3, Xmm(4 + i)); - vcvttps2dq(xmm1, xmm1); - vpackssdw(xmm1, xmm1); - - // m_local.d[i].ga = g.upl16(a); - - vpunpcklwd(xmm0, xmm1); - vmovdqa(ptr[&m_local.d[i].ga], xmm0); - } - } - else - { - // GSVector4i c = GSVector4i(vertex[index[last].c); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * last]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - } - - vcvttps2dq(xmm0, ptr[ecx + offsetof(GSVertexSW, c)]); - - // c = c.upl16(c.zwxy()); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(xmm0, xmm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - vpsrlw(xmm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - vpshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(xmm2, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vmovdqa(ptr[&m_local.c.rb], xmm1); - vmovdqa(ptr[&m_local.c.ga], xmm2); - } -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx2.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx2.cpp deleted file mode 100644 index a3f9ee653d453..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.avx2.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE >= 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 0; -static const int _vertex = _args + 4; -static const int _index = _args + 8; -static const int _dscan = _args + 12; - -void GSSetupPrimCodeGenerator::Generate_AVX2() -{ - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(edx, dword[esp + _dscan]); - - for (int i = 0; i < (m_sel.notest ? 2 : 5); i++) - { - vmovaps(Ymm(3 + i), ptr[g_const->m_shift_256b[i]]); - } - } - - Depth_AVX2(); - - Texture_AVX2(); - - Color_AVX2(); - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_AVX2() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4 dp8 = dscan.p * GSVector4::broadcast32(&shift[0]); - - vbroadcastf128(ymm0, ptr[edx + offsetof(GSVertexSW, p)]); - - vmulps(ymm1, ymm0, ymm3); - - if (m_en.z) - { - // m_local.d8.p.z = dp8.extract32<2>(); - - vextractps(ptr[&m_local.d8.p.z], xmm1, 2); - } - - if (m_en.f) - { - // m_local.d8.p.f = GSVector4i(dp8).extract32<3>(); - - vcvtps2dq(ymm2, ymm1); - vpextrd(ptr[&m_local.d8.p.f], xmm2, 3); - } - - if (m_en.z) - { - // GSVector8 dz = GSVector8(dscan.p).zzzz(); - - vshufps(ymm2, ymm0, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - } - - if (m_en.f) - { - // GSVector8 df = GSVector8(dscan.p).wwww(); - - vshufps(ymm1, ymm0, ymm0, _MM_SHUFFLE(3, 3, 3, 3)); - } - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - if (m_en.z) - { - // m_local.d[i].z = dz * shift[1 + i]; - - if (i < 4) - vmulps(ymm0, ymm2, Ymm(4 + i)); - else - vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); - vmovaps(ptr[&m_local.d[i].z], ymm0); - } - - if (m_en.f) - { - // m_local.d[i].f = GSVector8i(df * m_shift[i]).xxzzlh(); - - if (i < 4) - vmulps(ymm0, ymm1, Ymm(4 + i)); - else - vmulps(ymm0, ymm1, ptr[g_const->m_shift_256b[i + 1]]); - vcvttps2dq(ymm0, ymm0); - vpshuflw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vpshufhw(ymm0, ymm0, _MM_SHUFFLE(2, 2, 0, 0)); - vmovdqa(ptr[&m_local.d[i].f], ymm0); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * 1]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(vertex[index[1]].p).extract32<3>(); - - vmovaps(xmm0, ptr[ecx + offsetof(GSVertexSW, p)]); - vcvttps2dq(xmm0, xmm0); - vpextrd(ptr[&m_local.p.f], xmm0, 3); - } - - if (m_en.z) - { - // m_local.p.z = vertex[index[1]].t.u32[3]; // uint32 z is bypassed in t.w - - mov(eax, ptr[ecx + offsetof(GSVertexSW, t.w)]); - mov(ptr[&m_local.p.z], eax); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_AVX2() -{ - if (!m_en.t) - { - return; - } - - // GSVector8 dt(dscan.t); - - vbroadcastf128(ymm0, ptr[edx + offsetof(GSVertexSW, t)]); - - // GSVector8 dt8 = dt * shift[0]; - - vmulps(ymm1, ymm0, ymm3); - - if (m_sel.fst) - { - // m_local.d8.stq = GSVector8::cast(GSVector8i(dt8)); - - vcvttps2dq(ymm1, ymm1); - - vmovdqa(ptr[&m_local.d8.stq], xmm1); - } - else - { - // m_local.d8.stq = dt8; - - vmovaps(ptr[&m_local.d8.stq], xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector8 dstq = dt.xxxx/yyyy/zzzz(); - - vshufps(ymm1, ymm0, ymm0, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8 v = dstq * shift[1 + i]; - - if (i < 4) - vmulps(ymm2, ymm1, Ymm(4 + i)); - else - vmulps(ymm2, ymm1, ptr[g_const->m_shift_256b[i + 1]]); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector8::cast(GSVector8i(v)); - - vcvttps2dq(ymm2, ymm2); - - switch (j) - { - case 0: vmovdqa(ptr[&m_local.d[i].s], ymm2); break; - case 1: vmovdqa(ptr[&m_local.d[i].t], ymm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - switch (j) - { - case 0: vmovaps(ptr[&m_local.d[i].s], ymm2); break; - case 1: vmovaps(ptr[&m_local.d[i].t], ymm2); break; - case 2: vmovaps(ptr[&m_local.d[i].q], ymm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_AVX2() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - // GSVector8 dc(dscan.c); - - vbroadcastf128(ymm0, ptr[edx + offsetof(GSVertexSW, c)]); - - // m_local.d8.c = GSVector8i(dc * shift[0]).xzyw().ps32(); - - vmulps(ymm1, ymm0, ymm3); - vcvttps2dq(ymm1, ymm1); - vpshufd(ymm1, ymm1, _MM_SHUFFLE(3, 1, 2, 0)); - vpackssdw(ymm1, ymm1); - vmovq(ptr[&m_local.d8.c], xmm1); - - // ymm3 is not needed anymore - - // GSVector8 dr = dc.xxxx(); - // GSVector8 db = dc.zzzz(); - - vshufps(ymm2, ymm0, ymm0, _MM_SHUFFLE(0, 0, 0, 0)); - vshufps(ymm3, ymm0, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8i r = GSVector8i(dr * shift[1 + i]).ps32(); - - if (i < 4) - vmulps(ymm0, ymm2, Ymm(4 + i)); - else - vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); - vcvttps2dq(ymm0, ymm0); - vpackssdw(ymm0, ymm0); - - // GSVector4i b = GSVector8i(db * shift[1 + i]).ps32(); - - if (i < 4) - vmulps(ymm1, ymm3, Ymm(4 + i)); - else - vmulps(ymm1, ymm3, ptr[g_const->m_shift_256b[i + 1]]); - vcvttps2dq(ymm1, ymm1); - vpackssdw(ymm1, ymm1); - - // m_local.d[i].rb = r.upl16(b); - - vpunpcklwd(ymm0, ymm1); - vmovdqa(ptr[&m_local.d[i].rb], ymm0); - } - - // GSVector8 dc(dscan.c); - - vbroadcastf128(ymm0, ptr[edx + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it - - // GSVector8 dg = dc.yyyy(); - // GSVector8 da = dc.wwww(); - - vshufps(ymm2, ymm0, ymm0, _MM_SHUFFLE(1, 1, 1, 1)); - vshufps(ymm3, ymm0, ymm0, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 8); i++) - { - // GSVector8i g = GSVector8i(dg * shift[1 + i]).ps32(); - - if (i < 4) - vmulps(ymm0, ymm2, Ymm(4 + i)); - else - vmulps(ymm0, ymm2, ptr[g_const->m_shift_256b[i + 1]]); - vcvttps2dq(ymm0, ymm0); - vpackssdw(ymm0, ymm0); - - // GSVector8i a = GSVector8i(da * shift[1 + i]).ps32(); - - if (i < 4) - vmulps(ymm1, ymm3, Ymm(4 + i)); - else - vmulps(ymm1, ymm3, ptr[g_const->m_shift_256b[i + 1]]); - vcvttps2dq(ymm1, ymm1); - vpackssdw(ymm1, ymm1); - - // m_local.d[i].ga = g.upl16(a); - - vpunpcklwd(ymm0, ymm1); - vmovdqa(ptr[&m_local.d[i].ga], ymm0); - } - } - else - { - // GSVector8i c = GSVector8i(GSVector8(vertex[index[last]].c)); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * last]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - } - - vbroadcasti128(ymm0, ptr[ecx + offsetof(GSVertexSW, c)]); - vcvttps2dq(ymm0, ymm0); - - // c = c.upl16(c.zwxy()); - - vpshufd(ymm1, ymm0, _MM_SHUFFLE(1, 0, 3, 2)); - vpunpcklwd(ymm0, ymm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - vpsrlw(ymm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - vpshufd(ymm1, ymm0, _MM_SHUFFLE(0, 0, 0, 0)); - vpshufd(ymm2, ymm0, _MM_SHUFFLE(2, 2, 2, 2)); - - vmovdqa(ptr[&m_local.c.rb], ymm1); - vmovdqa(ptr[&m_local.c.ga], ymm2); - } -} - -#endif diff --git a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.cpp b/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.cpp deleted file mode 100644 index 0b70d8ffc7cc8..0000000000000 --- a/pcsx2/GS/Renderers/SW/GSSetupPrimCodeGenerator.x86.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "GSSetupPrimCodeGenerator.h" -#include "GSVertexSW.h" -#include "GS/GS_codegen.h" - -#if _M_SSE < 0x501 && !(defined(_M_AMD64) || defined(_WIN64)) - -static const int _args = 0; -static const int _vertex = _args + 4; -static const int _index = _args + 8; -static const int _dscan = _args + 12; - -void GSSetupPrimCodeGenerator::Generate_SSE() -{ - if ((m_en.z || m_en.f) && m_sel.prim != GS_SPRITE_CLASS || m_en.t || m_en.c && m_sel.iip) - { - mov(edx, dword[esp + _dscan]); - - for (int i = 0; i < (m_sel.notest ? 2 : 5); i++) - { - movaps(Xmm(3 + i), ptr[g_const->m_shift_128b[i]]); - } - } - - Depth_SSE(); - - Texture_SSE(); - - Color_SSE(); - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth_SSE() -{ - if (!m_en.z && !m_en.f) - { - return; - } - - if (m_sel.prim != GS_SPRITE_CLASS) - { - // GSVector4 p = dscan.p; - - movaps(xmm0, ptr[edx + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // GSVector4 df = p.wwww(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - // m_local.d4.f = GSVector4i(df * 4.0f).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(ptr[&m_local.d4.f], xmm2); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(ptr[&m_local.d[i].f], xmm2); - } - } - - if (m_en.z) - { - // GSVector4 dz = p.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - // m_local.d4.z = dz * 4.0f; - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - movdqa(ptr[&m_local.d4.z], xmm1); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // m_local.d[i].z = dz * m_shift[i]; - - movaps(xmm1, xmm0); - mulps(xmm1, Xmm(4 + i)); - movdqa(ptr[&m_local.d[i].z], xmm1); - } - } - } - else - { - // GSVector4 p = vertex[index[1]].p; - - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * 1]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - - movaps(xmm0, ptr[ecx + offsetof(GSVertexSW, p)]); - - if (m_en.f) - { - // m_local.p.f = GSVector4i(p).zzzzh().zzzz(); - - cvttps2dq(xmm1, xmm0); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - movdqa(ptr[&m_local.p.f], xmm1); - } - - if (m_en.z) - { - // uint32 z is bypassed in t.w - - movdqa(xmm0, ptr[ecx + offsetof(GSVertexSW, t)]); - pshufd(xmm0, xmm0, _MM_SHUFFLE(3, 3, 3, 3)); - movdqa(ptr[&m_local.p.z], xmm0); - } - } -} - -void GSSetupPrimCodeGenerator::Texture_SSE() -{ - if (!m_en.t) - { - return; - } - - // GSVector4 t = dscan.t; - - movaps(xmm0, ptr[edx + offsetof(GSVertexSW, t)]); - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - - if (m_sel.fst) - { - // m_local.d4.stq = GSVector4i(t * 4.0f); - - cvttps2dq(xmm1, xmm1); - - movdqa(ptr[&m_local.d4.stq], xmm1); - } - else - { - // m_local.d4.stq = t * 4.0f; - - movaps(ptr[&m_local.d4.stq], xmm1); - } - - for (int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector4 ds = t.xxxx(); - // GSVector4 dt = t.yyyy(); - // GSVector4 dq = t.zzzz(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4 v = ds/dt * m_shift[i]; - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - - if (m_sel.fst) - { - // m_local.d[i].s/t = GSVector4i(v); - - cvttps2dq(xmm2, xmm2); - - switch (j) - { - case 0: movdqa(ptr[&m_local.d[i].s], xmm2); break; - case 1: movdqa(ptr[&m_local.d[i].t], xmm2); break; - } - } - else - { - // m_local.d[i].s/t/q = v; - - switch (j) - { - case 0: movaps(ptr[&m_local.d[i].s], xmm2); break; - case 1: movaps(ptr[&m_local.d[i].t], xmm2); break; - case 2: movaps(ptr[&m_local.d[i].q], xmm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color_SSE() -{ - if (!m_en.c) - { - return; - } - - if (m_sel.iip) - { - // GSVector4 c = dscan.c; - - movaps(xmm0, ptr[edx + offsetof(GSVertexSW, c)]); - movaps(xmm1, xmm0); - - // m_local.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshufd(xmm2, xmm2, _MM_SHUFFLE(3, 1, 2, 0)); - packssdw(xmm2, xmm2); - movdqa(ptr[&m_local.d4.c], xmm2); - - // xmm3 is not needed anymore - - // GSVector4 dr = c.xxxx(); - // GSVector4 db = c.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_local.d[i].rb = r.upl16(b); - - punpcklwd(xmm2, xmm3); - movdqa(ptr[&m_local.d[i].rb], xmm2); - } - - // GSVector4 c = dscan.c; - - movaps(xmm0, ptr[edx + offsetof(GSVertexSW, c)]); // not enough regs, have to reload it - movaps(xmm1, xmm0); - - // GSVector4 dg = c.yyyy(); - // GSVector4 da = c.wwww(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - for (int i = 0; i < (m_sel.notest ? 1 : 4); i++) - { - // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_local.d[i].ga = g.upl16(a); - - punpcklwd(xmm2, xmm3); - movdqa(ptr[&m_local.d[i].ga], xmm2); - } - } - else - { - // GSVector4i c = GSVector4i(vertex[index[last].c); - - int last = 0; - - switch (m_sel.prim) - { - case GS_POINT_CLASS: last = 0; break; - case GS_LINE_CLASS: last = 1; break; - case GS_TRIANGLE_CLASS: last = 2; break; - case GS_SPRITE_CLASS: last = 1; break; - } - - if (!(m_sel.prim == GS_SPRITE_CLASS && (m_en.z || m_en.f))) // if this is a sprite, the last vertex was already loaded in Depth() - { - mov(ecx, ptr[esp + _index]); - mov(ecx, ptr[ecx + sizeof(uint32) * last]); - shl(ecx, 6); // * sizeof(GSVertexSW) - add(ecx, ptr[esp + _vertex]); - } - - cvttps2dq(xmm0, ptr[ecx + offsetof(GSVertexSW, c)]); - - // c = c.upl16(c.zwxy()); - - pshufd(xmm1, xmm0, _MM_SHUFFLE(1, 0, 3, 2)); - punpcklwd(xmm0, xmm1); - - // if(!tme) c = c.srl16(7); - - if (m_sel.tfx == TFX_NONE) - { - psrlw(xmm0, 7); - } - - // m_local.c.rb = c.xxxx(); - // m_local.c.ga = c.zzzz(); - - pshufd(xmm1, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm2, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - movdqa(ptr[&m_local.c.rb], xmm1); - movdqa(ptr[&m_local.c.ga], xmm2); - } -} - -#endif diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index e794b02cfa1ff..a19003ec4fdc4 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -466,12 +466,8 @@ - - - - - - + + @@ -490,12 +486,7 @@ - - - - - - + @@ -815,7 +806,6 @@ - @@ -834,6 +824,8 @@ + + @@ -853,6 +845,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 038d3a920ae38..c2fa268ccb200 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1517,22 +1517,10 @@ System\Ps2\GS\Renderers\Software - + System\Ps2\GS\Renderers\Software - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - + System\Ps2\GS\Renderers\Software @@ -1541,24 +1529,6 @@ System\Ps2\GS\Renderers\Software - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - System\Ps2\GS\Renderers\Software @@ -2508,9 +2478,6 @@ System\Ps2\GS - - System\Ps2\GS - System\Ps2\GS @@ -2631,6 +2598,12 @@ System\Ps2\GS\Renderers\Software + + System\Ps2\GS\Renderers\Software + + + System\Ps2\GS\Renderers\Software + System\Ps2\GS\Renderers\Software @@ -2640,6 +2613,9 @@ System\Ps2\GS\Renderers\Software + + System\Ps2\GS\Renderers\Software + System\Ps2\GS\Renderers\Software