GPU/HW: Add additional texture filters

wip3-rebase
Stenzek 4 days ago
parent 04f821c044
commit 045b83b2d0
No known key found for this signature in database

@ -139,9 +139,15 @@ ALWAYS_INLINE bool IsBlendedTextureFiltering(GPUTextureFilter filter)
((static_cast<u8>(GPUTextureFilter::BilinearBinAlpha) & 1u) == 0u) && ((static_cast<u8>(GPUTextureFilter::BilinearBinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::JINC2) & 1u) == 1u) && ((static_cast<u8>(GPUTextureFilter::JINC2) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::JINC2BinAlpha) & 1u) == 0u) && ((static_cast<u8>(GPUTextureFilter::JINC2BinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::MonotonicCubic) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::MonotonicCubicBinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::AdaptiveDiagonal) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::AdaptiveDiagonalBinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::DCCI) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::DCCIBinAlpha) & 1u) == 0u) &&
((static_cast<u8>(GPUTextureFilter::xBR) & 1u) == 1u) && ((static_cast<u8>(GPUTextureFilter::xBR) & 1u) == 1u) &&
((static_cast<u8>(GPUTextureFilter::xBRBinAlpha) & 1u) == 0u)); ((static_cast<u8>(GPUTextureFilter::xBRBinAlpha) & 1u) == 0u));
return (filter < GPUTextureFilter::Scale2x && ((static_cast<u8>(filter) & 1u) == 1u)); return (filter < GPUTextureFilter::SharpBilinear && ((static_cast<u8>(filter) & 1u) == 1u));
} }
/// Computes the area affected by a VRAM transfer, including wrap-around of X. /// Computes the area affected by a VRAM transfer, including wrap-around of X.

