GSdx: game fixes and small optimizations

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1495 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
gabest11 2009-07-12 13:46:05 +00:00
parent a72f0e6ee2
commit aa438a6b73
16 changed files with 703 additions and 536 deletions

View File

@ -147,6 +147,7 @@ CRC::Game CRC::m_games[] =
{0x04CCB600, ValkyrieProfile2, EU, 0}, {0x04CCB600, ValkyrieProfile2, EU, 0},
{0x47B9B2FD, RadiataStories, US, 0}, {0x47B9B2FD, RadiataStories, US, 0},
{0xE8FCF8EC, SMTNocturne, US, ZWriteMustNotClear}, // saves/reloads z buffer around shadow drawing {0xE8FCF8EC, SMTNocturne, US, ZWriteMustNotClear}, // saves/reloads z buffer around shadow drawing
{0x0B8AB37B, RozenMaidenGebetGarden, JP, 0},
}; };
hash_map<uint32, CRC::Game*> CRC::m_map; hash_map<uint32, CRC::Game*> CRC::m_map;

View File

@ -76,6 +76,7 @@ public:
ValkyrieProfile2, ValkyrieProfile2,
RadiataStories, RadiataStories,
SMTNocturne, SMTNocturne,
RozenMaidenGebetGarden,
TitleCount, TitleCount,
}; };
@ -91,7 +92,8 @@ public:
DE, DE,
IT, IT,
ES, ES,
ASIA ASIA,
RegionCount,
}; };
enum Flags enum Flags

View File

