diff --git a/CMakeLists.txt b/CMakeLists.txt index 2018645094..442aaa6ecc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,8 @@ if(NOT MSVC) add_compile_options(-fno-strict-aliasing) if (${CMAKE_C_COMPILER_ID} STREQUAL "Intel") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -parallel -fopenmp") + else() + add_compile_options(-ffast-math) endif() if(X86 OR X86_64) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 9a6d9afb82..83bbd08e3e 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -233,7 +233,7 @@ void ComputeFragmentShaderID(ShaderID *id_out) { bool enableFog = gstate.isFogEnabled() && !isModeThrough; bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue(); bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue(); - bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled(); + bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled() && gstate.getTextureFunction() == GE_TEXFUNC_MODULATE; bool doTextureProjection = (gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX && MatrixNeedsProjection(gstate.tgenMatrix)); bool doTextureAlpha = gstate.isTextureAlphaUsed(); bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT; diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index d607202b3e..5a852bce51 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -1,4 +1,5 @@ #include +#include #include "ShaderUniforms.h" #include "base/display.h" @@ -143,22 +144,15 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView getFloat24(gstate.fog1), getFloat24(gstate.fog2), }; - if (my_isinf(fogcoef[1])) { - // not really sure what a sensible value might be. - fogcoef[1] = fogcoef[1] < 0.0f ? -10000.0f : 10000.0f; - } else if (my_isnan(fogcoef[1])) { - // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 - // Just put the fog far away at a large finite distance. - // Infinities and NaNs are rather unpredictable in shaders on many GPUs - // so it's best to just make it a sane calculation. - fogcoef[0] = 100000.0f; - fogcoef[1] = 1.0f; + // The PSP just ignores infnan here (ignoring IEEE), so take it down to a valid float. + // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 + if (my_isnanorinf(fogcoef[0])) { + // Not really sure what a sensible value might be, but let's try 64k. + fogcoef[0] = std::signbit(fogcoef[0]) ? -65535.0f : 65535.0f; } -#ifndef MOBILE_DEVICE - else if (my_isnanorinf(fogcoef[1]) || my_isnanorinf(fogcoef[0])) { - ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef[0], fogcoef[1]); + if (my_isnanorinf(fogcoef[1])) { + fogcoef[1] = std::signbit(fogcoef[1]) ? -65535.0f : 65535.0f; } -#endif CopyFloat2(ub->fogCoef, fogcoef); } diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 93385d8aa3..fc943993b4 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "math/math_util.h" #include "gfx_es2/gpu_features.h" @@ -181,18 +182,13 @@ void SoftwareTransform( Lighter lighter(vertType); float fog_end = getFloat24(gstate.fog1); float fog_slope = getFloat24(gstate.fog2); - // Same fixup as in ShaderManager.cpp - if (my_isinf(fog_slope)) { - // not really sure what a sensible value might be. - fog_slope = fog_slope < 0.0f ? -10000.0f : 10000.0f; + // Same fixup as in ShaderManagerGLES.cpp + if (my_isnanorinf(fog_end)) { + // Not really sure what a sensible value might be, but let's try 64k. + fog_end = std::signbit(fog_end) ? -65535.0f : 65535.0f; } - if (my_isnan(fog_slope)) { - // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 - // Just put the fog far away at a large finite distance. - // Infinities and NaNs are rather unpredictable in shaders on many GPUs - // so it's best to just make it a sane calculation. - fog_end = 100000.0f; - fog_slope = 1.0f; + if (my_isnanorinf(fog_slope)) { + fog_slope = std::signbit(fog_slope) ? -65535.0f : 65535.0f; } int colorIndOffset = 0; diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index d36c6bd5f0..4c869d0d60 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -281,6 +281,11 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, char* scalerC WRITE(p, " float4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " float4 v = In.v_color0 %s;\n", secondary); @@ -316,6 +321,12 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, char* scalerC } } } + + if (enableFog) { + WRITE(p, " float fogCoef = clamp(In.v_fogdepth, 0.0, 1.0);\n"); + WRITE(p, " v = lerp(float4(u_fogcolor, v.a), v, fogCoef);\n"); + } + if (enableColorTest) { if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. @@ -352,18 +363,10 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, char* scalerC } } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } - if (enableFog) { - WRITE(p, " float fogCoef = clamp(In.v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = lerp(float4(u_fogcolor, v.a), v, fogCoef);\n"); - } - if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { const char *srcFactor = "ERROR"; switch (replaceBlendFuncA) { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 40764cd7a1..27fae8b03c 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -19,6 +19,7 @@ #define SHADERLOG #endif +#include #include #include "gfx/d3d9_shader.h" #include "base/logging.h" @@ -375,22 +376,15 @@ void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) { getFloat24(gstate.fog1), getFloat24(gstate.fog2), }; - if (my_isinf(fogcoef[1])) { - // not really sure what a sensible value might be. - fogcoef[1] = fogcoef[1] < 0.0f ? -10000.0f : 10000.0f; - } else if (my_isnan(fogcoef[1])) { - // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 - // Just put the fog far away at a large finite distance. - // Infinities and NaNs are rather unpredictable in shaders on many GPUs - // so it's best to just make it a sane calculation. - fogcoef[0] = 100000.0f; - fogcoef[1] = 1.0f; + // The PSP just ignores infnan here (ignoring IEEE), so take it down to a valid float. + // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 + if (my_isnanorinf(fogcoef[0])) { + // Not really sure what a sensible value might be, but let's try 64k. + fogcoef[0] = std::signbit(fogcoef[0]) ? -65535.0f : 65535.0f; } -#ifndef MOBILE_DEVICE - else if (my_isnanorinf(fogcoef[1]) || my_isnanorinf(fogcoef[0])) { - ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef[0], fogcoef[1]); + if (my_isnanorinf(fogcoef[1])) { + fogcoef[1] = std::signbit(fogcoef[1]) ? -65535.0f : 65535.0f; } -#endif VSSetFloatArray(CONST_VS_FOGCOEF, fogcoef, 2); } if (dirtyUniforms & DIRTY_GUARDBAND) { diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index c5ba24d893..e130c1a7e2 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -501,11 +501,22 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " vec4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " vec4 v = v_color0 %s;\n", secondary); } + 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"); + // WRITE(p, " v.x = v_depth;\n"); + } + // 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. std::string alphaTestXCoord = "0"; @@ -577,7 +588,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " float gResult = %s(testtex, vec2(vScale256.g, 0)).g;\n", texture); WRITE(p, " float bResult = %s(testtex, vec2(vScale256.b, 0)).b;\n", texture); if (colorTestFunc == GE_COMP_EQUAL) { - // Equal means all parts must be equal. + // Equal means all parts must be equal (so discard if any is not.) WRITE(p, " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) %s\n", discardStatement); } else { // Not equal means any part must be not equal. @@ -603,19 +614,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } - 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"); - // WRITE(p, " v.x = v_depth;\n"); - } - if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { const char *srcFactor = "ERROR"; switch (replaceBlendFuncA) { diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index ea482896a7..e870426ffd 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -81,6 +81,8 @@ void FragmentTestCacheGLES::BindTestTexture(int slot) { GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid); lastTexture_ = tex; render_->BindTexture(slot, tex); + // We only need to do this once for the texture. + render_->SetTextureSampler(slot, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); FragmentTestTexture item; item.lastFrame = gpuStats.numFlips; item.texture = tex; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 2dc5359a64..fc0bdc7e73 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -19,8 +19,9 @@ #include "Common/CommonWindows.h" #endif -#include +#include #include +#include #include "math/dataconv.h" #include "base/logging.h" @@ -385,22 +386,15 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { getFloat24(gstate.fog1), getFloat24(gstate.fog2), }; - if (my_isinf(fogcoef[1])) { - // not really sure what a sensible value might be. - fogcoef[1] = fogcoef[1] < 0.0f ? -10000.0f : 10000.0f; - } else if (my_isnan(fogcoef[1])) { - // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 - // Just put the fog far away at a large finite distance. - // Infinities and NaNs are rather unpredictable in shaders on many GPUs - // so it's best to just make it a sane calculation. - fogcoef[0] = 100000.0f; - fogcoef[1] = 1.0f; + // The PSP just ignores infnan here (ignoring IEEE), so take it down to a valid float. + // Workaround for https://github.com/hrydgard/ppsspp/issues/5384#issuecomment-38365988 + if (my_isnanorinf(fogcoef[0])) { + // Not really sure what a sensible value might be, but let's try 64k. + fogcoef[0] = std::signbit(fogcoef[0]) ? -65535.0f : 65535.0f; } -#ifndef MOBILE_DEVICE - else if (my_isnanorinf(fogcoef[1]) || my_isnanorinf(fogcoef[0])) { - ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef[0], fogcoef[1]); + if (my_isnanorinf(fogcoef[1])) { + fogcoef[1] = std::signbit(fogcoef[1]) ? -65535.0f : 65535.0f; } -#endif render_->SetUniformF(&u_fogcoef, 2, fogcoef); } diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 851747807d..1ab91015eb 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -157,7 +157,7 @@ void ProcessRect(const VertexData& v0, const VertexData& v1) // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; - buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth = 1.0f; + buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1]; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 2a5f2aa308..a507224193 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -484,9 +484,8 @@ static inline bool StencilTestPassed(u8 stencil) return true; } -static inline u8 ApplyStencilOp(int op, int x, int y) -{ - u8 old_stencil = GetPixelStencil(x, y); // TODO: Apply mask? +static inline u8 ApplyStencilOp(int op, u8 old_stencil) { + // TODO: Apply mask to reference or old stencil? u8 reference_stencil = gstate.getStencilTestRef(); // TODO: Apply mask? switch (op) { @@ -538,71 +537,71 @@ static inline u8 ApplyStencilOp(int op, int x, int y) return old_stencil; } -static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) -{ +static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) { + // All of the operations here intentionally preserve alpha/stencil. switch (op) { case GE_LOGIC_CLEAR: - new_color = 0; + new_color &= 0xFF000000; break; case GE_LOGIC_AND: - new_color = new_color & old_color; + new_color = new_color & (old_color | 0xFF000000); break; case GE_LOGIC_AND_REVERSE: - new_color = new_color & ~old_color; + new_color = new_color & (~old_color | 0xFF000000); break; case GE_LOGIC_COPY: - //new_color = new_color; + // No change to new_color. break; case GE_LOGIC_AND_INVERTED: - new_color = ~new_color & old_color; + new_color = (~new_color & (old_color & 0x00FFFFFF)) | (new_color & 0xFF000000); break; case GE_LOGIC_NOOP: - new_color = old_color; + new_color = (old_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_XOR: - new_color = new_color ^ old_color; + new_color = new_color ^ (old_color & 0x00FFFFFF); break; case GE_LOGIC_OR: - new_color = new_color | old_color; + new_color = new_color | (old_color & 0x00FFFFFF); break; case GE_LOGIC_NOR: - new_color = ~(new_color | old_color); + new_color = (~(new_color | old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_EQUIV: - new_color = ~(new_color ^ old_color); + new_color = (~(new_color ^ old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_INVERTED: - new_color = ~old_color; + new_color = (~old_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_OR_REVERSE: - new_color = new_color | ~old_color; + new_color = new_color | (~old_color & 0x00FFFFFF); break; case GE_LOGIC_COPY_INVERTED: - new_color = ~new_color; + new_color = (~new_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_OR_INVERTED: - new_color = ~new_color | old_color; + new_color = ((~new_color | old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_NAND: - new_color = ~(new_color & old_color); + new_color = (~(new_color & old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_SET: - new_color = 0xFFFFFFFF; + new_color |= 0x00FFFFFF; break; } @@ -623,7 +622,13 @@ static inline Vec4 GetTextureFunctionOutput(const Vec4& prim_color, co // We can be accurate up to 24 bit integers, should be enough. const __m128 p = _mm_cvtepi32_ps(prim_color.ivec); const __m128 t = _mm_cvtepi32_ps(texcolor.ivec); - out_rgb.ivec = _mm_cvtps_epi32(_mm_div_ps(_mm_mul_ps(p, t), _mm_set_ps1(255.0f))); + const __m128 b = _mm_mul_ps(p, t); + if (gstate.isColorDoublingEnabled()) { + // We double right here, only for modulate. Other tex funcs do not color double. + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(2.0f / 255.0f))); + } else { + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(1.0f / 255.0f))); + } if (rgba) { return Vec4(out_rgb.ivec); @@ -631,7 +636,11 @@ static inline Vec4 GetTextureFunctionOutput(const Vec4& prim_color, co out_a = prim_color.a(); } #else - out_rgb = prim_color.rgb() * texcolor.rgb() / 255; + if (gstate.isColorDoublingEnabled()) { + out_rgb = (prim_color.rgb() * texcolor.rgb() * 2) / 255; + } else { + out_rgb = prim_color.rgb() * texcolor.rgb() / 255; + } out_a = (rgba) ? (prim_color.a() * texcolor.a() / 255) : prim_color.a(); #endif break; @@ -895,28 +904,34 @@ static inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4< template inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in) { - Vec4 prim_color = color_in; - // Depth range test - // TODO: Clear mode? + Vec4 prim_color = color_in.Clamp(0, 255); + // Depth range test - applied in clear mode, if not through mode. if (!gstate.isModeThrough()) if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) return; + if (gstate.isAlphaTestEnabled() && !clearMode) + if (!AlphaTestPassed(prim_color.a())) + return; + + // Fog is applied prior to color test. + if (gstate.isFogEnabled() && !gstate.isModeThrough() && !clearMode) { + Vec3 fogColor = Vec3::FromRGB(gstate.fogcolor); + fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; + prim_color.r() = fogColor.r(); + prim_color.g() = fogColor.g(); + prim_color.b() = fogColor.b(); + } + if (gstate.isColorTestEnabled() && !clearMode) if (!ColorTestPassed(prim_color.rgb())) return; - // TODO: Does a need to be clamped? - if (gstate.isAlphaTestEnabled() && !clearMode) - if (!AlphaTestPassed(prim_color.a())) - return; - // In clear mode, it uses the alpha color as stencil. u8 stencil = clearMode ? prim_color.a() : GetPixelStencil(p.x, p.y); - // TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? Probably yes if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) { - stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y); + stencil = ApplyStencilOp(gstate.getStencilOpSFail(), stencil); SetPixelStencil(p.x, p.y, stencil); return; } @@ -924,12 +939,12 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 fogColor = Vec3::FromRGB(gstate.fogcolor); - fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; - prim_color.r() = fogColor.r(); - prim_color.g() = fogColor.g(); - prim_color.b() = fogColor.b(); - } - const u32 old_color = GetPixelColor(p.x, p.y); u32 new_color; if (gstate.isAlphaBlendEnabled() && !clearMode) { const Vec4 dst = Vec4::FromRGBA(old_color); - // ToRGBA() always automatically clamps. + // ToRGB() always automatically clamps. new_color = AlphaBlendingResult(prim_color, dst).ToRGB(); new_color |= stencil << 24; } else { @@ -974,8 +973,8 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 +#include "math/math_util.h" #include "Common/MemoryUtil.h" #include "Core/Config.h" #include "GPU/GPUState.h" @@ -216,8 +218,17 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader) ModelCoords viewpos = TransformUnit::WorldToView(vertex.worldpos); vertex.clippos = ClipCoords(TransformUnit::ViewToClip(viewpos)); if (gstate.isFogEnabled()) { - // TODO: Validate inf/nan. - vertex.fogdepth = (viewpos.z + getFloat24(gstate.fog1)) * getFloat24(gstate.fog2); + float fog_end = getFloat24(gstate.fog1); + float fog_slope = getFloat24(gstate.fog2); + // Same fixup as in ShaderManagerGLES.cpp + if (my_isnanorinf(fog_end)) { + // Not really sure what a sensible value might be, but let's try 64k. + fog_end = std::signbit(fog_end) ? -65535.0f : 65535.0f; + } + if (my_isnanorinf(fog_slope)) { + fog_slope = std::signbit(fog_slope) ? -65535.0f : 65535.0f; + } + vertex.fogdepth = (viewpos.z + fog_end) * fog_slope; } else { vertex.fogdepth = 1.0f; } diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 4abb57df7d..fc134301f1 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -340,6 +340,11 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " vec4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " vec4 v = v_color0 %s;\n", secondary); @@ -375,6 +380,12 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } } + if (enableFog) { + WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); + WRITE(p, " v = mix(vec4(base.fogcolor, v.a), v, fogCoef);\n"); + // WRITE(p, " v.x = v_depth;\n"); + } + if (enableColorTest) { if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. @@ -401,19 +412,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } - if (enableFog) { - WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = mix(vec4(base.fogcolor, v.a), v, fogCoef);\n"); - // WRITE(p, " v.x = v_depth;\n"); - } - if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { const char *srcFactor = "ERROR"; switch (replaceBlendFuncA) { diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 84b5bda269..0734e1d8ca 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -375,6 +375,7 @@ int main(int argc, const char* argv[]) g_Config.iSplineBezierQuality = 2; g_Config.bHighQualityDepth = true; g_Config.bMemStickInserted = true; + g_Config.bFragmentTestCache = true; #ifdef _WIN32 InitSysDirectories();