mirror of https://github.com/PCSX2/pcsx2.git
commit
59ef668fd2
|
@ -192,19 +192,22 @@ bool GSRenderer::Merge(int field)
|
|||
GSVector2i ds(0, 0);
|
||||
|
||||
GSTexture* tex[2] = {NULL, NULL};
|
||||
int y_offset[2] = {0, 0};
|
||||
|
||||
if(samesrc && fr[0].bottom == fr[1].bottom)
|
||||
{
|
||||
tex[0] = GetOutput(0);
|
||||
tex[1] = tex[0]; // saves one texture fetch
|
||||
tex[0] = GetOutput(0, y_offset[0]);
|
||||
tex[1] = tex[0]; // saves one texture fetch
|
||||
y_offset[1] = y_offset[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(en[0]) tex[0] = GetOutput(0);
|
||||
if(en[1]) tex[1] = GetOutput(1);
|
||||
if(en[0]) tex[0] = GetOutput(0, y_offset[0]);
|
||||
if(en[1]) tex[1] = GetOutput(1, y_offset[1]);
|
||||
}
|
||||
|
||||
GSVector4 src[2];
|
||||
GSVector4 src_hw[2];
|
||||
GSVector4 dst[2];
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
|
@ -224,6 +227,7 @@ bool GSRenderer::Merge(int field)
|
|||
GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy();
|
||||
|
||||
src[i] = GSVector4(r) * scale / GSVector4(tex[i]->GetSize()).xyxy();
|
||||
src_hw[i] = (GSVector4(r) + GSVector4 (0, y_offset[i], 0, y_offset[i])) * scale / GSVector4(tex[i]->GetSize()).xyxy();
|
||||
|
||||
GSVector2 off(0, 0);
|
||||
|
||||
|
@ -264,7 +268,7 @@ bool GSRenderer::Merge(int field)
|
|||
|
||||
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
|
||||
|
||||
m_dev->Merge(tex, src, dst, fs, slbg, mmod, c);
|
||||
m_dev->Merge(tex, src_hw, dst, fs, slbg, mmod, c);
|
||||
|
||||
if(m_regs->SMODE2.INT && m_interlace > 0)
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ protected:
|
|||
bool m_shadeboost;
|
||||
bool m_texture_shuffle;
|
||||
|
||||
virtual GSTexture* GetOutput(int i) = 0;
|
||||
virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
|
||||
|
||||
public:
|
||||
GSWnd* m_wnd;
|
||||
|
|
|
@ -157,7 +157,7 @@ void GSRendererCL::ResetDevice()
|
|||
}
|
||||
}
|
||||
|
||||
GSTexture* GSRendererCL::GetOutput(int i)
|
||||
GSTexture* GSRendererCL::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ protected:
|
|||
void Reset();
|
||||
void VSync(int field);
|
||||
void ResetDevice();
|
||||
GSTexture* GetOutput(int i);
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
|
||||
void Draw();
|
||||
void Sync(int reason);
|
||||
|
|
|
@ -338,7 +338,7 @@ void GSRendererCS::VSync(int field)
|
|||
//printf("%lld\n", m_perfmon.GetFrame());
|
||||
}
|
||||
|
||||
GSTexture* GSRendererCS::GetOutput(int i)
|
||||
GSTexture* GSRendererCS::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
// TODO: create a compute shader which unswizzles the frame from m_vm to the output texture
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ protected:
|
|||
bool CreateDevice(GSDevice* dev);
|
||||
void ResetDevice();
|
||||
void VSync(int field);
|
||||
GSTexture* GetOutput(int i);
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
void Draw();
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut);
|
||||
|
|
|
@ -58,7 +58,16 @@ void GSRendererHW::SetScaling()
|
|||
int fb_width = max({ (int)m_context->FRAME.FBW * 64, crtc_size.x , 512 });
|
||||
// GS doesn't have a specific register for the FrameBuffer height. so we get the height
|
||||
// from physical units of the display rectangle in case the game uses a heigher value of height.
|
||||
int fb_height = (fb_width < 1024) ? max(512, crtc_size.y) : 1024;
|
||||
//
|
||||
// Gregory: the framebuffer must have enough room to draw
|
||||
// * at least 2 frames such as FMV (see OI_BlitFMV)
|
||||
// * high resolution game such as snowblind engine game
|
||||
//
|
||||
// Autodetection isn't a good idea because it will create flickering
|
||||
// If memory consumption is an issue, there are 2 possibilities
|
||||
// * 1/ Avoid to create hundreds of RT
|
||||
// * 2/ Use sparse texture (requires recent HW)
|
||||
int fb_height = (fb_width < 1024) ? 1280 : 1024;
|
||||
|
||||
int upscaled_fb_w = fb_width * m_upscale_multiplier;
|
||||
int upscaled_fb_h = fb_height * m_upscale_multiplier;
|
||||
|
@ -151,7 +160,7 @@ void GSRendererHW::ResetDevice()
|
|||
GSRenderer::ResetDevice();
|
||||
}
|
||||
|
||||
GSTexture* GSRendererHW::GetOutput(int i)
|
||||
GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
||||
|
||||
|
@ -169,6 +178,13 @@ GSTexture* GSRendererHW::GetOutput(int i)
|
|||
{
|
||||
t = rt->m_texture;
|
||||
|
||||
int delta = TEX0.TBP0 - rt->m_TEX0.TBP0;
|
||||
if (delta > 0) {
|
||||
ASSERT(DISPFB.PSM == PSM_PSMCT32 || DISPFB.PSM == PSM_PSMCT24);
|
||||
y_offset = delta / DISPFB.FBW;
|
||||
GL_CACHE("Frame y offset %d pixels, unit %d", y_offset, i);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if(s_dump)
|
||||
{
|
||||
|
@ -197,7 +213,7 @@ void GSRendererHW::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GS
|
|||
// printf("[%d] InvalidateLocalMem %d,%d - %d,%d %05x (%d)\n", (int)m_perfmon.GetFrame(), r.left, r.top, r.right, r.bottom, (int)BITBLTBUF.SBP, (int)BITBLTBUF.SPSM);
|
||||
|
||||
if(clut) return; // FIXME
|
||||
|
||||
|
||||
m_tc->InvalidateLocalMem(m_mem.GetOffset(BITBLTBUF.SBP, BITBLTBUF.SBW, BITBLTBUF.SPSM), r);
|
||||
}
|
||||
|
||||
|
@ -383,7 +399,7 @@ void GSRendererHW::Draw()
|
|||
if(PRIM->TME)
|
||||
{
|
||||
/*
|
||||
|
||||
|
||||
// m_tc->LookupSource will mess with the palette, should not, but we do this after, until it is sorted out
|
||||
|
||||
if(GSLocalMemory::m_psm[context->TEX0.PSM].pal > 0)
|
||||
|
@ -484,9 +500,20 @@ void GSRendererHW::Draw()
|
|||
}
|
||||
#endif
|
||||
|
||||
// The rectangle of the draw
|
||||
GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
|
||||
if(m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt_tex, ds_tex, tex))
|
||||
{
|
||||
s_n += 1; // keep counter sync
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
GL_POP();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OI_BlitFMV(rt, tex, r)) {
|
||||
s_n += 1; // keep counter sync
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
GL_POP();
|
||||
return;
|
||||
}
|
||||
|
@ -565,8 +592,6 @@ void GSRendererHW::Draw()
|
|||
|
||||
//
|
||||
|
||||
GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
|
||||
// Help to detect rendering outside of the framebuffer
|
||||
#if _DEBUG
|
||||
if (m_upscale_multiplier * r.z > m_width) {
|
||||
|
@ -579,7 +604,8 @@ void GSRendererHW::Draw()
|
|||
|
||||
if(fm != 0xffffffff && rt)
|
||||
{
|
||||
rt->m_valid = rt->m_valid.runion(r);
|
||||
//rt->m_valid = rt->m_valid.runion(r);
|
||||
rt->UpdateValidity(r);
|
||||
|
||||
m_tc->InvalidateVideoMem(context->offset.fb, r, false);
|
||||
|
||||
|
@ -588,7 +614,8 @@ void GSRendererHW::Draw()
|
|||
|
||||
if(zm != 0xffffffff && ds)
|
||||
{
|
||||
ds->m_valid = ds->m_valid.runion(r);
|
||||
//ds->m_valid = ds->m_valid.runion(r);
|
||||
ds->UpdateValidity(r);
|
||||
|
||||
m_tc->InvalidateVideoMem(context->offset.zb, r, false);
|
||||
|
||||
|
@ -817,6 +844,71 @@ void GSRendererHW::OI_GsMemClear()
|
|||
}
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* tex, const GSVector4i& r_draw)
|
||||
{
|
||||
if (r_draw.w > 1024 && (m_vt.m_primclass == GS_SPRITE_CLASS) && (m_vertex.next == 2) && PRIM->TME && !PRIM->ABE) {
|
||||
GL_PUSH("OI_BlitFMV");
|
||||
|
||||
GL_INS("OI_BlitFMV");
|
||||
|
||||
// The draw is done past the RT at the location of the texture. To avoid various upscaling mess
|
||||
// We will blit the data from the top to the bottom of the texture manually.
|
||||
|
||||
// Expected memory representation
|
||||
// -----------------------------------------------------------------
|
||||
// RT (2 half frame)
|
||||
// -----------------------------------------------------------------
|
||||
// Top of Texture (full height frame)
|
||||
//
|
||||
// Bottom of Texture (half height frame, will be the copy of Top texture after the draw)
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// sRect is the top of texture
|
||||
int tw = (int)(1 << m_context->TEX0.TW);
|
||||
int th = (int)(1 << m_context->TEX0.TH);
|
||||
GSVector4 sRect;
|
||||
sRect.x = m_vt.m_min.t.x / tw;
|
||||
sRect.y = m_vt.m_min.t.y / th;
|
||||
sRect.z = m_vt.m_max.t.x / tw;
|
||||
sRect.w = m_vt.m_max.t.y / th;
|
||||
|
||||
// Compute the Bottom of texture rectangle
|
||||
ASSERT(m_context->TEX0.TBP0 > m_context->FRAME.Block());
|
||||
int offset = (m_context->TEX0.TBP0 - m_context->FRAME.Block()) / m_context->TEX0.TBW;
|
||||
GSVector4i r_texture(r_draw);
|
||||
r_texture.y -= offset;
|
||||
r_texture.w -= offset;
|
||||
|
||||
GSVector4 dRect(r_texture);
|
||||
|
||||
// Do the blit. With a Copy mess to avoid issue with limited API (dx)
|
||||
// m_dev->StretchRect(tex->m_texture, sRect, tex->m_texture, dRect);
|
||||
GSVector4i r_full(0, 0, tw, th);
|
||||
if (GSTexture* rt = m_dev->CreateRenderTarget(tw, th, false)) {
|
||||
m_dev->CopyRect(tex->m_texture, rt, r_full);
|
||||
|
||||
m_dev->StretchRect(tex->m_texture, sRect, rt, dRect);
|
||||
|
||||
m_dev->CopyRect(rt, tex->m_texture, r_full);
|
||||
|
||||
m_dev->Recycle(rt);
|
||||
}
|
||||
|
||||
// Copy back the texture into the GS mem. I don't know why but it will be
|
||||
// reuploaded again later
|
||||
m_tc->Read(tex, r_texture);
|
||||
|
||||
m_tc->InvalidateVideoMemSubTarget(_rt);
|
||||
|
||||
GL_POP();
|
||||
|
||||
return false; // skip current draw
|
||||
}
|
||||
|
||||
// Nothing to see keep going
|
||||
return true;
|
||||
}
|
||||
|
||||
// OI (others input?/implementation?) hacks replace current draw call
|
||||
|
||||
bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
|
@ -850,9 +942,9 @@ bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source
|
|||
{
|
||||
int x = (v->XYZ.X - ox) >> 4;
|
||||
int y = (v->XYZ.Y - oy) >> 4;
|
||||
|
||||
|
||||
if (x < 0 || x >= 448 || y < 0 || y >= (int)lines) return false; // le sigh
|
||||
|
||||
|
||||
video[(y << 8) + (y << 7) + (y << 6) + x] = v->RGBAQ.u32[0];
|
||||
}
|
||||
|
||||
|
@ -938,7 +1030,7 @@ bool GSRendererHW::OI_MetalSlug6(GSTexture* rt, GSTexture* ds, GSTextureCache::S
|
|||
}
|
||||
|
||||
m_vt.Update(m_vertex.buff, m_index.buff, m_index.tail, m_vt.m_primclass);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -979,7 +1071,7 @@ bool GSRendererHW::OI_SimpsonsGame(GSTexture* rt, GSTexture* ds, GSTextureCache:
|
|||
// 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...
|
||||
|
||||
|
||||
// TODO: tony hawk pro skater 4 same problem, the empty half is not visible though, painted over fully
|
||||
|
||||
m_dev->ClearDepth(ds, 0);
|
||||
|
|
|
@ -47,6 +47,7 @@ private:
|
|||
typedef bool (GSRendererHW::*CU_Ptr)();
|
||||
|
||||
// Require special argument
|
||||
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
|
||||
void OI_GsMemClear(); // always on
|
||||
|
||||
bool OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
@ -164,7 +165,7 @@ public:
|
|||
void Reset();
|
||||
void VSync(int field);
|
||||
void ResetDevice();
|
||||
GSTexture* GetOutput(int i);
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
|
||||
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
|
||||
void Draw();
|
||||
|
|
|
@ -36,7 +36,7 @@ protected:
|
|||
{
|
||||
}
|
||||
|
||||
GSTexture* GetOutput(int i)
|
||||
GSTexture* GetOutput(int i, int& y_offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ void GSRendererSW::ResetDevice()
|
|||
}
|
||||
}
|
||||
|
||||
GSTexture* GSRendererSW::GetOutput(int i)
|
||||
GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
Sync(1);
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ protected:
|
|||
void Reset();
|
||||
void VSync(int field);
|
||||
void ResetDevice();
|
||||
GSTexture* GetOutput(int i);
|
||||
GSTexture* GetOutput(int i, int& y_offset);
|
||||
|
||||
void Draw();
|
||||
void Queue(shared_ptr<GSRasterizerData>& item);
|
||||
|
|
|
@ -1417,7 +1417,7 @@ void GSState::FlushWrite()
|
|||
r.bottom = r.top + m_env.TRXREG.RRH;
|
||||
|
||||
InvalidateVideoMem(m_env.BITBLTBUF, r);
|
||||
|
||||
|
||||
//int y = m_tr.y;
|
||||
|
||||
GSLocalMemory::writeImage wi = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].wi;
|
||||
|
|
|
@ -424,6 +424,52 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
|||
|
||||
Target* dst = NULL;
|
||||
|
||||
#if 0
|
||||
// Dump the list of targets for debug
|
||||
for(auto t : m_dst[RenderTarget]) {
|
||||
GL_INS("TC: frame 0x%x -> 0x%x : %d (age %d)", t->m_TEX0.TBP0, t->m_end_block, t->m_texture->GetID(), t->m_age);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Let's try to find a perfect frame that contains valid data
|
||||
for(auto t : m_dst[RenderTarget]) {
|
||||
if(bp == t->m_TEX0.TBP0 && t->m_end_block > bp) {
|
||||
dst = t;
|
||||
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: %d (0x%x -> 0x%x)", w, h, dst->m_texture->GetID(), bp, t->m_end_block);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd try ! Try to find a frame that include the bp
|
||||
if (dst == NULL) {
|
||||
for(auto t : m_dst[RenderTarget]) {
|
||||
if (t->m_TEX0.TBP0 < bp && bp < t->m_end_block) {
|
||||
dst = t;
|
||||
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, inclusive hit: %d (0x%x, took 0x%x -> 0x%x)", w, h, t->m_texture->GetID(), bp, t->m_TEX0.TBP0, t->m_end_block);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3rd try ! Try to find a frame that doesn't contain valid data (honestly I'm not sure we need to do it)
|
||||
if (dst == NULL) {
|
||||
for(auto t : m_dst[RenderTarget]) {
|
||||
if(bp == t->m_TEX0.TBP0) {
|
||||
dst = t;
|
||||
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, empty hit: %d (0x%x -> 0x%x)", w, h, dst->m_texture->GetID(), bp, t->m_end_block);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
for(list<Target*>::iterator i = m_dst[RenderTarget].begin(); i != m_dst[RenderTarget].end(); i++)
|
||||
{
|
||||
Target* t = *i;
|
||||
|
@ -432,7 +478,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
|||
{
|
||||
dst = t;
|
||||
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: %d (0x%x)", w, h, dst->m_texture->GetID(), bp);
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: %d (0x%x -> 0x%x)", w, h, dst->m_texture->GetID(), bp, t->m_end_block);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -442,11 +488,12 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
|
|||
|
||||
if(t->m_TEX0.TBP0 <= bp && bp < t->m_TEX0.TBP0 + 0xe00UL && (!dst || t->m_TEX0.TBP0 >= dst->m_TEX0.TBP0))
|
||||
{
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, close hit: %d (0x%x, took 0x%x)", w, h, t->m_texture->GetID(), bp, t->m_TEX0.TBP0);
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, close hit: %d (0x%x, took 0x%x -> 0x%x)", w, h, t->m_texture->GetID(), bp, t->m_TEX0.TBP0, t->m_end_block);
|
||||
dst = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(dst == NULL)
|
||||
{
|
||||
|
@ -670,48 +717,69 @@ void GSTextureCache::InvalidateVideoMem(GSOffset* off, const GSVector4i& rect, b
|
|||
}
|
||||
|
||||
// GH: Try to detect texture write that will overlap with a target buffer
|
||||
if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && bp < t->m_TEX0.TBP0)
|
||||
{
|
||||
uint32 rowsize = bw * 8192;
|
||||
uint32 offset = (uint32)((t->m_TEX0.TBP0 - bp) * 256);
|
||||
|
||||
if(rowsize > 0 && offset % rowsize == 0)
|
||||
if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM)) {
|
||||
if (bp < t->m_TEX0.TBP0)
|
||||
{
|
||||
uint32 rowsize = bw * 8192;
|
||||
uint32 offset = (uint32)((t->m_TEX0.TBP0 - bp) * 256);
|
||||
|
||||
if(rowsize > 0 && offset % rowsize == 0)
|
||||
{
|
||||
int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
|
||||
|
||||
if(r.bottom > y)
|
||||
{
|
||||
GL_CACHE("TC: Dirty After Target(%s) %d (0x%x)", to_string(type),
|
||||
t->m_texture ? t->m_texture->GetID() : 0,
|
||||
t->m_TEX0.TBP0);
|
||||
// TODO: do not add this rect above too
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top - y, r.right, r.bottom - y), psm));
|
||||
t->m_TEX0.TBW = bw;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this code "fixes" black FMV issue with rule of rose.
|
||||
// Code is completely hardcoded so maybe not the best solution. Besides I don't
|
||||
// know the full impact of it.
|
||||
// Let's keep this code for the future
|
||||
#if 0
|
||||
if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && (t->m_TEX0.TBP0 + 0x200 == bp))
|
||||
{
|
||||
GL_CACHE("TC: Dirty in the middle of Target(%s) %d (0x%x)", to_string(type),
|
||||
t->m_texture ? t->m_texture->GetID() : 0,
|
||||
t->m_TEX0.TBP0);
|
||||
|
||||
uint32 rowsize = bw * 8192u;
|
||||
uint32 offset = 0x200 * 256u;
|
||||
int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
|
||||
|
||||
if(r.bottom > y)
|
||||
{
|
||||
GL_CACHE("TC: Dirty After Target(%s) %d (0x%x)", to_string(type),
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top + y, r.right, r.bottom + y), psm));
|
||||
t->m_TEX0.TBW = bw;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
// Greg: I'm not sure the 'bw' equality is required but it won't hurt too much
|
||||
if (t->m_TEX0.TBW == bw && t->Inside(bp, psm, rect)) {
|
||||
uint32 rowsize = bw * 8192u;
|
||||
uint32 offset = (uint32)((bp - t->m_TEX0.TBP0) * 256);
|
||||
|
||||
if(rowsize > 0 && offset % rowsize == 0) {
|
||||
GL_CACHE("TC: Dirty in the middle of Target(%s) %d (0x%x)", to_string(type),
|
||||
t->m_texture ? t->m_texture->GetID() : 0,
|
||||
t->m_TEX0.TBP0);
|
||||
// TODO: do not add this rect above too
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top - y, r.right, r.bottom - y), psm));
|
||||
|
||||
int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
|
||||
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top + y, r.right, r.bottom + y), psm));
|
||||
t->m_TEX0.TBW = bw;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this code "fixes" black FMV issue with rule of rose.
|
||||
// Code is completely hardcoded so maybe not the best solution. Besides I don't
|
||||
// know the full impact of it.
|
||||
// Let's keep this code for the future
|
||||
#if 0
|
||||
if(GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && (t->m_TEX0.TBP0 + 0x200 == bp))
|
||||
{
|
||||
GL_CACHE("TC: Dirty in the middle of Target(%s) %d (0x%x)", to_string(type),
|
||||
t->m_texture ? t->m_texture->GetID() : 0,
|
||||
t->m_TEX0.TBP0);
|
||||
|
||||
uint32 rowsize = bw * 8192u;
|
||||
uint32 offset = 0x200 * 256u;
|
||||
int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
|
||||
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top + y, r.right, r.bottom + y), psm));
|
||||
t->m_TEX0.TBW = bw;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -846,6 +914,30 @@ void GSTextureCache::InvalidateLocalMem(GSOffset* off, const GSVector4i& r)
|
|||
// TODO: ds
|
||||
}
|
||||
|
||||
// Hack: remove Target that are strictly included in current rt. Typically uses for FMV
|
||||
// For example, game is rendered at 0x800->0x1000, fmv will be uploaded to 0x0->0x2800
|
||||
// FIXME In theory, we ought to report the data from the sub rt to the main rt. But let's
|
||||
// postpone it for later.
|
||||
void GSTextureCache::InvalidateVideoMemSubTarget(GSTextureCache::Target* rt)
|
||||
{
|
||||
if (!rt)
|
||||
return;
|
||||
|
||||
for(list<Target*>::iterator i = m_dst[RenderTarget].begin(); i != m_dst[RenderTarget].end(); ) {
|
||||
list<Target*>::iterator j = i++;
|
||||
Target* t = *j;
|
||||
|
||||
if ((t->m_TEX0.TBP0 > rt->m_TEX0.TBP0) && (t->m_end_block < rt->m_end_block) && (t->m_TEX0.TBW == rt->m_TEX0.TBW)
|
||||
&& (t->m_TEX0.TBP0 < t->m_end_block)) {
|
||||
GL_INS("InvalidateVideoMemSubTarget: rt 0x%x -> 0x%x, sub rt 0x%x -> 0x%x",
|
||||
rt->m_TEX0.TBP0, rt->m_end_block, t->m_TEX0.TBP0, t->m_end_block);
|
||||
|
||||
m_dst[RenderTarget].erase(j);
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSTextureCache::IncAge()
|
||||
{
|
||||
int maxage = m_src.m_used ? 3 : 30;
|
||||
|
@ -1616,13 +1708,13 @@ void GSTextureCache::Target::Update()
|
|||
// Copy the new GS memory content into the destination texture.
|
||||
if(m_type == RenderTarget)
|
||||
{
|
||||
GL_INS("ERROR: Update RenderTarget");
|
||||
GL_INS("ERROR: Update RenderTarget 0x%x", m_TEX0.TBP0);
|
||||
|
||||
m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy());
|
||||
}
|
||||
else if(m_type == DepthStencil)
|
||||
{
|
||||
GL_INS("ERROR: Update DepthStencil");
|
||||
GL_INS("ERROR: Update DepthStencil 0x%x", m_TEX0.TBP0);
|
||||
|
||||
// FIXME linear or not?
|
||||
m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy(), ShaderConvert_RGBA8_TO_FLOAT32);
|
||||
|
@ -1631,6 +1723,29 @@ void GSTextureCache::Target::Update()
|
|||
m_renderer->m_dev->Recycle(t);
|
||||
}
|
||||
|
||||
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
|
||||
{
|
||||
m_valid = m_valid.runion(rect);
|
||||
|
||||
uint32 nb_block = m_TEX0.TBW * m_valid.height();
|
||||
if (m_TEX0.PSM == PSM_PSMCT16)
|
||||
nb_block >>= 1;
|
||||
|
||||
m_end_block = m_TEX0.TBP0 + nb_block;
|
||||
//fprintf(stderr, "S: 0x%x E:0x%x\n", m_TEX0.TBP0, m_end_block);
|
||||
}
|
||||
|
||||
bool GSTextureCache::Target::Inside(uint32 bp, uint32 psm, const GSVector4i& rect)
|
||||
{
|
||||
uint32 nb_block = rect.height() * rect.width();
|
||||
if (m_TEX0.PSM == PSM_PSMCT16)
|
||||
nb_block >>= 7;
|
||||
else
|
||||
nb_block >>= 6;
|
||||
|
||||
return bp > m_TEX0.TBP0 && (bp + nb_block) < m_end_block;
|
||||
}
|
||||
|
||||
// GSTextureCache::SourceMap
|
||||
|
||||
void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, const GSOffset* off)
|
||||
|
|
|
@ -83,10 +83,14 @@ public:
|
|||
GSVector4i m_valid;
|
||||
bool m_depth_supported;
|
||||
bool m_dirty_alpha;
|
||||
uint32 m_end_block; // Hint of the target area
|
||||
|
||||
public:
|
||||
Target(GSRenderer* r, const GIFRegTEX0& TEX0, uint8* temp, bool depth_supported);
|
||||
|
||||
void UpdateValidity(const GSVector4i& rect);
|
||||
bool Inside(uint32 bp, uint32 psm, const GSVector4i& rect);
|
||||
|
||||
virtual void Update();
|
||||
};
|
||||
|
||||
|
@ -125,18 +129,14 @@ protected:
|
|||
|
||||
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
|
||||
// TODO: virtual void Write(Target* t, const GSVector4i& r) = 0;
|
||||
#ifndef DISABLE_HW_TEXTURE_CACHE
|
||||
virtual void Read(Target* t, const GSVector4i& r) = 0;
|
||||
#endif
|
||||
|
||||
virtual bool CanConvertDepth() { return m_can_convert_depth; }
|
||||
|
||||
public:
|
||||
GSTextureCache(GSRenderer* r);
|
||||
virtual ~GSTextureCache();
|
||||
#ifdef DISABLE_HW_TEXTURE_CACHE
|
||||
virtual void Read(Target* t, const GSVector4i& r) = 0;
|
||||
#endif
|
||||
virtual void Read(Source* t, const GSVector4i& r) = 0;
|
||||
void RemoveAll();
|
||||
void RemovePartial();
|
||||
|
||||
|
@ -145,6 +145,7 @@ public:
|
|||
Target* LookupTarget(const GIFRegTEX0& TEX0, int w, int h, int real_h);
|
||||
|
||||
void InvalidateVideoMemType(int type, uint32 bp);
|
||||
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
|
||||
void InvalidateVideoMem(GSOffset* off, const GSVector4i& r, bool target = true);
|
||||
void InvalidateLocalMem(GSOffset* off, const GSVector4i& r);
|
||||
|
||||
|
|
|
@ -97,3 +97,27 @@ void GSTextureCache11::Read(Target* t, const GSVector4i& r)
|
|||
}
|
||||
}
|
||||
|
||||
void GSTextureCache11::Read(Source* t, const GSVector4i& r)
|
||||
{
|
||||
// FIXME: copy was copyied from openGL. It is unlikely to work.
|
||||
|
||||
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
||||
|
||||
if (GSTexture* offscreen = m_renderer->m_dev->CreateOffscreen(r.width(), r.height())) {
|
||||
m_renderer->m_dev->CopyRect(t->m_texture, offscreen, r);
|
||||
|
||||
GSTexture::GSMap m;
|
||||
GSVector4i r_offscreen(0, 0, r.width(), r.height());
|
||||
|
||||
if (offscreen->Map(m, &r_offscreen)) {
|
||||
GSOffset* off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||
|
||||
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
|
||||
|
||||
offscreen->Unmap();
|
||||
}
|
||||
|
||||
// FIXME invalidate data
|
||||
m_renderer->m_dev->Recycle(offscreen);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ protected:
|
|||
int Get8bitFormat() {return DXGI_FORMAT_A8_UNORM;}
|
||||
|
||||
void Read(Target* t, const GSVector4i& r);
|
||||
void Read(Source* t, const GSVector4i& r);
|
||||
|
||||
virtual bool CanConvertDepth() { return false; }
|
||||
|
||||
|
|
|
@ -95,3 +95,27 @@ void GSTextureCache9::Read(Target* t, const GSVector4i& r)
|
|||
}
|
||||
}
|
||||
|
||||
void GSTextureCache9::Read(Source* t, const GSVector4i& r)
|
||||
{
|
||||
// FIXME: copy was copyied from openGL. It is unlikely to work.
|
||||
|
||||
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
||||
|
||||
if (GSTexture* offscreen = m_renderer->m_dev->CreateOffscreen(r.width(), r.height())) {
|
||||
m_renderer->m_dev->CopyRect(t->m_texture, offscreen, r);
|
||||
|
||||
GSTexture::GSMap m;
|
||||
GSVector4i r_offscreen(0, 0, r.width(), r.height());
|
||||
|
||||
if (offscreen->Map(m, &r_offscreen)) {
|
||||
GSOffset* off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||
|
||||
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
|
||||
|
||||
offscreen->Unmap();
|
||||
}
|
||||
|
||||
// FIXME invalidate data
|
||||
m_renderer->m_dev->Recycle(offscreen);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ protected:
|
|||
int Get8bitFormat() {return D3DFMT_A8;}
|
||||
|
||||
void Read(Target* t, const GSVector4i& r);
|
||||
void Read(Source* t, const GSVector4i& r);
|
||||
|
||||
virtual bool CanConvertDepth() { return false; }
|
||||
|
||||
|
|
|
@ -129,3 +129,28 @@ void GSTextureCacheOGL::Read(Target* t, const GSVector4i& r)
|
|||
GL_POP();
|
||||
}
|
||||
|
||||
void GSTextureCacheOGL::Read(Source* t, const GSVector4i& r)
|
||||
{
|
||||
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
||||
|
||||
// FIXME Create a get function to avoid the useless copy
|
||||
// Note: With openGL 4.5 you can use glGetTextureSubImage
|
||||
|
||||
if (GSTexture* offscreen = m_renderer->m_dev->CreateOffscreen(r.width(), r.height())) {
|
||||
m_renderer->m_dev->CopyRect(t->m_texture, offscreen, r);
|
||||
|
||||
GSTexture::GSMap m;
|
||||
GSVector4i r_offscreen(0, 0, r.width(), r.height());
|
||||
|
||||
if (offscreen->Map(m, &r_offscreen)) {
|
||||
GSOffset* off = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||
|
||||
m_renderer->m_mem.WritePixel32(m.bits, m.pitch, off, r);
|
||||
|
||||
offscreen->Unmap();
|
||||
}
|
||||
|
||||
// FIXME invalidate data
|
||||
m_renderer->m_dev->Recycle(offscreen);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ protected:
|
|||
int Get8bitFormat() { return GL_R8;}
|
||||
|
||||
void Read(Target* t, const GSVector4i& r);
|
||||
void Read(Source* t, const GSVector4i& r);
|
||||
|
||||
public:
|
||||
GSTextureCacheOGL(GSRenderer* r);
|
||||
|
|
Loading…
Reference in New Issue