mirror of https://github.com/PCSX2/pcsx2.git
2958 lines
55 KiB
C++
2958 lines
55 KiB
C++
/*
|
|
* 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;
|
|
|
|
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);
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
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
|