mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
During culling, evaluate minZ,maxZ,minW,maxW, and use them to cull some things. Also, assorted vshader work.
This commit is contained in:
@@ -353,6 +353,8 @@ static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, i
|
||||
Mat4F32 worldViewProjMat(worldViewProj);
|
||||
alignas(16) static const float planesXYData[4] = { 1, -1, 1, -1 };
|
||||
Vec4F32 planesXY = Vec4F32::LoadAligned(planesXYData);
|
||||
Vec4F32 minClipPos = Vec4F32::Splat(INFINITY);
|
||||
Vec4F32 maxClipPos = Vec4F32::Splat(-INFINITY);
|
||||
Vec4S32 insideMask = Vec4S32::Zero();
|
||||
const s8 *data = (const s8 *)vdata + offset;
|
||||
for (int i = 0; i < vertexCount; i++, data += stride) {
|
||||
@@ -368,14 +370,35 @@ static bool TestBoundingBoxFast(const float *worldViewProj, const void *vdata, i
|
||||
objPos = Vec4F32::Load((const float *)data);
|
||||
break;
|
||||
}
|
||||
Vec4F32 clippos = objPos.AsVec3ByMatrix44(worldViewProjMat);
|
||||
Vec4F32 posXY = clippos.ShuffleXXYY();
|
||||
Vec4F32 posW = clippos.ShuffleWWWW();
|
||||
Vec4F32 clipPos = objPos.AsVec3ByMatrix44(worldViewProjMat);
|
||||
minClipPos = minClipPos.Min(clipPos);
|
||||
maxClipPos = maxClipPos.Max(clipPos);
|
||||
Vec4F32 posXY = clipPos.ShuffleXXYY();
|
||||
Vec4F32 posW = clipPos.ShuffleWWWW();
|
||||
Vec4F32 planeDist = posXY * planesXY + posW;
|
||||
insideMask |= planeDist.CompareGe(Vec4F32::Zero());
|
||||
}
|
||||
// At least one vertex is inside one of the planes.
|
||||
return AllCompareBitsSet(insideMask);
|
||||
|
||||
const float maxZ = maxClipPos[2];
|
||||
const float maxW = maxClipPos[3];
|
||||
const float minZ = minClipPos[2];
|
||||
const float minW = minClipPos[3];
|
||||
|
||||
if (AllCompareBitsSet(insideMask)) {
|
||||
// At least one vertex is inside each one of the planes.
|
||||
// Process min/max. Z is in minProj[2]/maxProj[2], W is in minProj[3]/maxProj[3].
|
||||
// Here we can perform the culling that happens if all vertices are closer than the near plane, or farther than the far plane.
|
||||
// Technically this should happen per-primitive, which we try to do with cull planes or software vertex processing if available, but this is still valid
|
||||
// for groups of primitives, avoiding detailed checking, and is a good fallback for games that rely on the min/max culling
|
||||
// behavior (like LocoRoco2's Tropuca level). The question is, do the inequalities really work like that properly? It seem
|
||||
// s like they should.
|
||||
if (maxZ < -maxW || minZ > maxW) {
|
||||
passesCull = false;
|
||||
}
|
||||
return passesCull;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType) {
|
||||
@@ -383,6 +406,8 @@ bool DrawEngineCommon::TestBoundingBoxFast(const float *worldViewProj, const voi
|
||||
// Let's always say objects are within bounds.
|
||||
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
|
||||
return true;
|
||||
} else if (vertexCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modify the transform matrix to take the viewport into account before culling. This is not necessary
|
||||
|
||||
@@ -17,6 +17,8 @@ std::string VertexShaderDesc(const VShaderID &id) {
|
||||
std::stringstream desc;
|
||||
desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]);
|
||||
if (id.Bit(VS_BIT_IS_THROUGH)) desc << "THR ";
|
||||
if (id.Bit(VS_BIT_CLIP_ENABLE)) desc << "Clip ";
|
||||
if (id.Bit(VS_BIT_MINMAX_DISCARD)) desc << "ZClip ";
|
||||
if (id.Bit(VS_BIT_USE_HW_TRANSFORM)) desc << "HWX ";
|
||||
if (id.Bit(VS_BIT_HAS_COLOR)) desc << "C ";
|
||||
if (id.Bit(VS_BIT_HAS_TEXCOORD)) desc << "T ";
|
||||
@@ -91,6 +93,7 @@ void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform,
|
||||
id.SetBit(VS_BIT_LMODE, lmode);
|
||||
id.SetBit(VS_BIT_IS_THROUGH, isModeThrough);
|
||||
id.SetBit(VS_BIT_HAS_COLOR, vtypeHasColor);
|
||||
id.SetBit(VS_BIT_CLIP_ENABLE, gstate.isDepthClipEnabled());
|
||||
id.SetBit(VS_BIT_VERTEX_RANGE_CULLING, vertexRangeCulling);
|
||||
|
||||
if (!isModeThrough && gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {
|
||||
|
||||
@@ -13,10 +13,10 @@ enum VShaderBit : uint8_t {
|
||||
VS_BIT_IS_THROUGH = 1,
|
||||
// bit 2 is free.
|
||||
VS_BIT_HAS_COLOR = 3,
|
||||
// bit 4 is free.
|
||||
VS_BIT_CLIP_ENABLE = 4,
|
||||
VS_BIT_VERTEX_RANGE_CULLING = 5,
|
||||
VS_BIT_SIMPLE_STEREO = 6,
|
||||
// 7 is free.
|
||||
VS_BIT_MINMAX_DISCARD = 7, // Do min/max in the fragment shader.
|
||||
VS_BIT_USE_HW_TRANSFORM = 8,
|
||||
VS_BIT_HAS_NORMAL = 9, // conditioned on hw transform
|
||||
VS_BIT_NORM_REVERSE = 10,
|
||||
@@ -83,7 +83,7 @@ enum FShaderBit : uint8_t {
|
||||
FS_BIT_COLOR_AGAINST_ZERO = 20,
|
||||
FS_BIT_ENABLE_FOG = 21, // Not used with FS_BIT_UBERSHADER
|
||||
FS_BIT_DO_TEXTURE_PROJ = 22,
|
||||
// 1 free bit
|
||||
FS_BIT_MINMAX_DISCARD = 23,
|
||||
FS_BIT_STENCIL_TO_ALPHA = 24, // 2 bits
|
||||
FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE = 26, // 4 bits (ReplaceAlphaType)
|
||||
FS_BIT_SIMULATE_LOGIC_OP_TYPE = 30, // 2 bits
|
||||
|
||||
@@ -175,6 +175,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
|
||||
bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled;
|
||||
bool needsZWHack = bugs.Has(Draw::Bugs::EQUAL_WZ_CORRUPTS_DEPTH) && g_Config.bVendorBugChecksEnabled;
|
||||
bool nanBug = bugs.Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL) && g_Config.bVendorBugChecksEnabled;
|
||||
|
||||
bool doFlatShading = id.Bit(VS_BIT_FLATSHADE) && !flatBug;
|
||||
|
||||
bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM);
|
||||
@@ -236,10 +238,13 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
}
|
||||
bool texCoordInVec3 = false;
|
||||
|
||||
bool vertexRangeCulling = id.Bit(VS_BIT_VERTEX_RANGE_CULLING) && !isModeThrough;
|
||||
bool clipClampedDepth = !isModeThrough && gstate_c.Use(GPU_USE_DEPTH_CLAMP) && gstate_c.Use(GPU_USE_CLIP_DISTANCE);
|
||||
const char *clipClampedDepthSuffix = "[0]";
|
||||
const char *vertexRangeClipSuffix = clipClampedDepth ? "[1]" : "[0]";
|
||||
const bool clipEnable = id.Bit(VS_BIT_CLIP_ENABLE) && !isModeThrough;
|
||||
const bool rangeCulling = id.Bit(VS_BIT_VERTEX_RANGE_CULLING);
|
||||
|
||||
// bool clipClampedDepth = !isModeThrough && gstate_c.Use(GPU_USE_DEPTH_CLAMP) && gstate_c.Use(GPU_USE_CLIP_DISTANCE);
|
||||
const char *zClipPlaneSuffix = "[0]";
|
||||
const char *minZClipPlaneSuffix = "[1]";
|
||||
const char *maxZClipPlaneSuffix = "[2]";
|
||||
|
||||
if (compat.shaderLanguage == GLSL_VULKAN) {
|
||||
WRITE(p, "\n");
|
||||
@@ -347,19 +352,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
WRITE(p, " float v_fogdepth : TEXCOORD1;\n");
|
||||
{
|
||||
WRITE(p, " vec4 gl_Position : SV_Position;\n");
|
||||
bool clipRange = vertexRangeCulling && gstate_c.Use(GPU_USE_CLIP_DISTANCE);
|
||||
if (clipClampedDepth && clipRange) {
|
||||
WRITE(p, " float2 gl_ClipDistance : SV_ClipDistance;\n");
|
||||
clipClampedDepthSuffix = ".x";
|
||||
vertexRangeClipSuffix = ".y";
|
||||
} else if (clipClampedDepth || clipRange) {
|
||||
WRITE(p, " float gl_ClipDistance : SV_ClipDistance;\n");
|
||||
clipClampedDepthSuffix = "";
|
||||
vertexRangeClipSuffix = "";
|
||||
}
|
||||
if (vertexRangeCulling && gstate_c.Use(GPU_USE_CULL_DISTANCE)) {
|
||||
WRITE(p, " float2 gl_CullDistance : SV_CullDistance0;\n");
|
||||
}
|
||||
zClipPlaneSuffix = ".x";
|
||||
minZClipPlaneSuffix = ".y";
|
||||
maxZClipPlaneSuffix = ".z";
|
||||
bool clipRange = gstate_c.Use(GPU_USE_CLIP_DISTANCE);
|
||||
WRITE(p, " float3 gl_ClipDistance : SV_ClipDistance;\n");
|
||||
WRITE(p, " float2 gl_CullDistance : SV_CullDistance0;\n");
|
||||
}
|
||||
WRITE(p, "};\n");
|
||||
} else {
|
||||
@@ -840,11 +838,19 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
}
|
||||
}
|
||||
|
||||
// We're in clip space, so here we check for virtual clipping. We don't actually clip, but we check if the actual hardware would have clipped.
|
||||
// We're in clip space, so here we check for clipping. We check if the actual hardware will clip.
|
||||
// If so, we skip the "range culling" (x and y out-of-bounds checks) since they wouldn't have happened, most likely.
|
||||
WRITE(p, " if (outPos.z < -outPos.w || outPos.z > outPos.w) {\n");
|
||||
WRITE(p, " if (outPos.z < -outPos.w) {\n");
|
||||
WRITE(p, " zClipped = true;\n");
|
||||
WRITE(p, " }\n");
|
||||
// Then we actually add the clip plane.
|
||||
WRITE(p, " gl_ClipDistance%s = outPos.z + outPos.w;\n", zClipPlaneSuffix);
|
||||
|
||||
if (gstate_c.Use(GPU_USE_CULL_DISTANCE)) {
|
||||
// Not quite understanding this one.
|
||||
// WRITE(p, " gl_CullDistance[0] = outPos.z + outPos.w;\n");
|
||||
// WRITE(p, " gl_CullDistance[1] = outPos.w - outPos.z;\n");
|
||||
}
|
||||
|
||||
// Perform the perspective projection and viewport transform. (We'll have to undo the division before passing the coordinate along).
|
||||
// In software transform mode, this is performed in on the CPU.
|
||||
@@ -1209,7 +1215,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
if (!isModeThrough) {
|
||||
// Cull against X and Y limits (unless the GPU has a certain driver bug).
|
||||
// It's not clear what the limits should be in through mode though, although I'm sure they exist.
|
||||
if (gstate_c.Use(GPU_USE_VS_RANGE_CULLING)) {
|
||||
if (!nanBug && rangeCulling) {
|
||||
WRITE(p, " if (!zClipped && (outPos.x < 0.0 || outPos.y < 0.0 || outPos.x >= 4096.0 || outPos.y >= 4096.0 || outPos.w < -1.0)) {\n");
|
||||
// Discard the whole triangle by setting one vertex to NaN.
|
||||
WRITE(p, " outPos = vec4(u_NaN, u_NaN, u_NaN, u_NaN);\n");
|
||||
@@ -1220,17 +1226,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
// Apply raster offset after the range culling.
|
||||
WRITE(p, " outPos.xy -= u_rasterOffset.xy;\n");
|
||||
|
||||
if (gstate_c.Use(GPU_USE_CULL_DISTANCE)) {
|
||||
// Cull against the Z=-W and Z=W planes.
|
||||
WRITE(p, " gl_CullDistance[0] = outPos.w + outPos.z; \n");
|
||||
WRITE(p, " gl_CullDistance[1] = outPos.z - outPos.w; \n");
|
||||
}
|
||||
|
||||
if (gstate_c.Use(GPU_USE_CLIP_DISTANCE)) {
|
||||
// We use clipping to implement min/max Z.
|
||||
// 1.0 effectively disables the clip plane.
|
||||
WRITE(p, " gl_ClipDistance[0] = u_minZmaxZ.x > 0.0 ? outPos.z - u_minZmaxZ.x : 1.0;\n");
|
||||
WRITE(p, " gl_ClipDistance[1] = u_minZmaxZ.y < 65535.0 ? u_minZmaxZ.y - outPos.z : 1.0;\n");
|
||||
// TODO: Should we move this to homogenous coordinates after the multiplication by w?
|
||||
WRITE(p, " gl_ClipDistance%s = u_minZmaxZ.x > 0.0 ? outPos.z - u_minZmaxZ.x : 1.0;\n", minZClipPlaneSuffix);
|
||||
WRITE(p, " gl_ClipDistance%s = u_minZmaxZ.y < 65535.0 ? u_minZmaxZ.y - outPos.z : 1.0;\n", maxZClipPlaneSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ static constexpr GECmdInfo geCmdInfo[] = {
|
||||
{ GE_CMD_LIGHTENABLE1, "light1_on", GECmdFormat::FLAG, "Light 1 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
|
||||
{ GE_CMD_LIGHTENABLE2, "light2_on", GECmdFormat::FLAG, "Light 2 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
|
||||
{ GE_CMD_LIGHTENABLE3, "light3_on", GECmdFormat::FLAG, "Light 3 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
|
||||
{ GE_CMD_DEPTHCLAMPENABLE, "zclamp_on", GECmdFormat::FLAG, "Depth clamp enable", CMD_FMT_FLAG},
|
||||
{ GE_CMD_DEPTHCLIPENABLE, "zclamp_on", GECmdFormat::FLAG, "Depth clamp enable", CMD_FMT_FLAG},
|
||||
{ GE_CMD_CULLFACEENABLE, "cull_on", GECmdFormat::FLAG, "Cullface enable", CMD_FMT_FLAG},
|
||||
{ GE_CMD_TEXTUREMAPENABLE, "tex_on", GECmdFormat::FLAG, "Texture enable", CMD_FMT_FLAG},
|
||||
{ GE_CMD_FOGENABLE, "fog_on", GECmdFormat::FLAG, "Fog enable", CMD_FMT_FLAG},
|
||||
@@ -306,7 +306,7 @@ static constexpr GECmdAlias geCmdAliases[] = {
|
||||
{ GE_CMD_LIGHTENABLE1, { "light1enable" } },
|
||||
{ GE_CMD_LIGHTENABLE2, { "light2enable" } },
|
||||
{ GE_CMD_LIGHTENABLE3, { "light3enable" } },
|
||||
{ GE_CMD_DEPTHCLAMPENABLE, { "zclampenable", "depthclamp_on", "depthclampenable" } },
|
||||
{ GE_CMD_DEPTHCLIPENABLE, { "zclampenable", "depthclamp_on", "depthclampenable" } },
|
||||
{ GE_CMD_CULLFACEENABLE, { "cullenable", "cullface_on", "cullfaceenable" } },
|
||||
{ GE_CMD_TEXTUREMAPENABLE, { "texenable", "texture_on", "textureenable" } },
|
||||
{ GE_CMD_FOGENABLE, { "fogenable" } },
|
||||
|
||||
+1
-8
@@ -200,7 +200,7 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
{ GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VIEWPORT_UNIFORMS },
|
||||
{ GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VIEWPORT_UNIFORMS },
|
||||
{ GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VIEWPORT_UNIFORMS },
|
||||
{ GE_CMD_DEPTHCLAMPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE },
|
||||
{ GE_CMD_DEPTHCLIPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE },
|
||||
|
||||
// Z range.
|
||||
{ GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE | DIRTY_RASTER_OFFSET},
|
||||
@@ -602,13 +602,6 @@ u32 GPUCommonHW::CheckGPUFeatures() const {
|
||||
features |= GPU_USE_DEPTH_CLAMP;
|
||||
}
|
||||
|
||||
bool canClipOrCull = draw_->GetDeviceCaps().maxClipDistances >= 3 || draw_->GetDeviceCaps().maxCullDistances >= 1;
|
||||
bool canDiscardVertex = !draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL);
|
||||
if ((canClipOrCull || canDiscardVertex) && !g_Config.bDisableRangeCulling) {
|
||||
// We'll dynamically use the parts that are supported, to reduce artifacts as much as possible.
|
||||
features |= GPU_USE_VS_RANGE_CULLING;
|
||||
}
|
||||
|
||||
if (draw_->GetDeviceCaps().framebufferFetchSupported) {
|
||||
features |= GPU_USE_FRAMEBUFFER_FETCH;
|
||||
features |= GPU_USE_SHADER_BLENDING; // doesn't matter if we are buffered or not here.
|
||||
|
||||
+1
-1
@@ -323,7 +323,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_DEPTHCLAMPENABLE:
|
||||
case GE_CMD_DEPTHCLIPENABLE:
|
||||
snprintf(buffer, bufsize, "Depth clamp enable: %i", data);
|
||||
break;
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = {
|
||||
{ GE_CMD_VIEWPORTYCENTER, 0, SoftDirty::TRANSFORM_VIEWPORT },
|
||||
{ GE_CMD_VIEWPORTZSCALE, 0, SoftDirty::TRANSFORM_VIEWPORT },
|
||||
{ GE_CMD_VIEWPORTZCENTER, 0, SoftDirty::TRANSFORM_VIEWPORT },
|
||||
{ GE_CMD_DEPTHCLAMPENABLE, 0, SoftDirty::TRANSFORM_BASIC },
|
||||
{ GE_CMD_DEPTHCLIPENABLE, 0, SoftDirty::TRANSFORM_BASIC },
|
||||
|
||||
// Z clipping.
|
||||
{ GE_CMD_MINZ, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ enum GECommand : uint8_t {
|
||||
GE_CMD_LIGHTENABLE1 = 0x19,
|
||||
GE_CMD_LIGHTENABLE2 = 0x1A,
|
||||
GE_CMD_LIGHTENABLE3 = 0x1B,
|
||||
GE_CMD_DEPTHCLAMPENABLE = 0x1C,
|
||||
GE_CMD_DEPTHCLIPENABLE = 0x1C,
|
||||
GE_CMD_CULLFACEENABLE = 0x1D,
|
||||
GE_CMD_TEXTUREMAPENABLE = 0x1E,
|
||||
GE_CMD_FOGENABLE = 0x1F,
|
||||
|
||||
@@ -1407,7 +1407,7 @@ static const StateItem g_rasterState[] = {
|
||||
{true, GE_CMD_NOP, "Clipping/Clamping"},
|
||||
{false, GE_CMD_MINZ},
|
||||
{false, GE_CMD_MAXZ},
|
||||
{false, GE_CMD_DEPTHCLAMPENABLE},
|
||||
{false, GE_CMD_DEPTHCLIPENABLE},
|
||||
|
||||
{true, GE_CMD_NOP, "Other raster state"},
|
||||
{false, GE_CMD_MASKRGB},
|
||||
|
||||
@@ -66,7 +66,7 @@ const GECommand g_stateFlagsRows[] = {
|
||||
GE_CMD_LIGHTENABLE1,
|
||||
GE_CMD_LIGHTENABLE2,
|
||||
GE_CMD_LIGHTENABLE3,
|
||||
GE_CMD_DEPTHCLAMPENABLE,
|
||||
GE_CMD_DEPTHCLIPENABLE,
|
||||
GE_CMD_CULLFACEENABLE,
|
||||
GE_CMD_TEXTUREMAPENABLE,
|
||||
GE_CMD_FOGENABLE,
|
||||
|
||||
Reference in New Issue
Block a user