softjit: Optimize common case bloom blending.

Bloom often uses fixed ONE + ONE, which is a lot less work for us.  And
bloom often runs over and over again on pixels, so saving work is good.
This commit is contained in:
Unknown W. Brackets 2022-01-01 20:40:28 -08:00
parent 6fb5d82fe0
commit 355bad666c
6 changed files with 189 additions and 86 deletions

View file

@ -591,47 +591,73 @@ void ComputePixelBlendState(PixelBlendState &state, const PixelFuncID &id) {
if (state.usesFactors) {
switch (id.AlphaBlendSrc()) {
case GE_SRCBLEND_DSTALPHA:
case GE_SRCBLEND_INVDSTALPHA:
case GE_SRCBLEND_DOUBLEDSTALPHA:
case GE_SRCBLEND_DOUBLEINVDSTALPHA:
case PixelBlendFactor::DSTALPHA:
case PixelBlendFactor::INVDSTALPHA:
case PixelBlendFactor::DOUBLEDSTALPHA:
case PixelBlendFactor::DOUBLEINVDSTALPHA:
state.usesDstAlpha = true;
break;
case PixelBlendFactor::OTHERCOLOR:
case PixelBlendFactor::INVOTHERCOLOR:
state.dstColorAsFactor = true;
break;
case PixelBlendFactor::SRCALPHA:
case PixelBlendFactor::INVSRCALPHA:
case PixelBlendFactor::DOUBLESRCALPHA:
case PixelBlendFactor::DOUBLEINVSRCALPHA:
state.srcColorAsFactor = true;
break;
default:
break;
}
switch (id.AlphaBlendDst()) {
case GE_DSTBLEND_INVSRCALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == GE_SRCBLEND_SRCALPHA;
case PixelBlendFactor::INVSRCALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == PixelBlendFactor::SRCALPHA;
state.srcColorAsFactor = true;
break;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == GE_SRCBLEND_DOUBLESRCALPHA;
case PixelBlendFactor::DOUBLEINVSRCALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == PixelBlendFactor::DOUBLESRCALPHA;
state.srcColorAsFactor = true;
break;
case GE_DSTBLEND_DSTALPHA:
case PixelBlendFactor::DSTALPHA:
state.usesDstAlpha = true;
break;
case GE_DSTBLEND_INVDSTALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == GE_SRCBLEND_DSTALPHA;
case PixelBlendFactor::INVDSTALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == PixelBlendFactor::DSTALPHA;
state.usesDstAlpha = true;
break;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case PixelBlendFactor::DOUBLEDSTALPHA:
state.usesDstAlpha = true;
break;
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == GE_SRCBLEND_DOUBLEDSTALPHA;
case PixelBlendFactor::DOUBLEINVDSTALPHA:
state.dstFactorIsInverse = id.AlphaBlendSrc() == PixelBlendFactor::DOUBLEDSTALPHA;
state.usesDstAlpha = true;
break;
case PixelBlendFactor::OTHERCOLOR:
case PixelBlendFactor::INVOTHERCOLOR:
state.dstColorAsFactor = true;
break;
case PixelBlendFactor::SRCALPHA:
case PixelBlendFactor::DOUBLESRCALPHA:
state.srcColorAsFactor = true;
break;
default:
break;
}
state.dstColorAsFactor = state.dstColorAsFactor || state.usesDstAlpha;
}
}

View file

@ -47,6 +47,8 @@ struct PixelBlendState {
bool usesFactors = false;
bool usesDstAlpha = false;
bool dstFactorIsInverse = false;
bool srcColorAsFactor = false;
bool dstColorAsFactor = false;
};
void ComputePixelBlendState(PixelBlendState &state, const PixelFuncID &id);
@ -88,7 +90,7 @@ private:
bool Jit_DepthTest(const PixelFuncID &id);
bool Jit_WriteDepth(const PixelFuncID &id);
bool Jit_AlphaBlend(const PixelFuncID &id);
bool Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorReg, RegCache::Reg dstReg, GEBlendSrcFactor factor);
bool Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorReg, RegCache::Reg dstReg, PixelBlendFactor factor);
bool Jit_DstBlendFactor(const PixelFuncID &id, RegCache::Reg srcFactorReg, RegCache::Reg dstFactorReg, RegCache::Reg dstReg);
bool Jit_Dither(const PixelFuncID &id);
bool Jit_WriteColor(const PixelFuncID &id);

