2 Commits e412140298 ... 7cbdb78fff

Tác giả SHA1 Thông báo Ngày
  202226701016 7cbdb78fff 初步demo 10 tháng trước cách đây
  202226701016 e412140298 初步完成 10 tháng trước cách đây
100 tập tin đã thay đổi với 4111 bổ sung0 xóa
  1. 0 0
      .editorconfig
  2. 0 0
      .eslintrc.json
  3. 0 0
      .gitattributes
  4. 0 0
      .github/CODEOWNERS
  5. 0 0
      .github/workflows/CI.yml
  6. 0 0
      .gitignore
  7. 0 0
      LICENSE
  8. BIN
      OBS-Studio-31.0.0-Windows-Installer.exe
  9. BIN
      OBS-Studio-31.0.0-Windows.zip
  10. BIN
      bin/64bit/Qt6Core.dll
  11. BIN
      bin/64bit/Qt6Gui.dll
  12. BIN
      bin/64bit/Qt6Network.dll
  13. BIN
      bin/64bit/Qt6Svg.dll
  14. BIN
      bin/64bit/Qt6Widgets.dll
  15. BIN
      bin/64bit/Qt6Xml.dll
  16. BIN
      bin/64bit/avcodec-61.dll
  17. BIN
      bin/64bit/avdevice-61.dll
  18. BIN
      bin/64bit/avfilter-10.dll
  19. BIN
      bin/64bit/avformat-61.dll
  20. BIN
      bin/64bit/avutil-59.dll
  21. BIN
      bin/64bit/datachannel.dll
  22. BIN
      bin/64bit/iconengines/qsvgicon.dll
  23. BIN
      bin/64bit/imageformats/qgif.dll
  24. BIN
      bin/64bit/imageformats/qicns.dll
  25. BIN
      bin/64bit/imageformats/qico.dll
  26. BIN
      bin/64bit/imageformats/qjpeg.dll
  27. BIN
      bin/64bit/imageformats/qsvg.dll
  28. BIN
      bin/64bit/imageformats/qtga.dll
  29. BIN
      bin/64bit/imageformats/qtiff.dll
  30. BIN
      bin/64bit/imageformats/qwbmp.dll
  31. BIN
      bin/64bit/imageformats/qwebp.dll
  32. BIN
      bin/64bit/libcurl.dll
  33. BIN
      bin/64bit/libobs-d3d11.dll
  34. BIN
      bin/64bit/libobs-d3d11.pdb
  35. BIN
      bin/64bit/libobs-opengl.dll
  36. BIN
      bin/64bit/libobs-opengl.pdb
  37. BIN
      bin/64bit/libobs-winrt.dll
  38. BIN
      bin/64bit/libobs-winrt.pdb
  39. BIN
      bin/64bit/librist.dll
  40. BIN
      bin/64bit/libx264-164.dll
  41. BIN
      bin/64bit/lua51.dll
  42. BIN
      bin/64bit/obs-amf-test.exe
  43. BIN
      bin/64bit/obs-amf-test.pdb
  44. BIN
      bin/64bit/obs-ffmpeg-mux.exe
  45. BIN
      bin/64bit/obs-ffmpeg-mux.pdb
  46. BIN
      bin/64bit/obs-frontend-api.dll
  47. BIN
      bin/64bit/obs-frontend-api.pdb
  48. BIN
      bin/64bit/obs-nvenc-test.exe
  49. BIN
      bin/64bit/obs-nvenc-test.pdb
  50. BIN
      bin/64bit/obs-qsv-test.exe
  51. BIN
      bin/64bit/obs-qsv-test.pdb
  52. BIN
      bin/64bit/obs-scripting.dll
  53. BIN
      bin/64bit/obs-scripting.pdb
  54. BIN
      bin/64bit/obs.dll
  55. BIN
      bin/64bit/obs.pdb
  56. BIN
      bin/64bit/obs64.exe
  57. BIN
      bin/64bit/obs64.pdb
  58. BIN
      bin/64bit/platforms/qminimal.dll
  59. BIN
      bin/64bit/platforms/qwindows.dll
  60. BIN
      bin/64bit/srt.dll
  61. BIN
      bin/64bit/styles/qwindowsvistastyle.dll
  62. BIN
      bin/64bit/swresample-5.dll
  63. BIN
      bin/64bit/swscale-8.dll
  64. BIN
      bin/64bit/w32-pthreads.dll
  65. BIN
      bin/64bit/w32-pthreads.pdb
  66. BIN
      bin/64bit/zlib.dll
  67. 250 0
      data/libobs/area.effect
  68. 236 0
      data/libobs/bicubic_scale.effect
  69. 123 0
      data/libobs/bilinear_lowres_scale.effect
  70. 172 0
      data/libobs/color.effect
  71. 254 0
      data/libobs/default.effect
  72. 84 0
      data/libobs/default_rect.effect
  73. 325 0
      data/libobs/deinterlace_base.effect
  74. 21 0
      data/libobs/deinterlace_blend.effect
  75. 21 0
      data/libobs/deinterlace_blend_2x.effect
  76. 21 0
      data/libobs/deinterlace_discard.effect
  77. 21 0
      data/libobs/deinterlace_discard_2x.effect
  78. 21 0
      data/libobs/deinterlace_linear.effect
  79. 21 0
      data/libobs/deinterlace_linear_2x.effect
  80. 21 0
      data/libobs/deinterlace_yadif.effect
  81. 21 0
      data/libobs/deinterlace_yadif_2x.effect
  82. 1823 0
      data/libobs/format_conversion.effect
  83. 292 0
      data/libobs/lanczos_scale.effect
  84. 159 0
      data/libobs/opaque.effect
  85. 38 0
      data/libobs/premultiplied_alpha.effect
  86. 36 0
      data/libobs/repeat.effect
  87. 80 0
      data/libobs/solid.effect
  88. 4 0
      data/obs-plugins/aja-output-ui/locale/af-ZA.ini
  89. 6 0
      data/obs-plugins/aja-output-ui/locale/ar-SA.ini
  90. 6 0
      data/obs-plugins/aja-output-ui/locale/az-AZ.ini
  91. 6 0
      data/obs-plugins/aja-output-ui/locale/be-BY.ini
  92. 6 0
      data/obs-plugins/aja-output-ui/locale/bg-BG.ini
  93. 6 0
      data/obs-plugins/aja-output-ui/locale/ca-ES.ini
  94. 6 0
      data/obs-plugins/aja-output-ui/locale/cs-CZ.ini
  95. 6 0
      data/obs-plugins/aja-output-ui/locale/da-DK.ini
  96. 6 0
      data/obs-plugins/aja-output-ui/locale/de-DE.ini
  97. 6 0
      data/obs-plugins/aja-output-ui/locale/el-GR.ini
  98. 1 0
      data/obs-plugins/aja-output-ui/locale/en-GB.ini
  99. 6 0
      data/obs-plugins/aja-output-ui/locale/en-US.ini
  100. 6 0
      data/obs-plugins/aja-output-ui/locale/es-ES.ini

+ 0 - 0
docs-demo-main/.editorconfig → .editorconfig


+ 0 - 0
docs-demo-main/.eslintrc.json → .eslintrc.json


+ 0 - 0
docs-demo-main/.gitattributes → .gitattributes


+ 0 - 0
docs-demo-main/.github/CODEOWNERS → .github/CODEOWNERS


+ 0 - 0
docs-demo-main/.github/workflows/CI.yml → .github/workflows/CI.yml


+ 0 - 0
docs-demo-main/.gitignore → .gitignore


+ 0 - 0
docs-demo-main/LICENSE → LICENSE


BIN
OBS-Studio-31.0.0-Windows-Installer.exe


BIN
OBS-Studio-31.0.0-Windows.zip


BIN
bin/64bit/Qt6Core.dll


BIN
bin/64bit/Qt6Gui.dll


BIN
bin/64bit/Qt6Network.dll


BIN
bin/64bit/Qt6Svg.dll


BIN
bin/64bit/Qt6Widgets.dll


BIN
bin/64bit/Qt6Xml.dll


BIN
bin/64bit/avcodec-61.dll


BIN
bin/64bit/avdevice-61.dll


BIN
bin/64bit/avfilter-10.dll


BIN
bin/64bit/avformat-61.dll


BIN
bin/64bit/avutil-59.dll


BIN
bin/64bit/datachannel.dll


BIN
bin/64bit/iconengines/qsvgicon.dll


BIN
bin/64bit/imageformats/qgif.dll


BIN
bin/64bit/imageformats/qicns.dll


BIN
bin/64bit/imageformats/qico.dll


BIN
bin/64bit/imageformats/qjpeg.dll


BIN
bin/64bit/imageformats/qsvg.dll


BIN
bin/64bit/imageformats/qtga.dll


BIN
bin/64bit/imageformats/qtiff.dll


BIN
bin/64bit/imageformats/qwbmp.dll


BIN
bin/64bit/imageformats/qwebp.dll


BIN
bin/64bit/libcurl.dll


BIN
bin/64bit/libobs-d3d11.dll


BIN
bin/64bit/libobs-d3d11.pdb


BIN
bin/64bit/libobs-opengl.dll


BIN
bin/64bit/libobs-opengl.pdb


BIN
bin/64bit/libobs-winrt.dll


BIN
bin/64bit/libobs-winrt.pdb


BIN
bin/64bit/librist.dll


BIN
bin/64bit/libx264-164.dll


BIN
bin/64bit/lua51.dll


BIN
bin/64bit/obs-amf-test.exe


BIN
bin/64bit/obs-amf-test.pdb


BIN
bin/64bit/obs-ffmpeg-mux.exe


BIN
bin/64bit/obs-ffmpeg-mux.pdb


BIN
bin/64bit/obs-frontend-api.dll


BIN
bin/64bit/obs-frontend-api.pdb


BIN
bin/64bit/obs-nvenc-test.exe


BIN
bin/64bit/obs-nvenc-test.pdb


BIN
bin/64bit/obs-qsv-test.exe


BIN
bin/64bit/obs-qsv-test.pdb


BIN
bin/64bit/obs-scripting.dll


BIN
bin/64bit/obs-scripting.pdb


BIN
bin/64bit/obs.dll


BIN
bin/64bit/obs.pdb


BIN
bin/64bit/obs64.exe


BIN
bin/64bit/obs64.pdb


BIN
bin/64bit/platforms/qminimal.dll


BIN
bin/64bit/platforms/qwindows.dll


BIN
bin/64bit/srt.dll


BIN
bin/64bit/styles/qwindowsvistastyle.dll


BIN
bin/64bit/swresample-5.dll


BIN
bin/64bit/swscale-8.dll


BIN
bin/64bit/w32-pthreads.dll


BIN
bin/64bit/w32-pthreads.pdb


BIN
bin/64bit/zlib.dll


+ 250 - 0
data/libobs/area.effect

