From 608bb5ccb26a26368b27bba23822c5452ff0e183 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Tue, 22 Nov 2016 19:00:06 +0100 Subject: [PATCH] gsdx sw x64: add AVX2 implementation for VS FS was copied from 32 bits (require massive update) --- plugins/GSdx/CMakeLists.txt | 14 +- .../GSDrawScanlineCodeGenerator.x64.avx2.cpp | 3055 +++++++++++++++++ .../GSDrawScanlineCodeGenerator.x86.avx2.cpp | 2 +- plugins/GSdx/GSSetupPrimCodeGenerator.cpp | 20 +- plugins/GSdx/GSSetupPrimCodeGenerator.h | 9 +- .../GSSetupPrimCodeGenerator.x64.avx2.cpp | 376 ++ .../GSSetupPrimCodeGenerator.x86.avx2.cpp | 18 +- plugins/GSdx/GSdx.vcxproj | 2 + plugins/GSdx/GSdx.vcxproj.filters | 6 + 9 files changed, 3469 insertions(+), 33 deletions(-) create mode 100644 plugins/GSdx/GSDrawScanlineCodeGenerator.x64.avx2.cpp create mode 100644 plugins/GSdx/GSSetupPrimCodeGenerator.x64.avx2.cpp diff --git a/plugins/GSdx/CMakeLists.txt b/plugins/GSdx/CMakeLists.txt index 0fec3cef05..5bfbebe9b5 100644 --- a/plugins/GSdx/CMakeLists.txt +++ b/plugins/GSdx/CMakeLists.txt @@ -65,11 +65,12 @@ set(GSdxSources GSDrawingContext.cpp GSDrawScanline.cpp GSDrawScanlineCodeGenerator.cpp + GSDrawScanlineCodeGenerator.x64.cpp + GSDrawScanlineCodeGenerator.x64.avx.cpp + GSDrawScanlineCodeGenerator.x64.avx2.cpp + GSDrawScanlineCodeGenerator.x86.cpp GSDrawScanlineCodeGenerator.x86.avx.cpp GSDrawScanlineCodeGenerator.x86.avx2.cpp - GSDrawScanlineCodeGenerator.x64.cpp - GSDrawScanlineCodeGenerator.x86.cpp - GSDrawScanlineCodeGenerator.x64.avx.cpp GSDump.cpp GSFunctionMap.cpp GSHwHack.cpp @@ -87,11 +88,12 @@ set(GSdxSources GSRendererSW.cpp GSSetting.cpp GSSetupPrimCodeGenerator.cpp + GSSetupPrimCodeGenerator.x64.cpp + GSSetupPrimCodeGenerator.x64.avx.cpp + GSSetupPrimCodeGenerator.x64.avx2.cpp + GSSetupPrimCodeGenerator.x86.cpp GSSetupPrimCodeGenerator.x86.avx.cpp GSSetupPrimCodeGenerator.x86.avx2.cpp - GSSetupPrimCodeGenerator.x64.avx.cpp - GSSetupPrimCodeGenerator.x86.cpp - GSSetupPrimCodeGenerator.x64.cpp GSShaderOGL.cpp GSState.cpp GSTables.cpp diff --git a/plugins/GSdx/GSDrawScanlineCodeGenerator.x64.avx2.cpp b/plugins/GSdx/GSDrawScanlineCodeGenerator.x64.avx2.cpp new file mode 100644 index 0000000000..42c74fcb7a --- /dev/null +++ b/plugins/GSdx/GSDrawScanlineCodeGenerator.x64.avx2.cpp @@ -0,0 +1,3055 @@ +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSDrawScanlineCodeGenerator.h" +#include "GSVertexSW.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; + +// 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)&m_test[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)&m_test[0]]); + vpmovsxbd(ymm0, ptr[eax * 8 + (size_t)&m_test[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)&m_test[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[&GSDrawScanlineCodeGenerator::m_log2_coef[3]]); + + // ymm4 = mant(q) | 1.0f + + if(m_cpu.has(util::Cpu::tFMA)) + { + vmovaps(ymm5, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[0]]); // c0 + vfmadd213ps(ymm5, ymm4, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[1]]); // c0 * ymm4 + c1 + vfmadd213ps(ymm5, ymm4, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[2]]); // (c0 * ymm4 + c1) * ymm4 + c2 + vsubps(ymm4, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[3]]); // ymm4 - 1.0f + vfmadd213ps(ymm4, ymm5, ymm0); // ((c0 * ymm4 + c1) * ymm4 + c2) * (ymm4 - 1.0f) + ymm0 + } + else + { + vmulps(ymm5, ymm4, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[0]]); + vaddps(ymm5, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[1]]); + vmulps(ymm5, ymm4); + vsubps(ymm4, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[3]]); + vaddps(ymm5, ptr[&GSDrawScanlineCodeGenerator::m_log2_coef[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/plugins/GSdx/GSDrawScanlineCodeGenerator.x86.avx2.cpp b/plugins/GSdx/GSDrawScanlineCodeGenerator.x86.avx2.cpp index b2be17df92..29a8b7130b 100644 --- a/plugins/GSdx/GSDrawScanlineCodeGenerator.x86.avx2.cpp +++ b/plugins/GSdx/GSDrawScanlineCodeGenerator.x86.avx2.cpp @@ -31,7 +31,7 @@ static const int _v = _args + 8; void GSDrawScanlineCodeGenerator::Generate() { -//ret(8); + //ret(8); push(ebx); push(esi); diff --git a/plugins/GSdx/GSSetupPrimCodeGenerator.cpp b/plugins/GSdx/GSSetupPrimCodeGenerator.cpp index 018e9d8bcf..ca170bd483 100644 --- a/plugins/GSdx/GSSetupPrimCodeGenerator.cpp +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.cpp @@ -77,19 +77,15 @@ GSSetupPrimCodeGenerator::GSSetupPrimCodeGenerator(void* param, uint64 key, void m_en.c = m_sel.fb && !(m_sel.tfx == TFX_DECAL && m_sel.tcc) ? 1 : 0; try { - Generate(); +#if _M_SSE >= 0x501 + Generate_AVX2(); +#else + if(g_cpu.has(util::Cpu::tAVX)) + Generate_AVX(); + else + Generate_SSE(); +#endif } catch (std::exception& e) { fprintf(stderr, "ERR:GSSetupPrimCodeGenerator %s\n", e.what()); } } - -#if _M_SSE >= 0x501 -#else -void GSSetupPrimCodeGenerator::Generate() -{ - if(g_cpu.has(util::Cpu::tAVX)) - Generate_AVX(); - else - Generate_SSE(); -} -#endif diff --git a/plugins/GSdx/GSSetupPrimCodeGenerator.h b/plugins/GSdx/GSSetupPrimCodeGenerator.h index 05918c0258..dee01fddc7 100644 --- a/plugins/GSdx/GSSetupPrimCodeGenerator.h +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.h @@ -35,8 +35,6 @@ class GSSetupPrimCodeGenerator : public GSCodeGenerator struct {uint32 z:1, f:1, t:1, c:1;} m_en; - void Generate(); - #if _M_SSE < 0x501 void Generate_SSE(); void Depth_SSE(); @@ -48,9 +46,10 @@ class GSSetupPrimCodeGenerator : public GSCodeGenerator void Texture_AVX(); void Color_AVX(); #else - void Depth(); - void Texture(); - void Color(); + void Generate_AVX2(); + void Depth_AVX2(); + void Texture_AVX2(); + void Color_AVX2(); #endif public: diff --git a/plugins/GSdx/GSSetupPrimCodeGenerator.x64.avx2.cpp b/plugins/GSdx/GSSetupPrimCodeGenerator.x64.avx2.cpp new file mode 100644 index 0000000000..6b080b74b7 --- /dev/null +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.x64.avx2.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2016-2016 Gregory + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSSetupPrimCodeGenerator.h" +#include "GSVertexSW.h" + +#if _M_SSE >= 0x501 && (defined(_M_AMD64) || defined(_WIN64)) + +using namespace Xbyak; + +#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)&m_shift[0]); + + 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/plugins/GSdx/GSSetupPrimCodeGenerator.x86.avx2.cpp b/plugins/GSdx/GSSetupPrimCodeGenerator.x86.avx2.cpp index 172d053a5a..e1f7cc995e 100644 --- a/plugins/GSdx/GSSetupPrimCodeGenerator.x86.avx2.cpp +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.x86.avx2.cpp @@ -32,7 +32,7 @@ static const int _vertex = _args + 4; static const int _index = _args + 8; static const int _dscan = _args + 12; -void GSSetupPrimCodeGenerator::Generate() +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) { @@ -44,16 +44,16 @@ void GSSetupPrimCodeGenerator::Generate() } } - Depth(); + Depth_AVX2(); - Texture(); + Texture_AVX2(); - Color(); + Color_AVX2(); ret(); } -void GSSetupPrimCodeGenerator::Depth() +void GSSetupPrimCodeGenerator::Depth_AVX2() { if(!m_en.z && !m_en.f) { @@ -74,7 +74,7 @@ void GSSetupPrimCodeGenerator::Depth() vextractps(ptr[&m_local.d8.p.z], xmm1, 2); } - + if(m_en.f) { // m_local.d8.p.f = GSVector4i(dp8).extract32<3>(); @@ -149,7 +149,7 @@ void GSSetupPrimCodeGenerator::Depth() } } -void GSSetupPrimCodeGenerator::Texture() +void GSSetupPrimCodeGenerator::Texture_AVX2() { if(!m_en.t) { @@ -219,7 +219,7 @@ void GSSetupPrimCodeGenerator::Texture() } } -void GSSetupPrimCodeGenerator::Color() +void GSSetupPrimCodeGenerator::Color_AVX2() { if(!m_en.c) { @@ -350,4 +350,4 @@ void GSSetupPrimCodeGenerator::Color() } } -#endif \ No newline at end of file +#endif diff --git a/plugins/GSdx/GSdx.vcxproj b/plugins/GSdx/GSdx.vcxproj index b9cef5b385..e7f6988ac2 100644 --- a/plugins/GSdx/GSdx.vcxproj +++ b/plugins/GSdx/GSdx.vcxproj @@ -147,6 +147,7 @@ + @@ -173,6 +174,7 @@ + diff --git a/plugins/GSdx/GSdx.vcxproj.filters b/plugins/GSdx/GSdx.vcxproj.filters index fcc3b30d05..9e2983e577 100644 --- a/plugins/GSdx/GSdx.vcxproj.filters +++ b/plugins/GSdx/GSdx.vcxproj.filters @@ -231,6 +231,9 @@ Source Files + + Source Files + Source Files @@ -249,6 +252,9 @@ Source Files + + Source Files + Source Files