View file

@ -1081,34 +1081,59 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) {
}
colorIs16Bit_ = true;
// Skip multiplying by factors if we can.
bool multiplySrc = id.AlphaBlendSrc() != PixelBlendFactor::ZERO && id.AlphaBlendSrc() != PixelBlendFactor::ONE;
bool multiplyDst = id.AlphaBlendDst() != PixelBlendFactor::ZERO && id.AlphaBlendDst() != PixelBlendFactor::ONE;
// We also shift left by 4, so mulhi gives us a free shift
// We also need to add a half bit later, so this gives us space.
PSLLW(argColorReg, 4);
PSLLW(dstReg, 4);
if (multiplySrc || blendState.srcColorAsFactor)
PSLLW(argColorReg, 4);
if (multiplyDst || blendState.dstColorAsFactor)
PSLLW(dstReg, 4);
// Okay, now grab our factors.
success = success && Jit_BlendFactor(id, srcFactorReg, dstReg, id.AlphaBlendSrc());
success = success && Jit_DstBlendFactor(id, srcFactorReg, dstFactorReg, dstReg);
// Okay, now grab our factors. Don't bother if they're known values.
if (id.AlphaBlendSrc() < PixelBlendFactor::ZERO)
success = success && Jit_BlendFactor(id, srcFactorReg, dstReg, id.AlphaBlendSrc());
if (id.AlphaBlendDst() < PixelBlendFactor::ZERO)
success = success && Jit_DstBlendFactor(id, srcFactorReg, dstFactorReg, dstReg);
X64Reg constReg = GetConstBase();
X64Reg halfReg = regCache_.Alloc(RegCache::VEC_TEMP3);
// We'll use this several times, so load into a reg.
MOVDQA(halfReg, MConstDisp(constReg, &blendHalf_11_4s[0]));
regCache_.Unlock(constReg, RegCache::GEN_CONST_BASE);
X64Reg halfReg = INVALID_REG;
if (multiplySrc || multiplyDst) {
X64Reg constReg = GetConstBase();
halfReg = regCache_.Alloc(RegCache::VEC_TEMP3);
// We'll use this several times, so load into a reg.
MOVDQA(halfReg, MConstDisp(constReg, &blendHalf_11_4s[0]));
regCache_.Unlock(constReg, RegCache::GEN_CONST_BASE);
}
// Add in the half bit to the factors and color values, then multiply.
// We take the high 16 bits to get a free right shift by 16.
POR(srcFactorReg, R(halfReg));
POR(argColorReg, R(halfReg));
PMULHUW(argColorReg, R(srcFactorReg));
if (multiplySrc) {
POR(srcFactorReg, R(halfReg));
POR(argColorReg, R(halfReg));
PMULHUW(argColorReg, R(srcFactorReg));
} else if (id.AlphaBlendSrc() == PixelBlendFactor::ZERO) {
PXOR(argColorReg, R(argColorReg));
} else if (id.AlphaBlendSrc() == PixelBlendFactor::ONE) {
if (blendState.srcColorAsFactor)
PSRLW(argColorReg, 4);
}
POR(dstFactorReg, R(halfReg));
POR(dstReg, R(halfReg));
PMULHUW(dstReg, R(dstFactorReg));
if (multiplyDst) {
POR(dstFactorReg, R(halfReg));
POR(dstReg, R(halfReg));
PMULHUW(dstReg, R(dstFactorReg));
} else if (id.AlphaBlendDst() == PixelBlendFactor::ZERO) {
PXOR(dstReg, R(dstReg));
} else if (id.AlphaBlendDst() == PixelBlendFactor::ONE) {
if (blendState.dstColorAsFactor)
PSRLW(dstReg, 4);
}
regCache_.Release(srcFactorReg, RegCache::VEC_TEMP1);
regCache_.Release(dstFactorReg, RegCache::VEC_TEMP2);
regCache_.Release(halfReg, RegCache::VEC_TEMP3);
if (halfReg != INVALID_REG)
regCache_.Release(halfReg, RegCache::VEC_TEMP3);
} else if (colorIs16Bit_) {
// If it's expanded, shrink and clamp for our min/max/absdiff handling.
PACKUSWB(argColorReg, R(argColorReg));
@ -1165,7 +1190,7 @@ bool PixelJitCache::Jit_AlphaBlend(const PixelFuncID &id) {
}
bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorReg, RegCache::Reg dstReg, GEBlendSrcFactor factor) {
bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorReg, RegCache::Reg dstReg, PixelBlendFactor factor) {
X64Reg constReg = INVALID_REG;
X64Reg gstateReg = INVALID_REG;
X64Reg tempReg = INVALID_REG;
@ -1178,21 +1203,21 @@ bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorR
// In those cases, it uses SRCCOLOR, INVSRCCOLOR, and FIXB respectively.
switch (factor) {
case GE_SRCBLEND_DSTCOLOR:
case PixelBlendFactor::OTHERCOLOR:
MOVDQA(factorReg, R(dstReg));
break;
case GE_SRCBLEND_INVDSTCOLOR:
case PixelBlendFactor::INVOTHERCOLOR:
constReg = GetConstBase();
MOVDQA(factorReg, MConstDisp(constReg, &blendInvert_11_4s[0]));
PSUBUSW(factorReg, R(dstReg));
break;
case GE_SRCBLEND_SRCALPHA:
case PixelBlendFactor::SRCALPHA:
PSHUFLW(factorReg, R(argColorReg), _MM_SHUFFLE(3, 3, 3, 3));
break;
case GE_SRCBLEND_INVSRCALPHA:
case PixelBlendFactor::INVSRCALPHA:
constReg = GetConstBase();
tempReg = regCache_.Alloc(RegCache::VEC_TEMP3);
@ -1201,11 +1226,11 @@ bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorR
PSUBUSW(factorReg, R(tempReg));
break;
case GE_SRCBLEND_DSTALPHA:
case PixelBlendFactor::DSTALPHA:
PSHUFLW(factorReg, R(dstReg), _MM_SHUFFLE(3, 3, 3, 3));
break;
case GE_SRCBLEND_INVDSTALPHA:
case PixelBlendFactor::INVDSTALPHA:
constReg = GetConstBase();
tempReg = regCache_.Alloc(RegCache::VEC_TEMP3);
@ -1214,12 +1239,12 @@ bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorR
PSUBUSW(factorReg, R(tempReg));
break;
case GE_SRCBLEND_DOUBLESRCALPHA:
case PixelBlendFactor::DOUBLESRCALPHA:
PSHUFLW(factorReg, R(argColorReg), _MM_SHUFFLE(3, 3, 3, 3));
PSLLW(factorReg, 1);
break;
case GE_SRCBLEND_DOUBLEINVSRCALPHA:
case PixelBlendFactor::DOUBLEINVSRCALPHA:
constReg = GetConstBase();
tempReg = regCache_.Alloc(RegCache::VEC_TEMP3);
@ -1229,12 +1254,12 @@ bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorR
PSUBUSW(factorReg, R(tempReg));
break;
case GE_SRCBLEND_DOUBLEDSTALPHA:
case PixelBlendFactor::DOUBLEDSTALPHA:
PSHUFLW(factorReg, R(dstReg), _MM_SHUFFLE(3, 3, 3, 3));
PSLLW(factorReg, 1);
break;
case GE_SRCBLEND_DOUBLEINVDSTALPHA:
case PixelBlendFactor::DOUBLEINVDSTALPHA:
constReg = GetConstBase();
tempReg = regCache_.Alloc(RegCache::VEC_TEMP3);
@ -1244,7 +1269,19 @@ bool PixelJitCache::Jit_BlendFactor(const PixelFuncID &id, RegCache::Reg factorR
PSUBUSW(factorReg, R(tempReg));
break;
case GE_SRCBLEND_FIXA:
case PixelBlendFactor::ZERO:
// Special value meaning zero.
PXOR(factorReg, R(factorReg));
break;
case PixelBlendFactor::ONE:
// Special value meaning all 255s.
PCMPEQD(factorReg, R(factorReg));
PSLLW(factorReg, 8);
PSRLW(factorReg, 4);
break;
case PixelBlendFactor::FIX:
default:
gstateReg = GetGState();
if (cpu_info.bSSE4_1) {
@ -1285,37 +1322,39 @@ bool PixelJitCache::Jit_DstBlendFactor(const PixelFuncID &id, RegCache::Reg srcF
// We might be able to reuse srcFactorReg for dst, in some cases.
switch (id.AlphaBlendDst()) {
case GE_DSTBLEND_SRCCOLOR:
case PixelBlendFactor::OTHERCOLOR:
MOVDQA(dstFactorReg, R(argColorReg));
break;
case GE_DSTBLEND_INVSRCCOLOR:
case PixelBlendFactor::INVOTHERCOLOR:
constReg = GetConstBase();
MOVDQA(dstFactorReg, MConstDisp(constReg, &blendInvert_11_4s[0]));
PSUBUSW(dstFactorReg, R(argColorReg));
break;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
case GE_DSTBLEND_DSTALPHA:
case GE_DSTBLEND_INVDSTALPHA:
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
case PixelBlendFactor::SRCALPHA:
case PixelBlendFactor::INVSRCALPHA:
case PixelBlendFactor::DSTALPHA:
case PixelBlendFactor::INVDSTALPHA:
case PixelBlendFactor::DOUBLESRCALPHA:
case PixelBlendFactor::DOUBLEINVSRCALPHA:
case PixelBlendFactor::DOUBLEDSTALPHA:
case PixelBlendFactor::DOUBLEINVDSTALPHA:
case PixelBlendFactor::ZERO:
case PixelBlendFactor::ONE:
// These are all equivalent for src factor, so reuse that logic.
if (id.AlphaBlendSrc() == GEBlendSrcFactor(id.AlphaBlendDst())) {
if (id.AlphaBlendSrc() == id.AlphaBlendDst()) {
MOVDQA(dstFactorReg, R(srcFactorReg));
} else if (blendState.dstFactorIsInverse) {
constReg = GetConstBase();
MOVDQA(dstFactorReg, MConstDisp(constReg, &blendInvert_11_4s[0]));
PSUBUSW(dstFactorReg, R(srcFactorReg));
} else {
success = success && Jit_BlendFactor(id, dstFactorReg, dstReg, GEBlendSrcFactor(id.AlphaBlendDst()));
success = success && Jit_BlendFactor(id, dstFactorReg, dstReg, id.AlphaBlendDst());
}
break;
case GE_DSTBLEND_FIXB:
case PixelBlendFactor::FIX:
default:
gstateReg = GetGState();
if (cpu_info.bSSE4_1) {

View file

@ -42,6 +42,14 @@ static inline GEComparison OptimizeRefByteCompare(GEComparison func, u8 ref) {
return func;
}
static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) {
if (color == 0x00000000)
return PixelBlendFactor::ZERO;
if (color == 0x00FFFFFF)
return PixelBlendFactor::ONE;
return PixelBlendFactor::FIX;
}
void ComputePixelFuncID(PixelFuncID *id) {
id->fullKey = 0;
@ -143,10 +151,16 @@ void ComputePixelFuncID(PixelFuncID *id) {
if (srcFixedOne && dstFixedZero)
id->alphaBlend = false;
}
if (id->alphaBlend) {
if (id->alphaBlend)
id->alphaBlendEq = gstate.getBlendEq();
if (id->alphaBlend && id->alphaBlendEq <= GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE) {
id->alphaBlendSrc = gstate.getBlendFuncA();
id->alphaBlendDst = gstate.getBlendFuncB();
// Special values.
if (id->alphaBlendSrc == GE_SRCBLEND_FIXA)
id->alphaBlendSrc = (uint8_t)OptimizeAlphaFactor(gstate.getFixA());
if (id->alphaBlendDst == GE_DSTBLEND_FIXB)
id->alphaBlendDst = (uint8_t)OptimizeAlphaFactor(gstate.getFixB());
}
id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY;
@ -301,30 +315,34 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
case GE_BLENDMODE_ABSDIFF: desc += "BlendDiff<"; break;
}
switch (id.AlphaBlendSrc()) {
case GE_SRCBLEND_DSTCOLOR: desc += "DstRGB,"; break;
case GE_SRCBLEND_INVDSTCOLOR: desc += "1-DstRGB,"; break;
case GE_SRCBLEND_SRCALPHA: desc += "SrcA,"; break;
case GE_SRCBLEND_INVSRCALPHA: desc += "1-SrcA,"; break;
case GE_SRCBLEND_DSTALPHA: desc += "DstA,"; break;
case GE_SRCBLEND_INVDSTALPHA: desc += "1-DstA,"; break;
case GE_SRCBLEND_DOUBLESRCALPHA: desc += "2*SrcA,"; break;
case GE_SRCBLEND_DOUBLEINVSRCALPHA: desc += "1-2*SrcA,"; break;
case GE_SRCBLEND_DOUBLEDSTALPHA: desc += "2*DstA,"; break;
case GE_SRCBLEND_DOUBLEINVDSTALPHA: desc += "1-2*DstA,"; break;
case GE_SRCBLEND_FIXA: desc += "Fix,"; break;
case PixelBlendFactor::OTHERCOLOR: desc += "DstRGB,"; break;
case PixelBlendFactor::INVOTHERCOLOR: desc += "1-DstRGB,"; break;
case PixelBlendFactor::SRCALPHA: desc += "SrcA,"; break;
case PixelBlendFactor::INVSRCALPHA: desc += "1-SrcA,"; break;
case PixelBlendFactor::DSTALPHA: desc += "DstA,"; break;
case PixelBlendFactor::INVDSTALPHA: desc += "1-DstA,"; break;
case PixelBlendFactor::DOUBLESRCALPHA: desc += "2*SrcA,"; break;
case PixelBlendFactor::DOUBLEINVSRCALPHA: desc += "1-2*SrcA,"; break;
case PixelBlendFactor::DOUBLEDSTALPHA: desc += "2*DstA,"; break;
case PixelBlendFactor::DOUBLEINVDSTALPHA: desc += "1-2*DstA,"; break;
case PixelBlendFactor::FIX: desc += "Fix,"; break;
case PixelBlendFactor::ZERO: desc += "0,"; break;
case PixelBlendFactor::ONE: desc += "1,"; break;
}
switch (id.AlphaBlendDst()) {
case GE_DSTBLEND_SRCCOLOR: desc += "SrcRGB>:"; break;
case GE_DSTBLEND_INVSRCCOLOR: desc += "1-SrcRGB>:"; break;
case GE_DSTBLEND_SRCALPHA: desc += "SrcA>:"; break;
case GE_DSTBLEND_INVSRCALPHA: desc += "1-SrcA>:"; break;
case GE_DSTBLEND_DSTALPHA: desc += "DstA>:"; break;
case GE_DSTBLEND_INVDSTALPHA: desc += "1-DstA>:"; break;
case GE_DSTBLEND_DOUBLESRCALPHA: desc += "2*SrcA>:"; break;
case GE_DSTBLEND_DOUBLEINVSRCALPHA: desc += "1-2*SrcA>:"; break;
case GE_DSTBLEND_DOUBLEDSTALPHA: desc += "2*DstA>:"; break;
case GE_DSTBLEND_DOUBLEINVDSTALPHA: desc += "1-2*DstA>:"; break;
case GE_DSTBLEND_FIXB: desc += "Fix>:"; break;
case PixelBlendFactor::OTHERCOLOR: desc += "SrcRGB>:"; break;
case PixelBlendFactor::INVOTHERCOLOR: desc += "1-SrcRGB>:"; break;
case PixelBlendFactor::SRCALPHA: desc += "SrcA>:"; break;
case PixelBlendFactor::INVSRCALPHA: desc += "1-SrcA>:"; break;
case PixelBlendFactor::DSTALPHA: desc += "DstA>:"; break;
case PixelBlendFactor::INVDSTALPHA: desc += "1-DstA>:"; break;
case PixelBlendFactor::DOUBLESRCALPHA: desc += "2*SrcA>:"; break;
case PixelBlendFactor::DOUBLEINVSRCALPHA: desc += "1-2*SrcA>:"; break;
case PixelBlendFactor::DOUBLEDSTALPHA: desc += "2*DstA>:"; break;
case PixelBlendFactor::DOUBLEINVDSTALPHA: desc += "1-2*DstA>:"; break;
case PixelBlendFactor::FIX: desc += "Fix>:"; break;
case PixelBlendFactor::ZERO: desc += "0>:"; break;
case PixelBlendFactor::ONE: desc += "1>:"; break;
}
}

View file

@ -25,6 +25,24 @@
#define SOFTPIXEL_USE_CACHE 1
// 0-10 match GEBlendSrcFactor/GEBlendDstFactor.
enum class PixelBlendFactor {
OTHERCOLOR,
INVOTHERCOLOR,
SRCALPHA,
INVSRCALPHA,
DSTALPHA,
INVDSTALPHA,
DOUBLESRCALPHA,
DOUBLEINVSRCALPHA,
DOUBLEDSTALPHA,
DOUBLEINVDSTALPHA,
FIX,
// These are invented, but common FIX values.
ZERO,
ONE,
};
#pragma pack(push, 1)
struct PixelFuncID {
@ -110,11 +128,11 @@ struct PixelFuncID {
GEBlendMode AlphaBlendEq() const {
return GEBlendMode(alphaBlendEq);
}
GEBlendSrcFactor AlphaBlendSrc() const {
return GEBlendSrcFactor(alphaBlendSrc);
PixelBlendFactor AlphaBlendSrc() const {
return PixelBlendFactor(alphaBlendSrc);
}
GEBlendDstFactor AlphaBlendDst() const {
return GEBlendDstFactor(alphaBlendDst);
PixelBlendFactor AlphaBlendDst() const {
return PixelBlendFactor(alphaBlendDst);
}
GEStencilOp SFail() const {

View file

@ -379,8 +379,8 @@ static inline Vec3<int> GetDestFactor(GEBlendDstFactor factor, const Vec4<int> &
Vec3<int> AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4<int> &source, const Vec4<int> &dst)
{
// Note: These factors cannot go below 0, but they can go above 255 when doubling.
Vec3<int> srcfactor = GetSourceFactor(pixelID.AlphaBlendSrc(), source, dst);
Vec3<int> dstfactor = GetDestFactor(pixelID.AlphaBlendDst(), source, dst);
Vec3<int> srcfactor = GetSourceFactor(GEBlendSrcFactor(pixelID.AlphaBlendSrc()), source, dst);
Vec3<int> dstfactor = GetDestFactor(GEBlendDstFactor(pixelID.AlphaBlendDst()), source, dst);
switch (pixelID.AlphaBlendEq()) {
case GE_BLENDMODE_MUL_AND_ADD: