From 71dcaf4b07b308e1e968ed7e04cb3949c84c73a7 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:11:32 +0700 Subject: [PATCH] Optimize Clarity boost shader --- dist/better-xcloud.lite.user.js | 6 +- dist/better-xcloud.user.js | 6 +- src/modules/player/shaders/clarity_boost.fs | 100 ++++++++------------ 3 files changed, 48 insertions(+), 64 deletions(-) diff --git a/dist/better-xcloud.lite.user.js b/dist/better-xcloud.lite.user.js index 8aefc6a..fd970be 100644 --- a/dist/better-xcloud.lite.user.js +++ b/dist/better-xcloud.lite.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Better xCloud (Lite) // @namespace https://github.com/redphx -// @version 5.8.2 +// @version 5.8.3-beta // @description Improve Xbox Cloud Gaming (xCloud) experience // @author redphx // @license MIT @@ -118,7 +118,7 @@ function deepClone(obj) { if (!obj) return {}; return JSON.parse(JSON.stringify(obj)); } -var SCRIPT_VERSION = "5.8.2", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface; +var SCRIPT_VERSION = "5.8.3-beta", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface; UserAgent.init(); var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = { supportedRegion: !0, @@ -5048,7 +5048,7 @@ function patchSdpBitrate(sdp, video, audio) { return lines.join("\r\n"); } var clarity_boost_default = "attribute vec4 position;\n\nvoid main() {\ngl_Position = position;\n}\n"; -var clarity_boost_default2 = "precision mediump float;\nuniform sampler2D data;\nuniform vec2 iResolution;\n\nconst int FILTER_UNSHARP_MASKING = 1;\nconst int FILTER_CAS = 2;\n\nconst float CAS_CONTRAST_PEAK = (-3.0 * 0.8 + 8.0);\n\nconst vec3 LUMINOSITY_FACTOR = vec3(0.2126, 0.7152, 0.0722);\n\nuniform int filterId;\nuniform float sharpenFactor;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\n\nvec3 clarityBoost(sampler2D tex, vec2 coord) {\nvec2 texelSize = 1.0 / iResolution.xy;\n\nvec3 a = texture2D(tex, coord + texelSize * vec2(-1, 1)).rgb;\nvec3 b = texture2D(tex, coord + texelSize * vec2(0, 1)).rgb;\nvec3 c = texture2D(tex, coord + texelSize * vec2(1, 1)).rgb;\n\nvec3 d = texture2D(tex, coord + texelSize * vec2(-1, 0)).rgb;\nvec3 e = texture2D(tex, coord).rgb;\nvec3 f = texture2D(tex, coord + texelSize * vec2(1, 0)).rgb;\n\nvec3 g = texture2D(tex, coord + texelSize * vec2(-1, -1)).rgb;\nvec3 h = texture2D(tex, coord + texelSize * vec2(0, -1)).rgb;\nvec3 i = texture2D(tex, coord + texelSize * vec2(1, -1)).rgb;\n\nif (filterId == FILTER_CAS) {\nvec3 minRgb = min(min(min(d, e), min(f, b)), h);\nvec3 minRgb2 = min(min(a, c), min(g, i));\nminRgb += min(minRgb, minRgb2);\n\nvec3 maxRgb = max(max(max(d, e), max(f, b)), h);\nvec3 maxRgb2 = max(max(a, c), max(g, i));\nmaxRgb += max(maxRgb, maxRgb2);\n\nvec3 reciprocalMaxRgb = 1.0 / maxRgb;\nvec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\n\namplifyRgb = inversesqrt(amplifyRgb);\n\nvec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK));\nvec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\n\nvec3 window = (b + d) + (f + h);\nvec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\n\noutColor = mix(e, outColor, sharpenFactor / 2.0);\n\nreturn outColor;\n} else if (filterId == FILTER_UNSHARP_MASKING) {\nvec3 gaussianBlur = (a + c + g + i) * 1.0 +\n(b + d + f + h) * 2.0 +\ne * 4.0;\ngaussianBlur /= 16.0;\n\nreturn e + (e - gaussianBlur) * sharpenFactor / 3.0;\n}\n\nreturn e;\n}\n\nvoid main() {\nvec3 color;\nvec2 uv = gl_FragCoord.xy / iResolution.xy;\n\nif (sharpenFactor > 0.0) {\ncolor = clarityBoost(data, uv);\n} else {\ncolor = texture2D(data, uv).rgb;\n}\n\nif (saturation != 1.0) {\nvec3 grayscale = vec3(dot(color, LUMINOSITY_FACTOR));\ncolor = mix(grayscale, color, saturation);\n}\n\nif (contrast != 1.0) {\ncolor = 0.5 + contrast * (color - 0.5);\n}\n\nif (brightness != 1.0) {\ncolor = brightness * color;\n}\n\ngl_FragColor = vec4(color, 1.0);\n}\n"; +var clarity_boost_default2 = "precision mediump float;\nuniform sampler2D data;\nuniform vec2 iResolution;\n\nconst int FILTER_UNSHARP_MASKING = 1;\n\nconst float CAS_CONTRAST_PEAK = 0.8 * -3.0 + 8.0;\n\nconst vec3 LUMINOSITY_FACTOR = vec3(0.2126, 0.7152, 0.0722);\n\nuniform int filterId;\nuniform float sharpenFactor;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\n\nvec3 clarityBoost(sampler2D tex, vec2 coord, vec3 e) {\nvec2 texelSize = 1.0 / iResolution.xy;\n\nvec3 a = texture2D(tex, coord + texelSize * vec2(-1, 1)).rgb;\nvec3 b = texture2D(tex, coord + texelSize * vec2(0, 1)).rgb;\nvec3 c = texture2D(tex, coord + texelSize * vec2(1, 1)).rgb;\n\nvec3 d = texture2D(tex, coord + texelSize * vec2(-1, 0)).rgb;\nvec3 f = texture2D(tex, coord + texelSize * vec2(1, 0)).rgb;\n\nvec3 g = texture2D(tex, coord + texelSize * vec2(-1, -1)).rgb;\nvec3 h = texture2D(tex, coord + texelSize * vec2(0, -1)).rgb;\nvec3 i = texture2D(tex, coord + texelSize * vec2(1, -1)).rgb;\n\nif (filterId == FILTER_UNSHARP_MASKING) {\nvec3 gaussianBlur = (a + c + g + i) * 1.0 + (b + d + f + h) * 2.0 + e * 4.0;\ngaussianBlur /= 16.0;\n\nreturn e + (e - gaussianBlur) * sharpenFactor / 3.0;\n}\n\nvec3 minRgb = min(min(min(d, e), min(f, b)), h);\nminRgb += min(min(a, c), min(g, i));\n\nvec3 maxRgb = max(max(max(d, e), max(f, b)), h);\nmaxRgb += max(max(a, c), max(g, i));\n\nvec3 reciprocalMaxRgb = 1.0 / maxRgb;\nvec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\n\namplifyRgb = inversesqrt(amplifyRgb);\n\nvec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK));\nvec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\n\nvec3 window = b + d + f + h;\nvec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\n\nreturn mix(e, outColor, sharpenFactor / 2.0);\n}\n\nvoid main() {\nvec2 uv = gl_FragCoord.xy / iResolution.xy;\nvec3 color = texture2D(data, uv).rgb;\n\ncolor = sharpenFactor > 0.0 ? clarityBoost(data, uv, color) : color;\n\ncolor = saturation != 1.0 ? mix(vec3(dot(color, LUMINOSITY_FACTOR)), color, saturation) : color;\n\ncolor = contrast * (color - 0.5) + 0.5;\n\ncolor = brightness * color;\n\ngl_FragColor = vec4(color, 1.0);\n}\n"; var LOG_TAG3 = "WebGL2Player"; class WebGL2Player { $video; diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js index a23af0b..52b2cf3 100644 --- a/dist/better-xcloud.user.js +++ b/dist/better-xcloud.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Better xCloud // @namespace https://github.com/redphx -// @version 5.8.2 +// @version 5.8.3-beta // @description Improve Xbox Cloud Gaming (xCloud) experience // @author redphx // @license MIT @@ -120,7 +120,7 @@ function deepClone(obj) { if (!obj) return {}; return JSON.parse(JSON.stringify(obj)); } -var SCRIPT_VERSION = "5.8.2", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface; +var SCRIPT_VERSION = "5.8.3-beta", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface; UserAgent.init(); var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = { supportedRegion: !0, @@ -6957,7 +6957,7 @@ function patchSdpBitrate(sdp, video, audio) { return lines.join("\r\n"); } var clarity_boost_default = "attribute vec4 position;\n\nvoid main() {\ngl_Position = position;\n}\n"; -var clarity_boost_default2 = "precision mediump float;\nuniform sampler2D data;\nuniform vec2 iResolution;\n\nconst int FILTER_UNSHARP_MASKING = 1;\nconst int FILTER_CAS = 2;\n\nconst float CAS_CONTRAST_PEAK = (-3.0 * 0.8 + 8.0);\n\nconst vec3 LUMINOSITY_FACTOR = vec3(0.2126, 0.7152, 0.0722);\n\nuniform int filterId;\nuniform float sharpenFactor;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\n\nvec3 clarityBoost(sampler2D tex, vec2 coord) {\nvec2 texelSize = 1.0 / iResolution.xy;\n\nvec3 a = texture2D(tex, coord + texelSize * vec2(-1, 1)).rgb;\nvec3 b = texture2D(tex, coord + texelSize * vec2(0, 1)).rgb;\nvec3 c = texture2D(tex, coord + texelSize * vec2(1, 1)).rgb;\n\nvec3 d = texture2D(tex, coord + texelSize * vec2(-1, 0)).rgb;\nvec3 e = texture2D(tex, coord).rgb;\nvec3 f = texture2D(tex, coord + texelSize * vec2(1, 0)).rgb;\n\nvec3 g = texture2D(tex, coord + texelSize * vec2(-1, -1)).rgb;\nvec3 h = texture2D(tex, coord + texelSize * vec2(0, -1)).rgb;\nvec3 i = texture2D(tex, coord + texelSize * vec2(1, -1)).rgb;\n\nif (filterId == FILTER_CAS) {\nvec3 minRgb = min(min(min(d, e), min(f, b)), h);\nvec3 minRgb2 = min(min(a, c), min(g, i));\nminRgb += min(minRgb, minRgb2);\n\nvec3 maxRgb = max(max(max(d, e), max(f, b)), h);\nvec3 maxRgb2 = max(max(a, c), max(g, i));\nmaxRgb += max(maxRgb, maxRgb2);\n\nvec3 reciprocalMaxRgb = 1.0 / maxRgb;\nvec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\n\namplifyRgb = inversesqrt(amplifyRgb);\n\nvec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK));\nvec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\n\nvec3 window = (b + d) + (f + h);\nvec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\n\noutColor = mix(e, outColor, sharpenFactor / 2.0);\n\nreturn outColor;\n} else if (filterId == FILTER_UNSHARP_MASKING) {\nvec3 gaussianBlur = (a + c + g + i) * 1.0 +\n(b + d + f + h) * 2.0 +\ne * 4.0;\ngaussianBlur /= 16.0;\n\nreturn e + (e - gaussianBlur) * sharpenFactor / 3.0;\n}\n\nreturn e;\n}\n\nvoid main() {\nvec3 color;\nvec2 uv = gl_FragCoord.xy / iResolution.xy;\n\nif (sharpenFactor > 0.0) {\ncolor = clarityBoost(data, uv);\n} else {\ncolor = texture2D(data, uv).rgb;\n}\n\nif (saturation != 1.0) {\nvec3 grayscale = vec3(dot(color, LUMINOSITY_FACTOR));\ncolor = mix(grayscale, color, saturation);\n}\n\nif (contrast != 1.0) {\ncolor = 0.5 + contrast * (color - 0.5);\n}\n\nif (brightness != 1.0) {\ncolor = brightness * color;\n}\n\ngl_FragColor = vec4(color, 1.0);\n}\n"; +var clarity_boost_default2 = "precision mediump float;\nuniform sampler2D data;\nuniform vec2 iResolution;\n\nconst int FILTER_UNSHARP_MASKING = 1;\n\nconst float CAS_CONTRAST_PEAK = 0.8 * -3.0 + 8.0;\n\nconst vec3 LUMINOSITY_FACTOR = vec3(0.2126, 0.7152, 0.0722);\n\nuniform int filterId;\nuniform float sharpenFactor;\nuniform float brightness;\nuniform float contrast;\nuniform float saturation;\n\nvec3 clarityBoost(sampler2D tex, vec2 coord, vec3 e) {\nvec2 texelSize = 1.0 / iResolution.xy;\n\nvec3 a = texture2D(tex, coord + texelSize * vec2(-1, 1)).rgb;\nvec3 b = texture2D(tex, coord + texelSize * vec2(0, 1)).rgb;\nvec3 c = texture2D(tex, coord + texelSize * vec2(1, 1)).rgb;\n\nvec3 d = texture2D(tex, coord + texelSize * vec2(-1, 0)).rgb;\nvec3 f = texture2D(tex, coord + texelSize * vec2(1, 0)).rgb;\n\nvec3 g = texture2D(tex, coord + texelSize * vec2(-1, -1)).rgb;\nvec3 h = texture2D(tex, coord + texelSize * vec2(0, -1)).rgb;\nvec3 i = texture2D(tex, coord + texelSize * vec2(1, -1)).rgb;\n\nif (filterId == FILTER_UNSHARP_MASKING) {\nvec3 gaussianBlur = (a + c + g + i) * 1.0 + (b + d + f + h) * 2.0 + e * 4.0;\ngaussianBlur /= 16.0;\n\nreturn e + (e - gaussianBlur) * sharpenFactor / 3.0;\n}\n\nvec3 minRgb = min(min(min(d, e), min(f, b)), h);\nminRgb += min(min(a, c), min(g, i));\n\nvec3 maxRgb = max(max(max(d, e), max(f, b)), h);\nmaxRgb += max(max(a, c), max(g, i));\n\nvec3 reciprocalMaxRgb = 1.0 / maxRgb;\nvec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);\n\namplifyRgb = inversesqrt(amplifyRgb);\n\nvec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK));\nvec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);\n\nvec3 window = b + d + f + h;\nvec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);\n\nreturn mix(e, outColor, sharpenFactor / 2.0);\n}\n\nvoid main() {\nvec2 uv = gl_FragCoord.xy / iResolution.xy;\nvec3 color = texture2D(data, uv).rgb;\n\ncolor = sharpenFactor > 0.0 ? clarityBoost(data, uv, color) : color;\n\ncolor = saturation != 1.0 ? mix(vec3(dot(color, LUMINOSITY_FACTOR)), color, saturation) : color;\n\ncolor = contrast * (color - 0.5) + 0.5;\n\ncolor = brightness * color;\n\ngl_FragColor = vec4(color, 1.0);\n}\n"; var LOG_TAG7 = "WebGL2Player"; class WebGL2Player { $video; diff --git a/src/modules/player/shaders/clarity_boost.fs b/src/modules/player/shaders/clarity_boost.fs index 8b5b03a..def5542 100644 --- a/src/modules/player/shaders/clarity_boost.fs +++ b/src/modules/player/shaders/clarity_boost.fs @@ -3,10 +3,10 @@ uniform sampler2D data; uniform vec2 iResolution; const int FILTER_UNSHARP_MASKING = 1; -const int FILTER_CAS = 2; +// const int FILTER_CAS = 2; // constrast = 0.8 -const float CAS_CONTRAST_PEAK = (-3.0 * 0.8 + 8.0); +const float CAS_CONTRAST_PEAK = 0.8 * -3.0 + 8.0; // Luminosity factor const vec3 LUMINOSITY_FACTOR = vec3(0.2126, 0.7152, 0.0722); @@ -17,7 +17,7 @@ uniform float brightness; uniform float contrast; uniform float saturation; -vec3 clarityBoost(sampler2D tex, vec2 coord) { +vec3 clarityBoost(sampler2D tex, vec2 coord, vec3 e) { vec2 texelSize = 1.0 / iResolution.xy; // Load a collection of samples in a 3x3 neighorhood, where e is the current pixel. @@ -29,84 +29,68 @@ vec3 clarityBoost(sampler2D tex, vec2 coord) { vec3 c = texture2D(tex, coord + texelSize * vec2(1, 1)).rgb; vec3 d = texture2D(tex, coord + texelSize * vec2(-1, 0)).rgb; - vec3 e = texture2D(tex, coord).rgb; vec3 f = texture2D(tex, coord + texelSize * vec2(1, 0)).rgb; vec3 g = texture2D(tex, coord + texelSize * vec2(-1, -1)).rgb; vec3 h = texture2D(tex, coord + texelSize * vec2(0, -1)).rgb; vec3 i = texture2D(tex, coord + texelSize * vec2(1, -1)).rgb; - if (filterId == FILTER_CAS) { - // Soft min and max. - // a b c b - // d e f * 0.5 + d e f * 0.5 - // g h i h - // These are 2.0x bigger (factored out the extra multiply). - vec3 minRgb = min(min(min(d, e), min(f, b)), h); - vec3 minRgb2 = min(min(a, c), min(g, i)); - minRgb += min(minRgb, minRgb2); - - vec3 maxRgb = max(max(max(d, e), max(f, b)), h); - vec3 maxRgb2 = max(max(a, c), max(g, i)); - maxRgb += max(maxRgb, maxRgb2); - - // Smooth minimum distance to signal limit divided by smooth max. - vec3 reciprocalMaxRgb = 1.0 / maxRgb; - vec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0); - - // Shaping amount of sharpening. - amplifyRgb = inversesqrt(amplifyRgb); - - vec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK)); - vec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0); - - // 0 w 0 - // Filter shape: w 1 w - // 0 w 0 - vec3 window = (b + d) + (f + h); - vec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0); - - outColor = mix(e, outColor, sharpenFactor / 2.0); - - return outColor; - } else if (filterId == FILTER_UNSHARP_MASKING) { - vec3 gaussianBlur = (a + c + g + i) * 1.0 + - (b + d + f + h) * 2.0 + - e * 4.0; + // USM + if (filterId == FILTER_UNSHARP_MASKING) { + vec3 gaussianBlur = (a + c + g + i) * 1.0 + (b + d + f + h) * 2.0 + e * 4.0; gaussianBlur /= 16.0; // Return edge detection return e + (e - gaussianBlur) * sharpenFactor / 3.0; } - return e; + // CAS + // Soft min and max. + // a b c b + // d e f * 0.5 + d e f * 0.5 + // g h i h + // These are 2.0x bigger (factored out the extra multiply). + vec3 minRgb = min(min(min(d, e), min(f, b)), h); + minRgb += min(min(a, c), min(g, i)); + + vec3 maxRgb = max(max(max(d, e), max(f, b)), h); + maxRgb += max(max(a, c), max(g, i)); + + // Smooth minimum distance to signal limit divided by smooth max. + vec3 reciprocalMaxRgb = 1.0 / maxRgb; + vec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0); + + // Shaping amount of sharpening. + amplifyRgb = inversesqrt(amplifyRgb); + + vec3 weightRgb = -(1.0 / (amplifyRgb * CAS_CONTRAST_PEAK)); + vec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0); + + // 0 w 0 + // Filter shape: w 1 w + // 0 w 0 + vec3 window = b + d + f + h; + vec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0); + + return mix(e, outColor, sharpenFactor / 2.0); } void main() { - vec3 color; vec2 uv = gl_FragCoord.xy / iResolution.xy; + // Get current pixel + vec3 color = texture2D(data, uv).rgb; - if (sharpenFactor > 0.0) { - color = clarityBoost(data, uv); - } else { - color = texture2D(data, uv).rgb; - } + // Clarity boost + color = sharpenFactor > 0.0 ? clarityBoost(data, uv, color) : color; // Saturation - if (saturation != 1.0) { - vec3 grayscale = vec3(dot(color, LUMINOSITY_FACTOR)); - color = mix(grayscale, color, saturation); - } + color = saturation != 1.0 ? mix(vec3(dot(color, LUMINOSITY_FACTOR)), color, saturation) : color; // Contrast - if (contrast != 1.0) { - color = 0.5 + contrast * (color - 0.5); - } + color = contrast * (color - 0.5) + 0.5; // Brightness - if (brightness != 1.0) { - color = brightness * color; - } + color = brightness * color; gl_FragColor = vec4(color, 1.0); }