Merge pull request from PCSX2/gsdx-black-fmv

Gsdx black fmv
This commit is contained in:
Gregory Hainaut 2016-04-21 09:25:36 +02:00
commit 59ef668fd2
20 changed files with 357 additions and 68 deletions

View File

@ -192,19 +192,22 @@ bool GSRenderer::Merge(int field)
GSVector2i ds(0, 0); GSVector2i ds(0, 0);
GSTexture* tex[2] = {NULL, NULL}; GSTexture* tex[2] = {NULL, NULL};
int y_offset[2] = {0, 0};
if(samesrc && fr[0].bottom == fr[1].bottom) if(samesrc && fr[0].bottom == fr[1].bottom)
{ {
tex[0] = GetOutput(0); tex[0] = GetOutput(0, y_offset[0]);
tex[1] = tex[0]; // saves one texture fetch tex[1] = tex[0]; // saves one texture fetch
y_offset[1] = y_offset[0];
} }
else else
{ {
if(en[0]) tex[0] = GetOutput(0); if(en[0]) tex[0] = GetOutput(0, y_offset[0]);
if(en[1]) tex[1] = GetOutput(1); if(en[1]) tex[1] = GetOutput(1, y_offset[1]);
} }
GSVector4 src[2]; GSVector4 src[2];
GSVector4 src_hw[2];
GSVector4 dst[2]; GSVector4 dst[2];
for(int i = 0; i < 2; i++) for(int i = 0; i < 2; i++)
@ -224,6 +227,7 @@ bool GSRenderer::Merge(int field)
GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy(); GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy();
src[i] = GSVector4(r) * scale / GSVector4(tex[i]->GetSize()).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); 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; 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) if(m_regs->SMODE2.INT && m_interlace > 0)
{ {

View File

@ -50,7 +50,7 @@ protected:
bool m_shadeboost; bool m_shadeboost;
bool m_texture_shuffle; bool m_texture_shuffle;
virtual GSTexture* GetOutput(int i) = 0; virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
public: public:
GSWnd* m_wnd; GSWnd* m_wnd;

View File

@ -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; const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;

View File

@ -251,7 +251,7 @@ protected:
void Reset(); void Reset();
void VSync(int field); void VSync(int field);
void ResetDevice(); void ResetDevice();
GSTexture* GetOutput(int i); GSTexture* GetOutput(int i, int& y_offset);
void Draw(); void Draw();
void Sync(int reason); void Sync(int reason);

View File

@ -338,7 +338,7 @@ void GSRendererCS::VSync(int field)
//printf("%lld\n", m_perfmon.GetFrame()); //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 // TODO: create a compute shader which unswizzles the frame from m_vm to the output texture

View File

@ -134,7 +134,7 @@ protected:
bool CreateDevice(GSDevice* dev); bool CreateDevice(GSDevice* dev);
void ResetDevice(); void ResetDevice();
void VSync(int field); void VSync(int field);
GSTexture* GetOutput(int i); GSTexture* GetOutput(int i, int& y_offset);
void Draw(); void Draw();
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r); void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut); void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut);

View File

@ -58,7 +58,16 @@ void GSRendererHW::SetScaling()
int fb_width = max({ (int)m_context->FRAME.FBW * 64, crtc_size.x , 512 }); 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 // 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. // 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_w = fb_width * m_upscale_multiplier;
int upscaled_fb_h = fb_height * m_upscale_multiplier; int upscaled_fb_h = fb_height * m_upscale_multiplier;
@ -151,7 +160,7 @@ void GSRendererHW::ResetDevice()
GSRenderer::ResetDevice(); GSRenderer::ResetDevice();
} }
GSTexture* GSRendererHW::GetOutput(int i) GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
{ {
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB; const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
@ -169,6 +178,13 @@ GSTexture* GSRendererHW::GetOutput(int i)
{ {
t = rt->m_texture; 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 #ifndef NDEBUG
if(s_dump) if(s_dump)
{ {
@ -484,9 +500,20 @@ void GSRendererHW::Draw()
} }
#endif #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)) if(m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt_tex, ds_tex, tex))
{ {
s_n += 1; // keep counter sync 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(); GL_POP();
return; 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 // Help to detect rendering outside of the framebuffer
#if _DEBUG #if _DEBUG
if (m_upscale_multiplier * r.z > m_width) { if (m_upscale_multiplier * r.z > m_width) {
@ -579,7 +604,8 @@ void GSRendererHW::Draw()
if(fm != 0xffffffff && rt) 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); m_tc->InvalidateVideoMem(context->offset.fb, r, false);
@ -588,7 +614,8 @@ void GSRendererHW::Draw()
if(zm != 0xffffffff && ds) 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); 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 // OI (others input?/implementation?) hacks replace current draw call
bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t) bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)

View File

@ -47,6 +47,7 @@ private:
typedef bool (GSRendererHW::*CU_Ptr)(); typedef bool (GSRendererHW::*CU_Ptr)();
// Require special argument // Require special argument
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
void OI_GsMemClear(); // always on void OI_GsMemClear(); // always on
bool OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); bool OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
@ -164,7 +165,7 @@ public:
void Reset(); void Reset();
void VSync(int field); void VSync(int field);
void ResetDevice(); void ResetDevice();
GSTexture* GetOutput(int i); GSTexture* GetOutput(int i, int& y_offset);
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r); void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r);
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false); void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false);
void Draw(); void Draw();

View File

@ -36,7 +36,7 @@ protected:
{ {
} }
GSTexture* GetOutput(int i) GSTexture* GetOutput(int i, int& y_offset)
{ {
return NULL; return NULL;
} }

View File

@ -231,7 +231,7 @@ void GSRendererSW::ResetDevice()
} }
} }
GSTexture* GSRendererSW::GetOutput(int i) GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
{ {
Sync(1); Sync(1);

View File

@ -78,7 +78,7 @@ protected:
void Reset(); void Reset();
void VSync(int field); void VSync(int field);
void ResetDevice(); void ResetDevice();
GSTexture* GetOutput(int i); GSTexture* GetOutput(int i, int& y_offset);
void Draw(); void Draw();
void Queue(shared_ptr<GSRasterizerData>& item); void Queue(shared_ptr<GSRasterizerData>& item);

View File

@ -424,6 +424,52 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
Target* dst = NULL; 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++) for(list<Target*>::iterator i = m_dst[RenderTarget].begin(); i != m_dst[RenderTarget].end(); i++)
{ {
Target* t = *i; Target* t = *i;
@ -432,7 +478,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int
{ {
dst = t; 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; 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)) 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; dst = t;
} }
} }
} }
#endif
if(dst == NULL) 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 // 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) 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)
{ {
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; int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
if(r.bottom > y) t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top + y, r.right, r.bottom + y), psm));
{ t->m_TEX0.TBW = bw;
GL_CACHE("TC: Dirty After Target(%s) %d (0x%x)", to_string(type), 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_texture ? t->m_texture->GetID() : 0,
t->m_TEX0.TBP0); 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; t->m_TEX0.TBW = bw;
continue; 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 #endif
}
} }
} }
} }
@ -846,6 +914,30 @@ void GSTextureCache::InvalidateLocalMem(GSOffset* off, const GSVector4i& r)
// TODO: ds // 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() void GSTextureCache::IncAge()
{ {
int maxage = m_src.m_used ? 3 : 30; 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. // Copy the new GS memory content into the destination texture.
if(m_type == RenderTarget) 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()); m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy());
} }
else if(m_type == DepthStencil) else if(m_type == DepthStencil)
{ {
GL_INS("ERROR: Update DepthStencil"); GL_INS("ERROR: Update DepthStencil 0x%x", m_TEX0.TBP0);
// FIXME linear or not? // FIXME linear or not?
m_renderer->m_dev->StretchRect(t, m_texture, GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy(), ShaderConvert_RGBA8_TO_FLOAT32); 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); 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 // GSTextureCache::SourceMap
void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, const GSOffset* off) void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, const GSOffset* off)

View File

@ -83,10 +83,14 @@ public:
GSVector4i m_valid; GSVector4i m_valid;
bool m_depth_supported; bool m_depth_supported;
bool m_dirty_alpha; bool m_dirty_alpha;
uint32 m_end_block; // Hint of the target area
public: public:
Target(GSRenderer* r, const GIFRegTEX0& TEX0, uint8* temp, bool depth_supported); 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(); virtual void Update();
}; };
@ -125,18 +129,14 @@ protected:
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0; // TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
// TODO: virtual void Write(Target* t, 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; } virtual bool CanConvertDepth() { return m_can_convert_depth; }
public: public:
GSTextureCache(GSRenderer* r); GSTextureCache(GSRenderer* r);
virtual ~GSTextureCache(); virtual ~GSTextureCache();
#ifdef DISABLE_HW_TEXTURE_CACHE
virtual void Read(Target* t, const GSVector4i& r) = 0; virtual void Read(Target* t, const GSVector4i& r) = 0;
#endif virtual void Read(Source* t, const GSVector4i& r) = 0;
void RemoveAll(); void RemoveAll();
void RemovePartial(); void RemovePartial();
@ -145,6 +145,7 @@ public:
Target* LookupTarget(const GIFRegTEX0& TEX0, int w, int h, int real_h); Target* LookupTarget(const GIFRegTEX0& TEX0, int w, int h, int real_h);
void InvalidateVideoMemType(int type, uint32 bp); void InvalidateVideoMemType(int type, uint32 bp);
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(GSOffset* off, const GSVector4i& r, bool target = true); void InvalidateVideoMem(GSOffset* off, const GSVector4i& r, bool target = true);
void InvalidateLocalMem(GSOffset* off, const GSVector4i& r); void InvalidateLocalMem(GSOffset* off, const GSVector4i& r);

View File

@ -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);
}
}

View File

@ -30,6 +30,7 @@ protected:
int Get8bitFormat() {return DXGI_FORMAT_A8_UNORM;} int Get8bitFormat() {return DXGI_FORMAT_A8_UNORM;}
void Read(Target* t, const GSVector4i& r); void Read(Target* t, const GSVector4i& r);
void Read(Source* t, const GSVector4i& r);
virtual bool CanConvertDepth() { return false; } virtual bool CanConvertDepth() { return false; }

View File

@ -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);
}
}

View File

@ -30,6 +30,7 @@ protected:
int Get8bitFormat() {return D3DFMT_A8;} int Get8bitFormat() {return D3DFMT_A8;}
void Read(Target* t, const GSVector4i& r); void Read(Target* t, const GSVector4i& r);
void Read(Source* t, const GSVector4i& r);
virtual bool CanConvertDepth() { return false; } virtual bool CanConvertDepth() { return false; }

View File

@ -129,3 +129,28 @@ void GSTextureCacheOGL::Read(Target* t, const GSVector4i& r)
GL_POP(); 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);
}
}

View File

@ -31,6 +31,7 @@ protected:
int Get8bitFormat() { return GL_R8;} int Get8bitFormat() { return GL_R8;}
void Read(Target* t, const GSVector4i& r); void Read(Target* t, const GSVector4i& r);
void Read(Source* t, const GSVector4i& r);
public: public:
GSTextureCacheOGL(GSRenderer* r); GSTextureCacheOGL(GSRenderer* r);