@ -28,22 +28,22 @@ template<class Vertex>
class GSRendererDX : public GSRendererHW<Vertex> class GSRendererDX : public GSRendererHW<Vertex>
{ {
GSTextureFX* m_tfx; GSTextureFX* m_tfx;
GSVector2 m_pixelcenter;
bool m_logz; bool m_logz;
bool m_fba; bool m_fba;
protected: protected:
int m_topology; int m_topology;
GSVector2 m_pixelcenter;
virtual void SetupDATE(GSTexture* rt, GSTexture* ds) {} virtual void SetupDATE(GSTexture* rt, GSTexture* ds) {}
virtual void UpdateFBA(GSTexture* rt) {} virtual void UpdateFBA(GSTexture* rt) {}
public: public:
GSRendererDX(uint8* base, bool mt, void (*irq)(), GSDevice* dev, GSTextureCache* tc, GSTextureFX* tfx) GSRendererDX(uint8* base, bool mt, void (*irq)(), GSDevice* dev, GSTextureCache* tc, GSTextureFX* tfx, const GSVector2& pixelcenter = GSVector2(0, 0))
: GSRendererHW<Vertex>(base, mt, irq, dev, tc) : GSRendererHW<Vertex>(base, mt, irq, dev, tc)
, m_tfx(tfx) , m_tfx(tfx)
, m_pixelcenter(pixelcenter)
, m_topology(-1) , m_topology(-1)
, m_pixelcenter(0, 0)
{ {
m_logz = !!theApp.GetConfig("logz", 0); m_logz = !!theApp.GetConfig("logz", 0);
m_fba = !!theApp.GetConfig("fba", 1); m_fba = !!theApp.GetConfig("fba", 1);
@ -189,36 +189,25 @@ public:
// ps // ps
GSTextureFX::PSSelector ps_sel; GSTextureFX::PSSelector ps_sel;
ps_sel.fst = PRIM->FST;
ps_sel.wms = context->CLAMP.WMS;
ps_sel.wmt = context->CLAMP.WMT;
ps_sel.aem = env.TEXA.AEM;
ps_sel.tfx = context->TEX0.TFX;
ps_sel.tcc = context->TEX0.TCC;
ps_sel.ate = context->TEST.ATE;
ps_sel.atst = context->TEST.ATST;
ps_sel.fog = PRIM->FGE;
ps_sel.clr1 = om_bsel.abe && om_bsel.a == 1 && om_bsel.b == 2 && om_bsel.d == 1;
ps_sel.fba = context->FBA.FBA;
ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0;
ps_sel.ltf = m_filter == 2 ? IsLinear() : m_filter;
GSTextureFX::PSSamplerSelector ps_ssel; GSTextureFX::PSSamplerSelector ps_ssel;
ps_ssel.tau = 0;
ps_ssel.tav = 0;
ps_ssel.ltf = ps_sel.ltf;
GSTextureFX::PSConstantBuffer ps_cb; GSTextureFX::PSConstantBuffer ps_cb;
if(ps_sel.fog) ps_sel.clr1 = om_bsel.IsCLR1();
ps_sel.fba = context->FBA.FBA;
ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0;
if(PRIM->FGE)
{ {
ps_cb.FogColor_AREF = GSVector4((int)env.FOGCOL.FCR, (int)env.FOGCOL.FCG, (int)env.FOGCOL.FCB, 0) / 255; ps_sel.fog = 1;
ps_cb.FogColor_AREF = GSVector4(env.FOGCOL.u32[0]) / 255;
} }
if(ps_sel.ate) if(context->TEST.ATE)
{ {
ps_sel.ate = 1;
ps_sel.atst = context->TEST.ATST;
switch(ps_sel.atst) switch(ps_sel.atst)
{ {
case ATST_LESS: case ATST_LESS:
@ -235,61 +224,55 @@ public:
if(tex) if(tex)
{ {
ps_sel.fst = PRIM->FST;
ps_sel.wms = context->CLAMP.WMS;
ps_sel.wmt = context->CLAMP.WMT;
ps_sel.fmt = tex->m_fmt; ps_sel.fmt = tex->m_fmt;
ps_sel.aem = env.TEXA.AEM;
ps_sel.tfx = context->TEX0.TFX;
ps_sel.tcc = context->TEX0.TCC;
ps_sel.ltf = m_filter == 2 ? IsLinear() : m_filter;
ps_sel.rt = tex->m_target; ps_sel.rt = tex->m_target;
int w = tex->m_texture->GetWidth(); int w = tex->m_texture->GetWidth();
int h = tex->m_texture->GetHeight(); int h = tex->m_texture->GetHeight();
ps_cb.WH = GSVector4((int)(1 << context->TEX0.TW), (int)(1 << context->TEX0.TH), w, h); int tw = (int)(1 << context->TEX0.TW);
int th = (int)(1 << context->TEX0.TH);
ps_cb.WH = GSVector4(tw, th, w, h);
ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / GSVector4(w, h).xyxy(); ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / GSVector4(w, h).xyxy();
ps_cb.MinF_TA.z = (float)(int)env.TEXA.TA0 / 255; ps_cb.MinF_TA.z = (float)(int)env.TEXA.TA0 / 255;
ps_cb.MinF_TA.w = (float)(int)env.TEXA.TA1 / 255; ps_cb.MinF_TA.w = (float)(int)env.TEXA.TA1 / 255;
switch(context->CLAMP.WMS) ps_ssel.tau = (context->CLAMP.WMS + 3) >> 1;
ps_ssel.tav = (context->CLAMP.WMT + 3) >> 1;
ps_ssel.ltf = ps_sel.ltf;
switch(ps_sel.wms)
{ {
case CLAMP_REPEAT:
ps_ssel.tau = 1;
break;
case CLAMP_CLAMP:
ps_ssel.tau = 0;
break;
case CLAMP_REGION_CLAMP: case CLAMP_REGION_CLAMP:
ps_cb.MinMax.x = ((float)(int)context->CLAMP.MINU) / (1 << context->TEX0.TW); ps_cb.MinMax.x = (float)(int)context->CLAMP.MINU / tw;
ps_cb.MinMax.z = ((float)(int)context->CLAMP.MAXU) / (1 << context->TEX0.TW); ps_cb.MinMax.z = (float)(int)context->CLAMP.MAXU / tw;
ps_cb.MinF_TA.x = ((float)(int)context->CLAMP.MINU + 0.5f) / (1 << context->TEX0.TW); ps_cb.MinF_TA.x = ((float)(int)context->CLAMP.MINU + 0.5f) / tw;
ps_ssel.tau = 0;
break; break;
case CLAMP_REGION_REPEAT: case CLAMP_REGION_REPEAT:
ps_cb.MskFix.x = context->CLAMP.MINU; ps_cb.MskFix.x = context->CLAMP.MINU;
ps_cb.MskFix.z = context->CLAMP.MAXU; ps_cb.MskFix.z = context->CLAMP.MAXU;
ps_ssel.tau = 1;
break; break;
default:
__assume(0);
} }
switch(context->CLAMP.WMT) switch(ps_sel.wmt)
{ {
case CLAMP_REPEAT:
ps_ssel.tav = 1;
break;
case CLAMP_CLAMP:
ps_ssel.tav = 0;
break;
case CLAMP_REGION_CLAMP: case CLAMP_REGION_CLAMP:
ps_cb.MinMax.y = ((float)(int)context->CLAMP.MINV) / (1 << context->TEX0.TH); ps_cb.MinMax.y = (float)(int)context->CLAMP.MINV / th;
ps_cb.MinMax.w = ((float)(int)context->CLAMP.MAXV) / (1 << context->TEX0.TH); ps_cb.MinMax.w = (float)(int)context->CLAMP.MAXV / th;
ps_cb.MinF_TA.y = ((float)(int)context->CLAMP.MINV + 0.5f) / (1 << context->TEX0.TH); ps_cb.MinF_TA.y = ((float)(int)context->CLAMP.MINV + 0.5f) / th;
ps_ssel.tav = 0;
break; break;
case CLAMP_REGION_REPEAT: case CLAMP_REGION_REPEAT:
ps_cb.MskFix.y = context->CLAMP.MINV; ps_cb.MskFix.y = context->CLAMP.MINV;
ps_cb.MskFix.w = context->CLAMP.MAXV; ps_cb.MskFix.w = context->CLAMP.MAXV;
ps_ssel.tav = 1;
break; break;
default:
__assume(0);
} }
} }
else else

View File

@ -25,11 +25,9 @@
#include "resource.h" #include "resource.h"
GSRendererDX10::GSRendererDX10(uint8* base, bool mt, void (*irq)()) GSRendererDX10::GSRendererDX10(uint8* base, bool mt, void (*irq)())
: GSRendererDX<GSVertexHW10>(base, mt, irq, new GSDevice10(), new GSTextureCache10(this), new GSTextureFX10()) : GSRendererDX<GSVertexHW10>(base, mt, irq, new GSDevice10(), new GSTextureCache10(this), new GSTextureFX10(), GSVector2(-0.5f, -0.5f))
{ {
InitVertexKick<GSRendererDX10>(); InitVertexKick<GSRendererDX10>();
m_pixelcenter = GSVector2(-0.5f, -0.5f);
} }
bool GSRendererDX10::Create(const string& title) bool GSRendererDX10::Create(const string& title)

View File

@ -25,11 +25,9 @@
#include "resource.h" #include "resource.h"
GSRendererDX11::GSRendererDX11(uint8* base, bool mt, void (*irq)()) GSRendererDX11::GSRendererDX11(uint8* base, bool mt, void (*irq)())
: GSRendererDX<GSVertexHW11>(base, mt, irq, new GSDevice11(), new GSTextureCache11(this), new GSTextureFX11()) : GSRendererDX<GSVertexHW11>(base, mt, irq, new GSDevice11(), new GSTextureCache11(this), new GSTextureFX11(), GSVector2(-0.5f, -0.5f))
{ {
InitVertexKick<GSRendererDX11>(); InitVertexKick<GSRendererDX11>();
m_pixelcenter = GSVector2(-0.5f, -0.5f);
} }
bool GSRendererDX11::Create(const string& title) bool GSRendererDX11::Create(const string& title)

View File

@ -24,6 +24,8 @@
#include "GSRenderer.h" #include "GSRenderer.h"
#include "GSTextureCache.h" #include "GSTextureCache.h"
#include "GSCrc.h" #include "GSCrc.h"
#include "GSFunctionMap.h"
template<class Vertex> template<class Vertex>
class GSRendererHW : public GSRendererT<Vertex> class GSRendererHW : public GSRendererT<Vertex>
@ -33,6 +35,398 @@ class GSRendererHW : public GSRendererT<Vertex>
int m_skip; int m_skip;
bool m_reset; bool m_reset;
#pragma region hacks
typedef bool (GSRendererHW::*OI_Ptr)(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
typedef void (GSRendererHW::*OO_Ptr)();
typedef bool (GSRendererHW::*CU_Ptr)();
bool OI_FFXII(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
static uint32* video = NULL;
static bool ok = false;
if(prim == GS_POINTLIST && m_count >= 448 * 448 && m_count <= 448 * 512)
{
// incoming pixels are stored in columns, one column is 16x512, total res 448x512 or 448x454
if(!video) video = new uint32[512 * 512];
for(int x = 0, i = 0, rows = m_count / 448; x < 448; x += 16)
{
uint32* dst = &video[x];
for(int y = 0; y < rows; y++, dst += 512)
{
for(int j = 0; j < 16; j++, i++)
{
dst[j] = m_vertices[i].c0;
}
}
}
ok = true;
return false;
}
else if(prim == GS_LINELIST && m_count == 512 * 2 && ok)
{
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
// but we use the stored video data to create a new texture, and replace the lines with two triangles
ok = false;
m_dev->Recycle(t->m_texture);
t->m_texture = m_dev->CreateTexture(512, 512);
t->m_texture->Update(GSVector4i(0, 0, 448, 512), video, 512 * 4);
m_vertices[0] = m_vertices[0];
m_vertices[1] = m_vertices[1];
m_vertices[2] = m_vertices[m_count - 2];
m_vertices[3] = m_vertices[1];
m_vertices[4] = m_vertices[2];
m_vertices[5] = m_vertices[m_count - 1];
prim = GS_TRIANGLELIST;
m_count = 6;
return true;
}
return true;
}
bool OI_FFX(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
uint32 FBP = m_context->FRAME.Block();
uint32 ZBP = m_context->ZBUF.Block();
uint32 TBP = m_context->TEX0.TBP0;
if((FBP == 0x00d00 || FBP == 0x00000) && ZBP == 0x02100 && PRIM->TME && TBP == 0x01a00 && m_context->TEX0.PSM == PSM_PSMCT16S)
{
// random battle transition (z buffer written directly, clear it now)
m_dev->ClearDepth(ds, 0);
}
return true;
}
bool OI_MetalSlug6(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// missing red channel fix
for(int i = 0, j = m_count; i < j; i++)
{
if(m_vertices[i].r == 0 && m_vertices[i].g != 0 && m_vertices[i].b != 0)
{
m_vertices[i].r = (m_vertices[i].g + m_vertices[i].b) / 2;
}
}
return true;
}
bool OI_GodOfWar2(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
uint32 FBP = m_context->FRAME.Block();
uint32 FBW = m_context->FRAME.FBW;
uint32 FPSM = m_context->FRAME.PSM;
if((FBP == 0x00f00 || FBP == 0x00100) && FPSM == PSM_PSMZ24) // ntsc 0xf00, pal 0x100
{
// z buffer clear
GIFRegTEX0 TEX0;
TEX0.TBP0 = FBP;
TEX0.TBW = FBW;
TEX0.PSM = FPSM;
if(GSTextureCache::Target* ds = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::DepthStencil, true))
{
m_dev->ClearDepth(ds->m_texture, 0);
}
return false;
}
return true;
}
bool OI_SimpsonsGame(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
uint32 FBP = m_context->FRAME.Block();
uint32 FBW = m_context->FRAME.FBW;
uint32 FPSM = m_context->FRAME.PSM;
if(FBP == 0x01800 && FPSM == PSM_PSMZ24)
{
// instead of just simply drawing a full height 512x512 sprite to clear the z buffer,
// it uses a 512x256 sprite only, yet it is still able to fill the whole surface with zeros,
// how? by using a render target that overlaps with the lower half of the z buffer...
m_dev->ClearDepth(ds, 0);
return false;
}
return true;
}
bool OI_RozenMaidenGebetGarden(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if(!PRIM->TME)
{
uint32 FBP = m_context->FRAME.Block();
uint32 ZBP = m_context->ZBUF.Block();
if(FBP == 0x008c0 && ZBP == 0x01a40)
{
// frame buffer clear, atst = fail, afail = write z only, z buffer points to frame buffer
GIFRegTEX0 TEX0;
TEX0.TBP0 = ZBP;
TEX0.TBW = m_context->FRAME.FBW;
TEX0.PSM = m_context->FRAME.PSM;
if(GSTextureCache::Target* rt = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::RenderTarget, true))
{
m_dev->ClearRenderTarget(rt->m_texture, 0);
}
return false;
}
else if(FBP == 0x00000 && m_context->ZBUF.Block() == 0x01180)
{
// z buffer clear, frame buffer now points to the z buffer (how can they be so clever?)
GIFRegTEX0 TEX0;
TEX0.TBP0 = FBP;
TEX0.TBW = m_context->FRAME.FBW;
TEX0.PSM = m_context->ZBUF.PSM;
if(GSTextureCache::Target* ds = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::DepthStencil, true))
{
m_dev->ClearDepth(ds->m_texture, 0);
}
return false;
}
}
return true;
}
bool OI_PointListPalette(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if(prim == GS_POINTLIST && !PRIM->TME)
{
uint32 FBP = m_context->FRAME.Block();
uint32 FBW = m_context->FRAME.FBW;
if(FBP >= 0x03f40 && (FBP & 0x1f) == 0)
{
if(m_count == 16)
{
for(int i = 0; i < 16; i++)
{
m_vertices[i].a = m_vertices[i].a >= 0x80 ? 0xff : m_vertices[i].a * 2;
m_mem.WritePixel32(i & 7, i >> 3, m_vertices[i].c0, FBP, FBW);
}
m_mem.m_clut.Invalidate();
return false;
}
else if(m_count == 256)
{
for(int i = 0; i < 256; i++)
{
m_vertices[i].a = m_vertices[i].a >= 0x80 ? 0xff : m_vertices[i].a * 2;
m_mem.WritePixel32(i & 15, i >> 4, m_vertices[i].c0, FBP, FBW);
}
m_mem.m_clut.Invalidate();
return false;
}
else
{
ASSERT(0);
}
}
}
return true;
}
void OO_DBZBT2()
{
// palette readback (cannot detect yet, when fetching the texture later)
uint32 FBP = m_context->FRAME.Block();
uint32 TBP0 = m_context->TEX0.TBP0;
if(PRIM->TME && (FBP == 0x03c00 && TBP0 == 0x03c80 || FBP == 0x03ac0 && TBP0 == 0x03b40))
{
GIFRegBITBLTBUF BITBLTBUF;
BITBLTBUF.SBP = FBP;
BITBLTBUF.SBW = 1;
BITBLTBUF.SPSM = PSM_PSMCT32;
InvalidateLocalMem(BITBLTBUF, GSVector4i(0, 0, 64, 64));
}
}
void OO_MajokkoALaMode2()
{
// palette readback
uint32 FBP = m_context->FRAME.Block();
if(!PRIM->TME && FBP == 0x03f40)
{
GIFRegBITBLTBUF BITBLTBUF;
BITBLTBUF.SBP = FBP;
BITBLTBUF.SBW = 1;
BITBLTBUF.SPSM = PSM_PSMCT32;
InvalidateLocalMem(BITBLTBUF, GSVector4i(0, 0, 16, 16));
}
}
bool CU_DBZBT2()
{
// palette should stay 64 x 64
uint32 FBP = m_context->FRAME.Block();
return FBP != 0x03c00 && FBP != 0x03ac0;
}
bool CU_MajokkoALaMode2()
{
// palette should stay 16 x 16
uint32 FBP = m_context->FRAME.Block();
return FBP != 0x03f40;
}
bool CU_TalesOfAbyss()
{
// full image blur and brightening
uint32 FBP = m_context->FRAME.Block();
return FBP != 0x036e0 && FBP != 0x03560 && FBP != 0x038e0;
}
class Hacks
{
template<class T> struct HackEntry
{
CRC::Title title;
CRC::Region region;
T func;
struct HackEntry(CRC::Title t, CRC::Region r, T f)
{
title = t;
region = r;
func = f;
}
};
template<class T> class FunctionMap : public GSFunctionMap<uint32, T>
{
list<HackEntry<T> >& m_tbl;
T GetDefaultFunction(uint32 key)
{
CRC::Title title = (CRC::Title)(key & 0xffffff);
CRC::Region region = (CRC::Region)(key >> 24);
for(list<HackEntry<T> >::iterator i = m_tbl.begin(); i != m_tbl.end(); i++)
{
if(i->title == title && (i->region == CRC::RegionCount || i->region == region))
{
return i->func;
}
}
return NULL;
}
public:
FunctionMap(list<HackEntry<T> >& tbl) : m_tbl(tbl) {}
};
list<HackEntry<OI_Ptr> > m_oi_list;
list<HackEntry<OO_Ptr> > m_oo_list;
list<HackEntry<CU_Ptr> > m_cu_list;
FunctionMap<OI_Ptr> m_oi_map;
FunctionMap<OO_Ptr> m_oo_map;
FunctionMap<CU_Ptr> m_cu_map;
public:
OI_Ptr m_oi;
OO_Ptr m_oo;
CU_Ptr m_cu;
Hacks()
: m_oi_map(m_oi_list)
, m_oo_map(m_oo_list)
, m_cu_map(m_cu_list)
, m_oi(NULL)
, m_oo(NULL)
, m_cu(NULL)
{
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::FFXII, CRC::EU, &GSRendererHW::OI_FFXII));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::FFX, CRC::RegionCount, &GSRendererHW::OI_FFX));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::MetalSlug6, CRC::RegionCount, &GSRendererHW::OI_MetalSlug6));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::GodOfWar2, CRC::RegionCount, &GSRendererHW::OI_GodOfWar2));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::SimpsonsGame, CRC::RegionCount, &GSRendererHW::OI_SimpsonsGame));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::RozenMaidenGebetGarden, CRC::RegionCount, &GSRendererHW::OI_RozenMaidenGebetGarden));
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OO_DBZBT2));
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::OO_MajokkoALaMode2));
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::CU_DBZBT2));
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::CU_MajokkoALaMode2));
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::TalesOfAbyss, CRC::RegionCount, &GSRendererHW::CU_TalesOfAbyss));
}
void SetGame(const CRC::Game& game)
{
uint32 hash = (uint32)((game.region << 24) | game.title);
m_oi = m_oi_map[hash];
m_oo = m_oo_map[hash];
m_cu = m_cu_map[hash];
if(game.flags & CRC::PointListPalette)
{
ASSERT(m_oi == NULL);
m_oi = &GSRendererHW::OI_PointListPalette;
}
}
} m_hacks;
#pragma endregion
protected: protected:
GSTextureCache* m_tc; GSTextureCache* m_tc;
@ -196,7 +590,7 @@ protected:
int prim = PRIM->PRIM; int prim = PRIM->PRIM;
if(!OverrideInput(prim, rt->m_texture, ds->m_texture, tex)) if(m_hacks.m_oi && !(this->*m_hacks.m_oi)(prim, rt->m_texture, ds->m_texture, tex))
{ {
return; return;
} }
@ -241,6 +635,8 @@ protected:
if(fm != 0xffffffff) if(fm != 0xffffffff)
{ {
rt->m_valid = rt->m_valid.runion(r);
BITBLTBUF.DBP = context->FRAME.Block(); BITBLTBUF.DBP = context->FRAME.Block();
BITBLTBUF.DPSM = context->FRAME.PSM; BITBLTBUF.DPSM = context->FRAME.PSM;
@ -249,6 +645,8 @@ protected:
if(zm != 0xffffffff) if(zm != 0xffffffff)
{ {
ds->m_valid = ds->m_valid.runion(r);
BITBLTBUF.DBP = context->ZBUF.Block(); BITBLTBUF.DBP = context->ZBUF.Block();
BITBLTBUF.DPSM = context->ZBUF.PSM; BITBLTBUF.DPSM = context->ZBUF.PSM;
@ -257,7 +655,10 @@ protected:
// //
OverrideOutput(); if(m_hacks.m_oo)
{
(this->*m_hacks.m_oo)();
}
if(s_dump) if(s_dump)
{ {
@ -285,294 +686,12 @@ protected:
virtual void Draw(GS_PRIM_CLASS primclass, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0; virtual void Draw(GS_PRIM_CLASS primclass, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
virtual bool OverrideInput(int& prim, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
#pragma region ffxii pal video conversion
if(m_game.title == CRC::FFXII && m_game.region == CRC::EU)
{
static uint32* video = NULL;
static bool ok = false;
if(prim == GS_POINTLIST && m_count >= 448 * 448 && m_count <= 448 * 512)
{
// incoming pixels are stored in columns, one column is 16x512, total res 448x512 or 448x454
if(!video) video = new uint32[512 * 512];
for(int x = 0, i = 0, rows = m_count / 448; x < 448; x += 16)
{
uint32* dst = &video[x];
for(int y = 0; y < rows; y++, dst += 512)
{
for(int j = 0; j < 16; j++, i++)
{
dst[j] = m_vertices[i].c0;
}
}
}
ok = true;
return false;
}
else if(prim == GS_LINELIST && m_count == 512 * 2 && ok)
{
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
// but we use the stored video data to create a new texture, and replace the lines with two triangles
ok = false;
m_dev->Recycle(t->m_texture);
t->m_texture = m_dev->CreateTexture(512, 512);
t->m_texture->Update(GSVector4i(0, 0, 448, 512), video, 512 * 4);
m_vertices[0] = m_vertices[0];
m_vertices[1] = m_vertices[1];
m_vertices[2] = m_vertices[m_count - 2];
m_vertices[3] = m_vertices[1];
m_vertices[4] = m_vertices[2];
m_vertices[5] = m_vertices[m_count - 1];
prim = GS_TRIANGLELIST;
m_count = 6;
return true;
}
}
#pragma endregion
#pragma region ffx random battle transition (z buffer written directly, clear it now)
if(m_game.title == CRC::FFX)
{
uint32 FBP = m_context->FRAME.Block();
uint32 ZBP = m_context->ZBUF.Block();
uint32 TBP = m_context->TEX0.TBP0;
if((FBP == 0x00d00 || FBP == 0x00000) && ZBP == 0x02100 && PRIM->TME && TBP == 0x01a00 && m_context->TEX0.PSM == PSM_PSMCT16S)
{
m_dev->ClearDepth(ds, 0);
}
return true;
}
#pragma endregion
#pragma region metal slug missing red channel fix
if(m_game.title == CRC::MetalSlug6)
{
for(int i = 0, j = m_count; i < j; i++)
{
if(m_vertices[i].r == 0 && m_vertices[i].g != 0 && m_vertices[i].b != 0)
{
m_vertices[i].r = (m_vertices[i].g + m_vertices[i].b) / 2;
}
}
return true;
}
#pragma endregion
#pragma region palette uploaded in a point list, pure genius...
if(m_game.flags & CRC::PointListPalette)
{
if(prim == GS_POINTLIST && !PRIM->TME)
{
uint32 bp = m_context->FRAME.Block();
uint32 bw = m_context->FRAME.FBW;
if(bp >= 0x03f40 && (bp & 0x1f) == 0)
{
if(m_count == 16)
{
for(int i = 0; i < 16; i++)
{
m_vertices[i].a = m_vertices[i].a >= 0x80 ? 0xff : m_vertices[i].a * 2;
m_mem.WritePixel32(i & 7, i >> 3, m_vertices[i].c0, bp, bw);
}
m_mem.m_clut.Invalidate();
return false;
}
else if(m_count == 256)
{
for(int i = 0; i < 256; i++)
{
m_vertices[i].a = m_vertices[i].a >= 0x80 ? 0xff : m_vertices[i].a * 2;
m_mem.WritePixel32(i & 15, i >> 4, m_vertices[i].c0, bp, bw);
}
m_mem.m_clut.Invalidate();
return false;
}
else
{
ASSERT(0);
}
}
}
return true;
}
#pragma endregion
#pragma region GoW2 z buffer clear
if(m_game.title == CRC::GodOfWar2)
{
uint32 FBP = m_context->FRAME.Block();
uint32 FBW = m_context->FRAME.FBW;
uint32 FPSM = m_context->FRAME.PSM;
if((FBP == 0x00f00 || FBP == 0x00100) && FPSM == PSM_PSMZ24) // ntsc 0xf00, pal 0x100
{
GIFRegTEX0 TEX0;
TEX0.TBP0 = FBP;
TEX0.TBW = FBW;
TEX0.PSM = FPSM;
if(GSTextureCache::Target* ds = m_tc->LookupTarget(TEX0, m_width, m_height, GSTextureCache::DepthStencil, true))
{
m_dev->ClearDepth(ds->m_texture, 0);
}
return false;
}
return true;
}
#pragma endregion
#pragma region Simpsons Game z buffer clear
if(m_game.title == CRC::SimpsonsGame)
{
uint32 FBP = m_context->FRAME.Block();
uint32 FBW = m_context->FRAME.FBW;
uint32 FPSM = m_context->FRAME.PSM;
if(FBP == 0x01800 && FPSM == PSM_PSMZ24)
{
// instead of just simply drawing a full height 512x512 sprite to clear the z buffer,
// it uses a 512x256 sprite only, yet it is still able to fill the whole surface with zeros,
// how? by using a render target that overlaps with the lower half of the z buffer...
m_dev->ClearDepth(ds, 0);
return false;
}
return true;
}
#pragma endregion
return true;
}
virtual void OverrideOutput()
{
#pragma region dbzbt2 palette readback (cannot detect yet, when fetching the texture later)
if(m_game.title == CRC::DBZBT2)
{
uint32 FBP = m_context->FRAME.Block();
uint32 TBP0 = m_context->TEX0.TBP0;
if(PRIM->TME && (FBP == 0x03c00 && TBP0 == 0x03c80 || FBP == 0x03ac0 && TBP0 == 0x03b40))
{
GIFRegBITBLTBUF BITBLTBUF;
BITBLTBUF.SBP = FBP;
BITBLTBUF.SBW = 1;
BITBLTBUF.SPSM = PSM_PSMCT32;
InvalidateLocalMem(BITBLTBUF, GSVector4i(0, 0, 64, 64));
}
}
#pragma endregion
#pragma region MajokkoALaMode2 palette readback
if(m_game.title == CRC::MajokkoALaMode2)
{
uint32 FBP = m_context->FRAME.Block();
if(!PRIM->TME && FBP == 0x03f40)
{
GIFRegBITBLTBUF BITBLTBUF;
BITBLTBUF.SBP = FBP;
BITBLTBUF.SBW = 1;
BITBLTBUF.SPSM = PSM_PSMCT32;
InvalidateLocalMem(BITBLTBUF, GSVector4i(0, 0, 16, 16));
}
}
#pragma endregion
}
bool CanUpscale() bool CanUpscale()
{ {
#pragma region dbzbt2 palette should stay 64 x 64 if(m_hacks.m_cu && !(this->*m_hacks.m_cu)())
if(m_game.title == CRC::DBZBT2)
{
uint32 FBP = m_context->FRAME.Block();
if(FBP == 0x03c00 || FBP == 0x03ac0)
{ {
return false; return false;
} }
}
#pragma endregion
#pragma region MajokkoALaMode2 palette should stay 16 x 16
if(m_game.title == CRC::MajokkoALaMode2)
{
uint32 FBP = m_context->FRAME.Block();
if(FBP == 0x03f40)
{
return false;
}
}
#pragma endregion
#pragma region TalesOfAbyss full image blur and brightening
if(m_game.title == CRC::TalesOfAbyss)
{
uint32 FBP = m_context->FRAME.Block();
if(FBP == 0x036e0 || FBP == 0x03560 || FBP == 0x038e0)
{
return false;
}
}
#pragma endregion
return __super::CanUpscale(); return __super::CanUpscale();
} }
@ -602,6 +721,8 @@ public:
{ {
__super::SetGameCRC(crc, options); __super::SetGameCRC(crc, options);
m_hacks.SetGame(m_game);
if(m_game.title == CRC::JackieChanAdv) if(m_game.title == CRC::JackieChanAdv)
{ {
m_width = 1280; // TODO: uses a 1280px wide 16 bit render target, but this only fixes half of the problem m_width = 1280; // TODO: uses a 1280px wide 16 bit render target, but this only fixes half of the problem

View File

@ -322,7 +322,7 @@ void GSRendererSW::GetScanlineParam(GSScanlineParam& p, GS_PRIM_CLASS primclass)
p.sel.wms = context->CLAMP.WMS; p.sel.wms = context->CLAMP.WMS;
p.sel.wmt = context->CLAMP.WMT; p.sel.wmt = context->CLAMP.WMT;
if(/*p.sel.iip == 0 &&*/ p.sel.tfx == TFX_MODULATE && p.sel.tcc && m_vt.m_eq.rgba == 0xffff && m_vt.m_min.c.eq(GSVector4i(128))) if(p.sel.tfx == TFX_MODULATE && p.sel.tcc && m_vt.m_eq.rgba == 0xffff && m_vt.m_min.c.eq(GSVector4i(128)))
{ {
// modulate does not do anything when vertex color is 0x80 // modulate does not do anything when vertex color is 0x80

View File

@ -53,11 +53,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
Source* src = NULL; Source* src = NULL;
hash_map<Source*, bool>& m = m_src.m_map[TEX0.TBP0 >> 5]; list<Source*>& m = m_src.m_map[TEX0.TBP0 >> 5];
for(hash_map<Source*, bool>::iterator i = m.begin(); i != m.end(); i++) for(list<Source*>::iterator i = m.begin(); i != m.end(); i++)
{ {
Source* s = i->first; Source* s = *i;
if(((TEX0.u32[0] ^ s->m_TEX0.u32[0]) | ((TEX0.u32[1] ^ s->m_TEX0.u32[1]) & 3)) != 0) // TBP0 TBW PSM TW TH if(((TEX0.u32[0] ^ s->m_TEX0.u32[0]) | ((TEX0.u32[1] ^ s->m_TEX0.u32[1]) & 3)) != 0) // TBP0 TBW PSM TW TH
{ {
@ -74,6 +74,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
continue; continue;
} }
m.splice(m.begin(), m, i);
src = s; src = s;
break; break;
@ -245,13 +247,13 @@ void GSTextureCache::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const
if(!target) if(!target)
{ {
const hash_map<Source*, bool>& m = m_src.m_map[bp >> 5]; const list<Source*>& m = m_src.m_map[bp >> 5];
for(hash_map<Source*, bool>::const_iterator i = m.begin(); i != m.end(); ) for(list<Source*>::const_iterator i = m.begin(); i != m.end(); )
{ {
hash_map<Source*, bool>::const_iterator j = i++; list<Source*>::const_iterator j = i++;
Source* s = j->first; Source* s = *j;
if(GSUtil::HasSharedBits(bp, psm, s->m_TEX0.TBP0, s->m_TEX0.PSM)) if(GSUtil::HasSharedBits(bp, psm, s->m_TEX0.TBP0, s->m_TEX0.PSM))
{ {
@ -272,13 +274,13 @@ void GSTextureCache::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const
if(page < MAX_PAGES) if(page < MAX_PAGES)
{ {
const hash_map<Source*, bool>& m = m_src.m_map[page]; const list<Source*>& m = m_src.m_map[page];
for(hash_map<Source*, bool>::const_iterator i = m.begin(); i != m.end(); ) for(list<Source*>::const_iterator i = m.begin(); i != m.end(); )
{ {
hash_map<Source*, bool>::const_iterator j = i++; list<Source*>::const_iterator j = i++;
Source* s = j->first; Source* s = *j;
if(GSUtil::HasSharedBits(psm, s->m_TEX0.PSM)) if(GSUtil::HasSharedBits(psm, s->m_TEX0.PSM))
{ {
@ -368,7 +370,12 @@ void GSTextureCache::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const
{ {
if(GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM)) if(GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM))
{ {
t->Read(r); GSVector4i r2 = r.rintersect(t->m_valid);
if(!r2.rempty())
{
t->Read(r2);
}
return; return;
} }
@ -376,7 +383,12 @@ void GSTextureCache::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const
{ {
// ffx-2 riku changing to her default (shoots some reflecting glass at the end), 16-bit rt read as 32-bit // ffx-2 riku changing to her default (shoots some reflecting glass at the end), 16-bit rt read as 32-bit
t->Read(GSVector4i(r.left, r.top, r.right, r.top + (r.bottom - r.top) * 2)); GSVector4i r2 = GSVector4i(r.left, r.top, r.right, r.top + (r.bottom - r.top) * 2).rintersect(t->m_valid);
if(!r2.rempty())
{
t->Read(r2);
}
return; return;
} }
@ -447,7 +459,7 @@ void GSTextureCache::IncAge()
m_src.m_used = false; m_src.m_used = false;
maxage = 3; maxage = 4; // ffx intro scene changes leave the old image untouched for a couple of frames and only then start using it
for(int type = 0; type < 2; type++) for(int type = 0; type < 2; type++)
{ {
@ -785,7 +797,7 @@ void GSTextureCache::Source::Update(const GIFRegTEX0& TEX0, const GIFRegTEXA& TE
} }
} }
m_renderer->m_perfmon.Put(GSPerfMon::Unswizzle, bs.x * bs.y * sizeof(uint32) * blocks); m_renderer->m_perfmon.Put(GSPerfMon::Unswizzle, bs.x * bs.y * blocks << (m_fmt == GSTextureFX::FMT_32 ? 2 : 0));
Flush(m_write.count); Flush(m_write.count);
} }
@ -894,6 +906,7 @@ GSTextureCache::Target::Target(GSRenderer* r)
, m_type(-1) , m_type(-1)
, m_used(false) , m_used(false)
{ {
m_valid = GSVector4i::zero();
} }
bool GSTextureCache::Target::Create(int w, int h, int type) bool GSTextureCache::Target::Create(int w, int h, int type)
@ -988,7 +1001,7 @@ void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, GSLocalMe
{ {
// TODO // TODO
m_map[TEX0.TBP0 >> 5][s] = true; m_map[TEX0.TBP0 >> 5].push_front(s);
return; return;
} }
@ -1023,13 +1036,13 @@ void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, GSLocalMe
{ {
m_pages[i] = 0; m_pages[i] = 0;
hash_map<Source*, bool>* m = &m_map[i << 5]; list<Source*>* m = &m_map[i << 5];
for(int j = 0; j < 32; j++) for(int j = 0; j < 32; j++)
{ {
if(p & (1 << j)) if(p & (1 << j))
{ {
m[j][s] = true; m[j].push_front(s);
} }
} }
} }
@ -1052,19 +1065,15 @@ void GSTextureCache::SourceMap::RemoveAt(Source* s)
{ {
m_surfaces.erase(s); m_surfaces.erase(s);
uint32 page = s->m_TEX0.TBP0 >> 5; for(uint32 start = s->m_TEX0.TBP0 >> 5, end = s->m_target ? start : countof(m_map) - 1; start <= end; start++)
{
list<Source*>& m = m_map[start];
if(s->m_target) for(list<Source*>::iterator i = m.begin(); i != m.end(); )
{ {
// TODO list<Source*>::iterator j = i++;
m_map[page].erase(s); if(*j == s) {m.erase(j); break;}
}
else
{
for(uint32 i = page; i < countof(m_map); i++)
{
m_map[i].erase(s);
} }
} }

View File

@ -82,6 +82,7 @@ public:
int m_type; int m_type;
bool m_used; bool m_used;
GSDirtyRectList m_dirty; GSDirtyRectList m_dirty;
GSVector4i m_valid;
public: public:
explicit Target(GSRenderer* r); explicit Target(GSRenderer* r);
@ -98,7 +99,7 @@ protected:
struct SourceMap struct SourceMap
{ {
hash_map<Source*, bool> m_surfaces; hash_map<Source*, bool> m_surfaces;
hash_map<Source*, bool> m_map[MAX_PAGES]; list<Source*> m_map[MAX_PAGES];
uint32 m_pages[16]; uint32 m_pages[16];
bool m_used; bool m_used;

View File

@ -39,11 +39,11 @@ const GSTextureCacheSW::GSTexture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TE
GSTexture* t = NULL; GSTexture* t = NULL;
const hash_map<GSTexture*, bool>& map = m_map[TEX0.TBP0 >> 5]; list<GSTexture*>& m = m_map[TEX0.TBP0 >> 5];
for(hash_map<GSTexture*, bool>::const_iterator i = map.begin(); i != map.end(); i++) for(list<GSTexture*>::iterator i = m.begin(); i != m.end(); i++)
{ {
GSTexture* t2 = i->first; GSTexture* t2 = *i;
if(((TEX0.u32[0] ^ t2->m_TEX0.u32[0]) | ((TEX0.u32[1] ^ t2->m_TEX0.u32[1]) & 3)) != 0) // TBP0 TBW PSM TW TH if(((TEX0.u32[0] ^ t2->m_TEX0.u32[0]) | ((TEX0.u32[1] ^ t2->m_TEX0.u32[1]) & 3)) != 0) // TBP0 TBW PSM TW TH
{ {
@ -55,6 +55,8 @@ const GSTextureCacheSW::GSTexture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TE
continue; continue;
} }
m.splice(m.begin(), m, i);
t = t2; t = t2;
t->m_age = 0; t->m_age = 0;
@ -96,13 +98,13 @@ const GSTextureCacheSW::GSTexture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TE
{ {
m_pages[i] = 0; m_pages[i] = 0;
hash_map<GSTexture*, bool>* m = &m_map[i << 5]; list<GSTexture*>* m = &m_map[i << 5];
for(int j = 0; j < 32; j++) for(int j = 0; j < 32; j++)
{ {
if(p & (1 << j)) if(p & (1 << j))
{ {
m[j][t] = true; m[j].push_front(t);
} }
} }
} }
@ -113,16 +115,9 @@ const GSTextureCacheSW::GSTexture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TE
{ {
printf("!@#$%\n"); // memory allocation may fail if the game is too hungry printf("!@#$%\n"); // memory allocation may fail if the game is too hungry
m_textures.erase(t); RemoveAt(t);
for(int i = 0; i < MAX_PAGES; i++) t = NULL;
{
m_map[i].erase(t);
}
delete t;
return NULL;
} }
return t; return t;
@ -140,6 +135,25 @@ void GSTextureCacheSW::RemoveAll()
} }
} }
void GSTextureCacheSW::RemoveAt(GSTexture* t)
{
m_textures.erase(t);
for(uint32 start = t->m_TEX0.TBP0 >> 5, end = countof(m_map) - 1; start <= end; start++)
{
list<GSTexture*>& m = m_map[start];
for(list<GSTexture*>::iterator i = m.begin(); i != m.end(); )
{
list<GSTexture*>::iterator j = i++;
if(*j == t) {m.erase(j); break;}
}
}
delete t;
}
void GSTextureCacheSW::IncAge() void GSTextureCacheSW::IncAge()
{ {
for(hash_map<GSTexture*, bool>::iterator i = m_textures.begin(); i != m_textures.end(); ) for(hash_map<GSTexture*, bool>::iterator i = m_textures.begin(); i != m_textures.end(); )
@ -150,14 +164,7 @@ void GSTextureCacheSW::IncAge()
if(++t->m_age > 30) if(++t->m_age > 30)
{ {
m_textures.erase(j); RemoveAt(t);
for(int i = 0; i < MAX_PAGES; i++)
{
m_map[i].erase(t);
}
delete t;
} }
} }
} }
@ -184,11 +191,11 @@ void GSTextureCacheSW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, cons
if(page < MAX_PAGES) if(page < MAX_PAGES)
{ {
const hash_map<GSTexture*, bool>& map = m_map[page]; const list<GSTexture*>& map = m_map[page];
for(hash_map<GSTexture*, bool>::const_iterator i = map.begin(); i != map.end(); i++) for(list<GSTexture*>::const_iterator i = map.begin(); i != map.end(); i++)
{ {
GSTexture* t = i->first; GSTexture* t = *i;
if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM)) if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM))
{ {

View File

@ -47,7 +47,7 @@ public:
protected: protected:
GSState* m_state; GSState* m_state;
hash_map<GSTexture*, bool> m_textures; hash_map<GSTexture*, bool> m_textures;
hash_map<GSTexture*, bool> m_map[MAX_PAGES]; list<GSTexture*> m_map[MAX_PAGES];
uint32 m_pages[16]; uint32 m_pages[16];
public: public:
@ -57,6 +57,7 @@ public:
const GSTexture* Lookup(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r); const GSTexture* Lookup(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r);
void RemoveAll(); void RemoveAll();
void RemoveAt(GSTexture* t);
void IncAge(); void IncAge();
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r); void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
}; };

View File

@ -122,7 +122,7 @@ public:
GSVector4i b4 = b[4]; GSVector4i b4 = b[4];
GSVector4i b5 = b[5]; GSVector4i b5 = b[5];
if(!((a[0] == b0) & (a[1] == b1) & (a[2] == b2) & (a[3] == b3) & (a[4] == b4) & (a[5] == b5)).alltrue()) if(!((a[0] == b0) /*& (a[1] == b1)*/ & (a[2] == b2) & (a[3] == b3) & (a[4] == b4) & (a[5] == b5)).alltrue()) // if WH matches HalfTexel does too
{ {
a[0] = b0; a[0] = b0;
a[1] = b1; a[1] = b1;
@ -250,6 +250,11 @@ public:
operator uint32() {return key & 0x1fff;} operator uint32() {return key & 0x1fff;}
OMBlendSelector() : key(0) {} OMBlendSelector() : key(0) {}
bool IsCLR1() const
{
return (key & 0x19f) == 0x93; // abe == 1 && a == 1 && b == 2 && d == 1
}
}; };
#pragma pack(pop) #pragma pack(pop)

View File

@ -265,7 +265,7 @@ void GSTextureFX10::UpdatePS(PSSelector sel, const PSConstantBuffer* cb, PSSampl
if(i != m_ps_ss.end()) if(i != m_ps_ss.end())
{ {
ss0 = (*i).second; ss0 = i->second;
} }
else else
{ {
@ -360,7 +360,7 @@ void GSTextureFX10::UpdateOM(OMDepthStencilSelector dssel, OMBlendSelector bsel,
i = m_om_dss.find(dssel); i = m_om_dss.find(dssel);
} }
dev->OMSetDepthStencilState((*i).second, 1); dev->OMSetDepthStencilState(i->second, 1);
hash_map<uint32, CComPtr<ID3D10BlendState> >::const_iterator j = m_om_bs.find(bsel); hash_map<uint32, CComPtr<ID3D10BlendState> >::const_iterator j = m_om_bs.find(bsel);

View File

@ -51,17 +51,6 @@ __declspec(align(16)) struct GSVertex
GSVector4 GetUV() const {return GSVector4(GSVector4i::load(UV.u32[0]).upl16());} GSVector4 GetUV() const {return GSVector4(GSVector4i::load(UV.u32[0]).upl16());}
}; };
struct GSVertexOld
{
GIFRegRGBAQ RGBAQ;
GIFRegST ST;
GIFRegUV UV;
GIFRegXYZ XYZ;
GIFRegFOG FOG;
GSVertexOld() {memset(this, 0, sizeof(*this));}
};
struct GSVertexP struct GSVertexP
{ {
GSVector4 p; GSVector4 p;

View File

@ -12,18 +12,13 @@ struct VS_OUTPUT
float2 t : TEXCOORD0; float2 t : TEXCOORD0;
}; };
VS_OUTPUT vs_main(VS_INPUT input)
{
VS_OUTPUT output;
output.p = input.p;
output.t = input.t;
return output;
}
Texture2D Texture; Texture2D Texture;
SamplerState Sampler; SamplerState TextureSampler;
float4 sample_c(float2 uv)
{
return Texture.Sample(TextureSampler, uv);
}
struct PS_INPUT struct PS_INPUT
{ {
@ -31,70 +26,10 @@ struct PS_INPUT
float2 t : TEXCOORD0; float2 t : TEXCOORD0;
}; };
float4 ps_main0(PS_INPUT input) : SV_Target0 struct PS_OUTPUT
{ {
return Texture.Sample(Sampler, input.t); float4 c : SV_Target0;
} };
uint ps_main1(PS_INPUT input) : SV_Target0
{
float4 f = Texture.Sample(Sampler, input.t);
f.a *= 256.0f/127; // hm, 0.5 won't give us 1.0 if we just multiply with 2
uint4 i = f * float4(0x001f, 0x03e0, 0x7c00, 0x8000);
return (i.x & 0x001f) | (i.y & 0x03e0) | (i.z & 0x7c00) | (i.w & 0x8000);
}
float4 ps_main2(PS_INPUT input) : SV_Target0
{
clip(Texture.Sample(Sampler, input.t).a - (0.5 - 0.9f/256));
return 0;
}
float4 ps_main3(PS_INPUT input) : SV_Target0
{
clip((0.5 - 0.9f/256) - Texture.Sample(Sampler, input.t).a);
return 0;
}
float4 ps_main4(PS_INPUT input) : SV_Target0
{
float4 c = Texture.Sample(Sampler, input.t);
return fmod(c * 255 + 0.5f, 256) / 255;
}
float4 ps_crt(PS_INPUT input, uint i)
{
float4 mask[4] =
{
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(1, 1, 1, 0)
};
return Texture.Sample(Sampler, input.t) * saturate(mask[i] + 0.5f);
}
float4 ps_main5(PS_INPUT input) : SV_Target0 // triangular
{
uint4 p = (uint4)input.p;
// return ps_crt(input, ((p.x + (p.y & 1) * 3) >> 1) % 3);
return ps_crt(input, ((p.x + ((p.y >> 1) & 1) * 3) >> 1) % 3);
}
float4 ps_main6(PS_INPUT input) : SV_Target0 // diagonal
{
uint4 p = (uint4)input.p;
return ps_crt(input, (p.x + (p.y % 3)) % 3);
}
#elif SHADER_MODEL <= 0x300 #elif SHADER_MODEL <= 0x300
@ -110,6 +45,26 @@ struct VS_OUTPUT
float2 t : TEXCOORD0; float2 t : TEXCOORD0;
}; };
struct PS_INPUT
{
float4 p : VPOS;
float2 t : TEXCOORD0;
};
struct PS_OUTPUT
{
float4 c : COLOR;
};
sampler Texture : register(s0);
float4 sample_c(float2 uv)
{
return tex2D(Texture, uv);
}
#endif
VS_OUTPUT vs_main(VS_INPUT input) VS_OUTPUT vs_main(VS_INPUT input)
{ {
VS_OUTPUT output; VS_OUTPUT output;
@ -120,40 +75,16 @@ VS_OUTPUT vs_main(VS_INPUT input)
return output; return output;
} }
sampler Texture : register(s0); PS_OUTPUT ps_main0(PS_INPUT input)
float4 ps_main0(float2 t : TEXCOORD0) : COLOR
{ {
return tex2D(Texture, t); PS_OUTPUT output;
output.c = sample_c(input.t);
return output;
} }
float4 ps_main1(float2 t : TEXCOORD0) : COLOR float4 ps_crt(PS_INPUT input, int i)
{
float4 c = tex2D(Texture, t);
c.a *= 128.0f / 255; // *= 0.5f is no good here, need to do this in order to get 0x80 for 1.0f (instead of 0x7f)
return c;
}
float4 ps_main2(float2 t : TEXCOORD0) : COLOR
{
clip(tex2D(Texture, t).a - (1.0f - 0.9f/256));
return 0;
}
float4 ps_main3(float2 t : TEXCOORD0) : COLOR
{
clip((1.0f - 0.9f/256) - tex2D(Texture, t).a);
return 0;
}
float4 ps_main4() : COLOR
{
return 1;
}
float4 ps_crt(float2 t, int i)
{ {
float4 mask[4] = float4 mask[4] =
{ {
@ -163,22 +94,143 @@ float4 ps_crt(float2 t, int i)
float4(1, 1, 1, 0) float4(1, 1, 1, 0)
}; };
return tex2D(Texture, t) * saturate(mask[i] + 0.5f); return sample_c(input.t) * saturate(mask[i] + 0.5f);
} }
float4 ps_main5(float2 t : TEXCOORD0, float4 vPos : VPOS) : COLOR // triangular #if SHADER_MODEL >= 0x400
{
int4 p = (int4)vPos;
// return ps_crt(t, ((p.x + (p.y % 2) * 3) / 2) % 3); uint ps_main1(PS_INPUT input) : SV_Target0
return ps_crt(t, ((p.x + ((p.y / 2) % 2) * 3) / 2) % 3); {
float4 c = sample_c(input.t);
c.a *= 256.0f / 127; // hm, 0.5 won't give us 1.0 if we just multiply with 2
uint4 i = c * float4(0x001f, 0x03e0, 0x7c00, 0x8000);
return (i.x & 0x001f) | (i.y & 0x03e0) | (i.z & 0x7c00) | (i.w & 0x8000);
} }
float4 ps_main6(float2 t : TEXCOORD0, float4 vPos : VPOS) : COLOR // diagonal PS_OUTPUT ps_main2(PS_INPUT input)
{ {
int4 p = (int4)vPos; PS_OUTPUT output;
return ps_crt(t, (p.x + (p.y % 3)) % 3); clip(sample_c(input.t).a - 128.0f / 255); // >= 0x80 pass
output.c = 0;
return output;
}
PS_OUTPUT ps_main3(PS_INPUT input)
{
PS_OUTPUT output;
clip(127.95f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
output.c = 0;
return output;
}
PS_OUTPUT ps_main4(PS_INPUT input)
{
PS_OUTPUT output;
output.c = fmod(sample_c(input.t) * 255 + 0.5f, 256) / 255;
return output;
}
PS_OUTPUT ps_main5(PS_INPUT input) // triangular
{
PS_OUTPUT output;
uint4 p = (uint4)input.p;
// output.c = ps_crt(input, ((p.x + (p.y & 1) * 3) >> 1) % 3);
output.c = ps_crt(input, ((p.x + ((p.y >> 1) & 1) * 3) >> 1) % 3);
return output;
}
PS_OUTPUT ps_main6(PS_INPUT input) // diagonal
{
PS_OUTPUT output;
uint4 p = (uint4)input.p;
output.c = ps_crt(input, (p.x + (p.y % 3)) % 3);
return output;
}
#elif SHADER_MODEL <= 0x300
PS_OUTPUT ps_main1(PS_INPUT input)
{
PS_OUTPUT output;
float4 c = sample_c(input.t);
c.a *= 128.0f / 255; // *= 0.5f is no good here, need to do this in order to get 0x80 for 1.0f (instead of 0x7f)
output.c = c;
return output;
}
PS_OUTPUT ps_main2(PS_INPUT input)
{
PS_OUTPUT output;
clip(sample_c(input.t).a - 255.0f / 255); // >= 0x80 pass
output.c = 0;
return output;
}
PS_OUTPUT ps_main3(PS_INPUT input)
{
PS_OUTPUT output;
clip(254.95f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
output.c = 0;
return output;
}
PS_OUTPUT ps_main4(PS_INPUT input)
{
PS_OUTPUT output;
output.c = 1;
return output;
}
PS_OUTPUT ps_main5(PS_INPUT input) // triangular
{
PS_OUTPUT output;
int4 p = (int4)input.p;
// output.c = ps_crt(input, ((p.x + (p.y % 2) * 3) / 2) % 3);
output.c = ps_crt(input, ((p.x + ((p.y / 2) % 2) * 3) / 2) % 3);
return output;
}
PS_OUTPUT ps_main6(PS_INPUT input) // diagonal
{
PS_OUTPUT output;
int4 p = (int4)input.p;
output.c = ps_crt(input, (p.x + (p.y % 3)) % 3);
return output;
} }
#endif #endif

View File

@ -298,7 +298,7 @@ float4 sample_4a(float4 uv)
c.w = sample_c(uv.zw).a; c.w = sample_c(uv.zw).a;
#if SHADER_MODEL <= 0x300 #if SHADER_MODEL <= 0x300
if(PS_RT) c *= 0.5; if(PS_RT) c *= 128.0f / 255;
#endif #endif
return c; return c;
@ -387,7 +387,7 @@ float4 sample(float2 st, float q)
if(PS_FMT == FMT_32) if(PS_FMT == FMT_32)
{ {
#if SHADER_MODEL <= 0x300 #if SHADER_MODEL <= 0x300
if(PS_RT) t.a *= 0.5; if(PS_RT) t.a *= 128.0f / 255;
#endif #endif
} }
else if(PS_FMT == FMT_24) else if(PS_FMT == FMT_24)