diff --git a/CMakeLists.txt b/CMakeLists.txt
index b60f2ae449..5617392c7f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1443,6 +1443,7 @@ add_library(GPU OBJECT
GPU/Common/FramebufferCommon.h
GPU/Common/GPUDebugInterface.cpp
GPU/Common/GPUDebugInterface.h
+ GPU/Common/GPUStateUtils.cpp
GPU/Common/GPUStateUtils.h
GPU/Common/DrawEngineCommon.cpp
GPU/Common/DrawEngineCommon.h
diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp
new file mode 100644
index 0000000000..9880fb1ee0
--- /dev/null
+++ b/GPU/Common/GPUStateUtils.cpp
@@ -0,0 +1,365 @@
+#include "Common/StringUtils.h"
+#include "Core/Config.h"
+
+#include "GPU/ge_constants.h"
+#include "GPU/GPUState.h"
+#include "GPU/Common/ShaderId.h"
+#include "GPU/Common/VertexDecoderCommon.h"
+
+#include "GPU/Common/GPUStateUtils.h"
+
+
+bool CanUseHardwareTransform(int prim) {
+ if (!g_Config.bHardwareTransform)
+ return false;
+ return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES;
+}
+
+// Dest factors where it's safe to eliminate the alpha test under certain conditions
+static const bool safeDestFactors[16] = {
+ true, // GE_DSTBLEND_SRCCOLOR,
+ true, // GE_DSTBLEND_INVSRCCOLOR,
+ false, // GE_DSTBLEND_SRCALPHA,
+ true, // GE_DSTBLEND_INVSRCALPHA,
+ true, // GE_DSTBLEND_DSTALPHA,
+ true, // GE_DSTBLEND_INVDSTALPHA,
+ false, // GE_DSTBLEND_DOUBLESRCALPHA,
+ false, // GE_DSTBLEND_DOUBLEINVSRCALPHA,
+ true, // GE_DSTBLEND_DOUBLEDSTALPHA,
+ true, // GE_DSTBLEND_DOUBLEINVDSTALPHA,
+ true, //GE_DSTBLEND_FIXB,
+};
+
+bool IsAlphaTestTriviallyTrue() {
+ switch (gstate.getAlphaTestFunction()) {
+ case GE_COMP_NEVER:
+ return false;
+
+ case GE_COMP_ALWAYS:
+ return true;
+
+ case GE_COMP_GEQUAL:
+ if (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed()))
+ return true; // If alpha is full, it doesn't matter what the ref value is.
+ return gstate.getAlphaTestRef() == 0;
+
+ // Non-zero check. If we have no depth testing (and thus no depth writing), and an alpha func that will result in no change if zero alpha, get rid of the alpha test.
+ // Speeds up Lumines by a LOT on PowerVR.
+ case GE_COMP_NOTEQUAL:
+ if (gstate.getAlphaTestRef() == 255) {
+ // Likely to be rare. Let's just skip the vertexFullAlpha optimization here instead of adding
+ // complicated code to discard the draw or whatnot.
+ return false;
+ }
+ // Fallthrough on purpose
+
+ case GE_COMP_GREATER:
+ {
+#if 0
+ // Easy way to check the values in the debugger without ruining && early-out
+ bool doTextureAlpha = gstate.isTextureAlphaUsed();
+ bool stencilTest = gstate.isStencilTestEnabled();
+ bool depthTest = gstate.isDepthTestEnabled();
+ GEComparison depthTestFunc = gstate.getDepthTestFunction();
+ int alphaRef = gstate.getAlphaTestRef();
+ int blendA = gstate.getBlendFuncA();
+ bool blendEnabled = gstate.isAlphaBlendEnabled();
+ int blendB = gstate.getBlendFuncA();
+#endif
+ return (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed())) || (
+ (!gstate.isStencilTestEnabled() &&
+ !gstate.isDepthTestEnabled() &&
+ gstate.getAlphaTestRef() == 0 &&
+ gstate.isAlphaBlendEnabled() &&
+ gstate.getBlendFuncA() == GE_SRCBLEND_SRCALPHA &&
+ safeDestFactors[(int)gstate.getBlendFuncB()]));
+ }
+
+ case GE_COMP_LEQUAL:
+ return gstate.getAlphaTestRef() == 255;
+
+ case GE_COMP_EQUAL:
+ case GE_COMP_LESS:
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+bool IsAlphaTestAgainstZero() {
+ return gstate.getAlphaTestRef() == 0 && gstate.getAlphaTestMask() == 0xFF;
+}
+
+bool IsColorTestAgainstZero() {
+ return gstate.getColorTestRef() == 0 && gstate.getColorTestMask() == 0xFFFFFF;
+}
+
+bool IsColorTestTriviallyTrue() {
+ switch (gstate.getColorTestFunction()) {
+ case GE_COMP_NEVER:
+ return false;
+
+ case GE_COMP_ALWAYS:
+ return true;
+
+ case GE_COMP_EQUAL:
+ case GE_COMP_NOTEQUAL:
+ return false;
+ default:
+ return false;
+ }
+}
+
+const bool nonAlphaSrcFactors[16] = {
+ true, // GE_SRCBLEND_DSTCOLOR,
+ true, // GE_SRCBLEND_INVDSTCOLOR,
+ false, // GE_SRCBLEND_SRCALPHA,
+ false, // GE_SRCBLEND_INVSRCALPHA,
+ true, // GE_SRCBLEND_DSTALPHA,
+ true, // GE_SRCBLEND_INVDSTALPHA,
+ false, // GE_SRCBLEND_DOUBLESRCALPHA,
+ false, // GE_SRCBLEND_DOUBLEINVSRCALPHA,
+ true, // GE_SRCBLEND_DOUBLEDSTALPHA,
+ true, // GE_SRCBLEND_DOUBLEINVDSTALPHA,
+ true, // GE_SRCBLEND_FIXA,
+};
+
+const bool nonAlphaDestFactors[16] = {
+ true, // GE_DSTBLEND_SRCCOLOR,
+ true, // GE_DSTBLEND_INVSRCCOLOR,
+ false, // GE_DSTBLEND_SRCALPHA,
+ false, // GE_DSTBLEND_INVSRCALPHA,
+ true, // GE_DSTBLEND_DSTALPHA,
+ true, // GE_DSTBLEND_INVDSTALPHA,
+ false, // GE_DSTBLEND_DOUBLESRCALPHA,
+ false, // GE_DSTBLEND_DOUBLEINVSRCALPHA,
+ true, // GE_DSTBLEND_DOUBLEDSTALPHA,
+ true, // GE_DSTBLEND_DOUBLEINVDSTALPHA,
+ true, // GE_DSTBLEND_FIXB,
+};
+
+ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
+ if (!gstate.isStencilTestEnabled() || gstate.isModeClear()) {
+ return REPLACE_ALPHA_NO;
+ }
+
+ if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) {
+ if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
+ return REPLACE_ALPHA_YES;
+ } else {
+ if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) {
+ return REPLACE_ALPHA_DUALSOURCE;
+ } else {
+ return REPLACE_ALPHA_NO;
+ }
+ }
+ }
+
+ return REPLACE_ALPHA_YES;
+}
+
+StencilValueType ReplaceAlphaWithStencilType() {
+ switch (gstate.FrameBufFormat()) {
+ case GE_FORMAT_565:
+ // There's never a stencil value. Maybe the right alpha is 1?
+ return STENCIL_VALUE_ONE;
+
+ case GE_FORMAT_5551:
+ switch (gstate.getStencilOpZPass()) {
+ // Technically, this should only ever use zero/one.
+ case GE_STENCILOP_REPLACE:
+ return (gstate.getStencilTestRef() & 0x80) != 0 ? STENCIL_VALUE_ONE : STENCIL_VALUE_ZERO;
+
+ // Decrementing always zeros, since there's only one bit.
+ case GE_STENCILOP_DECR:
+ case GE_STENCILOP_ZERO:
+ return STENCIL_VALUE_ZERO;
+
+ // Incrementing always fills, since there's only one bit.
+ case GE_STENCILOP_INCR:
+ return STENCIL_VALUE_ONE;
+
+ case GE_STENCILOP_INVERT:
+ return STENCIL_VALUE_INVERT;
+
+ case GE_STENCILOP_KEEP:
+ return STENCIL_VALUE_KEEP;
+ }
+ break;
+
+ case GE_FORMAT_4444:
+ case GE_FORMAT_8888:
+ case GE_FORMAT_INVALID:
+ switch (gstate.getStencilOpZPass()) {
+ case GE_STENCILOP_REPLACE:
+ // TODO: Could detect zero here and force ZERO - less uniform updates?
+ return STENCIL_VALUE_UNIFORM;
+
+ case GE_STENCILOP_ZERO:
+ return STENCIL_VALUE_ZERO;
+
+ case GE_STENCILOP_DECR:
+ return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_DECR_4 : STENCIL_VALUE_DECR_8;
+
+ case GE_STENCILOP_INCR:
+ return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_INCR_4 : STENCIL_VALUE_INCR_8;
+
+ case GE_STENCILOP_INVERT:
+ return STENCIL_VALUE_INVERT;
+
+ case GE_STENCILOP_KEEP:
+ return STENCIL_VALUE_KEEP;
+ }
+ break;
+ }
+
+ return STENCIL_VALUE_KEEP;
+}
+
+ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend) {
+ if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
+ return REPLACE_BLEND_NO;
+ }
+
+ GEBlendMode eq = gstate.getBlendEq();
+ // Let's get the non-factor modes out of the way first.
+ switch (eq) {
+ case GE_BLENDMODE_ABSDIFF:
+ return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
+
+ case GE_BLENDMODE_MIN:
+ case GE_BLENDMODE_MAX:
+ if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) {
+ return REPLACE_BLEND_STANDARD;
+ } else {
+ return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
+ }
+
+ default:
+ break;
+ }
+
+ GEBlendSrcFactor funcA = gstate.getBlendFuncA();
+ GEBlendDstFactor funcB = gstate.getBlendFuncB();
+
+ switch (funcA) {
+ case GE_SRCBLEND_DOUBLESRCALPHA:
+ case GE_SRCBLEND_DOUBLEINVSRCALPHA:
+ // 2x alpha in the source function and not in the dest = source color doubling.
+ // Even dest alpha is safe, since we're moving the * 2.0 into the src color.
+ switch (funcB) {
+ case GE_DSTBLEND_SRCCOLOR:
+ case GE_DSTBLEND_INVSRCCOLOR:
+ // Can't double, we need the source color to be correct.
+ return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+
+ case GE_DSTBLEND_DOUBLEDSTALPHA:
+ case GE_DSTBLEND_DOUBLEINVDSTALPHA:
+ return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+
+ case GE_DSTBLEND_DOUBLESRCALPHA:
+ case GE_DSTBLEND_DOUBLEINVSRCALPHA:
+ // We can't technically do this correctly (due to clamping) without reading the dst color.
+ // Using a copy isn't accurate either, though, when there's overlap.
+ if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
+ return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+ return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
+
+ default:
+ // TODO: Could use vertexFullAlpha, but it's not calculated yet.
+ return REPLACE_BLEND_PRE_SRC;
+ }
+
+ case GE_SRCBLEND_DOUBLEDSTALPHA:
+ case GE_SRCBLEND_DOUBLEINVDSTALPHA:
+ switch (funcB) {
+ case GE_DSTBLEND_SRCCOLOR:
+ case GE_DSTBLEND_INVSRCCOLOR:
+ // Can't double, we need the source color to be correct.
+ return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
+
+ case GE_DSTBLEND_DOUBLEDSTALPHA:
+ case GE_DSTBLEND_DOUBLEINVDSTALPHA:
+ case GE_DSTBLEND_DOUBLESRCALPHA:
+ case GE_DSTBLEND_DOUBLEINVSRCALPHA:
+ return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
+
+ default:
+ // We can't technically do this correctly (due to clamping) without reading the dst alpha.
+ return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
+ }
+
+ case GE_SRCBLEND_FIXA:
+ switch (funcB) {
+ case GE_DSTBLEND_DOUBLESRCALPHA:
+ case GE_DSTBLEND_DOUBLEINVSRCALPHA:
+ // Can't safely double alpha, will clamp.
+ return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+
+ case GE_DSTBLEND_DOUBLEDSTALPHA:
+ case GE_DSTBLEND_DOUBLEINVDSTALPHA:
+ return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
+
+ case GE_DSTBLEND_FIXB:
+ if (gstate.getFixA() == 0xFFFFFF && gstate.getFixB() == 0x000000) {
+ // Some games specify this. Some cards may prefer blending off entirely.
+ return REPLACE_BLEND_NO;
+ } else if (gstate.getFixA() == 0xFFFFFF || gstate.getFixA() == 0x000000 || gstate.getFixB() == 0xFFFFFF || gstate.getFixB() == 0x000000) {
+ return REPLACE_BLEND_STANDARD;
+ } else {
+ return REPLACE_BLEND_PRE_SRC;
+ }
+
+ default:
+ return REPLACE_BLEND_STANDARD;
+ }
+
+ default:
+ switch (funcB) {
+ case GE_DSTBLEND_DOUBLESRCALPHA:
+ case GE_DSTBLEND_DOUBLEINVSRCALPHA:
+ if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
+ // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap.
+ if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
+ return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+ return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
+ } else {
+ // This means dst alpha/color is used in the src factor.
+ // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?)
+ // We will just hope that doubling alpha for the dst factor will not clamp too badly.
+ if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
+ return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
+ return REPLACE_BLEND_2X_ALPHA;
+ }
+
+ case GE_DSTBLEND_DOUBLEDSTALPHA:
+ case GE_DSTBLEND_DOUBLEINVDSTALPHA:
+ return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
+
+ default:
+ return REPLACE_BLEND_STANDARD;
+ }
+ }
+}
+
+LogicOpReplaceType ReplaceLogicOpType() {
+ if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) {
+ switch (gstate.getLogicOp()) {
+ case GE_LOGIC_COPY_INVERTED:
+ case GE_LOGIC_AND_INVERTED:
+ case GE_LOGIC_OR_INVERTED:
+ case GE_LOGIC_NOR:
+ case GE_LOGIC_NAND:
+ case GE_LOGIC_EQUIV:
+ return LOGICOPTYPE_INVERT;
+ case GE_LOGIC_INVERTED:
+ return LOGICOPTYPE_ONE;
+ case GE_LOGIC_SET:
+ return LOGICOPTYPE_ONE;
+ default:
+ return LOGICOPTYPE_NORMAL;
+ }
+ }
+ return LOGICOPTYPE_NORMAL;
+}
diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h
index c3f0406e7f..595c8e1ae1 100644
--- a/GPU/Common/GPUStateUtils.h
+++ b/GPU/Common/GPUStateUtils.h
@@ -1,115 +1,47 @@
-// Copyright (c) 2012- PPSSPP Project.
+#pragma once
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 2.0 or later versions.
-
-// 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/.
-
-#include "GPU/ge_constants.h"
-#include "GPU/GPUState.h"
-
-// Dest factors where it's safe to eliminate the alpha test under certain conditions
-static const bool safeDestFactors[16] = {
- true, // GE_DSTBLEND_SRCCOLOR,
- true, // GE_DSTBLEND_INVSRCCOLOR,
- false, // GE_DSTBLEND_SRCALPHA,
- true, // GE_DSTBLEND_INVSRCALPHA,
- true, // GE_DSTBLEND_DSTALPHA,
- true, // GE_DSTBLEND_INVDSTALPHA,
- false, // GE_DSTBLEND_DOUBLESRCALPHA,
- false, // GE_DSTBLEND_DOUBLEINVSRCALPHA,
- true, // GE_DSTBLEND_DOUBLEDSTALPHA,
- true, // GE_DSTBLEND_DOUBLEINVDSTALPHA,
- true, //GE_DSTBLEND_FIXB,
+enum StencilValueType {
+ STENCIL_VALUE_UNIFORM,
+ STENCIL_VALUE_ZERO,
+ STENCIL_VALUE_ONE,
+ STENCIL_VALUE_KEEP,
+ STENCIL_VALUE_INVERT,
+ STENCIL_VALUE_INCR_4,
+ STENCIL_VALUE_INCR_8,
+ STENCIL_VALUE_DECR_4,
+ STENCIL_VALUE_DECR_8,
};
-static inline bool IsAlphaTestTriviallyTrue() {
- switch (gstate.getAlphaTestFunction()) {
- case GE_COMP_NEVER:
- return false;
+enum ReplaceAlphaType {
+ REPLACE_ALPHA_NO = 0,
+ REPLACE_ALPHA_YES = 1,
+ REPLACE_ALPHA_DUALSOURCE = 2,
+};
- case GE_COMP_ALWAYS:
- return true;
+enum ReplaceBlendType {
+ REPLACE_BLEND_NO,
+ REPLACE_BLEND_STANDARD,
+ REPLACE_BLEND_PRE_SRC,
+ REPLACE_BLEND_PRE_SRC_2X_ALPHA,
+ REPLACE_BLEND_2X_ALPHA,
+ REPLACE_BLEND_2X_SRC,
+ REPLACE_BLEND_COPY_FBO,
+};
- case GE_COMP_GEQUAL:
- if (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed()))
- return true; // If alpha is full, it doesn't matter what the ref value is.
- return gstate.getAlphaTestRef() == 0;
+enum LogicOpReplaceType {
+ LOGICOPTYPE_NORMAL,
+ LOGICOPTYPE_ONE,
+ LOGICOPTYPE_INVERT,
+};
- // Non-zero check. If we have no depth testing (and thus no depth writing), and an alpha func that will result in no change if zero alpha, get rid of the alpha test.
- // Speeds up Lumines by a LOT on PowerVR.
- case GE_COMP_NOTEQUAL:
- if (gstate.getAlphaTestRef() == 255) {
- // Likely to be rare. Let's just skip the vertexFullAlpha optimization here instead of adding
- // complicated code to discard the draw or whatnot.
- return false;
- }
- // Fallthrough on purpose
+bool IsAlphaTestTriviallyTrue();
+bool IsColorTestAgainstZero();
+bool IsColorTestTriviallyTrue();
+bool IsAlphaTestAgainstZero();
- case GE_COMP_GREATER:
- {
-#if 0
- // Easy way to check the values in the debugger without ruining && early-out
- bool doTextureAlpha = gstate.isTextureAlphaUsed();
- bool stencilTest = gstate.isStencilTestEnabled();
- bool depthTest = gstate.isDepthTestEnabled();
- GEComparison depthTestFunc = gstate.getDepthTestFunction();
- int alphaRef = gstate.getAlphaTestRef();
- int blendA = gstate.getBlendFuncA();
- bool blendEnabled = gstate.isAlphaBlendEnabled();
- int blendB = gstate.getBlendFuncA();
-#endif
- return (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed())) || (
- (!gstate.isStencilTestEnabled() &&
- !gstate.isDepthTestEnabled() &&
- gstate.getAlphaTestRef() == 0 &&
- gstate.isAlphaBlendEnabled() &&
- gstate.getBlendFuncA() == GE_SRCBLEND_SRCALPHA &&
- safeDestFactors[(int)gstate.getBlendFuncB()]));
- }
+StencilValueType ReplaceAlphaWithStencilType();
+ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
+ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend);
- case GE_COMP_LEQUAL:
- return gstate.getAlphaTestRef() == 255;
-
- case GE_COMP_EQUAL:
- case GE_COMP_LESS:
- return false;
-
- default:
- return false;
- }
-}
-
-static inline bool IsAlphaTestAgainstZero() {
- return gstate.getAlphaTestRef() == 0 && gstate.getAlphaTestMask() == 0xFF;
-}
-
-static inline bool IsColorTestAgainstZero() {
- return gstate.getColorTestRef() == 0 && gstate.getColorTestMask() == 0xFFFFFF;
-}
-
-static inline bool IsColorTestTriviallyTrue() {
- switch (gstate.getColorTestFunction()) {
- case GE_COMP_NEVER:
- return false;
-
- case GE_COMP_ALWAYS:
- return true;
-
- case GE_COMP_EQUAL:
- case GE_COMP_NOTEQUAL:
- return false;
- default:
- return false;
- }
-}
+bool CanUseHardwareTransform(int prim);
+LogicOpReplaceType ReplaceLogicOpType();
diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp
index bdc059b0f1..dbd5cd5afe 100644
--- a/GPU/Common/ShaderId.cpp
+++ b/GPU/Common/ShaderId.cpp
@@ -6,10 +6,10 @@
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
+#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/VertexDecoderCommon.h"
-
std::string VertexShaderDesc(const ShaderID &id) {
std::stringstream desc;
desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]);
@@ -51,12 +51,6 @@ std::string VertexShaderDesc(const ShaderID &id) {
return desc.str();
}
-bool CanUseHardwareTransform(int prim) {
- if (!g_Config.bHardwareTransform)
- return false;
- return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES;
-}
-
void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) {
bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
@@ -135,3 +129,158 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
*id_out = id;
}
+
+
+static const char *alphaTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
+
+std::string FragmentShaderDesc(const ShaderID &id) {
+ std::stringstream desc;
+ desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]);
+ if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear ";
+ if (id.Bit(FS_BIT_DO_TEXTURE)) desc << "Tex ";
+ if (id.Bit(FS_BIT_DO_TEXTURE_PROJ)) desc << "TexProj ";
+ if (id.Bit(FS_BIT_FLIP_TEXTURE)) desc << "Flip ";
+ if (id.Bit(FS_BIT_TEXALPHA)) desc << "TexAlpha ";
+ if (id.Bit(FS_BIT_TEXTURE_AT_OFFSET)) desc << "TexOffs ";
+ if (id.Bit(FS_BIT_LMODE)) desc << "LM ";
+ if (id.Bit(FS_BIT_ENABLE_FOG)) desc << "Fog ";
+ if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x ";
+ if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat ";
+ if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) {
+ desc << "TClamp";
+ if (id.Bit(FS_BIT_CLAMP_S)) desc << "S";
+ if (id.Bit(FS_BIT_CLAMP_T)) desc << "T";
+ desc << " ";
+ }
+ if (id.Bits(FS_BIT_REPLACE_BLEND, 3)) {
+ desc << "ReplaceBlend_" << id.Bits(FS_BIT_REPLACE_BLEND, 3) << ":" << id.Bits(38, 4) << "_B:" << id.Bits(42, 4) << "_Eq:" << id.Bits(35, 3) << " ";
+ }
+
+ switch (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)) {
+ case REPLACE_ALPHA_NO: break;
+ case REPLACE_ALPHA_YES: desc << "StenToAlpha "; break;
+ case REPLACE_ALPHA_DUALSOURCE: desc << "StenToAlphaDual "; break;
+ }
+ if (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2) != REPLACE_ALPHA_NO) {
+ switch (id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4)) {
+ case STENCIL_VALUE_UNIFORM: desc << "StenUniform "; break;
+ case STENCIL_VALUE_ZERO: desc << "Sten0 "; break;
+ case STENCIL_VALUE_ONE: desc << "Sten1 "; break;
+ case STENCIL_VALUE_KEEP: desc << "StenKeep "; break;
+ case STENCIL_VALUE_INVERT: desc << "StenInv "; break;
+ case STENCIL_VALUE_INCR_4: desc << "StenIncr4 "; break;
+ case STENCIL_VALUE_INCR_8: desc << "StenIncr8 "; break;
+ case STENCIL_VALUE_DECR_4: desc << "StenDecr4 "; break;
+ case STENCIL_VALUE_DECR_8: desc << "StenDecr4 "; break;
+ default: desc << "StenUnknown"; break;
+ }
+ }
+ if (id.Bit(FS_BIT_DO_TEXTURE)) {
+ switch (id.Bits(FS_BIT_TEXFUNC, 3)) {
+ case GE_TEXFUNC_ADD: desc << "TFuncAdd "; break;
+ case GE_TEXFUNC_BLEND: desc << "TFuncBlend "; break;
+ case GE_TEXFUNC_DECAL: desc << "TFuncDecal "; break;
+ case GE_TEXFUNC_MODULATE: desc << "TFuncMod "; break;
+ case GE_TEXFUNC_REPLACE: desc << "TFuncRepl "; break;
+ default: desc << "TFuncUnk "; break;
+ }
+ }
+
+ if (id.Bit(FS_BIT_ALPHA_AGAINST_ZERO)) desc << "AlphaTest0 " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " ";
+ else if (id.Bit(FS_BIT_ALPHA_TEST)) desc << "AlphaTest " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " ";
+ if (id.Bit(FS_BIT_COLOR_AGAINST_ZERO)) desc << "ColorTest0 " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match;
+ else if (id.Bit(FS_BIT_COLOR_TEST)) desc << "ColorTest " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match
+
+ return desc.str();
+}
+
+// Here we must take all the bits of the gstate that determine what the fragment shader will
+// look like, and concatenate them together into an ID.
+void ComputeFragmentShaderID(ShaderID *id_out, uint32_t vertType) {
+ ShaderID id;
+ if (gstate.isModeClear()) {
+ // We only need one clear shader, so let's ignore the rest of the bits.
+ id.SetBit(FS_BIT_CLEARMODE);
+ } else {
+ bool isModeThrough = gstate.isModeThrough();
+ bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough;
+ bool enableFog = gstate.isFogEnabled() && !isModeThrough;
+ bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !g_Config.bDisableAlphaTest;
+ bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue();
+ bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled();
+ bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
+ bool doTextureAlpha = gstate.isTextureAlphaUsed();
+ bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT;
+
+ ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend);
+ ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
+
+ // All texfuncs except replace are the same for RGB as for RGBA with full alpha.
+ if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE)
+ doTextureAlpha = false;
+
+ if (gstate.isTextureMapEnabled()) {
+ id.SetBit(FS_BIT_DO_TEXTURE);
+ id.SetBits(FS_BIT_TEXFUNC, 3, gstate.getTextureFunction());
+ id.SetBit(FS_BIT_TEXALPHA, doTextureAlpha & 1); // rgb or rgba
+ id.SetBit(FS_BIT_FLIP_TEXTURE, gstate_c.flipTexture);
+ if (gstate_c.needShaderTexClamp) {
+ bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0;
+ // 4 bits total.
+ id.SetBit(FS_BIT_SHADER_TEX_CLAMP);
+ id.SetBit(FS_BIT_CLAMP_S, gstate.isTexCoordClampedS());
+ id.SetBit(FS_BIT_CLAMP_T, gstate.isTexCoordClampedT());
+ id.SetBit(FS_BIT_TEXTURE_AT_OFFSET, textureAtOffset);
+ }
+ }
+
+ id.SetBit(FS_BIT_LMODE, lmode);
+#if !defined(DX9_USE_HW_ALPHA_TEST)
+ if (enableAlphaTest) {
+ // 5 bits total.
+ id.SetBit(FS_BIT_ALPHA_TEST);
+ id.SetBits(FS_BIT_ALPHA_TEST_FUNC, 3, gstate.getAlphaTestFunction());
+ id.SetBit(FS_BIT_ALPHA_AGAINST_ZERO, IsAlphaTestAgainstZero());
+ }
+#endif
+ if (enableColorTest) {
+ // 4 bits total.
+ id.SetBit(FS_BIT_COLOR_TEST);
+ id.SetBits(FS_BIT_COLOR_TEST_FUNC, 2, gstate.getColorTestFunction());
+ id.SetBit(FS_BIT_COLOR_AGAINST_ZERO, IsColorTestAgainstZero());
+ }
+
+ id.SetBit(FS_BIT_ENABLE_FOG, enableFog);
+ id.SetBit(FS_BIT_DO_TEXTURE_PROJ, doTextureProjection);
+ id.SetBit(FS_BIT_COLOR_DOUBLE, enableColorDoubling);
+
+ // 2 bits
+ id.SetBits(FS_BIT_STENCIL_TO_ALPHA, 2, stencilToAlpha);
+
+ if (stencilToAlpha != REPLACE_ALPHA_NO) {
+ // 4 bits
+ id.SetBits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4, ReplaceAlphaWithStencilType());
+ }
+
+ if (enableAlphaTest)
+ gpuStats.numAlphaTestedDraws++;
+ else
+ gpuStats.numNonAlphaTestedDraws++;
+
+ // 2 bits.
+ id.SetBits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2, ReplaceLogicOpType());
+
+ // If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits.
+ if (replaceBlend > REPLACE_BLEND_STANDARD) {
+ // 3 bits.
+ id.SetBits(FS_BIT_REPLACE_BLEND, 3, replaceBlend);
+ // 11 bits total.
+ id.SetBits(FS_BIT_BLENDEQ, 3, gstate.getBlendEq());
+ id.SetBits(FS_BIT_BLENDFUNC_A, 4, gstate.getBlendFuncA());
+ id.SetBits(FS_BIT_BLENDFUNC_B, 4, gstate.getBlendFuncB());
+ }
+ id.SetBit(FS_BIT_FLATSHADE, doFlatShading);
+ }
+
+ *id_out = id;
+}
diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h
index 929804b751..7c28175eda 100644
--- a/GPU/Common/ShaderId.h
+++ b/GPU/Common/ShaderId.h
@@ -134,4 +134,14 @@ struct ShaderID {
void FromString(std::string src) {
memcpy(d, &(src)[0], sizeof(d));
}
-};
\ No newline at end of file
+};
+
+
+bool CanUseHardwareTransform(int prim);
+void ComputeVertexShaderID(ShaderID *id, u32 vertexType, bool useHWTransform);
+// Generates a compact string that describes the shader. Useful in a list to get an overview
+// of the current flora of shaders.
+std::string VertexShaderDesc(const ShaderID &id);
+
+void ComputeFragmentShaderID(ShaderID *id, uint32_t vertType);
+std::string FragmentShaderDesc(const ShaderID &id);
diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp
index 3056b8eee5..06d5d14126 100644
--- a/GPU/Common/TextureCacheCommon.cpp
+++ b/GPU/Common/TextureCacheCommon.cpp
@@ -17,8 +17,9 @@
#include "Core/Config.h"
#include "GPU/Common/FramebufferCommon.h"
-#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Common/TextureCacheCommon.h"
+#include "GPU/Common/ShaderId.h"
+#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GPUState.h"
// Ugly.
diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp
index b2cd3bf7bb..c93859d594 100644
--- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp
+++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp
@@ -19,11 +19,11 @@
#include "Core/Reporting.h"
#include "Core/Config.h"
-#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Directx9/helper/global.h"
#include "GPU/Directx9/PixelShaderGeneratorDX9.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
+#include "GPU/Common/GPUStateUtils.h"
#define WRITE p+=sprintf
@@ -31,287 +31,6 @@
namespace DX9 {
-
-const bool nonAlphaSrcFactors[16] = {
- true, // GE_SRCBLEND_DSTCOLOR,
- true, // GE_SRCBLEND_INVDSTCOLOR,
- false, // GE_SRCBLEND_SRCALPHA,
- false, // GE_SRCBLEND_INVSRCALPHA,
- true, // GE_SRCBLEND_DSTALPHA,
- true, // GE_SRCBLEND_INVDSTALPHA,
- false, // GE_SRCBLEND_DOUBLESRCALPHA,
- false, // GE_SRCBLEND_DOUBLEINVSRCALPHA,
- true, // GE_SRCBLEND_DOUBLEDSTALPHA,
- true, // GE_SRCBLEND_DOUBLEINVDSTALPHA,
- true, // GE_SRCBLEND_FIXA,
-};
-
-const bool nonAlphaDestFactors[16] = {
- true, // GE_DSTBLEND_SRCCOLOR,
- true, // GE_DSTBLEND_INVSRCCOLOR,
- false, // GE_DSTBLEND_SRCALPHA,
- false, // GE_DSTBLEND_INVSRCALPHA,
- true, // GE_DSTBLEND_DSTALPHA,
- true, // GE_DSTBLEND_INVDSTALPHA,
- false, // GE_DSTBLEND_DOUBLESRCALPHA,
- false, // GE_DSTBLEND_DOUBLEINVSRCALPHA,
- true, // GE_DSTBLEND_DOUBLEDSTALPHA,
- true, // GE_DSTBLEND_DOUBLEINVDSTALPHA,
- true, // GE_DSTBLEND_FIXB,
-};
-
-ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
- if (!gstate.isStencilTestEnabled() || gstate.isModeClear()) {
- return REPLACE_ALPHA_NO;
- }
-
- if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) {
- if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
- return REPLACE_ALPHA_YES;
- } else {
- // TODO
-#if 0
- if (pD3DdeviceEx) {
- return REPLACE_ALPHA_DUALSOURCE;
- } else {
-#else
- {
-#endif
- return REPLACE_ALPHA_NO;
- }
- }
- }
-
- return REPLACE_ALPHA_YES;
-}
-
-StencilValueType ReplaceAlphaWithStencilType() {
- switch (gstate.FrameBufFormat()) {
- case GE_FORMAT_565:
- // There's never a stencil value. Maybe the right alpha is 1?
- return STENCIL_VALUE_ONE;
-
- case GE_FORMAT_5551:
- switch (gstate.getStencilOpZPass()) {
- // Technically, this should only ever use zero/one.
- case GE_STENCILOP_REPLACE:
- return (gstate.getStencilTestRef() & 0x80) != 0 ? STENCIL_VALUE_ONE : STENCIL_VALUE_ZERO;
-
- // Decrementing always zeros, since there's only one bit.
- case GE_STENCILOP_DECR:
- case GE_STENCILOP_ZERO:
- return STENCIL_VALUE_ZERO;
-
- // Incrementing always fills, since there's only one bit.
- case GE_STENCILOP_INCR:
- return STENCIL_VALUE_ONE;
-
- case GE_STENCILOP_INVERT:
- return STENCIL_VALUE_INVERT;
-
- case GE_STENCILOP_KEEP:
- return STENCIL_VALUE_KEEP;
- }
- break;
-
- case GE_FORMAT_4444:
- case GE_FORMAT_8888:
- case GE_FORMAT_INVALID:
- switch (gstate.getStencilOpZPass()) {
- case GE_STENCILOP_REPLACE:
- return STENCIL_VALUE_UNIFORM;
-
- case GE_STENCILOP_ZERO:
- return STENCIL_VALUE_ZERO;
-
- case GE_STENCILOP_DECR:
- return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_DECR_4 : STENCIL_VALUE_DECR_8;
-
- case GE_STENCILOP_INCR:
- return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_INCR_4 : STENCIL_VALUE_INCR_8;
-
- case GE_STENCILOP_INVERT:
- return STENCIL_VALUE_INVERT;
-
- case GE_STENCILOP_KEEP:
- return STENCIL_VALUE_KEEP;
- }
- break;
- }
-
- return STENCIL_VALUE_KEEP;
-}
-
-ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend) {
- if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
- return REPLACE_BLEND_NO;
- }
-
- GEBlendSrcFactor funcA = gstate.getBlendFuncA();
- GEBlendDstFactor funcB = gstate.getBlendFuncB();
- GEBlendMode eq = gstate.getBlendEq();
-
- // Let's get the non-factor modes out of the way first.
- switch (eq) {
- case GE_BLENDMODE_ABSDIFF:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_BLENDMODE_MIN:
- case GE_BLENDMODE_MAX:
- return REPLACE_BLEND_STANDARD;
-
- default:
- break;
- }
-
- switch (funcA) {
- case GE_SRCBLEND_DOUBLESRCALPHA:
- case GE_SRCBLEND_DOUBLEINVSRCALPHA:
- // 2x alpha in the source function and not in the dest = source color doubling.
- // Even dest alpha is safe, since we're moving the * 2.0 into the src color.
- switch (funcB) {
- case GE_DSTBLEND_SRCCOLOR:
- case GE_DSTBLEND_INVSRCCOLOR:
- // Can't double, we need the source color to be correct.
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- // We can't technically do this correctly (due to clamping) without reading the dst color.
- // Using a copy isn't accurate either, though, when there's overlap.
- return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
-
- default:
- // TODO: Could use vertexFullAlpha, but it's not calculated yet.
- return REPLACE_BLEND_PRE_SRC;
- }
-
- case GE_SRCBLEND_DOUBLEDSTALPHA:
- case GE_SRCBLEND_DOUBLEINVDSTALPHA:
- switch (funcB) {
- case GE_DSTBLEND_SRCCOLOR:
- case GE_DSTBLEND_INVSRCCOLOR:
- // Can't double, we need the source color to be correct.
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
-
- default:
- // We can't technically do this correctly (due to clamping) without reading the dst alpha.
- return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
- }
-
- case GE_SRCBLEND_FIXA:
- switch (funcB) {
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- // Can't safely double alpha, will clamp.
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_FIXB:
- if (gstate.getFixA() == 0xFFFFFF && gstate.getFixB() == 0x000000) {
- // Some games specify this. Some cards may prefer blending off entirely.
- return REPLACE_BLEND_NO;
- } else if (gstate.getFixA() == 0xFFFFFF || gstate.getFixA() == 0x000000 || gstate.getFixB() == 0xFFFFFF || gstate.getFixB() == 0x000000) {
- return REPLACE_BLEND_STANDARD;
- } else {
- return REPLACE_BLEND_PRE_SRC;
- }
-
- default:
- return REPLACE_BLEND_STANDARD;
- }
-
- default:
- switch (funcB) {
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
- // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap.
- return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
- } else {
- // This means dst alpha/color is used in the src factor.
- // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?)
- // We will just hope that doubling alpha for the dst factor will not clamp too badly.
- return REPLACE_BLEND_2X_ALPHA;
- }
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- default:
- return REPLACE_BLEND_STANDARD;
- }
- }
-}
-
-static bool CanDoubleSrcBlendMode() {
- if (!gstate.isAlphaBlendEnabled()) {
- return false;
- }
-
- int funcA = gstate.getBlendFuncA();
- int funcB = gstate.getBlendFuncB();
- if (funcA != GE_SRCBLEND_DOUBLESRCALPHA) {
- funcB = funcA;
- funcA = gstate.getBlendFuncB();
- }
- if (funcA != GE_SRCBLEND_DOUBLESRCALPHA) {
- return false;
- }
-
- // One side should be doubled. Let's check the other side.
- // LittleBigPlanet, for example, uses 2.0 * src, 1.0 - src, which can't double.
- switch (funcB) {
- case GE_DSTBLEND_SRCALPHA:
- case GE_DSTBLEND_INVSRCALPHA:
- return false;
-
- default:
- return true;
- }
-}
-
-enum LogicOpReplaceType {
- LOGICOPTYPE_NORMAL,
- LOGICOPTYPE_ONE,
- LOGICOPTYPE_INVERT,
-};
-
-static inline LogicOpReplaceType ReplaceLogicOpType() {
- if (gstate.isLogicOpEnabled()) {
- switch (gstate.getLogicOp()) {
- case GE_LOGIC_COPY_INVERTED:
- case GE_LOGIC_AND_INVERTED:
- case GE_LOGIC_OR_INVERTED:
- case GE_LOGIC_NOR:
- case GE_LOGIC_NAND:
- case GE_LOGIC_EQUIV:
- return LOGICOPTYPE_INVERT;
- case GE_LOGIC_INVERTED:
- return LOGICOPTYPE_ONE;
- case GE_LOGIC_SET:
- return LOGICOPTYPE_ONE;
- default:
- return LOGICOPTYPE_NORMAL;
- }
- }
- return LOGICOPTYPE_NORMAL;
-}
-
// Here we must take all the bits of the gstate that determine what the fragment shader will
// look like, and concatenate them together into an ID.
void ComputeFragmentShaderIDDX9(ShaderID *id) {
diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.h b/GPU/Directx9/PixelShaderGeneratorDX9.h
index 23fe0b8794..ac633dce3d 100644
--- a/GPU/Directx9/PixelShaderGeneratorDX9.h
+++ b/GPU/Directx9/PixelShaderGeneratorDX9.h
@@ -29,38 +29,6 @@ namespace DX9 {
void ComputeFragmentShaderIDDX9(ShaderID *id);
void GenerateFragmentShaderDX9(char *buffer);
-enum StencilValueType {
- STENCIL_VALUE_UNIFORM,
- STENCIL_VALUE_ZERO,
- STENCIL_VALUE_ONE,
- STENCIL_VALUE_KEEP,
- STENCIL_VALUE_INVERT,
- STENCIL_VALUE_INCR_4,
- STENCIL_VALUE_INCR_8,
- STENCIL_VALUE_DECR_4,
- STENCIL_VALUE_DECR_8,
-};
-
-enum ReplaceAlphaType {
- REPLACE_ALPHA_NO = 0,
- REPLACE_ALPHA_YES = 1,
- REPLACE_ALPHA_DUALSOURCE = 2,
-};
-
-enum ReplaceBlendType {
- REPLACE_BLEND_NO,
- REPLACE_BLEND_STANDARD,
- REPLACE_BLEND_PRE_SRC,
- REPLACE_BLEND_PRE_SRC_2X_ALPHA,
- REPLACE_BLEND_2X_ALPHA,
- REPLACE_BLEND_2X_SRC,
- REPLACE_BLEND_COPY_FBO,
-};
-
-StencilValueType ReplaceAlphaWithStencilType();
-ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
-ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend);
-
#define CONST_PS_TEXENV 0
#define CONST_PS_ALPHACOLORREF 1
#define CONST_PS_ALPHACOLORMASK 2
diff --git a/GPU/Directx9/TransformPipelineDX9.h b/GPU/Directx9/TransformPipelineDX9.h
index cec618529e..dac30f7dcc 100644
--- a/GPU/Directx9/TransformPipelineDX9.h
+++ b/GPU/Directx9/TransformPipelineDX9.h
@@ -26,6 +26,7 @@
#include "GPU/Common/IndexGenerator.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
+#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Directx9/PixelShaderGeneratorDX9.h"
struct DecVtxFormat;
diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp
index 8b5a600f51..f592f84348 100644
--- a/GPU/GLES/FragmentShaderGenerator.cpp
+++ b/GPU/GLES/FragmentShaderGenerator.cpp
@@ -43,419 +43,6 @@
// #define DEBUG_SHADER
-const bool nonAlphaSrcFactors[16] = {
- true, // GE_SRCBLEND_DSTCOLOR,
- true, // GE_SRCBLEND_INVDSTCOLOR,
- false, // GE_SRCBLEND_SRCALPHA,
- false, // GE_SRCBLEND_INVSRCALPHA,
- true, // GE_SRCBLEND_DSTALPHA,
- true, // GE_SRCBLEND_INVDSTALPHA,
- false, // GE_SRCBLEND_DOUBLESRCALPHA,
- false, // GE_SRCBLEND_DOUBLEINVSRCALPHA,
- true, // GE_SRCBLEND_DOUBLEDSTALPHA,
- true, // GE_SRCBLEND_DOUBLEINVDSTALPHA,
- true, // GE_SRCBLEND_FIXA,
-};
-
-const bool nonAlphaDestFactors[16] = {
- true, // GE_DSTBLEND_SRCCOLOR,
- true, // GE_DSTBLEND_INVSRCCOLOR,
- false, // GE_DSTBLEND_SRCALPHA,
- false, // GE_DSTBLEND_INVSRCALPHA,
- true, // GE_DSTBLEND_DSTALPHA,
- true, // GE_DSTBLEND_INVDSTALPHA,
- false, // GE_DSTBLEND_DOUBLESRCALPHA,
- false, // GE_DSTBLEND_DOUBLEINVSRCALPHA,
- true, // GE_DSTBLEND_DOUBLEDSTALPHA,
- true, // GE_DSTBLEND_DOUBLEINVDSTALPHA,
- true, // GE_DSTBLEND_FIXB,
-};
-
-ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
- if (!gstate.isStencilTestEnabled() || gstate.isModeClear()) {
- return REPLACE_ALPHA_NO;
- }
-
- if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) {
- if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
- return REPLACE_ALPHA_YES;
- } else {
- if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) {
- return REPLACE_ALPHA_DUALSOURCE;
- } else {
- return REPLACE_ALPHA_NO;
- }
- }
- }
-
- return REPLACE_ALPHA_YES;
-}
-
-StencilValueType ReplaceAlphaWithStencilType() {
- switch (gstate.FrameBufFormat()) {
- case GE_FORMAT_565:
- // There's never a stencil value. Maybe the right alpha is 1?
- return STENCIL_VALUE_ONE;
-
- case GE_FORMAT_5551:
- switch (gstate.getStencilOpZPass()) {
- // Technically, this should only ever use zero/one.
- case GE_STENCILOP_REPLACE:
- return (gstate.getStencilTestRef() & 0x80) != 0 ? STENCIL_VALUE_ONE : STENCIL_VALUE_ZERO;
-
- // Decrementing always zeros, since there's only one bit.
- case GE_STENCILOP_DECR:
- case GE_STENCILOP_ZERO:
- return STENCIL_VALUE_ZERO;
-
- // Incrementing always fills, since there's only one bit.
- case GE_STENCILOP_INCR:
- return STENCIL_VALUE_ONE;
-
- case GE_STENCILOP_INVERT:
- return STENCIL_VALUE_INVERT;
-
- case GE_STENCILOP_KEEP:
- return STENCIL_VALUE_KEEP;
- }
- break;
-
- case GE_FORMAT_4444:
- case GE_FORMAT_8888:
- case GE_FORMAT_INVALID:
- switch (gstate.getStencilOpZPass()) {
- case GE_STENCILOP_REPLACE:
- // TODO: Could detect zero here and force ZERO - less uniform updates?
- return STENCIL_VALUE_UNIFORM;
-
- case GE_STENCILOP_ZERO:
- return STENCIL_VALUE_ZERO;
-
- case GE_STENCILOP_DECR:
- return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_DECR_4 : STENCIL_VALUE_DECR_8;
-
- case GE_STENCILOP_INCR:
- return gstate.FrameBufFormat() == GE_FORMAT_4444 ? STENCIL_VALUE_INCR_4 : STENCIL_VALUE_INCR_8;
-
- case GE_STENCILOP_INVERT:
- return STENCIL_VALUE_INVERT;
-
- case GE_STENCILOP_KEEP:
- return STENCIL_VALUE_KEEP;
- }
- break;
- }
-
- return STENCIL_VALUE_KEEP;
-}
-
-ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend) {
- if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
- return REPLACE_BLEND_NO;
- }
-
- GEBlendMode eq = gstate.getBlendEq();
- // Let's get the non-factor modes out of the way first.
- switch (eq) {
- case GE_BLENDMODE_ABSDIFF:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_BLENDMODE_MIN:
- case GE_BLENDMODE_MAX:
- if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) {
- return REPLACE_BLEND_STANDARD;
- } else {
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
- }
-
- default:
- break;
- }
-
- GEBlendSrcFactor funcA = gstate.getBlendFuncA();
- GEBlendDstFactor funcB = gstate.getBlendFuncB();
-
- switch (funcA) {
- case GE_SRCBLEND_DOUBLESRCALPHA:
- case GE_SRCBLEND_DOUBLEINVSRCALPHA:
- // 2x alpha in the source function and not in the dest = source color doubling.
- // Even dest alpha is safe, since we're moving the * 2.0 into the src color.
- switch (funcB) {
- case GE_DSTBLEND_SRCCOLOR:
- case GE_DSTBLEND_INVSRCCOLOR:
- // Can't double, we need the source color to be correct.
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- // We can't technically do this correctly (due to clamping) without reading the dst color.
- // Using a copy isn't accurate either, though, when there's overlap.
- if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
- return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
- return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
-
- default:
- // TODO: Could use vertexFullAlpha, but it's not calculated yet.
- return REPLACE_BLEND_PRE_SRC;
- }
-
- case GE_SRCBLEND_DOUBLEDSTALPHA:
- case GE_SRCBLEND_DOUBLEINVDSTALPHA:
- switch (funcB) {
- case GE_DSTBLEND_SRCCOLOR:
- case GE_DSTBLEND_INVSRCCOLOR:
- // Can't double, we need the source color to be correct.
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
-
- default:
- // We can't technically do this correctly (due to clamping) without reading the dst alpha.
- return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
- }
-
- case GE_SRCBLEND_FIXA:
- switch (funcB) {
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- // Can't safely double alpha, will clamp.
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- case GE_DSTBLEND_FIXB:
- if (gstate.getFixA() == 0xFFFFFF && gstate.getFixB() == 0x000000) {
- // Some games specify this. Some cards may prefer blending off entirely.
- return REPLACE_BLEND_NO;
- } else if (gstate.getFixA() == 0xFFFFFF || gstate.getFixA() == 0x000000 || gstate.getFixB() == 0xFFFFFF || gstate.getFixB() == 0x000000) {
- return REPLACE_BLEND_STANDARD;
- } else {
- return REPLACE_BLEND_PRE_SRC;
- }
-
- default:
- return REPLACE_BLEND_STANDARD;
- }
-
- default:
- switch (funcB) {
- case GE_DSTBLEND_DOUBLESRCALPHA:
- case GE_DSTBLEND_DOUBLEINVSRCALPHA:
- if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
- // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap.
- if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
- return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
- return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
- } else {
- // This means dst alpha/color is used in the src factor.
- // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?)
- // We will just hope that doubling alpha for the dst factor will not clamp too badly.
- if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)
- return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
- return REPLACE_BLEND_2X_ALPHA;
- }
-
- case GE_DSTBLEND_DOUBLEDSTALPHA:
- case GE_DSTBLEND_DOUBLEINVDSTALPHA:
- return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
-
- default:
- return REPLACE_BLEND_STANDARD;
- }
- }
-}
-
-enum LogicOpReplaceType {
- LOGICOPTYPE_NORMAL,
- LOGICOPTYPE_ONE,
- LOGICOPTYPE_INVERT,
-};
-
-static inline LogicOpReplaceType ReplaceLogicOpType() {
- if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) {
- switch (gstate.getLogicOp()) {
- case GE_LOGIC_COPY_INVERTED:
- case GE_LOGIC_AND_INVERTED:
- case GE_LOGIC_OR_INVERTED:
- case GE_LOGIC_NOR:
- case GE_LOGIC_NAND:
- case GE_LOGIC_EQUIV:
- return LOGICOPTYPE_INVERT;
- case GE_LOGIC_INVERTED:
- return LOGICOPTYPE_ONE;
- case GE_LOGIC_SET:
- return LOGICOPTYPE_ONE;
- default:
- return LOGICOPTYPE_NORMAL;
- }
- }
- return LOGICOPTYPE_NORMAL;
-}
-
-static const char *alphaTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
-
-std::string FragmentShaderDesc(const ShaderID &id) {
- std::stringstream desc;
- desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]);
- if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear ";
- if (id.Bit(FS_BIT_DO_TEXTURE)) desc << "Tex ";
- if (id.Bit(FS_BIT_DO_TEXTURE_PROJ)) desc << "TexProj ";
- if (id.Bit(FS_BIT_FLIP_TEXTURE)) desc << "Flip ";
- if (id.Bit(FS_BIT_TEXALPHA)) desc << "TexAlpha ";
- if (id.Bit(FS_BIT_TEXTURE_AT_OFFSET)) desc << "TexOffs ";
- if (id.Bit(FS_BIT_LMODE)) desc << "LM ";
- if (id.Bit(FS_BIT_ENABLE_FOG)) desc << "Fog ";
- if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x ";
- if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat ";
- if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) {
- desc << "TClamp";
- if (id.Bit(FS_BIT_CLAMP_S)) desc << "S";
- if (id.Bit(FS_BIT_CLAMP_T)) desc << "T";
- desc << " ";
- }
- if (id.Bits(FS_BIT_REPLACE_BLEND, 3)) {
- desc << "ReplaceBlend_" << id.Bits(FS_BIT_REPLACE_BLEND, 3) << ":" << id.Bits(38, 4) << "_B:" << id.Bits(42, 4) << "_Eq:" << id.Bits(35, 3) << " ";
- }
-
- switch (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)) {
- case REPLACE_ALPHA_NO: break;
- case REPLACE_ALPHA_YES: desc << "StenToAlpha "; break;
- case REPLACE_ALPHA_DUALSOURCE: desc << "StenToAlphaDual "; break;
- }
- if (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2) != REPLACE_ALPHA_NO) {
- switch (id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4)) {
- case STENCIL_VALUE_UNIFORM: desc << "StenUniform "; break;
- case STENCIL_VALUE_ZERO: desc << "Sten0 "; break;
- case STENCIL_VALUE_ONE: desc << "Sten1 "; break;
- case STENCIL_VALUE_KEEP: desc << "StenKeep "; break;
- case STENCIL_VALUE_INVERT: desc << "StenInv "; break;
- case STENCIL_VALUE_INCR_4: desc << "StenIncr4 "; break;
- case STENCIL_VALUE_INCR_8: desc << "StenIncr8 "; break;
- case STENCIL_VALUE_DECR_4: desc << "StenDecr4 "; break;
- case STENCIL_VALUE_DECR_8: desc << "StenDecr4 "; break;
- default: desc << "StenUnknown"; break;
- }
- }
- if (id.Bit(FS_BIT_DO_TEXTURE)) {
- switch (id.Bits(FS_BIT_TEXFUNC, 3)) {
- case GE_TEXFUNC_ADD: desc << "TFuncAdd "; break;
- case GE_TEXFUNC_BLEND: desc << "TFuncBlend "; break;
- case GE_TEXFUNC_DECAL: desc << "TFuncDecal "; break;
- case GE_TEXFUNC_MODULATE: desc << "TFuncMod "; break;
- case GE_TEXFUNC_REPLACE: desc << "TFuncRepl "; break;
- default: desc << "TFuncUnk "; break;
- }
- }
-
- if (id.Bit(FS_BIT_ALPHA_AGAINST_ZERO)) desc << "AlphaTest0 " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " ";
- else if (id.Bit(FS_BIT_ALPHA_TEST)) desc << "AlphaTest " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " ";
- if (id.Bit(FS_BIT_COLOR_AGAINST_ZERO)) desc << "ColorTest0 " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match;
- else if (id.Bit(FS_BIT_COLOR_TEST)) desc << "ColorTest " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match
-
- return desc.str();
-}
-
-// Here we must take all the bits of the gstate that determine what the fragment shader will
-// look like, and concatenate them together into an ID.
-void ComputeFragmentShaderID(ShaderID *id_out, uint32_t vertType) {
- ShaderID id;
- if (gstate.isModeClear()) {
- // We only need one clear shader, so let's ignore the rest of the bits.
- id.SetBit(FS_BIT_CLEARMODE);
- } else {
- bool isModeThrough = gstate.isModeThrough();
- bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough;
- bool enableFog = gstate.isFogEnabled() && !isModeThrough;
- bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !g_Config.bDisableAlphaTest;
- bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue();
- bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled();
- bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
- bool doTextureAlpha = gstate.isTextureAlphaUsed();
- bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT;
-
- ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend);
- ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
-
- // All texfuncs except replace are the same for RGB as for RGBA with full alpha.
- if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE)
- doTextureAlpha = false;
-
- if (gstate.isTextureMapEnabled()) {
- id.SetBit(FS_BIT_DO_TEXTURE);
- id.SetBits(FS_BIT_TEXFUNC, 3, gstate.getTextureFunction());
- id.SetBit(FS_BIT_TEXALPHA, doTextureAlpha & 1); // rgb or rgba
- id.SetBit(FS_BIT_FLIP_TEXTURE, gstate_c.flipTexture);
- if (gstate_c.needShaderTexClamp) {
- bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0;
- // 4 bits total.
- id.SetBit(FS_BIT_SHADER_TEX_CLAMP);
- id.SetBit(FS_BIT_CLAMP_S, gstate.isTexCoordClampedS());
- id.SetBit(FS_BIT_CLAMP_T, gstate.isTexCoordClampedT());
- id.SetBit(FS_BIT_TEXTURE_AT_OFFSET, textureAtOffset);
- }
- }
-
- id.SetBit(FS_BIT_LMODE, lmode);
-#if !defined(DX9_USE_HW_ALPHA_TEST)
- if (enableAlphaTest) {
- // 5 bits total.
- id.SetBit(FS_BIT_ALPHA_TEST);
- id.SetBits(FS_BIT_ALPHA_TEST_FUNC, 3, gstate.getAlphaTestFunction());
- id.SetBit(FS_BIT_ALPHA_AGAINST_ZERO, IsAlphaTestAgainstZero());
- }
-#endif
- if (enableColorTest) {
- // 4 bits total.
- id.SetBit(FS_BIT_COLOR_TEST);
- id.SetBits(FS_BIT_COLOR_TEST_FUNC, 2, gstate.getColorTestFunction());
- id.SetBit(FS_BIT_COLOR_AGAINST_ZERO, IsColorTestAgainstZero());
- }
-
- id.SetBit(FS_BIT_ENABLE_FOG, enableFog);
- id.SetBit(FS_BIT_DO_TEXTURE_PROJ, doTextureProjection);
- id.SetBit(FS_BIT_COLOR_DOUBLE, enableColorDoubling);
-
- // 2 bits
- id.SetBits(FS_BIT_STENCIL_TO_ALPHA, 2, stencilToAlpha);
-
- if (stencilToAlpha != REPLACE_ALPHA_NO) {
- // 4 bits
- id.SetBits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4, ReplaceAlphaWithStencilType());
- }
-
- if (enableAlphaTest)
- gpuStats.numAlphaTestedDraws++;
- else
- gpuStats.numNonAlphaTestedDraws++;
-
- // 2 bits.
- id.SetBits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2, ReplaceLogicOpType());
-
- // If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits.
- if (replaceBlend > REPLACE_BLEND_STANDARD) {
- // 3 bits.
- id.SetBits(FS_BIT_REPLACE_BLEND, 3, replaceBlend);
- // 11 bits total.
- id.SetBits(FS_BIT_BLENDEQ, 3, gstate.getBlendEq());
- id.SetBits(FS_BIT_BLENDFUNC_A, 4, gstate.getBlendFuncA());
- id.SetBits(FS_BIT_BLENDFUNC_B, 4, gstate.getBlendFuncB());
- }
- id.SetBit(FS_BIT_FLATSHADE, doFlatShading);
- }
-
- *id_out = id;
-}
-
// Missing: Z depth range
bool GenerateFragmentShader(const ShaderID &id, char *buffer) {
char *p = buffer;
diff --git a/GPU/GLES/FragmentShaderGenerator.h b/GPU/GLES/FragmentShaderGenerator.h
index c4fab7dc9b..534a3c8cb3 100644
--- a/GPU/GLES/FragmentShaderGenerator.h
+++ b/GPU/GLES/FragmentShaderGenerator.h
@@ -21,38 +21,4 @@
struct ShaderID;
-void ComputeFragmentShaderID(ShaderID *id, uint32_t vertType);
bool GenerateFragmentShader(const ShaderID &id, char *buffer);
-std::string FragmentShaderDesc(const ShaderID &id);
-
-enum StencilValueType {
- STENCIL_VALUE_UNIFORM,
- STENCIL_VALUE_ZERO,
- STENCIL_VALUE_ONE,
- STENCIL_VALUE_KEEP,
- STENCIL_VALUE_INVERT,
- STENCIL_VALUE_INCR_4,
- STENCIL_VALUE_INCR_8,
- STENCIL_VALUE_DECR_4,
- STENCIL_VALUE_DECR_8,
-};
-
-enum ReplaceAlphaType {
- REPLACE_ALPHA_NO = 0,
- REPLACE_ALPHA_YES = 1,
- REPLACE_ALPHA_DUALSOURCE = 2,
-};
-
-enum ReplaceBlendType {
- REPLACE_BLEND_NO,
- REPLACE_BLEND_STANDARD,
- REPLACE_BLEND_PRE_SRC,
- REPLACE_BLEND_PRE_SRC_2X_ALPHA,
- REPLACE_BLEND_2X_ALPHA,
- REPLACE_BLEND_2X_SRC,
- REPLACE_BLEND_COPY_FBO,
-};
-
-StencilValueType ReplaceAlphaWithStencilType();
-ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
-ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend);
diff --git a/GPU/GLES/FragmentTestCache.cpp b/GPU/GLES/FragmentTestCache.cpp
index b6357b02ea..10d04789fe 100644
--- a/GPU/GLES/FragmentTestCache.cpp
+++ b/GPU/GLES/FragmentTestCache.cpp
@@ -16,9 +16,10 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Core/Config.h"
-#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GLES/FragmentTestCache.h"
#include "GPU/GPUState.h"
+#include "GPU/Common/GPUStateUtils.h"
+#include "GPU/Common/ShaderId.h"
// These are small, let's give them plenty of frames.
static const int FRAGTEST_TEXTURE_OLD_AGE = 307;
diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h
index 1571e4a159..610f9fb7c0 100644
--- a/GPU/GLES/TransformPipeline.h
+++ b/GPU/GLES/TransformPipeline.h
@@ -24,6 +24,7 @@
#include "GPU/Common/IndexGenerator.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
+#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GLES/FragmentShaderGenerator.h"
#include "gfx/gl_common.h"
#include "gfx/gl_lost_manager.h"
diff --git a/GPU/GLES/VertexShaderGenerator.h b/GPU/GLES/VertexShaderGenerator.h
index aa1596f308..a513513d8c 100644
--- a/GPU/GLES/VertexShaderGenerator.h
+++ b/GPU/GLES/VertexShaderGenerator.h
@@ -23,11 +23,4 @@
struct ShaderID;
-bool CanUseHardwareTransform(int prim);
-
-void ComputeVertexShaderID(ShaderID *id, u32 vertexType, bool useHWTransform);
void GenerateVertexShader(const ShaderID &id, char *buffer);
-
-// Generates a compact string that describes the shader. Useful in a list to get an overview
-// of the current flora of shaders.
-std::string VertexShaderDesc(const ShaderID &id);
diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj
index af040299e0..6c9d211149 100644
--- a/GPU/GPU.vcxproj
+++ b/GPU/GPU.vcxproj
@@ -249,6 +249,7 @@
+
diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters
index a8f1f0bee2..82555761dd 100644
--- a/GPU/GPU.vcxproj.filters
+++ b/GPU/GPU.vcxproj.filters
@@ -192,13 +192,11 @@
GLES
-
- Common
-
Common
+
@@ -379,5 +377,6 @@
GLES
+
\ No newline at end of file
diff --git a/Qt/GPU.pro b/Qt/GPU.pro
index 02e0dbd725..c3e6b55f8b 100644
--- a/Qt/GPU.pro
+++ b/Qt/GPU.pro
@@ -47,6 +47,7 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU
$$P/GPU/Debugger/*.cpp \
$$P/GPU/Common/DepalettizeShaderCommon.cpp \
$$P/GPU/Common/GPUDebugInterface.cpp \
+ $$P/GPU/Common/GPUStateUtils.cpp \
$$P/GPU/Common/ShaderId.cpp \
$$P/GPU/Common/IndexGenerator.cpp \
$$P/GPU/Common/TextureDecoder.cpp \
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 838d44d3a3..0df78ee0f7 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -157,6 +157,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/GPU/Common/GPUDebugInterface.cpp \
$(SRC)/GPU/Common/IndexGenerator.cpp.arm \
$(SRC)/GPU/Common/ShaderId.cpp.arm \
+ $(SRC)/GPU/Common/GPUStateUtils.cpp.arm \
$(SRC)/GPU/Common/SoftwareTransformCommon.cpp.arm \
$(SRC)/GPU/Common/VertexDecoderCommon.cpp.arm \
$(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \