pcsx2/plugins/zzogl-pg/opengl/ZZoglFlush.cpp

2628 lines
74 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
*/
// Realisation of Flush -- drawing function of GS
//------------------ Includes
//#if defined(_WIN32)
//# include <windows.h>
//# include "resource.h"
//#endif
#include <stdlib.h>
#include "GS.h"
#include "Mem.h"
#include "zerogs.h"
#include "targets.h"
using namespace ZeroGS;
//------------------ Defines
#ifndef DEVBUILD
#define INC_GENVARS()
#define INC_TEXVARS()
#define INC_ALPHAVARS()
#define INC_RESOLVE()
#define g_bUpdateEffect 0
#define g_bSaveTex 0
bool g_bSaveTrans = 0;
#define g_bSaveResolved 0
#else // NOT RELEASE_TO_PUBLIC
#define INC_GENVARS() ++g_nGenVars
#define INC_TEXVARS() ++g_nTexVars
#define INC_ALPHAVARS() ++g_nAlphaVars
#define INC_RESOLVE() ++g_nResolve
bool g_bSaveTrans = 0;
bool g_bUpdateEffect = 0;
bool g_bSaveTex = 0; // saves the curent texture
bool g_bSaveResolved = 0;
#endif // RELEASE_TO_PUBLIC
#define STENCIL_ALPHABIT 1 // if set, dest alpha >= 0x80
#define STENCIL_PIXELWRITE 2 // if set, pixel just written (reset after every Flush)
#define STENCIL_FBA 4 // if set, just written pixel's alpha >= 0 (reset after every Flush)
#define STENCIL_SPECIAL 8 // if set, indicates that pixel passed its alpha test (reset after every Flush)
//#define STENCIL_PBE 16
#define STENCIL_CLEAR (2|4|8|16)
#define DRAW() glDrawArrays(primtype[curvb.curprim.prim], 0, curvb.nCount)
#define GL_BLEND_RGB(src, dst) { \
s_srcrgb = src; \
s_dstrgb = dst; \
zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha); \
}
#define GL_BLEND_ALPHA(src, dst) { \
s_srcalpha = src; \
s_dstalpha = dst; \
zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha); \
}
#define GL_BLEND_ALL(srcrgb, dstrgb, srcalpha, dstalpha) { \
s_srcrgb = srcrgb; \
s_dstrgb = dstrgb; \
s_srcalpha = srcalpha; \
s_dstalpha = dstalpha; \
zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha); \
}
#define GL_ZTEST(enable) { \
if (enable) glEnable(GL_DEPTH_TEST); \
else glDisable(GL_DEPTH_TEST); \
}
#define GL_ALPHATEST(enable) { \
if( enable ) glEnable(GL_ALPHA_TEST); \
else glDisable(GL_ALPHA_TEST); \
}
#define GL_BLENDEQ_RGB(eq) { \
s_rgbeq = eq; \
zgsBlendEquationSeparateEXT(s_rgbeq, s_alphaeq); \
}
#define GL_BLENDEQ_ALPHA(eq) { \
s_alphaeq = eq; \
zgsBlendEquationSeparateEXT(s_rgbeq, s_alphaeq); \
}
#define COLORMASK_RED 1
#define COLORMASK_GREEN 2
#define COLORMASK_BLUE 4
#define COLORMASK_ALPHA 8
#define GL_COLORMASK(mask) glColorMask(!!((mask)&COLORMASK_RED), !!((mask)&COLORMASK_GREEN), !!((mask)&COLORMASK_BLUE), !!((mask)&COLORMASK_ALPHA))
// ----------------- Types
//------------------ Dummies
//------------------ variables
extern bool g_bIsLost;
bool g_bUpdateStencil = 1;
u32 g_SaveFrameNum = 0; // ZZ
int GPU_TEXWIDTH = 512;
float g_fiGPU_TEXWIDTH = 1/512.0f;
extern CGprogram g_psprog; // 2 -- ZZ
// local alpha blending settings
static GLenum s_rgbeq, s_alphaeq; // set by zgsBlendEquationSeparateEXT // ZZ
static const u32 blendalpha[3] = { GL_SRC_ALPHA, GL_DST_ALPHA, GL_CONSTANT_COLOR_EXT }; // ZZ
static const u32 blendinvalpha[3] = { GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_CONSTANT_COLOR_EXT }; //ZZ
static const u32 g_dwAlphaCmp[] = { GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL }; // ZZ
// used for afail case
static const u32 g_dwReverseAlphaCmp[] = { GL_ALWAYS, GL_NEVER, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_EQUAL };
static const u32 g_dwZCmp[] = { GL_NEVER, GL_ALWAYS, GL_GEQUAL, GL_GREATER };
/////////////////////
// graphics resources
#define s_bForceTexFlush 1 // ZZ
u32 s_ptexCurSet[2] = {0};
static u32 s_ptexNextSet[2] = {0}; // ZZ
vector<u32> s_vecTempTextures; // temporary textures, released at the end of every frame
extern bool s_bTexFlush;
bool s_bWriteDepth = FALSE;
bool s_bDestAlphaTest = FALSE;
int s_ClutResolve = 0; // ZZ
int g_nDepthUsed = 0; // ffx2 pal movies
int s_nWriteDepthCount = 0; // ZZ
int s_nWriteDestAlphaTest = 0; // ZZ
////////////////////
// State parameters
static Vector vAlphaBlendColor; // used for GPU_COLOR
static u8 bNeedBlendFactorInAlpha; // set if the output source alpha is different from the real source alpha (only when blend factor > 0x80)
static u32 s_dwColorWrite = 0xf; // the color write mask of the current target
union {
struct {
u8 _bNeedAlphaColor; // set if vAlphaBlendColor needs to be set
u8 _b2XAlphaTest; // Only valid when bNeedAlphaColor is set. if 1st bit set set, double all alpha testing values
// otherwise alpha testing needs to be done separately.
u8 _bDestAlphaColor; // set to 1 if blending with dest color (process only one tri at a time). If 2, dest alpha is always 1.
u8 _bAlphaClamping; // if first bit is set, do min; if second bit, do max
};
u32 _bAlphaState;
} g_vars;
//#define bNeedAlphaColor g_vars._bNeedAlphaColor
#define b2XAlphaTest g_vars._b2XAlphaTest
#define bDestAlphaColor g_vars._bDestAlphaColor
#define bAlphaClamping g_vars._bAlphaClamping
int g_PrevBitwiseTexX = -1, g_PrevBitwiseTexY = -1; // textures stored in SAMP_BITWISEANDX and SAMP_BITWISEANDY // ZZ
static alphaInfo s_alphaInfo; // ZZ
extern u8* g_pbyGSClut;
//------------------ Namespace
namespace ZeroGS {
VB vb[2];
float fiTexWidth[2], fiTexHeight[2]; // current tex width and height
u8 s_AAx = 0, s_AAy = 0; // if AAy is set, then AAx has to be set
u8 s_AAz = 0, s_AAw = 0; // if AAy is set, then AAx has to be set
int icurctx = -1;
extern CRangeManager s_RangeMngr; // manages overwritten memory // zz
void FlushTransferRanges(const tex0Info* ptex); //zz
RenderFormatType GetRenderFormat() { return g_RenderFormatType; } //zz
// use to update the state
void SetTexVariables(int context, FRAGMENTSHADER* pfragment); // zz
void SetTexInt(int context, FRAGMENTSHADER* pfragment, int settexint); // zz
void SetAlphaVariables(const alphaInfo& ainfo); // zzz
void ResetAlphaVariables();
inline void SetAlphaTestInt(pixTest curtest);
inline void RenderAlphaTest(const VB& curvb, CGparameter sOneColor);
inline void RenderStencil(const VB& curvb, u32 dwUsingSpecialTesting);
inline void ProcessStencil(const VB& curvb);
inline void RenderFBA(const VB& curvb, CGparameter sOneColor);
inline void ProcessFBA(const VB& curvb, CGparameter sOneColor); // zz
}
//------------------ Code
inline float AlphaReferedValue(int aref) {
return b2XAlphaTest ? min (1.0f, (float)aref / 127.5f) : (float)aref /255.0f ;
}
inline void SetAlphaTest(const pixTest& curtest) {
// if s_dwColorWrite is nontrivial, than we should not off alphatest.
// This fix GOW and Okami.
if( !curtest.ate && USEALPHATESTING && (s_dwColorWrite != 2 && s_dwColorWrite != 14 )) {
glDisable(GL_ALPHA_TEST);
}
else {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(g_dwAlphaCmp[curtest.atst], AlphaReferedValue(curtest.aref));
}
}
// Switch wireframe rendering off for first flush, so it's draw few solid primitives
inline void SwitchWireframeOff() {
if( conf.options & GSOPTION_WIREFRAME ) {
if( s_nWireframeCount > 0 ) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
}
// Switch wireframe rendering on, look at previous function
inline void SwitchWireframeOn() {
if( conf.options & GSOPTION_WIREFRAME ) {
if( s_nWireframeCount > 0 ) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
--s_nWireframeCount;
}
}
}
int GetTexFilter(const tex1Info& tex1)
{
// always force
if( conf.bilinear == 2 )
return 1;
int texfilter = 0;
if( conf.bilinear && ptexBilinearBlocks != 0 ) {
if( tex1.mmin <= 1 )
texfilter = tex1.mmin|tex1.mmag;
else
texfilter = tex1.mmag ? ((tex1.mmin+2)&5) : tex1.mmin;
texfilter = texfilter == 1 || texfilter == 4 || texfilter == 5;
}
return texfilter;
}
void ZeroGS::ReloadEffects()
{
#ifndef RELEASE_TO_PUBLIC
for(int i = 0; i < ARRAY_SIZE(ppsTexture); ++i) {
SAFE_RELEASE_PROG(ppsTexture[i].prog);
}
memset(ppsTexture, 0, sizeof(ppsTexture));
LoadExtraEffects();
#endif
}
long BufferNumber = 0;
// This is debug function. It's print all buffer info and save current texture into the file, than printf file name.
inline void
VisualBufferMessage(int context) {
#if defined(PRIM_LOG) && defined(_DEBUG)
BufferNumber++;
ZeroGS::VB& curvb = vb[context];
static const char* patst[8] = { "NEVER", "ALWAYS", "LESS", "LEQUAL", "EQUAL", "GEQUAL", "GREATER", "NOTEQUAL"};
static const char* pztst[4] = { "NEVER", "ALWAYS", "GEQUAL", "GREATER" };
static const char* pafail[4] = { "KEEP", "FB_ONLY", "ZB_ONLY", "RGB_ONLY" };
ERROR_LOG("**Drawing ctx %d, num %d, fbp: 0x%x, zbp: 0x%x, fpsm: %d, zpsm: %d, fbw: %d\n", context, vb[context].nCount, curvb.prndr->fbp, curvb.zbuf.zbp, curvb.prndr->psm, curvb.zbuf.psm, curvb.prndr->fbw);
ERROR_LOG("prim: prim=%x iip=%x tme=%x fge=%x abe=%x aa1=%x fst=%x ctxt=%x fix=%x\n",
curvb.curprim.prim, curvb.curprim.iip, curvb.curprim.tme, curvb.curprim.fge, curvb.curprim.abe, curvb.curprim.aa1, curvb.curprim.fst, curvb.curprim.ctxt, curvb.curprim.fix);
ERROR_LOG("test: ate:%d, atst: %s, aref: %d, afail: %s, date: %d, datm: %d, zte: %d, ztst: %s, fba: %d\n",
curvb.test.ate, patst[curvb.test.atst], curvb.test.aref, pafail[curvb.test.afail], curvb.test.date, curvb.test.datm, curvb.test.zte, pztst[curvb.test.ztst], curvb.fba.fba);
ERROR_LOG("alpha: A%d B%d C%d D%d FIX:%d pabe: %d; aem: %d, ta0: %d, ta1: %d\n", curvb.alpha.a, curvb.alpha.b, curvb.alpha.c, curvb.alpha.d, curvb.alpha.fix, gs.pabe, gs.texa.aem, gs.texa.ta[0], gs.texa.ta[1]);
ERROR_LOG("tex0: tbp0=0x%x, tbw=%d, psm=0x%x, tw=%d, th=%d, tcc=%d, tfx=%d, cbp=0x%x, cpsm=0x%x, csm=%d, csa=%d, cld=%d\n",
curvb.tex0.tbp0, curvb.tex0.tbw, curvb.tex0.psm, curvb.tex0.tw,
curvb.tex0.th, curvb.tex0.tcc, curvb.tex0.tfx, curvb.tex0.cbp,
curvb.tex0.cpsm, curvb.tex0.csm, curvb.tex0.csa, curvb.tex0.cld);
char* Name;
// if (g_bSaveTex) {
// if (g_bSaveTex == 1)
Name = NamedSaveTex(&curvb.tex0, 1);
// else
// Name = NamedSaveTex(&curvb.tex0, 0);
ERROR_LOG("TGA name %s\n", Name);
free(Name);
// }
ERROR_LOG("frame: %d, buffer %ld\n\n", g_SaveFrameNum, BufferNumber);
#endif
}
inline void SaveRendererTarget(VB& curvb) {
#ifdef _DEBUG
if( g_bSaveFlushedFrame & 0x80000000 ) {
char str[255];
sprintf(str, "rndr.tga", g_SaveFrameNum);
SaveRenderTarget(str, curvb.prndr->fbw, curvb.prndr->fbh, 0);
}
#endif
}
// Stop effects in Developers mode
inline void FlushUpdateEffect() {
#ifdef DEVBUID
if( g_bUpdateEffect ) {
ReloadEffects();
g_bUpdateEffect = 0;
}
#endif
}
// Check, maybe we cold skip flush
inline bool IsFlushNoNeed(VB& curvb, const pixTest& curtest ) {
if (curvb.nCount == 0 || (curtest.zte && curtest.ztst == 0) || g_bIsLost) {
curvb.nCount = 0;
return true;
}
return false;
}
// Transfer targets, that are located in current texture.
inline void FlushTransferRangesHelper(VB& curvb) {
if( s_RangeMngr.ranges.size() > 0 ) {
// don't want infinite loop, so set nCount to 0.
u32 prevcount = curvb.nCount;
curvb.nCount = 0;
FlushTransferRanges(curvb.curprim.tme ? &curvb.tex0 : NULL);
curvb.nCount += prevcount;
}
}
// If set bit for texture cheking, do it. Maybe it's all.
inline bool FushTexDataHelper(VB& curvb){
if( curvb.bNeedFrameCheck || curvb.bNeedZCheck ) {
curvb.CheckFrame(curvb.curprim.tme ? curvb.tex0.tbp0 : 0);
}
if( curvb.bNeedTexCheck ) { // Zeydlitz want to try this
curvb.FlushTexData();
if (curvb.nCount == 0)
return true;
}
return false;
}
// Null target mean that we do something really bad.
inline bool FlushCheckForNULLTarget(VB& curvb, int context){
if ((curvb.prndr == NULL) || (curvb.pdepth == NULL)) {
ERROR_LOG_SPAMA("Current render target NULL (ctx: %d)", context);
curvb.nCount = 0;
return true;
}
return false;
}
// O.k. A set of resolutions, we do before real flush. We do RangeManager, FrameCheck and
// ZCheck before this.
inline bool FlushInitialTest(VB& curvb, const pixTest& curtest, int context) {
GL_REPORT_ERRORD();
assert( context >= 0 && context <= 1 );
FlushUpdateEffect();
if (IsFlushNoNeed(curvb, curtest))
return true;
FlushTransferRangesHelper(curvb);
if (FushTexDataHelper(curvb))
return true;
GL_REPORT_ERRORD();
if (FlushCheckForNULLTarget(curvb, context))
return true;
return false;
}
// Try to different approach if texture target was not found
inline CRenderTarget* FlushReGetTarget(int& tbw, int& tbp0, int& tpsm, VB& curvb) {
// This was incorrect code
CRenderTarget* ptextarg = NULL;
if (ptextarg == NULL && tpsm == PSMT8 && (g_GameSettings & GAME_REGETHACK) ) {
// check for targets with half the width. Break Valkyrie Chronicles
ptextarg = s_RTs.GetTarg(tbp0, tbw/2, curvb);
if( ptextarg == NULL ) {
tbp0 &= ~0x7ff;
ptextarg = s_RTs.GetTarg(tbp0, tbw/2, curvb); // mgs3 hack
if( ptextarg == NULL ) {
// check the next level (mgs3)
tbp0 &= ~0xfff;
ptextarg = s_RTs.GetTarg(tbp0, tbw/2, curvb); // mgs3 hack
}
if( ptextarg != NULL && ptextarg->start > tbp0*256 ) {
// target beyond range, so ignore
ptextarg = NULL;
}
}
}
if( PSMT_ISZTEX(tpsm) && (ptextarg == NULL) ) {
// try depth
ptextarg = s_DepthRTs.GetTarg(tbp0, tbw, curvb);
}
if ((ptextarg == NULL) && (g_GameSettings & GAME_TEXTURETARGS) ) {
// check if any part of the texture intersects the current target
if( !PSMT_ISCLUT(tpsm) && (curvb.tex0.tbp0 >= curvb.frame.fbp) && ((curvb.tex0.tbp0 ) < curvb.prndr->end)) {
ptextarg = curvb.prndr;
}
}
#ifdef DEBUG
if (tbp0 == 0x3600 && tbw == 0x100) {
if (ptextarg == NULL) {
printf ("Miss %x 0x%x %d\n", tbw, tbp0, tpsm);
typedef map<u32, CRenderTarget*> MAPTARGETS;
for(MAPTARGETS::iterator itnew = s_RTs.mapTargets.begin(); itnew != s_RTs.mapTargets.end(); ++itnew) {
printf("\tRender %x 0x%x %x\n", itnew->second->fbw, itnew->second->fbp, itnew->second->psm);
}
for(MAPTARGETS::iterator itnew = s_DepthRTs.mapTargets.begin(); itnew != s_DepthRTs.mapTargets.end(); ++itnew) {
printf("\tDepth %x 0x%x %x\n", itnew->second->fbw, itnew->second->fbp, itnew->second->psm);
}
printf ("\tCurvb 0x%x 0x%x 0x%x %x\n", curvb.frame.fbp, curvb.prndr->end, curvb.prndr->fbp, curvb.prndr->fbw);
}
else
printf ("Hit %x 0x%x %x\n", tbw, tbp0, tpsm);
}
#endif
return ptextarg;
}
// Find target to draw a texture, it's highly p
inline CRenderTarget* FlushGetTarget(VB& curvb) {
int tbw, tbp0, tpsm;
CRenderTarget* ptextarg = NULL;
if (!curvb.curprim.tme)
return ptextarg;
if (curvb.bNeedTexCheck) {
printf ("How it is possible?\n");
// not yet initied, but still need to get correct target! (xeno3 ingame)
tbp0 = ZZOglGet_tbp0_TexBits(curvb.uNextTex0Data[0]);
tbw = ZZOglGet_tbw_TexBitsMult(curvb.uNextTex0Data[0]);
tpsm = ZZOglGet_psm_TexBitsFix(curvb.uNextTex0Data[0]);
}
else{
tbw = curvb.tex0.tbw;
tbp0 = curvb.tex0.tbp0;
tpsm = curvb.tex0.psm;
}
ptextarg = s_RTs.GetTarg(tbp0, tbw, curvb);
if (ptextarg == NULL)
ptextarg = FlushReGetTarget(tbw, tbp0, tpsm, curvb);
if ((ptextarg != NULL) && !(ptextarg->status & CRenderTarget::TS_NeedUpdate)) {
if (PSMT_BITMODE(tpsm) == 4) { // handle 8h cluts
// don't support clut targets, read from mem
// 4hl - kh2 check - from dx version -- arcum42
if ( tpsm == PSMT4 && s_ClutResolve <= 1 )
{ // xenosaga requires 2 resolves
u32 prevcount = curvb.nCount;
curvb.nCount = 0;
ptextarg->Resolve();
s_ClutResolve++;
curvb.nCount += prevcount;
}
ptextarg = NULL;
}
else {
if (ptextarg == curvb.prndr) {
// need feedback
curvb.prndr->CreateFeedback();
if (s_bWriteDepth && (curvb.pdepth != NULL))
curvb.pdepth->SetRenderTarget(1);
else
ResetRenderTarget(1);
}
}
}
else ptextarg = NULL;
return ptextarg;
}
// Set target for current context
inline void FlushSetContextTarget(VB& curvb, int context) {
if( !curvb.bVarsSetTarg )
SetContextTarget(context);
else {
assert( curvb.pdepth != NULL );
if( curvb.pdepth->status & CRenderTarget::TS_Virtual) {
if( !curvb.zbuf.zmsk ) {
CRenderTarget* ptemp = s_DepthRTs.Promote(GetFrameKey(curvb.pdepth));
assert( ptemp == curvb.pdepth );
}
else
curvb.pdepth->status &= ~CRenderTarget::TS_NeedUpdate;
}
if( (curvb.pdepth->status & CRenderTarget::TS_NeedUpdate) || (curvb.prndr->status & CRenderTarget::TS_NeedUpdate) )
SetContextTarget(context);
}
assert( !(curvb.prndr->status&CRenderTarget::TS_NeedUpdate) );
curvb.prndr->status = 0;
if( curvb.pdepth != NULL ) {
assert( !(curvb.pdepth->status&CRenderTarget::TS_NeedUpdate) );
if( !curvb.zbuf.zmsk ) {
assert( !(curvb.pdepth->status & CRenderTarget::TS_Virtual) );
curvb.pdepth->status = 0;
}
}
}
inline void FlushSetStream(VB& curvb) {
glBindBuffer(GL_ARRAY_BUFFER, g_vboBuffers[g_nCurVBOIndex]);
g_nCurVBOIndex = (g_nCurVBOIndex + 1) % g_vboBuffers.size();
glBufferData(GL_ARRAY_BUFFER, curvb.nCount * sizeof(VertexGPU), curvb.pBufferData, GL_STREAM_DRAW);
// void* pdata = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// memcpy_amd(pdata, curvb.pBufferData, curvb.nCount * sizeof(VertexGPU));
// glUnmapBuffer(GL_ARRAY_BUFFER);
SET_STREAM();
#ifdef _DEBUG
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
assert( glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT );
#endif
}
int SetMaskR = 0x0; int SetMaskG = 0x0; int SetMaskB = 0x0;
// Set color mask. Really it's not as good as PS2 one.
inline void FlushSetColorMask(VB& curvb) {
s_dwColorWrite = (PSMT_BITMODE(curvb.prndr->psm) == 1) ? (COLORMASK_BLUE|COLORMASK_GREEN|COLORMASK_RED) : 0xf;
int maskR = ZZOglGet_fbmRed_FrameBits(curvb.frame.fbm);
int maskG = ZZOglGet_fbmGreen_FrameBits(curvb.frame.fbm);
int maskB = ZZOglGet_fbmBlue_FrameBits(curvb.frame.fbm);
int maskA = ZZOglGet_fbmAlpha_FrameBits(curvb.frame.fbm);
if (maskR == 0xff)
s_dwColorWrite &= ~COLORMASK_RED;
if (maskG == 0xff)
s_dwColorWrite &= ~COLORMASK_GREEN;
if (maskB == 0xff)
s_dwColorWrite &= ~COLORMASK_BLUE;
if ((maskA == 0xff) || (curvb.curprim.abe && (curvb.test.atst == 2 && curvb.test.aref == 128)))
s_dwColorWrite &= ~COLORMASK_ALPHA;
GL_COLORMASK(s_dwColorWrite);
}
// Set Scissors for scissor test.
inline void FlushSetScissorRect(VB& curvb) {
Rect& scissor = curvb.prndr->scissorrect;
glScissor(scissor.x, scissor.y, scissor.w, scissor.h);
}
// Prior really doing something check context
inline void FlushDoContextJob(VB& curvb, int context) {
SaveRendererTarget(curvb);
FlushSetContextTarget(curvb, context);
icurctx = context;
FlushSetStream(curvb);
FlushSetColorMask(curvb);
FlushSetScissorRect(curvb);
}
// Set 1 is Alpha test is EQUAL and alpha should be proceed with care.
inline int FlushGetExactcolor(const pixTest curtest) {
if (!(g_nPixelShaderVer&SHADER_REDUCED))
// ffx2 breaks when ==7
return ((curtest.ate && curtest.aref <= 128) && (curtest.atst==4));//||curtest.atst==7);
return 0;
}
// fill the buffer by decoding the clut
inline void FlushDecodeClut(VB& curvb, GLuint& ptexclut) {
glGenTextures(1, &ptexclut);
glBindTexture(GL_TEXTURE_2D, ptexclut);
vector<char> data(PSMT_ISHALF_STORAGE(curvb.tex0) ? 512 : 1024);
if( ptexclut != 0 ) {
int nClutOffset = 0, clutsize;
int entries = PSMT_IS8CLUT(curvb.tex0.psm) ? 256 : 16;
if (curvb.tex0.csm && curvb.tex0.csa )
printf ("ERROR, csm1\n");
if (curvb.tex0.cpsm <= 1) { // 32 bit
nClutOffset = 64 * curvb.tex0.csa;
clutsize = min(entries, 256 - curvb.tex0.csa * 16) * 4;
}
else {
nClutOffset = 64 * (curvb.tex0.csa & 15) + (curvb.tex0.csa >= 16 ? 2 : 0);
clutsize = min(entries, 512 - curvb.tex0.csa * 16) * 2;
}
if( curvb.tex0.cpsm <= 1 ) { // 32 bit
memcpy_amd(&data[0], g_pbyGSClut+nClutOffset, clutsize);
}
else {
u16* pClutBuffer = (u16*)(g_pbyGSClut + nClutOffset);
u16* pclut = (u16*)&data[0];
int left = ((u32)nClutOffset & 2) ? 0 : ((nClutOffset&0x3ff)/2)+clutsize-512;
if( left > 0 ) clutsize -= left;
while(clutsize > 0) {
pclut[0] = pClutBuffer[0];
pclut++;
pClutBuffer+=2;
clutsize -= 2;
}
if( left > 0) {
pClutBuffer = (u16*)(g_pbyGSClut + 2);
while(left > 0) {
pclut[0] = pClutBuffer[0];
left -= 2;
pClutBuffer += 2;
pclut++;
}
}
}
glTexImage2D(GL_TEXTURE_2D, 0, 4, 256, 1, 0, GL_RGBA, PSMT_ISHALF_STORAGE(curvb.tex0)?GL_UNSIGNED_SHORT_5_5_5_1:GL_UNSIGNED_BYTE, &data[0]);
s_vecTempTextures.push_back(ptexclut);
if (g_bSaveTex)
SaveTexture("clut.tga", GL_TEXTURE_2D, ptexclut, 256, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
inline int FlushGetShaderType(VB& curvb, CRenderTarget* ptextarg, GLuint& ptexclut) {
if( PSMT_ISCLUT(curvb.tex0.psm) && !(g_GameSettings&GAME_NOTARGETCLUT) ) {
FlushDecodeClut(curvb, ptexclut);
if( !(g_nPixelShaderVer&SHADER_REDUCED) && PSMT_ISHALF(ptextarg->psm) ) {
return 4;
}
else {
// Valkyrie
return 2;
}
}
if (PSMT_ISHALF_STORAGE(curvb.tex0) != PSMT_ISHALF(ptextarg->psm) && (!(g_nPixelShaderVer&SHADER_REDUCED) || !curvb.curprim.fge) ) {
if (PSMT_ISHALF_STORAGE(curvb.tex0)) {
// converting from 32->16
// Radiata Chronicles
return 3;
}
else {
// converting from 16->32
// Star Ward: Force
return 0;
}
}
return 1;
}
//Set page offsets depends omn shader type.
inline Vector FlushSetPageOffset(FRAGMENTSHADER* pfragment, int shadertype, CRenderTarget* ptextarg) {
SetShaderCaller("FlushSetPageOffset");
Vector vpageoffset;
vpageoffset.w = 0;
switch (shadertype) {
case 3:
vpageoffset.x = -0.1f / 256.0f;
vpageoffset.y = -0.001f / 256.0f;
vpageoffset.z = -0.1f / (ptextarg->fbh);
vpageoffset.w = 0.0f;
break;
case 4:
vpageoffset.x = 2;
vpageoffset.y = 1;
vpageoffset.z = 0;
vpageoffset.w = 0.0001f;
break;
}
// zoe2
if (PSMT_ISZTEX(ptextarg->psm))
vpageoffset.w = -1.0f;
ZZcgSetParameter4fv(pfragment->fPageOffset, vpageoffset, "g_fPageOffset");
return vpageoffset;
}
//Set texture offsets depends omn shader type.
inline Vector FlushSetTexOffset(FRAGMENTSHADER* pfragment, int shadertype, VB& curvb, CRenderTarget* ptextarg) {
SetShaderCaller("FlushSetTexOffset");
Vector v;
if( shadertype == 3 ) {
Vector v;
v.x = 16.0f / (float)curvb.tex0.tw;
v.y = 16.0f / (float)curvb.tex0.th;
v.z = 0.5f * v.x;
v.w = 0.5f * v.y;
ZZcgSetParameter4fv(pfragment->fTexOffset, v, "g_fTexOffset");
}
else if( shadertype == 4 ) {
Vector v;
v.x = 16.0f / (float)ptextarg->fbw;
v.y = 16.0f / (float)ptextarg->fbh;
v.z = -1;
v.w = 8.0f / (float)ptextarg->fbh;
ZZcgSetParameter4fv(pfragment->fTexOffset, v, "g_fTexOffset");
}
return v;
}
// Set dimension (Real!) of texture. z and w
inline Vector FlushTextureDims(FRAGMENTSHADER* pfragment, int shadertype, VB& curvb, CRenderTarget* ptextarg) {
SetShaderCaller("FlushTextureDims");
Vector vTexDims;
vTexDims.x = (float)RW(curvb.tex0.tw) ;
vTexDims.y = (float)RH(curvb.tex0.th) ;
// look at the offset of tbp0 from fbp
if( curvb.tex0.tbp0 <= ptextarg->fbp ) {
vTexDims.z = 0;//-0.5f/(float)ptextarg->fbw;
vTexDims.w = 0;//0.2f/(float)ptextarg->fbh;
}
else {
u32 tbp0 = curvb.tex0.tbp0 >> 5; // align to a page
int blockheight = PSMT_ISHALF(ptextarg->psm) ? 64 : 32;
int ycoord = ((curvb.tex0.tbp0 - ptextarg->fbp) / (32 * (ptextarg->fbw >> 6))) * blockheight;
int xcoord = (((curvb.tex0.tbp0 - ptextarg->fbp) % (32 * (ptextarg -> fbw >> 6)))) * 2;
vTexDims.z = (float)xcoord;
vTexDims.w = (float)ycoord;
}
if (shadertype == 4)
vTexDims.z += 8.0f;
ZZcgSetParameter4fv(pfragment->fTexDims, vTexDims, "g_fTexDims");
return vTexDims;
}
// Apply TEX1 mmag and mmin -- filter for expanding/reducing texture
// We ignore all settings, only NEAREST (0) is used
inline void FlushApplyResizeFilter(VB& curvb, u32& dwFilterOpts, CRenderTarget* ptextarg, int context) {
u32 ptexset = (ptextarg == curvb.prndr) ? ptextarg->ptexFeedback : ptextarg->ptex;
s_ptexCurSet[context] = ptexset;
if ((!curvb.tex1.mmag) || (!curvb.tex1.mmin))
glBindTexture(GL_TEXTURE_RECTANGLE_NV, ptexset);
if( !curvb.tex1.mmag ) {
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
dwFilterOpts |= 1;
}
if( !curvb.tex1.mmin ) {
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
dwFilterOpts |= 2;
}
}
// Usage existing targets depends on several tricks, 32-16 conversion and CLUTing, so we need handle it.
inline FRAGMENTSHADER* FlushUseExistRenderTaget(VB& curvb, CRenderTarget* ptextarg, u32& dwFilterOpts, int exactcolor, int context) {
if (ptextarg->IsDepth())
SetWriteDepth();
GLuint ptexclut = 0;
int psm = GetTexCPSM(curvb.tex0);
int shadertype = FlushGetShaderType(curvb, ptextarg, ptexclut);
FRAGMENTSHADER* pfragment = LoadShadeEffect(shadertype, 0, curvb.curprim.fge,
IsAlphaTestExpansion(curvb), exactcolor, curvb.clamp, context, NULL);
Vector vpageoffset = FlushSetPageOffset(pfragment, shadertype, ptextarg);
Vector v = FlushSetTexOffset(pfragment, shadertype, curvb, ptextarg);
Vector vTexDims = FlushTextureDims(pfragment, shadertype, curvb, ptextarg);
if( pfragment->sCLUT != NULL && ptexclut != 0 ) {
cgGLSetTextureParameter(pfragment->sCLUT, ptexclut);
cgGLEnableTextureParameter(pfragment->sCLUT);
}
FlushApplyResizeFilter(curvb, dwFilterOpts, ptextarg, context);
if( g_bSaveTex )
SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_NV,
ptextarg == curvb.prndr ? ptextarg->ptexFeedback : ptextarg->ptex, RW(ptextarg->fbw), RH(ptextarg->fbh));
return pfragment;
}
// Usage most major shader.
inline FRAGMENTSHADER* FlushMadeNewTarget(VB& curvb, int exactcolor, int context) {
// save the texture
if( g_bSaveTex ) {
if( g_bSaveTex == 1 ) {
SaveTex(&curvb.tex0, 1);
CMemoryTarget* pmemtarg = g_MemTargs.GetMemoryTarget(curvb.tex0, 0);
}
else SaveTex(&curvb.tex0, 0);
}
FRAGMENTSHADER* pfragment = LoadShadeEffect(0, GetTexFilter(curvb.tex1), curvb.curprim.fge,
IsAlphaTestExpansion(curvb), exactcolor, curvb.clamp, context, NULL);
if (pfragment == NULL)
ERROR_LOG("Could not find memory target shader\n");
return pfragment;
}
// We made an shader, so now need to put all common variables.
inline void FlushSetTexture(VB& curvb, FRAGMENTSHADER* pfragment, CRenderTarget* ptextarg, int context) {
SetTexVariables(context, pfragment);
SetTexInt(context, pfragment, ptextarg == NULL);
// have to enable the texture parameters(curtest.atst=
if( curvb.ptexClamp[0] != 0 ) {
cgGLSetTextureParameter(pfragment->sBitwiseANDX, curvb.ptexClamp[0]);
cgGLEnableTextureParameter(pfragment->sBitwiseANDX);
}
if( curvb.ptexClamp[1] != 0 ) {
cgGLSetTextureParameter(pfragment->sBitwiseANDY, curvb.ptexClamp[1]);
cgGLEnableTextureParameter(pfragment->sBitwiseANDY);
}
if( pfragment->sMemory != NULL && s_ptexCurSet[context] != 0) {
cgGLSetTextureParameter(pfragment->sMemory, s_ptexCurSet[context]);
cgGLEnableTextureParameter(pfragment->sMemory);
}
}
// Reset programm and texture variables;
inline void FlushBindProgramm( FRAGMENTSHADER* pfragment, int context) {
vb[context].bTexConstsSync = 0;
vb[context].bVarsTexSync = 0;
cgGLBindProgram(pfragment->prog);
g_psprog = pfragment->prog;
}
inline FRAGMENTSHADER* FlushRendererStage(VB& curvb, u32& dwFilterOpts, CRenderTarget* ptextarg, int exactcolor, int context) {
FRAGMENTSHADER* pfragment = NULL;
// set the correct pixel shaders
if (curvb.curprim.tme) {
if (ptextarg != NULL)
pfragment = FlushUseExistRenderTaget(curvb, ptextarg, dwFilterOpts, exactcolor, context);
else
pfragment = FlushMadeNewTarget(curvb, exactcolor, context);
if (pfragment == NULL) {
ERROR_LOG("Shader does not found\n");
// return NULL;
}
FlushSetTexture(curvb, pfragment, ptextarg, context);
}
else {
pfragment = &ppsRegular[curvb.curprim.fge + 2 * s_bWriteDepth];
}
GL_REPORT_ERRORD();
// set the shaders
SetShaderCaller("FlushRendererStage") ;
SETVERTEXSHADER(pvs[2 * ((curvb.curprim._val >> 1) & 3) + 8 * s_bWriteDepth + context] );
FlushBindProgramm(pfragment, context);
GL_REPORT_ERRORD();
return pfragment;
}
inline bool AlphaCanRenderStencil(VB& curvb) {
return g_bUpdateStencil && (PSMT_BITMODE(curvb.prndr->psm) != 1) &&
!ZZOglGet_fbmHighByte(curvb.frame.fbm) && !(g_GameSettings & GAME_NOSTENCIL);
}
inline void AlphaSetStencil (bool DoIt) {
if (DoIt) {
glEnable(GL_STENCIL_TEST);
GL_STENCILFUNC(GL_ALWAYS, 0, 0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
else glDisable(GL_STENCIL_TEST);
}
inline void AlphaSetDepthTest(VB& curvb, const pixTest curtest, FRAGMENTSHADER* pfragment) {
glDepthMask(!curvb.zbuf.zmsk && curtest.zte);
// && curtest.zte && (curtest.ztst > 1) );
if (curtest.zte) {
if (curtest.ztst > 1) g_nDepthUsed = 2;
if ((curtest.ztst == 2) ^ (g_nDepthBias != 0) ) {
g_nDepthBias = (curtest.ztst == 2);
//SETRS(D3DRS_DEPTHBIAS, g_nDepthBias?FtoDW(0.0003f):FtoDW(0.000015f));
}
glDepthFunc(g_dwZCmp[curtest.ztst]);
}
GL_ZTEST(curtest.zte);
// glEnable (GL_POLYGON_OFFSET_FILL);
// glPolygonOffset (-1., -1.);
if (s_bWriteDepth) {
if(!curvb.zbuf.zmsk)
curvb.pdepth->SetRenderTarget(1);
else
ResetRenderTarget(1);
}
}
inline u32 AlphaSetupBlendTest(VB& curvb) {
if (curvb.curprim.abe)
SetAlphaVariables(curvb.alpha);
else
glDisable(GL_BLEND);
u32 oldabe = curvb.curprim.abe;
if (gs.pabe) {
//ERROR_LOG("PBE!\n");
curvb.curprim.abe = 1;
glEnable(GL_BLEND);
}
return oldabe;
}
inline void AlphaRenderFBA(VB& curvb, FRAGMENTSHADER* pfragment, bool s_bDestAlphaTest, bool bCanRenderStencil) {
// needs to be before RenderAlphaTest
if ((gs.pabe) || (curvb.fba.fba && !ZZOglGet_fbmHighByte(curvb.frame.fbm)) || (s_bDestAlphaTest && bCanRenderStencil)) {
RenderFBA(curvb, pfragment->sOneColor);
}
}
inline u32 AlphaRenderAlpha(VB& curvb, const pixTest curtest, FRAGMENTSHADER* pfragment, int exactcolor) {
SetShaderCaller("AlphaRenderAlpha");
u32 dwUsingSpecialTesting = 0;
if (curvb.curprim.abe) {
if ((bNeedBlendFactorInAlpha || ((curtest.ate && curtest.atst > 1) && (curtest.aref > 0x80)))) {
// need special stencil processing for the alpha
RenderAlphaTest(curvb, pfragment->sOneColor);
dwUsingSpecialTesting = 1;
}
// harvest fishing
Vector v = vAlphaBlendColor;
if (exactcolor) {
v.y *= 255;
v.w *= 255;
}
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
}
else {
// not using blending so set to defaults
Vector v = exactcolor ? Vector(1, 510*255.0f/256.0f, 0, 0) : Vector(1,2*255.0f/256.0f,0,0);
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
}
return dwUsingSpecialTesting;
}
inline void AlphaRenderStencil(VB& curvb, bool s_bDestAlphaTest, bool bCanRenderStencil, u32 dwUsingSpecialTesting) {
if (s_bDestAlphaTest && bCanRenderStencil) {
// if not 24bit and can write to high alpha bit
RenderStencil(curvb, dwUsingSpecialTesting);
}
else {
s_stencilref = STENCIL_SPECIAL;
s_stencilmask = STENCIL_SPECIAL;
// setup the stencil to only accept the test pixels
if (dwUsingSpecialTesting) {
glEnable(GL_STENCIL_TEST);
glStencilMask(STENCIL_PIXELWRITE);
GL_STENCILFUNC(GL_EQUAL, STENCIL_SPECIAL|STENCIL_PIXELWRITE, STENCIL_SPECIAL);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
}
#ifdef _DEBUG
if (bDestAlphaColor == 1) {
WARN_LOG("dest alpha blending! manipulate alpha here\n");
}
#endif
if( bCanRenderStencil && gs.pabe ) {
// only render the pixels with alpha values >= 0x80
GL_STENCILFUNC(GL_EQUAL, s_stencilref|STENCIL_FBA, s_stencilmask|STENCIL_FBA);
}
GL_REPORT_ERRORD();
}
inline void AlphaTest(VB& curvb) {
// printf ("%d %d %d %d %d\n", curvb.test.date, curvb.test.datm, gs.texa.aem, curvb.test.ate, curvb.test.atst );
// return;
// Zeydlitz change this with a reason! It's "Alpha more than 1 hack."
if (curvb.test.ate == 1 && curvb.test.atst == 1 && curvb.test.date == 1) {
if (curvb.test.datm == 1)
glAlphaFunc(GL_GREATER, 1.0f);
else {
glAlphaFunc(GL_LESS, 1.0f);
printf ("%d %d %d\n", curvb.test.date, curvb.test.datm, gs.texa.aem);
}
}
if (!curvb.test.ate || curvb.test.atst > 0) {
DRAW();
}
GL_REPORT_ERRORD();
}
inline void AlphaPabe(VB& curvb, FRAGMENTSHADER* pfragment, int exactcolor) {
if( gs.pabe ) {
SetShaderCaller("AlphaPabe");
// only render the pixels with alpha values < 0x80
glDisable(GL_BLEND);
GL_STENCILFUNC_SET();
Vector v;
v.x = 1; v.y = 2; v.z = 0; v.w = 0;
if( exactcolor ) v.y *= 255;
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
DRAW();
// reset
if (!s_stencilmask)
s_stencilfunc = GL_ALWAYS;
GL_STENCILFUNC_SET();
}
GL_REPORT_ERRORD();
}
// Alfa Failure does not work properly on this cases. True means that no failure job should be done
// First three cases are trivail manual
inline bool AlphaFailureIgnore(const pixTest curtest) {
if (!curtest.ate)
return true;
if (curtest.atst == 1)
return true;
if (curtest.afail == 0)
return true;
if (g_GameSettings & GAME_NOALPHAFAIL && ((s_dwColorWrite < 8) || (s_dwColorWrite == 15 && curtest.atst == 5 && (curtest.aref == 64))))
return true;
// old and seemingly incorrect code.
// if ((s_dwColorWrite < 8 && s_dwColorWrite !=8) && curtest.afail == 1)
// return true;
// if ((s_dwColorWrite == 0xf) && curtest.atst == 5 && curtest.afail == 1 && !(g_GameSettings & GAME_REGETHACK))
// return true;
return false;
}
// more work on alpha failure case
inline void AlphaFailureTestJob(VB& curvb, const pixTest curtest, FRAGMENTSHADER* pfragment, int exactcolor, bool bCanRenderStencil, int oldabe) {
// Note, case when ate == 1, atst == 0 and afail > 0 in documentation wrote as failure case. But it seems that
// either doc's are incorrect nor this case have some issues.
if (AlphaFailureIgnore(curtest)) {
return;
}
#ifdef NOALFAFAIL
ERROR_LOG("Alpha job here %d %d %d %d %d %d\n", s_dwColorWrite, curtest.atst, curtest.afail, curtest.aref, gs.pabe, s_bWriteDepth);
// return;
#endif
SetShaderCaller("AlphaFailureTestJob");
// need to reverse the test and disable some targets
glAlphaFunc(g_dwReverseAlphaCmp[curtest.atst], AlphaReferedValue(curtest.aref));
if (curtest.afail & 1) { // front buffer update only
if( curtest.afail == 3 ) // disable alpha
glColorMask(1,1,1,0);
glDepthMask(0);
if (s_bWriteDepth)
ResetRenderTarget(1);
}
else {
// zbuffer update only
glColorMask(0,0,0,0);
}
if( gs.pabe && bCanRenderStencil ) {
// only render the pixels with alpha values >= 0x80
Vector v = vAlphaBlendColor;
if( exactcolor ) { v.y *= 255; v.w *= 255; }
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
glEnable(GL_BLEND);
GL_STENCILFUNC(GL_EQUAL, s_stencilref|STENCIL_FBA, s_stencilmask|STENCIL_FBA);
}
DRAW();
GL_REPORT_ERRORD();
if (gs.pabe) {
// only render the pixels with alpha values < 0x80
glDisable(GL_BLEND);
GL_STENCILFUNC_SET();
Vector v;
v.x = 1; v.y = 2; v.z = 0; v.w = 0;
if (exactcolor) v.y *= 255;
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
DRAW();
// reset
if (oldabe)
glEnable(GL_BLEND);
if (!s_stencilmask)
s_stencilfunc = GL_ALWAYS;
GL_STENCILFUNC_SET();
}
// restore
if ((curtest.afail & 1) && !curvb.zbuf.zmsk ) {
glDepthMask(1);
if( s_bWriteDepth ) {
assert( curvb.pdepth != NULL);
curvb.pdepth->SetRenderTarget(1);
}
}
GL_COLORMASK(s_dwColorWrite);
// not needed anymore since rest of ops concentrate on image processing
GL_REPORT_ERRORD();
}
inline void AlphaSpecialTesting(VB& curvb, FRAGMENTSHADER* pfragment, u32 dwUsingSpecialTesting, int exactcolor) {
if (dwUsingSpecialTesting) {
SetShaderCaller("AlphaSpecialTesting");
// render the real alpha
glDisable(GL_ALPHA_TEST);
glColorMask(0,0,0,1);
if (s_bWriteDepth) {
ResetRenderTarget(1);
}
glDepthMask(0);
glStencilFunc(GL_EQUAL, STENCIL_SPECIAL|STENCIL_PIXELWRITE, STENCIL_SPECIAL|STENCIL_PIXELWRITE);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
Vector v = Vector(0,exactcolor ? 510.0f : 2.0f,0,0);
ZZcgSetParameter4fv(pfragment->sOneColor, v, "g_fOneColor");
DRAW();
// don't need to restore
}
GL_REPORT_ERRORD();
}
inline void AlphaDestinationTest(VB& curvb, FRAGMENTSHADER* pfragment, bool s_bDestAlphaTest, bool bCanRenderStencil) {
if (s_bDestAlphaTest) {
if( (s_dwColorWrite & COLORMASK_ALPHA) ) {
if( curvb.fba.fba )
ProcessFBA(curvb, pfragment->sOneColor);
else if (bCanRenderStencil)
// finally make sure all entries are 1 when the dest alpha >= 0x80 (if fba is 1, this is already the case)
ProcessStencil(curvb);
}
}
else if ((s_dwColorWrite & COLORMASK_ALPHA) && curvb.fba.fba)
ProcessFBA(curvb, pfragment->sOneColor);
if( bDestAlphaColor == 1 ) {
// need to reset the dest colors to their original counter parts
//WARN_LOG("Need to reset dest alpha color\n");
}
}
inline void AlphaSaveTarget(VB& curvb) {
#ifdef _DEBUG
return; // Do nothing
if( g_bSaveFlushedFrame & 0xf ) {
#ifdef _WIN32
CreateDirectory("frames", NULL);
#else
char* strdir="";
sprintf(strdir, "mkdir %s", "frames");
system(strdir);
#endif
char str[255];
sprintf(str, "frames/frame%.4d.tga", g_SaveFrameNum++);
if( (g_bSaveFlushedFrame & 2) ) {
//glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch to the backbuffer
//glFlush();
//SaveTexture("tex.jpg", GL_TEXTURE_RECTANGLE_NV, curvb.prndr->ptex, RW(curvb.prndr->fbw), RH(curvb.prndr->fbh));
SaveRenderTarget(str, RW(curvb.prndr->fbw), RH(curvb.prndr->fbh), 0);
}
}
#endif
}
inline void AlphaColorClamping (VB& curvb, const pixTest curtest) {
// clamp the final colors, when enabled ffx2 credits mess up
if (curvb.curprim.abe && bAlphaClamping && GetRenderFormat() != RFT_byte8 && !(g_GameSettings&GAME_NOCOLORCLAMP)) { // if !colclamp, skip
ResetAlphaVariables();
// if processing the clamping case, make sure can write to the front buffer
glDisable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDisable(GL_DEPTH_TEST);
glDepthMask(0);
glColorMask(1,1,1,0);
if( s_bWriteDepth ) {
ResetRenderTarget(1);
}
SetShaderCaller("AlphaColorClamping");
SETPIXELSHADER(ppsOne.prog);
GL_BLEND_RGB(GL_ONE, GL_ONE);
float f;
if( bAlphaClamping & 1 ) { // min
f = 0;
ZZcgSetParameter4fv(ppsOne.sOneColor, &f, "g_fOneColor");
GL_BLENDEQ_RGB(GL_MAX_EXT);
DRAW();
}
// bios shows white screen
if( bAlphaClamping & 2 ) { // max
f = 1;
ZZcgSetParameter4fv(ppsOne.sOneColor, &f, "g_fOneColor");
GL_BLENDEQ_RGB(GL_MIN_EXT);
DRAW();
}
if( !curvb.zbuf.zmsk ) {
glDepthMask(1);
if( s_bWriteDepth ) {
assert( curvb.pdepth != NULL );
curvb.pdepth->SetRenderTarget(1);
}
}
if( curvb.test.ate && USEALPHATESTING )
glEnable(GL_ALPHA_TEST);
GL_ZTEST(curtest.zte);
}
}
inline void FlushUndoFiter(u32 dwFilterOpts) {
if( dwFilterOpts ) {
// undo filter changes (binding didn't change)
if( dwFilterOpts & 1 ) glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if( dwFilterOpts & 2 ) glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
// This is the most important function! it's draw all collected info oncscreen.
void
ZeroGS::Flush(int context)
{
FUNCLOG
VB& curvb = vb[context];
const pixTest curtest = curvb.test;
if (FlushInitialTest(curvb, curtest, context))
return;
VisualBufferMessage(context);
GL_REPORT_ERRORD();
CRenderTarget* ptextarg = FlushGetTarget(curvb);
SwitchWireframeOff();
FlushDoContextJob(curvb, context);
u32 dwUsingSpecialTesting = 0;
u32 dwFilterOpts = 0;
int exactcolor = FlushGetExactcolor(curtest);
FRAGMENTSHADER* pfragment = FlushRendererStage(curvb, dwFilterOpts, ptextarg, exactcolor, context);
bool bCanRenderStencil = AlphaCanRenderStencil(curvb);
if (curtest.date || gs.pabe)
SetDestAlphaTest();
AlphaSetStencil(s_bDestAlphaTest && bCanRenderStencil);
AlphaSetDepthTest(curvb, curtest, pfragment); // Error!
SetAlphaTest(curtest);
u32 oldabe = AlphaSetupBlendTest(curvb); // Unavoidable
// needs to be before RenderAlphaTest
AlphaRenderFBA(curvb, pfragment, s_bDestAlphaTest, bCanRenderStencil);
dwUsingSpecialTesting = AlphaRenderAlpha(curvb, curtest, pfragment, exactcolor); // Unavoidable
AlphaRenderStencil(curvb, s_bDestAlphaTest, bCanRenderStencil, dwUsingSpecialTesting);
AlphaTest(curvb); // Unavoidable
AlphaPabe(curvb, pfragment, exactcolor);
AlphaFailureTestJob(curvb, curtest, pfragment, exactcolor, bCanRenderStencil, oldabe);
AlphaSpecialTesting(curvb, pfragment, dwUsingSpecialTesting, exactcolor);
AlphaDestinationTest(curvb, pfragment, s_bDestAlphaTest, bCanRenderStencil);
AlphaSaveTarget(curvb);
GL_REPORT_ERRORD();
AlphaColorClamping (curvb, curtest);
FlushUndoFiter(dwFilterOpts);
ppf += curvb.nCount+0x100000;
curvb.nCount = 0;
curvb.curprim.abe = oldabe;
SwitchWireframeOn();
GL_REPORT_ERRORD();
}
inline void ZeroGS::RenderFBA(const VB& curvb, CGparameter sOneColor)
{
// add fba to all pixels
GL_STENCILFUNC(GL_ALWAYS, STENCIL_FBA, 0xff);
glStencilMask(STENCIL_CLEAR);
glStencilOp(GL_ZERO, GL_KEEP, GL_REPLACE);
glDisable(GL_DEPTH_TEST);
glDepthMask(0);
glColorMask(0,0,0,0);
if( s_bWriteDepth )
ResetRenderTarget(1);
SetShaderCaller("RenderFBA");
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 1);
Vector v;
v.x = 1; v.y = 2; v.z = 0; v.w = 0;
ZZcgSetParameter4fv(sOneColor, v, "g_fOneColor");
DRAW();
SetAlphaTest(curvb.test);
// reset (not necessary)
GL_COLORMASK(s_dwColorWrite);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
if( !curvb.zbuf.zmsk )
{
glDepthMask(1);
assert( curvb.pdepth != NULL );
if( s_bWriteDepth )
curvb.pdepth->SetRenderTarget(1);
}
GL_ZTEST(curvb.test.zte);
}
__forceinline void ZeroGS::RenderAlphaTest(const VB& curvb, CGparameter sOneColor)
{
if( !g_bUpdateStencil ) return;
if( (curvb.test.ate) && (curvb.test.afail == 1))
glDisable(GL_ALPHA_TEST);
glDepthMask(0);
glColorMask(0,0,0,0);
if (s_bWriteDepth)
ResetRenderTarget(1);
SetShaderCaller("RenderAlphaTest");
Vector v;
v.x = 1; v.y = 2; v.z = 0; v.w = 0;
ZZcgSetParameter4fv(sOneColor, v, "g_fOneColor");
// or a 1 to the stencil buffer wherever alpha passes
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
s_stencilfunc = GL_ALWAYS;
glEnable(GL_STENCIL_TEST);
if( !s_bDestAlphaTest )
{
// clear everything
s_stencilref = 0;
glStencilMask(STENCIL_CLEAR);
glDisable(GL_ALPHA_TEST);
GL_STENCILFUNC_SET();
DRAW();
if( curvb.test.ate && curvb.test.afail != 1 && USEALPHATESTING )
glEnable(GL_ALPHA_TEST);
}
if( curvb.test.ate && curvb.test.atst>1 && curvb.test.aref > 0x80)
{
v.x = 1; v.y = 1; v.z = 0; v.w = 0;
ZZcgSetParameter4fv(sOneColor, v, "g_fOneColor");
glAlphaFunc(g_dwAlphaCmp[curvb.test.atst], AlphaReferedValue(curvb.test.aref));
}
s_stencilref = STENCIL_SPECIAL;
glStencilMask(STENCIL_SPECIAL);
GL_STENCILFUNC_SET();
glDisable(GL_DEPTH_TEST);
DRAW();
if( curvb.test.zte )
glEnable(GL_DEPTH_TEST);
GL_ALPHATEST(0);
GL_COLORMASK(s_dwColorWrite);
if( !curvb.zbuf.zmsk )
{
glDepthMask(1);
// set rt next level
if (s_bWriteDepth) curvb.pdepth->SetRenderTarget(1);
}
}
inline void ZeroGS::RenderStencil(const VB& curvb, u32 dwUsingSpecialTesting)
{
//NOTE: This stencil hack for dest alpha testing ONLY works when
// the geometry in one DrawPrimitive call does not overlap
// mark the stencil buffer for the new data's bits (mark 4 if alpha is >= 0xff)
// mark 4 if a pixel was written (so that the stencil buf can be changed with new values)
glStencilMask(STENCIL_PIXELWRITE);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
s_stencilmask = (curvb.test.date?STENCIL_ALPHABIT:0)|(dwUsingSpecialTesting?STENCIL_SPECIAL:0);
s_stencilfunc = s_stencilmask ? GL_EQUAL : GL_ALWAYS;
s_stencilref = curvb.test.date*curvb.test.datm|STENCIL_PIXELWRITE|(dwUsingSpecialTesting?STENCIL_SPECIAL:0);
GL_STENCILFUNC_SET();
}
inline void ZeroGS::ProcessStencil(const VB& curvb)
{
assert( !curvb.fba.fba );
// set new alpha bit
glStencilMask(STENCIL_ALPHABIT);
GL_STENCILFUNC(GL_EQUAL, STENCIL_PIXELWRITE, STENCIL_PIXELWRITE|STENCIL_FBA);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glDisable(GL_DEPTH_TEST);
glDepthMask(0);
glColorMask(0,0,0,0);
if (s_bWriteDepth) ResetRenderTarget(1);
GL_ALPHATEST(0);
SetShaderCaller("ProcessStencil");
SETPIXELSHADER(ppsOne.prog);
DRAW();
// process when alpha >= 0xff
GL_STENCILFUNC(GL_EQUAL, STENCIL_PIXELWRITE|STENCIL_FBA|STENCIL_ALPHABIT, STENCIL_PIXELWRITE|STENCIL_FBA);
DRAW();
// clear STENCIL_PIXELWRITE bit
glStencilMask(STENCIL_CLEAR);
GL_STENCILFUNC(GL_ALWAYS, 0, STENCIL_PIXELWRITE|STENCIL_FBA);
DRAW();
// restore state
GL_COLORMASK(s_dwColorWrite);
if( curvb.test.ate && USEALPHATESTING)
glEnable(GL_ALPHA_TEST);
if( !curvb.zbuf.zmsk ) {
glDepthMask(1);
if( s_bWriteDepth ) {
assert( curvb.pdepth != NULL );
curvb.pdepth->SetRenderTarget(1);
}
}
GL_ZTEST(curvb.test.zte);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
__forceinline void ZeroGS::ProcessFBA(const VB& curvb, CGparameter sOneColor)
{
if( (curvb.frame.fbm&0x80000000) ) return;
// add fba to all pixels that were written and alpha was less than 0xff
glStencilMask(STENCIL_ALPHABIT);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
GL_STENCILFUNC(GL_EQUAL, STENCIL_FBA|STENCIL_PIXELWRITE|STENCIL_ALPHABIT, STENCIL_PIXELWRITE|STENCIL_FBA);
glDisable(GL_DEPTH_TEST);
glDepthMask(0);
glColorMask(0,0,0,1);
if( s_bWriteDepth ) {
ResetRenderTarget(1);
}
SetShaderCaller("ProcessFBA");
// processes the pixels with ALPHA < 0x80*2
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_LEQUAL, 1);
// add 1 to dest
GL_BLEND_ALPHA(GL_ONE, GL_ONE);
GL_BLENDEQ_ALPHA(GL_FUNC_ADD);
float f = 1;
ZZcgSetParameter4fv(sOneColor, &f, "g_fOneColor");
SETPIXELSHADER(ppsOne.prog);
DRAW();
glDisable(GL_ALPHA_TEST);
// reset bits
glStencilMask(STENCIL_CLEAR);
GL_STENCILFUNC(GL_GREATER, 0, STENCIL_PIXELWRITE|STENCIL_FBA);
glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
DRAW();
if( curvb.test.atst && USEALPHATESTING) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(g_dwAlphaCmp[curvb.test.atst], AlphaReferedValue(curvb.test.aref));
}
// restore (SetAlphaVariables)
GL_BLEND_ALPHA(GL_ONE, GL_ZERO);
if(vAlphaBlendColor.y<0) GL_BLENDEQ_ALPHA(GL_FUNC_REVERSE_SUBTRACT);
// reset (not necessary)
GL_COLORMASK(s_dwColorWrite);
if( !curvb.zbuf.zmsk ) {
glDepthMask(1);
if (s_bWriteDepth) curvb.pdepth->SetRenderTarget(1);
}
GL_ZTEST(curvb.test.zte);
}
void ZeroGS::SetContextTarget(int context)
{
FUNCLOG
VB& curvb = vb[context];
GL_REPORT_ERRORD();
if( curvb.prndr == NULL )
curvb.prndr = s_RTs.GetTarg(curvb.frame, 0, get_maxheight(curvb.gsfb.fbp, curvb.gsfb.fbw, curvb.gsfb.psm));
// make sure targets are valid
if( curvb.pdepth == NULL ) {
frameInfo f;
f.fbp = curvb.zbuf.zbp;
f.fbw = curvb.frame.fbw;
f.fbh = curvb.prndr->fbh;
f.psm = curvb.zbuf.psm;
f.fbm = 0;
curvb.pdepth = (CDepthTarget*)s_DepthRTs.GetTarg(f, CRenderTargetMngr::TO_DepthBuffer|CRenderTargetMngr::TO_StrictHeight|
(curvb.zbuf.zmsk?CRenderTargetMngr::TO_Virtual:0), get_maxheight(curvb.zbuf.zbp, curvb.gsfb.fbw, 0));
}
assert( curvb.prndr != NULL && curvb.pdepth != NULL );
assert( curvb.pdepth->fbh == curvb.prndr->fbh );
if( curvb.pdepth->status & CRenderTarget::TS_Virtual) {
if( !curvb.zbuf.zmsk ) {
CRenderTarget* ptemp = s_DepthRTs.Promote(curvb.pdepth->fbp|(curvb.pdepth->fbw<<16));
assert( ptemp == curvb.pdepth );
}
else
curvb.pdepth->status &= ~CRenderTarget::TS_NeedUpdate;
}
bool bSetTarg = 1;
if( curvb.pdepth->status & CRenderTarget::TS_NeedUpdate ) {
assert( !(curvb.pdepth->status & CRenderTarget::TS_Virtual) );
// don't update if virtual
curvb.pdepth->Update(context, curvb.prndr);
bSetTarg = 0;
}
GL_REPORT_ERRORD();
if( curvb.prndr->status & CRenderTarget::TS_NeedUpdate ) {
/* if(bSetTarg) {
* printf ( " Here\n ");
* if(s_bWriteDepth) {
* curvb.pdepth->SetRenderTarget(1);
* curvb.pdepth->SetDepthStencilSurface();
* }
* else
* curvb.pdepth->SetDepthStencilSurface();
* }*/
curvb.prndr->Update(context, curvb.pdepth);
}
else {
//if( (vb[0].prndr != vb[1].prndr && vb[!context].bVarsSetTarg) || !vb[context].bVarsSetTarg )
curvb.prndr->SetRenderTarget(0);
//if( bSetTarg && ((vb[0].pdepth != vb[1].pdepth && vb[!context].bVarsSetTarg) || !vb[context].bVarsSetTarg) )
curvb.pdepth->SetDepthStencilSurface();
if (conf.mrtdepth && ZeroGS::IsWriteDepth()) curvb.pdepth->SetRenderTarget(1);
if (s_ptexCurSet[0] == curvb.prndr->ptex) s_ptexCurSet[0] = 0;
if (s_ptexCurSet[1] == curvb.prndr->ptex) s_ptexCurSet[1] = 0;
curvb.prndr->SetViewport();
}
curvb.prndr->SetTarget(curvb.frame.fbp, curvb.scissor, context);
if( (curvb.zbuf.zbp-curvb.pdepth->fbp) != (curvb.frame.fbp - curvb.prndr->fbp) && curvb.test.zte )
WARN_LOG("frame and zbuf not aligned\n");
curvb.bVarsSetTarg = TRUE;
if( vb[!context].prndr != curvb.prndr ) vb[!context].bVarsSetTarg = FALSE;
assert( !(curvb.prndr->status&CRenderTarget::TS_NeedUpdate) );
assert( curvb.pdepth == NULL || !(curvb.pdepth->status&CRenderTarget::TS_NeedUpdate) );
GL_REPORT_ERRORD();
}
void ZeroGS::SetTexInt (int context, FRAGMENTSHADER* pfragment, int settexint ) {
FUNCLOG
if (settexint) {
tex0Info& tex0 = vb[context].tex0;
CMemoryTarget* pmemtarg = g_MemTargs.GetMemoryTarget(tex0, 1);
if (vb[context].bVarsTexSync) {
if (vb[context].pmemtarg != pmemtarg) {
SetTexVariablesInt(context, GetTexFilter(vb[context].tex1), tex0, pmemtarg, pfragment, s_bForceTexFlush);
vb[context].bVarsTexSync = TRUE;
}
}
else {
SetTexVariablesInt(context, GetTexFilter(vb[context].tex1), tex0, pmemtarg, pfragment, s_bForceTexFlush);
vb[context].bVarsTexSync = TRUE;
INC_TEXVARS();
}
}
else {
vb[context].bVarsTexSync = FALSE;
}
}
// clamp relies on texture width
void ZeroGS::SetTexClamping(int context, FRAGMENTSHADER* pfragment ) {
FUNCLOG
SetShaderCaller("SetTexClamping");
clampInfo* pclamp = &ZeroGS::vb[context].clamp;
Vector v, v2;
v.x = v.y = 0;
u32* ptex = ZeroGS::vb[context].ptexClamp;
ptex[0] = ptex[1] = 0;
float fw = ZeroGS::vb[context].tex0.tw ;
float fh = ZeroGS::vb[context].tex0.th ;
switch(pclamp->wms) {
case 0:
v2.x = -1e10; v2.z = 1e10;
break;
case 1: // pclamp
// suikoden5 movie text
v2.x = 0; v2.z = 1-0.5f/fw;
break;
case 2: // reg pclamp
v2.x = (pclamp->minu+0.5f)/fw; v2.z = (pclamp->maxu-0.5f)/fw;
break;
case 3: // region rep x
v.x = 0.9999f;
v.z = fw;
v2.x = (float)GPU_TEXMASKWIDTH / fw;
v2.z = pclamp->maxu / fw;
int correctMinu = pclamp->minu & (~pclamp->maxu); // (A && B) || C == (A && (B && !C)) + C
if( correctMinu != g_PrevBitwiseTexX ) {
g_PrevBitwiseTexX = correctMinu;
ptex[0] = ZeroGS::s_BitwiseTextures.GetTex(correctMinu, 0);
}
break;
}
switch(pclamp->wmt) {
case 0:
v2.y = -1e10; v2.w = 1e10;
break;
case 1: // pclamp
// suikoden5 movie text
v2.y = 0; v2.w = 1-0.5f/fh;
break;
case 2: // reg pclamp
v2.y = (pclamp->minv+0.5f)/fh; v2.w = (pclamp->maxv-0.5f)/fh;
break;
case 3: // region rep y
v.y = 0.9999f;
v.w = fh;
v2.y = (float)GPU_TEXMASKWIDTH / fh;
v2.w = pclamp->maxv / fh;
int correctMinv = pclamp->minv & (~pclamp->maxv); // (A && B) || C == (A && (B && !C)) + C
if( correctMinv != g_PrevBitwiseTexY ) {
g_PrevBitwiseTexY = correctMinv;
ptex[1] = ZeroGS::s_BitwiseTextures.GetTex(correctMinv, ptex[0]);
}
break;
}
if( pfragment->fTexWrapMode != 0 )
ZZcgSetParameter4fv(pfragment->fTexWrapMode, v, "g_fTexWrapMode");
if( pfragment->fClampExts != 0 )
ZZcgSetParameter4fv(pfragment->fClampExts, v2, "g_fClampExts");
}
// Fixme should be in Vector lib
inline bool equal_vectors( Vector a, Vector b){
if ( abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w) < 0.01 )
return true;
else
return false;
}
int CheckTexArray[4][2][2][2] = {{{{0,}}}};
void ZeroGS::SetTexVariables(int context, FRAGMENTSHADER* pfragment ) {
FUNCLOG
if (!vb[context].curprim.tme) return;
assert( !vb[context].bNeedTexCheck );
Vector v, v2;
tex0Info& tex0 = vb[context].tex0;
float fw = (float)tex0.tw;
float fh = (float)tex0.th;
if( !vb[context].bTexConstsSync ) {
SetShaderCaller("SetTexVariables");
// alpha and texture highlighting
Vector valpha, valpha2 ;
// if clut, use the frame format
int psm = GetTexCPSM(tex0);
// printf ( "A %d psm, is-clut %d. cpsm %d | %d %d\n", psm, PSMT_ISCLUT(psm), tex0.cpsm, tex0.tfx, tex0.tcc );
Vector vblack;
vblack.x = vblack.y = vblack.z = vblack.w = 10;
/* tcc -- Tecture Color Component 0=RGB, 1=RGBA + use Alpha from TEXA reg when not in PSM
* tfx -- Texture Function (0=modulate, 1=decal, 2=hilight, 3=hilight2)
*
* valpha2 = 0 0 2 1 0 0 2 1
* 1 0 0 0 1 1 0 0
* 0 0 2 0 0 1 2 0
* 0 0 2 0 0 1 2 0
*
* 0 1,!nNeed 1, psm=2, 10 1, psm=1
* valpha = 0 0 0 1 0 2 0 0 2ta0 2ta1-2ta0 0 0 2ta0 0 0 0
* 0 0 0 1 0 1 0 0 ta0 ta1-ta0 0 0 ta0 0 0 0
* 0 0 1 1 0 1 1 1 1 1 ta0 0 1 1
* 0 0 1 1 0 1 1 0 1 0 ta0 0 1 0
*/
valpha2.x = ( tex0.tfx == 1 ) ;
valpha2.y = ( tex0.tcc == 1 ) && ( tex0.tfx != 0 ) ;
valpha2.z = ( tex0.tfx != 1 ) * 2 ;
valpha2.w = ( tex0.tfx == 0 ) ;
if ( tex0.tcc == 0 || !nNeedAlpha(psm) ) {
valpha.x = 0 ;
valpha.y = ( !!tex0.tcc ) * ( 1 + ( tex0.tfx == 0) ) ; }
else {
valpha.x = ( gs.texa.fta[0] ) * ( 1 + ( tex0.tfx == 0) ) ;
valpha.y = ( gs.texa.fta[psm!=1] - gs.texa.fta[0] ) * ( 1 + ( tex0.tfx == 0) ) ;
}
valpha.z = ( tex0.tfx >= 3 ) ;
valpha.w = ( tex0.tcc == 0 ) || ( tex0.tcc == 1 && tex0.tfx == 2 ) ;
if( tex0.tcc && gs.texa.aem && psm == PSMCT24 )
vblack.w = 0;
/*
// Test, old code.
Vector valpha3, valpha4;
switch(tex0.tfx) {
case 0:
valpha3.z = 0; valpha3.w = 0;
valpha4.x = 0; valpha4.y = 0;
valpha4.z = 2; valpha4.w = 1;
break;
case 1:
valpha3.z = 0; valpha3.w = 1;
valpha4.x = 1; valpha4.y = 0;
valpha4.z = 0; valpha4.w = 0;
break;
case 2:
valpha3.z = 1; valpha3.w = 1.0f;
valpha4.x = 0; valpha4.y = tex0.tcc ? 1.0f : 0.0f;
valpha4.z = 2; valpha4.w = 0;
break;
case 3:
valpha3.z = 1; valpha3.w = tex0.tcc ? 0.0f : 1.0f;
valpha4.x = 0; valpha4.y = tex0.tcc ? 1.0f : 0.0f;
valpha4.z = 2; valpha4.w = 0;
break;
}
if( tex0.tcc ) {
if( tex0.tfx == 1 ) {
//mode.x = 10;
valpha3.z = 0; valpha3.w = 0;
valpha4.x = 1; valpha4.y = 1;
valpha4.z = 0; valpha4.w = 0;
}
if( nNeedAlpha(psm) ) {
if( tex0.tfx == 0 ) {
// make sure alpha is mult by two when the output is Cv = Ct*Cf
valpha3.x = 2*gs.texa.fta[0];
// if 24bit, always choose ta[0]
valpha3.y = 2*gs.texa.fta[psm != 1];
valpha3.y -= valpha.x;
}
else {
valpha3.x = gs.texa.fta[0];
// if 24bit, always choose ta[0]
valpha3.y = gs.texa.fta[psm != 1];
valpha3.y -= valpha.x;
}
}
else {
if( tex0.tfx == 0 ) {
valpha3.x = 0;
valpha3.y = 2;
}
else {
valpha3.x = 0;
valpha3.y = 1;
}
}
}
else {
// reset alpha to color
valpha3.x = valpha3.y = 0;
valpha3.w = 1;
}
if ( equal_vectors(valpha, valpha3) && equal_vectors(valpha2, valpha4) ) {
if (CheckTexArray[tex0.tfx][tex0.tcc][psm!=1][nNeedAlpha(psm)] == 0) {
printf ( "Good issue %d %d %d %d\n", tex0.tfx, tex0.tcc, psm, nNeedAlpha(psm) );
CheckTexArray[tex0.tfx][tex0.tcc][psm!=1][nNeedAlpha(psm) ] = 1;
}
}
else if (CheckTexArray[tex0.tfx][tex0.tcc][psm!=1][nNeedAlpha(psm)] == -1) {
printf ("Bad array, %d %d %d %d\n\tolf valpha %f, %f, %f, %f : valpha2 %f %f %f %f\n\tnew valpha %f, %f, %f, %f : valpha2 %f %f %f %f\n",
tex0.tfx, tex0.tcc, psm, nNeedAlpha(psm),
valpha3.x, valpha3.y, valpha3.z, valpha3.w, valpha4.x, valpha4.y, valpha4.z, valpha4.w,
valpha.x, valpha.y, valpha.z, valpha.w, valpha2.x, valpha2.y, valpha2.z, valpha2.w);
CheckTexArray[tex0.tfx][tex0.tcc][psm!=1][nNeedAlpha(psm)] = -1 ;
}
// Test;*/
ZZcgSetParameter4fv(pfragment->fTexAlpha, valpha, "g_fTexAlpha");
ZZcgSetParameter4fv(pfragment->fTexAlpha2, valpha2, "g_fTexAlpha2");
if(tex0.tcc && gs.texa.aem && nNeedAlpha(psm))
ZZcgSetParameter4fv(pfragment->fTestBlack, vblack, "g_fTestBlack");
SetTexClamping(context, pfragment);
vb[context].bTexConstsSync = TRUE;
}
if(s_bTexFlush ) {
if( PSMT_ISCLUT(tex0.psm) )
texClutWrite(context);
else
s_bTexFlush = FALSE;
}
}
void ZeroGS::SetTexVariablesInt(int context, int bilinear, const tex0Info& tex0, CMemoryTarget* pmemtarg, FRAGMENTSHADER* pfragment, int force) {
FUNCLOG
Vector v;
assert( pmemtarg != NULL && pfragment != NULL && pmemtarg->ptex != NULL);
if (pmemtarg == NULL || pfragment == NULL || pmemtarg->ptex == NULL )
{
printf ("SetTexVariablesInt error\n");
return;
}
SetShaderCaller("SetTexVariablesInt");
float fw = (float)tex0.tw;
float fh = (float)tex0.th;
bool bUseBilinear = bilinear > 1 || (bilinear && conf.bilinear);
if( bUseBilinear ) {
v.x = (float)fw;
v.y = (float)fh;
v.z = 1.0f / (float)fw;
v.w = 1.0f / (float)fh;
if (pfragment->fRealTexDims)
ZZcgSetParameter4fv(pfragment->fRealTexDims, v, "g_fRealTexDims");
else
ZZcgSetParameter4fv(cgGetNamedParameter(pfragment->prog,"g_fRealTexDims"),v, "g_fRealTexDims");
}
if( m_Blocks[tex0.psm].bpp == 0 ) {
ERROR_LOG("Undefined tex psm 0x%x!\n", tex0.psm);
return;
}
const BLOCK& b = m_Blocks[tex0.psm];
float fbw = (float)tex0.tbw;
Vector vTexDims;
vTexDims.x = b.vTexDims.x * (fw);
vTexDims.y = b.vTexDims.y * (fh);
vTexDims.z = (float)BLOCK_TEXWIDTH * (0.002f / 64.0f + 0.01f / 128.0f);
vTexDims.w = (float)BLOCK_TEXHEIGHT * 0.1f / 512.0f;
if (bUseBilinear) {
vTexDims.x *= 1/128.0f;
vTexDims.y *= 1/512.0f;
vTexDims.z *= 1/128.0f;
vTexDims.w *= 1/512.0f;
}
float g_fitexwidth = g_fiGPU_TEXWIDTH/(float)pmemtarg->widthmult;
float g_texwidth = GPU_TEXWIDTH*(float)pmemtarg->widthmult;
float fpage = tex0.tbp0*(64.0f*g_fitexwidth);// + 0.05f * g_fitexwidth;
float fpageint = floorf(fpage);
int starttbp = (int)fpage;
// 2048 is number of words to span one page
//float fblockstride = (2048.0f /(float)(g_texwidth*BLOCK_TEXWIDTH)) * b.vTexDims.x * fbw;
float fblockstride = (2048.0f /(float)(GPU_TEXWIDTH*(float)pmemtarg->widthmult*BLOCK_TEXWIDTH)) * b.vTexDims.x * fbw;
assert( fblockstride >= 1.0f );
v.x = (float)(2048 * g_fitexwidth);
v.y = fblockstride;
v.z = g_fBlockMult/(float)pmemtarg->widthmult;
v.w = fpage-fpageint ;
if( g_fBlockMult > 1 ) {
// make sure to divide by mult (since the G16R16 texture loses info)
v.z *= b.bpp * (1/32.0f);
}
ZZcgSetParameter4fv(pfragment->fTexDims, vTexDims, "g_fTexDims");
// ZZcgSetParameter4fv(pfragment->fTexBlock, b.vTexBlock, "g_fTexBlock"); // I change it, and it's working. Seems casting from Vector to float[4] is ok.
ZZcgSetParameter4fv(pfragment->fTexBlock, &b.vTexBlock.x, "g_fTexBlock");
ZZcgSetParameter4fv(pfragment->fTexOffset, v, "g_fTexOffset");
// get hardware texture dims
int texheight = (pmemtarg->realheight+pmemtarg->widthmult-1)/pmemtarg->widthmult;
int texwidth = GPU_TEXWIDTH*pmemtarg->widthmult*pmemtarg->channels;
v.y = 1.0f;
v.x = (fpageint-(float)pmemtarg->realy/(float)pmemtarg->widthmult+0.5f);//*v.y;
v.z = (float)texwidth;
/* if( !(g_nPixelShaderVer & SHADER_ACCURATE) || bUseBilinear ) {
if (tex0.psm == PSMT4 )
v.w = 0.0f;
else
v.w = 0.25f;
}
else
v.w = 0.5f;*/
v.w = 0.5f;
ZZcgSetParameter4fv(pfragment->fPageOffset, v, "g_fPageOffset");
if( force )
s_ptexCurSet[context] = pmemtarg->ptex->tex;
else
s_ptexNextSet[context] = pmemtarg->ptex->tex;
vb[context].pmemtarg = pmemtarg;
vb[context].bVarsTexSync = FALSE;
}
#define SET_ALPHA_COLOR_FACTOR(sign) \
{ \
switch(a.c) \
{ \
case 0: \
vAlphaBlendColor.y = (sign) ? 2.0f*255.0f/256.0f : -2.0f*255.0f/256.0f; \
s_srcalpha = GL_ONE; \
s_alphaeq = (sign) ? GL_FUNC_ADD : GL_FUNC_REVERSE_SUBTRACT; \
break; \
\
case 1: \
/* if in 24 bit mode, dest alpha should be one */ \
switch(PSMT_BITMODE(vb[icurctx].prndr->psm)) \
{ \
case 0: \
bDestAlphaColor = (a.d!=2)&&((a.a==a.d)||(a.b==a.d)); \
break; \
\
case 1: \
/* dest alpha should be one */ \
bDestAlphaColor = 2; \
break; \
/* default: 16bit surface, so returned alpha is ok */ \
} \
break; \
\
case 2: \
bNeedBlendFactorInAlpha = 1; /* should disable alpha channel writing */ \
vAlphaBlendColor.y = 0; \
vAlphaBlendColor.w = (sign) ? (float)a.fix * (2.0f/255.0f) : (float)a.fix * (-2.0f/255.0f); \
usec = 0; /* change so that alpha comes from source*/ \
break; \
} \
} \
//if( a.fix <= 0x80 ) { \
// dwTemp = (a.fix*2)>255?255:(a.fix*2); \
// dwTemp = dwTemp|(dwTemp<<8)|(dwTemp<<16)|0x80000000; \
// printf("bfactor: %8.8x\n", dwTemp); \
// glBlendColorEXT(dwTemp); \
// } \
// else { \
void ZeroGS::ResetAlphaVariables() {
FUNCLOG
}
inline void ZeroGS::NeedFactor( int w ) {
if (bDestAlphaColor == 2){
bNeedBlendFactorInAlpha = (w+1) ? 1 : 0;
vAlphaBlendColor.y = 0;
vAlphaBlendColor.w = (float)w;
}
}
static int CheckArray[48][2] = {{0,}};
void ZeroGS::SetAlphaVariables(const alphaInfo& a)
{
FUNCLOG
bool alphaenable = true;
// TODO: negative color when not clamping turns to positive???
g_vars._bAlphaState = 0; // set all to zero
bNeedBlendFactorInAlpha = 0;
b2XAlphaTest = 1;
u32 dwTemp = 0xffffffff;
bDestAlphaColor = 0;
// default
s_srcalpha = GL_ONE;
s_dstalpha = GL_ZERO;
s_alphaeq = GL_FUNC_ADD;
s_rgbeq = 1;
s_alphaInfo = a;
vAlphaBlendColor = Vector(1,2*255.0f/256.0f,0,0);
u32 usec = a.c;
/*
* Alpha table
* a + b + d
* S D
* 0 a -a 1 | 0 0 0
* 1 0 0 0 | a -a 1
* 2 0 0 0 | 0 0 0
*
* d = 0 Cs
* a b 0 Cs 1 Cd 2 0
* | |
* 0 000: a+-a+ 1 | 0+ 0+ 0 = 1 | 010: a+ 0+ 1 | 0+-a+ 0 = 1-(-a)(+)(-a) | 020: a+ 0+ 1 | 0+ 0+ 0 = 1-(-a) (+) 0
* 1 100: 0+-a+ 1 | a+ 0+ 0 = 1-a (+) a | 110: 0+ 0+ 1 | a+-a+ 0 = 1 | 120: 0+ 0+ 1 | a+ 0+ 0 = 1 (+) a
* 2 200: 0+-a+ 1 | 0+ 0+ 0 = 1-a (+) 0 | 210: 0+ 0+ 1 | 0+-a+ 0 = 1 (-) a | 220: 0+ 0+ 1 | 0+ 0+ 0 = 1
*
* d = 1 Cd
* 0 | 1 | 2
* 0 001: a+-a+ 0 | 0+ 0+ 1 = 0 (+) 1 | 011: a+ 0+ 0 | 0+-a+ 1 = a (+) 1-a | 021: a+ 0+ 0 | 0+ 0+ 1 = a (+) 1
* 1 101: 0+-a+ 0 | a+ 0+ 1 = (-a)(+) 1-(-a) | 111: 0+ 0+ 0 | a+-a+ 1 = 0 (+) 1 | 121: 0+ 0+ 0 | a+ 0+ 1 = 0 (+) 1-(-a)
* 2 201: 0+-a+ 0 | 0+ 0+ 1 = a (R-)1 | 211: 0+ 0+ 0 | 0+-a+ 1 = 0 (+) 1-a | 221: 0+ 0+ 0 | 0+ 0+ 1 = 0 (+) 1
*
* d = 2 0
* 0 | 1 | 2
* 0 002: a+-a+ 0 | 0+ 0+ 0 = 0 | 012: a+ 0+ 0 | 0+-a+ 0 = a (-) a | 022: a+ 0+ 0 | 0+ 0+ 0 = a (+) 0
* 1 102: 0+-a+ 0 | a+ 0+ 0 = a (R-) a | 112: 0+ 0+ 0 | a+-a+ 0 = 0 | 122: 0+ 0+ 0 | a+ 0+ 0 = 0 (+) a
* 2 202: 0+-a+ 0 | 0+ 0+ 0 = a (R-) 0 | 212: 0+ 0+ 0 | 0+-a+ 0 = 0 (-) a | 222: 0+ 0+ 0 | 0+ 0+ 0 = 0
*
* Formulae is: (a-b) * (c /32) + d
* 0 1 2
* a Cs Cd 0
* b Cs Cd 0
* c As Ad ALPHA.FIX
* d Cs Cd 0
*
* We want to emulate Cs * F1(alpha) + Cd * F2(alpha) by OpenGl blending: (Cs * Ss (+,-,R-) Cd * Sd)
* SET_ALPHA_COLOR_FACTOR(sign) set Set A (as As>>7, Ad>>7 or FIX>>7) with sign.
* So we could use 1+a as one_minus_alpha and -a as alpha.
*
*/
int code = ( a.a * 16 ) + ( a.b * 4 ) + a.d ;
#define one_minus_alpha (bDestAlphaColor == 2) ? GL_ONE_MINUS_SRC_ALPHA : blendinvalpha[usec]
#define alpha (bDestAlphaColor == 2) ? GL_SRC_ALPHA : blendalpha[usec]
#define one (bDestAlphaColor == 2) ? GL_ONE : blendalpha[usec]
#define zero (bDestAlphaColor == 2) ? GL_ZERO : blendinvalpha[usec]
switch (code)
{
case 0: // 000 // Cs -- nothing changed
case 20: // 110 = 16+4=20 // Cs
case 40: { // 220 = 32+8=40 // Cs
alphaenable = false;
break;
}
case 2: //002 // 0 -- should be zero
case 22: //112 // 0
case 42: { //222 = 32+8+2 =42 // 0
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = GL_ZERO;
break;
}
case 1: //001 // Cd -- Should be destination alpha
case 21: //111, // Cd -- 0*Source + 1*Desrinarion
case 41: { //221 = 32+8+1=41 // Cd --
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = GL_ONE;
break;
}
case 4: { // 010 // (Cs-Cd)*A+Cs = Cs * (A + 1) - Cd * A
bAlphaClamping = 3;
SET_ALPHA_COLOR_FACTOR(0); // a = -A
s_rgbeq = GL_FUNC_ADD; // Cs*(1-a)+Cd*a
s_srcrgb = one_minus_alpha ;
s_dstrgb = alpha;
NeedFactor(-1);
break;
}
case 5: { // 011 // (Cs-Cd)*A+Cs = Cs * A + Cd * (1-A)
bAlphaClamping = 3; // all testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = alpha;
s_dstrgb = one_minus_alpha;
NeedFactor(1);
break;
}
case 6: { //012 // (Cs-Cd)*FIX
bAlphaClamping = 3;
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_SUBTRACT;
s_srcrgb = alpha;
s_dstrgb = alpha;
break;
}
case 8: { //020 // Cs*A+Cs = Cs * (1+A)
bAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(0); // Zeyflitz change this! a = -A
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = one_minus_alpha; // Cs*(1-a).
s_dstrgb = GL_ZERO;
// NeedFactor(1);
break;
}
case 9: { //021 // Cs*A+Cd
bAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = alpha; // ZZ change it to.
s_dstrgb = GL_ONE;
break;
}
case 10: { //022 // Cs*A
bAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = alpha;
s_dstrgb = GL_ZERO;
break;
}
case 16: { //100
bAlphaClamping = 3;
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = one_minus_alpha;
s_dstrgb = alpha;
NeedFactor(1);
break;
}
case 17: { //101
bAlphaClamping = 3; // all testing
SET_ALPHA_COLOR_FACTOR(0);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = alpha;
s_dstrgb = one_minus_alpha;
NeedFactor(-1);
break;
}
case 18: { //102
bAlphaClamping = 3;
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_REVERSE_SUBTRACT;
s_srcrgb = alpha;
s_dstrgb = alpha;
break;
}
case 24: { //120 = 16+8
bAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ONE;
s_dstrgb = alpha;
break;
}
case 25: { //121 // Cd*(1+A)
bAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(0);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = one_minus_alpha;
// NeedFactor(-1);
break;
}
case 26: { //122
bAlphaClamping = 2;
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = alpha;
break;
}
case 32: {// 200 = 32
bAlphaClamping = 1; // min testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = one_minus_alpha;
s_dstrgb = GL_ZERO;
break;
}
case 33: {//201 // -Cs*A + Cd
bAlphaClamping = 1; // min testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_REVERSE_SUBTRACT;
s_srcrgb = alpha;
s_dstrgb = GL_ONE;
break;
}
case 34: //202
case 38: {//212
bAlphaClamping = 1; // min testing -- negative values
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = GL_ZERO;
break;
}
case 36: {//210
bAlphaClamping = 1; // min testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_SUBTRACT;
s_srcrgb = GL_ONE;
s_dstrgb = alpha;
break;
}
case 37: {//211
bAlphaClamping = 1; // min testing
SET_ALPHA_COLOR_FACTOR(1);
s_rgbeq = GL_FUNC_ADD;
s_srcrgb = GL_ZERO;
s_dstrgb = one_minus_alpha;
break;
}
default: {
ERROR_LOG ( "Bad alpha code %d | %d %d %d\n", code, a.a, a.b, a.d );
}
}
/*
int t_rgbeq = GL_FUNC_ADD;
int t_srcrgb = GL_ONE;
int t_dstrgb = GL_ZERO;
int tAlphaClamping = 0;
if( a.a == a.b )
{ // just d remains
if( a.d == 0 ) {}
else
{
t_dstrgb = a.d == 1 ? GL_ONE : GL_ZERO;
t_srcrgb = GL_ZERO;
t_rgbeq = GL_FUNC_ADD; //a) (001) (111) (221) b) (002) (112) (222)
}
goto EndSetAlpha;
}
else if( a.d == 2 )
{ // zero
if( a.a == 2 )
{
// zero all color
t_srcrgb = GL_ZERO;
t_dstrgb = GL_ZERO;
goto EndSetAlpha; // (202) (212)
}
else if( a.b == 2 )
{
//b2XAlphaTest = 1; // a) (022) // b) (122)
SET_ALPHA_COLOR_FACTOR(1);
if( bDestAlphaColor == 2 )
{
t_rgbeq = GL_FUNC_ADD;
t_srcrgb = a.a == 0 ? GL_ONE : GL_ZERO;
t_dstrgb = a.a == 0 ? GL_ZERO : GL_ONE;
}
else
{
tAlphaClamping = 2;
t_rgbeq = GL_FUNC_ADD;
t_srcrgb = a.a == 0 ? blendalpha[usec] : GL_ZERO;
t_dstrgb = a.a == 0 ? GL_ZERO : blendalpha[usec];
}
goto EndSetAlpha;
}
// nothing is zero, so must do some real blending //b2XAlphaTest = 1; //a) (012) //b) (102)
tAlphaClamping = 3;
SET_ALPHA_COLOR_FACTOR(1);
t_rgbeq = a.a == 0 ? GL_FUNC_SUBTRACT : GL_FUNC_REVERSE_SUBTRACT;
t_srcrgb = bDestAlphaColor == 2 ? GL_ONE : blendalpha[usec];
t_dstrgb = bDestAlphaColor == 2 ? GL_ONE : blendalpha[usec];
}
else if( a.a == 2 )
{ // zero
//b2XAlphaTest = 1;
tAlphaClamping = 1; // min testing
SET_ALPHA_COLOR_FACTOR(1);
if( a.b == a.d )
{
// can get away with 1-A
// a.a == a.d == 2!! (200) (211)
t_rgbeq = GL_FUNC_ADD;
t_srcrgb = (a.b == 0 && bDestAlphaColor != 2) ? blendinvalpha[usec] : GL_ZERO;
t_dstrgb = (a.b == 0 || bDestAlphaColor == 2) ? GL_ZERO : blendinvalpha[usec];
}
else
{
// a) (201) b)(210)
t_rgbeq = a.b==0 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_SUBTRACT;
t_srcrgb = (a.b == 0 && bDestAlphaColor != 2) ? blendalpha[usec] : GL_ONE;
t_dstrgb = (a.b == 0 || bDestAlphaColor == 2 ) ? GL_ONE : blendalpha[usec];
}
}
else if( a.b == 2 )
{
tAlphaClamping = 2; // max testing
SET_ALPHA_COLOR_FACTOR(a.a!=a.d);
if( a.a == a.d )
{
// can get away with 1+A, but need to set alpha to negative
// a)(020)
// b)(121)
t_rgbeq = GL_FUNC_ADD;
if( bDestAlphaColor == 2 )
{
t_srcrgb = (a.a == 0) ? GL_ONE_MINUS_SRC_ALPHA : GL_ZERO;
t_dstrgb = (a.a == 0) ? GL_ZERO : GL_ONE_MINUS_SRC_ALPHA;
}
else
{
t_srcrgb = a.a == 0 ? blendinvalpha[usec] : GL_ZERO;
t_dstrgb = a.a == 0 ? GL_ZERO : blendinvalpha[usec];
}
}
else
{
//a)(021) //b)(120) //b2XAlphaTest = 1;
t_rgbeq = GL_FUNC_ADD;
t_srcrgb = (a.a == 0 && bDestAlphaColor != 2) ? blendalpha[usec] : GL_ONE;
t_dstrgb = (a.a == 0 || bDestAlphaColor == 2) ? GL_ONE : blendalpha[usec];
}
}
else
{
// all 3 components are valid!
tAlphaClamping = 3; // all testing
SET_ALPHA_COLOR_FACTOR(a.a!=a.d);
if( a.a == a.d )
{
// can get away with 1+A, but need to set alpha to negative // a) 010, // b) 101
t_rgbeq = GL_FUNC_ADD;
if( bDestAlphaColor == 2 )
{
// all ones
t_srcrgb = a.a == 0 ? GL_ONE_MINUS_SRC_ALPHA : GL_SRC_ALPHA;
t_dstrgb = a.a == 0 ? GL_SRC_ALPHA : GL_ONE_MINUS_SRC_ALPHA;
}
else
{
t_srcrgb = a.a == 0 ? blendinvalpha[usec] : blendalpha[usec];
t_dstrgb = a.a == 0 ? blendalpha[usec] : blendinvalpha[usec];
}
}
else
{
t_rgbeq = GL_FUNC_ADD; // a) 011 // b) 100 //
if( bDestAlphaColor == 2 )
{
// all ones
t_srcrgb = a.a != 0 ? GL_ONE_MINUS_SRC_ALPHA : GL_SRC_ALPHA;
t_dstrgb = a.a != 0 ? GL_SRC_ALPHA : GL_ONE_MINUS_SRC_ALPHA;
}
else
{
//b2XAlphaTest = 1;
t_srcrgb = a.a != 0 ? blendinvalpha[usec] : blendalpha[usec];
t_dstrgb = a.a != 0 ? blendalpha[usec] : blendinvalpha[usec];
}
}
}
EndSetAlpha:
if ( alphaenable && (t_rgbeq != s_rgbeq || s_srcrgb != t_srcrgb || t_dstrgb != s_dstrgb || tAlphaClamping != bAlphaClamping)) {
if (CheckArray[code][(bDestAlphaColor==2)] != -1) {
printf ( "A code %d, 0x%x, 0x%x, 0x%x, 0x%x %d\n", code, alpha, one_minus_alpha, one, zero, bDestAlphaColor );
printf ( " Difference %d %d %d %d | 0x%x 0x%x | 0x%x 0x%x | 0x%x 0x%x | %d %d\n",
code, a.a, a.b, a.d,
t_rgbeq, s_rgbeq, t_srcrgb, s_srcrgb, t_dstrgb, s_dstrgb, tAlphaClamping, bAlphaClamping);
CheckArray[code][(bDestAlphaColor==2)] = -1;
}
}
else
if (CheckArray[code][(bDestAlphaColor==2)] == 0){
printf ( "Add good code %d %d, psm %d destA %d\n", code, a.c, vb[icurctx].prndr->psm, bDestAlphaColor);
CheckArray[code][(bDestAlphaColor==2)] = 1;
}*/
if( alphaenable ) {
zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha);
zgsBlendEquationSeparateEXT(s_rgbeq, s_alphaeq);
glEnable(GL_BLEND); // always set
}
else
glDisable(GL_BLEND);
INC_ALPHAVARS();
}
void ZeroGS::SetWriteDepth() {
FUNCLOG
if( conf.mrtdepth ) {
s_bWriteDepth = TRUE;
s_nWriteDepthCount = 4;
}
}
bool ZeroGS::IsWriteDepth() {
FUNCLOG
return s_bWriteDepth;
}
bool ZeroGS::IsWriteDestAlphaTest() {
FUNCLOG
return s_bDestAlphaTest;
}
void ZeroGS::SetDestAlphaTest() {
FUNCLOG
s_bDestAlphaTest = TRUE;
s_nWriteDestAlphaTest = 4;
}
void ZeroGS::SetTexFlush() {
FUNCLOG
s_bTexFlush = TRUE;
// if( PSMT_ISCLUT(vb[0].tex0.psm) )
// texClutWrite(0);
// if( PSMT_ISCLUT(vb[1].tex0.psm) )
// texClutWrite(1);
if( !s_bForceTexFlush )
{
if (s_ptexCurSet[0] != s_ptexNextSet[0]) s_ptexCurSet[0] = s_ptexNextSet[0];
if (s_ptexCurSet[1] != s_ptexNextSet[1]) s_ptexCurSet[1] = s_ptexNextSet[1];
}
}