2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// 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
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2012-11-03 10:33:24 +08:00
# include <cstdio>
2015-10-14 17:45:21 +02:00
# include <sstream>
2012-11-01 16:19:01 +01:00
2020-08-15 12:25:39 +02:00
# include "Common/Log.h"
2015-10-14 19:50:39 +02:00
# include "Common/StringUtils.h"
2020-10-04 23:24:14 +02:00
# include "Common/GPU/OpenGL/GLFeatures.h"
2020-11-04 09:18:35 +01:00
# include "Common/GPU/ShaderWriter.h"
2020-12-16 14:25:00 +01:00
# include "Common/GPU/thin3d.h"
2022-07-20 00:05:07 +02:00
# include "Core/Compatibility.h"
2013-10-22 13:00:19 +02:00
# include "Core/Config.h"
2022-07-20 00:05:07 +02:00
# include "Core/System.h"
2015-03-15 19:25:34 -07:00
# include "GPU/Common/GPUStateUtils.h"
2015-10-24 23:24:06 +02:00
# include "GPU/Common/ShaderId.h"
2020-10-22 22:36:02 +02:00
# include "GPU/Common/ShaderUniforms.h"
2020-10-31 18:56:44 +01:00
# include "GPU/Common/FragmentShaderGenerator.h"
2022-11-06 16:56:21 +01:00
# include "GPU/Vulkan/DrawEngineVulkan.h"
2013-10-16 00:47:57 +02:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2020-11-02 11:45:47 +01:00
# define WRITE(p, ...) p.F(__VA_ARGS__)
2020-10-22 22:36:02 +02:00
2022-10-26 00:34:28 +02:00
static const SamplerDef samplersMono [ 3 ] = {
{ 0 , " tex " } ,
{ 1 , " fbotex " , SamplerFlags : : ARRAY_ON_VULKAN } ,
{ 2 , " pal " } ,
} ;
static const SamplerDef samplersStereo [ 3 ] = {
{ 0 , " tex " , SamplerFlags : : ARRAY_ON_VULKAN } ,
{ 1 , " fbotex " , SamplerFlags : : ARRAY_ON_VULKAN } ,
{ 2 , " pal " } ,
} ;
2022-09-06 13:30:18 +02:00
bool GenerateFragmentShader ( const FShaderID & id , char * buffer , const ShaderLanguageDesc & compat , Draw : : Bugs bugs , uint64_t * uniformMask , FragmentShaderFlags * fragmentShaderFlags , std : : string * errorString ) {
2017-11-19 11:23:16 +01:00
* uniformMask = 0 ;
2022-11-29 00:17:21 +01:00
* fragmentShaderFlags = ( FragmentShaderFlags ) 0 ;
2020-10-31 11:31:16 +01:00
errorString - > clear ( ) ;
2013-12-29 11:25:05 +01:00
2022-10-23 11:21:35 +02:00
bool useStereo = id . Bit ( FS_BIT_STEREO ) ;
2020-10-22 00:37:11 +02:00
bool highpFog = false ;
bool highpTexcoord = false ;
2022-10-17 08:27:49 +02:00
bool enableFragmentTestCache = gstate_c . Use ( GPU_USE_FRAGMENT_TEST_CACHE ) ;
2020-10-22 22:36:02 +02:00
2020-10-22 00:37:11 +02:00
if ( compat . gles ) {
// PowerVR needs highp to do the fog in MHU correctly.
// Others don't, and some can't handle highp in the fragment shader.
highpFog = ( gl_extensions . bugs & BUG_PVR_SHADER_PRECISION_BAD ) ? true : false ;
highpTexcoord = highpFog ;
}
2022-07-31 00:18:28 +02:00
bool texture3D = id . Bit ( FS_BIT_3D_TEXTURE ) ;
2022-10-26 11:02:58 +02:00
bool arrayTexture = id . Bit ( FS_BIT_SAMPLE_ARRAY_TEXTURE ) ;
2022-07-31 00:18:28 +02:00
2020-10-22 00:37:11 +02:00
ReplaceAlphaType stencilToAlpha = static_cast < ReplaceAlphaType > ( id . Bits ( FS_BIT_STENCIL_TO_ALPHA , 2 ) ) ;
2022-10-23 11:21:35 +02:00
std : : vector < const char * > extensions ;
2020-11-02 08:59:30 +01:00
if ( ShaderLanguageIsOpenGL ( compat . shaderLanguage ) ) {
2020-10-22 22:36:02 +02:00
if ( stencilToAlpha = = REPLACE_ALPHA_DUALSOURCE & & gl_extensions . EXT_blend_func_extended ) {
2022-10-23 11:21:35 +02:00
extensions . push_back ( " #extension GL_EXT_blend_func_extended : require " ) ;
2020-10-22 22:36:02 +02:00
}
if ( gl_extensions . EXT_gpu_shader4 ) {
2022-10-23 11:21:35 +02:00
extensions . push_back ( " #extension GL_EXT_gpu_shader4 : enable " ) ;
2020-10-22 22:36:02 +02:00
}
if ( compat . framebufferFetchExtension ) {
2022-10-23 11:21:35 +02:00
extensions . push_back ( compat . framebufferFetchExtension ) ;
2020-10-22 22:36:02 +02:00
}
2022-07-31 00:18:28 +02:00
if ( gl_extensions . OES_texture_3D & & texture3D ) {
2022-10-23 11:21:35 +02:00
extensions . push_back ( " #extension GL_OES_texture_3D: enable " ) ;
2022-07-31 00:18:28 +02:00
}
2022-10-23 11:21:35 +02:00
}
2022-10-26 00:34:28 +02:00
ShaderWriterFlags flags = ShaderWriterFlags : : NONE ;
if ( useStereo ) {
flags | = ShaderWriterFlags : : FS_AUTO_STEREO ;
}
ShaderWriter p ( buffer , compat , ShaderStage : : Fragment , extensions , flags ) ;
2022-11-07 22:25:03 +01:00
p . F ( " // %s \n " , FragmentShaderDesc ( id ) . c_str ( ) ) ;
2022-10-26 11:02:58 +02:00
p . ApplySamplerMetadata ( arrayTexture ? samplersStereo : samplersMono ) ;
2020-11-02 08:59:30 +01:00
2023-05-09 17:50:43 +02:00
bool lmode = id . Bit ( FS_BIT_LMODE ) ;
2015-10-24 23:09:28 +02:00
bool doTexture = id . Bit ( FS_BIT_DO_TEXTURE ) ;
2023-05-08 22:01:14 +02:00
bool enableFog = id . Bit ( FS_BIT_ENABLE_FOG ) ;
2015-10-24 23:09:28 +02:00
bool enableAlphaTest = id . Bit ( FS_BIT_ALPHA_TEST ) ;
2014-12-02 01:13:26 +01:00
2015-10-24 23:09:28 +02:00
bool alphaTestAgainstZero = id . Bit ( FS_BIT_ALPHA_AGAINST_ZERO ) ;
2018-07-27 20:06:41 -07:00
bool testForceToZero = id . Bit ( FS_BIT_TEST_DISCARD_TO_ZERO ) ;
2015-10-24 23:09:28 +02:00
bool enableColorTest = id . Bit ( FS_BIT_COLOR_TEST ) ;
bool colorTestAgainstZero = id . Bit ( FS_BIT_COLOR_AGAINST_ZERO ) ;
bool doTextureProjection = id . Bit ( FS_BIT_DO_TEXTURE_PROJ ) ;
2021-01-31 15:50:19 +01:00
2023-05-10 16:14:33 +02:00
bool ubershader = id . Bit ( FS_BIT_UBERSHADER ) ;
// ubershader-controlled bits. If ubershader is on, these will not be used below (and will be false).
bool useTexAlpha = id . Bit ( FS_BIT_TEXALPHA ) ;
bool enableColorDouble = id . Bit ( FS_BIT_DOUBLE_COLOR ) ;
2022-10-24 17:36:54 +02:00
if ( texture3D & & arrayTexture ) {
* errorString = " Invalid combination of 3D texture and array texture, shouldn't happen " ;
return false ;
}
2022-10-26 23:48:45 +02:00
if ( compat . shaderLanguage ! = ShaderLanguage : : GLSL_VULKAN & & arrayTexture ) {
* errorString = " We only do array textures for framebuffers in Vulkan. " ;
return false ;
}
2022-10-18 00:26:10 +02:00
2021-01-31 15:50:19 +01:00
bool flatBug = bugs . Has ( Draw : : Bugs : : BROKEN_FLAT_IN_SHADER ) & & g_Config . bVendorBugChecksEnabled ;
bool doFlatShading = id . Bit ( FS_BIT_FLATSHADE ) & & ! flatBug ;
2024-07-17 10:36:05 +02:00
if ( doFlatShading ) {
* fragmentShaderFlags | = FragmentShaderFlags : : USES_FLAT_SHADING ;
}
2022-09-11 14:14:18 +02:00
ShaderDepalMode shaderDepalMode = ( ShaderDepalMode ) id . Bits ( FS_BIT_SHADER_DEPAL_MODE , 2 ) ;
if ( texture3D ) {
shaderDepalMode = ShaderDepalMode : : OFF ;
}
2022-09-11 16:17:38 +02:00
if ( ! compat . bitwiseOps & & shaderDepalMode ! = ShaderDepalMode : : OFF ) {
* errorString = " depal requires bitwise ops " ;
return false ;
}
2020-10-28 23:19:40 +01:00
bool bgraTexture = id . Bit ( FS_BIT_BGRA_TEXTURE ) ;
2021-01-03 08:12:42 -08:00
bool colorWriteMask = id . Bit ( FS_BIT_COLOR_WRITEMASK ) & & compat . bitwiseOps ;
2015-10-14 10:50:08 +02:00
2015-10-24 23:09:28 +02:00
GEComparison alphaTestFunc = ( GEComparison ) id . Bits ( FS_BIT_ALPHA_TEST_FUNC , 3 ) ;
GEComparison colorTestFunc = ( GEComparison ) id . Bits ( FS_BIT_COLOR_TEST_FUNC , 2 ) ;
bool needShaderTexClamp = id . Bit ( FS_BIT_SHADER_TEX_CLAMP ) ;
2015-10-14 10:50:08 +02:00
2015-10-24 23:09:28 +02:00
GETexFunc texFunc = ( GETexFunc ) id . Bits ( FS_BIT_TEXFUNC , 3 ) ;
2015-10-14 10:50:08 +02:00
2015-10-24 23:09:28 +02:00
ReplaceBlendType replaceBlend = static_cast < ReplaceBlendType > ( id . Bits ( FS_BIT_REPLACE_BLEND , 3 ) ) ;
2015-10-14 10:50:08 +02:00
2022-04-24 20:53:09 +02:00
bool blueToAlpha = false ;
if ( replaceBlend = = ReplaceBlendType : : REPLACE_BLEND_BLUE_TO_ALPHA ) {
blueToAlpha = true ;
}
2015-10-24 23:09:28 +02:00
bool isModeClear = id . Bit ( FS_BIT_CLEARMODE ) ;
2013-05-08 08:18:12 -07:00
2014-12-01 19:16:20 +09:00
const char * shading = " " ;
2022-10-13 22:34:21 +02:00
if ( compat . glslES30 | | compat . shaderLanguage = = ShaderLanguage : : GLSL_VULKAN ) {
2014-12-02 01:13:26 +01:00
shading = doFlatShading ? " flat " : " " ;
2022-10-13 22:34:21 +02:00
}
2014-11-28 18:59:14 +09:00
2024-02-02 11:06:49 +01:00
bool forceDepthWritesOff = id . Bit ( FS_BIT_DEPTH_TEST_NEVER ) ;
bool useDiscardStencilBugWorkaround = id . Bit ( FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL ) & & ! forceDepthWritesOff ;
2015-10-14 10:50:08 +02:00
2022-09-04 11:14:47 +02:00
GEBlendSrcFactor replaceBlendFuncA = ( GEBlendSrcFactor ) id . Bits ( FS_BIT_BLENDFUNC_A , 4 ) ;
GEBlendDstFactor replaceBlendFuncB = ( GEBlendDstFactor ) id . Bits ( FS_BIT_BLENDFUNC_B , 4 ) ;
GEBlendMode replaceBlendEq = ( GEBlendMode ) id . Bits ( FS_BIT_BLENDEQ , 3 ) ;
StencilValueType replaceAlphaWithStencilType = ( StencilValueType ) id . Bits ( FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE , 4 ) ;
// Distinct from the logic op simulation support.
GELogicOp replaceLogicOpType = isModeClear ? GE_LOGIC_COPY : ( GELogicOp ) id . Bits ( FS_BIT_REPLACE_LOGIC_OP , 4 ) ;
2022-09-04 20:06:59 +02:00
bool replaceLogicOp = replaceLogicOpType ! = GE_LOGIC_COPY & & compat . bitwiseOps ;
2022-09-04 11:14:47 +02:00
2022-02-19 20:40:27 +01:00
bool needFramebufferRead = replaceBlend = = REPLACE_BLEND_READ_FRAMEBUFFER | | colorWriteMask | | replaceLogicOp ;
2020-11-08 23:17:06 +01:00
2022-11-29 15:29:31 +01:00
bool fetchFramebuffer = needFramebufferRead & & id . Bit ( FS_BIT_USE_FRAMEBUFFER_FETCH ) ;
bool readFramebufferTex = needFramebufferRead & & ! id . Bit ( FS_BIT_USE_FRAMEBUFFER_FETCH ) ;
2023-06-13 20:46:27 +02:00
if ( fetchFramebuffer & & ( compat . shaderLanguage ! = GLSL_3xx | | ! compat . lastFragData ) ) {
* errorString = " framebuffer fetch requires GLSL 3xx " ;
2022-11-29 15:29:31 +01:00
return false ;
}
2022-02-19 20:40:27 +01:00
2022-10-17 08:27:49 +02:00
bool needFragCoord = readFramebufferTex | | gstate_c . Use ( GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT ) ;
2024-02-02 11:06:49 +01:00
bool writeDepth = gstate_c . Use ( GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT ) & & ! forceDepthWritesOff ;
2020-11-09 16:05:28 +01:00
2023-05-09 17:59:16 +02:00
// TODO: We could have a separate mechanism to support more ops using the shader blending mechanism,
// on hardware that can do proper bit math in fragment shaders.
SimulateLogicOpType simulateLogicOpType = ( SimulateLogicOpType ) id . Bits ( FS_BIT_SIMULATE_LOGIC_OP_TYPE , 2 ) ;
2022-09-11 14:14:18 +02:00
if ( shaderDepalMode ! = ShaderDepalMode : : OFF & & ! doTexture ) {
2020-11-11 23:09:48 +01:00
* errorString = " depal requires a texture " ;
return false ;
}
2022-10-24 17:36:54 +02:00
// Currently only used by Vulkan.
std : : vector < SamplerDef > samplers ;
2020-10-30 10:22:40 +01:00
if ( compat . shaderLanguage = = ShaderLanguage : : GLSL_VULKAN ) {
2024-02-02 10:35:10 +01:00
if ( useDiscardStencilBugWorkaround & & ! writeDepth ) {
2020-10-22 22:36:02 +02:00
WRITE ( p , " layout (depth_unchanged) out float gl_FragDepth; \n " ) ;
2014-05-11 09:51:35 -07:00
}
2015-10-14 10:50:08 +02:00
2022-12-16 13:03:44 +01:00
WRITE ( p , " layout (std140, set = 0, binding = %d) uniform baseUBO { \n %s}; \n " , DRAW_BINDING_DYNUBO_BASE , ub_baseStr ) ;
2020-10-22 22:36:02 +02:00
if ( doTexture ) {
2022-12-16 13:03:44 +01:00
WRITE ( p , " layout (set = 0, binding = %d) uniform %s%s tex; \n " , DRAW_BINDING_TEXTURE , texture3D ? " sampler3D " : " sampler2D " , arrayTexture ? " Array " : " " ) ;
2014-06-08 14:24:38 -07:00
}
2013-03-25 22:57:40 +08:00
2020-11-08 12:31:43 +01:00
if ( readFramebufferTex ) {
2022-10-23 11:21:35 +02:00
// The framebuffer texture is always bound as an array.
2022-12-16 13:03:44 +01:00
p . F ( " layout (set = 0, binding = %d) uniform sampler2DArray fbotex; \n " , DRAW_BINDING_2ND_TEXTURE ) ;
2014-06-16 00:33:48 -07:00
}
2015-10-14 10:50:08 +02:00
2022-09-11 14:14:18 +02:00
if ( shaderDepalMode ! = ShaderDepalMode : : OFF ) {
2022-12-16 13:03:44 +01:00
WRITE ( p , " layout (set = 0, binding = %d) uniform sampler2D pal; \n " , DRAW_BINDING_DEPAL_TEXTURE ) ;
2020-10-22 22:36:02 +02:00
}
2018-04-13 20:00:14 +02:00
2020-11-10 22:26:34 +01:00
// Note: the precision qualifiers must match the vertex shader!
WRITE ( p , " layout (location = 1) %s in lowp vec4 v_color0; \n " , shading ) ;
2023-05-09 17:50:43 +02:00
if ( lmode ) {
WRITE ( p , " layout (location = 2) %s in lowp vec3 v_color1; \n " , shading ) ;
}
2022-09-26 11:20:17 +02:00
WRITE ( p , " layout (location = 3) in highp float v_fogdepth; \n " ) ;
2020-10-22 22:36:02 +02:00
if ( doTexture ) {
2020-11-10 22:26:34 +01:00
WRITE ( p , " layout (location = 0) in highp vec3 v_texcoord; \n " ) ;
2020-10-22 22:36:02 +02:00
}
2013-10-28 15:22:33 +01:00
2014-08-24 15:26:38 -07:00
if ( enableAlphaTest & & ! alphaTestAgainstZero ) {
2021-03-09 00:09:00 +01:00
WRITE ( p , " int roundAndScaleTo255i(in highp float x) { return int(floor(x * 255.0 + 0.5)); } \n " ) ;
2014-06-16 00:33:48 -07:00
}
2015-03-08 19:08:21 -07:00
if ( enableColorTest & & ! colorTestAgainstZero ) {
2022-09-25 20:34:35 -07:00
WRITE ( p , " uint roundAndScaleTo8x4(in highp vec3 x) { uvec3 u = uvec3(floor(x * 255.0 + 0.5)); return u.r | (u.g << 8) | (u.b << 16); } \n " ) ;
2022-09-26 22:51:16 +02:00
WRITE ( p , " uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 8) | (u.b << 16); } \n " ) ;
2020-10-22 22:36:02 +02:00
}
WRITE ( p , " layout (location = 0, index = 0) out vec4 fragColor0; \n " ) ;
if ( stencilToAlpha = = REPLACE_ALPHA_DUALSOURCE ) {
WRITE ( p , " layout (location = 0, index = 1) out vec4 fragColor1; \n " ) ;
}
2020-10-30 10:22:51 +01:00
} else if ( compat . shaderLanguage = = HLSL_D3D11 | | compat . shaderLanguage = = HLSL_D3D9 ) {
if ( compat . shaderLanguage = = HLSL_D3D9 ) {
if ( doTexture )
WRITE ( p , " sampler tex : register(s0); \n " ) ;
2020-11-08 12:31:43 +01:00
if ( readFramebufferTex ) {
WRITE ( p , " vec2 u_fbotexSize : register(c%i); \n " , CONST_PS_FBOTEXSIZE ) ;
WRITE ( p , " sampler fbotex : register(s1); \n " ) ;
}
if ( replaceBlend > REPLACE_BLEND_STANDARD ) {
2020-10-30 10:22:51 +01:00
if ( replaceBlendFuncA > = GE_SRCBLEND_FIXA ) {
WRITE ( p , " float3 u_blendFixA : register(c%i); \n " , CONST_PS_BLENDFIXA ) ;
}
if ( replaceBlendFuncB > = GE_DSTBLEND_FIXB ) {
WRITE ( p , " float3 u_blendFixB : register(c%i); \n " , CONST_PS_BLENDFIXB ) ;
}
2020-10-28 23:19:40 +01:00
}
2020-10-30 18:49:50 +01:00
if ( needShaderTexClamp & & doTexture ) {
2020-10-30 10:22:51 +01:00
WRITE ( p , " vec4 u_texclamp : register(c%i); \n " , CONST_PS_TEXCLAMP ) ;
2023-01-10 12:01:25 +01:00
WRITE ( p , " vec2 u_texclampoff : register(c%i); \n " , CONST_PS_TEXCLAMPOFF ) ;
2020-10-30 10:22:51 +01:00
}
if ( enableAlphaTest | | enableColorTest ) {
WRITE ( p , " vec4 u_alphacolorref : register(c%i); \n " , CONST_PS_ALPHACOLORREF ) ;
WRITE ( p , " vec4 u_alphacolormask : register(c%i); \n " , CONST_PS_ALPHACOLORMASK ) ;
}
if ( stencilToAlpha & & replaceAlphaWithStencilType = = STENCIL_VALUE_UNIFORM ) {
WRITE ( p , " float u_stencilReplaceValue : register(c%i); \n " , CONST_PS_STENCILREPLACE ) ;
}
2023-01-09 11:19:50 +01:00
if ( doTexture ) {
if ( texFunc = = GE_TEXFUNC_BLEND ) {
WRITE ( p , " float3 u_texenv : register(c%i); \n " , CONST_PS_TEXENV ) ;
}
2023-05-10 16:14:33 +02:00
if ( ubershader ) {
WRITE ( p , " float2 u_texNoAlphaMul : register(c%i); \n " , CONST_PS_TEX_NO_ALPHA_MUL ) ;
}
2020-10-30 10:22:51 +01:00
}
2023-05-08 22:01:14 +02:00
if ( enableFog ) {
WRITE ( p , " float3 u_fogcolor : register(c%i); \n " , CONST_PS_FOGCOLOR ) ;
}
2022-07-30 20:41:31 +02:00
if ( texture3D ) {
WRITE ( p , " float u_mipBias : register(c%i); \n " , CONST_PS_MIPBIAS ) ;
}
2020-10-30 10:22:51 +01:00
} else {
2022-09-11 13:27:17 +02:00
WRITE ( p , " SamplerState texSamp : register(s0); \n " ) ;
2022-07-30 19:55:56 +02:00
if ( texture3D ) {
WRITE ( p , " Texture3D<vec4> tex : register(t0); \n " ) ;
} else {
WRITE ( p , " Texture2D<vec4> tex : register(t0); \n " ) ;
}
2020-11-08 23:17:06 +01:00
if ( readFramebufferTex ) {
// No sampler required, we Load
2022-08-30 11:14:55 +02:00
WRITE ( p , " Texture2D<vec4> fbotex : register(t1); \n " ) ;
2020-10-30 10:22:51 +01:00
}
2022-09-11 13:27:17 +02:00
2022-09-11 14:14:18 +02:00
if ( shaderDepalMode ! = ShaderDepalMode : : OFF ) {
2022-09-11 13:27:17 +02:00
WRITE ( p , " SamplerState palSamp : register(s3); \n " ) ;
WRITE ( p , " Texture2D<vec4> pal : register(t3); \n " ) ;
2022-09-11 14:14:18 +02:00
WRITE ( p , " float2 textureSize(Texture2D<float4> tex, int mip) { float2 size; tex.GetDimensions(size.x, size.y); return size; } \n " ) ;
2022-09-11 13:27:17 +02:00
}
2020-11-01 12:45:35 +01:00
WRITE ( p , " cbuffer base : register(b0) { \n %s}; \n " , ub_baseStr ) ;
2020-10-28 23:19:40 +01:00
}
if ( enableAlphaTest ) {
2020-10-30 10:22:51 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
WRITE ( p , " int roundAndScaleTo255i(float x) { return int(floor(x * 255.0f + 0.5f)); } \n " ) ;
} else {
// D3D11 level 9 gets to take this path.
WRITE ( p , " float roundAndScaleTo255f(float x) { return floor(x * 255.0f + 0.5f); } \n " ) ;
}
2020-10-28 23:19:40 +01:00
}
if ( enableColorTest ) {
2020-10-30 10:22:51 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
2022-09-25 20:34:35 -07:00
WRITE ( p , " uint roundAndScaleTo8x4(float3 x) { uvec3 u = (floor(x * 255.0f + 0.5f)); return u.r | (u.g << 8) | (u.b << 16); } \n " ) ;
2022-09-26 22:51:16 +02:00
WRITE ( p , " uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 8) | (u.b << 16); } \n " ) ;
2020-10-30 10:22:51 +01:00
} else {
WRITE ( p , " vec3 roundAndScaleTo255v(float3 x) { return floor(x * 255.0f + 0.5f); } \n " ) ;
}
2020-10-28 23:19:40 +01:00
}
WRITE ( p , " struct PS_IN { \n " ) ;
2023-01-11 12:14:47 +01:00
if ( doTexture | | compat . shaderLanguage = = HLSL_D3D11 ) {
// In D3D11, if we always have a texcoord in the VS, we always need it in the PS too for the structs to match.
2020-10-28 23:19:40 +01:00
WRITE ( p , " vec3 v_texcoord: TEXCOORD0; \n " ) ;
}
2020-10-30 10:22:51 +01:00
const char * colorInterpolation = doFlatShading & & compat . shaderLanguage = = HLSL_D3D11 ? " nointerpolation " : " " ;
2020-10-28 23:19:40 +01:00
WRITE ( p , " %svec4 v_color0: COLOR0; \n " , colorInterpolation ) ;
2023-05-09 17:50:43 +02:00
if ( lmode ) {
WRITE ( p , " vec3 v_color1: COLOR1; \n " ) ;
}
2022-09-26 11:20:17 +02:00
WRITE ( p , " float v_fogdepth: TEXCOORD1; \n " ) ;
2022-08-30 11:14:55 +02:00
if ( needFragCoord ) {
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
WRITE ( p , " vec4 pixelPos : SV_POSITION; \n " ) ;
} else if ( compat . shaderLanguage = = HLSL_D3D9 ) {
WRITE ( p , " vec4 pixelPos : VPOS; \n " ) ; // VPOS is only supported for Shader Model 3.0, but we can probably forget about D3D9 SM2.0 at this point...
}
2020-10-28 23:19:40 +01:00
}
WRITE ( p , " }; \n " ) ;
2020-10-30 10:22:51 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
WRITE ( p , " struct PS_OUT { \n " ) ;
if ( stencilToAlpha = = REPLACE_ALPHA_DUALSOURCE ) {
WRITE ( p , " vec4 target : SV_Target0; \n " ) ;
WRITE ( p , " vec4 target1 : SV_Target1; \n " ) ;
} else {
WRITE ( p , " vec4 target : SV_Target; \n " ) ;
}
2020-11-09 16:05:28 +01:00
if ( writeDepth ) {
WRITE ( p , " float depth : SV_Depth; \n " ) ;
2020-10-30 10:22:51 +01:00
}
WRITE ( p , " }; \n " ) ;
2022-08-01 12:11:42 +02:00
} else if ( compat . shaderLanguage = = HLSL_D3D9 ) {
WRITE ( p , " struct PS_OUT { \n " ) ;
WRITE ( p , " vec4 target : COLOR; \n " ) ;
if ( writeDepth ) {
WRITE ( p , " float depth : DEPTH; \n " ) ;
}
WRITE ( p , " }; \n " ) ;
2020-10-28 23:19:40 +01:00
}
2020-10-30 10:22:40 +01:00
} else if ( ShaderLanguageIsOpenGL ( compat . shaderLanguage ) ) {
2022-09-11 14:14:18 +02:00
if ( ( shaderDepalMode ! = ShaderDepalMode : : OFF | | colorWriteMask ) & & gl_extensions . IsGLES ) {
2020-10-22 22:36:02 +02:00
WRITE ( p , " precision highp int; \n " ) ;
}
2022-07-30 21:33:24 +02:00
if ( doTexture ) {
2022-07-31 00:18:28 +02:00
if ( texture3D ) {
// For whatever reason, a precision specifier is required here.
WRITE ( p , " uniform lowp sampler3D tex; \n " ) ;
} else {
WRITE ( p , " uniform sampler2D tex; \n " ) ;
}
2023-01-05 17:05:42 +01:00
* uniformMask | = DIRTY_TEX_ALPHA_MUL ;
2023-05-10 16:14:33 +02:00
if ( ubershader ) {
WRITE ( p , " uniform vec2 u_texNoAlphaMul; \n " ) ;
}
2022-07-30 21:33:24 +02:00
}
2020-10-22 22:36:02 +02:00
2020-11-08 23:17:06 +01:00
if ( readFramebufferTex ) {
if ( ! compat . texelFetch ) {
WRITE ( p , " uniform vec2 u_fbotexSize; \n " ) ;
}
WRITE ( p , " uniform sampler2D fbotex; \n " ) ;
}
2020-10-22 22:36:02 +02:00
if ( ! isModeClear & & replaceBlend > REPLACE_BLEND_STANDARD ) {
* uniformMask | = DIRTY_SHADERBLEND ;
if ( replaceBlendFuncA > = GE_SRCBLEND_FIXA ) {
WRITE ( p , " uniform vec3 u_blendFixA; \n " ) ;
}
if ( replaceBlendFuncB > = GE_DSTBLEND_FIXB ) {
WRITE ( p , " uniform vec3 u_blendFixB; \n " ) ;
2014-08-24 15:26:38 -07:00
}
2014-06-16 00:33:48 -07:00
}
2013-12-16 23:41:42 +01:00
2020-10-22 22:36:02 +02:00
if ( needShaderTexClamp & & doTexture ) {
* uniformMask | = DIRTY_TEXCLAMP ;
WRITE ( p , " uniform vec4 u_texclamp; \n " ) ;
2023-01-10 12:01:25 +01:00
WRITE ( p , " uniform vec2 u_texclampoff; \n " ) ;
2015-11-27 16:08:41 -08:00
}
2020-10-22 22:36:02 +02:00
2023-05-09 17:59:16 +02:00
// TODO: Can get rid of some of this in the != 0 cases.
2020-10-22 22:36:02 +02:00
if ( enableAlphaTest | | enableColorTest ) {
2020-10-28 23:51:22 +01:00
if ( enableFragmentTestCache ) {
2020-10-22 22:36:02 +02:00
WRITE ( p , " uniform sampler2D testtex; \n " ) ;
} else {
* uniformMask | = DIRTY_ALPHACOLORREF ;
2022-10-10 17:16:52 +02:00
if ( compat . bitwiseOps ) {
WRITE ( p , " uniform uint u_alphacolorref; \n " ) ;
} else {
WRITE ( p , " uniform vec4 u_alphacolorref; \n " ) ;
}
2020-10-22 22:36:02 +02:00
if ( compat . bitwiseOps & & ( ( enableColorTest & & ! colorTestAgainstZero ) | | ( enableAlphaTest & & ! alphaTestAgainstZero ) ) ) {
* uniformMask | = DIRTY_ALPHACOLORMASK ;
2022-09-25 10:33:14 +02:00
WRITE ( p , " uniform uint u_alphacolormask; \n " ) ;
2020-10-22 22:36:02 +02:00
}
}
2015-11-27 16:01:25 -08:00
}
2020-10-22 22:36:02 +02:00
2022-09-11 14:14:18 +02:00
if ( shaderDepalMode ! = ShaderDepalMode : : OFF ) {
2020-10-22 22:36:02 +02:00
WRITE ( p , " uniform sampler2D pal; \n " ) ;
2020-11-07 11:25:05 +01:00
WRITE ( p , " uniform uint u_depal_mask_shift_off_fmt; \n " ) ;
2020-10-22 22:36:02 +02:00
* uniformMask | = DIRTY_DEPAL ;
}
2020-11-08 23:17:06 +01:00
if ( colorWriteMask ) {
WRITE ( p , " uniform uint u_colorWriteMask; \n " ) ;
* uniformMask | = DIRTY_COLORWRITEMASK ;
}
2020-10-22 22:36:02 +02:00
if ( stencilToAlpha & & replaceAlphaWithStencilType = = STENCIL_VALUE_UNIFORM ) {
* uniformMask | = DIRTY_STENCILREPLACEVALUE ;
WRITE ( p , " uniform float u_stencilReplaceValue; \n " ) ;
}
if ( doTexture & & texFunc = = GE_TEXFUNC_BLEND ) {
* uniformMask | = DIRTY_TEXENV ;
WRITE ( p , " uniform vec3 u_texenv; \n " ) ;
}
2022-07-30 21:33:24 +02:00
if ( texture3D ) {
2022-07-31 10:43:12 +02:00
* uniformMask | = DIRTY_MIPBIAS ;
2022-07-30 21:33:24 +02:00
WRITE ( p , " uniform float u_mipBias; \n " ) ;
}
2021-01-24 18:28:56 +01:00
WRITE ( p , " %s %s lowp vec4 v_color0; \n " , shading , compat . varying_fs ) ;
2023-05-09 17:50:43 +02:00
if ( lmode ) {
WRITE ( p , " %s %s lowp vec3 v_color1; \n " , shading , compat . varying_fs ) ;
}
2023-05-08 22:01:14 +02:00
if ( enableFog ) {
* uniformMask | = DIRTY_FOGCOLOR ;
WRITE ( p , " uniform vec3 u_fogcolor; \n " ) ;
}
2022-09-26 11:20:17 +02:00
WRITE ( p , " %s %s float v_fogdepth; \n " , compat . varying_fs , highpFog ? " highp " : " mediump " ) ;
2020-10-22 22:36:02 +02:00
if ( doTexture ) {
WRITE ( p , " %s %s vec3 v_texcoord; \n " , compat . varying_fs , highpTexcoord ? " highp " : " mediump " ) ;
}
2020-10-28 23:51:22 +01:00
if ( ! enableFragmentTestCache ) {
2020-10-22 22:36:02 +02:00
if ( enableAlphaTest & & ! alphaTestAgainstZero ) {
if ( compat . bitwiseOps ) {
WRITE ( p , " int roundAndScaleTo255i(in float x) { return int(floor(x * 255.0 + 0.5)); } \n " ) ;
} else if ( gl_extensions . gpuVendor = = GPU_VENDOR_IMGTEC ) {
WRITE ( p , " float roundTo255thf(in mediump float x) { mediump float y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); } \n " ) ;
} else {
WRITE ( p , " float roundAndScaleTo255f(in float x) { return floor(x * 255.0 + 0.5); } \n " ) ;
}
}
if ( enableColorTest & & ! colorTestAgainstZero ) {
if ( compat . bitwiseOps ) {
2023-01-02 14:32:44 -08:00
WRITE ( p , " uint roundAndScaleTo8x4(in vec3 x) { uvec3 u = uvec3(floor(x * 255.99)); return u.r | (u.g << 0x8u) | (u.b << 0x10u); } \n " ) ;
WRITE ( p , " uint packFloatsTo8x4(in vec3 x) { uvec3 u = uvec3(x); return u.r | (u.g << 0x8u) | (u.b << 0x10u); } \n " ) ;
2020-10-22 22:36:02 +02:00
} else if ( gl_extensions . gpuVendor = = GPU_VENDOR_IMGTEC ) {
WRITE ( p , " vec3 roundTo255thv(in vec3 x) { vec3 y = x + (0.5/255.0); return y - fract(y * 255.0) * (1.0 / 255.0); } \n " ) ;
} else {
WRITE ( p , " vec3 roundAndScaleTo255v(in vec3 x) { return floor(x * 255.0 + 0.5); } \n " ) ;
}
}
}
if ( ! strcmp ( compat . fragColor0 , " fragColor0 " ) ) {
const char * qualifierColor0 = " out " ;
2022-02-19 20:40:27 +01:00
if ( fetchFramebuffer & & compat . lastFragData & & ! strcmp ( compat . lastFragData , compat . fragColor0 ) ) {
2020-10-22 22:36:02 +02:00
qualifierColor0 = " inout " ;
}
// Output the output color definitions.
if ( stencilToAlpha = = REPLACE_ALPHA_DUALSOURCE ) {
WRITE ( p , " %s vec4 fragColor0; \n " , qualifierColor0 ) ;
WRITE ( p , " out vec4 fragColor1; \n " ) ;
} else {
WRITE ( p , " %s vec4 fragColor0; \n " , qualifierColor0 ) ;
}
}
2020-11-16 23:26:37 +01:00
}
2020-10-22 22:36:02 +02:00
2020-11-16 23:26:37 +01:00
bool hasPackUnorm4x8 = false ;
if ( compat . shaderLanguage = = GLSL_VULKAN ) {
hasPackUnorm4x8 = true ;
} else if ( ShaderLanguageIsOpenGL ( compat . shaderLanguage ) ) {
if ( compat . gles ) {
hasPackUnorm4x8 = compat . glslVersionNumber > = 310 ;
} else {
hasPackUnorm4x8 = compat . glslVersionNumber > = 400 ;
}
2013-12-15 12:49:13 +01:00
}
2013-04-29 01:08:23 -07:00
2023-01-02 22:24:00 +01:00
const char * packSuffix = " " ;
if ( ! hasPackUnorm4x8 ) {
packSuffix = " R " ;
}
2020-11-08 23:17:06 +01:00
// Provide implementations of packUnorm4x8 and unpackUnorm4x8 if not available.
2022-09-04 19:27:26 +02:00
if ( ( colorWriteMask | | replaceLogicOp ) & & ! hasPackUnorm4x8 ) {
2023-01-02 22:24:00 +01:00
WRITE ( p , " uint packUnorm4x8%s(%svec4 v) { \n " , packSuffix , compat . shaderLanguage = = GLSL_VULKAN ? " highp " : " " ) ;
2021-01-03 08:40:12 -08:00
WRITE ( p , " highp vec4 f = clamp(v, 0.0, 1.0); \n " ) ;
WRITE ( p , " uvec4 u = uvec4(255.0 * f); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " return u.x | (u.y << 0x8u) | (u.z << 0x10u) | (u.w << 0x18u); \n " ) ;
2020-11-08 23:17:06 +01:00
WRITE ( p , " } \n " ) ;
2023-01-02 22:24:00 +01:00
WRITE ( p , " vec4 unpackUnorm4x8%s(highp uint x) { \n " , packSuffix ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " highp uvec4 u = uvec4(x & 0xFFu, (x >> 0x8u) & 0xFFu, (x >> 0x10u) & 0xFFu, (x >> 0x18u) & 0xFFu); \n " ) ;
2021-01-03 08:40:12 -08:00
WRITE ( p , " highp vec4 f = vec4(u); \n " ) ;
WRITE ( p , " return f * (1.0 / 255.0); \n " ) ;
2020-11-08 23:17:06 +01:00
WRITE ( p , " } \n " ) ;
}
2022-09-25 10:33:14 +02:00
if ( compat . bitwiseOps & & enableColorTest ) {
2022-09-25 17:56:10 +02:00
p . C ( " uvec3 unpackUVec3(highp uint x) { \n " ) ;
2023-01-02 14:32:44 -08:00
p . C ( " return uvec3(x & 0xFFu, (x >> 0x8u) & 0xFFu, (x >> 0x10u) & 0xFFu); \n " ) ;
2022-09-25 10:33:14 +02:00
p . C ( " } \n " ) ;
}
2014-12-14 20:59:21 +01:00
// PowerVR needs a custom modulo function. For some reason, this has far higher precision than the builtin one.
2015-10-14 10:50:08 +02:00
if ( ( gl_extensions . bugs & BUG_PVR_SHADER_PRECISION_BAD ) & & needShaderTexClamp ) {
2014-12-14 20:59:21 +01:00
WRITE ( p , " float mymod(float a, float b) { return a - b * floor(a / b); } \n " ) ;
}
2020-10-30 10:22:51 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
2020-10-28 23:19:40 +01:00
WRITE ( p , " PS_OUT main( PS_IN In ) { \n " ) ;
2020-11-09 16:05:28 +01:00
WRITE ( p , " PS_OUT outfragment; \n " ) ;
if ( needFragCoord ) {
WRITE ( p , " vec4 gl_FragCoord = In.pixelPos; \n " ) ;
}
if ( writeDepth ) {
WRITE ( p , " float gl_FragDepth; \n " ) ;
}
2020-10-30 10:22:51 +01:00
} else if ( compat . shaderLanguage = = HLSL_D3D9 ) {
2022-08-01 12:11:42 +02:00
WRITE ( p , " PS_OUT main( PS_IN In ) { \n " ) ;
WRITE ( p , " PS_OUT outfragment; \n " ) ;
2022-08-30 11:14:55 +02:00
if ( needFragCoord ) {
WRITE ( p , " vec4 gl_FragCoord = In.pixelPos; \n " ) ;
}
2020-10-28 23:19:40 +01:00
} else {
WRITE ( p , " void main() { \n " ) ;
}
2020-10-31 23:22:06 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 | | compat . shaderLanguage = = HLSL_D3D9 ) {
WRITE ( p , " vec4 v_color0 = In.v_color0; \n " ) ;
2023-05-09 17:50:43 +02:00
if ( lmode ) {
WRITE ( p , " vec3 v_color1 = In.v_color1; \n " ) ;
}
2023-05-08 22:01:14 +02:00
if ( enableFog ) {
WRITE ( p , " float v_fogdepth = In.v_fogdepth; \n " ) ;
}
2020-10-31 23:22:06 +01:00
if ( doTexture ) {
WRITE ( p , " vec3 v_texcoord = In.v_texcoord; \n " ) ;
}
}
2020-11-09 13:31:39 +01:00
// Two things read from the old framebuffer - shader replacement blending and bit-level masking.
2022-02-19 20:40:27 +01:00
if ( readFramebufferTex ) {
2020-11-09 13:31:39 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
2022-08-30 11:14:55 +02:00
WRITE ( p , " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0)); \n " ) ;
} else if ( compat . shaderLanguage = = HLSL_D3D9 ) {
WRITE ( p , " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy); \n " , compat . texture ) ;
2022-10-23 11:21:35 +02:00
} else if ( compat . shaderLanguage = = GLSL_VULKAN ) {
WRITE ( p , " lowp vec4 destColor = %s(fbotex, ivec3(gl_FragCoord.x, gl_FragCoord.y, %s), 0); \n " , compat . texelFetch , useStereo ? " float(gl_ViewIndex) " : " 0 " ) ;
2020-11-09 13:31:39 +01:00
} else if ( ! compat . texelFetch ) {
WRITE ( p , " lowp vec4 destColor = %s(fbotex, gl_FragCoord.xy * u_fbotexSize.xy); \n " , compat . texture ) ;
} else {
WRITE ( p , " lowp vec4 destColor = %s(fbotex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0); \n " , compat . texelFetch ) ;
}
2022-02-19 20:40:27 +01:00
} else if ( fetchFramebuffer ) {
// If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit.
// We can just read the prev value more directly.
if ( compat . shaderLanguage = = GLSL_3xx ) {
WRITE ( p , " lowp vec4 destColor = %s; \n " , compat . lastFragData ) ;
} else if ( compat . shaderLanguage = = GLSL_VULKAN ) {
2022-12-01 00:59:22 -08:00
WRITE ( p , " lowp vec4 destColor = subpassLoad(inputColor); \n " ) ;
2022-02-19 20:40:27 +01:00
} else {
_assert_msg_ ( false , " Need fetch destColor, but not a compatible language " ) ;
}
2020-11-09 13:31:39 +01:00
}
2015-10-14 10:50:08 +02:00
if ( isModeClear ) {
2012-12-05 10:45:28 +07:00
// Clear mode does not allow any fancy shading.
2020-10-31 23:22:06 +01:00
WRITE ( p , " vec4 v = v_color0; \n " ) ;
2013-04-06 22:28:38 +08:00
} else {
2023-05-09 17:50:43 +02:00
const char * secondary = " " ;
2012-11-23 01:26:12 +01:00
// Secondary color for specular on top of texture
2023-05-09 17:50:43 +02:00
if ( lmode ) {
WRITE ( p , " vec4 s = vec4(v_color1, 0.0); \n " ) ;
secondary = " + s " ;
}
2012-11-01 16:19:01 +01:00
2015-10-14 10:50:08 +02:00
if ( doTexture ) {
2020-10-31 23:22:06 +01:00
char texcoord [ 64 ] = " v_texcoord " ;
2014-06-07 12:21:52 -07:00
// TODO: Not sure the right way to do this for projection.
2017-02-17 12:12:15 +01:00
// This path destroys resolution on older PowerVR no matter what I do if projection is needed,
// so we disable it on SGX 540 and lesser, and live with the consequences.
2019-02-26 10:39:17 +01:00
bool terriblePrecision = ( gl_extensions . bugs & BUG_PVR_SHADER_PRECISION_TERRIBLE ) ! = 0 ;
bool clampDisabled = doTextureProjection & & terriblePrecision ;
// Also with terrible precision we can't do wrapping without destroying the image. See #9189
if ( terriblePrecision & & ( ! id . Bit ( FS_BIT_CLAMP_S ) | | ! id . Bit ( FS_BIT_CLAMP_T ) ) ) {
clampDisabled = true ;
}
if ( needShaderTexClamp & & ! clampDisabled ) {
2014-06-07 12:21:52 -07:00
// We may be clamping inside a larger surface (tex = 64x64, buffer=480x272).
// We may also be wrapping in such a surface, or either one in a too-small surface.
// Obviously, clamping to a smaller surface won't work. But better to clamp to something.
2020-10-31 23:22:06 +01:00
std : : string ucoord = " v_texcoord.x " ;
std : : string vcoord = " v_texcoord.y " ;
2014-06-07 14:19:53 -07:00
if ( doTextureProjection ) {
2020-10-31 23:22:06 +01:00
ucoord = " (v_texcoord.x / v_texcoord.z) " ;
vcoord = " (v_texcoord.y / v_texcoord.z) " ;
2014-06-07 12:21:52 -07:00
}
2014-12-15 01:15:09 +01:00
std : : string modulo = ( gl_extensions . bugs & BUG_PVR_SHADER_PRECISION_BAD ) ? " mymod " : " mod " ;
2014-12-14 20:59:21 +01:00
2015-10-24 23:09:28 +02:00
if ( id . Bit ( FS_BIT_CLAMP_S ) ) {
2014-06-07 19:30:46 -07:00
ucoord = " clamp( " + ucoord + " , u_texclamp.z, u_texclamp.x - u_texclamp.z) " ;
2014-06-07 14:19:53 -07:00
} else {
2014-12-14 20:59:21 +01:00
ucoord = modulo + " ( " + ucoord + " , u_texclamp.x) " ;
2014-06-07 14:19:53 -07:00
}
2015-10-24 23:09:28 +02:00
if ( id . Bit ( FS_BIT_CLAMP_T ) ) {
2014-06-07 19:30:46 -07:00
vcoord = " clamp( " + vcoord + " , u_texclamp.w, u_texclamp.y - u_texclamp.w) " ;
2014-06-07 14:19:53 -07:00
} else {
2014-12-14 20:59:21 +01:00
vcoord = modulo + " ( " + vcoord + " , u_texclamp.y) " ;
2014-06-07 14:19:53 -07:00
}
2023-01-10 12:01:25 +01:00
ucoord = " ( " + ucoord + " + u_texclampoff.x) " ;
vcoord = " ( " + vcoord + " + u_texclampoff.y) " ;
2014-06-07 14:19:53 -07:00
2014-06-07 19:32:19 -07:00
WRITE ( p , " vec2 fixedcoord = vec2(%s, %s); \n " , ucoord . c_str ( ) , vcoord . c_str ( ) ) ;
2020-10-31 11:08:01 +01:00
truncate_cpy ( texcoord , " fixedcoord " ) ;
2014-06-07 14:19:53 -07:00
// We already projected it.
doTextureProjection = false ;
2014-06-07 12:21:52 -07:00
}
2022-09-11 14:14:18 +02:00
switch ( shaderDepalMode ) {
case ShaderDepalMode : : OFF :
2020-10-30 10:22:40 +01:00
if ( compat . shaderLanguage = = HLSL_D3D11 ) {
2022-07-30 19:55:56 +02:00
if ( texture3D ) {
if ( doTextureProjection ) {
2022-09-11 13:27:17 +02:00
WRITE ( p , " vec4 t = tex.Sample(texSamp, vec3(v_texcoord.xy / v_texcoord.z, u_mipBias))%s; \n " , bgraTexture ? " .bgra " : " " ) ;
2022-07-30 19:55:56 +02:00
} else {
2022-09-11 13:27:17 +02:00
WRITE ( p , " vec4 t = tex.Sample(texSamp, vec3(%s.xy, u_mipBias))%s; \n " , texcoord , bgraTexture ? " .bgra " : " " ) ;
2022-07-30 19:55:56 +02:00
}
2020-10-28 23:19:40 +01:00
} else {
2022-07-30 19:55:56 +02:00
if ( doTextureProjection ) {
2022-09-11 13:27:17 +02:00
WRITE ( p , " vec4 t = tex.Sample(texSamp, v_texcoord.xy / v_texcoord.z)%s; \n " , bgraTexture ? " .bgra " : " " ) ;
2022-07-30 19:55:56 +02:00
} else {
2022-09-11 13:27:17 +02:00
WRITE ( p , " vec4 t = tex.Sample(texSamp, %s.xy)%s; \n " , texcoord , bgraTexture ? " .bgra " : " " ) ;
2022-07-30 19:55:56 +02:00
}
2020-10-28 23:19:40 +01:00
}
2020-10-30 10:22:51 +01:00
} else if ( compat . shaderLanguage = = HLSL_D3D9 ) {
2022-07-30 20:41:31 +02:00
if ( texture3D ) {
if ( doTextureProjection ) {
WRITE ( p , " vec4 t = tex3Dproj(tex, vec4(v_texcoord.x, v_texcoord.y, u_mipBias, v_texcoord.z))%s; \n " , bgraTexture ? " .bgra " : " " ) ;
} else {
WRITE ( p , " vec4 t = tex3D(tex, vec3(%s.x, %s.y, u_mipBias))%s; \n " , texcoord , texcoord , bgraTexture ? " .bgra " : " " ) ;
}
2020-10-30 10:22:51 +01:00
} else {
2022-07-30 20:41:31 +02:00
if ( doTextureProjection ) {
2022-07-31 00:21:54 +02:00
WRITE ( p , " vec4 t = tex2Dproj(tex, vec4(v_texcoord.x, v_texcoord.y, 0.0, v_texcoord.z))%s; \n " , bgraTexture ? " .bgra " : " " ) ;
2022-07-30 20:41:31 +02:00
} else {
WRITE ( p , " vec4 t = tex2D(tex, %s.xy)%s; \n " , texcoord , bgraTexture ? " .bgra " : " " ) ;
}
2020-10-30 10:22:51 +01:00
}
2018-08-12 19:49:08 -07:00
} else {
2022-07-26 10:43:44 +02:00
// Note that here we're relying on the filter to be linear. We would have to otherwise to do two samples and manually filter in Z.
// Let's add that if we run into a case...
2022-07-25 18:51:08 +02:00
if ( texture3D ) {
if ( doTextureProjection ) {
2022-07-30 21:52:58 +02:00
WRITE ( p , " vec4 t = %sProj(tex, vec4(%s.xy, u_mipBias, %s.z)); \n " , compat . texture3D , texcoord , texcoord ) ;
2022-07-25 18:51:08 +02:00
} else {
2022-07-30 21:52:58 +02:00
WRITE ( p , " vec4 t = %s(tex, vec3(%s.xy, u_mipBias)); \n " , compat . texture3D , texcoord ) ;
2022-07-25 18:51:08 +02:00
}
2022-10-24 17:36:54 +02:00
} else if ( arrayTexture ) {
2022-10-26 23:48:45 +02:00
_dbg_assert_ ( compat . shaderLanguage = = GLSL_VULKAN ) ;
2022-10-24 17:36:54 +02:00
// Used for stereo rendering.
2022-10-26 00:24:43 +02:00
const char * arrayIndex = useStereo ? " float(gl_ViewIndex) " : " 0.0 " ;
2022-10-24 17:36:54 +02:00
if ( doTextureProjection ) {
2022-10-26 13:40:13 +02:00
// There's no textureProj for array textures, so we need to emulate it.
// Should be fine on any Vulkan-compatible hardware.
WRITE ( p , " vec2 uv_proj = (%s.xy) / (%s.z); \n " , texcoord , texcoord ) ;
WRITE ( p , " vec4 t = %s(tex, vec3(uv_proj, %s)); \n " , compat . texture , texcoord , arrayIndex ) ;
2022-10-26 00:24:43 +02:00
} else {
WRITE ( p , " vec4 t = %s(tex, vec3(%s.xy, %s)); \n " , compat . texture , texcoord , arrayIndex ) ;
2022-10-24 17:36:54 +02:00
}
2020-10-28 23:19:40 +01:00
} else {
2022-07-25 18:51:08 +02:00
if ( doTextureProjection ) {
WRITE ( p , " vec4 t = %sProj(tex, %s); \n " , compat . texture , texcoord ) ;
} else {
WRITE ( p , " vec4 t = %s(tex, %s.xy); \n " , compat . texture , texcoord ) ;
}
2020-10-28 23:19:40 +01:00
}
2022-07-25 20:51:52 +02:00
}
2022-09-11 14:14:18 +02:00
break ;
case ShaderDepalMode : : SMOOTHED :
2022-07-20 00:05:07 +02:00
// Specific mode for Test Drive. Fixes the banding.
if ( doTextureProjection ) {
// We don't use textureProj because we need better control and it's probably not much of a savings anyway.
// However it is good for precision on older hardware like PowerVR.
2022-09-11 13:27:17 +02:00
p . F ( " vec2 uv = %s.xy/%s.z; \n vec2 uv_round; \n " , texcoord , texcoord ) ;
2022-07-20 00:05:07 +02:00
} else {
2022-09-11 13:27:17 +02:00
p . F ( " vec2 uv = %s.xy; \n vec2 uv_round; \n " , texcoord ) ;
2022-07-20 00:05:07 +02:00
}
2022-08-22 10:32:02 +02:00
// Restrictions on this are checked before setting the smoothed flag.
// Only RGB565 and RGBA5551 are supported, and only the specific shifts hitting the
// channels directly.
2022-08-24 17:13:12 +02:00
// Also, since we know the CLUT is smooth, we do not need to do the bilinear filter manually, we can just
// lookup with the filtered value once.
2022-09-11 13:27:17 +02:00
p . F ( " vec4 t = " ) . SampleTexture2D ( " tex " , " uv " ) . C ( " ; \n " ) ;
2023-01-02 14:32:44 -08:00
p . C ( " uint depalShift = (u_depal_mask_shift_off_fmt >> 0x8u) & 0xFFu; \n " ) ;
2023-10-03 23:30:18 +02:00
p . C ( " uint depalOffset = ((u_depal_mask_shift_off_fmt >> 0x10u) & 0xFFu) << 0x4u; \n " ) ;
2023-01-02 14:32:44 -08:00
p . C ( " uint depalFmt = (u_depal_mask_shift_off_fmt >> 0x18u) & 0x3u; \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " float index0 = t.r; \n " ) ;
p . C ( " float factor = 31.0 / 256.0; \n " ) ;
2023-01-02 13:12:37 -08:00
p . C ( " if (depalFmt == 0x0u) { \n " ) ; // yes, different versions of Test Drive use different formats. Could do compile time by adding more compat flags but meh.
p . C ( " if (depalShift == 0x5u) { index0 = t.g; factor = 63.0 / 256.0; } \n " ) ;
p . C ( " else if (depalShift == 0xBu) { index0 = t.b; } \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " } else { \n " ) ;
2023-01-02 13:12:37 -08:00
p . C ( " if (depalShift == 0x5u) { index0 = t.g; } \n " ) ;
p . C ( " else if (depalShift == 0xAu) { index0 = t.b; } \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " } \n " ) ;
2023-10-03 23:30:18 +02:00
p . C ( " float offset = float(depalOffset) / 256.0; \n " ) ;
p . F ( " t = " ) . SampleTexture2D ( " pal " , " vec2((index0 * factor + offset) * 0.5 + 0.5 / 512.0, 0.0) " ) . C ( " ; \n " ) ; // 0.5 for 512-entry CLUT.
2022-09-11 14:14:18 +02:00
break ;
case ShaderDepalMode : : NORMAL :
2018-08-12 19:49:08 -07:00
if ( doTextureProjection ) {
// We don't use textureProj because we need better control and it's probably not much of a savings anyway.
2019-02-26 10:39:17 +01:00
// However it is good for precision on older hardware like PowerVR.
2020-10-31 11:08:01 +01:00
WRITE ( p , " vec2 uv = %s.xy/%s.z; \n vec2 uv_round; \n " , texcoord , texcoord ) ;
2018-08-12 19:49:08 -07:00
} else {
2020-10-31 11:08:01 +01:00
WRITE ( p , " vec2 uv = %s.xy; \n vec2 uv_round; \n " , texcoord ) ;
2018-04-13 20:00:14 +02:00
}
2022-08-12 21:31:55 +02:00
WRITE ( p , " vec2 tsize = vec2(textureSize(tex, 0).xy); \n " ) ;
2018-08-12 19:49:08 -07:00
WRITE ( p , " vec2 fraction; \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " bool bilinear = (u_depal_mask_shift_off_fmt >> 0x2Fu) != 0x0u; \n " ) ;
2018-08-12 19:49:08 -07:00
WRITE ( p , " if (bilinear) { \n " ) ;
WRITE ( p , " uv_round = uv * tsize - vec2(0.5, 0.5); \n " ) ;
WRITE ( p , " fraction = fract(uv_round); \n " ) ;
WRITE ( p , " uv_round = (uv_round - fraction + vec2(0.5, 0.5)) / tsize; \n " ) ; // We want to take our four point samples at pixel centers.
WRITE ( p , " } else { \n " ) ;
WRITE ( p , " uv_round = uv; \n " ) ;
WRITE ( p , " } \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " highp vec4 t = " ) . SampleTexture2D ( " tex " , " uv_round " ) . C ( " ; \n " ) ;
p . C ( " highp vec4 t1 = " ) . SampleTexture2DOffset ( " tex " , " uv_round " , 1 , 0 ) . C ( " ; \n " ) ;
p . C ( " highp vec4 t2 = " ) . SampleTexture2DOffset ( " tex " , " uv_round " , 0 , 1 ) . C ( " ; \n " ) ;
p . C ( " highp vec4 t3 = " ) . SampleTexture2DOffset ( " tex " , " uv_round " , 1 , 1 ) . C ( " ; \n " ) ;
2023-01-02 13:12:37 -08:00
WRITE ( p , " uint depalMask = (u_depal_mask_shift_off_fmt & 0xFFu); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " uint depalShift = (u_depal_mask_shift_off_fmt >> 0x8u) & 0xFFu; \n " ) ;
WRITE ( p , " uint depalOffset = ((u_depal_mask_shift_off_fmt >> 0x10u) & 0xFFu) << 0x4u; \n " ) ;
WRITE ( p , " uint depalFmt = (u_depal_mask_shift_off_fmt >> 0x18u) & 0x3u; \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " uvec4 col; uint index0; uint index1; uint index2; uint index3; \n " ) ;
2021-04-10 14:01:33 -07:00
WRITE ( p , " switch (int(depalFmt)) { \n " ) ; // We might want to include fmt in the shader ID if this is a performance issue.
WRITE ( p , " case 0: \n " ) ; // 565
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index0 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " if (bilinear) { \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index1 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index2 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index3 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " } \n " ) ;
WRITE ( p , " break; \n " ) ;
2021-04-10 14:01:33 -07:00
WRITE ( p , " case 1: \n " ) ; // 5551
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0)); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index0 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " if (bilinear) { \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0)); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index1 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0)); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index2 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0)); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index3 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " } \n " ) ;
WRITE ( p , " break; \n " ) ;
2021-04-10 14:01:33 -07:00
WRITE ( p , " case 2: \n " ) ; // 4444
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t.rgba * 15.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index0 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " if (bilinear) { \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t1.rgba * 15.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index1 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t2.rgba * 15.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index2 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t3.rgba * 15.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index3 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " } \n " ) ;
WRITE ( p , " break; \n " ) ;
2021-04-10 14:01:33 -07:00
WRITE ( p , " case 3: \n " ) ; // 8888
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t.rgba * 255.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index0 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " if (bilinear) { \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t1.rgba * 255.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index1 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t2.rgba * 255.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index2 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r); \n " ) ;
2020-10-22 23:05:53 +02:00
WRITE ( p , " col = uvec4(t3.rgba * 255.99); \n " ) ;
2023-01-02 14:32:44 -08:00
WRITE ( p , " index3 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r); \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " } \n " ) ;
WRITE ( p , " break; \n " ) ;
WRITE ( p , " }; \n " ) ;
WRITE ( p , " index0 = ((index0 >> depalShift) & depalMask) | depalOffset; \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " t = " ) . LoadTexture2D ( " pal " , " ivec2(index0, 0) " , 0 ) . C ( " ; \n " ) ;
2018-08-12 19:49:08 -07:00
WRITE ( p , " if (bilinear && !(index0 == index1 && index1 == index2 && index2 == index3)) { \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " index1 = ((index1 >> depalShift) & depalMask) | depalOffset; \n " ) ;
WRITE ( p , " index2 = ((index2 >> depalShift) & depalMask) | depalOffset; \n " ) ;
WRITE ( p , " index3 = ((index3 >> depalShift) & depalMask) | depalOffset; \n " ) ;
2022-09-11 13:27:17 +02:00
p . C ( " t1 = " ) . LoadTexture2D ( " pal " , " ivec2(index1, 0) " , 0 ) . C ( " ; \n " ) ;
p . C ( " t2 = " ) . LoadTexture2D ( " pal " , " ivec2(index2, 0) " , 0 ) . C ( " ; \n " ) ;
p . C ( " t3 = " ) . LoadTexture2D ( " pal " , " ivec2(index3, 0) " , 0 ) . C ( " ; \n " ) ;
2018-04-13 20:00:14 +02:00
WRITE ( p , " t = mix(t, t1, fraction.x); \n " ) ;
WRITE ( p , " t2 = mix(t2, t3, fraction.x); \n " ) ;
WRITE ( p , " t = mix(t, t2, fraction.y); \n " ) ;
WRITE ( p , " } \n " ) ;
2022-09-11 14:14:18 +02:00
break ;
case ShaderDepalMode : : CLUT8_8888 :
2022-09-11 18:23:57 +02:00
if ( doTextureProjection ) {
// We don't use textureProj because we need better control and it's probably not much of a savings anyway.
// However it is good for precision on older hardware like PowerVR.
p . F ( " vec2 uv = %s.xy/%s.z; \n vec2 uv_round; \n " , texcoord , texcoord ) ;
} else {
p . F ( " vec2 uv = %s.xy; \n vec2 uv_round; \n " , texcoord ) ;
}
p . C ( " vec2 tsize = vec2(textureSize(tex, 0).xy); \n " ) ;
p . C ( " uv_round = floor(uv * tsize); \n " ) ;
p . C ( " int component = int(uv_round.x) & 3; \n " ) ;
p . C ( " uv_round.x *= 0.25; \n " ) ;
2022-09-12 15:34:32 +02:00
p . C ( " uv_round /= tsize; \n " ) ;
p . C ( " vec4 t = " ) . SampleTexture2D ( " tex " , " uv_round " ) . C ( " ; \n " ) ;
2022-09-11 18:23:57 +02:00
p . C ( " int index; \n " ) ;
p . C ( " switch (component) { \n " ) ;
2022-09-12 15:34:32 +02:00
p . C ( " case 0: index = int(t.x * 254.99); break; \n " ) ; // TODO: Not sure why 254.99 instead of 255.99, but it's currently needed.
p . C ( " case 1: index = int(t.y * 254.99); break; \n " ) ;
p . C ( " case 2: index = int(t.z * 254.99); break; \n " ) ;
p . C ( " case 3: index = int(t.w * 254.99); break; \n " ) ;
2022-09-11 18:23:57 +02:00
p . C ( " } \n " ) ;
p . C ( " t = " ) . LoadTexture2D ( " pal " , " ivec2(index, 0) " , 0 ) . C ( " ; \n " ) ;
2022-09-11 14:14:18 +02:00
break ;
2018-04-13 20:00:14 +02:00
}
2023-01-09 11:19:50 +01:00
WRITE ( p , " vec4 p = v_color0; \n " ) ;
2013-01-22 21:57:47 +01:00
2023-01-09 11:19:50 +01:00
if ( texFunc ! = GE_TEXFUNC_REPLACE ) {
2023-05-10 16:14:33 +02:00
if ( ubershader ) {
WRITE ( p , " t.a = max(t.a, u_texNoAlphaMul.x); \n " ) ;
} else if ( ! useTexAlpha ) {
WRITE ( p , " t.a = 1.0; \n " ) ;
}
2013-01-22 21:57:47 +01:00
}
2018-09-09 21:11:51 -07:00
2023-01-09 11:19:50 +01:00
switch ( texFunc ) {
case GE_TEXFUNC_MODULATE :
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = p * t%s; \n " , secondary ) ;
2023-01-09 11:19:50 +01:00
break ;
case GE_TEXFUNC_DECAL :
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = vec4(mix(p.rgb, t.rgb, t.a), p.a)%s; \n " , secondary ) ;
2023-01-09 11:19:50 +01:00
break ;
case GE_TEXFUNC_BLEND :
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = vec4(mix(p.rgb, u_texenv.rgb, t.rgb), p.a * t.a)%s; \n " , secondary ) ;
2023-01-09 11:19:50 +01:00
break ;
case GE_TEXFUNC_REPLACE :
2023-01-10 10:08:34 +01:00
WRITE ( p , " vec4 r = t; \n " ) ;
2023-05-10 16:14:33 +02:00
if ( ubershader ) {
WRITE ( p , " r.a = mix(r.a, p.a, u_texNoAlphaMul.x); \n " ) ;
} else if ( ! useTexAlpha ) {
WRITE ( p , " r.a = p.a; \n " ) ;
}
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = r%s; \n " , secondary ) ;
2023-01-09 11:19:50 +01:00
break ;
case GE_TEXFUNC_ADD :
case GE_TEXFUNC_UNKNOWN1 :
case GE_TEXFUNC_UNKNOWN2 :
case GE_TEXFUNC_UNKNOWN3 :
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = vec4(p.rgb + t.rgb, p.a * t.a)%s; \n " , secondary ) ;
2023-01-09 11:19:50 +01:00
break ;
default :
// Doesn't happen
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = p%s; \n " , secondary ) ; break ;
2023-01-09 11:19:50 +01:00
break ;
}
2023-01-05 17:05:42 +01:00
// This happens before fog is applied.
* uniformMask | = DIRTY_TEX_ALPHA_MUL ;
2023-05-09 17:59:16 +02:00
// We only need a clamp if the color will be further processed. Otherwise the hardware color conversion will clamp for us.
2023-05-10 16:14:33 +02:00
if ( ubershader ) {
if ( enableFog | | enableColorTest | | replaceBlend ! = REPLACE_BLEND_NO | | simulateLogicOpType ! = LOGICOPTYPE_NORMAL | | colorWriteMask | | blueToAlpha ) {
WRITE ( p , " v.rgb = clamp(v.rgb * u_texNoAlphaMul.y, 0.0, 1.0); \n " ) ;
} else {
WRITE ( p , " v.rgb *= u_texNoAlphaMul.y; \n " ) ;
}
} else if ( enableColorDouble ) {
p . C ( " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0); \n " ) ;
2023-05-09 17:59:16 +02:00
}
2012-11-01 16:19:01 +01:00
} else {
// No texture mapping
2023-05-09 17:50:43 +02:00
WRITE ( p , " vec4 v = v_color0%s; \n " , secondary ) ;
2012-11-01 16:19:01 +01:00
}
2013-03-03 15:05:50 +08:00
2023-05-08 22:01:14 +02:00
if ( enableFog ) {
WRITE ( p , " float fogCoef = clamp(v_fogdepth, 0.0, 1.0); \n " ) ;
WRITE ( p , " v = mix(vec4(u_fogcolor, v.a), v, fogCoef); \n " ) ;
}
2018-09-09 21:11:51 -07:00
Account for texel offsets in the frag test lookup.
Darn, thought this wouldn't be necessary but it is is for 1/255 and other
values.
Without cache:
PowerVR: temp, add, mul, fract, mul, compare
Other: mul, add, floor, compare
With cache: mul, add, texture lookup, compare
2014-09-08 18:58:33 -07:00
// Texture access is at half texels [0.5/256, 255.5/256], but colors are normalized [0, 255].
// So we have to scale to account for the difference.
2020-10-31 11:31:16 +01:00
char alphaTestXCoord [ 64 ] = " 0 " ;
2020-10-28 23:51:22 +01:00
if ( enableFragmentTestCache ) {
2015-03-08 19:08:21 -07:00
if ( enableColorTest & & ! colorTestAgainstZero ) {
Account for texel offsets in the frag test lookup.
Darn, thought this wouldn't be necessary but it is is for 1/255 and other
values.
Without cache:
PowerVR: temp, add, mul, fract, mul, compare
Other: mul, add, floor, compare
With cache: mul, add, texture lookup, compare
2014-09-08 18:58:33 -07:00
WRITE ( p , " vec4 vScale256 = v * %f + %f; \n " , 255.0 / 256.0 , 0.5 / 256.0 ) ;
2020-10-31 11:31:16 +01:00
truncate_cpy ( alphaTestXCoord , " vScale256.a " ) ;
Account for texel offsets in the frag test lookup.
Darn, thought this wouldn't be necessary but it is is for 1/255 and other
values.
Without cache:
PowerVR: temp, add, mul, fract, mul, compare
Other: mul, add, floor, compare
With cache: mul, add, texture lookup, compare
2014-09-08 18:58:33 -07:00
} else if ( enableAlphaTest & & ! alphaTestAgainstZero ) {
2020-10-31 11:31:16 +01:00
snprintf ( alphaTestXCoord , sizeof ( alphaTestXCoord ) , " v.a * %f + %f " , 255.0 / 256.0 , 0.5 / 256.0 ) ;
Account for texel offsets in the frag test lookup.
Darn, thought this wouldn't be necessary but it is is for 1/255 and other
values.
Without cache:
PowerVR: temp, add, mul, fract, mul, compare
Other: mul, add, floor, compare
With cache: mul, add, texture lookup, compare
2014-09-08 18:58:33 -07:00
}
}
2020-10-28 23:19:40 +01:00
const char * discardStatement = testForceToZero ? " v.a = 0.0; " : " DISCARD; " ;
2013-10-22 15:15:13 +02:00
if ( enableAlphaTest ) {
2022-11-28 22:59:42 +01:00
* fragmentShaderFlags | = FragmentShaderFlags : : USES_DISCARD ;
2014-08-24 15:26:38 -07:00
if ( alphaTestAgainstZero ) {
// When testing against 0 (extremely common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if ( alphaTestFunc = = GE_COMP_NOTEQUAL | | alphaTestFunc = = GE_COMP_GREATER ) {
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (v.a < 0.002) %s \n " , discardStatement ) ;
2014-08-24 15:26:38 -07:00
} else if ( alphaTestFunc ! = GE_COMP_NEVER ) {
// Anything else is a test for == 0. Happens sometimes, actually...
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (v.a > 0.002) %s \n " , discardStatement ) ;
2013-08-28 00:09:24 +02:00
} else {
2014-08-24 15:26:38 -07:00
// NEVER has been logged as used by games, although it makes little sense - statically failing.
// Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here.
2018-07-27 20:06:41 -07:00
WRITE ( p , " %s \n " , discardStatement ) ;
2013-08-28 00:09:24 +02:00
}
2020-10-28 23:51:22 +01:00
} else if ( enableFragmentTestCache ) {
2020-10-31 11:31:16 +01:00
WRITE ( p , " float aResult = %s(testtex, vec2(%s, 0)).a; \n " , compat . texture , alphaTestXCoord ) ;
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (aResult < 0.5) %s \n " , discardStatement ) ;
2014-06-13 23:07:07 -07:00
} else {
2014-08-24 15:26:38 -07:00
const char * alphaTestFuncs [ ] = { " # " , " # " , " != " , " == " , " >= " , " > " , " <= " , " < " } ;
if ( alphaTestFuncs [ alphaTestFunc ] [ 0 ] ! = ' # ' ) {
2020-10-21 23:39:34 +02:00
if ( compat . bitwiseOps ) {
2023-01-02 14:32:44 -08:00
WRITE ( p , " if ((roundAndScaleTo255i(v.a) & int(u_alphacolormask >> 0x18u)) %s int(u_alphacolorref >> 0x18u)) %s \n " , alphaTestFuncs [ alphaTestFunc ] , discardStatement ) ;
2017-11-21 15:42:01 +01:00
} else if ( gl_extensions . gpuVendor = = GPU_VENDOR_IMGTEC ) {
2014-08-24 15:26:38 -07:00
// Work around bad PVR driver problem where equality check + discard just doesn't work.
if ( alphaTestFunc ! = GE_COMP_NOTEQUAL ) {
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (roundTo255thf(v.a) %s u_alphacolorref.a) %s \n " , alphaTestFuncs [ alphaTestFunc ] , discardStatement ) ;
2014-08-24 15:26:38 -07:00
}
} else {
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) %s \n " , alphaTestFuncs [ alphaTestFunc ] , discardStatement ) ;
2014-08-24 15:26:38 -07:00
}
} else {
// This means NEVER. See above.
2018-07-27 20:06:41 -07:00
WRITE ( p , " %s \n " , discardStatement ) ;
2014-08-24 15:26:38 -07:00
}
2013-04-29 01:08:23 -07:00
}
2012-11-23 12:43:31 +01:00
}
2014-01-12 11:14:48 -08:00
2013-02-13 07:50:08 +08:00
if ( enableColorTest ) {
2022-11-28 22:59:42 +01:00
* fragmentShaderFlags | = FragmentShaderFlags : : USES_DISCARD ;
2015-03-08 19:08:21 -07:00
if ( colorTestAgainstZero ) {
// When testing against 0 (common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if ( colorTestFunc = = GE_COMP_NOTEQUAL ) {
2021-01-16 12:56:04 +01:00
if ( compat . shaderLanguage = = GLSL_VULKAN ) {
// Old workaround for Adreno driver bug. We could make this the main path actually
// since the math is roughly equivalent given the non-negative inputs.
WRITE ( p , " if (v.r + v.g + v.b < 0.002) %s \n " , discardStatement ) ;
} else {
WRITE ( p , " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) %s \n " , discardStatement ) ;
}
2015-03-08 19:08:21 -07:00
} else if ( colorTestFunc ! = GE_COMP_NEVER ) {
2021-01-16 12:56:04 +01:00
if ( compat . shaderLanguage = = GLSL_VULKAN ) {
// See the GE_COMP_NOTEQUAL case.
WRITE ( p , " if (v.r + v.g + v.b > 0.002) %s \n " , discardStatement ) ;
} else {
// Anything else is a test for == 0.
WRITE ( p , " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) %s \n " , discardStatement ) ;
}
2015-03-08 19:08:21 -07:00
} else {
// NEVER has been logged as used by games, although it makes little sense - statically failing.
// Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here.
2018-07-27 20:06:41 -07:00
WRITE ( p , " %s \n " , discardStatement ) ;
2015-03-08 19:08:21 -07:00
}
2020-10-28 23:51:22 +01:00
} else if ( enableFragmentTestCache ) {
2020-10-21 23:39:34 +02:00
WRITE ( p , " float rResult = %s(testtex, vec2(vScale256.r, 0)).r; \n " , compat . texture ) ;
WRITE ( p , " float gResult = %s(testtex, vec2(vScale256.g, 0)).g; \n " , compat . texture ) ;
WRITE ( p , " float bResult = %s(testtex, vec2(vScale256.b, 0)).b; \n " , compat . texture ) ;
2014-09-07 23:12:56 -07:00
if ( colorTestFunc = = GE_COMP_EQUAL ) {
2018-09-09 20:09:48 -07:00
// Equal means all parts must be equal (so discard if any is not.)
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) %s \n " , discardStatement ) ;
2014-09-07 23:12:56 -07:00
} else {
// Not equal means any part must be not equal.
2018-07-27 20:06:41 -07:00
WRITE ( p , " if (rResult < 0.5 && gResult < 0.5 && bResult < 0.5) %s \n " , discardStatement ) ;
2014-09-07 23:12:56 -07:00
}
2014-06-13 23:07:07 -07:00
} else {
2014-08-24 15:26:38 -07:00
const char * colorTestFuncs [ ] = { " # " , " # " , " != " , " == " } ;
2022-09-25 20:34:35 -07:00
const char * test = colorTestFuncs [ colorTestFunc ] ;
if ( test [ 0 ] ! = ' # ' ) {
2020-10-29 00:38:52 +01:00
// TODO: Unify these paths better.
2022-09-25 20:34:35 -07:00
if ( compat . shaderLanguage = = HLSL_D3D9 ) {
2020-10-30 18:49:50 +01:00
// TODO: Use a texture to lookup bitwise ops instead?
WRITE ( p , " vec3 colortest = roundAndScaleTo255v(v.rgb); \n " ) ;
WRITE ( p , " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) %s \n " , test , test , test , discardStatement ) ;
2020-10-29 00:38:52 +01:00
} else if ( compat . bitwiseOps ) {
2022-09-25 20:34:35 -07:00
WRITE ( p , " uint v_uint = roundAndScaleTo8x4(v.rgb); \n " ) ;
WRITE ( p , " uint v_masked = v_uint & u_alphacolormask; \n " ) ;
2022-10-10 17:16:52 +02:00
WRITE ( p , " uint colorTestRef = (u_alphacolorref & u_alphacolormask) & 0xFFFFFFu; \n " ) ;
2022-09-25 20:34:35 -07:00
WRITE ( p , " if (v_masked %s colorTestRef) %s \n " , test , discardStatement ) ;
2017-11-21 15:42:01 +01:00
} else if ( gl_extensions . gpuVendor = = GPU_VENDOR_IMGTEC ) {
2022-09-25 20:34:35 -07:00
WRITE ( p , " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) %s \n " , test , discardStatement ) ;
2014-08-24 15:26:38 -07:00
} else {
2022-09-25 20:34:35 -07:00
WRITE ( p , " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) %s \n " , test , discardStatement ) ;
2014-08-24 15:26:38 -07:00
}
} else {
2018-07-27 20:06:41 -07:00
WRITE ( p , " %s \n " , discardStatement ) ;
2014-08-24 15:26:38 -07:00
}
2013-05-07 07:52:49 -07:00
}
2014-01-12 11:14:48 -08:00
}
2018-09-09 21:11:51 -07:00
if ( replaceBlend = = REPLACE_BLEND_2X_SRC ) {
WRITE ( p , " v.rgb = v.rgb * 2.0; \n " ) ;
2012-11-28 13:45:22 +01:00
}
2012-11-23 12:43:31 +01:00
2022-04-24 22:54:08 +02:00
// In some cases we need to replicate the first half of the blend equation here.
// In case of blue-to-alpha, it's since we overwrite alpha with blue before the actual blend equation runs.
if ( replaceBlend = = REPLACE_BLEND_PRE_SRC | | replaceBlend = = REPLACE_BLEND_PRE_SRC_2X_ALPHA | | replaceBlend = = REPLACE_BLEND_BLUE_TO_ALPHA ) {
2014-08-03 10:28:14 -07:00
const char * srcFactor = " ERROR " ;
2015-10-14 10:50:08 +02:00
switch ( replaceBlendFuncA ) {
2014-08-03 10:28:14 -07:00
case GE_SRCBLEND_DSTCOLOR : srcFactor = " ERROR " ; break ;
case GE_SRCBLEND_INVDSTCOLOR : srcFactor = " ERROR " ; break ;
2020-10-23 23:30:02 +02:00
case GE_SRCBLEND_SRCALPHA : srcFactor = " splat3(v.a) " ; break ;
case GE_SRCBLEND_INVSRCALPHA : srcFactor = " splat3(1.0 - v.a) " ; break ;
2014-08-03 10:28:14 -07:00
case GE_SRCBLEND_DSTALPHA : srcFactor = " ERROR " ; break ;
case GE_SRCBLEND_INVDSTALPHA : srcFactor = " ERROR " ; break ;
2020-10-23 23:30:02 +02:00
case GE_SRCBLEND_DOUBLESRCALPHA : srcFactor = " splat3(v.a * 2.0) " ; break ;
case GE_SRCBLEND_DOUBLEINVSRCALPHA : srcFactor = " splat3(1.0 - v.a * 2.0) " ; break ;
2015-11-14 12:37:32 -08:00
// PRE_SRC for REPLACE_BLEND_PRE_SRC_2X_ALPHA means "double the src."
// It's close to the same, but clamping can still be an issue.
2020-10-23 23:30:02 +02:00
case GE_SRCBLEND_DOUBLEDSTALPHA : srcFactor = " splat3(2.0) " ; break ;
2014-08-03 10:28:14 -07:00
case GE_SRCBLEND_DOUBLEINVDSTALPHA : srcFactor = " ERROR " ; break ;
case GE_SRCBLEND_FIXA : srcFactor = " u_blendFixA " ; break ;
2015-11-19 06:48:06 -08:00
default : srcFactor = " u_blendFixA " ; break ;
2014-08-03 10:28:14 -07:00
}
2020-10-22 22:36:02 +02:00
if ( ! strcmp ( srcFactor , " ERROR " ) ) {
* errorString = " Bad replaceblend src factor " ;
return false ;
}
2014-08-03 10:28:14 -07:00
WRITE ( p , " v.rgb = v.rgb * %s; \n " , srcFactor ) ;
}
2022-09-02 22:20:11 +02:00
if ( replaceBlend = = REPLACE_BLEND_READ_FRAMEBUFFER ) {
2020-10-29 00:38:52 +01:00
const char * srcFactor = nullptr ;
const char * dstFactor = nullptr ;
2014-05-11 09:51:35 -07:00
2015-10-14 10:50:08 +02:00
switch ( replaceBlendFuncA ) {
2014-05-11 09:51:35 -07:00
case GE_SRCBLEND_DSTCOLOR : srcFactor = " destColor.rgb " ; break ;
2020-10-23 23:30:02 +02:00
case GE_SRCBLEND_INVDSTCOLOR : srcFactor = " (splat3(1.0) - destColor.rgb) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_SRCBLEND_SRCALPHA : srcFactor = " v.aaa " ; break ;
2020-10-23 23:30:02 +02:00
case GE_SRCBLEND_INVSRCALPHA : srcFactor = " splat3(1.0 - v.a) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_SRCBLEND_DSTALPHA : srcFactor = " destColor.aaa " ; break ;
2020-11-02 21:56:43 -08:00
case GE_SRCBLEND_INVDSTALPHA : srcFactor = " (splat3(1.0) - destColor.aaa) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_SRCBLEND_DOUBLESRCALPHA : srcFactor = " v.aaa * 2.0 " ; break ;
2020-11-02 21:56:43 -08:00
case GE_SRCBLEND_DOUBLEINVSRCALPHA : srcFactor = " (splat3(1.0) - v.aaa * 2.0) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_SRCBLEND_DOUBLEDSTALPHA : srcFactor = " destColor.aaa * 2.0 " ; break ;
2020-11-02 21:56:43 -08:00
case GE_SRCBLEND_DOUBLEINVDSTALPHA : srcFactor = " (splat3(1.0) - destColor.aaa * 2.0) " ; break ;
2014-05-11 09:51:35 -07:00
case GE_SRCBLEND_FIXA : srcFactor = " u_blendFixA " ; break ;
2015-11-19 06:48:06 -08:00
default : srcFactor = " u_blendFixA " ; break ;
2014-05-11 09:51:35 -07:00
}
2015-10-14 10:50:08 +02:00
switch ( replaceBlendFuncB ) {
2014-05-11 09:51:35 -07:00
case GE_DSTBLEND_SRCCOLOR : dstFactor = " v.rgb " ; break ;
2020-10-23 23:30:02 +02:00
case GE_DSTBLEND_INVSRCCOLOR : dstFactor = " (splat3(1.0) - v.rgb) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_DSTBLEND_SRCALPHA : dstFactor = " v.aaa " ; break ;
2020-11-02 21:56:43 -08:00
case GE_DSTBLEND_INVSRCALPHA : dstFactor = " (splat3(1.0) - v.aaa) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_DSTBLEND_DSTALPHA : dstFactor = " destColor.aaa " ; break ;
2020-11-02 21:56:43 -08:00
case GE_DSTBLEND_INVDSTALPHA : dstFactor = " (splat3(1.0) - destColor.aaa) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_DSTBLEND_DOUBLESRCALPHA : dstFactor = " v.aaa * 2.0 " ; break ;
2020-11-02 21:56:43 -08:00
case GE_DSTBLEND_DOUBLEINVSRCALPHA : dstFactor = " (splat3(1.0) - v.aaa * 2.0) " ; break ;
2020-10-29 00:38:52 +01:00
case GE_DSTBLEND_DOUBLEDSTALPHA : dstFactor = " destColor.aaa * 2.0 " ; break ;
2020-11-02 21:56:43 -08:00
case GE_DSTBLEND_DOUBLEINVDSTALPHA : dstFactor = " (splat3(1.0) - destColor.aaa * 2.0) " ; break ;
2014-05-11 09:51:35 -07:00
case GE_DSTBLEND_FIXB : dstFactor = " u_blendFixB " ; break ;
2020-10-22 23:05:53 +02:00
default : dstFactor = " u_blendFixB " ; break ;
2014-05-11 09:51:35 -07:00
}
2015-10-14 10:50:08 +02:00
switch ( replaceBlendEq ) {
2014-05-11 09:51:35 -07:00
case GE_BLENDMODE_MUL_AND_ADD :
WRITE ( p , " v.rgb = v.rgb * %s + destColor.rgb * %s; \n " , srcFactor , dstFactor ) ;
break ;
case GE_BLENDMODE_MUL_AND_SUBTRACT :
WRITE ( p , " v.rgb = v.rgb * %s - destColor.rgb * %s; \n " , srcFactor , dstFactor ) ;
break ;
case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE :
2014-08-26 00:39:14 -07:00
WRITE ( p , " v.rgb = destColor.rgb * %s - v.rgb * %s; \n " , dstFactor , srcFactor ) ;
2014-05-11 09:51:35 -07:00
break ;
case GE_BLENDMODE_MIN :
2014-05-11 17:55:02 -07:00
WRITE ( p , " v.rgb = min(v.rgb, destColor.rgb); \n " ) ;
2014-05-11 09:51:35 -07:00
break ;
case GE_BLENDMODE_MAX :
2014-05-11 17:55:02 -07:00
WRITE ( p , " v.rgb = max(v.rgb, destColor.rgb); \n " ) ;
2014-05-11 09:51:35 -07:00
break ;
case GE_BLENDMODE_ABSDIFF :
2014-05-11 17:55:02 -07:00
WRITE ( p , " v.rgb = abs(v.rgb - destColor.rgb); \n " ) ;
2014-05-11 09:51:35 -07:00
break ;
2020-10-22 22:36:02 +02:00
default :
* errorString = " Bad replace blend eq " ;
return false ;
2014-05-11 09:51:35 -07:00
}
}
2014-08-03 10:28:14 -07:00
if ( replaceBlend = = REPLACE_BLEND_2X_ALPHA | | replaceBlend = = REPLACE_BLEND_PRE_SRC_2X_ALPHA ) {
2020-11-08 12:31:43 +01:00
WRITE ( p , " v.a *= 2.0; \n " ) ;
2014-08-03 10:28:14 -07:00
}
2014-04-13 16:01:55 +08:00
}
2020-10-31 11:31:16 +01:00
char replacedAlpha [ 64 ] = " 0.0 " ;
2013-12-16 23:41:42 +01:00
if ( stencilToAlpha ! = REPLACE_ALPHA_NO ) {
2015-10-14 10:50:08 +02:00
switch ( replaceAlphaWithStencilType ) {
2013-12-03 00:15:08 -08:00
case STENCIL_VALUE_UNIFORM :
2020-10-31 11:31:16 +01:00
truncate_cpy ( replacedAlpha , " u_stencilReplaceValue " ) ;
2013-12-03 00:15:08 -08:00
break ;
case STENCIL_VALUE_ZERO :
2020-10-31 11:31:16 +01:00
truncate_cpy ( replacedAlpha , " 0.0 " ) ;
2013-12-03 00:15:08 -08:00
break ;
case STENCIL_VALUE_ONE :
2014-08-24 09:32:38 -07:00
case STENCIL_VALUE_INVERT :
// In invert, we subtract by one, but we want to output one here.
2020-10-31 11:31:16 +01:00
truncate_cpy ( replacedAlpha , " 1.0 " ) ;
2013-12-03 00:15:08 -08:00
break ;
2014-08-24 09:20:17 -07:00
case STENCIL_VALUE_INCR_4 :
case STENCIL_VALUE_DECR_4 :
// We're adding/subtracting, just by the smallest value in 4-bit.
2020-10-31 11:31:16 +01:00
snprintf ( replacedAlpha , sizeof ( replacedAlpha ) , " %f " , 1.0 / 15.0 ) ;
2014-08-24 09:20:17 -07:00
break ;
case STENCIL_VALUE_INCR_8 :
case STENCIL_VALUE_DECR_8 :
// We're adding/subtracting, just by the smallest value in 8-bit.
2020-10-31 11:31:16 +01:00
snprintf ( replacedAlpha , sizeof ( replacedAlpha ) , " %f " , 1.0 / 255.0 ) ;
2014-08-24 09:20:17 -07:00
break ;
2013-12-06 11:15:13 +01:00
case STENCIL_VALUE_KEEP :
// Do nothing. We'll mask out the alpha using color mask.
break ;
2013-12-03 00:15:08 -08:00
}
2013-12-03 00:09:48 +01:00
}
2013-12-24 22:36:57 +08:00
2014-09-06 08:21:03 -07:00
switch ( stencilToAlpha ) {
case REPLACE_ALPHA_DUALSOURCE :
2020-11-09 09:07:52 +01:00
WRITE ( p , " %s = vec4(v.rgb, %s); \n " , compat . fragColor0 , replacedAlpha ) ;
WRITE ( p , " %s = vec4(0.0, 0.0, 0.0, v.a); \n " , compat . fragColor1 ) ;
2014-09-06 08:21:03 -07:00
break ;
case REPLACE_ALPHA_YES :
2020-11-09 09:07:52 +01:00
WRITE ( p , " %s = vec4(v.rgb, %s); \n " , compat . fragColor0 , replacedAlpha ) ;
2014-09-06 08:21:03 -07:00
break ;
case REPLACE_ALPHA_NO :
2020-11-09 09:07:52 +01:00
WRITE ( p , " %s = v; \n " , compat . fragColor0 ) ;
2014-09-06 08:21:03 -07:00
break ;
2015-10-14 11:17:13 +02:00
default :
2020-10-21 23:09:34 +02:00
* errorString = " Bad stencil-to-alpha type, corrupt ID? " ;
2015-10-14 17:44:50 +02:00
return false ;
2014-09-06 08:21:03 -07:00
}
2022-09-03 23:28:03 +02:00
switch ( simulateLogicOpType ) {
2014-09-23 21:13:47 -07:00
case LOGICOPTYPE_ONE :
2020-11-09 09:07:52 +01:00
WRITE ( p , " %s.rgb = splat3(1.0); \n " , compat . fragColor0 ) ;
2014-09-23 21:13:47 -07:00
break ;
case LOGICOPTYPE_INVERT :
2020-11-09 09:07:52 +01:00
WRITE ( p , " %s.rgb = splat3(1.0) - %s.rgb; \n " , compat . fragColor0 , compat . fragColor0 ) ;
2014-09-23 21:13:47 -07:00
break ;
case LOGICOPTYPE_NORMAL :
break ;
2015-10-14 11:17:13 +02:00
default :
2020-10-21 23:09:34 +02:00
* errorString = " Bad logic op type, corrupt ID? " ;
2015-10-14 17:44:50 +02:00
return false ;
2014-09-23 21:13:47 -07:00
}
2022-09-04 11:14:47 +02:00
// Final color computed - apply logic ops and bitwise color write mask, through shader blending, if specified.
if ( colorWriteMask | | replaceLogicOp ) {
2023-01-02 22:24:00 +01:00
WRITE ( p , " highp uint v32 = packUnorm4x8%s(%s); \n " , packSuffix , compat . fragColor0 ) ;
WRITE ( p , " highp uint d32 = packUnorm4x8%s(destColor); \n " , packSuffix ) ;
2022-09-04 11:14:47 +02:00
// v32 is both the "s" to the logical operation, and the value that we'll merge to the destination with masking later.
// d32 is the "d" to the logical operation.
2022-09-04 19:27:26 +02:00
// NOTE: Alpha of v32 needs to be preserved. Same equations as in the software renderer.
2022-09-04 11:14:47 +02:00
switch ( replaceLogicOpType ) {
2022-09-04 19:27:26 +02:00
case GE_LOGIC_CLEAR : p . C ( " v32 &= 0xFF000000u; \n " ) ; break ;
case GE_LOGIC_AND : p . C ( " v32 = v32 & (d32 | 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_AND_REVERSE : p . C ( " v32 = v32 & (~d32 | 0xFF000000u); \n " ) ; break ;
2022-09-04 11:14:47 +02:00
case GE_LOGIC_COPY : break ; // source to dest, do nothing. Will be set to this, if not used.
2022-09-04 19:27:26 +02:00
case GE_LOGIC_AND_INVERTED : p . C ( " v32 = (~v32 & (d32 & 0x00FFFFFFu)) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_NOOP : p . C ( " v32 = (d32 & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_XOR : p . C ( " v32 = v32 ^ (d32 & 0x00FFFFFFu); \n " ) ; break ;
case GE_LOGIC_OR : p . C ( " v32 = v32 | (d32 & 0x00FFFFFFu); \n " ) ; break ;
case GE_LOGIC_NOR : p . C ( " v32 = (~(v32 | d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_EQUIV : p . C ( " v32 = (~(v32 ^ d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_INVERTED : p . C ( " v32 = (~d32 & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_OR_REVERSE : p . C ( " v32 = v32 | (~d32 & 0x00FFFFFFu); \n " ) ; break ;
2022-09-04 22:52:24 +02:00
case GE_LOGIC_COPY_INVERTED : p . C ( " v32 = (~v32 & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
2022-09-04 19:27:26 +02:00
case GE_LOGIC_OR_INVERTED : p . C ( " v32 = ((~v32 | d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
case GE_LOGIC_NAND : p . C ( " v32 = (~(v32 & d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u); \n " ) ; break ;
2022-09-27 19:22:41 -07:00
case GE_LOGIC_SET : p . C ( " v32 |= 0x00FFFFFFu; \n " ) ; break ;
2022-09-04 11:14:47 +02:00
}
// Note that the mask has already been flipped to the PC way - 1 means write.
if ( colorWriteMask ) {
2022-10-11 22:26:31 -07:00
if ( stencilToAlpha ! = REPLACE_ALPHA_NO )
WRITE ( p , " v32 = (v32 & u_colorWriteMask) | (d32 & ~u_colorWriteMask); \n " ) ;
else
WRITE ( p , " v32 = (v32 & u_colorWriteMask & 0x00FFFFFFu) | (d32 & (~u_colorWriteMask | 0xFF000000u)); \n " ) ;
2022-09-04 11:14:47 +02:00
}
2023-01-02 22:24:00 +01:00
WRITE ( p , " %s = unpackUnorm4x8%s(v32); \n " , compat . fragColor0 , packSuffix ) ;
2020-11-08 23:17:06 +01:00
}
2022-04-24 20:53:09 +02:00
if ( blueToAlpha ) {
WRITE ( p , " %s = vec4(0.0, 0.0, 0.0, %s.z); // blue to alpha \n " , compat . fragColor0 , compat . fragColor0 ) ;
}
2022-10-17 08:27:49 +02:00
if ( gstate_c . Use ( GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT ) ) {
2023-02-11 14:45:01 +01:00
DepthScaleFactors depthScale = GetDepthScaleFactors ( gstate_c . UseFlags ( ) ) ;
const double scale = depthScale . ScaleU16 ( ) ;
2016-01-19 20:19:50 -08:00
2015-08-27 00:28:16 +02:00
WRITE ( p , " highp float z = gl_FragCoord.z; \n " ) ;
2022-10-17 08:30:27 +02:00
if ( gstate_c . Use ( GPU_USE_ACCURATE_DEPTH ) ) {
2016-02-06 20:18:50 -08:00
// We center the depth with an offset, but only its fraction matters.
// When (DepthSliceFactor() - 1) is odd, it will be 0.5, otherwise 0.
2023-02-11 14:45:01 +01:00
if ( ( ( int ) ( depthScale . Scale ( ) - 1.0f ) & 1 ) = = 1 ) {
2016-02-06 20:18:50 -08:00
WRITE ( p , " z = (floor((z * %f) - (1.0 / 2.0)) + (1.0 / 2.0)) * (1.0 / %f); \n " , scale , scale ) ;
} else {
WRITE ( p , " z = floor(z * %f) * (1.0 / %f); \n " , scale , scale ) ;
}
2016-01-19 20:19:50 -08:00
} else {
2022-07-31 13:01:50 +02:00
WRITE ( p , " z = (1.0 / 65535.0) * floor(z * 65535.0); \n " ) ;
2016-01-19 20:19:50 -08:00
}
2015-08-27 00:28:16 +02:00
WRITE ( p , " gl_FragDepth = z; \n " ) ;
2022-07-12 00:17:26 +02:00
} else if ( useDiscardStencilBugWorkaround ) {
// Adreno and some Mali drivers apply early frag tests even with discard in the shader,
// when only stencil is used. The exact situation seems to vary by driver.
// Writing depth prevents the bug for both vendors, even with depth_unchanged specified.
// This doesn't make a ton of sense, but empirically does work.
2020-10-22 22:36:02 +02:00
WRITE ( p , " gl_FragDepth = gl_FragCoord.z; \n " ) ;
2015-08-27 00:28:16 +02:00
}
2022-08-01 12:11:42 +02:00
if ( compat . shaderLanguage = = HLSL_D3D11 | | compat . shaderLanguage = = HLSL_D3D9 ) {
2020-11-09 16:05:28 +01:00
if ( writeDepth ) {
WRITE ( p , " outfragment.depth = gl_FragDepth; \n " ) ;
}
2020-10-28 23:19:40 +01:00
WRITE ( p , " return outfragment; \n " ) ;
}
2012-11-01 16:19:01 +01:00
WRITE ( p , " } \n " ) ;
2015-10-14 17:44:50 +02:00
return true ;
2012-11-01 16:19:01 +01:00
}