Merge pull request #1975 from jpark37/area-upscale-shader
libobs: obs-filters: Area upscale shadermaster
commit
3f7d4fe1f5
|
@ -1,13 +1,29 @@
|
||||||
uniform float4x4 ViewProj;
|
uniform float4x4 ViewProj;
|
||||||
|
uniform float2 base_dimension;
|
||||||
uniform float2 base_dimension_i;
|
uniform float2 base_dimension_i;
|
||||||
uniform texture2d image;
|
uniform texture2d image;
|
||||||
|
|
||||||
struct VertInOut {
|
sampler_state textureSampler {
|
||||||
|
Filter = Linear;
|
||||||
|
AddressU = Clamp;
|
||||||
|
AddressV = Clamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertData {
|
||||||
float4 pos : POSITION;
|
float4 pos : POSITION;
|
||||||
float2 uv : TEXCOORD0;
|
float2 uv : TEXCOORD0;
|
||||||
};
|
};
|
||||||
|
|
||||||
VertInOut VSDefault(VertInOut vert_in)
|
struct VertInOut {
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
float4 pos : POSITION;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FragData {
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VertInOut VSDefault(VertData vert_in)
|
||||||
{
|
{
|
||||||
VertInOut vert_out;
|
VertInOut vert_out;
|
||||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||||
|
@ -15,50 +31,85 @@ VertInOut VSDefault(VertInOut vert_in)
|
||||||
return vert_out;
|
return vert_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
float4 PSDrawAreaRGBA(VertInOut vert_in) : TARGET
|
float4 PSDrawAreaRGBA(FragData frag_in) : TARGET
|
||||||
{
|
{
|
||||||
float4 totalcolor = float4(0.0, 0.0, 0.0, 0.0);
|
float2 uv = frag_in.uv;
|
||||||
|
float2 uv_delta = float2(ddx(uv.x), ddy(uv.y));
|
||||||
float2 uv = vert_in.uv;
|
|
||||||
float2 uvdelta = float2(ddx(uv.x), ddy(uv.y));
|
|
||||||
|
|
||||||
// Handle potential OpenGL flip.
|
// Handle potential OpenGL flip.
|
||||||
uvdelta.y = abs(uvdelta.y);
|
if (obs_glsl_compile)
|
||||||
|
uv_delta.y = abs(uv_delta.y);
|
||||||
|
|
||||||
float2 uvhalfdelta = 0.5 * uvdelta;
|
float2 uv_min = uv - 0.5 * uv_delta;
|
||||||
float2 uvmin = uv - uvhalfdelta;
|
float2 uv_max = uv_min + uv_delta;
|
||||||
float2 uvmax = uv + uvhalfdelta;
|
|
||||||
|
|
||||||
float2 imagesize = 1.0 / base_dimension_i;
|
float2 load_index_begin = floor(uv_min * base_dimension);
|
||||||
float2 loadindexmin = floor(uvmin * imagesize);
|
float2 load_index_end = ceil(uv_max * base_dimension);
|
||||||
float2 loadindexmax = floor(uvmax * imagesize);
|
|
||||||
|
|
||||||
float2 targetsize = 1.0 / uvdelta;
|
float2 target_dimension = 1.0 / uv_delta;
|
||||||
float2 targetpos = uv * targetsize;
|
float2 target_pos = uv * target_dimension;
|
||||||
float2 targetposmin = targetpos - 0.5;
|
float2 target_pos_min = target_pos - 0.5;
|
||||||
float2 targetposmax = targetpos + 0.5;
|
float2 target_pos_max = target_pos + 0.5;
|
||||||
float2 scale = base_dimension_i * targetsize;
|
float2 scale = base_dimension_i * target_dimension;
|
||||||
|
|
||||||
float loadindexy = loadindexmin.y;
|
float4 total_color = float4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
float load_index_y = load_index_begin.y;
|
||||||
do {
|
do {
|
||||||
float loadindexx = loadindexmin.x;
|
float source_y_min = load_index_y * scale.y;
|
||||||
|
float source_y_max = source_y_min + scale.y;
|
||||||
|
float y_min = max(source_y_min, target_pos_min.y);
|
||||||
|
float y_max = min(source_y_max, target_pos_max.y);
|
||||||
|
float height = y_max - y_min;
|
||||||
|
|
||||||
|
float load_index_x = load_index_begin.x;
|
||||||
do {
|
do {
|
||||||
float2 loadindex = float2(loadindexx, loadindexy);
|
float source_x_min = load_index_x * scale.x;
|
||||||
float2 potentialtargetmin = loadindex * scale;
|
float source_x_max = source_x_min + scale.x;
|
||||||
float2 potentialtargetmax = potentialtargetmin + scale;
|
float x_min = max(source_x_min, target_pos_min.x);
|
||||||
float2 targetmin = max(potentialtargetmin, targetposmin);
|
float x_max = min(source_x_max, target_pos_max.x);
|
||||||
float2 targetmax = min(potentialtargetmax, targetposmax);
|
float width = x_max - x_min;
|
||||||
float area = (targetmax.x - targetmin.x) * (targetmax.y - targetmin.y);
|
float area = width * height;
|
||||||
float4 sample = image.Load(int3(loadindex, 0));
|
|
||||||
totalcolor += area * sample;
|
|
||||||
|
|
||||||
++loadindexx;
|
float4 color = image.Load(int3(load_index_x, load_index_y, 0));
|
||||||
} while (loadindexx <= loadindexmax.x);
|
total_color += area * color;
|
||||||
|
|
||||||
++loadindexy;
|
++load_index_x;
|
||||||
} while (loadindexy <= loadindexmax.y);
|
} while (load_index_x < load_index_end.x);
|
||||||
|
|
||||||
return totalcolor;
|
++load_index_y;
|
||||||
|
} while (load_index_y < load_index_end.y);
|
||||||
|
|
||||||
|
return total_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PSDrawAreaUpscaleRGBA(FragData frag_in) : TARGET
|
||||||
|
{
|
||||||
|
float2 uv = frag_in.uv;
|
||||||
|
float2 uv_delta = float2(ddx(uv.x), ddy(uv.y));
|
||||||
|
|
||||||
|
// Handle potential OpenGL flip.
|
||||||
|
if (obs_glsl_compile)
|
||||||
|
uv_delta.y = abs(uv_delta.y);
|
||||||
|
|
||||||
|
float2 uv_min = uv - 0.5 * uv_delta;
|
||||||
|
float2 uv_max = uv_min + uv_delta;
|
||||||
|
|
||||||
|
float2 load_index_first = floor(uv_min * base_dimension);
|
||||||
|
float2 load_index_last = ceil(uv_max * base_dimension) - 1.0;
|
||||||
|
|
||||||
|
if (load_index_first.x < load_index_last.x) {
|
||||||
|
float uv_boundary_x = load_index_last.x * base_dimension_i.x;
|
||||||
|
uv.x = ((uv.x - uv_boundary_x) / uv_delta.x) * base_dimension_i.x + uv_boundary_x;
|
||||||
|
} else
|
||||||
|
uv.x = (load_index_first.x + 0.5) * base_dimension_i.x;
|
||||||
|
if (load_index_first.y < load_index_last.y) {
|
||||||
|
float uv_boundary_y = load_index_last.y * base_dimension_i.y;
|
||||||
|
uv.y = ((uv.y - uv_boundary_y) / uv_delta.y) * base_dimension_i.y + uv_boundary_y;
|
||||||
|
} else
|
||||||
|
uv.y = (load_index_first.y + 0.5) * base_dimension_i.y;
|
||||||
|
|
||||||
|
return image.Sample(textureSampler, uv);
|
||||||
}
|
}
|
||||||
|
|
||||||
technique Draw
|
technique Draw
|
||||||
|
@ -66,6 +117,15 @@ technique Draw
|
||||||
pass
|
pass
|
||||||
{
|
{
|
||||||
vertex_shader = VSDefault(vert_in);
|
vertex_shader = VSDefault(vert_in);
|
||||||
pixel_shader = PSDrawAreaRGBA(vert_in);
|
pixel_shader = PSDrawAreaRGBA(frag_in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
technique DrawUpscale
|
||||||
|
{
|
||||||
|
pass
|
||||||
|
{
|
||||||
|
vertex_shader = VSDefault(vert_in);
|
||||||
|
pixel_shader = PSDrawAreaUpscaleRGBA(frag_in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,6 +470,7 @@ static void render_item_texture(struct obs_scene_item *item)
|
||||||
enum obs_scale_type type = item->scale_filter;
|
enum obs_scale_type type = item->scale_filter;
|
||||||
uint32_t cx = gs_texture_get_width(tex);
|
uint32_t cx = gs_texture_get_width(tex);
|
||||||
uint32_t cy = gs_texture_get_height(tex);
|
uint32_t cy = gs_texture_get_height(tex);
|
||||||
|
const char *tech = "Draw";
|
||||||
|
|
||||||
if (type != OBS_SCALE_DISABLE) {
|
if (type != OBS_SCALE_DISABLE) {
|
||||||
if (type == OBS_SCALE_POINT) {
|
if (type == OBS_SCALE_POINT) {
|
||||||
|
@ -481,6 +482,7 @@ static void render_item_texture(struct obs_scene_item *item)
|
||||||
} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
|
} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
|
||||||
!close_float(item->output_scale.y, 1.0f, EPSILON)) {
|
!close_float(item->output_scale.y, 1.0f, EPSILON)) {
|
||||||
gs_eparam_t *scale_param;
|
gs_eparam_t *scale_param;
|
||||||
|
gs_eparam_t *scale_i_param;
|
||||||
|
|
||||||
if (item->output_scale.x < 0.5f ||
|
if (item->output_scale.x < 0.5f ||
|
||||||
item->output_scale.y < 0.5f) {
|
item->output_scale.y < 0.5f) {
|
||||||
|
@ -491,15 +493,26 @@ static void render_item_texture(struct obs_scene_item *item)
|
||||||
effect = obs->video.lanczos_effect;
|
effect = obs->video.lanczos_effect;
|
||||||
} else if (type == OBS_SCALE_AREA) {
|
} else if (type == OBS_SCALE_AREA) {
|
||||||
effect = obs->video.area_effect;
|
effect = obs->video.area_effect;
|
||||||
|
if ((item->output_scale.x >= 1.0f) &&
|
||||||
|
(item->output_scale.y >= 1.0f))
|
||||||
|
tech = "DrawUpscale";
|
||||||
}
|
}
|
||||||
|
|
||||||
scale_param = gs_effect_get_param_by_name(
|
scale_param = gs_effect_get_param_by_name(
|
||||||
effect, "base_dimension_i");
|
effect, "base_dimension");
|
||||||
if (scale_param) {
|
if (scale_param) {
|
||||||
|
struct vec2 base_res_i = {(float)cx, (float)cy};
|
||||||
|
|
||||||
|
gs_effect_set_vec2(scale_param, &base_res_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
scale_i_param = gs_effect_get_param_by_name(
|
||||||
|
effect, "base_dimension_i");
|
||||||
|
if (scale_i_param) {
|
||||||
struct vec2 base_res_i = {1.0f / (float)cx,
|
struct vec2 base_res_i = {1.0f / (float)cx,
|
||||||
1.0f / (float)cy};
|
1.0f / (float)cy};
|
||||||
|
|
||||||
gs_effect_set_vec2(scale_param, &base_res_i);
|
gs_effect_set_vec2(scale_i_param, &base_res_i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -507,7 +520,7 @@ static void render_item_texture(struct obs_scene_item *item)
|
||||||
gs_blend_state_push();
|
gs_blend_state_push();
|
||||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
|
||||||
|
|
||||||
while (gs_effect_loop(effect, "Draw"))
|
while (gs_effect_loop(effect, tech))
|
||||||
obs_source_draw(tex, 0, 0, 0, 0, 0);
|
obs_source_draw(tex, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
gs_blend_state_pop();
|
gs_blend_state_pop();
|
||||||
|
|
|
@ -208,8 +208,9 @@ static inline void render_output_texture(struct obs_core_video *video)
|
||||||
gs_texture_t *target = video->output_texture;
|
gs_texture_t *target = video->output_texture;
|
||||||
uint32_t width = gs_texture_get_width(target);
|
uint32_t width = gs_texture_get_width(target);
|
||||||
uint32_t height = gs_texture_get_height(target);
|
uint32_t height = gs_texture_get_height(target);
|
||||||
struct vec2 base_i;
|
struct vec2 base, base_i;
|
||||||
|
|
||||||
|
vec2_set(&base, (float)video->base_width, (float)video->base_height);
|
||||||
vec2_set(&base_i, 1.0f / (float)video->base_width,
|
vec2_set(&base_i, 1.0f / (float)video->base_width,
|
||||||
1.0f / (float)video->base_height);
|
1.0f / (float)video->base_height);
|
||||||
|
|
||||||
|
@ -225,6 +226,8 @@ static inline void render_output_texture(struct obs_core_video *video)
|
||||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||||
gs_eparam_t *matrix =
|
gs_eparam_t *matrix =
|
||||||
gs_effect_get_param_by_name(effect, "color_matrix");
|
gs_effect_get_param_by_name(effect, "color_matrix");
|
||||||
|
gs_eparam_t *bres =
|
||||||
|
gs_effect_get_param_by_name(effect, "base_dimension");
|
||||||
gs_eparam_t *bres_i =
|
gs_eparam_t *bres_i =
|
||||||
gs_effect_get_param_by_name(effect, "base_dimension_i");
|
gs_effect_get_param_by_name(effect, "base_dimension_i");
|
||||||
size_t passes, i;
|
size_t passes, i;
|
||||||
|
@ -232,6 +235,8 @@ static inline void render_output_texture(struct obs_core_video *video)
|
||||||
gs_set_render_target(target, NULL);
|
gs_set_render_target(target, NULL);
|
||||||
set_render_size(width, height);
|
set_render_size(width, height);
|
||||||
|
|
||||||
|
if (bres)
|
||||||
|
gs_effect_set_vec2(bres, &base);
|
||||||
if (bres_i)
|
if (bres_i)
|
||||||
gs_effect_set_vec2(bres_i, &base_i);
|
gs_effect_set_vec2(bres_i, &base_i);
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ struct scale_filter_data {
|
||||||
gs_effect_t *effect;
|
gs_effect_t *effect;
|
||||||
gs_eparam_t *image_param;
|
gs_eparam_t *image_param;
|
||||||
gs_eparam_t *dimension_param;
|
gs_eparam_t *dimension_param;
|
||||||
|
gs_eparam_t *dimension_i_param;
|
||||||
gs_eparam_t *undistort_factor_param;
|
gs_eparam_t *undistort_factor_param;
|
||||||
|
struct vec2 dimension;
|
||||||
struct vec2 dimension_i;
|
struct vec2 dimension_i;
|
||||||
double undistort_factor;
|
double undistort_factor;
|
||||||
int cx_in;
|
int cx_in;
|
||||||
|
@ -49,6 +51,7 @@ struct scale_filter_data {
|
||||||
bool target_valid;
|
bool target_valid;
|
||||||
bool valid;
|
bool valid;
|
||||||
bool undistort;
|
bool undistort;
|
||||||
|
bool upscale;
|
||||||
bool base_canvas_resolution;
|
bool base_canvas_resolution;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -203,6 +206,7 @@ static void scale_filter_tick(void *data, float seconds)
|
||||||
filter->cy_out = filter->cy_in;
|
filter->cy_out = filter->cy_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec2_set(&filter->dimension, (float)cx, (float)cy);
|
||||||
vec2_set(&filter->dimension_i, 1.0f / (float)cx, 1.0f / (float)cy);
|
vec2_set(&filter->dimension_i, 1.0f / (float)cx, 1.0f / (float)cy);
|
||||||
|
|
||||||
if (filter->undistort) {
|
if (filter->undistort) {
|
||||||
|
@ -211,6 +215,8 @@ static void scale_filter_tick(void *data, float seconds)
|
||||||
filter->undistort_factor = 1.0;
|
filter->undistort_factor = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter->upscale = false;
|
||||||
|
|
||||||
/* ------------------------- */
|
/* ------------------------- */
|
||||||
|
|
||||||
lower_than_2x = filter->cx_out < cx / 2 || filter->cy_out < cy / 2;
|
lower_than_2x = filter->cx_out < cx / 2 || filter->cy_out < cy / 2;
|
||||||
|
@ -232,6 +238,8 @@ static void scale_filter_tick(void *data, float seconds)
|
||||||
break;
|
break;
|
||||||
case OBS_SCALE_AREA:
|
case OBS_SCALE_AREA:
|
||||||
type = OBS_EFFECT_AREA;
|
type = OBS_EFFECT_AREA;
|
||||||
|
if ((filter->cx_out >= cx) && (filter->cy_out >= cy))
|
||||||
|
filter->upscale = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,9 +250,12 @@ static void scale_filter_tick(void *data, float seconds)
|
||||||
|
|
||||||
if (type != OBS_EFFECT_DEFAULT) {
|
if (type != OBS_EFFECT_DEFAULT) {
|
||||||
filter->dimension_param = gs_effect_get_param_by_name(
|
filter->dimension_param = gs_effect_get_param_by_name(
|
||||||
|
filter->effect, "base_dimension");
|
||||||
|
filter->dimension_i_param = gs_effect_get_param_by_name(
|
||||||
filter->effect, "base_dimension_i");
|
filter->effect, "base_dimension_i");
|
||||||
} else {
|
} else {
|
||||||
filter->dimension_param = NULL;
|
filter->dimension_param = NULL;
|
||||||
|
filter->dimension_i_param = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == OBS_EFFECT_BICUBIC || type == OBS_EFFECT_LANCZOS) {
|
if (type == OBS_EFFECT_BICUBIC || type == OBS_EFFECT_LANCZOS) {
|
||||||
|
@ -260,7 +271,9 @@ static void scale_filter_tick(void *data, float seconds)
|
||||||
static void scale_filter_render(void *data, gs_effect_t *effect)
|
static void scale_filter_render(void *data, gs_effect_t *effect)
|
||||||
{
|
{
|
||||||
struct scale_filter_data *filter = data;
|
struct scale_filter_data *filter = data;
|
||||||
const char *technique = filter->undistort ? "DrawUndistort" : "Draw";
|
const char *technique =
|
||||||
|
filter->undistort ? "DrawUndistort"
|
||||||
|
: (filter->upscale ? "DrawUpscale" : "Draw");
|
||||||
|
|
||||||
if (!filter->valid || !filter->target_valid) {
|
if (!filter->valid || !filter->target_valid) {
|
||||||
obs_source_skip_video_filter(filter->context);
|
obs_source_skip_video_filter(filter->context);
|
||||||
|
@ -272,7 +285,10 @@ static void scale_filter_render(void *data, gs_effect_t *effect)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (filter->dimension_param)
|
if (filter->dimension_param)
|
||||||
gs_effect_set_vec2(filter->dimension_param,
|
gs_effect_set_vec2(filter->dimension_param, &filter->dimension);
|
||||||
|
|
||||||
|
if (filter->dimension_i_param)
|
||||||
|
gs_effect_set_vec2(filter->dimension_i_param,
|
||||||
&filter->dimension_i);
|
&filter->dimension_i);
|
||||||
|
|
||||||
if (filter->undistort_factor_param)
|
if (filter->undistort_factor_param)
|
||||||
|
|
Loading…
Reference in New Issue