//#version 420 Keep it for text editor detection

//  ZZ Open GL graphics plugin
//  Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com, gregory.hainaut@gmail.com
//  Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
//
//  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

// divides by z for every pixel, instead of in vertex shader
// fixes kh textures

#extension GL_ARB_texture_rectangle: require
#extension GL_ARB_shading_language_420pack: require
#extension GL_ARB_separate_shader_objects : require


//#define TEST_AEM // tests AEM for black pixels
//#define REGION_REPEAT // set if texture wrapping mode is region repeat
//#define WRITE_DEPTH // set if depth is also written in a MRT
//#define ACCURATE_DECOMPRESSION // set for less capable hardware ATI Radeon 9000 series
//#define EXACT_COLOR	// make sure the output color is clamped to 1/255 boundaries (for alpha testing)

#define PERSPECTIVE_CORRECT_TEX

#ifdef PERSPECTIVE_CORRECT_TEX
#define TEX_XY tex.xy/tex.z
#define TEX_DECL vec3
#else
#define TEX_XY tex.xy
#define TEX_DECL vec2
#endif

// NVidia CG-data types
#define half2 vec2
#define half3 vec3
#define half4 vec4
#define float2 vec2
#define float3 vec3
#define float4 vec4

////////////////////////////////////////////////////////////////////
// INPUT/OUTPUT
////////////////////////////////////////////////////////////////////
// NOTE: Future optimization tex.w is normally useless (in cg it is a float3) so it can contains the fog value
struct vertex
{
    vec4 color;
    TEX_DECL tex;
    vec4 z;
    float fog;
};

#ifdef VERTEX_SHADER
out gl_PerVertex {
    invariant vec4 gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
};

layout(location = 0) in ivec4 Vert;
layout(location = 1) in vec4 Color;
layout(location = 2) in vec4 SecondaryColor;
layout(location = 3) in vec3 TexCoord;

layout(location = 0) out vertex VSout;

#endif

#ifdef FRAGMENT_SHADER

layout(location = 0) in vertex PSin;

// FIXME: host only do glDrawBuffers of 1 buffers not 2. I think this is a major bug
layout(location = 0) out vec4 FragData0;
layout(location = 1) out vec4 FragData1;

#endif

////////////////////////////////////////////////////////////////////
// Texture SAMPLER
////////////////////////////////////////////////////////////////////
// // main ps2 memory, each pixel is stored in 32bit color
// uniform sampler2DRect g_sMemory[2];
//
// // used to get the tiled offset into a page given the linear offset
// uniform sampler2DRect g_sSrcFinal;
// uniform sampler2D g_sBlocks;
// uniform sampler2D g_sBilinearBlocks;
// uniform sampler2D g_sConv16to32;
// uniform sampler3D g_sConv32to16;
// uniform sampler2DRect g_sBitwiseANDX;
// uniform sampler2DRect g_sBitwiseANDY;
// uniform sampler2DRect g_sInterlace;
//
// // used only on rare cases where the render target is PSMT8H
// uniform sampler2D g_sCLUT;
// main ps2 memory, each pixel is stored in 32bit color
layout(binding = 10) uniform sampler2DRect g_sMemory; // dual context

// used to get the tiled offset into a page given the linear offset
layout(binding = 1) uniform sampler2DRect g_sSrcFinal;
layout(binding = 2) uniform sampler2D g_sBlocks;
layout(binding = 3) uniform sampler2D g_sBilinearBlocks;
layout(binding = 4) uniform sampler2D g_sConv16to32;
layout(binding = 5) uniform sampler3D g_sConv32to16;
layout(binding = 6) uniform sampler2DRect g_sBitwiseANDX;
layout(binding = 7) uniform sampler2DRect g_sBitwiseANDY;
layout(binding = 8) uniform sampler2DRect g_sInterlace;

// used only on rare cases where the render target is PSMT8H
layout(binding = 9) uniform sampler2D g_sCLUT;

