mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-09-25 14:05:49 +02:00
This pull request is for the pending reorganization of the folder structure on GSdx, making it better organized and easier to work with. Also remove unused GSTextureFX.cpp file.
2972 lines
61 KiB
C++
2972 lines
61 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 "GSDrawScanline.h"
|
|
#include "GSTextureCacheSW.h"
|
|
|
|
// Lack of a better home
|
|
std::unique_ptr<GSScanlineConstantData> g_const(new GSScanlineConstantData());
|
|
|
|
GSDrawScanline::GSDrawScanline()
|
|
: m_sp_map("GSSetupPrim", &m_local)
|
|
, m_ds_map("GSDrawScanline", &m_local)
|
|
{
|
|
memset(&m_local, 0, sizeof(m_local));
|
|
|
|
m_local.gd = &m_global;
|
|
}
|
|
|
|
void GSDrawScanline::BeginDraw(const GSRasterizerData* data)
|
|
{
|
|
memcpy(&m_global, &((const SharedData*)data)->global, sizeof(m_global));
|
|
|
|
if(m_global.sel.mmin && m_global.sel.lcm)
|
|
{
|
|
#if defined(__GNUC__) && _M_SSE >= 0x501
|
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80286
|
|
//
|
|
// GCC 4.9/5/6 doesn't generate correct AVX2 code for extract32<0>. It is fixed in GCC7
|
|
// Intrinsic code is _mm_cvtsi128_si32(_mm256_castsi256_si128(m))
|
|
// It seems recent Clang got _mm256_cvtsi256_si32(m) instead. I don't know about GCC.
|
|
//
|
|
// Generated code keep the integer in an XMM register but bit [64:32] aren't cleared.
|
|
// So the srl16 shift will be huge and v will be 0.
|
|
//
|
|
int lod_x = m_global.lod.i.x0;
|
|
GSVector4i v = m_global.t.minmax.srl16(lod_x);
|
|
#else
|
|
GSVector4i v = m_global.t.minmax.srl16(m_global.lod.i.extract32<0>());//.x);
|
|
#endif
|
|
|
|
v = v.upl16(v);
|
|
|
|
m_local.temp.uv_minmax[0] = v.upl32(v);
|
|
m_local.temp.uv_minmax[1] = v.uph32(v);
|
|
}
|
|
|
|
m_ds = m_ds_map[m_global.sel];
|
|
|
|
if(m_global.sel.aa1)
|
|
{
|
|
GSScanlineSelector sel;
|
|
|
|
sel.key = m_global.sel.key;
|
|
sel.zwrite = 0;
|
|
sel.edge = 1;
|
|
|
|
m_de = m_ds_map[sel];
|
|
}
|
|
else
|
|
{
|
|
m_de = NULL;
|
|
}
|
|
|
|
if(m_global.sel.IsSolidRect())
|
|
{
|
|
m_dr = (DrawRectPtr)&GSDrawScanline::DrawRect;
|
|
}
|
|
else
|
|
{
|
|
m_dr = NULL;
|
|
}
|
|
|
|
// doesn't need all bits => less functions generated
|
|
|
|
GSScanlineSelector sel;
|
|
|
|
sel.key = 0;
|
|
|
|
sel.iip = m_global.sel.iip;
|
|
sel.tfx = m_global.sel.tfx;
|
|
sel.tcc = m_global.sel.tcc;
|
|
sel.fst = m_global.sel.fst;
|
|
sel.fge = m_global.sel.fge;
|
|
sel.prim = m_global.sel.prim;
|
|
sel.fb = m_global.sel.fb;
|
|
sel.zb = m_global.sel.zb;
|
|
sel.zoverflow = m_global.sel.zoverflow;
|
|
sel.notest = m_global.sel.notest;
|
|
|
|
m_sp = m_sp_map[sel];
|
|
}
|
|
|
|
void GSDrawScanline::EndDraw(uint64 frame, uint64 ticks, int actual, int total)
|
|
{
|
|
m_ds_map.UpdateStats(frame, ticks, actual, total);
|
|
}
|
|
|
|
#ifndef ENABLE_JIT_RASTERIZER
|
|
|
|
void GSDrawScanline::SetupPrim(const GSVertexSW* vertex, const uint32* index, const GSVertexSW& dscan)
|
|
{
|
|
GSScanlineSelector sel = m_global.sel;
|
|
|
|
bool has_z = sel.zb != 0;
|
|
bool has_f = sel.fb && sel.fge;
|
|
bool has_t = sel.fb && sel.tfx != TFX_NONE;
|
|
bool has_c = sel.fb && !(sel.tfx == TFX_DECAL && sel.tcc);
|
|
|
|
#if _M_SSE >= 0x501
|
|
|
|
const GSVector8* shift = (GSVector8*)g_const->m_shift_256b;
|
|
|
|
if(has_z || has_f)
|
|
{
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
GSVector4 dp8 = dscan.p * GSVector4::broadcast32(&shift[0]);
|
|
|
|
if(has_f)
|
|
{
|
|
m_local.d8.p.f = GSVector4i(dp8).extract32<3>();
|
|
|
|
GSVector8 df = GSVector8::broadcast32(&dscan.p.w);
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
m_local.d[i].f = GSVector8i(df * shift[1 + i]).xxzzlh();
|
|
}
|
|
}
|
|
|
|
if(has_z)
|
|
{
|
|
m_local.d8.p.z = dp8.extract32<2>();
|
|
|
|
GSVector8 dz = GSVector8::broadcast32(&dscan.p.z);
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
m_local.d[i].z = dz * shift[1 + i];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(has_f)
|
|
{
|
|
m_local.p.f = GSVector4i(vertex[index[1]].p).extract32<3>();
|
|
}
|
|
|
|
if(has_z)
|
|
{
|
|
m_local.p.z = vertex[index[1]].t.u32[3]; // uint32 z is bypassed in t.w
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_t)
|
|
{
|
|
GSVector4 dt8 = dscan.t * GSVector4::broadcast32(&shift[0]);
|
|
|
|
if(sel.fst)
|
|
{
|
|
m_local.d8.stq = GSVector4::cast(GSVector4i(dt8));
|
|
}
|
|
else
|
|
{
|
|
m_local.d8.stq = dt8;
|
|
}
|
|
|
|
GSVector8 dt(dscan.t);
|
|
|
|
for(int j = 0, k = sel.fst ? 2 : 3; j < k; j++)
|
|
{
|
|
GSVector8 dstq;
|
|
|
|
switch(j)
|
|
{
|
|
case 0: dstq = dt.xxxx(); break;
|
|
case 1: dstq = dt.yyyy(); break;
|
|
case 2: dstq = dt.zzzz(); break;
|
|
}
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
GSVector8 v = dstq * shift[1 + i];
|
|
|
|
if(sel.fst)
|
|
{
|
|
switch(j)
|
|
{
|
|
case 0: m_local.d[i].s = GSVector8::cast(GSVector8i(v)); break;
|
|
case 1: m_local.d[i].t = GSVector8::cast(GSVector8i(v)); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(j)
|
|
{
|
|
case 0: m_local.d[i].s = v; break;
|
|
case 1: m_local.d[i].t = v; break;
|
|
case 2: m_local.d[i].q = v; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_c)
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
GSVector4 dc8 = dscan.c * GSVector4::broadcast32(&shift[0]);
|
|
|
|
GSVector4i::storel(&m_local.d8.c, GSVector4i(dc8).xzyw().ps32());
|
|
|
|
GSVector8 dc(dscan.c);
|
|
|
|
GSVector8 dr = dc.xxxx();
|
|
GSVector8 db = dc.zzzz();
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
GSVector8i r = GSVector8i(dr * shift[1 + i]).ps32();
|
|
GSVector8i b = GSVector8i(db * shift[1 + i]).ps32();
|
|
|
|
m_local.d[i].rb = r.upl16(b);
|
|
}
|
|
|
|
GSVector8 dg = dc.yyyy();
|
|
GSVector8 da = dc.wwww();
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
GSVector8i g = GSVector8i(dg * shift[1 + i]).ps32();
|
|
GSVector8i a = GSVector8i(da * shift[1 + i]).ps32();
|
|
|
|
m_local.d[i].ga = g.upl16(a);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int last = 0;
|
|
|
|
switch(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;
|
|
}
|
|
|
|
GSVector8i c = GSVector8i(GSVector8(vertex[index[last]].c));
|
|
|
|
c = c.upl16(c.zwxy());
|
|
|
|
if(sel.tfx == TFX_NONE) c = c.srl16(7);
|
|
|
|
m_local.c.rb = c.xxxx();
|
|
m_local.c.ga = c.zzzz();
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
const GSVector4* shift = (GSVector4*)g_const->m_shift_128b;
|
|
|
|
if(has_z || has_f)
|
|
{
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
if(has_f)
|
|
{
|
|
GSVector4 df = dscan.p.wwww();
|
|
|
|
m_local.d4.f = GSVector4i(df * shift[0]).xxzzlh();
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
m_local.d[i].f = GSVector4i(df * shift[1 + i]).xxzzlh();
|
|
}
|
|
}
|
|
|
|
if(has_z)
|
|
{
|
|
GSVector4 dz = dscan.p.zzzz();
|
|
|
|
m_local.d4.z = dz * shift[0];
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
m_local.d[i].z = dz * shift[1 + i];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(has_f)
|
|
{
|
|
m_local.p.f = GSVector4i(vertex[index[1]].p).zzzzh().zzzz();
|
|
}
|
|
|
|
if(has_z)
|
|
{
|
|
m_local.p.z = vertex[index[1]].t.u32[3]; // uint32 z is bypassed in t.w
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_t)
|
|
{
|
|
GSVector4 t = dscan.t;
|
|
|
|
if(sel.fst)
|
|
{
|
|
m_local.d4.stq = GSVector4::cast(GSVector4i(t * shift[0]));
|
|
}
|
|
else
|
|
{
|
|
m_local.d4.stq = t * shift[0];
|
|
}
|
|
|
|
for(int j = 0, k = sel.fst ? 2 : 3; j < k; j++)
|
|
{
|
|
GSVector4 dstq;
|
|
|
|
switch(j)
|
|
{
|
|
case 0: dstq = t.xxxx(); break;
|
|
case 1: dstq = t.yyyy(); break;
|
|
case 2: dstq = t.zzzz(); break;
|
|
}
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
GSVector4 v = dstq * shift[1 + i];
|
|
|
|
if(sel.fst)
|
|
{
|
|
switch(j)
|
|
{
|
|
case 0: m_local.d[i].s = GSVector4::cast(GSVector4i(v)); break;
|
|
case 1: m_local.d[i].t = GSVector4::cast(GSVector4i(v)); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(j)
|
|
{
|
|
case 0: m_local.d[i].s = v; break;
|
|
case 1: m_local.d[i].t = v; break;
|
|
case 2: m_local.d[i].q = v; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_c)
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
m_local.d4.c = GSVector4i(dscan.c * shift[0]).xzyw().ps32();
|
|
|
|
GSVector4 dr = dscan.c.xxxx();
|
|
GSVector4 db = dscan.c.zzzz();
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
GSVector4i r = GSVector4i(dr * shift[1 + i]).ps32();
|
|
GSVector4i b = GSVector4i(db * shift[1 + i]).ps32();
|
|
|
|
m_local.d[i].rb = r.upl16(b);
|
|
}
|
|
|
|
GSVector4 dg = dscan.c.yyyy();
|
|
GSVector4 da = dscan.c.wwww();
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
GSVector4i g = GSVector4i(dg * shift[1 + i]).ps32();
|
|
GSVector4i a = GSVector4i(da * shift[1 + i]).ps32();
|
|
|
|
m_local.d[i].ga = g.upl16(a);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int last = 0;
|
|
|
|
switch(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;
|
|
}
|
|
|
|
GSVector4i c = GSVector4i(vertex[index[last]].c);
|
|
|
|
c = c.upl16(c.zwxy());
|
|
|
|
if(sel.tfx == TFX_NONE) c = c.srl16(7);
|
|
|
|
m_local.c.rb = c.xxxx();
|
|
m_local.c.ga = c.zzzz();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void GSDrawScanline::DrawScanline(int pixels, int left, int top, const GSVertexSW& scan)
|
|
{
|
|
GSScanlineSelector sel = m_global.sel;
|
|
|
|
#if _M_SSE >= 0x501
|
|
|
|
GSVector8i test;
|
|
GSVector8 zo;
|
|
GSVector8i f;
|
|
GSVector8 s, t, q;
|
|
GSVector8i uf, vf;
|
|
GSVector8i rbf, gaf;
|
|
GSVector8i cov;
|
|
|
|
// Init
|
|
|
|
int skip, steps;
|
|
|
|
if(!sel.notest)
|
|
{
|
|
skip = left & 7;
|
|
steps = pixels + skip - 8;
|
|
left -= skip;
|
|
test = GSVector8i::i8to32c(g_const->m_test_256b[skip]) | GSVector8i::i8to32c(g_const->m_test_256b[15 + (steps & (steps >> 31))]);
|
|
}
|
|
else
|
|
{
|
|
skip = 0;
|
|
steps = pixels - 8;
|
|
}
|
|
|
|
ASSERT((left & 7) == 0);
|
|
|
|
const GSVector2i* fza_base = &m_global.fzbr[top];
|
|
const GSVector2i* fza_offset = &m_global.fzbc[left >> 2];
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
f = GSVector8i::broadcast16(GSVector4i(scan.p).srl<12>()).add16(m_local.d[skip].f);
|
|
}
|
|
|
|
if(sel.zb)
|
|
{
|
|
zo = m_local.d[skip].z;
|
|
}
|
|
}
|
|
|
|
if(sel.fb)
|
|
{
|
|
if(sel.edge)
|
|
{
|
|
cov = GSVector8i::broadcast16(GSVector4i::cast(scan.t).srl<12>()).srl16(9);
|
|
}
|
|
|
|
if(sel.tfx != TFX_NONE)
|
|
{
|
|
if(sel.fst)
|
|
{
|
|
GSVector4i vt(scan.t);
|
|
|
|
GSVector8i u = GSVector8i::broadcast32(vt.xxxx()) + GSVector8i::cast(m_local.d[skip].s);
|
|
GSVector8i v = GSVector8i::broadcast32(vt.yyyy());
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS || sel.mmin)
|
|
{
|
|
v += GSVector8i::cast(m_local.d[skip].t);
|
|
}
|
|
else if(sel.ltf)
|
|
{
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
s = GSVector8::cast(u);
|
|
t = GSVector8::cast(v);
|
|
}
|
|
else
|
|
{
|
|
s = GSVector8::broadcast32(&scan.t.x) + m_local.d[skip].s;
|
|
t = GSVector8::broadcast32(&scan.t.y) + m_local.d[skip].t;
|
|
q = GSVector8::broadcast32(&scan.t.z) + m_local.d[skip].q;
|
|
}
|
|
}
|
|
|
|
if(!(sel.tfx == TFX_DECAL && sel.tcc))
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
GSVector4i c(scan.c);
|
|
|
|
c = c.upl16(c.zwxy());
|
|
|
|
rbf = GSVector8i::broadcast32(&c.x).add16(m_local.d[skip].rb);
|
|
gaf = GSVector8i::broadcast32(&c.z).add16(m_local.d[skip].ga);
|
|
}
|
|
else
|
|
{
|
|
rbf = m_local.c.rb;
|
|
gaf = m_local.c.ga;
|
|
}
|
|
}
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
do
|
|
{
|
|
int fa = 0, za = 0;
|
|
GSVector8i fd, zs, zd;
|
|
GSVector8i fm, zm;
|
|
GSVector8i rb, ga;
|
|
|
|
// TestZ
|
|
|
|
if(sel.zb)
|
|
{
|
|
za = (fza_base->y + fza_offset->y) % HALF_VM_SIZE;
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
GSVector8 z = GSVector8::broadcast32(&scan.p.z) + zo;
|
|
|
|
if(sel.zoverflow)
|
|
{
|
|
zs = (GSVector8i(z * 0.5f) << 1) | (GSVector8i(z) & GSVector8i::x00000001());
|
|
}
|
|
else
|
|
{
|
|
zs = GSVector8i(z);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
zs = GSVector8i::broadcast32(&m_local.p.z);
|
|
}
|
|
|
|
if(sel.ztest)
|
|
{
|
|
zd = GSVector8i::load(
|
|
(uint8*)m_global.vm + za * 2, (uint8*)m_global.vm + za * 2 + 16,
|
|
(uint8*)m_global.vm + za * 2 + 32, (uint8*)m_global.vm + za * 2 + 48);
|
|
|
|
switch(sel.zpsm)
|
|
{
|
|
case 1: zd = zd.sll32(8).srl32(8); break;
|
|
case 2: zd = zd.sll32(16).srl32(16); break;
|
|
default: break;
|
|
}
|
|
|
|
GSVector8i zso = zs;
|
|
GSVector8i zdo = zd;
|
|
|
|
if(sel.zoverflow || sel.zpsm == 0)
|
|
{
|
|
zso -= GSVector8i::x80000000();
|
|
zdo -= GSVector8i::x80000000();
|
|
}
|
|
|
|
switch(sel.ztst)
|
|
{
|
|
case ZTST_GEQUAL: test |= zso < zdo; break;
|
|
case ZTST_GREATER: test |= zso <= zdo; break;
|
|
}
|
|
|
|
if(test.alltrue()) continue;
|
|
}
|
|
}
|
|
|
|
// SampleTexture
|
|
|
|
if(sel.fb && sel.tfx != TFX_NONE)
|
|
{
|
|
GSVector8i u, v, uv[2];
|
|
GSVector8i lodi, lodf;
|
|
GSVector8i minuv, maxuv;
|
|
GSVector8i addr00, addr01, addr10, addr11;
|
|
GSVector8i c00, c01, c10, c11;
|
|
|
|
if(sel.mmin)
|
|
{
|
|
if(!sel.fst)
|
|
{
|
|
u = GSVector8i(s / q);
|
|
v = GSVector8i(t / q);
|
|
}
|
|
else
|
|
{
|
|
u = GSVector8i::cast(s);
|
|
v = GSVector8i::cast(t);
|
|
}
|
|
|
|
if(!sel.lcm)
|
|
{
|
|
GSVector8 tmp = q.log2(3) * m_global.l + m_global.k; // (-log2(Q) * (1 << L) + K) * 0x10000
|
|
|
|
GSVector8i lod = GSVector8i(tmp.sat(GSVector8::zero(), m_global.mxl), false);
|
|
|
|
if(sel.mmin == 1) // round-off mode
|
|
{
|
|
lod += 0x8000;
|
|
}
|
|
|
|
lodi = lod.srl32(16);
|
|
|
|
if(sel.mmin == 2) // trilinear mode
|
|
{
|
|
lodf = lod.xxzzlh();
|
|
}
|
|
|
|
// shift u/v by (int)lod
|
|
|
|
u = u.srav32(lodi);
|
|
v = v.srav32(lodi);
|
|
|
|
uv[0] = u.srav32(lodi);
|
|
uv[1] = v.srav32(lodi);
|
|
|
|
GSVector8i tmin = GSVector8i::broadcast128(m_global.t.min);
|
|
GSVector8i tminu = tmin.upl16().srlv32(lodi);
|
|
GSVector8i tminv = tmin.uph16().srlv32(lodi);
|
|
|
|
GSVector8i tmax = GSVector8i::broadcast128(m_global.t.max);
|
|
GSVector8i tmaxu = tmax.upl16().srlv32(lodi);
|
|
GSVector8i tmaxv = tmax.uph16().srlv32(lodi);
|
|
|
|
minuv = tminu.pu32(tminv);
|
|
maxuv = tmaxu.pu32(tmaxv);
|
|
}
|
|
else
|
|
{
|
|
lodi = m_global.lod.i;
|
|
|
|
u = u.srav32(lodi);
|
|
v = v.srav32(lodi);
|
|
|
|
uv[0] = u;
|
|
uv[1] = v;
|
|
|
|
minuv = m_local.temp.uv_minmax[0];
|
|
maxuv = m_local.temp.uv_minmax[1];
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
|
|
uf = u.xxzzlh().srl16(12);
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector8i uv1 = uv0;
|
|
|
|
{
|
|
GSVector8i repeat = (uv0 & minuv) | maxuv;
|
|
GSVector8i clamp = uv0.sat_i16(minuv, maxuv);
|
|
|
|
uv0 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector8i::x0001());
|
|
|
|
GSVector8i repeat = (uv1 & minuv) | maxuv;
|
|
GSVector8i clamp = uv1.sat_i16(minuv, maxuv);
|
|
|
|
uv1 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
GSVector8i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector8i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector8i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector8i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = m_global.clut[tex[addr00.u32[i]]];
|
|
c01.u32[i] = m_global.clut[tex[addr01.u32[i]]];
|
|
c10.u32[i] = m_global.clut[tex[addr10.u32[i]]];
|
|
c11.u32[i] = m_global.clut[tex[addr11.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = tex[addr00.u32[i]];
|
|
c01.u32[i] = tex[addr01.u32[i]];
|
|
c10.u32[i] = tex[addr10.u32[i]];
|
|
c11.u32[i] = tex[addr11.u32[i]];
|
|
}
|
|
}
|
|
|
|
GSVector8i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector8i ga00 = c00.srl16(8);
|
|
GSVector8i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector8i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector8i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector8i ga10 = c10.srl16(8);
|
|
GSVector8i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector8i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb = rb00.lerp16_4(rb10, vf);
|
|
ga = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
c00.u32[i] = m_global.clut[((const uint8*)m_global.tex[lodi.u32[i]])[addr00.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
c00.u32[i] = ((const uint32*)m_global.tex[lodi.u32[i]])[addr00.u32[i]];
|
|
}
|
|
}
|
|
|
|
rb = c00.sll16(8).srl16(8);
|
|
ga = c00.srl16(8);
|
|
}
|
|
|
|
if(sel.mmin != 1) // !round-off mode
|
|
{
|
|
GSVector8i rb2, ga2;
|
|
|
|
lodi += GSVector8i::x00000001();
|
|
|
|
u = uv[0].sra32(1);
|
|
v = uv[1].sra32(1);
|
|
|
|
minuv = minuv.srl16(1);
|
|
maxuv = maxuv.srl16(1);
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
|
|
uf = u.xxzzlh().srl16(12);
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector8i uv1 = uv0;
|
|
|
|
{
|
|
GSVector8i repeat = (uv0 & minuv) | maxuv;
|
|
GSVector8i clamp = uv0.sat_i16(minuv, maxuv);
|
|
|
|
uv0 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector8i::x0001());
|
|
|
|
GSVector8i repeat = (uv1 & minuv) | maxuv;
|
|
GSVector8i clamp = uv1.sat_i16(minuv, maxuv);
|
|
|
|
uv1 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
GSVector8i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector8i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector8i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector8i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = m_global.clut[tex[addr00.u32[i]]];
|
|
c01.u32[i] = m_global.clut[tex[addr01.u32[i]]];
|
|
c10.u32[i] = m_global.clut[tex[addr10.u32[i]]];
|
|
c11.u32[i] = m_global.clut[tex[addr11.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = tex[addr00.u32[i]];
|
|
c01.u32[i] = tex[addr01.u32[i]];
|
|
c10.u32[i] = tex[addr10.u32[i]];
|
|
c11.u32[i] = tex[addr11.u32[i]];
|
|
}
|
|
}
|
|
|
|
GSVector8i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector8i ga00 = c00.srl16(8);
|
|
GSVector8i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector8i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector8i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector8i ga10 = c10.srl16(8);
|
|
GSVector8i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector8i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb2 = rb00.lerp16_4(rb10, vf);
|
|
ga2 = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
c00.u32[i] = m_global.clut[((const uint8*)m_global.tex[lodi.u32[i]])[addr00.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
c00.u32[i] = ((const uint32*)m_global.tex[lodi.u32[i]])[addr00.u32[i]];
|
|
}
|
|
}
|
|
|
|
rb2 = c00.sll16(8).srl16(8);
|
|
ga2 = c00.srl16(8);
|
|
}
|
|
|
|
if(sel.lcm) lodf = m_global.lod.f;
|
|
|
|
lodf = lodf.srl16(1);
|
|
|
|
rb = rb.lerp16<0>(rb2, lodf);
|
|
ga = ga.lerp16<0>(ga2, lodf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!sel.fst)
|
|
{
|
|
u = GSVector8i(s / q);
|
|
v = GSVector8i(t / q);
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u = GSVector8i::cast(s);
|
|
v = GSVector8i::cast(t);
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uf = u.xxzzlh().srl16(12);
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
}
|
|
|
|
GSVector8i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector8i uv1 = uv0;
|
|
|
|
GSVector8i tmin = GSVector8i::broadcast128(m_global.t.min);
|
|
GSVector8i tmax = GSVector8i::broadcast128(m_global.t.max);
|
|
|
|
{
|
|
GSVector8i repeat = (uv0 & tmin) | tmax;
|
|
GSVector8i clamp = uv0.sat_i16(tmin, tmax);
|
|
|
|
uv0 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector8i::x0001());
|
|
|
|
GSVector8i repeat = (uv1 & tmin) | tmax;
|
|
GSVector8i clamp = uv1.sat_i16(tmin, tmax);
|
|
|
|
uv1 = clamp.blend8(repeat, GSVector8i::broadcast128(m_global.t.mask));
|
|
}
|
|
|
|
GSVector8i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector8i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector8i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector8i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[0];
|
|
|
|
c00 = addr00.gather32_32(tex, m_global.clut);
|
|
c01 = addr01.gather32_32(tex, m_global.clut);
|
|
c10 = addr10.gather32_32(tex, m_global.clut);
|
|
c11 = addr11.gather32_32(tex, m_global.clut);
|
|
}
|
|
else
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[0];
|
|
|
|
c00 = addr00.gather32_32(tex);
|
|
c01 = addr01.gather32_32(tex);
|
|
c10 = addr10.gather32_32(tex);
|
|
c11 = addr11.gather32_32(tex);
|
|
}
|
|
|
|
GSVector8i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector8i ga00 = c00.srl16(8);
|
|
GSVector8i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector8i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector8i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector8i ga10 = c10.srl16(8);
|
|
GSVector8i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector8i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb = rb00.lerp16_4(rb10, vf);
|
|
ga = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
c00 = addr00.gather32_32((const uint8*)m_global.tex[0], m_global.clut);
|
|
}
|
|
else
|
|
{
|
|
c00 = addr00.gather32_32((const uint32*)m_global.tex[0]);
|
|
}
|
|
|
|
rb = c00.sll16(8).srl16(8);
|
|
ga = c00.srl16(8);
|
|
}
|
|
}
|
|
}
|
|
|
|
// AlphaTFX
|
|
|
|
if(sel.fb)
|
|
{
|
|
switch(sel.tfx)
|
|
{
|
|
case TFX_MODULATE:
|
|
ga = ga.modulate16<1>(gaf).clamp8();
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_DECAL:
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_HIGHLIGHT:
|
|
ga = ga.mix16(!sel.tcc ? gaf.srl16(7) : ga.addus8(gaf.srl16(7)));
|
|
break;
|
|
case TFX_HIGHLIGHT2:
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_NONE:
|
|
ga = sel.iip ? gaf.srl16(7) : gaf;
|
|
break;
|
|
}
|
|
|
|
if(sel.aa1)
|
|
{
|
|
GSVector8i x00800080(0x00800080);
|
|
|
|
GSVector8i a = sel.edge ? cov : x00800080;
|
|
|
|
if(!sel.abe)
|
|
{
|
|
ga = ga.mix16(a);
|
|
}
|
|
else
|
|
{
|
|
ga = ga.blend8(a, ga.eq16(x00800080).srl32(16).sll32(16));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ReadMask
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
fm = m_global.fm;
|
|
}
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
zm = m_global.zm;
|
|
}
|
|
|
|
// TestAlpha
|
|
|
|
if(!TestAlpha(test, fm, zm, ga)) continue;
|
|
|
|
// ColorTFX
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
GSVector8i af;
|
|
|
|
switch(sel.tfx)
|
|
{
|
|
case TFX_MODULATE:
|
|
rb = rb.modulate16<1>(rbf).clamp8();
|
|
break;
|
|
case TFX_DECAL:
|
|
break;
|
|
case TFX_HIGHLIGHT:
|
|
case TFX_HIGHLIGHT2:
|
|
af = gaf.yywwlh().srl16(7);
|
|
rb = rb.modulate16<1>(rbf).add16(af).clamp8();
|
|
ga = ga.modulate16<1>(gaf).add16(af).clamp8().mix16(ga);
|
|
break;
|
|
case TFX_NONE:
|
|
rb = sel.iip ? rbf.srl16(7) : rbf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fog
|
|
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
GSVector8i fog = sel.prim != GS_SPRITE_CLASS ? f : GSVector8i::broadcast16(&m_local.p.f);
|
|
|
|
GSVector8i frb((int)m_global.frb);
|
|
GSVector8i fga((int)m_global.fga);
|
|
|
|
rb = frb.lerp16<0>(rb, fog);
|
|
ga = fga.lerp16<0>(ga, fog).mix16(ga);
|
|
|
|
/*
|
|
fog = fog.srl16(7);
|
|
|
|
GSVector8i ifog = GSVector4i::x00ff().sub16(fog);
|
|
|
|
rb = rb.mul16l(fog).add16(frb.mul16l(ifog)).srl16(8);
|
|
ga = ga.mul16l(fog).add16(fga.mul16l(ifog)).srl16(8).mix16(ga);
|
|
*/
|
|
}
|
|
|
|
// ReadFrame
|
|
|
|
if(sel.fb)
|
|
{
|
|
fa = (fza_base->x + fza_offset->x) % HALF_VM_SIZE;
|
|
|
|
if(sel.rfb)
|
|
{
|
|
fd = GSVector8i::load(
|
|
(uint8*)m_global.vm + fa * 2, (uint8*)m_global.vm + fa * 2 + 16,
|
|
(uint8*)m_global.vm + fa * 2 + 32, (uint8*)m_global.vm + fa * 2 + 48);
|
|
}
|
|
}
|
|
|
|
// TestDestAlpha
|
|
|
|
if(sel.date && (sel.fpsm == 0 || sel.fpsm == 2))
|
|
{
|
|
if(sel.datm)
|
|
{
|
|
if(sel.fpsm == 2)
|
|
{
|
|
// test |= fd.srl32(15) == GSVector8i::zero();
|
|
test |= fd.sll32(16).sra32(31) == GSVector8i::zero();
|
|
}
|
|
else
|
|
{
|
|
test |= (~fd).sra32(31);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sel.fpsm == 2)
|
|
{
|
|
test |= fd.sll32(16).sra32(31); // == GSVector8i::xffffffff();
|
|
}
|
|
else
|
|
{
|
|
test |= fd.sra32(31);
|
|
}
|
|
}
|
|
|
|
if(test.alltrue()) continue;
|
|
}
|
|
|
|
// WriteMask
|
|
|
|
int fzm = 0;
|
|
|
|
if(!sel.notest)
|
|
{
|
|
if(sel.fwrite)
|
|
{
|
|
fm |= test;
|
|
}
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
zm |= test;
|
|
}
|
|
|
|
if(sel.fwrite && sel.zwrite)
|
|
{
|
|
fzm = ~(fm == GSVector8i::xffffffff()).ps32(zm == GSVector8i::xffffffff()).mask();
|
|
}
|
|
else if(sel.fwrite)
|
|
{
|
|
fzm = ~(fm == GSVector8i::xffffffff()).ps32().mask();
|
|
}
|
|
else if(sel.zwrite)
|
|
{
|
|
fzm = ~(zm == GSVector8i::xffffffff()).ps32().mask();
|
|
}
|
|
}
|
|
|
|
// WriteZBuf
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
if(sel.ztest && sel.zpsm < 2)
|
|
{
|
|
zs = zs.blend8(zd, zm);
|
|
}
|
|
|
|
bool fast = sel.ztest ? sel.zpsm < 2 : sel.zpsm == 0 && sel.notest;
|
|
|
|
if(sel.notest)
|
|
{
|
|
if(fast)
|
|
{
|
|
GSVector4i::storel((uint8*)m_global.vm + za * 2, zs.extract<0>());
|
|
GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 16, zs.extract<0>());
|
|
GSVector4i::storel((uint8*)m_global.vm + za * 2 + 32, zs.extract<1>());
|
|
GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 48, zs.extract<1>());
|
|
}
|
|
else
|
|
{
|
|
WritePixel(zs, za, 0, sel.zpsm);
|
|
WritePixel(zs, za, 1, sel.zpsm);
|
|
WritePixel(zs, za, 2, sel.zpsm);
|
|
WritePixel(zs, za, 3, sel.zpsm);
|
|
WritePixel(zs, za, 4, sel.zpsm);
|
|
WritePixel(zs, za, 5, sel.zpsm);
|
|
WritePixel(zs, za, 6, sel.zpsm);
|
|
WritePixel(zs, za, 7, sel.zpsm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(fast)
|
|
{
|
|
if(fzm & 0x00000f00) GSVector4i::storel((uint8*)m_global.vm + za * 2, zs.extract<0>());
|
|
if(fzm & 0x0000f000) GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 16, zs.extract<0>());
|
|
if(fzm & 0x0f000000) GSVector4i::storel((uint8*)m_global.vm + za * 2 + 32, zs.extract<1>());
|
|
if(fzm & 0xf0000000) GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 48, zs.extract<1>());
|
|
}
|
|
else
|
|
{
|
|
if(fzm & 0x00000300) WritePixel(zs, za, 0, sel.zpsm);
|
|
if(fzm & 0x00000c00) WritePixel(zs, za, 1, sel.zpsm);
|
|
if(fzm & 0x00003000) WritePixel(zs, za, 2, sel.zpsm);
|
|
if(fzm & 0x0000c000) WritePixel(zs, za, 3, sel.zpsm);
|
|
if(fzm & 0x03000000) WritePixel(zs, za, 4, sel.zpsm);
|
|
if(fzm & 0x0c000000) WritePixel(zs, za, 5, sel.zpsm);
|
|
if(fzm & 0x30000000) WritePixel(zs, za, 6, sel.zpsm);
|
|
if(fzm & 0xc0000000) WritePixel(zs, za, 7, sel.zpsm);
|
|
}
|
|
}
|
|
}
|
|
|
|
// AlphaBlend
|
|
|
|
if(sel.fwrite && (sel.abe || sel.aa1))
|
|
{
|
|
GSVector8i rbs = rb, gas = ga, rbd, gad, a, mask;
|
|
|
|
if(sel.aba != sel.abb && (sel.aba == 1 || sel.abb == 1 || sel.abc == 1) || sel.abd == 1)
|
|
{
|
|
switch(sel.fpsm)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
rbd = fd.sll16(8).srl16(8);
|
|
gad = fd.srl16(8);
|
|
break;
|
|
case 2:
|
|
rbd = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3);
|
|
gad = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(sel.aba != sel.abb)
|
|
{
|
|
switch(sel.aba)
|
|
{
|
|
case 0: break;
|
|
case 1: rb = rbd; break;
|
|
case 2: rb = GSVector8i::zero(); break;
|
|
}
|
|
|
|
switch(sel.abb)
|
|
{
|
|
case 0: rb = rb.sub16(rbs); break;
|
|
case 1: rb = rb.sub16(rbd); break;
|
|
case 2: break;
|
|
}
|
|
|
|
if(!(sel.fpsm == 1 && sel.abc == 1))
|
|
{
|
|
switch(sel.abc)
|
|
{
|
|
case 0: a = gas.yywwlh().sll16(7); break;
|
|
case 1: a = gad.yywwlh().sll16(7); break;
|
|
case 2: a = m_global.afix; break;
|
|
}
|
|
|
|
rb = rb.modulate16<1>(a);
|
|
}
|
|
|
|
switch(sel.abd)
|
|
{
|
|
case 0: rb = rb.add16(rbs); break;
|
|
case 1: rb = rb.add16(rbd); break;
|
|
case 2: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(sel.abd)
|
|
{
|
|
case 0: break;
|
|
case 1: rb = rbd; break;
|
|
case 2: rb = GSVector8i::zero(); break;
|
|
}
|
|
}
|
|
|
|
if(sel.pabe)
|
|
{
|
|
mask = (gas << 8).sra32(31);
|
|
|
|
rb = rbs.blend8(rb, mask);
|
|
}
|
|
|
|
if(sel.aba != sel.abb)
|
|
{
|
|
switch(sel.aba)
|
|
{
|
|
case 0: break;
|
|
case 1: ga = gad; break;
|
|
case 2: ga = GSVector8i::zero(); break;
|
|
}
|
|
|
|
switch(sel.abb)
|
|
{
|
|
case 0: ga = ga.sub16(gas); break;
|
|
case 1: ga = ga.sub16(gad); break;
|
|
case 2: break;
|
|
}
|
|
|
|
if(!(sel.fpsm == 1 && sel.abc == 1))
|
|
{
|
|
ga = ga.modulate16<1>(a);
|
|
}
|
|
|
|
switch(sel.abd)
|
|
{
|
|
case 0: ga = ga.add16(gas); break;
|
|
case 1: ga = ga.add16(gad); break;
|
|
case 2: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(sel.abd)
|
|
{
|
|
case 0: break;
|
|
case 1: ga = gad; break;
|
|
case 2: ga = GSVector8i::zero(); break;
|
|
}
|
|
}
|
|
|
|
if(sel.pabe)
|
|
{
|
|
ga = gas.blend8(ga, mask >> 16);
|
|
}
|
|
else
|
|
{
|
|
if(sel.fpsm != 1)
|
|
{
|
|
ga = ga.mix16(gas);
|
|
}
|
|
}
|
|
}
|
|
|
|
// WriteFrame
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
if(sel.fpsm == 2 && sel.dthe)
|
|
{
|
|
int y = (top & 3) << 1;
|
|
|
|
rb = rb.add16(GSVector8i::broadcast128(m_global.dimx[0 + y]));
|
|
ga = ga.add16(GSVector8i::broadcast128(m_global.dimx[1 + y]));
|
|
}
|
|
|
|
if(sel.colclamp == 0)
|
|
{
|
|
rb &= GSVector8i::x00ff();
|
|
ga &= GSVector8i::x00ff();
|
|
}
|
|
|
|
GSVector8i fs = rb.upl16(ga).pu16(rb.uph16(ga));
|
|
|
|
if(sel.fba && sel.fpsm != 1)
|
|
{
|
|
fs |= GSVector8i::x80000000();
|
|
}
|
|
|
|
if(sel.fpsm == 2)
|
|
{
|
|
GSVector8i rb = fs & 0x00f800f8;
|
|
GSVector8i ga = fs & 0x8000f800;
|
|
|
|
fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3);
|
|
}
|
|
|
|
if(sel.rfb)
|
|
{
|
|
fs = fs.blend(fd, fm);
|
|
}
|
|
|
|
bool fast = sel.rfb ? sel.fpsm < 2 : sel.fpsm == 0 && sel.notest;
|
|
|
|
if(sel.notest)
|
|
{
|
|
if(fast)
|
|
{
|
|
GSVector4i::storel((uint8*)m_global.vm + fa * 2, fs.extract<0>());
|
|
GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 16, fs.extract<0>());
|
|
GSVector4i::storel((uint8*)m_global.vm + fa * 2 + 32, fs.extract<1>());
|
|
GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 48, fs.extract<1>());
|
|
}
|
|
else
|
|
{
|
|
WritePixel(fs, fa, 0, sel.fpsm);
|
|
WritePixel(fs, fa, 1, sel.fpsm);
|
|
WritePixel(fs, fa, 2, sel.fpsm);
|
|
WritePixel(fs, fa, 3, sel.fpsm);
|
|
WritePixel(fs, fa, 4, sel.fpsm);
|
|
WritePixel(fs, fa, 5, sel.fpsm);
|
|
WritePixel(fs, fa, 6, sel.fpsm);
|
|
WritePixel(fs, fa, 7, sel.fpsm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(fast)
|
|
{
|
|
if(fzm & 0x0000000f) GSVector4i::storel((uint8*)m_global.vm + fa * 2, fs.extract<0>());
|
|
if(fzm & 0x000000f0) GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 16, fs.extract<0>());
|
|
if(fzm & 0x000f0000) GSVector4i::storel((uint8*)m_global.vm + fa * 2 + 32, fs.extract<1>());
|
|
if(fzm & 0x00f00000) GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 48, fs.extract<1>());
|
|
}
|
|
else
|
|
{
|
|
if(fzm & 0x00000003) WritePixel(fs, fa, 0, sel.fpsm);
|
|
if(fzm & 0x0000000c) WritePixel(fs, fa, 1, sel.fpsm);
|
|
if(fzm & 0x00000030) WritePixel(fs, fa, 2, sel.fpsm);
|
|
if(fzm & 0x000000c0) WritePixel(fs, fa, 3, sel.fpsm);
|
|
if(fzm & 0x00030000) WritePixel(fs, fa, 4, sel.fpsm);
|
|
if(fzm & 0x000c0000) WritePixel(fs, fa, 5, sel.fpsm);
|
|
if(fzm & 0x00300000) WritePixel(fs, fa, 6, sel.fpsm);
|
|
if(fzm & 0x00c00000) WritePixel(fs, fa, 7, sel.fpsm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(0);
|
|
|
|
if(sel.edge) break;
|
|
|
|
if(steps <= 0) break;
|
|
|
|
// Step
|
|
|
|
steps -= 8;
|
|
|
|
fza_offset += 2;
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
if(sel.zb)
|
|
{
|
|
zo += GSVector8::broadcast32(&m_local.d8.p.z);
|
|
}
|
|
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
f = f.add16(GSVector8i::broadcast16(&m_local.d8.p.f));
|
|
}
|
|
}
|
|
|
|
if(sel.fb)
|
|
{
|
|
if(sel.tfx != TFX_NONE)
|
|
{
|
|
if(sel.fst)
|
|
{
|
|
GSVector8i stq = GSVector8i::cast(GSVector8(m_local.d8.stq));
|
|
|
|
s = GSVector8::cast(GSVector8i::cast(s) + stq.xxxx());
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS || sel.mmin)
|
|
{
|
|
t = GSVector8::cast(GSVector8i::cast(t) + stq.yyyy());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GSVector8 stq(m_local.d8.stq);
|
|
|
|
s += stq.xxxx();
|
|
t += stq.yyyy();
|
|
q += stq.zzzz();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!(sel.tfx == TFX_DECAL && sel.tcc))
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
GSVector8i c = GSVector8i::broadcast64(&m_local.d8.c);
|
|
|
|
rbf = rbf.add16(c.xxxx()).max_i16(GSVector8i::zero());
|
|
gaf = gaf.add16(c.yyyy()).max_i16(GSVector8i::zero());
|
|
}
|
|
}
|
|
|
|
if(!sel.notest)
|
|
{
|
|
test = GSVector8i::i8to32c(g_const->m_test_256b[15 + (steps & (steps >> 31))]);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
const GSVector4i* const_test = (GSVector4i*)g_const->m_test_128b;
|
|
GSVector4i test;
|
|
GSVector4 zo;
|
|
GSVector4i f;
|
|
GSVector4 s, t, q;
|
|
GSVector4i uf, vf;
|
|
GSVector4i rbf, gaf;
|
|
GSVector4i cov;
|
|
|
|
// Init
|
|
|
|
int skip, steps;
|
|
|
|
if(!sel.notest)
|
|
{
|
|
skip = left & 3;
|
|
steps = pixels + skip - 4;
|
|
left -= skip;
|
|
test = const_test[skip] | const_test[7 + (steps & (steps >> 31))];
|
|
}
|
|
else
|
|
{
|
|
skip = 0;
|
|
steps = pixels - 4;
|
|
}
|
|
|
|
ASSERT((left & 3) == 0);
|
|
|
|
const GSVector2i* fza_base = &m_global.fzbr[top];
|
|
const GSVector2i* fza_offset = &m_global.fzbc[left >> 2];
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
f = GSVector4i(scan.p).zzzzh().zzzz().add16(m_local.d[skip].f);
|
|
}
|
|
|
|
if(sel.zb)
|
|
{
|
|
zo = m_local.d[skip].z;
|
|
}
|
|
}
|
|
|
|
if(sel.fb)
|
|
{
|
|
if(sel.edge)
|
|
{
|
|
cov = GSVector4i::cast(scan.t).zzzzh().wwww().srl16(9);
|
|
}
|
|
|
|
if(sel.tfx != TFX_NONE)
|
|
{
|
|
if(sel.fst)
|
|
{
|
|
GSVector4i vt(scan.t);
|
|
|
|
GSVector4i u = vt.xxxx() + GSVector4i::cast(m_local.d[skip].s);
|
|
GSVector4i v = vt.yyyy();
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS || sel.mmin)
|
|
{
|
|
v += GSVector4i::cast(m_local.d[skip].t);
|
|
}
|
|
else if(sel.ltf)
|
|
{
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
s = GSVector4::cast(u);
|
|
t = GSVector4::cast(v);
|
|
}
|
|
else
|
|
{
|
|
s = scan.t.xxxx() + m_local.d[skip].s;
|
|
t = scan.t.yyyy() + m_local.d[skip].t;
|
|
q = scan.t.zzzz() + m_local.d[skip].q;
|
|
}
|
|
}
|
|
|
|
if(!(sel.tfx == TFX_DECAL && sel.tcc))
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
GSVector4i c(scan.c);
|
|
|
|
c = c.upl16(c.zwxy());
|
|
|
|
rbf = c.xxxx().add16(m_local.d[skip].rb);
|
|
gaf = c.zzzz().add16(m_local.d[skip].ga);
|
|
}
|
|
else
|
|
{
|
|
rbf = m_local.c.rb;
|
|
gaf = m_local.c.ga;
|
|
}
|
|
}
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
do
|
|
{
|
|
int fa = 0, za = 0;
|
|
GSVector4i fd, zs, zd;
|
|
GSVector4i fm, zm;
|
|
GSVector4i rb, ga;
|
|
|
|
// TestZ
|
|
|
|
if(sel.zb)
|
|
{
|
|
za = (fza_base->y + fza_offset->y) % HALF_VM_SIZE;
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
GSVector4 z = scan.p.zzzz() + zo;
|
|
|
|
if(sel.zoverflow)
|
|
{
|
|
zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001());
|
|
}
|
|
else
|
|
{
|
|
zs = GSVector4i(z);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
zs = m_local.p.z;
|
|
}
|
|
|
|
if(sel.ztest)
|
|
{
|
|
zd = GSVector4i::load((uint8*)m_global.vm + za * 2, (uint8*)m_global.vm + za * 2 + 16);
|
|
|
|
switch(sel.zpsm)
|
|
{
|
|
case 1: zd = zd.sll32(8).srl32(8); break;
|
|
case 2: zd = zd.sll32(16).srl32(16); break;
|
|
default: break;
|
|
}
|
|
|
|
GSVector4i zso = zs;
|
|
GSVector4i zdo = zd;
|
|
|
|
if(sel.zoverflow || sel.zpsm == 0)
|
|
{
|
|
zso -= GSVector4i::x80000000();
|
|
zdo -= GSVector4i::x80000000();
|
|
}
|
|
|
|
switch(sel.ztst)
|
|
{
|
|
case ZTST_GEQUAL: test |= zso < zdo; break;
|
|
case ZTST_GREATER: test |= zso <= zdo; break;
|
|
}
|
|
|
|
if(test.alltrue()) continue;
|
|
}
|
|
}
|
|
|
|
// SampleTexture
|
|
|
|
if(sel.fb && sel.tfx != TFX_NONE)
|
|
{
|
|
GSVector4i u, v, uv[2];
|
|
GSVector4i lodi, lodf;
|
|
GSVector4i minuv, maxuv;
|
|
GSVector4i addr00, addr01, addr10, addr11;
|
|
GSVector4i c00, c01, c10, c11;
|
|
|
|
if(sel.mmin)
|
|
{
|
|
if(!sel.fst)
|
|
{
|
|
u = GSVector4i(s / q);
|
|
v = GSVector4i(t / q);
|
|
}
|
|
else
|
|
{
|
|
u = GSVector4i::cast(s);
|
|
v = GSVector4i::cast(t);
|
|
}
|
|
|
|
if(!sel.lcm)
|
|
{
|
|
GSVector4 tmp = q.log2(3) * m_global.l + m_global.k; // (-log2(Q) * (1 << L) + K) * 0x10000
|
|
|
|
GSVector4i lod = GSVector4i(tmp.sat(GSVector4::zero(), m_global.mxl), false);
|
|
|
|
if(sel.mmin == 1) // round-off mode
|
|
{
|
|
lod += 0x8000;
|
|
}
|
|
|
|
lodi = lod.srl32(16);
|
|
|
|
if(sel.mmin == 2) // trilinear mode
|
|
{
|
|
lodf = lod.xxzzlh();
|
|
}
|
|
|
|
// shift u/v by (int)lod
|
|
|
|
GSVector4i aabb = u.upl32(v);
|
|
GSVector4i ccdd = u.uph32(v);
|
|
|
|
GSVector4i aaxx = aabb.sra32(lodi.x);
|
|
GSVector4i xxbb = aabb.sra32(lodi.y);
|
|
GSVector4i ccxx = ccdd.sra32(lodi.z);
|
|
GSVector4i xxdd = ccdd.sra32(lodi.w);
|
|
|
|
GSVector4i acac = aaxx.upl32(ccxx);
|
|
GSVector4i bdbd = xxbb.uph32(xxdd);
|
|
|
|
u = acac.upl32(bdbd);
|
|
v = acac.uph32(bdbd);
|
|
|
|
uv[0] = u;
|
|
uv[1] = v;
|
|
|
|
GSVector4i minmax = m_global.t.minmax;
|
|
|
|
GSVector4i v0 = minmax.srl16(lodi.x);
|
|
GSVector4i v1 = minmax.srl16(lodi.y);
|
|
GSVector4i v2 = minmax.srl16(lodi.z);
|
|
GSVector4i v3 = minmax.srl16(lodi.w);
|
|
|
|
v0 = v0.upl16(v1);
|
|
v2 = v2.upl16(v3);
|
|
|
|
minuv = v0.upl32(v2);
|
|
maxuv = v0.uph32(v2);
|
|
}
|
|
else
|
|
{
|
|
lodi = m_global.lod.i;
|
|
|
|
u = u.sra32(lodi.x);
|
|
v = v.sra32(lodi.x);
|
|
|
|
uv[0] = u;
|
|
uv[1] = v;
|
|
|
|
minuv = m_local.temp.uv_minmax[0];
|
|
maxuv = m_local.temp.uv_minmax[1];
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
|
|
uf = u.xxzzlh().srl16(12);
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector4i uv1 = uv0;
|
|
|
|
{
|
|
GSVector4i repeat = (uv0 & minuv) | maxuv;
|
|
GSVector4i clamp = uv0.sat_i16(minuv, maxuv);
|
|
|
|
uv0 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector4i::x0001());
|
|
|
|
GSVector4i repeat = (uv1 & minuv) | maxuv;
|
|
GSVector4i clamp = uv1.sat_i16(minuv, maxuv);
|
|
|
|
uv1 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
GSVector4i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector4i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector4i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector4i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = m_global.clut[tex[addr00.u32[i]]];
|
|
c01.u32[i] = m_global.clut[tex[addr01.u32[i]]];
|
|
c10.u32[i] = m_global.clut[tex[addr10.u32[i]]];
|
|
c11.u32[i] = m_global.clut[tex[addr11.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = tex[addr00.u32[i]];
|
|
c01.u32[i] = tex[addr01.u32[i]];
|
|
c10.u32[i] = tex[addr10.u32[i]];
|
|
c11.u32[i] = tex[addr11.u32[i]];
|
|
}
|
|
}
|
|
|
|
GSVector4i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector4i ga00 = c00.srl16(8);
|
|
GSVector4i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector4i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector4i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector4i ga10 = c10.srl16(8);
|
|
GSVector4i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector4i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb = rb00.lerp16_4(rb10, vf);
|
|
ga = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
c00.u32[i] = m_global.clut[((const uint8*)m_global.tex[lodi.u32[i]])[addr00.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
c00.u32[i] = ((const uint32*)m_global.tex[lodi.u32[i]])[addr00.u32[i]];
|
|
}
|
|
}
|
|
|
|
rb = c00.sll16(8).srl16(8);
|
|
ga = c00.srl16(8);
|
|
}
|
|
|
|
if(sel.mmin != 1) // !round-off mode
|
|
{
|
|
GSVector4i rb2, ga2;
|
|
|
|
lodi += GSVector4i::x00000001();
|
|
|
|
u = uv[0].sra32(1);
|
|
v = uv[1].sra32(1);
|
|
|
|
minuv = minuv.srl16(1);
|
|
maxuv = maxuv.srl16(1);
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
|
|
uf = u.xxzzlh().srl16(12);
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
|
|
GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector4i uv1 = uv0;
|
|
|
|
{
|
|
GSVector4i repeat = (uv0 & minuv) | maxuv;
|
|
GSVector4i clamp = uv0.sat_i16(minuv, maxuv);
|
|
|
|
uv0 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector4i::x0001());
|
|
|
|
GSVector4i repeat = (uv1 & minuv) | maxuv;
|
|
GSVector4i clamp = uv1.sat_i16(minuv, maxuv);
|
|
|
|
uv1 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
GSVector4i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector4i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector4i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector4i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = m_global.clut[tex[addr00.u32[i]]];
|
|
c01.u32[i] = m_global.clut[tex[addr01.u32[i]]];
|
|
c10.u32[i] = m_global.clut[tex[addr10.u32[i]]];
|
|
c11.u32[i] = m_global.clut[tex[addr11.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[lodi.u32[i]];
|
|
|
|
c00.u32[i] = tex[addr00.u32[i]];
|
|
c01.u32[i] = tex[addr01.u32[i]];
|
|
c10.u32[i] = tex[addr10.u32[i]];
|
|
c11.u32[i] = tex[addr11.u32[i]];
|
|
}
|
|
}
|
|
|
|
GSVector4i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector4i ga00 = c00.srl16(8);
|
|
GSVector4i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector4i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector4i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector4i ga10 = c10.srl16(8);
|
|
GSVector4i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector4i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb2 = rb00.lerp16_4(rb10, vf);
|
|
ga2 = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
c00.u32[i] = m_global.clut[((const uint8*)m_global.tex[lodi.u32[i]])[addr00.u32[i]]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
c00.u32[i] = ((const uint32*)m_global.tex[lodi.u32[i]])[addr00.u32[i]];
|
|
}
|
|
}
|
|
|
|
rb2 = c00.sll16(8).srl16(8);
|
|
ga2 = c00.srl16(8);
|
|
}
|
|
|
|
if(sel.lcm) lodf = m_global.lod.f;
|
|
|
|
lodf = lodf.srl16(1);
|
|
|
|
rb = rb.lerp16<0>(rb2, lodf);
|
|
ga = ga.lerp16<0>(ga2, lodf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!sel.fst)
|
|
{
|
|
u = GSVector4i(s / q);
|
|
v = GSVector4i(t / q);
|
|
|
|
if(sel.ltf)
|
|
{
|
|
u -= 0x8000;
|
|
v -= 0x8000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u = GSVector4i::cast(s);
|
|
v = GSVector4i::cast(t);
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uf = u.xxzzlh().srl16(12);
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
vf = v.xxzzlh().srl16(12);
|
|
}
|
|
}
|
|
|
|
GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16));
|
|
GSVector4i uv1 = uv0;
|
|
|
|
{
|
|
GSVector4i repeat = (uv0 & m_global.t.min) | m_global.t.max;
|
|
GSVector4i clamp = uv0.sat_i16(m_global.t.min, m_global.t.max);
|
|
|
|
uv0 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
if(sel.ltf)
|
|
{
|
|
uv1 = uv1.add16(GSVector4i::x0001());
|
|
|
|
GSVector4i repeat = (uv1 & m_global.t.min) | m_global.t.max;
|
|
GSVector4i clamp = uv1.sat_i16(m_global.t.min, m_global.t.max);
|
|
|
|
uv1 = clamp.blend8(repeat, m_global.t.mask);
|
|
}
|
|
|
|
GSVector4i y0 = uv0.uph16() << (sel.tw + 3);
|
|
GSVector4i x0 = uv0.upl16();
|
|
|
|
if(sel.ltf)
|
|
{
|
|
GSVector4i y1 = uv1.uph16() << (sel.tw + 3);
|
|
GSVector4i x1 = uv1.upl16();
|
|
|
|
addr00 = y0 + x0;
|
|
addr01 = y0 + x1;
|
|
addr10 = y1 + x0;
|
|
addr11 = y1 + x1;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
const uint8* tex = (const uint8*)m_global.tex[0];
|
|
|
|
c00 = addr00.gather32_32(tex, m_global.clut);
|
|
c01 = addr01.gather32_32(tex, m_global.clut);
|
|
c10 = addr10.gather32_32(tex, m_global.clut);
|
|
c11 = addr11.gather32_32(tex, m_global.clut);
|
|
}
|
|
else
|
|
{
|
|
const uint32* tex = (const uint32*)m_global.tex[0];
|
|
|
|
c00 = addr00.gather32_32(tex);
|
|
c01 = addr01.gather32_32(tex);
|
|
c10 = addr10.gather32_32(tex);
|
|
c11 = addr11.gather32_32(tex);
|
|
}
|
|
|
|
GSVector4i rb00 = c00.sll16(8).srl16(8);
|
|
GSVector4i ga00 = c00.srl16(8);
|
|
GSVector4i rb01 = c01.sll16(8).srl16(8);
|
|
GSVector4i ga01 = c01.srl16(8);
|
|
|
|
rb00 = rb00.lerp16_4(rb01, uf);
|
|
ga00 = ga00.lerp16_4(ga01, uf);
|
|
|
|
GSVector4i rb10 = c10.sll16(8).srl16(8);
|
|
GSVector4i ga10 = c10.srl16(8);
|
|
GSVector4i rb11 = c11.sll16(8).srl16(8);
|
|
GSVector4i ga11 = c11.srl16(8);
|
|
|
|
rb10 = rb10.lerp16_4(rb11, uf);
|
|
ga10 = ga10.lerp16_4(ga11, uf);
|
|
|
|
rb = rb00.lerp16_4(rb10, vf);
|
|
ga = ga00.lerp16_4(ga10, vf);
|
|
}
|
|
else
|
|
{
|
|
addr00 = y0 + x0;
|
|
|
|
if(sel.tlu)
|
|
{
|
|
c00 = addr00.gather32_32((const uint8*)m_global.tex[0], m_global.clut);
|
|
}
|
|
else
|
|
{
|
|
c00 = addr00.gather32_32((const uint32*)m_global.tex[0]);
|
|
}
|
|
|
|
rb = c00.sll16(8).srl16(8);
|
|
ga = c00.srl16(8);
|
|
}
|
|
}
|
|
}
|
|
|
|
// AlphaTFX
|
|
|
|
if(sel.fb)
|
|
{
|
|
switch(sel.tfx)
|
|
{
|
|
case TFX_MODULATE:
|
|
ga = ga.modulate16<1>(gaf).clamp8();
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_DECAL:
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_HIGHLIGHT:
|
|
ga = ga.mix16(!sel.tcc ? gaf.srl16(7) : ga.addus8(gaf.srl16(7)));
|
|
break;
|
|
case TFX_HIGHLIGHT2:
|
|
if(!sel.tcc) ga = ga.mix16(gaf.srl16(7));
|
|
break;
|
|
case TFX_NONE:
|
|
ga = sel.iip ? gaf.srl16(7) : gaf;
|
|
break;
|
|
}
|
|
|
|
if(sel.aa1)
|
|
{
|
|
GSVector4i x00800080(0x00800080);
|
|
|
|
GSVector4i a = sel.edge ? cov : x00800080;
|
|
|
|
if(!sel.abe)
|
|
{
|
|
ga = ga.mix16(a);
|
|
}
|
|
else
|
|
{
|
|
ga = ga.blend8(a, ga.eq16(x00800080).srl32(16).sll32(16));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ReadMask
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
fm = m_global.fm;
|
|
}
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
zm = m_global.zm;
|
|
}
|
|
|
|
// TestAlpha
|
|
|
|
if(!TestAlpha(test, fm, zm, ga)) continue;
|
|
|
|
// ColorTFX
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
GSVector4i af;
|
|
|
|
switch(sel.tfx)
|
|
{
|
|
case TFX_MODULATE:
|
|
rb = rb.modulate16<1>(rbf).clamp8();
|
|
break;
|
|
case TFX_DECAL:
|
|
break;
|
|
case TFX_HIGHLIGHT:
|
|
case TFX_HIGHLIGHT2:
|
|
af = gaf.yywwlh().srl16(7);
|
|
rb = rb.modulate16<1>(rbf).add16(af).clamp8();
|
|
ga = ga.modulate16<1>(gaf).add16(af).clamp8().mix16(ga);
|
|
break;
|
|
case TFX_NONE:
|
|
rb = sel.iip ? rbf.srl16(7) : rbf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fog
|
|
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
GSVector4i fog = sel.prim != GS_SPRITE_CLASS ? f : m_local.p.f;
|
|
|
|
rb = m_global.frb.lerp16<0>(rb, fog);
|
|
ga = m_global.fga.lerp16<0>(ga, fog).mix16(ga);
|
|
|
|
/*
|
|
fog = fog.srl16(7);
|
|
|
|
GSVector4i ifog = GSVector4i::x00ff().sub16(fog);
|
|
|
|
rb = rb.mul16l(fog).add16(m_global.frb.mul16l(ifog)).srl16(8);
|
|
ga = ga.mul16l(fog).add16(m_global.fga.mul16l(ifog)).srl16(8).mix16(ga);
|
|
*/
|
|
}
|
|
|
|
// ReadFrame
|
|
|
|
if(sel.fb)
|
|
{
|
|
fa = (fza_base->x + fza_offset->x) % HALF_VM_SIZE;
|
|
|
|
if(sel.rfb)
|
|
{
|
|
fd = GSVector4i::load((uint8*)m_global.vm + fa * 2, (uint8*)m_global.vm + fa * 2 + 16);
|
|
}
|
|
}
|
|
|
|
// TestDestAlpha
|
|
|
|
if(sel.date && (sel.fpsm == 0 || sel.fpsm == 2))
|
|
{
|
|
if(sel.datm)
|
|
{
|
|
if(sel.fpsm == 2)
|
|
{
|
|
// test |= fd.srl32(15) == GSVector4i::zero();
|
|
test |= fd.sll32(16).sra32(31) == GSVector4i::zero();
|
|
}
|
|
else
|
|
{
|
|
test |= (~fd).sra32(31);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sel.fpsm == 2)
|
|
{
|
|
test |= fd.sll32(16).sra32(31); // == GSVector4i::xffffffff();
|
|
}
|
|
else
|
|
{
|
|
test |= fd.sra32(31);
|
|
}
|
|
}
|
|
|
|
if(test.alltrue()) continue;
|
|
}
|
|
|
|
// WriteMask
|
|
|
|
int fzm = 0;
|
|
|
|
if(!sel.notest)
|
|
{
|
|
if(sel.fwrite)
|
|
{
|
|
fm |= test;
|
|
}
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
zm |= test;
|
|
}
|
|
|
|
if(sel.fwrite && sel.zwrite)
|
|
{
|
|
fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask();
|
|
}
|
|
else if(sel.fwrite)
|
|
{
|
|
fzm = ~(fm == GSVector4i::xffffffff()).ps32().mask();
|
|
}
|
|
else if(sel.zwrite)
|
|
{
|
|
fzm = ~(zm == GSVector4i::xffffffff()).ps32().mask();
|
|
}
|
|
}
|
|
|
|
// WriteZBuf
|
|
|
|
if(sel.zwrite)
|
|
{
|
|
if(sel.ztest && sel.zpsm < 2)
|
|
{
|
|
zs = zs.blend8(zd, zm);
|
|
}
|
|
|
|
bool fast = sel.ztest ? sel.zpsm < 2 : sel.zpsm == 0 && sel.notest;
|
|
|
|
if(sel.notest)
|
|
{
|
|
if(fast)
|
|
{
|
|
GSVector4i::storel((uint8*)m_global.vm + za * 2, zs);
|
|
GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 16, zs);
|
|
}
|
|
else
|
|
{
|
|
WritePixel(zs, za, 0, sel.zpsm);
|
|
WritePixel(zs, za, 1, sel.zpsm);
|
|
WritePixel(zs, za, 2, sel.zpsm);
|
|
WritePixel(zs, za, 3, sel.zpsm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(fast)
|
|
{
|
|
if(fzm & 0x0f00) GSVector4i::storel((uint8*)m_global.vm + za * 2, zs);
|
|
if(fzm & 0xf000) GSVector4i::storeh((uint8*)m_global.vm + za * 2 + 16, zs);
|
|
}
|
|
else
|
|
{
|
|
if(fzm & 0x0300) WritePixel(zs, za, 0, sel.zpsm);
|
|
if(fzm & 0x0c00) WritePixel(zs, za, 1, sel.zpsm);
|
|
if(fzm & 0x3000) WritePixel(zs, za, 2, sel.zpsm);
|
|
if(fzm & 0xc000) WritePixel(zs, za, 3, sel.zpsm);
|
|
}
|
|
}
|
|
}
|
|
|
|
// AlphaBlend
|
|
|
|
if(sel.fwrite && (sel.abe || sel.aa1))
|
|
{
|
|
GSVector4i rbs = rb, gas = ga, rbd, gad, a, mask;
|
|
|
|
if(sel.aba != sel.abb && (sel.aba == 1 || sel.abb == 1 || sel.abc == 1) || sel.abd == 1)
|
|
{
|
|
switch(sel.fpsm)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
rbd = fd.sll16(8).srl16(8);
|
|
gad = fd.srl16(8);
|
|
break;
|
|
case 2:
|
|
rbd = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3);
|
|
gad = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(sel.aba != sel.abb)
|
|
{
|
|
switch(sel.aba)
|
|
{
|
|
case 0: break;
|
|
case 1: rb = rbd; break;
|
|
case 2: rb = GSVector4i::zero(); break;
|
|
}
|
|
|
|
switch(sel.abb)
|
|
{
|
|
case 0: rb = rb.sub16(rbs); break;
|
|
case 1: rb = rb.sub16(rbd); break;
|
|
case 2: break;
|
|
}
|
|
|
|
if(!(sel.fpsm == 1 && sel.abc == 1))
|
|
{
|
|
switch(sel.abc)
|
|
{
|
|
case 0: a = gas.yywwlh().sll16(7); break;
|
|
case 1: a = gad.yywwlh().sll16(7); break;
|
|
case 2: a = m_global.afix; break;
|
|
}
|
|
|
|
rb = rb.modulate16<1>(a);
|
|
}
|
|
|
|
switch(sel.abd)
|
|
{
|
|
case 0: rb = rb.add16(rbs); break;
|
|
case 1: rb = rb.add16(rbd); break;
|
|
case 2: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(sel.abd)
|
|
{
|
|
case 0: break;
|
|
case 1: rb = rbd; break;
|
|
case 2: rb = GSVector4i::zero(); break;
|
|
}
|
|
}
|
|
|
|
if(sel.pabe)
|
|
{
|
|
mask = (gas << 8).sra32(31);
|
|
|
|
rb = rbs.blend8(rb, mask);
|
|
}
|
|
|
|
if(sel.aba != sel.abb)
|
|
{
|
|
switch(sel.aba)
|
|
{
|
|
case 0: break;
|
|
case 1: ga = gad; break;
|
|
case 2: ga = GSVector4i::zero(); break;
|
|
}
|
|
|
|
switch(sel.abb)
|
|
{
|
|
case 0: ga = ga.sub16(gas); break;
|
|
case 1: ga = ga.sub16(gad); break;
|
|
case 2: break;
|
|
}
|
|
|
|
if(!(sel.fpsm == 1 && sel.abc == 1))
|
|
{
|
|
ga = ga.modulate16<1>(a);
|
|
}
|
|
|
|
switch(sel.abd)
|
|
{
|
|
case 0: ga = ga.add16(gas); break;
|
|
case 1: ga = ga.add16(gad); break;
|
|
case 2: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(sel.abd)
|
|
{
|
|
case 0: break;
|
|
case 1: ga = gad; break;
|
|
case 2: ga = GSVector4i::zero(); break;
|
|
}
|
|
}
|
|
|
|
if(sel.pabe)
|
|
{
|
|
ga = gas.blend8(ga, mask >> 16);
|
|
}
|
|
else
|
|
{
|
|
if(sel.fpsm != 1)
|
|
{
|
|
ga = ga.mix16(gas);
|
|
}
|
|
}
|
|
}
|
|
|
|
// WriteFrame
|
|
|
|
if(sel.fwrite)
|
|
{
|
|
if(sel.fpsm == 2 && sel.dthe)
|
|
{
|
|
int y = (top & 3) << 1;
|
|
|
|
rb = rb.add16(m_global.dimx[0 + y]);
|
|
ga = ga.add16(m_global.dimx[1 + y]);
|
|
}
|
|
|
|
if(sel.colclamp == 0)
|
|
{
|
|
rb &= GSVector4i::x00ff();
|
|
ga &= GSVector4i::x00ff();
|
|
}
|
|
|
|
GSVector4i fs = rb.upl16(ga).pu16(rb.uph16(ga));
|
|
|
|
if(sel.fba && sel.fpsm != 1)
|
|
{
|
|
fs |= GSVector4i::x80000000();
|
|
}
|
|
|
|
if(sel.fpsm == 2)
|
|
{
|
|
GSVector4i rb = fs & 0x00f800f8;
|
|
GSVector4i ga = fs & 0x8000f800;
|
|
|
|
fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3);
|
|
}
|
|
|
|
if(sel.rfb)
|
|
{
|
|
fs = fs.blend(fd, fm);
|
|
}
|
|
|
|
bool fast = sel.rfb ? sel.fpsm < 2 : sel.fpsm == 0 && sel.notest;
|
|
|
|
if(sel.notest)
|
|
{
|
|
if(fast)
|
|
{
|
|
GSVector4i::storel((uint8*)m_global.vm + fa * 2, fs);
|
|
GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 16, fs);
|
|
}
|
|
else
|
|
{
|
|
WritePixel(fs, fa, 0, sel.fpsm);
|
|
WritePixel(fs, fa, 1, sel.fpsm);
|
|
WritePixel(fs, fa, 2, sel.fpsm);
|
|
WritePixel(fs, fa, 3, sel.fpsm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(fast)
|
|
{
|
|
if(fzm & 0x000f) GSVector4i::storel((uint8*)m_global.vm + fa * 2, fs);
|
|
if(fzm & 0x00f0) GSVector4i::storeh((uint8*)m_global.vm + fa * 2 + 16, fs);
|
|
}
|
|
else
|
|
{
|
|
if(fzm & 0x0003) WritePixel(fs, fa, 0, sel.fpsm);
|
|
if(fzm & 0x000c) WritePixel(fs, fa, 1, sel.fpsm);
|
|
if(fzm & 0x0030) WritePixel(fs, fa, 2, sel.fpsm);
|
|
if(fzm & 0x00c0) WritePixel(fs, fa, 3, sel.fpsm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(0);
|
|
|
|
if(sel.edge) break;
|
|
|
|
if(steps <= 0) break;
|
|
|
|
// Step
|
|
|
|
steps -= 4;
|
|
|
|
fza_offset++;
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS)
|
|
{
|
|
if(sel.zb)
|
|
{
|
|
zo += m_local.d4.z;
|
|
}
|
|
|
|
if(sel.fwrite && sel.fge)
|
|
{
|
|
f = f.add16(m_local.d4.f);
|
|
}
|
|
}
|
|
|
|
if(sel.fb)
|
|
{
|
|
if(sel.tfx != TFX_NONE)
|
|
{
|
|
if(sel.fst)
|
|
{
|
|
GSVector4i stq = GSVector4i::cast(m_local.d4.stq);
|
|
|
|
s = GSVector4::cast(GSVector4i::cast(s) + stq.xxxx());
|
|
|
|
if(sel.prim != GS_SPRITE_CLASS || sel.mmin)
|
|
{
|
|
t = GSVector4::cast(GSVector4i::cast(t) + stq.yyyy());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GSVector4 stq = m_local.d4.stq;
|
|
|
|
s += stq.xxxx();
|
|
t += stq.yyyy();
|
|
q += stq.zzzz();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!(sel.tfx == TFX_DECAL && sel.tcc))
|
|
{
|
|
if(sel.iip)
|
|
{
|
|
GSVector4i c = m_local.d4.c;
|
|
|
|
rbf = rbf.add16(c.xxxx()).max_i16(GSVector4i::zero());
|
|
gaf = gaf.add16(c.yyyy()).max_i16(GSVector4i::zero());
|
|
}
|
|
}
|
|
|
|
if(!sel.notest)
|
|
{
|
|
test = const_test[7 + (steps & (steps >> 31))];
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void GSDrawScanline::DrawEdge(int pixels, int left, int top, const GSVertexSW& scan)
|
|
{
|
|
uint32 zwrite = m_global.sel.zwrite;
|
|
uint32 edge = m_global.sel.edge;
|
|
|
|
m_global.sel.zwrite = 0;
|
|
m_global.sel.edge = 1;
|
|
|
|
DrawScanline(pixels, left, top, scan);
|
|
|
|
m_global.sel.zwrite = zwrite;
|
|
m_global.sel.edge = edge;
|
|
}
|
|
|
|
template<class T>
|
|
bool GSDrawScanline::TestAlpha(T& test, T& fm, T& zm, const T& ga)
|
|
{
|
|
GSScanlineSelector sel = m_global.sel;
|
|
|
|
switch(sel.afail)
|
|
{
|
|
case AFAIL_FB_ONLY:
|
|
if(!sel.zwrite) return true;
|
|
break;
|
|
|
|
case AFAIL_ZB_ONLY:
|
|
if(!sel.fwrite) return true;
|
|
break;
|
|
|
|
case AFAIL_RGB_ONLY:
|
|
if(!sel.zwrite && sel.fpsm == 1) return true;
|
|
break;
|
|
}
|
|
|
|
T t;
|
|
|
|
switch(sel.atst)
|
|
{
|
|
case ATST_NEVER:
|
|
t = GSVector4i::xffffffff();
|
|
break;
|
|
|
|
case ATST_ALWAYS:
|
|
return true;
|
|
|
|
case ATST_LESS:
|
|
case ATST_LEQUAL:
|
|
t = (ga >> 16) > T(m_global.aref);
|
|
break;
|
|
|
|
case ATST_EQUAL:
|
|
t = (ga >> 16) != T(m_global.aref);
|
|
break;
|
|
|
|
case ATST_GEQUAL:
|
|
case ATST_GREATER:
|
|
t = (ga >> 16) < T(m_global.aref);
|
|
break;
|
|
|
|
case ATST_NOTEQUAL:
|
|
t = (ga >> 16) == T(m_global.aref);
|
|
break;
|
|
|
|
default:
|
|
__assume(0);
|
|
}
|
|
|
|
switch(sel.afail)
|
|
{
|
|
case AFAIL_KEEP:
|
|
test |= t;
|
|
if(test.alltrue()) return false;
|
|
break;
|
|
|
|
case AFAIL_FB_ONLY:
|
|
zm |= t;
|
|
break;
|
|
|
|
case AFAIL_ZB_ONLY:
|
|
fm |= t;
|
|
break;
|
|
|
|
case AFAIL_RGB_ONLY:
|
|
zm |= t;
|
|
fm |= t & T::xff000000(); // fpsm 16 bit => & 0xffff8000?
|
|
break;
|
|
|
|
default:
|
|
__assume(0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static const int s_offsets[] = {0, 2, 8, 10, 16, 18, 24, 26}; // columnTable16[0]
|
|
|
|
template<class T> void GSDrawScanline::WritePixel(const T& src, int addr, int i, uint32 psm)
|
|
{
|
|
uint8* dst = (uint8*)m_global.vm + addr * 2 + s_offsets[i] * 2;
|
|
|
|
switch(psm)
|
|
{
|
|
case 0:
|
|
*(uint32*)dst = src.u32[i];
|
|
break;
|
|
case 1:
|
|
*(uint32*)dst = (src.u32[i] & 0xffffff) | (*(uint32*)dst & 0xff000000);
|
|
break;
|
|
case 2:
|
|
*(uint16*)dst = src.u16[i * 2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void GSDrawScanline::DrawRect(const GSVector4i& r, const GSVertexSW& v)
|
|
{
|
|
ASSERT(r.y >= 0);
|
|
ASSERT(r.w >= 0);
|
|
|
|
// FIXME: sometimes the frame and z buffer may overlap, the outcome is undefined
|
|
|
|
uint32 m;
|
|
|
|
#if _M_SSE >= 0x501
|
|
m = m_global.zm;
|
|
#else
|
|
m = m_global.zm.u32[0];
|
|
#endif
|
|
|
|
if(m != 0xffffffff)
|
|
{
|
|
const int* zbr = m_global.zbr;
|
|
const int* zbc = m_global.zbc;
|
|
|
|
uint32 z = v.t.u32[3]; // (uint32)v.p.z;
|
|
|
|
if(m_global.sel.zpsm != 2)
|
|
{
|
|
if(m == 0)
|
|
{
|
|
DrawRectT<uint32, false>(zbr, zbc, r, z, m);
|
|
}
|
|
else
|
|
{
|
|
DrawRectT<uint32, true>(zbr, zbc, r, z, m);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((m & 0xffff) == 0)
|
|
{
|
|
DrawRectT<uint16, false>(zbr, zbc, r, z, m);
|
|
}
|
|
else
|
|
{
|
|
DrawRectT<uint16, true>(zbr, zbc, r, z, m);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if _M_SSE >= 0x501
|
|
m = m_global.fm;
|
|
#else
|
|
m = m_global.fm.u32[0];
|
|
#endif
|
|
|
|
if(m != 0xffffffff)
|
|
{
|
|
const int* fbr = m_global.fbr;
|
|
const int* fbc = m_global.fbc;
|
|
|
|
uint32 c = (GSVector4i(v.c) >> 7).rgba32();
|
|
|
|
if(m_global.sel.fba)
|
|
{
|
|
c |= 0x80000000;
|
|
}
|
|
|
|
if(m_global.sel.fpsm != 2)
|
|
{
|
|
if(m == 0)
|
|
{
|
|
DrawRectT<uint32, false>(fbr, fbc, r, c, m);
|
|
}
|
|
else
|
|
{
|
|
DrawRectT<uint32, true>(fbr, fbc, r, c, m);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
c = ((c & 0xf8) >> 3) | ((c & 0xf800) >> 6) | ((c & 0xf80000) >> 9) | ((c & 0x80000000) >> 16);
|
|
|
|
if((m & 0xffff) == 0)
|
|
{
|
|
DrawRectT<uint16, false>(fbr, fbc, r, c, m);
|
|
}
|
|
else
|
|
{
|
|
DrawRectT<uint16, true>(fbr, fbc, r, c, m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class T, bool masked>
|
|
void GSDrawScanline::DrawRectT(const int* RESTRICT row, const int* RESTRICT col, const GSVector4i& r, uint32 c, uint32 m)
|
|
{
|
|
if(m == 0xffffffff) return;
|
|
|
|
#if _M_SSE >= 0x501
|
|
|
|
GSVector8i color((int)c);
|
|
GSVector8i mask((int)m);
|
|
|
|
#else
|
|
|
|
GSVector4i color((int)c);
|
|
GSVector4i mask((int)m);
|
|
|
|
#endif
|
|
|
|
if(sizeof(T) == sizeof(uint16))
|
|
{
|
|
color = color.xxzzlh();
|
|
mask = mask.xxzzlh();
|
|
c = (c & 0xffff) | (c << 16);
|
|
m = (m & 0xffff) | (m << 16);
|
|
}
|
|
|
|
color = color.andnot(mask);
|
|
c = c & (~m);
|
|
|
|
if(masked) ASSERT(mask.u32[0] != 0);
|
|
|
|
GSVector4i br = r.ralign<Align_Inside>(GSVector2i(8 * 4 / sizeof(T), 8));
|
|
|
|
if(!br.rempty())
|
|
{
|
|
FillRect<T, masked>(row, col, GSVector4i(r.x, r.y, r.z, br.y), c, m);
|
|
FillRect<T, masked>(row, col, GSVector4i(r.x, br.w, r.z, r.w), c, m);
|
|
|
|
if(r.x < br.x || br.z < r.z)
|
|
{
|
|
FillRect<T, masked>(row, col, GSVector4i(r.x, br.y, br.x, br.w), c, m);
|
|
FillRect<T, masked>(row, col, GSVector4i(br.z, br.y, r.z, br.w), c, m);
|
|
}
|
|
|
|
FillBlock<T, masked>(row, col, br, color, mask);
|
|
}
|
|
else
|
|
{
|
|
FillRect<T, masked>(row, col, r, c, m);
|
|
}
|
|
}
|
|
|
|
template<class T, bool masked>
|
|
void GSDrawScanline::FillRect(const int* RESTRICT row, const int* RESTRICT col, const GSVector4i& r, uint32 c, uint32 m)
|
|
{
|
|
if(r.x >= r.z) return;
|
|
|
|
T* vm = (T*)m_global.vm;
|
|
|
|
for(int y = r.y; y < r.w; y++)
|
|
{
|
|
T* RESTRICT d = &vm[row[y]];
|
|
|
|
for(int x = r.x; x < r.z; x++)
|
|
{
|
|
d[col[x]] = (T)(!masked ? c : (c | (d[col[x]] & m)));
|
|
}
|
|
}
|
|
}
|
|
|
|
#if _M_SSE >= 0x501
|
|
|
|
template<class T, bool masked>
|
|
void GSDrawScanline::FillBlock(const int* RESTRICT row, const int* RESTRICT col, const GSVector4i& r, const GSVector8i& c, const GSVector8i& m)
|
|
{
|
|
if(r.x >= r.z) return;
|
|
|
|
T* vm = (T*)m_global.vm;
|
|
|
|
for(int y = r.y; y < r.w; y += 8)
|
|
{
|
|
T* RESTRICT d = &vm[row[y]];
|
|
|
|
for(int x = r.x; x < r.z; x += 8 * 4 / sizeof(T))
|
|
{
|
|
GSVector8i* RESTRICT p = (GSVector8i*)&d[col[x]];
|
|
|
|
p[0] = !masked ? c : (c | (p[0] & m));
|
|
p[1] = !masked ? c : (c | (p[1] & m));
|
|
p[2] = !masked ? c : (c | (p[2] & m));
|
|
p[3] = !masked ? c : (c | (p[3] & m));
|
|
p[4] = !masked ? c : (c | (p[4] & m));
|
|
p[5] = !masked ? c : (c | (p[5] & m));
|
|
p[6] = !masked ? c : (c | (p[6] & m));
|
|
p[7] = !masked ? c : (c | (p[7] & m));
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
template<class T, bool masked>
|
|
void GSDrawScanline::FillBlock(const int* RESTRICT row, const int* RESTRICT col, const GSVector4i& r, const GSVector4i& c, const GSVector4i& m)
|
|
{
|
|
if(r.x >= r.z) return;
|
|
|
|
T* vm = (T*)m_global.vm;
|
|
|
|
for(int y = r.y; y < r.w; y += 8)
|
|
{
|
|
T* RESTRICT d = &vm[row[y]];
|
|
|
|
for(int x = r.x; x < r.z; x += 8 * 4 / sizeof(T))
|
|
{
|
|
GSVector4i* RESTRICT p = (GSVector4i*)&d[col[x]];
|
|
|
|
for(int i = 0; i < 16; i += 4)
|
|
{
|
|
p[i + 0] = !masked ? c : (c | (p[i + 0] & m));
|
|
p[i + 1] = !masked ? c : (c | (p[i + 1] & m));
|
|
p[i + 2] = !masked ? c : (c | (p[i + 2] & m));
|
|
p[i + 3] = !masked ? c : (c | (p[i + 3] & m));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|