@ -408,6 +408,469 @@ void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limi
if (ialpha > 0.0) if (ialpha > 0.0)
texcol.rgb /= float3(ialpha, ialpha, ialpha); texcol.rgb /= float3(ialpha, ialpha, ialpha);
#if !TEXTURE_ALPHA_BLENDING
ialpha = (ialpha >= 0.5) ? 1.0 : 0.0;
#endif
}
)";
}
else if (texture_filter == GPUTextureFilter::MonotonicCubic ||
texture_filter == GPUTextureFilter::MonotonicCubicBinAlpha)
{
ss << R"(
float4 MonotonicSlope(float4 left, float4 right)
{
float4 valid = step(float4(0.000001, 0.000001, 0.000001, 0.000001), left * right);
float4 denominator = valid * (left + right) + (float4(1.0, 1.0, 1.0, 1.0) - valid);
return valid * ((2.0 * left * right) / denominator);
}
float4 MonotonicInterpolate(float4 p0, float4 p1, float4 p2, float4 p3, float t)
{
float4 m1 = MonotonicSlope(p1 - p0, p2 - p1);
float4 m2 = MonotonicSlope(p2 - p1, p3 - p2);
float t2 = t * t;
float t3 = t2 * t;
float4 value = (2.0 * t3 - 3.0 * t2 + 1.0) * p1 + (t3 - 2.0 * t2 + t) * m1 +
(-2.0 * t3 + 3.0 * t2) * p2 + (t3 - t2) * m2;
return clamp(value, min(p1, p2), max(p1, p2));
}
void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limits,
out float4 texcol, out float ialpha)
{
float2 sample_pos = coords - float2(0.5, 0.5);
float2 base = floor(sample_pos);
float2 fpart = frac(sample_pos);
float4 color_rows[4];
float4 coverage_rows;
for (int y = 0; y < 4; y++)
{
float4 samples[4];
float4 coverage;
for (int x = 0; x < 4; x++)
{
samples[x] = SampleFromVRAM(texpage, base + float2(float(x - 1), float(y - 1)), uv_limits);
coverage[x] = float(VECTOR_NEQ(samples[x], TRANSPARENT_PIXEL_COLOR));
}
color_rows[y] = MonotonicInterpolate(samples[0], samples[1], samples[2], samples[3], fpart.x);
coverage_rows[y] = MonotonicInterpolate(coverage.xxxx, coverage.yyyy, coverage.zzzz,
coverage.wwww, fpart.x).x;
}
texcol = MonotonicInterpolate(color_rows[0], color_rows[1], color_rows[2], color_rows[3], fpart.y);
ialpha = MonotonicInterpolate(coverage_rows.xxxx, coverage_rows.yyyy, coverage_rows.zzzz,
coverage_rows.wwww, fpart.y).x;
if (ialpha > 0.0)
texcol.rgb = saturate(texcol.rgb / float3(ialpha, ialpha, ialpha));
#if !TEXTURE_ALPHA_BLENDING
ialpha = (ialpha >= 0.5) ? 1.0 : 0.0;
#endif
}
)";
}
else if (texture_filter == GPUTextureFilter::AdaptiveDiagonal ||
texture_filter == GPUTextureFilter::AdaptiveDiagonalBinAlpha)
{
ss << R"(
float AdaptiveDiagonalLuma(float4 color)
{
return dot(color.rgb, float3(0.299, 0.587, 0.114));
}
void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limits,
out float4 texcol, out float ialpha)
{
float2 sample_pos = coords - float2(0.5, 0.5);
float2 base = floor(sample_pos);
float2 fpart = frac(sample_pos);
float4 samples[16];
float coverage[16];
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
int index = y * 4 + x;
samples[index] = SampleFromVRAM(texpage, base + float2(float(x - 1), float(y - 1)), uv_limits);
coverage[index] = float(VECTOR_NEQ(samples[index], TRANSPARENT_PIXEL_COLOR));
}
}
// Baseline interpolation. This guarantees sensible behaviour away from
// strongly directional structure.
float4 spatial = float4((1.0 - fpart.x) * (1.0 - fpart.y), fpart.x * (1.0 - fpart.y),
(1.0 - fpart.x) * fpart.y, fpart.x * fpart.y);
float4 bilinear = samples[5] * spatial.x + samples[6] * spatial.y +
samples[9] * spatial.z + samples[10] * spatial.w;
float bilinear_coverage = coverage[5] * spatial.x + coverage[6] * spatial.y +
coverage[9] * spatial.z + coverage[10] * spatial.w;
// Measure variation along the two diagonals.
float down_mean = (AdaptiveDiagonalLuma(samples[0]) + AdaptiveDiagonalLuma(samples[5]) +
AdaptiveDiagonalLuma(samples[10]) + AdaptiveDiagonalLuma(samples[15])) * 0.25;
float up_mean = (AdaptiveDiagonalLuma(samples[12]) + AdaptiveDiagonalLuma(samples[9]) +
AdaptiveDiagonalLuma(samples[6]) + AdaptiveDiagonalLuma(samples[3])) * 0.25;
float4 down_delta = float4(AdaptiveDiagonalLuma(samples[0]),
AdaptiveDiagonalLuma(samples[5]), AdaptiveDiagonalLuma(samples[10]),
AdaptiveDiagonalLuma(samples[15])) - VECTOR_BROADCAST(float4, down_mean);
float4 up_delta = float4(AdaptiveDiagonalLuma(samples[12]), AdaptiveDiagonalLuma(samples[9]),
AdaptiveDiagonalLuma(samples[6]), AdaptiveDiagonalLuma(samples[3])) -
VECTOR_BROADCAST(float4, up_mean);
const float epsilon = 0.0001;
float down_variance =
epsilon + dot(down_delta, down_delta);
float up_variance =
epsilon + dot(up_delta, up_delta);
// Project the fractional position onto each diagonal.
float down_t = (fpart.x + fpart.y) * 0.5;
float up_t = (fpart.x + (1.0 - fpart.y)) * 0.5;
float4 down = lerp(samples[5], samples[10], down_t);
float4 up = lerp(samples[9], samples[6], up_t);
float down_coverage = lerp(coverage[5], coverage[10], down_t);
float up_coverage = lerp(coverage[9], coverage[6], up_t);
// Low variance means that diagonal is the more plausible continuation.
float down_weight = 1.0 / down_variance;
float up_weight = 1.0 / up_variance;
float weight_sum = down_weight + up_weight;
float4 directional_prediction = (down * down_weight + up * up_weight) / weight_sum;
float directional_coverage = (down_coverage * down_weight + up_coverage * up_weight) / weight_sum;
// IMPORTANT: closeness must be weighted using the SAME directional weights as the prediction. Taking max()
// here associates the confidence of one diagonal with a prediction dominated by the other diagonal.
float down_closeness = 1.0 - saturate(abs(fpart.x - fpart.y) * 2.0);
float up_closeness = 1.0 - saturate(abs(fpart.x + fpart.y - 1.0) * 2.0);
float directional_closeness = (down_weight * down_closeness + up_weight * up_closeness) / weight_sum;
float anisotropy = abs(down_variance - up_variance) / (down_variance + up_variance);
float adaptive_amount = anisotropy * directional_closeness;
texcol = lerp(bilinear, directional_prediction, adaptive_amount);
ialpha = saturate(lerp(bilinear_coverage, directional_coverage, adaptive_amount));
if (ialpha > 0.0)
texcol.rgb = saturate(texcol.rgb / float3(ialpha, ialpha, ialpha));
else
texcol.rgb = float3(0.0, 0.0, 0.0);
texcol.a = saturate(texcol.a);
#if !TEXTURE_ALPHA_BLENDING
ialpha = (ialpha >= 0.5) ? 1.0 : 0.0;
#endif
}
)";
}
else if (texture_filter == GPUTextureFilter::DCCI || texture_filter == GPUTextureFilter::DCCIBinAlpha)
{
ss << R"(
float DCCILuma(float4 color)
{
return dot(color.rgb, float3(0.299, 0.587, 0.114));
}
float4 DCCIMidpoint(float4 p0, float4 p1, float4 p2, float4 p3)
{
// Cubic convolution at t = 0.5:
// [-1, 9, 9, -1] / 16.
return (-p0 + 9.0 * p1 + 9.0 * p2 - p3) * (1.0 / 16.0);
}
float DCCIMidpointCoverage(float p0, float p1, float p2, float p3)
{
return (-p0 + 9.0 * p1 + 9.0 * p2 - p3) * (1.0 / 16.0);
}
float DCCIWeight(float d)
{
// Reference DCCI uses 1 / (1 + d^k), k = 5.
float d2 = d * d;
return 1.0 / (1.0 + d2 * d2 * d);
}
void DCCISource(TEXPAGE_VALUE texpage, float2 p, float4 uv_limits,
out float4 color, out float coverage)
{
color = SampleFromVRAM(texpage, p, uv_limits);
coverage = float(VECTOR_NEQ(color, TRANSPARENT_PIXEL_COLOR));
}
// First DCCI reconstruction stage.
//
// 'cell' is the upper-left source texel of the 2x2 source cell whose
// diagonal midpoint is being reconstructed. In the 2x lattice this
// corresponds to an odd/odd sample.
void DCCIStage1(TEXPAGE_VALUE texpage, float2 cell, float4 uv_limits,
out float4 color, out float coverage)
{
float4 s[16];
float a[16];
float l[16];
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
int i = y * 4 + x;
DCCISource(texpage,
cell + float2(float(x - 1), float(y - 1)),
uv_limits, s[i], a[i]);
l[i] = DCCILuma(s[i]);
}
}
// Exact type-1 direction detector from DCCI's 7x7 formulation,
// reduced to the sixteen occupied source-lattice samples.
//
// d1: 45-degree gradient measure.
float d1 =
abs(l[4] - l[1]) +
abs(l[8] - l[5]) + abs(l[5] - l[2]) +
abs(l[12] - l[9]) + abs(l[9] - l[6]) + abs(l[6] - l[3]) +
abs(l[13] - l[10]) + abs(l[10] - l[7]) +
abs(l[14] - l[11]);
// d2: 135-degree gradient measure.
float d2 =
abs(l[2] - l[7]) +
abs(l[1] - l[6]) + abs(l[6] - l[11]) +
abs(l[0] - l[5]) + abs(l[5] - l[10]) + abs(l[10] - l[15]) +
abs(l[4] - l[9]) + abs(l[9] - l[14]) +
abs(l[8] - l[13]);
// v1 in the reference implementation: anti-diagonal.
float4 p1 = DCCIMidpoint(s[12], s[9], s[6], s[3]);
float p1a = DCCIMidpointCoverage(a[12], a[9], a[6], a[3]);
// v2 in the reference implementation: main diagonal.
float4 p2 = DCCIMidpoint(s[0], s[5], s[10], s[15]);
float p2a = DCCIMidpointCoverage(a[0], a[5], a[10], a[15]);
CONSTANT float DCCI_THRESHOLD = 1.15;
if ((1.0 + d1) > DCCI_THRESHOLD * (1.0 + d2))
{
// Gradient is stronger in direction 1, interpolate along direction 2.
color = p2;
coverage = p2a;
}
else if ((1.0 + d2) > DCCI_THRESHOLD * (1.0 + d1))
{
color = p1;
coverage = p1a;
}
else
{
float w1 = DCCIWeight(d1);
float w2 = DCCIWeight(d2);
float inv_sum = 1.0 / (w1 + w2);
color = (w1 * p1 + w2 * p2) * inv_sum;
coverage = (w1 * p1a + w2 * p2a) * inv_sum;
}
}
// Fetch a point which is already known after DCCI stage 1.
//
// The 2x reconstruction lattice uses:
// even/even -> original source samples
// odd/odd -> stage-1 diagonal samples
void DCCIKnown(TEXPAGE_VALUE texpage, float2 h, float4 uv_limits,
out float4 color, out float coverage)
{
int hx = int(h.x);
int hy = int(h.y);
if (((hx & 1) == 0) && ((hy & 1) == 0))
{
DCCISource(texpage, h * 0.5, uv_limits, color, coverage);
}
else
{
// This function is only called for points belonging to the
// even-parity lattice, so the remaining possibility is odd/odd.
DCCIStage1(texpage, floor(h * 0.5), uv_limits, color, coverage);
}
}
// Second DCCI reconstruction stage.
//
// h has mixed parity (odd/even or even/odd). The surrounding
// even-parity lattice already consists of original samples plus the
// stage-1 diagonal samples.
void DCCIStage2(TEXPAGE_VALUE texpage, float2 h, float4 uv_limits,
out float4 color, out float coverage)
{
float4 s0, s1, s2, s3;
float4 s4, s5, s6, s7;
float4 s8, s9, s10, s11;
float4 s12, s13, s14, s15;
float a0, a1, a2, a3;
float a4, a5, a6, a7;
float a8, a9, a10, a11;
float a12, a13, a14, a15;
// Unique known samples required by the reference 5x5 direction
// detector and the 7x7 cubic support.
DCCIKnown(texpage, h + float2(-1.0, -2.0), uv_limits, s0, a0);
DCCIKnown(texpage, h + float2( 1.0, -2.0), uv_limits, s1, a1);
DCCIKnown(texpage, h + float2(-2.0, -1.0), uv_limits, s2, a2);
DCCIKnown(texpage, h + float2( 0.0, -1.0), uv_limits, s3, a3);
DCCIKnown(texpage, h + float2( 2.0, -1.0), uv_limits, s4, a4);
DCCIKnown(texpage, h + float2(-3.0, 0.0), uv_limits, s5, a5);
DCCIKnown(texpage, h + float2(-1.0, 0.0), uv_limits, s6, a6);
DCCIKnown(texpage, h + float2( 1.0, 0.0), uv_limits, s7, a7);
DCCIKnown(texpage, h + float2( 3.0, 0.0), uv_limits, s8, a8);
DCCIKnown(texpage, h + float2(-2.0, 1.0), uv_limits, s9, a9);
DCCIKnown(texpage, h + float2( 0.0, 1.0), uv_limits, s10, a10);
DCCIKnown(texpage, h + float2( 2.0, 1.0), uv_limits, s11, a11);
DCCIKnown(texpage, h + float2(-1.0, 2.0), uv_limits, s12, a12);
DCCIKnown(texpage, h + float2( 1.0, 2.0), uv_limits, s13, a13);
DCCIKnown(texpage, h + float2( 0.0, -3.0), uv_limits, s14, a14);
DCCIKnown(texpage, h + float2( 0.0, 3.0), uv_limits, s15, a15);
float l0 = DCCILuma(s0);
float l1 = DCCILuma(s1);
float l2 = DCCILuma(s2);
float l3 = DCCILuma(s3);
float l4 = DCCILuma(s4);
float l6 = DCCILuma(s6);
float l7 = DCCILuma(s7);
float l9 = DCCILuma(s9);
float l10 = DCCILuma(s10);
float l11 = DCCILuma(s11);
float l12 = DCCILuma(s12);
float l13 = DCCILuma(s13);
// Exact type-2/type-3 horizontal direction detector.
float d1 =
abs(l0 - l1) +
abs(l6 - l7) +
abs(l12 - l13) +
abs(l2 - l3) + abs(l3 - l4) +
abs(l9 - l10) + abs(l10 - l11);
// Exact type-2/type-3 vertical direction detector.
float d2 =
abs(l2 - l9) +
abs(l3 - l10) +
abs(l4 - l11) +
abs(l0 - l6) + abs(l6 - l12) +
abs(l1 - l7) + abs(l7 - l13);
// Horizontal candidate.
float4 p1 = DCCIMidpoint(s5, s6, s7, s8);
float p1a = DCCIMidpointCoverage(a5, a6, a7, a8);
// Vertical candidate.
float4 p2 = DCCIMidpoint(s14, s3, s10, s15);
float p2a = DCCIMidpointCoverage(a14, a3, a10, a15);
CONSTANT float DCCI_THRESHOLD = 1.15;
if ((1.0 + d1) > DCCI_THRESHOLD * (1.0 + d2))
{
color = p2;
coverage = p2a;
}
else if ((1.0 + d2) > DCCI_THRESHOLD * (1.0 + d1))
{
color = p1;
coverage = p1a;
}
else
{
float w1 = DCCIWeight(d1);
float w2 = DCCIWeight(d2);
float inv_sum = 1.0 / (w1 + w2);
color = (w1 * p1 + w2 * p2) * inv_sum;
coverage = (w1 * p1a + w2 * p2a) * inv_sum;
}
}
void DCCILattice(TEXPAGE_VALUE texpage, float2 h, float4 uv_limits,
out float4 color, out float coverage)
{
int hx = int(h.x);
int hy = int(h.y);
bool x_odd = ((hx & 1) != 0);
bool y_odd = ((hy & 1) != 0);
if (!x_odd && !y_odd)
{
DCCISource(texpage, h * 0.5, uv_limits, color, coverage);
}
else if (x_odd && y_odd)
{
DCCIStage1(texpage, floor(h * 0.5), uv_limits, color, coverage);
}
else
{
DCCIStage2(texpage, h, uv_limits, color, coverage);
}
}
void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limits,
out float4 texcol, out float ialpha)
{
// DCCI is a 2x reconstruction algorithm. Work in its canonical
// high-resolution lattice, where source texels occupy even/even
// positions.
float2 source_pos = coords - float2(0.5, 0.5);
float2 hpos = source_pos * 2.0;
float2 hbase = floor(hpos);
float2 fpart = frac(hpos);
float4 c00, c10, c01, c11;
float a00, a10, a01, a11;
DCCILattice(texpage, hbase + float2(0.0, 0.0), uv_limits, c00, a00);
DCCILattice(texpage, hbase + float2(1.0, 0.0), uv_limits, c10, a10);
DCCILattice(texpage, hbase + float2(0.0, 1.0), uv_limits, c01, a01);
DCCILattice(texpage, hbase + float2(1.0, 1.0), uv_limits, c11, a11);
// Continuous sampling of the canonical DCCI 2x reconstruction.
texcol = lerp(lerp(c00, c10, fpart.x),
lerp(c01, c11, fpart.x), fpart.y);
ialpha = lerp(lerp(a00, a10, fpart.x),
lerp(a01, a11, fpart.x), fpart.y);
ialpha = saturate(ialpha);
if (ialpha > 0.0)
texcol.rgb = saturate(texcol.rgb / float3(ialpha, ialpha, ialpha));
else
texcol.rgb = float3(0.0, 0.0, 0.0);
texcol.a = saturate(texcol.a);
#if !TEXTURE_ALPHA_BLENDING #if !TEXTURE_ALPHA_BLENDING
ialpha = (ialpha >= 0.5) ? 1.0 : 0.0; ialpha = (ialpha >= 0.5) ? 1.0 : 0.0;
#endif #endif
@ -722,6 +1185,64 @@ void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limi
#undef P #undef P
)";
}
else if (texture_filter == GPUTextureFilter::SharpBilinear)
{
ss << R"(
void FilteredSampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords, float4 uv_limits,
out float4 texcol, out float ialpha)
{
// Coordinates are normally in native texel units. One output pixel therefore
// spans 1 / resolution_scale texels. Direct upscaled textures are the exception:
// their texture coordinates have already been multiplied by resolution_scale.
#if UPSCALED && !PALETTE && !PAGE_TEXTURE && !DISABLE_UPSCALED_DIRECT_TEXTURES
float filter_width = 1.0;
#else
float filter_width = 1.0 / u_resolution_scale;
#endif
// Determine the nearest texel and the neighboring texel in the direction of the current sample.
float2 texel_center_offset = frac(coords) - float2(0.5, 0.5);
float2 texel_offset = sign(texel_center_offset);
float4 fcoords =
max(coords.xyxy + float4(0.0, 0.0, texel_offset.x, texel_offset.y),
float4(0.0, 0.0, 0.0, 0.0));
float4 s00 = SampleFromVRAM(texpage, fcoords.xy, uv_limits);
float4 s10 = SampleFromVRAM(texpage, fcoords.zy, uv_limits);
float4 s01 = SampleFromVRAM(texpage, fcoords.xw, uv_limits);
float4 s11 = SampleFromVRAM(texpage, fcoords.zw, uv_limits);
float a00 = float(VECTOR_NEQ(s00, TRANSPARENT_PIXEL_COLOR));
float a10 = float(VECTOR_NEQ(s10, TRANSPARENT_PIXEL_COLOR));
float a01 = float(VECTOR_NEQ(s01, TRANSPARENT_PIXEL_COLOR));
float a11 = float(VECTOR_NEQ(s11, TRANSPARENT_PIXEL_COLOR));
// Keep the central portion of each texel nearest-neighbor sharp.
// The transition across each texel boundary is one output pixel wide.
float half_filter_width = filter_width * 0.5;
float sharp_region = 0.5 - half_filter_width;
float2 weights =
saturate((abs(texel_center_offset) - sharp_region) / filter_width);
texcol = lerp(lerp(s00, s10, weights.x),
lerp(s01, s11, weights.x),
weights.y);
ialpha = lerp(lerp(a00, a10, weights.x),
lerp(a01, a11, weights.x),
weights.y);
if (ialpha > 0.0)
texcol.rgb /= float3(ialpha, ialpha, ialpha);
#if !TEXTURE_ALPHA_BLENDING
ialpha = (ialpha >= 0.5) ? 1.0 : 0.0;
#endif
}
)"; )";
} }
else if (texture_filter == GPUTextureFilter::MMPX) else if (texture_filter == GPUTextureFilter::MMPX)

@ -1706,8 +1706,25 @@ GPURenderer Settings::GetRendererForRenderAPI(RenderAPI api)
} }
static constexpr const std::array s_texture_filter_names = { static constexpr const std::array s_texture_filter_names = {
"Nearest", "Bilinear", "BilinearBinAlpha", "JINC2", "JINC2BinAlpha", "xBR", "Nearest",
"xBRBinAlpha", "Scale2x", "Scale3x", "MMPX", "MMPXEnhanced", "MMPXAdvanced", "Bilinear",
"BilinearBinAlpha",
"JINC2",
"JINC2BinAlpha",
"MonotonicCubic",
"MonotonicCubicBinAlpha",
"AdaptiveDiagonal",
"AdaptiveDiagonalBinAlpha",
"DCCI",
"DCCIBinAlpha",
"xBR",
"xBRBinAlpha",
"SharpBilinear",
"Scale2x",
"Scale3x",
"MMPX",
"MMPXEnhanced",
"MMPXAdvanced",
}; };
static constexpr const std::array s_texture_filter_display_names = { static constexpr const std::array s_texture_filter_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Nearest-Neighbor", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "Nearest-Neighbor", "GPUTextureFilter"),
@ -1715,8 +1732,15 @@ static constexpr const std::array s_texture_filter_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Bilinear (No Edge Blending)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "Bilinear (No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "JINC2 (Slow)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "JINC2 (Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "JINC2 (Slow, No Edge Blending)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "JINC2 (Slow, No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Monotonic Cubic (Very Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Monotonic Cubic (Very Slow, No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Adaptive Diagonal (Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Adaptive Diagonal (Slow, No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "DCCI (Extremely Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "DCCI (Extremely Slow, No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "xBR (Very Slow)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "xBR (Very Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "xBR (Very Slow, No Edge Blending)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "xBR (Very Slow, No Edge Blending)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Sharp Bilinear", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Scale2x (EPX)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "Scale2x (EPX)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Scale3x (Slow)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "Scale3x (Slow)", "GPUTextureFilter"),
TRANSLATE_DISAMBIG_NOOP("Settings", "MMPX (Slow)", "GPUTextureFilter"), TRANSLATE_DISAMBIG_NOOP("Settings", "MMPX (Slow)", "GPUTextureFilter"),

@ -5,7 +5,7 @@
#include "common/types.h" #include "common/types.h"
inline constexpr u32 SHADER_CACHE_VERSION = 41; inline constexpr u32 SHADER_CACHE_VERSION = 42;
// Used to tag opaque keys. // Used to tag opaque keys.
enum class ShaderCacheKeyType : u16 enum class ShaderCacheKeyType : u16

@ -95,8 +95,15 @@ enum class GPUTextureFilter : u8
BilinearBinAlpha, BilinearBinAlpha,
JINC2, JINC2,
JINC2BinAlpha, JINC2BinAlpha,
MonotonicCubic,
MonotonicCubicBinAlpha,
AdaptiveDiagonal,
AdaptiveDiagonalBinAlpha,
DCCI,
DCCIBinAlpha,
xBR, xBR,
xBRBinAlpha, xBRBinAlpha,
SharpBilinear,
Scale2x, Scale2x,
Scale3x, Scale3x,
MMPX, MMPX,

Loading…
Cancel
Save