Add Clarity boost mode

This commit is contained in:
redphx
2025-05-14 17:21:50 +07:00
parent e502e49d64
commit 3290a36886
12 changed files with 103 additions and 38 deletions

View File

@@ -1,5 +1,5 @@
import type { BaseSettingsStorage } from "@/utils/settings-storages/base-settings-storage";
import type { BlockFeature, CodecProfile, DeviceVibrationMode, GameBarPosition, LoadingScreenRocket, NativeMkbMode, StreamPlayerType, StreamResolution, StreamStat, StreamStatPosition, StreamVideoProcessing, TouchControllerMode, TouchControllerStyleCustom, TouchControllerStyleStandard, UiLayout, UiSection, UiTheme, VideoPosition, VideoPowerPreference, VideoRatio } from "./pref-values"
import type { BlockFeature, CodecProfile, DeviceVibrationMode, GameBarPosition, LoadingScreenRocket, NativeMkbMode, StreamPlayerType, StreamResolution, StreamStat, StreamStatPosition, StreamVideoProcessing, StreamVideoProcessingMode, TouchControllerMode, TouchControllerStyleCustom, TouchControllerStyleStandard, UiLayout, UiSection, UiTheme, VideoPosition, VideoPowerPreference, VideoRatio } from "./pref-values"
export const enum StorageKey {
GLOBAL = 'BetterXcloud',
@@ -156,6 +156,7 @@ export const enum StreamPref {
VIDEO_PLAYER_TYPE = 'video.player.type',
VIDEO_POWER_PREFERENCE = 'video.player.powerPreference',
VIDEO_PROCESSING = 'video.processing',
VIDEO_PROCESSING_MODE = 'video.processing.mode',
VIDEO_SHARPNESS = 'video.processing.sharpness',
VIDEO_MAX_FPS = 'video.maxFps',
VIDEO_RATIO = 'video.ratio',
@@ -205,6 +206,7 @@ export type StreamPrefTypeMap = {
[StreamPref.VIDEO_POSITION]: VideoPosition;
[StreamPref.VIDEO_POWER_PREFERENCE]: VideoPowerPreference;
[StreamPref.VIDEO_PROCESSING]: StreamVideoProcessing;
[StreamPref.VIDEO_PROCESSING_MODE]: StreamVideoProcessingMode;
[StreamPref.VIDEO_RATIO]: VideoRatio;
[StreamPref.VIDEO_SATURATION]: number;
[StreamPref.VIDEO_SHARPNESS]: number;
@@ -294,6 +296,7 @@ export const ALL_PREFS: {
StreamPref.VIDEO_POSITION,
StreamPref.VIDEO_POWER_PREFERENCE,
StreamPref.VIDEO_PROCESSING,
StreamPref.VIDEO_PROCESSING_MODE,
StreamPref.VIDEO_RATIO,
StreamPref.VIDEO_SATURATION,
StreamPref.VIDEO_SHARPNESS,

View File

@@ -130,6 +130,11 @@ export const enum StreamVideoProcessing {
CAS = 'cas',
}
export const enum StreamVideoProcessingMode {
QUALITY = 'quality',
PERFORMANCE = 'performance',
}
export const enum BlockFeature {
CHAT = 'chat',
FRIENDS = 'friends',

View File

@@ -5,7 +5,7 @@ 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 = 0.8 * -3.0 + 8.0;
@@ -14,6 +14,7 @@ const float CAS_CONTRAST_PEAK = 0.8 * -3.0 + 8.0;
const vec3 LUMINOSITY_FACTOR = vec3(0.299, 0.587, 0.114);
uniform int filterId;
uniform bool qualityMode;
uniform float sharpenFactor;
uniform float brightness;
uniform float contrast;
@@ -28,16 +29,22 @@ vec3 clarityBoost(sampler2D tex, vec2 coord, vec3 e) {
// a b c
// d e f
// g h i
vec3 a = texture(tex, coord + texelSize * vec2(-1, 1)).rgb;
vec3 b = texture(tex, coord + texelSize * vec2(0, 1)).rgb;
vec3 c = texture(tex, coord + texelSize * vec2(1, 1)).rgb;
vec3 d = texture(tex, coord + texelSize * vec2(-1, 0)).rgb;
vec3 f = texture(tex, coord + texelSize * vec2(1, 0)).rgb;
vec3 g = texture(tex, coord + texelSize * vec2(-1, -1)).rgb;
vec3 h = texture(tex, coord + texelSize * vec2(0, -1)).rgb;
vec3 i = texture(tex, coord + texelSize * vec2(1, -1)).rgb;
vec3 a;
vec3 c;
vec3 g;
vec3 i;
if (filterId == FILTER_UNSHARP_MASKING || qualityMode) {
a = texture(tex, coord + texelSize * vec2(-1, 1)).rgb;
c = texture(tex, coord + texelSize * vec2(1, 1)).rgb;
g = texture(tex, coord + texelSize * vec2(-1, -1)).rgb;
i = texture(tex, coord + texelSize * vec2(1, -1)).rgb;
}
// USM
if (filterId == FILTER_UNSHARP_MASKING) {
@@ -55,10 +62,12 @@ vec3 clarityBoost(sampler2D tex, vec2 coord, vec3 e) {
// 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));
if (qualityMode) {
minRgb += min(min(a, c), min(g, i));
maxRgb += max(max(a, c), max(g, i));
}
// Smooth minimum distance to signal limit divided by smooth max.
vec3 reciprocalMaxRgb = 1.0 / maxRgb;
@@ -85,10 +94,12 @@ void main() {
vec3 color = texture(data, uv).rgb;
// Clarity boost
color = sharpenFactor > 0.0 ? clarityBoost(data, uv, color) : color;
if (sharpenFactor > 0.0) {
color = clarityBoost(data, uv, color);
}
// Saturation
color = saturation != 1.0 ? mix(vec3(dot(color, LUMINOSITY_FACTOR)), color, saturation) : color;
color = mix(vec3(dot(color, LUMINOSITY_FACTOR)), color, saturation);
// Contrast
color = contrast * (color - 0.5) + 0.5;

View File

@@ -3,7 +3,7 @@ import { compressCodeFile } from "@macros/build" with { type: "macro" };
import { StreamPref } from "@/enums/pref-keys";
import { getStreamPref } from "@/utils/pref-utils";
import { BaseCanvasPlayer } from "../base-canvas-player";
import { StreamPlayerType } from "@/enums/pref-values";
import { StreamPlayerType, StreamVideoProcessingMode } from "@/enums/pref-values";
export class WebGL2Player extends BaseCanvasPlayer {
@@ -25,7 +25,8 @@ export class WebGL2Player extends BaseCanvasPlayer {
gl.uniform2f(gl.getUniformLocation(program, 'iResolution'), this.$canvas.width, this.$canvas.height);
gl.uniform1i(gl.getUniformLocation(program, 'filterId'), filterId);
gl.uniform1f(gl.getUniformLocation(program, 'sharpenFactor'), this.options.sharpness);
gl.uniform1i(gl.getUniformLocation(program, 'qualityMode'), this.options.processingMode === StreamVideoProcessingMode.QUALITY ? 1 : 0);
gl.uniform1f(gl.getUniformLocation(program, 'sharpenFactor'), this.options.sharpness / (this.options.processingMode === StreamVideoProcessingMode.QUALITY ? 1 : 1.2));
gl.uniform1f(gl.getUniformLocation(program, 'brightness'), this.options.brightness / 100);
gl.uniform1f(gl.getUniformLocation(program, 'contrast'), this.options.contrast / 100);
gl.uniform1f(gl.getUniformLocation(program, 'saturation'), this.options.saturation / 100);

View File

@@ -82,6 +82,10 @@ export class SettingsManager {
},
[StreamPref.VIDEO_PROCESSING]: {
onChange: updateVideoPlayer,
onChangeUi: onChangeVideoPlayerType,
},
[StreamPref.VIDEO_PROCESSING_MODE]: {
onChange: updateVideoPlayer,
},
[StreamPref.VIDEO_SHARPNESS]: {
onChange: updateVideoPlayer,

View File

@@ -8,6 +8,7 @@ import type { StreamPlayerOptions } from "@/types/stream";
export function onChangeVideoPlayerType() {
const playerType = getStreamPref(StreamPref.VIDEO_PLAYER_TYPE);
const processing = getStreamPref(StreamPref.VIDEO_PROCESSING);
const settingsManager = SettingsManager.getInstance();
if (!settingsManager.hasElement(StreamPref.VIDEO_PROCESSING)) {
return;
@@ -16,6 +17,7 @@ export function onChangeVideoPlayerType() {
let isDisabled = false;
const $videoProcessing = settingsManager.getElement(StreamPref.VIDEO_PROCESSING) as HTMLSelectElement;
const $videoProcessingMode = settingsManager.getElement(StreamPref.VIDEO_PROCESSING_MODE) as HTMLSelectElement;
const $videoSharpness = settingsManager.getElement(StreamPref.VIDEO_SHARPNESS);
const $videoPowerPreference = settingsManager.getElement(StreamPref.VIDEO_POWER_PREFERENCE);
const $videoMaxFps = settingsManager.getElement(StreamPref.VIDEO_MAX_FPS);
@@ -40,6 +42,7 @@ export function onChangeVideoPlayerType() {
$videoSharpness.dataset.disabled = isDisabled.toString();
// Hide Power Preference setting if renderer isn't WebGL2
$videoProcessingMode.closest('.bx-settings-row')!.classList.toggle('bx-gone', !(playerType === StreamPlayerType.WEBGL2 && processing === StreamVideoProcessing.CAS));
$videoPowerPreference.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2);
$videoMaxFps.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType === StreamPlayerType.VIDEO);
}
@@ -59,6 +62,7 @@ export function updateVideoPlayer() {
const options = {
processing: getStreamPref(StreamPref.VIDEO_PROCESSING),
processingMode: getStreamPref(StreamPref.VIDEO_PROCESSING_MODE),
sharpness: getStreamPref(StreamPref.VIDEO_SHARPNESS),
saturation: getStreamPref(StreamPref.VIDEO_SATURATION),
contrast: getStreamPref(StreamPref.VIDEO_CONTRAST),

View File

@@ -452,6 +452,7 @@ export class SettingsDialog extends NavigationDialog {
StreamPref.VIDEO_MAX_FPS,
StreamPref.VIDEO_POWER_PREFERENCE,
StreamPref.VIDEO_PROCESSING,
StreamPref.VIDEO_PROCESSING_MODE,
StreamPref.VIDEO_RATIO,
StreamPref.VIDEO_POSITION,
StreamPref.VIDEO_SHARPNESS,

View File

@@ -1,7 +1,8 @@
import type { StreamVideoProcessing } from "@/enums/pref-values";
import type { StreamVideoProcessing, StreamVideoProcessingMode } from "@/enums/pref-values";
type StreamPlayerOptions = {
processing: StreamVideoProcessing,
processingMode: StreamVideoProcessingMode,
sharpness: number,
saturation: number,
contrast: number,

View File

@@ -22,6 +22,7 @@ export function patchVideoApi() {
const playerOptions = {
processing: getStreamPref(StreamPref.VIDEO_PROCESSING),
processingMode: getStreamPref(StreamPref.VIDEO_PROCESSING_MODE),
sharpness: getStreamPref(StreamPref.VIDEO_SHARPNESS),
saturation: getStreamPref(StreamPref.VIDEO_SATURATION),
contrast: getStreamPref(StreamPref.VIDEO_CONTRAST),

View File

@@ -1,9 +1,9 @@
import { StreamPref, StorageKey, type PrefTypeMap } from "@/enums/pref-keys";
import { DeviceVibrationMode, StreamPlayerType, StreamVideoProcessing, VideoPowerPreference, VideoRatio, VideoPosition, StreamStat, StreamStatPosition } from "@/enums/pref-values";
import { DeviceVibrationMode, StreamPlayerType, StreamVideoProcessing, VideoPowerPreference, VideoRatio, VideoPosition, StreamStat, StreamStatPosition, StreamVideoProcessingMode } from "@/enums/pref-values";
import { STATES } from "../global";
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
import { t } from "../translation";
import { t, ut } from "../translation";
import { BaseSettingsStorage } from "./base-settings-storage";
import { CE } from "../html";
import type { SettingActionOrigin, SettingDefinitions } from "@/types/setting-definition";
@@ -179,6 +179,18 @@ export class StreamSettingsStorage extends BaseSettingsStorage<StreamPref> {
highest: StreamVideoProcessing.CAS,
},
},
[StreamPref.VIDEO_PROCESSING_MODE]: {
label: ut('clarity-boost-mode'),
default: StreamVideoProcessingMode.PERFORMANCE,
options: {
[StreamVideoProcessingMode.QUALITY]: ut('quality'),
[StreamVideoProcessingMode.PERFORMANCE]: ut('performance'),
},
suggest: {
lowest: StreamVideoProcessingMode.PERFORMANCE,
highest: StreamVideoProcessingMode.QUALITY,
},
},
[StreamPref.VIDEO_POWER_PREFERENCE]: {
label: t('renderer-configuration'),
default: VideoPowerPreference.DEFAULT,