Remove the concept of scaled/offset depth buffers.

This commit is contained in:
Henrik Rydgård
2026-06-10 10:41:30 +02:00
parent 5265422c0e
commit 5c340ccd09
15 changed files with 27 additions and 194 deletions
@@ -181,9 +181,6 @@ static std::string DescribeFormat(GPUDebugBufferFormat fmt) {
case GPU_DBG_FORMAT_24BIT_8X: return "D24_X8";
case GPU_DBG_FORMAT_24X_8BIT: return "X24_S8";
case GPU_DBG_FORMAT_FLOAT_DIV_256: return "D32F_DIV_256";
case GPU_DBG_FORMAT_24BIT_8X_DIV_256: return "D32F_X8_DIV_256";
case GPU_DBG_FORMAT_888_RGB: return "R8G8B8_UNORM";
case GPU_DBG_FORMAT_INVALID:
-17
View File
@@ -199,23 +199,6 @@ static bool ConvertPixelTo8888RGBA(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b
b = 0;
a = (src >> 24) & 0xFF;
break;
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
src = buf32[offset]& 0x00FFFFFF;
src = src - 0x800000 + 0x8000;
r = 255;
g = 0;
b = 0;
a = (src >> 8) & 0xFF;
break;
case GPU_DBG_FORMAT_FLOAT_DIV_256:
fsrc = fbuf[offset];
src = (int)(fsrc * 16777215.0);
src = src - 0x800000 + 0x8000;
r = 255;
g = 0;
b = 0;
a = (src >> 8) & 0xFF;
break;
default:
_assert_msg_(false, "Unsupported framebuffer format for screenshot: %d", fmt);
return false;
+6 -14
View File
@@ -124,20 +124,18 @@ void GenerateDepalShader300(ShaderWriter &writer, const DepalConfig &config) {
break;
case GE_FORMAT_DEPTH16:
// Decode depth buffer.
writer.C(" float depth = (color.x - z_offset) * z_scale * 65535.0f;\n");
writer.C(" float depth = clamp(color.x * 65535.0, 0.0, 65535.0);\n"); // Probably don't need to clamp here.
if (config.bufferFormat == GE_FORMAT_DEPTH16 && config.textureFormat == GE_TFMT_5650) {
// Convert depth to 565, without going through a CLUT.
// TODO: Make "depal without a CLUT" a separate concept, to avoid redundantly creating a CLUT texture.
writer.C(" int idepth = int(clamp(depth, 0.0, 65535.0));\n");
writer.C(" int idepth = int(depth);\n");
writer.C(" float r = float(idepth & 31) / 31.0;\n");
writer.C(" float g = float((idepth >> 5) & 63) / 63.0;\n");
writer.C(" float b = float((idepth >> 11) & 31) / 31.0;\n");
writer.C(" vec4 outColor = vec4(r, g, b, 1.0);\n");
return;
}
writer.C(" int index = int(clamp(depth, 0.0, 65535.0));\n");
writer.C(" int index = int(depth);\n");
break;
default:
break;
@@ -167,12 +165,6 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) {
const int shift = config.shift;
const int mask = config.mask;
if (config.bufferFormat == GE_FORMAT_DEPTH16) {
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
writer.ConstFloat("z_scale", factors.ScaleU16());
writer.ConstFloat("z_offset", factors.Offset());
}
writer.C(" vec4 index = ").SampleTexture2D("tex", "v_texcoord").C(";\n");
float index_multiplier = 1.0f;
@@ -273,8 +265,8 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) {
// TODO: I think we can handle most scenarios here, but texturing from depth buffers requires an extension on ES 2.0 anyway.
if (config.bufferFormat == GE_FORMAT_DEPTH16 && config.textureFormat == GE_TFMT_5650) {
// Convert depth to 565, without going through a CLUT.
writer.C(" float depth = (index.x - z_offset) * z_scale;\n");
writer.C(" float idepth = floor(clamp(depth, 0.0, 65535.0));\n");
writer.C(" float depth = index.x * 65535.0;\n");
writer.C(" float idepth = floor(clamp(depth, 0.0, 65535.0));\n"); // clamping should no longer be necessary.
writer.C(" float r = mod(idepth, 32.0) / 31.0;\n");
writer.C(" float g = mod(floor(idepth / 32.0), 64.0) / 63.0;\n");
writer.C(" float b = mod(floor(idepth / 2048.0), 32.0) / 31.0;\n");
@@ -284,7 +276,7 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) {
if (shift < 16) {
index_multiplier = 1.0f / (float)(1 << shift);
truncate_cpy(lookupMethod, "((index.x - z_offset) * z_scale)");
truncate_cpy(lookupMethod, "(index.x * 65535.0)");
if ((mask & (mask + 1)) != 0) {
// But we'll try with the above anyway.
+7 -22
View File
@@ -31,21 +31,18 @@ static const InputDef vs_inputs[] = {
};
struct DepthUB {
float u_depthFactor[4];
float u_depthShift[4];
float u_depthTo8[4];
};
const UniformDef depthUniforms[] = {
{ "vec4", "u_depthFactor", 0 },
{ "vec4", "u_depthShift", 1},
{ "vec4", "u_depthTo8", 2},
{ "vec4", "u_depthShift", 0 },
{ "vec4", "u_depthTo8", 1 },
};
const UniformBufferDesc depthUBDesc{ sizeof(DepthUB), {
{ "u_depthFactor", -1, -1, UniformType::FLOAT4, 0 },
{ "u_depthShift", -1, -1, UniformType::FLOAT4, 16 },
{ "u_depthTo8", -1, -1, UniformType::FLOAT4, 32 },
{ "u_depthShift", -1, -1, UniformType::FLOAT4, 0 },
{ "u_depthTo8", -1, -1, UniformType::FLOAT4, 16 },
} };
static const SamplerDef samplers[] = {
@@ -59,9 +56,9 @@ static const VaryingDef varyings[] = {
void GenerateDepthDownloadFs(ShaderWriter &writer) {
writer.DeclareSamplers(samplers);
writer.BeginFSMain(depthUniforms, varyings);
writer.C(" float depth = ").SampleTexture2D("tex", "v_texcoord").C(".r; \n");
writer.C(" float depth = ").SampleTexture2D("tex", "v_texcoord").C(".r;\n");
// At this point, clamped maps [0, 1] to [0, 65535].
writer.C(" float clamped = clamp((depth - u_depthFactor.x) * u_depthFactor.y, 0.0, 1.0);\n");
writer.C(" float clamped = clamp(depth, 0.0, 1.0);\n");
writer.C(" vec4 enc = u_depthShift * clamped;\n");
writer.C(" enc = floor(mod(enc, 256.0)) * u_depthTo8;\n");
writer.C(" vec4 outColor = enc.yzww;\n"); // Let's ignore the bits outside 16 bit precision.
@@ -215,10 +212,6 @@ bool FramebufferManagerCommon::ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x
DepthUB ub{};
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
ub.u_depthFactor[0] = depthScale.Offset();
ub.u_depthFactor[1] = depthScale.Scale();
// These are for packing a float in u8x4 colors. We should support more suitable readback formats on APIs that can do it.
static constexpr float shifts[] = { 16777215.0f, 16777215.0f / 256.0f, 16777215.0f / 65536.0f, 16777215.0f / 16777216.0f };
memcpy(ub.u_depthShift, shifts, sizeof(shifts));
@@ -265,17 +258,9 @@ bool FramebufferManagerCommon::ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x
// We downloaded float values directly in this case.
uint16_t *dest = pixels;
const float *packedf = (float *)convBuf_;
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
for (int yp = 0; yp < destH; ++yp) {
for (int xp = 0; xp < destW; ++xp) {
float scaled = depthScale.DecodeToU16(packedf[xp]);
if (scaled <= 0.0f) {
dest[xp] = 0;
} else if (scaled >= 65535.0f) {
dest[xp] = 65535;
} else {
dest[xp] = (int)scaled;
}
dest[xp] = (u16)std::clamp(65535.0f * packedf[xp], 0.0f, 65535.0f);
}
dest += pixelsStride;
packedf += destW;
+6 -20
View File
@@ -36,12 +36,10 @@ static const SamplerDef samplers[1] = {
{ 0, "tex", SamplerFlags::ARRAY_ON_VULKAN },
};
const UniformDef g_draw2Duniforms[5] = {
// the size is also specified in the header.
const UniformDef g_draw2Duniforms[2] = {
{ "vec2", "texSize", 0 },
{ "float", "scaleFactor", 1},
{ "float", "z_scale", 2 },
{ "float", "z_scale_inv", 3 },
{ "float", "z_offset", 4 },
};
struct Draw2DUB {
@@ -56,9 +54,6 @@ struct Draw2DUB {
const UniformBufferDesc draw2DUBDesc{ sizeof(Draw2DUB), {
{ "texSize", -1, 0, UniformType::FLOAT2, 0 },
{ "scaleFactor", -1, 1, UniformType::FLOAT1, 8 },
{ "z_scale", -1, 1, UniformType::FLOAT1, 12 },
{ "z_scale_inv", -1, 1, UniformType::FLOAT1, 16 },
{ "z_offset", -1, 1, UniformType::FLOAT1, 20 },
} };
Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) {
@@ -113,7 +108,7 @@ Draw2DPipelineInfo GenerateDraw2DEncodeDepthFs(ShaderWriter &writer) {
writer.BeginFSMain(g_draw2Duniforms, varyings);
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
writer.C(" float depthValue = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n");
writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");
writer.C(" gl_FragDepth = depthValue;\n");
writer.EndFSMain("outColor");
return Draw2DPipelineInfo{
@@ -131,10 +126,9 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthFs(ShaderWriter &writer) {
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
// Unlike when just copying a depth buffer, here we're generating new depth values so we'll
// have to apply the scaling.
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
writer.C(" vec3 rgb = ").SampleTexture2D("tex", "v_texcoord.xy").C(".xyz;\n");
writer.F(" float depthValue = ((floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0)) / 65535.0; \n");
writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");
writer.F(" float depthValue = ((floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0)); \n");
writer.C(" gl_FragDepth = depthValue / 65535.0;\n");
writer.EndFSMain("outColor");
return Draw2DPipelineInfo{
@@ -150,9 +144,6 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) {
writer.DeclareSamplers(samplers);
writer.BeginFSMain(g_draw2Duniforms, varyings);
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
// Unlike when just copying a depth buffer, here we're generating new depth values so we'll
// have to apply the scaling.
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
writer.C(" vec2 tsize = texSize;\n");
writer.C(" vec2 coord = v_texcoord * tsize;\n");
writer.F(" float strip = 4.0 * scaleFactor;\n");
@@ -161,7 +152,7 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) {
writer.C(" coord /= tsize;\n");
writer.C(" highp vec3 rgb = ").SampleTexture2D("tex", "coord").C(".xyz;\n");
writer.F(" highp float depthValue = floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0; \n");
writer.C(" gl_FragDepth = z_offset + ((depthValue / 65535.0) * z_scale_inv);\n");
writer.C(" gl_FragDepth = depthValue / 65535.0;\n");
writer.EndFSMain("outColor");
return Draw2DPipelineInfo{
@@ -347,11 +338,6 @@ void Draw2D::DrawStrip2D(Draw::Texture *tex, const Draw2DVertex *verts, int vert
ub.texSizeY = tex ? tex->Height() : texH;
ub.scaleFactor = (float)scaleFactor;
DepthScaleFactors zScaleFactors = GetDepthScaleFactors(gstate_c.UseFlags());
ub.zScale = zScaleFactors.Scale();
ub.zScaleInv = 1.0f / ub.zScale;
ub.zOffset = zScaleFactors.Offset();
draw_->BindPipeline(pipeline->pipeline);
draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub));
+1 -1
View File
@@ -42,7 +42,7 @@ struct Draw2DPipelineInfo {
Slice<SamplerDef> samplers;
};
extern const UniformDef g_draw2Duniforms[5];
extern const UniformDef g_draw2Duniforms[2];
struct Draw2DPipeline {
Draw::Pipeline *pipeline;
-1
View File
@@ -1099,7 +1099,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
float clearDepth = 0.0f;
if (vfb->usageFlags & FB_USAGE_INVALIDATE_DEPTH) {
depthAction = Draw::RPAction::CLEAR;
clearDepth = GetDepthScaleFactors(gstate_c.UseFlags()).Offset();
vfb->usageFlags &= ~FB_USAGE_INVALIDATE_DEPTH;
}
draw_->BindFramebufferAsRenderTarget(vfb->fbo, {Draw::RPAction::KEEP, depthAction, Draw::RPAction::KEEP, 0, clearDepth}, "FBSwitch");
-2
View File
@@ -1011,8 +1011,6 @@ u32 GPUDebugBuffer::PixelSize() const {
case GPU_DBG_FORMAT_FLOAT:
case GPU_DBG_FORMAT_24BIT_8X:
case GPU_DBG_FORMAT_24X_8BIT:
case GPU_DBG_FORMAT_FLOAT_DIV_256:
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
return 4;
case GPU_DBG_FORMAT_888_RGB:
-3
View File
@@ -75,9 +75,6 @@ enum GPUDebugBufferFormat {
GPU_DBG_FORMAT_24BIT_8X = 0x13,
GPU_DBG_FORMAT_24X_8BIT = 0x14,
GPU_DBG_FORMAT_FLOAT_DIV_256 = 0x18,
GPU_DBG_FORMAT_24BIT_8X_DIV_256 = 0x1B,
// This is used for screenshots, mainly.
GPU_DBG_FORMAT_888_RGB = 0x20,
};
-12
View File
@@ -518,18 +518,6 @@ ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) {
return REPLACE_BLEND_STANDARD;
}
// The supported flag combinations. TODO: Maybe they should be distilled down into an enum.
// Currently obsolete. We may reintroduce squeezing 24-bit depth into a 16-bit range, although that
// will mess with hardware depth clamp, so likely not worth it anymore.
float DepthSliceFactor(u32 useFlags) {
return 1.0f;
}
// See class DepthScaleFactors for how to apply.
DepthScaleFactors GetDepthScaleFactors(u32 useFlags) {
return DepthScaleFactors(0.0f, 65535.0f);
}
void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
float renderWidthFactor, renderHeightFactor;
float renderX = 0.0f, renderY = 0.0f;
-30
View File
@@ -82,36 +82,6 @@ struct ViewportAndScissor {
struct DisplayLayoutConfig;
void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out);
// NOTE: See the .cpp file for detailed comment about how the use flags are interpreted.
class DepthScaleFactors {
public:
// This should only be used from GetDepthScaleFactors.
DepthScaleFactors(double offset, double scale) : offset_(offset), scale_(scale) {}
// Decodes a value from a depth buffer to a value of range 0..65536
float DecodeToU16(float z) const {
return (float)((z - offset_) * scale_);
}
// 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 {
return (float)(((double)z_u16 / scale_) + offset_);
}
float Offset() const { return (float)offset_; }
float ScaleU16() const { return (float)scale_; }
float Scale() const { return (float)(scale_ / 65535.0); }
private:
// Doubles hardly cost anything these days, and precision matters here.
double offset_;
double scale_;
};
DepthScaleFactors GetDepthScaleFactors(u32 useFlags);
// These are common to all modern APIs and can be easily converted with a lookup table.
enum class BlendFactor : uint8_t {
ZERO,
+1 -2
View File
@@ -199,9 +199,8 @@ SoftwareTransformAction RunSoftwareTransform(SoftwareTransformParams &params, in
bool matchingComponents = params.allowSeparateAlphaClear || (alphaMatchesColor && depthMatchesStencil);
bool stencilNotMasked = !gstate.isClearModeAlphaMask() || gstate.getStencilWriteMask() == 0x00;
if (matchingComponents && stencilNotMasked) {
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
// Need to rescale from a [0, 1] float. This is the final transformed value.
float depth = depthScale.EncodeFromU16(transformed[1].z);
float depth = 65535.0 * transformed[1].z;
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
if (!(params.everUsedEqualDepth && gstate.isClearModeDepthMask() && result->depth > 0.0f && result->depth < 1.0f)) {
+3 -3
View File
@@ -220,7 +220,7 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe
config.smoothedDepal = smoothedDepal;
config.depthUpperBits = depthUpperBits;
Draw2DPipeline *ts = draw2D_->Create2DPipeline([=](ShaderWriter &writer) -> Draw2DPipelineInfo {
Draw2DPipeline *ts = draw2D_->Create2DPipeline([config](ShaderWriter &writer) -> Draw2DPipelineInfo {
GenerateDepalFs(writer, config);
return Draw2DPipelineInfo{
"depal",
@@ -237,8 +237,8 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe
std::vector<std::string> TextureShaderCache::DebugGetShaderIDs(DebugShaderType type) {
std::vector<std::string> ids;
for (auto &iter : depalCache_) {
ids.push_back(StringFromFormat("%08x", iter.first));
for (auto &entry : depalCache_) {
ids.push_back(StringFromFormat("%08x", entry.first));
}
return ids;
}
+3 -29
View File
@@ -954,48 +954,22 @@ void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[25
case GPU_DBG_FORMAT_24BIT_8X:
{
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
// These are only ever going to be depth values, so let's also show scaled to 16 bit.
snprintf(desc, 256, "%d,%d: %d / %f / %f", x, y, pix & 0x00FFFFFF, (pix & 0x00FFFFFF) * (1.0f / 16777215.0f), depthScale.DecodeToU16((pix & 0x00FFFFFF) * (1.0f / 16777215.0f)));
snprintf(desc, 256, "%d,%d: %d / %f / %f", x, y, pix & 0x00FFFFFF, (pix & 0x00FFFFFF) * (1.0f / 16777215.0f), 65535.0f * ((pix & 0x00FFFFFF) * (1.0f / 16777215.0f)));
break;
}
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
{
// These are only ever going to be depth values, so let's also show scaled to 16 bit.
int z24 = pix & 0x00FFFFFF;
int z16 = z24 - 0x800000 + 0x8000;
snprintf(desc, 256, "%d,%d: %d / %f", x, y, z16, z16 * (1.0f / 65535.0f));
}
break;
case GPU_DBG_FORMAT_24X_8BIT:
snprintf(desc, 256, "%d,%d: %d / %f", x, y, (pix >> 24) & 0xFF, ((pix >> 24) & 0xFF) * (1.0f / 255.0f));
break;
case GPU_DBG_FORMAT_FLOAT:
{
float pixf = *(float *)&pix;
DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags());
snprintf(desc, 256, "%d,%d: %f / %f", x, y, pixf, depthScale.DecodeToU16(pixf));
const float pixf = *(const float *)&pix;
snprintf(desc, 256, "%d,%d: %f / %f", x, y, pixf, 65535.0 * pixf);
break;
}
case GPU_DBG_FORMAT_FLOAT_DIV_256:
{
double z = *(float *)&pix;
int z24 = (int)(z * 16777215.0);
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
// TODO: Use GetDepthScaleFactors here too, verify it's the same.
int z16 = z24 - 0x800000 + 0x8000;
int z16_2 = factors.DecodeToU16(z);
snprintf(desc, 256, "%d,%d: %d / %f", x, y, z16, (z - 0.5 + (1.0 / 512.0)) * 256.0);
}
break;
default:
snprintf(desc, 256, "Unexpected format");
}
-35
View File
@@ -900,40 +900,6 @@ static bool TestSmallDataConvert() {
return true;
}
float DepthSliceFactor(u32 useFlags);
static bool TestDepthMath() {
// These are in normalized space.
static const volatile float testValues[] = { 0.0f, 0.1f, 0.5f, M_PI / 4.0f, 0.9f, 1.0f };
// Flag combinations that can happen (any combination not included here is invalid, see comment
// over in GPUStateUtils.cpp):
static const u32 useFlagsArray[] = {
0,
GPU_USE_DEPTH_CLAMP,
};
EXPECT_REL_EQ_FLOAT(100000.0f, 100001.0f, 0.00001f);
for (int j = 0; j < ARRAY_SIZE(useFlagsArray); j++) {
u32 useFlags = useFlagsArray[j];
printf("j: %d useflags: %d\n", j, useFlags);
DepthScaleFactors factors = GetDepthScaleFactors(useFlags);
EXPECT_REL_EQ_FLOAT(factors.Scale(), DepthSliceFactor(useFlags), 0.0001f);
for (int i = 0; i < ARRAY_SIZE(testValues); i++) {
float testValue = testValues[i] * 65535.0f;
float encoded = factors.EncodeFromU16(testValue);
float decodedU16 = factors.DecodeToU16(encoded);
EXPECT_REL_EQ_FLOAT(decodedU16, testValue, 0.0001f);
}
}
return true;
}
bool TestInputMapping() {
InputMapping mapping;
mapping.deviceId = DEVICE_ID_PAD_0;
@@ -1412,7 +1378,6 @@ TestItem availableTests[] = {
TEST_ITEM(TinySet),
TEST_ITEM(FastVec),
TEST_ITEM(SmallDataConvert),
TEST_ITEM(DepthMath),
TEST_ITEM(InputMapping),
TEST_ITEM(EscapeMenuString),
TEST_ITEM(VFS),