////////////////////////////////////////////////////////////////////
// UNIFORM BUFFER
////////////////////////////////////////////////////////////////////
layout(std140, binding = 0) uniform constant_buffer
{
	// Both shader
	// .z is used for the addressing fn
	// FIXME: not same value between c and shader...
	// float4 g_fBilinear = float4(-0.7f, -0.65f, 0.9,1/32767.0f);
	float4 g_fBilinear;
	float4 g_fZBias;
	float4 g_fc0;
	float4 g_fMult;
	// Vertex
	float4 g_fZ;				// transforms d3dcolor z into float z
	float4 g_fZMin;
	float4 g_fZNorm;
	// Pixel
	half4 g_fExactColor;
};
layout(std140, binding = 1) uniform common_buffer
{
	float4 g_fPosXY;
	float4 g_fFogColor;
};
layout(std140, binding = 2) uniform vertex_buffer
{
	float4 g_fBitBltPos;
	float4 g_fBitBltTex;
	float4 g_fBitBltTrans;
};
layout(std140, binding = 3) uniform fragment_buffer
{
	half4 fTexAlpha2;

	float4 g_fTexOffset;			// converts the page and block offsets into the mem addr/1024
	float4 g_fTexDims;				// mult by tex dims when accessing the block texture
	float4 g_fTexBlock;

	float4 g_fClampExts;		// if clamping the texture, use (minu, minv, maxu, maxv)
	float4 TexWrapMode;			// 0 - repeat/clamp, 1 - region rep (use fRegRepMask)

	float4 g_fRealTexDims; // tex dims used for linear filtering (w,h,1/w,1/h)

	// (alpha0, alpha1, 1 if highlight2 and tcc is rgba, 1-y)
	half4 g_fTestBlack;	// used for aem bit

	float4 g_fPageOffset;

	half4 fTexAlpha;

	float4 g_fInvTexDims; // similar to g_fClutOff

	// used for rectblitting
	float4 g_fBitBltZ;

	half4 g_fOneColor; // col*.xxxy+.zzzw
};


// given a local tex coord, returns the coord in the memory
float2 ps2memcoord(float2 realtex)
{
	float4 off;

	// block off
	realtex.xy = realtex.xy * g_fTexDims.xy + g_fTexDims.zw;
	realtex.xy = (realtex.xy - fract(realtex.xy)) * g_fMult.zw;
	float2 fblock = fract(realtex.xy);
	off.xy = realtex.xy-fblock.xy;

#ifdef ACCURATE_DECOMPRESSION
	off.zw = texture(g_sBlocks, g_fTexBlock.xy*fblock + g_fTexBlock.zw).ar;
	off.x = dot(off.xy, g_fTexOffset.xy);
	float r = g_fTexOffset.w;
	float f = fract(off.x);
	float fadd = g_fTexOffset.z * off.z;
	off.w = off.x + fadd + r;
	off.x = fract(f + fadd + r);
	off.w -= off.x ;
#else
	off.z = texture(g_sBlocks, g_fTexBlock.xy*fblock + g_fTexBlock.zw).a;

	// combine the two
    off.x = dot(off.xyz, g_fTexOffset.xyz)+g_fTexOffset.w;
	off.x = modf(off.x, off.w);
#endif

	off.xy = off.xw * g_fPageOffset.zy + g_fPageOffset.wx;
    //off.y = off.w * g_fPageOffset.y + g_fPageOffset.x;
	return off.xy;
}

// find all texcoords for bilinear filtering
// assume that orgtex are already on boundaries
void ps2memcoord4(float4 orgtex, out float4 off0, out float4 off1)
{
	//float4 off0, off1, off2, off3;
	float4 realtex;

	// block off
	realtex = (orgtex * g_fTexDims.xyxy + g_fTexDims.zwzw);// * g_fMult.zwzw;
	float4 fblock = fract(realtex.xyzw);
	float4 ftransblock = g_fTexBlock.xyxy*fblock + g_fTexBlock.zwzw;
	realtex -= fblock;

	float4 transvals = g_fTexOffset.x * realtex.xzxz + g_fTexOffset.y * realtex.yyww + g_fTexOffset.w;

	float4 colors;// = texture(g_sBilinearBlocks, ftransblock.xy);

	// this is faster on ffx ingame
	colors.x = texture(g_sBlocks, ftransblock.xy).a;
	colors.y = texture(g_sBlocks, ftransblock.zy).a;
	colors.z = texture(g_sBlocks, ftransblock.xw).a;
	colors.w = texture(g_sBlocks, ftransblock.zw).a;

	float4 fr, rem;

#ifdef ACCURATE_DECOMPRESSION
	fr = fract(transvals);
	float4 fadd = colors * g_fTexOffset.z;
	rem = transvals + fadd;
	fr = fract(fr + fadd);
	rem -= fr;
#else
	transvals += colors * g_fTexOffset.z;

	fr = modf(transvals, rem);
#endif

	rem = rem * g_fPageOffset.y + g_fPageOffset.x;
	fr = fr * g_fPageOffset.z + g_fPageOffset.w;

	// combine
	off0 = g_fc0.yxyx * fr.xxyy + g_fc0.xyxy * rem.xxyy;
	off1 = g_fc0.yxyx * fr.zzww + g_fc0.xyxy * rem.zzww;
}