@@ -0,0 +1,250 @@
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform float2 base_dimension;
+uniform float2 base_dimension_i;
+uniform texture2d image;
+uniform float multiplier;
+
+sampler_state textureSampler {
+	Filter    = Linear;
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+};
+
+struct VertData {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+struct VertInOut {
+	float2 uv  : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct FragData {
+	float2 uv : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertData vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 DrawArea(FragData frag_in)
+{
+	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_begin = floor(uv_min * base_dimension);
+	float2 load_index_end = ceil(uv_max * base_dimension);
+
+	float2 target_dimension = 1.0 / uv_delta;
+	float2 target_pos = uv * target_dimension;
+	float2 target_pos_min = target_pos - 0.5;
+	float2 target_pos_max = target_pos + 0.5;
+	float2 scale = base_dimension_i * target_dimension;
+
+	float4 total_color = float4(0.0, 0.0, 0.0, 0.0);
+
+	float load_index_y = load_index_begin.y;
+	do {
+		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 {
+			float source_x_min = load_index_x * scale.x;
+			float source_x_max = source_x_min + scale.x;
+			float x_min = max(source_x_min, target_pos_min.x);
+			float x_max = min(source_x_max, target_pos_max.x);
+			float width = x_max - x_min;
+			float area = width * height;
+
+			float4 color = image.Load(int3(load_index_x, load_index_y, 0));
+			total_color += area * color;
+
+			++load_index_x;
+		} while (load_index_x < load_index_end.x);
+
+		++load_index_y;
+	} while (load_index_y < load_index_end.y);
+
+	return total_color;
+}
+
+float4 PSDrawAreaRGBA(FragData frag_in) : TARGET
+{
+	return DrawArea(frag_in);
+}
+
+float4 PSDrawAreaRGBAMultiply(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawArea(frag_in);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawAreaRGBATonemap(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawArea(frag_in);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawAreaRGBAMultiplyTonemap(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawArea(frag_in);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 DrawAreaUpscale(FragData frag_in)
+{
+	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);
+}
+
+float4 PSDrawAreaRGBAUpscale(FragData frag_in) : TARGET
+{
+	return DrawAreaUpscale(frag_in);
+}
+
+float4 PSDrawAreaRGBAUpscaleMultiply(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawAreaUpscale(frag_in);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawAreaRGBAUpscaleTonemap(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawAreaUpscale(frag_in);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawAreaRGBAUpscaleMultiplyTonemap(FragData frag_in) : TARGET
+{
+	float4 rgba = DrawAreaUpscale(frag_in);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBA(frag_in);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAMultiply(frag_in);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBATonemap(frag_in);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAMultiplyTonemap(frag_in);
+	}
+}
+
+technique DrawUpscale
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAUpscale(frag_in);
+	}
+}
+
+technique DrawUpscaleMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAUpscaleMultiply(frag_in);
+	}
+}
+
+technique DrawUpscaleTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAUpscaleTonemap(frag_in);
+	}
+}
+
+technique DrawUpscaleMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAreaRGBAUpscaleMultiplyTonemap(frag_in);
+	}
+}

+ 236 - 0
data/libobs/bicubic_scale.effect

@@ -0,0 +1,236 @@
+/*
+ * bicubic sharper (better for downscaling)
+ * note - this shader is adapted from the GPL bsnes shader, very good stuff
+ * there.
+ */
+
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float2 base_dimension;
+uniform float2 base_dimension_i;
+uniform float undistort_factor = 1.0;
+uniform float multiplier;
+
+sampler_state textureSampler {
+	Filter    = Linear;
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+};
+
+struct VertData {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+struct VertOut {
+	float2 uv  : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct FragData {
+	float2 uv : TEXCOORD0;
+};
+
+VertOut VSDefault(VertData v_in)
+{
+	VertOut vert_out;
+	vert_out.uv = v_in.uv * base_dimension;
+	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
+	return vert_out;
+}
+
+float4 weight4(float x)
+{
+	/* Sharper version.  May look better in some cases. B=0, C=0.75 */
+	return float4(
+		((-0.75 * x + 1.5) * x - 0.75) * x,
+		(1.25 * x - 2.25) * x * x + 1.0,
+		((-1.25 * x + 1.5) * x + 0.75) * x,
+		(0.75 * x - 0.75) * x * x);
+}
+
+float AspectUndistortX(float x, float a)
+{
+	// The higher the power, the longer the linear part will be.
+	return (1.0 - a) * (x * x * x * x * x) + a * x;
+}
+
+float AspectUndistortU(float u)
+{
+	// Normalize texture coord to -1.0 to 1.0 range, and back.
+	return AspectUndistortX((u - 0.5) * 2.0, undistort_factor) * 0.5 + 0.5;
+}
+
+float2 undistort_coord(float xpos, float ypos)
+{
+	return float2(AspectUndistortU(xpos), ypos);
+}
+
+float4 undistort_pixel(float xpos, float ypos)
+{
+	return image.Sample(textureSampler, undistort_coord(xpos, ypos));
+}
+
+float4 undistort_line(float4 xpos, float ypos, float4 rowtaps)
+{
+	return undistort_pixel(xpos.x, ypos) * rowtaps.x +
+	       undistort_pixel(xpos.y, ypos) * rowtaps.y +
+	       undistort_pixel(xpos.z, ypos) * rowtaps.z +
+	       undistort_pixel(xpos.w, ypos) * rowtaps.w;
+}
+
+float4 DrawBicubic(FragData f_in, bool undistort)
+{
+	float2 pos = f_in.uv;
+	float2 pos1 = floor(pos - 0.5) + 0.5;
+	float2 f = pos - pos1;
+
+	float4 rowtaps = weight4(f.x);
+	float4 coltaps = weight4(f.y);
+
+	float2 uv1 = pos1 * base_dimension_i;
+	float2 uv0 = uv1 - base_dimension_i;
+	float2 uv2 = uv1 + base_dimension_i;
+	float2 uv3 = uv2 + base_dimension_i;
+
+	if (undistort) {
+		float4 xpos = float4(uv0.x, uv1.x, uv2.x, uv3.x);
+		return undistort_line(xpos, uv0.y, rowtaps) * coltaps.x +
+		       undistort_line(xpos, uv1.y, rowtaps) * coltaps.y +
+		       undistort_line(xpos, uv2.y, rowtaps) * coltaps.z +
+		       undistort_line(xpos, uv3.y, rowtaps) * coltaps.w;
+	}
+
+	float u_weight_sum = rowtaps.y + rowtaps.z;
+	float u_middle_offset = rowtaps.z * base_dimension_i.x / u_weight_sum;
+	float u_middle = uv1.x + u_middle_offset;
+
+	float v_weight_sum = coltaps.y + coltaps.z;
+	float v_middle_offset = coltaps.z * base_dimension_i.y / v_weight_sum;
+	float v_middle = uv1.y + v_middle_offset;
+
+	int2 coord_top_left = int2(max(uv0 * base_dimension, 0.5));
+	int2 coord_bottom_right = int2(min(uv3 * base_dimension, base_dimension - 0.5));
+
+	float4 top = image.Load(int3(coord_top_left, 0)) * rowtaps.x;
+	top += image.Sample(textureSampler, float2(u_middle, uv0.y)) * u_weight_sum;
+	top += image.Load(int3(coord_bottom_right.x, coord_top_left.y, 0)) * rowtaps.w;
+	float4 total = top * coltaps.x;
+
+	float4 middle = image.Sample(textureSampler, float2(uv0.x, v_middle)) * rowtaps.x;
+	middle += image.Sample(textureSampler, float2(u_middle, v_middle)) * u_weight_sum;
+	middle += image.Sample(textureSampler, float2(uv3.x, v_middle)) * rowtaps.w;
+	total += middle * v_weight_sum;
+
+	float4 bottom = image.Load(int3(coord_top_left.x, coord_bottom_right.y, 0)) * rowtaps.x;
+	bottom += image.Sample(textureSampler, float2(u_middle, uv3.y)) * u_weight_sum;
+	bottom += image.Load(int3(coord_bottom_right, 0)) * rowtaps.w;
+	total += bottom * coltaps.w;
+
+	return total;
+}
+
+float4 PSDrawBicubicRGBA(FragData f_in, bool undistort) : TARGET
+{
+	return DrawBicubic(f_in, undistort);
+}
+
+float4 PSDrawBicubicRGBAMultiply(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawBicubic(f_in, undistort);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawBicubicRGBATonemap(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawBicubic(f_in, undistort);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawBicubicRGBAMultiplyTonemap(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawBicubic(f_in, undistort);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader = PSDrawBicubicRGBA(f_in, false);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBAMultiply(f_in, false);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBATonemap(f_in, false);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBAMultiplyTonemap(f_in, false);
+	}
+}
+
+technique DrawUndistort
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader = PSDrawBicubicRGBA(f_in, true);
+	}
+}
+
+technique DrawUndistortMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBAMultiply(f_in, true);
+	}
+}
+
+technique DrawUndistortTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBATonemap(f_in, true);
+	}
+}
+
+technique DrawUndistortMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawBicubicRGBAMultiplyTonemap(f_in, true);
+	}
+}

+ 123 - 0
data/libobs/bilinear_lowres_scale.effect

