#include "ppsspp_config.h"
#include <algorithm>
#include <limits>
#include "Core/ConfigValues.h"
#include "Core/System.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
#include "GPU/Math3D.h"
#include "GPU/Common/PresentationCommon.h"
#include "GPU/Common/GPUStateUtils.h"
bool IsStencilTestOutputDisabled() {
if (gstate.isStencilTestEnabled() && (gstate.pmska & 0xFF) != 0xFF) {
if (gstate_c.framebufFormat == GE_FORMAT_565) {
return true;
}
return gstate.getStencilOpZPass() == GE_STENCILOP_KEEP && gstate.getStencilOpZFail() == GE_STENCILOP_KEEP && gstate.getStencilOpSFail() == GE_STENCILOP_KEEP;
}
return true;
}
bool NeedsTestDiscard() {
if (gstate.isStencilTestEnabled() && (gstate.pmska & 0xFF) != 0xFF)
return true;
if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled())
return true;
if (!gstate.isAlphaBlendEnabled())
return true;
if (gstate.getBlendFuncA() != GE_SRCBLEND_SRCALPHA && gstate.getBlendFuncA() != GE_SRCBLEND_DOUBLESRCALPHA)
return true;
if (gstate.getBlendFuncB() != GE_DSTBLEND_INVSRCALPHA && gstate.getBlendFuncB() != GE_DSTBLEND_DOUBLEINVSRCALPHA) {
if (gstate.getBlendFuncB() != GE_DSTBLEND_FIXB || gstate.getFixB() != 0xFFFFFF)
return true;
}
if (gstate.getBlendEq() != GE_BLENDMODE_MUL_AND_ADD && gstate.getBlendEq() != GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE)
return true;
if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY)
return true;
return false;
}
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;
return gstate.getAlphaTestRef() == 0;
case GE_COMP_NOTEQUAL:
if (gstate.getAlphaTestRef() == 255) {
return false;
}
[[fallthrough]];
case GE_COMP_GREATER:
{
if (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed()))
return true;
return gstate.getAlphaTestRef() == 0 && !NeedsTestDiscard();
}
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;
}
}
bool IsDepthTestEffectivelyDisabled() {
if (!gstate.isDepthTestEnabled())
return true;
if (gstate.getDepthTestFunction() != GE_COMP_ALWAYS)
return false;
return !gstate.isDepthWriteEnabled();
}
const bool nonAlphaSrcFactors[16] = {
true,
true,
false,
false,
true,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
};
const bool nonAlphaDestFactors[16] = {
true,
true,
false,
false,
true,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
};
ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
if (IsStencilTestOutputDisabled() || gstate.isModeClear()) {
return REPLACE_ALPHA_NO;
}
if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_READ_FRAMEBUFFER) {
if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
return REPLACE_ALPHA_YES;
} else {
if (gstate_c.Use(GPU_USE_DUALSOURCE_BLEND)) {
return REPLACE_ALPHA_DUALSOURCE;
} else {
return REPLACE_ALPHA_NO;
}
}
}
if (replaceBlend == ReplaceBlendType::REPLACE_BLEND_BLUE_TO_ALPHA) {
return REPLACE_ALPHA_NO;
}
return REPLACE_ALPHA_YES;
}
StencilValueType ReplaceAlphaWithStencilType() {
switch (gstate_c.framebufFormat) {
case GE_FORMAT_565:
return STENCIL_VALUE_ONE;
case GE_FORMAT_5551:
switch (gstate.getStencilOpZPass()) {
case GE_STENCILOP_REPLACE:
return (gstate.getStencilTestRef() & 0x80) != 0 ? STENCIL_VALUE_ONE : STENCIL_VALUE_ZERO;
case GE_STENCILOP_DECR:
case GE_STENCILOP_ZERO:
return STENCIL_VALUE_ZERO;
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:
case GE_FORMAT_DEPTH16:
case GE_FORMAT_CLUT8:
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_c.framebufFormat == GE_FORMAT_4444 ? STENCIL_VALUE_DECR_4 : STENCIL_VALUE_DECR_8;
case GE_STENCILOP_INCR:
return gstate_c.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(GEBufferFormat bufferFormat) {
if (gstate_c.blueToAlpha) {
return REPLACE_BLEND_BLUE_TO_ALPHA;
}
if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
return REPLACE_BLEND_NO;
}
GEBlendMode eq = gstate.getBlendEq();
switch (eq) {
case GE_BLENDMODE_ABSDIFF:
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_BLENDMODE_MIN:
case GE_BLENDMODE_MAX:
if (gstate_c.Use(GPU_USE_BLEND_MINMAX)) {
return REPLACE_BLEND_STANDARD;
} else {
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_BLENDMODE_MUL_AND_ADD:
case GE_BLENDMODE_MUL_AND_SUBTRACT:
case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE:
break;
default:
return REPLACE_BLEND_NO;
}
GEBlendSrcFactor funcA = gstate.getBlendFuncA();
GEBlendDstFactor funcB = gstate.getBlendFuncB();
switch (funcA) {
case GE_SRCBLEND_DOUBLESRCALPHA:
case GE_SRCBLEND_DOUBLEINVSRCALPHA:
switch (funcB) {
case GE_DSTBLEND_SRCCOLOR:
case GE_DSTBLEND_INVSRCCOLOR:
if (funcA == GE_SRCBLEND_DOUBLEINVSRCALPHA)
return REPLACE_BLEND_2X_ALPHA;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565)
return REPLACE_BLEND_2X_ALPHA;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
if (gstate_c.Use(GPU_USE_FRAMEBUFFER_FETCH))
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
case GE_DSTBLEND_DSTALPHA:
case GE_DSTBLEND_INVDSTALPHA:
case GE_DSTBLEND_FIXB:
default:
return REPLACE_BLEND_PRE_SRC;
}
case GE_SRCBLEND_DOUBLEDSTALPHA:
switch (funcB) {
case GE_DSTBLEND_SRCCOLOR:
case GE_DSTBLEND_INVSRCCOLOR:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_2X_ALPHA;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
case GE_DSTBLEND_DSTALPHA:
case GE_DSTBLEND_INVDSTALPHA:
case GE_DSTBLEND_FIXB:
default:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_SRCBLEND_DOUBLEINVDSTALPHA:
switch (funcB) {
case GE_DSTBLEND_SRCCOLOR:
case GE_DSTBLEND_INVSRCCOLOR:
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_2X_ALPHA;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
case GE_DSTBLEND_DSTALPHA:
case GE_DSTBLEND_INVDSTALPHA:
case GE_DSTBLEND_FIXB:
default:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_SRCBLEND_FIXA:
default:
switch (funcB) {
case GE_DSTBLEND_DOUBLESRCALPHA:
{
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
return REPLACE_BLEND_2X_ALPHA;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_FIXB:
default:
if (gstate.getFixA() == 0xFFFFFF && gstate.getFixB() == 0x000000) {
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;
}
case GE_DSTBLEND_SRCCOLOR:
case GE_DSTBLEND_INVSRCCOLOR:
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
case GE_DSTBLEND_DSTALPHA:
case GE_DSTBLEND_INVDSTALPHA:
return REPLACE_BLEND_STANDARD;
}
case GE_SRCBLEND_DSTCOLOR:
case GE_SRCBLEND_INVDSTCOLOR:
case GE_SRCBLEND_SRCALPHA:
case GE_SRCBLEND_INVSRCALPHA:
case GE_SRCBLEND_DSTALPHA:
case GE_SRCBLEND_INVDSTALPHA:
switch (funcB) {
case GE_DSTBLEND_DOUBLESRCALPHA:
if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
if (gstate_c.Use(GPU_USE_FRAMEBUFFER_FETCH))
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
} else {
if (gstate_c.Use(GPU_USE_FRAMEBUFFER_FETCH))
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_2X_ALPHA;
}
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
}
return REPLACE_BLEND_2X_ALPHA;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return REPLACE_BLEND_READ_FRAMEBUFFER;
default:
return REPLACE_BLEND_STANDARD;
}
}
return REPLACE_BLEND_STANDARD;
}
static const float DEPTH_SLICE_FACTOR_HIGH = 4.0f;
static const float DEPTH_SLICE_FACTOR_16BIT = 256.0f;
float DepthSliceFactor(u32 useFlags) {
if (!(useFlags & GPU_USE_ACCURATE_DEPTH)) {
return 1.0f;
}
if (useFlags & GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT) {
return DEPTH_SLICE_FACTOR_16BIT;
}
if (useFlags & GPU_USE_DEPTH_CLAMP) {
return 1.0f;
}
return DEPTH_SLICE_FACTOR_HIGH;
}
DepthScaleFactors GetDepthScaleFactors(u32 useFlags) {
if (!(useFlags & GPU_USE_ACCURATE_DEPTH)) {
return DepthScaleFactors(0.0f, 65535.0f);
}
if (useFlags & GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT) {
const double offset = 0.5 * (DEPTH_SLICE_FACTOR_16BIT - 1.0) / DEPTH_SLICE_FACTOR_16BIT;
const double scale = 16777215.0;
return DepthScaleFactors(offset, scale);
} else if (useFlags & GPU_USE_DEPTH_CLAMP) {
return DepthScaleFactors(0.0f, 65535.0f);
} else {
const double offset = 0.5f * (DEPTH_SLICE_FACTOR_HIGH - 1.0f) * (1.0f / DEPTH_SLICE_FACTOR_HIGH);
return DepthScaleFactors(offset, (float)(DEPTH_SLICE_FACTOR_HIGH * 65535.0));
}
}
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
out.throughMode = gstate.isModeThrough();
float renderWidthFactor, renderHeightFactor;
float renderX = 0.0f, renderY = 0.0f;
float displayOffsetX, displayOffsetY;
if (useBufferedRendering) {
displayOffsetX = 0.0f;
displayOffsetY = 0.0f;
renderWidthFactor = (float)renderWidth / (float)bufferWidth;
renderHeightFactor = (float)renderHeight / (float)bufferHeight;
} else {
float pixelW = PSP_CoreParameter().pixelWidth;
float pixelH = PSP_CoreParameter().pixelHeight;
FRect frame = GetScreenFrame(pixelW, pixelH);
FRect rc;
CalculateDisplayOutputRect(&rc, 480, 272, frame, ROTATION_LOCKED_HORIZONTAL);
displayOffsetX = rc.x;
displayOffsetY = rc.y;
renderWidth = rc.w;
renderHeight = rc.h;
renderWidthFactor = renderWidth / 480.0f;
renderHeightFactor = renderHeight / 272.0f;
}
renderX = std::max(gstate_c.curRTOffsetX, 0);
renderY = std::max(gstate_c.curRTOffsetY, 0);
int scissorX1 = gstate.getScissorX1();
int scissorY1 = gstate.getScissorY1();
int scissorX2 = gstate.getScissorX2() + 1;
int scissorY2 = gstate.getScissorY2() + 1;
if (scissorX2 < scissorX1 || scissorY2 < scissorY1) {
out.scissorX = 0;
out.scissorY = 0;
out.scissorW = 0;
out.scissorH = 0;
} else {
out.scissorX = (renderX * renderWidthFactor) + displayOffsetX + scissorX1 * renderWidthFactor;
out.scissorY = (renderY * renderHeightFactor) + displayOffsetY + scissorY1 * renderHeightFactor;
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
}
int curRTWidth = gstate_c.curRTWidth;
int curRTHeight = gstate_c.curRTHeight;
float offsetX = gstate.getOffsetX();
float offsetY = gstate.getOffsetY();
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
if (out.throughMode) {
out.viewportX = renderX * renderWidthFactor + displayOffsetX;
out.viewportY = renderY * renderHeightFactor + displayOffsetY;
out.viewportW = curRTWidth * renderWidthFactor;
out.viewportH = curRTHeight * renderHeightFactor;
out.depthRangeMin = depthScale.EncodeFromU16(0.0f);
out.depthRangeMax = depthScale.EncodeFromU16(65536.0f);
} else {
float vpXScale = gstate.getViewportXScale();
float vpXCenter = gstate.getViewportXCenter();
float vpYScale = gstate.getViewportYScale();
float vpYCenter = gstate.getViewportYCenter();
float vpX0 = vpXCenter - offsetX - fabsf(vpXScale);
float vpY0 = vpYCenter - offsetY - fabsf(vpYScale);
gstate_c.vpWidth = vpXScale * 2.0f;
gstate_c.vpHeight = vpYScale * 2.0f;
float vpWidth = fabsf(gstate_c.vpWidth);
float vpHeight = fabsf(gstate_c.vpHeight);
float left = renderX + vpX0;
float top = renderY + vpY0;
float right = left + vpWidth;
float bottom = top + vpHeight;
out.widthScale = 1.0f;
out.xOffset = 0.0f;
out.heightScale = 1.0f;
out.yOffset = 0.0f;
{
float overageLeft = std::max(-left, 0.0f);
float overageRight = std::max(right - bufferWidth, 0.0f);
if (right < scissorX2) {
overageRight -= scissorX2 - right;
}
if (left > scissorX1) {
overageLeft += scissorX1 - left;
}
float drift = overageRight - overageLeft;
if (overageLeft != 0.0f || overageRight != 0.0f) {
left += overageLeft;
right -= overageRight;
if (right <= left) {
right = left + 1.0f;
}
out.widthScale = vpWidth / (right - left);
out.xOffset = drift / (right - left);
}
}
{
float overageTop = std::max(-top, 0.0f);
float overageBottom = std::max(bottom - bufferHeight, 0.0f);
if (bottom < scissorY2) {
overageBottom -= scissorY2 - bottom;
}
if (top > scissorY1) {
overageTop += scissorY1 - top;
}
float drift = overageBottom - overageTop;
if (overageTop != 0.0f || overageBottom != 0.0f) {
top += overageTop;
bottom -= overageBottom;
if (bottom <= top) {
bottom = top + 1.0f;
}
out.heightScale = vpHeight / (bottom - top);
out.yOffset = drift / (bottom - top);
}
}
out.viewportX = left * renderWidthFactor + displayOffsetX;
out.viewportY = top * renderHeightFactor + displayOffsetY;
out.viewportW = (right - left) * renderWidthFactor;
out.viewportH = (bottom - top) * renderHeightFactor;
float vpZScale = gstate.getViewportZScale();
float vpZCenter = gstate.getViewportZCenter();
float minz = gstate.getDepthRangeMin();
float maxz = gstate.getDepthRangeMax();
if (gstate.isDepthClampEnabled() && (minz == 0 || maxz == 65535)) {
float fullDepthRange = 65535.0f * (depthScale.Scale() - 1.0f) * (1.0f / 2.0f);
if (minz == 0) {
minz -= fullDepthRange;
}
if (maxz == 65535) {
maxz += fullDepthRange;
}
} else if (maxz == 65535) {
if (depthScale.Scale() > 1.0f)
maxz = 65535.99f;
}
float halfActualZRange = (maxz - minz) * (1.0f / 2.0f);
out.depthScale = halfActualZRange < std::numeric_limits<float>::epsilon() ? 1.0f : vpZScale / halfActualZRange;
out.zOffset = halfActualZRange < std::numeric_limits<float>::epsilon() ? 0.0f : (vpZCenter - (minz + halfActualZRange)) / halfActualZRange;
if (!gstate_c.Use(GPU_USE_ACCURATE_DEPTH)) {
out.depthScale = 1.0f;
out.zOffset = 0.0f;
out.depthRangeMin = depthScale.EncodeFromU16(vpZCenter - vpZScale);
out.depthRangeMax = depthScale.EncodeFromU16(vpZCenter + vpZScale);
} else {
out.depthRangeMin = depthScale.EncodeFromU16(minz);
out.depthRangeMax = depthScale.EncodeFromU16(maxz);
}
out.depthRangeMin = std::max(out.depthRangeMin, 0.0f);
out.depthRangeMax = std::min(out.depthRangeMax, 1.0f);
}
}
void UpdateCachedViewportState(const ViewportAndScissor &vpAndScissor) {
if (vpAndScissor.throughMode)
return;
bool scaleChanged = gstate_c.vpWidthScale != vpAndScissor.widthScale || gstate_c.vpHeightScale != vpAndScissor.heightScale;
bool offsetChanged = gstate_c.vpXOffset != vpAndScissor.xOffset || gstate_c.vpYOffset != vpAndScissor.yOffset;
bool depthChanged = gstate_c.vpDepthScale != vpAndScissor.depthScale || gstate_c.vpZOffset != vpAndScissor.zOffset;
if (scaleChanged || offsetChanged || depthChanged) {
gstate_c.vpWidthScale = vpAndScissor.widthScale;
gstate_c.vpHeightScale = vpAndScissor.heightScale;
gstate_c.vpDepthScale = vpAndScissor.depthScale;
gstate_c.vpXOffset = vpAndScissor.xOffset;
gstate_c.vpYOffset = vpAndScissor.yOffset;
gstate_c.vpZOffset = vpAndScissor.zOffset;
gstate_c.Dirty(DIRTY_PROJMATRIX);
if (depthChanged) {
gstate_c.Dirty(DIRTY_DEPTHRANGE);
}
}
}
static const BlendFactor genericALookup[11] = {
BlendFactor::DST_COLOR,
BlendFactor::ONE_MINUS_DST_COLOR,
BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA,
BlendFactor::DST_ALPHA,
BlendFactor::ONE_MINUS_DST_ALPHA,
BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA,
BlendFactor::DST_ALPHA,
BlendFactor::ONE_MINUS_DST_ALPHA,
BlendFactor::CONSTANT_COLOR,
};
static const BlendFactor genericBLookup[11] = {
BlendFactor::SRC_COLOR,
BlendFactor::ONE_MINUS_SRC_COLOR,
BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA,
BlendFactor::DST_ALPHA,
BlendFactor::ONE_MINUS_DST_ALPHA,
BlendFactor::SRC_ALPHA,
BlendFactor::ONE_MINUS_SRC_ALPHA,
BlendFactor::DST_ALPHA,
BlendFactor::ONE_MINUS_DST_ALPHA,
BlendFactor::CONSTANT_COLOR,
};
static const BlendEq eqLookupNoMinMax[] = {
BlendEq::ADD,
BlendEq::SUBTRACT,
BlendEq::REVERSE_SUBTRACT,
BlendEq::ADD,
BlendEq::ADD,
BlendEq::ADD,
BlendEq::ADD,
BlendEq::ADD,
};
static const BlendEq eqLookup[] = {
BlendEq::ADD,
BlendEq::SUBTRACT,
BlendEq::REVERSE_SUBTRACT,
BlendEq::MIN,
BlendEq::MAX,
BlendEq::MAX,
BlendEq::ADD,
BlendEq::ADD,
};
static BlendFactor toDualSource(BlendFactor blendfunc) {
switch (blendfunc) {
case BlendFactor::SRC_ALPHA:
return BlendFactor::SRC1_ALPHA;
case BlendFactor::ONE_MINUS_SRC_ALPHA:
return BlendFactor::ONE_MINUS_SRC1_ALPHA;
default:
return blendfunc;
}
}
static BlendFactor blendColor2Func(u32 fix, bool &approx) {
if (fix == 0xFFFFFF)
return BlendFactor::ONE;
if (fix == 0)
return BlendFactor::ZERO;
approx = true;
const Vec3f fix3 = Vec3f::FromRGB(fix);
if (fix3.x >= 0.99 && fix3.y >= 0.99 && fix3.z >= 0.99)
return BlendFactor::ONE;
else if (fix3.x <= 0.01 && fix3.y <= 0.01 && fix3.z <= 0.01)
return BlendFactor::ZERO;
return BlendFactor::INVALID;
}
inline int iabs(int x) {
return x >= 0 ? x : -x;
}
static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) {
int diffx = iabs((a & 0xff) - (b & 0xff));
int diffy = iabs(((a >> 8) & 0xff) - ((b >> 8) & 0xff));
int diffz = iabs(((a >> 16) & 0xff) - ((b >> 16) & 0xff));
if (diffx <= margin && diffy <= margin && diffz <= margin)
return true;
return false;
}
static bool SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) {
if (!gstate.isLogicOpEnabled())
return false;
if (!gstate_c.Use(GPU_USE_LOGIC_OP)) {
switch (gstate.getLogicOp()) {
case GE_LOGIC_CLEAR:
srcBlend = BlendFactor::ZERO;
dstBlend = BlendFactor::ZERO;
blendEq = BlendEq::ADD;
return true;
case GE_LOGIC_AND:
case GE_LOGIC_AND_REVERSE:
WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, Log::G3D, "Unsupported AND logic op: %x", gstate.getLogicOp());
break;
case GE_LOGIC_COPY:
break;
case GE_LOGIC_COPY_INVERTED:
break;
case GE_LOGIC_AND_INVERTED:
case GE_LOGIC_NOR:
case GE_LOGIC_NAND:
case GE_LOGIC_EQUIV:
WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, Log::G3D, "Attempted invert for logic op: %x", gstate.getLogicOp());
break;
case GE_LOGIC_INVERTED:
srcBlend = BlendFactor::ONE;
dstBlend = BlendFactor::ONE;
blendEq = BlendEq::SUBTRACT;
WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, Log::G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp());
return true;
case GE_LOGIC_NOOP:
srcBlend = BlendFactor::ZERO;
dstBlend = BlendFactor::ONE;
blendEq = BlendEq::ADD;
return true;
case GE_LOGIC_XOR:
WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, Log::G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp());
break;
case GE_LOGIC_OR:
case GE_LOGIC_OR_INVERTED:
srcBlend = BlendFactor::ONE;
dstBlend = BlendFactor::ONE;
blendEq = BlendEq::ADD;
WARN_LOG_REPORT_ONCE(d3dLogicOpOr, Log::G3D, "Attempted or for logic op: %x", gstate.getLogicOp());
return true;
case GE_LOGIC_OR_REVERSE:
WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, Log::G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp());
break;
case GE_LOGIC_SET:
srcBlend = BlendFactor::ONE;
dstBlend = BlendFactor::ONE;
blendEq = BlendEq::ADD;
WARN_LOG_REPORT_ONCE(d3dLogicOpSet, Log::G3D, "Attempted set for logic op: %x", gstate.getLogicOp());
return true;
}
} else {
switch (gstate.getLogicOp()) {
case GE_LOGIC_CLEAR:
srcBlend = BlendFactor::ZERO;
dstBlend = BlendFactor::ZERO;
blendEq = BlendEq::ADD;
return true;
case GE_LOGIC_NOOP:
srcBlend = BlendFactor::ZERO;
dstBlend = BlendFactor::ONE;
blendEq = BlendEq::ADD;
return true;
default:
return false;
}
}
return false;
}
SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded() {
if (!gstate_c.Use(GPU_USE_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;
}
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) {
StencilValueType stencilType = STENCIL_VALUE_KEEP;
if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) {
stencilType = ReplaceAlphaWithStencilType();
}
BlendFactor srcBlend = BlendFactor::ONE;
BlendFactor dstBlend = BlendFactor::ZERO;
BlendEq blendEq = BlendEq::ADD;
switch (stencilType) {
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_INCR_8:
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::ADD);
break;
case STENCIL_VALUE_DECR_4:
case STENCIL_VALUE_DECR_8:
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::SUBTRACT);
break;
case STENCIL_VALUE_INVERT:
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::REVERSE_SUBTRACT);
break;
default:
if (srcBlend == BlendFactor::ONE && dstBlend == BlendFactor::ZERO && blendEq == BlendEq::ADD) {
blendState.blendEnabled = false;
} else {
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ZERO);
blendState.setEquation(blendEq, BlendEq::ADD);
}
break;
}
}
enum class FBReadSetting {
Forced,
Allowed,
Disallowed,
};
static void ConvertMaskState(GenericMaskState &maskState, FBReadSetting useShader) {
if (gstate_c.blueToAlpha) {
maskState.applyFramebufferRead = false;
maskState.uniformMask = 0xFF000000;
maskState.channelMask = 0x8;
return;
}
uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24));
maskState.uniformMask = colorMask;
maskState.applyFramebufferRead = false;
maskState.channelMask = 0;
for (int i = 0; i < 4; i++) {
uint32_t channelMask = (colorMask >> (i * 8)) & 0xFF;
switch (channelMask) {
case 0x0:
break;
case 0xFF:
maskState.channelMask |= 1 << i;
break;
default:
if (useShader != FBReadSetting::Disallowed && PSP_CoreParameter().compat.flags().ShaderColorBitmask) {
maskState.applyFramebufferRead = true;
maskState.channelMask |= 1 << i;
} else {
if (channelMask >= 128) {
maskState.channelMask |= 1 << i;
}
}
}
}
if (IsStencilTestOutputDisabled() || ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
maskState.channelMask &= ~8;
maskState.uniformMask &= ~0xFF000000;
}
if (gstate.FrameBufFormat() == GE_FORMAT_5551) {
if ((maskState.uniformMask & 0x80000000) != 0)
maskState.uniformMask |= 0xFF000000;
else
maskState.uniformMask &= ~0xFF000000;
}
}
static void ConvertBlendState(GenericBlendState &blendState, FBReadSetting useFBRead) {
blendState.applyFramebufferRead = false;
blendState.dirtyShaderBlendFixValues = false;
blendState.useBlendColor = false;
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.framebufFormat);
if (useFBRead == FBReadSetting::Forced) {
replaceBlend = gstate.isAlphaBlendEnabled() ? REPLACE_BLEND_READ_FRAMEBUFFER : REPLACE_BLEND_NO;
}
blendState.replaceBlend = replaceBlend;
blendState.simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded();
ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend);
blendState.replaceAlphaWithStencil = replaceAlphaWithStencil;
bool usePreSrc = false;
bool blueToAlpha = false;
switch (replaceBlend) {
case REPLACE_BLEND_NO:
ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState);
if (useFBRead == FBReadSetting::Forced) {
blendState.simulateLogicOpType = LOGICOPTYPE_NORMAL;
}
return;
case REPLACE_BLEND_BLUE_TO_ALPHA:
blueToAlpha = true;
blendState.blendEnabled = gstate.isAlphaBlendEnabled();
break;
case REPLACE_BLEND_READ_FRAMEBUFFER:
blendState.blendEnabled = true;
blendState.applyFramebufferRead = true;
blendState.simulateLogicOpType = LOGICOPTYPE_NORMAL;
break;
case REPLACE_BLEND_PRE_SRC:
case REPLACE_BLEND_PRE_SRC_2X_ALPHA:
blendState.blendEnabled = true;
usePreSrc = true;
break;
case REPLACE_BLEND_STANDARD:
case REPLACE_BLEND_2X_ALPHA:
case REPLACE_BLEND_2X_SRC:
blendState.blendEnabled = true;
break;
}
const GEBlendMode blendFuncEq = gstate.getBlendEq();
GEBlendSrcFactor blendFuncA = gstate.getBlendFuncA();
GEBlendDstFactor blendFuncB = gstate.getBlendFuncB();
const u32 fixA = gstate.getFixA();
const u32 fixB = gstate.getFixB();
if (blendFuncA > GE_SRCBLEND_FIXA)
blendFuncA = GE_SRCBLEND_FIXA;
if (blendFuncB > GE_DSTBLEND_FIXB)
blendFuncB = GE_DSTBLEND_FIXB;
int constantAlpha = 255;
BlendFactor constantAlphaGL = BlendFactor::ONE;
if (!IsStencilTestOutputDisabled() && replaceAlphaWithStencil == REPLACE_ALPHA_NO) {
switch (ReplaceAlphaWithStencilType()) {
case STENCIL_VALUE_UNIFORM:
constantAlpha = gstate.getStencilTestRef();
break;
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_DECR_4:
constantAlpha = 16;
break;
case STENCIL_VALUE_INCR_8:
case STENCIL_VALUE_DECR_8:
constantAlpha = 1;
break;
default:
break;
}
if (constantAlpha <= 0) {
constantAlphaGL = BlendFactor::ZERO;
} else if (constantAlpha < 255) {
constantAlphaGL = BlendFactor::CONSTANT_ALPHA;
}
}
bool approxFuncA = false;
BlendFactor glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(fixA, approxFuncA) : genericALookup[blendFuncA];
bool approxFuncB = false;
BlendFactor glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(fixB, approxFuncB) : genericBLookup[blendFuncB];
if (gstate_c.framebufFormat == GE_FORMAT_565) {
if (blendFuncA == GE_SRCBLEND_DSTALPHA || blendFuncA == GE_SRCBLEND_DOUBLEDSTALPHA) {
glBlendFuncA = BlendFactor::ZERO;
}
if (blendFuncA == GE_SRCBLEND_INVDSTALPHA || blendFuncA == GE_SRCBLEND_DOUBLEINVDSTALPHA) {
glBlendFuncA = BlendFactor::ONE;
}
if (blendFuncB == GE_DSTBLEND_DSTALPHA || blendFuncB == GE_DSTBLEND_DOUBLEDSTALPHA) {
glBlendFuncB = BlendFactor::ZERO;
}
if (blendFuncB == GE_DSTBLEND_INVDSTALPHA || blendFuncB == GE_DSTBLEND_DOUBLEINVDSTALPHA) {
glBlendFuncB = BlendFactor::ONE;
}
}
if (usePreSrc) {
glBlendFuncA = BlendFactor::ONE;
if (blendFuncA == GE_SRCBLEND_FIXA) {
blendState.dirtyShaderBlendFixValues = true;
}
}
if (replaceAlphaWithStencil == REPLACE_ALPHA_DUALSOURCE) {
glBlendFuncA = toDualSource(glBlendFuncA);
glBlendFuncB = toDualSource(glBlendFuncB);
}
if (blendFuncA == GE_SRCBLEND_FIXA || blendFuncB == GE_DSTBLEND_FIXB) {
if (glBlendFuncA == BlendFactor::INVALID && glBlendFuncB != BlendFactor::INVALID) {
blendState.setBlendColor(fixA, constantAlpha);
glBlendFuncA = BlendFactor::CONSTANT_COLOR;
} else if (glBlendFuncA != BlendFactor::INVALID && glBlendFuncB == BlendFactor::INVALID) {
blendState.setBlendColor(fixB, constantAlpha);
glBlendFuncB = BlendFactor::CONSTANT_COLOR;
} else if (glBlendFuncA == BlendFactor::INVALID && glBlendFuncB == BlendFactor::INVALID) {
if (blendColorSimilar(fixA, 0xFFFFFF ^ fixB)) {
glBlendFuncA = BlendFactor::CONSTANT_COLOR;
glBlendFuncB = BlendFactor::ONE_MINUS_CONSTANT_COLOR;
blendState.setBlendColor(fixA, constantAlpha);
} else if (blendColorSimilar(fixA, fixB)) {
glBlendFuncA = BlendFactor::CONSTANT_COLOR;
glBlendFuncB = BlendFactor::CONSTANT_COLOR;
blendState.setBlendColor(fixA, constantAlpha);
} else {
DEBUG_LOG(Log::G3D, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", fixA, fixB, blendFuncA, blendFuncB);
const bool nearZeroA = blendColorSimilar(fixA, 0, 64);
const bool nearZeroB = blendColorSimilar(fixB, 0, 64);
if (nearZeroA || blendColorSimilar(fixA, 0xFFFFFF, 64)) {
glBlendFuncA = nearZeroA ? BlendFactor::ZERO : BlendFactor::ONE;
glBlendFuncB = BlendFactor::CONSTANT_COLOR;
blendState.setBlendColor(fixB, constantAlpha);
} else {
glBlendFuncA = BlendFactor::CONSTANT_COLOR;
glBlendFuncB = nearZeroB ? BlendFactor::ZERO : BlendFactor::ONE;
blendState.setBlendColor(fixA, constantAlpha);
}
}
} else {
if (blendFuncA == GE_SRCBLEND_FIXA && !usePreSrc && approxFuncA) {
glBlendFuncA = BlendFactor::CONSTANT_COLOR;
blendState.setBlendColor(fixA, constantAlpha);
} else if (approxFuncB) {
glBlendFuncB = BlendFactor::CONSTANT_COLOR;
blendState.setBlendColor(fixB, constantAlpha);
} else {
if (constantAlphaGL == BlendFactor::CONSTANT_ALPHA) {
blendState.defaultBlendColor(constantAlpha);
}
}
}
} else {
if (constantAlphaGL == BlendFactor::CONSTANT_ALPHA) {
blendState.defaultBlendColor(constantAlpha);
}
}
#if PPSSPP_PLATFORM(ANDROID)
if (g_Config.bSkipBufferEffects) {
if (glBlendFuncA == BlendFactor::DST_ALPHA) glBlendFuncA = BlendFactor::ZERO;
if (glBlendFuncB == BlendFactor::DST_ALPHA) glBlendFuncB = BlendFactor::ZERO;
if (glBlendFuncA == BlendFactor::ONE_MINUS_DST_ALPHA) glBlendFuncA = BlendFactor::ONE;
if (glBlendFuncB == BlendFactor::ONE_MINUS_DST_ALPHA) glBlendFuncB = BlendFactor::ONE;
}
#endif
BlendEq colorEq;
if (gstate_c.Use(GPU_USE_BLEND_MINMAX)) {
colorEq = eqLookup[blendFuncEq];
} else {
colorEq = eqLookupNoMinMax[blendFuncEq];
}
BlendEq alphaEq = BlendEq::ADD;
if (replaceAlphaWithStencil != REPLACE_ALPHA_NO) {
switch (ReplaceAlphaWithStencilType()) {
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_INCR_8:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ONE);
break;
case STENCIL_VALUE_DECR_4:
case STENCIL_VALUE_DECR_8:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ONE);
alphaEq = BlendEq::SUBTRACT;
break;
case STENCIL_VALUE_INVERT:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ONE);
alphaEq = BlendEq::REVERSE_SUBTRACT;
break;
default:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ZERO);
break;
}
} else if (!IsStencilTestOutputDisabled()) {
StencilValueType stencilValue = ReplaceAlphaWithStencilType();
if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0x00) {
stencilValue = STENCIL_VALUE_ZERO;
} else if (stencilValue == STENCIL_VALUE_UNIFORM && constantAlpha == 0xFF) {
stencilValue = STENCIL_VALUE_ONE;
}
switch (stencilValue) {
case STENCIL_VALUE_KEEP:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE);
break;
case STENCIL_VALUE_ONE:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ONE);
break;
case STENCIL_VALUE_ZERO:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ZERO);
break;
case STENCIL_VALUE_UNIFORM:
blendState.setFactors(glBlendFuncA, glBlendFuncB, constantAlphaGL, BlendFactor::ZERO);
break;
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_INCR_8:
blendState.setFactors(glBlendFuncA, glBlendFuncB, constantAlphaGL, BlendFactor::ONE);
break;
case STENCIL_VALUE_DECR_4:
case STENCIL_VALUE_DECR_8:
blendState.setFactors(glBlendFuncA, glBlendFuncB, constantAlphaGL, BlendFactor::ONE);
alphaEq = BlendEq::SUBTRACT;
break;
case STENCIL_VALUE_INVERT:
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ONE, BlendFactor::ONE);
alphaEq = BlendEq::REVERSE_SUBTRACT;
break;
}
} else if (blueToAlpha) {
blendState.setFactors(BlendFactor::ZERO, BlendFactor::ZERO, BlendFactor::ONE, glBlendFuncB);
blendState.setEquation(BlendEq::ADD, colorEq);
return;
} else {
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE);
}
blendState.setEquation(colorEq, alphaEq);
}
static void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported, FBReadSetting useFBRead) {
if (!gstate.isLogicOpEnabled() || gstate.getLogicOp() == GE_LOGIC_COPY) {
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = useFBRead == FBReadSetting::Forced;
return;
}
if (useFBRead == FBReadSetting::Forced && shaderBitOpsSupported) {
logicOpState.logicOpEnabled = false;
logicOpState.applyFramebufferRead = true;
logicOpState.logicOp = gstate.getLogicOp();
} else if (logicSupported) {
logicOpState.applyFramebufferRead = false;
if (gstate.isLogicOpEnabled()) {
logicOpState.logicOpEnabled = true;
logicOpState.logicOp = gstate.getLogicOp();
} else {
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
}
} else if (shaderBitOpsSupported && useFBRead != FBReadSetting::Disallowed) {
logicOpState.logicOpEnabled = false;
logicOpState.applyFramebufferRead = true;
logicOpState.logicOp = gstate.getLogicOp();
} else {
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = false;
}
}
static void ConvertStencilFunc5551(GenericStencilFuncState &state) {
const bool usesRef = state.sFail == GE_STENCILOP_REPLACE || state.zFail == GE_STENCILOP_REPLACE || state.zPass == GE_STENCILOP_REPLACE;
const u8 maskedRef = state.testRef & state.testMask;
const u8 usedRef = (state.testRef & 0x80) != 0 ? 0xFF : 0x00;
auto rewriteFunc = [&](GEComparison func, u8 ref) {
if (!usesRef || usedRef == ref) {
state.testFunc = func;
state.testRef = ref;
state.testMask = 0xFF;
}
};
auto rewriteRef = [&](bool always) {
state.testFunc = always ? GE_COMP_ALWAYS : GE_COMP_NEVER;
if (usesRef) {
state.testRef = usedRef;
state.testMask = 0xFF;
} else {
state.testRef = 0xFF;
state.testMask = 0xFF;
}
};
switch (state.testFunc) {
case GE_COMP_NEVER:
case GE_COMP_ALWAYS:
rewriteRef(state.testFunc == GE_COMP_ALWAYS);
break;
case GE_COMP_EQUAL:
if (maskedRef == 0) {
rewriteFunc(GE_COMP_EQUAL, 0);
} else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) {
rewriteFunc(GE_COMP_NOTEQUAL, 0);
} else {
rewriteRef(false);
}
break;
case GE_COMP_NOTEQUAL:
if (maskedRef == 0) {
rewriteFunc(GE_COMP_NOTEQUAL, 0);
} else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) {
rewriteFunc(GE_COMP_EQUAL, 0);
} else {
rewriteRef(true);
}
break;
case GE_COMP_LESS:
if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) {
rewriteRef(false);
} else {
rewriteFunc(GE_COMP_NOTEQUAL, 0);
}
break;
case GE_COMP_LEQUAL:
if (maskedRef == 0) {
rewriteRef(true);
} else {
rewriteFunc(GE_COMP_NOTEQUAL, 0);
}
break;
case GE_COMP_GREATER:
if (maskedRef > 0) {
rewriteFunc(GE_COMP_EQUAL, 0);
} else {
rewriteRef(false);
}
break;
case GE_COMP_GEQUAL:
if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) {
rewriteRef(true);
} else {
rewriteFunc(GE_COMP_EQUAL, 0);
}
break;
}
auto rewriteOps = [&](GEStencilOp from, GEStencilOp to) {
if (state.sFail == from)
state.sFail = to;
if (state.zFail == from)
state.zFail = to;
if (state.zPass == from)
state.zPass = to;
};
rewriteOps(GE_STENCILOP_DECR, GE_STENCILOP_ZERO);
if (state.testFunc == GE_COMP_NOTEQUAL && state.testRef == 0 && state.testMask != 0) {
rewriteOps(GE_STENCILOP_INVERT, GE_STENCILOP_ZERO);
}
if (state.testFunc == GE_COMP_EQUAL && state.testRef == 0 && state.testMask != 0) {
rewriteOps(GE_STENCILOP_INCR, GE_STENCILOP_INVERT);
}
if (!usesRef && state.testRef == 0xFF) {
rewriteOps(GE_STENCILOP_INCR, GE_STENCILOP_REPLACE);
}
}
static void ConvertStencilMask5551(GenericStencilFuncState &state) {
state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00;
}
void ConvertStencilFuncState(GenericStencilFuncState &state) {
state.writeMask = (~gstate.getStencilWriteMask()) & 0xFF;
state.enabled = gstate.isStencilTestEnabled();
if (!state.enabled) {
if (gstate_c.framebufFormat == GE_FORMAT_5551)
ConvertStencilMask5551(state);
return;
}
state.sFail = gstate.getStencilOpSFail();
state.zFail = gstate.getStencilOpZFail();
state.zPass = gstate.getStencilOpZPass();
state.testFunc = gstate.getStencilTestFunction();
state.testRef = gstate.getStencilTestRef();
state.testMask = gstate.getStencilTestMask();
bool depthTest = gstate.isDepthTestEnabled();
if ((state.sFail == state.zFail || !depthTest) && state.sFail == state.zPass) {
bool depthWrite = gstate.isDepthWriteEnabled();
if ((gstate.getColorMask() & 0x00FFFFFF) == 0x00FFFFFF && (!depthTest || !depthWrite)) {
state.testFunc = GE_COMP_ALWAYS;
}
}
switch (gstate_c.framebufFormat) {
case GE_FORMAT_565:
state.writeMask = 0;
break;
case GE_FORMAT_5551:
ConvertStencilMask5551(state);
ConvertStencilFunc5551(state);
break;
default:
break;
}
}
void GenericMaskState::Log() {
WARN_LOG(Log::G3D, "Mask: %08x %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead);
}
void GenericBlendState::Log() {
WARN_LOG(Log::G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d",
blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil);
}
void ComputedPipelineState::Convert(bool shaderBitOpsSupported, bool fbReadAllowed) {
FBReadSetting readFB = (fbReadAllowed && shaderBitOpsSupported) ? FBReadSetting::Allowed : FBReadSetting::Disallowed;
ConvertMaskState(maskState, readFB);
readFB = maskState.applyFramebufferRead ? FBReadSetting::Forced : (fbReadAllowed ? FBReadSetting::Allowed : FBReadSetting::Disallowed);
ConvertLogicOpState(logicState, gstate_c.Use(GPU_USE_LOGIC_OP), shaderBitOpsSupported, readFB);
readFB = logicState.applyFramebufferRead ? FBReadSetting::Forced : (fbReadAllowed ? FBReadSetting::Allowed : FBReadSetting::Disallowed);
ConvertBlendState(blendState, readFB);
if (blendState.applyFramebufferRead || logicState.applyFramebufferRead) {
_dbg_assert_(fbReadAllowed);
maskState.ConvertToShaderBlend();
logicState.ConvertToShaderBlend();
} else {
logicState.ApplyToBlendState(blendState);
}
}
void GenericLogicState::ApplyToBlendState(GenericBlendState &blendState) {
if (SimulateLogicOpIfNeeded(blendState.srcColor, blendState.dstColor, blendState.eqColor)) {
if (!blendState.blendEnabled) {
blendState.blendEnabled = true;
blendState.srcAlpha = BlendFactor::ONE;
blendState.dstAlpha = BlendFactor::ZERO;
blendState.eqAlpha = BlendEq::ADD;
}
logicOpEnabled = false;
logicOp = GE_LOGIC_COPY;
}
}