void ps2memcoord4_fast(float4 orgtex, out float4 off0, out float4 off1)
{
	float4 realtex;

	realtex = (orgtex * g_fTexDims.xyxy + g_fTexDims.zwzw);// * g_fMult.zwzw;
	float4 fblock = fract(realtex.xyzw);
	float2 ftransblock = g_fTexBlock.xy*fblock.xy + g_fTexBlock.zw;
	realtex -= fblock;

	float4 transvals = g_fTexOffset.x * realtex.xzxz + g_fTexOffset.y * realtex.yyww + g_fTexOffset.w;

	float4 colors = texture(g_sBilinearBlocks, ftransblock.xy);
	float4 fr, rem;

#ifdef ACCURATE_DECOMPRESSION
	fr = fract(transvals);
	float4 fadd = colors * g_fTexOffset.z;
	rem = transvals + fadd;
	fr = fract(fr + fadd);
	rem -= fr;
#else
	transvals += colors * g_fTexOffset.z;

	fr = modf(transvals, rem);
#endif

	rem = rem * g_fPageOffset.y + g_fPageOffset.x;
	fr = fr * g_fPageOffset.z;

	off0 = g_fc0.yxyx * fr.xxyy + g_fc0.xyxy * rem.xxyy;
	off1 = g_fc0.yxyx * fr.zzww + g_fc0.xyxy * rem.zzww;
}

// Wrapping modes
#if defined(REPEAT)

float2 ps2addr(float2 coord)
{
	return fract(coord.xy);
}

#elif defined(CLAMP)

float2 ps2addr(float2 coord)
{
	return clamp(coord.xy, g_fClampExts.xy, g_fClampExts.zw);
}

#elif defined(REGION_REPEAT)

// computes the local tex coord along with addressing modes
float2 ps2addr(float2 coord)
{
	float2 final = fract(clamp(coord.xy, g_fClampExts.xy, g_fClampExts.zw));

	if( TexWrapMode.x > g_fBilinear.z ) // region repeat mode for x (umsk&x)|ufix
		final.x = texture(g_sBitwiseANDX, abs(coord.x)*TexWrapMode.zx).x * g_fClampExts.x + g_fClampExts.z;
	if( TexWrapMode.y > g_fBilinear.z ) // region repeat mode for x (vmsk&x)|vfix
		final.y = texture(g_sBitwiseANDY, abs(coord.y)*TexWrapMode.wy).x * g_fClampExts.y + g_fClampExts.w;

	return final;
}

#else

float2 ps2addr(float2 coord)
{
	return fract(clamp(coord.xy, g_fClampExts.xy, g_fClampExts.zw));
}

#endif

half4 tex2DPS_32(float2 tex0)
{
	return texture(g_sMemory, ps2memcoord(tex0));
}

// use when texture is not tiled -- shader 1
half4 tex2DPS_tex32(float2 tex0)
{
	return texture(g_sMemory, g_fTexDims.xy*tex0+g_fTexDims.zw)*g_fZBias.zzzw+g_fPageOffset.w;
}

// use when texture is not tiled -- shader 2
half4 tex2DPS_clut32(float2 tex0)
{
	float index = texture(g_sMemory, g_fTexDims.xy*tex0+g_fTexDims.zw).a+g_fPageOffset.w;
	return texture(g_sCLUT, index*g_fExactColor.xz+g_fExactColor.yz);
}

// Shader 3
// use when texture is not tiled and converting from 32bit to 16bit
// don't convert on the block level, only on the column level
// so every other 8 pixels, use the upper bits instead of lower
half4 tex2DPS_tex32to16(float2 tex0)
{
	bool upper = false;
	tex0.y += g_fPageOffset.z;
	float2 ffrac = mod(tex0, g_fTexOffset.xy);
	tex0.xy = g_fc0.ww * (tex0.xy + ffrac);
	if( ffrac.x > g_fTexOffset.z ) {
		tex0.x -= g_fTexOffset.z;
		upper = true;
	}
	if( ffrac.y >= g_fTexOffset.w ) {
		tex0.y -= g_fTexOffset.w;
		tex0.x += g_fc0.w;
	}

	half4 color = texture(g_sMemory, g_fTexDims.xy*tex0+g_fTexDims.zw)*g_fZBias.zzzw+g_fPageOffset.w;
	float2 uv = upper ? color.xw : color.zy;
	return texture(g_sConv16to32, uv+g_fPageOffset.xy);
}

// Shader 4
// used when a 16 bit texture is used an 8h
half4 tex2DPS_tex16to8h(float2 tex0)
{
	float4 final;
	float2 ffrac = mod(tex0+g_fPageOffset.zw, g_fTexOffset.xy);
	tex0.xy = g_fPageOffset.xy * tex0.xy - ffrac * g_fc0.yw;

	if( ffrac.x > g_fTexOffset.x*g_fc0.w )
		tex0.x += g_fTexOffset.x*g_fc0.w;
	if( tex0.x >= g_fc0.y ) tex0 += g_fTexOffset.zw;

	float4 upper = texture(g_sMemory, g_fTexDims.xy*tex0+g_fTexDims.zw);

	// only need alpha
	float index = texture(g_sConv32to16, upper.zyx-g_fc0.z).y + upper.w*g_fc0.w*g_fc0.w;
	return texture(g_sCLUT, index+g_fExactColor.yz);
}

// Shader 5
// used when a 16 bit texture is used a 32bit one
half4 tex2DPS_tex16to32(float2 tex0)
{
	float4 final;
	float2 ffrac = mod(tex0+g_fPageOffset.zw, g_fTexOffset.xy);
	//tex0.xy = g_fPageOffset.xy * tex0.xy - ffrac * g_fc0.yw;
	tex0.y += g_fPageOffset.y * ffrac.y;

	if( ffrac.x > g_fTexOffset.z ) {
		tex0.x -= g_fTexOffset.z;
		tex0.y += g_fTexOffset.w;
	}

	float fconst = g_fc0.w*g_fc0.w;
	float4 lower = texture(g_sSrcFinal, g_fTexDims.xy*tex0);
	float4 upper = texture(g_sMemory, g_fTexDims.xy*tex0+g_fTexDims.zw);

	final.zy = texture(g_sConv32to16, lower.zyx).xy + lower.ww*fconst;
	final.xw = texture(g_sConv32to16, upper.zyx).xy + upper.ww*fconst;
	return final;
}

half4 tex2DPS_tex16to32h(float2 tex0)
{
	float4 final =  vec4(0.0, 0.0, 0.0, 0.0);
	return final;
}

//half4 f;
//f.w = old.y > (127.2f/255.0f) ? 1 : 0;
//old.y -= 0.5f * f.w;
//f.xyz = fract(old.yyx*half3(2.002*255.0f/256.0f, 64.025f*255.0f/256.0f, 8.002*255.0f/256.0f));
//f.y += old.x * (0.25f*255.0f/256.0f);

////////////////////////////////
// calculates the texture color
////////////////////////////////

#define decl_ps2shade(num) \
decl_ps2shade_##num(_32) \
decl_ps2shade_##num(_tex32) \
decl_ps2shade_##num(_clut32) \
decl_ps2shade_##num(_tex32to16) \
decl_ps2shade_##num(_tex16to8h) \
decl_ps2shade_##num(_tex16to32h)

// nearest
#define decl_ps2shade_0(bit) \
float4 ps2shade0##bit( TEX_DECL tex) \
{ \
    return tex2DPS##bit( ps2addr(TEX_XY)); \
}

// do fast memcoord4 calcs when textures behave well
#ifdef REPEAT
#define PS2MEMCOORD4 ps2memcoord4
#else
#define PS2MEMCOORD4 ps2memcoord4
#endif


#define decl_BilinearFilter(bit, addrfn) \
half4 BilinearFilter##bit(float2 tex0) \
{ \
	float4 off0, off1; \
	float4 ftex; \
	float2 ffrac; \
	ftex.xy = tex0 + g_fBilinear.xy * g_fRealTexDims.zw; \
	ffrac = fract(ftex.xy*g_fRealTexDims.xy); \
	ftex.xy -= ffrac.xy * g_fRealTexDims.zw; \
	\
 	ftex.zw = ps2addr(ftex.xy + g_fRealTexDims.zw); \
 	ftex.xy = ps2addr(ftex.xy); \
 	\
 	PS2MEMCOORD4(ftex, off0, off1); \
	half4 c0 = texture(g_sMemory, off0.xy); \
	half4 c1 = texture(g_sMemory, off0.zw); \
	half4 c2 = texture(g_sMemory, off1.xy); \
	half4 c3 = texture(g_sMemory, off1.zw); \
	return mix( mix(c0, c1, vec4(ffrac.x)), mix(c2, c3, ffrac.x), vec4(ffrac.y) ); \
}

decl_BilinearFilter(_32, ps2addr)
decl_BilinearFilter(_tex32, ps2addr)
decl_BilinearFilter(_clut32, ps2addr)
decl_BilinearFilter(_tex32to16, ps2addr)
decl_BilinearFilter(_tex16to8h, ps2addr)
decl_BilinearFilter(_tex16to32h, ps2addr)

//TODO! For mip maps, only apply when LOD >= 0
// lcm == 0, LOD = log(1/Q)*L + K, lcm == 1, LOD = K

// bilinear
#define decl_ps2shade_1(bit) \
half4 ps2shade1##bit(TEX_DECL tex) \
{ \
	return BilinearFilter##bit(TEX_XY); \
}

// nearest, mip nearest
#define decl_ps2shade_2(bit) \
half4 ps2shade2##bit(TEX_DECL tex) \
{ \
    return tex2DPS##bit( ps2addr(TEX_XY)); \
}

// nearest, mip linear
#define decl_ps2shade_3(bit) \
half4 ps2shade3##bit(TEX_DECL tex) \
{ \
    return tex2DPS##bit(ps2addr(TEX_XY)); \
}

// linear, mip nearest
#define decl_ps2shade_4(bit) \
half4 ps2shade4##bit(TEX_DECL tex) \
{ \
    return BilinearFilter##bit(TEX_XY); \
}

// linear, mip linear
#define decl_ps2shade_5(bit) \
half4 ps2shade5##bit(TEX_DECL tex) \
{ \
    return BilinearFilter##bit(TEX_XY); \
}

decl_ps2shade(0)
decl_ps2shade(1)
decl_ps2shade(2)
decl_ps2shade(3)
decl_ps2shade(4)
decl_ps2shade(5)


half4 ps2CalcShade(half4 texcol, half4 color)
{
#ifdef TEST_AEM
	if( dot(texcol.xyzw, g_fTestBlack.xyzw) <= g_fc0.z )
		texcol.w = g_fc0.x;
	else
#endif
		texcol.w = texcol.w * fTexAlpha.y + fTexAlpha.x;

	texcol = texcol * (fTexAlpha2.zzzw * color + fTexAlpha2.xxxy) + fTexAlpha.zzzw * color.wwww;

	return texcol;
}

// final ops on the color
#ifdef EXACT_COLOR

half4 ps2FinalColor(half4 col)
{
	// g_fOneColor has to scale by 255
	half4 temp = col * g_fOneColor.xxxy + g_fOneColor.zzzw;
	temp.w = floor(temp.w)*g_fExactColor.w;
	return temp;
}

#else
half4 ps2FinalColor(half4 col)
{
	return col * g_fOneColor.xxxy + g_fOneColor.zzzw;
}
#endif


#ifdef FRAGMENT_SHADER 			// This is code only for FRAGMENTS (pixel shader)

#ifdef WRITE_DEPTH
void write_depth_target()
{
    FragData1 = PSin.z; 
}
#else
void write_depth_target() { }
#endif

void RegularPS() {
	// whenever outputting depth, make sure to mult by 255/256 and 1
	FragData0 = ps2FinalColor(PSin.color);
    write_depth_target();
}