@@ -0,0 +1,123 @@
+/*
+ * bilinear low res scaling, samples 8 pixels of a larger image to scale to a
+ * low resolution image below half size
+ */
+
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float multiplier;
+
+sampler_state textureSampler {
+	Filter    = Linear;
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+};
+
+struct VertData {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertData VSDefault(VertData v_in)
+{
+	VertData vert_out;
+	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = v_in.uv;
+	return vert_out;
+}
+
+float4 pixel(float2 uv)
+{
+	return image.Sample(textureSampler, uv);
+}
+
+float4 DrawLowresBilinear(VertData f_in)
+{
+	float2 uv = f_in.uv;
+	float2 stepxy  = float2(ddx(uv.x), ddy(uv.y));
+	float2 stepxy1 = stepxy * 0.0625;
+	float2 stepxy3 = stepxy * 0.1875;
+	float2 stepxy5 = stepxy * 0.3125;
+	float2 stepxy7 = stepxy * 0.4375;
+
+	// Simulate Direct3D 8-sample pattern
+	float4 out_color;
+	out_color  = pixel(uv + float2( stepxy1.x, -stepxy3.y));
+	out_color += pixel(uv + float2(-stepxy1.x,  stepxy3.y));
+	out_color += pixel(uv + float2( stepxy5.x,  stepxy1.y));
+	out_color += pixel(uv + float2(-stepxy3.x, -stepxy5.y));
+	out_color += pixel(uv + float2(-stepxy5.x,  stepxy5.y));
+	out_color += pixel(uv + float2(-stepxy7.x, -stepxy1.y));
+	out_color += pixel(uv + float2( stepxy3.x,  stepxy7.y));
+	out_color += pixel(uv + float2( stepxy7.x, -stepxy7.y));
+	return out_color * 0.125;
+}
+
+float4 PSDrawLowresBilinearRGBA(VertData f_in) : TARGET
+{
+	return DrawLowresBilinear(f_in);
+}
+
+float4 PSDrawLowresBilinearRGBAMultiply(VertData f_in) : TARGET
+{
+	float4 rgba = DrawLowresBilinear(f_in);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawLowresBilinearRGBATonemap(VertData f_in) : TARGET
+{
+	float4 rgba = DrawLowresBilinear(f_in);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawLowresBilinearRGBAMultiplyTonemap(VertData f_in) : TARGET
+{
+	float4 rgba = DrawLowresBilinear(f_in);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLowresBilinearRGBA(f_in);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLowresBilinearRGBAMultiply(f_in);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLowresBilinearRGBATonemap(f_in);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLowresBilinearRGBAMultiplyTonemap(f_in);
+	}
+}

+ 172 - 0
data/libobs/color.effect

@@ -0,0 +1,172 @@
+float srgb_linear_to_nonlinear_channel(float u)
+{
+	return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055);
+}
+
+float3 srgb_linear_to_nonlinear(float3 v)
+{
+	return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
+}
+
+float srgb_nonlinear_to_linear_channel(float u)
+{
+	return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
+}
+
+float3 srgb_nonlinear_to_linear(float3 v)
+{
+	return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
+}
+
+float3 rec709_to_rec2020(float3 v)
+{
+	float r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225));
+	float g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178));
+	float b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401));
+	return float3(r, g, b);
+}
+
+float3 d65p3_to_rec709(float3 v)
+{
+	float r = dot(v, float3(1.2249401762805598, -0.22494017628055996, 0.));
+	float g = dot(v, float3(-0.042056954709688163, 1.0420569547096881, 0.));
+	float b = dot(v, float3(-0.019637554590334432, -0.078636045550631889, 1.0982736001409663));
+	return float3(r, g, b);
+}
+
+float3 rec2020_to_rec709(float3 v)
+{
+	float r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883));
+	float g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768));
+	float b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127));
+	return float3(r, g, b);
+}
+
+float3 reinhard(float3 rgb)
+{
+	rgb /= rgb + float3(1., 1., 1.);
+	rgb = saturate(rgb);
+	rgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4));
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return rgb;
+}
+
+float linear_to_st2084_channel(float x)
+{
+	float c = pow(abs(x), 0.1593017578);
+	return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375);
+}
+
+float3 linear_to_st2084(float3 rgb)
+{
+	return float3(linear_to_st2084_channel(rgb.r), linear_to_st2084_channel(rgb.g), linear_to_st2084_channel(rgb.b));
+}
+
+float st2084_to_linear_channel(float u)
+{
+	float c = pow(abs(u), 1. / 78.84375);
+	return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578);
+}
+
+float3 st2084_to_linear(float3 rgb)
+{
+	return float3(st2084_to_linear_channel(rgb.r), st2084_to_linear_channel(rgb.g), st2084_to_linear_channel(rgb.b));
+}
+
+float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax)
+{
+	float Lw_pq = linear_to_st2084_channel(Lw / 10000.);
+	float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie
+	float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq;
+	float KS = (1.5 * maxLum) - 0.5;
+	float E2 = E1;
+	if (E1 > KS)
+	{
+		float T = (E1 - KS) / (1. - KS);
+		float Tsquared = T * T;
+		float Tcubed = Tsquared * T;
+		float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum;
+		E2 = P;
+	}
+	float E3 = E2;
+	float E4 = E3 * Lw_pq;
+	return E4;
+}
+
+float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax)
+{
+	float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax);
+	float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq);
+
+	// avoid divide-by-zero possibility
+	maxRGB1_linear = max(6.10352e-5, maxRGB1_linear);
+
+	rgb_linear *= maxRGB2_linear / maxRGB1_linear;
+	return rgb_linear;
+}
+
+float3 maxRGB_eetf_pq_to_linear(float3 rgb_pq, float Lw, float Lmax)
+{
+	float3 rgb_linear = st2084_to_linear(rgb_pq);
+	float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
+	float maxRGB1_pq = max(max(rgb_pq.r, rgb_pq.g), rgb_pq.b);
+	return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
+}
+
+float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax)
+{
+	float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
+	float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear);
+	return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
+}
+
+float3 st2084_to_linear_eetf(float3 rgb, float Lw, float Lmax)
+{
+	return (Lw > Lmax) ? maxRGB_eetf_pq_to_linear(rgb, Lw, Lmax) : st2084_to_linear(rgb);
+}
+
+float linear_to_hlg_channel(float u)
+{
+	float ln2_i = 1. / log(2.);
+	float m = 0.17883277 / ln2_i;
+	return (u <= (1. / 12.)) ? sqrt(3. * u) : ((log2((12. * u) - 0.28466892) * m) + 0.55991073);
+}
+
+float3 linear_to_hlg(float3 rgb, float Lw)
+{
+	rgb = saturate(rgb);
+
+	if (Lw > 1000.)
+	{
+		rgb = maxRGB_eetf_linear_to_linear(rgb, Lw, 1000.);
+		rgb *= 10000. / Lw;
+	}
+	else
+	{
+		rgb *= 10.;
+	}
+
+	float Yd = dot(rgb, float3(0.2627, 0.678, 0.0593));
+
+	// pow(0., exponent) can lead to NaN, use smallest positive normal number
+	Yd = max(6.10352e-5, Yd);
+
+	rgb *= pow(Yd, -1. / 6.);
+	return float3(linear_to_hlg_channel(rgb.r), linear_to_hlg_channel(rgb.g), linear_to_hlg_channel(rgb.b));
+}
+
+float hlg_to_linear_channel(float u)
+{
+	float ln2_i = 1. / log(2.);
+	float m = ln2_i / 0.17883277;
+	float a = -ln2_i * 0.55991073 / 0.17883277;
+	return (u <= 0.5) ? ((u * u) / 3.) : ((exp2(u * m + a) + 0.28466892) / 12.);
+}
+
+float3 hlg_to_linear(float3 v, float exponent)
+{
+	float3 rgb = float3(hlg_to_linear_channel(v.r), hlg_to_linear_channel(v.g), hlg_to_linear_channel(v.b));
+	float Ys = dot(rgb, float3(0.2627, 0.678, 0.0593));
+	rgb *= pow(Ys, exponent);
+	return rgb;
+}

+ 254 - 0
data/libobs/default.effect

@@ -0,0 +1,254 @@
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float multiplier;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 PSDrawBare(VertInOut vert_in) : TARGET
+{
+	return image.Sample(def_sampler, vert_in.uv);
+}
+
+float4 PSDrawAlphaDivide(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.;
+	return rgba;
+}
+
+float4 PSDrawAlphaDivideTonemap(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawAlphaDivideR10L(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb *= (rgba.a > 0.) ? (multiplier / rgba.a) : 0.;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = linear_to_st2084(rgba.rgb);
+	uint3 rgb1023 = uint3(mad(rgba.rgb, 1023., .5));
+	uint b = (rgb1023.b & 0x3Fu) << 2;
+	uint g = ((rgb1023.b & 0x3C0u) >> 6) | ((rgb1023.g & 0xFu) << 4);
+	uint r = ((rgb1023.g & 0x3F0u) >> 4) | ((rgb1023.r & 0x3u) << 6);
+	uint a = ((rgb1023.r & 0x3FCu) >> 2);
+	return float4(uint4(r, g, b, a)) / 255.;
+}
+
+float4 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
+	rgba.rgb *= rgba.a;
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawNonlinearAlphaMultiply(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
+	rgba.rgb *= rgba.a;
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawSrgbDecompressMultiply(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawMultiply(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawTonemap(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawMultiplyTonemap(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawPQ(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = st2084_to_linear(rgba.rgb) * multiplier;
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawTonemapPQ(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = st2084_to_linear(rgba.rgb) * multiplier;
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawBare(vert_in);
+	}
+}
+
+technique DrawAlphaDivide
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAlphaDivide(vert_in);
+	}
+}
+
+technique DrawAlphaDivideTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAlphaDivideTonemap(vert_in);
+	}
+}
+
+technique DrawAlphaDivideR10L
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawAlphaDivideR10L(vert_in);
+	}
+}
+
+technique DrawNonlinearAlpha
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawNonlinearAlpha(vert_in);
+	}
+}
+
+technique DrawNonlinearAlphaMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawNonlinearAlphaMultiply(vert_in);
+	}
+}
+
+technique DrawSrgbDecompress
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompress(vert_in);
+	}
+}
+
+technique DrawSrgbDecompressMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompressMultiply(vert_in);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawMultiply(vert_in);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawTonemap(vert_in);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawMultiplyTonemap(vert_in);
+	}
+}
+
+technique DrawPQ
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawPQ(vert_in);
+	}
+}
+
+technique DrawTonemapPQ
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawTonemapPQ(vert_in);
+	}
+}

+ 84 - 0
data/libobs/default_rect.effect

@@ -0,0 +1,84 @@
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture_rect image;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 PSDrawBare(VertInOut vert_in) : TARGET
+{
+	return image.Sample(def_sampler, vert_in.uv);
+}
+
+float4 PSDrawD65P3(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	rgba.rgb = d65p3_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawOpaque(VertInOut vert_in) : TARGET
+{
+	return float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);
+}
+
+float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawBare(vert_in);
+	}
+}
+
+technique DrawD65P3
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawD65P3(vert_in);
+	}
+}
+
+technique DrawOpaque
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawOpaque(vert_in);
+	}
+}
+
+technique DrawSrgbDecompress
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompress(vert_in);
+	}
+}

+ 325 - 0
data/libobs/deinterlace_base.effect

