mirror of
https://github.com/libretro/slang-shaders.git
synced 2024-05-09 16:24:22 -04:00
Add autocrop-koko shader
This commit is contained in:
parent
ca2c0223c5
commit
d49bab8d0c
22
border/autocrop-koko.slangp
Normal file
22
border/autocrop-koko.slangp
Normal file
|
@ -0,0 +1,22 @@
|
|||
shaders = 3
|
||||
|
||||
shader0 = shaders/autocrop-koko/autocrop0_precut.slang
|
||||
alias0 = "autocrop_precut"
|
||||
filter_linear0 = false
|
||||
scale_type0 = source
|
||||
scale0 = 1.0
|
||||
wrap_mode0 = "clamp_to_border"
|
||||
|
||||
shader1 = shaders/autocrop-koko/autocrop1_compute.slang
|
||||
alias1 = "autocrop_compute"
|
||||
float_framebuffer1 = true
|
||||
filter_linear1 = false
|
||||
scale_type1 = source
|
||||
scale1 = 1.0
|
||||
wrap_mode1 = "clamp_to_border"
|
||||
mipmap_input1 = "true"
|
||||
|
||||
shader2 = shaders/autocrop-koko/autocrop2_display.slang
|
||||
alias2 = "autocrop_display"
|
||||
filter_linear2 = true
|
||||
scale_type2 = viewport
|
30
border/autocrop-koko.txt
Normal file
30
border/autocrop-koko.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
**Autocrop**:
|
||||
Clears solid bars around the frame.
|
||||
|
||||
Autocrop maximum amount:
|
||||
The higher, the more solid borders wil be cropped around the image.
|
||||
0.3 means 30%
|
||||
|
||||
Number of mandatory lines to crop:
|
||||
The minimum lines to always crop; this is useful because sometimes
|
||||
games have one or two "spurious" lines at the very edge of the screen that
|
||||
won't allow autocrop to work at all.
|
||||
This can be used to ignore them.
|
||||
|
||||
Samples per frame:
|
||||
Higher values makes the shader search more in a single frame for solid areas.
|
||||
This leads to more accurate result in less time, however it will also stress the gpu more.
|
||||
Fortunately even low/lighter values like 10 will work good if you're ok
|
||||
in waiting 2..3 seconds for the final crop value to be found.
|
||||
|
||||
Sample size:
|
||||
Search multiple pixels at once, this provide a big performance boost, but less accuracy.
|
||||
It means that some solid bar could remain around the image.
|
||||
|
||||
Scene change treshold
|
||||
When autocrop finds a maximum crop value, it only tries to crop more when the scene changes.
|
||||
By lowering this value, you tell the shader to try higher the crop more often.
|
||||
Use 0.0 is probably useful only to trigger a new search.
|
||||
|
||||
Transition speed
|
||||
This modulates the smoothness of the animation between various crop values.
|
39
border/shaders/autocrop-koko/autocrop0_precut.slang
Normal file
39
border/shaders/autocrop-koko/autocrop0_precut.slang
Normal file
|
@ -0,0 +1,39 @@
|
|||
#version 450
|
||||
|
||||
///////////////////////////// GPL LICENSE NOTICE /////////////////////////////
|
||||
//
|
||||
// autocrop-koko
|
||||
// Copyright (C) 2024 Antonio Orefice <kokoko3k@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3 of the License, or any later version.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "autocrop_config.inc"
|
||||
|
||||
#pragma stage vertex
|
||||
layout(location = 0) in vec4 Position;
|
||||
layout(location = 1) in vec2 TexCoord;
|
||||
layout(location = 0) out vec2 vTexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = global.MVP * Position;
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
|
||||
#pragma stage fragment
|
||||
layout(location = 0) in vec2 vTexCoord;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
layout(set = 0, binding = 2) uniform sampler2D Source;
|
||||
|
||||
void main() {
|
||||
vec2 co = vTexCoord;
|
||||
if (AUTOCROP_MAX > 0.0)
|
||||
co = clamp(co, params.OriginalSize.zw * AUTOCROP_MIN,
|
||||
1-params.OriginalSize.zw * AUTOCROP_MIN );
|
||||
|
||||
FragColor = vec4(texture(Source, co).rgb, 1.0);
|
||||
}
|
199
border/shaders/autocrop-koko/autocrop1_compute.slang
Normal file
199
border/shaders/autocrop-koko/autocrop1_compute.slang
Normal file
|
@ -0,0 +1,199 @@
|
|||
#version 450
|
||||
|
||||
///////////////////////////// GPL LICENSE NOTICE /////////////////////////////
|
||||
//
|
||||
// autocrop-koko
|
||||
// Copyright (C) 2024 Antonio Orefice <kokoko3k@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3 of the License, or any later version.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "autocrop_config.inc"
|
||||
|
||||
#pragma stage vertex
|
||||
layout(location = 0) in vec4 Position;
|
||||
layout(location = 1) in vec2 TexCoord;
|
||||
layout(location = 0) out vec2 vTexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = global.MVP * Position;
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
|
||||
#pragma stage fragment
|
||||
layout(location = 0) in vec2 vTexCoord;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
layout(set = 0, binding = 2) uniform sampler2D autocrop_precut;
|
||||
layout(set = 0, binding = 3) uniform sampler2D autocrop_precutFeedback;
|
||||
layout(set = 0, binding = 4) uniform sampler2D autocrop_computeFeedback;
|
||||
|
||||
|
||||
#define RGB2GRAY_VEC3 vec3(0.299, 0.587, 0.114)
|
||||
float rgb_to_gray(vec3 rgb) {
|
||||
return dot(rgb, RGB2GRAY_VEC3);
|
||||
}
|
||||
|
||||
bool scene_changed( sampler2D pre_smp, sampler2D cur_smp, float threshold) {
|
||||
|
||||
vec3 pre_mip = textureLod( pre_smp, vec2(0.5), 20.0).rgb;
|
||||
vec3 cur_mip = textureLod( cur_smp, vec2(0.5), 20.0).rgb;
|
||||
|
||||
float pre_v = rgb_to_gray(pre_mip);
|
||||
float cur_v = rgb_to_gray(cur_mip);
|
||||
|
||||
float diff_v = cur_v - pre_v;
|
||||
|
||||
// fadein: if the previous image is almost black and
|
||||
// image is fading in, trigger a scene scange
|
||||
if (pre_v < 2.0/255.0)
|
||||
if (pre_v < cur_v)
|
||||
return true;
|
||||
|
||||
float diff_v_abs = abs(diff_v);
|
||||
|
||||
// if lum is different enough trigger scene change
|
||||
if (diff_v_abs >= threshold)
|
||||
return true;
|
||||
|
||||
/*
|
||||
// from blank screen to something else, trigger SC
|
||||
// This compares different lod mipmaps from the same samplig point
|
||||
// to guess if the screen is solid or not.
|
||||
vec3 pre_mip_l20 = pre_mip;
|
||||
vec3 pre_mip_l1 = textureLod( pre_smp, vec2(0.5), vLods.x).rgb;
|
||||
vec3 pre_mip_l2 = textureLod( pre_smp, vec2(0.5), vLods.y).rgb;
|
||||
|
||||
if ( pre_mip_l1 == pre_mip_l2 && pre_mip_l2 == pre_mip_l20 )
|
||||
return true;
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float quasirandom(float n, float dmin, float dmax) {
|
||||
// https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
|
||||
// Returns a number between dmin and dmax which would ideally
|
||||
// fills the range by iterating through n values.
|
||||
// n has to be integer for this to work.
|
||||
float x = fract(n*0.754877626895905);
|
||||
return ( (dmax-dmin) * x ) + dmin;
|
||||
}
|
||||
|
||||
float get_next_autocrop_amount(float max_autocrop_amount) {
|
||||
|
||||
//Implementation requires the line_step to be the same for vertical and horizontal sampling.
|
||||
//This could be a bit inefficient tho.
|
||||
const float size_max = max(params.OriginalSize.x,params.OriginalSize.y) ;
|
||||
const float next_line_step = 1/size_max;
|
||||
|
||||
//This is needed to avoid sampling edges which may return out out of range values.
|
||||
const vec2 d = params.OriginalSize.zw * 0.5 * (1+AUTOCROP_MIN*2);
|
||||
//Reference color, topleft.
|
||||
vec3 ref = textureLod(autocrop_precut, vec2(d), 0.0).rgb; // FIXME, ref sample needs to be fuzzy too?
|
||||
float ref_lum = ref.r + ref.g + ref.b ;
|
||||
|
||||
//convert zoom back to maximum cropped lines
|
||||
const float max_crop_lines = ( size_max * (max_autocrop_amount -1) ) / ( 2*max_autocrop_amount) ;
|
||||
|
||||
//Sample top,bottom,left,right lines; stop when a line sum contain a different pixel color than the reference
|
||||
for (float croppedlines = AUTOCROP_MIN ; croppedlines < max_crop_lines ; croppedlines++) {
|
||||
|
||||
vec3 smp_sum;
|
||||
float line_lum;
|
||||
float fuzzy = AUTOCROP_SAMPLE_SIZE;
|
||||
|
||||
//Top
|
||||
smp_sum = vec3(0.0);
|
||||
for ( float x = params.FrameCount ; x < params.FrameCount+AUTOCROP_SAMPLES ; x ++ ) {
|
||||
smp_sum += textureLod(autocrop_precut, vec2( quasirandom(x, d.x, 1-d.x ), 0.0+ next_line_step*croppedlines), fuzzy).rgb;
|
||||
}
|
||||
|
||||
line_lum = (smp_sum.r + smp_sum.g + smp_sum.b) / (AUTOCROP_SAMPLES);
|
||||
|
||||
if ( abs(ref_lum - line_lum) > AUTOCROP_TOLERANCE)
|
||||
return size_max / (size_max - (croppedlines) * 2) ;
|
||||
|
||||
//BOTTOM
|
||||
smp_sum = vec3(0.0);
|
||||
for ( float x = params.FrameCount ; x < params.FrameCount+AUTOCROP_SAMPLES ; x ++ ) {
|
||||
smp_sum += textureLod(autocrop_precut, vec2( quasirandom(x, d.x, 1-d.x) , (1.0-eps)-next_line_step*croppedlines), fuzzy).rgb;
|
||||
}
|
||||
|
||||
line_lum = (smp_sum.r + smp_sum.g + smp_sum.b) / (AUTOCROP_SAMPLES);
|
||||
|
||||
if (abs( ref_lum - line_lum) > AUTOCROP_TOLERANCE) {
|
||||
return size_max / (size_max - (croppedlines) * 2) ;
|
||||
}
|
||||
|
||||
//LEFT
|
||||
smp_sum = vec3(0.0);
|
||||
for ( float y = params.FrameCount; y < params.FrameCount+AUTOCROP_SAMPLES; y ++ ) {
|
||||
smp_sum += textureLod(autocrop_precut, vec2(0.0+next_line_step*croppedlines, quasirandom(y, d.y, 1-d.y)), fuzzy).rgb;
|
||||
}
|
||||
|
||||
line_lum = (smp_sum.r + smp_sum.g + smp_sum.b) / (AUTOCROP_SAMPLES);
|
||||
|
||||
if (abs( ref_lum - line_lum) > AUTOCROP_TOLERANCE)
|
||||
return size_max / (size_max - (croppedlines) * 2) ;
|
||||
|
||||
//RIGHT
|
||||
smp_sum = vec3(0.0);
|
||||
for ( float y = params.FrameCount; y < params.FrameCount+AUTOCROP_SAMPLES; y ++ ) {
|
||||
smp_sum += textureLod(autocrop_precut, vec2((1.0-eps)-next_line_step*croppedlines, quasirandom(y, d.y, 1-d.y)), fuzzy).rgb;
|
||||
}
|
||||
|
||||
line_lum = (smp_sum.r + smp_sum.g + smp_sum.b) / (AUTOCROP_SAMPLES);
|
||||
|
||||
if (abs( ref_lum - line_lum) > AUTOCROP_TOLERANCE)
|
||||
return size_max / (size_max - (croppedlines) * 2);
|
||||
|
||||
}
|
||||
|
||||
return max_autocrop_amount;
|
||||
}
|
||||
|
||||
float get_autocrop() {
|
||||
// This return a value that will be used in the final pass to zoom the picture.
|
||||
float previous_autocrop_amount = AUTOCROP_MAX+1;
|
||||
if (params.FrameCount < 30.0) {
|
||||
previous_autocrop_amount = AUTOCROP_MAX+1; //Good start point.
|
||||
} else {
|
||||
previous_autocrop_amount = texture(autocrop_computeFeedback, AUTOCROP_SAMPLING_POINT).a;
|
||||
}
|
||||
|
||||
// Reset crop if scene has changed?
|
||||
float next_autocrop_amount;
|
||||
|
||||
if (scene_changed( autocrop_precutFeedback, autocrop_precut, AUTOCROP_STEADINESS) ) {
|
||||
//When a scene changes, we must release the maximum crop amount and find a new one.
|
||||
|
||||
//For entrance effect:
|
||||
//return AUTOCROP_MAX+1; //"entrance effect?
|
||||
|
||||
//No frills way:
|
||||
next_autocrop_amount = get_next_autocrop_amount(AUTOCROP_MAX+1);
|
||||
return next_autocrop_amount;
|
||||
|
||||
} else {
|
||||
|
||||
previous_autocrop_amount = texture(autocrop_computeFeedback, AUTOCROP_SAMPLING_POINT).a;
|
||||
next_autocrop_amount = get_next_autocrop_amount(previous_autocrop_amount);
|
||||
float r = mix( previous_autocrop_amount , next_autocrop_amount, AUTOCROP_TRANSITION_SPEED);
|
||||
return clamp(r, 1.0, AUTOCROP_MAX+1); // needed to sanitize output when Feedback is unavailable (eg: just switched autocrop on)
|
||||
}
|
||||
}
|
||||
|
||||
bool is_first_inside_rect(vec2 point, vec4 rect) {
|
||||
vec2 bounded = clamp(point, rect.xy, rect.zw);
|
||||
return point == bounded;
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (is_first_inside_rect(vTexCoord, vec4(AUTOCROP_SAMPLING_POINT-0.01, AUTOCROP_SAMPLING_POINT+0.01)))
|
||||
if (AUTOCROP_MAX > 0.001)
|
||||
FragColor.a = get_autocrop();
|
||||
}
|
45
border/shaders/autocrop-koko/autocrop2_display.slang
Normal file
45
border/shaders/autocrop-koko/autocrop2_display.slang
Normal file
|
@ -0,0 +1,45 @@
|
|||
#version 450
|
||||
|
||||
///////////////////////////// GPL LICENSE NOTICE /////////////////////////////
|
||||
//
|
||||
// autocrop-koko
|
||||
// Copyright (C) 2024 Antonio Orefice <kokoko3k@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3 of the License, or any later version.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "autocrop_config.inc"
|
||||
|
||||
#pragma stage vertex
|
||||
layout(location = 0) in vec4 Position;
|
||||
layout(location = 1) in vec2 TexCoord;
|
||||
layout(location = 0) out vec2 vTexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = global.MVP * Position;
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
|
||||
#pragma stage fragment
|
||||
layout(location = 0) in vec2 vTexCoord;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
layout(set = 0, binding = 2) uniform sampler2D autocrop_precut;
|
||||
layout(set = 0, binding = 3) uniform sampler2D autocrop_compute;
|
||||
|
||||
vec2 zoom(vec2 co, float zoom_factor) {
|
||||
return 0.5 + (co - 0.5) / zoom_factor;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 co = vTexCoord;
|
||||
if (AUTOCROP_MAX > 0.0) {
|
||||
float z = texture(autocrop_compute, AUTOCROP_SAMPLING_POINT).a;
|
||||
co = zoom(co, z);
|
||||
}
|
||||
|
||||
FragColor = vec4(texture(autocrop_precut, co).rgb, 1.0);
|
||||
}
|
52
border/shaders/autocrop-koko/autocrop_config.inc
Normal file
52
border/shaders/autocrop-koko/autocrop_config.inc
Normal file
|
@ -0,0 +1,52 @@
|
|||
///////////////////////////// GPL LICENSE NOTICE /////////////////////////////
|
||||
//
|
||||
// autocrop-koko
|
||||
// Copyright (C) 2024 Antonio Orefice <kokoko3k@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3 of the License, or any later version.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
layout(push_constant) uniform Push {
|
||||
vec4 SourceSize;
|
||||
vec4 OriginalSize;
|
||||
vec4 OutputSize;
|
||||
uint FrameCount;
|
||||
} params;
|
||||
|
||||
|
||||
layout(std140, set = 0, binding = 0) uniform UBO {
|
||||
mat4 MVP;
|
||||
float AUTOCROP_MAX;
|
||||
float AUTOCROP_MIN;
|
||||
float AUTOCROP_SAMPLES;
|
||||
float AUTOCROP_SAMPLE_SIZE;
|
||||
float AUTOCROP_TRANSITION_SPEED;
|
||||
float AUTOCROP_STEADINESS;
|
||||
} global;
|
||||
|
||||
|
||||
#pragma parameter AUTOCROP_MAX "★ Autocrop: maximum amount" 25.0 0.0 0.5 0.01
|
||||
#pragma parameter AUTOCROP_MIN " Number of mandatory lines to crop" 1.0 0.0 10.0 1.0
|
||||
#pragma parameter AUTOCROP_SAMPLES " Samples per frame (faster response, higher gpu use)" 20.0 0.0 300.0 1.0
|
||||
#pragma parameter AUTOCROP_SAMPLE_SIZE " Sample size (big speedup, less accurate)" 2.0 0.0 5.0 1.0
|
||||
#pragma parameter AUTOCROP_STEADINESS " Scene change treshold (0 = continuous cropping)" 0.2 0.0 0.5 0.01
|
||||
#pragma parameter AUTOCROP_TRANSITION_SPEED " Transition speed" 0.1 0.05 1.0 0.05
|
||||
|
||||
|
||||
#define AUTOCROP_MAX global.AUTOCROP_MAX
|
||||
#define AUTOCROP_MIN global.AUTOCROP_MIN
|
||||
#define AUTOCROP_SAMPLES global.AUTOCROP_SAMPLES
|
||||
#define AUTOCROP_SAMPLE_SIZE global.AUTOCROP_SAMPLE_SIZE
|
||||
#define AUTOCROP_TRANSITION_SPEED global.AUTOCROP_TRANSITION_SPEED
|
||||
#define AUTOCROP_STEADINESS global.AUTOCROP_STEADINESS
|
||||
|
||||
//Coords holding the autocrop zoom value
|
||||
#define AUTOCROP_SAMPLING_POINT vec2(0.125)
|
||||
|
||||
//How much colors can differ to to be considered part of a solid area
|
||||
#define AUTOCROP_TOLERANCE (1.0/255.0)
|
||||
|
||||
#define eps 1e-5
|
Loading…
Reference in a new issue