mirror of https://github.com/PCSX2/pcsx2.git
451 lines
12 KiB
C++
451 lines
12 KiB
C++
/* ZZ Open GL graphics plugin
|
|
* Copyright (c)2009 zeydlitz@gmail.com
|
|
* Based on Zerofrog's ZeroGS KOSMOS (c)2005-2006
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
// Zerogs:VB implementation.
|
|
// VB stands for Visual Buffer, as I think
|
|
|
|
//------------------- Includes
|
|
#include "zerogs.h"
|
|
#include "targets.h"
|
|
#include "GS.h"
|
|
#include "Mem.h"
|
|
|
|
using namespace ZeroGS;
|
|
// ----------------- Defines
|
|
#define MINMAX_SHIFT 3
|
|
|
|
//------------------ Constants
|
|
|
|
// ----------------- Global Variables
|
|
int maxmin = 608;
|
|
// ----------------- Code
|
|
|
|
// Constructor. Set width and height to 1
|
|
ZeroGS::VB::VB()
|
|
{
|
|
memset(this, 0, sizeof(ZeroGS::VB));
|
|
tex0.tw = 1;
|
|
tex0.th = 1;
|
|
}
|
|
|
|
// Destructor
|
|
ZeroGS::VB::~VB()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void ZeroGS::VB::Destroy()
|
|
{
|
|
_aligned_free(pBufferData); pBufferData = NULL; nNumVertices = 0;
|
|
|
|
prndr = NULL;
|
|
pdepth = NULL;
|
|
}
|
|
|
|
int ConstraintReson;
|
|
|
|
// Return number of 64-pixels block, that guaranted could be hold in memory
|
|
// from gsfb.fbp and tbp (textrure pase), zbuf.zbp (Z-buffer), frame.fbp
|
|
// (previous frame).
|
|
inline int
|
|
ZeroGS::VB::FindMinimalMemoryConstrain(int tbp, int maxpos) {
|
|
int MinConstraint = maxpos;
|
|
|
|
// make sure texture is far away from tbp
|
|
{
|
|
int Constraint = tbp - gsfb.fbp;
|
|
if ((0 < Constraint) && (Constraint < MinConstraint)) {
|
|
MinConstraint = Constraint;
|
|
ConstraintReson = 1;
|
|
}
|
|
}
|
|
|
|
// offroad uses 0x80 fbp which messes up targets
|
|
// special case when double buffering (hamsterball)
|
|
// Suikoden 3 require e00 have this issue too. P3 - 0x1000.
|
|
if (prndr != NULL) {
|
|
int Constraint = frame.fbp - gsfb.fbp;
|
|
if ((0x0 < Constraint) && (Constraint < MinConstraint)) {
|
|
MinConstraint = Constraint;
|
|
ConstraintReson = 2;
|
|
}
|
|
}
|
|
|
|
// old caching method
|
|
// zmsk necessary for KH movie
|
|
if (!zbuf.zmsk){
|
|
int Constraint = zbuf.zbp - gsfb.fbp;
|
|
if ((0 < Constraint) && (Constraint < MinConstraint)) {
|
|
MinConstraint = Constraint;
|
|
ConstraintReson = 3;
|
|
}
|
|
}
|
|
|
|
// In 16Bit mode in one Word freame stored 2 pixels
|
|
if (PSMT_ISHALF(gsfb.psm))
|
|
MinConstraint *= 2;
|
|
|
|
return MinConstraint ;
|
|
}
|
|
|
|
// Return number of 64 pizel words that could be placed in Z-Buffer
|
|
// If no Z-buffer present return old constraint
|
|
inline int
|
|
ZeroGS::VB::FindZbufferMemoryConstrain(int tbp, int maxpos) {
|
|
int MinConstraint = maxpos;
|
|
|
|
// Check tbp / zbuffer constraint
|
|
if (!zbuf.zmsk) {
|
|
int Constraint = (tbp - zbuf.zbp) * (PSMT_ISHALF(zbuf.psm) ? 2 : 1 );
|
|
if ((0 < Constraint) && (Constraint < MinConstraint)) {
|
|
MinConstraint = Constraint;
|
|
ConstraintReson = 4;
|
|
}
|
|
}
|
|
|
|
return MinConstraint;
|
|
}
|
|
|
|
// Return heights limiter form scissor. .
|
|
inline int
|
|
GetScissorY(int y) {
|
|
int fbh = (y >> MINMAX_SHIFT) + 1;
|
|
if ( fbh > 2 && ( fbh & 1 ) ) fbh -= 1;
|
|
return fbh;
|
|
}
|
|
|
|
//There is several reason to limit a height of frame: maximum buffer size, calculated size
|
|
//from fbw and fbh and scissoring.
|
|
inline int
|
|
ZeroGS::VB::FindMinimalHeightConstrain(int maxpos) {
|
|
int MinConstraint = maxpos;
|
|
|
|
if (maxmin < MinConstraint) {
|
|
MinConstraint = maxmin;
|
|
ConstraintReson = 5;
|
|
}
|
|
|
|
if (gsfb.fbh < MinConstraint) {
|
|
MinConstraint = gsfb.fbh;
|
|
ConstraintReson = 6;
|
|
}
|
|
|
|
int ScissorConstraint = GetScissorY(scissor.y1) ;
|
|
if (ScissorConstraint < MinConstraint) {
|
|
MinConstraint = ScissorConstraint;
|
|
ConstraintReson = 7;
|
|
}
|
|
|
|
return MinConstraint;
|
|
}
|
|
|
|
// 32 bit frames have additional constraints to frame
|
|
// maxpos was maximum length of frame at normal constraints
|
|
inline void
|
|
ZeroGS::VB::CheckFrame32bitRes(int maxpos)
|
|
{
|
|
int fbh = frame.fbh;
|
|
|
|
if ( frame.fbh >= 512 ) {
|
|
// neopets hack
|
|
maxmin = min(maxmin, frame.fbh);
|
|
frame.fbh = maxmin;
|
|
ConstraintReson = 8;
|
|
}
|
|
|
|
// ffxii hack to stop resolving
|
|
if( frame.fbp >= 0x3000 && fbh >= 0x1a0 ) {
|
|
int endfbp = frame.fbp + frame.fbw * fbh / (PSMT_ISHALF(gsfb.psm) ? 128 : 64);
|
|
|
|
// see if there is a previous render target in the way, reduce
|
|
for (CRenderTargetMngr::MAPTARGETS::iterator itnew = s_RTs.mapTargets.begin(); itnew != s_RTs.mapTargets.end(); ++itnew) {
|
|
if ( itnew->second->fbp > frame.fbp && endfbp > itnew->second->fbp ) {
|
|
endfbp = itnew->second->fbp;
|
|
}
|
|
}
|
|
|
|
frame.fbh = (endfbp - frame.fbp) * ( PSMT_ISHALF(gsfb.psm) ? 128: 64 ) / frame.fbw;
|
|
if (frame.fbh < fbh)
|
|
ConstraintReson = 9;
|
|
}
|
|
|
|
}
|
|
|
|
// This is the main code of frame resising.
|
|
// It's check several reasons for resize and resize if it needs.
|
|
// 4Mb memory in 64 bit (4 bytes) words.
|
|
// |------------------------|---------------------|----------|----------|---------------------|
|
|
// 0 gsfb.fbp zbuff.zpb tbp frame.fbp 2^20/64
|
|
inline int
|
|
ZeroGS::VB::CheckFrameAddConstraints(int tbp)
|
|
{
|
|
if ( gsfb.fbw <= 0 ) {
|
|
ERROR_LOG_SPAM("render target null, no constraints. Ignoring\n");
|
|
return -1;
|
|
}
|
|
|
|
// Memory region after fbp
|
|
int maxmemorypos = 0x4000 - gsfb.fbp;
|
|
ConstraintReson = 0;
|
|
|
|
maxmemorypos = FindMinimalMemoryConstrain(tbp, maxmemorypos);
|
|
maxmemorypos = FindZbufferMemoryConstrain(tbp, maxmemorypos);
|
|
|
|
int maxpos = 64 * maxmemorypos ;
|
|
maxpos /= gsfb.fbw;
|
|
//? atelier iris crashes without it
|
|
if( maxpos > 256 )
|
|
maxpos &= ~0x1f;
|
|
|
|
//int noscissorpos = maxpos;
|
|
//int ConstrainR1 = ConstraintReson;
|
|
maxpos = FindMinimalHeightConstrain(maxpos);
|
|
|
|
frame = gsfb;
|
|
frame.fbh = maxpos;
|
|
|
|
if( !PSMT_ISHALF(frame.psm) || !(g_GameSettings&GAME_FULL16BITRES) )
|
|
CheckFrame32bitRes( maxpos ) ;
|
|
|
|
#ifdef DEVBUILD
|
|
if (frame.fbh == 0xe2)
|
|
ERROR_LOG ("Const: %x %x %d| %x %d %x %x\n", frame.fbh, frame.fbw, ConstraintReson, noscissorpos, ConstrainR1, tbp, frame.fbp);
|
|
#endif
|
|
|
|
// Fixme: Reserved psm for framebuffers
|
|
// gsfb.psm &= 0xf; // shadow tower
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
// Check if after resising new depth target is need to be used.
|
|
// it return 2 if new deapth target used. bool Chose is used to proprely check
|
|
// renderer target status
|
|
inline int
|
|
ZeroGS::VB::CheckFrameResolveDepth(int tbp)
|
|
{
|
|
int result = 0 ;
|
|
CDepthTarget* pprevdepth = pdepth;
|
|
pdepth = NULL;
|
|
|
|
// just z changed
|
|
frameInfo f = CreateFrame(zbuf.zbp, prndr->fbw, prndr->fbh, zbuf.psm, (zbuf.psm == 0x31) ? 0xff000000 : 0);
|
|
|
|
CDepthTarget* pnewdepth = (CDepthTarget*)s_DepthRTs.GetTarg(f, CRenderTargetMngr::TO_DepthBuffer|CRenderTargetMngr::TO_StrictHeight|
|
|
(zbuf.zmsk?CRenderTargetMngr::TO_Virtual:0), get_maxheight(zbuf.zbp, gsfb.fbw, 0));
|
|
|
|
assert( pnewdepth != NULL && prndr != NULL );
|
|
assert( pnewdepth->fbh == prndr->fbh );
|
|
|
|
if( (pprevdepth != pnewdepth) || (pprevdepth != NULL && (pprevdepth->status & CRenderTarget::TS_NeedUpdate)) )
|
|
result = 2;
|
|
|
|
pdepth = pnewdepth;
|
|
|
|
return result ;
|
|
}
|
|
|
|
// Check if after resings new render target is need to be used. Also perform deptaget check
|
|
// Return 1 if only render target is changed and 3 -- if both.
|
|
inline int
|
|
ZeroGS::VB::CheckFrameResolveRender(int tbp) {
|
|
int result = 0 ;
|
|
|
|
CRenderTarget* pprevrndr = prndr;
|
|
prndr = NULL;
|
|
CDepthTarget* pprevdepth = pdepth;
|
|
pdepth = NULL;
|
|
// Set renderes to NULL to prevent Flushing.
|
|
|
|
CRenderTarget* pnewtarg = s_RTs.GetTarg(frame, 0, maxmin);
|
|
assert( pnewtarg != NULL );
|
|
|
|
// pnewtarg->fbh >= 0x1c0 needed for ffx
|
|
if( (pnewtarg->fbh >= 0x1c0) && pnewtarg->fbh > frame.fbh && zbuf.zbp < tbp && !zbuf.zmsk ) {
|
|
// check if zbuf is in the way of the texture (suikoden5)
|
|
int maxallowedfbh = (tbp-zbuf.zbp)*(PSMT_ISHALF(zbuf.psm)?128:64) / gsfb.fbw;
|
|
if (PSMT_ISHALF(gsfb.psm)) maxallowedfbh *= 2;
|
|
|
|
if (pnewtarg->fbh > maxallowedfbh + 32) { // +32 needed for ffx2
|
|
// destroy and recreate
|
|
s_RTs.DestroyAllTargs(0, 0x100, pnewtarg->fbw);
|
|
pnewtarg = s_RTs.GetTarg(frame, 0, maxmin);
|
|
assert( pnewtarg != NULL );
|
|
}
|
|
}
|
|
|
|
PRIM_LOG("frame_%d: fbp=0x%x fbw=%d fbh=%d(%d) psm=0x%x fbm=0x%x\n", ictx, gsfb.fbp, gsfb.fbw, gsfb.fbh, pnewtarg->fbh, gsfb.psm, gsfb.fbm);
|
|
|
|
if( (pprevrndr != pnewtarg) || (pprevrndr != NULL && (pprevrndr->status & CRenderTarget::TS_NeedUpdate)) )
|
|
result = 1;
|
|
|
|
prndr = pnewtarg;
|
|
pdepth = pprevdepth ;
|
|
|
|
result |= CheckFrameResolveDepth(tbp) ;
|
|
return result ;
|
|
}
|
|
|
|
// After frame resetting it is possible that 16 to 32 or 32 to 16 (color bits) conversion should be made.
|
|
inline void
|
|
ZeroGS::VB::CheckFrame16vs32Convesion()
|
|
{
|
|
if( prndr->status & CRenderTarget::TS_NeedConvert32) {
|
|
if( pdepth->pdepth != 0 )
|
|
pdepth->SetDepthStencilSurface();
|
|
prndr->fbh *= 2;
|
|
prndr->ConvertTo32();
|
|
prndr->status &= ~CRenderTarget::TS_NeedConvert32;
|
|
}
|
|
else if( prndr->status & CRenderTarget::TS_NeedConvert16 ) {
|
|
if( pdepth->pdepth != 0 )
|
|
pdepth->SetDepthStencilSurface();
|
|
prndr->fbh /= 2;
|
|
prndr->ConvertTo16();
|
|
prndr->status &= ~CRenderTarget::TS_NeedConvert16;
|
|
}
|
|
}
|
|
|
|
// a lot of times, target is too big and overwrites the texture using,
|
|
// if tbp != 0, use it to bound
|
|
void ZeroGS::VB::CheckFrame(int tbp)
|
|
{
|
|
static int bChanged;
|
|
if( bNeedZCheck ) {
|
|
PRIM_LOG("zbuf_%d: zbp=0x%x psm=0x%x, zmsk=%d\n", ictx, zbuf.zbp, zbuf.psm, zbuf.zmsk);
|
|
//zbuf = *zb;
|
|
}
|
|
|
|
if( m_Blocks[gsfb.psm].bpp == 0 ) {
|
|
ERROR_LOG("CheckFrame invalid bpp %d\n", gsfb.psm);
|
|
return;
|
|
}
|
|
|
|
bChanged = 0;
|
|
|
|
if( bNeedFrameCheck ) {
|
|
|
|
// important to set before calling GetTarg
|
|
bNeedFrameCheck = 0;
|
|
bNeedZCheck = 0;
|
|
if ( CheckFrameAddConstraints(tbp) == -1 ) return ;
|
|
|
|
if ( ( prndr != NULL ) && ( prndr->psm != gsfb.psm ) ) {
|
|
// behavior for dest alpha varies
|
|
ResetAlphaVariables();
|
|
}
|
|
|
|
bChanged = CheckFrameResolveRender(tbp) ;
|
|
CheckFrame16vs32Convesion();
|
|
}
|
|
else if (bNeedZCheck) {
|
|
bNeedZCheck = 0;
|
|
if (prndr != NULL && gsfb.fbw > 0 )
|
|
CheckFrameResolveDepth(tbp);
|
|
}
|
|
|
|
if( prndr != NULL ) SetContextTarget(ictx);
|
|
}
|
|
|
|
// This is the case, most easy to perform, when nothinh was changed
|
|
inline void ZeroGS::VB::FlushTexUnchangedClutDontUpdate() {
|
|
if (ZZOglGet_cld_TexBits(uNextTex0Data[1])) {
|
|
ZeroGS::texClutWrite(ictx);
|
|
// invalidate to make sure target didn't change!
|
|
bVarsTexSync = FALSE;
|
|
}
|
|
}
|
|
|
|
// The second of easy branch. We does not change storage model, so we don't need to
|
|
// update anything except texture itself
|
|
inline void ZeroGS::VB::FlushTexClutDontUpdate() {
|
|
if (!ZZOglClutStorageUnchanged(uCurTex0Data, uNextTex0Data))
|
|
ZeroGS::Flush(ictx);
|
|
|
|
// clut memory isn't going to be loaded so can ignore, but at least update CSA and CPSM!
|
|
uCurTex0Data[1] = (uCurTex0Data[1] & CPSM_CSA_NOTMASK) | (uNextTex0Data[1] & CPSM_CSA_BITMASK);
|
|
|
|
tex0.csa = ZZOglGet_csa_TexBits(uNextTex0Data[1]);
|
|
tex0.cpsm = ZZOglGet_cpsm_TexBits(uNextTex0Data[1]);
|
|
|
|
ZeroGS::texClutWrite(ictx);
|
|
bVarsTexSync = FALSE;
|
|
}
|
|
|
|
|
|
// Set texture variables after big change
|
|
inline void ZeroGS::VB::FlushTexSetNewVars(u32 psm) {
|
|
tex0.tbp0 = ZZOglGet_tbp0_TexBits(uNextTex0Data[0]);
|
|
tex0.tbw = ZZOglGet_tbw_TexBitsMult(uNextTex0Data[0]);
|
|
tex0.psm = psm;
|
|
tex0.tw = ZZOglGet_tw_TexBitsExp(uNextTex0Data[0]);
|
|
tex0.th = ZZOglGet_th_TexBitsExp(uNextTex0Data[0], uNextTex0Data[1]);
|
|
|
|
tex0.tcc = ZZOglGet_tcc_TexBits(uNextTex0Data[1]);
|
|
tex0.tfx = ZZOglGet_tfx_TexBits(uNextTex0Data[1]);
|
|
|
|
ZeroGS::fiTexWidth[ictx] = (1/16.0f)/ tex0.tw;
|
|
ZeroGS::fiTexHeight[ictx] = (1/16.0f) / tex0.th;
|
|
}
|
|
|
|
// Flush == draw on screen
|
|
// This function made VB state consistant before real Flush.
|
|
void ZeroGS::VB::FlushTexData()
|
|
{
|
|
assert( bNeedTexCheck );
|
|
|
|
bNeedTexCheck = 0;
|
|
|
|
u32 psm = ZZOglGet_psm_TexBitsFix(uNextTex0Data[0]);
|
|
|
|
// don't update unless necessary
|
|
if (ZZOglAllExceptClutIsSame(uCurTex0Data, uNextTex0Data)) {
|
|
// Don't need to do anything if there is no clutting and VB tex data was not changed
|
|
if( !PSMT_ISCLUT(psm) )
|
|
return ;
|
|
|
|
// have to write the CLUT again if only CLD was changed
|
|
if( ZZOglClutMinusCLDunchanged(uCurTex0Data, uNextTex0Data) ) {
|
|
FlushTexUnchangedClutDontUpdate();
|
|
return;
|
|
}
|
|
|
|
// Cld bit is 0 means that clut buffer stay unchanged
|
|
if( ZZOglGet_cld_TexBits(uNextTex0Data[1]) == 0 ) {
|
|
FlushTexClutDontUpdate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Made the full update
|
|
ZeroGS::Flush(ictx);
|
|
bVarsTexSync = FALSE;
|
|
bTexConstsSync = FALSE;
|
|
|
|
uCurTex0Data[0] = uNextTex0Data[0];
|
|
uCurTex0Data[1] = uNextTex0Data[1];
|
|
|
|
FlushTexSetNewVars(psm);
|
|
|
|
if( PSMT_ISCLUT(psm) )
|
|
ZeroGS::CluttingForFlushedTex(&tex0, uNextTex0Data[1], ictx ) ;
|
|
}
|
|
|
|
|