#define DECL_TEXPS(num, bit) \
void Texture##num##bit##PS() \
{ \
	FragData0 = ps2FinalColor(ps2CalcShade(ps2shade##num##bit(PSin.tex), PSin.color)); \
	write_depth_target(); \
}

#define DECL_TEXPS_(num) \
DECL_TEXPS(num, _32) \
DECL_TEXPS(num, _tex32) \
DECL_TEXPS(num, _clut32) \
DECL_TEXPS(num, _tex32to16) \
DECL_TEXPS(num, _tex16to8h)

DECL_TEXPS_(0)
DECL_TEXPS_(1)
DECL_TEXPS_(2)
DECL_TEXPS_(3)
DECL_TEXPS_(4)
DECL_TEXPS_(5)

void RegularFogPS() {
	half4 c;
	c.xyz = mix(g_fFogColor.xyz, PSin.color.xyz, vec3(PSin.fog));
	c.w = PSin.color.w;
	FragData0 = ps2FinalColor(c);
    write_depth_target();
}

#define DECL_TEXFOGPS(num, bit) \
void TextureFog##num##bit##PS() \
{ \
	half4 c = ps2CalcShade(ps2shade##num##bit(PSin.tex), PSin.color); \
	c.xyz = mix(g_fFogColor.xyz, c.xyz, vec3(PSin.fog)); \
	FragData0 = ps2FinalColor(c); \
	write_depth_target(); \
}

#define DECL_TEXFOGPS_(num) \
DECL_TEXFOGPS(num, _32) \
DECL_TEXFOGPS(num, _tex32) \
DECL_TEXFOGPS(num, _clut32) \
DECL_TEXFOGPS(num, _tex32to16) \
DECL_TEXFOGPS(num, _tex16to8h)

DECL_TEXFOGPS_(0)
DECL_TEXFOGPS_(1)
DECL_TEXFOGPS_(2)
DECL_TEXFOGPS_(3)
DECL_TEXFOGPS_(4)
DECL_TEXFOGPS_(5)

//-------------------------------------------------------
// Techniques not related to the main primitive commands
half4 BilinearBitBlt(float2 tex0)
{
	float4 ftex;
	float2 ffrac;

	ffrac.xy = fract(tex0*g_fRealTexDims.xy);
	ftex.xy = tex0 - ffrac.xy * g_fRealTexDims.zw;
	ftex.zw = ftex.xy + g_fRealTexDims.zw;

	float4 off0, off1;
	ps2memcoord4_fast(ftex, off0, off1);
	half4 c0 = texture(g_sMemory, off0.xy);
	half4 c1 = texture(g_sMemory, off0.zw);
	half4 c2 = texture(g_sMemory, off1.xy);
	half4 c3 = texture(g_sMemory, off1.zw);

	return mix( mix(c0, c1, vec4(ffrac.x)), mix(c2, c3, vec4(ffrac.x)), vec4(ffrac.y) );
}

void BitBltPS() {
	FragData0 = texture(g_sMemory, ps2memcoord(PSin.tex.xy))*g_fOneColor.xxxy;
}

// used when AA
void BitBltAAPS() {
	FragData0 = BilinearBitBlt(PSin.tex.xy) * g_fOneColor.xxxy;
}

void BitBltDepthPS() {
	vec4 data;
	data = texture(g_sMemory, ps2memcoord(PSin.tex.xy));
	FragData0 = data + g_fZBias.y;
	gl_FragDepth   = (log(g_fc0.y + dot(data, g_fBitBltZ)) * g_fOneColor.w) * g_fZMin.y + dot(data, g_fBitBltZ) * g_fZMin.x ;
}

void BitBltDepthMRTPS() {
	vec4 data;
	data = texture(g_sMemory, ps2memcoord(PSin.tex.xy));
	FragData0 = data + g_fZBias.y;
	FragData1.x = g_fc0.x;
	gl_FragDepth = (log(g_fc0.y + dot(data, g_fBitBltZ)) * g_fOneColor.w) * g_fZMin.y + dot(data, g_fBitBltZ) * g_fZMin.x ;
}

// static const float BlurKernel[9] = {
// 	0.027601,
// 	0.066213,
// 	0.123701,
// 	0.179952,
// 	0.205065,
// 	0.179952,
// 	0.123701,
// 	0.066213,
// 	0.027601
// };

half4 BilinearFloat16(float2 tex0)
{
	return texture(g_sSrcFinal, tex0.xy);
}

void CRTCTargInterPS() {
	float finter = texture(g_sInterlace, PSin.z.yy).x * g_fOneColor.z + g_fOneColor.w + g_fc0.w;
	float4 c = BilinearFloat16(PSin.tex.xy);
	c.w = ( g_fc0.w*c.w * g_fOneColor.x + g_fOneColor.y ) * finter;
	FragData0 = c;
}

void CRTCTargPS() {
	float4 c = BilinearFloat16(PSin.tex.xy);
	// FIXME DEBUG: to validate tex coord on blit
	//c = vec4(PSin.tex.x/512.0f, PSin.tex.y/512.0f, 0.0, 1.0);
	c.w = g_fc0.w * c.w * g_fOneColor.x + g_fOneColor.y;
	FragData0 = c;
}

void CRTCInterPS() {
	float finter = texture(g_sInterlace, PSin.z.yy).x * g_fOneColor.z + g_fOneColor.w + g_fc0.w;
	float2 filtcoord = trunc(PSin.tex.xy) * g_fInvTexDims.xy + g_fInvTexDims.zw;
	half4 c = BilinearBitBlt(filtcoord);
	c.w = (c.w * g_fOneColor.x + g_fOneColor.y)*finter;
	FragData0 = c;
}

// simpler
void CRTCInterPS_Nearest() {
	float finter = texture(g_sInterlace, PSin.z.yy).x * g_fOneColor.z + g_fOneColor.w + g_fc0.w;
	half4 c = texture(g_sMemory, ps2memcoord(PSin.tex.xy));
	c.w = (c.w * g_fOneColor.x + g_fOneColor.y)*finter;
	FragData0 = c;
}

void CRTCPS() {
	float2 filtcoord = PSin.tex.xy * g_fInvTexDims.xy+g_fInvTexDims.zw;
	half4 c = BilinearBitBlt(filtcoord);
	c.w = c.w * g_fOneColor.x + g_fOneColor.y;
	FragData0 = c;
}

// simpler
void CRTCPS_Nearest() {
	half4 c = texture(g_sMemory, ps2memcoord(PSin.tex.xy));
	c.w = c.w * g_fOneColor.x + g_fOneColor.y;
	FragData0 = c;
}

void CRTC24InterPS() {
	float finter = texture(g_sInterlace, PSin.z.yy).x * g_fOneColor.z + g_fOneColor.w + g_fc0.w;
	float2 filtcoord = trunc(PSin.tex.xy) * g_fInvTexDims.xy + g_fInvTexDims.zw;

	half4 c = texture(g_sMemory, ps2memcoord(filtcoord));
	c.w = (c.w * g_fOneColor.x + g_fOneColor.y)*finter;
	FragData0 = c;
}

void CRTC24PS() {
	float2 filtcoord = trunc(PSin.tex.xy) * g_fInvTexDims.xy + g_fInvTexDims.zw;
	half4 c = texture(g_sMemory, ps2memcoord(filtcoord));
	c.w = c.w * g_fOneColor.x + g_fOneColor.y;
	FragData0 = c;
}

void ZeroPS() {
	FragData0 = g_fOneColor;
}

void ZeroDebugPS() {
	FragData0 = vec4(PSin.tex.x, PSin.tex.y, PSin.tex.z, 0.5);
}

void ZeroDebug2PS() {
    vec2 xy = ps2memcoord(fract(PSin.tex.xy/PSin.tex.z)) * vec2(1/4096.0f, 1/48.0f);
	FragData0 = vec4(xy.x, xy.y, 0.0, 0.5);
}

void ZeroDebug3PS() {
	//FragData0 = vec4(PSin.position.x/2.0f + 0.5f, PSin.position.y/2.0f + 0.5f, 1.0, 0.5);
}

void BaseTexturePS() {
	FragData0 = texture(g_sSrcFinal, PSin.tex.xy) * g_fOneColor;
}

void Convert16to32PS() {
	float4 final;
	float2 ffrac = mod ( PSin.tex.xy + g_fTexDims.zw, g_fTexOffset.xy);
	float2 tex0 = g_fTexDims.xy * PSin.tex.xy - ffrac * g_fc0.yw;

	if (ffrac.x > g_fTexOffset.x*g_fc0.w)
		tex0.x += g_fTexOffset.x*g_fc0.w;
	if (tex0.x >= g_fc0.y)
		tex0 += g_fTexOffset.zw;

	float4 lower = texture(g_sSrcFinal, tex0);
	float4 upper = texture(g_sSrcFinal, tex0 + g_fPageOffset.xy);

	final.zy = texture(g_sConv32to16, lower.zyx).xy + lower.ww*g_fPageOffset.zw;
	final.xw = texture(g_sConv32to16, upper.zyx).xy + upper.ww*g_fPageOffset.zw;

	FragData0= final;
}

// use when texture is not tiled and converting from 32bit to 16bit
// don't convert on the block level, only on the column level
// so every other 8 pixels, use the upper bits instead of lower
void Convert32to16PS() {
	bool upper = false;
	float2 ffrac = mod(PSin.tex.xy + g_fTexDims.zw, g_fTexOffset.xy);
	float2 tex0 = g_fc0.ww * (PSin.tex.xy + ffrac);
	if( ffrac.x > g_fTexOffset.z ) {
		tex0.x -= g_fTexOffset.z;
		upper = true;
	}
	if( ffrac.y >= g_fTexOffset.w ) {
		tex0.y -= g_fTexOffset.w;
		tex0.x += g_fc0.w;
	}

	half4 color = texture(g_sSrcFinal, tex0*g_fTexDims.xy)*g_fc0.yyyw;
	float2 uv = upper ? color.xw : color.zy;
	FragData0 = texture(g_sConv16to32, uv*g_fPageOffset.xy+g_fPageOffset.zw)*g_fTexDims.xxxy;
}
#endif 			//FRAGMENT_SHADER

#ifdef VERTEX_SHADER

void SetColor() {
	VSout.color = Color;
}

void SetTex() {
#ifdef PERSPECTIVE_CORRECT_TEX
	VSout.tex = TexCoord;
#else
	VSout.tex.xy = TexCoord.xy/TexCoord.z;
#endif
}

void SetZ() {
#ifdef WRITE_DEPTH
	VSout.z = SecondaryColor * g_fZBias.x + g_fZBias.y;
    VSout.z.w = g_fc0.y;
#endif
}

void SetPosition() {
	float4 position;
	position.xy = vec2(Vert.xy) * g_fPosXY.xy + g_fPosXY.zw;
    // FIXME: the factor in normal mode seem bogus. They don't have same order than in log mode. Or I failed to understand the logic
    //// normal mode.
    // -> dot(g_fZ, SecondaryColor.zyxw)) => reconstruct a float from normalized char. The float range from 0 to 1
    // position.z = dot(g_fZ, SecondaryColor.zyxw);
    //// logz mode
    // -> dot(g_fZ, SecondaryColor.zyxw)) => reconstruct a float from normalized char. The float range from 0 to 2**32
    // position.z = log(g_fc0.y + dot(g_fZ, SecondaryColor.zyxw)) * g_fZNorm.x
    // position.z = log(1 + Z_INT) * 0.999f / (32 * log(2.0)) = log2(1 + Z_INT) * 0.999f / 32
    // log2(...) will range from 0 to 32
	position.z = (log(g_fc0.y + dot(g_fZ, SecondaryColor.zyxw)) * g_fZNorm.x + g_fZNorm.y) * g_fZMin.y + dot(g_fZ, SecondaryColor.zyxw) * g_fZMin.x ;
	position.w = g_fc0.y;

    gl_Position = position;
}

void SetFog() {
    VSout.fog = float(Vert.z) * g_fBilinear.w;
}

// just smooth shadering
void RegularVS() {
    SetPosition();
    SetColor();
    SetZ();
}

// diffuse texture mapping
void TextureVS() {
    SetPosition();
    SetColor();
    SetTex();
    SetZ();
}

void RegularFogVS() {
    SetPosition();
    SetColor();
    SetZ();
    SetFog();
}

void TextureFogVS() {
    SetPosition();
    SetColor();
    SetTex();
    SetZ();
    SetFog();
}

void BitBltVS() {
	vec4 position;
	position.xy = vec2(Vert.xy) * g_fBitBltPos.xy + g_fBitBltPos.zw;
	position.zw = g_fc0.xy;
	gl_Position = position;

	VSout.tex.xy = TexCoord.xy * g_fBitBltTex.xy + g_fBitBltTex.zw;
	VSout.z.xy = position.xy * g_fBitBltTrans.xy + g_fBitBltTrans.zw;
}

#endif
