2015-05-23 22:55:12 -06:00
// Copyright 2008 Dolphin Emulator Project
2015-05-17 17:08:10 -06:00
// Licensed under GPLv2+
2013-04-17 21:09:55 -06:00
// Refer to the license.txt file included.
2008-12-07 22:30:24 -07:00
# include <cmath>
2014-02-17 03:18:15 -07:00
# include <cstdio>
2016-01-17 14:54:31 -07:00
# include <cstring>
2008-12-07 22:30:24 -07:00
2016-01-17 14:54:31 -07:00
# include "Common/Assert.h"
# include "Common/CommonTypes.h"
# include "Common/Logging/Log.h"
2014-11-13 15:26:49 -07:00
# include "VideoCommon/BoundingBox.h"
2014-02-17 03:18:15 -07:00
# include "VideoCommon/BPMemory.h"
2014-12-02 23:29:50 -07:00
# include "VideoCommon/DriverDetails.h"
2014-02-17 03:18:15 -07:00
# include "VideoCommon/LightingShaderGen.h"
# include "VideoCommon/PixelShaderGen.h"
2015-11-01 14:39:31 -07:00
# include "VideoCommon/VertexLoaderManager.h"
2014-02-17 03:18:15 -07:00
# include "VideoCommon/VideoConfig.h"
# include "VideoCommon/XFMemory.h" // for texture projection mode
2012-08-10 10:57:37 -06:00
2015-09-01 10:18:13 -06:00
// TODO: Get rid of these
enum : u32
{
C_COLORMATRIX = 0 , // 0
C_COLORS = 0 , // 0
C_KCOLORS = C_COLORS + 4 , // 4
C_ALPHA = C_KCOLORS + 4 , // 8
C_TEXDIMS = C_ALPHA + 1 , // 9
C_ZBIAS = C_TEXDIMS + 8 , // 17
C_INDTEXSCALE = C_ZBIAS + 2 , // 19
C_INDTEXMTX = C_INDTEXSCALE + 2 , // 21
C_FOGCOLOR = C_INDTEXMTX + 6 , // 27
C_FOGI = C_FOGCOLOR + 1 , // 28
C_FOGF = C_FOGI + 1 , // 29
C_ZSLOPE = C_FOGF + 2 , // 31
C_EFBSCALE = C_ZSLOPE + 1 , // 32
C_PENVCONST_END = C_EFBSCALE + 1
} ;
2013-01-08 08:40:15 -07:00
2014-02-28 12:34:09 -07:00
static const char * tevKSelTableC [ ] =
2008-12-07 22:30:24 -07:00
{
2013-08-14 08:25:39 -06:00
" 255,255,255 " , // 1 = 0x00
" 223,223,223 " , // 7_8 = 0x01
" 191,191,191 " , // 3_4 = 0x02
" 159,159,159 " , // 5_8 = 0x03
2014-05-14 14:27:00 -06:00
" 128,128,128 " , // 1_2 = 0x04
" 96,96,96 " , // 3_8 = 0x05
" 64,64,64 " , // 1_4 = 0x06
" 32,32,32 " , // 1_8 = 0x07
" 0,0,0 " , // INVALID = 0x08
" 0,0,0 " , // INVALID = 0x09
" 0,0,0 " , // INVALID = 0x0a
" 0,0,0 " , // INVALID = 0x0b
2013-10-10 12:26:41 -06:00
I_KCOLORS " [0].rgb " , // K0 = 0x0C
I_KCOLORS " [1].rgb " , // K1 = 0x0D
I_KCOLORS " [2].rgb " , // K2 = 0x0E
I_KCOLORS " [3].rgb " , // K3 = 0x0F
I_KCOLORS " [0].rrr " , // K0_R = 0x10
I_KCOLORS " [1].rrr " , // K1_R = 0x11
I_KCOLORS " [2].rrr " , // K2_R = 0x12
I_KCOLORS " [3].rrr " , // K3_R = 0x13
I_KCOLORS " [0].ggg " , // K0_G = 0x14
I_KCOLORS " [1].ggg " , // K1_G = 0x15
I_KCOLORS " [2].ggg " , // K2_G = 0x16
I_KCOLORS " [3].ggg " , // K3_G = 0x17
I_KCOLORS " [0].bbb " , // K0_B = 0x18
I_KCOLORS " [1].bbb " , // K1_B = 0x19
I_KCOLORS " [2].bbb " , // K2_B = 0x1A
I_KCOLORS " [3].bbb " , // K3_B = 0x1B
I_KCOLORS " [0].aaa " , // K0_A = 0x1C
I_KCOLORS " [1].aaa " , // K1_A = 0x1D
I_KCOLORS " [2].aaa " , // K2_A = 0x1E
I_KCOLORS " [3].aaa " , // K3_A = 0x1F
2008-12-07 22:30:24 -07:00
} ;
2014-02-28 12:34:09 -07:00
static const char * tevKSelTableA [ ] =
2008-12-07 22:30:24 -07:00
{
2013-08-14 08:25:39 -06:00
" 255 " , // 1 = 0x00
" 223 " , // 7_8 = 0x01
" 191 " , // 3_4 = 0x02
" 159 " , // 5_8 = 0x03
2014-05-14 14:27:00 -06:00
" 128 " , // 1_2 = 0x04
" 96 " , // 3_8 = 0x05
" 64 " , // 1_4 = 0x06
" 32 " , // 1_8 = 0x07
" 0 " , // INVALID = 0x08
" 0 " , // INVALID = 0x09
" 0 " , // INVALID = 0x0a
" 0 " , // INVALID = 0x0b
" 0 " , // INVALID = 0x0c
" 0 " , // INVALID = 0x0d
" 0 " , // INVALID = 0x0e
" 0 " , // INVALID = 0x0f
2013-10-10 12:26:41 -06:00
I_KCOLORS " [0].r " , // K0_R = 0x10
I_KCOLORS " [1].r " , // K1_R = 0x11
I_KCOLORS " [2].r " , // K2_R = 0x12
I_KCOLORS " [3].r " , // K3_R = 0x13
I_KCOLORS " [0].g " , // K0_G = 0x14
I_KCOLORS " [1].g " , // K1_G = 0x15
I_KCOLORS " [2].g " , // K2_G = 0x16
I_KCOLORS " [3].g " , // K3_G = 0x17
I_KCOLORS " [0].b " , // K0_B = 0x18
I_KCOLORS " [1].b " , // K1_B = 0x19
I_KCOLORS " [2].b " , // K2_B = 0x1A
I_KCOLORS " [3].b " , // K3_B = 0x1B
I_KCOLORS " [0].a " , // K0_A = 0x1C
I_KCOLORS " [1].a " , // K1_A = 0x1D
I_KCOLORS " [2].a " , // K2_A = 0x1E
I_KCOLORS " [3].a " , // K3_A = 0x1F
2008-12-07 22:30:24 -07:00
} ;
2014-02-28 12:34:09 -07:00
static const char * tevCInputTable [ ] =
2008-12-07 22:30:24 -07:00
{
2014-03-06 11:28:29 -07:00
" prev.rgb " , // CPREV,
" prev.aaa " , // APREV,
" c0.rgb " , // C0,
" c0.aaa " , // A0,
" c1.rgb " , // C1,
" c1.aaa " , // A1,
" c2.rgb " , // C2,
" c2.aaa " , // A2,
" textemp.rgb " , // TEXC,
" textemp.aaa " , // TEXA,
" rastemp.rgb " , // RASC,
" rastemp.aaa " , // RASA,
2013-08-14 09:26:15 -06:00
" int3(255,255,255) " , // ONE
2014-05-14 14:27:00 -06:00
" int3(128,128,128) " , // HALF
2014-03-06 11:28:29 -07:00
" konsttemp.rgb " , // KONST
2013-08-14 09:26:15 -06:00
" int3(0,0,0) " , // ZERO
2008-12-07 22:30:24 -07:00
} ;
2014-02-28 12:34:09 -07:00
static const char * tevAInputTable [ ] =
2008-12-07 22:30:24 -07:00
{
2014-03-24 07:41:56 -06:00
" prev.a " , // APREV,
" c0.a " , // A0,
" c1.a " , // A1,
" c2.a " , // A2,
" textemp.a " , // TEXA,
" rastemp.a " , // RASA,
" konsttemp.a " , // KONST, (hw1 had quarter)
" 0 " , // ZERO
2010-07-06 07:14:51 -06:00
} ;
2008-12-07 22:30:24 -07:00
static const char * tevRasTable [ ] =
{
2014-12-25 16:56:12 -07:00
" iround(col0 * 255.0) " ,
" iround(col1 * 255.0) " ,
2013-01-11 07:47:38 -07:00
" ERROR13 " , //2
" ERROR14 " , //3
" ERROR15 " , //4
2014-02-28 12:43:43 -07:00
" (int4(1, 1, 1, 1) * alphabump) " , // bump alpha (0..248)
" (int4(1, 1, 1, 1) * (alphabump | (alphabump >> 5))) " , // normalized bump alpha (0..255)
2013-08-14 08:16:06 -06:00
" int4(0, 0, 0, 0) " , // zero
2008-12-07 22:30:24 -07:00
} ;
2014-03-06 11:27:11 -07:00
static const char * tevCOutputTable [ ] = { " prev.rgb " , " c0.rgb " , " c1.rgb " , " c2.rgb " } ;
static const char * tevAOutputTable [ ] = { " prev.a " , " c0.a " , " c1.a " , " c2.a " } ;
2008-12-07 22:30:24 -07:00
2015-12-26 15:56:06 -07:00
template < class T > static void WriteStage ( T & out , pixel_shader_uid_data * uid_data , int n , API_TYPE ApiType , const char swapModeTable [ 4 ] [ 5 ] ) ;
template < class T > static void WriteTevRegular ( T & out , const char * components , int bias , int op , int clamp , int shift ) ;
template < class T > static void SampleTexture ( T & out , const char * texcoords , const char * texswap , int texmap , API_TYPE ApiType ) ;
template < class T > static void WriteAlphaTest ( T & out , pixel_shader_uid_data * uid_data , API_TYPE ApiType , DSTALPHA_MODE dstAlphaMode , bool per_pixel_depth ) ;
template < class T > static void WriteFog ( T & out , pixel_shader_uid_data * uid_data ) ;
2013-06-17 03:37:41 -06:00
2013-03-26 16:21:08 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static T GeneratePixelShader ( DSTALPHA_MODE dstAlphaMode , API_TYPE ApiType )
2008-12-07 22:30:24 -07:00
{
2015-11-02 19:47:05 -07:00
T out ;
2015-11-01 14:39:31 -07:00
const u32 components = VertexLoaderManager : : g_current_components ;
2013-03-29 14:53:57 -06:00
// Non-uid template parameters will write to the dummy data (=> gets optimized out)
pixel_shader_uid_data dummy_data ;
2014-10-21 00:52:45 -06:00
pixel_shader_uid_data * uid_data = out . template GetUidData < pixel_shader_uid_data > ( ) ;
2016-01-01 23:20:01 -07:00
if ( uid_data ! = nullptr )
memset ( uid_data , 0 , sizeof ( * uid_data ) ) ;
else
2014-10-21 00:52:45 -06:00
uid_data = & dummy_data ;
2013-03-29 14:53:57 -06:00
2012-08-06 17:02:04 -06:00
unsigned int numStages = bpmem . genMode . numtevstages + 1 ;
unsigned int numTexgen = bpmem . genMode . numtexgens ;
2008-12-07 22:30:24 -07:00
2012-08-06 17:02:04 -06:00
out . Write ( " //Pixel Shader for TEV stages \n " ) ;
2013-04-29 11:52:12 -06:00
out . Write ( " //%i TEV stages, %i texgens, %i IND stages \n " ,
2015-10-16 16:00:03 -06:00
numStages , numTexgen , bpmem . genMode . numindstages . Value ( ) ) ;
2008-12-07 22:30:24 -07:00
2014-10-21 00:52:45 -06:00
uid_data - > dstAlphaMode = dstAlphaMode ;
uid_data - > genMode_numindstages = bpmem . genMode . numindstages ;
uid_data - > genMode_numtevstages = bpmem . genMode . numtevstages ;
uid_data - > genMode_numtexgens = bpmem . genMode . numtexgens ;
2012-08-06 17:02:04 -06:00
2013-10-07 19:04:41 -06:00
// dot product for integer vectors
2014-03-06 11:21:03 -07:00
out . Write ( " int idot(int3 x, int3 y) \n "
2014-03-06 12:08:47 -07:00
" { \n "
" \t int3 tmp = x * y; \n "
" \t return tmp.x + tmp.y + tmp.z; \n "
" } \n " ) ;
2014-03-06 11:21:03 -07:00
out . Write ( " int idot(int4 x, int4 y) \n "
2014-03-06 12:08:47 -07:00
" { \n "
" \t int4 tmp = x * y; \n "
" \t return tmp.x + tmp.y + tmp.z + tmp.w; \n "
" } \n \n " ) ;
2013-10-07 19:04:41 -06:00
2014-03-17 10:11:27 -06:00
// rounding + casting to integer at once in a single function
out . Write ( " int iround(float x) { return int (round(x)); } \n "
" int2 iround(float2 x) { return int2(round(x)); } \n "
" int3 iround(float3 x) { return int3(round(x)); } \n "
" int4 iround(float4 x) { return int4(round(x)); } \n \n " ) ;
2015-02-18 20:29:02 -07:00
out . Write ( " int itrunc(float x) { return int (trunc(x)); } \n "
" int2 itrunc(float2 x) { return int2(trunc(x)); } \n "
" int3 itrunc(float3 x) { return int3(trunc(x)); } \n "
" int4 itrunc(float4 x) { return int4(trunc(x)); } \n \n " ) ;
2012-12-27 21:46:29 -07:00
if ( ApiType = = API_OPENGL )
2010-08-29 13:34:54 -06:00
{
2015-10-11 07:47:15 -06:00
out . Write ( " SAMPLER_BINDING(0) uniform sampler2DArray samp[8]; \n " ) ;
2010-07-06 07:14:51 -06:00
}
2013-09-22 10:07:21 -06:00
else // D3D
2010-09-22 20:17:48 -06:00
{
2011-12-08 00:51:08 -07:00
// Declare samplers
2015-10-11 07:47:15 -06:00
out . Write ( " SamplerState samp[8] : register(s0); \n " ) ;
2013-09-22 10:07:21 -06:00
out . Write ( " \n " ) ;
2015-10-11 07:47:15 -06:00
out . Write ( " Texture2DArray Tex[8] : register(t0); \n " ) ;
2010-09-22 20:17:48 -06:00
}
2013-03-26 15:16:29 -06:00
out . Write ( " \n " ) ;
2010-06-14 08:36:01 -06:00
2014-01-30 08:38:11 -07:00
if ( ApiType = = API_OPENGL )
2014-05-30 07:19:04 -06:00
{
2014-03-30 12:58:05 -06:00
out . Write ( " layout(std140%s) uniform PSBlock { \n " , g_ActiveConfig . backend_info . bSupportsBindingLayout ? " , binding = 1 " : " " ) ;
2014-05-30 07:19:04 -06:00
}
2014-03-17 03:19:54 -06:00
else
2014-05-30 07:19:04 -06:00
{
out . Write ( " cbuffer PSBlock : register(b0) { \n " ) ;
}
2014-03-17 03:29:32 -06:00
out . Write (
" \t int4 " I_COLORS " [4]; \n "
" \t int4 " I_KCOLORS " [4]; \n "
" \t int4 " I_ALPHA " ; \n "
" \t float4 " I_TEXDIMS " [8]; \n "
" \t int4 " I_ZBIAS " [2]; \n "
" \t int4 " I_INDTEXSCALE " [2]; \n "
" \t int4 " I_INDTEXMTX " [6]; \n "
" \t int4 " I_FOGCOLOR " ; \n "
" \t int4 " I_FOGI " ; \n "
" \t float4 " I_FOGF " [2]; \n "
2014-12-25 00:34:22 -07:00
" \t float4 " I_ZSLOPE " ; \n "
2015-01-02 10:06:56 -07:00
" \t float4 " I_EFBSCALE " ; \n "
2014-03-17 03:29:32 -06:00
" }; \n " ) ;
2013-03-26 15:16:29 -06:00
2014-05-30 08:17:30 -06:00
if ( g_ActiveConfig . bEnablePixelLighting )
2014-05-30 07:19:04 -06:00
{
2014-05-30 08:17:30 -06:00
out . Write ( " %s " , s_lighting_struct ) ;
2014-05-30 07:19:04 -06:00
2014-05-30 08:17:30 -06:00
if ( ApiType = = API_OPENGL )
{
out . Write ( " layout(std140%s) uniform VSBlock { \n " , g_ActiveConfig . backend_info . bSupportsBindingLayout ? " , binding = 2 " : " " ) ;
}
else
{
out . Write ( " cbuffer VSBlock : register(b1) { \n " ) ;
}
2014-10-29 07:15:12 -06:00
out . Write ( s_shader_uniforms ) ;
out . Write ( " }; \n " ) ;
2014-05-30 08:17:30 -06:00
}
2014-11-13 15:26:49 -07:00
2015-04-05 18:17:57 -06:00
if ( g_ActiveConfig . backend_info . bSupportsBBox & & g_ActiveConfig . bBBoxEnable )
2014-11-13 15:26:49 -07:00
{
2014-12-04 19:01:20 -07:00
if ( ApiType = = API_OPENGL )
{
out . Write (
" layout(std140, binding = 3) buffer BBox { \n "
2014-12-22 11:10:35 -07:00
" \t int4 bbox_data; \n "
2014-12-04 19:01:20 -07:00
" }; \n "
) ;
}
else
{
out . Write (
" globallycoherent RWBuffer<int> bbox_data : register(u2); \n "
) ;
}
2014-11-13 15:26:49 -07:00
}
2014-12-21 04:52:14 -07:00
out . Write ( " struct VS_OUTPUT { \n " ) ;
2016-03-07 17:05:52 -07:00
GenerateVSOutputMembers < T > ( out , ApiType , " " ) ;
2014-12-21 04:52:14 -07:00
out . Write ( " }; \n " ) ;
2014-10-24 18:05:49 -06:00
2015-01-13 02:55:25 -07:00
const bool forced_early_z = g_ActiveConfig . backend_info . bSupportsEarlyZ & & bpmem . UseEarlyDepthTest ( )
2015-01-22 08:38:36 -07:00
& & ( g_ActiveConfig . bFastDepthCalc | | bpmem . alpha_test . TestResult ( ) = = AlphaTest : : UNDETERMINED )
// We can't allow early_ztest for zfreeze because depth is overridden per-pixel.
// This means it's impossible for zcomploc to be emulated on a zfrozen polygon.
2015-02-20 14:34:34 -07:00
& & ! ( bpmem . zmode . testenable & & bpmem . genMode . zfreeze ) ;
const bool per_pixel_depth = ( bpmem . ztex2 . op ! = ZTEXTURE_DISABLE & & bpmem . UseLateDepthTest ( ) )
| | ( ! g_ActiveConfig . bFastDepthCalc & & bpmem . zmode . testenable & & ! forced_early_z )
| | ( bpmem . zmode . testenable & & bpmem . genMode . zfreeze ) ;
2013-03-31 15:29:33 -06:00
2014-03-17 02:02:45 -06:00
if ( forced_early_z )
{
// Zcomploc (aka early_ztest) is a way to control whether depth test is done before
// or after texturing and alpha test. PC graphics APIs used to provide no way to emulate
// this feature properly until 2012: Depth tests were always done after alpha testing.
// Most importantly, it was not possible to write to the depth buffer without also writing
// a color value (unless color writing was disabled altogether).
2015-10-17 05:43:22 -06:00
// OpenGL 4.2 actually provides two extensions which can force an early z test:
// * ARB_image_load_store has 'layout(early_fragment_tests)' which forces the driver to do z and stencil tests early.
// * ARB_conservative_depth has 'layout(depth_unchanged) which signals to the driver that it can make optimisations
// which assume the pixel shader won't update the depth buffer.
2015-11-02 13:17:43 -07:00
// early_fragment_tests is the best option, as it requires the driver to do early-z and defines early-z exactly as
2015-10-17 05:43:22 -06:00
// we expect, with discard causing the shader to exit with only the depth buffer updated.
2015-11-02 13:17:43 -07:00
// Conservative depth's 'depth_unchanged' only hints to the driver that an early-z optimisation can be made and
2015-10-17 05:43:22 -06:00
// doesn't define what will happen if we discard the fragment. But the way modern graphics hardware is implemented
// means it is not unreasonable to expect the the same behaviour as early_fragment_tests.
2015-11-02 13:17:43 -07:00
// We can also assume that if a driver has gone out of its way to support conservative depth and not image_load_store
2015-10-17 05:43:22 -06:00
// as required by OpenGL 4.2 that it will be doing the optimisation.
// If the driver doesn't actually do an early z optimisation, ZCompLoc will be broken and depth will only be written
// if the alpha test passes.
// We support Conservative as a fallback, because many drivers based on Mesa haven't implemented all of the
// ARB_image_load_store extension yet.
2014-03-17 02:02:45 -06:00
// D3D11 also has a way to force the driver to enable early-z, so we're fine here.
if ( ApiType = = API_OPENGL )
{
2015-10-17 05:43:22 -06:00
// This is a #define which signals whatever early-z method the driver supports.
out . Write ( " FORCE_EARLY_Z; \n " ) ;
2014-03-17 02:02:45 -06:00
}
else
{
out . Write ( " [earlydepthstencil] \n " ) ;
}
}
2015-12-26 14:00:23 -07:00
else if ( bpmem . UseEarlyDepthTest ( ) & & ( g_ActiveConfig . bFastDepthCalc | | bpmem . alpha_test . TestResult ( ) = = AlphaTest : : UNDETERMINED ) )
2014-03-17 02:02:45 -06:00
{
static bool warn_once = true ;
if ( warn_once )
WARN_LOG ( VIDEO , " Early z test enabled but not possible to emulate with current configuration. Make sure to enable fast depth calculations. If this message still shows up your hardware isn't able to emulate the feature properly (a GPU with D3D 11.0 / OGL 4.2 support is required). " ) ;
warn_once = false ;
}
2011-06-04 13:56:18 -06:00
2015-12-12 05:00:08 -07:00
uid_data - > msaa = g_ActiveConfig . iMultisamples > 1 ;
uid_data - > ssaa = g_ActiveConfig . iMultisamples > 1 & & g_ActiveConfig . bSSAA ;
2012-12-28 13:18:39 -07:00
if ( ApiType = = API_OPENGL )
2010-10-20 23:22:18 -06:00
{
2013-11-24 17:06:29 -07:00
out . Write ( " out vec4 ocol0; \n " ) ;
2011-12-25 22:15:54 -07:00
if ( dstAlphaMode = = DSTALPHA_DUAL_SOURCE_BLEND )
2013-09-03 12:56:24 -06:00
out . Write ( " out vec4 ocol1; \n " ) ;
2013-04-25 06:01:07 -06:00
2013-03-15 10:19:52 -06:00
if ( per_pixel_depth )
2013-03-26 15:16:29 -06:00
out . Write ( " #define depth gl_FragDepth \n " ) ;
2008-12-07 22:30:24 -07:00
2014-10-30 16:29:56 -06:00
uid_data - > stereo = g_ActiveConfig . iStereoMode > 0 ;
2014-12-17 01:18:40 -07:00
if ( g_ActiveConfig . backend_info . bSupportsGeometryShaders )
{
out . Write ( " in VertexData { \n " ) ;
2016-03-28 21:35:16 -06:00
GenerateVSOutputMembers < T > ( out , ApiType , GetInterpolationQualifier ( true , true ) ) ;
2014-12-16 15:52:42 -07:00
2014-12-17 01:18:40 -07:00
if ( g_ActiveConfig . iStereoMode > 0 )
2016-03-28 21:35:16 -06:00
out . Write ( " \t flat int layer; \n " ) ;
2014-12-16 15:52:42 -07:00
2014-12-17 01:18:40 -07:00
out . Write ( " }; \n " ) ;
}
else
{
2016-03-07 17:05:52 -07:00
out . Write ( " %s in float4 colors_0; \n " , GetInterpolationQualifier ( ) ) ;
out . Write ( " %s in float4 colors_1; \n " , GetInterpolationQualifier ( ) ) ;
2014-12-17 15:44:05 -07:00
// compute window position if needed because binding semantic WPOS is not widely supported
// Let's set up attributes
for ( unsigned int i = 0 ; i < numTexgen ; + + i )
{
2016-03-07 17:05:52 -07:00
out . Write ( " %s in float3 uv%d; \n " , GetInterpolationQualifier ( ) , i ) ;
2014-12-17 15:44:05 -07:00
}
2016-03-07 17:05:52 -07:00
out . Write ( " %s in float4 clipPos; \n " , GetInterpolationQualifier ( ) ) ;
2014-12-17 15:44:05 -07:00
if ( g_ActiveConfig . bEnablePixelLighting )
{
2016-03-07 17:05:52 -07:00
out . Write ( " %s in float3 Normal; \n " , GetInterpolationQualifier ( ) ) ;
out . Write ( " %s in float3 WorldPos; \n " , GetInterpolationQualifier ( ) ) ;
2014-12-17 15:44:05 -07:00
}
2014-12-17 01:18:40 -07:00
}
2014-10-26 17:29:37 -06:00
2014-10-24 18:05:49 -06:00
out . Write ( " void main() \n { \n " ) ;
2013-08-12 10:21:35 -06:00
2014-12-17 15:44:05 -07:00
if ( g_ActiveConfig . backend_info . bSupportsGeometryShaders )
2010-11-24 12:13:19 -07:00
{
2014-12-17 15:44:05 -07:00
for ( unsigned int i = 0 ; i < numTexgen ; + + i )
2014-12-21 04:52:14 -07:00
out . Write ( " \t float3 uv%d = tex%d; \n " , i , i ) ;
}
2014-10-24 18:05:49 -06:00
2014-03-30 04:36:57 -06:00
out . Write ( " \t float4 rawpos = gl_FragCoord; \n " ) ;
2010-10-20 23:22:18 -06:00
}
2013-09-22 10:07:21 -06:00
else // D3D
2010-10-20 23:22:18 -06:00
{
2013-03-26 15:16:29 -06:00
out . Write ( " void main( \n " ) ;
2013-09-22 10:07:21 -06:00
out . Write ( " out float4 ocol0 : SV_Target0,%s%s \n in float4 rawpos : SV_Position, \n " ,
dstAlphaMode = = DSTALPHA_DUAL_SOURCE_BLEND ? " \n out float4 ocol1 : SV_Target1, " : " " ,
2015-02-20 14:34:34 -07:00
per_pixel_depth ? " \n out float depth : SV_Depth, " : " " ) ;
2011-06-04 13:56:18 -06:00
2016-03-07 17:05:52 -07:00
out . Write ( " in %s float4 colors_0 : COLOR0, \n " , GetInterpolationQualifier ( ) ) ;
out . Write ( " in %s float4 colors_1 : COLOR1 \n " , GetInterpolationQualifier ( ) ) ;
2008-12-07 22:30:24 -07:00
2012-12-28 13:18:39 -07:00
// compute window position if needed because binding semantic WPOS is not widely supported
2013-10-15 06:48:40 -06:00
for ( unsigned int i = 0 ; i < numTexgen ; + + i )
2016-03-07 17:05:52 -07:00
out . Write ( " , \n in %s float3 uv%d : TEXCOORD%d " , GetInterpolationQualifier ( ) , i , i ) ;
out . Write ( " , \n in %s float4 clipPos : TEXCOORD%d " , GetInterpolationQualifier ( ) , numTexgen ) ;
2014-04-04 17:17:14 -06:00
if ( g_ActiveConfig . bEnablePixelLighting )
2015-01-02 05:44:04 -07:00
{
2016-03-07 17:05:52 -07:00
out . Write ( " , \n in %s float3 Normal : TEXCOORD%d " , GetInterpolationQualifier ( ) , numTexgen + 1 ) ;
out . Write ( " , \n in %s float3 WorldPos : TEXCOORD%d " , GetInterpolationQualifier ( ) , numTexgen + 2 ) ;
2015-01-02 05:44:04 -07:00
}
2014-11-16 05:47:28 -07:00
uid_data - > stereo = g_ActiveConfig . iStereoMode > 0 ;
if ( g_ActiveConfig . iStereoMode > 0 )
out . Write ( " , \n in uint layer : SV_RenderTargetArrayIndex \n " ) ;
2013-10-15 06:48:40 -06:00
out . Write ( " ) { \n " ) ;
2009-02-18 21:41:58 -07:00
}
2012-08-10 10:57:37 -06:00
2014-03-06 11:27:11 -07:00
out . Write ( " \t int4 c0 = " I_COLORS " [1], c1 = " I_COLORS " [2], c2 = " I_COLORS " [3], prev = " I_COLORS " [0]; \n "
2014-03-06 12:08:47 -07:00
" \t int4 rastemp = int4(0, 0, 0, 0), textemp = int4(0, 0, 0, 0), konsttemp = int4(0, 0, 0, 0); \n "
" \t int3 comp16 = int3(1, 256, 0), comp24 = int3(1, 256, 256*256); \n "
" \t int alphabump=0; \n "
" \t int3 tevcoord=int3(0, 0, 0); \n "
2014-03-20 13:34:51 -06:00
" \t int2 wrappedcoord=int2(0,0), tempcoord=int2(0,0); \n "
" \t int4 tevin_a=int4(0,0,0,0),tevin_b=int4(0,0,0,0),tevin_c=int4(0,0,0,0),tevin_d=int4(0,0,0,0); \n \n " ) ; // tev combiner inputs
2011-06-04 13:56:18 -06:00
2015-01-20 15:06:24 -07:00
// On GLSL, input variables must not be assigned to.
// This is why we declare these variables locally instead.
out . Write ( " \t float4 col0 = colors_0; \n " ) ;
out . Write ( " \t float4 col1 = colors_1; \n " ) ;
2014-04-04 17:17:14 -06:00
if ( g_ActiveConfig . bEnablePixelLighting )
2010-09-22 20:17:48 -06:00
{
2013-10-15 06:38:26 -06:00
out . Write ( " \t float3 _norm0 = normalize(Normal.xyz); \n \n " ) ;
2015-01-02 05:44:04 -07:00
out . Write ( " \t float3 pos = WorldPos; \n " ) ;
2010-09-22 20:17:48 -06:00
2013-10-27 07:43:29 -06:00
out . Write ( " \t int4 lacc; \n "
2015-01-21 15:55:32 -07:00
" \t float3 ldir, h, cosAttn, distAttn; \n "
2013-03-26 15:16:29 -06:00
" \t float dist, dist2, attn; \n " ) ;
2013-03-31 15:29:33 -06:00
2014-05-30 08:17:30 -06:00
// TODO: Our current constant usage code isn't able to handle more than one buffer.
// So we can't mark the VS constant as used here. But keep them here as reference.
2014-05-30 07:59:29 -06:00
//out.SetConstantsUsed(C_PLIGHT_COLORS, C_PLIGHT_COLORS+7); // TODO: Can be optimized further
//out.SetConstantsUsed(C_PLIGHTS, C_PLIGHTS+31); // TODO: Can be optimized further
//out.SetConstantsUsed(C_PMATERIALS, C_PMATERIALS+3);
2014-10-21 00:52:45 -06:00
uid_data - > components = components ;
2014-12-25 16:56:12 -07:00
GenerateLightingShader < T > ( out , uid_data - > lighting , components , " colors_ " , " col " ) ;
}
2009-04-14 21:55:38 -06:00
2010-01-11 20:39:14 -07:00
// HACK to handle cases where the tex gen is not enabled
2010-07-06 07:14:51 -06:00
if ( numTexgen = = 0 )
2010-01-11 20:39:14 -07:00
{
2014-03-01 06:08:45 -07:00
out . Write ( " \t int2 fixpoint_uv0 = int2(0, 0); \n \n " ) ;
2010-01-11 20:39:14 -07:00
}
else
2009-07-26 03:52:35 -06:00
{
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_TEXDIMS , C_TEXDIMS + numTexgen - 1 ) ;
2012-08-06 17:02:04 -06:00
for ( unsigned int i = 0 ; i < numTexgen ; + + i )
2010-01-11 20:39:14 -07:00
{
2015-02-18 20:29:02 -07:00
out . Write ( " \t int2 fixpoint_uv%d = itrunc( " , i ) ;
2010-01-11 20:39:14 -07:00
// optional perspective divides
2014-10-21 00:52:45 -06:00
uid_data - > texMtxInfo_n_projection | = xfmem . texMtxInfo [ i ] . projection < < i ;
2014-04-27 12:59:04 -06:00
if ( xfmem . texMtxInfo [ i ] . projection = = XF_TEXPROJ_STQ )
2010-11-30 21:26:21 -07:00
{
2014-03-30 04:36:57 -06:00
out . Write ( " (uv%d.z == 0.0 ? uv%d.xy : uv%d.xy / uv%d.z) " , i , i , i , i ) ;
2010-11-30 21:26:21 -07:00
}
2014-03-30 04:36:57 -06:00
else
{
out . Write ( " uv%d.xy " , i ) ;
}
2014-07-16 09:24:43 -06:00
out . Write ( " * " I_TEXDIMS " [%d].zw * 128.0); \n " , i ) ;
2014-01-29 07:28:22 -07:00
// TODO: S24 overflows here?
2010-01-11 20:39:14 -07:00
}
}
2008-12-07 22:30:24 -07:00
2010-07-06 07:14:51 -06:00
// indirect texture map lookup
2013-01-28 14:51:15 -07:00
int nIndirectStagesUsed = 0 ;
if ( bpmem . genMode . numindstages > 0 )
{
for ( unsigned int i = 0 ; i < numStages ; + + i )
{
if ( bpmem . tevind [ i ] . IsActive ( ) & & bpmem . tevind [ i ] . bt < bpmem . genMode . numindstages )
nIndirectStagesUsed | = 1 < < bpmem . tevind [ i ] . bt ;
}
}
2014-10-21 00:52:45 -06:00
uid_data - > nIndirectStagesUsed = nIndirectStagesUsed ;
2011-12-25 22:15:54 -07:00
for ( u32 i = 0 ; i < bpmem . genMode . numindstages ; + + i )
2009-07-26 03:52:35 -06:00
{
2013-01-28 14:51:15 -07:00
if ( nIndirectStagesUsed & ( 1 < < i ) )
2009-07-26 03:52:35 -06:00
{
2012-08-06 17:16:02 -06:00
unsigned int texcoord = bpmem . tevindref . getTexCoord ( i ) ;
unsigned int texmap = bpmem . tevindref . getTexMap ( i ) ;
2008-12-07 22:30:24 -07:00
2014-10-21 00:52:45 -06:00
uid_data - > SetTevindrefValues ( i , texcoord , texmap ) ;
2011-01-09 07:13:24 -07:00
if ( texcoord < numTexgen )
2012-09-02 12:00:15 -06:00
{
out . SetConstantsUsed ( C_INDTEXSCALE + i / 2 , C_INDTEXSCALE + i / 2 ) ;
2015-09-04 22:45:35 -06:00
out . Write ( " \t tempcoord = fixpoint_uv%d >> " I_INDTEXSCALE " [%d].%s; \n " , texcoord , i / 2 , ( i & 1 ) ? " zw " : " xy " ) ;
2012-09-02 12:00:15 -06:00
}
2010-07-06 07:14:51 -06:00
else
2014-01-29 07:52:24 -07:00
out . Write ( " \t tempcoord = int2(0, 0); \n " ) ;
2009-04-11 01:05:57 -06:00
2013-08-14 07:40:24 -06:00
out . Write ( " \t int3 iindtex%d = " , i ) ;
2014-01-29 07:52:24 -07:00
SampleTexture < T > ( out , " (float2(tempcoord)/128.0) " , " abg " , texmap , ApiType ) ;
2010-07-06 07:14:51 -06:00
}
}
2008-12-07 22:30:24 -07:00
2012-09-02 06:31:37 -06:00
// Uid fields for BuildSwapModeTable are set in WriteStage
2013-09-04 13:56:03 -06:00
char swapModeTable [ 4 ] [ 5 ] ;
const char * swapColors = " rgba " ;
for ( int i = 0 ; i < 4 ; i + + )
{
swapModeTable [ i ] [ 0 ] = swapColors [ bpmem . tevksel [ i * 2 ] . swap1 ] ;
swapModeTable [ i ] [ 1 ] = swapColors [ bpmem . tevksel [ i * 2 ] . swap2 ] ;
swapModeTable [ i ] [ 2 ] = swapColors [ bpmem . tevksel [ i * 2 + 1 ] . swap1 ] ;
swapModeTable [ i ] [ 3 ] = swapColors [ bpmem . tevksel [ i * 2 + 1 ] . swap2 ] ;
swapModeTable [ i ] [ 4 ] = ' \0 ' ;
}
2012-08-06 17:02:04 -06:00
for ( unsigned int i = 0 ; i < numStages ; i + + )
2013-08-14 10:24:47 -06:00
WriteStage < T > ( out , uid_data , i , ApiType , swapModeTable ) ; // build the equation for this stage
2010-06-29 08:40:37 -06:00
2013-06-22 13:24:21 -06:00
# define MY_STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str)))
2014-04-04 17:17:14 -06:00
bool enable_pl = g_ActiveConfig . bEnablePixelLighting ;
2014-10-25 13:02:12 -06:00
uid_data - > num_values = ( enable_pl ) ? sizeof ( * uid_data ) : MY_STRUCT_OFFSET ( * uid_data , stagehash [ numStages ] ) ;
2013-06-22 13:24:21 -06:00
2011-12-25 22:15:54 -07:00
if ( numStages )
2010-05-22 13:55:07 -06:00
{
// The results of the last texenv stage are put onto the screen,
// regardless of the used destination register
2014-03-10 05:30:55 -06:00
if ( bpmem . combiners [ numStages - 1 ] . colorC . dest ! = 0 )
2012-08-11 08:54:46 -06:00
{
2014-03-06 11:27:11 -07:00
out . Write ( " \t prev.rgb = %s; \n " , tevCOutputTable [ bpmem . combiners [ numStages - 1 ] . colorC . dest ] ) ;
2012-08-11 08:54:46 -06:00
}
2014-03-10 05:30:55 -06:00
if ( bpmem . combiners [ numStages - 1 ] . alphaC . dest ! = 0 )
2012-08-11 08:54:46 -06:00
{
2014-03-06 11:27:11 -07:00
out . Write ( " \t prev.a = %s; \n " , tevAOutputTable [ bpmem . combiners [ numStages - 1 ] . alphaC . dest ] ) ;
2012-08-11 08:54:46 -06:00
}
2010-05-22 13:55:07 -06:00
}
2014-03-06 11:27:11 -07:00
out . Write ( " \t prev = prev & 255; \n " ) ;
2011-06-04 13:56:18 -06:00
2013-01-08 09:18:45 -07:00
AlphaTest : : TEST_RESULT Pretest = bpmem . alpha_test . TestResult ( ) ;
2014-10-21 00:52:45 -06:00
uid_data - > Pretest = Pretest ;
2013-08-19 13:27:54 -06:00
2013-10-28 23:23:17 -06:00
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
2013-08-19 13:27:54 -06:00
// (in this case we need to write a depth value if depth test passes regardless of the alpha testing result)
if ( Pretest = = AlphaTest : : UNDETERMINED | | ( Pretest = = AlphaTest : : FAIL & & bpmem . UseLateDepthTest ( ) ) )
2013-03-29 14:53:57 -06:00
WriteAlphaTest < T > ( out , uid_data , ApiType , dstAlphaMode , per_pixel_depth ) ;
2012-08-10 10:57:37 -06:00
2015-05-07 14:48:49 -06:00
if ( bpmem . genMode . zfreeze )
2013-03-26 15:16:29 -06:00
{
2015-05-07 14:48:49 -06:00
out . SetConstantsUsed ( C_ZSLOPE , C_ZSLOPE ) ;
out . SetConstantsUsed ( C_EFBSCALE , C_EFBSCALE ) ;
out . Write ( " \t float2 screenpos = rawpos.xy * " I_EFBSCALE " .xy; \n " ) ;
// Opengl has reversed vertical screenspace coordiantes
if ( ApiType = = API_OPENGL )
2015-11-30 14:35:46 -07:00
out . Write ( " \t screenpos.y = %i.0 - screenpos.y; \n " , EFB_HEIGHT ) ;
2015-05-07 14:48:49 -06:00
out . Write ( " \t int zCoord = int( " I_ZSLOPE " .z + " I_ZSLOPE " .x * screenpos.x + " I_ZSLOPE " .y * screenpos.y); \n " ) ;
}
else if ( ! g_ActiveConfig . bFastDepthCalc )
{
// FastDepth means to trust the depth generated in perspective division.
// It should be correct, but it seems not to be as accurate as required. TODO: Find out why!
// For disabled FastDepth we just calculate the depth value again.
// The performance impact of this additional calculation doesn't matter, but it prevents
// the host GPU driver from performing any early depth test optimizations.
2013-03-26 15:16:29 -06:00
out . SetConstantsUsed ( C_ZBIAS + 1 , C_ZBIAS + 1 ) ;
2013-05-09 09:48:48 -06:00
// the screen space depth value = far z + (clip z / clip w) * z range
2015-05-07 15:49:09 -06:00
out . Write ( " \t int zCoord = " I_ZBIAS " [1].x + int((clipPos.z / clipPos.w) * float( " I_ZBIAS " [1].y)); \n " ) ;
2013-03-26 15:16:29 -06:00
}
2015-05-07 14:48:49 -06:00
else
{
2015-05-24 06:44:25 -06:00
if ( ApiType = = API_D3D )
out . Write ( " \t int zCoord = int((1.0 - rawpos.z) * 16777216.0); \n " ) ;
else
out . Write ( " \t int zCoord = int(rawpos.z * 16777216.0); \n " ) ;
2015-05-07 14:48:49 -06:00
}
2015-05-24 03:57:02 -06:00
out . Write ( " \t zCoord = clamp(zCoord, 0, 0xFFFFFF); \n " ) ;
2012-08-10 10:57:37 -06:00
2013-03-14 17:52:50 -06:00
// depth texture can safely be ignored if the result won't be written to the depth buffer (early_ztest) and isn't used for fog either
2013-06-17 04:51:57 -06:00
const bool skip_ztexture = ! per_pixel_depth & & ! bpmem . fog . c_proj_fsel . fsel ;
2013-06-17 04:05:47 -06:00
2014-10-21 00:52:45 -06:00
uid_data - > ztex_op = bpmem . ztex2 . op ;
uid_data - > per_pixel_depth = per_pixel_depth ;
uid_data - > forced_early_z = forced_early_z ;
uid_data - > fast_depth_calc = g_ActiveConfig . bFastDepthCalc ;
uid_data - > early_ztest = bpmem . UseEarlyDepthTest ( ) ;
uid_data - > fog_fsel = bpmem . fog . c_proj_fsel . fsel ;
2014-12-25 00:34:22 -07:00
uid_data - > zfreeze = bpmem . genMode . zfreeze ;
2013-03-14 17:52:50 -06:00
2013-05-10 04:51:06 -06:00
// Note: z-textures are not written to depth buffer if early depth test is used
2013-07-22 06:38:09 -06:00
if ( per_pixel_depth & & bpmem . UseEarlyDepthTest ( ) )
2014-12-25 00:34:22 -07:00
{
2015-05-24 06:44:25 -06:00
if ( ApiType = = API_D3D )
out . Write ( " \t depth = 1.0 - float(zCoord) / 16777216.0; \n " ) ;
else
out . Write ( " \t depth = float(zCoord) / 16777216.0; \n " ) ;
2014-12-25 00:34:22 -07:00
}
2013-06-17 04:05:47 -06:00
2013-06-29 15:14:08 -06:00
// Note: depth texture output is only written to depth buffer if late depth test is used
// theoretical final depth value is used for fog calculation, though, so we have to emulate ztextures anyway
2013-03-14 17:52:50 -06:00
if ( bpmem . ztex2 . op ! = ZTEXTURE_DISABLE & & ! skip_ztexture )
2012-03-24 21:01:47 -06:00
{
2014-03-06 11:27:11 -07:00
// use the texture input of the last texture stage (textemp), hopefully this has been read and is in correct format...
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_ZBIAS , C_ZBIAS + 1 ) ;
2014-03-06 11:27:11 -07:00
out . Write ( " \t zCoord = idot( " I_ZBIAS " [0].xyzw, textemp.xyzw) + " I_ZBIAS " [1].w %s; \n " ,
2013-01-08 08:40:15 -07:00
( bpmem . ztex2 . op = = ZTEXTURE_ADD ) ? " + zCoord " : " " ) ;
2014-03-01 10:25:21 -07:00
out . Write ( " \t zCoord = zCoord & 0xFFFFFF; \n " ) ;
2012-08-10 10:57:37 -06:00
}
2013-06-29 15:14:08 -06:00
2013-07-22 06:38:09 -06:00
if ( per_pixel_depth & & bpmem . UseLateDepthTest ( ) )
2014-12-25 00:34:22 -07:00
{
2015-05-24 06:44:25 -06:00
if ( ApiType = = API_D3D )
out . Write ( " \t depth = 1.0 - float(zCoord) / 16777216.0; \n " ) ;
else
out . Write ( " \t depth = float(zCoord) / 16777216.0; \n " ) ;
2014-12-25 00:34:22 -07:00
}
2010-10-19 21:11:22 -06:00
2012-08-10 10:57:37 -06:00
if ( dstAlphaMode = = DSTALPHA_ALPHA_PASS )
2012-09-02 12:00:15 -06:00
{
out . SetConstantsUsed ( C_ALPHA , C_ALPHA ) ;
2014-03-06 11:27:11 -07:00
out . Write ( " \t ocol0 = float4(float3(prev.rgb), float( " I_ALPHA " .a)) / 255.0; \n " ) ;
2012-09-02 12:00:15 -06:00
}
2012-03-24 21:01:47 -06:00
else
{
2013-06-18 06:52:36 -06:00
WriteFog < T > ( out , uid_data ) ;
2014-03-06 11:27:11 -07:00
out . Write ( " \t ocol0 = float4(prev) / 255.0; \n " ) ;
2010-07-06 07:14:51 -06:00
}
2012-03-24 21:01:47 -06:00
2013-04-25 06:01:07 -06:00
// Use dual-source color blending to perform dst alpha in a single pass
2012-08-10 10:57:37 -06:00
if ( dstAlphaMode = = DSTALPHA_DUAL_SOURCE_BLEND )
2012-03-24 21:01:47 -06:00
{
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_ALPHA , C_ALPHA ) ;
2013-09-22 10:07:21 -06:00
// Colors will be blended against the alpha from ocol1 and
// the alpha from ocol0 will be written to the framebuffer.
2014-03-06 11:27:11 -07:00
out . Write ( " \t ocol1 = float4(prev) / 255.0; \n " ) ;
2013-10-10 12:36:55 -06:00
out . Write ( " \t ocol0.a = float( " I_ALPHA " .a) / 255.0; \n " ) ;
2012-03-24 21:01:47 -06:00
}
2013-04-25 06:01:07 -06:00
2015-04-05 18:17:57 -06:00
if ( g_ActiveConfig . backend_info . bSupportsBBox & & g_ActiveConfig . bBBoxEnable & & BoundingBox : : active )
2014-11-13 15:26:49 -07:00
{
uid_data - > bounding_box = true ;
2014-12-09 11:32:24 -07:00
const char * atomic_op = ApiType = = API_OPENGL ? " atomic " : " Interlocked " ;
out . Write (
" \t if(bbox_data[0] > int(rawpos.x)) %sMin(bbox_data[0], int(rawpos.x)); \n "
" \t if(bbox_data[1] < int(rawpos.x)) %sMax(bbox_data[1], int(rawpos.x)); \n "
" \t if(bbox_data[2] > int(rawpos.y)) %sMin(bbox_data[2], int(rawpos.y)); \n "
" \t if(bbox_data[3] < int(rawpos.y)) %sMax(bbox_data[3], int(rawpos.y)); \n " ,
atomic_op , atomic_op , atomic_op , atomic_op ) ;
2014-11-13 15:26:49 -07:00
}
2012-08-06 17:02:04 -06:00
out . Write ( " } \n " ) ;
2013-03-29 14:53:57 -06:00
2015-11-02 19:47:05 -07:00
return out ;
2008-12-07 22:30:24 -07:00
}
2009-10-28 21:28:38 -06:00
2013-03-26 16:21:08 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static void WriteStage ( T & out , pixel_shader_uid_data * uid_data , int n , API_TYPE ApiType , const char swapModeTable [ 4 ] [ 5 ] )
2008-12-07 22:30:24 -07:00
{
2010-07-06 07:14:51 -06:00
int texcoord = bpmem . tevorders [ n / 2 ] . getTexCoord ( n & 1 ) ;
bool bHasTexCoord = ( u32 ) texcoord < bpmem . genMode . numtexgens ;
2014-05-04 22:36:09 -06:00
bool bHasIndStage = bpmem . tevind [ n ] . bt < bpmem . genMode . numindstages ;
2010-07-06 07:14:51 -06:00
// HACK to handle cases where the tex gen is not enabled
if ( ! bHasTexCoord )
texcoord = 0 ;
2009-03-04 22:10:25 -07:00
2014-07-16 09:24:43 -06:00
out . Write ( " \n \t // TEV stage %d \n " , n ) ;
2011-09-29 13:52:13 -06:00
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . hasindstage = bHasIndStage ;
uid_data - > stagehash [ n ] . tevorders_texcoord = texcoord ;
2010-07-06 07:14:51 -06:00
if ( bHasIndStage )
2009-07-26 03:52:35 -06:00
{
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . tevind = bpmem . tevind [ n ] . hex & 0x7FFFFF ;
2012-08-07 06:36:56 -06:00
2014-03-01 06:08:45 -07:00
out . Write ( " \t // indirect op \n " ) ;
2013-08-14 08:08:50 -06:00
// perform the indirect op on the incoming regular coordinates using iindtex%d as the offset coords
2010-07-06 07:14:51 -06:00
if ( bpmem . tevind [ n ] . bs ! = ITBA_OFF )
2009-07-26 03:52:35 -06:00
{
2013-09-12 05:55:38 -06:00
const char * tevIndAlphaSel [ ] = { " " , " x " , " y " , " z " } ;
2014-03-01 10:25:21 -07:00
const char * tevIndAlphaMask [ ] = { " 248 " , " 224 " , " 240 " , " 248 " } ; // 0b11111000, 0b11100000, 0b11110000, 0b11111000
2013-08-14 10:24:47 -06:00
out . Write ( " alphabump = iindtex%d.%s & %s; \n " ,
2011-06-04 13:56:18 -06:00
bpmem . tevind [ n ] . bt ,
tevIndAlphaSel [ bpmem . tevind [ n ] . bs ] ,
2013-08-14 08:08:50 -06:00
tevIndAlphaMask [ bpmem . tevind [ n ] . fmt ] ) ;
2010-07-06 07:14:51 -06:00
}
2013-09-12 05:55:38 -06:00
else
{
// TODO: Should we reset alphabump to 0 here?
}
2010-07-06 07:14:51 -06:00
if ( bpmem . tevind [ n ] . mid ! = 0 )
2009-07-26 03:52:35 -06:00
{
2014-05-04 22:36:09 -06:00
// format
const char * tevIndFmtMask [ ] = { " 255 " , " 31 " , " 15 " , " 7 " } ;
out . Write ( " \t int3 iindtevcrd%d = iindtex%d & %s; \n " , n , bpmem . tevind [ n ] . bt , tevIndFmtMask [ bpmem . tevind [ n ] . fmt ] ) ;
// bias - TODO: Check if this needs to be this complicated..
const char * tevIndBiasField [ ] = { " " , " x " , " y " , " xy " , " z " , " xz " , " yz " , " xyz " } ; // indexed by bias
const char * tevIndBiasAdd [ ] = { " -128 " , " 1 " , " 1 " , " 1 " } ; // indexed by fmt
if ( bpmem . tevind [ n ] . bias = = ITB_S | | bpmem . tevind [ n ] . bias = = ITB_T | | bpmem . tevind [ n ] . bias = = ITB_U )
out . Write ( " \t iindtevcrd%d.%s += int(%s); \n " , n , tevIndBiasField [ bpmem . tevind [ n ] . bias ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] ) ;
else if ( bpmem . tevind [ n ] . bias = = ITB_ST | | bpmem . tevind [ n ] . bias = = ITB_SU | | bpmem . tevind [ n ] . bias = = ITB_TU )
out . Write ( " \t iindtevcrd%d.%s += int2(%s, %s); \n " , n , tevIndBiasField [ bpmem . tevind [ n ] . bias ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] ) ;
else if ( bpmem . tevind [ n ] . bias = = ITB_STU )
out . Write ( " \t iindtevcrd%d.%s += int3(%s, %s, %s); \n " , n , tevIndBiasField [ bpmem . tevind [ n ] . bias ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] , tevIndBiasAdd [ bpmem . tevind [ n ] . fmt ] ) ;
// multiply by offset matrix and scale - calculations are likely to overflow badly,
// yet it works out since we only care about the lower 23 bits (+1 sign bit) of the result
2011-01-09 07:13:24 -07:00
if ( bpmem . tevind [ n ] . mid < = 3 )
2009-07-26 03:52:35 -06:00
{
2010-07-06 07:14:51 -06:00
int mtxidx = 2 * ( bpmem . tevind [ n ] . mid - 1 ) ;
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_INDTEXMTX + mtxidx , C_INDTEXMTX + mtxidx ) ;
2013-09-12 05:55:38 -06:00
2016-01-25 20:21:42 -07:00
out . Write ( " \t int2 indtevtrans%d = int2(idot( " I_INDTEXMTX " [%d].xyz, iindtevcrd%d), idot( " I_INDTEXMTX " [%d].xyz, iindtevcrd%d)); \n " , n , mtxidx , n , mtxidx + 1 , n ) ;
2015-09-04 22:45:35 -06:00
// TODO: should use a shader uid branch for this for better performance
2016-01-25 20:21:42 -07:00
out . Write ( " \t if ( " I_INDTEXMTX " [%d].w >= -3) indtevtrans%d = indtevtrans%d >> " I_INDTEXMTX " [%d].w + 3; \n " , mtxidx , n , n , mtxidx ) ;
out . Write ( " \t else indtevtrans%d = indtevtrans%d << (- " I_INDTEXMTX " [%d].w - 3); \n " , n , n , mtxidx ) ;
2010-07-06 07:14:51 -06:00
}
2011-01-09 07:13:24 -07:00
else if ( bpmem . tevind [ n ] . mid < = 7 & & bHasTexCoord )
2009-07-26 03:52:35 -06:00
{ // s matrix
2011-09-29 15:32:05 -06:00
_assert_ ( bpmem . tevind [ n ] . mid > = 5 ) ;
2010-07-06 07:14:51 -06:00
int mtxidx = 2 * ( bpmem . tevind [ n ] . mid - 5 ) ;
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_INDTEXMTX + mtxidx , C_INDTEXMTX + mtxidx ) ;
2013-10-10 13:09:00 -06:00
2016-01-25 20:21:42 -07:00
out . Write ( " \t int2 indtevtrans%d = int2(fixpoint_uv%d * iindtevcrd%d.xx); \n " , n , texcoord , n ) ;
2015-02-28 13:02:44 -07:00
2016-01-25 20:21:42 -07:00
out . Write ( " \t if ( " I_INDTEXMTX " [%d].w >= -8) indtevtrans%d = indtevtrans%d >> " I_INDTEXMTX " [%d].w + 8; \n " , mtxidx , n , n , mtxidx ) ;
out . Write ( " \t else indtevtrans%d = indtevtrans%d << (- " I_INDTEXMTX " [%d].w - 8); \n " , n , n , mtxidx ) ;
2010-07-06 07:14:51 -06:00
}
2011-01-09 07:13:24 -07:00
else if ( bpmem . tevind [ n ] . mid < = 11 & & bHasTexCoord )
2009-07-26 03:52:35 -06:00
{ // t matrix
2011-09-29 15:32:05 -06:00
_assert_ ( bpmem . tevind [ n ] . mid > = 9 ) ;
2010-07-06 07:14:51 -06:00
int mtxidx = 2 * ( bpmem . tevind [ n ] . mid - 9 ) ;
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_INDTEXMTX + mtxidx , C_INDTEXMTX + mtxidx ) ;
2013-10-10 13:09:00 -06:00
2016-01-25 20:21:42 -07:00
out . Write ( " \t int2 indtevtrans%d = int2(fixpoint_uv%d * iindtevcrd%d.yy); \n " , n , texcoord , n ) ;
2015-02-28 13:02:44 -07:00
2016-01-25 20:21:42 -07:00
out . Write ( " \t if ( " I_INDTEXMTX " [%d].w >= -8) indtevtrans%d = indtevtrans%d >> " I_INDTEXMTX " [%d].w + 8; \n " , mtxidx , n , n , mtxidx ) ;
out . Write ( " \t else indtevtrans%d = indtevtrans%d << (- " I_INDTEXMTX " [%d].w - 8); \n " , n , n , mtxidx ) ;
2010-07-06 07:14:51 -06:00
}
else
2013-04-24 07:21:54 -06:00
{
2014-03-01 06:08:45 -07:00
out . Write ( " \t int2 indtevtrans%d = int2(0, 0); \n " , n ) ;
2013-04-24 07:21:54 -06:00
}
2010-07-06 07:14:51 -06:00
}
else
2013-04-24 07:21:54 -06:00
{
2014-03-01 06:08:45 -07:00
out . Write ( " \t int2 indtevtrans%d = int2(0, 0); \n " , n ) ;
2013-04-24 07:21:54 -06:00
}
2010-07-06 07:14:51 -06:00
// ---------
2009-07-26 03:52:35 -06:00
// Wrapping
// ---------
2013-11-02 04:42:30 -06:00
const char * tevIndWrapStart [ ] = { " 0 " , " (256<<7) " , " (128<<7) " , " (64<<7) " , " (32<<7) " , " (16<<7) " , " 1 " } ; // TODO: Should the last one be 1 or (1<<7)?
2012-08-07 06:36:56 -06:00
2010-07-06 07:14:51 -06:00
// wrap S
if ( bpmem . tevind [ n ] . sw = = ITW_OFF )
2014-03-01 06:08:45 -07:00
out . Write ( " \t wrappedcoord.x = fixpoint_uv%d.x; \n " , texcoord ) ;
2010-07-06 07:14:51 -06:00
else if ( bpmem . tevind [ n ] . sw = = ITW_0 )
2014-03-01 06:08:45 -07:00
out . Write ( " \t wrappedcoord.x = 0; \n " ) ;
2010-07-06 07:14:51 -06:00
else
2016-01-13 08:57:01 -07:00
out . Write ( " \t wrappedcoord.x = fixpoint_uv%d.x & (%s - 1); \n " , texcoord , tevIndWrapStart [ bpmem . tevind [ n ] . sw ] ) ;
2010-07-06 07:14:51 -06:00
// wrap T
if ( bpmem . tevind [ n ] . tw = = ITW_OFF )
2014-03-01 06:08:45 -07:00
out . Write ( " \t wrappedcoord.y = fixpoint_uv%d.y; \n " , texcoord ) ;
2010-07-06 07:14:51 -06:00
else if ( bpmem . tevind [ n ] . tw = = ITW_0 )
2014-03-01 06:08:45 -07:00
out . Write ( " \t wrappedcoord.y = 0; \n " ) ;
2010-07-06 07:14:51 -06:00
else
2016-01-13 08:57:01 -07:00
out . Write ( " \t wrappedcoord.y = fixpoint_uv%d.y & (%s - 1); \n " , texcoord , tevIndWrapStart [ bpmem . tevind [ n ] . tw ] ) ;
2010-07-06 07:14:51 -06:00
if ( bpmem . tevind [ n ] . fb_addprev ) // add previous tevcoord
2014-03-01 06:08:45 -07:00
out . Write ( " \t tevcoord.xy += wrappedcoord + indtevtrans%d; \n " , n ) ;
2010-07-06 07:14:51 -06:00
else
2014-03-01 06:08:45 -07:00
out . Write ( " \t tevcoord.xy = wrappedcoord + indtevtrans%d; \n " , n ) ;
2013-09-12 05:55:38 -06:00
// Emulate s24 overflows
2015-09-04 22:45:35 -06:00
out . Write ( " \t tevcoord.xy = (tevcoord.xy << 8) >> 8; \n " ) ;
2010-07-06 07:14:51 -06:00
}
2010-08-16 16:37:04 -06:00
TevStageCombiner : : ColorCombiner & cc = bpmem . combiners [ n ] . colorC ;
TevStageCombiner : : AlphaCombiner & ac = bpmem . combiners [ n ] . alphaC ;
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . cc = cc . hex & 0xFFFFFF ;
uid_data - > stagehash [ n ] . ac = ac . hex & 0xFFFFF0 ; // Storing rswap and tswap later
2012-09-02 10:30:21 -06:00
2014-03-10 05:30:55 -06:00
if ( cc . a = = TEVCOLORARG_RASA | | cc . a = = TEVCOLORARG_RASC | |
cc . b = = TEVCOLORARG_RASA | | cc . b = = TEVCOLORARG_RASC | |
cc . c = = TEVCOLORARG_RASA | | cc . c = = TEVCOLORARG_RASC | |
cc . d = = TEVCOLORARG_RASA | | cc . d = = TEVCOLORARG_RASC | |
ac . a = = TEVALPHAARG_RASA | | ac . b = = TEVALPHAARG_RASA | |
ac . c = = TEVALPHAARG_RASA | | ac . d = = TEVALPHAARG_RASA )
2010-08-16 16:37:04 -06:00
{
2012-08-07 06:36:56 -06:00
const int i = bpmem . combiners [ n ] . alphaC . rswap ;
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . ac | = bpmem . combiners [ n ] . alphaC . rswap ;
uid_data - > stagehash [ n ] . tevksel_swap1a = bpmem . tevksel [ i * 2 ] . swap1 ;
uid_data - > stagehash [ n ] . tevksel_swap2a = bpmem . tevksel [ i * 2 ] . swap2 ;
uid_data - > stagehash [ n ] . tevksel_swap1b = bpmem . tevksel [ i * 2 + 1 ] . swap1 ;
uid_data - > stagehash [ n ] . tevksel_swap2b = bpmem . tevksel [ i * 2 + 1 ] . swap2 ;
uid_data - > stagehash [ n ] . tevorders_colorchan = bpmem . tevorders [ n / 2 ] . getColorChan ( n & 1 ) ;
2012-08-07 06:36:56 -06:00
2013-09-04 13:56:03 -06:00
const char * rasswap = swapModeTable [ bpmem . combiners [ n ] . alphaC . rswap ] ;
2014-03-06 11:27:11 -07:00
out . Write ( " \t rastemp = %s.%s; \n " , tevRasTable [ bpmem . tevorders [ n / 2 ] . getColorChan ( n & 1 ) ] , rasswap ) ;
2010-08-16 16:37:04 -06:00
}
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . tevorders_enable = bpmem . tevorders [ n / 2 ] . getEnable ( n & 1 ) ;
2010-07-06 07:14:51 -06:00
if ( bpmem . tevorders [ n / 2 ] . getEnable ( n & 1 ) )
2009-07-26 03:52:35 -06:00
{
2013-09-12 05:55:38 -06:00
int texmap = bpmem . tevorders [ n / 2 ] . getTexMap ( n & 1 ) ;
2011-12-25 22:15:54 -07:00
if ( ! bHasIndStage )
2009-07-26 03:52:35 -06:00
{
2010-07-06 07:14:51 -06:00
// calc tevcord
2014-03-10 05:30:55 -06:00
if ( bHasTexCoord )
2014-03-01 06:08:45 -07:00
out . Write ( " \t tevcoord.xy = fixpoint_uv%d; \n " , texcoord ) ;
2010-07-06 07:14:51 -06:00
else
2014-03-01 06:08:45 -07:00
out . Write ( " \t tevcoord.xy = int2(0, 0); \n " ) ;
2010-07-06 07:14:51 -06:00
}
2008-12-07 22:30:24 -07:00
2012-08-07 06:36:56 -06:00
const int i = bpmem . combiners [ n ] . alphaC . tswap ;
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . ac | = bpmem . combiners [ n ] . alphaC . tswap < < 2 ;
uid_data - > stagehash [ n ] . tevksel_swap1c = bpmem . tevksel [ i * 2 ] . swap1 ;
uid_data - > stagehash [ n ] . tevksel_swap2c = bpmem . tevksel [ i * 2 ] . swap2 ;
uid_data - > stagehash [ n ] . tevksel_swap1d = bpmem . tevksel [ i * 2 + 1 ] . swap1 ;
uid_data - > stagehash [ n ] . tevksel_swap2d = bpmem . tevksel [ i * 2 + 1 ] . swap2 ;
2013-06-22 12:41:32 -06:00
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . tevorders_texmap = bpmem . tevorders [ n / 2 ] . getTexMap ( n & 1 ) ;
2012-08-07 06:36:56 -06:00
2013-09-04 13:56:03 -06:00
const char * texswap = swapModeTable [ bpmem . combiners [ n ] . alphaC . tswap ] ;
2014-10-21 00:52:45 -06:00
uid_data - > SetTevindrefTexmap ( i , texmap ) ;
2013-08-12 05:31:29 -06:00
2014-03-06 11:27:11 -07:00
out . Write ( " \t textemp = " ) ;
2013-11-02 04:42:30 -06:00
SampleTexture < T > ( out , " (float2(tevcoord.xy)/128.0) " , texswap , texmap , ApiType ) ;
2010-07-06 07:14:51 -06:00
}
else
2013-04-24 07:21:54 -06:00
{
2014-03-06 11:27:11 -07:00
out . Write ( " \t textemp = int4(255, 255, 255, 255); \n " ) ;
2013-04-24 07:21:54 -06:00
}
2008-12-07 22:30:24 -07:00
2011-06-04 13:56:18 -06:00
2014-03-10 05:30:55 -06:00
if ( cc . a = = TEVCOLORARG_KONST | | cc . b = = TEVCOLORARG_KONST | |
cc . c = = TEVCOLORARG_KONST | | cc . d = = TEVCOLORARG_KONST | |
ac . a = = TEVALPHAARG_KONST | | ac . b = = TEVALPHAARG_KONST | |
ac . c = = TEVALPHAARG_KONST | | ac . d = = TEVALPHAARG_KONST )
2010-08-16 16:37:04 -06:00
{
2011-09-29 13:52:13 -06:00
int kc = bpmem . tevksel [ n / 2 ] . getKC ( n & 1 ) ;
int ka = bpmem . tevksel [ n / 2 ] . getKA ( n & 1 ) ;
2014-10-21 00:52:45 -06:00
uid_data - > stagehash [ n ] . tevksel_kc = kc ;
uid_data - > stagehash [ n ] . tevksel_ka = ka ;
2014-03-06 11:27:11 -07:00
out . Write ( " \t konsttemp = int4(%s, %s); \n " , tevKSelTableC [ kc ] , tevKSelTableA [ ka ] ) ;
2013-08-14 08:25:39 -06:00
2012-09-02 12:00:15 -06:00
if ( kc > 7 )
out . SetConstantsUsed ( C_KCOLORS + ( ( kc - 0xc ) % 4 ) , C_KCOLORS + ( ( kc - 0xc ) % 4 ) ) ;
if ( ka > 7 )
out . SetConstantsUsed ( C_KCOLORS + ( ( ka - 0xc ) % 4 ) , C_KCOLORS + ( ( ka - 0xc ) % 4 ) ) ;
2010-08-16 16:37:04 -06:00
}
2008-12-07 22:30:24 -07:00
2013-03-29 13:59:03 -06:00
if ( cc . d = = TEVCOLORARG_C0 | | cc . d = = TEVCOLORARG_A0 | | ac . d = = TEVALPHAARG_A0 )
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_COLORS + 1 , C_COLORS + 1 ) ;
2013-03-29 13:59:03 -06:00
2012-09-02 12:00:15 -06:00
if ( cc . d = = TEVCOLORARG_C1 | | cc . d = = TEVCOLORARG_A1 | | ac . d = = TEVALPHAARG_A1 )
out . SetConstantsUsed ( C_COLORS + 2 , C_COLORS + 2 ) ;
2013-03-29 13:59:03 -06:00
2012-09-02 12:00:15 -06:00
if ( cc . d = = TEVCOLORARG_C2 | | cc . d = = TEVCOLORARG_A2 | | ac . d = = TEVALPHAARG_A2 )
out . SetConstantsUsed ( C_COLORS + 3 , C_COLORS + 3 ) ;
2013-03-29 13:59:03 -06:00
if ( cc . dest > = GX_TEVREG0 & & cc . dest < = GX_TEVREG2 )
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_COLORS + cc . dest , C_COLORS + cc . dest ) ;
2013-03-29 13:59:03 -06:00
if ( ac . dest > = GX_TEVREG0 & & ac . dest < = GX_TEVREG2 )
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_COLORS + ac . dest , C_COLORS + ac . dest ) ;
2014-03-20 13:34:51 -06:00
2015-07-20 21:04:16 -06:00
out . Write ( " \t tevin_a = int4(%s, %s)&int4(255, 255, 255, 255); \n " , tevCInputTable [ cc . a ] , tevAInputTable [ ac . a ] ) ;
out . Write ( " \t tevin_b = int4(%s, %s)&int4(255, 255, 255, 255); \n " , tevCInputTable [ cc . b ] , tevAInputTable [ ac . b ] ) ;
out . Write ( " \t tevin_c = int4(%s, %s)&int4(255, 255, 255, 255); \n " , tevCInputTable [ cc . c ] , tevAInputTable [ ac . c ] ) ;
2014-07-16 09:24:43 -06:00
out . Write ( " \t tevin_d = int4(%s, %s); \n " , tevCInputTable [ cc . d ] , tevAInputTable [ ac . d ] ) ;
2014-03-20 13:34:51 -06:00
2014-03-01 06:08:45 -07:00
out . Write ( " \t // color combine \n " ) ;
out . Write ( " \t %s = clamp( " , tevCOutputTable [ cc . dest ] ) ;
2015-09-01 08:56:51 -06:00
if ( cc . bias ! = TEVBIAS_COMPARE )
2009-07-26 03:52:35 -06:00
{
2014-03-24 07:41:56 -06:00
WriteTevRegular ( out , " rgb " , cc . bias , cc . op , cc . clamp , cc . shift ) ;
2010-07-06 07:14:51 -06:00
}
else
2009-07-26 03:52:35 -06:00
{
2014-03-06 11:28:29 -07:00
const char * function_table [ ] =
2014-02-28 12:24:00 -07:00
{
2014-03-20 13:34:51 -06:00
" ((tevin_a.r > tevin_b.r) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_R8_GT
" ((tevin_a.r == tevin_b.r) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_R8_EQ
" ((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_GR16_GT
" ((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_GR16_EQ
" ((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_BGR24_GT
" ((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : int3(0,0,0)) " , // TEVCMP_BGR24_EQ
" (max(sign(tevin_a.rgb - tevin_b.rgb), int3(0,0,0)) * tevin_c.rgb) " , // TEVCMP_RGB8_GT
2014-04-15 15:35:54 -06:00
" ((int3(1,1,1) - sign(abs(tevin_a.rgb - tevin_b.rgb))) * tevin_c.rgb) " // TEVCMP_RGB8_EQ
2014-02-28 12:24:00 -07:00
} ;
int mode = ( cc . shift < < 1 ) | cc . op ;
2014-03-20 13:34:51 -06:00
out . Write ( " tevin_d.rgb + " ) ;
2015-10-16 21:05:50 -06:00
out . Write ( " %s " , function_table [ mode ] ) ;
2010-07-06 07:14:51 -06:00
}
2010-01-11 20:39:14 -07:00
if ( cc . clamp )
2013-08-14 10:24:47 -06:00
out . Write ( " , int3(0,0,0), int3(255,255,255)) " ) ;
2013-08-14 10:54:43 -06:00
else
out . Write ( " , int3(-1024,-1024,-1024), int3(1023,1023,1023)) " ) ;
2013-08-14 10:24:47 -06:00
out . Write ( " ; \n " ) ;
2012-08-11 08:54:46 -06:00
2013-08-14 10:54:43 -06:00
out . Write ( " \t // alpha combine \n " ) ;
out . Write ( " \t %s = clamp( " , tevAOutputTable [ ac . dest ] ) ;
2015-09-01 08:56:51 -06:00
if ( ac . bias ! = TEVBIAS_COMPARE )
2009-07-26 03:52:35 -06:00
{
2014-03-24 07:41:56 -06:00
WriteTevRegular ( out , " a " , ac . bias , ac . op , ac . clamp , ac . shift ) ;
2010-07-06 07:14:51 -06:00
}
else
2009-07-26 03:52:35 -06:00
{
2014-03-06 11:28:29 -07:00
const char * function_table [ ] =
2014-02-28 12:24:00 -07:00
{
2014-03-20 13:34:51 -06:00
" ((tevin_a.r > tevin_b.r) ? tevin_c.a : 0) " , // TEVCMP_R8_GT
" ((tevin_a.r == tevin_b.r) ? tevin_c.a : 0) " , // TEVCMP_R8_EQ
" ((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0) " , // TEVCMP_GR16_GT
" ((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0) " , // TEVCMP_GR16_EQ
" ((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0) " , // TEVCMP_BGR24_GT
" ((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0) " , // TEVCMP_BGR24_EQ
" ((tevin_a.a > tevin_b.a) ? tevin_c.a : 0) " , // TEVCMP_A8_GT
" ((tevin_a.a == tevin_b.a) ? tevin_c.a : 0) " // TEVCMP_A8_EQ
2014-02-28 12:24:00 -07:00
} ;
int mode = ( ac . shift < < 1 ) | ac . op ;
2014-03-20 13:34:51 -06:00
out . Write ( " tevin_d.a + " ) ;
2015-10-16 21:05:50 -06:00
out . Write ( " %s " , function_table [ mode ] ) ;
2010-07-06 07:14:51 -06:00
}
2010-01-11 20:39:14 -07:00
if ( ac . clamp )
2013-08-14 10:24:47 -06:00
out . Write ( " , 0, 255) " ) ;
2013-08-14 10:54:43 -06:00
else
out . Write ( " , -1024, 1023) " ) ;
2014-07-16 09:24:43 -06:00
out . Write ( " ; \n " ) ;
2008-12-07 22:30:24 -07:00
}
2014-03-24 07:41:56 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static void WriteTevRegular ( T & out , const char * components , int bias , int op , int clamp , int shift )
2014-03-24 07:41:56 -06:00
{
const char * tevScaleTableLeft [ ] =
{
" " , // SCALE_1
" << 1 " , // SCALE_2
" << 2 " , // SCALE_4
" " , // DIVIDE_2
} ;
const char * tevScaleTableRight [ ] =
{
" " , // SCALE_1
" " , // SCALE_2
" " , // SCALE_4
" >> 1 " , // DIVIDE_2
} ;
const char * tevLerpBias [ ] = // indexed by 2*op+(shift==3)
{
" " ,
" + 128 " ,
" " ,
" + 127 " ,
} ;
const char * tevBiasTable [ ] =
{
" " , // ZERO,
" + 128 " , // ADDHALF,
" - 128 " , // SUBHALF,
" " ,
} ;
const char * tevOpTable [ ] = {
" + " , // TEVOP_ADD = 0,
" - " , // TEVOP_SUB = 1,
} ;
// Regular TEV stage: (d + bias + lerp(a,b,c)) * scale
2015-01-10 22:17:29 -07:00
// The GameCube/Wii GPU uses a very sophisticated algorithm for scale-lerping:
2014-03-24 07:41:56 -06:00
// - c is scaled from 0..255 to 0..256, which allows dividing the result by 256 instead of 255
// - if scale is bigger than one, it is moved inside the lerp calculation for increased accuracy
// - a rounding bias is added before dividing by 256
2015-09-04 22:45:35 -06:00
out . Write ( " (((tevin_d.%s%s)%s) " , components , tevBiasTable [ bias ] , tevScaleTableLeft [ shift ] ) ;
out . Write ( " %s " , tevOpTable [ op ] ) ;
out . Write ( " (((((tevin_a.%s<<8) + (tevin_b.%s-tevin_a.%s)*(tevin_c.%s+(tevin_c.%s>>7)))%s)%s)>>8) " ,
components , components , components , components , components ,
tevScaleTableLeft [ shift ] , tevLerpBias [ 2 * op + ( shift ! = 3 ) ] ) ;
out . Write ( " )%s " , tevScaleTableRight [ shift ] ) ;
2014-03-24 07:41:56 -06:00
}
2013-03-26 16:21:08 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static void SampleTexture ( T & out , const char * texcoords , const char * texswap , int texmap , API_TYPE ApiType )
2008-12-07 22:30:24 -07:00
{
2012-09-02 12:00:15 -06:00
out . SetConstantsUsed ( C_TEXDIMS + texmap , C_TEXDIMS + texmap ) ;
2013-08-12 05:31:29 -06:00
2013-09-22 10:07:21 -06:00
if ( ApiType = = API_D3D )
2015-10-11 07:47:15 -06:00
out . Write ( " iround(255.0 * Tex[%d].Sample(samp[%d], float3(%s.xy * " I_TEXDIMS " [%d].xy, %s))).%s; \n " , texmap , texmap , texcoords , texmap , g_ActiveConfig . iStereoMode > 0 ? " layer " : " 0.0 " , texswap ) ;
2013-08-14 07:40:24 -06:00
else
2015-10-11 07:47:15 -06:00
out . Write ( " iround(255.0 * texture(samp[%d], float3(%s.xy * " I_TEXDIMS " [%d].xy, %s))).%s; \n " , texmap , texcoords , texmap , g_ActiveConfig . iStereoMode > 0 ? " layer " : " 0.0 " , texswap ) ;
2008-12-07 22:30:24 -07:00
}
2010-07-06 07:14:51 -06:00
static const char * tevAlphaFuncsTable [ ] =
2008-12-07 22:30:24 -07:00
{
2015-09-01 10:20:50 -06:00
" (false) " , // NEVER
" (prev.a < %s) " , // LESS
" (prev.a == %s) " , // EQUAL
" (prev.a <= %s) " , // LEQUAL
" (prev.a > %s) " , // GREATER
" (prev.a != %s) " , // NEQUAL
" (prev.a >= %s) " , // GEQUAL
" (true) " // ALWAYS
2009-10-24 20:35:21 -06:00
} ;
static const char * tevAlphaFunclogicTable [ ] =
{
" && " , // and
2010-07-06 07:14:51 -06:00
" || " , // or
" != " , // xor
" == " // xnor
2009-10-24 20:35:21 -06:00
} ;
2013-01-08 08:40:15 -07:00
2013-03-26 16:21:08 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static void WriteAlphaTest ( T & out , pixel_shader_uid_data * uid_data , API_TYPE ApiType , DSTALPHA_MODE dstAlphaMode , bool per_pixel_depth )
2010-10-10 08:35:31 -06:00
{
2011-09-29 13:52:13 -06:00
static const char * alphaRef [ 2 ] =
{
2013-10-10 12:36:55 -06:00
I_ALPHA " .r " ,
I_ALPHA " .g "
2012-09-02 12:00:15 -06:00
} ;
out . SetConstantsUsed ( C_ALPHA , C_ALPHA ) ;
2010-10-10 08:35:31 -06:00
2014-12-02 23:29:50 -07:00
if ( DriverDetails : : HasBug ( DriverDetails : : BUG_BROKENNEGATEDBOOLEAN ) )
out . Write ( " \t if(( " ) ;
else
out . Write ( " \t if(!( " ) ;
2009-07-26 03:52:35 -06:00
2014-10-21 00:52:45 -06:00
uid_data - > alpha_test_comp0 = bpmem . alpha_test . comp0 ;
uid_data - > alpha_test_comp1 = bpmem . alpha_test . comp1 ;
uid_data - > alpha_test_logic = bpmem . alpha_test . logic ;
2012-08-07 06:36:56 -06:00
// Lookup the first component from the alpha function table
2013-01-08 09:18:45 -07:00
int compindex = bpmem . alpha_test . comp0 ;
2012-08-07 06:36:56 -06:00
out . Write ( tevAlphaFuncsTable [ compindex ] , alphaRef [ 0 ] ) ;
2011-06-04 13:56:18 -06:00
2014-02-05 05:26:01 -07:00
out . Write ( " %s " , tevAlphaFunclogicTable [ bpmem . alpha_test . logic ] ) ; // lookup the logic op
2011-06-04 13:56:18 -06:00
2012-08-07 06:36:56 -06:00
// Lookup the second component from the alpha function table
2013-01-08 09:18:45 -07:00
compindex = bpmem . alpha_test . comp1 ;
2012-08-07 06:36:56 -06:00
out . Write ( tevAlphaFuncsTable [ compindex ] , alphaRef [ 1 ] ) ;
2014-12-02 23:29:50 -07:00
if ( DriverDetails : : HasBug ( DriverDetails : : BUG_BROKENNEGATEDBOOLEAN ) )
out . Write ( " ) == false) { \n " ) ;
else
out . Write ( " )) { \n " ) ;
2011-09-03 20:44:50 -06:00
2013-10-06 02:12:13 -06:00
out . Write ( " \t \t ocol0 = float4(0.0, 0.0, 0.0, 0.0); \n " ) ;
2012-08-10 10:57:37 -06:00
if ( dstAlphaMode = = DSTALPHA_DUAL_SOURCE_BLEND )
2013-10-06 02:12:13 -06:00
out . Write ( " \t \t ocol1 = float4(0.0, 0.0, 0.0, 0.0); \n " ) ;
2014-03-10 05:30:55 -06:00
if ( per_pixel_depth )
2015-05-24 06:44:25 -06:00
out . Write ( " \t \t depth = %s; \n " , ( ApiType = = API_D3D ) ? " 0.0 " : " 1.0 " ) ;
2012-08-10 10:57:37 -06:00
2014-03-17 02:02:45 -06:00
// ZCOMPLOC HACK:
// The only way to emulate alpha test + early-z is to force early-z in the shader.
// As this isn't available on all drivers and as we can't emulate this feature otherwise,
// we are only able to choose which one we want to respect more.
// Tests seem to have proven that writing depth even when the alpha test fails is more
// important that a reliable alpha test, so we just force the alpha test to always succeed.
// At least this seems to be less buggy.
2015-01-13 02:55:25 -07:00
uid_data - > alpha_test_use_zcomploc_hack = bpmem . UseEarlyDepthTest ( )
2015-01-22 08:38:36 -07:00
& & bpmem . zmode . updateenable
& & ! g_ActiveConfig . backend_info . bSupportsEarlyZ
& & ! bpmem . genMode . zfreeze ;
2015-01-13 02:55:25 -07:00
2014-10-21 00:52:45 -06:00
if ( ! uid_data - > alpha_test_use_zcomploc_hack )
2012-08-10 10:57:37 -06:00
{
2013-03-26 15:16:29 -06:00
out . Write ( " \t \t discard; \n " ) ;
2013-09-22 10:07:21 -06:00
if ( ApiType ! = API_D3D )
2013-03-26 15:16:29 -06:00
out . Write ( " \t \t return; \n " ) ;
2012-08-10 10:57:37 -06:00
}
2011-09-03 20:44:50 -06:00
2014-07-16 09:24:43 -06:00
out . Write ( " \t } \n " ) ;
2008-12-07 22:30:24 -07:00
}
2009-02-18 21:41:58 -07:00
2009-10-24 20:35:21 -06:00
static const char * tevFogFuncsTable [ ] =
{
2014-02-16 13:30:18 -07:00
" " , // No Fog
" " , // ?
" " , // Linear
" " , // ?
" \t fog = 1.0 - exp2(-8.0 * fog); \n " , // exp
" \t fog = 1.0 - exp2(-8.0 * fog * fog); \n " , // exp2
" \t fog = exp2(-8.0 * (1.0 - fog)); \n " , // backward exp
" \t fog = 1.0 - fog; \n fog = exp2(-8.0 * fog * fog); \n " // backward exp2
2009-10-24 20:35:21 -06:00
} ;
2013-03-26 16:21:08 -06:00
template < class T >
2015-12-26 15:56:06 -07:00
static void WriteFog ( T & out , pixel_shader_uid_data * uid_data )
2009-02-18 21:41:58 -07:00
{
2014-10-21 00:52:45 -06:00
uid_data - > fog_fsel = bpmem . fog . c_proj_fsel . fsel ;
2014-03-10 05:30:55 -06:00
if ( bpmem . fog . c_proj_fsel . fsel = = 0 )
2011-12-25 22:15:54 -07:00
return ; // no Fog
2009-02-18 21:41:58 -07:00
2014-10-21 00:52:45 -06:00
uid_data - > fog_proj = bpmem . fog . c_proj_fsel . proj ;
2012-09-02 10:30:21 -06:00
2013-10-27 06:10:00 -06:00
out . SetConstantsUsed ( C_FOGCOLOR , C_FOGCOLOR ) ;
2013-12-16 05:08:09 -07:00
out . SetConstantsUsed ( C_FOGI , C_FOGI ) ;
out . SetConstantsUsed ( C_FOGF , C_FOGF + 1 ) ;
2011-06-04 13:56:18 -06:00
if ( bpmem . fog . c_proj_fsel . proj = = 0 )
2009-07-26 03:52:35 -06:00
{
2010-07-06 07:14:51 -06:00
// perspective
2010-11-23 06:57:01 -07:00
// ze = A/(B - (Zs >> B_SHF)
2013-12-16 05:08:09 -07:00
// TODO: Verify that we want to drop lower bits here! (currently taken over from software renderer)
2014-02-28 12:53:31 -07:00
// Maybe we want to use "ze = (A << B_SHF)/((B << B_SHF) - Zs)" instead?
// That's equivalent, but keeps the lower bits of Zs.
2015-05-07 15:49:09 -06:00
out . Write ( " \t float ze = ( " I_FOGF " [1].x * 16777216.0) / float( " I_FOGI " .y - (zCoord >> " I_FOGI " .w)); \n " ) ;
2010-07-06 07:14:51 -06:00
}
else
2009-10-24 20:35:21 -06:00
{
2010-07-06 07:14:51 -06:00
// orthographic
2014-02-16 13:30:18 -07:00
// ze = a*Zs (here, no B_SHF)
2015-05-07 15:49:09 -06:00
out . Write ( " \t float ze = " I_FOGF " [1].x * float(zCoord) / 16777216.0; \n " ) ;
2010-07-06 07:14:51 -06:00
}
2011-06-04 13:56:18 -06:00
2010-11-23 06:57:01 -07:00
// x_adjust = sqrt((x-center)^2 + k^2)/k
// ze *= x_adjust
2013-12-16 05:08:09 -07:00
// TODO Instead of this theoretical calculation, we should use the
// coefficient table given in the fog range BP registers!
2014-10-21 00:52:45 -06:00
uid_data - > fog_RangeBaseEnabled = bpmem . fogRange . Base . Enabled ;
2011-12-25 22:15:54 -07:00
if ( bpmem . fogRange . Base . Enabled )
2011-01-28 21:31:56 -07:00
{
2013-12-16 05:08:09 -07:00
out . SetConstantsUsed ( C_FOGF , C_FOGF ) ;
2014-03-30 04:19:15 -06:00
out . Write ( " \t float x_adjust = (2.0 * (rawpos.x / " I_FOGF " [0].y)) - 1.0 - " I_FOGF " [0].x; \n " ) ;
2013-12-16 05:08:09 -07:00
out . Write ( " \t x_adjust = sqrt(x_adjust * x_adjust + " I_FOGF " [0].z * " I_FOGF " [0].z) / " I_FOGF " [0].z; \n " ) ;
2013-03-26 15:16:29 -06:00
out . Write ( " \t ze *= x_adjust; \n " ) ;
2011-01-28 21:31:56 -07:00
}
2009-02-18 21:41:58 -07:00
2013-12-16 05:08:09 -07:00
out . Write ( " \t float fog = clamp(ze - " I_FOGF " [1].z, 0.0, 1.0); \n " ) ;
2009-02-18 21:41:58 -07:00
2011-12-25 22:15:54 -07:00
if ( bpmem . fog . c_proj_fsel . fsel > 3 )
2009-10-24 20:35:21 -06:00
{
2012-08-06 17:02:04 -06:00
out . Write ( " %s " , tevFogFuncsTable [ bpmem . fog . c_proj_fsel . fsel ] ) ;
2009-10-24 20:35:21 -06:00
}
else
{
2015-12-26 14:00:23 -07:00
if ( bpmem . fog . c_proj_fsel . fsel ! = 2 )
2009-10-24 20:35:21 -06:00
WARN_LOG ( VIDEO , " Unknown Fog Type! %08x " , bpmem . fog . c_proj_fsel . fsel ) ;
2010-07-06 07:14:51 -06:00
}
2009-02-18 21:41:58 -07:00
2014-03-17 10:11:27 -06:00
out . Write ( " \t int ifog = iround(fog * 256.0); \n " ) ;
2015-09-04 22:45:35 -06:00
out . Write ( " \t prev.rgb = (prev.rgb * (256 - ifog) + " I_FOGCOLOR " .rgb * ifog) >> 8; \n " ) ;
2012-08-06 17:02:04 -06:00
}
2011-06-04 13:56:18 -06:00
2015-11-02 19:47:05 -07:00
PixelShaderUid GetPixelShaderUid ( DSTALPHA_MODE dstAlphaMode , API_TYPE ApiType )
2012-08-06 17:02:04 -06:00
{
2015-11-02 19:47:05 -07:00
return GeneratePixelShader < PixelShaderUid > ( dstAlphaMode , ApiType ) ;
2009-02-18 21:41:58 -07:00
}
2012-09-02 12:00:15 -06:00
2015-11-02 19:47:05 -07:00
ShaderCode GeneratePixelShaderCode ( DSTALPHA_MODE dstAlphaMode , API_TYPE ApiType )
2012-09-02 12:00:15 -06:00
{
2015-11-02 19:47:05 -07:00
return GeneratePixelShader < ShaderCode > ( dstAlphaMode , ApiType ) ;
2012-09-02 12:00:15 -06:00
}