mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Add option to redirect blue to alpha if 565 mode is rendered and mask is 0x0FFFFF.
This is used by several games to render to the alpha channel of RGBA4444 images, which cannot normally be done directly on the PSP. Can be used as a far more efficient replacement for ReinterpretFramebuffers/ShaderColorBitmask
This commit is contained in:
parent
7be86264d0
commit
462972f7ea
@ -79,6 +79,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "DisableFirstFrameReadback", &flags_.DisableFirstFrameReadback);
|
||||
CheckSetting(iniFile, gameID, "DisableRangeCulling", &flags_.DisableRangeCulling);
|
||||
CheckSetting(iniFile, gameID, "MpegAvcWarmUp", &flags_.MpegAvcWarmUp);
|
||||
CheckSetting(iniFile, gameID, "BlueToAlpha", &flags_.BlueToAlpha);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
|
@ -44,6 +44,7 @@
|
||||
//
|
||||
// We already have the Action Replay-based cheat system for such use cases.
|
||||
|
||||
// TODO: Turn into bitfield for smaller mem footprint. Though I think it still fits in a cacheline...
|
||||
struct CompatFlags {
|
||||
bool VertexDepthRounding;
|
||||
bool PixelDepthRounding;
|
||||
@ -77,6 +78,7 @@ struct CompatFlags {
|
||||
bool DisableFirstFrameReadback;
|
||||
bool DisableRangeCulling;
|
||||
bool MpegAvcWarmUp;
|
||||
bool BlueToAlpha;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
@ -95,6 +95,11 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
|
||||
ReplaceBlendType replaceBlend = static_cast<ReplaceBlendType>(id.Bits(FS_BIT_REPLACE_BLEND, 3));
|
||||
|
||||
bool blueToAlpha = false;
|
||||
if (replaceBlend == ReplaceBlendType::REPLACE_BLEND_BLUE_TO_ALPHA) {
|
||||
blueToAlpha = true;
|
||||
}
|
||||
|
||||
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
|
||||
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
|
||||
GEBlendMode replaceBlendEq = (GEBlendMode)id.Bits(FS_BIT_BLENDEQ, 3);
|
||||
@ -1025,6 +1030,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
WRITE(p, " %s = unpackUnorm4x8(v32);\n", compat.fragColor0);
|
||||
}
|
||||
|
||||
if (blueToAlpha) {
|
||||
WRITE(p, " %s = vec4(0.0, 0.0, 0.0, %s.z); // blue to alpha\n", compat.fragColor0, compat.fragColor0);
|
||||
}
|
||||
|
||||
if (gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
|
||||
const double scale = DepthSliceFactor() * 65535.0;
|
||||
|
||||
|
@ -1642,8 +1642,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
|
||||
|
||||
// Create a new fbo if none was found for the size
|
||||
if (!nvfb) {
|
||||
nvfb = new VirtualFramebuffer();
|
||||
memset(nvfb, 0, sizeof(VirtualFramebuffer));
|
||||
nvfb = new VirtualFramebuffer{};
|
||||
nvfb->fbo = nullptr;
|
||||
nvfb->fb_address = vfb->fb_address;
|
||||
nvfb->fb_stride = vfb->fb_stride;
|
||||
|
@ -100,6 +100,8 @@ struct VirtualFramebuffer {
|
||||
bool dirtyAfterDisplay;
|
||||
bool reallyDirtyAfterDisplay; // takes frame skipping into account
|
||||
|
||||
bool blueToAlphaUsed;
|
||||
|
||||
int last_frame_used;
|
||||
int last_frame_attached;
|
||||
int last_frame_render;
|
||||
|
@ -191,6 +191,10 @@ ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceBlend == ReplaceBlendType::REPLACE_BLEND_BLUE_TO_ALPHA) {
|
||||
return REPLACE_ALPHA_NO; // irrelevant
|
||||
}
|
||||
|
||||
return REPLACE_ALPHA_YES;
|
||||
}
|
||||
|
||||
@ -254,6 +258,10 @@ StencilValueType ReplaceAlphaWithStencilType() {
|
||||
}
|
||||
|
||||
ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferFormat bufferFormat) {
|
||||
if (gstate_c.blueToAlpha) {
|
||||
return REPLACE_BLEND_BLUE_TO_ALPHA;
|
||||
}
|
||||
|
||||
if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
|
||||
return REPLACE_BLEND_NO;
|
||||
}
|
||||
@ -976,6 +984,11 @@ bool IsColorWriteMaskComplex(bool allowFramebufferRead) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gstate_c.blueToAlpha) {
|
||||
// We'll generate a simple ___A mask.
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t colorMask = (gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@ -996,6 +1009,15 @@ bool IsColorWriteMaskComplex(bool allowFramebufferRead) {
|
||||
// When that's not enough, we fall back on a technique similar to shader blending,
|
||||
// we read from the framebuffer (or a copy of it).
|
||||
void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) {
|
||||
if (gstate_c.blueToAlpha) {
|
||||
maskState.applyFramebufferRead = false;
|
||||
maskState.rgba[0] = false;
|
||||
maskState.rgba[1] = false;
|
||||
maskState.rgba[2] = false;
|
||||
maskState.rgba[3] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Invert to convert masks from the PSP's format where 1 is don't draw to PC where 1 is draw.
|
||||
uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24));
|
||||
|
||||
@ -1056,6 +1078,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
|
||||
ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend);
|
||||
bool usePreSrc = false;
|
||||
|
||||
bool blueToAlpha = false;
|
||||
|
||||
switch (replaceBlend) {
|
||||
case REPLACE_BLEND_NO:
|
||||
blendState.resetFramebufferRead = true;
|
||||
@ -1063,6 +1087,10 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
|
||||
ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState);
|
||||
return;
|
||||
|
||||
case REPLACE_BLEND_BLUE_TO_ALPHA:
|
||||
blueToAlpha = true;
|
||||
break;
|
||||
|
||||
case REPLACE_BLEND_COPY_FBO:
|
||||
blendState.applyFramebufferRead = true;
|
||||
blendState.resetFramebufferRead = false;
|
||||
@ -1303,6 +1331,10 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
|
||||
alphaEq = BlendEq::REVERSE_SUBTRACT;
|
||||
break;
|
||||
}
|
||||
} else if (blueToAlpha) {
|
||||
blendState.setFactors(BlendFactor::ZERO, BlendFactor::ZERO, glBlendFuncA, glBlendFuncB);
|
||||
blendState.setEquation(BlendEq::ADD, colorEq);
|
||||
return;
|
||||
} else {
|
||||
// Retain the existing value when stencil testing is off.
|
||||
blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE);
|
||||
|
@ -39,6 +39,9 @@ enum ReplaceBlendType {
|
||||
// Full blend equation runs in shader.
|
||||
// We might have to make a copy of the framebuffer target to read from.
|
||||
REPLACE_BLEND_COPY_FBO,
|
||||
|
||||
// Color blend mode and color gets copied to alpha blend mode.
|
||||
REPLACE_BLEND_BLUE_TO_ALPHA,
|
||||
};
|
||||
|
||||
enum LogicOpReplaceType {
|
||||
|
@ -186,8 +186,19 @@ std::string FragmentShaderDesc(const FShaderID &id) {
|
||||
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) << "A:" << id.Bits(FS_BIT_BLENDFUNC_A, 4) << "_B:" << id.Bits(FS_BIT_BLENDFUNC_B, 4) << "_Eq:" << id.Bits(FS_BIT_BLENDEQ, 3) << " ";
|
||||
int blendBits = id.Bits(FS_BIT_REPLACE_BLEND, 3);
|
||||
if (blendBits) {
|
||||
switch (blendBits) {
|
||||
case ReplaceBlendType::REPLACE_BLEND_BLUE_TO_ALPHA:
|
||||
desc << "BlueToAlpha";
|
||||
break;
|
||||
default:
|
||||
desc << "ReplaceBlend_" << id.Bits(FS_BIT_REPLACE_BLEND, 3)
|
||||
<< "A:" << id.Bits(FS_BIT_BLENDFUNC_A, 4)
|
||||
<< "_B:" << id.Bits(FS_BIT_BLENDFUNC_B, 4)
|
||||
<< "_Eq:" << id.Bits(FS_BIT_BLENDEQ, 3) << " ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)) {
|
||||
@ -312,7 +323,9 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) {
|
||||
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) {
|
||||
if (replaceBlend == REPLACE_BLEND_BLUE_TO_ALPHA) {
|
||||
id.SetBits(FS_BIT_REPLACE_BLEND, 3, replaceBlend);
|
||||
} else if (replaceBlend > REPLACE_BLEND_STANDARD) {
|
||||
// 3 bits.
|
||||
id.SetBits(FS_BIT_REPLACE_BLEND, 3, replaceBlend);
|
||||
// 11 bits total.
|
||||
|
@ -915,7 +915,7 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(
|
||||
}
|
||||
// NOTE: This check is okay because the first texture formats are the same as the buffer formats.
|
||||
if (IsTextureFormatBufferCompatible(entry.format)) {
|
||||
if (TextureFormatMatchesBufferFormat(entry.format, framebuffer->format)) {
|
||||
if (TextureFormatMatchesBufferFormat(entry.format, framebuffer->format) || framebuffer->blueToAlphaUsed) {
|
||||
return FramebufferMatchInfo{ FramebufferMatch::VALID };
|
||||
} else if (IsTextureFormat16Bit(entry.format) && IsBufferFormat16Bit(framebuffer->format)) {
|
||||
WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with reinterpretable format: %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format));
|
||||
|
@ -1637,8 +1637,21 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
|
||||
// We store it in the cache so it can be modified for blue-to-alpha, next.
|
||||
gstate_c.framebufFormat = gstate.FrameBufFormat();
|
||||
|
||||
// See the documentation for gstate_c.blueToAlpha.
|
||||
bool blueToAlpha = false;
|
||||
if (gstate_c.framebufFormat == GEBufferFormat::GE_FORMAT_565 && gstate.getColorMask() == 0x0FFFFF && PSP_CoreParameter().compat.flags().BlueToAlpha) {
|
||||
blueToAlpha = true;
|
||||
}
|
||||
if (blueToAlpha != gstate_c.blueToAlpha) {
|
||||
gstate_c.blueToAlpha = blueToAlpha;
|
||||
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE | DIRTY_BLEND_STATE);
|
||||
}
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (blueToAlpha) {
|
||||
vfb->blueToAlphaUsed = true;
|
||||
}
|
||||
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
// Rough estimate, not sure what's correct.
|
||||
|
@ -594,6 +594,11 @@ struct GPUStateCache {
|
||||
KnownVertexBounds vertBounds;
|
||||
|
||||
GEBufferFormat framebufFormat;
|
||||
// Some games use a very specific masking setup to draw into the alpha channel of a 4444 target using the blue channel of a 565 target.
|
||||
// This is done because on PSP you can't write to destination alpha, other than stencil values, which can't be set from a texture.
|
||||
// Examples of games that do this: Outrun, Split/Second.
|
||||
// We detect this case and go into a special drawing mode.
|
||||
bool blueToAlpha;
|
||||
|
||||
// TODO: These should be accessed from the current VFB object directly.
|
||||
u32 curRTWidth;
|
||||
|
@ -990,6 +990,16 @@ ULES01367 = true
|
||||
NPEH00029 = true
|
||||
ULUS10455 = true
|
||||
|
||||
[BlueToAlpha]
|
||||
ULES01402 = true
|
||||
ULUS10513 = true
|
||||
ULJM05812 = true
|
||||
NPJH50371 = true
|
||||
|
||||
# Some games render first to RGB of a 4444 texture, then they switch to 565 and render masked to blue,
|
||||
# just to be able to render to the alpha channel of the 4444. We can detect that and reroute rendering
|
||||
# to avoid problems.
|
||||
|
||||
[DateLimited]
|
||||
# Car Jack Streets - issue #12698
|
||||
NPUZ00043 = true
|
||||
@ -1025,11 +1035,11 @@ ULES01441 = true
|
||||
ULJM05600 = true
|
||||
ULJM05775 = true
|
||||
|
||||
# Split/Second
|
||||
ULES01402 = true
|
||||
ULUS10513 = true
|
||||
ULJM05812 = true
|
||||
NPJH50371 = true
|
||||
# Split/Second now uses BlueToAlpha instead.
|
||||
# ULES01402 = true
|
||||
# ULUS10513 = true
|
||||
# ULJM05812 = true
|
||||
# NPJH50371 = true
|
||||
|
||||
[ShaderColorBitmask]
|
||||
# Outrun 2006: Coast to Coast - issue #11358
|
||||
@ -1043,11 +1053,11 @@ ULJM05533 = true
|
||||
NPJH50006 = true
|
||||
ULES01301 = true
|
||||
|
||||
# Split/Second
|
||||
ULES01402 = true
|
||||
ULUS10513 = true
|
||||
ULJM05812 = true
|
||||
NPJH50371 = true
|
||||
# Split/Second now uses BlueToAlpha instead.
|
||||
#ULES01402 = true
|
||||
#ULUS10513 = true
|
||||
#ULJM05812 = true
|
||||
#NPJH50371 = true
|
||||
|
||||
[DisableFirstFrameReadback]
|
||||
# Wipeout Pure: Temporary workaround for lens flare flicker. See #13344
|
||||
|
Loading…
Reference in New Issue
Block a user