2015-10-24 21:49:05 +00:00
|
|
|
#pragma once
|
|
|
|
|
2020-11-08 22:17:06 +00:00
|
|
|
#include <cstdint>
|
2017-02-24 19:26:38 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
|
2015-11-04 08:35:55 +00:00
|
|
|
#include "GPU/ge_constants.h"
|
2022-08-28 05:11:00 +00:00
|
|
|
#include "GPU/GPUState.h"
|
2015-11-04 08:35:55 +00:00
|
|
|
|
2016-12-26 22:51:17 +00:00
|
|
|
// TODO: Replace enums and structs with same from thin3d.h, for convenient mapping.
|
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
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,
|
2015-03-16 02:25:34 +00:00
|
|
|
};
|
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
enum ReplaceAlphaType {
|
|
|
|
REPLACE_ALPHA_NO = 0,
|
|
|
|
REPLACE_ALPHA_YES = 1,
|
|
|
|
REPLACE_ALPHA_DUALSOURCE = 2,
|
|
|
|
};
|
2015-03-16 02:25:34 +00:00
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
enum ReplaceBlendType {
|
2020-11-08 22:17:06 +00:00
|
|
|
REPLACE_BLEND_NO, // Blend function handled directly with blend states.
|
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
REPLACE_BLEND_STANDARD,
|
2020-11-08 22:17:06 +00:00
|
|
|
|
|
|
|
// SRC part of blend function handled in-shader.
|
2015-10-24 21:49:05 +00:00
|
|
|
REPLACE_BLEND_PRE_SRC,
|
|
|
|
REPLACE_BLEND_PRE_SRC_2X_ALPHA,
|
|
|
|
REPLACE_BLEND_2X_ALPHA,
|
|
|
|
REPLACE_BLEND_2X_SRC,
|
2020-11-08 22:17:06 +00:00
|
|
|
|
|
|
|
// Full blend equation runs in shader.
|
|
|
|
// We might have to make a copy of the framebuffer target to read from.
|
2022-09-02 20:20:11 +00:00
|
|
|
REPLACE_BLEND_READ_FRAMEBUFFER,
|
2022-04-24 18:53:09 +00:00
|
|
|
|
|
|
|
// Color blend mode and color gets copied to alpha blend mode.
|
|
|
|
REPLACE_BLEND_BLUE_TO_ALPHA,
|
2015-10-24 21:49:05 +00:00
|
|
|
};
|
2015-03-16 02:25:34 +00:00
|
|
|
|
2022-09-03 21:28:03 +00:00
|
|
|
enum SimulateLogicOpType {
|
2015-10-24 21:49:05 +00:00
|
|
|
LOGICOPTYPE_NORMAL,
|
|
|
|
LOGICOPTYPE_ONE,
|
|
|
|
LOGICOPTYPE_INVERT,
|
|
|
|
};
|
2015-03-16 02:25:34 +00:00
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
bool IsAlphaTestTriviallyTrue();
|
|
|
|
bool IsColorTestAgainstZero();
|
|
|
|
bool IsColorTestTriviallyTrue();
|
|
|
|
bool IsAlphaTestAgainstZero();
|
2018-07-28 03:04:36 +00:00
|
|
|
bool NeedsTestDiscard();
|
2022-11-08 04:40:14 +00:00
|
|
|
bool IsDepthTestEffectivelyDisabled();
|
2018-12-01 22:05:29 +00:00
|
|
|
bool IsStencilTestOutputDisabled();
|
2015-03-16 02:25:34 +00:00
|
|
|
|
2015-10-24 21:49:05 +00:00
|
|
|
StencilValueType ReplaceAlphaWithStencilType();
|
|
|
|
ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
|
2022-09-02 23:22:32 +00:00
|
|
|
ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat);
|
2015-03-16 02:25:34 +00:00
|
|
|
|
2022-09-03 21:28:03 +00:00
|
|
|
// This is for the fallback path if real logic ops are not available.
|
|
|
|
SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded();
|
2015-11-08 17:13:34 +00:00
|
|
|
|
|
|
|
// Common representation, should be able to set this directly with any modern API.
|
|
|
|
struct ViewportAndScissor {
|
|
|
|
int scissorX;
|
|
|
|
int scissorY;
|
|
|
|
int scissorW;
|
|
|
|
int scissorH;
|
|
|
|
float viewportX;
|
|
|
|
float viewportY;
|
|
|
|
float viewportW;
|
|
|
|
float viewportH;
|
|
|
|
float depthRangeMin;
|
|
|
|
float depthRangeMax;
|
2022-08-20 21:16:55 +00:00
|
|
|
float widthScale;
|
|
|
|
float heightScale;
|
|
|
|
float depthScale;
|
|
|
|
float xOffset;
|
|
|
|
float yOffset;
|
|
|
|
float zOffset;
|
|
|
|
bool throughMode;
|
2015-11-08 17:13:34 +00:00
|
|
|
};
|
2023-02-10 13:07:45 +00:00
|
|
|
|
2015-11-08 17:13:34 +00:00
|
|
|
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out);
|
2022-08-20 21:16:55 +00:00
|
|
|
void UpdateCachedViewportState(const ViewportAndScissor &vpAndScissor);
|
2020-08-07 09:19:48 +00:00
|
|
|
|
2023-02-11 00:11:02 +00:00
|
|
|
// NOTE: See the .cpp file for detailed comment about how the use flags are interpreted.
|
2023-02-10 13:07:45 +00:00
|
|
|
class DepthScaleFactors {
|
|
|
|
public:
|
|
|
|
// This should only be used from GetDepthScaleFactors.
|
2023-02-10 13:34:33 +00:00
|
|
|
DepthScaleFactors(double offset, double scale) : offset_(offset), scale_(scale) {}
|
2020-08-07 09:19:48 +00:00
|
|
|
|
2023-02-10 13:07:45 +00:00
|
|
|
// Decodes a value from a depth buffer to a value of range 0..65536
|
|
|
|
float DecodeToU16(float z) const {
|
2023-02-11 00:11:02 +00:00
|
|
|
return (float)((z - offset_) * scale_);
|
2020-08-07 09:19:48 +00:00
|
|
|
}
|
2022-08-01 09:57:22 +00:00
|
|
|
|
2023-02-10 13:07:45 +00:00
|
|
|
// Encodes a value from the range 0..65536 to a normalized depth value (0-1), in the
|
|
|
|
// range that we write to the depth buffer.
|
|
|
|
float EncodeFromU16(float z_u16) const {
|
2023-02-11 00:11:02 +00:00
|
|
|
return (float)(((double)z_u16 / scale_) + offset_);
|
2022-08-01 09:57:22 +00:00
|
|
|
}
|
2023-02-10 13:07:45 +00:00
|
|
|
|
2023-02-10 13:34:33 +00:00
|
|
|
float Offset() const { return (float)offset_; }
|
|
|
|
float ScaleU16() const { return (float)scale_; }
|
2023-02-10 13:07:45 +00:00
|
|
|
// float Scale() const { return scale_ / 65535.0f; }
|
|
|
|
|
|
|
|
private:
|
2023-02-11 00:11:02 +00:00
|
|
|
// Doubles hardly cost anything these days, and precision matters here.
|
2023-02-10 13:34:33 +00:00
|
|
|
double offset_;
|
|
|
|
double scale_;
|
2020-08-07 09:19:48 +00:00
|
|
|
};
|
|
|
|
|
2023-02-10 13:07:45 +00:00
|
|
|
DepthScaleFactors GetDepthScaleFactors(u32 useFlags);
|
2023-02-11 00:11:02 +00:00
|
|
|
|
2023-02-11 10:45:27 +00:00
|
|
|
// This will be replaced with just DepthScaleFactors.
|
2023-02-10 13:07:45 +00:00
|
|
|
float DepthSliceFactor(u32 useFlags);
|
2015-11-08 19:09:30 +00:00
|
|
|
|
|
|
|
// These are common to all modern APIs and can be easily converted with a lookup table.
|
|
|
|
enum class BlendFactor : uint8_t {
|
|
|
|
ZERO,
|
|
|
|
ONE,
|
|
|
|
SRC_COLOR,
|
|
|
|
ONE_MINUS_SRC_COLOR,
|
|
|
|
DST_COLOR,
|
|
|
|
ONE_MINUS_DST_COLOR,
|
|
|
|
SRC_ALPHA,
|
|
|
|
ONE_MINUS_SRC_ALPHA,
|
|
|
|
DST_ALPHA,
|
|
|
|
ONE_MINUS_DST_ALPHA,
|
|
|
|
CONSTANT_COLOR,
|
|
|
|
ONE_MINUS_CONSTANT_COLOR,
|
|
|
|
CONSTANT_ALPHA,
|
|
|
|
ONE_MINUS_CONSTANT_ALPHA,
|
2016-12-26 22:51:17 +00:00
|
|
|
SRC1_COLOR,
|
|
|
|
ONE_MINUS_SRC1_COLOR,
|
2015-11-08 19:09:30 +00:00
|
|
|
SRC1_ALPHA,
|
|
|
|
ONE_MINUS_SRC1_ALPHA,
|
|
|
|
INVALID,
|
|
|
|
COUNT,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class BlendEq : uint8_t {
|
|
|
|
ADD,
|
|
|
|
SUBTRACT,
|
|
|
|
REVERSE_SUBTRACT,
|
|
|
|
MIN,
|
|
|
|
MAX,
|
|
|
|
COUNT
|
|
|
|
};
|
|
|
|
|
2022-09-02 19:07:29 +00:00
|
|
|
// Computed blend setup, including shader stuff.
|
2015-11-08 19:09:30 +00:00
|
|
|
struct GenericBlendState {
|
2020-11-08 11:49:06 +00:00
|
|
|
bool applyFramebufferRead;
|
|
|
|
bool dirtyShaderBlendFixValues;
|
2022-09-02 19:07:29 +00:00
|
|
|
|
|
|
|
// Shader generation state
|
2015-11-08 20:47:08 +00:00
|
|
|
ReplaceAlphaType replaceAlphaWithStencil;
|
2022-09-02 20:43:58 +00:00
|
|
|
ReplaceBlendType replaceBlend;
|
2022-09-03 21:32:01 +00:00
|
|
|
SimulateLogicOpType simulateLogicOpType;
|
2015-11-08 19:09:30 +00:00
|
|
|
|
2022-09-02 19:07:29 +00:00
|
|
|
// Resulting hardware blend state
|
|
|
|
bool blendEnabled;
|
|
|
|
|
2015-11-08 19:09:30 +00:00
|
|
|
BlendFactor srcColor;
|
|
|
|
BlendFactor dstColor;
|
|
|
|
BlendFactor srcAlpha;
|
|
|
|
BlendFactor dstAlpha;
|
|
|
|
|
|
|
|
BlendEq eqColor;
|
|
|
|
BlendEq eqAlpha;
|
|
|
|
|
2015-11-08 21:29:43 +00:00
|
|
|
bool useBlendColor;
|
|
|
|
u32 blendColor;
|
|
|
|
|
2015-11-08 19:09:30 +00:00
|
|
|
void setFactors(BlendFactor srcC, BlendFactor dstC, BlendFactor srcA, BlendFactor dstA) {
|
|
|
|
srcColor = srcC;
|
|
|
|
dstColor = dstC;
|
|
|
|
srcAlpha = srcA;
|
|
|
|
dstAlpha = dstA;
|
|
|
|
}
|
|
|
|
void setEquation(BlendEq eqC, BlendEq eqA) {
|
|
|
|
eqColor = eqC;
|
|
|
|
eqAlpha = eqA;
|
|
|
|
}
|
2015-11-08 21:29:43 +00:00
|
|
|
void setBlendColor(uint32_t color, uint8_t alpha) {
|
2015-11-08 21:50:16 +00:00
|
|
|
blendColor = color | ((uint32_t)alpha << 24);
|
2015-11-08 21:29:43 +00:00
|
|
|
useBlendColor = true;
|
|
|
|
}
|
|
|
|
void defaultBlendColor(uint8_t alpha) {
|
2015-11-08 21:50:16 +00:00
|
|
|
blendColor = 0xFFFFFF | ((uint32_t)alpha << 24);
|
2015-11-08 21:29:43 +00:00
|
|
|
useBlendColor = true;
|
|
|
|
}
|
2022-09-03 21:35:58 +00:00
|
|
|
|
|
|
|
void Log();
|
2015-11-08 19:09:30 +00:00
|
|
|
};
|
2015-11-08 21:32:48 +00:00
|
|
|
|
2020-11-08 13:34:04 +00:00
|
|
|
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState);
|
2015-12-31 09:50:18 +00:00
|
|
|
|
2020-11-08 22:17:06 +00:00
|
|
|
struct GenericMaskState {
|
|
|
|
bool applyFramebufferRead;
|
|
|
|
uint32_t uniformMask; // For each bit, opposite to the PSP.
|
2022-09-03 21:28:03 +00:00
|
|
|
|
|
|
|
// The hardware channel masks, 1 bit per color component. From bit 0, order is RGBA like in all APIs!
|
2022-09-03 20:51:50 +00:00
|
|
|
uint8_t channelMask;
|
2022-09-03 21:28:03 +00:00
|
|
|
|
|
|
|
void ConvertToShaderBlend() {
|
|
|
|
// If we have to do it in the shader, we simply pass through all channels but mask only in the shader instead.
|
|
|
|
// Some GPUs have minor penalties for masks that are not all-channels-on or all-channels-off.
|
|
|
|
channelMask = 0xF;
|
|
|
|
applyFramebufferRead = true;
|
|
|
|
}
|
2022-09-03 21:35:58 +00:00
|
|
|
|
|
|
|
void Log();
|
2020-11-08 22:17:06 +00:00
|
|
|
};
|
|
|
|
|
2015-12-31 09:50:18 +00:00
|
|
|
struct GenericStencilFuncState {
|
|
|
|
bool enabled;
|
|
|
|
GEComparison testFunc;
|
|
|
|
u8 testRef;
|
|
|
|
u8 testMask;
|
|
|
|
u8 writeMask;
|
|
|
|
GEStencilOp sFail;
|
|
|
|
GEStencilOp zFail;
|
|
|
|
GEStencilOp zPass;
|
|
|
|
};
|
|
|
|
void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState);
|
2022-08-28 05:11:00 +00:00
|
|
|
|
2022-09-03 22:00:33 +00:00
|
|
|
struct GenericLogicState {
|
|
|
|
// If set, logic op is applied in the shader INSTEAD of in hardware.
|
|
|
|
// In this case, simulateLogicOpType and all that should be off.
|
|
|
|
bool applyFramebufferRead;
|
|
|
|
|
|
|
|
// Hardware
|
|
|
|
bool logicOpEnabled;
|
|
|
|
|
|
|
|
// Hardware and shader generation
|
|
|
|
GELogicOp logicOp;
|
|
|
|
|
2022-09-14 02:07:25 +00:00
|
|
|
void ApplyToBlendState(GenericBlendState &blendState);
|
2022-09-03 22:00:33 +00:00
|
|
|
void ConvertToShaderBlend() {
|
|
|
|
if (logicOp != GE_LOGIC_COPY) {
|
|
|
|
logicOpEnabled = false;
|
|
|
|
applyFramebufferRead = true;
|
|
|
|
// Same logicOp is kept.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Log();
|
|
|
|
};
|
|
|
|
|
2022-09-02 19:07:29 +00:00
|
|
|
struct ComputedPipelineState {
|
|
|
|
GenericBlendState blendState;
|
|
|
|
GenericMaskState maskState;
|
2022-09-03 22:00:33 +00:00
|
|
|
GenericLogicState logicState;
|
2022-09-03 21:28:03 +00:00
|
|
|
|
|
|
|
void Convert(bool shaderBitOpsSupported);
|
2022-09-04 20:52:24 +00:00
|
|
|
|
|
|
|
bool FramebufferRead() const {
|
2022-09-12 10:17:32 +00:00
|
|
|
// If blending is off, its applyFramebufferRead can be false even after state propagation.
|
|
|
|
// So it's not enough to check just that one.
|
|
|
|
return blendState.applyFramebufferRead || maskState.applyFramebufferRead || logicState.applyFramebufferRead;
|
2022-09-04 20:52:24 +00:00
|
|
|
}
|
2022-09-02 19:07:29 +00:00
|
|
|
};
|
|
|
|
|
2022-08-28 05:11:00 +00:00
|
|
|
// See issue #15898
|
|
|
|
inline bool SpongebobDepthInverseConditions(const GenericStencilFuncState &stencilState) {
|
2022-08-28 15:50:16 +00:00
|
|
|
// Check that the depth/stencil state matches the conditions exactly.
|
|
|
|
// Always with a depth test that's not writing to the depth buffer (only stencil.)
|
|
|
|
if (!gstate.isDepthTestEnabled() || gstate.isDepthWriteEnabled())
|
|
|
|
return false;
|
|
|
|
// Always GREATER_EQUAL, which we flip to LESS.
|
|
|
|
if (gstate.getDepthTestFunction() != GE_COMP_GEQUAL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// The whole purpose here is a depth fail that we need to write to alpha.
|
|
|
|
if (stencilState.zFail != GE_STENCILOP_ZERO || stencilState.sFail != GE_STENCILOP_KEEP || stencilState.zPass != GE_STENCILOP_KEEP)
|
|
|
|
return false;
|
|
|
|
if (stencilState.testFunc != GE_COMP_ALWAYS || stencilState.writeMask != 0xFF)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Lastly, verify no color is written. Natural way is a mask, in case another game uses it.
|
|
|
|
// Note that the PSP masks are reversed compared to typical APIs.
|
|
|
|
if (gstate.getColorMask() == 0xFFFFFF00)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// These games specifically use simple alpha blending with a constant zero alpha.
|
|
|
|
if (!gstate.isAlphaBlendEnabled() || gstate.getBlendFuncA() != GE_SRCBLEND_SRCALPHA || gstate.getBlendFuncB() != GE_DSTBLEND_INVSRCALPHA)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Also make sure there's no texture, in case its alpha gets involved.
|
|
|
|
if (gstate.isTextureMapEnabled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Spongebob uses material alpha.
|
|
|
|
if (gstate.getMaterialAmbientA() == 0x00 && gstate.getMaterialUpdate() == 0)
|
|
|
|
return true;
|
|
|
|
// MX vs ATV : Reflex uses vertex colors, should really check them...
|
|
|
|
if (gstate.getMaterialUpdate() == 1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Okay, color is most likely being used if we didn't hit the above.
|
|
|
|
return false;
|
2022-08-28 05:11:00 +00:00
|
|
|
}
|