Centralize the GPU state conversion functions

This commit is contained in:
Henrik Rydgard 2015-10-24 23:49:05 +02:00
parent 92389a4966
commit edafa9bc17
18 changed files with 584 additions and 888 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -134,4 +134,14 @@ struct ShaderID {
void FromString(std::string src) {
memcpy(d, &(src)[0], sizeof(d));
}
};
};
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);

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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"

View File

@ -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);

View File

@ -249,6 +249,7 @@
<ClCompile Include="Common\DrawEngineCommon.cpp" />
<ClCompile Include="Common\FramebufferCommon.cpp" />
<ClCompile Include="Common\GPUDebugInterface.cpp" />
<ClCompile Include="Common\GPUStateUtils.cpp" />
<ClCompile Include="Common\IndexGenerator.cpp" />
<ClCompile Include="Common\PostShader.cpp" />
<ClCompile Include="Common\ShaderId.cpp" />

View File

@ -192,13 +192,11 @@
<ClInclude Include="GLES\GLStateCache.h">
<Filter>GLES</Filter>
</ClInclude>
<ClInclude Include="Common\GPUStateUtils.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Common\ShaderCommon.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Common\ShaderId.h" />
<ClInclude Include="Common\GPUStateUtils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -379,5 +377,6 @@
<Filter>GLES</Filter>
</ClCompile>
<ClCompile Include="Common\ShaderId.cpp" />
<ClCompile Include="Common\GPUStateUtils.cpp" />
</ItemGroup>
</Project>

View File

@ -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 \

View File

@ -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 \