@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float multiplier;
+
+uniform texture2d previous_image;
+uniform float2 dimensions;
+uniform int field_order;
+uniform bool frame2;
+
+sampler_state textureSampler {
+	Filter    = Linear;
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+};
+
+struct VertData {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+int3 select(int2 texel, int x, int y)
+{
+	return int3(texel + int2(x, y), 0);
+}
+
+float4 load_at_prev(int2 texel, int x, int y)
+{
+	return previous_image.Load(select(texel, x, y));
+}
+
+float4 load_at_image(int2 texel, int x, int y)
+{
+	return image.Load(select(texel, x, y));
+}
+
+float4 load_at(int2 texel, int x, int y, int field)
+{
+	if(field == 0)
+		return load_at_image(texel, x, y);
+	else
+		return load_at_prev(texel, x, y);
+}
+
+#define YADIF_UPDATE(c, level) 	\
+	if(score.c < spatial_score.c) \
+	{ \
+		spatial_score.c = score.c; \
+		spatial_pred.c = (load_at(texel, level, -1, field) + load_at(texel, -level, 1, field)).c / 2; \
+
+#define YADIF_CHECK_ONE(level, c) \
+{ \
+	float4 score = abs(load_at(texel, -1 + level, 1, field) - load_at(texel, -1 - level, -1, field)) + \
+	               abs(load_at(texel, level,      1, field) - load_at(texel, -level,     -1, field)) + \
+	               abs(load_at(texel, 1 + level,  1, field) - load_at(texel, 1 - level,  -1, field)); \
+	YADIF_UPDATE(c, level) } \
+}
+
+#define YADIF_CHECK(level) \
+{ \
+	float4 score = abs(load_at(texel, -1 + level, 1, field) - load_at(texel, -1 - level, -1, field)) + \
+	               abs(load_at(texel, level,      1, field) - load_at(texel, -level,     -1, field)) + \
+	               abs(load_at(texel, 1 + level,  1, field) - load_at(texel, 1 - level,  -1, field)); \
+	YADIF_UPDATE(r, level) YADIF_CHECK_ONE(level * 2, r) } \
+	YADIF_UPDATE(g, level) YADIF_CHECK_ONE(level * 2, g) } \
+	YADIF_UPDATE(b, level) YADIF_CHECK_ONE(level * 2, b) } \
+	YADIF_UPDATE(a, level) YADIF_CHECK_ONE(level * 2, a) } \
+}
+
+float4 texel_at_yadif(int2 texel, int field, bool mode0)
+{
+	if((texel.y % 2) == field)
+		return load_at(texel, 0, 0, field);
+
+	#define YADIF_AVG(x_off, y_off) ((load_at_prev(texel, x_off, y_off) + load_at_image(texel, x_off, y_off))/2)
+	float4 c = load_at(texel, 0, 1, field),
+	       d = YADIF_AVG(0, 0),
+	       e = load_at(texel, 0, -1, field);
+
+	float4 temporal_diff0 = (abs(load_at_prev(texel,  0, 0)      -     load_at_image(texel, 0, 0)))      / 2,
+	       temporal_diff1 = (abs(load_at_prev(texel,  0, 1) - c) + abs(load_at_prev(texel,  0, -1) - e)) / 2,
+	       temporal_diff2 = (abs(load_at_image(texel, 0, 1) - c) + abs(load_at_image(texel, 0, -1) - e)) / 2,
+	       diff = max(temporal_diff0, max(temporal_diff1, temporal_diff2));
+
+	float4 spatial_pred = (c + e) / 2,
+	       spatial_score = abs(load_at(texel, -1, 1, field) - load_at(texel, -1, -1, field)) +
+	                       abs(c - e) +
+	                       abs(load_at(texel, 1,  1, field) - load_at(texel, 1,  -1, field)) - 1;
+
+	YADIF_CHECK(-1)
+	YADIF_CHECK(1)
+
+	if (mode0) {
+		float4 b = YADIF_AVG(0, 2),
+		       f = YADIF_AVG(0, -2);
+
+		float4 max_ = max(d - e, max(d - c, min(b - c, f - e))),
+		       min_ = min(d - e, min(d - c, max(b - c, f - e)));
+
+		diff = max(diff, max(min_, -max_));
+	} else {
+		diff = max(diff, max(min(d - e, d - c), -max(d - e, d - c)));
+	}
+
+#define YADIF_SPATIAL(c) \
+{ \
+	if(spatial_pred.c > d.c + diff.c) \
+		spatial_pred.c = d.c + diff.c; \
+	else if(spatial_pred.c < d.c - diff.c) \
+		spatial_pred.c = d.c - diff.c; \
+}
+
+	YADIF_SPATIAL(r)
+	YADIF_SPATIAL(g)
+	YADIF_SPATIAL(b)
+	YADIF_SPATIAL(a)
+
+	return spatial_pred;
+}
+
+float4 texel_at_yadif_2x(int2 texel, int field, bool mode0)
+{
+	field = frame2 ? (1 - field) : field;
+	return texel_at_yadif(texel, field, mode0);
+}
+
+float4 texel_at_discard(int2 texel, int field)
+{
+	texel.y = texel.y / 2 * 2;
+	return load_at_image(texel, 0, field);
+}
+
+float4 texel_at_discard_2x(int2 texel, int field)
+{
+	field = frame2 ? field : (1 - field);
+	return texel_at_discard(texel, field);
+}
+
+float4 texel_at_blend(int2 texel, int field)
+{
+	return (load_at_image(texel, 0, 0) + load_at_image(texel, 0, 1)) / 2;
+}
+
+float4 texel_at_blend_2x(int2 texel, int field)
+{
+	if (!frame2)
+		return (load_at_image(texel, 0, 0) +
+		        load_at_prev(texel, 0, 1)) / 2;
+	else
+		return (load_at_image(texel, 0, 0) +
+		        load_at_image(texel, 0, 1)) / 2;
+}
+
+float4 texel_at_linear(int2 texel, int field)
+{
+	if ((texel.y % 2) == field)
+		return load_at_image(texel, 0, 0);
+	return (load_at_image(texel, 0, -1) + load_at_image(texel, 0, 1)) / 2;
+}
+
+float4 texel_at_linear_2x(int2 texel, int field)
+{
+	field = frame2 ? field : (1 - field);
+	return texel_at_linear(texel, field);
+}
+
+float4 texel_at_yadif_discard(int2 texel, int field)
+{
+	return (texel_at_yadif(texel, field, true) + texel_at_discard(texel, field)) / 2;
+}
+
+float4 texel_at_yadif_discard_2x(int2 texel, int field)
+{
+	field = frame2 ? (1 - field) : field;
+	return (texel_at_yadif(texel, field, true) + texel_at_discard(texel, field)) / 2;
+}
+
+int2 pixel_uv(float2 uv)
+{
+	return int2(uv * dimensions);
+}
+
+float4 PSYadifMode0RGBA(VertData v_in) : TARGET
+{
+	return texel_at_yadif(pixel_uv(v_in.uv), field_order, true);
+}
+
+float4 PSYadifMode0RGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_yadif_2x(pixel_uv(v_in.uv), field_order, true);
+}
+
+float4 PSYadifMode2RGBA(VertData v_in) : TARGET
+{
+	return texel_at_yadif(pixel_uv(v_in.uv), field_order, false);
+}
+
+float4 PSYadifMode2RGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_yadif_2x(pixel_uv(v_in.uv), field_order, false);
+}
+
+float4 PSYadifDiscardRGBA(VertData v_in) : TARGET
+{
+	return texel_at_yadif_discard(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSYadifDiscardRGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_yadif_discard_2x(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSLinearRGBA(VertData v_in) : TARGET
+{
+	return texel_at_linear(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSLinearRGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_linear_2x(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSDiscardRGBA(VertData v_in) : TARGET
+{
+	return texel_at_discard(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSDiscardRGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_discard_2x(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSBlendRGBA(VertData v_in) : TARGET
+{
+	return texel_at_blend(pixel_uv(v_in.uv), field_order);
+}
+
+float4 PSBlendRGBA_2x(VertData v_in) : TARGET
+{
+	return texel_at_blend_2x(pixel_uv(v_in.uv), field_order);
+}
+
+VertData VSDefault(VertData v_in)
+{
+	VertData vert_out;
+	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = v_in.uv;
+	return vert_out;
+}
+
+#define TECHNIQUE(rgba_ps, rgba_ps_multiply, rgba_ps_tonemap, rgba_ps_multiply_tonemap) \
+float4 rgba_ps_multiply(VertData v_in) : TARGET \
+{ \
+	float4 rgba = rgba_ps(v_in); \
+	rgba.rgb *= multiplier; \
+	return rgba; \
+} \
+float4 rgba_ps_tonemap(VertData v_in) : TARGET \
+{ \
+	float4 rgba = rgba_ps(v_in); \
+	rgba.rgb = rec709_to_rec2020(rgba.rgb); \
+	rgba.rgb = reinhard(rgba.rgb); \
+	rgba.rgb = rec2020_to_rec709(rgba.rgb); \
+	return rgba; \
+} \
+float4 rgba_ps_multiply_tonemap(VertData v_in) : TARGET \
+{ \
+	float4 rgba = rgba_ps(v_in); \
+	rgba.rgb *= multiplier; \
+	rgba.rgb = rec709_to_rec2020(rgba.rgb); \
+	rgba.rgb = reinhard(rgba.rgb); \
+	rgba.rgb = rec2020_to_rec709(rgba.rgb); \
+	return rgba; \
+} \
+technique Draw \
+{ \
+	pass \
+	{ \
+		vertex_shader = VSDefault(v_in); \
+		pixel_shader  = rgba_ps(v_in); \
+	} \
+} \
+technique DrawMultiply \
+{ \
+	pass \
+	{ \
+		vertex_shader = VSDefault(v_in); \
+		pixel_shader  = rgba_ps_multiply(v_in); \
+	} \
+} \
+technique DrawTonemap \
+{ \
+	pass \
+	{ \
+		vertex_shader = VSDefault(v_in); \
+		pixel_shader  = rgba_ps_tonemap(v_in); \
+	} \
+} \
+technique DrawMultiplyTonemap \
+{ \
+	pass \
+	{ \
+		vertex_shader = VSDefault(v_in); \
+		pixel_shader  = rgba_ps_multiply_tonemap(v_in); \
+	} \
+}

+ 21 - 0
data/libobs/deinterlace_blend.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSBlendRGBA, PSBlendRGBA_multiply, PSBlendRGBA_tonemap, PSBlendRGBA_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_blend_2x.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSBlendRGBA_2x, PSBlendRGBA_2x_multiply, PSBlendRGBA_2x_tonemap, PSBlendRGBA_2x_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_discard.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSDiscardRGBA, PSDiscardRGBA_multiply, PSDiscardRGBA_tonemap, PSDiscardRGBA_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_discard_2x.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSDiscardRGBA_2x, PSDiscardRGBA_2x_multiply, PSDiscardRGBA_2x_tonemap, PSDiscardRGBA_2x_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_linear.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSLinearRGBA, PSLinearRGBA_multiply, PSLinearRGBA_tonemap, PSLinearRGBA_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_linear_2x.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSLinearRGBA_2x, PSLinearRGBA_2x_multiply, PSLinearRGBA_2x_tonemap, PSLinearRGBA_2x_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_yadif.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSYadifMode0RGBA, PSYadifMode0RGBA_multiply, PSYadifMode0RGBA_tonemap, PSYadifMode0RGBA_multiply_tonemap);

+ 21 - 0
data/libobs/deinterlace_yadif_2x.effect

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>
+ *                    John R. Bradley <jrb@turrettech.com>
+ *                    Lain Bailey <lain@obsproject.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "deinterlace_base.effect"
+
+TECHNIQUE(PSYadifMode0RGBA_2x, PSYadifMode0RGBA_2x_multiply, PSYadifMode0RGBA_2x_tonemap, PSYadifMode0RGBA_2x_multiply_tonemap);

+ 1823 - 0
data/libobs/format_conversion.effect

@@ -0,0 +1,1823 @@
+/******************************************************************************
+    Copyright (C) 2023 by Lain Bailey <lain@obsproject.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 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "color.effect"
+
+uniform float     width;
+uniform float     height;
+uniform float     width_i;
+uniform float     height_i;
+uniform float     width_d2;
+uniform float     height_d2;
+uniform float     width_x2_i;
+uniform float     height_x2_i;
+uniform float     maximum_over_sdr_white_nits;
+uniform float     sdr_white_nits_over_maximum;
+uniform float     hlg_exponent;
+uniform float     hdr_lw;
+uniform float     hdr_lmax;
+
+uniform float4    color_vec0;
+uniform float4    color_vec1;
+uniform float4    color_vec2;
+uniform float3    color_range_min = {0.0, 0.0, 0.0};
+uniform float3    color_range_max = {1.0, 1.0, 1.0};
+
+uniform texture2d image;
+uniform texture2d image1;
+uniform texture2d image2;
+uniform texture2d image3;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct FragPos {
+	float4 pos : POSITION;
+};
+
+struct VertTexPos {
+	float2 uv  : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct VertTexTexPos {
+	float4 uvuv  : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct VertTexPosWide {
+	float3 uuv : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct VertTexPosWideWide {
+	float4 uuvv : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct FragTex {
+	float2 uv : TEXCOORD0;
+};
+
+struct FragTexTex {
+	float4 uvuv : TEXCOORD0;
+};
+
+struct FragTexWide {
+	float3 uuv : TEXCOORD0;
+};
+
+struct FragTexWideWide {
+	float4 uuvv : TEXCOORD0;
+};
+
+FragPos VSPos(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4.0 - 1.0;
+	float y = idLow * 4.0 - 1.0;
+
+	FragPos vert_out;
+	vert_out.pos = float4(x, y, 0.0, 1.0);
+	return vert_out;
+}
+
+VertTexPosWide VSTexPos_Left(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4.0 - 1.0;
+	float y = idLow * 4.0 - 1.0;
+
+	float u_right = idHigh * 2.0;
+	float u_left = u_right - width_i;
+	float v = obs_glsl_compile ? (idLow * 2.0) : (1.0 - idLow * 2.0);
+
+	VertTexPosWide vert_out;
+	vert_out.uuv = float3(u_left, u_right, v);
+	vert_out.pos = float4(x, y, 0.0, 1.0);
+	return vert_out;
+}
+
+VertTexPosWideWide VSTexPos_TopLeft(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4.0 - 1.0;
+	float y = idLow * 4.0 - 1.0;
+
+	float u_right = idHigh * 2.0;
+	float u_left = u_right - width_i;
+	float v_bottom;
+	float v_top;
+	if (obs_glsl_compile) {
+		v_bottom = idLow * 2.0;
+		v_top = v_bottom + height_i;
+	} else {
+		v_bottom = 1.0 - idLow * 2.0;
+		v_top = v_bottom - height_i;
+	}
+
+	VertTexPosWideWide vert_out;
+	vert_out.uuvv = float4(u_left, u_right, v_top, v_bottom);
+	vert_out.pos = float4(x, y, 0.0, 1.0);
+	return vert_out;
+}
+
+VertTexTexPos VSPacked422Left_Reverse(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4. - 1.;
+	float y = idLow * 4. - 1.;
+
+	float u = idHigh * 2.;
+	float v = idLow * 2.;
+	v = obs_glsl_compile ? v : (1. - v);
+
+	VertTexTexPos vert_out;
+	vert_out.uvuv = float4(width_d2 * u, height * v, u + width_x2_i, v);
+	vert_out.pos = float4(x, y, 0., 1.);
+	return vert_out;
+}
+
+VertTexPos VS420Left_Reverse(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4. - 1.;
+	float y = idLow * 4. - 1.;
+
+	float u = idHigh * 2. + width_x2_i;
+	float v = idLow * 2.;
+	v = obs_glsl_compile ? v : (1. - v);
+
+	VertTexPos vert_out;
+	vert_out.uv = float2(u, v);
+	vert_out.pos = float4(x, y, 0., 1.);
+	return vert_out;
+}
+
+VertTexPos VS420TopLeft_Reverse(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4. - 1.;
+	float y = idLow * 4. - 1.;
+
+	float u = idHigh * 2. + width_x2_i;
+	float v = idLow * 2. - height_x2_i;
+	v = obs_glsl_compile ? v : (1. - v);
+
+	VertTexPos vert_out;
+	vert_out.uv = float2(u, v);
+	vert_out.pos = float4(x, y, 0., 1.);
+	return vert_out;
+}
+
+VertTexPos VS422Left_Reverse(uint id : VERTEXID)
+{
+	float idHigh = float(id >> 1);
+	float idLow = float(id & uint(1));
+
+	float x = idHigh * 4.0 - 1.0;
+	float y = idLow * 4.0 - 1.0;
+
+	float u = idHigh * 2.0 + width_x2_i;
+	float v = obs_glsl_compile ? (idLow * 2.0) : (1.0 - idLow * 2.0);
+
+	VertTexPos vert_out;
+	vert_out.uv = float2(u, v);
+	vert_out.pos = float4(x, y, 0.0, 1.0);
+	return vert_out;
+}
+
+float PS_Y(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P010_PQ_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	y = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);
+	return y;
+}
+
+float PS_P010_HLG_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	y = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);
+	return y;
+}
+
+float PS_P010_SRGB_Y(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	y = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);
+	return y;
+}
+
+float PS_P216_PQ_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P216_HLG_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P216_SRGB_Y(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P416_PQ_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P416_HLG_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_P416_SRGB_Y(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y;
+}
+
+float PS_I010_PQ_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y * (1023. / 65535.);
+}
+
+float PS_I010_HLG_Y_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y * (1023. / 65535.);
+}
+
+float PS_I010_SRGB_Y(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float y = dot(color_vec0.xyz, rgb) + color_vec0.w;
+	return y * (1023. / 65535.);
+}
+
+float2 PS_UV_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return float2(u, v);
+}
+
+float2 PS_P010_PQ_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	uv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);
+	return uv;
+}
+
+float2 PS_P010_HLG_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	uv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);
+	return uv;
+}
+
+float2 PS_P010_SRGB_UV_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	uv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);
+	return uv;
+}
+
+float2 PS_P216_PQ_UV_709_2020_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * (0.5 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float2 PS_P216_HLG_UV_709_2020_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * (0.5 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float2 PS_P216_SRGB_UV_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float2 PS_P416_PQ_UV_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float2 PS_P416_HLG_UV_709_2020(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float2 PS_P416_SRGB_UV(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	float2 uv = float2(u, v);
+	return uv;
+}
+
+float PS_U(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	return u;
+}
+
+float PS_V(FragPos frag_in) : TARGET
+{
+	float3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return v;
+}
+
+float PS_U_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	return u;
+}
+
+float PS_V_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return v;
+}
+
+float PS_I010_PQ_U_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	return u * (1023. / 65535.);
+}
+
+float PS_I010_HLG_U_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	return u * (1023. / 65535.);
+}
+
+float PS_I010_SRGB_U_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float u = dot(color_vec1.xyz, rgb) + color_vec1.w;
+	return u * (1023. / 65535.);
+}
+
+float PS_I010_PQ_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_st2084(rgb);
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return v * (1023. / 65535.);
+}
+
+float PS_I010_HLG_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET
+{
+	float3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;
+	float3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;
+	float3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;
+	float3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;
+	float3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);
+	rgb = rec709_to_rec2020(rgb);
+	rgb = linear_to_hlg(rgb, hdr_lw);
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return v * (1023. / 65535.);
+}
+
+float PS_I010_SRGB_V_Wide(FragTexWide frag_in) : TARGET
+{
+	float3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;
+	float3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;
+	float3 rgb = (rgb_left + rgb_right) * 0.5;
+	rgb = srgb_linear_to_nonlinear(rgb);
+	float v = dot(color_vec2.xyz, rgb) + color_vec2.w;
+	return v * (1023. / 65535.);
+}
+
+float3 YUV_to_RGB(float3 yuv)
+{
+	yuv = clamp(yuv, color_range_min, color_range_max);
+	float r = dot(color_vec0.xyz, yuv) + color_vec0.w;
+	float g = dot(color_vec1.xyz, yuv) + color_vec1.w;
+	float b = dot(color_vec2.xyz, yuv) + color_vec2.w;
+	return float3(r, g, b);
+}
+
+float3 PSUYVY_Reverse(FragTexTex frag_in) : TARGET
+{
+	float2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).yw;
+	float2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).zx;
+	float leftover = frac(frag_in.uvuv.x);
+	float y = (leftover < 0.5) ? y01.x : y01.y;
+	float3 yuv = float3(y, cbcr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float3 PSYUY2_Reverse(FragTexTex frag_in) : TARGET
+{
+	float2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;
+	float2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;
+	float leftover = frac(frag_in.uvuv.x);
+	float y = (leftover < 0.5) ? y01.x : y01.y;
+	float3 yuv = float3(y, cbcr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSYUY2_PQ_Reverse(FragTexTex frag_in) : TARGET
+{
+	float2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;
+	float2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;
+	float leftover = frac(frag_in.uvuv.x);
+	float y = (leftover < 0.5) ? y01.x : y01.y;
+	float3 yuv = float3(y, cbcr);
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSYUY2_HLG_Reverse(FragTexTex frag_in) : TARGET
+{
+	float2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;
+	float2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;
+	float leftover = frac(frag_in.uvuv.x);
+	float y = (leftover < 0.5) ? y01.x : y01.y;
+	float3 yuv = float3(y, cbcr);
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float3 PSYVYU_Reverse(FragTexTex frag_in) : TARGET
+{
+	float2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;
+	float2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).wy;
+	float leftover = frac(frag_in.uvuv.x);
+	float y = (leftover < 0.5) ? y01.x : y01.y;
+	float3 yuv = float3(y, cbcr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float3 PSPlanar420_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSPlanar420_PQ_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar420_HLG_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar420A_Reverse(VertTexPos frag_in) : TARGET
+{
+	int3 xy0_luma = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0_luma).x;
+	float alpha = image3.Load(xy0_luma).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float4 rgba = float4(YUV_to_RGB(yuv), alpha);
+	return rgba;
+}
+
+float3 PSPlanar422_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSPlanar422_10LE_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 1023.;
+	float3 rgb = YUV_to_RGB(yuv);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar422_10LE_PQ_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 1023.;
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar422_10LE_HLG_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 1023.;
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar422A_Reverse(VertTexPos frag_in) : TARGET
+{
+	int3 xy0_luma = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0_luma).x;
+	float alpha = image3.Load(xy0_luma).x;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x;
+	float3 yuv = float3(y, cb, cr);
+	float4 rgba = float4(YUV_to_RGB(yuv), alpha);
+	return rgba;
+}
+
+float3 PSPlanar444_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float3 yuv = float3(y, cb, cr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSPlanar444_12LE_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 4095.;
+	float3 rgb = YUV_to_RGB(yuv);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar444_12LE_PQ_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 4095;
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar444_12LE_HLG_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 4095;
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSPlanar444A_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float alpha = image3.Load(xy0).x;
+	float3 yuv = float3(y, cb, cr);
+	float4 rgba = float4(YUV_to_RGB(yuv), alpha);
+	return rgba;
+}
+
+float4 PSPlanar444A_12LE_Reverse(FragPos frag_in) : TARGET
+{
+	int3 xy0 = int3(frag_in.pos.xy, 0);
+	float y = image.Load(xy0).x;
+	float cb = image1.Load(xy0).x;
+	float cr = image2.Load(xy0).x;
+	float alpha = image3.Load(xy0).x * 16.;
+	float3 yuv = float3(y, cb, cr);
+	yuv *= 65535. / 4095.;
+	float3 rgb = YUV_to_RGB(yuv);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, alpha);
+}
+
+float4 PSAYUV_Reverse(FragPos frag_in) : TARGET
+{
+	float4 yuva = image.Load(int3(frag_in.pos.xy, 0));
+	float4 rgba = float4(YUV_to_RGB(yuva.xyz), yuva.a);
+	return rgba;
+}
+
+float3 PSNV12_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv = float3(y, cbcr);
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSNV12_PQ_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv = float3(y, cbcr);
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSNV12_HLG_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv = float3(y, cbcr);
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSI010_SRGB_Reverse(VertTexPos frag_in) : TARGET
+{
+	float ratio = 65535. / 1023.;
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;
+	float3 yuv = float3(y, cb, cr);
+	float3 rgb = YUV_to_RGB(yuv);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSI010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET
+{
+	float ratio = 65535. / 1023.;
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;
+	float3 yuv = float3(y, cb, cr);
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSI010_HLG_2020_709_Reverse(VertTexPos frag_in) : TARGET
+{
+	float ratio = 65535. / 1023.;
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;
+	float cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;
+	float cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;
+	float3 yuv = float3(y, cb, cr);
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSP010_SRGB_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);
+	float3 yuv_1023 = floor(yuv_65535 * 0.015625);
+	float3 yuv = yuv_1023 / 1023.;
+	float3 rgb = YUV_to_RGB(yuv);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSP010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);
+	float3 yuv_1023 = floor(yuv_65535 * 0.015625);
+	float3 yuv = yuv_1023 / 1023.;
+	float3 pq = YUV_to_RGB(yuv);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSP010_HLG_2020_709_Reverse(VertTexPos frag_in) : TARGET
+{
+	float y = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;
+	float3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);
+	float3 yuv_1023 = floor(yuv_65535 * 0.015625);
+	float3 yuv = yuv_1023 / 1023.;
+	float3 hlg = YUV_to_RGB(yuv);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float3 compute_v210_reverse(float2 pos)
+{
+	uint x = uint(pos.x);
+	uint packed_x = x % 6u;
+	uint base_x = x / 6u * 4u;
+	float y, cb, cr;
+	if (packed_x == 0u)
+	{
+		float3 word0_rgb = image.Load(int3(base_x, pos.y, 0)).rgb;
+		y = word0_rgb.y;
+		cb = word0_rgb.x;
+		cr = word0_rgb.z;
+	}
+	else if (packed_x == 1u)
+	{
+		float2 word0_rb = image.Load(int3(base_x, pos.y, 0)).rb;
+		float2 word1_rg = image.Load(int3(base_x + 1u, pos.y, 0)).rg;
+		y = word1_rg.x;
+		cb = (word0_rb.x + word1_rg.y) * 0.5;
+		cr = (word0_rb.y + image.Load(int3(base_x + 2u, pos.y, 0)).r) * 0.5;
+	}
+	else if (packed_x == 2u)
+	{
+		float2 word1_gb = image.Load(int3(base_x + 1u, pos.y, 0)).gb;
+		y = word1_gb.y;
+		cb = word1_gb.x;
+		cr = image.Load(int3(base_x + 2u, pos.y, 0)).r;
+	}
+	else if (packed_x == 3u)
+	{
+		float2 word2_rb = image.Load(int3(base_x + 2u, pos.y, 0)).rb;
+		y = image.Load(int3(base_x + 2u, pos.y, 0)).g;
+		cb = (image.Load(int3(base_x + 1u, pos.y, 0)).g + word2_rb.y) * 0.5;
+		cr = (word2_rb.x + image.Load(int3(base_x + 3u, pos.y, 0)).g) * 0.5;
+	}
+	else if (packed_x == 4u)
+	{
+		float2 word3_rg = image.Load(int3(base_x + 3u, pos.y, 0)).rg;
+		y = word3_rg.x;
+		cb = image.Load(int3(base_x + 2u, pos.y, 0)).b;
+		cr = word3_rg.y;
+	}
+	else
+	{
+		float2 word3_gb = image.Load(int3(base_x + 3u, pos.y, 0)).gb;
+		y = word3_gb.y;
+		cb = image.Load(int3(base_x + 2u, pos.y, 0)).b;
+		cr = word3_gb.x;
+		uint base_x_4 = base_x + 4u;
+		if ((pos.x + 1.) < width)
+		{
+			float2 word4_gb = image.Load(int3(base_x + 4u, pos.y, 0)).rb;
+			cb = (cb + word4_gb.x) * 0.5;
+			cr = (cr + word4_gb.y) * 0.5;
+		}
+	}
+	float3 yuv_65535 = floor(float3(y, cb, cr) * 65535. + 0.5);
+	float3 yuv_1023 = floor(yuv_65535 * 0.015625);
+	float3 yuv = yuv_1023 / 1023.;
+	float3 rgb = YUV_to_RGB(yuv);
+	return rgb;
+}
+
+float4 PSV210_SRGB_Reverse(FragPos frag_in) : TARGET
+{
+	float3 rgb = compute_v210_reverse(frag_in.pos.xy);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSV210_PQ_2020_709_Reverse(FragPos frag_in) : TARGET
+{
+	float3 pq = compute_v210_reverse(frag_in.pos.xy);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSV210_HLG_2020_709_Reverse(FragPos frag_in) : TARGET
+{
+	float3 hlg = compute_v210_reverse(frag_in.pos.xy);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float3 PSY800_Limited(FragPos frag_in) : TARGET
+{
+	float limited = image.Load(int3(frag_in.pos.xy, 0)).x;
+	float full = (255.0 / 219.0) * limited - (16.0 / 219.0);
+	return float3(full, full, full);
+}
+
+float3 PSY800_Full(FragPos frag_in) : TARGET
+{
+	float3 full = image.Load(int3(frag_in.pos.xy, 0)).xxx;
+	return full;
+}
+
+float4 PSRGB_Limited(FragPos frag_in) : TARGET
+{
+	float4 rgba = image.Load(int3(frag_in.pos.xy, 0));
+	rgba.rgb = (255.0 / 219.0) * rgba.rgb - (16.0 / 219.0);
+	return rgba;
+}
+
+float3 PSBGR3_Limited(FragPos frag_in) : TARGET
+{
+	float x = frag_in.pos.x * 3.0;
+	float y = frag_in.pos.y;
+	float b = image.Load(int3(x - 1.0, y, 0)).x;
+	float g = image.Load(int3(x, y, 0)).x;
+	float r = image.Load(int3(x + 1.0, y, 0)).x;
+	float3 rgb = float3(r, g, b);
+	rgb = (255.0 / 219.0) * rgb - (16.0 / 219.0);
+	return rgb;
+}
+
+float3 PSBGR3_Full(FragPos frag_in) : TARGET
+{
+	float x = frag_in.pos.x * 3.0;
+	float y = frag_in.pos.y;
+	float b = image.Load(int3(x - 1.0, y, 0)).x;
+	float g = image.Load(int3(x, y, 0)).x;
+	float r = image.Load(int3(x + 1.0, y, 0)).x;
+	float3 rgb = float3(r, g, b);
+	return rgb;
+}
+
+float3 compute_r10l_reverse(float2 pos, bool limited)
+{
+	float4 xyzw = image.Load(int3(pos, 0)).bgra;
+	uint4 xyzw255 = uint4(mad(xyzw, 255., 0.5));
+	uint r = ((xyzw255.z & 0xC0u) >> 6) | (xyzw255.w << 2);
+	uint g = ((xyzw255.y & 0xFu) >> 4) | ((xyzw255.z & 0x3Fu) << 4);
+	uint b = (xyzw255.x >> 2) | ((xyzw255.y & 0xFu) << 6);
+	float3 rgb = float3(uint3(r, g, b));
+	if (limited)
+	{
+		rgb = mad(rgb, 1. / 876., -16. / 219.);
+	}
+	else
+	{
+		rgb /= 1023.;
+	}
+
+	return rgb;
+}
+
+float4 PSR10L_SRGB_Full_Reverse(FragPos frag_in) : TARGET
+{
+	float3 rgb = compute_r10l_reverse(frag_in.pos.xy, false);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSR10L_PQ_2020_709_Full_Reverse(FragPos frag_in) : TARGET
+{
+	float3 pq = compute_r10l_reverse(frag_in.pos.xy, false);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSR10L_HLG_2020_709_Full_Reverse(FragPos frag_in) : TARGET
+{
+	float3 hlg = compute_r10l_reverse(frag_in.pos.xy, false);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSR10L_SRGB_Limited_Reverse(FragPos frag_in) : TARGET
+{
+	float3 rgb = compute_r10l_reverse(frag_in.pos.xy, true);
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.);
+}
+
+float4 PSR10L_PQ_2020_709_Limited_Reverse(FragPos frag_in) : TARGET
+{
+	float3 pq = compute_r10l_reverse(frag_in.pos.xy, true);
+	float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+float4 PSR10L_HLG_2020_709_Limited_Reverse(FragPos frag_in) : TARGET
+{
+	float3 hlg = compute_r10l_reverse(frag_in.pos.xy, true);
+	float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;
+	float3 rgb = rec2020_to_rec709(hdr2020);
+	return float4(rgb, 1.);
+}
+
+technique Planar_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_Y(frag_in);
+	}
+}
+
+technique Planar_U
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_U(frag_in);
+	}
+}
+
+technique Planar_V
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_V(frag_in);
+	}
+}
+
+technique Planar_U_Left
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_U_Wide(frag_in);
+	}
+}
+
+technique Planar_V_Left
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_V_Wide(frag_in);
+	}
+}
+
+technique NV12_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_Y(frag_in);
+	}
+}
+
+technique NV12_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_UV_Wide(frag_in);
+	}
+}
+
+technique I010_PQ_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_I010_PQ_Y_709_2020(frag_in);
+	}
+}
+
+technique I010_HLG_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_I010_HLG_Y_709_2020(frag_in);
+	}
+}
+
+technique I010_SRGB_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_I010_SRGB_Y(frag_in);
+	}
+}
+
+technique I010_PQ_U
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_I010_PQ_U_709_2020_WideWide(frag_in);
+	}
+}
+
+technique I010_HLG_U
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_I010_HLG_U_709_2020_WideWide(frag_in);
+	}
+}
+
+technique I010_SRGB_U
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_I010_SRGB_U_Wide(frag_in);
+	}
+}
+
+technique I010_PQ_V
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_I010_PQ_V_709_2020_WideWide(frag_in);
+	}
+}
+
+technique I010_HLG_V
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_I010_HLG_V_709_2020_WideWide(frag_in);
+	}
+}
+
+technique I010_SRGB_V
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_I010_SRGB_V_Wide(frag_in);
+	}
+}
+
+technique P010_PQ_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P010_PQ_Y_709_2020(frag_in);
+	}
+}
+
+technique P010_HLG_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P010_HLG_Y_709_2020(frag_in);
+	}
+}
+
+technique P010_SRGB_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P010_SRGB_Y(frag_in);
+	}
+}
+
+technique P010_PQ_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_P010_PQ_UV_709_2020_WideWide(frag_in);
+	}
+}
+
+technique P010_HLG_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_TopLeft(id);
+		pixel_shader  = PS_P010_HLG_UV_709_2020_WideWide(frag_in);
+	}
+}
+
+technique P010_SRGB_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_P010_SRGB_UV_Wide(frag_in);
+	}
+}
+
+technique P216_PQ_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P216_PQ_Y_709_2020(frag_in);
+	}
+}
+
+technique P216_HLG_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P216_HLG_Y_709_2020(frag_in);
+	}
+}
+
+technique P216_SRGB_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P216_SRGB_Y(frag_in);
+	}
+}
+
+technique P216_PQ_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_P216_PQ_UV_709_2020_Wide(frag_in);
+	}
+}
+
+technique P216_HLG_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_P216_HLG_UV_709_2020_Wide(frag_in);
+	}
+}
+
+technique P216_SRGB_UV
+{
+	pass
+	{
+		vertex_shader = VSTexPos_Left(id);
+		pixel_shader  = PS_P216_SRGB_UV_Wide(frag_in);
+	}
+}
+
+technique P416_PQ_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_PQ_Y_709_2020(frag_in);
+	}
+}
+
+technique P416_HLG_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_HLG_Y_709_2020(frag_in);
+	}
+}
+
+technique P416_SRGB_Y
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_SRGB_Y(frag_in);
+	}
+}
+
+technique P416_PQ_UV
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_PQ_UV_709_2020(frag_in);
+	}
+}
+
+technique P416_HLG_UV
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_HLG_UV_709_2020(frag_in);
+	}
+}
+
+technique P416_SRGB_UV
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PS_P416_SRGB_UV(frag_in);
+	}
+}
+
+technique UYVY_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPacked422Left_Reverse(id);
+		pixel_shader  = PSUYVY_Reverse(frag_in);
+	}
+}
+
+technique YUY2_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPacked422Left_Reverse(id);
+		pixel_shader  = PSYUY2_Reverse(frag_in);
+	}
+}
+
+technique YUY2_PQ_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPacked422Left_Reverse(id);
+		pixel_shader  = PSYUY2_PQ_Reverse(frag_in);
+	}
+}
+
+technique YUY2_HLG_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPacked422Left_Reverse(id);
+		pixel_shader  = PSYUY2_HLG_Reverse(frag_in);
+	}
+}
+
+technique YVYU_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPacked422Left_Reverse(id);
+		pixel_shader  = PSYVYU_Reverse(frag_in);
+	}
+}
+
+technique I420_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420Left_Reverse(id);
+		pixel_shader  = PSPlanar420_Reverse(frag_in);
+	}
+}
+
+technique I420_PQ_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSPlanar420_PQ_Reverse(frag_in);
+	}
+}
+
+technique I420_HLG_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSPlanar420_HLG_Reverse(frag_in);
+	}
+}
+
+technique I40A_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420Left_Reverse(id);
+		pixel_shader  = PSPlanar420A_Reverse(frag_in);
+	}
+}
+
+technique I422_Reverse
+{
+	pass
+	{
+		vertex_shader = VS422Left_Reverse(id);
+		pixel_shader  = PSPlanar422_Reverse(frag_in);
+	}
+}
+
+technique I210_Reverse
+{
+	pass
+	{
+		vertex_shader = VS422Left_Reverse(id);
+		pixel_shader  = PSPlanar422_10LE_Reverse(frag_in);
+	}
+}
+
+technique I210_PQ_Reverse
+{
+	pass
+	{
+		vertex_shader = VS422Left_Reverse(id);
+		pixel_shader  = PSPlanar422_10LE_PQ_Reverse(frag_in);
+	}
+}
+
+technique I210_HLG_Reverse
+{
+	pass
+	{
+		vertex_shader = VS422Left_Reverse(id);
+		pixel_shader  = PSPlanar422_10LE_HLG_Reverse(frag_in);
+	}
+}
+
+technique I42A_Reverse
+{
+	pass
+	{
+		vertex_shader = VS422Left_Reverse(id);
+		pixel_shader  = PSPlanar422A_Reverse(frag_in);
+	}
+}
+
+technique I444_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444_Reverse(frag_in);
+	}
+}
+
+technique I412_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444_12LE_Reverse(frag_in);
+	}
+}
+
+technique I412_PQ_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444_12LE_PQ_Reverse(frag_in);
+	}
+}
+
+technique I412_HLG_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444_12LE_HLG_Reverse(frag_in);
+	}
+}
+
+technique YUVA_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444A_Reverse(frag_in);
+	}
+}
+
+technique YA2L_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSPlanar444A_12LE_Reverse(frag_in);
+	}
+}
+
+technique AYUV_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSAYUV_Reverse(frag_in);
+	}
+}
+
+technique NV12_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420Left_Reverse(id);
+		pixel_shader  = PSNV12_Reverse(frag_in);
+	}
+}
+
+technique NV12_PQ_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSNV12_PQ_Reverse(frag_in);
+	}
+}
+
+technique NV12_HLG_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSNV12_HLG_Reverse(frag_in);
+	}
+}
+
+technique I010_SRGB_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420Left_Reverse(id);
+		pixel_shader  = PSI010_SRGB_Reverse(frag_in);
+	}
+}
+
+technique I010_PQ_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSI010_PQ_2020_709_Reverse(frag_in);
+	}
+}
+
+technique I010_HLG_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSI010_HLG_2020_709_Reverse(frag_in);
+	}
+}
+
+technique P010_SRGB_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420Left_Reverse(id);
+		pixel_shader  = PSP010_SRGB_Reverse(frag_in);
+	}
+}
+
+technique P010_PQ_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSP010_PQ_2020_709_Reverse(frag_in);
+	}
+}
+
+technique P010_HLG_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VS420TopLeft_Reverse(id);
+		pixel_shader  = PSP010_HLG_2020_709_Reverse(frag_in);
+	}
+}
+
+technique V210_SRGB_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSV210_SRGB_Reverse(frag_in);
+	}
+}
+
+technique V210_PQ_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSV210_PQ_2020_709_Reverse(frag_in);
+	}
+}
+
+technique V210_HLG_2020_709_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSV210_HLG_2020_709_Reverse(frag_in);
+	}
+}
+
+technique Y800_Limited
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSY800_Limited(frag_in);
+	}
+}
+
+technique Y800_Full
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSY800_Full(frag_in);
+	}
+}
+
+technique RGB_Limited
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSRGB_Limited(frag_in);
+	}
+}
+
+technique BGR3_Limited
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSBGR3_Limited(frag_in);
+	}
+}
+
+technique BGR3_Full
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSBGR3_Full(frag_in);
+	}
+}
+
+technique R10L_SRGB_Full_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_SRGB_Full_Reverse(frag_in);
+	}
+}
+
+technique R10L_PQ_2020_709_Full_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_PQ_2020_709_Full_Reverse(frag_in);
+	}
+}
+
+technique R10L_HLG_2020_709_Full_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_HLG_2020_709_Full_Reverse(frag_in);
+	}
+}
+
+technique R10L_SRGB_Limited_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_SRGB_Limited_Reverse(frag_in);
+	}
+}
+
+technique R10L_PQ_2020_709_Limited_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_PQ_2020_709_Limited_Reverse(frag_in);
+	}
+}
+
+technique R10L_HLG_2020_709_Limited_Reverse
+{
+	pass
+	{
+		vertex_shader = VSPos(id);
+		pixel_shader  = PSR10L_HLG_2020_709_Limited_Reverse(frag_in);
+	}
+}

+ 292 - 0
data/libobs/lanczos_scale.effect

@@ -0,0 +1,292 @@
+/*
+ * lanczos sharper
+ * note - this shader is adapted from the GPL bsnes shader, very good stuff
+ * there.
+ */
+
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float2 base_dimension;
+uniform float2 base_dimension_i;
+uniform float undistort_factor = 1.0;
+uniform float multiplier;
+
+sampler_state textureSampler
+{
+	AddressU  = Clamp;
+	AddressV  = Clamp;
+	Filter    = Linear;
+};
+
+struct VertData {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+struct VertOut {
+	float2 uv : TEXCOORD0;
+	float4 pos : POSITION;
+};
+
+struct FragData {
+	float2 uv : TEXCOORD0;
+};
+
+VertOut VSDefault(VertData v_in)
+{
+	VertOut vert_out;
+	vert_out.uv  = v_in.uv * base_dimension;
+	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
+
+	return vert_out;
+}
+
+float weight(float x)
+{
+	float x_pi = x * 3.141592654;
+	return 3.0 * sin(x_pi) * sin(x_pi * (1.0 / 3.0)) / (x_pi * x_pi);
+}
+
+void weight6(float f_neg, out float3 tap012, out float3 tap345)
+{
+	tap012 = float3(
+		weight(f_neg - 2.0),
+		weight(f_neg - 1.0),
+		min(1.0, weight(f_neg))); // Replace NaN with 1.0.
+	tap345 = float3(
+		weight(f_neg + 1.0),
+		weight(f_neg + 2.0),
+		weight(f_neg + 3.0));
+
+	// Normalize weights
+	float sum = tap012.x + tap012.y + tap012.z + tap345.x + tap345.y + tap345.z;
+	float sum_i = 1.0 / sum;
+	tap012 = tap012 * sum_i;
+	tap345 = tap345 * sum_i;
+}
+
+float AspectUndistortX(float x, float a)
+{
+	// The higher the power, the longer the linear part will be.
+	return (1.0 - a) * (x * x * x * x * x) + a * x;
+}
+
+float AspectUndistortU(float u)
+{
+	// Normalize texture coord to -1.0 to 1.0 range, and back.
+	return AspectUndistortX((u - 0.5) * 2.0, undistort_factor) * 0.5 + 0.5;
+}
+
+float2 undistort_coord(float xpos, float ypos)
+{
+	return float2(AspectUndistortU(xpos), ypos);
+}
+
+float4 undistort_pixel(float xpos, float ypos)
+{
+	return image.Sample(textureSampler, undistort_coord(xpos, ypos));
+}
+
+float4 undistort_line(float3 xpos012, float3 xpos345, float ypos, float3 rowtap012,
+		float3 rowtap345)
+{
+	return
+		undistort_pixel(xpos012.x, ypos) * rowtap012.x +
+		undistort_pixel(xpos012.y, ypos) * rowtap012.y +
+		undistort_pixel(xpos012.z, ypos) * rowtap012.z +
+		undistort_pixel(xpos345.x, ypos) * rowtap345.x +
+		undistort_pixel(xpos345.y, ypos) * rowtap345.y +
+		undistort_pixel(xpos345.z, ypos) * rowtap345.z;
+}
+
+float4 DrawLanczos(FragData f_in, bool undistort)
+{
+	float2 pos = f_in.uv;
+	float2 pos2 = floor(pos - 0.5) + 0.5;
+	float2 f_neg = pos2 - pos;
+
+	float3 rowtap012, rowtap345;
+	weight6(f_neg.x, rowtap012, rowtap345);
+
+	float3 coltap012, coltap345;
+	weight6(f_neg.y, coltap012, coltap345);
+
+	float2 uv2 = pos2 * base_dimension_i;
+	float2 uv1 = uv2 - base_dimension_i;
+	float2 uv0 = uv1 - base_dimension_i;
+	float2 uv3 = uv2 + base_dimension_i;
+	float2 uv4 = uv3 + base_dimension_i;
+	float2 uv5 = uv4 + base_dimension_i;
+
+	if (undistort) {
+		float3 xpos012 = float3(uv0.x, uv1.x, uv2.x);
+		float3 xpos345 = float3(uv3.x, uv4.x, uv5.x);
+		return undistort_line(xpos012, xpos345, uv0.y, rowtap012, rowtap345) * coltap012.x +
+		       undistort_line(xpos012, xpos345, uv1.y, rowtap012, rowtap345) * coltap012.y +
+		       undistort_line(xpos012, xpos345, uv2.y, rowtap012, rowtap345) * coltap012.z +
+		       undistort_line(xpos012, xpos345, uv3.y, rowtap012, rowtap345) * coltap345.x +
+		       undistort_line(xpos012, xpos345, uv4.y, rowtap012, rowtap345) * coltap345.y +
+		       undistort_line(xpos012, xpos345, uv5.y, rowtap012, rowtap345) * coltap345.z;
+	}
+
+	float u_weight_sum = rowtap012.z + rowtap345.x;
+	float u_middle_offset = rowtap345.x * base_dimension_i.x / u_weight_sum;
+	float u_middle = uv2.x + u_middle_offset;
+
+	float v_weight_sum = coltap012.z + coltap345.x;
+	float v_middle_offset = coltap345.x * base_dimension_i.y / v_weight_sum;
+	float v_middle = uv2.y + v_middle_offset;
+
+	float2 coord_limit = base_dimension - 0.5;
+	float2 coord0_f = max(uv0 * base_dimension, 0.5);
+	float2 coord1_f = max(uv1 * base_dimension, 0.5);
+	float2 coord4_f = min(uv4 * base_dimension, coord_limit);
+	float2 coord5_f = min(uv5 * base_dimension, coord_limit);
+
+	int2 coord0 = int2(coord0_f);
+	int2 coord1 = int2(coord1_f);
+	int2 coord4 = int2(coord4_f);
+	int2 coord5 = int2(coord5_f);
+
+	float4 row0 = image.Load(int3(coord0, 0)) * rowtap012.x;
+	row0 += image.Load(int3(coord1.x, coord0.y, 0)) * rowtap012.y;
+	row0 += image.Sample(textureSampler, float2(u_middle, uv0.y)) * u_weight_sum;
+	row0 += image.Load(int3(coord4.x, coord0.y, 0)) * rowtap345.y;
+	row0 += image.Load(int3(coord5.x, coord0.y, 0)) * rowtap345.z;
+	float4 total = row0 * coltap012.x;
+
+	float4 row1 = image.Load(int3(coord0.x, coord1.y, 0)) * rowtap012.x;
+	row1 += image.Load(int3(coord1.x, coord1.y, 0)) * rowtap012.y;
+	row1 += image.Sample(textureSampler, float2(u_middle, uv1.y)) * u_weight_sum;
+	row1 += image.Load(int3(coord4.x, coord1.y, 0)) * rowtap345.y;
+	row1 += image.Load(int3(coord5.x, coord1.y, 0)) * rowtap345.z;
+	total += row1 * coltap012.y;
+
+	float4 row23 = image.Sample(textureSampler, float2(uv0.x, v_middle)) * rowtap012.x;
+	row23 += image.Sample(textureSampler, float2(uv1.x, v_middle)) * rowtap012.y;
+	row23 += image.Sample(textureSampler, float2(u_middle, v_middle)) * u_weight_sum;
+	row23 += image.Sample(textureSampler, float2(uv4.x, v_middle)) * rowtap345.y;
+	row23 += image.Sample(textureSampler, float2(uv5.x, v_middle)) * rowtap345.z;
+	total += row23 * v_weight_sum;
+
+	float4 row4 = image.Load(int3(coord0.x, coord4.y, 0)) * rowtap012.x;
+	row4 += image.Load(int3(coord1.x, coord4.y, 0)) * rowtap012.y;
+	row4 += image.Sample(textureSampler, float2(u_middle, uv4.y)) * u_weight_sum;
+	row4 += image.Load(int3(coord4.x, coord4.y, 0)) * rowtap345.y;
+	row4 += image.Load(int3(coord5.x, coord4.y, 0)) * rowtap345.z;
+	total += row4 * coltap345.y;
+
+	float4 row5 = image.Load(int3(coord0.x, coord5.y, 0)) * rowtap012.x;
+	row5 += image.Load(int3(coord1.x, coord5.y, 0)) * rowtap012.y;
+	row5 += image.Sample(textureSampler, float2(u_middle, uv5.y)) * u_weight_sum;
+	row5 += image.Load(int3(coord4.x, coord5.y, 0)) * rowtap345.y;
+	row5 += image.Load(int3(coord5, 0)) * rowtap345.z;
+	total += row5 * coltap345.z;
+
+	return total;
+}
+
+float4 PSDrawLanczosRGBA(FragData f_in, bool undistort) : TARGET
+{
+	return DrawLanczos(f_in, undistort);
+}
+
+float4 PSDrawLanczosRGBAMultiply(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawLanczos(f_in, undistort);
+	rgba.rgb *= multiplier;
+	return rgba;
+}
+
+float4 PSDrawLanczosRGBATonemap(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawLanczos(f_in, undistort);
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+float4 PSDrawLanczosRGBAMultiplyTonemap(FragData f_in, bool undistort) : TARGET
+{
+	float4 rgba = DrawLanczos(f_in, undistort);
+	rgba.rgb *= multiplier;
+	rgba.rgb = rec709_to_rec2020(rgba.rgb);
+	rgba.rgb = reinhard(rgba.rgb);
+	rgba.rgb = rec2020_to_rec709(rgba.rgb);
+	return rgba;
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBA(f_in, false);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBAMultiply(f_in, false);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBATonemap(f_in, false);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBAMultiplyTonemap(f_in, false);
+	}
+}
+
+technique DrawUndistort
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBA(f_in, true);
+	}
+}
+
+technique DrawUndistortMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBAMultiply(f_in, true);
+	}
+}
+
+technique DrawUndistortTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBATonemap(f_in, true);
+	}
+}
+
+technique DrawUndistortMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(v_in);
+		pixel_shader  = PSDrawLanczosRGBAMultiplyTonemap(f_in, true);
+	}
+}

+ 159 - 0
data/libobs/opaque.effect

@@ -0,0 +1,159 @@
+#include "color.effect"
+
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float multiplier;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 PSDraw(VertInOut vert_in) : TARGET
+{
+	return float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);
+}
+
+float4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb = srgb_nonlinear_to_linear(rgb);
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawSrgbDecompressMultiply(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb = srgb_nonlinear_to_linear(rgb);
+	rgb *= multiplier;
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawMultiply(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb *= multiplier;
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawTonemap(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = reinhard(rgb);
+	rgb = rec2020_to_rec709(rgb);
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawMultiplyTonemap(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb *= multiplier;
+	rgb = rec709_to_rec2020(rgb);
+	rgb = reinhard(rgb);
+	rgb = rec2020_to_rec709(rgb);
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawPQ(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb = st2084_to_linear(rgb) * multiplier;
+	rgb = rec2020_to_rec709(rgb);
+	return float4(rgb, 1.0);
+}
+
+float4 PSDrawTonemapPQ(VertInOut vert_in) : TARGET
+{
+	float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;
+	rgb = st2084_to_linear(rgb) * multiplier;
+	rgb = reinhard(rgb);
+	rgb = rec2020_to_rec709(rgb);
+	return float4(rgb, 1.0);
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDraw(vert_in);
+	}
+}
+
+technique DrawSrgbDecompress
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompress(vert_in);
+	}
+}
+
+technique DrawSrgbDecompressMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawSrgbDecompressMultiply(vert_in);
+	}
+}
+
+technique DrawMultiply
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawMultiply(vert_in);
+	}
+}
+
+technique DrawTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawTonemap(vert_in);
+	}
+}
+
+technique DrawMultiplyTonemap
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawMultiplyTonemap(vert_in);
+	}
+}
+
+technique DrawPQ
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawPQ(vert_in);
+	}
+}
+
+technique DrawTonemapPQ
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawTonemapPQ(vert_in);
+	}
+}

+ 38 - 0
data/libobs/premultiplied_alpha.effect

@@ -0,0 +1,38 @@
+uniform float4x4 ViewProj;
+uniform texture2d image;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 PSDraw(VertInOut vert_in) : TARGET
+{
+	float4 rgba = image.Sample(def_sampler, vert_in.uv);
+	if (rgba.a > 0.0)
+		rgba.rgb /= rgba.a;
+	return saturate(rgba);
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDraw(vert_in);
+	}
+}

+ 36 - 0
data/libobs/repeat.effect

@@ -0,0 +1,36 @@
+uniform float4x4 ViewProj;
+uniform texture2d image;
+uniform float2 scale;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Repeat;
+	AddressV = Repeat;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv * scale;
+	return vert_out;
+}
+
+float4 PSDrawBare(VertInOut vert_in) : TARGET
+{
+	return image.Sample(def_sampler, vert_in.uv);
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDrawBare(vert_in);
+	}
+}

+ 80 - 0
data/libobs/solid.effect

@@ -0,0 +1,80 @@
+uniform float4x4 ViewProj;
+uniform float4 color = {1.0, 1.0, 1.0, 1.0};
+
+uniform float4 randomvals1;
+uniform float4 randomvals2;
+uniform float4 randomvals3;
+
+struct SolidVertInOut {
+	float4 pos : POSITION;
+};
+
+SolidVertInOut VSSolid(SolidVertInOut vert_in)
+{
+	SolidVertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	return vert_out;
+}
+
+float4 PSSolid(SolidVertInOut vert_in) : TARGET
+{
+	return color;
+}
+
+float rand(float4 pos, float4 rand_vals)
+{
+	return 0.5 + 0.5 * frac(sin(dot(pos.xy, float2(rand_vals.x, rand_vals.y))) * rand_vals.z);
+}
+
+float4 PSRandom(SolidVertInOut vert_in) : TARGET
+{
+	return float4(rand(vert_in.pos, randomvals1),
+	              rand(vert_in.pos, randomvals2),
+	              rand(vert_in.pos, randomvals3),
+	              1.0);
+}
+
+struct SolidColoredVertInOut {
+	float4 pos   : POSITION;
+	float4 color : COLOR;
+};
+
+SolidColoredVertInOut VSSolidColored(SolidColoredVertInOut vert_in)
+{
+	SolidColoredVertInOut vert_out;
+	vert_out.pos   = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.color = vert_in.color;
+	return vert_out;
+}
+
+float4 PSSolidColored(SolidColoredVertInOut vert_in) : TARGET
+{
+	return vert_in.color * color;
+}
+
+technique Solid
+{
+	pass
+	{
+		vertex_shader = VSSolid(vert_in);
+		pixel_shader  = PSSolid(vert_in);
+	}
+}
+
+technique SolidColored
+{
+	pass
+	{
+		vertex_shader = VSSolidColored(vert_in);
+		pixel_shader  = PSSolidColored(vert_in);
+	}
+}
+
+technique Random
+{
+	pass
+	{
+		vertex_shader = VSSolid(vert_in);
+		pixel_shader  = PSRandom(vert_in);
+	}
+}

+ 4 - 0
data/obs-plugins/aja-output-ui/locale/af-ZA.ini

@@ -0,0 +1,4 @@
+AJAOutput.Device="AJA I/O-toestelafvoer"
+AJAOutput.ProgramOutput="Programafvoer"
+AJAOutput.PreviewOutput="Voorskou-afvoer"
+AJAOutput.MiscOutput="Bykomende instellings"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/ar-SA.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="مَخرج جهاز AJA I/O"
+AJAOutput.ProgramOutput="مَخرج البرنامج"
+AJAOutput.PreviewOutput="مَخرج المعاينة"
+AJAOutput.MiscOutput="إعدادات إضافية"
+AJAOutput.MultiViewEnable="تمكين عرض متعدد"
+AJAOutput.MultiViewAudioSource="مصدر الصوت للعرض المتعدد"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/az-AZ.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="AJA I/O Cihaz Çıxışı"
+AJAOutput.ProgramOutput="Proqram çıxışı"
+AJAOutput.PreviewOutput="Önizləmə çıxışı"
+AJAOutput.MiscOutput="Əlavə Parametrlər"
+AJAOutput.MultiViewEnable="Çoxlu Görünüşü Aktivləşdir"
+AJAOutput.MultiViewAudioSource="Çoxbaxışlı Səs Mənbəsi"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/be-BY.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Захоп прылады AJA I/O"
+AJAOutput.ProgramOutput="Праграмны вывад"
+AJAOutput.PreviewOutput="Прадпрагляд вываду"
+AJAOutput.MiscOutput="Дадатковыя налады"
+AJAOutput.MultiViewEnable="Уключыць мультыпрагляд"
+AJAOutput.MultiViewAudioSource="Мультыпрагляд крыніц аўдыя"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/bg-BG.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Изход на устройството AJA I/O"
+AJAOutput.ProgramOutput="Програмен изход"
+AJAOutput.PreviewOutput="Нагледно изхода"
+AJAOutput.MiscOutput="Допълнителни настройки"
+AJAOutput.MultiViewEnable="Включване на множко изгледи"
+AJAOutput.MultiViewAudioSource="Източник на звука за множко изгледи"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/ca-ES.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Dispositiu de sortida d'E/S AJA"
+AJAOutput.ProgramOutput="Sortida del programa"
+AJAOutput.PreviewOutput="Sortida de la vista prèvia"
+AJAOutput.MiscOutput="Configuració addicional"
+AJAOutput.MultiViewEnable="Habilita la visualització múltiple"
+AJAOutput.MultiViewAudioSource="Font d'àudio de visualització múltiple"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/cs-CZ.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Výstup AJA I/O zařízení"
+AJAOutput.ProgramOutput="Výstup programu"
+AJAOutput.PreviewOutput="Náhled výstupu"
+AJAOutput.MiscOutput="Další nastavení"
+AJAOutput.MultiViewEnable="Povolit Multiview"
+AJAOutput.MultiViewAudioSource="Zdroj zvuku pro Multiview"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/da-DK.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="AJA I/O-enhedsoutput"
+AJAOutput.ProgramOutput="Programoutput"
+AJAOutput.PreviewOutput="Forhåndsvisningsoutput"
+AJAOutput.MiscOutput="Yderligere indstillinger"
+AJAOutput.MultiViewEnable="Aktivér Multivisning"
+AJAOutput.MultiViewAudioSource="Multivisning Lydkilde"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/de-DE.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="AJA-I/O-Geräteausgabe"
+AJAOutput.ProgramOutput="Programmausgabe"
+AJAOutput.PreviewOutput="Vorschauausgabe"
+AJAOutput.MiscOutput="Weitere Einstellungen"
+AJAOutput.MultiViewEnable="Multiview aktivieren"
+AJAOutput.MultiViewAudioSource="Multiview-Audioquelle"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/el-GR.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Συσκευή εξόδου I/O AJA"
+AJAOutput.ProgramOutput="Έξοδος Προγράμματος"
+AJAOutput.PreviewOutput="Προεπισκόπηση Εξόδου"
+AJAOutput.MiscOutput="Πρόσθετες Ρυθμίσεις"
+AJAOutput.MultiViewEnable="Ενεργοποίηση Πολλαπλής Προβολής"
+AJAOutput.MultiViewAudioSource="Πηγή Ήχου Πολλαπλής Προβολής"

+ 1 - 0
data/obs-plugins/aja-output-ui/locale/en-GB.ini

@@ -0,0 +1 @@
+#

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/en-US.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="AJA I/O Device Output"
+AJAOutput.ProgramOutput="Program Output"
+AJAOutput.PreviewOutput="Preview Output"
+AJAOutput.MiscOutput="Additional Settings"
+AJAOutput.MultiViewEnable="Enable Multi View"
+AJAOutput.MultiViewAudioSource="Multi View Audio Source"

+ 6 - 0
data/obs-plugins/aja-output-ui/locale/es-ES.ini

@@ -0,0 +1,6 @@
+AJAOutput.Device="Salida de dispositivo de E/S AJA"
+AJAOutput.ProgramOutput="Salida del programa"
+AJAOutput.PreviewOutput="Salida de la vista previa"
+AJAOutput.MiscOutput="Ajustes adicionales"
+AJAOutput.MultiViewEnable="Habilitar vista múltiple"
+AJAOutput.MultiViewAudioSource="Fuente de audio de la vista múltiple"

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác