From fff2cc67503c8224857cbeef686e76236e8aeadf Mon Sep 17 00:00:00 2001 From: Filippo Scognamiglio Date: Tue, 19 Mar 2024 22:19:00 +0100 Subject: [PATCH] Rewrite CUT2 shader (#101) * Introduce antialiasing rejection in CUT2. * Reduce sharpening when antialiasing is reduced. * Improve antialiasing interpolation function. * Change edge detection rules for antialiasing reduce. * Add missing file. * Tune sharpening function and parameters. * Fix issue with edge sharpening formula. * Completely rewrite how sharpening is done. This is more flexible and efficient. * Fix soft edge weights and float packing issues. * Make float packing more robust. This fixes the issue on some GPUs. * Reintroduce pattern4. Fix attenuation function. * Fix wrong order of clamp arguments. * Move back to simpler attenuation function. * Clamp maximum sharpness according to display density. * Change soft edge contrast window. Tune the attenuation function. * Remove some old attenuation function. * Try a different approach. Adjusting threshold instead of sharpness. * Refactor the CUT2 shader. * Change how contrast is computed for softedges. Reintroduce attenuation. * Greatly improved how weights are assigned and passed to the second stage. * Fix wrong sharpness adjustments. Some performnace improvements. * Performance improvements for the new CUT2 version. * Some code cosmesis. Update default CUT2 parameters. * Change edge detection rules to triangulate more. Guarantee continuity even if some edges overlap. * Heavily simplify the second shader stage. This makes it faster and extendable in the future. * Fix CUT2 code style issues. --- libretrodroid/src/main/cpp/shadermanager.cpp | 462 ++++++++++-------- libretrodroid/src/main/cpp/shadermanager.h | 2 +- .../swordfish/libretrodroid/GLRetroView.kt | 4 + .../libretrodroid/LibretroDroid.java | 4 + .../swordfish/libretrodroid/ShaderConfig.kt | 16 +- 5 files changed, 275 insertions(+), 213 deletions(-) diff --git a/libretrodroid/src/main/cpp/shadermanager.cpp b/libretrodroid/src/main/cpp/shadermanager.cpp index cc8afa3..b5fdc9c 100644 --- a/libretrodroid/src/main/cpp/shadermanager.cpp +++ b/libretrodroid/src/main/cpp/shadermanager.cpp @@ -132,7 +132,6 @@ const std::string ShaderManager::defaultSharpFragment = "#define HIGHP highp\n" "#else\n" "#define HIGHP mediump\n" - "precision mediump float;\n" "#endif\n" "\n" "precision mediump float;\n" @@ -351,6 +350,10 @@ const std::unordered_map ShaderManager::cut2UpscalePar { "EDGE_MIN_VALUE", "0.03" }, { "EDGE_MIN_CONTRAST", "2.0" }, { "LUMA_ADJUST_GAMMA", "0" }, + { "REDUCE_ANTI_ALIASING", "0" }, + { "REDUCE_ANTI_ALIASING_AMOUNT", "0.5" }, + { "REDUCE_ANTI_ALIASING_MIN_CONTRAST_EDGE", "0.03" }, + { "REDUCE_ANTI_ALIASING_CONTRAST_THRESHOLD", "0.98" }, }; const std::string ShaderManager::cut2UpscalePass0Vertex = @@ -405,11 +408,12 @@ const std::string ShaderManager::cut2UpscalePass0Fragment = "#define HIGHP highp\n" "#else\n" "#define HIGHP mediump\n" - "precision mediump float;\n" "#endif\n" "\n" "#define EPSILON 0.02\n" "\n" + "precision lowp float;\n" + "\n" "uniform lowp sampler2D texture;\n" "\n" "varying HIGHP vec2 c01;\n" @@ -425,11 +429,7 @@ const std::string ShaderManager::cut2UpscalePass0Fragment = "varying HIGHP vec2 c13;\n" "varying HIGHP vec2 c14;\n" "\n" - "struct Pattern {\n" - " lowp float type;\n" - " bvec3 flip;\n" - " bvec2 cuts;\n" - "};\n" + "#define Range lowp vec2\n" "\n" "lowp float luma(lowp vec3 v) {\n" "#if EDGE_USE_FAST_LUMA\n" @@ -443,47 +443,100 @@ const std::string ShaderManager::cut2UpscalePass0Fragment = " return result;\n" "}\n" "\n" - "lowp float maxOf(lowp float a, lowp float b, lowp float c, lowp float d) {\n" - " return max(max(a, b), max(c, d));\n" + "Range rangeOf(lowp float a) {\n" + " return vec2(a);\n" "}\n" "\n" - "lowp float minOf(lowp float a, lowp float b, lowp float c, lowp float d) {\n" - " return min(min(a, b), min(c, d));\n" + "Range rangeOf(lowp float a, lowp float b) {\n" + " return vec2(min(a, b), max(a, b));\n" "}\n" "\n" - "bvec2 hasDiagonal(lowp float a, lowp float b, lowp float c, lowp float d, lowp float e, lowp float f) {\n" - " lowp float dab = distance(a, b);\n" - " lowp float dac = distance(a, c);\n" - " lowp float dbc = distance(b, c);\n" - " lowp float dbd = distance(b, d);\n" - " lowp float dcd = distance(c, d);\n" - " lowp float dce = distance(c, e);\n" - " lowp float ddf = distance(d, f);\n" - " lowp float ded = distance(e, d);\n" - " lowp float def = distance(e, f);\n" - "\n" - " lowp float leftInnerContrast = maxOf(dac, dce, def, dbd);\n" - " lowp float leftOuterContrast = minOf(dab, dcd, ddf, ded);\n" - " bool leftCut = max(EDGE_MIN_CONTRAST * leftInnerContrast, EDGE_MIN_VALUE) < leftOuterContrast;\n" + "Range rangeOf(Range rangeA, Range rangeB) {\n" + " return vec2(min(rangeA.x, rangeB.x), max(rangeA.y, rangeB.y));\n" + "}\n" "\n" - " lowp float rightInnerContrast = maxOf(dab, dbd, ddf, dce);\n" - " lowp float rightOuterContrast = minOf(dac, dcd, def, dbc);\n" - " bool rightCut = max(EDGE_MIN_CONTRAST * rightInnerContrast, EDGE_MIN_VALUE) < rightOuterContrast;\n" + "lowp float rangeExtension(Range range) {\n" + " return range.y - range.x;\n" + "}\n" "\n" - " return bvec2(leftCut || rightCut, leftCut);\n" + "bool isEdge(Range rangeA, Range rangeB) {\n" + " Range contrast = rangeOf(rangeA, rangeB);\n" + " lowp float maxRange = max(rangeExtension(rangeA), rangeExtension(rangeB));\n" + " return max(EDGE_MIN_CONTRAST * maxRange, EDGE_MIN_VALUE) < rangeExtension(contrast);\n" "}\n" "\n" - "bool hasDiagonal(lowp float a, lowp float b, lowp float c, lowp float d) {\n" - " lowp float diff1 = distance(a, d);\n" - " lowp float diff2 = max(\n" - " min(distance(a, b), distance(b, d)),\n" - " min(distance(a, c), distance(c, d))\n" + "bvec2 primaryCuts(lowp float a, lowp float b, lowp float c, lowp float d) {\n" + " Range diagonal1Range = rangeOf(a, b);\n" + " Range diagonal2Range = rangeOf(c, d);\n" + " lowp float maxContrast = rangeExtension(rangeOf(diagonal1Range, diagonal2Range));\n" + " return bvec2(\n" + " max(EDGE_MIN_CONTRAST * rangeExtension(diagonal1Range), EDGE_MIN_VALUE) < maxContrast,\n" + " max(EDGE_MIN_CONTRAST * rangeExtension(diagonal2Range), EDGE_MIN_VALUE) < maxContrast\n" " );\n" - " return max(EDGE_MIN_CONTRAST * diff1, EDGE_MIN_VALUE) < diff2;\n" "}\n" "\n" - "lowp float pack(bool a, bool b, bool c) {\n" - " return dot(vec3(float(a), float(b), float(c)), vec3(4.0, 16.0, 64.0)) / 255.0;\n" + "bvec2 secondaryCut(lowp float a, lowp float b, lowp float c, lowp float d, lowp float e, lowp float f) {\n" + " Range rac = rangeOf(b, d);\n" + " Range rfd = rangeOf(e, c);\n" + " Range rbe = rangeOf(a, f);\n" + "\n" + " bool leftCut = isEdge(rangeOf(rac, rbe), rfd);\n" + " bool rightCut = isEdge(rangeOf(rfd, rbe), rac);\n" + "\n" + " return bvec2(leftCut || rightCut, rightCut);\n" + "}\n" + "\n" + "lowp float quickPackBools3(bvec3 values) {\n" + " return dot(vec3(values), vec3(0.5, 0.25, 0.125));\n" + "}\n" + "\n" + "lowp float quickPackFloats2(lowp vec2 values) {\n" + " return dot(floor(values * vec2(14.0) + vec2(0.5)), vec2(0.0625, 0.00390625));\n" + "}\n" + "\n" + "lowp vec4 flipEdges(lowp vec4 edges, bvec3 flip) {\n" + " lowp vec4 result = edges;\n" + "\n" + " if (flip.x) {\n" + " result = vec4(-result.x, result.w, -result.z, result.y);\n" + " }\n" + "\n" + " if (flip.y) {\n" + " result = vec4(result.z, -result.y, result.x, -result.w);\n" + " }\n" + "\n" + " if (flip.z) {\n" + " result = vec4(result.w, result.z, result.y, result.x);\n" + " }\n" + "\n" + " return result;\n" + "}\n" + "\n" + "lowp vec4 mergeEdges(lowp vec4 softEdges, lowp vec4 hardEdges) {\n" + " lowp vec4 result = softEdges;\n" + "\n" + " result.x = abs(hardEdges.x) > 0.5 ? hardEdges.x : result.x;\n" + " result.y = abs(hardEdges.y) > 0.5 ? hardEdges.y : result.y;\n" + " result.z = abs(hardEdges.z) > 0.5 ? hardEdges.z : result.z;\n" + " result.w = abs(hardEdges.w) > 0.5 ? hardEdges.w : result.w;\n" + "\n" + " return result;\n" + "}\n" + "\n" + "lowp vec2 pixelWeights(lowp float c, lowp float n, lowp float e, lowp float s, lowp float w) {\n" + " Range range = rangeOf(rangeOf(n, s), rangeOf(e, w));\n" + " range = rangeOf(range, rangeOf(c));\n" + "\n" + " lowp float threshold = max(\n" + " REDUCE_ANTI_ALIASING_MIN_CONTRAST_EDGE,\n" + " REDUCE_ANTI_ALIASING_CONTRAST_THRESHOLD * (range.y - range.x)\n" + " );\n" + "\n" + " lowp vec2 result = vec2(0.0);\n" + " result.x = (min(distance(c, e), distance(c, w)) / (distance(e, w) + 0.1)) * step(threshold, distance(w, e));\n" + " result.y = (min(distance(c, n), distance(c, s)) / (distance(n, s) + 0.1)) * step(threshold, distance(n, s));\n" + "\n" + " return clamp(2.0 * result, vec2(0.0), vec2(1.0));\n" "}\n" "\n" "void main() {\n" @@ -514,28 +567,28 @@ const std::string ShaderManager::cut2UpscalePass0Fragment = " lowp float l14 = luma(t14);\n" "\n" " // Main diagonals\n" - " bool d05_10 = hasDiagonal(l05, l06, l09, l10);\n" - " bool d06_09 = hasDiagonal(l06, l05, l10, l09);\n" + " bvec2 primaryCuts = primaryCuts(l05, l10, l06, l09);\n" + " bool d05_10 = primaryCuts.x;\n" + " bool d06_09 = primaryCuts.y;\n" "\n" - " // Saddle fix\n" " if (d05_10 && d06_09) {\n" - " lowp float diff1 = distance(l06, l01) + distance(l11, l06) + distance(l09, l04) + distance(l14, l09);\n" - " lowp float diff2 = distance(l05, l02) + distance(l08, l05) + distance(l10, l07) + distance(l13, l10);\n" - " d05_10 = diff1 + EPSILON < diff2;\n" - " d06_09 = diff2 + EPSILON < diff1;\n" + " lowp float diff1 = 4.0 * distance(l05, l10) + distance(l06, l01) + distance(l11, l06) + distance(l09, l04) + distance(l14, l09);\n" + " lowp float diff2 = 4.0 * distance(l06, l09) + distance(l05, l02) + distance(l08, l05) + distance(l10, l07) + distance(l13, l10);\n" + " d05_10 = diff1 + 0.5 < diff2;\n" + " d06_09 = diff2 + 0.5 < diff1;\n" " }\n" "\n" " // Vertical diagonals\n" - " bvec2 d01_10 = hasDiagonal(l10, l09, l06, l05, l02, l01);\n" - " bvec2 d02_09 = hasDiagonal(l09, l10, l05, l06, l01, l02);\n" - " bvec2 d05_14 = hasDiagonal(l05, l06, l09, l10, l13, l14);\n" - " bvec2 d06_13 = hasDiagonal(l06, l05, l10, l09, l14, l13);\n" + " bvec2 d01_10 = secondaryCut(l10, l09, l06, l05, l02, l01);\n" + " bvec2 d02_09 = secondaryCut(l09, l10, l05, l06, l01, l02);\n" + " bvec2 d05_14 = secondaryCut(l05, l06, l09, l10, l13, l14);\n" + " bvec2 d06_13 = secondaryCut(l06, l05, l10, l09, l14, l13);\n" "\n" " // Horizontal diagonals\n" - " bvec2 d04_10 = hasDiagonal(l10, l06, l09, l05, l08, l04);\n" - " bvec2 d06_08 = hasDiagonal(l06, l10, l05, l09, l04, l08);\n" - " bvec2 d05_11 = hasDiagonal(l05, l09, l06, l10, l07, l11);\n" - " bvec2 d07_09 = hasDiagonal(l09, l05, l10, l06, l11, l07);\n" + " bvec2 d04_10 = secondaryCut(l10, l06, l09, l05, l08, l04);\n" + " bvec2 d06_08 = secondaryCut(l06, l10, l05, l09, l04, l08);\n" + " bvec2 d05_11 = secondaryCut(l05, l09, l06, l10, l07, l11);\n" + " bvec2 d07_09 = secondaryCut(l09, l05, l10, l06, l11, l07);\n" "\n" " bvec4 type5 = bvec4(d02_09.x && d06_08.x, d01_10.x && d05_11.x, d06_13.x && d07_09.x, d05_14.x && d04_10.x);\n" " bvec4 type4 = bvec4(d05_11.x && d06_08.x, d04_10.x && d07_09.x, d05_14.x && d02_09.x, d01_10.x && d06_13.x);\n" @@ -544,44 +597,61 @@ const std::string ShaderManager::cut2UpscalePass0Fragment = " bvec4 type2_h = bvec4(d04_10.x, d06_08.x, d05_11.x, d07_09.x);\n" " bvec2 type1 = bvec2(d05_10, d06_09);\n" "\n" - " bool bottomCut = any(bvec4(all(d05_11), all(d07_09), all(d05_14), all(d06_13)));\n" - " bool topCut = any(bvec4(all(d01_10), all(d02_09), all(d04_10), all(d06_08)));\n" + " lowp vec4 edges = vec4(0.0);\n" + " edges.x += d01_10.x ? (d01_10.y ? -1.0 : +1.0) : 0.0;\n" + " edges.x += d02_09.x ? (d02_09.y ? +1.0 : -1.0) : 0.0;\n" + " edges.y += d07_09.x ? (d07_09.y ? -1.0 : +1.0) : 0.0;\n" + " edges.y += d05_11.x ? (d05_11.y ? +1.0 : -1.0) : 0.0;\n" + " edges.z += d05_14.x ? (d05_14.y ? +1.0 : -1.0) : 0.0;\n" + " edges.z += d06_13.x ? (d06_13.y ? -1.0 : +1.0) : 0.0;\n" + " edges.w += d06_08.x ? (d06_08.y ? +1.0 : -1.0) : 0.0;\n" + " edges.w += d04_10.x ? (d04_10.y ? -1.0 : +1.0) : 0.0;\n" "\n" - " lowp vec4 final = vec4(0.0, 0.0, 0.0, 1.0);\n" - "\n" - " Pattern pattern;\n" + " lowp vec2 angles = vec2(0.0);\n" + " bvec3 flips = bvec3(false);\n" "\n" " if (any(type5)) {\n" - " pattern.type = 0.55;\n" - " pattern.flip = bvec3(type5.z, type5.x, type5.y);\n" - " pattern.cuts = bvec2(false, false);\n" + " angles = vec2(1.0, 1.0);\n" + " flips = bvec3(type5.z, type5.x, type5.y);\n" " } else if (any(type4)) {\n" - " pattern.type = 0.45;\n" - " pattern.flip = bvec3(type4.w, type4.y, type4.x || type4.y);\n" - " pattern.cuts = bvec2(bottomCut, topCut);\n" + " angles = vec2(0.0, 0.0);\n" " } else if (any(type3)) {\n" - " pattern.type = 0.35;\n" - " pattern.flip = bvec3(type3.w, type3.y, type3.x || type3.y);\n" - " pattern.cuts = bvec2(bottomCut, !topCut);\n" + " angles = vec2(0.5, 0.5);\n" + " flips = bvec3(type3.w, type3.y, type3.x || type3.y);\n" " } else if (any(type2_v)) {\n" - " pattern.type = 0.25;\n" - " pattern.flip = bvec3(type2_v.x || type2_v.w, type2_v.y || type2_v.x, false);\n" - " pattern.cuts = bvec2(bottomCut || topCut, false);\n" + " angles = vec2(0.5, 0.0);\n" + " flips = bvec3(type2_v.x || type2_v.w, type2_v.y || type2_v.x, false);\n" " } else if (any(type2_h)) {\n" - " pattern.type = 0.25;\n" - " pattern.flip = bvec3(type2_h.y || type2_h.x, type2_h.w || type2_h.x, true);\n" - " pattern.cuts = bvec2(bottomCut || topCut, false);\n" + " angles = vec2(0.5, 0.0);\n" + " flips = bvec3(type2_h.y || type2_h.x, type2_h.w || type2_h.x, true);\n" " } else if (any(type1)) {\n" - " pattern.type = 0.15;\n" - " pattern.flip = bvec3(type1.y, false, false);\n" - " pattern.cuts = bvec2(false, false);\n" + " angles = vec2(1.0, 1.0);\n" + " flips = bvec3(type1.y, false, false);\n" " }\n" "\n" + "#if REDUCE_ANTI_ALIASING\n" + " lowp vec2 w0 = pixelWeights(l05, l01, l06, l09, l04);\n" + " lowp vec2 w1 = pixelWeights(l06, l02, l07, l10, l05);\n" + " lowp vec2 w2 = pixelWeights(l09, l05, l10, l13, l08);\n" + " lowp vec2 w3 = pixelWeights(l10, l06, l11, l14, l09);\n" + "\n" + " lowp vec4 softEdges = REDUCE_ANTI_ALIASING_AMOUNT * vec4(\n" + " w1.x - w0.x,\n" + " w3.y - w1.y,\n" + " w3.x - w2.x,\n" + " w2.y - w0.y\n" + " );\n" + "\n" + " edges = mergeEdges(softEdges, edges);\n" + "#endif\n" + "\n" + " edges = flipEdges(edges, flips);\n" + "\n" " gl_FragColor = vec4(\n" - " pattern.type,\n" - " pack(pattern.flip.x, pattern.flip.y, pattern.flip.z),\n" - " pack(pattern.cuts.x, pattern.cuts.y, false),\n" - " 1.0\n" + " quickPackFloats2(angles),\n" + " quickPackBools3(flips),\n" + " quickPackFloats2(edges.xy * 0.5 + vec2(0.5)),\n" + " quickPackFloats2(edges.zw * 0.5 + vec2(0.5))\n" " );\n" "}"; @@ -624,11 +694,11 @@ const std::string ShaderManager::cut2UpscalePass1Fragment = "#define HIGHP highp\n" "#else\n" "#define HIGHP mediump\n" - "precision mediump float;\n" "#endif\n" "#define EPSILON 0.02\n" "\n" - "precision mediump float;\n" + "precision lowp float;\n" + "\n" "uniform lowp sampler2D texture;\n" "uniform lowp sampler2D previousPass;\n" "\n" @@ -653,34 +723,74 @@ const std::string ShaderManager::cut2UpscalePass1Fragment = "\n" "struct Pattern {\n" " Pixels pixels;\n" - " bool triangle;\n" - " lowp vec2 coords;\n" + " lowp vec3 weights;\n" + " lowp vec3 edgeWeights;\n" "};\n" "\n" - "lowp vec3 triangle(lowp vec2 pxCoords) {\n" - " lowp vec3 ws = vec3(0.0);\n" - " ws.x = pxCoords.y - pxCoords.x;\n" - " ws.y = 1.0 - ws.x;\n" - " ws.z = (pxCoords.y - ws.x) / (ws.y + EPSILON);\n" - " return ws;\n" + "struct Flags {\n" + " bvec3 flips;\n" + " lowp vec2 angles;\n" + " lowp vec4 edgeWeight;\n" + "};\n" + "\n" + "lowp vec2 quickUnpackFloats2(lowp float value) {\n" + " lowp vec2 result = vec2(0.0);\n" + " lowp float current = value;\n" + "\n" + " current *= 16.0;\n" + " result.x = floor(current);\n" + " current -= result.x;\n" + "\n" + " current *= 16.0;\n" + " result.y = floor(current);\n" + " current -= result.y;\n" + "\n" + " return result / 14.0;\n" "}\n" "\n" - "lowp vec3 quad(lowp vec2 pxCoords) {\n" - " return vec3(pxCoords.x, pxCoords.x, pxCoords.y);\n" + "lowp vec3 quickUnpackFloats3(lowp float value) {\n" + " lowp vec3 result = vec3(0.0);\n" + " lowp float current = value;\n" + "\n" + " current *= 2.0;\n" + " result.x = floor(current);\n" + " current -= result.x;\n" + "\n" + " current *= 2.0;\n" + " result.y = floor(current);\n" + " current -= result.y;\n" + "\n" + " current *= 2.0;\n" + " result.z = floor(current);\n" + " current -= result.z;\n" + "\n" + " return result;\n" "}\n" "\n" - "lowp float linearStep(lowp float edge0, lowp float edge1, lowp float t) {\n" - " return clamp((t - edge0) / (edge1 - edge0 + EPSILON), 0.0, 1.0);\n" + "bvec3 quickUnpackBools3(lowp float value) {\n" + " return greaterThan(quickUnpackFloats3(value + 0.0625), vec3(0.5));\n" "}\n" "\n" - "lowp float sharpSmooth(lowp float t, lowp float sharpness) {\n" - " return linearStep(sharpness, 1.0 - sharpness, t);\n" + "Flags parseFlags(lowp vec4 flagsPixel) {\n" + " Flags flags;\n" + " flags.angles = quickUnpackFloats2(flagsPixel.x + 0.001953125);\n" + " flags.flips = quickUnpackBools3(flagsPixel.y);\n" + " flags.edgeWeight = vec4(\n" + " quickUnpackFloats2(flagsPixel.z + 0.001953125) - vec2(0.5),\n" + " quickUnpackFloats2(flagsPixel.w + 0.001953125) - vec2(0.5)\n" + " );\n" + " return flags;\n" + "}\n" + "\n" + "lowp float linearStep(lowp float edge0, lowp float edge1, lowp float t) {\n" + " return clamp((t - edge0) / (edge1 - edge0), 0.0, 1.0);\n" "}\n" "\n" "lowp float sharpness(lowp float l1, lowp float l2) {\n" "#if USE_DYNAMIC_BLEND\n" + " const lowp float blendDiffInv = 1.0 / (BLEND_MAX_CONTRAST_EDGE - BLEND_MIN_CONTRAST_EDGE);\n" " lowp float lumaDiff = abs(l1 - l2);\n" - " lowp float contrast = linearStep(BLEND_MIN_CONTRAST_EDGE, BLEND_MAX_CONTRAST_EDGE, lumaDiff);\n" + " lowp float contrast = clamp((lumaDiff - BLEND_MIN_CONTRAST_EDGE) * blendDiffInv, 0.0, 1.0);\n" " lowp float result = mix(BLEND_MIN_SHARPNESS * 0.5, BLEND_MAX_SHARPNESS * 0.5, contrast);\n" "#else\n" " lowp float result = STATIC_BLEND_SHARPNESS * 0.5;\n" @@ -688,94 +798,56 @@ const std::string ShaderManager::cut2UpscalePass1Fragment = " return result;\n" "}\n" "\n" - "lowp vec3 blend(lowp vec3 a, lowp vec3 b, lowp float t) {\n" - " return mix(a, b, sharpSmooth(t, sharpness(luma(a), luma(b))));\n" + "lowp vec3 blend(lowp vec3 a, lowp vec3 b, lowp float displacement, lowp float t) {\n" + " lowp float available = 1.0 - abs(displacement);\n" + " lowp float sharpness = sharpness(luma(a), luma(b)) * available;\n" + " lowp float nt = clamp((t - max(0.0, displacement) - sharpness) / (available - 2.0 * sharpness + EPSILON), 0.0 , 1.0);\n" + " return mix(a, b, nt);\n" "}\n" "\n" - "lowp vec3 unpack(lowp float values) {\n" - " return vec3(floor(mod(values / 4.0, 4.0)), floor(mod(values / 16.0, 4.0)), floor(mod(values / 64.0, 4.0)));\n" + "lowp vec3 triangleWeights(lowp vec2 pxCoords, lowp float angle) {\n" + " lowp vec3 weights = vec3(0.0);\n" + " weights.x = pxCoords.y - pxCoords.x / angle;\n" + " weights.y = (1.0 - weights.x) * angle;\n" + " weights.z = pxCoords.x / (weights.y + EPSILON);\n" + " return weights;\n" "}\n" "\n" - "Pattern pattern0(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" - " return Pattern(pixels, false, pxCoords);\n" + "lowp vec3 quadWeights(lowp vec2 pxCoords, lowp vec2 angles, lowp vec2 xs) {\n" + " lowp float relative = (pxCoords.x - xs.x) / (xs.y - xs.x);\n" + " return vec3(\n" + " relative * (1.0 - angles.y),\n" + " relative * (1.0 - angles.x) + angles.x,\n" + " pxCoords.y\n" + " );\n" "}\n" "\n" - "Pattern pattern1(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" + "Pattern pattern(Pixels pixels, lowp vec4 edgeWeights, lowp vec2 angles, lowp vec2 pxCoords) {\n" " Pattern result;\n" - " if (pxCoords.y > pxCoords.x) {\n" - " result.pixels = Pixels(pixels.p0, pixels.p2, pixels.p2, pixels.p3);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.x, pxCoords.y);\n" - " } else {\n" - " result.pixels = Pixels(pixels.p0, pixels.p1, pixels.p1, pixels.p3);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.y, pxCoords.x);\n" - " }\n" - " return result;\n" - "}\n" "\n" - "Pattern pattern2(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" - " Pattern result;\n" - " if (pxCoords.y > 2.0 * pxCoords.x) {\n" - " result.pixels = Pixels(pixels.p0, pixels.p2, pixels.p2, cd);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.x * 2.0, pxCoords.y);\n" - " } else {\n" - " result.pixels = Pixels(pixels.p0, pixels.p1, cd, pixels.p3);\n" - " result.triangle = false;\n" - " result.coords = vec2((pxCoords.x - 0.5 * pxCoords.y) / (1.0 - 0.5 * pxCoords.y + EPSILON), pxCoords.y);\n" - " }\n" - " return result;\n" - "}\n" + " lowp vec2 xs = vec2(pxCoords.y * angles.x, 1.0 - (1.0 - pxCoords.y) * angles.y);\n" "\n" - "Pattern pattern3(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" - " Pattern result;\n" - " if (pxCoords.y > 2.0 * pxCoords.x) {\n" - " result.pixels = Pixels(pixels.p0, pixels.p2, pixels.p2, cd);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.x * 2.0, pxCoords.y);\n" - " } else if (pxCoords.y < 2.0 * pxCoords.x - 1.0) {\n" - " result.pixels = Pixels(pixels.p3, pixels.p1, pixels.p1, ab);\n" - " result.triangle = true;\n" - " result.coords = vec2((1.0 - pxCoords.x) * 2.0, 1.0 - pxCoords.y);\n" - " } else {\n" - " result.pixels = Pixels(pixels.p0, ab, cd, pixels.p3);\n" - " result.triangle = false;\n" - " result.coords = vec2(2.0 * (pxCoords.x - 0.5 * pxCoords.y), pxCoords.y);\n" - " }\n" - " return result;\n" - "}\n" + " bool firstTriangle = pxCoords.x <= xs.x;\n" + " bool secondTriangle = pxCoords.x >= xs.y;\n" + " bool isTriangle = firstTriangle || secondTriangle;\n" "\n" - "Pattern pattern4(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" - " Pattern result;\n" - " lowp float splitX = 0.5 * (1.0 - min(pxCoords.y, 1.0 - pxCoords.y));\n" - " if (pxCoords.x < splitX) {\n" - " result.pixels = Pixels(pixels.p0, ab, pixels.p2, cd);\n" - " result.triangle = false;\n" - " result.coords = vec2(pxCoords.x / splitX, pxCoords.y);\n" - " } else {\n" - " result.pixels = Pixels(ab, pixels.p1, cd, pixels.p3);\n" - " result.triangle = false;\n" - " result.coords = vec2((pxCoords.x - splitX) / (1.0 - splitX), pxCoords.y);\n" - " }\n" - " return result;\n" - "}\n" + " result.weights = isTriangle\n" + " ? triangleWeights(firstTriangle ? pxCoords : pxCoords.yx, firstTriangle ? angles.x : (1.0 / angles.y))\n" + " : quadWeights(pxCoords, angles, xs);\n" + "\n" + " result.pixels = Pixels(\n" + " pixels.p0,\n" + " firstTriangle ? pixels.p2 : pixels.p1,\n" + " secondTriangle ? pixels.p1 : pixels.p2,\n" + " pixels.p3\n" + " );\n" + "\n" + " result.edgeWeights = vec3(\n" + " firstTriangle ? edgeWeights.w : edgeWeights.x,\n" + " secondTriangle ? edgeWeights.y : edgeWeights.z,\n" + " mix(edgeWeights.w, edgeWeights.y, pxCoords.x)\n" + " );\n" "\n" - "Pattern pattern5(Pixels pixels, lowp vec3 ab, lowp vec3 cd, lowp vec2 pxCoords) {\n" - " Pattern result;\n" - " if (pxCoords.y > pxCoords.x + 0.5) {\n" - " result.pixels = Pixels(pixels.p0, pixels.p2, pixels.p2, pixels.p3);\n" - " result.triangle = true;\n" - " result.coords = vec2(2.0 * pxCoords.x, 2.0 * (pxCoords.y - 0.5));\n" - " } else if (pxCoords.y > pxCoords.x) {\n" - " result.pixels = Pixels(pixels.p0, pixels.p0, pixels.p3, pixels.p3);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.x, pxCoords.y);\n" - " } else {\n" - " result.pixels = Pixels(pixels.p0, pixels.p1, pixels.p1, pixels.p3);\n" - " result.triangle = true;\n" - " result.coords = vec2(pxCoords.y, pxCoords.x);\n" - " }\n" " return result;\n" "}\n" "\n" @@ -785,59 +857,37 @@ const std::string ShaderManager::cut2UpscalePass1Fragment = " lowp vec3 t09 = texture2D(texture, c09).rgb;\n" " lowp vec3 t10 = texture2D(texture, c10).rgb;\n" "\n" + " lowp vec4 flagsPixel = texture2D(previousPass, passCoords);\n" + " Flags flags = parseFlags(flagsPixel);\n" " Pixels pixels = Pixels(t05, t06, t09, t10);\n" "\n" - " lowp vec3 flagsTexture = texture2D(previousPass, passCoords).xyz;\n" - "\n" - " int patternType = int(flagsTexture.x * 10.0);\n" - " lowp vec3 transform = unpack(floor(flagsTexture.y * 255.0 + 0.5));\n" - " lowp vec3 patternFlags = unpack(floor(flagsTexture.z * 255.0 + 0.5));\n" - "\n" " lowp vec2 pxCoords = fract(screenCoords);\n" "\n" - " if (transform.x > 0.0) {\n" + " if (flags.flips.x) {\n" " pixels = Pixels(pixels.p1, pixels.p0, pixels.p3, pixels.p2);\n" " pxCoords.x = 1.0 - pxCoords.x;\n" " }\n" "\n" - " if (transform.y > 0.0) {\n" + " if (flags.flips.y) {\n" " pixels = Pixels(pixels.p2, pixels.p3, pixels.p0, pixels.p1);\n" " pxCoords.y = 1.0 - pxCoords.y;\n" " }\n" "\n" - " if (transform.z > 0.0) {\n" + " if (flags.flips.z) {\n" " pixels = Pixels(pixels.p0, pixels.p2, pixels.p1, pixels.p3);\n" " pxCoords = pxCoords.yx;\n" " }\n" "\n" - " lowp vec3 ab = patternFlags.y > 0.5 ? pixels.p0 : pixels.p1;\n" - " lowp vec3 cd = patternFlags.x > 0.5 ? pixels.p2 : pixels.p3;\n" - "\n" - " Pattern pattern;\n" - "\n" - " if (patternType == 0) {\n" - " pattern = pattern0(pixels, ab, cd, pxCoords);\n" - " } else if (patternType == 1) {\n" - " pattern = pattern1(pixels, ab, cd, pxCoords);\n" - " } else if (patternType == 2) {\n" - " pattern = pattern2(pixels, ab, cd, pxCoords);\n" - " } else if (patternType == 3) {\n" - " pattern = pattern3(pixels, ab, cd, pxCoords);\n" - " } else if (patternType == 4) {\n" - " pattern = pattern4(pixels, ab, cd, pxCoords);\n" - " } else {\n" - " pattern = pattern5(pixels, ab, cd, pxCoords);\n" - " }\n" - "\n" - " lowp vec3 weights = pattern.triangle ? triangle(pattern.coords) : quad(pattern.coords);\n" + " Pattern pattern = pattern(pixels, flags.edgeWeight, flags.angles, pxCoords);\n" "\n" " lowp vec3 final = blend(\n" - " blend(pattern.pixels.p0, pattern.pixels.p1, weights.x),\n" - " blend(pattern.pixels.p2, pattern.pixels.p3, weights.y),\n" - " weights.z\n" + " blend(pattern.pixels.p0, pattern.pixels.p1, pattern.edgeWeights.x, pattern.weights.x),\n" + " blend(pattern.pixels.p2, pattern.pixels.p3, pattern.edgeWeights.y, pattern.weights.y),\n" + " pattern.edgeWeights.z,\n" + " pattern.weights.z\n" " );\n" "\n" - " gl_FragColor = vec4(final, 1.0);\n" + " gl_FragColor = vec4(final.rgb, 1.0);\n" "}"; ShaderManager::Chain ShaderManager::getShader(const ShaderManager::Config& config) { diff --git a/libretrodroid/src/main/cpp/shadermanager.h b/libretrodroid/src/main/cpp/shadermanager.h index 089751b..f467c80 100644 --- a/libretrodroid/src/main/cpp/shadermanager.h +++ b/libretrodroid/src/main/cpp/shadermanager.h @@ -49,7 +49,7 @@ class ShaderManager { SHADER_LCD = 2, SHADER_SHARP = 3, SHADER_UPSCALE_CUT = 4, - SHADER_UPSCALE_CUT2 = 5 + SHADER_UPSCALE_CUT2 = 5, }; struct Config { diff --git a/libretrodroid/src/main/java/com/swordfish/libretrodroid/GLRetroView.kt b/libretrodroid/src/main/java/com/swordfish/libretrodroid/GLRetroView.kt index 6e849ba..64eb0fb 100644 --- a/libretrodroid/src/main/java/com/swordfish/libretrodroid/GLRetroView.kt +++ b/libretrodroid/src/main/java/com/swordfish/libretrodroid/GLRetroView.kt @@ -404,6 +404,10 @@ class GLRetroView( LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_EDGE_MIN_VALUE to toParam(config.edgeMinValue), LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_EDGE_MIN_CONTRAST to toParam(config.edgeMinContrast), LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_LUMA_ADJUST_GAMMA to toParam(config.lumaAdjustGamma), + LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING to toParam(config.reduceAntiAliasing), + LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_AMOUNT to toParam(config.reduceAntiAliasingAmount), + LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_MIN_CONTRAST_EDGE to toParam(config.reduceAntiAliasingMinEdge), + LibretroDroid.SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_CONTRAST_THRESHOLD to toParam(config.reduceAntiAliasingContrastThreshold), ) ) } diff --git a/libretrodroid/src/main/java/com/swordfish/libretrodroid/LibretroDroid.java b/libretrodroid/src/main/java/com/swordfish/libretrodroid/LibretroDroid.java index 23052a7..fdf83b7 100644 --- a/libretrodroid/src/main/java/com/swordfish/libretrodroid/LibretroDroid.java +++ b/libretrodroid/src/main/java/com/swordfish/libretrodroid/LibretroDroid.java @@ -58,6 +58,10 @@ public class LibretroDroid { public static final String SHADER_UPSCALE_CUT2_PARAM_EDGE_MIN_VALUE = "EDGE_MIN_VALUE"; public static final String SHADER_UPSCALE_CUT2_PARAM_EDGE_MIN_CONTRAST = "EDGE_MIN_CONTRAST"; public static final String SHADER_UPSCALE_CUT2_PARAM_LUMA_ADJUST_GAMMA = "LUMA_ADJUST_GAMMA"; + public static final String SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING = "REDUCE_ANTI_ALIASING"; + public static final String SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_AMOUNT = "REDUCE_ANTI_ALIASING_AMOUNT"; + public static final String SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_MIN_CONTRAST_EDGE = "REDUCE_ANTI_ALIASING_MIN_CONTRAST_EDGE"; + public static final String SHADER_UPSCALE_CUT2_PARAM_REDUCE_ANTI_ALIASING_CONTRAST_THRESHOLD = "REDUCE_ANTI_ALIASING_CONTRAST_THRESHOLD"; public static final int ERROR_LOAD_LIBRARY = 0; public static final int ERROR_LOAD_GAME = 1; diff --git a/libretrodroid/src/main/java/com/swordfish/libretrodroid/ShaderConfig.kt b/libretrodroid/src/main/java/com/swordfish/libretrodroid/ShaderConfig.kt index fda70a6..015bd00 100644 --- a/libretrodroid/src/main/java/com/swordfish/libretrodroid/ShaderConfig.kt +++ b/libretrodroid/src/main/java/com/swordfish/libretrodroid/ShaderConfig.kt @@ -39,14 +39,18 @@ sealed interface ShaderConfig { data class CUT2( val useDynamicBlend: Boolean = true, - val blendMinContrastEdge: Float = 0.0f, - val blendMaxContrastEdge: Float = 1.0f, + val blendMinContrastEdge: Float = 0.2f, + val blendMaxContrastEdge: Float = 0.6f, val blendMinSharpness: Float = 0.0f, - val blendMaxSharpness: Float = 1.0f, - val staticSharpness: Float = 0.5f, + val blendMaxSharpness: Float = 0.75f, + val staticSharpness: Float = 0.75f, val edgeUseFastLuma: Boolean = false, - val edgeMinValue: Float = 0.03f, - val edgeMinContrast: Float = 1.20f, + val edgeMinValue: Float = 0.05f, + val edgeMinContrast: Float = 2.0f, val lumaAdjustGamma: Boolean = false, + val reduceAntiAliasing: Boolean = true, + val reduceAntiAliasingAmount: Float = 1.00f, + val reduceAntiAliasingMinEdge: Float = 0.02f, + val reduceAntiAliasingContrastThreshold: Float = 0.98f, ) : ShaderConfig }